diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h index 0bd530702118..b414b6ad4b33 100644 --- a/arch/arm/include/asm/arch_gicv3.h +++ b/arch/arm/include/asm/arch_gicv3.h @@ -276,6 +276,7 @@ static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr) * The upper-word (aff3) will always be 0, so there is no need for a lock. */ #define gic_write_irouter(v, c) __gic_writeq_nonatomic(v, c) +#define gic_read_irouter(c) __gic_readq_nonatomic(c) /* * GICR_TYPER is an ID register and doesn't need atomicity. diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 6dcf2da859a5..b525f4161d5f 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -230,6 +230,16 @@ config ARCH_SDM660 This enables support for the SDM660 chipset. If you do not wish to build a kernel that runs on this chipset, say 'N' here. +config ARCH_SDM845 + bool "Enable Support for Qualcomm Technologies Inc. SDM845" + depends on ARCH_QCOM + select COMMON_CLK + select COMMON_CLK_QCOM + select QCOM_GDSC + help + This enables support for the SDM845 chipset. If you do not + wish to build a kernel that runs on this chipset, say 'N' here. + config ARCH_REALTEK bool "Realtek Platforms" help diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index d3f2212c9ce2..3f97a65461e5 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -33,7 +33,11 @@ subdir-y += zte else + ifeq ($(CONFIG_ARCH_SDM845),y) + subdir-y += qcom + else subdir-y += vendor + endif endif dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(foreach d,$(subdir), $(wildcard $(dtstree)/$(d)/*.dts))) diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 48c84598f20c..f88a9dc6ccc4 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -1,12 +1,66 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc.dtb -dtb-$(CONFIG_ARCH_QCOM) += apq8096-db820c.dtb -dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk01.dtb -dtb-$(CONFIG_ARCH_QCOM) += msm8916-mtp.dtb -dtb-$(CONFIG_ARCH_QCOM) += msm8992-bullhead-rev-101.dtb -dtb-$(CONFIG_ARCH_QCOM) += msm8994-angler-rev-101.dtb -dtb-$(CONFIG_ARCH_QCOM) += msm8996-mtp.dtb +# dtb-$(CONFIG_ARCH_QCOM) += apq8016-sbc.dtb +# dtb-$(CONFIG_ARCH_QCOM) += apq8096-db820c.dtb +# dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk01.dtb +# dtb-$(CONFIG_ARCH_QCOM) += msm8916-mtp.dtb +# dtb-$(CONFIG_ARCH_QCOM) += msm8992-bullhead-rev-101.dtb +# dtb-$(CONFIG_ARCH_QCOM) += msm8994-angler-rev-101.dtb +# dtb-$(CONFIG_ARCH_QCOM) += msm8996-mtp.dtb + +ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) +dtbo-$(CONFIG_ARCH_QCOM) += \ + sdm845-mtp-overlay.dtbo \ + sdm845-v2-mtp-overlay.dtbo \ + sdm845-v2.1-mtp-overlay.dtbo \ + enchilada-evb-v2.1-overlay.dtbo \ + enchilada-t0-v2.1-overlay.dtbo \ + enchilada-evt1-v2.1-overlay.dtbo \ + enchilada-evt2-v2.1-overlay.dtbo \ + enchilada-evt2-cxo-v2.1-overlay.dtbo \ + enchilada-dvt-v2.1-overlay.dtbo \ + enchilada-dvt-v2.1-backup-overlay.dtbo \ + enchilada-dvt-v2.1-usb30-overlay.dtbo \ + enchilada-pvt-v2.1-overlay.dtbo \ + enchilada-pvt-v2.1-backup-overlay.dtbo \ + enchilada-mp-v2.1-overlay.dtbo \ + fajita-evb-v2.1-overlay.dtbo \ + fajita-t0-v2.1-overlay.dtbo \ + fajita-evt1-v2.1-overlay.dtbo \ + fajita-dvt-v2.1-overlay.dtbo \ + fajita-dvt-v2.1-bu-overlay.dtbo \ + fajita-pvt-v2.1-overlay.dtbo \ + fajita-pvt-v2.1-bu-overlay.dtbo \ + fajita-pvt-v2.1-v1-overlay.dtbo \ + fajita-pvt-v2.1-spec-overlay.dtbo \ + fajita-mp-v2.1-spec-overlay.dtbo + +sdm845-mtp-overlay.dtbo-base := sdm845.dtb +sdm845-v2-mtp-overlay.dtbo-base := sdm845-v2.dtb +sdm845-v2.1-mtp-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-evb-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-t0-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-evt1-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-evt2-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-evt2-cxo-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-dvt-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-dvt-v2.1-backup-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-dvt-v2.1-usb30-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-pvt-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-pvt-v2.1-backup-overlay.dtbo-base := sdm845-v2.1.dtb +enchilada-mp-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-evb-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-t0-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-evt1-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-dvt-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-dvt-v2.1-bu-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-pvt-v2.1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-pvt-v2.1-bu-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-pvt-v2.1-v1-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-pvt-v2.1-spec-overlay.dtbo-base := sdm845-v2.1.dtb +fajita-mp-v2.1-spec-overlay.dtbo-base := sdm845-v2.1.dtb +else dtb-$(CONFIG_ARCH_QCOM) += sdm845-mtp.dtb +endif always := $(dtb-y) subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/qcom/OP-batterydata-3300mah.dtsi b/arch/arm64/boot/dts/qcom/OP-batterydata-3300mah.dtsi new file mode 100644 index 000000000000..0ecee72799b3 --- /dev/null +++ b/arch/arm64/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/arm64/boot/dts/qcom/OP-batterydata-3700mah.dtsi b/arch/arm64/boot/dts/qcom/OP-batterydata-3700mah.dtsi new file mode 100644 index 000000000000..aef1d3f4f8d3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/OP-batterydata-3700mah.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_3700mAh { + 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_3700mAh"; + 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/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi new file mode 100644 index 000000000000..a17fd632981a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_nt35597_cmd: qcom,mdss_dsi_nt35597_wqxga_cmd{ + qcom,mdss-dsi-panel-name = + "Dual nt35597 cmd mode dsi panel without DSC"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + 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,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,ulps-enabled; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-on-command = + [15 01 00 00 10 00 02 ff 10 + 15 01 00 00 10 00 02 fb 01 + 15 01 00 00 10 00 02 ba 03 + 15 01 00 00 10 00 02 e5 01 + 15 01 00 00 10 00 02 35 00 + 15 01 00 00 10 00 02 bb 10 + 15 01 00 00 10 00 02 b0 03 + 15 01 00 00 10 00 02 ff e0 + 15 01 00 00 10 00 02 fb 01 + 15 01 00 00 10 00 02 6b 3d + 15 01 00 00 10 00 02 6c 3d + 15 01 00 00 10 00 02 6d 3d + 15 01 00 00 10 00 02 6e 3d + 15 01 00 00 10 00 02 6f 3d + 15 01 00 00 10 00 02 35 02 + 15 01 00 00 10 00 02 36 72 + 15 01 00 00 10 00 02 37 10 + 15 01 00 00 10 00 02 08 c0 + 15 01 00 00 10 00 02 ff 24 + 15 01 00 00 10 00 02 fb 01 + 15 01 00 00 10 00 02 c6 06 + 15 01 00 00 10 00 02 ff 10 + 05 01 00 00 a0 00 02 11 00 + 05 01 00 00 a0 00 02 29 00]; + + qcom,mdss-dsi-off-command = [05 01 00 00 0a 00 + 02 28 00 05 01 00 00 3c 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-panel-timings = [cd 32 22 00 60 + 64 26 34 29 03 04 00]; + + qcom,config-select = + <&dsi_dual_nt35597_cmd_config0>; + + dsi_dual_nt35597_cmd_config0: config0 { + qcom,split-mode = "dualctl-split"; + }; + + dsi_dual_nt35597_cmd_config1: config1 { + qcom,split-mode = "pingpong-split"; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi new file mode 100644 index 000000000000..7c29159da016 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_nt35597_video: qcom,mdss_dsi_nt35597_wqxga_video { + qcom,mdss-dsi-panel-name = + "Dual nt35597 video mode dsi panel without DSC"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0x3ff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + qcom,cmd-sync-wait-broadcast; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-min-refresh-rate = <55>; + qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = + "dfps_immediate_porch_mode_vfp"; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-on-command = + [15 01 00 00 00 00 02 ff 10 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 ba 03 + 15 01 00 00 00 00 02 e5 01 + 15 01 00 00 00 00 02 35 00 + 15 01 00 00 00 00 02 bb 03 + 15 01 00 00 00 00 02 b0 03 + 39 01 00 00 00 00 06 3b 03 + 08 08 64 9a + 15 01 00 00 00 00 02 ff e0 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 6b 3d + 15 01 00 00 00 00 02 6c 3d + 15 01 00 00 00 00 02 6d 3d + 15 01 00 00 00 00 02 6e 3d + 15 01 00 00 00 00 02 6f 3d + 15 01 00 00 00 00 02 35 02 + 15 01 00 00 00 00 02 36 72 + 15 01 00 00 00 00 02 37 10 + 15 01 00 00 00 00 02 08 c0 + 15 01 00 00 00 00 02 ff 10 + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 32 00 02 29 00]; + qcom,mdss-dsi-off-command = [05 01 00 00 + 0a 00 02 28 00 05 01 00 00 3c 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 = <0>; + qcom,mdss-dsi-panel-timings = [e2 36 24 00 66 + 6a 28 38 2a 03 04 00]; + + qcom,config-select = + <&dsi_dual_nt35597_video_config0>; + + dsi_dual_nt35597_video_config0: + config0 { + qcom,split-mode = + "dualctl-split"; + }; + + dsi_dual_nt35597_video_config1: + config1 { + qcom,split-mode = + "pingpong-split"; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi new file mode 100644 index 000000000000..65fe181d8f71 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_nt35597_truly_dsc_cmd: qcom,mdss_dsi_nt35597_dsc_cmd_truly { + qcom,mdss-dsi-panel-name = + "nt35597 cmd mode dsi truly panel with DSC"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + 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-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1440>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-front-porch = <10>; + 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-panel-jitter = <0x1 0x1>; + qcom,mdss-dsi-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 ff 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1e + 15 01 00 00 00 00 02 0b 73 + 15 01 00 00 00 00 02 0c 73 + 15 01 00 00 00 00 02 0e b0 + 15 01 00 00 00 00 02 0f ae + 15 01 00 00 00 00 02 11 b8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5a 00 + 15 01 00 00 00 00 02 5b 01 + 15 01 00 00 00 00 02 5c 80 + 15 01 00 00 00 00 02 5d 81 + 15 01 00 00 00 00 02 5e 00 + 15 01 00 00 00 00 02 5f 01 + 15 01 00 00 00 00 02 72 11 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 1c + 15 01 00 00 00 00 02 01 0b + 15 01 00 00 00 00 02 02 0c + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0f + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8a + 15 01 00 00 00 00 02 0a 13 + 15 01 00 00 00 00 02 0b 13 + 15 01 00 00 00 00 02 0c 15 + 15 01 00 00 00 00 02 0d 15 + 15 01 00 00 00 00 02 0e 17 + 15 01 00 00 00 00 02 0f 17 + 15 01 00 00 00 00 02 10 1c + 15 01 00 00 00 00 02 11 0b + 15 01 00 00 00 00 02 12 0c + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0f + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8a + 15 01 00 00 00 00 02 1a 13 + 15 01 00 00 00 00 02 1b 13 + 15 01 00 00 00 00 02 1c 15 + 15 01 00 00 00 00 02 1d 15 + 15 01 00 00 00 00 02 1e 17 + 15 01 00 00 00 00 02 1f 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6d + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 e0 00 + 15 01 00 00 00 00 02 dc 21 + 15 01 00 00 00 00 02 dd 22 + 15 01 00 00 00 00 02 de 07 + 15 01 00 00 00 00 02 df 07 + 15 01 00 00 00 00 02 e3 6D + 15 01 00 00 00 00 02 e1 07 + 15 01 00 00 00 00 02 e2 07 + /* UD */ + 15 01 00 00 00 00 02 29 d8 + 15 01 00 00 00 00 02 2a 2a + /* CLK */ + 15 01 00 00 00 00 02 4b 03 + 15 01 00 00 00 00 02 4c 11 + 15 01 00 00 00 00 02 4d 10 + 15 01 00 00 00 00 02 4e 01 + 15 01 00 00 00 00 02 4f 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5b 43 + 15 01 00 00 00 00 02 5c 00 + 15 01 00 00 00 00 02 5f 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7a 80 + 15 01 00 00 00 00 02 7b 91 + 15 01 00 00 00 00 02 7c D8 + 15 01 00 00 00 00 02 7d 60 + 15 01 00 00 00 00 02 7f 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 b3 C0 + 15 01 00 00 00 00 02 b4 00 + 15 01 00 00 00 00 02 b5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0a + 15 01 00 00 00 00 02 94 0a + /* Inversion Type */ + 15 01 00 00 00 00 02 8a 00 + 15 01 00 00 00 00 02 9b ff + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9d b0 + 15 01 00 00 00 00 02 9f 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 ec 00 + /* CMD1 */ + 15 01 00 00 00 00 02 ff 10 + /* VESA DSC PPS settings + * (1440x2560 slide 16H) + */ + 39 01 00 00 00 00 11 c1 09 + 20 00 10 02 00 02 68 01 bb + 00 0a 06 67 04 c5 + + 39 01 00 00 00 00 03 c2 10 f0 + /* C0h = 0x0(2 Port SDC) + * 0x01(1 PortA FBC) + * 0x02(MTK) 0x03(1 PortA VESA) + */ + 15 01 00 00 00 00 02 c0 03 + /* VBP+VSA=,VFP = 10H */ + 15 01 00 00 00 00 04 3b 03 0a 0a + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 e5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 bb 10 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 fb 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 00 02 10 00]; + + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <2>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi new file mode 100644 index 000000000000..93d894cb84aa --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_nt35597_truly_dsc_video: qcom,mdss_dsi_nt35597_dsc_video_truly { + qcom,mdss-dsi-panel-name = + "nt35597 video mode dsi truly panel with DSC"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + qcom,mdss-dsi-dma-schedule-line = <5>; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1440>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-front-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 ff 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1e + 15 01 00 00 00 00 02 0b 73 + 15 01 00 00 00 00 02 0c 73 + 15 01 00 00 00 00 02 0e b0 + 15 01 00 00 00 00 02 0f aE + 15 01 00 00 00 00 02 11 b8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5a 00 + 15 01 00 00 00 00 02 5b 01 + 15 01 00 00 00 00 02 5c 80 + 15 01 00 00 00 00 02 5d 81 + 15 01 00 00 00 00 02 5e 00 + 15 01 00 00 00 00 02 5f 01 + 15 01 00 00 00 00 02 72 11 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 1c + 15 01 00 00 00 00 02 01 0b + 15 01 00 00 00 00 02 02 0c + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0f + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8a + 15 01 00 00 00 00 02 0a 13 + 15 01 00 00 00 00 02 0b 13 + 15 01 00 00 00 00 02 0c 15 + 15 01 00 00 00 00 02 0d 15 + 15 01 00 00 00 00 02 0e 17 + 15 01 00 00 00 00 02 0f 17 + 15 01 00 00 00 00 02 10 1c + 15 01 00 00 00 00 02 11 0b + 15 01 00 00 00 00 02 12 0c + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0f + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8a + 15 01 00 00 00 00 02 1a 13 + 15 01 00 00 00 00 02 1b 13 + 15 01 00 00 00 00 02 1c 15 + 15 01 00 00 00 00 02 1d 15 + 15 01 00 00 00 00 02 1e 17 + 15 01 00 00 00 00 02 1f 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6d + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 e0 00 + 15 01 00 00 00 00 02 dc 21 + 15 01 00 00 00 00 02 dd 22 + 15 01 00 00 00 00 02 de 07 + 15 01 00 00 00 00 02 df 07 + 15 01 00 00 00 00 02 e3 6d + 15 01 00 00 00 00 02 e1 07 + 15 01 00 00 00 00 02 e2 07 + /* UD */ + 15 01 00 00 00 00 02 29 d8 + 15 01 00 00 00 00 02 2a 2a + /* CLK */ + 15 01 00 00 00 00 02 4b 03 + 15 01 00 00 00 00 02 4c 11 + 15 01 00 00 00 00 02 4d 10 + 15 01 00 00 00 00 02 4e 01 + 15 01 00 00 00 00 02 4f 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5b 43 + 15 01 00 00 00 00 02 5c 00 + 15 01 00 00 00 00 02 5f 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7a 80 + 15 01 00 00 00 00 02 7b 91 + 15 01 00 00 00 00 02 7c d8 + 15 01 00 00 00 00 02 7d 60 + 15 01 00 00 00 00 02 7f 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 b3 c0 + 15 01 00 00 00 00 02 b4 00 + 15 01 00 00 00 00 02 b5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0a + 15 01 00 00 00 00 02 94 0a + /* Inversion Type */ + 15 01 00 00 00 00 02 8a 00 + 15 01 00 00 00 00 02 9b ff + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9d b0 + 15 01 00 00 00 00 02 9f 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 ec 00 + /* CMD1 */ + 15 01 00 00 00 00 02 ff 10 + /* VESA DSC PPS settings + * (1440x2560 slide 16H) + */ + 39 01 00 00 00 00 11 c1 09 + 20 00 10 02 00 02 68 01 bb + 00 0a 06 67 04 c5 + + 39 01 00 00 00 00 03 c2 10 f0 + /* C0h = 0x00(2 Port SDC); + * 0x01(1 PortA FBC); + * 0x02(MTK); 0x03(1 PortA VESA) + */ + 15 01 00 00 00 00 02 c0 03 + /* VBP+VSA=,VFP = 10H */ + 39 01 00 00 00 00 04 3b 03 0a 0a + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 e5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 bb 03 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 fb 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 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 = <0>; + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <2>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi new file mode 100644 index 000000000000..eb714b92360b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_nt35597_truly_cmd: qcom,mdss_dsi_nt35597_truly_wqxga_cmd{ + qcom,mdss-dsi-panel-name = + "Dual nt35597 cmd mode dsi truly panel without DSC"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + 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,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + 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-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-jitter = <0x1 0x1>; + qcom,mdss-dsi-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 FF 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1E + 15 01 00 00 00 00 02 0B 73 + 15 01 00 00 00 00 02 0C 73 + 15 01 00 00 00 00 02 0E B0 + 15 01 00 00 00 00 02 0F AE + 15 01 00 00 00 00 02 11 B8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5A 00 + 15 01 00 00 00 00 02 5B 01 + 15 01 00 00 00 00 02 5C 80 + 15 01 00 00 00 00 02 5D 81 + 15 01 00 00 00 00 02 5E 00 + 15 01 00 00 00 00 02 5F 01 + 15 01 00 00 00 00 02 72 11 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 1C + 15 01 00 00 00 00 02 01 0B + 15 01 00 00 00 00 02 02 0C + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0F + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8A + 15 01 00 00 00 00 02 0A 13 + 15 01 00 00 00 00 02 0B 13 + 15 01 00 00 00 00 02 0C 15 + 15 01 00 00 00 00 02 0D 15 + 15 01 00 00 00 00 02 0E 17 + 15 01 00 00 00 00 02 0F 17 + 15 01 00 00 00 00 02 10 1C + 15 01 00 00 00 00 02 11 0B + 15 01 00 00 00 00 02 12 0C + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0F + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8A + 15 01 00 00 00 00 02 1A 13 + 15 01 00 00 00 00 02 1B 13 + 15 01 00 00 00 00 02 1C 15 + 15 01 00 00 00 00 02 1D 15 + 15 01 00 00 00 00 02 1E 17 + 15 01 00 00 00 00 02 1F 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6D + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 E0 00 + 15 01 00 00 00 00 02 DC 21 + 15 01 00 00 00 00 02 DD 22 + 15 01 00 00 00 00 02 DE 07 + 15 01 00 00 00 00 02 DF 07 + 15 01 00 00 00 00 02 E3 6D + 15 01 00 00 00 00 02 E1 07 + 15 01 00 00 00 00 02 E2 07 + /* UD */ + 15 01 00 00 00 00 02 29 D8 + 15 01 00 00 00 00 02 2A 2A + /* CLK */ + 15 01 00 00 00 00 02 4B 03 + 15 01 00 00 00 00 02 4C 11 + 15 01 00 00 00 00 02 4D 10 + 15 01 00 00 00 00 02 4E 01 + 15 01 00 00 00 00 02 4F 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5B 43 + 15 01 00 00 00 00 02 5C 00 + 15 01 00 00 00 00 02 5F 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7A 80 + 15 01 00 00 00 00 02 7B 91 + 15 01 00 00 00 00 02 7C D8 + 15 01 00 00 00 00 02 7D 60 + 15 01 00 00 00 00 02 7F 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 B3 C0 + 15 01 00 00 00 00 02 B4 00 + 15 01 00 00 00 00 02 B5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0A + 15 01 00 00 00 00 02 94 0A + /* Inversion Type */ + 15 01 00 00 00 00 02 8A 00 + 15 01 00 00 00 00 02 9B FF + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9D B0 + 15 01 00 00 00 00 02 9F 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 EC 00 + /* CMD1 */ + 15 01 00 00 00 00 02 ff 10 + /* VBP+VSA=,VFP = 10H */ + 15 01 00 00 00 00 04 3B 03 0A 0A + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 E5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 BB 10 + /* NVT SDC */ + 15 01 00 00 00 00 02 C0 00 + /* GRAM Slide Parameter */ + 29 01 00 00 00 00 0C C9 01 01 70 + 00 0A 06 67 04 C5 12 18 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 FB 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi new file mode 100644 index 000000000000..838a1480b65c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_nt35597_truly_video: qcom,mdss_dsi_nt35597_wqxga_video_truly { + qcom,mdss-dsi-panel-name = + "Dual nt35597 video mode dsi truly panel without DSC"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 50>; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-underflow-color = <0x3ff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-bpp = <24>; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 FF 20 + 15 01 00 00 00 00 02 FB 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1E + 15 01 00 00 00 00 02 0B 73 + 15 01 00 00 00 00 02 0C 73 + 15 01 00 00 00 00 02 0E B0 + 15 01 00 00 00 00 02 0F AE + 15 01 00 00 00 00 02 11 B8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5A 00 + 15 01 00 00 00 00 02 5B 01 + 15 01 00 00 00 00 02 5C 80 + 15 01 00 00 00 00 02 5D 81 + 15 01 00 00 00 00 02 5E 00 + 15 01 00 00 00 00 02 5F 01 + 15 01 00 00 00 00 02 72 11 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 FF 24 + 15 01 00 00 00 00 02 FB 01 + 15 01 00 00 00 00 02 00 1C + 15 01 00 00 00 00 02 01 0B + 15 01 00 00 00 00 02 02 0C + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0F + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8A + 15 01 00 00 00 00 02 0A 13 + 15 01 00 00 00 00 02 0B 13 + 15 01 00 00 00 00 02 0C 15 + 15 01 00 00 00 00 02 0D 15 + 15 01 00 00 00 00 02 0E 17 + 15 01 00 00 00 00 02 0F 17 + 15 01 00 00 00 00 02 10 1C + 15 01 00 00 00 00 02 11 0B + 15 01 00 00 00 00 02 12 0C + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0F + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8A + 15 01 00 00 00 00 02 1A 13 + 15 01 00 00 00 00 02 1B 13 + 15 01 00 00 00 00 02 1C 15 + 15 01 00 00 00 00 02 1D 15 + 15 01 00 00 00 00 02 1E 17 + 15 01 00 00 00 00 02 1F 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6D + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 E0 00 + 15 01 00 00 00 00 02 DC 21 + 15 01 00 00 00 00 02 DD 22 + 15 01 00 00 00 00 02 DE 07 + 15 01 00 00 00 00 02 DF 07 + 15 01 00 00 00 00 02 E3 6D + 15 01 00 00 00 00 02 E1 07 + 15 01 00 00 00 00 02 E2 07 + /* UD */ + 15 01 00 00 00 00 02 29 D8 + 15 01 00 00 00 00 02 2A 2A + /* CLK */ + 15 01 00 00 00 00 02 4B 03 + 15 01 00 00 00 00 02 4C 11 + 15 01 00 00 00 00 02 4D 10 + 15 01 00 00 00 00 02 4E 01 + 15 01 00 00 00 00 02 4F 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5B 43 + 15 01 00 00 00 00 02 5C 00 + 15 01 00 00 00 00 02 5F 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7A 80 + 15 01 00 00 00 00 02 7B 91 + 15 01 00 00 00 00 02 7C D8 + 15 01 00 00 00 00 02 7D 60 + 15 01 00 00 00 00 02 7F 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 B3 C0 + 15 01 00 00 00 00 02 B4 00 + 15 01 00 00 00 00 02 B5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0A + 15 01 00 00 00 00 02 94 0A + /* Inversion Type */ + 15 01 00 00 00 00 02 8A 00 + 15 01 00 00 00 00 02 9B FF + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9D B0 + 15 01 00 00 00 00 02 9F 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 EC 00 + /* CMD1 */ + 15 01 00 00 00 00 02 FF 10 + /* VBP+VSA=,VFP = 10H */ + 15 01 00 00 00 00 04 3B 03 0A 0A + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 E5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 BB 03 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 FB 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 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 = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi new file mode 100644 index 000000000000..fe029dff06f1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_nt36850_truly_cmd: qcom,mdss_dsi_nt36850_truly_wqhd_cmd { + qcom,mdss-dsi-panel-name = + "Dual nt36850 cmd mode dsi truly panel without DSC"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-tx-eot-append; + 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-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 50>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <140>; + qcom,mdss-dsi-h-pulse-width = <20>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <20>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <4>; + 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-on-command = [ + 15 01 00 00 00 00 02 ff 10 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 36 00 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 03 e8 + 15 01 00 00 00 00 02 51 ff + 15 01 00 00 00 00 02 53 2c + 15 01 00 00 00 00 02 55 01 + 05 01 00 00 0a 00 02 20 00 + 15 01 00 00 00 00 02 bb 10 + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 78 00 02 28 00 + 05 01 00 00 78 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 = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-r63417-truly-1080p-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-r63417-truly-1080p-cmd.dtsi new file mode 100644 index 000000000000..1311ee4203a3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-r63417-truly-1080p-cmd.dtsi @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_r63417_truly_1080_cmd: qcom,mdss_dsi_r63417_truly_1080p_cmd { + qcom,mdss-dsi-panel-name = + "r63417 truly 1080p cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-t-clk-post = <0x02>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-post-init-delay = <1>; + qcom,mdss-dsi-mdp-trigger = "none"; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <96>; + 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 = <16>; + qcom,mdss-dsi-v-front-porch = <4>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-on-command = + [23 01 00 00 00 00 02 d6 01 + 15 01 00 00 00 00 02 35 00 + 15 01 00 00 00 00 02 51 ff + 15 01 00 00 00 00 02 53 2c + 15 01 00 00 00 00 02 55 00 + 05 01 00 00 78 00 02 11 00 + 23 01 00 00 00 00 02 b0 04 + 29 01 00 00 00 00 07 b3 04 00 00 00 00 00 + 29 01 00 00 00 00 03 b6 3a d3 + 29 01 00 00 00 00 03 c0 00 00 + 29 01 00 00 00 00 23 c1 84 60 10 eb ff 6f ce ff ff 17 02 + 58 73 ae b1 20 c6 ff ff 1f f3 ff 5f 10 10 10 10 + 00 02 01 22 22 00 01 + 29 01 00 00 00 00 08 c2 31 f7 80 06 08 00 00 + 29 01 00 00 00 00 17 c4 70 00 00 00 00 04 00 00 00 0c 06 + 00 00 00 00 00 04 00 00 00 0c 06 + 29 01 00 00 00 00 29 c6 78 69 00 69 00 69 00 00 00 00 00 + 69 00 69 00 69 10 19 07 00 78 00 69 00 69 00 69 + 00 00 00 00 00 69 00 69 00 69 10 19 07 + 29 01 00 00 00 00 0a cb 31 fc 3f 8c 00 00 00 00 c0 + 23 01 00 00 00 00 02 cc 0b + 29 01 00 00 00 00 0b d0 11 81 bb 1e 1e 4c 19 19 0c 00 + 29 01 00 00 00 00 1a d3 1b 33 bb bb b3 33 33 33 00 01 00 + a0 d8 a0 0d 4e 4e 33 3b 22 72 07 3d bf 33 + 29 01 00 00 00 00 08 d5 06 00 00 01 51 01 32 + 29 01 00 00 00 00 1f c7 01 0a 11 18 26 33 3e 50 38 42 52 + 60 67 6e 77 01 0a 11 18 26 33 3e 50 38 42 52 60 + 67 6e 77 + 29 01 00 00 14 00 14 c8 01 00 00 00 00 fc 00 00 00 00 + 00 fc 00 00 00 00 00 fc 00 + 05 01 00 00 14 00 02 29 00]; + qcom,mdss-dsi-off-command = [05 01 00 00 14 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-timings = + [e6 38 26 00 68 6e 2a 3c 44 03 04 00]; + }; + }; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi new file mode 100644 index 000000000000..fad5491282fb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_s6e3ha3_amoled_cmd: qcom,mdss_dsi_s6e3ha3_amoled_wqhd_cmd{ + qcom,mdss-dsi-panel-name = + "Dual s6e3ha3 amoled cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <31>; + qcom,mdss-dsi-v-front-porch = <30>; + qcom,mdss-dsi-v-pulse-width = <8>; + 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-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-on-command = [05 01 00 00 05 00 02 11 00 + 39 01 00 00 00 00 05 2a 00 00 05 9f + 39 01 00 00 00 00 05 2b 00 00 09 ff + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 02 b0 10 + 39 01 00 00 00 00 02 b5 a0 + 39 01 00 00 00 00 02 c4 03 + 39 01 00 00 00 00 0a + f6 42 57 37 00 aa cc d0 00 00 + 39 01 00 00 00 00 02 f9 03 + 39 01 00 00 00 00 14 + c2 00 00 d8 d8 00 80 2b 05 08 + 0e 07 0b 05 0d 0a 15 13 20 1e + 39 01 00 00 78 00 03 f0 a5 a5 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 53 20 + 39 01 00 00 00 00 02 51 60 + 05 01 00 00 05 00 02 29 00]; + qcom,mdss-dsi-off-command = [05 01 00 00 3c 00 02 28 00 + 05 01 00 00 b4 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-lp-mode-on = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 10 + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb cd + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 02 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 09 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 c9 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 c0 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 aa + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 30 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; + qcom,mdss-dsi-lp-mode-off = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb 4d + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 04 + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 06 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 05 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 b8 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 80 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 8a + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 80 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-tx-eot-append; + qcom,dcs-cmd-by-left; + 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-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_dsc.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_dsc.dtsi new file mode 100644 index 000000000000..2dc8484ed814 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_dsc.dtsi @@ -0,0 +1,449 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_dsc_cmd: dsi_samsung_dsc_cmd_display { + qcom,mdss-dsi-panel-name = "samsung dsc cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "DSC"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <145>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + //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_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9F>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-id1-command = [06 01 00 01 05 00 02 0A 08]; + qcom,mdss-dsi-panel-id1-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id2-command = [06 01 00 01 05 00 02 0E 08]; + qcom,mdss-dsi-panel-id2-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id3-command = [06 01 00 01 05 00 02 E0 08]; + qcom,mdss-dsi-panel-id3-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id4-command = [06 01 00 01 05 00 02 0F 08]; + qcom,mdss-dsi-panel-id4-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id5-command = [06 01 00 01 05 00 02 E3 08]; + qcom,mdss-dsi-panel-id5-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id6-command = [06 01 00 01 05 00 02 E5 08]; + qcom,mdss-dsi-panel-id6-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id7-command = [06 01 00 01 05 00 02 FB 08]; + qcom,mdss-dsi-panel-id7-command-state = "dsi_hs_mode"; + + qcom,mdss-dsi-panel-read-register-open-command = [ + 39 01 00 00 00 00 03 FC 5A 5A]; + qcom,mdss-dsi-panel-read-register-close-command = [ + 39 01 00 00 00 00 03 FC A5 A5]; + qcom,mdss-dsi-panel-read-register-open-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-read-register-close-command-state = "dsi_hs_mode"; + + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-acl-cmd-index = <0>; + qcom,mdss-dsi-acl-mode-index = <1>; + qcom,mdss-bl-high2bit; + // qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + 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,ulps-enabled; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2340>; + qcom,mdss-dsi-h-front-porch = <72>; + qcom,mdss-dsi-h-back-porch = <36>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <18>; + qcom,mdss-dsi-v-front-porch = <32>; + qcom,mdss-dsi-v-pulse-width = <4>; + 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-panel-jitter = <0x4 0x1>; + //qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-on-command = [ + + + 29 00 00 00 00 00 03 F0 5A 5A + 07 01 00 00 00 00 01 01 /* DSC enable : Compression Mode*/ + 29 01 00 00 00 00 5A 0A 11 00 00 89 30 80 09 24 04 38 00 3C 02 1C 02 1C + 02 00 02 0E 00 20 05 D2 + 00 07 00 0C 01 A1 01 B2 + 18 00 10 F0 03 0C 20 00 + 06 0B 0B 33 0E 1C 2A 38 + 46 54 62 69 70 77 79 7B + 7D 7E 01 02 01 00 09 40 + 09 BE 19 FC 19 FA 19 F8 + 1A 38 1A 78 1A B6 2A F6 + 2B 34 2B 74 3B 74 6B F4 00 + 29 00 00 00 00 00 03 F0 A5 A5 + + + + 05 01 00 00 78 00 01 11 + + 39 01 00 00 00 00 03 FC 5A 5A /*OSC Speed */ + 39 01 00 00 00 00 02 B0 09 + 39 01 00 00 00 00 02 D7 2A + 39 01 00 00 00 00 02 FE B0 + 39 01 00 00 00 00 02 FE 30 + 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 F0 5A 5A + 15 01 00 00 00 00 02 53 20 + 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 9D 01 + 39 01 00 00 00 00 03 F0 A5 A5 + + 39 01 00 00 00 00 03 FC 5A 5A /*FFC Setting*/ + 39 01 00 00 00 00 06 C5 0D 10 B4 62 1A + 39 01 00 00 00 00 03 FC A5 A5 + + 05 01 00 00 00 00 02 29 00]; + + + qcom,mdss-dsi-off-command = [ + + /*Display off Delay 20ms*/ + 05 01 00 00 14 00 02 28 00 + /*Sleep in*/ + 05 01 00 00 00 00 02 10 00 + + ]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <60>; + qcom,mdss-dsc-slice-width = <540>; + qcom,mdss-dsc-slice-per-pkt = <2>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + + + + + /**************************************************************/ + qcom,mdss-dsi-panel-hbm-on-command = [ + 39 01 00 00 00 00 03 51 00 26 + 39 01 00 00 00 00 02 53 E0 + ]; + qcom,mdss-dsi-panel-hbm-on-command-2 = [ + 39 01 00 00 00 00 03 51 00 78 + 39 01 00 00 00 00 02 53 E0 + ]; + qcom,mdss-dsi-panel-hbm-on-command-3 = [ + 39 01 00 00 00 00 03 51 00 CA + 39 01 00 00 00 00 02 53 E0 + ]; + qcom,mdss-dsi-panel-hbm-on-command-4 = [ + 39 01 00 00 00 00 03 51 01 2A + 39 01 00 00 00 00 02 53 E0 + ]; + qcom,mdss-dsi-panel-hbm-on-command-5 = [ + 39 01 00 00 00 00 03 51 03 FF + 39 01 00 00 00 00 02 53 E0 + ]; + qcom,mdss-dsi-panel-hbm-off-command = [ + 39 01 00 00 00 00 02 53 20 + ]; + + qcom,mdss-dsi-hbm-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-hbm-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-1 = [ + 05 01 00 00 23 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 CD 02 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 08 EB 17 41 92 0E 10 82 5A + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 09 23 + 39 01 00 00 00 00 02 B0 09 + 39 01 00 00 00 00 03 E8 10 30 + 15 01 00 00 00 00 02 53 23 /*AOD Low mode 10nit*/ + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 01 /*ALPM*/ + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-2 = [ + 05 01 00 00 23 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 CD 02 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 08 EB 17 41 92 0E 10 82 5A + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 09 23 + 39 01 00 00 00 00 02 B0 09 + 39 01 00 00 00 00 03 E8 10 30 + 15 01 00 00 00 00 02 53 22 /*AOD Low mode 50nit*/ + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 01 /*ALPM*/ + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-3 = [ + + 05 01 00 00 23 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 CD 02 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 08 EB 17 41 92 0E 10 82 5A + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 09 23 + 39 01 00 00 00 00 02 B0 09 + 39 01 00 00 00 00 03 E8 10 30 + 15 01 00 00 00 00 02 53 23/*AOD Low mode 10nit*/ + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 00 /*HLPM*/ + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-4 = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 05 01 00 00 78 00 01 10 + 05 01 00 00 0A 00 01 11 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 20 00 02 CD 01 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 14 00 02 CD 02 + 39 01 00 00 00 00 03 51 03 FF + 15 01 00 00 00 00 02 53 22 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 00 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + + ]; + qcom,mdss-dsi-panel-aod-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 CD 01 + 39 01 00 00 00 00 02 53 20 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-panel-aod-mode-command-1 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 23 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 01 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-panel-aod-mode-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 22 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 01 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-panel-aod-mode-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 23 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 00 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-panel-aod-mode-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 22 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + + qcom,mdss-dsi-aod-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-aod-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-aod-mode-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-serial-num-command = [ + 06 01 00 00 00 00 01 A1 + ]; + qcom,mdss-dsi-panel-serial-num-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-srgb-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 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 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-srgb-off-command = [ + 39 01 00 00 00 00 02 81 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B1 00 01 /* SEED CRC OFF */ + 39 01 00 00 00 00 03 B3 00 C1 /* TCS OFF */ + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-srgb-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-srgb-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-dci-p3-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 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 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-dci-p3-off-command = [ + 39 01 00 00 00 00 02 81 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B1 00 01 /* SEED CRC OFF */ + 39 01 00 00 00 00 03 B3 00 C1 /* TCS OFF */ + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-dci-p3-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-dci-p3-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-night-mode-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 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 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-night-mode-off-command = [ + 39 01 00 00 00 00 02 81 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B1 00 01 /* SEED CRC OFF */ + 39 01 00 00 00 00 03 B3 00 C1 /* TCS OFF */ + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-night-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-night-mode-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-oneplus-mode-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 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 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-oneplus-mode-off-command = [ + 39 01 00 00 00 00 02 81 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B1 00 01 /* SEED CRC OFF */ + 39 01 00 00 00 00 03 B3 00 C1 /* TCS OFF */ + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-panel-oneplus-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-oneplus-mode-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-adaption-mode-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 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 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-adaption-mode-off-command = [ + 39 01 00 00 00 00 02 81 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B1 00 01 /* SEED CRC OFF */ + 39 01 00 00 00 00 03 B3 00 C1 /* TCS OFF */ + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-adaption-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-adaption-mode-off-command-state = "dsi_lp_mode"; + 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-id-command = [06 01 00 01 05 00 02 DC 08]; + qcom,mdss-dsi-panel-id-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id1-command = [06 01 00 01 05 00 02 0A 08]; + qcom,mdss-dsi-panel-id1-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id2-command = [06 01 00 01 05 00 02 0E 08]; + qcom,mdss-dsi-panel-id2-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id3-command = [06 01 00 01 05 00 02 E0 08]; + qcom,mdss-dsi-panel-id3-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id4-command = [06 01 00 01 05 00 02 0F 08]; + qcom,mdss-dsi-panel-id4-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id5-command = [06 01 00 01 05 00 02 E3 08]; + qcom,mdss-dsi-panel-id5-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id6-command = [06 01 00 01 05 00 02 E5 08]; + qcom,mdss-dsi-panel-id6-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-id7-command = [06 01 00 01 05 00 02 FB 08]; + qcom,mdss-dsi-panel-id7-command-state = "dsi_hs_mode"; + + qcom,mdss-dsi-panel-read-register-open-command = [ + 39 01 00 00 00 00 03 FC 5A 5A]; + qcom,mdss-dsi-panel-read-register-close-command = [ + 39 01 00 00 00 00 03 FC A5 A5]; + qcom,mdss-dsi-panel-read-register-open-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-read-register-close-command-state = "dsi_hs_mode"; + + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi new file mode 100644 index 000000000000..252955b54826 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi @@ -0,0 +1,217 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_s6e3fc1_cmd: dsi_samsung_s6e3fc1_cmd_display { + 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-type = "dsi_cmd_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <137>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + //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-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-acl-cmd-index = <0>; + qcom,mdss-dsi-acl-mode-index = <1>; + qcom,mdss-bl-high2bit; + 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,ulps-enabled; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + 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-panel-jitter = <0x8 0xa>; + 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 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 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 = <0>; + /**************************************************************/ + 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-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-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-serial-num-command = [ + 06 01 00 00 00 00 01 A1 + ]; + qcom,mdss-dsi-panel-serial-num-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 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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-srgb-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-srgb-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-dci-p3-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-dci-p3-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-night-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-night-mode-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-oneplus-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-oneplus-mode-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-adaption-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-adaption-mode-off-command-state = "dsi_lp_mode"; + + 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"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc2x01.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc2x01.dtsi new file mode 100644 index 000000000000..1f4e60797c3e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_s6e3fc2x01.dtsi @@ -0,0 +1,529 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_s6e3fc2x01_cmd: dsi_samsung_s6e3fc2x01_cmd_display { + qcom,mdss-dsi-panel-name = "samsung s6e3fc2x01 cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FC2X01"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <145>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + 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-value = <0x9F>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-acl-cmd-index = <0>; + qcom,mdss-dsi-acl-mode-index = <1>; + qcom,mdss-bl-high2bit; + //qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + 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,ulps-enabled; + /* HDR Setting */ + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <15635 16450 34000 16000 13250 34500 7500 3000>; + qcom,mdss-dsi-panel-peak-brightness = <5400000>; + qcom,mdss-dsi-panel-average-brightness = <2000000>; + qcom,mdss-dsi-panel-blackness-level = <2000>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2340>; + qcom,mdss-dsi-h-front-porch = <72>; + qcom,mdss-dsi-h-back-porch = <36>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <18>; + qcom,mdss-dsi-v-front-porch = <32>; + qcom,mdss-dsi-v-pulse-width = <4>; + 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-panel-jitter = <0x4 0x1>; + qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 9F 5A 5A + /*FD setting*/ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 CD 01 + 39 01 00 00 0F 00 03 F0 A5 A5 + /*TE ON*/ + 39 01 00 00 00 00 03 9F A5 A5 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 9F 5A 5A + /*MIC Setting*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 08 EB 17 41 92 0E 10 82 5A + 39 01 00 00 00 00 03 F0 A5 A5 + /*CASET/PASET*/ + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 09 23 + /*TSP H_sync Setting*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 B0 09 + 39 01 00 00 00 00 03 E8 10 30 + 39 01 00 00 00 00 03 F0 A5 A5 + /*Dimming Setting*/ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 01 + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 00 00 02 B7 12 + 39 01 00 00 00 00 03 F0 A5 A5 + /*ESD improvement Setting*/ + 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 02 E3 88 + 39 01 00 00 00 00 02 B0 07 + 39 01 00 00 00 00 02 ED 67 + 39 01 00 00 00 00 03 FC A5 A5 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 20 + 39 01 00 00 00 00 03 F0 A5 A5 + /*ACL off*/ + 39 01 00 00 01 00 02 55 00 + /*SEED OFF*/ + // 39 01 00 00 00 00 03 F0 5A 5A + // 39 01 00 00 00 00 03 B1 00 01 + // 39 01 00 00 00 00 03 F0 A5 A5 + /*SEED TCS OFF*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 B3 00 C1 + 39 01 00 00 00 00 03 F0 A5 A5 + /*Display on*/ + // 39 01 00 00 00 00 03 9F A5 A5 + // 05 01 00 00 00 00 02 29 00 + // 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-off-command = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 39 01 00 00 10 00 03 F0 5A 5A + 39 01 00 00 00 00 02 B0 50 + 39 01 00 00 00 00 02 B9 82 + 39 01 00 00 10 00 03 F0 A5 A5 + 05 01 00 00 00 00 01 10 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 05 + 15 01 00 00 00 00 02 F4 01 + 39 01 00 00 96 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-post-panel-on-command=[ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + /**************************************************************/ + qcom,mdss-dsi-panel-hbm-on-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 80 00 02 B7 92 + 39 01 00 00 00 00 02 53 E8 + 39 01 00 00 23 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 51 00 26 + ]; + qcom,mdss-dsi-panel-hbm-on-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 80 00 02 B7 92 + 39 01 00 00 00 00 02 53 E8 + 39 01 00 00 23 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 51 00 78 + ]; + qcom,mdss-dsi-panel-hbm-on-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 80 00 02 B7 92 + 39 01 00 00 00 00 02 53 E8 + 39 01 00 00 23 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 51 00 CA + ]; + qcom,mdss-dsi-panel-hbm-on-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 80 00 02 B7 92 + 39 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 51 01 2A + + ]; + qcom,mdss-dsi-panel-hbm-on-command-5 = [ + /*ELVSS OFF*/ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 00 00 02 B7 12 + 39 01 00 00 00 00 03 F0 A5 A5 + /*DLY OFF*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 5B + 39 01 00 00 22 00 03 F0 A5 A5 + /*HBM ON */ + 15 01 00 00 00 00 02 53 E0 + 39 01 00 00 0E 00 03 51 03 FF + /*DLY ON*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 53 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-off-command = [ + /*HBM off */ + 15 01 00 00 00 00 02 53 20 + ]; + qcom,mdss-dsi-panel-hbm-max-brightness-command-on = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 80 00 02 B7 92 + 39 01 00 00 40 00 02 53 E8 + 39 01 00 00 80 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 51 03 FF + ]; + qcom,mdss-dsi-panel-hbm-max-brightness-command-off = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 40 00 02 B7 7F + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 40 00 02 B7 92 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 10 00 02 53 28 + ]; + qcom,mdss-dsi-seed-command = [ + 29 01 00 00 00 00 02 81 90 + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 02 B0 02 + 29 01 00 00 00 00 16 B1 B4 02 04 05 FF 02 00 00 FF 00 FF FF F0 00 F0 E0 E1 18 FF F3 F8 + 29 01 00 00 00 00 03 B1 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 + ]; + + qcom,mdss-dsi-panel-command = [ + 29 01 00 00 00 00 02 81 90 + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 02 B0 02 + 29 01 00 00 00 00 16 B1 B4 02 04 05 FF 02 00 00 FF 00 FF FF F0 00 F0 E0 E1 18 FF F3 F8 + 29 01 00 00 00 00 03 B1 00 00 + 29 01 00 00 00 00 03 F0 A5 A5 + ]; + + qcom,mdss-dsi-panel-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-seed-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-hbm-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-hbm-on-command-5-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-hbm-max-brightness-command-on-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-max-brightness-command-off-state = "dsi_lp_mode"; + qcom,mdss-dsi-hbm-off-command-state = "dsi_hs_mode"; + + qcom,mdss-dsi-panel-aod-on-command-1 = [ + /* 10nit */ + 15 01 00 00 00 00 02 53 23 + ]; + qcom,mdss-dsi-panel-aod-on-command-2 = [ + ]; + qcom,mdss-dsi-panel-aod-on-command-3 = [ + /* 50nit */ + 15 01 00 00 00 00 02 53 22 + ]; + qcom,mdss-dsi-panel-aod-on-command-4 = [ + ]; + qcom,mdss-dsi-panel-aod-on-command-5 = [ + /* 50nit */ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 05 01 00 00 78 00 01 10 + 05 01 00 00 05 00 01 11 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 64 00 02 CD 02 + 15 01 00 00 00 00 02 53 22 + 15 01 00 00 00 00 02 B0 A5 + 15 01 00 00 00 00 02 C7 00 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 12 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-panel-aod-off-command = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 64 00 02 CD 01 + 15 01 00 00 00 00 02 53 28 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 13 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-panel-aod-off-samsung-command = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 64 00 02 CD 01 + 15 01 00 00 00 00 02 53 28 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 13 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-panel-aod-off-new-command = [ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 64 00 02 CD 01 + 15 01 00 00 00 00 02 53 28 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 13 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + ]; + qcom,mdss-dsi-panel-aod-off-new-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-off-samsung-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-off-hbm-on-command = [ + /*ELVSS OFF*/ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B7 01 + 15 01 00 00 00 00 02 B0 08 + 15 01 00 00 00 00 02 B7 12 + 39 01 00 00 10 00 03 F0 A5 A5 + /*DLY OFF*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 5B + 39 01 00 00 00 00 03 F0 A5 A5 + 15 01 00 00 00 00 02 53 E0 + 39 01 00 00 0E 00 03 51 03 FF + /*DLY ON*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 53 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-off-hbm-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-real-aod-off-hbm-on-command = [ + /*aod off*/ + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 0A 00 01 28 + 39 01 00 00 00 00 03 9F 5A 5A + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 64 00 02 CD 01 + 15 01 00 00 00 00 02 53 28 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 9F A5 A5 + 05 01 00 00 00 00 01 13 + 05 01 00 00 00 00 01 29 + 39 01 00 00 00 00 03 9F 5A 5A + /*hbm on*/ + /*ELVSS OFF*/ + 39 00 00 00 00 00 03 F0 5A 5A + 15 00 00 00 00 00 02 B0 08 + 15 00 00 00 00 00 02 B7 12 + 39 00 00 00 00 00 03 F0 A5 A5 + /*DLY OFF*/ + 39 00 00 00 00 00 03 F0 5A 5A + 39 00 00 00 00 00 04 B7 00 01 5B + 39 00 00 00 00 00 03 F0 A5 A5 + /*HBM ON */ + 15 00 00 00 00 00 02 53 E0 + 39 01 00 00 00 00 03 51 03 FF + /*DLY OFF*/ + 39 00 00 00 00 00 03 F0 5A 5A + 39 00 00 00 00 00 04 B7 00 01 53 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-real-aod-off-hbm-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-hbm-off-aod-on-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 5B + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 02 53 20 + /*DLY ON*/ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 04 B7 00 01 53 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-off-aod-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-1-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-2-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-3-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-4-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-5-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-mode-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-serial-num-command = [ + 06 01 00 00 00 00 01 A1 + ]; + qcom,mdss-dsi-panel-serial-num-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-srgb-color-mode-on-command = [ + + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 A7 07 05 48 D5 14 06 09 A7 54 EB CB C1 14 C4 E8 E2 1A FF FF E0 + 39 01 00 00 00 00 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + + ]; + qcom,mdss-dsi-panel-display-p3-mode-on-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 FF 00 00 12 F0 00 02 02 e9 19 FF FC ED 03 EC FC F9 00 FF FF FD + 39 01 00 00 00 00 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-display-wide-color-mode-on-command = [ + 39 01 00 00 02 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 FF 00 00 00 FF 00 00 00 FF 00 FF FF FF 00 FF FF FF 00 FF FF FF + 39 01 00 00 00 00 03 B1 00 00 + 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 B1 00 01 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-customer-srgb-enable-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 A7 07 05 48 D5 14 06 09 A7 54 EB CB C1 14 C4 E8 E2 1A FF FF E0 + 39 01 00 00 00 00 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-customer-p3-enable-command = [ + 39 01 00 00 00 00 02 81 90 + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 B0 02 + 39 01 00 00 00 00 16 B1 FF 00 00 12 F0 00 02 02 e9 19 FF FC ED 03 EC FC F9 00 FF FF FD + 39 01 00 00 00 00 03 B1 00 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-customer-p3-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-customer-srgb-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-laoding-effect-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-laoding-effect-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-srgb-color-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-p3-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-wide-color-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-night-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-dci-p3-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-adaption-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-adaption-mode-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0E 08]; + qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id-command = [06 01 00 01 05 00 02 DC 08]; + qcom,mdss-dsi-panel-id-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id1-command = [06 01 00 01 05 00 02 0A 08]; + qcom,mdss-dsi-panel-id1-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id2-command = [06 01 00 01 05 00 02 0E 08]; + qcom,mdss-dsi-panel-id2-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id3-command = [06 01 00 01 05 00 02 E0 08]; + qcom,mdss-dsi-panel-id3-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id4-command = [06 01 00 01 05 00 02 0F 08]; + qcom,mdss-dsi-panel-id4-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id5-command = [06 01 00 01 05 00 02 E3 08]; + qcom,mdss-dsi-panel-id5-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id6-command = [06 01 00 01 05 00 02 E5 08]; + qcom,mdss-dsi-panel-id6-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-id7-command = [06 01 00 01 05 00 02 FB 08]; + qcom,mdss-dsi-panel-id7-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-level1-command = [15 01 00 00 00 00 02 B0 08]; + qcom,mdss-dsi-panel-hbm-level1-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-read-command = [06 01 00 01 05 00 02 B7 08]; + qcom,mdss-dsi-panel-hbm-read-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-read-register-open-command = [ + 39 01 00 00 00 00 03 FC 5A 5A]; + qcom,mdss-dsi-panel-read-register-close-command = [ + 39 01 00 00 00 00 03 FC A5 A5]; + qcom,mdss-dsi-panel-read-register-open-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-read-register-close-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-read-hbm-elvss-open-command = [ + 39 01 00 00 00 00 03 F0 5A 5A]; + qcom,mdss-dsi-panel-read-hbm-elvss-close-command = [ + 39 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-panel-read-hbm-elvss-open-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-read-hbm-elvss-close-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-diming-on-command = [39 01 00 00 00 00 02 B7 92]; + qcom,mdss-dsi-panel-hbm-diming-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-diming-off-command = [39 01 00 00 00 00 02 B7 12]; + qcom,mdss-dsi-panel-hbm-diming-off-command-state = "dsi_lp_mode"; + + + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sefeg01_s_cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sefeg01_s_cmd.dtsi new file mode 100644 index 000000000000..4d0a9988a005 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sefeg01_s_cmd.dtsi @@ -0,0 +1,392 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_sofeg01_s_cmd: dsi_samsung_sofeg01_s_cmd_display { + qcom,mdss-dsi-panel-name = "samsung sofeg01_s cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "SOFEG01_S"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <145>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + //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-value = <0x9c>; + //qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-acl-cmd-index = <0>; + qcom,mdss-dsi-acl-mode-index = <1>; + qcom,mdss-bl-high2bit; + //qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + 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,ulps-enabled; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2280>; + qcom,mdss-dsi-h-front-porch = <112>; + qcom,mdss-dsi-h-back-porch = <36>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <36>; + qcom,mdss-dsi-v-pulse-width = <8>; + 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-panel-jitter = <0x8 0xa>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 24 + 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 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 = <0>; + /**************************************************************/ + 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-panel-hbm-on-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 04 B1 20 10 AC + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 04 B1 20 10 0C + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 04 B1 10 10 6C + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 04 B1 00 10 C0 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-5 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 E8 + 39 01 00 00 00 00 04 B1 00 10 10 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 28 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-hbm-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-hbm-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-1 = [ + 05 01 00 00 0A 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 28 + 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 64 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 03 + 15 01 00 00 00 00 02 BB 07 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-2 = [ + 05 01 00 00 0A 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 28 + 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 64 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 02 + 15 01 00 00 00 00 02 BB 07 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-3 = [ + 05 01 00 00 0A 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 28 + 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 64 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 03 + 15 01 00 00 00 00 02 BB 05 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-on-command-4 = [ + 05 01 00 00 0A 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 28 + 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 64 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 02 + 15 01 00 00 00 00 02 BB 05 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-off-command = [ + 05 01 00 00 0A 00 02 28 00 + 05 01 00 00 78 00 02 10 00 + 05 01 00 00 05 00 02 11 00 + 15 01 00 00 00 00 02 B0 1C + 15 01 00 00 0F 00 02 B5 24 + 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 64 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 20 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-panel-aod-mode-command-1 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 03 + 15 01 00 00 00 00 02 BB 07 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 02 + 15 01 00 00 00 00 02 BB 07 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 03 + 15 01 00 00 00 00 02 BB 05 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 02 + 15 01 00 00 00 00 02 BB 05 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-aod-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-off-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-panel-serial-num-command = [ + 06 01 00 00 00 00 01 A1 + ]; + qcom,mdss-dsi-panel-serial-num-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 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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-srgb-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-srgb-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-dci-p3-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-dci-p3-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-night-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-night-mode-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-oneplus-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-oneplus-mode-off-command-state = "dsi_lp_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 0E 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-adaption-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-adaption-mode-off-command-state = "dsi_lp_mode"; + + //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"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_cmd.dtsi new file mode 100644 index 000000000000..d22ff8e66d95 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_cmd.dtsi @@ -0,0 +1,397 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_sofef00_m_cmd: dsi_samsung_sofef00_m_cmd_display { + qcom,mdss-dsi-panel-name = "samsung sofef00_m cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "SOFEF00_M"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 12>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <145>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + //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-value = <0x9c>; + //qcom,mdss-dsi-panel-status-read-length = <1>; + //qcom,mdss-dsi-panel-status-value-2 = <0xdc>; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-acl-cmd-index = <0>; + qcom,mdss-dsi-acl-mode-index = <1>; + qcom,mdss-bl-high2bit; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + 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,ulps-enabled; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = + <15635 16450 34000 16000 13250 34500 7500 3000>; + qcom,mdss-dsi-panel-peak-brightness = <5400000>; + qcom,mdss-dsi-panel-average-brightness = <2000000>; + qcom,mdss-dsi-panel-blackness-level = <2000>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2280>; + qcom,mdss-dsi-h-front-porch = <112>; + qcom,mdss-dsi-h-back-porch = <36>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <36>; + qcom,mdss-dsi-v-pulse-width = <8>; + 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-panel-jitter = <0x8 0xa>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 07 + 15 01 00 00 00 00 02 B6 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 + 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-seed-command = [ + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 03 E2 00 41 + 29 01 00 00 00 00 02 B0 2C + 29 01 00 00 00 00 14 E2 B4 02 04 05 FF 02 00 00 FF 00 FF FF F0 E0 E1 18 FF F3 F8 + 29 01 00 00 00 00 03 F0 A5 A5 + ]; + + qcom,mdss-dsi-panel-command = [ + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 03 E2 00 41 + 29 01 00 00 00 00 02 B0 2C + 29 01 00 00 00 00 14 E2 B4 02 04 05 FF 02 00 00 FF 00 FF FF F0 E0 E1 18 FF F3 F8 + 29 01 00 00 00 00 03 F0 A5 A5 + ]; + + qcom,mdss-dsi-panel-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-seed-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + /**************************************************************/ + 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-panel-hbm-on-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 02 D4 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 02 3C + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 01 9C + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 00 F0 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-on-command-5 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 00 40 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 28 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-max-brightness-command-on = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 40 00 02 53 E8 + 39 01 00 00 10 00 03 B2 00 40 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-hbm-max-brightness-command-off = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 28 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-hbm-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-hbm-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-max-brightness-command-on-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-hbm-max-brightness-command-off-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-1 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 23 + 05 01 00 00 00 00 02 39 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-on-command-2 = [ + ]; + qcom,mdss-dsi-panel-aod-on-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 22 + 05 01 00 00 00 00 02 39 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-on-command-4 = [ + ]; + qcom,mdss-dsi-panel-aod-on-command-5 = [ + 15 01 00 00 00 00 01 28 + 39 01 00 00 12 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 49 + 15 01 00 00 00 00 02 CB FF + 15 01 00 00 00 00 02 B0 4F + 15 01 00 00 00 00 02 CB C8 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 12 00 01 10 + 05 01 00 00 82 00 01 11 + 39 01 00 00 06 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 BB 03 + 15 01 00 00 00 00 08 EF F0 31 00 33 31 14 35 + 15 01 00 00 78 00 02 53 22 + 05 01 00 00 01 00 02 39 + 39 01 00 00 00 00 03 F0 A5 A5 + 15 01 00 00 12 00 01 29 + ]; + qcom,mdss-dsi-panel-aod-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 28 + 05 01 00 00 00 00 02 38 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-1 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 23 + 05 01 00 00 01 00 02 39 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-2 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 22 + 05 01 00 00 01 00 02 39 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-3 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 23 + 05 01 00 00 01 00 02 38 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-panel-aod-mode-command-4 = [ + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 53 22 + 05 01 00 00 01 00 02 38 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-aod-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-aod-mode-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-real-aod-off-hbm-on-command = [ + 15 01 00 00 00 00 01 28 + 39 01 00 00 12 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 49 + 15 01 00 00 00 00 02 CB FF + 15 01 00 00 00 00 02 B0 4F + 15 01 00 00 00 00 02 CB C8 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 12 00 01 10 + 05 01 00 00 82 00 01 11 + 39 01 00 00 06 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B0 01 + 15 01 00 00 00 00 02 BB 03 + 15 01 00 00 00 00 08 EF F0 31 00 33 31 14 35 + 15 01 00 00 78 00 02 53 28 + 05 01 00 00 01 00 02 38 + 39 01 00 00 00 00 03 F0 A5 A5 + 15 01 00 00 12 00 01 29 + ]; + qcom,mdss-dsi-panel-real-aod-off-hbm-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-serial-num-command = [ + 06 01 00 00 00 00 01 A1 + ]; + qcom,mdss-dsi-panel-serial-num-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-srgb-color-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 E2 00 85 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 E2 CC 06 06 48 F8 18 01 01 D7 4F FE FD D0 12 E0 FF FB 1D FF FF FC + 39 01 00 00 00 00 02 b0 49 + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4A + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4C + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4D + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-display-p3-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 E2 00 85 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 E2 FF 00 00 12 F0 00 02 02 e9 19 FF FC ED 03 EC FC F9 00 FF FF FD + 39 01 00 00 00 00 02 b0 49 + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4A + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4C + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4D + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-display-wide-color-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 E2 00 85 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 E2 FF 00 00 00 FF 00 00 00 FF 00 FF FF FF 00 FF FF FF 00 FF FF FF + 39 01 00 00 00 00 02 b0 49 + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4A + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4C + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4D + 39 01 00 00 00 00 02 E2 00 + 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 E2 00 40 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-customer-srgb-enable-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 E2 00 85 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 E2 CC 06 06 48 F8 18 01 01 D7 4F FE FD D0 12 E0 FF FB 1D FF FF FC + 39 01 00 00 00 00 02 b0 49 + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4A + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4C + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4D + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-customer-p3-enable-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 E2 00 85 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 E2 FF 00 00 12 F0 00 02 02 e9 19 FF FC ED 03 EC FC F9 00 FF FF FD + 39 01 00 00 00 00 02 b0 49 + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4A + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4C + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 02 b0 4D + 39 01 00 00 00 00 02 E2 00 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + + qcom,mdss-dsi-panel-aod-on-command-1-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-2-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-3-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-4-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-aod-on-command-5-state = "dsi_lp_mode"; + + qcom,mdss-dsi-customer-p3-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-customer-srgb-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-laoding-effect-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-laoding-effect-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-srgb-color-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-p3-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-display-wide-color-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-night-mode-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-dci-p3-off-command-state = "dsi_lp_mode"; + 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-id-command = [06 01 00 01 05 00 02 dc 08]; + qcom,mdss-dsi-panel-id-command-state = "dsi_lp_mode"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_video.dtsi new file mode 100644 index 000000000000..819b967009db --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-samsung_sofef00_m_video.dtsi @@ -0,0 +1,78 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_samsung_sofef00_m_video: dsi_samsung_sofef00_m_video_display { + qcom,mdss-dsi-panel-name = + "samsung sofef00_m video mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "SOFEF00_M"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 5>, <0 2>, <1 12>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <145>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-bl-high2bit; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2280>; + qcom,mdss-dsi-h-front-porch = <112>; + qcom,mdss-dsi-h-back-porch = <36>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <36>; + qcom,mdss-dsi-v-pulse-width = <8>; + 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-on-command = [ + 05 01 00 00 0A 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 35 00 + 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 + ]; + 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 = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi new file mode 100644 index 000000000000..61338f2d099a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sharp_1080_cmd: qcom,mdss_dsi_sharp_1080p_cmd { + qcom,mdss-dsi-panel-name = "sharp 1080p cmd mode dsi panel"; + 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-clockrate = <850000000>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-pan-physical-width-dimension = <64>; + qcom,mdss-pan-physical-height-dimension = <117>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <0>; + qcom,mdss-dsi-h-back-porch = <0>; + qcom,mdss-dsi-h-pulse-width = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <0>; + qcom,mdss-dsi-v-front-porch = <0>; + qcom,mdss-dsi-v-pulse-width = <0>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 bb 10 + 15 01 00 00 00 00 02 b0 03 + 05 01 00 00 78 00 01 11 + 15 01 00 00 00 00 02 51 ff + 15 01 00 00 00 00 02 53 24 + 15 01 00 00 00 00 02 ff 23 + 15 01 00 00 00 00 02 08 05 + 15 01 00 00 00 00 02 46 90 + 15 01 00 00 00 00 02 ff 10 + 15 01 00 00 00 00 02 ff f0 + 15 01 00 00 00 00 02 92 01 + 15 01 00 00 00 00 02 ff 10 + /* enable TE generation */ + 15 01 00 00 00 00 02 35 00 + 05 01 00 00 28 00 01 29]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 10 00 01 28 + 05 01 00 00 40 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi new file mode 100644 index 000000000000..16c03f15b312 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sharp_4k_dsc_cmd: qcom,mdss_dsi_sharp_4k_dsc_cmd { + qcom,mdss-dsi-panel-name = "Sharp 4k cmd mode dsc dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 200>, <0 200>, <1 200>; + qcom,mdss-pan-physical-width-dimension = <71>; + qcom,mdss-pan-physical-height-dimension = <129>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-tx-eot-append; + qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-front-porch = <30>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-jitter = <0x8 0xa>; + + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 11 91 09 20 00 20 02 + 00 03 1c 04 21 00 + 0f 03 19 01 97 + 39 01 00 00 00 00 03 92 10 f0 + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 04 + 15 01 00 00 00 00 02 c0 03 + 39 01 00 00 00 00 06 f0 55 aa 52 08 07 + 15 01 00 00 00 00 02 ef 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 00 + 15 01 00 00 00 00 02 b4 01 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 06 f0 55 aa 52 08 01 + 39 01 00 00 00 00 05 ff aa 55 a5 80 + 15 01 00 00 00 00 02 6f 01 + 15 01 00 00 00 00 02 f3 10 + 39 01 00 00 00 00 05 ff aa 55 a5 00 + /* sleep out + delay 120ms */ + 05 01 00 00 78 00 01 11 + /* display on + delay 120ms */ + 05 01 00 00 78 00 01 29 + ]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 78 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <32>; + qcom,mdss-dsc-slice-width = <1080>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi new file mode 100644 index 000000000000..a8e45b1adbff --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sharp_4k_dsc_video: qcom,mdss_dsi_sharp_4k_dsc_video { + qcom,mdss-dsi-panel-name = "Sharp 4k video mode dsc dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 200>, <0 200>, <1 200>; + qcom,mdss-pan-physical-width-dimension = <71>; + qcom,mdss-pan-physical-height-dimension = <129>; + qcom,mdss-dsi-tx-eot-append; + + qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-front-porch = <30>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 11 91 09 20 00 20 02 + 00 03 1c 04 21 00 + 0f 03 19 01 97 + 39 01 00 00 00 00 03 92 10 f0 + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 04 + 15 01 00 00 00 00 02 c0 03 + 39 01 00 00 00 00 06 f0 55 aa 52 08 07 + 15 01 00 00 00 00 02 ef 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 00 + 15 01 00 00 00 00 02 b4 10 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 06 f0 55 aa 52 08 01 + 39 01 00 00 00 00 05 ff aa 55 a5 80 + 15 01 00 00 00 00 02 6f 01 + 15 01 00 00 00 00 02 f3 10 + 39 01 00 00 00 00 05 ff aa 55 a5 00 + /* sleep out + delay 120ms */ + 05 01 00 00 78 00 01 11 + /* display on + delay 120ms */ + 05 01 00 00 78 00 01 29 + ]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 78 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <32>; + qcom,mdss-dsc-slice-width = <1080>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi new file mode 100644 index 000000000000..235bbebb7a38 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_sharp_1080_120hz_cmd: qcom,mdss_dual_sharp_1080p_120hz_cmd { + qcom,mdss-dsi-panel-name = + "sharp 1080p 120hz dual dsi cmd mode panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-reset-sequence = <1 20>, <0 1>, <1 10>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,cmd-sync-wait-broadcast; + qcom,cmd-sync-wait-trigger; + qcom,mdss-tear-check-frame-rate = <12000>; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <28>; + qcom,mdss-dsi-h-back-porch = <4>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <12>; + 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-panel-framerate = <120>; + qcom,mdss-dsi-on-command = + [15 01 00 00 00 00 02 ff 10 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 ba 07 + 15 01 00 00 00 00 02 c0 00 + 15 01 00 00 00 00 02 bb 10 + 15 01 00 00 00 00 02 d9 00 + 15 01 00 00 00 00 02 ef 70 + 15 01 00 00 00 00 02 f7 80 + 39 01 00 00 00 00 06 3b 03 0e 0c 08 1c + 15 01 00 00 00 00 02 e9 0e + 15 01 00 00 00 00 02 ea 0c + 15 01 00 00 00 00 02 35 00 + 15 01 00 00 00 00 02 c0 00 + 15 01 00 00 00 00 02 ff 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 59 6a + 15 01 00 00 00 00 02 0b 1b + 15 01 00 00 00 00 02 61 f7 + 15 01 00 00 00 00 02 62 6c + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 04 c8 + 15 01 00 00 00 00 02 05 1a + 15 01 00 00 00 00 02 0d 93 + 15 01 00 00 00 00 02 0e 93 + 15 01 00 00 00 00 02 0f 7e + 15 01 00 00 00 00 02 06 69 + 15 01 00 00 00 00 02 07 bc + 15 01 00 00 00 00 02 10 03 + 15 01 00 00 00 00 02 11 64 + 15 01 00 00 00 00 02 12 5a + 15 01 00 00 00 00 02 13 40 + 15 01 00 00 00 00 02 14 40 + 15 01 00 00 00 00 02 15 00 + 15 01 00 00 00 00 02 33 13 + 15 01 00 00 00 00 02 5a 40 + 15 01 00 00 00 00 02 5b 40 + 15 01 00 00 00 00 02 5e 80 + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 80 + 15 01 00 00 00 00 02 14 80 + 15 01 00 00 00 00 02 01 80 + 15 01 00 00 00 00 02 15 80 + 15 01 00 00 00 00 02 02 80 + 15 01 00 00 00 00 02 16 80 + 15 01 00 00 00 00 02 03 0a + 15 01 00 00 00 00 02 17 0c + 15 01 00 00 00 00 02 04 06 + 15 01 00 00 00 00 02 18 08 + 15 01 00 00 00 00 02 05 80 + 15 01 00 00 00 00 02 19 80 + 15 01 00 00 00 00 02 06 80 + 15 01 00 00 00 00 02 1a 80 + 15 01 00 00 00 00 02 07 80 + 15 01 00 00 00 00 02 1b 80 + 15 01 00 00 00 00 02 08 80 + 15 01 00 00 00 00 02 1c 80 + 15 01 00 00 00 00 02 09 80 + 15 01 00 00 00 00 02 1d 80 + 15 01 00 00 00 00 02 0a 80 + 15 01 00 00 00 00 02 1e 80 + 15 01 00 00 00 00 02 0b 1a + 15 01 00 00 00 00 02 1f 1b + 15 01 00 00 00 00 02 0c 16 + 15 01 00 00 00 00 02 20 17 + 15 01 00 00 00 00 02 0d 1c + 15 01 00 00 00 00 02 21 1d + 15 01 00 00 00 00 02 0e 18 + 15 01 00 00 00 00 02 22 19 + 15 01 00 00 00 00 02 0f 0e + 15 01 00 00 00 00 02 23 10 + 15 01 00 00 00 00 02 10 80 + 15 01 00 00 00 00 02 24 80 + 15 01 00 00 00 00 02 11 80 + 15 01 00 00 00 00 02 25 80 + 15 01 00 00 00 00 02 12 80 + 15 01 00 00 00 00 02 26 80 + 15 01 00 00 00 00 02 13 80 + 15 01 00 00 00 00 02 27 80 + 15 01 00 00 00 00 02 74 ff + 15 01 00 00 00 00 02 75 ff + 15 01 00 00 00 00 02 8d 00 + 15 01 00 00 00 00 02 8e 00 + 15 01 00 00 00 00 02 8f 9c + 15 01 00 00 00 00 02 90 0c + 15 01 00 00 00 00 02 91 0e + 15 01 00 00 00 00 02 d6 00 + 15 01 00 00 00 00 02 d7 20 + 15 01 00 00 00 00 02 d8 00 + 15 01 00 00 00 00 02 d9 88 + 15 01 00 00 00 00 02 e5 05 + 15 01 00 00 00 00 02 e6 10 + 15 01 00 00 00 00 02 54 06 + 15 01 00 00 00 00 02 55 05 + 15 01 00 00 00 00 02 56 04 + 15 01 00 00 00 00 02 58 03 + 15 01 00 00 00 00 02 59 33 + 15 01 00 00 00 00 02 5a 33 + 15 01 00 00 00 00 02 5b 01 + 15 01 00 00 00 00 02 5c 00 + 15 01 00 00 00 00 02 5d 01 + 15 01 00 00 00 00 02 5e 0a + 15 01 00 00 00 00 02 5f 0a + 15 01 00 00 00 00 02 60 0a + 15 01 00 00 00 00 02 61 0a + 15 01 00 00 00 00 02 62 10 + 15 01 00 00 00 00 02 63 01 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 65 00 + 15 01 00 00 00 00 02 ef 00 + 15 01 00 00 00 00 02 f0 00 + 15 01 00 00 00 00 02 6d 20 + 15 01 00 00 00 00 02 66 44 + 15 01 00 00 00 00 02 68 01 + 15 01 00 00 00 00 02 69 00 + 15 01 00 00 00 00 02 67 11 + 15 01 00 00 00 00 02 6a 06 + 15 01 00 00 00 00 02 6b 31 + 15 01 00 00 00 00 02 6c 90 + 15 01 00 00 00 00 02 ab c3 + 15 01 00 00 00 00 02 b1 49 + 15 01 00 00 00 00 02 aa 80 + 15 01 00 00 00 00 02 b0 90 + 15 01 00 00 00 00 02 b2 a4 + 15 01 00 00 00 00 02 b3 00 + 15 01 00 00 00 00 02 b4 23 + 15 01 00 00 00 00 02 b5 00 + 15 01 00 00 00 00 02 b6 00 + 15 01 00 00 00 00 02 b7 00 + 15 01 00 00 00 00 02 b8 00 + 15 01 00 00 00 00 02 b9 00 + 15 01 00 00 00 00 02 ba 00 + 15 01 00 00 00 00 02 bb 00 + 15 01 00 00 00 00 02 bc 00 + 15 01 00 00 00 00 02 bd 00 + 15 01 00 00 00 00 02 be 00 + 15 01 00 00 00 00 02 bf 00 + 15 01 00 00 00 00 02 c0 00 + 15 01 00 00 00 00 02 c7 40 + 15 01 00 00 00 00 02 c9 00 + 15 01 00 00 00 00 02 c1 2a + 15 01 00 00 00 00 02 c2 2a + 15 01 00 00 00 00 02 c3 00 + 15 01 00 00 00 00 02 c4 00 + 15 01 00 00 00 00 02 c5 00 + 15 01 00 00 00 00 02 c6 00 + 15 01 00 00 00 00 02 c8 ab + 15 01 00 00 00 00 02 ca 00 + 15 01 00 00 00 00 02 cb 00 + 15 01 00 00 00 00 02 cc 20 + 15 01 00 00 00 00 02 cd 40 + 15 01 00 00 00 00 02 ce a8 + 15 01 00 00 00 00 02 cf a8 + 15 01 00 00 00 00 02 d0 00 + 15 01 00 00 00 00 02 d1 00 + 15 01 00 00 00 00 02 d2 00 + 15 01 00 00 00 00 02 d3 00 + 15 01 00 00 00 00 02 af 01 + 15 01 00 00 00 00 02 a4 1e + 15 01 00 00 00 00 02 95 41 + 15 01 00 00 00 00 02 96 03 + 15 01 00 00 00 00 02 98 00 + 15 01 00 00 00 00 02 9a 9a + 15 01 00 00 00 00 02 9b 03 + 15 01 00 00 00 00 02 9d 80 + 15 01 00 00 00 00 02 ff 26 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 fa d0 + 15 01 00 00 00 00 02 6b 80 + 15 01 00 00 00 00 02 6c 5c + 15 01 00 00 00 00 02 6d 0c + 15 01 00 00 00 00 02 6e 0e + 15 01 00 00 00 00 02 58 01 + 15 01 00 00 00 00 02 59 15 + 15 01 00 00 00 00 02 5a 01 + 15 01 00 00 00 00 02 5b 00 + 15 01 00 00 00 00 02 5c 01 + 15 01 00 00 00 00 02 5d 2b + 15 01 00 00 00 00 02 74 00 + 15 01 00 00 00 00 02 75 ba + 15 01 00 00 00 00 02 81 0a + 15 01 00 00 00 00 02 4e 81 + 15 01 00 00 00 00 02 4f 83 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 53 4d + 15 01 00 00 00 00 02 54 03 + 15 01 00 00 00 00 02 ff e0 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 b2 81 + 15 01 00 00 00 00 02 62 28 + 15 01 00 00 00 00 02 a2 09 + 15 01 00 00 00 00 02 b3 01 + 15 01 00 00 00 00 02 ed 00 + 15 01 00 00 00 00 02 ff 10 + 05 01 00 00 78 00 01 11 + 15 01 00 00 00 00 02 ff 20 + 15 01 00 00 00 00 02 75 00 + 15 01 00 00 00 00 02 76 71 + 15 01 00 00 00 00 02 77 00 + 15 01 00 00 00 00 02 78 84 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 7a a5 + 15 01 00 00 00 00 02 7b 00 + 15 01 00 00 00 00 02 7c bb + 15 01 00 00 00 00 02 7d 00 + 15 01 00 00 00 00 02 7e ce + 15 01 00 00 00 00 02 7f 00 + 15 01 00 00 00 00 02 80 e0 + 15 01 00 00 00 00 02 81 00 + 15 01 00 00 00 00 02 82 ef + 15 01 00 00 00 00 02 83 00 + 15 01 00 00 00 00 02 84 ff + 15 01 00 00 00 00 02 85 01 + 15 01 00 00 00 00 02 86 0b + 15 01 00 00 00 00 02 87 01 + 15 01 00 00 00 00 02 88 38 + 15 01 00 00 00 00 02 89 01 + 15 01 00 00 00 00 02 8a 5b + 15 01 00 00 00 00 02 8b 01 + 15 01 00 00 00 00 02 8c 95 + 15 01 00 00 00 00 02 8d 01 + 15 01 00 00 00 00 02 8e c4 + 15 01 00 00 00 00 02 8f 02 + 15 01 00 00 00 00 02 90 0d + 15 01 00 00 00 00 02 91 02 + 15 01 00 00 00 00 02 92 4a + 15 01 00 00 00 00 02 93 02 + 15 01 00 00 00 00 02 94 4c + 15 01 00 00 00 00 02 95 02 + 15 01 00 00 00 00 02 96 85 + 15 01 00 00 00 00 02 97 02 + 15 01 00 00 00 00 02 98 c3 + 15 01 00 00 00 00 02 99 02 + 15 01 00 00 00 00 02 9a e9 + 15 01 00 00 00 00 02 9b 03 + 15 01 00 00 00 00 02 9c 16 + 15 01 00 00 00 00 02 9d 03 + 15 01 00 00 00 00 02 9e 34 + 15 01 00 00 00 00 02 9f 03 + 15 01 00 00 00 00 02 a0 56 + 15 01 00 00 00 00 02 a2 03 + 15 01 00 00 00 00 02 a3 62 + 15 01 00 00 00 00 02 a4 03 + 15 01 00 00 00 00 02 a5 6c + 15 01 00 00 00 00 02 a6 03 + 15 01 00 00 00 00 02 a7 74 + 15 01 00 00 00 00 02 a9 03 + 15 01 00 00 00 00 02 aa 80 + 15 01 00 00 00 00 02 ab 03 + 15 01 00 00 00 00 02 ac 89 + 15 01 00 00 00 00 02 ad 03 + 15 01 00 00 00 00 02 ae 8b + 15 01 00 00 00 00 02 af 03 + 15 01 00 00 00 00 02 b0 8d + 15 01 00 00 00 00 02 b1 03 + 15 01 00 00 00 00 02 b2 8e + 15 01 00 00 00 00 02 b3 00 + 15 01 00 00 00 00 02 b4 71 + 15 01 00 00 00 00 02 b5 00 + 15 01 00 00 00 00 02 b6 84 + 15 01 00 00 00 00 02 b7 00 + 15 01 00 00 00 00 02 b8 a5 + 15 01 00 00 00 00 02 b9 00 + 15 01 00 00 00 00 02 ba bb + 15 01 00 00 00 00 02 bb 00 + 15 01 00 00 00 00 02 bc ce + 15 01 00 00 00 00 02 bd 00 + 15 01 00 00 00 00 02 be e0 + 15 01 00 00 00 00 02 bf 00 + 15 01 00 00 00 00 02 c0 ef + 15 01 00 00 00 00 02 c1 00 + 15 01 00 00 00 00 02 c2 ff + 15 01 00 00 00 00 02 c3 01 + 15 01 00 00 00 00 02 c4 0b + 15 01 00 00 00 00 02 c5 01 + 15 01 00 00 00 00 02 c6 38 + 15 01 00 00 00 00 02 c7 01 + 15 01 00 00 00 00 02 c8 5b + 15 01 00 00 00 00 02 c9 01 + 15 01 00 00 00 00 02 ca 95 + 15 01 00 00 00 00 02 cb 01 + 15 01 00 00 00 00 02 cc c4 + 15 01 00 00 00 00 02 cd 02 + 15 01 00 00 00 00 02 ce 0d + 15 01 00 00 00 00 02 cf 02 + 15 01 00 00 00 00 02 d0 4a + 15 01 00 00 00 00 02 d1 02 + 15 01 00 00 00 00 02 d2 4c + 15 01 00 00 00 00 02 d3 02 + 15 01 00 00 00 00 02 d4 85 + 15 01 00 00 00 00 02 d5 02 + 15 01 00 00 00 00 02 d6 c3 + 15 01 00 00 00 00 02 d7 02 + 15 01 00 00 00 00 02 d8 e9 + 15 01 00 00 00 00 02 d9 03 + 15 01 00 00 00 00 02 da 16 + 15 01 00 00 00 00 02 db 03 + 15 01 00 00 00 00 02 dc 34 + 15 01 00 00 00 00 02 dd 03 + 15 01 00 00 00 00 02 de 56 + 15 01 00 00 00 00 02 df 03 + 15 01 00 00 00 00 02 e0 62 + 15 01 00 00 00 00 02 e1 03 + 15 01 00 00 00 00 02 e2 6c + 15 01 00 00 00 00 02 e3 03 + 15 01 00 00 00 00 02 e4 74 + 15 01 00 00 00 00 02 e5 03 + 15 01 00 00 00 00 02 e6 80 + 15 01 00 00 00 00 02 e7 03 + 15 01 00 00 00 00 02 e8 89 + 15 01 00 00 00 00 02 e9 03 + 15 01 00 00 00 00 02 ea 8b + 15 01 00 00 00 00 02 eb 03 + 15 01 00 00 00 00 02 ec 8d + 15 01 00 00 00 00 02 ed 03 + 15 01 00 00 00 00 02 ee 8e + 15 01 00 00 00 00 02 ef 00 + 15 01 00 00 00 00 02 f0 71 + 15 01 00 00 00 00 02 f1 00 + 15 01 00 00 00 00 02 f2 84 + 15 01 00 00 00 00 02 f3 00 + 15 01 00 00 00 00 02 f4 a5 + 15 01 00 00 00 00 02 f5 00 + 15 01 00 00 00 00 02 f6 bb + 15 01 00 00 00 00 02 f7 00 + 15 01 00 00 00 00 02 f8 ce + 15 01 00 00 00 00 02 f9 00 + 15 01 00 00 00 00 02 fa e0 + 15 01 00 00 00 00 02 ff 21 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 00 + 15 01 00 00 00 00 02 01 ef + 15 01 00 00 00 00 02 02 00 + 15 01 00 00 00 00 02 03 ff + 15 01 00 00 00 00 02 04 01 + 15 01 00 00 00 00 02 05 0b + 15 01 00 00 00 00 02 06 01 + 15 01 00 00 00 00 02 07 38 + 15 01 00 00 00 00 02 08 01 + 15 01 00 00 00 00 02 09 5b + 15 01 00 00 00 00 02 0a 01 + 15 01 00 00 00 00 02 0b 95 + 15 01 00 00 00 00 02 0c 01 + 15 01 00 00 00 00 02 0d c4 + 15 01 00 00 00 00 02 0e 02 + 15 01 00 00 00 00 02 0f 0d + 15 01 00 00 00 00 02 10 02 + 15 01 00 00 00 00 02 11 4a + 15 01 00 00 00 00 02 12 02 + 15 01 00 00 00 00 02 13 4c + 15 01 00 00 00 00 02 14 02 + 15 01 00 00 00 00 02 15 85 + 15 01 00 00 00 00 02 16 02 + 15 01 00 00 00 00 02 17 c3 + 15 01 00 00 00 00 02 18 02 + 15 01 00 00 00 00 02 19 e9 + 15 01 00 00 00 00 02 1a 03 + 15 01 00 00 00 00 02 1b 16 + 15 01 00 00 00 00 02 1c 03 + 15 01 00 00 00 00 02 1d 34 + 15 01 00 00 00 00 02 1e 03 + 15 01 00 00 00 00 02 1f 56 + 15 01 00 00 00 00 02 20 03 + 15 01 00 00 00 00 02 21 62 + 15 01 00 00 00 00 02 22 03 + 15 01 00 00 00 00 02 23 6c + 15 01 00 00 00 00 02 24 03 + 15 01 00 00 00 00 02 25 74 + 15 01 00 00 00 00 02 26 03 + 15 01 00 00 00 00 02 27 80 + 15 01 00 00 00 00 02 28 03 + 15 01 00 00 00 00 02 29 89 + 15 01 00 00 00 00 02 2a 03 + 15 01 00 00 00 00 02 2b 8b + 15 01 00 00 00 00 02 2d 03 + 15 01 00 00 00 00 02 2f 8d + 15 01 00 00 00 00 02 30 03 + 15 01 00 00 00 00 02 31 8e + 15 01 00 00 00 00 02 32 00 + 15 01 00 00 00 00 02 33 71 + 15 01 00 00 00 00 02 34 00 + 15 01 00 00 00 00 02 35 84 + 15 01 00 00 00 00 02 36 00 + 15 01 00 00 00 00 02 37 a5 + 15 01 00 00 00 00 02 38 00 + 15 01 00 00 00 00 02 39 bb + 15 01 00 00 00 00 02 3a 00 + 15 01 00 00 00 00 02 3b ce + 15 01 00 00 00 00 02 3d 00 + 15 01 00 00 00 00 02 3f e0 + 15 01 00 00 00 00 02 40 00 + 15 01 00 00 00 00 02 41 ef + 15 01 00 00 00 00 02 42 00 + 15 01 00 00 00 00 02 43 ff + 15 01 00 00 00 00 02 44 01 + 15 01 00 00 00 00 02 45 0b + 15 01 00 00 00 00 02 46 01 + 15 01 00 00 00 00 02 47 38 + 15 01 00 00 00 00 02 48 01 + 15 01 00 00 00 00 02 49 5b + 15 01 00 00 00 00 02 4a 01 + 15 01 00 00 00 00 02 4b 95 + 15 01 00 00 00 00 02 4c 01 + 15 01 00 00 00 00 02 4d c4 + 15 01 00 00 00 00 02 4e 02 + 15 01 00 00 00 00 02 4f 0d + 15 01 00 00 00 00 02 50 02 + 15 01 00 00 00 00 02 51 4a + 15 01 00 00 00 00 02 52 02 + 15 01 00 00 00 00 02 53 4c + 15 01 00 00 00 00 02 54 02 + 15 01 00 00 00 00 02 55 85 + 15 01 00 00 00 00 02 56 02 + 15 01 00 00 00 00 02 58 c3 + 15 01 00 00 00 00 02 59 02 + 15 01 00 00 00 00 02 5a e9 + 15 01 00 00 00 00 02 5b 03 + 15 01 00 00 00 00 02 5c 16 + 15 01 00 00 00 00 02 5d 03 + 15 01 00 00 00 00 02 5e 34 + 15 01 00 00 00 00 02 5f 03 + 15 01 00 00 00 00 02 60 56 + 15 01 00 00 00 00 02 61 03 + 15 01 00 00 00 00 02 62 62 + 15 01 00 00 00 00 02 63 03 + 15 01 00 00 00 00 02 64 6c + 15 01 00 00 00 00 02 65 03 + 15 01 00 00 00 00 02 66 74 + 15 01 00 00 00 00 02 67 03 + 15 01 00 00 00 00 02 68 80 + 15 01 00 00 00 00 02 69 03 + 15 01 00 00 00 00 02 6a 89 + 15 01 00 00 00 00 02 6b 03 + 15 01 00 00 00 00 02 6c 8b + 15 01 00 00 00 00 02 6d 03 + 15 01 00 00 00 00 02 6e 8d + 15 01 00 00 00 00 02 6f 03 + 15 01 00 00 00 00 02 70 8e + 15 01 00 00 00 00 02 71 00 + 15 01 00 00 00 00 02 72 71 + 15 01 00 00 00 00 02 73 00 + 15 01 00 00 00 00 02 74 84 + 15 01 00 00 00 00 02 75 00 + 15 01 00 00 00 00 02 76 a5 + 15 01 00 00 00 00 02 77 00 + 15 01 00 00 00 00 02 78 bb + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 7a ce + 15 01 00 00 00 00 02 7b 00 + 15 01 00 00 00 00 02 7c e0 + 15 01 00 00 00 00 02 7d 00 + 15 01 00 00 00 00 02 7e ef + 15 01 00 00 00 00 02 7f 00 + 15 01 00 00 00 00 02 80 ff + 15 01 00 00 00 00 02 81 01 + 15 01 00 00 00 00 02 82 0b + 15 01 00 00 00 00 02 83 01 + 15 01 00 00 00 00 02 84 38 + 15 01 00 00 00 00 02 85 01 + 15 01 00 00 00 00 02 86 5b + 15 01 00 00 00 00 02 87 01 + 15 01 00 00 00 00 02 88 95 + 15 01 00 00 00 00 02 89 01 + 15 01 00 00 00 00 02 8a c4 + 15 01 00 00 00 00 02 8b 02 + 15 01 00 00 00 00 02 8c 0d + 15 01 00 00 00 00 02 8d 02 + 15 01 00 00 00 00 02 8e 4a + 15 01 00 00 00 00 02 8f 02 + 15 01 00 00 00 00 02 90 4c + 15 01 00 00 00 00 02 91 02 + 15 01 00 00 00 00 02 92 85 + 15 01 00 00 00 00 02 93 02 + 15 01 00 00 00 00 02 94 c3 + 15 01 00 00 00 00 02 95 02 + 15 01 00 00 00 00 02 96 e9 + 15 01 00 00 00 00 02 97 03 + 15 01 00 00 00 00 02 98 16 + 15 01 00 00 00 00 02 99 03 + 15 01 00 00 00 00 02 9a 34 + 15 01 00 00 00 00 02 9b 03 + 15 01 00 00 00 00 02 9c 56 + 15 01 00 00 00 00 02 9d 03 + 15 01 00 00 00 00 02 9e 62 + 15 01 00 00 00 00 02 9f 03 + 15 01 00 00 00 00 02 a0 6c + 15 01 00 00 00 00 02 a2 03 + 15 01 00 00 00 00 02 a3 74 + 15 01 00 00 00 00 02 a4 03 + 15 01 00 00 00 00 02 a5 80 + 15 01 00 00 00 00 02 a6 03 + 15 01 00 00 00 00 02 a7 89 + 15 01 00 00 00 00 02 a9 03 + 15 01 00 00 00 00 02 aa 8b + 15 01 00 00 00 00 02 ab 03 + 15 01 00 00 00 00 02 ac 8d + 15 01 00 00 00 00 02 ad 03 + 15 01 00 00 00 00 02 ae 8e + 15 01 00 00 00 00 02 af 00 + 15 01 00 00 00 00 02 b0 71 + 15 01 00 00 00 00 02 b1 00 + 15 01 00 00 00 00 02 b2 84 + 15 01 00 00 00 00 02 b3 00 + 15 01 00 00 00 00 02 b4 a5 + 15 01 00 00 00 00 02 b5 00 + 15 01 00 00 00 00 02 b6 bb + 15 01 00 00 00 00 02 b7 00 + 15 01 00 00 00 00 02 b8 ce + 15 01 00 00 00 00 02 b9 00 + 15 01 00 00 00 00 02 ba e0 + 15 01 00 00 00 00 02 bb 00 + 15 01 00 00 00 00 02 bc ef + 15 01 00 00 00 00 02 bd 00 + 15 01 00 00 00 00 02 be ff + 15 01 00 00 00 00 02 bf 01 + 15 01 00 00 00 00 02 c0 0b + 15 01 00 00 00 00 02 c1 01 + 15 01 00 00 00 00 02 c2 38 + 15 01 00 00 00 00 02 c3 01 + 15 01 00 00 00 00 02 c4 5b + 15 01 00 00 00 00 02 c5 01 + 15 01 00 00 00 00 02 c6 95 + 15 01 00 00 00 00 02 c7 01 + 15 01 00 00 00 00 02 c8 c4 + 15 01 00 00 00 00 02 c9 02 + 15 01 00 00 00 00 02 ca 0d + 15 01 00 00 00 00 02 cb 02 + 15 01 00 00 00 00 02 cc 4a + 15 01 00 00 00 00 02 cd 02 + 15 01 00 00 00 00 02 ce 4c + 15 01 00 00 00 00 02 cf 02 + 15 01 00 00 00 00 02 d0 85 + 15 01 00 00 00 00 02 d1 02 + 15 01 00 00 00 00 02 d2 c3 + 15 01 00 00 00 00 02 d3 02 + 15 01 00 00 00 00 02 d4 e9 + 15 01 00 00 00 00 02 d5 03 + 15 01 00 00 00 00 02 d6 16 + 15 01 00 00 00 00 02 d7 03 + 15 01 00 00 00 00 02 d8 34 + 15 01 00 00 00 00 02 d9 03 + 15 01 00 00 00 00 02 da 56 + 15 01 00 00 00 00 02 db 03 + 15 01 00 00 00 00 02 dc 62 + 15 01 00 00 00 00 02 dd 03 + 15 01 00 00 00 00 02 de 6c + 15 01 00 00 00 00 02 df 03 + 15 01 00 00 00 00 02 e0 74 + 15 01 00 00 00 00 02 e1 03 + 15 01 00 00 00 00 02 e2 80 + 15 01 00 00 00 00 02 e3 03 + 15 01 00 00 00 00 02 e4 89 + 15 01 00 00 00 00 02 e5 03 + 15 01 00 00 00 00 02 e6 8b + 15 01 00 00 00 00 02 e7 03 + 15 01 00 00 00 00 02 e8 8d + 15 01 00 00 00 00 02 e9 03 + 15 01 00 00 00 00 02 ea 8e + 15 01 00 00 00 00 02 FF 10 + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = + [15 01 00 00 00 00 02 ff 10 + 05 01 00 00 10 00 01 28 + 15 01 00 00 00 00 02 b0 00 + 05 01 00 00 40 00 01 10 + 15 01 00 00 00 00 02 4f 01]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi new file mode 100644 index 000000000000..fc18d060c9bc --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sim_cmd: qcom,mdss_dsi_sim_cmd{ + qcom,mdss-dsi-panel-name = "Simulator cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + 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-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-t-clk-post = <0x03>; + qcom,mdss-dsi-t-clk-pre = <0x27>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-wd; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,ulps-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1440>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <100>; + qcom,mdss-dsi-v-pulse-width = <40>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 21 09 09 24 23 08 08 08 03 04 00]; + qcom,mdss-dsi-on-command = + [29 01 00 00 00 00 02 b0 03 + 05 01 00 00 0a 00 01 00 + /* Soft reset, wait 10ms */ + 15 01 00 00 0a 00 02 3a 77 + /* Set Pixel format (24 bpp) */ + 39 01 00 00 0a 00 05 2a 00 00 04 ff + /* Set Column address */ + 39 01 00 00 0a 00 05 2b 00 00 05 9f + /* Set page address */ + 15 01 00 00 0a 00 02 35 00 + /* Set tear on */ + 39 01 00 00 0a 00 03 44 00 00 + /* Set tear scan line */ + 15 01 00 00 0a 00 02 51 ff + /* write display brightness */ + 15 01 00 00 0a 00 02 53 24 + /* write control brightness */ + 15 01 00 00 0a 00 02 55 00 + /* CABC brightness */ + 05 01 00 00 78 00 01 11 + /* exit sleep mode, wait 120ms */ + 05 01 00 00 10 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + timing@1{ + 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 = <460>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <740>; + qcom,mdss-dsi-v-pulse-width = <40>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 21 09 09 24 23 08 08 08 03 04 00]; + qcom,mdss-dsi-on-command = + [29 01 00 00 00 00 02 b0 03 + 05 01 00 00 0a 00 01 00 + /* Soft reset, wait 10ms */ + 15 01 00 00 0a 00 02 3a 77 + /* Set Pixel format (24 bpp) */ + 39 01 00 00 0a 00 05 2a 00 00 04 ff + /* Set Column address */ + 39 01 00 00 0a 00 05 2b 00 00 05 9f + /* Set page address */ + 15 01 00 00 0a 00 02 35 00 + /* Set tear on */ + 39 01 00 00 0a 00 03 44 00 00 + /* Set tear scan line */ + 15 01 00 00 0a 00 02 51 ff + /* write display brightness */ + 15 01 00 00 0a 00 02 53 24 + /* write control brightness */ + 15 01 00 00 0a 00 02 55 00 + /* CABC brightness */ + 05 01 00 00 78 00 01 11 + /* exit sleep mode, wait 120ms */ + 05 01 00 00 10 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <540>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + timing@2{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <840>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <1380>; + qcom,mdss-dsi-v-pulse-width = <40>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 21 09 09 24 23 08 08 08 03 04 00]; + qcom,mdss-dsi-on-command = + [29 01 00 00 00 00 02 b0 03 + 05 01 00 00 0a 00 01 00 + /* Soft reset, wait 10ms */ + 15 01 00 00 0a 00 02 3a 77 + /* Set Pixel format (24 bpp) */ + 39 01 00 00 0a 00 05 2a 00 00 04 ff + /* Set Column address */ + 39 01 00 00 0a 00 05 2b 00 00 05 9f + /* Set page address */ + 15 01 00 00 0a 00 02 35 00 + /* Set tear on */ + 39 01 00 00 0a 00 03 44 00 00 + /* Set tear scan line */ + 15 01 00 00 0a 00 02 51 ff + /* write display brightness */ + 15 01 00 00 0a 00 02 53 24 + /* write control brightness */ + 15 01 00 00 0a 00 02 55 00 + /* CABC brightness */ + 05 01 00 00 78 00 01 11 + /* exit sleep mode, wait 120ms */ + 05 01 00 00 10 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <360>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dsc375-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dsc375-cmd.dtsi new file mode 100644 index 000000000000..bea236f819e9 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dsc375-cmd.dtsi @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sim_dsc_375_cmd: qcom,mdss_dsi_sim_dsc_375_cmd { + qcom,mdss-dsi-panel-name = + "Simulator cmd mode DSC 3.75:1 dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + 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-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-wd; + qcom,mdss-dsi-te-using-te-pin; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <1440>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-front-porch = <10>; + 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-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 ff 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1e + 15 01 00 00 00 00 02 0b 73 + 15 01 00 00 00 00 02 0c 73 + 15 01 00 00 00 00 02 0e b0 + 15 01 00 00 00 00 02 0f aE + 15 01 00 00 00 00 02 11 b8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5a 00 + 15 01 00 00 00 00 02 5b 01 + 15 01 00 00 00 00 02 5c 80 + 15 01 00 00 00 00 02 5d 81 + 15 01 00 00 00 00 02 5e 00 + 15 01 00 00 00 00 02 5f 01 + 15 01 00 00 00 00 02 72 31 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 1c + 15 01 00 00 00 00 02 01 0b + 15 01 00 00 00 00 02 02 0c + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0f + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8a + 15 01 00 00 00 00 02 0a 13 + 15 01 00 00 00 00 02 0b 13 + 15 01 00 00 00 00 02 0c 15 + 15 01 00 00 00 00 02 0d 15 + 15 01 00 00 00 00 02 0e 17 + 15 01 00 00 00 00 02 0f 17 + 15 01 00 00 00 00 02 10 1c + 15 01 00 00 00 00 02 11 0b + 15 01 00 00 00 00 02 12 0c + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0f + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8a + 15 01 00 00 00 00 02 1a 13 + 15 01 00 00 00 00 02 1b 13 + 15 01 00 00 00 00 02 1c 15 + 15 01 00 00 00 00 02 1d 15 + 15 01 00 00 00 00 02 1e 17 + 15 01 00 00 00 00 02 1f 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6d + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 e0 00 + 15 01 00 00 00 00 02 dc 21 + 15 01 00 00 00 00 02 dd 22 + 15 01 00 00 00 00 02 de 07 + 15 01 00 00 00 00 02 df 07 + 15 01 00 00 00 00 02 e3 6d + 15 01 00 00 00 00 02 e1 07 + 15 01 00 00 00 00 02 e2 07 + /* UD */ + 15 01 00 00 00 00 02 29 d8 + 15 01 00 00 00 00 02 2a 2a + /* CLK */ + 15 01 00 00 00 00 02 4b 03 + 15 01 00 00 00 00 02 4c 11 + 15 01 00 00 00 00 02 4d 10 + 15 01 00 00 00 00 02 4e 01 + 15 01 00 00 00 00 02 4f 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5b 43 + 15 01 00 00 00 00 02 5c 00 + 15 01 00 00 00 00 02 5f 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7a 80 + 15 01 00 00 00 00 02 7b 91 + 15 01 00 00 00 00 02 7c d8 + 15 01 00 00 00 00 02 7d 60 + 15 01 00 00 00 00 02 7f 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 b3 c0 + 15 01 00 00 00 00 02 b4 00 + 15 01 00 00 00 00 02 b5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0a + 15 01 00 00 00 00 02 94 0a + /* Inversion Type */ + 15 01 00 00 00 00 02 8a 00 + 15 01 00 00 00 00 02 9b ff + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9d b0 + 15 01 00 00 00 00 02 9f 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 ec 00 + /* CMD1 */ + 15 01 00 00 00 00 02 ff 10 + /* VESA DSC PPS settings + * (1440x2560 slide 16H) + */ + 39 01 00 00 00 00 11 c1 09 + 20 00 10 02 00 02 68 01 bb + 00 0a 06 67 04 c5 + + 39 01 00 00 00 00 03 c2 10 f0 + /* C0h = 0x0(2 Port SDC) + * 0x01(1 PortA FBC) + * 0x02(MTK) 0x03(1 PortA VESA) + */ + 15 01 00 00 00 00 02 c0 03 + /* VBP+VSA=,VFP = 10H */ + 15 01 00 00 00 00 04 3b 03 0a 0a + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 e5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 bb 10 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 fb 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 00 02 10 00]; + + qcom,mdss-dsi-on-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <2>; + qcom,mdss-dsc-bit-per-component = <10>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + timing@1 { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <0>; + qcom,mdss-dsi-h-back-porch = <0>; + qcom,mdss-dsi-h-pulse-width = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <0>; + qcom,mdss-dsi-v-front-porch = <0>; + qcom,mdss-dsi-v-pulse-width = <0>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 bb 10 + 15 01 00 00 00 00 02 b0 03 + 05 01 00 00 78 00 01 11 + 15 01 00 00 00 00 02 51 ff + 15 01 00 00 00 00 02 53 24 + 15 01 00 00 00 00 02 ff 23 + 15 01 00 00 00 00 02 08 05 + 15 01 00 00 00 00 02 46 90 + 15 01 00 00 00 00 02 ff 10 + 15 01 00 00 00 00 02 ff f0 + 15 01 00 00 00 00 02 92 01 + 15 01 00 00 00 00 02 ff 10 + /* enable TE generation */ + 15 01 00 00 00 00 02 35 00 + 05 01 00 00 28 00 01 29]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 10 00 01 28 + 05 01 00 00 40 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <540>; + qcom,mdss-dsc-slice-per-pkt = <2>; + qcom,mdss-dsc-bit-per-component = <10>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi new file mode 100644 index 000000000000..b47609f5a614 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_sim_cmd: qcom,mdss_dsi_dual_sim_cmd { + qcom,mdss-dsi-panel-name = "Sim dual cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,cmd-sync-wait-broadcast; + 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-hor-line-idle = <0 40 256>, + <40 120 128>, + <120 240 64>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-wd; + qcom,mdss-dsi-te-using-te-pin; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <28>; + qcom,mdss-dsi-h-back-porch = <4>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <12>; + 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-panel-framerate = <120>; + qcom,mdss-dsi-on-command = + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + timing@1{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-on-command = + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + timing@2{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-front-porch = <30>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <40>; + qcom,mdss-dsi-on-command = + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-dsc375-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-dsc375-cmd.dtsi new file mode 100644 index 000000000000..96ee5e9fc30a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-dsc375-cmd.dtsi @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_sim_dsc_375_cmd: qcom,mdss_dsi_dual_sim_dsc_375_cmd { + qcom,mdss-dsi-panel-name = + "Sim dual cmd mode DSC 3.75:1 dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,cmd-sync-wait-broadcast; + 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-hor-line-idle = <0 40 256>, + <40 120 128>, + <120 240 64>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-wd; + qcom,mdss-dsi-te-using-te-pin; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-front-porch = <30>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 11 91 09 20 00 20 02 + 00 03 1c 04 21 00 + 0f 03 19 01 97 + 39 01 00 00 00 00 03 92 10 f0 + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 04 + 15 01 00 00 00 00 02 c0 03 + 39 01 00 00 00 00 06 f0 55 aa 52 08 07 + 15 01 00 00 00 00 02 ef 01 + 39 01 00 00 00 00 06 f0 55 aa 52 08 00 + 15 01 00 00 00 00 02 b4 01 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 06 f0 55 aa 52 08 01 + 39 01 00 00 00 00 05 ff aa 55 a5 80 + 15 01 00 00 00 00 02 6f 01 + 15 01 00 00 00 00 02 f3 10 + 39 01 00 00 00 00 05 ff aa 55 a5 00 + /* sleep out + delay 120ms */ + 05 01 00 00 78 00 01 11 + /* display on + delay 120ms */ + 05 01 00 00 78 00 01 29 + ]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 78 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <32>; + qcom,mdss-dsc-slice-width = <1080>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <10>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + timing@1 { + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <32>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + 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-h-sync-pulse = <0>; + qcom,mdss-dsi-on-command = [ + /* CMD2_P0 */ + 15 01 00 00 00 00 02 FF 20 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 01 + 15 01 00 00 00 00 02 01 55 + 15 01 00 00 00 00 02 02 45 + 15 01 00 00 00 00 02 05 40 + 15 01 00 00 00 00 02 06 19 + 15 01 00 00 00 00 02 07 1E + 15 01 00 00 00 00 02 0B 73 + 15 01 00 00 00 00 02 0C 73 + 15 01 00 00 00 00 02 0E B0 + 15 01 00 00 00 00 02 0F AE + 15 01 00 00 00 00 02 11 B8 + 15 01 00 00 00 00 02 13 00 + 15 01 00 00 00 00 02 58 80 + 15 01 00 00 00 00 02 59 01 + 15 01 00 00 00 00 02 5A 00 + 15 01 00 00 00 00 02 5B 01 + 15 01 00 00 00 00 02 5C 80 + 15 01 00 00 00 00 02 5D 81 + 15 01 00 00 00 00 02 5E 00 + 15 01 00 00 00 00 02 5F 01 + 15 01 00 00 00 00 02 72 31 + 15 01 00 00 00 00 02 68 03 + /* CMD2_P4 */ + 15 01 00 00 00 00 02 ff 24 + 15 01 00 00 00 00 02 fb 01 + 15 01 00 00 00 00 02 00 1C + 15 01 00 00 00 00 02 01 0B + 15 01 00 00 00 00 02 02 0C + 15 01 00 00 00 00 02 03 01 + 15 01 00 00 00 00 02 04 0F + 15 01 00 00 00 00 02 05 10 + 15 01 00 00 00 00 02 06 10 + 15 01 00 00 00 00 02 07 10 + 15 01 00 00 00 00 02 08 89 + 15 01 00 00 00 00 02 09 8A + 15 01 00 00 00 00 02 0A 13 + 15 01 00 00 00 00 02 0B 13 + 15 01 00 00 00 00 02 0C 15 + 15 01 00 00 00 00 02 0D 15 + 15 01 00 00 00 00 02 0E 17 + 15 01 00 00 00 00 02 0F 17 + 15 01 00 00 00 00 02 10 1C + 15 01 00 00 00 00 02 11 0B + 15 01 00 00 00 00 02 12 0C + 15 01 00 00 00 00 02 13 01 + 15 01 00 00 00 00 02 14 0F + 15 01 00 00 00 00 02 15 10 + 15 01 00 00 00 00 02 16 10 + 15 01 00 00 00 00 02 17 10 + 15 01 00 00 00 00 02 18 89 + 15 01 00 00 00 00 02 19 8A + 15 01 00 00 00 00 02 1A 13 + 15 01 00 00 00 00 02 1B 13 + 15 01 00 00 00 00 02 1C 15 + 15 01 00 00 00 00 02 1D 15 + 15 01 00 00 00 00 02 1E 17 + 15 01 00 00 00 00 02 1F 17 + /* STV */ + 15 01 00 00 00 00 02 20 40 + 15 01 00 00 00 00 02 21 01 + 15 01 00 00 00 00 02 22 00 + 15 01 00 00 00 00 02 23 40 + 15 01 00 00 00 00 02 24 40 + 15 01 00 00 00 00 02 25 6D + 15 01 00 00 00 00 02 26 40 + 15 01 00 00 00 00 02 27 40 + /* Vend */ + 15 01 00 00 00 00 02 E0 00 + 15 01 00 00 00 00 02 DC 21 + 15 01 00 00 00 00 02 DD 22 + 15 01 00 00 00 00 02 DE 07 + 15 01 00 00 00 00 02 DF 07 + 15 01 00 00 00 00 02 E3 6D + 15 01 00 00 00 00 02 E1 07 + 15 01 00 00 00 00 02 E2 07 + /* UD */ + 15 01 00 00 00 00 02 29 D8 + 15 01 00 00 00 00 02 2A 2A + /* CLK */ + 15 01 00 00 00 00 02 4B 03 + 15 01 00 00 00 00 02 4C 11 + 15 01 00 00 00 00 02 4D 10 + 15 01 00 00 00 00 02 4E 01 + 15 01 00 00 00 00 02 4F 01 + 15 01 00 00 00 00 02 50 10 + 15 01 00 00 00 00 02 51 00 + 15 01 00 00 00 00 02 52 80 + 15 01 00 00 00 00 02 53 00 + 15 01 00 00 00 00 02 56 00 + 15 01 00 00 00 00 02 54 07 + 15 01 00 00 00 00 02 58 07 + 15 01 00 00 00 00 02 55 25 + /* Reset XDONB */ + 15 01 00 00 00 00 02 5B 43 + 15 01 00 00 00 00 02 5C 00 + 15 01 00 00 00 00 02 5F 73 + 15 01 00 00 00 00 02 60 73 + 15 01 00 00 00 00 02 63 22 + 15 01 00 00 00 00 02 64 00 + 15 01 00 00 00 00 02 67 08 + 15 01 00 00 00 00 02 68 04 + /* Resolution:1440x2560*/ + 15 01 00 00 00 00 02 72 02 + /* mux */ + 15 01 00 00 00 00 02 7A 80 + 15 01 00 00 00 00 02 7B 91 + 15 01 00 00 00 00 02 7C D8 + 15 01 00 00 00 00 02 7D 60 + 15 01 00 00 00 00 02 7F 15 + 15 01 00 00 00 00 02 75 15 + /* ABOFF */ + 15 01 00 00 00 00 02 B3 C0 + 15 01 00 00 00 00 02 B4 00 + 15 01 00 00 00 00 02 B5 00 + /* Source EQ */ + 15 01 00 00 00 00 02 78 00 + 15 01 00 00 00 00 02 79 00 + 15 01 00 00 00 00 02 80 00 + 15 01 00 00 00 00 02 83 00 + /* FP BP */ + 15 01 00 00 00 00 02 93 0A + 15 01 00 00 00 00 02 94 0A + /* Inversion Type */ + 15 01 00 00 00 00 02 8A 00 + 15 01 00 00 00 00 02 9B FF + /* IMGSWAP =1 @PortSwap=1 */ + 15 01 00 00 00 00 02 9D B0 + 15 01 00 00 00 00 02 9F 63 + 15 01 00 00 00 00 02 98 10 + /* FRM */ + 15 01 00 00 00 00 02 EC 00 + /* CMD1 */ + 15 01 00 00 00 00 02 ff 10 + /* VBP+VSA=,VFP = 10H */ + 15 01 00 00 00 00 04 3B 03 0A 0A + /* FTE on */ + 15 01 00 00 00 00 02 35 00 + /* EN_BK =1(auto black) */ + 15 01 00 00 00 00 02 E5 01 + /* CMD mode(10) VDO mode(03) */ + 15 01 00 00 00 00 02 BB 10 + /* Non Reload MTP */ + 15 01 00 00 00 00 02 FB 01 + /* SlpOut + DispOn */ + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 78 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 78 00 + 02 28 00 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <10>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-video.dtsi new file mode 100644 index 000000000000..b0ef38fd6dad --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-video.dtsi @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_sim_vid: qcom,mdss_dsi_dual_sim_video { + qcom,mdss-dsi-panel-name = "Sim dual video mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-panel-broadcast-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-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 20>, <0 200>, <1 20>; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <1280>; + qcom,mdss-dsi-panel-height = <1440>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <44>; + 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 = <8>; + qcom,mdss-dsi-v-pulse-width = <4>; + 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-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi new file mode 100644 index 000000000000..bacbb97f7f3c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_sim_vid: qcom,mdss_dsi_sim_video { + qcom,mdss-dsi-panel-name = "Simulator video mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + 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-t-clk-post = <0x04>; + qcom,mdss-dsi-t-clk-pre = <0x1b>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-reset-sequence = <1 0>, <0 0>, <1 0>; + qcom,panel-ack-disabled; + + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-width = <640>; + qcom,mdss-dsi-panel-height = <480>; + qcom,mdss-dsi-h-front-porch = <8>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <6>; + 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-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 00 00 00 00 00 00 00 00 00 00 00]; + qcom,mdss-dsi-on-command = + [32 01 00 00 00 00 02 00 00]; + qcom,mdss-dsi-off-command = + [22 01 00 00 00 00 02 00 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-test-dualmipi-oled-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-test-dualmipi-oled-cmd.dtsi new file mode 100644 index 000000000000..ccca03c4aba2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-test-dualmipi-oled-cmd.dtsi @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +&mdss_mdp { + dsi_dual_test_cmd: qcom,mdss_dsi_test_oled_cmd { + qcom,mdss-dsi-panel-name = + "Dual test cmd mode DSI amoled non-DSC panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <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-traffic-mode = "non_burst_sync_event"; + 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,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-reset-sequence = <1 2>, <0 2>, <1 2>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-hfp-power-mode; + qcom,mdss-dsi-hbp-power-mode; + qcom,mdss-dsi-hsa-power-mode; + qcom,mdss-dsi-display-timings { + timing@0{ + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt-backup.dtsi b/arch/arm64/boot/dts/qcom/enchilada-dvt-backup.dtsi new file mode 100644 index 000000000000..56cfdde49805 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt-backup.dtsi @@ -0,0 +1,23 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +#include "enchilada-dvt.dtsi" +&vendor { + +}; + +&soc { +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-panel-mismatch-check; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt-usb30.dtsi b/arch/arm64/boot/dts/qcom/enchilada-dvt-usb30.dtsi new file mode 100644 index 000000000000..fc5f041454fc --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt-usb30.dtsi @@ -0,0 +1,62 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +#include "enchilada-dvt.dtsi" +&vendor { + +}; + +&soc { +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-panel-mismatch-check; +}; + +&sde_dp{ + status = "okay"; +}; + +&sde_dp { + qcom,aux-en-gpio = <&tlmm 122 0>; + qcom,aux-sel-gpio = <&tlmm 123 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; +}; + +&mdss_mdp { + connectors = <&sde_rscc &sde_wb &sde_dp &sde_dsi>; +}; + +&sde_dp_aux_active { + mux { + pins = "gpio122", "gpio123"; + function = "gpio"; + }; + + config { + pins = "gpio122", "gpio123"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; +}; + +&usb0 { + dwc3@a600000 { + op,enable_super_speed; + }; +}; + +&usb1 { + dwc3@a800000 { + op,enable_super_speed; + }; +}; \ No newline at end of file diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-backup-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-backup-overlay.dts new file mode 100644 index 000000000000..61dc5a429443 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-backup-overlay.dts @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-dvt-backup.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP DVT-BACKUP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 25>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-overlay.dts new file mode 100644 index 000000000000..9ace8d33934d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-overlay.dts @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-dvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP DVT"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 21>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-usb30-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-usb30-overlay.dts new file mode 100644 index 000000000000..d65ff283c422 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt-v2.1-usb30-overlay.dts @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-dvt-usb30.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP DVTUSB30"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 55>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-dvt.dtsi b/arch/arm64/boot/dts/qcom/enchilada-dvt.dtsi new file mode 100644 index 000000000000..282d9020ccf8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-dvt.dtsi @@ -0,0 +1,23 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +#include "enchilada-evt2.dtsi" +&vendor { + +}; + +&soc { +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-panel-mismatch-check; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-evt2.dtsi b/arch/arm64/boot/dts/qcom/enchilada-evt2.dtsi new file mode 100644 index 000000000000..d024de261349 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-evt2.dtsi @@ -0,0 +1,144 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +&vendor { + +}; + +&soc { + +}; + + +&tlmm { + oem_rf_cable_mux { + oem_rf_cable_active: oem_rf_cable_active { + mux { + pins = "gpio32","gpio86"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio86"; + drive-strength = <2>; + bias-disable; + }; + }; + oem_rf_cable_suspend: oem_rf_cable_suspend { + mux { + pins = "gpio32","gpio86"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio86"; + drive-strength = <2>; + bias-disable; + }; + }; + }; +}; + +&soc { + oem_rf_cable { + compatible = "oem,rf_cable"; + interrupt-parent = <&tlmm>; + rf,cable-gpio-0 = <&tlmm 32 0>; + rf,cable-gpio-1 = <&tlmm 86 0>; + pinctrl-names = "oem_rf_cable_active", "oem_rf_cable_suspend"; + pinctrl-0 = <&oem_rf_cable_active >; + pinctrl-1 = <&oem_rf_cable_suspend >; + }; +}; + + +&qupv3_se3_i2c { + status = "ok"; + nq@28 { + qcom,nq-firm = <&tlmm 62 0x00>; + }; +}; + +&tlmm { + cam_sensor_rear_0_vaf_active: cam_sensor_rear_0_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_vaf_suspend: cam_sensor_rear_0_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; +}; + +&soc{ + rear_0_actuator_regulator: gpio-regulator@0 { + compatible = "regulator-fixed"; + reg = <0x00 0x00>; + regulator-name = "actuator_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <100>; + enable-active-high; + gpio = <&tlmm 31 0>; + vin-supply = <&pmi8998_bob>; + }; +}; + +&actuator_rear_0{ + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + cci-master = <0>; + cam_vaf-supply = <&rear_0_actuator_regulator>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; +}; + +&ois_rear_0{ + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + cam_vaf-supply = <&pm8998_l21>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2850000>; + rgltr-max-voltage = <2850000>; + rgltr-load-current = <500000>; + gpio-no-mux = <0>; + gpios = <&tlmm 40 0>; + gpio-vaf = <0>; + gpio-req-tbl-num = <0>; + gpio-req-tbl-flags = <0>; + gpio-req-tbl-label = "CAM_OIS_PWD_0"; + cci-master = <0>; + status = "ok"; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-mp-v2.1-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-mp-v2.1-overlay.dts new file mode 100644 index 000000000000..c2226f1068de --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-mp-v2.1-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-mp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP MP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 23>; + oem,project-id = <17819>; + oem,hw-id = <23>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-mp.dtsi b/arch/arm64/boot/dts/qcom/enchilada-mp.dtsi new file mode 100644 index 000000000000..936081d968c6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-mp.dtsi @@ -0,0 +1,23 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +&vendor { + +}; + +&soc { + +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-panel-mismatch-check; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-pvt-backup.dtsi b/arch/arm64/boot/dts/qcom/enchilada-pvt-backup.dtsi new file mode 100644 index 000000000000..b8c1573eb5f0 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-pvt-backup.dtsi @@ -0,0 +1,55 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +#include "enchilada-pvt.dtsi" +&vendor { + +}; + +&soc { + +}; + +&qupv3_se10_i2c { + oneplus_fastchg@26{ + status = "disable"; + }; + + oneplus_fastchg@52{ + status = "ok"; + compatible = "microchip,oneplus_fastchg"; + reg = <0x52>; + microchip,mcu-en-gpio = <&tlmm 102 0x00>; + microchip,usb-sw-1-gpio = <&tlmm 37 0x00>; + microchip,usb-sw-2-gpio = <&tlmm 51 0x00>; + microchip,ap-clk = <&tlmm 43 0x00>; + microchip,ap-data = <&tlmm 44 0x00>; + op,fw-erase-count = <959>; + op,fw-addr-low = <0>; + op,fw-addr-high = <0>; + op,n76e_support; + + 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>; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-backup-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-backup-overlay.dts new file mode 100644 index 000000000000..817872eec9cb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-backup-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-pvt-backup.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT-MCU-BACK"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 24>; + oem,project-id = <17819>; + oem,hw-id = <24>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-overlay.dts b/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-overlay.dts new file mode 100644 index 000000000000..cb24ffded601 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-pvt-v2.1-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-pvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 17819 22>; + oem,project-id = <17819>; + oem,hw-id = <22>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-pvt.dtsi b/arch/arm64/boot/dts/qcom/enchilada-pvt.dtsi new file mode 100644 index 000000000000..1201436447fa --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-pvt.dtsi @@ -0,0 +1,24 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +#include "enchilada-dvt.dtsi" +&vendor { + +}; + +&soc { + +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-panel-mismatch-check; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada-t0.dtsi b/arch/arm64/boot/dts/qcom/enchilada-t0.dtsi new file mode 100644 index 000000000000..6f1361236f0f --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada-t0.dtsi @@ -0,0 +1,122 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ +&vendor { + +}; + +&soc { + +}; + +&tlmm { + oem_rf_cable_mux { + oem_rf_cable_active: oem_rf_cable_active { + mux { + pins = "gpio32","gpio86"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio86"; + drive-strength = <2>; + bias-disable; + }; + }; + oem_rf_cable_suspend: oem_rf_cable_suspend { + mux { + pins = "gpio32","gpio86"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio86"; + drive-strength = <2>; + bias-disable; + }; + }; + }; +}; + +&soc { + oem_rf_cable { + compatible = "oem,rf_cable"; + interrupt-parent = <&tlmm>; + rf,cable-gpio-0 = <&tlmm 32 0>; + rf,cable-gpio-1 = <&tlmm 86 0>; + pinctrl-names = "oem_rf_cable_active", "oem_rf_cable_suspend"; + pinctrl-0 = <&oem_rf_cable_active>; + pinctrl-1 = <&oem_rf_cable_suspend>; + }; +}; + +&qupv3_se3_i2c { + status = "ok"; + nq@28 { + qcom,nq-firm = <&tlmm 62 0x00>; + }; +}; + +&tlmm { + cam_sensor_rear_0_vaf_active: cam_sensor_rear_0_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_vaf_suspend: cam_sensor_rear_0_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; +}; + +&soc{ + rear_0_actuator_regulator: gpio-regulator@0 { + compatible = "regulator-fixed"; + reg = <0x00 0x00>; + regulator-name = "actuator_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <100>; + enable-active-high; + gpio = <&tlmm 31 0>; + vin-supply = <&pmi8998_bob>; + }; +}; + +&actuator_rear_0{ + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + cci-master = <0>; + cam_vaf-supply = <&rear_0_actuator_regulator>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; +}; diff --git a/arch/arm64/boot/dts/qcom/enchilada.dtsi b/arch/arm64/boot/dts/qcom/enchilada.dtsi new file mode 100644 index 000000000000..53d15cfa88df --- /dev/null +++ b/arch/arm64/boot/dts/qcom/enchilada.dtsi @@ -0,0 +1,1957 @@ +/* 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 + +/*recommand add our code to this dtsi*/ +#include "dsi-panel-samsung_s6e3fc1_cmd.dtsi" +#include "dsi-panel-samsung_sofef00_m_cmd.dtsi" +#include "dsi-panel-samsung_sofef00_m_video.dtsi" +#include "dsi-panel-samsung_sefeg01_s_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fc2x01.dtsi" +#include "dsi-panel-samsung_dsc.dtsi" + +/{ +reserved-memory { + +}; +}; + +&vendor { + +}; + +&wdog{ + qcom,bark-time = <15000>; +}; + +&snd_934x { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "hifi amp", "LINEOUT1", + "hifi amp", "LINEOUT2", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "AMIC3", "MIC BIAS4", + "MIC BIAS4", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS1", + "MIC BIAS1", "ANCLeft Headset Mic", + "AMIC5", "MIC BIAS4", + "MIC BIAS4", "Handset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "DMIC4", "MIC BIAS4", + "MIC BIAS4", "Digital Mic4", + "DMIC5", "MIC BIAS4", + "MIC BIAS4", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; +}; + +&wcd934x_cdc { + qcom,cdc-micbias2-mv = <2700>; +}; + +&soc { + + gpio_keys { + 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>; + }; + }; + + + qcom,qbt1000 { + status = "disabled"; + }; + + fingerprint_detect { + compatible = "oneplus,fpdetect"; + fp-gpio-id0 = <&tlmm 91 0>; + fp-gpio-id1 = <&tlmm 92 0>; + fp-gpio-id2 = <&tlmm 95 0>; + pinctrl-names = "fp_id_init", "fp_id_up", "fp_id_down"; + pinctrl-0 = <&fp_id_init>; + pinctrl-1 = <&fp_id0_up &fp_id1_up &fp_id2_up>; + pinctrl-2 = <&fp_id0_down &fp_id1_down &fp_id2_down>; + oem,enchilada; + }; + + fpc_fpc1020 { + compatible = "fpc,fpc1020"; + interrupt-parent = <&tlmm>; + fpc,irq-gpio = <&tlmm 121 0>; + fpc,reset-gpio = <&tlmm 35 0>; + pinctrl-names = "fp_reset_high", "fp_reset_low"; + pinctrl-0 = <&fp_reset_high>; + pinctrl-1 = <&fp_reset_low>; + }; + + goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + fp-gpio-irq = <&tlmm 121 0x00>; + fp-gpio-reset = <&tlmm 35 0x00>; + fp-gpio-enable = <&tlmm 80 0x00>; + pinctrl-names = "fp_en_init", "fp_dis_init"; + pinctrl-0 = <&goodixfp_enable_init &goodixfp_irq_init>; + pinctrl-1 = <&goodixfp_disable_init>; + oem,enchilada; + status = "okay"; + }; + + //add tri_state_key support + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 24 0x00>; + tristate,gpio_key2 = <&tlmm 52 0x00>; + tristate,gpio_key3 = <&tlmm 126 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>; + }; + + dsi_panel_pwr_supply_no_labibb_2: dsi_panel_pwr_supply_no_labibb_2 { + #address-cells = <1>; + #size-cells = <0>; + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1850000>; + qcom,supply-max-voltage = <1850000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <0>; + }; + }; + + oem_serial_pinctrl { + compatible = "oem,oem_serial_pinctrl"; + pinctrl-names = "uart_pinctrl_active","uart_pinctrl_deactive"; + pinctrl-0 = <&qupv3_se9_2uart_active>; + pinctrl-1 = <&qupv3_se9_2uart_oem_sleep>; + + }; + +}; + +&tlmm { + qupv3_se9_2uart_oem_sleep: qupv3_se9_2uart_oem_sleep { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-pull-down; + }; + }; + +}; + +&qupv3_se9_2uart { + compatible = "qcom,msm-geni-console-oem"; +}; + + +&tlmm{ + config { + pins = "gpio37"; + drive-strength = <2>; + bias-pull-up; + }; +}; +&soc { + gpio_keys { + vol_up { + /delete-property/ gpio-key,wakeup; + }; + vol_down { + label = "volume_down"; + gpios = <&pm8998_gpios 5 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = <114>; + debounce-interval = <15>; + linux,can-disable; + }; + //disable cam_snapshot,and cam_focus + cam_snapshot { + status = "disabled"; + }; + cam_focus { + status = "disabled"; + }; +}; + +}; + +&pm8998_gpios { + key_vol_down { + key_vol_down_default: key_vol_down_default { + pins = "gpio5"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; +}; + +//add for volume down function ,and we do not use qbt1000 fingerprint sensor. +&soc{ + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 121 0>; + qcom,finger-detect-gpio = <&pm8998_gpios 5 0>; + status = "disabled"; + }; + +}; + + +&qupv3_se12_i2c { + status = "ok"; + synaptics-rmi-ts@20 { + compatible = "HWK,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 99 0x00>; + synaptics,irq-gpio = <&tlmm 125 0x2008>; + synaptics,1v8-gpio = <&tlmm 88 0x00>; + 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>; + }; +}; + + +&wsa881x_0211{ +status = "disabled"; +}; + +&wsa881x_0212{ +status = "disabled"; +}; + +&wsa881x_0213{ +status = "disabled"; +}; + +&wsa881x_0214{ +status = "disabled"; +}; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + 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 >; +}; + +&mdss_dsi1 { +status = "disabled"; +}; + +&mdss_dsi0_pll { + qcom,ssc-frequency-hz = <33000>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,dsi-display-active; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x34>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 21 09 09 25 23 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofef00_m_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofef00_m_video { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofeg01_s_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofeg01_s_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_s6e3fc2x01_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_s6e3fc2x01_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x35>; + qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 22 09 09 25 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; +}; +}; + + +&dsi_samsung_dsc_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_dsc_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x35>; + //qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 14 05 05 1F 0A 05 + 05 03 03 04 00]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&sde_dsi { + qcom,dsi-default-panel = <&dsi_samsung_sofef00_m_cmd>; + qcom,dsi-ctrl = <&mdss_dsi0>; + qcom,dsi-phy = <&mdss_dsi_phy0>; + clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, + <&mdss_dsi0_pll PCLK_MUX_0_CLK>; + clock-names = "mux_byte_clk0", "mux_pixel_clk0"; + pinctrl-0 = <&sde_dsi_active &sde_te_active &esd_check_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend &esd_check_suspend>; + qcom,platform-te-gpio = <&tlmm 30 0>; +}; + +&qupv3_se4_i2c { + status = "ok"; + max98927@3a { + compatible = "maxim,max98927L"; + reg = <0x3a>; + mono_stereo_mode = <0>; + maxim,98927-reset-gpio = <&tlmm 69 0>; + status = "ok"; + }; +}; + +/* modify 0x240:0x45->0x46, 0x244:0x29->0x2b, 0x198:0x20->0x19*/ +/* add op host mode phy init parameters modify 0x198:0x20->0x20*/ +&qusb_phy0 { + qcom,overwrite-bias2-disable; + qcom,qusb-phy-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x19 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x07 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x46 0x240 /* TUNE1 */ + 0x2b 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ + + qcom,qusb-phy-ophost-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x20 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x07 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x46 0x240 /* TUNE1 */ + 0x2b 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ +}; + +&pmi8998_pdphy { + vbus-supply = <&smb2_vbus>; +}; + +&qupv3_se10_i2c { + qcom,clk-freq-out = <100000>; + bq27541-battery@55 { + status = "ok"; + compatible = "ti,bq27541-battery"; + reg = <0x55>; + qcom,modify-soc-smooth; + }; + + oneplus_fastchg@26{ + status = "ok"; + compatible = "microchip,oneplus_fastchg"; + reg = <0x26>; + microchip,mcu-en-gpio = <&tlmm 102 0x00>; + microchip,usb-sw-1-gpio = <&tlmm 37 0x00>; + microchip,usb-sw-2-gpio = <&tlmm 51 0x00>; + microchip,ap-clk = <&tlmm 43 0x00>; + microchip,ap-data = <&tlmm 44 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>; + op,fw-erase-count = <384>; + op,fw-addr-low = <0x88>; + op,fw-addr-high = <0>; + }; +}; + +&mtp_batterydata{ + #include "OP-batterydata-3300mah.dtsi" +}; + +&pmi8998_charger { + qcom,dc-icl-ua = <1200000>; + 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 = <275>; + ibatmax-cool-ma = <425>; + ibatmax-little-cool-ma = <725>; + ibatmax-pre-normal-ma = <1375>; + ibatmax-normal-ma = <1950>; + ibatmax-warm-ma = <750>; + /* vbatmax setting for different temp regions */ + vbatmax-little-cold-mv = <3975>; + vbatmax-cool-mv = <4400>; + vbatmax-little-cool-mv = <4400>; + vbatmax-pre-normal-mv = <4400>; + vbatmax-normal-mv = <4400>; + 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>; + + op,sw-iterm-ma = <264>; + op,sw-check-full-enable; + /*otg low battery current limit*/ + op,otg-icl-ctrl-enable; + otg-low-battery-thr = <15>; + otg-low-bat-icl-thr = <1000000>; + otg-normal-bat-icl-thr = <1500000>; + /* other settings */ + qcom,cutoff-voltage-with-charger = <3250>; + disable-pd; + + qcom,msm-bus,name = "dash_clk_vote"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = <1 731 0 300000000>, + <1 731 0 0>; +}; + +&pmi8998_fg { + qcom,fg-force-load-profile; + oem,use_external_fg; + qcom,fg-rsense-sel = <0>; + qcom,fg-sys-term-current = <180>; + qcom,fg-chg-term-current = <150>; +}; + +&qusb_phy0 { + pinctrl-names = "atest_usb13_suspend", "atest_usb13_active"; + pinctrl-0 = <&atest_usb13_suspend>; + pinctrl-1 = <&atest_usb13_active>; +}; + +&tlmm { + atest_usb13_active: atest_usb13_active { + mux { + pins = "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio7"; + drive-strength = <12>; + bias-pull-up; + }; + }; + + atest_usb13_suspend: atest_usb13_suspend { + mux { + pins = "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio7"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + oneplus_fastchg { + usb_sw_active: usb_sw_active { + mux { + pins = "gpio37", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio37", "gpio51"; + drive-strength = <16>; + bias-pull-down; + }; + }; + + usb_sw_suspend: usb_sw_suspend { + mux { + pins = "gpio37", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio37", "gpio51"; + drive-strength = <2>; + bias-disable; + }; + }; + + fastchg_active: fastchg_active { + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + fastchg_suspend: fastchg_suspend { + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_clk_active: ap_clk_active { + mux { + pins = "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio44"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_clk_suspend: ap_clk_suspend { + mux { + pins = "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio44"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_data_active: ap_data_active { + mux { + pins = "gpio43"; + function = "gpio"; + }; + + config { + pins = "gpio43"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_data_suspend: ap_data_suspend { + mux { + pins = "gpio43"; + function = "gpio"; + }; + + config { + pins = "gpio43"; + drive-strength = <2>; + bias-disable; + }; + }; + }; +}; + +&pcie0 { + status = "disabled"; +}; + +&pcie1 { + status = "disabled"; +}; + +&spss_utils { + status = "disabled"; +}; + + +&qupv3_se3_i2c { + status = "ok"; + nq@28 { + qcom,nq-firm = <&tlmm 34 0x00>; + + }; +}; + +&pmi8998_wled { + status = "disabled"; +}; + +&mdss_mdp { + connectors = <&sde_rscc &sde_wb &sde_dsi>; +}; +&sde_dp{ + status = "disabled"; +}; + +&labibb { + status = "disabled"; +}; +&sdhc_2 { + status = "disabled"; +}; + +&pmi8998_haptics { + status = "okay"; + compatible = "qcom,qpnp-haptics"; + qcom,pmic-misc = <&pmi8998_misc>; + qcom,misc-clk-trim-error-reg = <0xf3>; + qcom,actuator-type = <0>; + qcom,wave-shape = "sine"; + qcom,play-mode = "buffer"; + qcom,ilim-ma = <800>; + qcom,brake-pattern = <0x3 0x3 0x3 0x3>; + qcom,drive-period-code-max-variation-pct = <5>; + qcom,drive-period-code-min-variation-pct = <5>; + qcom,lra-auto-mode; + qcom,lra-high-z = "opt1"; + qcom,sc-dbc-cycles = <8>; + qcom,en-brake; + qcom,wave-rep-cnt = <1>; + qcom,wave-samp-rep-cnt = <1>; + qcom,vmax-mv = <2088>; + qcom,wave-play-rate-us = <4255>; + qcom,lra-auto-res-mode = "zxd-eop"; + qcom,lra-res-cal-period = <32>; +}; + +&sde_dsi_active { + mux { + pins = "gpio6", "gpio25", "gpio26"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio25", "gpio26"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + }; + }; +&sde_dsi_suspend { + mux { + pins = "gpio6", "gpio25", "gpio26"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio25", "gpio26"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + +&tlmm { + goodixfp_enable_init: goodixfp_enable_init { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + }; + + goodixfp_irq_init: goodixfp_irq_init { + mux { + pins = "gpio121"; + function = "gpio"; + }; + + config { + pins = "gpio121"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + goodixfp_disable_init: goodixfp_disable_init { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + }; + + fp_id_init: fp_id_init { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + }; + + fp_reset_high: fp_reset_high { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + }; + + fp_reset_low: fp_reset_low { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + }; + + fp_id0_up: fp_id0_up { + mux { + pins = "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio91"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + fp_id0_down: fp_id0_down { + mux { + pins = "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio91"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + fp_id1_up: fp_id1_up { + mux { + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + fp_id1_down: fp_id1_down { + mux { + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + fp_id2_up: fp_id2_up { + mux { + pins = "gpio95"; + function = "gpio"; + }; + + config { + pins = "gpio95"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + fp_id2_down: fp_id2_down { + mux { + pins = "gpio95"; + function = "gpio"; + }; + + config { + pins = "gpio95"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + tri_state_key_active: tri_state_key_active { + mux { + pins = "gpio24","gpio52","gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio24","gpio52","gpio126"; + drive-strength = <2>; + bias-disable; + }; + }; + + tri_state_key_suspend: tri_state_key_suspend { + mux { + pins = "gpio24","gpio52","gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio24","gpio52","gpio126"; + drive-strength = <2>; + bias-disable; + }; + }; + + esd_check_active: esd_check_active { + mux { + pins = "gpio30"; + function = "gpio"; + }; + config { + pins = "gpio30"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + esd_check_suspend: esd_check_suspend { + mux { + pins = "gpio30"; + function = "gpio"; + }; + config { + pins = "gpio30"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; +}; + +//overlay for camera start +&tlmm { + 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_rest_active: cam_sensor_rear_0_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_rest_suspend: cam_sensor_rear_0_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* VANA */ + 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 { + /* VANA */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_dig_active: cam_sensor_rear_0_dig_active { + /* DIG */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_dig_suspend: cam_sensor_rear_0_dig_suspend { + /* DIG */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + 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_rest_active: cam_sensor_rear_1_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_rest_suspend: cam_sensor_rear_1_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* VANA */ + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* VANA */ + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + 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_rear_1_dig_active: cam_sensor_rear_1_dig_active { + /* DIG */ + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_dig_suspend: cam_sensor_rear_1_dig_suspend { + /* DIG */ + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK2 */ + 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 { + /* MCLK2 */ + 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_rest_active: + cam_sensor_front_0_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_rest_suspend: + cam_sensor_front_0_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: + cam_sensor_front_0_ana_active { + /* VANA */ + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: + cam_sensor_front_0_ana_suspend { + /* VANA */ + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_dig_active: + cam_sensor_front_0_dig_active { + /* DIG */ + mux { + pins = "gpio117"; + function = "gpio"; + }; + + config { + pins = "gpio117"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_dig_suspend: + cam_sensor_front_0_dig_suspend { + /* DIG */ + mux { + pins = "gpio117"; + function = "gpio"; + }; + + config { + pins = "gpio117"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; +}; + +&soc { + led_flash_rear: qcom,camera-flash@0 { + cell-index = <0>; + reg = <0x00 0x00>; + compatible = "qcom,camera-flash"; + flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + rear_1_actuator_regulator: gpio-regulator@1 { + compatible = "regulator-fixed"; + reg = <0x01 0x00>; + regulator-name = "actuator_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <100>; + enable-active-high; + gpio = <&tlmm 77 0>; + vin-supply = <&pmi8998_bob>; + }; +}; + +&cam_cci { + qcom,cam-res-mgr { + compatible = "qcom,cam-res-mgr"; + status = "ok"; + }; + + actuator_rear_0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + cci-master = <0>; + cam_vaf-supply = <&pm8998_l22>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; + }; + + actuator_rear_1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + cci-master = <1>; + cam_vaf-supply = <&rear_1_actuator_regulator>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; + }; + + ois_rear_0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + cam_vaf-supply = <&pm8998_l22>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2864000>; + rgltr-max-voltage = <2864000>; + rgltr-load-current = <500000>; + gpio-no-mux = <0>; + gpios = <&tlmm 40 0>; + gpio-vaf = <0>; + gpio-req-tbl-num = <0>; + gpio-req-tbl-flags = <0>; + gpio-req-tbl-label = "CAM_OIS_PWD_0"; + cci-master = <0>; + status = "ok"; + }; + + eeprom_rear_0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 3600000 3600000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_rest_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_dig_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_rest_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_dig_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 28 0>, + <&tlmm 27 0>, + <&tlmm 8 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VANA_0", + "CAM_DIG_0"; + sensor-position = <0>; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK0_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + eeprom_rear_1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + cam_vdig-supply = <&pmi8998_bob>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vdig", "cam_vio", "cam_vana", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <3312000 0 3312000 0>; + rgltr-max-voltage = <3600000 0 3600000 0>; + rgltr-load-current = <80000 0 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_rest_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_dig_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_rest_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_dig_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 23 0>, + <&tlmm 78 0>, + <&tlmm 79 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VANA_1", + "CAM_DIG_1"; + + sensor-position = <0>; + sensor-mode = <0>; + cci-master = <1>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK2_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + eeprom_front_0: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 80000 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_rest_active + &cam_sensor_front_0_ana_active + &cam_sensor_front_0_dig_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_rest_suspend + &cam_sensor_front_0_ana_suspend + &cam_sensor_front_0_dig_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 9 0>, + <&tlmm 104 0>, + <&tlmm 117 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VANA_2", + "CAM_DIG_2"; + sensor-position = <1>; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK1_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@0 { + cell-index = <0>; + compatible = "qcom,cam-sensor"; + reg = <0x0>; + csiphy-sd-index = <0>; + sensor-position-roll = <270>; + sensor-position-pitch = <0>; + sensor-position-yaw = <180>; + led-flash-src = <&led_flash_rear>; + actuator-src = <&actuator_rear_0>; + ois-src = <&ois_rear_0>; + eeprom-src = <&eeprom_rear_0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 3600000 3600000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_rest_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_dig_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_rest_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_dig_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 28 0>, + <&tlmm 27 0>, + <&tlmm 8 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VANA_0", + "CAM_DIG_0"; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK0_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@1 { + cell-index = <1>; + compatible = "qcom,cam-sensor"; + reg = <0x1>; + csiphy-sd-index = <1>; + sensor-position-roll = <90>; + sensor-position-pitch = <0>; + sensor-position-yaw = <180>; + led-flash-src = <&led_flash_rear>; + actuator-src = <&actuator_rear_1>; + eeprom-src = <&eeprom_rear_1>; + cam_vdig-supply = <&pmi8998_bob>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vdig", "cam_vio", "cam_vana", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <3312000 0 3312000 0>; + rgltr-max-voltage = <3600000 0 3600000 0>; + rgltr-load-current = <80000 0 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_rest_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_dig_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_rest_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_dig_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 23 0>, + <&tlmm 78 0>, + <&tlmm 79 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VANA_1", + "CAM_DIG_1"; + sensor-mode = <0>; + cci-master = <1>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK2_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@2 { + cell-index = <2>; + compatible = "qcom,cam-sensor"; + reg = <0x02>; + csiphy-sd-index = <2>; + sensor-position-roll = <270>; + sensor-position-pitch = <0>; + sensor-position-yaw = <0>; + eeprom-src = <&eeprom_front_0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 80000 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_rest_active + &cam_sensor_front_0_ana_active + &cam_sensor_front_0_dig_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_rest_suspend + &cam_sensor_front_0_ana_suspend + &cam_sensor_front_0_dig_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 9 0>, + <&tlmm 104 0>, + <&tlmm 117 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VANA_2", + "CAM_DIG_2"; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK1_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + +}; + + + +//overlay for camera end. + +&tlmm { + //add by gu qicai begin + ts_mux { + ts_active: ts_active { + mux { + pins = "gpio99", "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio99", "gpio125"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ts_reset_suspend: ts_reset_suspend { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + ts_int_suspend: ts_int_suspend { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + //add by gu qicai end +}; + +/* OnePlus add thermistor, by rio.zhao*/ +&pm8998_vadc { + pa1_therm { + label = "pa1_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; +}; + +&pm8998_adc_tm { + io-channels = <&pm8998_vadc ADC_VPH_PWR>, + <&pm8998_vadc ADC_XO_THERM_PU2>, + <&pm8998_vadc ADC_AMUX_THM1_PU2>, + <&pm8998_vadc ADC_AMUX_THM3_PU2>, + <&pm8998_vadc ADC_AMUX_THM5_PU2>, + <&pm8998_vadc ADC_AMUX_THM4_PU2>; + pa1_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&thermal_zones { + pa1-therml-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm ADC_AMUX_THM4_PU2>; + thermal-governor = "user_space"; + wake-capable-sensor; + + trips { + active-config0 { + temperature = <65000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; +}; + +&spmi_bus { + qcom,pm8998@0 { + qcom,power-on@800 { + qcom,pon_1 { + qcom,support-reset = <0>; + }; + }; + }; +}; + + +&qupv3_se0_spi { + status = "ok"; + qcom,disable-autosuspend; + + ese@0 { + compatible = "nxp,p61"; + reg = <0>; + spi-max-frequency = <8000000>; + nxp,nfcc = "1-0028"; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/fajita-dvt-bu.dtsi b/arch/arm64/boot/dts/qcom/fajita-dvt-bu.dtsi new file mode 100644 index 000000000000..d1ff91d17f1b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-dvt-bu.dtsi @@ -0,0 +1,20 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-bu-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-bu-overlay.dts new file mode 100644 index 000000000000..148cc5969b80 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-bu-overlay.dts @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-dvt-bu.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP DVT 2nd"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 35>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-overlay.dts new file mode 100644 index 000000000000..914d27d58612 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-dvt-v2.1-overlay.dts @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-dvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP DVT"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 34>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-dvt.dtsi b/arch/arm64/boot/dts/qcom/fajita-dvt.dtsi new file mode 100644 index 000000000000..d1ff91d17f1b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-dvt.dtsi @@ -0,0 +1,20 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-mp-spec.dtsi b/arch/arm64/boot/dts/qcom/fajita-mp-spec.dtsi new file mode 100644 index 000000000000..00914208a77e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-mp-spec.dtsi @@ -0,0 +1,36 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; + +&oneplus_fastchg { + op,mcl_verion; +}; + +&pmi8998_charger { + warm-bat-decidegc = <480>; +}; + +&bq27541_battery { + op,mcl_verion; +}; + +&pmi8998_charger { + ibatmax-little-cool-low_ma = <480>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-mp-v2.1-spec-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-mp-v2.1-spec-overlay.dts new file mode 100644 index 000000000000..02e9464b5fbe --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-mp-v2.1-spec-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-mp-spec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP MP spec"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 45>; + oem,project-id = <18801>; + oem,hw-id = <45>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-bu.dtsi b/arch/arm64/boot/dts/qcom/fajita-pvt-bu.dtsi new file mode 100644 index 000000000000..d1ff91d17f1b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-bu.dtsi @@ -0,0 +1,20 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-spec.dtsi b/arch/arm64/boot/dts/qcom/fajita-pvt-spec.dtsi new file mode 100644 index 000000000000..e7c9b8fd331a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-spec.dtsi @@ -0,0 +1,32 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; + +&oneplus_fastchg { + op,mcl_verion; +}; + +&pmi8998_charger { + warm-bat-decidegc = <480>; +}; + +&pmi8998_charger { + ibatmax-little-cool-low_ma = <480>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-v1.dtsi b/arch/arm64/boot/dts/qcom/fajita-pvt-v1.dtsi new file mode 100644 index 000000000000..d1ff91d17f1b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-v1.dtsi @@ -0,0 +1,20 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-bu-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-bu-overlay.dts new file mode 100644 index 000000000000..c4520cd66338 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-bu-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-pvt-bu.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT/MP 2nd"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 42>; + oem,project-id = <18801>; + oem,hw-id = <42>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-overlay.dts new file mode 100644 index 000000000000..7ec4de135594 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-overlay.dts @@ -0,0 +1,36 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-pvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT/MP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 41>; + oem,project-id = <18801>; + oem,hw-id = <41>; + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-spec-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-spec-overlay.dts new file mode 100644 index 000000000000..6690e2dac784 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-spec-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-pvt-spec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT/MP spec"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 44>; + oem,project-id = <18801>; + oem,hw-id = <44>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-v1-overlay.dts b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-v1-overlay.dts new file mode 100644 index 000000000000..5aac83a76b5c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt-v2.1-v1-overlay.dts @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "fajita.dtsi" +#include "fajita-pvt-v1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP PVT/MP v1"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 18801 43>; + oem,project-id = <18801>; + oem,hw-id = <43>; +}; diff --git a/arch/arm64/boot/dts/qcom/fajita-pvt.dtsi b/arch/arm64/boot/dts/qcom/fajita-pvt.dtsi new file mode 100644 index 000000000000..d1ff91d17f1b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita-pvt.dtsi @@ -0,0 +1,20 @@ +/* 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. + */ +/*don't suggest add to this file, this file is only for different hw*/ + +&vendor { + +}; + +&soc { + +}; diff --git a/arch/arm64/boot/dts/qcom/fajita.dtsi b/arch/arm64/boot/dts/qcom/fajita.dtsi new file mode 100644 index 000000000000..b40661d54299 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fajita.dtsi @@ -0,0 +1,2249 @@ +/* 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 + +/*recommand add our code to this dtsi*/ +#include "dsi-panel-samsung_s6e3fc1_cmd.dtsi" +#include "dsi-panel-samsung_sofef00_m_cmd.dtsi" +#include "dsi-panel-samsung_sofef00_m_video.dtsi" +#include "dsi-panel-samsung_sefeg01_s_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fc2x01.dtsi" +#include "dsi-panel-samsung_dsc.dtsi" + + +/{ +reserved-memory { + +}; +}; + +&vendor { + +}; + +&wdog{ + qcom,bark-time = <15000>; +}; + +&snd_934x { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "hifi amp", "LINEOUT1", + "hifi amp", "LINEOUT2", + "AMIC1", "MIC BIAS3", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "AMIC3", "MIC BIAS4", + "MIC BIAS4", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS1", + "MIC BIAS1", "ANCLeft Headset Mic", + "AMIC5", "MIC BIAS3", + "MIC BIAS3", "Handset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "DMIC4", "MIC BIAS4", + "MIC BIAS4", "Digital Mic4", + "DMIC5", "MIC BIAS4", + "MIC BIAS4", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + op,smartpa ="tfa98xx"; +// qcom,us-euro-gpios = <&tlmm 103 0>; + op,usb_sw; + usb_sw = <&pmi8998_gpios 10 GPIO_ACTIVE_LOW>; + hp_sw = <&tlmm 103 0>; + mbhc_sw = <&tlmm 36 1>; + ldo_sw = <&pmi8998_gpios 6 GPIO_ACTIVE_LOW>; +}; + +&wcd934x_cdc { + qcom,cdc-micbias1-mv = <2700>; + qcom,cdc-micbias2-mv = <2700>; + qcom,cdc-micbias3-mv = <2700>; + qcom,cdc-micbias4-mv = <2700>; +}; + +&soc { + + gpio_keys { + 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>; + }; + }; + + + qcom,qbt1000 { + status = "disabled"; + }; + + fingerprint_detect { + compatible = "oneplus,fpdetect"; + fp-gpio-id0 = <&tlmm 91 0>; + pinctrl-names = "fp_id_up", "fp_id_down"; + pinctrl-0 = <&fp_id0_up>; + pinctrl-1 = <&fp_id0_down>; + oem,fajta; + }; + + fpc_fpc1020 { + compatible = "fpc,fpc1020"; + interrupt-parent = <&tlmm>; + fpc,irq-gpio = <&tlmm 121 0>; + fpc,reset-gpio = <&tlmm 35 0>; + pinctrl-names = "fp_reset_high", "fp_reset_low"; + pinctrl-0 = <&fp_reset_high>; + pinctrl-1 = <&fp_reset_low>; + }; + + goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + vdd-3v2-supply = <&pm8998_l22>; + vdd-voltage = <3200000 3200000>; + vdd-current = <50000>; + fp-gpio-irq = <&tlmm 121 0x00>; + fp-gpio-reset = <&tlmm 35 0x00>; + fp-gpio-enable = <&tlmm 80 0x00>; + pinctrl-names = "fp_en_init", "fp_dis_init"; + pinctrl-0 = <&fp_enable_init &fp_irq_init>; + pinctrl-1 = <&fp_disable_init>; + oem,fajta; + status = "okay"; + }; + + silead_fp { + compatible = "sil,fingerprint"; + interrupt-parent = <&tlmm>; + avdd-supply = <&pm8998_l22>; + irq-gpios = <&tlmm 121 0x00>; + rst-gpios = <&tlmm 35 0x00>; + pinctrl-names = "fp_en_init"; + pinctrl-0 = <&fp_reset_init &fp_irq_init>; + oem,fajta; + status = "okay"; + }; + + //add tri_state_key support + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 24 0x00>; + tristate,gpio_key2 = <&tlmm 52 0x00>; + tristate,gpio_key3 = <&tlmm 126 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>; + }; + + +/*#endif*/ + dsi_panel_pwr_supply_no_labibb_2: dsi_panel_pwr_supply_no_labibb_2 { + #address-cells = <1>; + #size-cells = <0>; + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1850000>; + qcom,supply-max-voltage = <1850000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <0>; + }; + }; + + oem_serial_pinctrl { + compatible = "oem,oem_serial_pinctrl"; + pinctrl-names = "uart_pinctrl_active","uart_pinctrl_deactive"; + pinctrl-0 = <&qupv3_se9_2uart_active>; + pinctrl-1 = <&qupv3_se9_2uart_oem_sleep>; + + }; + +}; + +&tlmm { + qupv3_se9_2uart_oem_sleep: qupv3_se9_2uart_oem_sleep { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-pull-down; + }; + }; + +}; + +&qupv3_se9_2uart { + compatible = "qcom,msm-geni-console-oem"; +}; + + +&tlmm{ + config { + pins = "gpio37"; + drive-strength = <2>; + bias-pull-up; + }; +}; +&soc { + gpio_keys { + vol_up { + /delete-property/ gpio-key,wakeup; + }; + //add volume down support , shankai @bsp . + vol_down { + label = "volume_down"; + gpios = <&pm8998_gpios 5 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = <114>; + debounce-interval = <15>; + linux,can-disable; + }; + //add end. + //disable cam_snapshot,and cam_focus + cam_snapshot { + status = "disabled"; + }; + cam_focus { + status = "disabled"; + }; +}; + +}; + +&pm8998_gpios { + //VENDOR_EIDT + key_vol_down { + key_vol_down_default: key_vol_down_default { + pins = "gpio5"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; +}; + +//add for volume down function ,and we do not use qbt1000 fingerprint sensor. +&soc{ + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 121 0>; + qcom,finger-detect-gpio = <&pm8998_gpios 5 0>; + status = "disabled"; + }; + +}; + +&qupv3_se4_i2c { + status = "ok"; + tfa98xx@34 { + compatible = "nxp,tfa9894"; + reg = <0x34>; + reset-gpio = <&tlmm 69 0>; + status = "ok"; + }; +}; + +&pmi8998_gpios { + gpio10_dig_out { + gpio10_dig_out_default: gpio10_dig_out_default { + pins = "gpio10"; /* GPIO 14 */ + function = "normal"; /* normal output */ + power-source = <0>; /* VIN0 */ + output-low; /* digital output, no invert */ + input-disable; /* prevent GPIO from being set to DIO */ + }; + }; +}; + +&pmi8998_gpios { + gpio6_dig_out { + gpio6_dig_out_default: gpio6_dig_out_default { + pins = "gpio6"; /* GPIO 6 */ + function = "normal"; /* normal output */ + power-source = <1>; /* VIN0 */ + output-low; /* digital output, no invert */ + input-disable; /* prevent GPIO from being set to DIO */ + }; + }; +}; + +&pmi8998_gpios { + qnovo_fet_ctrl { + qnovo_fet_ctrl_default { + pins = "gpio6"; + function = "normal"; + output-low; + input-disable; + bias-disable; + power-source = <1>; + qcom,drive-strength = <1>; + }; + }; +}; + + +//suzhiguang,add for fsa4480 +&qupv3_se7_i2c { + status = "ok"; + fsa4480@42 { + compatible = "fsa,fsa4480"; + reg = <0x42>; + mbhc_en = <&pmi8998_gpios 10 GPIO_ACTIVE_LOW>; + status = "ok"; + }; +}; +//suzhiguang,add for switch +&slim_aud { + tavil_codec { + op,usb_sw; + usb_sw = <&pmi8998_gpios 10 GPIO_ACTIVE_LOW>; + hp_sw = <&tlmm 103 0>; + mbhc_sw = <&tlmm 36 1>; + ldo_sw = <&pmi8998_gpios 6 GPIO_ACTIVE_LOW>; + }; +}; +&qupv3_se12_i2c { + status = "ok"; + synaptics-rmi-ts@20 { + compatible = "HWK,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 99 0x00>; + synaptics,irq-gpio = <&tlmm 125 0x2008>; + synaptics,1v8-gpio = <&tlmm 88 0x00>; + oem,support_1080x2160_tp; + oem,support_1080x2340_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>; + oem,fajta; + }; +}; + + +&wsa881x_0211{ +status = "disabled"; +}; + +&wsa881x_0212{ +status = "disabled"; +}; + +&wsa881x_0213{ +status = "disabled"; +}; + +&wsa881x_0214{ +status = "disabled"; +}; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + 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 >; +}; + +&mdss_dsi1 { +status = "disabled"; +}; + +&mdss_dsi0_pll { + qcom,ssc-frequency-hz = <33000>; +}; + +&sde_dp { + qcom,aux-en-gpio = <&tlmm 122 0>; + qcom,aux-sel-gpio = <&tlmm 123 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,dsi-display-active; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x34>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 21 09 09 25 23 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofef00_m_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofef00_m_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofef00_m_video { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_sofeg01_s_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_sofeg01_s_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 23 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_samsung_s6e3fc2x01_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-brightness-default-val = <200>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_s6e3fc2x01_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x35>; + qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 22 09 09 25 24 09 + 09 06 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; +}; +}; + + +&dsi_samsung_dsc_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb_2>; + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-vci-gpio = <&tlmm 26 0>; + qcom,platform-poc-gpio = <&tlmm 25 0>; +}; + +&dsi_samsung_dsc_cmd { + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x35>; + //qcom,mdss-dsi-panel-clockrate = <1037000000>;// 518.5MHZ + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 14 05 05 1F 0A 05 + 05 03 03 04 00]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&sde_dsi { + qcom,dsi-default-panel = <&dsi_samsung_s6e3fc2x01_cmd>; + qcom,dsi-ctrl = <&mdss_dsi0>; + qcom,dsi-phy = <&mdss_dsi_phy0>; + clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, + <&mdss_dsi0_pll PCLK_MUX_0_CLK>; + clock-names = "mux_byte_clk0", "mux_pixel_clk0"; + pinctrl-0 = <&sde_dsi_active &sde_te_active &esd_check_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend &esd_check_suspend>; + qcom,platform-te-gpio = <&tlmm 30 0>; +}; + +&qupv3_se4_i2c { + status = "ok"; + max98927@3a { + compatible = "maxim,max98927L"; + reg = <0x3a>; + mono_stereo_mode = <0>; + maxim,98927-reset-gpio = <&tlmm 69 0>; + status = "disabled"; + }; +}; + +/* Infi@bsp,20171130 usb-phy0 usb eye-diagram tunning */ +/* modify 0x240:0x45->0x47, 0x244:0x29->0x2b, 0x198:0x20->0x12*/ +/* add op host mode phy init parameters modify 0x198:0x20->0x21*/ +&qusb_phy0 { + qcom,usb-oe-exist; + qcom,overwrite-bias2-disable; + qcom,qusb-phy-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x12 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x07 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x47 0x240 /* TUNE1 */ + 0x2b 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ + + qcom,qusb-phy-ophost-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x21 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x07 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x46 0x240 /* TUNE1 */ + 0x2b 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ +}; + +&pmi8998_pdphy { + vbus-supply = <&smb2_vbus>; +}; + +/* david@bsp, 20171023 Battery & Charging porting STRAT */ +&qupv3_se10_i2c { + qcom,clk-freq-out = <100000>; + bq27541_battery:bq27541-battery@55 { + status = "ok"; + compatible = "ti,bq27541-battery"; + reg = <0x55>; + qcom,modify-soc-smooth; + }; + + oneplus_fastchg:oneplus_fastchg@26{ + status = "ok"; + compatible = "microchip,oneplus_fastchg"; + reg = <0x26>; + microchip,mcu-en-gpio = <&tlmm 102 0x00>; + microchip,usb-sw-1-gpio = <&tlmm 37 0x00>; + microchip,usb-sw-2-gpio = <&tlmm 51 0x00>; + microchip,ap-clk = <&tlmm 43 0x00>; + microchip,ap-data = <&tlmm 44 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>; + }; +}; + +&mtp_batterydata{ + #include "OP-batterydata-3700mah.dtsi" +}; + +&pm8998_gpios { + gpio12_adc { + gpio12_adc_default: gpio12_adc_default { + pins = "gpio12"; /* GPIO 12 */ + function = "normal"; /* normal */ + bias-pull-up; + bias-high-impedance; /* DISABLE GPIO12 for ADC*/ + }; + }; +}; + +&pm8998_vadc { + gpio12_v { + reg = ; + label = "gpio12_v"; + qcom,ratiometric; + qcom,hw-settle-time = <800>; + qcom,pre-scaling = <1 1>; + }; +}; + +&pmi8998_charger { + qcom,dc-icl-ua = <1200000>; + 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 = <500>; + ibatmax-little-cool-ma = <875>; + ibatmax-little-cool-low_ma = <450>; + ibatmax-pre-normal-ma = <1625>; + ibatmax-normal-ma = <1950>; + ibatmax-warm-ma = <875>; + /* vbatmax setting for different temp regions */ + vbatmax-little-cold-mv = <3975>; + vbatmax-cool-mv = <4400>; + vbatmax-little-cool-mv = <4400>; + vbatmax-pre-normal-mv = <4400>; + vbatmax-normal-mv = <4400>; + vbatmax-warm-mv = <4080>; + little-cool-vbat-thr-mv = <4180>; + /* 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>; + + /*ffc temp region*/ + ffc-pre-normal-decidegc = <160>; + ffc-normal-decidegc = <350>; + ffc-warm-decidegc = <400>; + ffc-normal-fcc-ma = <650>; + ffc-warm-fcc-ma = <750>; + ffc-normal-cutoff-ma = <550>; + ffc-warm-cutoff-ma = <650>; + ffc-full-vbat-mv = <4430>; + + /* 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>; + op,sw-iterm-ma = <296>; + op,sw-check-full-enable; + /*otg low battery current limit*/ + op,otg-icl-ctrl-enable; + otg-low-battery-thr = <15>; + otg-low-bat-icl-thr = <1000000>; + otg-normal-bat-icl-thr = <1500000>; + /* other settings */ + qcom,cutoff-voltage-with-charger = <3250>; + disable-pd; + op,usb-check = <&tlmm 95 0x00>; + qcom,msm-bus,name = "dash_clk_vote"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = <1 731 0 300000000>, + <1 731 0 0>; + op,vbus-ctrl-gpio = <&pmi8998_gpios 3 GPIO_ACTIVE_LOW>; + io-channels = <&pmi8998_rradc 8>, + <&pmi8998_rradc 10>, + <&pmi8998_rradc 3>, + <&pmi8998_rradc 4>, + <&pm8998_vadc VADC_LR_MUX7_HW_ID>; + io-channel-names = "charger_temp", + "charger_temp_max", + "usbin_i", + "usbin_v", + "gpio12_voltage"; + pinctrl-names = "op_usb_temp_adc_default"; + pinctrl-0 = <&gpio12_adc_default>; +}; + +&pmi8998_fg { + qcom,fg-force-load-profile; + oem,use_external_fg; + qcom,fg-rsense-sel = <0>; + qcom,fg-sys-term-current = <180>; + qcom,fg-chg-term-current = <165>; +}; + +&qusb_phy0 { + pinctrl-names = "atest_usb13_suspend", "atest_usb13_active", + "usb_oe_suspend", "usb_oe_active"; + pinctrl-0 = <&atest_usb13_suspend>; + pinctrl-1 = <&atest_usb13_active>; + pinctrl-2 = <&usb_oe_suspend>; + pinctrl-3 = <&usb_oe_active>; +}; + +&tlmm { + hp_sw_active: hp_sw_active { + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + hp_sw_suspend: hp_sw_suspend { + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + atest_usb13_active: atest_usb13_active { + mux { + pins = "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio7"; + drive-strength = <12>; + bias-pull-up; + }; + }; + + atest_usb13_suspend: atest_usb13_suspend { + mux { + pins = "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio7"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + usb_oe_active: usb_oe_active { + mux { + pins = "gpio87"; + function = "gpio"; + }; + + config { + pins = "gpio87"; + drive-strength = <12>; + bias-pull-up; + }; + }; + + usb_oe_suspend: usb_oe_suspend { + mux { + pins = "gpio87"; + function = "gpio"; + }; + + config { + pins = "gpio87"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + oneplus_fastchg { + usb_sw_active: usb_sw_active { + mux { + pins = "gpio37", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio37", "gpio51"; + drive-strength = <16>; + bias-pull-down; + }; + }; + + usb_sw_suspend: usb_sw_suspend { + mux { + pins = "gpio37", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio37", "gpio51"; + drive-strength = <2>; + bias-disable; + }; + }; + + fastchg_active: fastchg_active { + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + fastchg_suspend: fastchg_suspend { + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_clk_active: ap_clk_active { + mux { + pins = "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio44"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_clk_suspend: ap_clk_suspend { + mux { + pins = "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio44"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_data_active: ap_data_active { + mux { + pins = "gpio43"; + function = "gpio"; + }; + + config { + pins = "gpio43"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_data_suspend: ap_data_suspend { + mux { + pins = "gpio43"; + function = "gpio"; + }; + + config { + pins = "gpio43"; + drive-strength = <2>; + bias-disable; + }; + }; + }; +}; + +&pcie0 { + status = "disabled"; +}; + +&pcie1 { + status = "disabled"; +}; +/* david@bsp, 20171023 Battery & Charging porting END */ + +&qupv3_se3_i2c { + status = "ok"; + nq@28 { + qcom,nq-firm = <&tlmm 62 0x00>; + + }; +}; + +&pmi8998_wled { + status = "disabled"; +}; + +&mdss_mdp { + connectors = <&sde_rscc &sde_wb &sde_dsi>; +}; +&sde_dp{ + status = "disabled"; +}; + +&labibb { + status = "disabled"; +}; +&sdhc_2 { + status = "disabled"; +}; + +&pmi8998_haptics { + status = "okay"; + compatible = "qcom,qpnp-haptics"; + qcom,pmic-misc = <&pmi8998_misc>; + qcom,misc-clk-trim-error-reg = <0xf3>; + qcom,actuator-type = <0>; + qcom,wave-shape = "sine"; + qcom,play-mode = "buffer"; + qcom,ilim-ma = <800>; + qcom,brake-pattern = <0x3 0x3 0x3 0x3>; + qcom,drive-period-code-max-variation-pct = <5>; + qcom,drive-period-code-min-variation-pct = <5>; + qcom,lra-auto-mode; + qcom,lra-high-z = "opt1"; + qcom,sc-dbc-cycles = <8>; + qcom,en-brake; + qcom,wave-rep-cnt = <1>; + qcom,wave-samp-rep-cnt = <1>; + qcom,vmax-mv = <2088>; + qcom,wave-play-rate-us = <4255>; + qcom,lra-auto-res-mode = "zxd-eop"; + qcom,lra-res-cal-period = <32>; +}; + +&sde_dsi_active { + mux { + pins = "gpio6", "gpio25", "gpio26"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio25", "gpio26"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + }; + }; +&sde_dsi_suspend { + mux { + pins = "gpio6", "gpio25", "gpio26"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio25", "gpio26"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + +&sde_dp_aux_active { + mux { + pins = "gpio122", "gpio123"; + function = "gpio"; + }; + + config { + pins = "gpio122", "gpio123"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; +}; + +&sde_dp_aux_suspend { + mux { + pins = "gpio122", "gpio123"; + function = "gpio"; + }; + + config { + pins = "gpio122", "gpio123"; + bias-pull-down; + drive-strength = <2>; + }; +}; + +&tlmm { + fp_enable_init: fp_enable_init { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + }; + + fp_irq_init: fp_irq_init { + mux { + pins = "gpio121"; + function = "gpio"; + }; + + config { + pins = "gpio121"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + fp_disable_init: fp_disable_init { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + }; + + fp_reset_init: fp_reset_init { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + fp_reset_high: fp_reset_high { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <8>; + bias-pull-up; + output-high; + }; + }; + + fp_reset_low: fp_reset_low { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + }; + + fp_id0_up: fp_id0_up { + mux { + pins = "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio91"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + fp_id0_down: fp_id0_down { + mux { + pins = "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio91"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + tri_state_key_active: tri_state_key_active { + mux { + pins = "gpio24","gpio52","gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio24","gpio52","gpio126"; + drive-strength = <2>; + bias-disable; + }; + }; + + tri_state_key_suspend: tri_state_key_suspend { + mux { + pins = "gpio24","gpio52","gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio24","gpio52","gpio126"; + drive-strength = <2>; + bias-disable; + }; + }; + + esd_check_active: esd_check_active { + mux { + pins = "gpio30"; + function = "gpio"; + }; + config { + pins = "gpio30"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + esd_check_suspend: esd_check_suspend { + mux { + pins = "gpio30"; + function = "gpio"; + }; + config { + pins = "gpio30"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; +}; +/*#endif*/ + + +//overlay for camera start +&tlmm { + 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_rest_active: cam_sensor_rear_0_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_rest_suspend: cam_sensor_rear_0_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* VANA */ + 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 { + /* VANA */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_dig_active: cam_sensor_rear_0_dig_active { + /* DIG */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_dig_suspend: cam_sensor_rear_0_dig_suspend { + /* DIG */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_vaf_active: cam_sensor_rear_0_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_vaf_suspend: cam_sensor_rear_0_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + 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_rest_active: cam_sensor_rear_1_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_rest_suspend: cam_sensor_rear_1_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* VANA */ + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* VANA */ + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + 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_rear_1_dig_active: cam_sensor_rear_1_dig_active { + /* DIG */ + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_dig_suspend: cam_sensor_rear_1_dig_suspend { + /* DIG */ + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK2 */ + 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 { + /* MCLK2 */ + 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_rest_active: + cam_sensor_front_0_rest_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_rest_suspend: + cam_sensor_front_0_rest_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: + cam_sensor_front_0_ana_active { + /* VANA */ + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: + cam_sensor_front_0_ana_suspend { + /* VANA */ + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_dig_active: + cam_sensor_front_0_dig_active { + /* DIG */ + mux { + pins = "gpio117"; + function = "gpio"; + }; + + config { + pins = "gpio117"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_dig_suspend: + cam_sensor_front_0_dig_suspend { + /* DIG */ + mux { + pins = "gpio117"; + function = "gpio"; + }; + + config { + pins = "gpio117"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; +}; + +&soc { + led_flash_rear: qcom,camera-flash@0 { + cell-index = <0>; + reg = <0x00 0x00>; + compatible = "qcom,camera-flash"; + flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + rear_1_actuator_regulator: gpio-regulator@1 { + compatible = "regulator-fixed"; + reg = <0x01 0x00>; + regulator-name = "actuator_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <100>; + enable-active-high; + gpio = <&tlmm 77 0>; + vin-supply = <&pmi8998_bob>; + }; + + rear_0_actuator_regulator: gpio-regulator@0 { + compatible = "regulator-fixed"; + reg = <0x00 0x00>; + regulator-name = "actuator_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <100>; + enable-active-high; + gpio = <&tlmm 31 0>; + vin-supply = <&pmi8998_bob>; + }; +}; + +&cam_cci { + qcom,cam-res-mgr { + compatible = "qcom,cam-res-mgr"; + status = "ok"; + }; + + actuator_rear_0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + cci-master = <0>; + cam_vaf-supply = <&rear_0_actuator_regulator>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; + }; + + actuator_rear_1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + cci-master = <1>; + cam_vaf-supply = <&rear_1_actuator_regulator>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2800000>; + rgltr-max-voltage = <2800000>; + rgltr-load-current = <0>; + }; + + ois_rear_0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + cam_vaf-supply = <&pm8998_l21>; + regulator-names = "cam_vaf"; + rgltr-cntrl-support; + rgltr-min-voltage = <2850000>; + rgltr-max-voltage = <2850000>; + rgltr-load-current = <500000>; + gpio-no-mux = <0>; + gpios = <&tlmm 40 0>; + gpio-vaf = <0>; + gpio-req-tbl-num = <0>; + gpio-req-tbl-flags = <0>; + gpio-req-tbl-label = "CAM_OIS_PWD_0"; + cci-master = <0>; + status = "ok"; + }; + + eeprom_rear_0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 3600000 3600000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_rest_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_dig_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_rest_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_dig_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 28 0>, + <&tlmm 27 0>, + <&tlmm 8 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VANA_0", + "CAM_DIG_0"; + sensor-position = <0>; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK0_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + eeprom_rear_1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + cam_vdig-supply = <&pmi8998_bob>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vdig", "cam_vio", "cam_vana", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <3312000 0 3312000 0>; + rgltr-max-voltage = <3600000 0 3600000 0>; + rgltr-load-current = <80000 0 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_rest_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_dig_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_rest_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_dig_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 23 0>, + <&tlmm 78 0>, + <&tlmm 79 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VANA_1", + "CAM_DIG_1"; + + sensor-position = <0>; + sensor-mode = <0>; + cci-master = <1>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK2_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + eeprom_front_0: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 80000 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_rest_active + &cam_sensor_front_0_ana_active + &cam_sensor_front_0_dig_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_rest_suspend + &cam_sensor_front_0_ana_suspend + &cam_sensor_front_0_dig_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 9 0>, + <&tlmm 104 0>, + <&tlmm 117 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VANA_2", + "CAM_DIG_2"; + sensor-position = <1>; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK1_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@0 { + cell-index = <0>; + compatible = "qcom,cam-sensor"; + reg = <0x0>; + csiphy-sd-index = <0>; + sensor-position-roll = <270>; + sensor-position-pitch = <0>; + sensor-position-yaw = <180>; + led-flash-src = <&led_flash_rear>; + actuator-src = <&actuator_rear_0>; + ois-src = <&ois_rear_0>; + eeprom-src = <&eeprom_rear_0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 3600000 3600000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_rest_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_dig_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_rest_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_dig_suspend>; + gpios = <&tlmm 13 0>, + <&tlmm 28 0>, + <&tlmm 27 0>, + <&tlmm 8 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VANA_0", + "CAM_DIG_0"; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK0_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@1 { + cell-index = <1>; + compatible = "qcom,cam-sensor"; + reg = <0x1>; + csiphy-sd-index = <1>; + sensor-position-roll = <90>; + sensor-position-pitch = <0>; + sensor-position-yaw = <180>; + led-flash-src = <&led_flash_rear>; + actuator-src = <&actuator_rear_1>; + eeprom-src = <&eeprom_rear_1>; + cam_vdig-supply = <&pmi8998_bob>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vdig", "cam_vio", "cam_vana", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <3312000 0 3312000 0>; + rgltr-max-voltage = <3600000 0 3600000 0>; + rgltr-load-current = <80000 0 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_rest_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_dig_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_rest_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_dig_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 23 0>, + <&tlmm 78 0>, + <&tlmm 79 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VANA_1", + "CAM_DIG_1"; + sensor-mode = <0>; + cci-master = <1>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK2_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + + qcom,cam-sensor@2 { + cell-index = <2>; + compatible = "qcom,cam-sensor"; + reg = <0x02>; + csiphy-sd-index = <2>; + sensor-position-roll = <270>; + sensor-position-pitch = <0>; + sensor-position-yaw = <0>; + eeprom-src = <&eeprom_front_0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pmi8998_bob>; + cam_clk-supply = <&titan_top_gdsc>; + regulator-names = "cam_vio", "cam_vana", "cam_vdig", + "cam_clk"; + rgltr-cntrl-support; + rgltr-min-voltage = <0 3312000 3312000 0>; + rgltr-max-voltage = <0 3600000 3600000 0>; + rgltr-load-current = <0 80000 80000 0>; + gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_rest_active + &cam_sensor_front_0_ana_active + &cam_sensor_front_0_dig_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_rest_suspend + &cam_sensor_front_0_ana_suspend + &cam_sensor_front_0_dig_suspend>; + gpios = <&tlmm 14 0>, + <&tlmm 9 0>, + <&tlmm 104 0>, + <&tlmm 117 0>; + gpio-reset = <1>; + gpio-vana = <2>; + gpio-vdig = <3>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 0 0 0>; + gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VANA_2", + "CAM_DIG_2"; + sensor-mode = <0>; + cci-master = <0>; + status = "ok"; + clocks = <&clock_camcc CAM_CC_MCLK1_CLK>; + clock-names = "cam_clk"; + clock-cntl-level = "turbo"; + clock-rates = <24000000>; + }; + +}; + + + +//overlay for camera end. + +&tlmm { + //add by gu qicai begin + ts_mux { + ts_active: ts_active { + mux { + pins = "gpio99", "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio99", "gpio125"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ts_reset_suspend: ts_reset_suspend { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + ts_int_suspend: ts_int_suspend { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + //add by gu qicai end +}; + +/* OnePlus add thermistor, by rio.zhao*/ +&pm8998_vadc { + pa1_therm { + label = "pa1_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; +}; + +&pm8998_adc_tm { + io-channels = <&pm8998_vadc ADC_VPH_PWR>, + <&pm8998_vadc ADC_XO_THERM_PU2>, + <&pm8998_vadc ADC_AMUX_THM1_PU2>, + <&pm8998_vadc ADC_AMUX_THM3_PU2>, + <&pm8998_vadc ADC_AMUX_THM5_PU2>, + <&pm8998_vadc ADC_AMUX_THM4_PU2>; + pa1_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&thermal_zones { + pa1-therml-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm ADC_AMUX_THM4_PU2>; + thermal-governor = "user_space"; + wake-capable-sensor; + + trips { + active-config0 { + temperature = <65000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; +}; +&spmi_bus { + qcom,pm8998@0 { + qcom,power-on@800 { + qcom,pon_1 { + qcom,support-reset = <0>; + }; + }; + }; +}; + +/* add for rf cable support ,shankai@bsp*/ + +&tlmm { + oem_rf_cable_mux { + oem_rf_cable_active: oem_rf_cable_active { + mux { + pins = "gpio32","gpio128"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio128"; + drive-strength = <2>; + bias-disable; + }; + }; + oem_rf_cable_suspend: oem_rf_cable_suspend { + mux { + pins = "gpio32","gpio128"; + function = "gpio"; + }; + config { + pins = "gpio32","gpio128"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + oem_aboard_mux { + oem_aboard_mux_active: oem_aboard_mux_active { + mux { + pins = "gpio33","gpio34"; + function = "gpio"; + }; + config { + pins = "gpio33","gpio34"; + drive-strength = <2>; + bias-disable; + }; + }; + oem_aboard_mux_suspend: oem_aboard_mux_suspend { + mux { + pins = "gpio33","gpio34"; + function = "gpio"; + }; + config { + pins = "gpio33","gpio34"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + +}; + +&soc { + oem_rf_cable { + compatible = "oem,rf_cable"; + interrupt-parent = <&tlmm>; + rf,cable-gpio-0 = <&tlmm 32 0>; + rf,cable-gpio-1 = <&tlmm 128 0>; + pinctrl-names = "oem_rf_cable_active", "oem_rf_cable_suspend"; + pinctrl-0 = <&oem_rf_cable_active >; + pinctrl-1 = <&oem_rf_cable_suspend >; + }; + + oem_aboard_check { + compatible = "oem,aboard"; + interrupt-parent = <&tlmm>; + oem,aboard-gpio-0 = <&tlmm 33 0>; + oem,aboard-gpio-1 = <&tlmm 34 0>; + pinctrl-names = "oem_aboard_active", "oem_aboard_suspend"; + pinctrl-0 = <&oem_aboard_mux_active >; + pinctrl-1 = <&oem_aboard_mux_suspend >; + }; + +}; +/*shankai add for rf cable end shankai@bsp*/ + +&spss_utils { + status = "disabled"; +}; + +&qupv3_se0_spi { + status = "ok"; + qcom,disable-autosuspend; + + ese@0 { + compatible = "nxp,p61"; + reg = <0>; + spi-max-frequency = <8000000>; + nxp,nfcc = "1-0028"; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi new file mode 100644 index 000000000000..a0e73b2c6c2d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +qcom,ascent_3450mah { + /* Ascent_wConn_Aging_3450mAh_averaged_MasterSlave_Jul11th2017 */ + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <3450>; + qcom,batt-id-kohm = <60>; + qcom,jeita-fcc-ranges = <0 100 1725000 + 101 400 3450000 + 401 450 2760000>; + qcom,jeita-fv-ranges = <0 100 4250000 + 101 400 4350000 + 401 450 4250000>; + qcom,step-chg-ranges = <3600000 4200000 3450000 + 4201000 4300000 2760000 + 4301000 4350000 2070000>; + qcom,battery-beta = <3435>; + qcom,battery-type = "ascent_3450mah_averaged_masterslave_oct30th2017"; + qcom,checksum = <0xAAE2>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + 8F 1F 94 05 + 73 0A 4A 06 + 27 1D 21 EA + 16 0A 3A 0C + 07 18 97 22 + A5 3C EC 4A + 5C 00 00 00 + 10 00 00 00 + 00 00 43 C5 + 92 BC 89 BB + 11 00 08 00 + 69 DA AD 07 + 4B FD 19 FA + 7E 01 49 13 + EB F3 78 3B + 24 06 09 20 + 27 00 14 00 + 7E 1F F2 05 + 19 0A AB 06 + 6C 1D B9 07 + 1A 12 FF 1D + 6F 18 EB 22 + B9 45 6F 52 + 55 00 00 00 + 0E 00 00 00 + 00 00 33 CC + 72 CA B3 C4 + 0F 00 00 00 + 93 00 AD 07 + 8D FD F6 00 + 6F E3 44 0B + AB FC F9 1B + C3 33 CC FF + 07 10 00 00 + A4 0D 99 45 + 0F 00 40 00 + A4 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/arm64/boot/dts/qcom/fg-gen3-batterydata-demo-6000mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-demo-6000mah.dtsi new file mode 100644 index 000000000000..c776a100b1de --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-demo-6000mah.dtsi @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +qcom,demo_6000mah { + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <6000>; + qcom,batt-id-kohm = <75>; + qcom,battery-beta = <3435>; + qcom,battery-type = "Demo_battery_6000mah"; + qcom,fg-profile-data = [ + 2C 1F 3F FC + E9 03 A1 FD + 58 1D FD F5 + 27 12 2C 14 + 3F 18 FF 22 + 9B 45 A3 52 + 55 00 00 00 + 0E 00 00 00 + 00 00 1C AC + F7 CD 71 B5 + 1A 00 0C 00 + 3C EB 54 E4 + EC 05 7F FA + 76 05 F5 02 + CA F3 82 3A + 2A 09 40 40 + 07 00 05 00 + 58 1F 42 06 + 85 03 35 F4 + 4D 1D 37 F2 + 23 0A 79 15 + B7 18 32 23 + 26 45 72 53 + 55 00 00 00 + 0D 00 00 00 + 00 00 13 CC + 03 00 98 BD + 16 00 00 00 + 3C EB 54 E4 + 9F FC A3 F3 + 0F FC DF FA + FF E5 A9 23 + CB 33 08 33 + 07 10 00 00 + 81 0D 99 45 + 16 00 19 00 + 75 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/arm64/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi new file mode 100644 index 000000000000..600dbbf28e6a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +qcom,itech_3000mah { + /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jan10th2017*/ + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <2000>; + qcom,batt-id-kohm = <100>; + qcom,battery-beta = <3435>; + qcom,battery-type = "itech_b00826lf_3000mah_ver1660_jan10th2017"; + qcom,checksum = <0xFB8F>; + qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,fg-profile-data = [ + A4 1F 6E 05 + 9C 0A 2B FC + 32 1D 23 E5 + 60 0B 1B 15 + AD 17 8C 22 + EA 3C 89 4A + 5B 00 00 00 + 12 00 00 00 + 00 00 62 C2 + 0C CD D8 C2 + 19 00 08 00 + 85 EA C7 EC + E2 05 2F 01 + 9B F5 12 12 + 5E 05 88 3B + 22 06 09 20 + 27 00 14 00 + 7D 1F DD 05 + 3F 0A E5 FC + 72 1D E3 F5 + 6F 12 C0 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 + 59 06 61 03 + D9 FC 75 1B + B3 33 CC FF + 07 10 00 00 + 3E 0B 99 45 + 14 00 40 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/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi new file mode 100644 index 000000000000..4ee27426ee64 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +#include +#include + +&soc { + kgsl_smmu: arm,smmu-kgsl@5040000 { + status = "ok"; + compatible = "qcom,smmu-v2"; + reg = <0x5040000 0x10000>; + #iommu-cells = <1>; + qcom,dynamic; + qcom,use-3-lvl-tables; + qcom,disable-atos; + #global-interrupts = <2>; + qcom,regulator-names = "vdd"; + vdd-supply = <&gpu_cx_gdsc>; + interrupts = , + , + , + , + , + , + , + , + , + ; + clock-names = "gcc_gpu_memnoc_gfx_clk"; + clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>; + attach-impl-defs = + <0x6000 0x2378>, + <0x6060 0x1055>, + <0x678c 0x8>, + <0x6794 0x28>, + <0x6800 0x6>, + <0x6900 0x3ff>, + <0x6924 0x204>, + <0x6928 0x11000>, + <0x6930 0x800>, + <0x6960 0xffffffff>, + <0x6b64 0x1a5551>, + <0x6b68 0x9a82a382>; + }; + + apps_smmu: apps-smmu@0x15000000 { + compatible = "qcom,qsmmu-v500"; + reg = <0x15000000 0x80000>, + <0x150c2000 0x20>; + reg-names = "base", "tcu-base"; + #iommu-cells = <2>; + qcom,skip-init; + qcom,use-3-lvl-tables; + qcom,no-asid-retention; + qcom,disable-atos; + #global-interrupts = <1>; + #size-cells = <1>; + #address-cells = <1>; + ranges; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + + anoc_1_tbu: anoc_1_tbu@0x150c5000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150c5000 0x1000>, + <0x150c2200 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x0 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu1_gdsc>; + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + anoc_2_tbu: anoc_2_tbu@0x150c9000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150c9000 0x1000>, + <0x150c2208 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x400 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu2_gdsc>; + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + mnoc_hf_0_tbu: mnoc_hf_0_tbu@0x150cd000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150cd000 0x1000>, + <0x150c2210 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x800 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc>; + qcom,msm-bus,name = "mnoc_hf_0_tbu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + mnoc_hf_1_tbu: mnoc_hf_1_tbu@0x150d1000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d1000 0x1000>, + <0x150c2218 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0xc00 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc>; + qcom,msm-bus,name = "mnoc_hf_1_tbu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + mnoc_sf_0_tbu: mnoc_sf_0_tbu@0x150d5000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d5000 0x1000>, + <0x150c2220 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1000 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_sf_gdsc>; + qcom,msm-bus,name = "mnoc_sf_0_tbu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + compute_dsp_tbu: compute_dsp_tbu@0x150d9000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d9000 0x1000>, + <0x150c2228 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1400 0x400>; + /* No GDSC */ + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + adsp_tbu: adsp_tbu@0x150dd000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150dd000 0x1000>, + <0x150c2230 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1800 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc>; + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + + anoc_1_pcie_tbu: anoc_1_pcie_tbu@0x150e1000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150e1000 0x1000>, + <0x150c2238 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1c00 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc>; + clock-names = "gcc_aggre_noc_pcie_tbu_clk"; + clocks = <&clock_gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + qcom,msm-bus,name = "apps_smmu"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + <0 0>, + , + , + <0 1000>; + }; + }; + + kgsl_iommu_test_device { + status = "disabled"; + compatible = "iommu-debug-test"; + /* + * 0x7 isn't a valid sid, but should pass the sid sanity check. + * We just need _something_ here to get this node recognized by + * the SMMU driver. Our test uses ATOS, which doesn't use SIDs + * anyways, so using a dummy value is ok. + */ + iommus = <&kgsl_smmu 0x7>; + qcom,iommu-dma = "disabled"; + }; + + apps_iommu_test_device { + compatible = "iommu-debug-test"; + /* + * This SID belongs to TSIF. We can't use a fake SID for + * the apps_smmu device. + */ + iommus = <&apps_smmu 0x20 0>; + qcom,iommu-dma = "disabled"; + }; + + apps_iommu_coherent_test_device { + compatible = "iommu-debug-test"; + /* + * This SID belongs to TSIF. We can't use a fake SID for + * the apps_smmu device. + */ + iommus = <&apps_smmu 0x20 0>; + qcom,iommu-dma = "disabled"; + dma-coherent; + }; +}; + +&apps_smmu { + qcom,actlr = <0x0880 0x8 0x103>, + <0x0881 0x8 0x103>, + <0x0c80 0x8 0x103>, + <0x0c81 0x8 0x103>, + <0x1090 0x0 0x103>, + <0x1091 0x0 0x103>, + <0x10a0 0x8 0x103>, + <0x10b0 0x0 0x103>, + <0x10a1 0x8 0x103>, + <0x10a3 0x8 0x103>, + <0x10a4 0x8 0x103>, + <0x10b4 0x0 0x103>, + <0x10a5 0x8 0x103>; + qcom,mmu500-errata-1 = <0x800 0x3ff>, + <0xc00 0x3ff>; + hwlocks = <&tcsr_mutex 6>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi new file mode 100644 index 000000000000..4d3b8d9681fb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi @@ -0,0 +1,593 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + */ + +&soc { + + pcm0: qcom,msm-pcm { + compatible = "qcom,msm-pcm-dsp"; + qcom,msm-pcm-dsp-id = <0>; + }; + + routing: qcom,msm-pcm-routing { + compatible = "qcom,msm-pcm-routing"; + }; + + compr: qcom,msm-compr-dsp { + compatible = "qcom,msm-compr-dsp"; + }; + + pcm1: qcom,msm-pcm-low-latency { + compatible = "qcom,msm-pcm-dsp"; + qcom,msm-pcm-dsp-id = <1>; + qcom,msm-pcm-low-latency; + qcom,latency-level = "regular"; + }; + + pcm2: qcom,msm-ultra-low-latency { + compatible = "qcom,msm-pcm-dsp"; + qcom,msm-pcm-dsp-id = <2>; + qcom,msm-pcm-low-latency; + qcom,latency-level = "ultra"; + }; + + pcm_noirq: qcom,msm-pcm-dsp-noirq { + compatible = "qcom,msm-pcm-dsp-noirq"; + qcom,msm-pcm-low-latency; + qcom,latency-level = "ultra"; + }; + + compress: qcom,msm-compress-dsp { + compatible = "qcom,msm-compress-dsp"; + }; + + voip: qcom,msm-voip-dsp { + compatible = "qcom,msm-voip-dsp"; + }; + + voice: qcom,msm-pcm-voice { + compatible = "qcom,msm-pcm-voice"; + qcom,destroy-cvd; + }; + + stub_codec: qcom,msm-stub-codec { + compatible = "qcom,msm-stub-codec"; + }; + + qcom,msm-dai-fe { + compatible = "qcom,msm-dai-fe"; + }; + + afe: qcom,msm-pcm-afe { + compatible = "qcom,msm-pcm-afe"; + }; + + dai_hdmi: qcom,msm-dai-q6-hdmi { + compatible = "qcom,msm-dai-q6-hdmi"; + qcom,msm-dai-q6-dev-id = <8>; + }; + + dai_dp: qcom,msm-dai-q6-dp { + compatible = "qcom,msm-dai-q6-hdmi"; + qcom,msm-dai-q6-dev-id = <0>; + }; + + loopback: qcom,msm-pcm-loopback { + compatible = "qcom,msm-pcm-loopback"; + }; + + msm_dai_mi2s: qcom,msm-dai-mi2s { + compatible = "qcom,msm-dai-mi2s"; + dai_mi2s0: qcom,msm-dai-q6-mi2s-prim { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <0>; + qcom,msm-mi2s-rx-lines = <3>; + qcom,msm-mi2s-tx-lines = <0>; + }; + + dai_mi2s1: qcom,msm-dai-q6-mi2s-sec { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <1>; + qcom,msm-mi2s-rx-lines = <1>; + qcom,msm-mi2s-tx-lines = <0>; + }; + + dai_mi2s2: qcom,msm-dai-q6-mi2s-tert { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <2>; + qcom,msm-mi2s-rx-lines = <0>; + qcom,msm-mi2s-tx-lines = <3>; + }; + + dai_mi2s3: qcom,msm-dai-q6-mi2s-quat { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <3>; + qcom,msm-mi2s-rx-lines = <1>; + qcom,msm-mi2s-tx-lines = <2>; + }; + + dai_mi2s4: qcom,msm-dai-q6-mi2s-quin { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <4>; + qcom,msm-mi2s-rx-lines = <1>; + qcom,msm-mi2s-tx-lines = <2>; + }; + + dai_mi2s5: qcom,msm-dai-q6-mi2s-senary { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <6>; + qcom,msm-mi2s-rx-lines = <0>; + qcom,msm-mi2s-tx-lines = <3>; + }; + }; + + lsm: qcom,msm-lsm-client { + compatible = "qcom,msm-lsm-client"; + }; + + qcom,msm-dai-q6 { + compatible = "qcom,msm-dai-q6"; + sb_0_rx: qcom,msm-dai-q6-sb-0-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16384>; + }; + + sb_0_tx: qcom,msm-dai-q6-sb-0-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16385>; + }; + + sb_1_rx: qcom,msm-dai-q6-sb-1-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16386>; + }; + + sb_1_tx: qcom,msm-dai-q6-sb-1-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16387>; + }; + + sb_2_rx: qcom,msm-dai-q6-sb-2-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16388>; + }; + + sb_2_tx: qcom,msm-dai-q6-sb-2-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16389>; + }; + + + sb_3_rx: qcom,msm-dai-q6-sb-3-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16390>; + }; + + sb_3_tx: qcom,msm-dai-q6-sb-3-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16391>; + }; + + sb_4_rx: qcom,msm-dai-q6-sb-4-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16392>; + }; + + sb_4_tx: qcom,msm-dai-q6-sb-4-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16393>; + }; + + sb_5_tx: qcom,msm-dai-q6-sb-5-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16395>; + }; + + sb_5_rx: qcom,msm-dai-q6-sb-5-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16394>; + }; + + sb_6_rx: qcom,msm-dai-q6-sb-6-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16396>; + }; + + sb_7_rx: qcom,msm-dai-q6-sb-7-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16398>; + qcom,msm-dai-q6-slim-dev-id = <1>; + }; + + sb_7_tx: qcom,msm-dai-q6-sb-7-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16399>; + qcom,msm-dai-q6-slim-dev-id = <1>; + }; + + sb_8_rx: qcom,msm-dai-q6-sb-8-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16400>; + qcom,msm-dai-q6-slim-dev-id = <1>; + }; + + sb_8_tx: qcom,msm-dai-q6-sb-8-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16401>; + qcom,msm-dai-q6-slim-dev-id = <1>; + }; + + bt_sco_rx: qcom,msm-dai-q6-bt-sco-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12288>; + }; + + bt_sco_tx: qcom,msm-dai-q6-bt-sco-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12289>; + }; + + int_fm_rx: qcom,msm-dai-q6-int-fm-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12292>; + }; + + int_fm_tx: qcom,msm-dai-q6-int-fm-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <12293>; + }; + + afe_pcm_rx: qcom,msm-dai-q6-be-afe-pcm-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <224>; + }; + + afe_pcm_tx: qcom,msm-dai-q6-be-afe-pcm-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <225>; + }; + + afe_proxy_rx: qcom,msm-dai-q6-afe-proxy-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <241>; + }; + + afe_proxy_tx: qcom,msm-dai-q6-afe-proxy-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <240>; + }; + + afe_loopback_tx: qcom,msm-dai-q6-afe-loopback-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <24577>; + }; + + incall_record_rx: qcom,msm-dai-q6-incall-record-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32771>; + }; + + incall_record_tx: qcom,msm-dai-q6-incall-record-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32772>; + }; + + incall_music_rx: qcom,msm-dai-q6-incall-music-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32773>; + }; + + incall_music_2_rx: qcom,msm-dai-q6-incall-music-2-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <32770>; + }; + proxy_rx: qcom,msm-dai-q6-proxy-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <8194>; + }; + + proxy_tx: qcom,msm-dai-q6-proxy-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <8195>; + }; + + usb_audio_rx: qcom,msm-dai-q6-usb-audio-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <28672>; + }; + + usb_audio_tx: qcom,msm-dai-q6-usb-audio-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <28673>; + }; + }; + + hostless: qcom,msm-pcm-hostless { + compatible = "qcom,msm-pcm-hostless"; + }; + + audio_apr: qcom,msm-audio-apr { + compatible = "qcom,msm-audio-apr"; + qcom,subsys-name = "apr_adsp"; + + msm_audio_ion: qcom,msm-audio-ion { + compatible = "qcom,msm-audio-ion"; + qcom,smmu-version = <2>; + qcom,smmu-enabled; + iommus = <&apps_smmu 0x1821 0x0>; + qcom,iommu-dma-addr-pool = <0x10000000 0x10000000>; + }; + }; + + dai_pri_auxpcm: qcom,msm-pri-auxpcm { + compatible = "qcom,msm-auxpcm-dev"; + qcom,msm-cpudai-auxpcm-mode = <0>, <0>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "primary"; + qcom,msm-cpudai-afe-clk-ver = <2>; + }; + + dai_sec_auxpcm: qcom,msm-sec-auxpcm { + compatible = "qcom,msm-auxpcm-dev"; + qcom,msm-cpudai-auxpcm-mode = <0>, <0>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "secondary"; + qcom,msm-cpudai-afe-clk-ver = <2>; + }; + + dai_tert_auxpcm: qcom,msm-tert-auxpcm { + compatible = "qcom,msm-auxpcm-dev"; + qcom,msm-cpudai-auxpcm-mode = <0>, <0>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "tertiary"; + qcom,msm-cpudai-afe-clk-ver = <2>; + }; + + dai_quat_auxpcm: qcom,msm-quat-auxpcm { + compatible = "qcom,msm-auxpcm-dev"; + qcom,msm-cpudai-auxpcm-mode = <0>, <0>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "quaternary"; + qcom,msm-cpudai-afe-clk-ver = <2>; + }; + + dai_quin_auxpcm: qcom,msm-quin-auxpcm { + compatible = "qcom,msm-auxpcm-dev"; + qcom,msm-cpudai-auxpcm-mode = <0>, <0>; + qcom,msm-cpudai-auxpcm-sync = <1>, <1>; + qcom,msm-cpudai-auxpcm-frame = <5>, <4>; + qcom,msm-cpudai-auxpcm-quant = <2>, <2>; + qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>; + qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>; + qcom,msm-cpudai-auxpcm-data = <0>, <0>; + qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>; + qcom,msm-auxpcm-interface = "quinary"; + qcom,msm-cpudai-afe-clk-ver = <2>; + }; + + hdmi_dba: qcom,msm-hdmi-dba-codec-rx { + compatible = "qcom,msm-hdmi-dba-codec-rx"; + qcom,dba-bridge-chip = "adv7533"; + }; + + qcom,msm-adsp-loader { + status = "ok"; + compatible = "qcom,adsp-loader"; + qcom,adsp-state = <0>; + }; + + qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36864>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36865>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-sec-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37136>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36880>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_sec_tdm_rx_0: qcom,msm-dai-q6-tdm-sec-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36880>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-sec-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37137>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36881>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36881>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-tert-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37152>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36896>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_tert_tdm_rx_0: qcom,msm-dai-q6-tdm-tert-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36896>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-tert-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37153>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36897 >; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_tert_tdm_tx_0: qcom,msm-dai-q6-tdm-tert-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36897 >; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + msm_dai_tdm_quat_rx: qcom,msm-dai-tdm-quat-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37168>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36912>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36912>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-quat-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37169>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36913 >; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_quat_tdm_tx_0: qcom,msm-dai-q6-tdm-quat-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36913 >; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-quin-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37184>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36928>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_quin_tdm_rx_0: qcom,msm-dai-q6-tdm-quin-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36928>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-quin-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37185>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36929>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + qcom,msm-cpudai-tdm-clk-internal = <1>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + dai_quin_tdm_tx_0: qcom,msm-dai-q6-tdm-quin-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36929>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi new file mode 100644 index 000000000000..cfc500f1050d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&soc { + /* GDSCs in Global CC */ + pcie_0_gdsc: qcom,gdsc@0x16b004 { + compatible = "qcom,gdsc"; + regulator-name = "pcie_0_gdsc"; + reg = <0x16b004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + pcie_1_gdsc: qcom,gdsc@0x18d004 { + compatible = "qcom,gdsc"; + regulator-name = "pcie_1_gdsc"; + reg = <0x18d004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ufs_card_gdsc: qcom,gdsc@0x175004 { + compatible = "qcom,gdsc"; + regulator-name = "ufs_card_gdsc"; + reg = <0x175004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ufs_phy_gdsc: qcom,gdsc@0x177004 { + compatible = "qcom,gdsc"; + regulator-name = "ufs_phy_gdsc"; + reg = <0x177004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + usb30_prim_gdsc: qcom,gdsc@0x10f004 { + compatible = "qcom,gdsc"; + regulator-name = "usb30_prim_gdsc"; + reg = <0x10f004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + usb30_sec_gdsc: qcom,gdsc@0x110004 { + compatible = "qcom,gdsc"; + regulator-name = "usb30_sec_gdsc"; + reg = <0x110004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc: qcom,gdsc@0x17d030 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc"; + reg = <0x17d030 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc: qcom,gdsc@0x17d03c { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc"; + reg = <0x17d03c 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_aggre_noc_mmu_tbu1_gdsc: qcom,gdsc@0x17d034 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_aggre_noc_mmu_tbu1_gdsc"; + reg = <0x17d034 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_aggre_noc_mmu_tbu2_gdsc: qcom,gdsc@0x17d038 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_aggre_noc_mmu_tbu2_gdsc"; + reg = <0x17d038 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc: qcom,gdsc@0x17d040 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc"; + reg = <0x17d040 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc: qcom,gdsc@0x17d048 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc"; + reg = <0x17d048 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + hlos1_vote_mmnoc_mmu_tbu_sf_gdsc: qcom,gdsc@0x17d044 { + compatible = "qcom,gdsc"; + regulator-name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc"; + reg = <0x17d044 0x4>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + status = "disabled"; + }; + + /* GDSCs in Camera CC */ + bps_gdsc: qcom,gdsc@0xad06004 { + compatible = "qcom,gdsc"; + regulator-name = "bps_gdsc"; + reg = <0xad06004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ife_0_gdsc: qcom,gdsc@0xad09004 { + compatible = "qcom,gdsc"; + regulator-name = "ife_0_gdsc"; + reg = <0xad09004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ife_1_gdsc: qcom,gdsc@0xad0a004 { + compatible = "qcom,gdsc"; + regulator-name = "ife_1_gdsc"; + reg = <0xad0a004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ipe_0_gdsc: qcom,gdsc@0xad07004 { + compatible = "qcom,gdsc"; + regulator-name = "ipe_0_gdsc"; + reg = <0xad07004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + ipe_1_gdsc: qcom,gdsc@0xad08004 { + compatible = "qcom,gdsc"; + regulator-name = "ipe_1_gdsc"; + reg = <0xad08004 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + titan_top_gdsc: qcom,gdsc@0xad0b134 { + compatible = "qcom,gdsc"; + regulator-name = "titan_top_gdsc"; + reg = <0xad0b134 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + /* GDSCs in Display CC */ + mdss_core_gdsc: qcom,gdsc@0xaf03000 { + compatible = "qcom,gdsc"; + regulator-name = "mdss_core_gdsc"; + reg = <0xaf03000 0x4>; + qcom,poll-cfg-gdscr; + qcom,support-hw-trigger; + status = "disabled"; + proxy-supply = <&mdss_core_gdsc>; + qcom,proxy-consumer-enable; + }; + + /* GDSCs in Graphics CC */ + gpu_cx_hw_ctrl: syscon@0x5091540 { + compatible = "syscon"; + reg = <0x5091540 0x4>; + }; + + gpu_cx_gdsc: qcom,gdsc@0x509106c { + compatible = "qcom,gdsc"; + regulator-name = "gpu_cx_gdsc"; + reg = <0x509106c 0x4>; + hw-ctrl-addr = <&gpu_cx_hw_ctrl>; + qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; + qcom,clk-dis-wait-val = <8>; + status = "disabled"; + }; + + gpu_gx_gdsc: qcom,gdsc@0x509100c { + compatible = "qcom,gdsc"; + regulator-name = "gpu_gx_gdsc"; + reg = <0x509100c 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + /* GDSCs in Video CC */ + vcodec0_gdsc: qcom,gdsc@0xab00874 { + compatible = "qcom,gdsc"; + regulator-name = "vcodec0_gdsc"; + reg = <0xab00874 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + vcodec1_gdsc: qcom,gdsc@0xab008b4 { + compatible = "qcom,gdsc"; + regulator-name = "vcodec1_gdsc"; + reg = <0xab008b4 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; + + venus_gdsc: qcom,gdsc@0xab00814 { + compatible = "qcom,gdsc"; + regulator-name = "venus_gdsc"; + reg = <0xab00814 0x4>; + qcom,poll-cfg-gdscr; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-rdbg.dtsi b/arch/arm64/boot/dts/qcom/msm-rdbg.dtsi new file mode 100644 index 000000000000..e0d7b571efc1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-rdbg.dtsi @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +&soc { + smp2pgpio_rdbg_2_in: qcom,smp2pgpio-rdbg-2-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <2>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_2_in { + compatible = "qcom,smp2pgpio_client_rdbg_2_in"; + gpios = <&smp2pgpio_rdbg_2_in 0 0>; + }; + + smp2pgpio_rdbg_2_out: qcom,smp2pgpio-rdbg-2-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <2>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_2_out { + compatible = "qcom,smp2pgpio_client_rdbg_2_out"; + gpios = <&smp2pgpio_rdbg_2_out 0 0>; + }; + + smp2pgpio_rdbg_1_in: qcom,smp2pgpio-rdbg-1-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <1>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_1_in { + compatible = "qcom,smp2pgpio_client_rdbg_1_in"; + gpios = <&smp2pgpio_rdbg_1_in 0 0>; + }; + + smp2pgpio_rdbg_1_out: qcom,smp2pgpio-rdbg-1-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <1>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_1_out { + compatible = "qcom,smp2pgpio_client_rdbg_1_out"; + gpios = <&smp2pgpio_rdbg_1_out 0 0>; + }; + + smp2pgpio_rdbg_5_in: qcom,smp2pgpio-rdbg-5-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <5>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_5_in { + compatible = "qcom,smp2pgpio_client_rdbg_5_in"; + gpios = <&smp2pgpio_rdbg_5_in 0 0>; + }; + + smp2pgpio_rdbg_5_out: qcom,smp2pgpio-rdbg-5-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "rdbg"; + qcom,remote-pid = <5>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + qcom,smp2pgpio_client_rdbg_5_out { + compatible = "qcom,smp2pgpio_client_rdbg_5_out"; + gpios = <&smp2pgpio_rdbg_5_out 0 0>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-wsa881x.dtsi b/arch/arm64/boot/dts/qcom/msm-wsa881x.dtsi new file mode 100644 index 000000000000..d638c006d043 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-wsa881x.dtsi @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&slim_aud { + tavil_codec { + swr_master { + compatible = "qcom,swr-wcd"; + #address-cells = <2>; + #size-cells = <0>; + + wsa881x_0211: wsa881x@20170211 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x20170211>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>; + }; + + wsa881x_0212: wsa881x@20170212 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x20170212>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>; + }; + + wsa881x_0213: wsa881x@21170213 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x21170213>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>; + }; + + wsa881x_0214: wsa881x@21170214 { + compatible = "qcom,wsa881x"; + reg = <0x00 0x21170214>; + qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pm8005.dtsi b/arch/arm64/boot/dts/qcom/pm8005.dtsi index 4d5aca3eeb69..ca974db9053c 100644 --- a/arch/arm64/boot/dts/qcom/pm8005.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8005.dtsi @@ -1,33 +1,104 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* Copyright 2018 Google LLC. */ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ -#include #include +#include +#include +#include +#include &spmi_bus { - pm8005_lsid0: pmic@4 { - compatible = "qcom,pm8005", "qcom,spmi-pmic"; + qcom,pm8005@4 { + compatible = "qcom,spmi-pmic"; reg = <0x4 SPMI_USID>; - #address-cells = <1>; + #address-cells = <2>; #size-cells = <0>; - pm8005_gpio: gpios@c000 { - compatible = "qcom,pm8005-gpio", "qcom,spmi-gpio"; - reg = <0xc000>; + pm8005_revid: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + pm8005_tz: qcom,temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400 0x100>; + interrupts = <0x4 0x24 0x0 IRQ_TYPE_EDGE_RISING>; + label = "pm8005_tz"; + #thermal-sensor-cells = <0>; + qcom,temperature-threshold-set = <1>; + }; + + pm8005_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0x400>; + interrupts = <0x4 0xc0 0 IRQ_TYPE_NONE>, + <0x4 0xc1 0 IRQ_TYPE_NONE>; + interrupt-names = "pm8005_gpio1", "pm8005_gpio2"; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>; + qcom,gpios-disallowed = <3 4>; }; - }; - pm8005_lsid1: pmic@5 { - compatible = "qcom,pm8005", "qcom,spmi-pmic"; + qcom,pm8005@5 { + compatible ="qcom,spmi-pmic"; reg = <0x5 SPMI_USID>; - #address-cells = <1>; + #address-cells = <2>; #size-cells = <0>; + + regulator@1400 { + compatible = "qcom,qpnp-regulator"; + reg = <0x1400 0x100>; + regulator-name = "pm8005_s1"; + status = "disabled"; + }; + + regulator@1700 { + compatible = "qcom,qpnp-regulator"; + reg = <0x1700 0x100>; + regulator-name = "pm8005_s2"; + status = "disabled"; + }; + + regulator@1a00 { + compatible = "qcom,qpnp-regulator"; + reg = <0x1a00 0x100>; + regulator-name = "pm8005_s3"; + status = "disabled"; + }; + + regulator@1d00 { + compatible = "qcom,qpnp-regulator"; + reg = <0x1d00 0x100>; + regulator-name = "pm8005_s4"; + status = "disabled"; + }; + }; +}; + +&thermal_zones { + pm8005_tz { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8005_tz>; + trips { + pm8005-trip0 { + temperature = <105000>; + hysteresis = <0>; + type = "passive"; + }; + pm8005-trip1 { + temperature = <125000>; + hysteresis = <0>; + type = "passive"; + }; + pm8005-trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "passive"; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi index 92bed1e7d4bb..7d7168eeee2d 100644 --- a/arch/arm64/boot/dts/qcom/pm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -1,55 +1,207 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* Copyright 2018 Google LLC. */ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. + */ -#include +#include +#include +#include #include +#include +#include &spmi_bus { - pm8998_lsid0: pmic@0 { - compatible = "qcom,pm8998", "qcom,spmi-pmic"; + qcom,pm8998@0 { + compatible = "qcom,spmi-pmic"; reg = <0x0 SPMI_USID>; - #address-cells = <1>; + #address-cells = <2>; #size-cells = <0>; - pm8998_gpio: gpios@c000 { - compatible = "qcom,pm8998-gpio", "qcom,spmi-gpio"; - reg = <0xc000>; + pm8998_revid: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + qcom,power-on@800 { + compatible = "qcom,qpnp-power-on"; + reg = <0x800 0x100>; + interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>, + <0x0 0x8 0x1 IRQ_TYPE_NONE>, + <0x0 0x8 0x4 IRQ_TYPE_NONE>, + <0x0 0x8 0x5 IRQ_TYPE_NONE>; + interrupt-names = "kpdpwr", "resin", + "resin-bark", "kpdpwr-resin-bark"; + qcom,pon-dbc-delay = <15625>; + qcom,kpdpwr-sw-debounce; + qcom,system-reset; + qcom,store-hard-reset-reason; + + qcom,pon_1 { + qcom,pon-type = ; + qcom,pull-up = <1>; + linux,code = ; + }; + + qcom,pon_2 { + qcom,pon-type = ; + qcom,pull-up = <1>; + linux,code = ; + }; + + qcom,pon_3 { + qcom,pon-type = ; + qcom,support-reset = <1>; + qcom,pull-up = <1>; + qcom,s1-timer = <6720>; + qcom,s2-timer = <2000>; + qcom,s2-type = ; + qcom,use-bark; + }; + }; + + pm8998_tz: qcom,temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400 0x100>; + interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>; + io-channels = <&pm8998_vadc ADC_DIE_TEMP>; + io-channel-names = "thermal"; + #thermal-sensor-cells = <0>; + qcom,temperature-threshold-set = <1>; + }; + + pm8998_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0x1a00>; + interrupts = <0x0 0xc0 0 IRQ_TYPE_NONE>, + <0x0 0xc1 0 IRQ_TYPE_NONE>, + <0x0 0xc3 0 IRQ_TYPE_NONE>, + <0x0 0xc4 0 IRQ_TYPE_NONE>, + <0x0 0xc5 0 IRQ_TYPE_NONE>, + <0x0 0xc6 0 IRQ_TYPE_NONE>, + <0x0 0xc7 0 IRQ_TYPE_NONE>, + <0x0 0xc8 0 IRQ_TYPE_NONE>, + <0x0 0xc9 0 IRQ_TYPE_NONE>, + <0x0 0xca 0 IRQ_TYPE_NONE>, + <0x0 0xcb 0 IRQ_TYPE_NONE>, + <0x0 0xcc 0 IRQ_TYPE_NONE>, + <0x0 0xcd 0 IRQ_TYPE_NONE>, + <0x0 0xcf 0 IRQ_TYPE_NONE>, + <0x0 0xd0 0 IRQ_TYPE_NONE>, + <0x0 0xd1 0 IRQ_TYPE_NONE>, + <0x0 0xd2 0 IRQ_TYPE_NONE>, + <0x0 0xd4 0 IRQ_TYPE_NONE>, + <0x0 0xd6 0 IRQ_TYPE_NONE>; + interrupt-names = "pm8998_gpio1", "pm8998_gpio2", + "pm8998_gpio4", "pm8998_gpio5", + "pm8998_gpio6", "pm8998_gpio7", + "pm8998_gpio8", "pm8998_gpio9", + "pm8998_gpio10", "pm8998_gpio11", + "pm8998_gpio12", "pm8998_gpio13", + "pm8998_gpio14", "pm8998_gpio16", + "pm8998_gpio17", "pm8998_gpio18", + "pm8998_gpio19", "pm8998_gpio21", + "pm8998_gpio23"; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>; + qcom,gpios-disallowed = <3 15 20 22 24 25 26>; + }; + + pm8998_coincell: qcom,coincell@2800 { + compatible = "qcom,qpnp-coincell"; + reg = <0x2800 0x100>; + }; + + pm8998_rtc: qcom,pm8998_rtc { + compatible = "qcom,pm8941-rtc"; + interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; + }; + + pm8998_vadc: vadc@3100 { + compatible = "qcom,spmi-adc-rev2"; + reg = <0x3100 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "eoc-int-en-set"; + qcom,adc-vdd-reference = <1875>; + #io-channel-cells = <1>; + io-channel-ranges; + + /* Channel nodes */ + ref_gnd@0 { + label = "ref_gnd"; + reg = ; + qcom,pre-scaling = <1 1>; + }; + + vref_1p25@1 { + label = "vref_1p25"; + reg = ; + qcom,pre-scaling = <1 1>; + }; + + die_temp@6 { + label = "die_temp"; + reg = ; + qcom,pre-scaling = <1 1>; + }; + }; + + pm8998_adc_tm: vadc@3400 { + compatible = "qcom,adc-tm-rev2"; + reg = <0x3400 0x100>; + interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "thr-int-en"; + #address-cells = <1>; + #size-cells = <0>; + #thermal-sensor-cells = <1>; + qcom,pmic-revid = <&pm8998_revid>; }; + pm8998_clkdiv: clock-controller@5b00 { + compatible = "qcom,spmi-clkdiv"; + reg = <0x5b00 0x300>; + #clock-cells = <1>; + qcom,num-clkdivs = <3>; + clock-output-names = "pm8998_div_clk1", + "pm8998_div_clk2", "pm8998_div_clk3"; + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + }; }; - pm8998_lsid1: pmic@1 { - compatible = "qcom,pm8998", "qcom,spmi-pmic"; + qcom,pm8998@1 { + compatible = "qcom,spmi-pmic"; reg = <0x1 SPMI_USID>; - #address-cells = <1>; + #address-cells = <2>; #size-cells = <0>; }; }; + +&thermal_zones { + pm8998_temp_alarm: pm8998_tz { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "step_wise"; + thermal-sensors = <&pm8998_tz>; + wake-capable-sensor; + + trips { + pm8998_trip0: pm8998-trip0 { + temperature = <105000>; + hysteresis = <0>; + type = "passive"; + }; + pm8998_trip1: pm8998-trip1 { + temperature = <125000>; + hysteresis = <0>; + type = "passive"; + }; + pm8998_trip2: pm8998-trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi new file mode 100644 index 000000000000..fce3e3b06c54 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +&spmi_bus { + pmi8998_lsid0: qcom,pmi8998@2 { + compatible = "qcom,spmi-pmic"; + reg = <0x2 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + + pmi8998_revid: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + qcom,fab-id-valid; + }; + + pmi8998_misc: qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + }; + + qcom,power-on@800 { + compatible = "qcom,qpnp-power-on"; + reg = <0x800 0x100>; + }; + + pmi8998_tz: qcom,temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400 0x100>; + interrupts = <0x2 0x24 0x0 IRQ_TYPE_EDGE_RISING>; + io-channels = <&pmi8998_rradc 7>; + io-channel-names = "thermal"; + #thermal-sensor-cells = <0>; + qcom,temperature-threshold-set = <1>; + }; + + pmi8998_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0xe00>; + interrupts = <0x2 0xc0 0 IRQ_TYPE_NONE>, + <0x2 0xc1 0 IRQ_TYPE_NONE>, + <0x2 0xc2 0 IRQ_TYPE_NONE>, + <0x2 0xc4 0 IRQ_TYPE_NONE>, + <0x2 0xc5 0 IRQ_TYPE_NONE>, + <0x2 0xc7 0 IRQ_TYPE_NONE>, + <0x2 0xc8 0 IRQ_TYPE_NONE>, + <0x2 0xc9 0 IRQ_TYPE_NONE>, + <0x2 0xca 0 IRQ_TYPE_NONE>, + <0x2 0xcb 0 IRQ_TYPE_NONE>, + <0x2 0xcd 0 IRQ_TYPE_NONE>; + interrupt-names = "pmi8998_gpio1", "pmi8998_gpio2", + "pmi8998_gpio3", "pmi8998_gpio5", + "pmi8998_gpio6", "pmi8998_gpio8", + "pmi8998_gpio9", "pmi8998_gpio10", + "pmi8998_gpio11", "pmi8998_gpio12", + "pmi8998_gpio14"; + gpio-controller; + #gpio-cells = <2>; + qcom,gpios-disallowed = <4 7 13>; + }; + + pmi8998_qnovo: qcom,qpnp-qnovo@1500 { + compatible = "qcom,qpnp-qnovo"; + reg = <0x1500 0x100>; + interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>; + interrupt-names = "ptrain-done"; + qcom,pmic-revid = <&pmi8998_revid>; + }; + + pmi8998_charger: qcom,qpnp-smb2 { + compatible = "qcom,qpnp-smb2"; + #address-cells = <1>; + #size-cells = <1>; + #cooling-cells = <2>; + + qcom,pmic-revid = <&pmi8998_revid>; + + io-channels = <&pmi8998_rradc 8>, + <&pmi8998_rradc 10>, + <&pmi8998_rradc 3>, + <&pmi8998_rradc 4>; + io-channel-names = "charger_temp", + "charger_temp_max", + "usbin_i", + "usbin_v"; + + qcom,boost-threshold-ua = <100000>; + qcom,wipower-max-uw = <5000000>; + dpdm-supply = <&qusb_phy0>; + + qcom,thermal-mitigation + = <3000000 1500000 1000000 500000>; + qcom,auto-recharge-soc; + qcom,suspend-input-on-debug-batt; + + qcom,chgr@1000 { + reg = <0x1000 0x100>; + interrupts = + <0x2 0x10 0x0 IRQ_TYPE_EDGE_RISING>, + <0x2 0x10 0x1 IRQ_TYPE_EDGE_RISING>, + <0x2 0x10 0x2 IRQ_TYPE_EDGE_RISING>, + <0x2 0x10 0x3 IRQ_TYPE_EDGE_RISING>, + <0x2 0x10 0x4 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "chg-error", + "chg-state-change", + "step-chg-state-change", + "step-chg-soc-update-fail", + "step-chg-soc-update-request"; + }; + + qcom,otg@1100 { + reg = <0x1100 0x100>; + interrupts = <0x2 0x11 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x11 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x11 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x11 0x3 IRQ_TYPE_EDGE_BOTH>; + + interrupt-names = "otg-fail", + "otg-overcurrent", + "otg-oc-dis-sw-sts", + "testmode-change-detect"; + }; + + qcom,bat-if@1200 { + reg = <0x1200 0x100>; + interrupts = + <0x2 0x12 0x0 IRQ_TYPE_EDGE_RISING>, + <0x2 0x12 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x12 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x12 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x12 0x5 IRQ_TYPE_EDGE_BOTH>; + + interrupt-names = "bat-temp", + "bat-ocp", + "bat-ov", + "bat-low", + "bat-therm-or-id-missing", + "bat-terminal-missing"; + }; + + qcom,usb-chgpth@1300 { + reg = <0x1300 0x100>; + interrupts = + <0x2 0x13 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x5 IRQ_TYPE_EDGE_RISING>, + <0x2 0x13 0x6 IRQ_TYPE_EDGE_RISING>, + <0x2 0x13 0x7 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "usbin-collapse", + "usbin-lt-3p6v", + "usbin-uv", + "usbin-ov", + "usbin-plugin", + "usbin-src-change", + "usbin-icl-change", + "type-c-change"; + }; + + qcom,dc-chgpth@1400 { + reg = <0x1400 0x100>; + interrupts = + <0x2 0x14 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x5 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x14 0x6 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "dcin-collapse", + "dcin-lt-3p6v", + "dcin-uv", + "dcin-ov", + "dcin-plugin", + "div2-en-dg", + "dcin-icl-change"; + }; + + qcom,chgr-misc@1600 { + reg = <0x1600 0x100>; + interrupts = + <0x2 0x16 0x0 IRQ_TYPE_EDGE_RISING>, + <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>, + <0x2 0x16 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x16 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x16 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x16 0x5 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x16 0x6 IRQ_TYPE_EDGE_FALLING>, + <0x2 0x16 0x7 IRQ_TYPE_EDGE_BOTH>; + + interrupt-names = "wdog-snarl", + "wdog-bark", + "aicl-fail", + "aicl-done", + "high-duty-cycle", + "input-current-limiting", + "temperature-change", + "switcher-power-ok"; + }; + }; + + pmi8998_pdphy: qcom,usb-pdphy@1700 { + compatible = "qcom,qpnp-pdphy"; + reg = <0x1700 0x100>; + vdd-pdphy-supply = <&pm8998_l24>; + vbus-supply = <&ext_5v_boost>; + vconn-supply = <&smb2_vconn>; + interrupts = <0x2 0x17 0x0 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x1 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x2 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x3 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x4 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x5 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x6 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "sig-tx", + "sig-rx", + "msg-tx", + "msg-rx", + "msg-tx-failed", + "msg-tx-discarded", + "msg-rx-discarded"; + + qcom,default-sink-caps = <5000 3000>, /* 5V @ 3A */ + <9000 3000>, /* 9V @ 3A */ + <12000 2250>; /* 12V @ 2.25A */ + }; + + bcl_sensor: bcl@4200 { + compatible = "qcom,msm-bcl-lmh"; + reg = <0x4200 0xff>, + <0x4300 0xff>; + reg-names = "fg_user_adc", + "fg_lmh"; + interrupts = <0x2 0x42 0x0 IRQ_TYPE_NONE>, + <0x2 0x42 0x1 IRQ_TYPE_NONE>, + <0x2 0x42 0x2 IRQ_TYPE_NONE>, + <0x2 0x42 0x3 IRQ_TYPE_NONE>, + <0x2 0x42 0x4 IRQ_TYPE_NONE>; + interrupt-names = "bcl-high-ibat", + "bcl-very-high-ibat", + "bcl-low-vbat", + "bcl-very-low-vbat", + "bcl-crit-low-vbat"; + #thermal-sensor-cells = <1>; + }; + + pmi8998_rradc: rradc@4500 { + compatible = "qcom,rradc"; + reg = <0x4500 0x100>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + qcom,pmic-revid = <&pmi8998_revid>; + }; + + pmi8998_fg: qpnp,fg { + compatible = "qcom,fg-gen3"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmi8998_revid>; + io-channels = <&pmi8998_rradc 0>; + io-channel-names = "rradc_batt_id"; + qcom,rradc-base = <0x4500>; + qcom,fg-esr-timer-awake = <96 96>; + qcom,fg-esr-timer-asleep = <256 256>; + qcom,fg-esr-timer-charging = <0 96>; + qcom,cycle-counter-en; + qcom,hold-soc-while-full; + qcom,fg-auto-recharge-soc; + qcom,fg-recharge-soc-thr = <98>; + status = "okay"; + + qcom,fg-batt-soc@4000 { + status = "okay"; + reg = <0x4000 0x100>; + interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 + IRQ_TYPE_EDGE_RISING>, + <0x2 0x40 0x3 + IRQ_TYPE_EDGE_RISING>, + <0x2 0x40 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x5 + IRQ_TYPE_EDGE_RISING>, + <0x2 0x40 0x6 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x7 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "soc-update", + "soc-ready", + "bsoc-delta", + "msoc-delta", + "msoc-low", + "msoc-empty", + "msoc-high", + "msoc-full"; + }; + + qcom,fg-batt-info@4100 { + status = "okay"; + reg = <0x4100 0x100>; + interrupts = <0x2 0x41 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x41 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x41 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x41 0x6 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "vbatt-pred-delta", + "vbatt-low", + "esr-delta", + "batt-missing", + "batt-temp-delta"; + }; + + qcom,fg-memif@4400 { + status = "okay"; + reg = <0x4400 0x100>; + interrupts = <0x2 0x44 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x44 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x44 0x2 + IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ima-rdy", + "mem-xcp", + "dma-grant"; + }; + }; + }; + + pmi8998_lsid1: qcom,pmi8998@3 { + compatible ="qcom,spmi-pmic"; + reg = <0x3 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + + pmi8998_lpg: qcom,pwms@b100 { + compatible = "qcom,pwm-lpg"; + reg = <0xb100 0x600>, <0xb000 0x100>; + reg-names = "lpg-base", "lut-base"; + #pwm-cells = <2>; + qcom,num-lpg-channels = <6>; + qcom,lut-patterns = <0 10 20 30 40 50 60 70 80 90 100 + 90 80 70 60 50 40 30 20 10 0>; + + lpg1 { + qcom,lpg-chan-id = <1>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg2 { + qcom,lpg-chan-id = <2>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg3 { + qcom,lpg-chan-id = <3>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg4 { + qcom,lpg-chan-id = <4>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg5 { + qcom,lpg-chan-id = <5>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg6 { + qcom,lpg-chan-id = <6>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + }; + + pmi8998_rgb_led: qcom,leds@d000 { + compatible = "qcom,tri-led"; + reg = <0xd000 0x100>; + + red { + label = "red"; + pwms = <&pmi8998_lpg 4 1000000>; + led-sources = <0>; + linux,default-trigger = "timer"; + }; + + green { + label = "green"; + pwms = <&pmi8998_lpg 3 1000000>; + led-sources = <1>; + linux,default-trigger = "timer"; + }; + + blue { + label = "blue"; + pwms = <&pmi8998_lpg 2 1000000>; + led-sources = <2>; + linux,default-trigger = "timer"; + }; + }; + + labibb: qpnp-labibb-regulator { + compatible = "qcom,qpnp-labibb-regulator"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmi8998_revid>; + status = "disabled"; + + ibb_regulator: qcom,ibb@dc00 { + reg = <0xdc00 0x100>; + reg-names = "ibb_reg"; + regulator-name = "ibb_reg"; + + regulator-min-microvolt = <4600000>; + regulator-max-microvolt = <6000000>; + + interrupts = <0x3 0xdc 0x2 + IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ibb-sc-err"; + + qcom,qpnp-ibb-min-voltage = <1400000>; + qcom,qpnp-ibb-step-size = <100000>; + qcom,qpnp-ibb-slew-rate = <2000000>; + qcom,qpnp-ibb-use-default-voltage; + qcom,qpnp-ibb-init-voltage = <5500000>; + qcom,qpnp-ibb-init-amoled-voltage = <4000000>; + qcom,qpnp-ibb-init-lcd-voltage = <5500000>; + + qcom,qpnp-ibb-soft-start = <1000>; + + qcom,qpnp-ibb-lab-pwrup-delay = <8000>; + qcom,qpnp-ibb-lab-pwrdn-delay = <8000>; + qcom,qpnp-ibb-en-discharge; + + qcom,qpnp-ibb-full-pull-down; + qcom,qpnp-ibb-pull-down-enable; + qcom,qpnp-ibb-switching-clock-frequency = + <1480>; + qcom,qpnp-ibb-limit-maximum-current = <1550>; + qcom,qpnp-ibb-debounce-cycle = <16>; + qcom,qpnp-ibb-limit-max-current-enable; + qcom,qpnp-ibb-ps-enable; + }; + + lab_regulator: qcom,lab@de00 { + reg = <0xde00 0x100>; + reg-names = "lab"; + regulator-name = "lab_reg"; + + regulator-min-microvolt = <4600000>; + regulator-max-microvolt = <6000000>; + + interrupts = <0x3 0xde 0x0 + IRQ_TYPE_EDGE_RISING>, + <0x3 0xde 0x1 + IRQ_TYPE_EDGE_RISING>; + interrupt-names = "lab-vreg-ok", "lab-sc-err"; + + qcom,qpnp-lab-min-voltage = <4600000>; + qcom,qpnp-lab-step-size = <100000>; + qcom,qpnp-lab-slew-rate = <5000>; + qcom,qpnp-lab-use-default-voltage; + qcom,qpnp-lab-init-voltage = <5500000>; + qcom,qpnp-lab-init-amoled-voltage = <4600000>; + qcom,qpnp-lab-init-lcd-voltage = <5500000>; + + qcom,qpnp-lab-soft-start = <800>; + + qcom,qpnp-lab-full-pull-down; + qcom,qpnp-lab-pull-down-enable; + qcom,qpnp-lab-switching-clock-frequency = + <1600>; + qcom,qpnp-lab-limit-maximum-current = <1600>; + qcom,qpnp-lab-limit-max-current-enable; + qcom,qpnp-lab-ps-threshold = <70>; + qcom,qpnp-lab-ps-enable; + qcom,qpnp-lab-nfet-size = <100>; + qcom,qpnp-lab-pfet-size = <100>; + qcom,qpnp-lab-max-precharge-time = <500>; + }; + }; + + pmi8998_wled: qcom,leds@d800 { + compatible = "qcom,pmi8998-spmi-wled"; + reg = <0xd800 0x100>, + <0xd900 0x100>; + reg-names = "wled-ctrl-base", + "wled-sink-base"; + interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ovp-irq", "sc-irq"; + label = "backlight"; + qcom,fs-current-limit = <25000>; + qcom,boost-current-limit = <970>; + qcom,switching-freq = <800>; + qcom,ovp = <29600>; + qcom,sync-dly = <800>; + qcom,string-cfg = <15>; + qcom,pmic-revid = <&pmi8998_revid>; + qcom,auto-calibration; + status = "disabled"; + }; + + flash_led: qcom,leds@d300 { + compatible = "qcom,qpnp-flash-led-v2"; + status = "okay"; + reg = <0xd300 0x100>; + label = "flash"; + interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "led-fault-irq", + "all-ramp-down-done-irq", + "all-ramp-up-done-irq"; + qcom,hdrm-auto-mode; + qcom,short-circuit-det; + qcom,open-circuit-det; + qcom,vph-droop-det; + qcom,thermal-derate-en; + qcom,thermal-derate-current = <200 500 1000>; + qcom,isc-delay = <192>; + qcom,pmic-revid = <&pmi8998_revid>; + + pmi8998_flash0: qcom,flash_0 { + label = "flash"; + qcom,led-name = "led:flash_0"; + qcom,max-current = <1500>; + qcom,default-led-trigger = "flash0_trigger"; + qcom,id = <0>; + qcom,current-ma = <1000>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_flash1: qcom,flash_1 { + label = "flash"; + qcom,led-name = "led:flash_1"; + qcom,max-current = <1500>; + qcom,default-led-trigger = "flash1_trigger"; + qcom,id = <1>; + qcom,current-ma = <1000>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_flash2: qcom,flash_2 { + label = "flash"; + qcom,led-name = "led:flash_2"; + qcom,max-current = <750>; + qcom,default-led-trigger = "flash2_trigger"; + qcom,id = <2>; + qcom,current-ma = <500>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_torch0: qcom,torch_0 { + label = "torch"; + qcom,led-name = "led:torch_0"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch0_trigger"; + qcom,id = <0>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_torch1: qcom,torch_1 { + label = "torch"; + qcom,led-name = "led:torch_1"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch1_trigger"; + qcom,id = <1>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_torch2: qcom,torch_2 { + label = "torch"; + qcom,led-name = "led:torch_2"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch2_trigger"; + qcom,id = <2>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_switch0: qcom,led_switch_0 { + label = "switch"; + qcom,led-name = "led:switch_0"; + qcom,led-mask = <3>; + qcom,default-led-trigger = "switch0_trigger"; + }; + + pmi8998_switch1: qcom,led_switch_1 { + label = "switch"; + qcom,led-name = "led:switch_1"; + qcom,led-mask = <4>; + qcom,default-led-trigger = "switch1_trigger"; + }; + + pmi8998_switch2: qcom,led_switch_2 { + label = "switch"; + qcom,led-name = "led:switch_2"; + qcom,led-mask = <4>; + qcom,default-led-trigger = "switch2_trigger"; + }; + + }; + + pmi8998_haptics: qcom,haptics@c000 { + compatible = "qcom,haptics"; + reg = <0xc000 0x100>; + interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "hap-sc-irq", "hap-play-irq"; + qcom,actuator-type = "lra"; + qcom,vmax-mv = <3200>; + // qcom,lra-high-z = "opt1"; Need to implement that later + qcom,play-rate-us = <6667>; + qcom,lra-resonance-sig-shape = "sine"; + qcom,lra-auto-resonance-mode = "qwd"; + qcom,lra-allow-variable-play-rate; + qcom,pmic-revid = <&pmi8998_revid>; + status = "disabled"; + + wf_0 { + /* CLICK */ + qcom,effect-id = <0>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [3e 3e 3e]; + qcom,wf-play-rate-us = <6667>; + qcom,wf-brake-pattern = [01 00 00 00]; + qcom,lra-auto-resonance-disable; + }; + + wf_1 { + /* DOUBLE CLICK */ + qcom,effect-id = <1>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [7e 7e 02 02 02 02 02 02]; + qcom,wf-play-rate-us = <7143>; + qcom,wf-repeat-count = <2>; + qcom,wf-s-repeat-count = <1>; + qcom,lra-auto-resonance-disable; + }; + + wf_2 { + /* TICK */ + qcom,effect-id = <2>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [7e 7e]; + qcom,wf-play-rate-us = <4000>; + qcom,lra-auto-resonance-disable; + }; + + wf_3 { + /* THUD */ + qcom,effect-id = <3>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [7e 7e 7e]; + qcom,wf-play-rate-us = <6667>; + qcom,lra-auto-resonance-disable; + }; + + wf_4 { + /* POP */ + qcom,effect-id = <4>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [7e 7e]; + qcom,wf-play-rate-us = <5000>; + qcom,lra-auto-resonance-disable; + }; + + wf_5 { + /* HEAVY CLICK */ + qcom,effect-id = <5>; + qcom,wf-vmax-mv = <3600>; + qcom,wf-pattern = [7e 7e 7e]; + qcom,wf-play-rate-us = <6667>; + qcom,wf-brake-pattern = [03 00 00 00]; + qcom,lra-auto-resonance-disable; + }; + }; + }; +}; + +&thermal_zones { + ibat-high { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "step_wise"; + thermal-sensors = <&bcl_sensor 0>; + wake-capable-sensor; + + trips { + ibat_high: low-ibat { + temperature = <5000>; + hysteresis = <200>; + type = "passive"; + }; + }; + }; + ibat-vhigh { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "step_wise"; + thermal-sensors = <&bcl_sensor 1>; + wake-capable-sensor; + + trips { + ibat_vhigh: ibat_vhigh { + temperature = <6000>; + hysteresis = <100>; + type = "passive"; + }; + }; + }; + vbat { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-governor = "low_limits_cap"; + thermal-sensors = <&bcl_sensor 2>; + wake-capable-sensor; + tracks-low; + + trips { + low_vbat: low-vbat { + temperature = <3200>; + hysteresis = <100>; + type = "passive"; + }; + }; + cooling-maps { + vbat_cpu4 { + trip = <&low_vbat>; + cooling-device = <&cpu4_isolate 1 1>; + }; + vbat_cpu5 { + trip = <&low_vbat>; + cooling-device = <&cpu5_isolate 1 1>; + }; + vbat_map6 { + trip = <&low_vbat>; + cooling-device = <&cpu6_isolate 1 1>; + }; + vbat_map7 { + trip = <&low_vbat>; + cooling-device = <&cpu7_isolate 1 1>; + }; + }; + }; + vbat_low { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_cap"; + thermal-sensors = <&bcl_sensor 3>; + wake-capable-sensor; + tracks-low; + + trips { + low-vbat { + temperature = <2800>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; + vbat_too_low { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_cap"; + thermal-sensors = <&bcl_sensor 4>; + wake-capable-sensor; + tracks-low; + + trips { + low-vbat { + temperature = <2600>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; + soc { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-governor = "low_limits_cap"; + thermal-sensors = <&bcl_sensor 5>; + wake-capable-sensor; + tracks-low; + + trips { + low_soc: low-soc { + temperature = <5>; + hysteresis = <0>; + type = "passive"; + }; + }; + cooling-maps { + soc_map6 { + trip = <&low_soc>; + cooling-device = <&cpu6_isolate 1 1>; + }; + soc_map7 { + trip = <&low_soc>; + cooling-device = <&cpu7_isolate 1 1>; + }; + }; + }; + + pmi8998_tz { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pmi8998_tz>; + wake-capable-sensor; + + trips { + pmi8998_trip0: pmi8998-trip0 { + temperature = <105000>; + hysteresis = <0>; + type = "passive"; + }; + pmi8998_trip1: pmi8998-trip1 { + temperature = <125000>; + hysteresis = <0>; + type = "passive"; + }; + pmi8998_trip2: pmi8998-trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi new file mode 100644 index 000000000000..9c9459b1e9d8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include + +&soc { + /* Primary USB port related DWC3 controller */ + usb0: ssusb@a600000 { + compatible = "qcom,dwc-usb3-msm"; + reg = <0x0a600000 0xf8c00>, + <0x088ee000 0x400>; + reg-names = "core_base", "ahb2phy_base"; + iommus = <&apps_smmu 0x740 0x0>; + qcom,iommu-dma = "atomic"; + qcom,iommu-dma-addr-pool = <0x90000000 0x60000000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupts-extended = <&pdc 9 IRQ_TYPE_EDGE_RISING>, + <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 6 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 8 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "dp_hs_phy_irq", "pwr_event_irq", + "ss_phy_irq", "dm_hs_phy_irq"; + + USB3_GDSC-supply = <&usb30_prim_gdsc>; + dpdm-supply = <&qusb_phy0>; + qcom,reset-ep-after-lpm-resume; + qcom,dwc-usb3-msm-tx-fifo-size = <21288>; + qcom,num-gsi-evt-buffs = <0x3>; + qcom,gsi-reg-offset = + <0x0fc /* GSI_GENERAL_CFG */ + 0x110 /* GSI_DBL_ADDR_L */ + 0x120 /* GSI_DBL_ADDR_H */ + 0x130 /* GSI_RING_BASE_ADDR_L */ + 0x144 /* GSI_RING_BASE_ADDR_H */ + 0x1a4>; /* GSI_IF_STS */ + qcom,use-pdc-interrupts; + qcom,pm-qos-latency = <44>; + extcon = <0>, <0>, <&eud>, <0>, <0>; + + clocks = <&clock_gcc GCC_USB30_PRIM_MASTER_CLK>, + <&clock_gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, + <&clock_gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&clock_gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, + <&clock_gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>, + <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>; + + clock-names = "core_clk", "iface_clk", "bus_aggr_clk", + "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + + qcom,core-clk-rate = <133333333>; + qcom,core-clk-rate-hs = <66666667>; + + resets = <&clock_gcc GCC_USB30_PRIM_BCR>; + reset-names = "core_reset"; + + qcom,msm-bus,name = "usb0"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <3>; + qcom,msm-bus,vectors-KBps = + , + , + , + , + , + ; + + dwc3@a600000 { + compatible = "snps,dwc3"; + reg = <0x0a600000 0xcd00>; + interrupts = ; + usb-phy = <&qusb_phy0>, <&usb_nop_phy>; + tx-fifo-resize; + linux,sysdev_is_parent; + snps,disable-clk-gating; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + snps,usb3_lpm_capable; + usb-core-id = <0>; + dr_mode = "drd"; + maximum-speed = "high-speed"; + }; + + qcom,usbbam@a704000 { + compatible = "qcom,usb-bam-msm"; + reg = <0xa704000 0x17000>; + interrupts = ; + + qcom,bam-type = <0>; + qcom,usb-bam-fifo-baseaddr = <0x146bb000>; + qcom,usb-bam-num-pipes = <8>; + qcom,ignore-core-reset-ack; + qcom,disable-clk-gating; + qcom,usb-bam-override-threshold = <0x4001>; + qcom,usb-bam-max-mbps-highspeed = <400>; + qcom,usb-bam-max-mbps-superspeed = <3600>; + qcom,reset-bam-on-connect; + + qcom,pipe0 { + label = "ssusb-qdss-in-0"; + qcom,usb-bam-mem-type = <2>; + qcom,dir = <1>; + qcom,pipe-num = <0>; + qcom,peer-bam = <0>; + qcom,peer-bam-physical-address = <0x6064000>; + qcom,src-bam-pipe-index = <0>; + qcom,dst-bam-pipe-index = <0>; + qcom,data-fifo-offset = <0x0>; + qcom,data-fifo-size = <0x1800>; + qcom,descriptor-fifo-offset = <0x1800>; + qcom,descriptor-fifo-size = <0x800>; + }; + }; + }; + + /* Primary USB port related QUSB2 PHY */ + qusb_phy0: qusb@88e2000 { + compatible = "qcom,qusb2phy-v2"; + reg = <0x088e2000 0x400>, + <0x007801e8 0x4>, + <0x088e7014 0x4>; + reg-names = "qusb_phy_base", "efuse_addr", + "refgen_north_bg_reg_addr"; + + qcom,efuse-bit-pos = <25>; + qcom,efuse-num-bits = <3>; + vdd-supply = <&pm8998_l1>; + vdda18-supply = <&pm8998_l12>; + vdda33-supply = <&pm8998_l24>; + refgen-supply = <&pm8998_l26>; + qcom,override-bias-ctrl2; + qcom,vdd-voltage-level = <0 880000 880000>; + qcom,qusb-phy-reg-offset = + <0x240 /* QUSB2PHY_PORT_TUNE1 */ + 0x1a0 /* QUSB2PHY_PLL_COMMON_STATUS_ONE */ + 0x210 /* QUSB2PHY_PWR_CTRL1 */ + 0x230 /* QUSB2PHY_INTR_CTRL */ + 0x0a8 /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE */ + 0x254 /* QUSB2PHY_TEST1 */ + 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x27c /* QUSB2PHY_DEBUG_CTRL1 */ + 0x280 /* QUSB2PHY_DEBUG_CTRL2 */ + 0x284 /* QUSB2PHY_DEBUG_CTRL3 */ + 0x288 /* QUSB2PHY_DEBUG_CTRL4 */ + 0x2a0>; /* QUSB2PHY_STAT5 */ + + qcom,qusb-phy-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x20 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x00 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x30 0x240 /* TUNE1 */ + 0x29 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ + + phy_type= "utmi"; + clocks = <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + clock-names = "ref_clk_src", "cfg_ahb_clk"; + + resets = <&clock_gcc GCC_QUSB2PHY_PRIM_BCR>; + reset-names = "phy_reset"; + }; + + /* Primary USB port related QMP USB DP Combo PHY */ + usb_qmp_dp_phy: ssphy@88e8000 { + compatible = "qcom,usb-ssphy-qmp-dp-combo"; + reg = <0x88e8000 0x3000>; + reg-names = "qmp_phy_base"; + + vdd-supply = <&pm8998_l1>; + core-supply = <&pm8998_l26>; + qcom,vdd-voltage-level = <0 880000 880000>; + qcom,vbus-valid-override; + qcom,qmp-phy-init-seq = + /* */ + <0x1048 0x07 0x00 /* COM_PLL_IVCO */ + 0x1080 0x14 0x00 /* COM_SYSCLK_EN_SEL */ + 0x1034 0x08 0x00 /* COM_BIAS_EN_CLKBUFLR_EN */ + 0x1138 0x30 0x00 /* COM_CLK_SELECT */ + 0x103c 0x02 0x00 /* COM_SYS_CLK_CTRL */ + 0x108c 0x08 0x00 /* COM_RESETSM_CNTRL2 */ + 0x115c 0x16 0x00 /* COM_CMN_CONFIG */ + 0x1164 0x01 0x00 /* COM_SVS_MODE_CLK_SEL */ + 0x113c 0x80 0x00 /* COM_HSCLK_SEL */ + 0x10b0 0x82 0x00 /* COM_DEC_START_MODE0 */ + 0x10b8 0xab 0x00 /* COM_DIV_FRAC_START1_MODE0 */ + 0x10bc 0xea 0x00 /* COM_DIV_FRAC_START2_MODE0 */ + 0x10c0 0x02 0x00 /* COM_DIV_FRAC_START3_MODE0 */ + 0x1060 0x06 0x00 /* COM_CP_CTRL_MODE0 */ + 0x1068 0x16 0x00 /* COM_PLL_RCTRL_MODE0 */ + 0x1070 0x36 0x00 /* COM_PLL_CCTRL_MODE0 */ + 0x10dc 0x00 0x00 /* COM_INTEGLOOP_GAIN1_MODE0 */ + 0x10d8 0x3f 0x00 /* COM_INTEGLOOP_GAIN0_MODE0 */ + 0x10f8 0x01 0x00 /* COM_VCO_TUNE2_MODE0 */ + 0x10f4 0xc9 0x00 /* COM_VCO_TUNE1_MODE0 */ + 0x1148 0x0a 0x00 /* COM_CORECLK_DIV_MODE0 */ + 0x10a0 0x00 0x00 /* COM_LOCK_CMP3_MODE0 */ + 0x109c 0x34 0x00 /* COM_LOCK_CMP2_MODE0 */ + 0x1098 0x15 0x00 /* COM_LOCK_CMP1_MODE0 */ + 0x1090 0x04 0x00 /* COM_LOCK_CMP_EN */ + 0x1154 0x00 0x00 /* COM_CORE_CLK_EN */ + 0x1094 0x00 0x00 /* COM_LOCK_CMP_CFG */ + 0x10f0 0x00 0x00 /* COM_VCO_TUNE_MAP */ + 0x1040 0x0a 0x00 /* COM_SYSCLK_BUF_ENABLE */ + 0x1010 0x01 0x00 /* COM_SSC_EN_CENTER */ + 0x101c 0x31 0x00 /* COM_SSC_PER1 */ + 0x1020 0x01 0x00 /* COM_SSC_PER2 */ + 0x1014 0x00 0x00 /* COM_SSC_ADJ_PER1 */ + 0x1018 0x00 0x00 /* COM_SSC_ADJ_PER2 */ + 0x1024 0x85 0x00 /* COM_SSC_STEP_SIZE1 */ + 0x1028 0x07 0x00 /* COM_SSC_STEP_SIZE2 */ + 0x1430 0x0b 0x00 /* RXA_UCDR_FASTLOCK_FO_GAIN */ + 0x14d4 0x0f 0x00 /* RXA_RX_EQU_ADAPTOR_CNTRL2 */ + 0x14d8 0x4e 0x00 /* RXA_RX_EQU_ADAPTOR_CNTRL3 */ + 0x14dc 0x18 0x00 /* RXA_RX_EQU_ADAPTOR_CNTRL4 */ + 0x14f8 0x77 0x00 /* RXA_RX_EQ_OFFSET_ADAPTOR_CNTRL1 */ + 0x14fc 0x80 0x00 /* RXA_RX_OFFSET_ADAPTOR_CNTRL2 */ + 0x1504 0x03 0x00 /* RXA_SIGDET_CNTRL */ + 0x150c 0x16 0x00 /* RXA_SIGDET_DEGLITCH_CNTRL */ + 0x1564 0x05 0x00 /* RXA_RX_MODE_00 */ + 0x14c0 0x03 0x00 /* RXA_VGA_CAL_CNTRL2 */ + 0x1830 0x0b 0x00 /* RXB_UCDR_FASTLOCK_FO_GAIN */ + 0x18d4 0x0f 0x00 /* RXB_RX_EQU_ADAPTOR_CNTRL2 */ + 0x18d8 0x4e 0x00 /* RXB_RX_EQU_ADAPTOR_CNTRL3 */ + 0x18dc 0x18 0x00 /* RXB_RX_EQU_ADAPTOR_CNTRL4 */ + 0x18f8 0x77 0x00 /* RXB_RX_EQ_OFFSET_ADAPTOR_CNTRL1 */ + 0x18fc 0x80 0x00 /* RXB_RX_OFFSET_ADAPTOR_CNTRL2 */ + 0x1904 0x03 0x00 /* RXB_SIGDET_CNTRL */ + 0x190c 0x16 0x00 /* RXB_SIGDET_DEGLITCH_CNTRL */ + 0x1964 0x05 0x00 /* RXB_RX_MODE_00 */ + 0x18c0 0x03 0x00 /* RXB_VGA_CAL_CNTRL2 */ + 0x1260 0x10 0x00 /* TXA_HIGHZ_DRVR_EN */ + 0x12a4 0x12 0x00 /* TXA_RCV_DETECT_LVL_2 */ + 0x128c 0x16 0x00 /* TXA_LANE_MODE_1 */ + 0x1248 0x09 0x00 /* TXA_RES_CODE_LANE_OFFSET_RX */ + 0x1244 0x06 0x00 /* TXA_RES_CODE_LANE_OFFSET_TX */ + 0x1660 0x10 0x00 /* TXB_HIGHZ_DRVR_EN */ + 0x16a4 0x12 0x00 /* TXB_RCV_DETECT_LVL_2 */ + 0x168c 0x16 0x00 /* TXB_LANE_MODE_1 */ + 0x1648 0x09 0x00 /* TXB_RES_CODE_LANE_OFFSET_RX */ + 0x1644 0x06 0x00 /* TXB_RES_CODE_LANE_OFFSET_TX */ + 0x1cc8 0x83 0x00 /* PCS_FLL_CNTRL2 */ + 0x1ccc 0x09 0x00 /* PCS_FLL_CNT_VAL_L */ + 0x1cd0 0xa2 0x00 /* PCS_FLL_CNT_VAL_H_TOL */ + 0x1cd4 0x40 0x00 /* PCS_FLL_MAN_CODE */ + 0x1cc4 0x02 0x00 /* PCS_FLL_CNTRL1 */ + 0x1c80 0xd1 0x00 /* PCS_LOCK_DETECT_CONFIG1 */ + 0x1c84 0x1f 0x00 /* PCS_LOCK_DETECT_CONFIG2 */ + 0x1c88 0x47 0x00 /* PCS_LOCK_DETECT_CONFIG3 */ + 0x1c64 0x1b 0x00 /* PCS_POWER_STATE_CONFIG2 */ + 0x1434 0x75 0x00 /* RXA_UCDR_SO_SATURATION */ + 0x1834 0x75 0x00 /* RXB_UCDR_SO_SATURATION */ + 0x1dd8 0xba 0x00 /* PCS_RX_SIGDET_LVL */ + 0x1c0c 0x9f 0x00 /* PCS_TXMGN_V0 */ + 0x1c10 0x9f 0x00 /* PCS_TXMGN_V1 */ + 0x1c14 0xb7 0x00 /* PCS_TXMGN_V2 */ + 0x1c18 0x4e 0x00 /* PCS_TXMGN_V3 */ + 0x1c1c 0x65 0x00 /* PCS_TXMGN_V4 */ + 0x1c20 0x6b 0x00 /* PCS_TXMGN_LS */ + 0x1c24 0x15 0x00 /* PCS_TXDEEMPH_M6DB_V0 */ + 0x1c28 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_V0 */ + 0x1c2c 0x15 0x00 /* PCS_TXDEEMPH_M6DB_V1 */ + 0x1c30 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_V1 */ + 0x1c34 0x15 0x00 /* PCS_TXDEEMPH_M6DB_V2 */ + 0x1c38 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_V2 */ + 0x1c3c 0x15 0x00 /* PCS_TXDEEMPH_M6DB_V3 */ + 0x1c40 0x1d 0x00 /* PCS_TXDEEMPH_M3P5DB_V3 */ + 0x1c44 0x15 0x00 /* PCS_TXDEEMPH_M6DB_V4 */ + 0x1c48 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_V4 */ + 0x1c4c 0x15 0x00 /* PCS_TXDEEMPH_M6DB_LS */ + 0x1c50 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_LS */ + 0x1e0c 0x21 0x00 /* PCS_REFGEN_REQ_CONFIG1 */ + 0x1e10 0x60 0x00 /* PCS_REFGEN_REQ_CONFIG2 */ + 0x1c5c 0x02 0x00 /* PCS_RATE_SLEW_CNTRL */ + 0x1ca0 0x04 0x00 /* PCS_PWRUP_RESET_DLY_TIME_AUXCLK */ + 0x1c8c 0x44 0x00 /* PCS_TSYNC_RSYNC_TIME */ + 0x1c70 0xe7 0x00 /* PCS_RCVR_DTCT_DLY_P1U2_L */ + 0x1c74 0x03 0x00 /* PCS_RCVR_DTCT_DLY_P1U2_H */ + 0x1c78 0x40 0x00 /* PCS_RCVR_DTCT_DLY_U3_L */ + 0x1c7c 0x00 0x00 /* PCS_RCVR_DTCT_DLY_U3_H */ + 0x1cb8 0x75 0x00 /* PCS_RXEQTRAINING_WAIT_TIME */ + 0x1cb0 0x86 0x00 /* PCS_LFPS_TX_ECSTART_EQTLOCK */ + 0x1cbc 0x13 0x00 /* PCS_RXEQTRAINING_RUN_TIME */ + 0x1cac 0x04 0x00 /* PCS_LFPS_DET_HIGH_COUNT_VAL */ + 0xffffffff 0xffffffff 0x00>; + + qcom,qmp-phy-reg-offset = + <0x1d74 /* USB3_DP_PCS_PCS_STATUS */ + 0x1cd8 /* USB3_DP_PCS_AUTONOMOUS_MODE_CTRL */ + 0x1cdc /* USB3_DP_PCS_LFPS_RXTERM_IRQ_CLEAR */ + 0x1c04 /* USB3_DP_PCS_POWER_DOWN_CONTROL */ + 0x1c00 /* USB3_DP_PCS_SW_RESET */ + 0x1c08 /* USB3_DP_PCS_START_CONTROL */ + 0x2a18 /* USB3_DP_DP_PHY_PD_CTL */ + 0x0008 /* USB3_DP_COM_POWER_DOWN_CTRL */ + 0x0004 /* USB3_DP_COM_SW_RESET */ + 0x001c /* USB3_DP_COM_RESET_OVRD_CTRL */ + 0x0000 /* USB3_DP_COM_PHY_MODE_CTRL */ + 0x0010 /* USB3_DP_COM_TYPEC_CTRL */ + 0x000c /* USB3_DP_COM_SWI_CTRL */ + 0x1a0c>; /* USB3_DP_PCS_MISC_CLAMP_ENABLE */ + + clocks = <&clock_gcc GCC_USB3_PRIM_PHY_AUX_CLK>, + <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>, + <&clock_gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + + clock-names = "aux_clk", "pipe_clk", "ref_clk_src", + "ref_clk", "com_aux_clk", "cfg_ahb_clk"; + + resets = <&clock_gcc GCC_USB3_DP_PHY_PRIM_BCR>, + <&clock_gcc GCC_USB3_PHY_PRIM_BCR>; + reset-names = "global_phy_reset", "phy_reset"; + }; + + usb_audio_qmi_dev { + compatible = "qcom,usb-audio-qmi-dev"; + iommus = <&apps_smmu 0x182c 0x0>; + qcom,iommu-dma = "disabled"; + qcom,usb-audio-stream-id = <0xc>; + qcom,usb-audio-intr-num = <2>; + }; + + usb_nop_phy: usb_nop_phy { + compatible = "usb-nop-xceiv"; + }; + + /* Secondary USB port related DWC3 controller */ + usb1: ssusb@a800000 { + compatible = "qcom,dwc-usb3-msm"; + reg = <0x0a800000 0xf8c00>, + <0x088ee000 0x400>; + reg-names = "core_base", "ahb2phy_base"; + iommus = <&apps_smmu 0x760 0x0>; + qcom,iommu-dma = "atomic"; + qcom,iommu-dma-addr-pool = <0x90000000 0x60000000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupts-extended = <&pdc 11 IRQ_TYPE_EDGE_RISING>, + <&intc GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 7 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 10 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "dp_hs_phy_irq", "pwr_event_irq", + "ss_phy_irq", "dm_hs_phy_irq"; + + USB3_GDSC-supply = <&usb30_sec_gdsc>; + qcom,dwc-usb3-msm-tx-fifo-size = <21288>; + qcom,gsi-reg-offset = + <0x0fc /* GSI_GENERAL_CFG */ + 0x110 /* GSI_DBL_ADDR_L */ + 0x120 /* GSI_DBL_ADDR_H */ + 0x130 /* GSI_RING_BASE_ADDR_L */ + 0x144 /* GSI_RING_BASE_ADDR_H */ + 0x1a4>; /* GSI_IF_STS */ + qcom,use-pdc-interrupts; + + clocks = <&clock_gcc GCC_USB30_SEC_MASTER_CLK>, + <&clock_gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, + <&clock_gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&clock_gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, + <&clock_gcc GCC_USB30_SEC_SLEEP_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>, + <&clock_gcc GCC_USB3_SEC_CLKREF_CLK>; + + clock-names = "core_clk", "iface_clk", "bus_aggr_clk", + "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + + qcom,core-clk-rate = <133333333>; + qcom,core-clk-rate-hs = <66666667>; + + resets = <&clock_gcc GCC_USB30_SEC_BCR>; + reset-names = "core_reset"; + status = "disabled"; + + qcom,msm-bus,name = "usb1"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + , + , + , + ; + + dwc3@a800000 { + compatible = "snps,dwc3"; + reg = <0x0a800000 0xcd00>; + interrupts = ; + usb-phy = <&qusb_phy1>, <&usb_qmp_phy>; + tx-fifo-resize; + linux,sysdev_is_parent; + snps,disable-clk-gating; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + snps,usb3_lpm_capable; + usb-core-id = <1>; + dr_mode = "host"; + maximum-speed = "super-speed"; + }; + }; + + /* Secondary USB port related QUSB2 PHY */ + qusb_phy1: qusb@88e3000 { + compatible = "qcom,qusb2phy-v2"; + reg = <0x088e3000 0x400>, + <0x088e7014 0x4>; + reg-names = "qusb_phy_base", + "refgen_north_bg_reg_addr"; + + vdd-supply = <&pm8998_l1>; + vdda18-supply = <&pm8998_l12>; + vdda33-supply = <&pm8998_l24>; + qcom,override-bias-ctrl2; + qcom,vdd-voltage-level = <0 880000 880000>; + qcom,qusb-phy-reg-offset = + <0x240 /* QUSB2PHY_PORT_TUNE1 */ + 0x1a0 /* QUSB2PHY_PLL_COMMON_STATUS_ONE */ + 0x210 /* QUSB2PHY_PWR_CTRL1 */ + 0x230 /* QUSB2PHY_INTR_CTRL */ + 0x0a8 /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE */ + 0x254 /* QUSB2PHY_TEST1 */ + 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x27c /* QUSB2PHY_DEBUG_CTRL1 */ + 0x280 /* QUSB2PHY_DEBUG_CTRL2 */ + 0x284 /* QUSB2PHY_DEBUG_CTRL3 */ + 0x288 /* QUSB2PHY_DEBUG_CTRL4 */ + 0x2a0>; /* QUSB2PHY_STAT5 */ + + qcom,qusb-phy-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x20 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x00 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x20 0x240 /* TUNE1 */ + 0x29 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ + + phy_type= "utmi"; + clocks = <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + clock-names = "ref_clk_src", "cfg_ahb_clk"; + + resets = <&clock_gcc GCC_QUSB2PHY_SEC_BCR>; + reset-names = "phy_reset"; + status = "disabled"; + }; + + /* Secondary USB port related QMP PHY */ + usb_qmp_phy: ssphy@88eb000 { + compatible = "qcom,usb-ssphy-qmp-v2"; + reg = <0x88eb000 0x1000>, + <0x01fcbff0 0x4>; + reg-names = "qmp_phy_base", + "vls_clamp_reg"; + + vdd-supply = <&pm8998_l1>; + core-supply = <&pm8998_l26>; + qcom,vdd-voltage-level = <0 880000 880000>; + qcom,vbus-valid-override; + qcom,qmp-phy-init-seq = + /* */ + <0x048 0x07 0x00 /* QSERDES_COM_PLL_IVCO */ + 0x080 0x14 0x00 /* QSERDES_COM_SYSCLK_EN_SEL */ + 0x034 0x04 0x00 /* QSERDES_COM_BIAS_EN_CLKBUFLR_EN */ + 0x138 0x30 0x00 /* QSERDES_COM_CLK_SELECT */ + 0x03c 0x02 0x00 /* QSERDES_COM_SYS_CLK_CTRL */ + 0x08c 0x08 0x00 /* QSERDES_COM_RESETSM_CNTRL2 */ + 0x15c 0x06 0x00 /* QSERDES_COM_CMN_CONFIG */ + 0x164 0x01 0x00 /* QSERDES_COM_SVS_MODE_CLK_SEL */ + 0x13c 0x80 0x00 /* QSERDES_COM_HSCLK_SEL */ + 0x0b0 0x82 0x00 /* QSERDES_COM_DEC_START_MODE0 */ + 0x0b8 0xab 0x00 /* QSERDES_COM_DIV_FRAC_START1_MODE0 */ + 0x0bc 0xea 0x00 /* QSERDES_COM_DIV_FRAC_START2_MODE0 */ + 0x0c0 0x02 0x00 /* QSERDES_COM_DIV_FRAC_START3_MODE0 */ + 0x060 0x06 0x00 /* QSERDES_COM_CP_CTRL_MODE0 */ + 0x068 0x16 0x00 /* QSERDES_COM_PLL_RCTRL_MODE0 */ + 0x070 0x36 0x00 /* QSERDES_COM_PLL_CCTRL_MODE0 */ + 0x0dc 0x00 0x00 /* QSERDES_COM_INTEGLOOP_GAIN1_MODE0 */ + 0x0d8 0x3f 0x00 /* QSERDES_COM_INTEGLOOP_GAIN0_MODE0 */ + 0x0f8 0x01 0x00 /* QSERDES_COM_VCO_TUNE2_MODE0 */ + 0x0f4 0xc9 0x00 /* QSERDES_COM_VCO_TUNE1_MODE0 */ + 0x148 0x0a 0x00 /* QSERDES_COM_CORECLK_DIV_MODE0 */ + 0x0a0 0x00 0x00 /* QSERDES_COM_LOCK_CMP3_MODE0 */ + 0x09c 0x34 0x00 /* QSERDES_COM_LOCK_CMP2_MODE0 */ + 0x098 0x15 0x00 /* QSERDES_COM_LOCK_CMP1_MODE0 */ + 0x090 0x04 0x00 /* QSERDES_COM_LOCK_CMP_EN */ + 0x154 0x00 0x00 /* QSERDES_COM_CORE_CLK_EN */ + 0x094 0x00 0x00 /* QSERDES_COM_LOCK_CMP_CFG */ + 0x0f0 0x00 0x00 /* QSERDES_COM_VCO_TUNE_MAP */ + 0x040 0x0a 0x00 /* QSERDES_COM_SYSCLK_BUF_ENABLE */ + 0x0d0 0x80 0x00 /* QSERDES_COM_INTEGLOOP_INITVAL */ + 0x010 0x01 0x00 /* QSERDES_COM_SSC_EN_CENTER */ + 0x01c 0x31 0x00 /* QSERDES_COM_SSC_PER1 */ + 0x020 0x01 0x00 /* QSERDES_COM_SSC_PER2 */ + 0x014 0x00 0x00 /* QSERDES_COM_SSC_ADJ_PER1 */ + 0x018 0x00 0x00 /* QSERDES_COM_SSC_ADJ_PER2 */ + 0x024 0x85 0x00 /* QSERDES_COM_SSC_STEP_SIZE1 */ + 0x028 0x07 0x00 /* QSERDES_COM_SSC_STEP_SIZE2 */ + 0x4c0 0x0c 0x00 /* QSERDES_RX_VGA_CAL_CNTRL2 */ + 0x564 0x50 0x00 /* QSERDES_RX_RX_MODE_00 */ + 0x430 0x0b 0x00 /* QSERDES_RX_UCDR_FASTLOCK_FO_GAIN */ + 0x4d4 0x0e 0x00 /* QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 */ + 0x4d8 0x4e 0x00 /* QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 */ + 0x4dc 0x18 0x00 /* QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 */ + 0x4f8 0x77 0x00 /* RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 */ + 0x4fc 0x80 0x00 /* RX_RX_OFFSET_ADAPTOR_CNTRL2 */ + 0x504 0x03 0x00 /* QSERDES_RX_SIGDET_CNTRL */ + 0x50c 0x1c 0x00 /* QSERDES_RX_SIGDET_DEGLITCH_CNTRL */ + 0x434 0x75 0x00 /* RX_UCDR_SO_SATURATION_AND_ENABLE */ + 0x444 0x80 0x00 /* QSERDES_RX_UCDR_PI_CONTROLS */ + 0x408 0x0a 0x00 /* QSERDES_RX_UCDR_FO_GAIN */ + 0x40c 0x06 0x00 /* QSERDES_RX_UCDR_SO_GAIN */ + 0x500 0x00 0x00 /* QSERDES_RX_SIGDET_ENABLES */ + 0x260 0x10 0x00 /* QSERDES_TX_HIGHZ_DRVR_EN */ + 0x2a4 0x12 0x00 /* QSERDES_TX_RCV_DETECT_LVL_2 */ + 0x28c 0xc6 0x00 /* QSERDES_TX_LANE_MODE_1 */ + 0x248 0x06 0x00 /* TX_RES_CODE_LANE_OFFSET_RX */ + 0x244 0x06 0x00 /* TX_RES_CODE_LANE_OFFSET_TX */ + 0x8c8 0x83 0x00 /* USB3_UNI_PCS_FLL_CNTRL2 */ + 0x8cc 0x09 0x00 /* USB3_UNI_PCS_FLL_CNT_VAL_L */ + 0x8d0 0xa2 0x00 /* USB3_UNI_PCS_FLL_CNT_VAL_H_TOL */ + 0x8d4 0x40 0x00 /* USB3_UNI_PCS_FLL_MAN_CODE */ + 0x8c4 0x02 0x00 /* USB3_UNI_PCS_FLL_CNTRL1 */ + 0x864 0x1b 0x00 /* USB3_UNI_PCS_POWER_STATE_CONFIG2 */ + 0x80c 0x9f 0x00 /* USB3_UNI_PCS_TXMGN_V0 */ + 0x810 0x9f 0x00 /* USB3_UNI_PCS_TXMGN_V1 */ + 0x814 0xb5 0x00 /* USB3_UNI_PCS_TXMGN_V2 */ + 0x818 0x4c 0x00 /* USB3_UNI_PCS_TXMGN_V3 */ + 0x81c 0x64 0x00 /* USB3_UNI_PCS_TXMGN_V4 */ + 0x820 0x6a 0x00 /* USB3_UNI_PCS_TXMGN_LS */ + 0x824 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_V0 */ + 0x828 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_V0 */ + 0x82c 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_V1 */ + 0x830 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_V1 */ + 0x834 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_V2 */ + 0x838 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_V2 */ + 0x83c 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_V3 */ + 0x840 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_V3 */ + 0x844 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_V4 */ + 0x848 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_V4 */ + 0x84c 0x15 0x00 /* USB3_UNI_PCS_TXDEEMPH_M6DB_LS */ + 0x850 0x0d 0x00 /* USB3_UNI_PCS_TXDEEMPH_M3P5DB_LS */ + 0x85c 0x02 0x00 /* USB3_UNI_PCS_RATE_SLEW_CNTRL */ + 0x8a0 0x04 0x00 /* PCS_PWRUP_RESET_DLY_TIME_AUXCLK */ + 0x88c 0x44 0x00 /* USB3_UNI_PCS_TSYNC_RSYNC_TIME */ + 0x880 0xd1 0x00 /* USB3_UNI_PCS_LOCK_DETECT_CONFIG1 */ + 0x884 0x1f 0x00 /* USB3_UNI_PCS_LOCK_DETECT_CONFIG2 */ + 0x888 0x47 0x00 /* USB3_UNI_PCS_LOCK_DETECT_CONFIG3 */ + 0x870 0xe7 0x00 /* USB3_UNI_PCS_RCVR_DTCT_DLY_P1U2_L */ + 0x874 0x03 0x00 /* USB3_UNI_PCS_RCVR_DTCT_DLY_P1U2_H */ + 0x878 0x40 0x00 /* USB3_UNI_PCS_RCVR_DTCT_DLY_U3_L */ + 0x87c 0x00 0x00 /* USB3_UNI_PCS_RCVR_DTCT_DLY_U3_H */ + 0x9d8 0xba 0x00 /* USB3_UNI_PCS_RX_SIGDET_LVL */ + 0x8b8 0x75 0x00 /* RXEQTRAINING_WAIT_TIME */ + 0x8b0 0x86 0x00 /* PCS_LFPS_TX_ECSTART_EQTLOCK */ + 0x8bc 0x13 0x00 /* PCS_RXEQTRAINING_RUN_TIME */ + 0xa0c 0x21 0x00 /* USB3_UNI_PCS_REFGEN_REQ_CONFIG1 */ + 0xa10 0x60 0x00 /* USB3_UNI_PCS_REFGEN_REQ_CONFIG2 */ + 0xffffffff 0xffffffff 0x00>; + + qcom,qmp-phy-reg-offset = + <0x974 /* USB3_UNI_PCS_PCS_STATUS */ + 0x8d8 /* USB3_UNI_PCS_AUTONOMOUS_MODE_CTRL */ + 0x8dc /* USB3_UNI_PCS_LFPS_RXTERM_IRQ_CLEAR */ + 0x804 /* USB3_UNI_PCS_POWER_DOWN_CONTROL */ + 0x800 /* USB3_UNI_PCS_SW_RESET */ + 0x808>; /* USB3_UNI_PCS_START_CONTROL */ + + clocks = <&clock_gcc GCC_USB3_SEC_PHY_AUX_CLK>, + <&clock_gcc GCC_USB3_SEC_PHY_PIPE_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB3_SEC_CLKREF_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + + clock-names = "aux_clk", "pipe_clk", "ref_clk_src", + "ref_clk", "cfg_ahb_clk"; + + resets = <&clock_gcc GCC_USB3_PHY_SEC_BCR>, + <&clock_gcc GCC_USB3PHY_PHY_SEC_BCR>; + reset-names = "phy_reset", "phy_phy_reset"; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi new file mode 100644 index 000000000000..4a746638523a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include "sdm845-wcd.dtsi" +#include "msm-wsa881x.dtsi" +#include + +&snd_934x { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "hifi amp", "LINEOUT1", + "hifi amp", "LINEOUT2", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "AMIC3", "MIC BIAS3", + "MIC BIAS3", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS1", + "MIC BIAS1", "ANCLeft Headset Mic", + "AMIC5", "MIC BIAS4", + "MIC BIAS4", "Handset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "DMIC4", "MIC BIAS4", + "MIC BIAS4", "Digital Mic4", + "DMIC5", "MIC BIAS4", + "MIC BIAS4", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + + qcom,msm-mbhc-hphl-swh = <1>; + qcom,msm-mbhc-gnd-swh = <1>; + qcom,msm-mbhc-hs-mic-max-threshold-mv = <1700>; + qcom,msm-mbhc-hs-mic-min-threshold-mv = <50>; + qcom,hph-en0-gpio = <&tavil_hph_en0>; + qcom,hph-en1-gpio = <&tavil_hph_en1>; + qcom,tavil-mclk-clk-freq = <9600000>; + + asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; + asoc-codec-names = "msm-stub-codec.1", + "msm-ext-disp-audio-codec-rx"; + + qcom,usbc-analog-en1-gpio = <&wcd_usbc_analog_en1_gpio>; + qcom,usbc-analog-en2-gpio = <&tlmm 51 0>; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&wcd_usbc_analog_en2_active>; + pinctrl-1 = <&wcd_usbc_analog_en2_idle>; + + qcom,wsa-max-devs = <0>; + //qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>, + // <&wsa881x_0213>, <&wsa881x_0214>; + //qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight", + // "SpkrLeft", "SpkrRight"; +}; + +&soc { + wcd_usbc_analog_en1_gpio: msm_cdc_pinctrl@49 { + compatible = "qcom,msm-cdc-pinctrl"; + //pinctrl-names = "aud_active", "aud_sleep"; + //pinctrl-0 = <&wcd_usbc_analog_en1_active>; + //pinctrl-1 = <&wcd_usbc_analog_en1_idle>; + }; + + wcd9xxx_intc: wcd9xxx-irq { + status = "ok"; + compatible = "qcom,wcd9xxx-irq"; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&tlmm>; + qcom,gpio-connect = <&tlmm 54 0>; + pinctrl-names = "default"; + pinctrl-0 = <&wcd_intr_default>; + }; + + clock_audio_lnbb: audio_ext_clk_lnbb { + status = "ok"; + compatible = "qcom,audio-ref-clk"; + qcom,codec-ext-clk-src = <1>; + clock-names = "osr_clk"; + clocks = <&clock_rpmh RPMH_LN_BB_CLK2>; + qcom,node_has_rpm_clock; + #clock-cells = <1>; + }; + + wcd_rst_gpio: msm_cdc_pinctrl@64 { + compatible = "qcom,msm-cdc-pinctrl"; + qcom,cdc-rst-n-gpio = <&tlmm 64 0>; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&cdc_reset_active>; + pinctrl-1 = <&cdc_reset_sleep>; + }; + + qocm,wcd-dsp-glink { + compatible = "qcom,wcd-dsp-glink"; + qcom,wdsp-channels = "g_glink_ctrl", + "g_glink_persistent_data_nild", + "g_glink_persistent_data_ild", + "g_glink_audio_data"; + }; + + qcom,wcd-dsp-mgr { + compatible = "qcom,wcd-dsp-mgr"; + qcom,wdsp-components = <&wcd934x_cdc 0>, + <&wcd_spi_0 1>, + <&glink_spi_xprt_wdsp 2>; + qcom,img-filename = "cpe_9340"; + }; +}; + +&slim_aud { + wcd934x_cdc: tavil_codec { + compatible = "qcom,tavil-slim-pgd"; + elemental-addr = [00 01 50 02 17 02]; + + interrupt-parent = <&wcd9xxx_intc>; + interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + 17 18 19 20 21 22 23 24 25 26 27 28 29 + 30 31>; + + qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>; + + clock-names = "wcd_clk"; + clocks = <&clock_audio_lnbb 0>; + + cdc-vdd-buck-supply = <&pm8998_s4>; + qcom,cdc-vdd-buck-voltage = <1800000 1800000>; + qcom,cdc-vdd-buck-current = <650000>; + + cdc-buck-sido-supply = <&pm8998_s4>; + qcom,cdc-buck-sido-voltage = <1800000 1800000>; + qcom,cdc-buck-sido-current = <250000>; + + cdc-vdd-tx-h-supply = <&pm8998_s4>; + qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-tx-h-current = <25000>; + + cdc-vdd-rx-h-supply = <&pm8998_s4>; + qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>; + qcom,cdc-vdd-rx-h-current = <25000>; + + cdc-vddpx-1-supply = <&pm8998_s4>; + qcom,cdc-vddpx-1-voltage = <1800000 1800000>; + qcom,cdc-vddpx-1-current = <10000>; + + qcom,cdc-static-supplies = "cdc-vdd-buck", + "cdc-buck-sido", + "cdc-vdd-tx-h", + "cdc-vdd-rx-h", + "cdc-vddpx-1"; + + qcom,cdc-micbias1-mv = <1800>; + qcom,cdc-micbias2-mv = <1800>; + qcom,cdc-micbias3-mv = <1800>; + qcom,cdc-micbias4-mv = <1800>; + + qcom,cdc-mclk-clk-rate = <9600000>; + qcom,cdc-slim-ifd = "tavil-slim-ifd"; + qcom,cdc-slim-ifd-elemental-addr = [00 00 50 02 17 02]; + qcom,cdc-dmic-sample-rate = <4800000>; + qcom,cdc-mad-dmic-rate = <600000>; + + qcom,wdsp-cmpnt-dev-name = "tavil_codec"; + + wcd_spi_0: wcd_spi { + compatible = "qcom,wcd-spi-v2"; + qcom,master-bus-num = <0>; + qcom,chip-select = <0>; + qcom,max-frequency = <24000000>; + qcom,mem-base-addr = <0x100000>; + }; + + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi new file mode 100644 index 000000000000..807e30ca7aab --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include "msm-audio-lpass.dtsi" + +&msm_audio_ion { + iommus = <&apps_smmu 0x1821 0x0>; + qcom,smmu-sid-mask = /bits/ 64 <0xf>; +}; + +&soc { + qcom,avtimer@170f7000 { + compatible = "qcom,avtimer"; + reg = <0x170f700c 0x4>, + <0x170f7010 0x4>; + reg-names = "avtimer_lsb_addr", "avtimer_msb_addr"; + qcom,clk-div = <192>; + qcom,clk-mult = <10>; + }; +}; + +&audio_apr { + q6core: qcom,q6core-audio { + compatible = "qcom,q6core-audio"; + }; +}; + +&q6core { + snd_934x: sound-tavil { + compatible = "qcom,sdm845-asoc-snd-tavil"; + qcom,model = "sdm845-tavil-snd-card"; + /*MM.Audio, 2019/07/13, add for screen record headset mic path*/ + qcom,afe-rxtx-lb = <1>; + /*add end*/ + qcom,ext-disp-audio-rx; + qcom,wcn-btfm; + qcom,mi2s-audio-intf; + qcom,auxpcm-audio-intf; + qcom,msm-mi2s-master = <1>, <1>, <1>, <1>; + + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&compr>, + <&pcm_noirq>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-compr-dsp", + "msm-pcm-dsp-noirq"; + asoc-cpu = <&dai_hdmi>, <&dai_dp>, + <&dai_mi2s0>, <&dai_mi2s1>, + <&dai_mi2s2>, <&dai_mi2s3>, + <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, + <&dai_tert_auxpcm>, <&dai_quat_auxpcm>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, + <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, + <&afe_proxy_tx>, <&incall_record_rx>, + <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>, + <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>, + <&usb_audio_rx>, <&usb_audio_tx>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>, + <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>, + <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>, + <&dai_quat_tdm_rx_1>, + <&proxy_rx>, <&proxy_tx>, + /*MM.Audio, 2019/07/13, add for screen record headset mic path*/ + <&afe_loopback_tx>; + /*add end*/ + asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.0", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", + "msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.16395", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394", + "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398", + "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401", + "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673", + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865", + "msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881", + "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913", + "msm-dai-q6-tdm.36914", + "msm-dai-q6-dev.8194", "msm-dai-q6-dev.8195", + /*MM.Audio, 2019/07/13, add for screen record headset mic path*/ + "msm-dai-q6-dev.24577"; + /*add end*/ +/*suzhiguang, temp add here */ + op,smartpa ="max98927"; + }; +}; + +&slim_aud { + msm_dai_slim { + compatible = "qcom,msm-dai-slim"; + elemental-addr = [ff ff ff fe 17 02]; + }; +}; + +&msm_dai_tdm_quat_rx { + qcom,msm-cpudai-tdm-group-num-ports = <2>; + qcom,msm-cpudai-tdm-group-port-id = <36912 36914>; + dai_quat_tdm_rx_1: qcom,msm-dai-q6-tdm-quat-rx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36914>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi new file mode 100644 index 000000000000..49e0f2721113 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi @@ -0,0 +1,1974 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +&soc { + ad_hoc_bus: ad-hoc-bus { + compatible = "qcom,msm-bus-device"; + reg = <0x016E0000 0x40000>, + <0x1700000 0x40000>, + <0x1500000 0x40000>, + <0x14E0000 0x40000>, + <0x17900000 0x40000>, + <0x1380000 0x40000>, + <0x1380000 0x40000>, + <0x1740000 0x40000>, + <0x1620000 0x40000>, + <0x1620000 0x40000>, + <0x1620000 0x40000>; + + reg-names = "aggre1_noc-base", "aggre2_noc-base", + "config_noc-base", "dc_noc-base", + "gladiator_noc-base", "mc_virt-base", "mem_noc-base", + "mmss_noc-base", "system_noc-base", "ipa_virt-base", + "camnoc_virt-base"; + + /*RSCs*/ + rsc_apps: rsc-apps { + cell-id = ; + label = "apps_rsc"; + qcom,rsc-dev; + qcom,req_state = <2>; + }; + + rsc_disp: rsc-disp { + cell-id = ; + label = "disp_rsc"; + qcom,rsc-dev; + qcom,req_state = <3>; + }; + + /*BCMs*/ + bcm_acv: bcm-acv { + cell-id = ; + label = "ACV"; + qcom,bcm-name = "ACV"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_alc: bcm-alc { + cell-id = ; + label = "ALC"; + qcom,bcm-name = "ALC"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mc0: bcm-mc0 { + cell-id = ; + label = "MC0"; + qcom,bcm-name = "MC0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh0: bcm-sh0 { + cell-id = ; + label = "SH0"; + qcom,bcm-name = "SH0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mm0: bcm-mm0 { + cell-id = ; + label = "MM0"; + qcom,bcm-name = "MM0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh1: bcm-sh1 { + cell-id = ; + label = "SH1"; + qcom,bcm-name = "SH1"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mm1: bcm-mm1 { + cell-id = ; + label = "MM1"; + qcom,bcm-name = "MM1"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh2: bcm-sh2 { + cell-id = ; + label = "SH2"; + qcom,bcm-name = "SH2"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mm2: bcm-mm2 { + cell-id = ; + label = "MM2"; + qcom,bcm-name = "MM2"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh3: bcm-sh3 { + cell-id = ; + label = "SH3"; + qcom,bcm-name = "SH3"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mm3: bcm-mm3 { + cell-id = ; + label = "MM3"; + qcom,bcm-name = "MM3"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh4: bcm-sh4 { + cell-id = ; + label = "SH4"; + qcom,bcm-name = "SH4"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sh5: bcm-sh5 { + cell-id = ; + label = "SH5"; + qcom,bcm-name = "SH5"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn0: bcm-sn0 { + cell-id = ; + label = "SN0"; + qcom,bcm-name = "SN0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_ce0: bcm-ce0 { + cell-id = ; + label = "CE0"; + qcom,bcm-name = "CE0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_ip0: bcm-ip0 { + cell-id = ; + label = "IP0"; + qcom,bcm-name = "IP0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_cn0: bcm-cn0 { + cell-id = ; + label = "CN0"; + qcom,bcm-name = "CN0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_qup0: bcm-qup0 { + cell-id = ; + label = "QUP0"; + qcom,bcm-name = "QUP0"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn1: bcm-sn1 { + cell-id = ; + label = "SN1"; + qcom,bcm-name = "SN1"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn2: bcm-sn2 { + cell-id = ; + label = "SN2"; + qcom,bcm-name = "SN2"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn3: bcm-sn3 { + cell-id = ; + label = "SN3"; + qcom,bcm-name = "SN3"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn4: bcm-sn4 { + cell-id = ; + label = "SN4"; + qcom,bcm-name = "SN4"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn5: bcm-sn5 { + cell-id = ; + label = "SN5"; + qcom,bcm-name = "SN5"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn6: bcm-sn6 { + cell-id = ; + label = "SN6"; + qcom,bcm-name = "SN6"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn7: bcm-sn7 { + cell-id = ; + label = "SN7"; + qcom,bcm-name = "SN7"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn8: bcm-sn8 { + cell-id = ; + label = "SN8"; + qcom,bcm-name = "SN8"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn9: bcm-sn9 { + cell-id = ; + label = "SN9"; + qcom,bcm-name = "SN9"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn11: bcm-sn11 { + cell-id = ; + label = "SN11"; + qcom,bcm-name = "SN11"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn12: bcm-sn12 { + cell-id = ; + label = "SN12"; + qcom,bcm-name = "SN12"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn14: bcm-sn14 { + cell-id = ; + label = "SN14"; + qcom,bcm-name = "SN14"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_sn15: bcm-sn15 { + cell-id = ; + label = "SN15"; + qcom,bcm-name = "SN15"; + qcom,rscs = <&rsc_apps>; + qcom,bcm-dev; + }; + + bcm_mc0_display: bcm-mc0_display { + cell-id = ; + label = "MC0_DISPLAY"; + qcom,bcm-name = "MC0"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + bcm_sh0_display: bcm-sh0_display { + cell-id = ; + label = "SH0_DISPLAY"; + qcom,bcm-name = "SH0"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + bcm_mm0_display: bcm-mm0_display { + cell-id = ; + label = "MM0_DISPLAY"; + qcom,bcm-name = "MM0"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + bcm_mm1_display: bcm-mm1_display { + cell-id = ; + label = "MM1_DISPLAY"; + qcom,bcm-name = "MM1"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + bcm_mm2_display: bcm-mm2_display { + cell-id = ; + label = "MM2_DISPLAY"; + qcom,bcm-name = "MM2"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + bcm_mm3_display: bcm-mm3_display { + cell-id = ; + label = "MM3_DISPLAY"; + qcom,bcm-name = "MM3"; + qcom,rscs = <&rsc_disp>; + qcom,bcm-dev; + }; + + /*Buses*/ + fab_aggre1_noc: fab-aggre1_noc { + cell-id = ; + label = "fab-aggre1_noc"; + qcom,fab-dev; + qcom,base-name = "aggre1_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <16384>; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_aggre2_noc: fab-aggre2_noc { + cell-id = ; + label = "fab-aggre2_noc"; + qcom,fab-dev; + qcom,base-name = "aggre2_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <16384>; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_camnoc_virt: fab-camnoc_virt { + cell-id = ; + label = "fab-camnoc_virt"; + qcom,fab-dev; + qcom,base-name = "camnoc_virt-base"; + qcom,bypass-qos-prg; + clocks = <>; + }; + + fab_config_noc: fab-config_noc { + cell-id = ; + label = "fab-config_noc"; + qcom,fab-dev; + qcom,base-name = "config_noc-base"; + qcom,bypass-qos-prg; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_dc_noc: fab-dc_noc { + cell-id = ; + label = "fab-dc_noc"; + qcom,fab-dev; + qcom,base-name = "dc_noc-base"; + qcom,bypass-qos-prg; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_gladiator_noc: fab-gladiator_noc { + cell-id = ; + label = "fab-gladiator_noc"; + qcom,fab-dev; + qcom,base-name = "gladiator_noc-base"; + qcom,bypass-qos-prg; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_ipa_virt: fab-ipa_virt { + cell-id = ; + label = "fab-ipa_virt"; + qcom,fab-dev; + qcom,base-name = "ipa_virt-base"; + qcom,bypass-qos-prg; + clocks = <>; + }; + + fab_mc_virt: fab-mc_virt { + cell-id = ; + label = "fab-mc_virt"; + qcom,fab-dev; + qcom,base-name = "mc_virt-base"; + qcom,bypass-qos-prg; + clocks = <>; + }; + + fab_mem_noc: fab-mem_noc { + cell-id = ; + label = "fab-mem_noc"; + qcom,fab-dev; + qcom,base-name = "mem_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <65536>; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_mmss_noc: fab-mmss_noc { + cell-id = ; + label = "fab-mmss_noc"; + qcom,fab-dev; + qcom,base-name = "mmss_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <36864>; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_system_noc: fab-system_noc { + cell-id = ; + label = "fab-system_noc"; + qcom,fab-dev; + qcom,base-name = "system_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <36864>; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_mc_virt_display: fab-mc_virt_display { + cell-id = ; + label = "fab-mc_virt_display"; + qcom,fab-dev; + qcom,base-name = "mc_virt-base"; + qcom,bypass-qos-prg; + clocks = <>; + }; + + fab_mem_noc_display: fab-mem_noc_display { + cell-id = ; + label = "fab-mem_noc_display"; + qcom,fab-dev; + qcom,base-name = "mem_noc-base"; + qcom,qos-off = <4096>; + qcom,base-offset = <65536>; + qcom,bypass-qos-prg; + qcom,bus-type = <1>; + clocks = <>; + }; + + fab_mmss_noc_display: fab-mmss_noc_display { + cell-id = ; + label = "fab-mmss_noc_display"; + qcom,fab-dev; + qcom,base-name = "mmss_noc-base"; + qcom,bypass-qos-prg; + qcom,bus-type = <1>; + clocks = <>; + }; + + /*Masters*/ + + mas_qhm_a1noc_cfg: mas-qhm-a1noc-cfg { + cell-id = ; + label = "mas-qhm-a1noc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_aggre1_noc>; + qcom,bus-dev = <&fab_aggre1_noc>; + }; + + mas_qhm_qup1: mas-qhm-qup1 { + cell-id = ; + label = "mas-qhm-qup1"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,bcms = <&bcm_qup0>; + }; + + mas_qhm_tsif: mas-qhm-tsif { + cell-id = ; + label = "mas-qhm-tsif"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + }; + + mas_xm_sdc2: mas-xm-sdc2 { + cell-id = ; + label = "mas-xm-sdc2"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <1>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,ap-owned; + qcom,prio = <1>; + }; + + mas_xm_sdc4: mas-xm-sdc4 { + cell-id = ; + label = "mas-xm-sdc4"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <2>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,ap-owned; + qcom,prio = <1>; + }; + + mas_xm_ufs_card: mas-xm-ufs-card { + cell-id = ; + label = "mas-xm-ufs-card"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <3>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_xm_ufs_mem: mas-xm-ufs-mem { + cell-id = ; + label = "mas-xm-ufs-mem"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <4>; + qcom,connections = <&slv_qns_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_xm_pcie_0: mas-xm-pcie-0 { + cell-id = ; + label = "mas-xm-pcie-0"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <5>; + qcom,connections = <&slv_qns_pcie_a1noc_snoc>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_qhm_a2noc_cfg: mas-qhm-a2noc-cfg { + cell-id = ; + label = "mas-qhm-a2noc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_aggre2_noc>; + qcom,bus-dev = <&fab_aggre2_noc>; + }; + + mas_qhm_qdss_bam: mas-qhm-qdss-bam { + cell-id = ; + label = "mas-qhm-qdss-bam"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + }; + + mas_qhm_qup2: mas-qhm-qup2 { + cell-id = ; + label = "mas-qhm-qup2"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,bcms = <&bcm_qup0>; + }; + + mas_qnm_cnoc: mas-qnm-cnoc { + cell-id = ; + label = "mas-qnm-cnoc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <0>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <1>; + }; + + mas_qxm_crypto: mas-qxm-crypto { + cell-id = ; + label = "mas-qxm-crypto"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <1>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,bcms = <&bcm_ce0>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_qxm_ipa: mas-qxm-ipa { + cell-id = ; + label = "mas-qxm-ipa"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <2>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <2>; + qcom,defer-init-qos; + qcom,node-qos-bcms = <7035 0 1>; + }; + + mas_xm_pcie3_1: mas-xm-pcie3-1 { + cell-id = ; + label = "mas-xm-pcie3-1"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <6>; + qcom,connections = <&slv_qns_pcie_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_xm_qdss_etr: mas-xm-qdss-etr { + cell-id = ; + label = "mas-xm-qdss-etr"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <7>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_xm_usb3_0: mas-xm-usb3-0 { + cell-id = ; + label = "mas-xm-usb3-0"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <10>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <2>; + qcom,node-qos-clks { + clocks = + <&clock_gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>; + clock-names = + "clk-usb3-prim-axi-no-rate"; + }; + }; + + mas_xm_usb3_1: mas-xm-usb3-1 { + cell-id = ; + label = "mas-xm-usb3-1"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <11>; + qcom,connections = <&slv_qns_a2noc_snoc>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,ap-owned; + qcom,prio = <2>; + qcom,node-qos-clks { + clocks = + <&clock_gcc GCC_AGGRE_USB3_SEC_AXI_CLK>; + clock-names = + "clk-usb3-sec-axi-no-rate"; + }; + }; + + mas_qxm_camnoc_hf0_uncomp: mas-qxm-camnoc-hf0-uncomp { + cell-id = ; + label = "mas-qxm-camnoc-hf0-uncomp"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_camnoc_uncomp>; + qcom,bus-dev = <&fab_camnoc_virt>; + qcom,bcms = <&bcm_mm1>; + }; + + mas_qxm_camnoc_hf1_uncomp: mas-qxm-camnoc-hf1-uncomp { + cell-id = ; + label = "mas-qxm-camnoc-hf1-uncomp"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_camnoc_uncomp>; + qcom,bus-dev = <&fab_camnoc_virt>; + qcom,bcms = <&bcm_mm1>; + }; + + mas_qxm_camnoc_sf_uncomp: mas-qxm-camnoc-sf-uncomp { + cell-id = ; + label = "mas-qxm-camnoc-sf-uncomp"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_camnoc_uncomp>; + qcom,bus-dev = <&fab_camnoc_virt>; + qcom,bcms = <&bcm_mm1>; + }; + + mas_qhm_spdm: mas-qhm-spdm { + cell-id = ; + label = "mas-qhm-spdm"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qns_cnoc_a2noc>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + mas_qnm_snoc: mas-qnm-snoc { + cell-id = ; + label = "mas-qnm-snoc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qhs_tlmm_south + &slv_qhs_spss_cfg &slv_qhs_camera_cfg + &slv_qhs_sdc4 &slv_qhs_sdc2 + &slv_qhs_mnoc_cfg &slv_qhs_ufs_mem_cfg + &slv_qhs_snoc_cfg &slv_qhs_glm + &slv_qhs_pdm &slv_qhs_a2_noc_cfg + &slv_qhs_qdss_cfg &slv_qhs_display_cfg + &slv_qhs_tcsr &slv_qhs_dcc_cfg + &slv_qhs_ddrss_cfg &slv_qhs_phy_refgen_south + &slv_qhs_pcie_gen3_cfg &slv_qhs_pcie0_cfg + &slv_qhs_gpuss_cfg &slv_qhs_venus_cfg + &slv_qhs_tsif &slv_qhs_compute_dsp_cfg + &slv_qhs_aop &slv_qhs_qupv3_north + &slv_qhs_usb3_0 &slv_srvc_cnoc + &slv_qhs_ufs_card_cfg &slv_qhs_usb3_1 + &slv_qhs_ipa &slv_qhs_cpr_cx + &slv_qhs_a1_noc_cfg &slv_qhs_aoss + &slv_qhs_prng &slv_qhs_vsense_ctrl_cfg + &slv_qhs_qupv3_south &slv_qhs_spdm + &slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg + &slv_qhs_tlmm_north &slv_qhs_clk_ctl + &slv_qhs_imem_cfg>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + mas_qhm_cnoc: mas-qhm-cnoc { + cell-id = ; + label = "mas-qhm-cnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qhs_memnoc &slv_qhs_llcc>; + qcom,bus-dev = <&fab_dc_noc>; + }; + + mas_acm_l3: mas-acm-l3 { + cell-id = ; + label = "mas-acm-l3"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_gnoc + &slv_qns_gladiator_sodv &slv_qns_gnoc_memnoc>; + qcom,bus-dev = <&fab_gladiator_noc>; + }; + + mas_pm_gnoc_cfg: mas-pm-gnoc-cfg { + cell-id = ; + label = "mas-pm-gnoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_gnoc>; + qcom,bus-dev = <&fab_gladiator_noc>; + }; + + mas_ipa_core_master: mas-ipa-core-master { + cell-id = ; + label = "mas-ipa-core-master"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_ipa_core_slave>; + qcom,bus-dev = <&fab_ipa_virt>; + }; + + mas_llcc_mc: mas-llcc-mc { + cell-id = ; + label = "mas-llcc-mc"; + qcom,buswidth = <4>; + qcom,agg-ports = <4>; + qcom,connections = <&slv_ebi>; + qcom,bus-dev = <&fab_mc_virt>; + }; + + mas_acm_tcu: mas-acm-tcu { + cell-id = ; + label = "mas-acm-tcu"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <0>; + qcom,connections = <&slv_qns_apps_io &slv_qns_llcc + &slv_qns_memnoc_snoc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,bcms = <&bcm_sh3>; + qcom,ap-owned; + qcom,prio = <7>; + }; + + mas_qhm_memnoc_cfg: mas-qhm-memnoc-cfg { + cell-id = ; + label = "mas-qhm-memnoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_memnoc + &slv_qhs_mdsp_ms_mpu_cfg>; + qcom,bus-dev = <&fab_mem_noc>; + }; + + mas_qnm_apps: mas-qnm-apps { + cell-id = ; + label = "mas-qnm-apps"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,qport = <2 3>; + qcom,connections = <&slv_qns_llcc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,bcms = <&bcm_sh5>; + qcom,ap-owned; + qcom,prio = <0>; + }; + + mas_qnm_mnoc_hf: mas-qnm-mnoc-hf { + cell-id = ; + label = "mas-qnm-mnoc-hf"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,qport = <4 5>; + qcom,connections = <&slv_qns_apps_io &slv_qns_llcc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qnm_mnoc_sf: mas-qnm-mnoc-sf { + cell-id = ; + label = "mas-qnm-mnoc-sf"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <7>; + qcom,connections = <&slv_qns_apps_io + &slv_qns_llcc &slv_qns_memnoc_snoc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qnm_snoc_gc: mas-qnm-snoc-gc { + cell-id = ; + label = "mas-qnm-snoc-gc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <8>; + qcom,connections = <&slv_qns_llcc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + }; + + mas_qnm_snoc_sf: mas-qnm-snoc-sf { + cell-id = ; + label = "mas-qnm-snoc-sf"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,qport = <9>; + qcom,connections = <&slv_qns_apps_io &slv_qns_llcc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + }; + + mas_qxm_gpu: mas-qxm-gpu { + cell-id = ; + label = "mas-qxm-gpu"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,qport = <10 11>; + qcom,connections = <&slv_qns_apps_io + &slv_qns_llcc &slv_qns_memnoc_snoc>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,ap-owned; + qcom,prio = <0>; + }; + + mas_qhm_mnoc_cfg: mas-qhm-mnoc-cfg { + cell-id = ; + label = "mas-qhm-mnoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_mnoc>; + qcom,bus-dev = <&fab_mmss_noc>; + }; + + mas_qxm_camnoc_hf0: mas-qxm-camnoc-hf0 { + cell-id = ; + label = "mas-qxm-camnoc-hf0"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <1>; + qcom,connections = <&slv_qns_mem_noc_hf>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm1>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_camnoc_hf1: mas-qxm-camnoc-hf1 { + cell-id = ; + label = "mas-qxm-camnoc-hf1"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <2>; + qcom,connections = <&slv_qns_mem_noc_hf>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm1>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_camnoc_sf: mas-qxm-camnoc-sf { + cell-id = ; + label = "mas-qxm-camnoc-sf"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <0>; + qcom,connections = <&slv_qns2_mem_noc>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm3>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_mdp0: mas-qxm-mdp0 { + cell-id = ; + label = "mas-qxm-mdp0"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <3>; + qcom,connections = <&slv_qns_mem_noc_hf>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm1>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_mdp1: mas-qxm-mdp1 { + cell-id = ; + label = "mas-qxm-mdp1"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <4>; + qcom,connections = <&slv_qns_mem_noc_hf>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm1>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_rot: mas-qxm-rot { + cell-id = ; + label = "mas-qxm-rot"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <5>; + qcom,connections = <&slv_qns2_mem_noc>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm3>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_venus0: mas-qxm-venus0 { + cell-id = ; + label = "mas-qxm-venus0"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <6>; + qcom,connections = <&slv_qns2_mem_noc>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm3>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_venus1: mas-qxm-venus1 { + cell-id = ; + label = "mas-qxm-venus1"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <7>; + qcom,connections = <&slv_qns2_mem_noc>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm3>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qxm_venus_arm9: mas-qxm-venus-arm9 { + cell-id = ; + label = "mas-qxm-venus-arm9"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <8>; + qcom,connections = <&slv_qns2_mem_noc>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,bcms = <&bcm_mm3>; + qcom,ap-owned; + qcom,prio = <0>; + qcom,forwarding; + qcom,node-qos-bcms = <7012 0 1>; + }; + + mas_qhm_snoc_cfg: mas-qhm-snoc-cfg { + cell-id = ; + label = "mas-qhm-snoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_srvc_snoc>; + qcom,bus-dev = <&fab_system_noc>; + }; + + mas_qnm_aggre1_noc: mas-qnm-aggre1-noc { + cell-id = ; + label = "mas-qnm-aggre1-noc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qxs_pimem + &slv_qns_memnoc_sf &slv_qxs_imem + &slv_qhs_apss &slv_qns_cnoc + &slv_xs_qdss_stm>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn9>; + }; + + mas_qnm_aggre2_noc: mas-qnm-aggre2-noc { + cell-id = ; + label = "mas-qnm-aggre2-noc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qxs_pimem + &slv_qns_memnoc_sf &slv_qxs_pcie_gen3 + &slv_qxs_imem &slv_qhs_apss + &slv_qns_cnoc &slv_qxs_pcie + &slv_xs_sys_tcu_cfg &slv_xs_qdss_stm>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn11>; + }; + + mas_qnm_gladiator_sodv: mas-qnm-gladiator-sodv { + cell-id = ; + label = "mas-qnm-gladiator-sodv"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qxs_pimem + &slv_qxs_pcie_gen3 &slv_qxs_imem + &slv_qhs_apss &slv_qns_cnoc + &slv_qxs_pcie &slv_xs_sys_tcu_cfg + &slv_xs_qdss_stm>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn12>; + }; + + mas_qnm_memnoc: mas-qnm-memnoc { + cell-id = ; + label = "mas-qnm-memnoc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qxs_imem + &slv_qhs_apss &slv_qxs_pimem + &slv_qns_cnoc &slv_xs_qdss_stm>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn15>; + }; + + mas_qnm_pcie_anoc: mas-qnm-pcie-anoc { + cell-id = ; + label = "mas-qnm-pcie-anoc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,connections = <&slv_qxs_imem + &slv_qhs_apss &slv_qns_cnoc + &slv_qns_memnoc_sf &slv_xs_qdss_stm>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn14>; + }; + + mas_qxm_pimem: mas-qxm-pimem { + cell-id = ; + label = "mas-qxm-pimem"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <3>; + qcom,connections = <&slv_qxs_imem &slv_qns_memnoc_gc>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn4>; + qcom,ap-owned; + qcom,prio = <2>; + }; + + mas_xm_gic: mas-xm-gic { + cell-id = ; + label = "mas-xm-gic"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,qport = <0>; + qcom,connections = <&slv_qxs_imem &slv_qns_memnoc_gc>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn12>; + qcom,ap-owned; + qcom,prio = <1>; + }; + + mas_alc: mas-alc { + cell-id = ; + label = "mas-alc"; + qcom,buswidth = <1>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mc_virt>; + qcom,bcms = <&bcm_alc>; + }; + + mas_llcc_mc_display: mas-llcc-mc_display { + cell-id = ; + label = "mas-llcc-mc_display"; + qcom,buswidth = <4>; + qcom,agg-ports = <4>; + qcom,connections = <&slv_ebi_display>; + qcom,bus-dev = <&fab_mc_virt_display>; + }; + + mas_qnm_mnoc_hf_display: mas-qnm-mnoc-hf_display { + cell-id = ; + label = "mas-qnm-mnoc-hf_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,qport = <4 5>; + qcom,connections = <&slv_qns_llcc_display>; + qcom,bus-dev = <&fab_mem_noc_display>; + }; + + mas_qnm_mnoc_sf_display: mas-qnm-mnoc-sf_display { + cell-id = ; + label = "mas-qnm-mnoc-sf_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <7>; + qcom,connections = <&slv_qns_llcc_display>; + qcom,bus-dev = <&fab_mem_noc_display>; + }; + + mas_qxm_mdp0_display: mas-qxm-mdp0_display { + cell-id = ; + label = "mas-qxm-mdp0_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <3>; + qcom,connections = <&slv_qns_mem_noc_hf_display>; + qcom,bus-dev = <&fab_mmss_noc_display>; + qcom,bcms = <&bcm_mm1_display>; + }; + + mas_qxm_mdp1_display: mas-qxm-mdp1_display { + cell-id = ; + label = "mas-qxm-mdp1_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <4>; + qcom,connections = <&slv_qns_mem_noc_hf_display>; + qcom,bus-dev = <&fab_mmss_noc_display>; + qcom,bcms = <&bcm_mm1_display>; + }; + + mas_qxm_rot_display: mas-qxm-rot_display { + cell-id = ; + label = "mas-qxm-rot_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,qport = <5>; + qcom,connections = <&slv_qns2_mem_noc_display>; + qcom,bus-dev = <&fab_mmss_noc_display>; + qcom,bcms = <&bcm_mm3_display>; + }; + + /*Internal nodes*/ + + /*Slaves*/ + + slv_qns_a1noc_snoc:slv-qns-a1noc-snoc { + cell-id = ; + label = "slv-qns-a1noc-snoc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,connections = <&mas_qnm_aggre1_noc>; + }; + + slv_srvc_aggre1_noc:slv-srvc-aggre1-noc { + cell-id = ; + label = "slv-srvc-aggre1-noc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,bcms = <&bcm_sn9>; + }; + + slv_qns_pcie_a1noc_snoc:slv-qns-pcie-a1noc-snoc { + cell-id = ; + label = "slv-qns-pcie-a1noc-snoc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre1_noc>; + qcom,connections = <&mas_qnm_pcie_anoc>; + }; + + slv_qns_a2noc_snoc:slv-qns-a2noc-snoc { + cell-id = ; + label = "slv-qns-a2noc-snoc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,connections = <&mas_qnm_aggre2_noc>; + }; + + slv_qns_pcie_snoc:slv-qns-pcie-snoc { + cell-id = ; + label = "slv-qns-pcie-snoc"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,connections = <&mas_qnm_pcie_anoc>; + }; + + slv_srvc_aggre2_noc:slv-srvc-aggre2-noc { + cell-id = ; + label = "slv-srvc-aggre2-noc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_aggre2_noc>; + qcom,bcms = <&bcm_sn11>; + }; + + slv_qns_camnoc_uncomp:slv-qns-camnoc-uncomp { + cell-id = ; + label = "slv-qns-camnoc-uncomp"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_camnoc_virt>; + }; + + slv_qhs_a1_noc_cfg:slv-qhs-a1-noc-cfg { + cell-id = ; + label = "slv-qhs-a1-noc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qhm_a1noc_cfg>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_a2_noc_cfg:slv-qhs-a2-noc-cfg { + cell-id = ; + label = "slv-qhs-a2-noc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qhm_a2noc_cfg>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_aop:slv-qhs-aop { + cell-id = ; + label = "slv-qhs-aop"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_aoss:slv-qhs-aoss { + cell-id = ; + label = "slv-qhs-aoss"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_camera_cfg:slv-qhs-camera-cfg { + cell-id = ; + label = "slv-qhs-camera-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_clk_ctl:slv-qhs-clk-ctl { + cell-id = ; + label = "slv-qhs-clk-ctl"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_compute_dsp_cfg:slv-qhs-compute-dsp-cfg { + cell-id = ; + label = "slv-qhs-compute-dsp-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_cpr_cx:slv-qhs-cpr-cx { + cell-id = ; + label = "slv-qhs-cpr-cx"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_crypto0_cfg:slv-qhs-crypto0-cfg { + cell-id = ; + label = "slv-qhs-crypto0-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_dcc_cfg:slv-qhs-dcc-cfg { + cell-id = ; + label = "slv-qhs-dcc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qhm_cnoc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_ddrss_cfg:slv-qhs-ddrss-cfg { + cell-id = ; + label = "slv-qhs-ddrss-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_display_cfg:slv-qhs-display-cfg { + cell-id = ; + label = "slv-qhs-display-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_glm:slv-qhs-glm { + cell-id = ; + label = "slv-qhs-glm"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_gpuss_cfg:slv-qhs-gpuss-cfg { + cell-id = ; + label = "slv-qhs-gpuss-cfg"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_imem_cfg:slv-qhs-imem-cfg { + cell-id = ; + label = "slv-qhs-imem-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_ipa:slv-qhs-ipa { + cell-id = ; + label = "slv-qhs-ipa"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_mnoc_cfg:slv-qhs-mnoc-cfg { + cell-id = ; + label = "slv-qhs-mnoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qhm_mnoc_cfg>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_pcie0_cfg:slv-qhs-pcie0-cfg { + cell-id = ; + label = "slv-qhs-pcie0-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_pcie_gen3_cfg:slv-qhs-pcie-gen3-cfg { + cell-id = ; + label = "slv-qhs-pcie-gen3-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_pdm:slv-qhs-pdm { + cell-id = ; + label = "slv-qhs-pdm"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_phy_refgen_south:slv-qhs-phy-refgen-south { + cell-id = ; + label = "slv-qhs-phy-refgen-south"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_pimem_cfg:slv-qhs-pimem-cfg { + cell-id = ; + label = "slv-qhs-pimem-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_prng:slv-qhs-prng { + cell-id = ; + label = "slv-qhs-prng"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_qdss_cfg:slv-qhs-qdss-cfg { + cell-id = ; + label = "slv-qhs-qdss-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_qupv3_north:slv-qhs-qupv3-north { + cell-id = ; + label = "slv-qhs-qupv3-north"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_qupv3_south:slv-qhs-qupv3-south { + cell-id = ; + label = "slv-qhs-qupv3-south"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_sdc2:slv-qhs-sdc2 { + cell-id = ; + label = "slv-qhs-sdc2"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_sdc4:slv-qhs-sdc4 { + cell-id = ; + label = "slv-qhs-sdc4"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_snoc_cfg:slv-qhs-snoc-cfg { + cell-id = ; + label = "slv-qhs-snoc-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qhm_snoc_cfg>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_spdm:slv-qhs-spdm { + cell-id = ; + label = "slv-qhs-spdm"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_spss_cfg:slv-qhs-spss-cfg { + cell-id = ; + label = "slv-qhs-spss-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_tcsr:slv-qhs-tcsr { + cell-id = ; + label = "slv-qhs-tcsr"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_tlmm_north:slv-qhs-tlmm-north { + cell-id = ; + label = "slv-qhs-tlmm-north"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_tlmm_south:slv-qhs-tlmm-south { + cell-id = ; + label = "slv-qhs-tlmm-south"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_tsif:slv-qhs-tsif { + cell-id = ; + label = "slv-qhs-tsif"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_ufs_card_cfg:slv-qhs-ufs-card-cfg { + cell-id = ; + label = "slv-qhs-ufs-card-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_ufs_mem_cfg:slv-qhs-ufs-mem-cfg { + cell-id = ; + label = "slv-qhs-ufs-mem-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_usb3_0:slv-qhs-usb3-0 { + cell-id = ; + label = "slv-qhs-usb3-0"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_usb3_1:slv-qhs-usb3-1 { + cell-id = ; + label = "slv-qhs-usb3-1"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_venus_cfg:slv-qhs-venus-cfg { + cell-id = ; + label = "slv-qhs-venus-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_vsense_ctrl_cfg:slv-qhs-vsense-ctrl-cfg { + cell-id = ; + label = "slv-qhs-vsense-ctrl-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qns_cnoc_a2noc:slv-qns-cnoc-a2noc { + cell-id = ; + label = "slv-qns-cnoc-a2noc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,connections = <&mas_qnm_cnoc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_srvc_cnoc:slv-srvc-cnoc { + cell-id = ; + label = "slv-srvc-cnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_config_noc>; + qcom,bcms = <&bcm_cn0>; + }; + + slv_qhs_llcc:slv-qhs-llcc { + cell-id = ; + label = "slv-qhs-llcc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_dc_noc>; + }; + + slv_qhs_memnoc:slv-qhs-memnoc { + cell-id = ; + label = "slv-qhs-memnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_dc_noc>; + qcom,connections = <&mas_qhm_memnoc_cfg>; + }; + + slv_qns_gladiator_sodv:slv-qns-gladiator-sodv { + cell-id = ; + label = "slv-qns-gladiator-sodv"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_gladiator_noc>; + qcom,connections = <&mas_qnm_gladiator_sodv>; + }; + + slv_qns_gnoc_memnoc:slv-qns-gnoc-memnoc { + cell-id = ; + label = "slv-qns-gnoc-memnoc"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,bus-dev = <&fab_gladiator_noc>; + qcom,connections = <&mas_qnm_apps>; + }; + + slv_srvc_gnoc:slv-srvc-gnoc { + cell-id = ; + label = "slv-srvc-gnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_gladiator_noc>; + }; + + slv_ipa_core_slave:slv-ipa-core-slave { + cell-id = ; + label = "slv-ipa-core-slave"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_ipa_virt>; + qcom,bcms = <&bcm_ip0>; + }; + + slv_ebi:slv-ebi { + cell-id = ; + label = "slv-ebi"; + qcom,buswidth = <4>; + qcom,agg-ports = <4>; + qcom,bus-dev = <&fab_mc_virt>; + qcom,bcms = <&bcm_mc0>, <&bcm_acv>; + }; + + slv_qhs_mdsp_ms_mpu_cfg:slv-qhs-mdsp-ms-mpu-cfg { + cell-id = ; + label = "slv-qhs-mdsp-ms-mpu-cfg"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mem_noc>; + }; + + slv_qns_apps_io:slv-qns-apps-io { + cell-id = ; + label = "slv-qns-apps-io"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,bcms = <&bcm_sh1>; + }; + + slv_qns_llcc:slv-qns-llcc { + cell-id = ; + label = "slv-qns-llcc"; + qcom,buswidth = <16>; + qcom,agg-ports = <4>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,connections = <&mas_llcc_mc>; + qcom,bcms = <&bcm_sh0>; + }; + + slv_qns_memnoc_snoc:slv-qns-memnoc-snoc { + cell-id = ; + label = "slv-qns-memnoc-snoc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mem_noc>; + qcom,connections = <&mas_qnm_memnoc>; + qcom,bcms = <&bcm_sh2>; + }; + + slv_srvc_memnoc:slv-srvc-memnoc { + cell-id = ; + label = "slv-srvc-memnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mem_noc>; + }; + + slv_qns2_mem_noc:slv-qns2-mem-noc { + cell-id = ; + label = "slv-qns2-mem-noc"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,connections = <&mas_qnm_mnoc_sf>; + qcom,bcms = <&bcm_mm2>; + }; + + slv_qns_mem_noc_hf:slv-qns-mem-noc-hf { + cell-id = ; + label = "slv-qns-mem-noc-hf"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,bus-dev = <&fab_mmss_noc>; + qcom,connections = <&mas_qnm_mnoc_hf>; + qcom,bcms = <&bcm_mm0>; + }; + + slv_srvc_mnoc:slv-srvc-mnoc { + cell-id = ; + label = "slv-srvc-mnoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mmss_noc>; + }; + + slv_qhs_apss:slv-qhs-apss { + cell-id = ; + label = "slv-qhs-apss"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn6>; + }; + + slv_qns_cnoc:slv-qns-cnoc { + cell-id = ; + label = "slv-qns-cnoc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,connections = <&mas_qnm_snoc>; + qcom,bcms = <&bcm_sn3>; + }; + + slv_qns_memnoc_gc:slv-qns-memnoc-gc { + cell-id = ; + label = "slv-qns-memnoc-gc"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,connections = <&mas_qnm_snoc_gc>; + qcom,bcms = <&bcm_sn2>; + }; + + slv_qns_memnoc_sf:slv-qns-memnoc-sf { + cell-id = ; + label = "slv-qns-memnoc-sf"; + qcom,buswidth = <16>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,connections = <&mas_qnm_snoc_sf>; + qcom,bcms = <&bcm_sn0>; + }; + + slv_qxs_imem:slv-qxs-imem { + cell-id = ; + label = "slv-qxs-imem"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn1>; + }; + + slv_qxs_pcie:slv-qxs-pcie { + cell-id = ; + label = "slv-qxs-pcie"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn7>; + }; + + slv_qxs_pcie_gen3:slv-qxs-pcie-gen3 { + cell-id = ; + label = "slv-qxs-pcie-gen3"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn8>; + }; + + slv_qxs_pimem:slv-qxs-pimem { + cell-id = ; + label = "slv-qxs-pimem"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + }; + + slv_srvc_snoc:slv-srvc-snoc { + cell-id = ; + label = "slv-srvc-snoc"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn6>; + }; + + slv_xs_qdss_stm:slv-xs-qdss-stm { + cell-id = ; + label = "slv-xs-qdss-stm"; + qcom,buswidth = <4>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn5>; + }; + + slv_xs_sys_tcu_cfg:slv-xs-sys-tcu-cfg { + cell-id = ; + label = "slv-xs-sys-tcu-cfg"; + qcom,buswidth = <8>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_system_noc>; + qcom,bcms = <&bcm_sn6>; + }; + + slv_ebi_display:slv-ebi_display { + cell-id = ; + label = "slv-ebi_display"; + qcom,buswidth = <4>; + qcom,agg-ports = <4>; + qcom,bus-dev = <&fab_mc_virt_display>; + qcom,bcms = <&bcm_mc0_display>; + }; + + slv_qns_llcc_display:slv-qns-llcc_display { + cell-id = ; + label = "slv-qns-llcc_display"; + qcom,buswidth = <16>; + qcom,agg-ports = <4>; + qcom,bus-dev = <&fab_mem_noc_display>; + qcom,connections = <&mas_llcc_mc_display>; + qcom,bcms = <&bcm_sh0_display>; + }; + + slv_qns2_mem_noc_display:slv-qns2-mem-noc_display { + cell-id = ; + label = "slv-qns2-mem-noc_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mmss_noc_display>; + qcom,connections = <&mas_qnm_mnoc_sf_display>; + qcom,bcms = <&bcm_mm2_display>; + }; + + slv_qns_mem_noc_hf_display:slv-qns-mem-noc-hf_display { + cell-id = ; + label = "slv-qns-mem-noc-hf_display"; + qcom,buswidth = <32>; + qcom,agg-ports = <2>; + qcom,bus-dev = <&fab_mmss_noc_display>; + qcom,connections = <&mas_qnm_mnoc_hf_display>; + qcom,bcms = <&bcm_mm0_display>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi new file mode 100644 index 000000000000..a3550ca9acde --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi new file mode 100644 index 000000000000..e7717a1d3afb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi @@ -0,0 +1,1093 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ + +&soc { + qcom,cam-req-mgr { + compatible = "qcom,cam-req-mgr"; + status = "ok"; + }; + + cam_csiphy0: qcom,csiphy@ac65000 { + cell-index = <0>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0x0ac65000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x65000>; + interrupts = ; + interrupt-names = "csiphy"; + gdscr-supply = <&titan_top_gdsc>; + regulator-names = "gdscr"; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY0_CLK>, + <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy0_clk", + "csi0phytimer_clk_src", + "csi0phytimer_clk"; + src-clock-name = "csi0phytimer_clk_src"; + clock-cntl-level = "svs", "turbo"; + clock-rates = + <0 0 0 0 320000000 0 269333333 0>, + <0 0 0 0 384000000 0 269333333 0>; + status = "ok"; + }; + + cam_csiphy1: qcom,csiphy@ac66000{ + cell-index = <1>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0xac66000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x66000>; + interrupts = ; + interrupt-names = "csiphy"; + gdscr-supply = <&titan_top_gdsc>; + regulator-names = "gdscr"; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY1_CLK>, + <&clock_camcc CAM_CC_CSI1PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI1PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy1_clk", + "csi1phytimer_clk_src", + "csi1phytimer_clk"; + src-clock-name = "csi1phytimer_clk_src"; + clock-cntl-level = "svs", "turbo"; + clock-rates = + <0 0 0 0 320000000 0 269333333 0>, + <0 0 0 0 384000000 0 269333333 0>; + + status = "ok"; + }; + + cam_csiphy2: qcom,csiphy@ac67000 { + cell-index = <2>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0xac67000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x67000>; + interrupts = ; + interrupt-names = "csiphy"; + gdscr-supply = <&titan_top_gdsc>; + regulator-names = "gdscr"; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY2_CLK>, + <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy2_clk", + "csi2phytimer_clk_src", + "csi2phytimer_clk"; + src-clock-name = "csi2phytimer_clk_src"; + clock-cntl-level = "svs", "turbo"; + clock-rates = + <0 0 0 0 320000000 0 269333333 0>, + <0 0 0 0 384000000 0 269333333 0>; + status = "ok"; + }; + + cam_cci: qcom,cci@ac4a000 { + cell-index = <0>; + compatible = "qcom,cci"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xac4a000 0x4000>; + reg-names = "cci"; + reg-cam-base = <0x4a000>; + interrupt-names = "cci"; + interrupts = ; + status = "ok"; + gdscr-supply = <&titan_top_gdsc>; + regulator-names = "gdscr"; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CCI_CLK>, + <&clock_camcc CAM_CC_CCI_CLK_SRC>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cci_clk", + "cci_clk_src"; + src-clock-name = "cci_clk_src"; + clock-cntl-level = "lowsvs"; + clock-rates = <0 0 0 0 0 37500000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cci0_active &cci1_active>; + pinctrl-1 = <&cci0_suspend &cci1_suspend>; + gpios = <&tlmm 17 0>, + <&tlmm 18 0>, + <&tlmm 19 0>, + <&tlmm 20 0>; + gpio-req-tbl-num = <0 1 2 3>; + gpio-req-tbl-flags = <1 1 1 1>; + gpio-req-tbl-label = "CCI_I2C_DATA0", + "CCI_I2C_CLK0", + "CCI_I2C_DATA1", + "CCI_I2C_CLK1"; + + i2c_freq_100Khz: qcom,i2c_standard_mode { + hw-thigh = <201>; + hw-tlow = <174>; + hw-tsu-sto = <204>; + hw-tsu-sta = <231>; + hw-thd-dat = <22>; + hw-thd-sta = <162>; + hw-tbuf = <227>; + hw-scl-stretch-en = <0>; + hw-trdhld = <6>; + hw-tsp = <3>; + cci-clk-src = <37500000>; + status = "ok"; + }; + + i2c_freq_400Khz: qcom,i2c_fast_mode { + hw-thigh = <38>; + hw-tlow = <56>; + hw-tsu-sto = <40>; + hw-tsu-sta = <40>; + hw-thd-dat = <22>; + hw-thd-sta = <35>; + hw-tbuf = <62>; + hw-scl-stretch-en = <0>; + hw-trdhld = <6>; + hw-tsp = <3>; + cci-clk-src = <37500000>; + status = "ok"; + }; + + i2c_freq_custom: qcom,i2c_custom_mode { + hw-thigh = <38>; + hw-tlow = <56>; + hw-tsu-sto = <40>; + hw-tsu-sta = <40>; + hw-thd-dat = <22>; + hw-thd-sta = <35>; + hw-tbuf = <62>; + hw-scl-stretch-en = <1>; + hw-trdhld = <6>; + hw-tsp = <3>; + cci-clk-src = <37500000>; + status = "ok"; + }; + + i2c_freq_1Mhz: qcom,i2c_fast_plus_mode { + hw-thigh = <16>; + hw-tlow = <22>; + hw-tsu-sto = <17>; + hw-tsu-sta = <18>; + hw-thd-dat = <16>; + hw-thd-sta = <15>; + hw-tbuf = <24>; + hw-scl-stretch-en = <0>; + hw-trdhld = <3>; + hw-tsp = <3>; + cci-clk-src = <37500000>; + status = "ok"; + }; + }; + + qcom,cam_smmu { + compatible = "qcom,msm-cam-smmu"; + status = "ok"; + + msm_cam_smmu_ife { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x808 0x0>, + <&apps_smmu 0x810 0x8>, + <&apps_smmu 0xc08 0x0>, + <&apps_smmu 0xc10 0x8>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "ife"; + ife_iova_mem_map: iova-mem-map { + /* IO region is approximately 3.4 GB */ + iova-mem-region-io { + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_jpeg { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1060 0x8>, + <&apps_smmu 0x1068 0x8>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "jpeg"; + jpeg_iova_mem_map: iova-mem-map { + /* IO region is approximately 3.4 GB */ + iova-mem-region-io { + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_icp_fw { + compatible = "qcom,msm-cam-smmu-fw-dev"; + label="icp"; + memory-region = <&pil_camera_mem>; + }; + + msm_cam_smmu_icp { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1078 0x2>, + <&apps_smmu 0x1020 0x8>, + <&apps_smmu 0x1040 0x8>, + <&apps_smmu 0x1030 0x0>, + <&apps_smmu 0x1050 0x0>; + qcom,iommu-dma-addr-pool = <0x10c00000 0xcf300000>; + label = "icp"; + icp_iova_mem_map: iova-mem-map { + iova-mem-region-firmware { + /* Firmware region is 5MB */ + iova-region-name = "firmware"; + iova-region-start = <0x0>; + iova-region-len = <0x500000>; + iova-region-id = <0x0>; + status = "ok"; + }; + + iova-mem-region-shared { + /* Shared region is 150MB long */ + iova-region-name = "shared"; + iova-region-start = <0x7400000>; + iova-region-len = <0x9600000>; + iova-region-id = <0x1>; + status = "ok"; + }; + + iova-mem-region-secondary-heap { + /* Secondary heap region is 1MB long */ + iova-region-name = "secheap"; + iova-region-start = <0x10A00000>; + iova-region-len = <0x100000>; + iova-region-id = <0x4>; + status = "ok"; + }; + + iova-mem-region-io { + /* IO region is approximately 3 GB */ + iova-region-name = "io"; + iova-region-start = <0x10C00000>; + iova-region-len = <0xCF300000>; + iova-region-id = <0x3>; + status = "ok"; + }; + + iova-mem-qdss-region { + /* qdss region is approximately 1MB */ + iova-region-name = "qdss"; + iova-region-start = <0x10B00000>; + iova-region-len = <0x100000>; + iova-region-id = <0x5>; + qdss-phy-addr = <0x16790000>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_cpas_cdm { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1000 0x0>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "cpas-cdm0"; + cpas_cdm_iova_mem_map: iova-mem-map { + iova-mem-region-io { + /* IO region is approximately 3.4 GB */ + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_secure { + compatible = "qcom,msm-cam-smmu-cb"; + label = "cam-secure"; + qcom,secure-cb; + }; + + msm_cam_smmu_fd { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1070 0x0>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "fd"; + fd_iova_mem_map: iova-mem-map { + iova-mem-region-io { + /* IO region is approximately 3.4 GB */ + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + }; + + qcom,cam-cpas@ac40000 { + cell-index = <0>; + compatible = "qcom,cam-cpas"; + label = "cpas"; + arch-compat = "cpas_top"; + status = "ok"; + reg-names = "cam_cpas_top", "cam_camnoc"; + reg = <0xac40000 0x1000>, + <0xac42000 0x5000>; + reg-cam-base = <0x40000 0x42000>; + interrupt-names = "cpas_camnoc"; + interrupts = ; + qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */ + camnoc-axi-min-ib-bw = <3000000000>; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "gcc_ahb_clk", + "gcc_axi_clk", + "soc_ahb_clk", + "slow_ahb_clk_src", + "cpas_ahb_clk", + "camnoc_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>; + src-clock-name = "slow_ahb_clk_src"; + clock-rates = <0 0 0 0 0 0>, + <0 0 0 19200000 0 0>, + <0 0 0 60000000 0 0>, + <0 0 0 66660000 0 0>, + <0 0 0 73840000 0 0>, + <0 0 0 80000000 0 0>, + <0 0 0 80000000 0 0>; + clock-cntl-level = "suspend", "minsvs", "lowsvs", "svs", + "svs_l1", "nominal", "turbo"; + qcom,msm-bus,name = "cam_ahb"; + qcom,msm-bus,num-cases = <7>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + , + , + , + , + ; + vdd-corners = ; + vdd-corner-ahb-mapping = "suspend", "minsvs", + "lowsvs", "svs", "svs_l1", + "nominal", "nominal", "nominal", + "turbo", "turbo"; + client-id-based; + client-names = + "csiphy0", "csiphy1", "csiphy2", "cci0", + "csid0", "csid1", "csid2", + "ife0", "ife1", "ife2", "ipe0", + "ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0", + "icp0", "jpeg-dma0", "jpeg-enc0", "fd0", "lrmecpas"; + client-axi-port-names = + "cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1", + "cam_hf_1", "cam_hf_2", "cam_hf_2", + "cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1", + "cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1", + "cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1", + "cam_sf_1"; + client-bus-camnoc-based; + qcom,axi-port-list { + qcom,axi-port1 { + qcom,axi-port-name = "cam_hf_1"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_hf_1_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_hf_1_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + qcom,axi-port2 { + qcom,axi-port-name = "cam_hf_2"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_hf_2_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_hf_2_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + qcom,axi-port3 { + qcom,axi-port-name = "cam_sf_1"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_sf_1_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_sf_1_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + }; + }; + + qcom,cam-cdm-intf { + compatible = "qcom,cam-cdm-intf"; + cell-index = <0>; + label = "cam-cdm-intf"; + num-hw-cdm = <1>; + cdm-client-names = "vfe", + "jpegdma", + "jpegenc", + "fd", + "lrmecdm"; + status = "ok"; + }; + + qcom,cpas-cdm0@ac48000 { + cell-index = <0>; + compatible = "qcom,cam170-cpas-cdm0"; + label = "cpas-cdm"; + reg = <0xac48000 0x1000>; + reg-names = "cpas-cdm"; + reg-cam-base = <0x48000>; + interrupts = ; + interrupt-names = "cpas-cdm"; + regulator-names = "camss"; + camss-supply = <&titan_top_gdsc>; + clock-names = "gcc_camera_ahb", + "gcc_camera_axi", + "cam_cc_soc_ahb_clk", + "cam_cc_cpas_ahb_clk", + "cam_cc_camnoc_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>; + clock-rates = <0 0 0 0 0>; + clock-cntl-level = "svs"; + cdm-client-names = "ife"; + status = "ok"; + }; + + qcom,cam-isp { + compatible = "qcom,cam-isp"; + arch-compat = "ife"; + status = "ok"; + }; + + cam_csid0: qcom,csid0@acb3000 { + cell-index = <0>; + compatible = "qcom,csid170"; + reg-names = "csid"; + reg = <0xacb3000 0x1000>; + reg-cam-base = <0xb3000>; + interrupt-names = "csid"; + interrupts = ; + regulator-names = "camss", "ife0"; + camss-supply = <&titan_top_gdsc>; + ife0-supply = <&ife_0_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_csid_clk", + "ife_csid_clk_src", + "ife_cphy_rx_clk", + "cphy_rx_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk", + "ife_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_0_CSID_CLK>, + <&clock_camcc CAM_CC_IFE_0_CSID_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_0_CPHY_RX_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_0_CLK>, + <&clock_camcc CAM_CC_IFE_0_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_IFE_0_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>, + <0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>; + clock-cntl-level = "svs", "turbo"; + src-clock-name = "ife_csid_clk_src"; + clock-control-debugfs = "true"; + status = "ok"; + }; + + cam_vfe0: qcom,vfe0@acaf000 { + cell-index = <0>; + compatible = "qcom,vfe170"; + reg-names = "ife"; + reg = <0xacaf000 0x4000>; + reg-cam-base = <0xaf000>; + interrupt-names = "ife"; + interrupts = ; + regulator-names = "camss", "ife0"; + camss-supply = <&titan_top_gdsc>; + ife0-supply = <&ife_0_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk", + "ife_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_0_CLK>, + <&clock_camcc CAM_CC_IFE_0_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_IFE_0_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 404000000 0 0>, + <0 0 0 0 0 0 480000000 0 0>, + <0 0 0 0 0 0 600000000 0 0>; + clock-cntl-level = "svs", "svs_l1", "turbo"; + src-clock-name = "ife_clk_src"; + clock-control-debugfs = "true"; + clock-names-option = "ife_dsp_clk"; + clocks-option = <&clock_camcc CAM_CC_IFE_0_DSP_CLK>; + clock-rates-option = <600000000>; + status = "ok"; + }; + + cam_csid1: qcom,csid1@acba000 { + cell-index = <1>; + compatible = "qcom,csid170"; + reg-names = "csid"; + reg = <0xacba000 0x1000>; + reg-cam-base = <0xba000>; + interrupt-names = "csid"; + interrupts = ; + regulator-names = "camss", "ife1"; + camss-supply = <&titan_top_gdsc>; + ife1-supply = <&ife_1_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_csid_clk", + "ife_csid_clk_src", + "ife_cphy_rx_clk", + "cphy_rx_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk", + "ife_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_1_CSID_CLK>, + <&clock_camcc CAM_CC_IFE_1_CSID_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_1_CPHY_RX_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_1_CLK>, + <&clock_camcc CAM_CC_IFE_1_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_IFE_1_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>, + <0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>; + clock-cntl-level = "svs", "turbo"; + src-clock-name = "ife_csid_clk_src"; + clock-control-debugfs = "true"; + status = "ok"; + }; + + cam_vfe1: qcom,vfe1@acb6000 { + cell-index = <1>; + compatible = "qcom,vfe170"; + reg-names = "ife"; + reg = <0xacb6000 0x4000>; + reg-cam-base = <0xb6000>; + interrupt-names = "ife"; + interrupts = ; + regulator-names = "camss", "ife1"; + camss-supply = <&titan_top_gdsc>; + ife1-supply = <&ife_1_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk", + "ife_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_1_CLK>, + <&clock_camcc CAM_CC_IFE_1_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_IFE_1_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 404000000 0 0>, + <0 0 0 0 0 0 480000000 0 0>, + <0 0 0 0 0 0 600000000 0 0>; + clock-cntl-level = "svs", "svs_l1", "turbo"; + src-clock-name = "ife_clk_src"; + clock-control-debugfs = "true"; + clock-names-option = "ife_dsp_clk"; + clocks-option = <&clock_camcc CAM_CC_IFE_1_DSP_CLK>; + clock-rates-option = <600000000>; + status = "ok"; + }; + + cam_csid_lite: qcom,csid-lite@acc8000 { + cell-index = <2>; + compatible = "qcom,csid-lite170"; + reg-names = "csid-lite"; + reg = <0xacc8000 0x1000>; + reg-cam-base = <0xc8000>; + interrupt-names = "csid-lite"; + interrupts = ; + regulator-names = "camss"; + camss-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_csid_clk", + "ife_csid_clk_src", + "ife_cphy_rx_clk", + "cphy_rx_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK>, + <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_LITE_CLK>, + <&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 384000000 0 0 0 404000000 0>, + <0 0 0 0 0 0 538000000 0 0 0 600000000 0>; + clock-cntl-level = "svs", "turbo"; + src-clock-name = "ife_csid_clk_src"; + clock-control-debugfs = "true"; + status = "ok"; + }; + + cam_vfe_lite: qcom,vfe-lite@acc4000 { + cell-index = <2>; + compatible = "qcom,vfe-lite170"; + reg-names = "ife-lite"; + reg = <0xacc4000 0x4000>; + reg-cam-base = <0xc4000>; + interrupt-names = "ife-lite"; + interrupts = ; + regulator-names = "camss"; + camss-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "slow_ahb_clk_src", + "ife_clk", + "ife_clk_src", + "camnoc_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_IFE_LITE_CLK>, + <&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>; + clock-rates = + <0 0 0 0 0 0 404000000 0>, + <0 0 0 0 0 0 480000000 0>, + <0 0 0 0 0 0 600000000 0>; + clock-cntl-level = "svs", "svs_l1", "turbo"; + src-clock-name = "ife_clk_src"; + clock-control-debugfs = "true"; + status = "ok"; + }; + + qcom,cam-icp { + compatible = "qcom,cam-icp"; + compat-hw-name = "qcom,a5", + "qcom,ipe0", + "qcom,ipe1", + "qcom,bps"; + num-a5 = <1>; + num-ipe = <2>; + num-bps = <1>; + status = "ok"; + }; + + cam_a5: qcom,a5@ac00000 { + cell-index = <0>; + compatible = "qcom,cam-a5"; + reg = <0xac00000 0x6000>, + <0xac10000 0x8000>, + <0xac18000 0x3000>; + reg-names = "a5_qgic", "a5_sierra", "a5_csr"; + reg-cam-base = <0x00000 0x10000 0x18000>; + interrupts = ; + interrupt-names = "a5"; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "gcc_cam_ahb_clk", + "gcc_cam_axi_clk", + "soc_fast_ahb", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "icp_clk", + "icp_clk_src"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_FAST_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_ICP_CLK>, + <&clock_camcc CAM_CC_ICP_CLK_SRC>; + + clock-rates = + <0 0 200000000 0 0 0 0 400000000>, + <0 0 200000000 0 0 0 0 600000000>; + clock-cntl-level = "svs", "turbo"; + fw_name = "CAMERA_ICP.elf"; + ubwc-cfg = <0x7B 0x1EF>; + status = "ok"; + }; + + cam_ipe0: qcom,ipe0 { + cell-index = <0>; + compatible = "qcom,cam-ipe"; + reg = <0xac87000 0x3000>; + reg-names = "ipe0_top"; + reg-cam-base = <0x87000>; + regulator-names = "ipe0-vdd"; + ipe0-vdd-supply = <&ipe_0_gdsc>; + clock-names = "ipe_0_ahb_clk", + "ipe_0_areg_clk", + "ipe_0_axi_clk", + "ipe_0_clk", + "ipe_0_clk_src"; + src-clock-name = "ipe_0_clk_src"; + clock-control-debugfs = "true"; + clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>, + <&clock_camcc CAM_CC_IPE_0_AREG_CLK>, + <&clock_camcc CAM_CC_IPE_0_AXI_CLK>, + <&clock_camcc CAM_CC_IPE_0_CLK>, + <&clock_camcc CAM_CC_IPE_0_CLK_SRC>; + + clock-rates = + <0 0 0 0 404000000>, + <0 0 0 0 480000000>, + <0 0 0 0 538000000>, + <0 0 0 0 600000000>; + clock-cntl-level = "svs", + "svs_l1", "nominal", "turbo"; + status = "ok"; + }; + + cam_ipe1: qcom,ipe1 { + cell-index = <1>; + compatible = "qcom,cam-ipe"; + reg = <0xac91000 0x3000>; + reg-names = "ipe1_top"; + reg-cam-base = <0x91000>; + regulator-names = "ipe1-vdd"; + ipe1-vdd-supply = <&ipe_1_gdsc>; + clock-names = "ipe_1_ahb_clk", + "ipe_1_areg_clk", + "ipe_1_axi_clk", + "ipe_1_clk", + "ipe_1_clk_src"; + src-clock-name = "ipe_1_clk_src"; + clock-control-debugfs = "true"; + clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>, + <&clock_camcc CAM_CC_IPE_1_AREG_CLK>, + <&clock_camcc CAM_CC_IPE_1_AXI_CLK>, + <&clock_camcc CAM_CC_IPE_1_CLK>, + <&clock_camcc CAM_CC_IPE_1_CLK_SRC>; + + clock-rates = + <0 0 0 0 404000000>, + <0 0 0 0 480000000>, + <0 0 0 0 538000000>, + <0 0 0 0 600000000>; + clock-cntl-level = "svs", + "svs_l1", "nominal", "turbo"; + status = "ok"; + }; + + cam_bps: qcom,bps { + cell-index = <0>; + compatible = "qcom,cam-bps"; + reg = <0xac6f000 0x3000>; + reg-names = "bps_top"; + reg-cam-base = <0x6f000>; + regulator-names = "bps-vdd"; + bps-vdd-supply = <&bps_gdsc>; + clock-names = "bps_ahb_clk", + "bps_areg_clk", + "bps_axi_clk", + "bps_clk", + "bps_clk_src"; + src-clock-name = "bps_clk_src"; + clock-control-debugfs = "true"; + clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>, + <&clock_camcc CAM_CC_BPS_AREG_CLK>, + <&clock_camcc CAM_CC_BPS_AXI_CLK>, + <&clock_camcc CAM_CC_BPS_CLK>, + <&clock_camcc CAM_CC_BPS_CLK_SRC>; + + clock-rates = + <0 0 0 0 404000000>, + <0 0 0 0 480000000>, + <0 0 0 0 600000000>, + <0 0 0 0 600000000>; + clock-cntl-level = "svs", + "svs_l1", "nominal", "turbo"; + status = "ok"; + }; + + qcom,cam-jpeg { + compatible = "qcom,cam-jpeg"; + compat-hw-name = "qcom,jpegenc", + "qcom,jpegdma"; + num-jpeg-enc = <1>; + num-jpeg-dma = <1>; + status = "ok"; + }; + + cam_jpeg_enc: qcom,jpegenc@ac4e000 { + cell-index = <0>; + compatible = "qcom,cam_jpeg_enc"; + reg-names = "jpege_hw"; + reg = <0xac4e000 0x4000>; + reg-cam-base = <0x4e000>; + interrupt-names = "jpeg"; + interrupts = ; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "jpegenc_clk_src", + "jpegenc_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_JPEG_CLK_SRC>, + <&clock_camcc CAM_CC_JPEG_CLK>; + + clock-rates = <0 0 0 0 0 600000000 0>; + src-clock-name = "jpegenc_clk_src"; + clock-cntl-level = "nominal"; + status = "ok"; + }; + + cam_jpeg_dma: qcom,jpegdma@0xac52000{ + cell-index = <0>; + compatible = "qcom,cam_jpeg_dma"; + reg-names = "jpegdma_hw"; + reg = <0xac52000 0x4000>; + reg-cam-base = <0x52000>; + interrupt-names = "jpegdma"; + interrupts = ; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "jpegdma_clk_src", + "jpegdma_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_JPEG_CLK_SRC>, + <&clock_camcc CAM_CC_JPEG_CLK>; + + clock-rates = <0 0 0 0 0 600000000 0>; + src-clock-name = "jpegdma_clk_src"; + clock-cntl-level = "nominal"; + status = "ok"; + }; + + qcom,cam-fd { + compatible = "qcom,cam-fd"; + compat-hw-name = "qcom,fd"; + num-fd = <1>; + status = "ok"; + }; + + cam_fd: qcom,fd@ac5a000 { + cell-index = <0>; + compatible = "qcom,fd41"; + reg-names = "fd_core", "fd_wrapper"; + reg = <0xac5a000 0x1000>, + <0xac5b000 0x400>; + reg-cam-base = <0x5a000 0x5b000>; + interrupt-names = "fd"; + interrupts = ; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "gcc_ahb_clk", + "gcc_axi_clk", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "fd_core_clk_src", + "fd_core_clk", + "fd_core_uar_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_FD_CORE_CLK_SRC>, + <&clock_camcc CAM_CC_FD_CORE_CLK>, + <&clock_camcc CAM_CC_FD_CORE_UAR_CLK>; + src-clock-name = "fd_core_clk_src"; + clock-control-debugfs = "true"; + clock-cntl-level = "svs", "svs_l1", "turbo"; + clock-rates = + <0 0 0 0 0 400000000 0 0>, + <0 0 0 0 0 538000000 0 0>, + <0 0 0 0 0 600000000 0 0>; + status = "ok"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi new file mode 100644 index 000000000000..a4a5f1cc55a2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi @@ -0,0 +1,2450 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +&soc { + + csr: csr@6001000 { + compatible = "qcom,coresight-csr"; + reg = <0x6001000 0x1000>; + reg-names = "csr-base"; + + coresight-name = "coresight-csr"; + qcom,usb-bam-support; + qcom,hwctrl-set-support; + qcom,set-byte-cntr-support; + + qcom,blk-size = <1>; + }; + + swao_csr: csr@6b0e000 { + compatible = "qcom,coresight-csr"; + reg = <0x6b0e000 0x1000>; + reg-names = "csr-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + coresight-name = "coresight-swao-csr"; + qcom,timestamp-support; + + qcom,blk-size = <1>; + }; + + replicator_qdss: replicator@6046000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b909>; + + reg = <0x6046000 0x1000>; + reg-names = "replicator-base"; + + coresight-name = "coresight-replicator"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + replicator_out_tmc_etr: endpoint { + remote-endpoint= + <&tmc_etr_in_replicator>; + }; + }; + + port@1 { + reg = <0>; + replicator_in_tmc_etf: endpoint { + slave-mode; + remote-endpoint= + <&tmc_etf_out_replicator>; + }; + }; + }; + }; + + replicator_swao: replicator@6b0a000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b909>; + + reg = <0x6b0a000 0x1000>; + reg-names = "replicator-base"; + + coresight-name = "coresight-replicator-swao"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + replicator_swao_in_tmc_etf_swao: endpoint { + slave-mode; + remote-endpoint = + <&tmc_etf_swao_out_replicator>; + }; + }; + + /* Always have EUD before funnel leading to ETR. If both + * sink are active we need to give preference to EUD + * over ETR + */ + port@1 { + reg = <1>; + replicator_swao_out_eud: endpoint { + remote-endpoint = + <&eud_in_replicator_swao>; + }; + }; + + port@2 { + reg = <0>; + replicator_swao_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_replicator_swao>; + }; + }; + + }; + }; + + tmc_etf_swao: tmc@6b09000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; + + reg = <0x6b09000 0x1000>; + reg-names = "tmc-base"; + + coresight-name = "coresight-tmc-etf-swao"; + coresight-csr = <&csr>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tmc_etf_swao_out_replicator: endpoint { + remote-endpoint= + <&replicator_swao_in_tmc_etf_swao>; + }; + }; + + port@1 { + reg = <0>; + tmc_etf_swao_in_funnel_swao: endpoint { + slave-mode; + remote-endpoint= + <&funnel_swao_out_tmc_etf_swao>; + }; + }; + }; + + }; + + funnel_swao:funnel@0x6b08000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6b08000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-swao"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_swao_out_tmc_etf_swao: endpoint { + remote-endpoint = + <&tmc_etf_swao_in_funnel_swao>; + }; + }; + + port@1 { + reg = <6>; + funnel_swao_in_sensor_etm0: endpoint { + slave-mode; + remote-endpoint= + <&sensor_etm0_out_funnel_swao>; + }; + }; + + port@2 { + reg = <7>; + funnel_swao_in_tpda_swao: endpoint { + slave-mode; + remote-endpoint= + <&tpda_swao_out_funnel_swao>; + }; + }; + }; + }; + + tpda_swao: tpda@6b01000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x6b01000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-swao"; + + qcom,tpda-atid = <71>; + qcom,dsb-elem-size = <1 32>; + qcom,cmb-elem-size = <0 64>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tpda_swao_out_funnel_swao: endpoint { + remote-endpoint = + <&funnel_swao_in_tpda_swao>; + }; + + }; + + port@1 { + reg = <0>; + tpda_swao_in_tpdm_swao0: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_swao0_out_tpda_swao>; + }; + }; + + port@2 { + reg = <1>; + tpda_swao_in_tpdm_swao1: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_swao1_out_tpda_swao>; + }; + + }; + }; + }; + + tpdm_swao0: tpdm@6b02000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + + reg = <0x6b02000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-swao-0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_swao0_out_tpda_swao: endpoint { + remote-endpoint = <&tpda_swao_in_tpdm_swao0>; + }; + }; + }; + + tpdm_swao1: tpdm@6b03000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6b03000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name="coresight-tpdm-swao-1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_swao1_out_tpda_swao: endpoint { + remote-endpoint = <&tpda_swao_in_tpdm_swao1>; + }; + }; + }; + + tmc_etr: tmc@6048000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; + + reg = <0x6048000 0x1000>, + <0x6064000 0x15000>; + reg-names = "tmc-base", "bam-base"; + + arm,buffer-size = <0x400000>; + arm,sg-enable; + + coresight-name = "coresight-tmc-etr"; + coresight-ctis = <&cti0 &cti8>; + coresight-csr = <&csr>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + interrupts = ; + interrupt-names = "byte-cntr-irq"; + + port { + tmc_etr_in_replicator: endpoint { + slave-mode; + remote-endpoint = <&replicator_out_tmc_etr>; + }; + }; + }; + + tmc_etf: tmc@6047000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; + + reg = <0x6047000 0x1000>; + reg-names = "tmc-base"; + + coresight-name = "coresight-tmc-etf"; + coresight-ctis = <&cti0 &cti8>; + coresight-csr = <&csr>; + arm,default-sink; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tmc_etf_out_replicator: endpoint { + remote-endpoint = + <&replicator_in_tmc_etf>; + }; + }; + + port@1 { + reg = <1>; + tmc_etf_in_funnel_merg: endpoint { + slave-mode; + remote-endpoint = + <&funnel_merg_out_tmc_etf>; + }; + }; + }; + + }; + + funnel_merg: funnel@6045000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6045000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-merg"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_merg_out_tmc_etf: endpoint { + remote-endpoint = + <&tmc_etf_in_funnel_merg>; + }; + }; + + port@1 { + reg = <0>; + funnel_merg_in_funnel_in0: endpoint { + slave-mode; + remote-endpoint = + <&funnel_in0_out_funnel_merg>; + }; + }; + + port@2 { + reg = <2>; + funnel_merg_in_funnel_in2: endpoint { + slave-mode; + remote-endpoint = + <&funnel_in2_out_funnel_merg>; + }; + }; + }; + }; + + stm: stm@6002000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b962>; + + reg = <0x6002000 0x1000>, + <0x16280000 0x180000>; + reg-names = "stm-base", "stm-stimulus-base"; + + coresight-name = "coresight-stm"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + stm_out_funnel_in0: endpoint { + remote-endpoint = <&funnel_in0_in_stm>; + }; + }; + + }; + + hwevent: hwevent@0x014066f0 { + compatible = "qcom,coresight-hwevent"; + reg = <0x14066f0 0x4>, + <0x14166f0 0x4>, + <0x1406038 0x4>, + <0x1416038 0x4>; + reg-names = "ddr-ch0-cfg", "ddr-ch23-cfg", "ddr-ch0-ctrl", + "ddr-ch23-ctrl"; + + coresight-name = "coresight-hwevent"; + coresight-csr = <&csr>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + funnel_in0: funnel@0x6041000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6041000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in0_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in0>; + }; + }; + + port@1 { + reg = <3>; + funnel_in0_in_funnel_spss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_spss_out_funnel_in0>; + }; + }; + + port@2 { + reg = <6>; + funnel_in0_in_funnel_qatb: endpoint { + slave-mode; + remote-endpoint = + <&funnel_qatb_out_funnel_in0>; + }; + }; + + port@3 { + reg = <7>; + funnel_in0_in_stm: endpoint { + slave-mode; + remote-endpoint = <&stm_out_funnel_in0>; + }; + }; + }; + }; + + funnel_in2: funnel@0x6043000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6043000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in2"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in2_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in2>; + }; + }; + + port@1 { + reg = <0>; + funnel_in2_in_modem_etm0: endpoint { + slave-mode; + remote-endpoint = + <&modem_etm0_out_funnel_in2>; + }; + + }; + + port@2 { + reg = <1>; + funnel_in2_in_replicator_swao: endpoint { + slave-mode; + remote-endpoint = + <&replicator_swao_out_funnel_in2>; + }; + + }; + + port@3 { + reg = <2>; + funnel_in2_in_funnel_modem: endpoint { + slave-mode; + remote-endpoint = + <&funnel_modem_out_funnel_in2>; + }; + + }; + + port@4 { + reg = <5>; + funnel_in2_in_funnel_apss_merg: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_merg_out_funnel_in2>; + }; + }; + + port@5 { + reg = <6>; + funnel_in2_in_funnel_gfx: endpoint { + slave-mode; + remote-endpoint = + <&funnel_gfx_out_funnel_in2>; + }; + }; + }; + }; + + funnel_gfx: funnel@0x6943000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6943000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-gfx"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_gfx_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_funnel_gfx>; + }; + }; + + port@1 { + reg = <0>; + funnel_in2_in_gfx: endpoint { + slave-mode; + remote-endpoint = + <&gfx_out_funnel_in2>; + }; + }; + + port@2 { + reg = <1>; + funnel_in2_in_gfx_cx: endpoint { + slave-mode; + remote-endpoint = + <&gfx_cx_out_funnel_in2>; + }; + }; + }; + }; + + tpda: tpda@6004000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x6004000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda"; + + qcom,tpda-atid = <65>; + qcom,bc-elem-size = <10 32>, + <13 32>; + qcom,tc-elem-size = <13 32>; + qcom,dsb-elem-size = <0 32>, + <2 32>, + <3 32>, + <5 32>, + <6 32>, + <10 32>, + <11 32>, + <13 32>; + qcom,cmb-elem-size = <3 64>, + <7 64>, + <9 64>, + <13 64>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_tpda>; + }; + + }; + + port@1 { + reg = <0>; + tpda_in_tpdm_center: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_center_out_tpda>; + }; + }; + + port@2 { + reg = <2>; + tpda_in_funnel_dl_mm: endpoint { + slave-mode; + remote-endpoint = + <&funnel_dl_mm_out_tpda>; + }; + }; + + port@3 { + reg = <3>; + tpda_in_funnel_ddr_0: endpoint { + slave-mode; + remote-endpoint = + <&funnel_ddr_0_out_tpda>; + }; + }; + + port@4 { + reg = <5>; + tpda_in_funnel_lpass: endpoint { + slave-mode; + remote-endpoint = + <&funnel_lpass_out_tpda>; + }; + }; + + port@5 { + reg = <6>; + tpda_in_funnel_turing: endpoint { + slave-mode; + remote-endpoint = + <&funnel_turing_out_tpda>; + }; + }; + + port@6 { + reg = <7>; + tpda_in_tpdm_vsense: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_vsense_out_tpda>; + }; + }; + + port@7 { + reg = <9>; + tpda_in_tpdm_prng: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_prng_out_tpda>; + }; + }; + + port@8 { + reg = <10>; + tpda_in_tpdm_qm: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_qm_out_tpda>; + }; + }; + + port@9 { + reg = <11>; + tpda_in_tpdm_north: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_north_out_tpda>; + }; + }; + + port@10 { + reg = <13>; + tpda_in_tpdm_pimem: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_pimem_out_tpda>; + }; + }; + }; + }; + + funnel_modem: funnel@6832000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6832000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-modem"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_modem_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_funnel_modem>; + }; + }; + + port@1 { + reg = <0>; + funnel_modem_in_tpda_modem: endpoint { + slave-mode; + remote-endpoint = + <&tpda_modem_out_funnel_modem>; + }; + }; + }; + }; + + tpda_modem: tpda@6831000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x6831000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-modem"; + + qcom,tpda-atid = <67>; + qcom,dsb-elem-size = <0 32>; + qcom,cmb-elem-size = <0 64>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_modem_out_funnel_modem: endpoint { + remote-endpoint = + <&funnel_modem_in_tpda_modem>; + }; + }; + + port@1 { + reg = <0>; + tpda_modem_in_tpdm_modem: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_modem_out_tpda_modem>; + }; + }; + }; + }; + + tpdm_modem: tpdm@6830000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6830000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-modem"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_modem_out_tpda_modem: endpoint { + remote-endpoint = <&tpda_modem_in_tpdm_modem>; + }; + }; + }; + + funnel_lpass: funnel@6845000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6845000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-lpass"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_lpass_out_tpda: endpoint { + remote-endpoint = + <&tpda_in_funnel_lpass>; + }; + }; + + port@1 { + reg = <0>; + funnel_lpass_in_tpdm_lpass: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_lpass_out_funnel_lpass>; + }; + }; + }; + }; + + funnel_lpass_1: funnel_1@6845000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6867010 0x10>, + <0x6845000 0x1000>; + reg-names = "funnel-base-dummy", "funnel-base-real"; + + coresight-name = "coresight-funnel-lpass-1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,duplicate-funnel; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_lpass_1_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_funnel_lpass_1>; + }; + }; + + port@1 { + reg = <1>; + funnel_lpass_1_in_audio_etm0: endpoint { + slave-mode; + remote-endpoint = + <&audio_etm0_out_funnel_lpass_1>; + }; + }; + }; + }; + + tpdm_lpass: tpdm@6844000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6844000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-lpass"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_lpass_out_funnel_lpass: endpoint { + remote-endpoint = <&funnel_lpass_in_tpdm_lpass>; + }; + }; + }; + + tpdm_center: tpdm@6c28000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6c28000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-center"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_center_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_center>; + }; + }; + }; + + tpdm_north: tpdm@6a24000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6a24000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-north"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_north_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_north>; + }; + }; + }; + + tpdm_qm: tpdm@69d0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x69d0000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-qm"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_qm_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_qm>; + }; + }; + }; + + tpda_apss: tpda@7862000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x7862000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-apss"; + + qcom,tpda-atid = <66>; + qcom,dsb-elem-size = <0 32>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_apss_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_tpda_apss>; + }; + }; + + port@1 { + reg = <0>; + tpda_apss_in_tpdm_apss: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_apss_out_tpda_apss>; + }; + }; + }; + }; + + tpdm_apss: tpdm@7860000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x7860000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-apss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_apss_out_tpda_apss: endpoint { + remote-endpoint = <&tpda_apss_in_tpdm_apss>; + }; + }; + }; + + tpda_llm_silver: tpda@78c0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x78c0000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-llm-silver"; + + qcom,tpda-atid = <72>; + qcom,cmb-elem-size = <0 32>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_llm_silver_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_tpda_llm_silver>; + }; + }; + + port@1 { + reg = <0>; + tpda_llm_silver_in_tpdm_llm_silver: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_llm_silver_out_tpda_llm_silver>; + }; + }; + }; + }; + + tpdm_llm_silver: tpdm@78a0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x78a0000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-llm-silver"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_llm_silver_out_tpda_llm_silver: endpoint { + remote-endpoint = + <&tpda_llm_silver_in_tpdm_llm_silver>; + }; + }; + }; + + tpda_llm_gold: tpda@78d0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x78d0000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-llm-gold"; + + qcom,tpda-atid = <73>; + qcom,cmb-elem-size = <0 32>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_llm_gold_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_tpda_llm_gold>; + }; + }; + + port@1 { + reg = <0>; + tpda_llm_gold_in_tpdm_llm_gold: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_llm_gold_out_tpda_llm_gold>; + }; + }; + }; + }; + + tpdm_llm_gold: tpdm@78b0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x78b0000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-llm-gold"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_llm_gold_out_tpda_llm_gold: endpoint { + remote-endpoint = + <&tpda_llm_gold_in_tpdm_llm_gold>; + }; + }; + }; + + funnel_dl_mm: funnel@6c0b000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6c0b000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-dl-mm"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_dl_mm_out_tpda: endpoint { + remote-endpoint = + <&tpda_in_funnel_dl_mm>; + }; + }; + + port@1 { + reg = <1>; + funnel_dl_mm_in_tpdm_mm: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_mm_out_funnel_dl_mm>; + }; + }; + }; + }; + + tpdm_mm: tpdm@6c08000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6c08000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-mm"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_mm_out_funnel_dl_mm: endpoint { + remote-endpoint = <&funnel_dl_mm_in_tpdm_mm>; + }; + }; + }; + + funnel_turing: funnel@6861000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6861000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-turing"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_turing_out_tpda: endpoint { + remote-endpoint = + <&tpda_in_funnel_turing>; + }; + }; + + port@1 { + reg = <0>; + funnel_turing_in_tpdm_turing: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_turing_out_funnel_turing>; + }; + }; + }; + }; + + funnel_turing_1: funnel_1@6861000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6867000 0x10>, + <0x6861000 0x1000>; + reg-names = "funnel-base-dummy", "funnel-base-real"; + + coresight-name = "coresight-funnel-turing-1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,duplicate-funnel; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_turing_1_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_funnel_turing_1>; + }; + }; + + port@1 { + reg = <1>; + funnel_turing_1_in_turing_etm0: endpoint { + slave-mode; + remote-endpoint = + <&turing_etm0_out_funnel_turing_1>; + }; + }; + }; + }; + + tpdm_turing: tpdm@6860000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6860000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-turing"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_turing_out_funnel_turing: endpoint { + remote-endpoint = + <&funnel_turing_in_tpdm_turing>; + }; + }; + }; + + funnel_ddr_0: funnel@69e2000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x69e2000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-ddr-0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_ddr_0_out_tpda: endpoint { + remote-endpoint = + <&tpda_in_funnel_ddr_0>; + }; + }; + + port@1 { + reg = <0>; + funnel_ddr_0_in_tpdm_ddr: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_ddr_out_funnel_ddr_0>; + }; + }; + }; + }; + + tpdm_ddr: tpdm@69e0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x69e0000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-ddr"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,msr-fix-req; + + port { + tpdm_ddr_out_funnel_ddr_0: endpoint { + remote-endpoint = <&funnel_ddr_0_in_tpdm_ddr>; + }; + }; + }; + + tpdm_pimem: tpdm@6850000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6850000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-pimem"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_pimem_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_pimem>; + }; + }; + }; + + tpdm_prng: tpdm@684c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x684c000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-prng"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port{ + tpdm_prng_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_prng>; + }; + }; + }; + + tpdm_vsense: tpdm@6840000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6840000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-vsense"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port{ + tpdm_vsense_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_vsense>; + }; + }; + }; + + tpda_olc: tpda@7832000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x7832000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-olc"; + + qcom,tpda-atid = <69>; + qcom,cmb-elem-size = <0 64>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_olc_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_tpda_olc>; + }; + }; + port@1 { + reg = <0>; + tpda_olc_in_tpdm_olc: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_olc_out_tpda_olc>; + }; + }; + }; + }; + + tpdm_olc: tpdm@7830000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x7830000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-olc"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port{ + tpdm_olc_out_tpda_olc: endpoint { + remote-endpoint = <&tpda_olc_in_tpdm_olc>; + }; + }; + }; + + tpda_spss: tpda@6882000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x6882000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-spss"; + + qcom,tpda-atid = <70>; + qcom,dsb-elem-size = <0 32>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_spss_out_funnel_spss: endpoint { + remote-endpoint = + <&funnel_spss_in_tpda_spss>; + }; + }; + port@1 { + reg = <0>; + tpda_spss_in_tpdm_spss: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_spss_out_tpda_spss>; + }; + }; + }; + }; + + tpdm_spss: tpdm@6880000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6880000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-spss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port{ + tpdm_spss_out_tpda_spss: endpoint { + remote-endpoint = <&tpda_spss_in_tpdm_spss>; + }; + }; + }; + + funnel_spss: funnel@6883000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6883000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-spss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_spss_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_spss>; + }; + }; + + port@1 { + reg = <0>; + funnel_spss_in_tpda_spss: endpoint { + slave-mode; + remote-endpoint = + <&tpda_spss_out_funnel_spss>; + }; + }; + + port@2 { + reg = <1>; + funnel_spss_in_spss_etm0: endpoint { + slave-mode; + remote-endpoint = + <&spss_etm0_out_funnel_spss>; + }; + }; + }; + }; + + funnel_qatb: funnel@6005000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6005000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-qatb"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_qatb_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_qatb>; + }; + }; + + port@1 { + reg = <0>; + funnel_qatb_in_tpda: endpoint { + slave-mode; + remote-endpoint = + <&tpda_out_funnel_qatb>; + }; + }; + + port@2 { + reg = <6>; + funnel_qatb_in_funnel_lpass_1: endpoint { + slave-mode; + remote-endpoint = + <&funnel_lpass_1_out_funnel_qatb>; + }; + }; + + port@3 { + reg = <7>; + funnel_qatb_in_funnel_turing_1: endpoint { + slave-mode; + remote-endpoint = + <&funnel_turing_1_out_funnel_qatb>; + }; + }; + }; + }; + + cti0_ddr0: cti@69e1000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x69e1000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-ddr_dl_0_cti"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti0_ddr1: cti@69e4000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x69e4000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-ddr_dl_1_cti0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti1_ddr1: cti@69e5000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x69e5000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-ddr_dl_1_cti1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti0_dlmm: cti@6c09000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6c09000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-dlmm_cti0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti1_dlmm: cti@6c0a000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6c0a000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-dlmm_cti1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti0_apss: cti@78e0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x78e0000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-apss_cti0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti1_apss: cti@78f0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x78f0000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-apss_cti1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti2_apss: cti@7900000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7900000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-apss_cti2"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti0: cti@6010000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6010000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti1: cti@6011000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6011000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti2: cti@6012000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6012000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti2"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + qcom,cti-gpio-trigout = <4>; + pinctrl-names = "cti-trigout-pctrl"; + pinctrl-0 = <&trigout_a>; + }; + + cti3: cti@6013000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6013000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti3"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti4: cti@6014000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6014000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti4"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti5: cti@6015000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6015000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti5"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti6: cti@6016000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6016000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti6"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti7: cti@6017000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6017000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti7"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti8: cti@6018000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6018000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti8"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti9: cti@6019000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6019000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti9"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti10: cti@601a000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601a000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti10"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti11: cti@601b000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601b000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti11"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti12: cti@601c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601c000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti12"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti13: cti@601d000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601d000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti13"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti14: cti@601e000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601e000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti14"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti15: cti@601f000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601f000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti15"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti_cpu0: cti@7020000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7020000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu0"; + cpu = <&CPU0>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + }; + + cti_cpu1: cti@7120000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7120000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu1"; + cpu = <&CPU1>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu2: cti@7220000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7220000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu2"; + cpu = <&CPU2>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu3: cti@7320000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7320000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu3"; + cpu = <&CPU3>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu4: cti@7420000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7420000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu4"; + cpu = <&CPU4>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu5: cti@7520000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7520000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu5"; + cpu = <&CPU5>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu6: cti@7620000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7620000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu6"; + cpu = <&CPU6>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti_cpu7: cti@7720000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x7720000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu7"; + cpu = <&CPU7>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + cti0_swao:cti@6b04000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6b04000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-swao_cti0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + ipcb_tgu: tgu@6b0c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b999>; + reg = <0x06B0C000 0x1000>; + reg-names = "tgu-base"; + tgu-steps = <3>; + tgu-conditions = <4>; + tgu-regs = <4>; + tgu-timer-counters = <8>; + + coresight-name = "coresight-tgu-ipcb"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + turing_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-turing-etm0"; + qcom,inst-id = <13>; + + port{ + turing_etm0_out_funnel_turing_1: endpoint { + remote-endpoint = + <&funnel_turing_1_in_turing_etm0>; + }; + }; + }; + + dummy_eud: dummy_sink { + compatible = "qcom,coresight-dummy"; + + coresight-name = "coresight-eud"; + + qcom,dummy-sink; + port { + eud_in_replicator_swao: endpoint { + slave-mode; + remote-endpoint = + <&replicator_swao_out_eud>; + }; + }; + }; + + sensor_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-sensor-etm0"; + qcom,inst-id = <8>; + + port { + sensor_etm0_out_funnel_swao: endpoint { + remote-endpoint = + <&funnel_swao_in_sensor_etm0>; + }; + }; + }; + + modem_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-modem-etm0"; + qcom,inst-id = <2>; + + port { + modem_etm0_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_modem_etm0>; + }; + }; + }; + + audio_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-audio-etm0"; + qcom,inst-id = <5>; + + port { + audio_etm0_out_funnel_lpass_1: endpoint { + remote-endpoint = + <&funnel_lpass_1_in_audio_etm0>; + }; + }; + }; + + spss_etm0 { + compatible = "qcom,coresight-dummy"; + + coresight-name = "coresight-spss-etm0"; + + qcom,dummy-source; + port { + spss_etm0_out_funnel_spss: endpoint { + remote-endpoint = + <&funnel_spss_in_spss_etm0>; + }; + }; + }; + + funnel_apss_merg: funnel@7810000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x7810000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-apss-merg"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_apss_merg_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_funnel_apss_merg>; + }; + }; + + port@1 { + reg = <0>; + funnel_apss_merg_in_funnel_apss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_out_funnel_apss_merg>; + }; + }; + + port@2 { + reg = <2>; + funnel_apss_merg_in_tpda_olc: endpoint { + slave-mode; + remote-endpoint = + <&tpda_olc_out_funnel_apss_merg>; + }; + }; + + port@3 { + reg = <4>; + funnel_apss_merg_in_tpda_apss: endpoint { + slave-mode; + remote-endpoint = + <&tpda_apss_out_funnel_apss_merg>; + }; + }; + + port@4 { + reg = <5>; + funnel_apss_merg_in_tpda_llm_silver: endpoint { + slave-mode; + remote-endpoint = + <&tpda_llm_silver_out_funnel_apss_merg>; + }; + }; + + port@5 { + reg = <6>; + funnel_apss_merg_in_tpda_llm_gold: endpoint { + slave-mode; + remote-endpoint = + <&tpda_llm_gold_out_funnel_apss_merg>; + }; + }; + }; + }; + + etm0: etm@7040000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7040000 0x1000>; + cpu = <&CPU0>; + + coresight-name = "coresight-etm0"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm0_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm0>; + }; + }; + }; + + etm1: etm@7140000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7140000 0x1000>; + cpu = <&CPU1>; + + coresight-name = "coresight-etm1"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm1_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm1>; + }; + }; + }; + + etm2: etm@7240000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7240000 0x1000>; + cpu = <&CPU2>; + + coresight-name = "coresight-etm2"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm2_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm2>; + }; + }; + }; + + etm3: etm@7340000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7340000 0x1000>; + cpu = <&CPU3>; + + coresight-name = "coresight-etm3"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm3_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm3>; + }; + }; + }; + + etm4: etm@7440000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7440000 0x1000>; + cpu = <&CPU4>; + + coresight-name = "coresight-etm4"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm4_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm4>; + }; + }; + }; + + etm5: etm@7540000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7540000 0x1000>; + cpu = <&CPU5>; + + coresight-name = "coresight-etm5"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm5_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm5>; + }; + }; + }; + + etm6: etm@7640000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7640000 0x1000>; + cpu = <&CPU6>; + + coresight-name = "coresight-etm6"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm6_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm6>; + }; + }; + }; + + etm7: etm@7740000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x7740000 0x1000>; + cpu = <&CPU7>; + + coresight-name = "coresight-etm7"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + etm7_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm7>; + }; + }; + }; + + funnel_apss: funnel@7800000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x7800000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-apss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_apss_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_funnel_apss>; + }; + }; + port@1 { + reg = <0>; + funnel_apss_in_etm0: endpoint { + slave-mode; + remote-endpoint = + <&etm0_out_funnel_apss>; + }; + }; + + port@2 { + reg = <1>; + funnel_apss_in_etm1: endpoint { + slave-mode; + remote-endpoint = + <&etm1_out_funnel_apss>; + }; + }; + + port@3 { + reg = <2>; + funnel_apss_in_etm2: endpoint { + slave-mode; + remote-endpoint = + <&etm2_out_funnel_apss>; + }; + }; + + port@4 { + reg = <3>; + funnel_apss_in_etm3: endpoint { + slave-mode; + remote-endpoint = + <&etm3_out_funnel_apss>; + }; + }; + + port@5 { + reg = <4>; + funnel_apss_in_etm4: endpoint { + slave-mode; + remote-endpoint = + <&etm4_out_funnel_apss>; + }; + }; + + port@6 { + reg = <5>; + funnel_apss_in_etm5: endpoint { + slave-mode; + remote-endpoint = + <&etm5_out_funnel_apss>; + }; + }; + + port@7 { + reg = <6>; + funnel_apss_in_etm6: endpoint { + slave-mode; + remote-endpoint = + <&etm6_out_funnel_apss>; + }; + }; + + port@8 { + reg = <7>; + funnel_apss_in_etm7: endpoint { + slave-mode; + remote-endpoint = + <&etm7_out_funnel_apss>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi new file mode 100644 index 000000000000..8bb45ce5b084 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ + +&soc { + + pil_gpu: qcom,kgsl-hyp { + compatible = "qcom,pil-tz-generic"; + qcom,pas-id = <13>; + qcom,firmware-name = "a630_zap"; + }; + + msm_bus: qcom,kgsl-busmon{ + label = "kgsl-busmon"; + compatible = "qcom,kgsl-busmon"; + }; + + gpu_bw_tbl: gpu-bw-tbl { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 0, 4); /* 0 MB/s */ + BW_OPP_ENTRY( 100, 4); /* 381 MB/s */ + BW_OPP_ENTRY( 150, 4); /* 572 MB/s */ + BW_OPP_ENTRY( 200, 4); /* 762 MB/s */ + BW_OPP_ENTRY( 300, 4); /* 1144 MB/s */ + BW_OPP_ENTRY( 412, 4); /* 1571 MB/s */ + BW_OPP_ENTRY( 547, 4); /* 2086 MB/s */ + BW_OPP_ENTRY( 681, 4); /* 2597 MB/s */ + BW_OPP_ENTRY( 768, 4); /* 2929 MB/s */ + BW_OPP_ENTRY(1017, 4); /* 3879 MB/s */ + BW_OPP_ENTRY(1296, 4); /* 4943 MB/s */ + BW_OPP_ENTRY(1555, 4); /* 5931 MB/s */ + BW_OPP_ENTRY(1804, 4); /* 6881 MB/s */ + }; + + gpubw: qcom,gpubw { + compatible = "qcom,devbw"; + governor = "bw_vbif"; + qcom,src-dst-ports = <26 512>; + operating-points-v2 = <&gpu_bw_tbl>; + }; + + msm_gpu: qcom,kgsl-3d0@5000000 { + label = "kgsl-3d0"; + compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d"; + status = "ok"; + reg = <0x5000000 0x40000>, <0x5061000 0x800>, + <0x509e000 0x1000>; + reg-names = "kgsl_3d0_reg_memory", "cx_dbgc", + "cx_misc"; + interrupts = ; + interrupt-names = "kgsl_3d0_irq"; + qcom,id = <0>; + + qcom,chipid = <0x06030000>; + + qcom,initial-pwrlevel = <5>; + + qcom,gpu-quirk-hfi-use-reg; + qcom,gpu-quirk-secvid-set-once; + + qcom,idle-timeout = <80>; //msecs + qcom,no-nap; + + qcom,highest-bank-bit = <15>; + + qcom,min-access-length = <32>; + + qcom,ubwc-mode = <2>; + + qcom,snapshot-size = <1048576>; //bytes + + qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size + + qcom,tsens-name = "tsens_tz_sensor12"; + #cooling-cells = <2>; + + tzone-names = "gpu0-usr", "gpu1-usr"; + + qcom,pm-qos-active-latency = <460>; + + clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>, + <&clock_gpucc GPU_CC_CXO_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>, + <&clock_gpucc GPU_CC_CX_GMU_CLK>, + <&clock_cpucc L3_GPU_VOTE_CLK>; + + clock-names = "core_clk", "rbbmtimer_clk", "mem_clk", + "mem_iface_clk", "gmu_clk", "l3_vote"; + + qcom,isense-clk-on-level = <1>; + + /* Bus Scale Settings */ + qcom,gpubw-dev = <&gpubw>; + qcom,bus-control; + qcom,msm-bus,name = "grp3d"; + qcom,bus-width = <32>; + qcom,msm-bus,num-cases = <13>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <26 512 0 0>, + + <26 512 0 400000>, // 1 bus=100 + <26 512 0 600000>, // 2 bus=150 + <26 512 0 800000>, // 3 bus=200 + <26 512 0 1200000>, // 4 bus=300 + <26 512 0 1648000>, // 5 bus=412 + <26 512 0 2188000>, // 6 bus=547 + <26 512 0 2724000>, // 7 bus=681 + <26 512 0 3072000>, // 8 bus=768 + <26 512 0 4068000>, // 9 bus=1017 + <26 512 0 5184000>, // 10 bus=1296 + <26 512 0 6220000>, // 11 bus=1555 + <26 512 0 7216000>; // 12 bus=1804 + + /* GDSC regulator names */ + regulator-names = "vddcx", "vdd"; + /* GDSC oxili regulators */ + vddcx-supply = <&gpu_cx_gdsc>; + vdd-supply = <&gpu_gx_gdsc>; + + /* GPU related llc slices */ + cache-slice-names = "gpu", "gpuhtw"; + + qcom,l3-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,l3-pwrlevels"; + + qcom,l3-pwrlevel@0 { + reg = <0>; + qcom,l3-freq = <0>; + }; + + qcom,l3-pwrlevel@1 { + reg = <1>; + qcom,l3-freq = <806400000>; + }; + + qcom,l3-pwrlevel@2 { + reg = <2>; + qcom,l3-freq = <1305600000>; + }; + }; + + /* GPU Mempools */ + qcom,gpu-mempools { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,gpu-mempools"; + + /* 4K Page Pool configuration */ + qcom,gpu-mempool@0 { + reg = <0>; + qcom,mempool-page-size = <4096>; + qcom,mempool-reserved = <2048>; + qcom,mempool-allocate; + }; + /* 8K Page Pool configuration */ + qcom,gpu-mempool@1 { + reg = <1>; + qcom,mempool-page-size = <8192>; + qcom,mempool-reserved = <1024>; + qcom,mempool-allocate; + }; + /* 64K Page Pool configuration */ + qcom,gpu-mempool@2 { + reg = <2>; + qcom,mempool-page-size = <65536>; + qcom,mempool-reserved = <256>; + }; + /* 1M Page Pool configuration */ + qcom,gpu-mempool@3 { + reg = <3>; + qcom,mempool-page-size = <1048576>; + qcom,mempool-reserved = <32>; + }; + }; + + /* Power levels */ + qcom,gpu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gpu-pwrlevels"; + + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <600000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <11>; + qcom,bus-max = <12>; + }; + + + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <548000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <10>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <487000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; + qcom,bus-max = <11>; + }; + + + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <425000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <10>; + }; + + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <338000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <280000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <5>; + qcom,bus-max = <7>; + }; + + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <210000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <0>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; + + }; + + kgsl_msm_iommu: qcom,kgsl-iommu { + compatible = "qcom,kgsl-smmu-v2"; + + reg = <0x05040000 0x10000>; + /* CB5(ATOS) & CB5/6/7 are protected by HYP */ + qcom,protect = <0x40000 0xc000>; + qcom,micro-mmu-control = <0x6000>; + + clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>; + + clock-names = "iface_clk", "mem_clk", "mem_iface_clk"; + + qcom,secure_align_mask = <0xfff>; + qcom,retention; + qcom,hyp_secure_alloc; + + gfx3d_user: gfx3d_user { + compatible = "qcom,smmu-kgsl-cb"; + label = "gfx3d_user"; + iommus = <&kgsl_smmu 0>; + qcom,iommu-dma = "disabled"; + qcom,gpu-offset = <0x48000>; + }; + + gfx3d_secure: gfx3d_secure { + compatible = "qcom,smmu-kgsl-cb"; + iommus = <&kgsl_smmu 2>, <&kgsl_smmu 1>; + qcom,iommu-dma = "disabled"; + }; + }; + + gmu: qcom,gmu { + label = "kgsl-gmu"; + compatible = "qcom,gpu-gmu"; + + reg = <0x506a000 0x30000>, <0xb200000 0x300000>; + reg-names = "kgsl_gmu_reg", "kgsl_gmu_pdc_reg"; + + interrupts = , + ; + interrupt-names = "kgsl_hfi_irq", "kgsl_gmu_irq"; + + qcom,msm-bus,name = "cnoc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <26 10036 0 0>, // CNOC off + <26 10036 0 100>; // CNOC on + + regulator-names = "vddcx", "vdd"; + vddcx-supply = <&gpu_cx_gdsc>; + vdd-supply = <&gpu_gx_gdsc>; + + + clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>, + <&clock_gpucc GPU_CC_CXO_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>; + + clock-names = "gmu_clk", "cxo_clk", "axi_clk", + "memnoc_clk"; + + qcom,gmu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gmu-pwrlevels"; + + /* GMU power levels must go from lowest to highest */ + qcom,gmu-pwrlevel@0 { + reg = <0>; + qcom,gmu-freq = <0>; + }; + + qcom,gmu-pwrlevel@1 { + reg = <1>; + qcom,gmu-freq = <200000000>; + }; + + qcom,gmu-pwrlevel@2 { + reg = <2>; + qcom,gmu-freq = <400000000>; + }; + }; + + gmu_user: gmu_user { + compatible = "qcom,smmu-gmu-user-cb"; + iommus = <&kgsl_smmu 4>; + qcom,iommu-dma = "disabled"; + }; + + gmu_kernel: gmu_kernel { + compatible = "qcom,smmu-gmu-kernel-cb"; + iommus = <&kgsl_smmu 5>; + qcom,iommu-dma = "disabled"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-ion.dtsi b/arch/arm64/boot/dts/qcom/sdm845-ion.dtsi new file mode 100644 index 000000000000..5c899e5d2f01 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-ion.dtsi @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&soc { + qcom,ion { + compatible = "qcom,msm-ion"; + #address-cells = <1>; + #size-cells = <0>; + + system_heap: qcom,ion-heap@25 { + reg = <25>; + qcom,ion-heap-type = "SYSTEM"; + }; + + qcom,ion-heap@22 { /* ADSP HEAP */ + reg = <22>; + memory-region = <&adsp_mem>; + qcom,ion-heap-type = "DMA"; + }; + + qcom,ion-heap@27 { /* QSEECOM HEAP */ + reg = <27>; + memory-region = <&qseecom_mem>; + qcom,ion-heap-type = "DMA"; + }; + + qcom,ion-heap@19 { /* QSEECOM TA HEAP */ + reg = <19>; + memory-region = <&qseecom_ta_mem>; + qcom,ion-heap-type = "DMA"; + }; + + qcom,ion-heap@13 { /* SECURE SPSS HEAP */ + reg = <13>; + memory-region = <&secure_sp_mem>; + qcom,ion-heap-type = "HYP_CMA"; + }; + + qcom,ion-heap@10 { /* SECURE DISPLAY HEAP */ + reg = <10>; + memory-region = <&secure_display_memory>; + qcom,ion-heap-type = "HYP_CMA"; + }; + + qcom,ion-heap@9 { + reg = <9>; + qcom,ion-heap-type = "SYSTEM_SECURE"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-mtp-overlay.dts new file mode 100644 index 000000000000..ec03a644588c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-mtp-overlay.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-t0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v1 MTP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x10000>; + qcom,board-id = <8 0 0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts index 6921f8dc5ebb..cf56e16b3de7 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts @@ -1,64 +1,17 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* - * SDM845 MTP board device tree source - * - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. */ + /dts-v1/; #include "sdm845.dtsi" +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. SDM845 MTP"; - compatible = "qcom,sdm845-mtp"; - - aliases { - serial0 = &uart9; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; -}; - -&i2c10 { - status = "okay"; - clock-frequency = <400000>; -}; - -&qupv3_id_1 { - status = "okay"; -}; - -&tlmm { - gpio-reserved-ranges = <0 4>, <81 4>; -}; - -&uart9 { - status = "okay"; -}; - -/* PINCTRL - additions to nodes defined in sdm845.dtsi */ - -&qup_i2c10_default { - pinconf { - pins = "gpio55", "gpio56"; - drive-strength = <2>; - bias-disable; - }; -}; - -&qup_uart9_default { - pinconf-tx { - pins = "gpio4"; - drive-strength = <2>; - bias-disable; - }; - - pinconf-rx { - pins = "gpio5"; - drive-strength = <2>; - bias-pull-up; - }; + model = "Qualcomm Technologies, Inc. MSM sdm845 v1 MTP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,board-id = <8 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi new file mode 100644 index 000000000000..65d42feb436d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include "sdm845-pmic-overlay.dtsi" +#include "sdm845-pinctrl-overlay.dtsi" +#include "sdm845-camera-sensor-mtp.dtsi" + +&qupv3_se10_i2c { +#include "smb1355.dtsi" +}; + +&vendor { + bluetooth: bt_wcn3990 { + compatible = "qca,wcn3990"; + qca,bt-vdd-io-supply = <&pm8998_s3>; + qca,bt-vdd-xtal-supply = <&pm8998_s5>; + qca,bt-vdd-core-supply = <&pm8998_l7>; + qca,bt-vdd-pa-supply = <&pm8998_l17>; + qca,bt-vdd-ldo-supply = <&pm8998_l25>; + + qca,bt-vdd-io-voltage-level = <1352000 1352000>; + qca,bt-vdd-xtal-voltage-level = <2040000 2040000>; + qca,bt-vdd-core-voltage-level = <1800000 1800000>; + qca,bt-vdd-pa-voltage-level = <1304000 1304000>; + qca,bt-vdd-ldo-voltage-level = <3312000 3312000>; + + qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */ + qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */ + }; +}; + +&soc { + gpio_keys { + compatible = "gpio-keys"; + label = "gpio-keys"; + + pinctrl-names = "default"; + pinctrl-0 = <&key_vol_up_default>; + + vol_up { + label = "volume_up"; + gpios = <&pm8998_gpios 6 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = <115>; + gpio-key,wakeup; + debounce-interval = <15>; + linux,can-disable; + }; + /* + cam_snapshot { + label = "cam_snapshot"; + gpios = <&pm8998_gpios 7 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = <766>; + gpio-key,wakeup; + debounce-interval = <15>; + linux,can-disable; + }; + + cam_focus { + label = "cam_focus"; + gpios = <&pm8998_gpios 8 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = <528>; + gpio-key,wakeup; + debounce-interval = <15>; + linux,can-disable; + }; + */ + }; +}; + +&labibb { + status = "ok"; + qcom,qpnp-labibb-mode = "lcd"; +}; + +&dsi_dual_nt35597_truly_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_nt35597_truly_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; +}; + +&dsi_nt35597_truly_dsc_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "single_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_nt35597_truly_dsc_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "single_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; +}; + +&dsi_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_nt35597_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_nt35597_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 52 0>; + qcom,platform-reset-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; +}; + +&pmi8998_wled { + qcom,string-cfg = <3>; + status = "okay"; +}; + +&pmi8998_haptics { + qcom,vmax-mv = <2400>; + status = "okay"; +}; + +&mdss_mdp { + #cooling-cells = <2>; +}; + +&ufsphy_mem { + compatible = "qcom,ufs-phy-qmp-v3"; + + vdda-phy-supply = <&pm8998_l1>; /* 0.88v */ + vdda-pll-supply = <&pm8998_l26>; /* 1.2v */ + vdda-phy-max-microamp = <62900>; + vdda-pll-max-microamp = <18300>; + + status = "ok"; +}; + +&ufshc_mem { + vdd-hba-supply = <&ufs_phy_gdsc>; + vdd-hba-fixed-regulator; + vcc-supply = <&pm8998_l20>; + vcc-voltage-level = <2950000 2960000>; + vccq2-supply = <&pm8998_s4>; + vcc-max-microamp = <600000>; + vccq2-max-microamp = <600000>; + + qcom,vddp-ref-clk-supply = <&pm8998_l2>; + qcom,vddp-ref-clk-max-microamp = <100>; + + status = "ok"; +}; + +&sdhc_2 { + vdd-supply = <&pm8998_l21>; + qcom,vdd-voltage-level = <2950000 2960000>; + qcom,vdd-current-level = <200 800000>; + + vdd-io-supply = <&pm8998_l13>; + qcom,vdd-io-voltage-level = <1808000 2960000>; + qcom,vdd-io-current-level = <200 22000>; + + pinctrl-names = "active", "sleep", "ds_400KHz", + "ds_50MHz", "ds_100MHz", "ds_200MHz"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &storage_cd>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>; + pinctrl-2 = <&sdc2_clk_ds_400KHz + &sdc2_cmd_ds_400KHz &sdc2_data_ds_400KHz>; + pinctrl-3 = <&sdc2_clk_ds_50MHz + &sdc2_cmd_ds_50MHz &sdc2_data_ds_50MHz>; + pinctrl-4 = <&sdc2_clk_ds_100MHz + &sdc2_cmd_ds_100MHz &sdc2_data_ds_100MHz>; + pinctrl-5 = <&sdc2_clk_ds_200MHz + &sdc2_cmd_ds_200MHz &sdc2_data_ds_200MHz>; + + cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>; + + status = "ok"; +}; + +&pmi8998_switch1 { + pinctrl-names = "led_enable", "led_disable"; + pinctrl-0 = <&flash_led3_front_en>; + pinctrl-1 = <&flash_led3_front_dis>; +}; + +&pmi8998_switch2 { + pinctrl-names = "led_enable", "led_disable"; + pinctrl-0 = <&flash_led3_iris_en>; + pinctrl-1 = <&flash_led3_iris_dis>; +}; + +&vendor { + mtp_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + }; + + extcon_usb1: extcon_usb1 { + compatible = "linux,extcon-usb-gpio"; + vbus-gpio = <&pmi8998_gpios 8 GPIO_ACTIVE_HIGH>; + + pinctrl-names = "default"; + pinctrl-0 = <&usb2_vbus_det_default>; + }; +}; + +&pmi8998_fg { + qcom,battery-data = <&mtp_batterydata>; +}; + +&smb1355_charger_0 { + status = "ok"; + qcom,disable-ctm; +}; + +&smb1355_charger_1 { + status = "ok"; + qcom,disable-ctm; +}; + +&qupv3_se9_2uart { + status = "ok"; +}; + +&qupv3_se8_spi { + status = "ok"; +}; + +&qupv3_se3_i2c { + status = "ok"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&tlmm 63 0x00>; + qcom,nq-ven = <&tlmm 12 0x00>; + qcom,nq-firm = <&tlmm 62 0x00>; + qcom,nq-clkreq = <&pm8998_gpios 21 0x00>; + qcom,nq-esepwr = <&tlmm 116 0x00>; + interrupt-parent = <&tlmm>; + interrupts = <63 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active + &nfc_enable_active + &nfc_clk_default>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>; + }; +}; + +&qupv3_se10_i2c { + status = "ok"; +}; + +&qupv3_se6_4uart { + status = "ok"; +}; + +&usb1 { + extcon = <&extcon_usb1>; +}; + +&ext_5v_boost { + status = "ok"; +}; + +&pm8998_vadc { + vph_pwr@83 { + label = "vph_pwr"; + reg = ; + qcom,pre-scaling = <1 1>; + }; + + vcoin@85 { + label = "vcoin"; + reg = ; + qcom,pre-scaling = <1 1>; + }; + + xo_therm@4c { + label = "xo_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; + + msm_therm@4d { + label = "msm_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; + + pa_therm1@4f { + label = "pa_therm1"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; + + quiet_therm@51 { + label = "quiet_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; +}; + +&pm8998_adc_tm { + io-channels = <&pm8998_vadc ADC_VPH_PWR>, + <&pm8998_vadc ADC_XO_THERM_PU2>, + <&pm8998_vadc ADC_AMUX_THM1_PU2>, + <&pm8998_vadc ADC_AMUX_THM3_PU2>, + <&pm8998_vadc ADC_AMUX_THM5_PU2>; + + /* Channel nodes */ + vph_pwr@83 { + label = "vph_pwr"; + reg = ; + }; + + xo_therm@4c { + label = "xo_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + msm_therm@4d { + label = "msm_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa_therm1@4f { + label = "pa_therm1"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + quiet_therm@51 { + label = "quiet_therm"; + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&thermal_zones { + xo-therm-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm 0x4c>; + wake-capable-sensor; + thermal-governor = "user_space"; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <10000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + msm-therm-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm 0x4d>; + wake-capable-sensor; + thermal-governor = "user_space"; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + }; + + pa-therm1-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm 0x4f>; + wake-capable-sensor; + thermal-governor = "user_space"; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + }; + + quiet-therm-adc { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8998_adc_tm 0x51>; + wake-capable-sensor; + thermal-governor = "user_space"; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + }; +}; + +&wil6210 { + status = "ok"; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi new file mode 100644 index 000000000000..41918e2d0cc9 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include + +&soc { + pcie0: qcom,pcie@0x1c00000 { + compatible = "qcom,pci-msm"; + cell-index = <0>; + + reg = <0x1c00000 0x2000>, + <0x1c06000 0x1000>, + <0x60000000 0xf1d>, + <0x60000f20 0xa8>, + <0x60100000 0x100000>, + <0x60200000 0x100000>, + <0x60300000 0xd00000>; + + reg-names = "parf", "phy", "dm_core", "elbi", + "conf", "io", "bars"; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x60200000 0x60200000 0x0 0x100000>, + <0x02000000 0x0 0x60300000 0x60300000 0x0 0xd00000>; + interrupt-parent = <&pcie0>; + interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 + 36 37>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0xffffffff>; + interrupt-map = <0 0 0 0 &intc GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH + 0 0 0 1 &intc GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH + 0 0 0 2 &intc GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH + 0 0 0 3 &intc GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH + 0 0 0 4 &intc GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH + 0 0 0 5 &intc GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH + 0 0 0 6 &intc GIC_SPI 672 IRQ_TYPE_LEVEL_HIGH + 0 0 0 7 &intc GIC_SPI 673 IRQ_TYPE_LEVEL_HIGH + 0 0 0 8 &intc GIC_SPI 674 IRQ_TYPE_LEVEL_HIGH + 0 0 0 9 &intc GIC_SPI 675 IRQ_TYPE_LEVEL_HIGH + 0 0 0 10 &intc GIC_SPI 676 IRQ_TYPE_LEVEL_HIGH + 0 0 0 11 &intc GIC_SPI 677 IRQ_TYPE_LEVEL_HIGH + 0 0 0 12 &intc GIC_SPI 678 IRQ_TYPE_LEVEL_HIGH + 0 0 0 13 &intc GIC_SPI 679 IRQ_TYPE_LEVEL_HIGH + 0 0 0 14 &intc GIC_SPI 680 IRQ_TYPE_LEVEL_HIGH + 0 0 0 15 &intc GIC_SPI 681 IRQ_TYPE_LEVEL_HIGH + 0 0 0 16 &intc GIC_SPI 682 IRQ_TYPE_LEVEL_HIGH + 0 0 0 17 &intc GIC_SPI 683 IRQ_TYPE_LEVEL_HIGH + 0 0 0 18 &intc GIC_SPI 684 IRQ_TYPE_LEVEL_HIGH + 0 0 0 19 &intc GIC_SPI 685 IRQ_TYPE_LEVEL_HIGH + 0 0 0 20 &intc GIC_SPI 686 IRQ_TYPE_LEVEL_HIGH + 0 0 0 21 &intc GIC_SPI 687 IRQ_TYPE_LEVEL_HIGH + 0 0 0 22 &intc GIC_SPI 688 IRQ_TYPE_LEVEL_HIGH + 0 0 0 23 &intc GIC_SPI 689 IRQ_TYPE_LEVEL_HIGH + 0 0 0 24 &intc GIC_SPI 690 IRQ_TYPE_LEVEL_HIGH + 0 0 0 25 &intc GIC_SPI 691 IRQ_TYPE_LEVEL_HIGH + 0 0 0 26 &intc GIC_SPI 692 IRQ_TYPE_LEVEL_HIGH + 0 0 0 27 &intc GIC_SPI 693 IRQ_TYPE_LEVEL_HIGH + 0 0 0 28 &intc GIC_SPI 694 IRQ_TYPE_LEVEL_HIGH + 0 0 0 29 &intc GIC_SPI 695 IRQ_TYPE_LEVEL_HIGH + 0 0 0 30 &intc GIC_SPI 696 IRQ_TYPE_LEVEL_HIGH + 0 0 0 31 &intc GIC_SPI 697 IRQ_TYPE_LEVEL_HIGH + 0 0 0 32 &intc GIC_SPI 698 IRQ_TYPE_LEVEL_HIGH + 0 0 0 33 &intc GIC_SPI 699 IRQ_TYPE_LEVEL_HIGH + 0 0 0 34 &intc GIC_SPI 700 IRQ_TYPE_LEVEL_HIGH + 0 0 0 35 &intc GIC_SPI 701 IRQ_TYPE_LEVEL_HIGH + 0 0 0 36 &intc GIC_SPI 702 IRQ_TYPE_LEVEL_HIGH + 0 0 0 37 &intc GIC_SPI 703 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "int_msi", "int_a", "int_b", "int_c", + "int_d", "int_global_int", + "msi_0", "msi_1", "msi_2", "msi_3", + "msi_4", "msi_5", "msi_6", "msi_7", + "msi_8", "msi_9", "msi_10", "msi_11", + "msi_12", "msi_13", "msi_14", "msi_15", + "msi_16", "msi_17", "msi_18", "msi_19", + "msi_20", "msi_21", "msi_22", "msi_23", + "msi_24", "msi_25", "msi_26", "msi_27", + "msi_28", "msi_29", "msi_30", "msi_31"; + + qcom,phy-sequence = <0x804 0x01 0x0 + 0x034 0x14 0x0 + 0x138 0x30 0x0 + 0x048 0x07 0x0 + 0x15c 0x06 0x0 + 0x090 0x01 0x0 + 0x088 0x20 0x0 + 0x0f0 0x00 0x0 + 0x0f8 0x01 0x0 + 0x0f4 0xc9 0x0 + 0x11c 0xff 0x0 + 0x120 0x3f 0x0 + 0x164 0x01 0x0 + 0x154 0x00 0x0 + 0x148 0x0a 0x0 + 0x05c 0x19 0x0 + 0x038 0x90 0x0 + 0x0b0 0x82 0x0 + 0x0c0 0x02 0x0 + 0x0bc 0xea 0x0 + 0x0b8 0xab 0x0 + 0x0a0 0x00 0x0 + 0x09c 0x0d 0x0 + 0x098 0x04 0x0 + 0x13c 0x00 0x0 + 0x060 0x06 0x0 + 0x068 0x16 0x0 + 0x070 0x36 0x0 + 0x184 0x01 0x0 + 0x138 0x33 0x0 + 0x03c 0x02 0x0 + 0x040 0x06 0x0 + 0x080 0x04 0x0 + 0x0dc 0x00 0x0 + 0x0d8 0x3f 0x0 + 0x00c 0x09 0x0 + 0x010 0x01 0x0 + 0x01c 0x40 0x0 + 0x020 0x01 0x0 + 0x014 0x02 0x0 + 0x018 0x00 0x0 + 0x024 0x7e 0x0 + 0x028 0x15 0x0 + 0x244 0x02 0x0 + 0x2a4 0x12 0x0 + 0x260 0x10 0x0 + 0x28c 0x06 0x0 + 0x504 0x03 0x0 + 0x500 0x10 0x0 + 0x50c 0x14 0x0 + 0x4d4 0x0e 0x0 + 0x4d8 0x04 0x0 + 0x4dc 0x1a 0x0 + 0x434 0x4b 0x0 + 0x414 0x04 0x0 + 0x40c 0x04 0x0 + 0x4f8 0x71 0x0 + 0x564 0x59 0x0 + 0x568 0x59 0x0 + 0x4fc 0x80 0x0 + 0x51c 0x40 0x0 + 0x444 0x71 0x0 + 0x43c 0x40 0x0 + 0x854 0x04 0x0 + 0x62c 0x52 0x0 + 0x654 0x10 0x0 + 0x65c 0x1a 0x0 + 0x660 0x06 0x0 + 0x8c8 0x83 0x0 + 0x8cc 0x09 0x0 + 0x8d0 0xa2 0x0 + 0x8d4 0x40 0x0 + 0x8c4 0x02 0x0 + 0x9ac 0x00 0x0 + 0x8a0 0x01 0x0 + 0x9e0 0x00 0x0 + 0x9dc 0x20 0x0 + 0x9a8 0x00 0x0 + 0x8a4 0x01 0x0 + 0x8a8 0x73 0x0 + 0x9d8 0xbb 0x0 + 0x9b0 0x03 0x0 + 0xa0c 0x0d 0x0 + 0x86c 0x00 0x0 + 0x644 0x00 0x0 + 0x804 0x03 0x0 + 0x800 0x00 0x0 + 0x808 0x03 0x0>; + + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_clkreq_default + &pcie0_perst_default + &pcie0_wake_default>; + + perst-gpio = <&tlmm 35 0>; + wake-gpio = <&tlmm 37 0>; + + gdsc-vdd-supply = <&pcie_0_gdsc>; + vreg-1.8-supply = <&pm8998_l26>; + vreg-0.9-supply = <&pm8998_l1>; + vreg-cx-supply = <&pm8998_s9_level>; + + qcom,vreg-1.8-voltage-level = <1200000 1200000 24000>; + qcom,vreg-0.9-voltage-level = <880000 880000 24000>; + qcom,vreg-cx-voltage-level = ; + + qcom,l1-supported; + qcom,l1ss-supported; + qcom,aux-clk-sync; + + qcom,ep-latency = <10>; + + qcom,phy-status-offset = <0x974>; + + qcom,boot-option = <0x1>; + + linux,pci-domain = <0>; + + qcom,msi-gicm-addr = <0x17a00040>; + qcom,msi-gicm-base = <0x2c0>; + + qcom,pcie-phy-ver = <0x30>; + qcom,use-19p2mhz-aux-clk; + + qcom,smmu-sid-base = <0x1c10>; + + iommu-map = <0x0 &apps_smmu 0x1c10 0x1>, + <0x100 &apps_smmu 0x1c11 0x1>, + <0x200 &apps_smmu 0x1c12 0x1>, + <0x300 &apps_smmu 0x1c13 0x1>, + <0x400 &apps_smmu 0x1c14 0x1>, + <0x500 &apps_smmu 0x1c15 0x1>, + <0x600 &apps_smmu 0x1c16 0x1>, + <0x700 &apps_smmu 0x1c17 0x1>, + <0x800 &apps_smmu 0x1c18 0x1>, + <0x900 &apps_smmu 0x1c19 0x1>, + <0xa00 &apps_smmu 0x1c1a 0x1>, + <0xb00 &apps_smmu 0x1c1b 0x1>, + <0xc00 &apps_smmu 0x1c1c 0x1>, + <0xd00 &apps_smmu 0x1c1d 0x1>, + <0xe00 &apps_smmu 0x1c1e 0x1>, + <0xf00 &apps_smmu 0x1c1f 0x1>; + + qcom,msm-bus,name = "pcie0"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <45 512 0 0>, + <45 512 500 800>; + + clocks = <&clock_gcc GCC_PCIE_0_PIPE_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_PCIE_0_AUX_CLK>, + <&clock_gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&clock_gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&clock_gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&clock_gcc GCC_PCIE_0_CLKREF_CLK>, + <&clock_gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, + <&clock_gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>, + <&clock_gcc GCC_PCIE_PHY_REFGEN_CLK>, + <&clock_gcc GCC_PCIE_PHY_AUX_CLK>; + + clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src", + "pcie_0_aux_clk", "pcie_0_cfg_ahb_clk", + "pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk", + "pcie_0_ldo", "pcie_0_slv_q2a_axi_clk", + "pcie_tbu_clk", "pcie_phy_refgen_clk", + "pcie_phy_aux_clk"; + + max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, <0>, + <0>, <0>, <0>, <0>, <100000000>, <0>; + + resets = <&clock_gcc GCC_PCIE_0_BCR>, + <&clock_gcc GCC_PCIE_0_PHY_BCR>; + + reset-names = "pcie_0_core_reset", + "pcie_0_phy_reset"; + }; + + pcie1: qcom,pcie@0x1c08000 { + compatible = "qcom,pci-msm"; + cell-index = <1>; + + reg = <0x1c08000 0x2000>, + <0x1c0a000 0x2000>, + <0x40000000 0xf1d>, + <0x40000f20 0xa8>, + <0x40100000 0x100000>, + <0x40200000 0x100000>, + <0x40300000 0x1fd00000>; + + reg-names = "parf", "phy", "dm_core", "elbi", + "conf", "io", "bars"; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x40200000 0x40200000 0x0 0x100000>, + <0x02000000 0x0 0x40300000 0x40300000 0x0 0x1fd00000>; + interrupt-parent = <&pcie1>; + interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 + 36 37>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0xffffffff>; + interrupt-map = <0 0 0 0 &intc GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH + 0 0 0 1 &intc GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH + 0 0 0 2 &intc GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH + 0 0 0 3 &intc GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH + 0 0 0 4 &intc GIC_SPI 439 IRQ_TYPE_LEVEL_HIGH + 0 0 0 5 &intc GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH + 0 0 0 6 &intc GIC_SPI 704 IRQ_TYPE_LEVEL_HIGH + 0 0 0 7 &intc GIC_SPI 705 IRQ_TYPE_LEVEL_HIGH + 0 0 0 8 &intc GIC_SPI 706 IRQ_TYPE_LEVEL_HIGH + 0 0 0 9 &intc GIC_SPI 707 IRQ_TYPE_LEVEL_HIGH + 0 0 0 10 &intc GIC_SPI 708 IRQ_TYPE_LEVEL_HIGH + 0 0 0 11 &intc GIC_SPI 709 IRQ_TYPE_LEVEL_HIGH + 0 0 0 12 &intc GIC_SPI 710 IRQ_TYPE_LEVEL_HIGH + 0 0 0 13 &intc GIC_SPI 711 IRQ_TYPE_LEVEL_HIGH + 0 0 0 14 &intc GIC_SPI 712 IRQ_TYPE_LEVEL_HIGH + 0 0 0 15 &intc GIC_SPI 713 IRQ_TYPE_LEVEL_HIGH + 0 0 0 16 &intc GIC_SPI 714 IRQ_TYPE_LEVEL_HIGH + 0 0 0 17 &intc GIC_SPI 715 IRQ_TYPE_LEVEL_HIGH + 0 0 0 18 &intc GIC_SPI 716 IRQ_TYPE_LEVEL_HIGH + 0 0 0 19 &intc GIC_SPI 717 IRQ_TYPE_LEVEL_HIGH + 0 0 0 20 &intc GIC_SPI 718 IRQ_TYPE_LEVEL_HIGH + 0 0 0 21 &intc GIC_SPI 719 IRQ_TYPE_LEVEL_HIGH + 0 0 0 22 &intc GIC_SPI 720 IRQ_TYPE_LEVEL_HIGH + 0 0 0 23 &intc GIC_SPI 721 IRQ_TYPE_LEVEL_HIGH + 0 0 0 24 &intc GIC_SPI 722 IRQ_TYPE_LEVEL_HIGH + 0 0 0 25 &intc GIC_SPI 723 IRQ_TYPE_LEVEL_HIGH + 0 0 0 26 &intc GIC_SPI 724 IRQ_TYPE_LEVEL_HIGH + 0 0 0 27 &intc GIC_SPI 725 IRQ_TYPE_LEVEL_HIGH + 0 0 0 28 &intc GIC_SPI 726 IRQ_TYPE_LEVEL_HIGH + 0 0 0 29 &intc GIC_SPI 727 IRQ_TYPE_LEVEL_HIGH + 0 0 0 30 &intc GIC_SPI 728 IRQ_TYPE_LEVEL_HIGH + 0 0 0 31 &intc GIC_SPI 729 IRQ_TYPE_LEVEL_HIGH + 0 0 0 32 &intc GIC_SPI 730 IRQ_TYPE_LEVEL_HIGH + 0 0 0 33 &intc GIC_SPI 731 IRQ_TYPE_LEVEL_HIGH + 0 0 0 34 &intc GIC_SPI 732 IRQ_TYPE_LEVEL_HIGH + 0 0 0 35 &intc GIC_SPI 733 IRQ_TYPE_LEVEL_HIGH + 0 0 0 36 &intc GIC_SPI 734 IRQ_TYPE_LEVEL_HIGH + 0 0 0 37 &intc GIC_SPI 735 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "int_msi", "int_a", "int_b", "int_c", + "int_d", "int_global_int", + "msi_0", "msi_1", "msi_2", "msi_3", + "msi_4", "msi_5", "msi_6", "msi_7", + "msi_8", "msi_9", "msi_10", "msi_11", + "msi_12", "msi_13", "msi_14", "msi_15", + "msi_16", "msi_17", "msi_18", "msi_19", + "msi_20", "msi_21", "msi_22", "msi_23", + "msi_24", "msi_25", "msi_26", "msi_27", + "msi_28", "msi_29", "msi_30", "msi_31"; + + qcom,phy-sequence = <0x1804 0x03 0x0 + 0x00dc 0x27 0x0 + 0x0014 0x01 0x0 + 0x0020 0x31 0x0 + 0x0024 0x01 0x0 + 0x0028 0xde 0x0 + 0x002c 0x07 0x0 + 0x0034 0x4c 0x0 + 0x0038 0x06 0x0 + 0x0054 0x18 0x0 + 0x0058 0xb0 0x0 + 0x006c 0x8c 0x0 + 0x0070 0x20 0x0 + 0x0078 0x14 0x0 + 0x007c 0x34 0x0 + 0x00b4 0x06 0x0 + 0x00b8 0x06 0x0 + 0x00c0 0x16 0x0 + 0x00c4 0x16 0x0 + 0x00cc 0x36 0x0 + 0x00d0 0x36 0x0 + 0x00f0 0x05 0x0 + 0x00f8 0x42 0x0 + 0x0100 0x82 0x0 + 0x0108 0x68 0x0 + 0x011c 0x55 0x0 + 0x0120 0x55 0x0 + 0x0124 0x03 0x0 + 0x0128 0xab 0x0 + 0x012c 0xaa 0x0 + 0x0130 0x02 0x0 + 0x0150 0x3f 0x0 + 0x0158 0x3f 0x0 + 0x0178 0x10 0x0 + 0x01cc 0x04 0x0 + 0x01d0 0x30 0x0 + 0x01e0 0x04 0x0 + 0x01e8 0x73 0x0 + 0x01f0 0x0c 0x0 + 0x01fc 0x15 0x0 + 0x021c 0x04 0x0 + 0x0224 0x01 0x0 + 0x0228 0x22 0x0 + 0x022c 0x00 0x0 + 0x0098 0x20 0x0 + 0x01c8 0x07 0x0 + 0x080c 0x00 0x0 + 0x0818 0x0d 0x0 + 0x0860 0x01 0x0 + 0x0864 0x1a 0x0 + 0x087c 0x2f 0x0 + 0x08c0 0x09 0x0 + 0x08c4 0x09 0x0 + 0x08c8 0x1b 0x0 + 0x08d0 0x01 0x0 + 0x08d4 0x07 0x0 + 0x08d8 0x31 0x0 + 0x08dc 0x31 0x0 + 0x08e0 0x03 0x0 + 0x08fc 0x02 0x0 + 0x0900 0x00 0x0 + 0x0908 0x12 0x0 + 0x0914 0x25 0x0 + 0x0918 0x00 0x0 + 0x091c 0x05 0x0 + 0x0920 0x01 0x0 + 0x0924 0x26 0x0 + 0x0928 0x12 0x0 + 0x0930 0x04 0x0 + 0x0934 0x04 0x0 + 0x0938 0x09 0x0 + 0x0954 0x15 0x0 + 0x0960 0x28 0x0 + 0x0968 0x7f 0x0 + 0x096c 0x07 0x0 + 0x0978 0x04 0x0 + 0x0980 0x70 0x0 + 0x0984 0x8b 0x0 + 0x0988 0x08 0x0 + 0x098c 0x0a 0x0 + 0x0990 0x03 0x0 + 0x0994 0x04 0x0 + 0x0998 0x04 0x0 + 0x099c 0x0c 0x0 + 0x09a4 0x02 0x0 + 0x09c0 0x5c 0x0 + 0x09c4 0x3e 0x0 + 0x09c8 0x3f 0x0 + 0x0a30 0x01 0x0 + 0x0a34 0xa0 0x0 + 0x0a38 0x08 0x0 + 0x0aa4 0x01 0x0 + 0x0aac 0xc3 0x0 + 0x0ab0 0x00 0x0 + 0x0ab8 0xbc 0x0 + 0x0ac0 0x7f 0x0 + 0x0ac4 0x15 0x0 + 0x0810 0x0c 0x0 + 0x0814 0x0f 0x0 + 0x0acc 0x04 0x0 + 0x093c 0x20 0x0 + 0x195c 0x3f 0x0 + 0x1974 0x50 0x0 + 0x182c 0x19 0x0 + 0x1840 0x07 0x0 + 0x1854 0x17 0x0 + 0x1868 0x09 0x0 + 0x196c 0x9f 0x0 + 0x1800 0x00 0x0 + 0x0aa8 0x01 0x0 + 0x1808 0x01 0x0>; + + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_clkreq_default + &pcie1_perst_default + &pcie1_wake_default>; + + perst-gpio = <&tlmm 102 0>; + wake-gpio = <&tlmm 104 0>; + + gdsc-vdd-supply = <&pcie_1_gdsc>; + vreg-1.8-supply = <&pm8998_l26>; + vreg-0.9-supply = <&pm8998_l1>; + vreg-cx-supply = <&pm8998_s9_level>; + + qcom,vreg-1.8-voltage-level = <1200000 1200000 24000>; + qcom,vreg-0.9-voltage-level = <880000 880000 24000>; + qcom,vreg-cx-voltage-level = ; + + qcom,l1-supported; + qcom,l1ss-supported; + qcom,aux-clk-sync; + + qcom,ep-latency = <10>; + + qcom,slv-addr-space-size = <0x20000000>; + + qcom,phy-status-offset = <0x1aac>; + + qcom,boot-option = <0x1>; + + linux,pci-domain = <1>; + + qcom,msi-gicm-addr = <0x17a00040>; + qcom,msi-gicm-base = <0x2e0>; + + qcom,max-link-speed = <0x3>; + + qcom,use-19p2mhz-aux-clk; + + qcom,smmu-sid-base = <0x1c00>; + + iommu-map = <0x0 &apps_smmu 0x1c00 0x1>, + <0x100 &apps_smmu 0x1c01 0x1>, + <0x200 &apps_smmu 0x1c02 0x1>, + <0x300 &apps_smmu 0x1c03 0x1>, + <0x400 &apps_smmu 0x1c04 0x1>, + <0x500 &apps_smmu 0x1c05 0x1>, + <0x600 &apps_smmu 0x1c06 0x1>, + <0x700 &apps_smmu 0x1c07 0x1>, + <0x800 &apps_smmu 0x1c08 0x1>, + <0x900 &apps_smmu 0x1c09 0x1>, + <0xa00 &apps_smmu 0x1c0a 0x1>, + <0xb00 &apps_smmu 0x1c0b 0x1>, + <0xc00 &apps_smmu 0x1c0c 0x1>, + <0xd00 &apps_smmu 0x1c0d 0x1>, + <0xe00 &apps_smmu 0x1c0e 0x1>, + <0xf00 &apps_smmu 0x1c0f 0x1>; + + qcom,msm-bus,name = "pcie1"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <100 512 0 0>, + <100 512 500 800>; + + clocks = <&clock_gcc GCC_PCIE_1_PIPE_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_PCIE_1_AUX_CLK>, + <&clock_gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&clock_gcc GCC_PCIE_1_MSTR_AXI_CLK>, + <&clock_gcc GCC_PCIE_1_SLV_AXI_CLK>, + <&clock_gcc GCC_PCIE_1_CLKREF_CLK>, + <&clock_gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>, + <&clock_gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>, + <&clock_gcc GCC_PCIE_PHY_REFGEN_CLK>, + <&clock_gcc GCC_PCIE_PHY_AUX_CLK>; + + clock-names = "pcie_1_pipe_clk", "pcie_1_ref_clk_src", + "pcie_1_aux_clk", "pcie_1_cfg_ahb_clk", + "pcie_1_mstr_axi_clk", "pcie_1_slv_axi_clk", + "pcie_1_ldo", "pcie_1_slv_q2a_axi_clk", + "pcie_tbu_clk", "pcie_phy_refgen_clk", + "pcie_phy_aux_clk"; + + max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, <0>, + <0>, <0>, <0>, <0>, <100000000>, <0>; + + resets = <&clock_gcc GCC_PCIE_1_BCR>, + <&clock_gcc GCC_PCIE_1_PHY_BCR>; + + reset-names = "pcie_1_core_reset", + "pcie_1_phy_reset"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi new file mode 100644 index 000000000000..2ac159a79871 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +&pmi8998_gpios { + usb2_vbus_boost { + usb2_vbus_boost_default: usb2_vbus_boost_default { + pins = "gpio2"; + function = "normal"; + output-low; + power-source = <0>; + }; + }; + + qnovo_fet_ctrl { + qnovo_fet_ctrl_default: qnovo_fet_ctrl_default { + pins = "gpio6"; + function = "func1"; + output-low; + input-disable; + bias-disable; + power-source = <0>; + qcom,drive-strength = <1>; + }; + }; + + usb2_vbus_det { + usb2_vbus_det_default: usb2_vbus_det_default { + pins = "gpio8"; + function = "normal"; + input-enable; + bias-pull-down; + power-source = <1>; /* VPH input supply */ + }; + }; + + usb2_id_det { + usb2_id_det_default: usb2_id_det_default { + pins = "gpio9"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + usb2_ext_5v_boost { + usb2_ext_5v_boost_default: usb2_ext_5v_boost_default { + pins = "gpio10"; + function = "normal"; + output-low; + power-source = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi new file mode 100644 index 000000000000..0cfda27e63e6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi @@ -0,0 +1,3746 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +&soc { + tlmm: pinctrl@03400000 { + compatible = "qcom,sdm845-pinctrl"; + reg = <0x03400000 0xc00000>, <0x179900F0 0x60>; + reg-names = "pinctrl_regs", "spi_cfg_regs"; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + wakeup-parent = <&pdc>; + irqdomain-map = <1 0 &pdc 30 0>, + <3 0 &pdc 31 0>, + <5 0 &pdc 32 0>, + <10 0 &pdc 33 0>, + <11 0 &pdc 34 0>, + <20 0 &pdc 35 0>, + <22 0 &pdc 36 0>, + <24 0 &pdc 37 0>, + <26 0 &pdc 38 0>, + <30 0 &pdc 39 0>, + <31 0 &pdc 117 0>, + <32 0 &pdc 41 0>, + <34 0 &pdc 42 0>, + <36 0 &pdc 43 0>, + <37 0 &pdc 44 0>, + <38 0 &pdc 45 0>, + <39 0 &pdc 46 0>, + <40 0 &pdc 47 0>, + <41 0 &pdc 115 0>, + <43 0 &pdc 49 0>, + <44 0 &pdc 50 0>, + <46 0 &pdc 51 0>, + <48 0 &pdc 52 0>, + <49 0 &pdc 118 0>, + <52 0 &pdc 54 0>, + <53 0 &pdc 55 0>, + <54 0 &pdc 56 0>, + <56 0 &pdc 57 0>, + <57 0 &pdc 58 0>, + <58 0 &pdc 59 0>, + <59 0 &pdc 60 0>, + <60 0 &pdc 61 0>, + <61 0 &pdc 62 0>, + <62 0 &pdc 63 0>, + <63 0 &pdc 64 0>, + <64 0 &pdc 65 0>, + <66 0 &pdc 66 0>, + <68 0 &pdc 67 0>, + <71 0 &pdc 68 0>, + <73 0 &pdc 69 0>, + <77 0 &pdc 70 0>, + <78 0 &pdc 71 0>, + <79 0 &pdc 72 0>, + <80 0 &pdc 73 0>, + <84 0 &pdc 74 0>, + <85 0 &pdc 75 0>, + <86 0 &pdc 76 0>, + <88 0 &pdc 77 0>, + <89 0 &pdc 116 0>, + <91 0 &pdc 79 0>, + <92 0 &pdc 80 0>, + <95 0 &pdc 81 0>, + <96 0 &pdc 82 0>, + <97 0 &pdc 83 0>, + <101 0 &pdc 84 0>, + <103 0 &pdc 85 0>, + <104 0 &pdc 86 0>, + <115 0 &pdc 90 0>, + <116 0 &pdc 91 0>, + <117 0 &pdc 92 0>, + <118 0 &pdc 93 0>, + <119 0 &pdc 94 0>, + <120 0 &pdc 95 0>, + <121 0 &pdc 96 0>, + <122 0 &pdc 97 0>, + <123 0 &pdc 98 0>, + <124 0 &pdc 99 0>, + <125 0 &pdc 100 0>, + //<126 0 &pdc ?? 0>, + <127 0 &pdc 102 0>, + <128 0 &pdc 103 0>, + <129 0 &pdc 104 0>, + <130 0 &pdc 105 0>, + <132 0 &pdc 106 0>, + <133 0 &pdc 107 0>, + <145 0 &pdc 108 0>; + irqdomain-map-mask = <0xff 0>; + irqdomain-map-pass-thru = <0 0xff>; + + ufs_dev_reset_assert: ufs_dev_reset_assert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * UFS_RESET driver strengths are having + * different values/steps compared to typical + * GPIO drive strengths. + * + * Following table clarifies: + * + * HDRV value | UFS_RESET | Typical GPIO + * (dec) | (mA) | (mA) + * 0 | 0.8 | 2 + * 1 | 1.55 | 4 + * 2 | 2.35 | 6 + * 3 | 3.1 | 8 + * 4 | 3.9 | 10 + * 5 | 4.65 | 12 + * 6 | 5.4 | 14 + * 7 | 6.15 | 16 + * + * POR value for UFS_RESET HDRV is 3 which means + * 3.1mA and we want to use that. Hence just + * specify 8mA to "drive-strength" binding and + * that should result into writing 3 to HDRV + * field. + */ + drive-strength = <8>; /* default: 3.1 mA */ + output-low; /* active low reset */ + }; + }; + + ufs_dev_reset_deassert: ufs_dev_reset_deassert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * default: 3.1 mA + * check comments under ufs_dev_reset_assert + */ + drive-strength = <8>; + output-high; /* active low reset */ + }; + }; + + flash_led3_front { + flash_led3_front_en: flash_led3_front_en { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive_strength = <2>; + output-high; + bias-disable; + }; + }; + + flash_led3_front_dis: flash_led3_front_dis { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive_strength = <2>; + output-low; + bias-disable; + }; + }; + }; + + flash_led3_iris { + flash_led3_iris_en: flash_led3_iris_en { + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + drive_strength = <2>; + output-high; + bias-disable; + }; + }; + + flash_led3_iris_dis: flash_led3_iris_dis { + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + drive_strength = <2>; + output-low; + bias-disable; + }; + }; + }; + + + wcd9xxx_intr { + wcd_intr_default: wcd_intr_default{ + mux { + pins = "gpio54"; + function = "gpio"; + }; + + config { + pins = "gpio54"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + input-enable; + }; + }; + }; + + storage_cd: storage_cd { + mux { + pins = "gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio126"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_clk_on: sdc2_clk_on { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_off: sdc2_clk_off { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_clk_ds_400KHz: sdc2_clk_ds_400KHz { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_ds_50MHz: sdc2_clk_ds_50MHz { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_ds_100MHz: sdc2_clk_ds_100MHz { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_ds_200MHz: sdc2_clk_ds_200MHz { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_cmd_on: sdc2_cmd_on { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_off: sdc2_cmd_off { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cmd_ds_400KHz: sdc2_cmd_ds_400KHz { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_ds_50MHz: sdc2_cmd_ds_50MHz { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_ds_100MHz: sdc2_cmd_ds_100MHz { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_ds_200MHz: sdc2_cmd_ds_200MHz { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_on: sdc2_data_on { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_off: sdc2_data_off { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_data_ds_400KHz: sdc2_data_ds_400KHz { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_ds_50MHz: sdc2_data_ds_50MHz { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_ds_100MHz: sdc2_data_ds_100MHz { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_ds_200MHz: sdc2_data_ds_200MHz { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + pcie0 { + pcie0_clkreq_default: pcie0_clkreq_default { + mux { + pins = "gpio36"; + function = "pci_e0"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie0_perst_default: pcie0_perst_default { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + pcie0_wake_default: pcie0_wake_default { + mux { + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie0_3v3_on: pcie0_3v3_on { + mux { + pins = "gpio90"; + function = "gpio"; + }; + config { + pins = "gpio90"; + drive_strength = <2>; + bias-disable; + output-high; + }; + }; + + pcie0_1v5_on: pcie0_1v5_on { + mux { + pins = "gpio90"; + function = "gpio"; + }; + config { + pins = "gpio90"; + drive_strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + pcie1 { + pcie1_clkreq_default: pcie1_clkreq_default { + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie1_perst_default: pcie1_perst_default { + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + pcie1_wake_default: pcie1_wake_default { + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + cdc_reset_ctrl { + cdc_reset_sleep: cdc_reset_sleep { + mux { + pins = "gpio64"; + function = "gpio"; + }; + config { + pins = "gpio64"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + cdc_reset_active:cdc_reset_active { + mux { + pins = "gpio64"; + function = "gpio"; + }; + config { + pins = "gpio64"; + drive-strength = <8>; + bias-pull-down; + output-high; + }; + }; + }; + + spkr_i2s_clk_pin { + spkr_i2s_clk_sleep: spkr_i2s_clk_sleep { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + spkr_i2s_clk_active: spkr_i2s_clk_active { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + wcd_gnd_mic_swap { + wcd_gnd_mic_swap_idle: wcd_gnd_mic_swap_idle { + mux { + pins = "gpio51"; + function = "gpio"; + }; + config { + pins = "gpio51"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + + wcd_gnd_mic_swap_active: wcd_gnd_mic_swap_active { + mux { + pins = "gpio51"; + function = "gpio"; + }; + config { + pins = "gpio51"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + /* USB C analog configuration */ + wcd_usbc_analog_en1 { + wcd_usbc_analog_en1_idle: wcd_usbc_ana_en1_idle { + mux { + pins = "gpio49"; + function = "gpio"; + }; + config { + pins = "gpio49"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + + wcd_usbc_analog_en1_active: wcd_usbc_ana_en1_active { + mux { + pins = "gpio49"; + function = "gpio"; + }; + config { + pins = "gpio49"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + wcd_usbc_analog_en2 { + wcd_usbc_analog_en2_idle: wcd_usbc_ana_en2_idle { + mux { + pins = "gpio51"; + function = "gpio"; + }; + config { + pins = "gpio51"; + drive-strength = <2>; + bias-pull-down; + output-low; + }; + }; + + wcd_usbc_analog_en2_active: wcd_usbc_ana_en2_active { + mux { + pins = "gpio51"; + function = "gpio"; + }; + config { + pins = "gpio51"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + }; + + pri_aux_pcm_clk { + pri_aux_pcm_clk_sleep: pri_aux_pcm_clk_sleep { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_clk_active: pri_aux_pcm_clk_active { + mux { + pins = "gpio65"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_aux_pcm_sync { + pri_aux_pcm_sync_sleep: pri_aux_pcm_sync_sleep { + mux { + pins = "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio66"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_sync_active: pri_aux_pcm_sync_active { + mux { + pins = "gpio66"; + function = "pri_mi2s_ws"; + }; + + config { + pins = "gpio66"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_aux_pcm_din { + pri_aux_pcm_din_sleep: pri_aux_pcm_din_sleep { + mux { + pins = "gpio67"; + function = "gpio"; + }; + + config { + pins = "gpio67"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_din_active: pri_aux_pcm_din_active { + mux { + pins = "gpio67"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio67"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_aux_pcm_dout { + pri_aux_pcm_dout_sleep: pri_aux_pcm_dout_sleep { + mux { + pins = "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio68"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_aux_pcm_dout_active: pri_aux_pcm_dout_active { + mux { + pins = "gpio68"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio68"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pmx_sde: pmx_sde { + sde_dsi_active: sde_dsi_active { + mux { + pins = "gpio6", "gpio52"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio52"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + }; + }; + sde_dsi_suspend: sde_dsi_suspend { + mux { + pins = "gpio6", "gpio52"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio52"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + }; + + pmx_sde_te { + sde_te_active: sde_te_active { + mux { + pins = "gpio10"; + function = "mdp_vsync"; + }; + + config { + pins = "gpio10"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + sde_te_suspend: sde_te_suspend { + mux { + pins = "gpio10"; + function = "mdp_vsync"; + }; + + config { + pins = "gpio10"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + }; + + sde_dp_aux_active: sde_dp_aux_active { + mux { + pins = "gpio43", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio43", "gpio51"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; + }; + + sde_dp_aux_suspend: sde_dp_aux_suspend { + mux { + pins = "gpio43", "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio43", "gpio51"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + sde_dp_usbplug_cc_active: sde_dp_usbplug_cc_active { + mux { + pins = "gpio38"; + function = "gpio"; + }; + + config { + pins = "gpio38"; + bias-disable; + drive-strength = <16>; + }; + }; + + sde_dp_usbplug_cc_suspend: sde_dp_usbplug_cc_suspend { + mux { + pins = "gpio38"; + function = "gpio"; + }; + + config { + pins = "gpio38"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + /* add pingrp for touchscreen */ + pmx_ts_int_active { + ts_int_active: ts_int_active { + mux { + pins = "gpio122"; + function = "gpio"; + }; + + config { + pins = "gpio122"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + pmx_ts_int_suspend { + ts_int_suspend1: ts_int_suspend1 { + mux { + pins = "gpio122"; + function = "gpio"; + }; + + config { + pins = "gpio122"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + pmx_ts_reset_active { + ts_reset_active: ts_reset_active { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + pmx_ts_reset_suspend { + ts_reset_suspend1: ts_reset_suspend1 { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + pmx_ts_release { + ts_release: ts_release { + mux { + pins = "gpio122", "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio122", "gpio99"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + ts_mux { + ts_active: ts_active { + mux { + pins = "gpio99", "gpio122"; + function = "gpio"; + }; + + config { + pins = "gpio99", "gpio122"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ts_reset_suspend: ts_reset_suspend { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + ts_int_suspend: ts_int_suspend { + mux { + pins = "gpio122"; + function = "gpio"; + }; + + config { + pins = "gpio122"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + ext_bridge_mux { + lt9611_pins: lt9611_pins { + mux { + pins = "gpio84", "gpio128", "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio84", "gpio128", "gpio89"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; + }; + }; + + sec_aux_pcm { + sec_aux_pcm_sleep: sec_aux_pcm_sleep { + mux { + pins = "gpio80", "gpio81"; + function = "gpio"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_active: sec_aux_pcm_active { + mux { + pins = "gpio80", "gpio81"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_aux_pcm_din { + sec_aux_pcm_din_sleep: sec_aux_pcm_din_sleep { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_din_active: sec_aux_pcm_din_active { + mux { + pins = "gpio82"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio82"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_aux_pcm_dout { + sec_aux_pcm_dout_sleep: sec_aux_pcm_dout_sleep { + mux { + pins = "gpio83"; + function = "gpio"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_aux_pcm_dout_active: sec_aux_pcm_dout_active { + mux { + pins = "gpio83"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio83"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_aux_pcm { + tert_aux_pcm_sleep: tert_aux_pcm_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "gpio"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_active: tert_aux_pcm_active { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + tert_aux_pcm_din { + tert_aux_pcm_din_sleep: tert_aux_pcm_din_sleep { + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_din_active: tert_aux_pcm_din_active { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_aux_pcm_dout { + tert_aux_pcm_dout_sleep: tert_aux_pcm_dout_sleep { + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_aux_pcm_dout_active: tert_aux_pcm_dout_active { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_aux_pcm { + quat_aux_pcm_sleep: quat_aux_pcm_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "gpio"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_active: quat_aux_pcm_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + quat_aux_pcm_din { + quat_aux_pcm_din_sleep: quat_aux_pcm_din_sleep { + mux { + pins = "gpio60"; + function = "gpio"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_din_active: quat_aux_pcm_din_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_aux_pcm_dout { + quat_aux_pcm_dout_sleep: quat_aux_pcm_dout_sleep { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_aux_pcm_dout_active: quat_aux_pcm_dout_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_mi2s_mclk { + pri_mi2s_mclk_sleep: pri_mi2s_mclk_sleep { + mux { + pins = "gpio64"; + function = "gpio"; + }; + + config { + pins = "gpio64"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_mclk_active: pri_mi2s_mclk_active { + mux { + pins = "gpio64"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio64"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_sck { + pri_mi2s_sck_sleep: pri_mi2s_sck_sleep { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sck_active: pri_mi2s_sck_active { + mux { + pins = "gpio65"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_ws { + pri_mi2s_ws_sleep: pri_mi2s_ws_sleep { + mux { + pins = "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio66"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_ws_active: pri_mi2s_ws_active { + mux { + pins = "gpio66"; + function = "pri_mi2s_ws"; + }; + + config { + pins = "gpio66"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + pri_mi2s_sd0 { + pri_mi2s_sd0_sleep: pri_mi2s_sd0_sleep { + mux { + pins = "gpio67"; + function = "gpio"; + }; + + config { + pins = "gpio67"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sd0_active: pri_mi2s_sd0_active { + mux { + pins = "gpio67"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio67"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + pri_mi2s_sd1 { + pri_mi2s_sd1_sleep: pri_mi2s_sd1_sleep { + mux { + pins = "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio68"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + pri_mi2s_sd1_active: pri_mi2s_sd1_active { + mux { + pins = "gpio68"; + function = "pri_mi2s"; + }; + + config { + pins = "gpio68"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_mclk { + sec_mi2s_mclk_sleep: sec_mi2s_mclk_sleep { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_mclk_active: sec_mi2s_mclk_active { + mux { + pins = "gpio79"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio79"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s { + sec_mi2s_sleep: sec_mi2s_sleep { + mux { + pins = "gpio80", "gpio81"; + function = "gpio"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <2>; /* 2 mA */ + bias-disable; /* NO PULL */ + input-enable; + }; + }; + + sec_mi2s_active: sec_mi2s_active { + mux { + pins = "gpio80", "gpio81"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio80", "gpio81"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_sd0 { + sec_mi2s_sd0_sleep: sec_mi2s_sd0_sleep { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_sd0_active: sec_mi2s_sd0_active { + mux { + pins = "gpio82"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio82"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + sec_mi2s_sd1 { + sec_mi2s_sd1_sleep: sec_mi2s_sd1_sleep { + mux { + pins = "gpio83"; + function = "gpio"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + sec_mi2s_sd1_active: sec_mi2s_sd1_active { + mux { + pins = "gpio83"; + function = "sec_mi2s"; + }; + + config { + pins = "gpio83"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s_mclk { + tert_mi2s_mclk_sleep: tert_mi2s_mclk_sleep { + mux { + pins = "gpio74"; + function = "gpio"; + }; + + config { + pins = "gpio74"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_mclk_active: tert_mi2s_mclk_active { + mux { + pins = "gpio74"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio74"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s { + tert_mi2s_sleep: tert_mi2s_sleep { + mux { + pins = "gpio75", "gpio76"; + function = "gpio"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_active: tert_mi2s_active { + mux { + pins = "gpio75", "gpio76"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio75", "gpio76"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + tert_mi2s_sd0 { + tert_mi2s_sd0_sleep: tert_mi2s_sd0_sleep { + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_sd0_active: tert_mi2s_sd0_active { + mux { + pins = "gpio77"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio77"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + tert_mi2s_sd1 { + tert_mi2s_sd1_sleep: tert_mi2s_sd1_sleep { + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + tert_mi2s_sd1_active: tert_mi2s_sd1_active { + mux { + pins = "gpio78"; + function = "ter_mi2s"; + }; + + config { + pins = "gpio78"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_mclk { + quat_mi2s_mclk_sleep: quat_mi2s_mclk_sleep { + mux { + pins = "gpio57"; + function = "gpio"; + }; + + config { + pins = "gpio57"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_mclk_active: quat_mi2s_mclk_active { + mux { + pins = "gpio57"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio57"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s { + quat_mi2s_sleep: quat_mi2s_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "gpio"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_active: quat_mi2s_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + output-high; + }; + }; + }; + + quat_mi2s_sd0 { + quat_mi2s_sd0_sleep: quat_mi2s_sd0_sleep { + mux { + pins = "gpio60"; + function = "gpio"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd0_active: quat_mi2s_sd0_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd1 { + quat_mi2s_sd1_sleep: quat_mi2s_sd1_sleep { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd1_active: quat_mi2s_sd1_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd2 { + quat_mi2s_sd2_sleep: quat_mi2s_sd2_sleep { + mux { + pins = "gpio62"; + function = "gpio"; + }; + + config { + pins = "gpio62"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd2_active: quat_mi2s_sd2_active { + mux { + pins = "gpio62"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio62"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_mi2s_sd3 { + quat_mi2s_sd3_sleep: quat_mi2s_sd3_sleep { + mux { + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + input-enable; + }; + }; + + quat_mi2s_sd3_active: quat_mi2s_sd3_active { + mux { + pins = "gpio63"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio63"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_tdm { + quat_tdm_sleep: quat_tdm_sleep { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + quat_tdm_active: quat_tdm_active { + mux { + pins = "gpio58", "gpio59"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio58", "gpio59"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_tdm_dout { + quat_tdm_dout_sleep: quat_tdm_dout_sleep { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + quat_tdm_dout_active: quat_tdm_dout_active { + mux { + pins = "gpio61"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; /* 2 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + quat_tdm_din { + quat_tdm_din_sleep: quat_tdm_din_sleep { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + quat_tdm_din_active: quat_tdm_din_active { + mux { + pins = "gpio60"; + function = "qua_mi2s"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; /* 2 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; + + /* QUPv3 South SE mappings */ + /* SE 0 pin mappings */ + qupv3_se0_i2c_pins: qupv3_se0_i2c_pins { + qupv3_se0_i2c_active: qupv3_se0_i2c_active { + mux { + pins = "gpio0", "gpio1"; + function = "qup0"; + }; + + config { + pins = "gpio0", "gpio1"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se0_i2c_sleep: qupv3_se0_i2c_sleep { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + }; + + config { + pins = "gpio0", "gpio1"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se0_spi_pins: qupv3_se0_spi_pins { + qupv3_se0_spi_active: qupv3_se0_spi_active { + mux { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + function = "qup0"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se0_spi_sleep: qupv3_se0_spi_sleep { + mux { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + function = "gpio"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 1 pin mappings */ + qupv3_se1_i2c_pins: qupv3_se1_i2c_pins { + qupv3_se1_i2c_active: qupv3_se1_i2c_active { + mux { + pins = "gpio17", "gpio18"; + function = "qup1"; + }; + + config { + pins = "gpio17", "gpio18"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se1_i2c_sleep: qupv3_se1_i2c_sleep { + mux { + pins = "gpio17", "gpio18"; + function = "gpio"; + }; + + config { + pins = "gpio17", "gpio18"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se1_spi_pins: qupv3_se1_spi_pins { + qupv3_se1_spi_active: qupv3_se1_spi_active { + mux { + pins = "gpio17", "gpio18", "gpio19", + "gpio20"; + function = "qup1"; + }; + + config { + pins = "gpio17", "gpio18", "gpio19", + "gpio20"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se1_spi_sleep: qupv3_se1_spi_sleep { + mux { + pins = "gpio17", "gpio18", "gpio19", + "gpio20"; + function = "gpio"; + }; + + config { + pins = "gpio17", "gpio18", "gpio19", + "gpio20"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 2 pin mappings */ + qupv3_se2_i2c_pins: qupv3_se2_i2c_pins { + qupv3_se2_i2c_active: qupv3_se2_i2c_active { + mux { + pins = "gpio27", "gpio28"; + function = "qup2"; + }; + + config { + pins = "gpio27", "gpio28"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se2_i2c_sleep: qupv3_se2_i2c_sleep { + mux { + pins = "gpio27", "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio27", "gpio28"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se2_spi_pins: qupv3_se2_spi_pins { + qupv3_se2_spi_active: qupv3_se2_spi_active { + mux { + pins = "gpio27", "gpio28", "gpio29", + "gpio30"; + function = "qup2"; + }; + + config { + pins = "gpio27", "gpio28", "gpio29", + "gpio30"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se2_spi_sleep: qupv3_se2_spi_sleep { + mux { + pins = "gpio27", "gpio28", "gpio29", + "gpio30"; + function = "gpio"; + }; + + config { + pins = "gpio27", "gpio28", "gpio29", + "gpio30"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 3 pin mappings */ + qupv3_se3_i2c_pins: qupv3_se3_i2c_pins { + qupv3_se3_i2c_active: qupv3_se3_i2c_active { + mux { + pins = "gpio41", "gpio42"; + function = "qup3"; + }; + + config { + pins = "gpio41", "gpio42"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se3_i2c_sleep: qupv3_se3_i2c_sleep { + mux { + pins = "gpio41", "gpio42"; + function = "gpio"; + }; + + config { + pins = "gpio41", "gpio42"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + nfc { + nfc_int_active: nfc_int_active { + /* active state */ + mux { + /* GPIO 63 NFC Read Interrupt */ + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_int_suspend: nfc_int_suspend { + /* sleep state */ + mux { + /* GPIO 63 NFC Read Interrupt */ + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_enable_active: nfc_enable_active { + /* active state */ + mux { + /* 12: NFC ENABLE 116:ESE Enable */ + pins = "gpio12", "gpio62", "gpio116"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio62", "gpio116"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_enable_suspend: nfc_enable_suspend { + /* sleep state */ + mux { + /* 12: NFC ENABLE 116:ESE Enable */ + pins = "gpio12", "gpio62", "gpio116"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio62", "gpio116"; + drive-strength = <2>; /* 2 MA */ + bias-disable; + }; + }; + }; + + qupv3_se3_spi_pins: qupv3_se3_spi_pins { + qupv3_se3_spi_active: qupv3_se3_spi_active { + mux { + pins = "gpio41", "gpio42", "gpio43", + "gpio44"; + function = "qup3"; + }; + + config { + pins = "gpio41", "gpio42", "gpio43", + "gpio44"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se3_spi_sleep: qupv3_se3_spi_sleep { + mux { + pins = "gpio41", "gpio42", "gpio43", + "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio41", "gpio42", "gpio43", + "gpio44"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 4 pin mappings */ + qupv3_se4_i2c_pins: qupv3_se4_i2c_pins { + qupv3_se4_i2c_active: qupv3_se4_i2c_active { + mux { + pins = "gpio89", "gpio90"; + function = "qup4"; + }; + + config { + pins = "gpio89", "gpio90"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se4_i2c_sleep: qupv3_se4_i2c_sleep { + mux { + pins = "gpio89", "gpio90"; + function = "gpio"; + }; + + config { + pins = "gpio89", "gpio90"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se4_spi_pins: qupv3_se4_spi_pins { + qupv3_se4_spi_active: qupv3_se4_spi_active { + mux { + pins = "gpio89", "gpio90", "gpio91", + "gpio92"; + function = "qup4"; + }; + + config { + pins = "gpio89", "gpio90", "gpio91", + "gpio92"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se4_spi_sleep: qupv3_se4_spi_sleep { + mux { + pins = "gpio89", "gpio90", "gpio91", + "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio89", "gpio90", "gpio91", + "gpio92"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 5 pin mappings */ + qupv3_se5_i2c_pins: qupv3_se5_i2c_pins { + qupv3_se5_i2c_active: qupv3_se5_i2c_active { + mux { + pins = "gpio85", "gpio86"; + function = "qup5"; + }; + + config { + pins = "gpio85", "gpio86"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se5_i2c_sleep: qupv3_se5_i2c_sleep { + mux { + pins = "gpio85", "gpio86"; + function = "gpio"; + }; + + config { + pins = "gpio85", "gpio86"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se5_spi_pins: qupv3_se5_spi_pins { + qupv3_se5_spi_active: qupv3_se5_spi_active { + mux { + pins = "gpio85", "gpio86", "gpio87", + "gpio88"; + function = "qup5"; + }; + + config { + pins = "gpio85", "gpio86", "gpio87", + "gpio88"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se5_spi_sleep: qupv3_se5_spi_sleep { + mux { + pins = "gpio85", "gpio86", "gpio87", + "gpio88"; + function = "gpio"; + }; + + config { + pins = "gpio85", "gpio86", "gpio87", + "gpio88"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 6 pin mappings */ + qupv3_se6_i2c_pins: qupv3_se6_i2c_pins { + qupv3_se6_i2c_active: qupv3_se6_i2c_active { + mux { + pins = "gpio45", "gpio46"; + function = "qup6"; + }; + + config { + pins = "gpio45", "gpio46"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se6_i2c_sleep: qupv3_se6_i2c_sleep { + mux { + pins = "gpio45", "gpio46"; + function = "gpio"; + }; + + config { + pins = "gpio45", "gpio46"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se6_4uart_pins: qupv3_se6_4uart_pins { + qupv3_se6_ctsrx: qupv3_se6_ctsrx { + mux { + pins = "gpio45", "gpio48"; + function = "qup6"; + }; + + config { + pins = "gpio45", "gpio48"; + drive-strength = <2>; + bias-no-pull; + }; + }; + + qupv3_se6_rts: qupv3_se6_rts { + mux { + pins = "gpio46"; + function = "qup6"; + }; + + config { + pins = "gpio46"; + drive-strength = <2>; + bias-pull-down; + }; + }; + qupv3_se6_tx: qupv3_se6_tx { + mux { + pins = "gpio47"; + function = "qup6"; + }; + + config { + pins = "gpio47"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se6_spi_pins: qupv3_se6_spi_pins { + qupv3_se6_spi_active: qupv3_se6_spi_active { + mux { + pins = "gpio45", "gpio46", "gpio47", + "gpio48"; + function = "qup6"; + }; + + config { + pins = "gpio45", "gpio46", "gpio47", + "gpio48"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se6_spi_sleep: qupv3_se6_spi_sleep { + mux { + pins = "gpio45", "gpio46", "gpio47", + "gpio48"; + function = "gpio"; + }; + + config { + pins = "gpio45", "gpio46", "gpio47", + "gpio48"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 7 pin mappings */ + qupv3_se7_i2c_pins: qupv3_se7_i2c_pins { + qupv3_se7_i2c_active: qupv3_se7_i2c_active { + mux { + pins = "gpio93", "gpio94"; + function = "qup7"; + }; + + config { + pins = "gpio93", "gpio94"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se7_i2c_sleep: qupv3_se7_i2c_sleep { + mux { + pins = "gpio93", "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio93", "gpio94"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se7_4uart_pins: qupv3_se7_4uart_pins { + qupv3_se7_4uart_active: qupv3_se7_4uart_active { + mux { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + function = "qup7"; + }; + + config { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se7_4uart_sleep: qupv3_se7_4uart_sleep { + mux { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + qupv3_se7_spi_pins: qupv3_se7_spi_pins { + qupv3_se7_spi_active: qupv3_se7_spi_active { + mux { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + function = "qup7"; + }; + + config { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se7_spi_sleep: qupv3_se7_spi_sleep { + mux { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio93", "gpio94", "gpio95", + "gpio96"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* QUPv3 North instances */ + /* SE 8 pin mappings */ + qupv3_se8_i2c_pins: qupv3_se8_i2c_pins { + qupv3_se8_i2c_active: qupv3_se8_i2c_active { + mux { + pins = "gpio65", "gpio66"; + function = "qup8"; + }; + + config { + pins = "gpio65", "gpio66"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se8_i2c_sleep: qupv3_se8_i2c_sleep { + mux { + pins = "gpio65", "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio65", "gpio66"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se8_spi_pins: qupv3_se8_spi_pins { + qupv3_se8_spi_active: qupv3_se8_spi_active { + mux { + pins = "gpio65", "gpio66", "gpio67", + "gpio68"; + function = "qup8"; + }; + + config { + pins = "gpio65", "gpio66", "gpio67", + "gpio68"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se8_spi_sleep: qupv3_se8_spi_sleep { + mux { + pins = "gpio65", "gpio66", "gpio67", + "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio65", "gpio66", "gpio67", + "gpio68"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 9 pin mappings */ + qupv3_se9_i2c_pins: qupv3_se9_i2c_pins { + qupv3_se9_i2c_active: qupv3_se9_i2c_active { + mux { + pins = "gpio6", "gpio7"; + function = "qup9"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se9_i2c_sleep: qupv3_se9_i2c_sleep { + mux { + pins = "gpio6", "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se9_2uart_pins: qupv3_se9_2uart_pins { + qupv3_se9_2uart_active: qupv3_se9_2uart_active { + mux { + pins = "gpio4", "gpio5"; + function = "qup9"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se9_2uart_sleep: qupv3_se9_2uart_sleep { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + qupv3_se9_spi_pins: qupv3_se9_spi_pins { + qupv3_se9_spi_active: qupv3_se9_spi_active { + mux { + pins = "gpio4", "gpio5", "gpio6", + "gpio7"; + function = "qup9"; + }; + + config { + pins = "gpio4", "gpio5", "gpio6", + "gpio7"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se9_spi_sleep: qupv3_se9_spi_sleep { + mux { + pins = "gpio4", "gpio5", "gpio6", + "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5", "gpio6", + "gpio7"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 10 pin mappings */ + qupv3_se10_i2c_pins: qupv3_se10_i2c_pins { + qupv3_se10_i2c_active: qupv3_se10_i2c_active { + mux { + pins = "gpio55", "gpio56"; + function = "qup10"; + }; + + config { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se10_i2c_sleep: qupv3_se10_i2c_sleep { + mux { + pins = "gpio55", "gpio56"; + function = "gpio"; + }; + + config { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-pull-up; + }; + }; + qupv3_se10_i2c_reset: qupv3_se10_i2c_reset { + mux { + pins = "gpio55", "gpio56"; + function = "gpio"; + }; + + config { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + qupv3_se10_2uart_pins: qupv3_se10_2uart_pins { + qupv3_se10_2uart_active: qupv3_se10_2uart_active { + mux { + pins = "gpio53", "gpio54"; + function = "qup10"; + }; + + config { + pins = "gpio53", "gpio54"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se10_2uart_sleep: qupv3_se10_2uart_sleep { + mux { + pins = "gpio53", "gpio54"; + function = "gpio"; + }; + + config { + pins = "gpio53", "gpio54"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + qupv3_se10_spi_pins: qupv3_se10_spi_pins { + qupv3_se10_spi_active: qupv3_se10_spi_active { + mux { + pins = "gpio53", "gpio54", "gpio55", + "gpio56"; + function = "qup10"; + }; + + config { + pins = "gpio53", "gpio54", "gpio55", + "gpio56"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se10_spi_sleep: qupv3_se10_spi_sleep { + mux { + pins = "gpio53", "gpio54", "gpio55", + "gpio56"; + function = "gpio"; + }; + + config { + pins = "gpio53", "gpio54", "gpio55", + "gpio56"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 11 pin mappings */ + qupv3_se11_i2c_pins: qupv3_se11_i2c_pins { + qupv3_se11_i2c_active: qupv3_se11_i2c_active { + mux { + pins = "gpio31", "gpio32"; + function = "qup11"; + }; + + config { + pins = "gpio31", "gpio32"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se11_i2c_sleep: qupv3_se11_i2c_sleep { + mux { + pins = "gpio31", "gpio32"; + function = "gpio"; + }; + + config { + pins = "gpio31", "gpio32"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se11_spi_pins: qupv3_se11_spi_pins { + qupv3_se11_spi_active: qupv3_se11_spi_active { + mux { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + function = "qup11"; + }; + + config { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se11_spi_sleep: qupv3_se11_spi_sleep { + mux { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + function = "gpio"; + }; + + config { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 12 pin mappings */ + qupv3_se12_i2c_pins: qupv3_se12_i2c_pins { + qupv3_se12_i2c_active: qupv3_se12_i2c_active { + mux { + pins = "gpio49", "gpio50"; + function = "qup12"; + }; + + config { + pins = "gpio49", "gpio50"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se12_i2c_sleep: qupv3_se12_i2c_sleep { + mux { + pins = "gpio49", "gpio50"; + function = "gpio"; + }; + + config { + pins = "gpio49", "gpio50"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se12_spi_pins: qupv3_se12_spi_pins { + qupv3_se12_spi_active: qupv3_se12_spi_active { + mux { + pins = "gpio49", "gpio50", "gpio51", + "gpio52"; + function = "qup12"; + }; + + config { + pins = "gpio49", "gpio50", "gpio51", + "gpio52"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se12_spi_sleep: qupv3_se12_spi_sleep { + mux { + pins = "gpio49", "gpio50", "gpio51", + "gpio52"; + function = "gpio"; + }; + + config { + pins = "gpio49", "gpio50", "gpio51", + "gpio52"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 13 pin mappings */ + qupv3_se13_i2c_pins: qupv3_se13_i2c_pins { + qupv3_se13_i2c_active: qupv3_se13_i2c_active { + mux { + pins = "gpio105", "gpio106"; + function = "qup13"; + }; + + config { + pins = "gpio105", "gpio106"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se13_i2c_sleep: qupv3_se13_i2c_sleep { + mux { + pins = "gpio105", "gpio106"; + function = "gpio"; + }; + + config { + pins = "gpio105", "gpio106"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se13_spi_pins: qupv3_se13_spi_pins { + qupv3_se13_spi_active: qupv3_se13_spi_active { + mux { + pins = "gpio105", "gpio106", "gpio107", + "gpio108"; + function = "qup13"; + }; + + config { + pins = "gpio105", "gpio106", "gpio107", + "gpio108"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se13_spi_sleep: qupv3_se13_spi_sleep { + mux { + pins = "gpio105", "gpio106", "gpio107", + "gpio108"; + function = "gpio"; + }; + + config { + pins = "gpio105", "gpio106", "gpio107", + "gpio108"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 14 pin mappings */ + qupv3_se14_i2c_pins: qupv3_se14_i2c_pins { + qupv3_se14_i2c_active: qupv3_se14_i2c_active { + mux { + pins = "gpio33", "gpio34"; + function = "qup14"; + }; + + config { + pins = "gpio33", "gpio34"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se14_i2c_sleep: qupv3_se14_i2c_sleep { + mux { + pins = "gpio33", "gpio34"; + function = "gpio"; + }; + + config { + pins = "gpio33", "gpio34"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se14_spi_pins: qupv3_se14_spi_pins { + qupv3_se14_spi_active: qupv3_se14_spi_active { + mux { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + function = "qup14"; + }; + + config { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se14_spi_sleep: qupv3_se14_spi_sleep { + mux { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + function = "gpio"; + }; + + config { + pins = "gpio31", "gpio32", "gpio33", + "gpio34"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 15 pin mappings */ + qupv3_se15_i2c_pins: qupv3_se15_i2c_pins { + qupv3_se15_i2c_active: qupv3_se15_i2c_active { + mux { + pins = "gpio81", "gpio82"; + function = "qup15"; + }; + + config { + pins = "gpio81", "gpio82"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se15_i2c_sleep: qupv3_se15_i2c_sleep { + mux { + pins = "gpio81", "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio81", "gpio82"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se15_spi_pins: qupv3_se15_spi_pins { + qupv3_se15_spi_active: qupv3_se15_spi_active { + mux { + pins = "gpio81", "gpio82", "gpio83", + "gpio84"; + function = "qup15"; + }; + + config { + pins = "gpio81", "gpio82", "gpio83", + "gpio84"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se15_spi_sleep: qupv3_se15_spi_sleep { + mux { + pins = "gpio81", "gpio82", "gpio83", + "gpio84"; + function = "gpio"; + }; + + config { + pins = "gpio81", "gpio82", "gpio83", + "gpio84"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + cci0_active: cci0_active { + mux { + /* CLK, DATA */ + pins = "gpio17","gpio18"; // Only 2 + function = "cci_i2c"; + }; + + config { + pins = "gpio17","gpio18"; + bias-pull-up; /* PULL UP*/ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci0_suspend: cci0_suspend { + mux { + /* CLK, DATA */ + pins = "gpio17","gpio18"; + function = "cci_i2c"; + }; + + config { + pins = "gpio17","gpio18"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci1_active: cci1_active { + mux { + /* CLK, DATA */ + pins = "gpio19","gpio20"; + function = "cci_i2c"; + }; + + config { + pins = "gpio19","gpio20"; + bias-pull-up; /* PULL UP*/ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cci1_suspend: cci1_suspend { + mux { + /* CLK, DATA */ + pins = "gpio19","gpio20"; + function = "cci_i2c"; + }; + + config { + pins = "gpio19","gpio20"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_fisheye_active: cam_sensor_fisheye_active { + /* RESET, AVDD LO */ + mux { + pins = "gpio76","gpio75"; + function = "gpio"; + }; + + config { + pins = "gpio76","gpio75"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_fisheye_suspend: cam_sensor_fisheye_suspend { + /* RESET, AVDD LO*/ + mux { + pins = "gpio76","gpio75"; + function = "gpio"; + }; + + config { + pins = "gpio76","gpio75"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + cam_sensor_depth_active: cam_sensor_depth_active { + /* RESET,AVDD LO ,IMG_START, ILLU_EN */ + mux { + pins = "gpio28","gpio23","gpio24"; + function = "gpio"; + }; + + config { + pins = "gpio28","gpio23","gpio24"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_depth_suspend: cam_sensor_depth_suspend { + /* RESET, AVDD LO ,IMG_START, ILLU_EN */ + mux { + pins = "gpio28","gpio23","gpio24"; + function = "gpio"; + }; + + config { + pins = "gpio28","gpio23","gpio24"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + max_rst_active: max_rst_active { + /* RESET */ + mux { + pins = "gpio31","gpio77","gpio78","gpio32"; + function = "gpio"; + }; + + config { + pins = "gpio31","gpio77","gpio78","gpio32"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + max_rst_suspend: max_rst_suspend { + /* RESET */ + mux { + pins = "gpio31","gpio77","gpio78","gpio32"; + function = "gpio"; + }; + + config { + pins = "gpio31","gpio77","gpio78","gpio32"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + max_6dof_active: max_6dof_active { + /* RESET */ + mux { + pins = "gpio30","gpio95","gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio95","gpio94"; + bias-disable; /* No PULL */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + max_6dof_suspend: max_6dof_suspend { + /* RESET */ + mux { + pins = "gpio30","gpio95","gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio95","gpio94"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <8>; /* 2 MA */ + }; + }; + + cam_sensor_mclk0_active: cam_sensor_mclk0_active { + /* MCLK0 */ + mux { + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk0_suspend: cam_sensor_mclk0_suspend { + /* MCLK0 */ + mux { + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_active: cam_sensor_rear_active { + /* RESET, AVDD LDO */ + mux { + pins = "gpio80","gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio80","gpio79"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_suspend: cam_sensor_rear_suspend { + /* RESET, AVDD LDO */ + mux { + pins = "gpio80","gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio80","gpio79"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + cam_sensor_mclk1_active: cam_sensor_mclk1_active { + /* MCLK1 */ + mux { + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk1_suspend: cam_sensor_mclk1_suspend { + /* MCLK1 */ + mux { + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk3_active: cam_sensor_mclk3_active { + /* MCLK3 */ + mux { + pins = "gpio16"; + function = "cam_mclk"; + }; + + config { + pins = "gpio16"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk3_suspend: cam_sensor_mclk3_suspend { + /* MCLK3 */ + mux { + pins = "gpio16"; + function = "cam_mclk"; + }; + + config { + pins = "gpio16"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + + cam_sensor_front_active: cam_sensor_front_active { + /* RESET AVDD_LDO*/ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_suspend: cam_sensor_front_suspend { + /* RESET */ + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + cam_sensor_iris_active: cam_sensor_iris_active { + /* RESET AVDD_LDO*/ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_iris_suspend: cam_sensor_iris_suspend { + /* RESET */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + + cam_sensor_mclk2_active: cam_sensor_mclk2_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_mclk2_suspend: cam_sensor_mclk2_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_rear2_active: cam_sensor_rear2_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear2_suspend: cam_sensor_rear2_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9"; + function = "gpio"; + }; + config { + pins = "gpio9"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + cam_sensor_rear_vana: cam_sensor_rear_vana { + /* AVDD LDO */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_res_mgr_active: cam_res_mgr_active { + /* AVDD_LDO*/ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_res_mgr_suspend: cam_res_mgr_suspend { + /* AVDD_LDO */ + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; + }; + + trigout_a: trigout_a { + mux { + pins = "gpio90"; + function = "qdss_cti"; + }; + config { + pins = "gpio90"; + drive-strength = <2>; + bias-disable; + }; + }; + + tsif0_signals_active: tsif0_signals_active { + tsif1_clk { + pins = "gpio89"; /* TSIF0 CLK */ + function = "tsif1_clk"; + }; + tsif1_en { + pins = "gpio90"; /* TSIF0 Enable */ + function = "tsif1_en"; + }; + tsif1_data { + pins = "gpio91"; /* TSIF0 DATA */ + function = "tsif1_data"; + }; + signals_cfg { + pins = "gpio89", "gpio90", "gpio91"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + /* sync signal is only used if configured to mode-2 */ + tsif0_sync_active: tsif0_sync_active { + tsif1_sync { + pins = "gpio12"; /* TSIF0 SYNC */ + function = "tsif1_sync"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + tsif1_signals_active: tsif1_signals_active { + tsif2_clk { + pins = "gpio93"; /* TSIF1 CLK */ + function = "tsif2_clk"; + }; + tsif2_en { + pins = "gpio94"; /* TSIF1 Enable */ + function = "tsif2_en"; + }; + tsif2_data { + pins = "gpio95"; /* TSIF1 DATA */ + function = "tsif2_data"; + }; + signals_cfg { + pins = "gpio93", "gpio94", "gpio95"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + /* sync signal is only used if configured to mode-2 */ + tsif1_sync_active: tsif1_sync_active { + tsif2_sync { + pins = "gpio96"; /* TSIF1 SYNC */ + function = "tsif2_sync"; + drive_strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; + + ap2mdm { + ap2mdm_active: ap2mdm_active { + mux { + /* ap2mdm-status + * ap2mdm-errfatal + * ap2mdm-vddmin + */ + pins = "gpio21", "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio21", "gpio23"; + drive-strength = <16>; + bias-disable; + }; + }; + ap2mdm_sleep: ap2mdm_sleep { + mux { + /* ap2mdm-status + * ap2mdm-errfatal + * ap2mdm-vddmin + */ + pins = "gpio21", "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio21", "gpio23"; + drive-strength = <8>; + bias-disable; + }; + + }; + }; + + mdm2ap { + mdm2ap_active: mdm2ap_active { + mux { + /* mdm2ap-status + * mdm2ap-errfatal + * mdm2ap-vddmin + */ + pins = "gpio22", "gpio20"; + function = "gpio"; + }; + + config { + pins = "gpio22", "gpio20"; + drive-strength = <8>; + bias-disable; + }; + }; + mdm2ap_sleep: mdm2ap_sleep { + mux { + /* mdm2ap-status + * mdm2ap-errfatal + * mdm2ap-vddmin + */ + pins = "gpio22", "gpio20"; + function = "gpio"; + }; + + config { + pins = "gpio22", "gpio20"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + }; +}; + +&pm8998_gpios { + key_home { + key_home_default: key_home_default { + pins = "gpio5"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + led_bt { + led_bt_default: led_bt_default { + pins = "gpio5"; + function = "normal"; + power-source = <0>; + output-low; + }; + }; + + key_vol_up { + key_vol_up_default: key_vol_up_default { + pins = "gpio6"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + key_cam_snapshot { + key_cam_snapshot_default: key_cam_snapshot_default { + pins = "gpio7"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + key_cam_focus { + key_cam_focus_default: key_cam_focus_default { + pins = "gpio8"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + led_wifi { + led_wifi_default: led_wifi_default { + pins = "gpio9"; + function = "normal"; + power-source = <0>; + output-low; + }; + }; + + camera_dvdd_en { + camera_dvdd_en_default: camera_dvdd_en_default { + pins = "gpio9"; + function = "normal"; + power-source = <0>; + output-low; + }; + }; + + camera_rear_avdd_en { + camera_rear_avdd_en_default: camera_rear_avdd_en_default { + pins = "gpio10"; + function = "normal"; + power-source = <0>; + output-low; + }; + }; + + camera_rear_dvdd_en { + camera_rear_dvdd_en_default: camera_rear_dvdd_en_default { + pins = "gpio12"; + function = "normal"; + power-source = <0>; + output-low; + }; + }; + + nfc_clk { + nfc_clk_default: nfc_clk_default { + pins = "gpio21"; + function = "normal"; + input-enable; + power-source = <1>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi new file mode 100644 index 000000000000..d772a31fd912 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +&soc { + qcom,lpm-levels { + compatible = "qcom,lpm-levels"; + #address-cells = <1>; + #size-cells = <0>; + + qcom,pm-cluster@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + label = "L3"; + qcom,clstr-tmr-add = <1000>; + qcom,psci-mode-shift = <4>; + qcom,psci-mode-mask = <0xfff>; + + qcom,pm-cluster-level@0 { /* D1 */ + reg = <0>; + label = "l3-wfi"; + qcom,psci-mode = <0x1>; + qcom,entry-latency-us = <48>; + qcom,exit-latency-us = <51>; + qcom,min-residency-us = <99>; + }; + + qcom,pm-cluster-level@1 { /* LLCC off, AOSS sleep */ + reg = <1>; + label = "llcc-off"; + qcom,psci-mode = <0xC24>; + qcom,entry-latency-us = <1562>; + qcom,exit-latency-us = <6562>; + qcom,min-residency-us = <9987>; + qcom,min-child-idx = <2>; + qcom,is-reset; + qcom,notify-rpm; + }; + + qcom,pm-cpu@0 { + #address-cells = <1>; + #size-cells = <0>; + qcom,psci-mode-shift = <0>; + qcom,psci-mode-mask = <0xf>; + qcom,ref-stddev = <500>; + qcom,tmr-add = <1000>; + qcom,ref-premature-cnt = <1>; + qcom,disable-ipi-prediction; + qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>; + + qcom,pm-cpu-level@0 { /* C1 */ + reg = <0>; + label = "wfi"; + qcom,psci-cpu-mode = <0x1>; + qcom,entry-latency-us = <57>; + qcom,exit-latency-us = <43>; + qcom,min-residency-us = <100>; + }; + + qcom,pm-cpu-level@1 { /* C3 */ + reg = <1>; + label = "pc"; + qcom,psci-cpu-mode = <0x3>; + qcom,entry-latency-us = <39>; + qcom,exit-latency-us = <461>; + qcom,min-residency-us = <5990>; + qcom,is-reset; + qcom,use-broadcast-timer; + }; + + qcom,pm-cpu-level@2 { /* C4 */ + reg = <2>; + label = "rail-pc"; + qcom,psci-cpu-mode = <0x4>; + qcom,entry-latency-us = <69>; + qcom,exit-latency-us = <531>; + qcom,min-residency-us = <3934>; + qcom,is-reset; + qcom,use-broadcast-timer; + }; + }; + + qcom,pm-cpu@1 { + #address-cells = <1>; + #size-cells = <0>; + qcom,psci-mode-shift = <0>; + qcom,psci-mode-mask = <0xf>; + qcom,ref-stddev = <100>; + qcom,tmr-add = <100>; + qcom,ref-premature-cnt = <3>; + qcom,disable-ipi-prediction; + qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>; + + qcom,pm-cpu-level@0 { /* C1 */ + reg = <0>; + label = "wfi"; + qcom,psci-cpu-mode = <0x1>; + qcom,entry-latency-us = <40>; + qcom,exit-latency-us = <43>; + qcom,min-residency-us = <83>; + }; + + qcom,pm-cpu-level@1 { /* C3 */ + reg = <1>; + label = "pc"; + qcom,psci-cpu-mode = <0x3>; + qcom,entry-latency-us = <264>; + qcom,exit-latency-us = <621>; + qcom,min-residency-us = <1001>; + qcom,is-reset; + qcom,use-broadcast-timer; + }; + + qcom,pm-cpu-level@2 { /* C4 */ + reg = <2>; + label = "rail-pc"; + qcom,psci-cpu-mode = <0x4>; + qcom,entry-latency-us = <61>; + qcom,exit-latency-us = <1061>; + qcom,min-residency-us = <1001>; + qcom,is-reset; + qcom,use-broadcast-timer; + }; + }; + }; + }; + + qcom,rpm-stats@c300000 { + compatible = "qcom,rpm-stats"; + reg = <0xC300000 0x1000>, <0xC3F0004 0x4>; + reg-names = "phys_addr_base", "offset_addr"; + }; + + qcom,rpmh-master-stats@b221200 { + compatible = "qcom,rpmh-master-stats-v1"; + reg = <0xb221200 0x60>; + qcom,use-alt-unit = <3>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi new file mode 100644 index 000000000000..b66eb5c20032 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include + +#include "pmi8998.dtsi" + +&vendor { + ext_5v_boost: ext_5v_boost { + status = "disabled"; + compatible = "regulator-fixed"; + regulator-name = "ext_5v_boost"; + gpio = <&pmi8998_gpios 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + + regulator-enable-ramp-delay = <1600>; + pinctrl-names = "default"; + pinctrl-0 = <&usb2_ext_5v_boost_default>; + }; +}; + +&pmi8998_charger { + smb2_vconn: qcom,smb2-vconn { + regulator-name = "smb2-vconn"; + }; + smb2_vbus: qcom,smb2-vbus { + regulator-name = "smb2-vbus"; + }; +}; + +&pmi8998_qnovo { + pinctrl-names = "default"; + pinctrl-0 = <&qnovo_fet_ctrl_default>; +}; + +&usb0 { + extcon = <&pmi8998_pdphy>, <&pmi8998_pdphy>, <&eud>; +}; + +&usb_qmp_dp_phy { + extcon = <&pmi8998_pdphy>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi new file mode 100644 index 000000000000..e7af96176a29 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi @@ -0,0 +1,785 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include + +&soc { + /* QUPv3 South instances */ + qupv3_0: qcom,qupv3_0_geni_se@8c0000 { + compatible = "qcom,qupv3-geni-se"; + reg = <0x8c0000 0x6000>; + qcom,bus-mas-id = ; + qcom,bus-slv-id = ; + iommus = <&apps_smmu 0x003 0x0>; + qcom,iommu-dma-addr-pool = <0x40000000 0xc0000000>; + qcom,iommu-dma = "fastmap"; + }; + + /* + * HS UART instances. HS UART usecases can be supported on these + * instances only. + */ + qupv3_se6_4uart: qcom,qup_uart@0x898000 { + compatible = "qcom,msm-geni-serial-hs"; + reg = <0x898000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se6_ctsrx>, <&qupv3_se6_rts>, + <&qupv3_se6_tx> ; + pinctrl-1 = <&qupv3_se6_ctsrx>, <&qupv3_se6_rts>, + <&qupv3_se6_tx> ; + interrupts-extended = <&intc GIC_SPI 607 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 48 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + qcom,wakeup-byte = <0xFD>; + qcom,wrapper-core = <&qupv3_0>; + }; + + qupv3_se7_4uart: qcom,qup_uart@0x89c000 { + compatible = "qcom,msm-geni-serial-hs"; + reg = <0x89c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_4uart_active>; + pinctrl-1 = <&qupv3_se7_4uart_sleep>; + interrupts-extended = <&intc GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 96 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + qcom,wakeup-byte = <0xFD>; + qcom,wrapper-core = <&qupv3_0>; + }; + + /* I2C */ + qupv3_se0_i2c: i2c@880000 { + compatible = "qcom,i2c-geni"; + reg = <0x880000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 0 3 64 0>, + <&gpi_dma0 1 0 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se0_i2c_active>; + pinctrl-1 = <&qupv3_se0_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se1_i2c: i2c@884000 { + compatible = "qcom,i2c-geni"; + reg = <0x884000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 1 3 64 0>, + <&gpi_dma0 1 1 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se1_i2c_active>; + pinctrl-1 = <&qupv3_se1_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se2_i2c: i2c@888000 { + compatible = "qcom,i2c-geni"; + reg = <0x888000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 2 3 64 0>, + <&gpi_dma0 1 2 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se2_i2c_active>; + pinctrl-1 = <&qupv3_se2_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se3_i2c: i2c@88c000 { + compatible = "qcom,i2c-geni"; + reg = <0x88c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 3 3 64 0>, + <&gpi_dma0 1 3 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se3_i2c_active>; + pinctrl-1 = <&qupv3_se3_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se4_i2c: i2c@890000 { + compatible = "qcom,i2c-geni"; + reg = <0x890000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S4_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 4 3 64 0>, + <&gpi_dma0 1 4 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se4_i2c_active>; + pinctrl-1 = <&qupv3_se4_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se5_i2c: i2c@894000 { + compatible = "qcom,i2c-geni"; + reg = <0x894000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 5 3 64 0>, + <&gpi_dma0 1 5 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se5_i2c_active>; + pinctrl-1 = <&qupv3_se5_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se6_i2c: i2c@898000 { + compatible = "qcom,i2c-geni"; + reg = <0x898000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 6 3 64 0>, + <&gpi_dma0 1 6 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se6_i2c_active>; + pinctrl-1 = <&qupv3_se6_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se7_i2c: i2c@89c000 { + compatible = "qcom,i2c-geni"; + reg = <0x89c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 7 3 64 0>, + <&gpi_dma0 1 7 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_i2c_active>; + pinctrl-1 = <&qupv3_se7_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + /* SPI */ + qupv3_se0_spi: spi@880000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x880000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se0_spi_active>; + pinctrl-1 = <&qupv3_se0_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 0 1 64 0>, + <&gpi_dma0 1 0 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se1_spi: spi@884000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x884000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se1_spi_active>; + pinctrl-1 = <&qupv3_se1_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 1 1 64 0>, + <&gpi_dma0 1 1 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se2_spi: spi@888000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x888000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se2_spi_active>; + pinctrl-1 = <&qupv3_se2_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 2 1 64 0>, + <&gpi_dma0 1 2 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se3_spi: spi@88c000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x88c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se3_spi_active>; + pinctrl-1 = <&qupv3_se3_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 3 1 64 0>, + <&gpi_dma0 1 3 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se4_spi: spi@890000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x890000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S4_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se4_spi_active>; + pinctrl-1 = <&qupv3_se4_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 4 1 64 0>, + <&gpi_dma0 1 4 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se5_spi: spi@894000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x894000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se5_spi_active>; + pinctrl-1 = <&qupv3_se5_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 5 1 64 0>, + <&gpi_dma0 1 5 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se6_spi: spi@898000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x898000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se6_spi_active>; + pinctrl-1 = <&qupv3_se6_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 6 1 64 0>, + <&gpi_dma0 1 6 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se7_spi: spi@89c000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x89c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_spi_active>; + pinctrl-1 = <&qupv3_se7_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 7 1 64 0>, + <&gpi_dma0 1 7 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + /* QUPv3 North Instances */ + qupv3_1: qcom,qupv3_1_geni_se@ac0000 { + compatible = "qcom,qupv3-geni-se"; + reg = <0xac0000 0x6000>; + qcom,bus-mas-id = ; + qcom,bus-slv-id = ; + iommus = <&apps_smmu 0x6c3 0x0>; + qcom,iommu-dma-addr-pool = <0x40000000 0xc0000000>; + qcom,iommu-dma = "fastmap"; + }; + + /* 2-wire UART */ + + /* Debug UART Instance for CDP/MTP platform */ + qupv3_se9_2uart: qcom,qup_uart@0xa84000 { + compatible = "qcom,msm-geni-console"; + reg = <0xa84000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se9_2uart_active>; + pinctrl-1 = <&qupv3_se9_2uart_sleep>; + interrupts = ; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + /* Debug UART Instance for RUMI platform */ + qupv3_se10_2uart: qcom,qup_uart@0xa88000 { + compatible = "qcom,msm-geni-console"; + reg = <0xa88000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se10_2uart_active>; + pinctrl-1 = <&qupv3_se10_2uart_sleep>; + interrupts = ; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + /* I2C */ + qupv3_se8_i2c: i2c@a80000 { + compatible = "qcom,i2c-geni"; + reg = <0xa80000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 0 3 64 0>, + <&gpi_dma1 1 0 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se8_i2c_active>; + pinctrl-1 = <&qupv3_se8_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se9_i2c: i2c@a84000 { + compatible = "qcom,i2c-geni"; + reg = <0xa84000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 1 3 64 0>, + <&gpi_dma1 1 1 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se9_i2c_active>; + pinctrl-1 = <&qupv3_se9_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se10_i2c: i2c@a88000 { + compatible = "qcom,i2c-geni"; + reg = <0xa88000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 2 3 64 0>, + <&gpi_dma1 1 2 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep", "reset"; + pinctrl-0 = <&qupv3_se10_i2c_active>; + pinctrl-1 = <&qupv3_se10_i2c_sleep>; + pinctrl-2 = <&qupv3_se10_i2c_reset>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se11_i2c: i2c@a8c000 { + compatible = "qcom,i2c-geni"; + reg = <0xa8c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 3 3 64 0>, + <&gpi_dma1 1 3 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se11_i2c_active>; + pinctrl-1 = <&qupv3_se11_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se12_i2c: i2c@a90000 { + compatible = "qcom,i2c-geni"; + reg = <0xa90000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S4_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 4 3 64 0>, + <&gpi_dma1 1 4 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se12_i2c_active>; + pinctrl-1 = <&qupv3_se12_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se13_i2c: i2c@a94000 { + compatible = "qcom,i2c-geni"; + reg = <0xa94000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S5_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 5 3 64 0>, + <&gpi_dma1 1 5 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se13_i2c_active>; + pinctrl-1 = <&qupv3_se13_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se14_i2c: i2c@a98000 { + compatible = "qcom,i2c-geni"; + reg = <0xa98000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S6_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 6 3 64 0>, + <&gpi_dma1 1 6 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se14_i2c_active>; + pinctrl-1 = <&qupv3_se14_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se15_i2c: i2c@a9c000 { + compatible = "qcom,i2c-geni"; + reg = <0xa9c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S7_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 7 3 64 0>, + <&gpi_dma1 1 7 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se15_i2c_active>; + pinctrl-1 = <&qupv3_se15_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + /* SPI */ + qupv3_se8_spi: spi@a80000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa80000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se8_spi_active>; + pinctrl-1 = <&qupv3_se8_spi_active>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 0 1 64 0>, + <&gpi_dma1 1 0 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se9_spi: spi@a84000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa84000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se9_spi_active>; + pinctrl-1 = <&qupv3_se9_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 1 1 64 0>, + <&gpi_dma1 1 1 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se10_spi: spi@a88000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa88000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se10_spi_active>; + pinctrl-1 = <&qupv3_se10_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 2 1 64 0>, + <&gpi_dma1 1 2 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se11_spi: spi@a8c000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa8c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se11_spi_active>; + pinctrl-1 = <&qupv3_se11_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 3 1 64 0>, + <&gpi_dma1 1 3 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se12_spi: spi@a90000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa90000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S4_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se12_spi_active>; + pinctrl-1 = <&qupv3_se12_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 4 1 64 0>, + <&gpi_dma1 1 4 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se13_spi: spi@a94000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa94000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S5_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se13_spi_active>; + pinctrl-1 = <&qupv3_se13_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 5 1 64 0>, + <&gpi_dma1 1 5 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se14_spi: spi@a98000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa98000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S6_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se14_spi_active>; + pinctrl-1 = <&qupv3_se14_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 6 1 64 0>, + <&gpi_dma1 1 6 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se15_spi: spi@a9c000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa9c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S7_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se15_spi_active>; + pinctrl-1 = <&qupv3_se15_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 7 1 64 0>, + <&gpi_dma1 1 7 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi new file mode 100644 index 000000000000..41c1695433a0 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi @@ -0,0 +1,774 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +#include +#include + +/* Stub regulators */ +/ { + + /* + * RPMh does not provide support for PM8998 S4 because it is always-on + * at 1.8 V in auto mode. Therefore, use a stub regulator for S4. + */ + pm8998_s4: regulator-pm8998-s4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm8998_s4"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; +}; + + +/* RPMh regulators */ +&apps_rsc { + /* PM8998 S1 = VDD_EBI supply */ + rpmh-regulator-ebilvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "ebi.lvl"; + pm8998_s1_level: regulator-s1 { + regulator-name = "pm8998_s1_level"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + ebi_cdev: regulator-cdev { + compatible = "qcom,rpmh-reg-cdev"; + mboxes = <&qmp_aop 0>; + qcom,reg-resource-name = "ebi"; + #cooling-cells = <2>; + }; + }; + + rpmh-regulator-smpa2 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "smpa2"; + pm8998_s2: regulator-s2 { + regulator-name = "pm8998_s2"; + qcom,set = ; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; + }; + }; + + rpmh-regulator-smpa3 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "smpa3"; + pm8998_s3: regulator-s3 { + regulator-name = "pm8998_s3"; + qcom,set = ; + regulator-min-microvolt = <1352000>; + regulator-max-microvolt = <1352000>; + qcom,init-voltage = <1352000>; + }; + }; + + rpmh-regulator-smpa5 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "smpa5"; + pm8998_s5: regulator-s5 { + regulator-name = "pm8998_s5"; + qcom,set = ; + regulator-min-microvolt = <1904000>; + regulator-max-microvolt = <2040000>; + qcom,init-voltage = <1904000>; + }; + }; + + /* PM8998 S6 = VDD_MX supply */ + rpmh-regulator-mxlvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "mx.lvl"; + pm8998_s6_level: regulator-s6-level { + regulator-name = "pm8998_s6_level"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pm8998_s6_level_ao: regulator-s6-level-ao { + regulator-name = "pm8998_s6_level_ao"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + mx_cdev: mx-cdev-lvl { + compatible = "qcom,regulator-cooling-device"; + regulator-cdev-supply = <&pm8998_s6_level>; + regulator-levels = ; + #cooling-cells = <2>; + }; + }; + + rpmh-regulator-smpa7 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "smpa7"; + pm8998_s7: regulator-s7 { + regulator-name = "pm8998_s7"; + qcom,set = ; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1028000>; + qcom,init-voltage = <900000>; + }; + }; + + /* PM8998 S9 + S8 = VDD_CX supply */ + rpmh-regulator-cxlvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "cx.lvl"; + pm8998_s9_level-parent-supply = <&pm8998_s6_level>; + pm8998_s9_level_ao-parent-supply = <&pm8998_s6_level_ao>; + pm8998_s9_level: regulator-s9-level { + regulator-name = "pm8998_s9_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt = ; + qcom,min-dropout-voltage-level = <(-1)>; + }; + + pm8998_s9_level_ao: regulator-s9-level-ao { + regulator-name = "pm8998_s9_level_ao"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt = ; + qcom,min-dropout-voltage-level = <(-1)>; + }; + + cx_cdev: regulator-cdev { + compatible = "qcom,rpmh-reg-cdev"; + mboxes = <&qmp_aop 0>; + qcom,reg-resource-name = "cx"; + #cooling-cells = <2>; + }; + }; + + rpmh-regulator-ldoa1 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa1"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + proxy-supply = <&pm8998_l1>; + pm8998_l1: regulator-l1 { + regulator-name = "pm8998_l1"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <72000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + }; + + pm8998_l1_ao: regulator-l1-ao { + regulator-name = "pm8998_l1_ao"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + }; + + regulator-l1-so { + regulator-name = "pm8998_l1_so"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + qcom,init-enable = <0>; + }; + }; + + rpmh-regulator-ldoa2 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa2"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 30000>; + pm8998_l2: regulator-l2 { + regulator-name = "pm8998_l2"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + regulator-always-on; + }; + }; + + rpmh-regulator-ldoa3 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa3"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l3: regulator-l3 { + regulator-name = "pm8998_l3"; + qcom,set = ; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + qcom,init-voltage = <1000000>; + qcom,init-mode = ; + }; + }; + + /* PM8998 L4 = VDD_SSC_MX supply */ + rpmh-regulator-lmxlvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "lmx.lvl"; + pm8998_l4_level: regulator-l4-level { + regulator-name = "pm8998_l4_level"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + }; + + rpmh-regulator-ldoa5 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa5"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l5: regulator-l5 { + regulator-name = "pm8998_l5"; + qcom,set = ; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + qcom,init-voltage = <800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa6 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa6"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l6: regulator-l6 { + regulator-name = "pm8998_l6"; + qcom,set = ; + regulator-min-microvolt = <1856000>; + regulator-max-microvolt = <1856000>; + qcom,init-voltage = <1856000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa7 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa7"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l7: regulator-l7 { + regulator-name = "pm8998_l7"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa8 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa8"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l8: regulator-l8 { + regulator-name = "pm8998_l8"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1248000>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa9 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa9"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l9: regulator-l9 { + regulator-name = "pm8998_l9"; + qcom,set = ; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <2928000>; + qcom,init-voltage = <1704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa10 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa10"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l10: regulator-l10 { + regulator-name = "pm8998_l10"; + qcom,set = ; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <2928000>; + qcom,init-voltage = <1704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa11 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa11"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l11: regulator-l11 { + regulator-name = "pm8998_l11"; + qcom,set = ; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1048000>; + qcom,init-voltage = <1000000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa12 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa12"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l12: regulator-l12 { + regulator-name = "pm8998_l12"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa13 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa13"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l13: regulator-l13 { + regulator-name = "pm8998_l13"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa14 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa14"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + proxy-supply = <&pm8998_l14>; + pm8998_l14: regulator-l14 { + regulator-name = "pm8998_l14"; + qcom,set = ; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <115000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1880000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa15 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa15"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l15: regulator-l15 { + regulator-name = "pm8998_l15"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa16 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa16"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l16: regulator-l16 { + regulator-name = "pm8998_l16"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2704000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa17 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa17"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l17: regulator-l17 { + regulator-name = "pm8998_l17"; + qcom,set = ; + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1304000>; + qcom,init-voltage = <1304000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa18 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa18"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l18: regulator-l18 { + regulator-name = "pm8998_l18"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa19 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa19"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l19: regulator-l19 { + regulator-name = "pm8998_l19"; + qcom,set = ; + regulator-min-microvolt = <2856000>; + regulator-max-microvolt = <3104000>; + qcom,init-voltage = <2856000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa20 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa20"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l20: regulator-l20 { + regulator-name = "pm8998_l20"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa21 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa21"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l21: regulator-l21 { + regulator-name = "pm8998_l21"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa22 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa22"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l22: regulator-l22 { + regulator-name = "pm8998_l22"; + qcom,set = ; + regulator-min-microvolt = <2864000>; + regulator-max-microvolt = <3312000>; + qcom,init-voltage = <2864000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa23 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa23"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l23: regulator-l23 { + regulator-name = "pm8998_l23"; + qcom,set = ; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + qcom,init-voltage = <3000000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa24 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa24"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l24-parent-supply = <&pm8998_l12>; + pm8998_l24: regulator-l24 { + regulator-name = "pm8998_l24"; + qcom,set = ; + regulator-min-microvolt = <3088000>; + regulator-max-microvolt = <3088000>; + qcom,init-voltage = <3088000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa25 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa25"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + pm8998_l25: regulator-l25 { + regulator-name = "pm8998_l25"; + qcom,set = ; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + qcom,init-voltage = <3000000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa26 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa26"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + proxy-supply = <&pm8998_l26>; + pm8998_l26: regulator-l26 { + regulator-name = "pm8998_l26"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <43600>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + }; + }; + + /* PM8998 L27 = VDD_SSC_CX supply */ + rpmh-regulator-lcxlvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "lcx.lvl"; + pm8998_l27_level: regulator-l27-level { + regulator-name = "pm8998_l27_level"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + }; + + rpmh-regulator-ldoa28 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "ldoa28"; + qcom,regulator-type = "pmic4-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + pm8998_l28: regulator-l28 { + regulator-name = "pm8998_l28"; + qcom,set = ; + regulator-min-microvolt = <2856000>; + regulator-max-microvolt = <3008000>; + qcom,init-voltage = <2856000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-vsa1 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "vsa1"; + pm8998_lvs1: regulator-lvs1 { + regulator-name = "pm8998_lvs1"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + + rpmh-regulator-vsa2 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "vsa2"; + pm8998_lvs2: regulator-lvs2 { + regulator-name = "pm8998_lvs2"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + + rpmh-regulator-bobb1 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "bobb1"; + qcom,regulator-type = "pmic4-bob"; + qcom,send-defaults; + + pmi8998_bob: regulator-bob { + regulator-name = "pmi8998_bob"; + qcom,set = ; + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3600000>; + qcom,init-voltage = <3312000>; + qcom,init-mode = ; + }; + + pmi8998_bob_ao: regulator-bob-ao { + regulator-name = "pmi8998_bob_ao"; + qcom,set = ; + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3600000>; + qcom,init-voltage = <3312000>; + qcom,init-mode = ; + }; + }; + + /* PM8005 S1 + S4 = 2 phase VDD_GFX supply */ + rpmh-regulator-gfxlvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "gfx.lvl"; + pm8005_s1_level: regulator-s1-level { + regulator-name = "pm8005_s1_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + }; + + /* PM8005 S2 = VDD_MODEM supply */ + rpmh-regulator-msslvl { + compatible = "qcom,rpmh-arc-regulator"; + qcom,resource-name = "mss.lvl"; + pm8005_s2_level: regulator-s2-level { + regulator-name = "pm8005_s2_level"; + qcom,set = ; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + }; + + rpmh-regulator-smpc3 { + compatible = "qcom,rpmh-vrm-regulator"; + qcom,resource-name = "smpc3"; + pm8005_s3: regulator-s3 { + regulator-name = "pm8005_s3"; + qcom,set = ; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <600000>; + qcom,init-voltage = <600000>; + }; + }; +}; + +&soc { + refgen: refgen-regulator@ff1000 { + compatible = "qcom,refgen-sdm845-regulator"; + reg = <0xff1000 0x60>; + regulator-name = "refgen"; + regulator-enable-ramp-delay = <5>; + status = "disabled"; + proxy-supply = <&refgen>; + qcom,proxy-consumer-enable; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi new file mode 100644 index 000000000000..3aef751a67f8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include "dsi-panel-sim-video.dtsi" +#include "dsi-panel-sim-cmd.dtsi" +#include "dsi-panel-sim-dsc375-cmd.dtsi" +#include "dsi-panel-sim-dualmipi-video.dtsi" +#include "dsi-panel-sim-dualmipi-cmd.dtsi" +#include "dsi-panel-sim-dualmipi-dsc375-cmd.dtsi" +#include "dsi-panel-sharp-dsc-4k-video.dtsi" +#include "dsi-panel-sharp-dsc-4k-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi" +#include "dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi" +#include "dsi-panel-sharp-1080p-cmd.dtsi" +#include "dsi-panel-r63417-truly-1080p-cmd.dtsi" +#include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi" +#include "dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi" +#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi" +#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi" +#include "dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi" +#include "dsi-panel-test-dualmipi-oled-cmd.dtsi" +#include + +&soc { + dsi_panel_pwr_supply: dsi_panel_pwr_supply { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "ibb"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <20>; + }; + }; + + dsi_panel_pwr_supply_no_labibb: dsi_panel_pwr_supply_no_labibb { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + }; + + dsi_panel_pwr_supply_vdd_no_labibb: dsi_panel_pwr_supply_vdd_no_labibb { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <3000000>; + qcom,supply-max-voltage = <3000000>; + qcom,supply-enable-load = <857000>; + qcom,supply-disable-load = <0>; + qcom,supply-post-on-sleep = <0>; + }; + }; + + sde_dsi: qcom,dsi-display-primary { + compatible = "qcom,dsi-display"; + label = "primary"; + + qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; + qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; + + clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, + <&mdss_dsi0_pll PCLK_MUX_0_CLK>, + <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, + <&mdss_dsi1_pll PCLK_MUX_1_CLK>; + clock-names = "mux_byte_clk0", "mux_pixel_clk0", + "mux_byte_clk1", "mux_pixel_clk1"; + + pinctrl-names = "panel_active", "panel_suspend"; + pinctrl-0 = <&sde_dsi_active &sde_te_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-te-source = <0>; + + vddio-supply = <&pm8998_l14>; + + qcom,mdp = <&mdss_mdp>; + qcom,dsi-default-panel = <&dsi_nt35597_truly_dsc_cmd>; + }; + + sde_wb: qcom,wb-display@0 { + compatible = "qcom,wb-display"; + cell-index = <0>; + label = "wb_display"; + }; + + ext_disp: qcom,msm-ext-disp { + compatible = "qcom,msm-ext-disp"; + + ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + }; + }; +}; + +&sde_dp { + qcom,dp-usbpd-detection = <&pmi8998_pdphy>; + qcom,ext-disp = <&ext_disp>; + + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>; + pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>; + qcom,aux-en-gpio = <&tlmm 43 0>; + qcom,aux-sel-gpio = <&tlmm 51 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; +}; + +&mdss_mdp { + connectors = <&sde_rscc &sde_wb &sde_dsi &sde_dp>; +}; + +&dsi_dual_nt35597_truly_video { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0D>; + qcom,mdss-dsi-t-clk-pre = <0x2D>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = + "dfps_immediate_porch_mode_vfp"; + qcom,dsi-supported-dfps-list = <60 55 53>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07 + 07 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_nt35597_truly_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0D>; + qcom,mdss-dsi-t-clk-pre = <0x2D>; + qcom,ulps-enabled; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07 + 07 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + qcom,partial-update-enabled = "single_roi"; + qcom,panel-roi-alignment = <720 128 720 128 1440 128>; + }; + }; +}; + +&dsi_nt35597_truly_dsc_cmd { + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; + qcom,mdss-dsi-t-clk-post = <0x0b>; + qcom,mdss-dsi-t-clk-pre = <0x23>; + qcom,ulps-enabled; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 + 05 03 03 04 00]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&dsi_nt35597_truly_dsc_video { + qcom,dsi-select-clocks = "mux_byte_clk1", "mux_pixel_clk1"; + qcom,mdss-dsi-t-clk-post = <0x0b>; + qcom,mdss-dsi-t-clk-pre = <0x23>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = + "dfps_immediate_porch_mode_vfp"; + qcom,dsi-supported-dfps-list = <60 55 53>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 + 04 03 03 04 00]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&dsi_sharp_4k_dsc_video { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x27>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06 + 06 04 03 04 00]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sharp_4k_dsc_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x27>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06 + 06 04 03 04 00]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sharp_1080_120hz_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0f>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sharp_1080_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1E 08 08 24 22 08 + 08 05 03 04 00]; + qcom,mdss-dsi-panel-clockrate = <900000000>; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_r63417_truly_1080_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x1c>; + qcom,mdss-dsi-panel-on-check-value = <0x1c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1E 08 08 24 22 08 + 08 05 03 04 00]; + qcom,mdss-dsi-panel-clockrate = <900000000>; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_vid { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07 + 07 05 03 04 00]; + qcom,display-topology = <1 0 1>, + <2 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sim_vid { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07 + 07 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,display-topology = <1 0 1>, + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <720 40 720 40 720 40>; + qcom,partial-update-enabled = "single_roi"; + qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07 + 07 04 03 04 00]; + }; + timing@1{ + qcom,display-topology = <1 0 1>, + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <540 40 540 40 540 40>; + qcom,partial-update-enabled = "single_roi"; + qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07 + 07 04 03 04 00]; + }; + timing@2{ + qcom,display-topology = <1 0 1>, + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <360 40 360 40 360 40>; + qcom,partial-update-enabled = "single_roi"; + qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07 + 07 04 03 04 00]; + }; + }; +}; + +&dsi_dual_sim_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <2 0 2>; + qcom,default-topology-index = <0>; + }; + timing@1{ + qcom,mdss-dsi-panel-phy-timings = [00 30 0c 0d 2a 27 0c + 0d 09 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + timing@2{ + qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06 + 06 04 03 04 00]; + qcom,display-topology = <2 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_dsc_375_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0 { /* 1080p */ + qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07 + 07 04 03 04 00]; + qcom,display-topology = <1 1 1>; + qcom,default-topology-index = <0>; + }; + timing@1 { /* qhd */ + qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 + 05 03 03 04 00]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sim_dsc_375_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0 { /* qhd */ + qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07 + 07 05 03 04 00]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + timing@1 { /* 4k */ + qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06 + 06 04 03 04 00]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_nt35597_video { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 + 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_nt35597_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,ulps-enabled; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 + 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + qcom,partial-update-enabled = "single_roi"; + qcom,panel-roi-alignment = <720 128 720 128 1440 128>; + }; + }; +}; + +&dsi_dual_nt36850_truly_cmd { + qcom,dsi-select-clocks = "mux_byte_clk0", "mux_pixel_clk0"; + qcom,mdss-dsi-t-clk-post = <0x0E>; + qcom,mdss-dsi-t-clk-pre = <0x30>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08 + 08 05 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi new file mode 100644 index 000000000000..5d21169bd949 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&soc { + mdss_dsi0_pll: qcom,mdss_dsi_pll@ae94a00 { + compatible = "qcom,mdss_dsi_pll_10nm"; + label = "MDSS DSI 0 PLL"; + cell-index = <0>; + #clock-cells = <1>; + reg = <0xae94a00 0x1e0>, + <0xae94200 0x100>, + <0xae94400 0x800>, + <0xaf03000 0x8>; + reg-names = "pll_base", "dynamic_pll_base", + "phy_base", "gdsc_base"; + clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>; + clock-names = "iface_clk"; + clock-rate = <0>; + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + gdsc-supply = <&mdss_core_gdsc>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; + + mdss_dsi1_pll: qcom,mdss_dsi_pll@ae96a00 { + compatible = "qcom,mdss_dsi_pll_10nm"; + label = "MDSS DSI 1 PLL"; + cell-index = <1>; + #clock-cells = <1>; + reg = <0xae96a00 0x1e0>, + <0xae96200 0x100>, + <0xae96400 0x800>, + <0xaf03000 0x8>; + reg-names = "pll_base", "dynamic_pll_base", + "phy_base", "gdsc_base"; + clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>; + clock-names = "iface_clk"; + clock-rate = <0>; + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + gdsc-supply = <&mdss_core_gdsc>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; + + mdss_dp_pll: qcom,mdss_dp_pll@c011000 { + compatible = "qcom,mdss_dp_pll_10nm"; + label = "MDSS DP PLL"; + cell-index = <0>; + #clock-cells = <1>; + + reg = <0x088ea000 0x200>, + <0x088eaa00 0x200>, + <0x088ea200 0x200>, + <0x088ea600 0x200>, + <0xaf03000 0x8>; + reg-names = "pll_base", "phy_base", "ln_tx0_base", + "ln_tx1_base", "gdsc_base"; + + gdsc-supply = <&mdss_core_gdsc>; + + clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>, + <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; + clock-names = "iface_clk", "ref_clk_src", "ref_clk", + "cfg_ahb_clk", "pipe_clk"; + clock-rate = <0>; + + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + + }; + }; + +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi new file mode 100644 index 000000000000..71dd4cde966e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +#include + +&soc { + mdss_mdp: qcom,mdss_mdp@ae00000 { + compatible = "qcom,sde-kms"; + reg = <0x0ae00000 0x81d40>, + <0x0aeb0000 0x2008>, + <0x0aeac000 0xf0>; + reg-names = "mdp_phys", + "vbif_phys", + "regdma_phys"; + + clocks = + <&clock_gcc GCC_DISP_AHB_CLK>, + <&clock_gcc GCC_DISP_AXI_CLK>, + <&clock_dispcc DISP_CC_MDSS_AHB_CLK>, + <&clock_dispcc DISP_CC_MDSS_AXI_CLK>, + <&clock_dispcc DISP_CC_MDSS_MDP_CLK>, + <&clock_dispcc DISP_CC_MDSS_VSYNC_CLK>, + <&clock_dispcc DISP_CC_MDSS_MDP_LUT_CLK>, + <&clock_dispcc DISP_CC_MDSS_ROT_CLK>; + clock-names = "gcc_iface", "gcc_bus", "iface_clk", + "bus_clk", "core_clk", "vsync_clk", + "lut_clk", "rot_clk"; + clock-rate = <0 0 0 0 300000000 19200000 300000000 19200000>; + clock-max-rate = <0 0 0 0 412500000 19200000 412500000 412500000>; + + /* interrupt config */ + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + + #power-domain-cells = <0>; + + /* hw blocks */ + qcom,sde-off = <0x1000>; + qcom,sde-len = <0x45C>; + + qcom,sde-ctl-off = <0x2000 0x2200 0x2400 + 0x2600 0x2800>; + qcom,sde-ctl-size = <0xE4>; + qcom,sde-ctl-display-pref = "primary", "primary", "none", + "none", "none"; + + qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0 0 0x4a000>; + qcom,sde-mixer-size = <0x320>; + qcom,sde-mixer-display-pref = "primary", "primary", "none", + "none", "none", "none"; + qcom,sde-mixer-cwb-pref = "none", "none", "cwb", + "none", "none", "cwb"; + + qcom,sde-dspp-top-off = <0x1300>; + qcom,sde-dspp-top-size = <0xc>; + + qcom,sde-dspp-off = <0x55000 0x57000 0x59000 0x5b000>; + qcom,sde-dspp-size = <0x17e0>; + + qcom,sde-dest-scaler-top-off = <0x00061000>; + qcom,sde-dest-scaler-top-size = <0xc>; + qcom,sde-dest-scaler-off = <0x800 0x1000>; + qcom,sde-dest-scaler-size = <0x800>; + + qcom,sde-wb-off = <0x66000>; + qcom,sde-wb-size = <0x2c8>; + qcom,sde-wb-xin-id = <6>; + qcom,sde-wb-id = <2>; + qcom,sde-wb-clk-ctrl = <0x3b8 24>; + + qcom,sde-intf-off = <0x6b000 0x6b800 + 0x6c000 0x6c800>; + qcom,sde-intf-size = <0x280>; + + qcom,sde-intf-type = "dp", "dsi", "dsi", "dp"; + qcom,sde-pp-off = <0x71000 0x71800 + 0x72000 0x72800 0x73000>; + qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>; + qcom,sde-pp-size = <0xd4>; + + qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>; + qcom,sde-cdm-off = <0x7a200>; + qcom,sde-cdm-size = <0x224>; + + qcom,sde-dsc-off = <0x81000 0x81400 0x81800 0x81c00>; + qcom,sde-dsc-size = <0x140>; + qcom,sde-dsc-pair-mask = <2 1 4 3>; + + qcom,sde-dither-off = <0x30e0 0x30e0 0x30e0 0x30e0 0x0>; + qcom,sde-dither-version = <0x00010000>; + qcom,sde-dither-size = <0x20>; + + qcom,sde-sspp-type = "vig", "vig", "vig", "vig", + "dma", "dma", "dma", "dma"; + + qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000 + 0x25000 0x27000 0x29000 0x2b000>; + qcom,sde-sspp-src-size = <0x1c8>; + + qcom,sde-sspp-xin-id = <0 4 8 12 + 1 5 9 13>; + qcom,sde-sspp-excl-rect = <1 1 1 1 + 1 1 1 1>; + qcom,sde-sspp-smart-dma-priority = <5 6 7 8 1 2 3 4>; + qcom,sde-smart-dma-rev = "smart_dma_v2"; + + qcom,sde-mixer-pair-mask = <2 1 6 0 0 3>; + qcom,sde-max-per-pipe-bw-kbps = <4500000 4500000 + 4500000 4500000 + 4500000 4500000 + 4500000 4500000>; + + qcom,sde-mixer-blend-op-off = <0x20 0x38 0x50 0x68 0x80 0x98 + 0xb0 0xc8 0xe0 0xf8 0x110>; + + /* offsets are relative to "mdp_phys + qcom,sde-off */ + qcom,sde-sspp-clk-ctrl = + <0x2ac 0>, <0x2b4 0>, <0x2bc 0>, <0x2c4 0>, + <0x2ac 8>, <0x2b4 8>, <0x2bc 8>, <0x2c4 8>; + qcom,sde-sspp-csc-off = <0x1a00>; + qcom,sde-csc-type = "csc-10bit"; + qcom,sde-qseed-type = "qseedv3"; + qcom,sde-sspp-qseed-off = <0xa00>; + qcom,sde-mixer-linewidth = <2560>; + qcom,sde-sspp-linewidth = <2560>; + qcom,sde-wb-linewidth = <4096>; + qcom,sde-mixer-blendstages = <0xb>; + qcom,sde-highest-bank-bit = <0x2>; + qcom,sde-ubwc-version = <0x200>; + qcom,sde-ubwc-swizzle = <0x6>; + qcom,sde-ubwc-bw-calc-version = <0x1>; + qcom,sde-ubwc-static = <0x1e>; + qcom,sde-smart-panel-align-mode = <0xc>; + qcom,sde-panic-per-pipe; + qcom,sde-has-cdp; + qcom,sde-has-src-split; + qcom,sde-has-dim-layer; + qcom,sde-has-idle-pc; + qcom,sde-has-dest-scaler; + qcom,sde-max-dest-scaler-input-linewidth = <2048>; + qcom,sde-max-dest-scaler-output-linewidth = <2560>; + qcom,sde-max-bw-low-kbps = <6800000>; + qcom,sde-max-bw-high-kbps = <6800000>; + qcom,sde-min-core-ib-kbps = <2400000>; + qcom,sde-min-llcc-ib-kbps = <800000>; + qcom,sde-min-dram-ib-kbps = <800000>; + qcom,sde-dram-channels = <2>; + qcom,sde-num-nrt-paths = <0>; + qcom,sde-dspp-ad-version = <0x00040000>; + qcom,sde-dspp-ad-off = <0x28000 0x27000>; + + qcom,sde-vbif-off = <0>; + qcom,sde-vbif-size = <0x1040>; + qcom,sde-vbif-id = <0>; + qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>; + qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>; + + qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>; + qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>; + + qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000 + 0x00000000>; + qcom,sde-safe-lut-linear = + <4 0xfff8>, + <0 0xfff0>; + qcom,sde-safe-lut-macrotile = + <10 0xfe00>, + <11 0xfc00>, + <12 0xf800>, + <0 0xf000>; + qcom,sde-safe-lut-macrotile-qseed = + <10 0xfe00>, + <11 0xfc00>, + <12 0xf800>, + <0 0xf000>; + qcom,sde-safe-lut-nrt = + <0 0xffff>; + qcom,sde-safe-lut-cwb = + <0 0xffff>; + qcom,sde-qos-lut-linear = + <4 0x00000000 0x00000357>, + <5 0x00000000 0x00003357>, + <6 0x00000000 0x00023357>, + <7 0x00000000 0x00223357>, + <8 0x00000000 0x02223357>, + <9 0x00000000 0x22223357>, + <10 0x00000002 0x22223357>, + <11 0x00000022 0x22223357>, + <12 0x00000222 0x22223357>, + <13 0x00002222 0x22223357>, + <14 0x00012222 0x22223357>, + <0 0x00112222 0x22223357>; + qcom,sde-qos-lut-macrotile = + <10 0x00000003 0x44556677>, + <11 0x00000033 0x44556677>, + <12 0x00000233 0x44556677>, + <13 0x00002233 0x44556677>, + <14 0x00012233 0x44556677>, + <0 0x00112233 0x44556677>; + qcom,sde-qos-lut-macrotile-qseed = + <10 0x00000003 0x66777777>, + <11 0x00000033 0x66777777>, + <12 0x00000233 0x66777777>, + <13 0x00002233 0x66777777>, + <14 0x00012233 0x66777777>, + <0 0x00112233 0x66777777>; + qcom,sde-qos-lut-nrt = + <0 0x00000000 0x00000000>; + qcom,sde-qos-lut-cwb = + <0 0x75300000 0x00000000>; + + qcom,sde-cdp-setting = <1 1>, <1 0>; + + qcom,sde-qos-cpu-mask = <0x3>; + qcom,sde-qos-cpu-dma-latency = <300>; + + qcom,sde-inline-rotator = <&mdss_rotator 0>; + qcom,sde-inline-rot-xin = <10 11>; + qcom,sde-inline-rot-xin-type = "sspp", "wb"; + + /* offsets are relative to "mdp_phys + qcom,sde-off */ + qcom,sde-inline-rot-clk-ctrl = <0x2bc 0x8>, <0x2bc 0xc>; + + qcom,sde-reg-dma-off = <0>; + qcom,sde-reg-dma-version = <0x1>; + qcom,sde-reg-dma-trigger-off = <0x119c>; + + qcom,sde-sspp-vig-blocks { + qcom,sde-vig-csc-off = <0x1a00>; + qcom,sde-vig-qseed-off = <0xa00>; + qcom,sde-vig-qseed-size = <0xa0>; + }; + + qcom,sde-dspp-blocks { + qcom,sde-dspp-igc = <0x0 0x00030001>; + qcom,sde-dspp-hsic = <0x800 0x00010007>; + qcom,sde-dspp-memcolor = <0x880 0x00010007>; + qcom,sde-dspp-sixzone= <0x900 0x00010007>; + qcom,sde-dspp-vlut = <0xa00 0x00010008>; + qcom,sde-dspp-gamut = <0x1000 0x00040000>; + qcom,sde-dspp-pcc = <0x1700 0x00040000>; + qcom,sde-dspp-gc = <0x17c0 0x00010008>; + qcom,sde-dspp-hist = <0x800 0x00010007>; + qcom,sde-dspp-dither = <0x82c 0x00010007>; + }; + + smmu_sde_unsec: qcom,smmu_sde_unsec_cb { + compatible = "qcom,smmu_sde_unsec"; + iommus = <&apps_smmu 0x880 0x8>, + <&apps_smmu 0xc80 0x8>; + qcom,iommu-dma-addr-pool = <0x00020000 0xfffe0000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-earlymap; /* for cont-splash */ + }; + + smmu_sde_sec: qcom,smmu_sde_sec_cb { + compatible = "qcom,smmu_sde_sec"; + iommus = <&apps_smmu 0x881 0x8>, + <&apps_smmu 0xc81 0x8>; + qcom,iommu-dma-addr-pool = <0x00020000 0xfffe0000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-vmid = <0xa>; /* VMID_CP_PIXEL */ + }; + + /* data and reg bus scale settings */ + qcom,sde-data-bus { + qcom,msm-bus,name = "mdss_sde"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + <22 512 0 0>, <23 512 0 0>, + <22 512 0 6400000>, <23 512 0 6400000>, + <22 512 0 6400000>, <23 512 0 6400000>; + }; + + qcom,sde-reg-bus { + qcom,msm-bus,name = "mdss_reg"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>, + <1 590 0 150000>, + <1 590 0 300000>; + }; + }; + + sde_rscc: qcom,sde_rscc@af20000 { + cell-index = <0>; + compatible = "qcom,sde-rsc"; + reg = <0xaf20000 0x1c44>, + <0xaf30000 0x3fd4>; + reg-names = "drv", "wrapper"; + qcom,sde-rsc-version = <1>; + + vdd-supply = <&mdss_core_gdsc>; + clocks = <&clock_dispcc DISP_CC_MDSS_RSCC_VSYNC_CLK>, + <&clock_dispcc DISP_CC_MDSS_RSCC_AHB_CLK>; + clock-names = "vsync_clk", "iface_clk"; + clock-rate = <0 0>; + + qcom,sde-dram-channels = <2>; + + /* data and reg bus scale settings */ + qcom,sde-data-bus { + qcom,msm-bus,name = "disp_rsc_mnoc"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + <20003 20515 0 0>, <20004 20515 0 0>, + <20003 20515 0 6400000>, <20004 20515 0 6400000>, + <20003 20515 0 6400000>, <20004 20515 0 6400000>; + }; + + qcom,sde-llcc-bus { + qcom,msm-bus,name = "disp_rsc_llcc"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <20001 20513 0 0>, + <20001 20513 0 6400000>, + <20001 20513 0 6400000>; + }; + + qcom,sde-ebi-bus { + qcom,msm-bus,name = "disp_rsc_ebi"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <20000 20512 0 0>, + <20000 20512 0 6400000>, + <20000 20512 0 6400000>; + }; + }; + + mdss_rotator: qcom,mdss_rotator@ae00000 { + compatible = "qcom,sde_rotator"; + reg = <0x0ae00000 0xac000>, + <0x0aeb8000 0x3000>; + reg-names = "mdp_phys", + "rot_vbif_phys"; + + #list-cells = <1>; + + qcom,mdss-rot-mode = <1>; + qcom,mdss-highest-bank-bit = <0x2>; + + /* Bus Scale Settings */ + qcom,msm-bus,name = "mdss_rotator"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <25 512 0 0>, + <25 512 0 6400000>, + <25 512 0 6400000>; + + rot-vdd-supply = <&mdss_core_gdsc>; + qcom,supply-names = "rot-vdd"; + + clocks = + <&clock_gcc GCC_DISP_AHB_CLK>, + <&clock_gcc GCC_DISP_AXI_CLK>, + <&clock_dispcc DISP_CC_MDSS_AHB_CLK>, + <&clock_dispcc DISP_CC_MDSS_ROT_CLK>, + <&clock_dispcc DISP_CC_MDSS_AXI_CLK>; + clock-names = "gcc_iface", "gcc_bus", + "iface_clk", "rot_clk", "axi_clk"; + + interrupt-parent = <&mdss_mdp>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + + power-domains = <&mdss_mdp>; + + /* Offline rotator QoS setting */ + qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>; + qcom,mdss-rot-vbif-memtype = <3 3>; + qcom,mdss-rot-cdp-setting = <1 1>; + qcom,mdss-rot-qos-lut = <0x0 0x0 0x0 0x0>; + qcom,mdss-rot-danger-lut = <0x0 0x0>; + qcom,mdss-rot-safe-lut = <0x0000ffff 0x0000ffff>; + + /* Inline rotator QoS Setting */ + /* setting default register values for RD - qos/danger/safe */ + qcom,mdss-inline-rot-qos-lut = <0x44556677 0x00112233 + 0x44556677 0x00112233>; + qcom,mdss-inline-rot-danger-lut = <0x0055aaff 0x0000ffff>; + qcom,mdss-inline-rot-safe-lut = <0x0000f000 0x0000ff00>; + + qcom,mdss-rot-qos-cpu-mask = <0xf>; + qcom,mdss-rot-qos-cpu-dma-latency = <75>; + + qcom,mdss-default-ot-rd-limit = <32>; + qcom,mdss-default-ot-wr-limit = <32>; + + qcom,mdss-sbuf-headroom = <20>; + + cache-slice-names = "rotator"; + + /* reg bus scale settings */ + rot_reg: qcom,rot-reg-bus { + qcom,msm-bus,name = "mdss_rot_reg"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>; + }; + + smmu_rot_unsec: qcom,smmu_rot_unsec_cb { + compatible = "qcom,smmu_sde_rot_unsec"; + iommus = <&apps_smmu 0x1090 0x0>; + qcom,iommu-dma-addr-pool = <0x00020000 0xfffe0000>; + qcom,iommu-faults = "non-fatal"; + }; + + smmu_rot_sec: qcom,smmu_rot_sec_cb { + compatible = "qcom,smmu_sde_rot_sec"; + iommus = <&apps_smmu 0x1091 0x0>; + qcom,iommu-dma-addr-pool = <0x00020000 0xfffe0000>; + qcom,iommu-faults = "non-fatal"; + }; + }; + + mdss_dsi0: qcom,mdss_dsi_ctrl0@ae94000 { + compatible = "qcom,dsi-ctrl-hw-v2.2"; + label = "dsi-ctrl-0"; + cell-index = <0>; + reg = <0xae94000 0x400>, + <0xaf03000 0x5004>; + reg-names = "dsi_ctrl", "disp_cc_base"; + interrupt-parent = <&mdss_mdp>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + vdda-1p2-supply = <&pm8998_l26>; + clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_ESC0_CLK>; + clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk", + "pixel_clk", "pixel_clk_rcg", + "esc_clk"; + qcom,null-insertion-enabled; + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <21800>; + qcom,supply-disable-load = <4>; + }; + }; + }; + + mdss_dsi1: qcom,mdss_dsi_ctrl1@ae96000 { + compatible = "qcom,dsi-ctrl-hw-v2.2"; + label = "dsi-ctrl-1"; + cell-index = <1>; + reg = <0xae96000 0x400>, + <0xaf03000 0x5004>; + reg-names = "dsi_ctrl", "disp_cc_base"; + interrupt-parent = <&mdss_mdp>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + vdda-1p2-supply = <&pm8998_l26>; + clocks = <&clock_dispcc DISP_CC_MDSS_BYTE1_CLK>, + <&clock_dispcc DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK1_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK1_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_ESC1_CLK>; + clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk", + "pixel_clk", "pixel_clk_rcg", "esc_clk"; + qcom,null-insertion-enabled; + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <21800>; + qcom,supply-disable-load = <4>; + }; + }; + }; + + mdss_dsi_phy0: qcom,mdss_dsi_phy0@ae94400 { + compatible = "qcom,dsi-phy-v3.0"; + label = "dsi-phy-0"; + cell-index = <0>; + reg = <0xae94400 0x7c0>, + <0xae94200 0x100>; + reg-names = "dsi_phy", "dyn_refresh_base"; + + vdda-0p9-supply = <&pm8998_l1>; + qcom,platform-strength-ctrl = [55 03 + 55 03 + 55 03 + 55 03 + 55 00]; + qcom,platform-lane-config = [00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 80]; + qcom,platform-regulator-settings = [1d 1d 1d 1d 1d]; + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <36000>; + qcom,supply-disable-load = <32>; + }; + }; + }; + + mdss_dsi_phy1: qcom,mdss_dsi_phy0@ae96400 { + compatible = "qcom,dsi-phy-v3.0"; + label = "dsi-phy-1"; + cell-index = <1>; + reg = <0xae96400 0x7c0>, + <0xae96200 0x100>; + reg-names = "dsi_phy", "dyn_refresh_base"; + + vdda-0p9-supply = <&pm8998_l1>; + qcom,platform-strength-ctrl = [55 03 + 55 03 + 55 03 + 55 03 + 55 00]; + qcom,platform-regulator-settings = [1d 1d 1d 1d 1d]; + qcom,platform-lane-config = [00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 80]; + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <36000>; + qcom,supply-disable-load = <32>; + }; + }; + }; + + sde_dp: qcom,dp_display@0{ + cell-index = <0>; + compatible = "qcom,dp-display"; + + vdda-1p2-supply = <&pm8998_l26>; + vdda-0p9-supply = <&pm8998_l1>; + + reg = <0xae90000 0x0dc>, + <0xae90200 0x0c0>, + <0xae90400 0x508>, + <0xae90a00 0x094>, + <0x88eaa00 0x200>, + <0x88ea200 0x200>, + <0x88ea600 0x200>, + <0xaf02000 0x1a0>, + <0x780000 0x621c>, + <0x88ea030 0x10>, + <0x88e8000 0x20>, + <0x0aee1000 0x034>; + /* dp_ctrl: dp_ahb, dp_aux, dp_link, dp_p0 */ + reg-names = "dp_ahb", "dp_aux", "dp_link", + "dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1", + "dp_mmss_cc", "qfprom_physical", "dp_pll", + "usb3_dp_com", "hdcp_physical"; + + interrupt-parent = <&mdss_mdp>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>, + <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>, + <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>, + <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>, + <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>, + <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>, + <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>, + <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>, + <&mdss_dp_pll DP_PHY_PLL_VCO_DIV_CLK>; + clock-names = "core_aux_clk", "core_usb_ref_clk_src", + "core_usb_ref_clk", "core_usb_cfg_ahb_clk", + "core_usb_pipe_clk", "ctrl_link_clk", + "ctrl_link_iface_clk", "ctrl_pixel_clk", + "crypto_clk", "pixel_clk_rcg", "pixel_parent"; + + qcom,aux-cfg0-settings = [20 00]; + qcom,aux-cfg1-settings = [24 13 23 1d]; + qcom,aux-cfg2-settings = [28 24]; + qcom,aux-cfg3-settings = [2c 00]; + qcom,aux-cfg4-settings = [30 0a]; + qcom,aux-cfg5-settings = [34 26]; + qcom,aux-cfg6-settings = [38 0a]; + qcom,aux-cfg7-settings = [3c 03]; + qcom,aux-cfg8-settings = [40 bb]; + qcom,aux-cfg9-settings = [44 03]; + + qcom,max-pclk-frequency-khz = <675000>; + + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <21800>; + qcom,supply-disable-load = <4>; + }; + }; + + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <36000>; + qcom,supply-disable-load = <32>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-smp2p.dtsi b/arch/arm64/boot/dts/qcom/sdm845-smp2p.dtsi new file mode 100644 index 000000000000..8901a3792132 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-smp2p.dtsi @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +#include + +&soc { + qcom,smp2p-modem { + compatible = "qcom,smp2p"; + qcom,smem = <435>, <428>; + interrupts = ; + mboxes = <&apss_shared 14>; + qcom,local-pid = <0>; + qcom,remote-pid = <1>; + + modem_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + modem_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + smp2p_ipa_1_out: qcom,smp2p-ipa-1-out { + qcom,entry-name = "ipa"; + #qcom,smem-state-cells = <1>; + }; + + /* ipa - inbound entry from mss */ + smp2p_ipa_1_in: qcom,smp2p-ipa-1-in { + qcom,entry-name = "ipa"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + smp2p_wlan_1_in: qcom,smp2p-wlan-1-in { + qcom,entry-name = "wlan"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-adsp { + compatible = "qcom,smp2p"; + qcom,smem = <443>, <429>; + interrupts = ; + mboxes = <&apss_shared 10>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + adsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + adsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + smp2p_rdbg2_out: qcom,smp2p-rdbg2-out { + qcom,entry-name = "rdbg"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_rdbg2_in: qcom,smp2p-rdbg2-in { + qcom,entry-name = "rdbg"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-dsps { + compatible = "qcom,smp2p"; + qcom,smem = <481>, <430>; + interrupts = ; + mboxes = <&apss_shared 26>; + qcom,local-pid = <0>; + qcom,remote-pid = <3>; + + dsps_smp2p_out: master-kernel { + #address-cells = <0>; + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + dsps_smp2p_in: slave-kernel { + #address-cells = <0>; + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + sleepstate_smp2p_out: sleepstate-out { + #address-cells = <0>; + qcom,entry-name = "sleepstate"; + #qcom,smem-state-cells = <1>; + }; + + sleepstate_smp2p_in: qcom,sleepstate-in { + #address-cells = <0>; + qcom,entry-name = "sleepstate_see"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-cdsp { + compatible = "qcom,smp2p"; + qcom,smem = <94>, <432>; + interrupts = ; + mboxes = <&apss_shared 6>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + cdsp_smp2p_out: master-kernel { + #address-cells = <0>; + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + cdsp_smp2p_in: slave-kernel { + #address-cells = <0>; + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + + smp2p_qvrexternal5_out: qcom,smp2p-qvrexternal5-out { + #address-cells = <0>; + qcom,entry-name = "qvrexternal"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_rdbg5_out: qcom,smp2p-rdbg5-out { + #address-cells = <0>; + qcom,entry-name = "rdbg"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_rdbg5_in: qcom,smp2p-rdbg5-in { + #address-cells = <0>; + qcom,entry-name = "rdbg"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi new file mode 100644 index 000000000000..355c9c9ca4db --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ + +&soc { + cam_csiphy0: qcom,csiphy@ac65000 { + cell-index = <0>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0x0ac65000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x65000>; + interrupts = ; + interrupt-names = "csiphy"; + regulator-names = "gdscr", "refgen"; + gdscr-supply = <&titan_top_gdsc>; + refgen-supply = <&refgen>; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY0_CLK>, + <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy0_clk", + "csi0phytimer_clk_src", + "csi0phytimer_clk"; + clock-cntl-level = "turbo"; + clock-rates = + <0 0 0 0 384000000 0 269333333 0>; + status = "ok"; + }; + + cam_csiphy1: qcom,csiphy@ac66000{ + cell-index = <1>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0xac66000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x66000>; + interrupts = ; + interrupt-names = "csiphy"; + regulator-names = "gdscr", "refgen"; + gdscr-supply = <&titan_top_gdsc>; + refgen-supply = <&refgen>; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY1_CLK>, + <&clock_camcc CAM_CC_CSI1PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI1PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy1_clk", + "csi1phytimer_clk_src", + "csi1phytimer_clk"; + clock-cntl-level = "turbo"; + clock-rates = + <0 0 0 0 384000000 0 269333333 0>; + + status = "ok"; + }; + + cam_csiphy2: qcom,csiphy@ac67000 { + cell-index = <2>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0xac67000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x67000>; + interrupts = ; + interrupt-names = "csiphy"; + regulator-names = "gdscr", "refgen"; + gdscr-supply = <&titan_top_gdsc>; + refgen-supply = <&refgen>; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY2_CLK>, + <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy2_clk", + "csi2phytimer_clk_src", + "csi2phytimer_clk"; + clock-cntl-level = "turbo"; + clock-rates = + <0 0 0 0 384000000 0 269333333 0>; + status = "ok"; + }; + + cam_csiphy3: qcom,csiphy@ac68000 { + cell-index = <3>; + compatible = "qcom,csiphy-v1.0", "qcom,csiphy"; + reg = <0xac68000 0x1000>; + reg-names = "csiphy"; + reg-cam-base = <0x68000>; + interrupts = ; + interrupt-names = "csiphy"; + regulator-names = "gdscr", "refgen"; + gdscr-supply = <&titan_top_gdsc>; + refgen-supply = <&refgen>; + csi-vdd-voltage = <1200000>; + mipi-csi-vdd-supply = <&pm8998_l1>; + clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&clock_camcc CAM_CC_CSIPHY3_CLK>, + <&clock_camcc CAM_CC_CSI3PHYTIMER_CLK_SRC>, + <&clock_camcc CAM_CC_CSI3PHYTIMER_CLK>; + clock-names = "camnoc_axi_clk", + "soc_ahb_clk", + "slow_ahb_src_clk", + "cpas_ahb_clk", + "cphy_rx_clk_src", + "csiphy3_clk", + "csi3phytimer_clk_src", + "csi3phytimer_clk"; + src-clock-name = "csi3phytimer_clk_src"; + clock-cntl-level = "turbo"; + clock-rates = + <0 0 0 0 384000000 0 269333333 0>; + status = "ok"; + }; + + qcom,cam_smmu { + compatible = "qcom,msm-cam-smmu"; + status = "ok"; + non-fatal-fault-disabled; + + msm_cam_smmu_lrme { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1038 0x0>, + <&apps_smmu 0x1058 0x0>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "lrme"; + lrme_iova_mem_map: iova-mem-map { + iova-mem-region-shared { + /* Shared region is 100MB long */ + iova-region-name = "shared"; + iova-region-start = <0x7400000>; + iova-region-len = <0x6400000>; + iova-region-id = <0x1>; + status = "ok"; + }; + /* IO region is approximately 3.3 GB */ + iova-mem-region-io { + iova-region-name = "io"; + iova-region-start = <0xd800000>; + iova-region-len = <0xd2800000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_ife { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x808 0x0>, + <&apps_smmu 0x810 0x8>, + <&apps_smmu 0xc08 0x0>, + <&apps_smmu 0xc10 0x8>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "ife"; + ife_iova_mem_map: iova-mem-map { + /* IO region is approximately 3.4 GB */ + iova-mem-region-io { + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_icp_fw { + compatible = "qcom,msm-cam-smmu-fw-dev"; + label="icp"; + memory-region = <&pil_camera_mem>; + }; + + msm_cam_smmu_icp { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x107A 0x2>, + <&apps_smmu 0x1020 0x8>, + <&apps_smmu 0x1040 0x8>, + <&apps_smmu 0x1030 0x0>, + <&apps_smmu 0x1050 0x0>; + qcom,iommu-dma-addr-pool = <0x10c00000 0xcf300000>; + label = "icp"; + icp_iova_mem_map: iova-mem-map { + iova-mem-region-firmware { + /* Firmware region is 5MB */ + iova-region-name = "firmware"; + iova-region-start = <0x0>; + iova-region-len = <0x500000>; + iova-region-id = <0x0>; + status = "ok"; + }; + + iova-mem-region-shared { + /* Shared region is 150MB long */ + iova-region-name = "shared"; + iova-region-start = <0x7400000>; + iova-region-len = <0x9600000>; + iova-region-id = <0x1>; + iova-granularity = <0x15>; + status = "ok"; + }; + + iova-mem-region-secondary-heap { + /* Secondary heap region is 1MB long */ + iova-region-name = "secheap"; + iova-region-start = <0x10A00000>; + iova-region-len = <0x100000>; + iova-region-id = <0x4>; + status = "ok"; + }; + + iova-mem-region-io { + /* IO region is approximately 3 GB */ + iova-region-name = "io"; + iova-region-start = <0x10C00000>; + iova-region-len = <0xCF300000>; + iova-region-id = <0x3>; + status = "ok"; + }; + + iova-mem-qdss-region { + /* qdss region is approximately 1MB */ + iova-region-name = "qdss"; + iova-region-start = <0x10B00000>; + iova-region-len = <0x100000>; + iova-region-id = <0x5>; + qdss-phy-addr = <0x16790000>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_cpas_cdm { + compatible = "qcom,msm-cam-smmu-cb"; + iommus = <&apps_smmu 0x1000 0x0>; + qcom,iommu-dma-addr-pool = <0x7400000 0xd8c00000>; + label = "cpas-cdm0"; + cpas_cdm_iova_mem_map: iova-mem-map { + iova-mem-region-io { + /* IO region is approximately 3.4 GB */ + iova-region-name = "io"; + iova-region-start = <0x7400000>; + iova-region-len = <0xd8c00000>; + iova-region-id = <0x3>; + status = "ok"; + }; + }; + }; + + msm_cam_smmu_secure { + compatible = "qcom,msm-cam-smmu-cb"; + label = "cam-secure"; + qcom,secure-cb; + }; + }; + + qcom,cam-cpas@ac40000 { + cell-index = <0>; + compatible = "qcom,cam-cpas"; + label = "cpas"; + arch-compat = "cpas_top"; + status = "ok"; + reg-names = "cam_cpas_top", "cam_camnoc"; + reg = <0xac40000 0x1000>, + <0xac42000 0x5000>; + reg-cam-base = <0x40000 0x42000>; + interrupt-names = "cpas_camnoc"; + interrupts = ; + qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */ + camnoc-axi-min-ib-bw = <3000000000>; + regulator-names = "camss-vdd"; + camss-vdd-supply = <&titan_top_gdsc>; + clock-names = "gcc_ahb_clk", + "gcc_axi_clk", + "soc_ahb_clk", + "slow_ahb_clk_src", + "cpas_ahb_clk", + "camnoc_axi_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>; + src-clock-name = "slow_ahb_clk_src"; + clock-rates = <0 0 0 0 0 0>, + <0 0 0 19200000 0 0>, + <0 0 0 80000000 0 0>, + <0 0 0 80000000 0 0>, + <0 0 0 80000000 0 0>, + <0 0 0 80000000 0 0>, + <0 0 0 80000000 0 0>; + clock-cntl-level = "suspend", "minsvs", "lowsvs", "svs", + "svs_l1", "nominal", "turbo"; + qcom,msm-bus,name = "cam_ahb"; + qcom,msm-bus,num-cases = <7>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + , + , + , + , + , + ; + vdd-corners = ; + vdd-corner-ahb-mapping = "suspend", "minsvs", + "lowsvs", "svs", "svs_l1", + "nominal", "nominal", "nominal", + "turbo", "turbo"; + client-id-based; + client-names = + "csiphy0", "csiphy1", "csiphy2", "csiphy3", "cci0", + "csid0", "csid1", "csid2", + "ife0", "ife1", "ife2", "ipe0", + "ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0", + "icp0", "jpeg-dma0", "jpeg-enc0", "fd0", "lrmecpas0"; + client-axi-port-names = + "cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_hf_2", + "cam_sf_1", "cam_hf_1", "cam_hf_2", "cam_hf_2", + "cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1", + "cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1", + "cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1", + "cam_sf_1"; + client-bus-camnoc-based; + qcom,axi-port-list { + qcom,axi-port1 { + qcom,axi-port-name = "cam_hf_1"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_hf_1_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_hf_1_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + qcom,axi-port2 { + qcom,axi-port-name = "cam_hf_2"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_hf_2_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_hf_2_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + qcom,axi-port3 { + qcom,axi-port-name = "cam_sf_1"; + qcom,axi-port-mnoc { + qcom,msm-bus,name = "cam_sf_1_mnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + qcom,axi-port-camnoc { + qcom,msm-bus,name = "cam_sf_1_camnoc"; + qcom,msm-bus-vector-dyn-vote; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; + }; + }; + }; + }; + + qcom,cam-lrme { + compatible = "qcom,cam-lrme"; + arch-compat = "lrme"; + status = "ok"; + }; + + cam_lrme: qcom,lrme@ac6b000 { + cell-index = <0>; + compatible = "qcom,lrme"; + reg-names = "lrme"; + reg = <0xac6b000 0xa00>; + reg-cam-base = <0x6b000>; + interrupt-names = "lrme"; + interrupts = ; + regulator-names = "camss"; + camss-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "lrme_clk_src", + "lrme_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_LRME_CLK_SRC>, + <&clock_camcc CAM_CC_LRME_CLK>; + clock-rates = <0 0 0 0 0 200000000 200000000>, + <0 0 0 0 0 269000000 269000000>, + <0 0 0 0 0 320000000 320000000>, + <0 0 0 0 0 400000000 400000000>; + + clock-cntl-level = "lowsvs", "svs", "svs_l1", "turbo"; + src-clock-name = "lrme_clk_src"; + status = "ok"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-mtp-overlay.dts new file mode 100644 index 000000000000..23f67a1aa6d3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2-mtp-overlay.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-t0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2 MTP default"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20000>; + qcom,board-id = <8 0 0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-mtp.dts new file mode 100644 index 000000000000..a2447e2e1436 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2-mtp.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; + +#include "sdm845-v2.dtsi" +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM sdm845 V2 MTP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts new file mode 100644 index 000000000000..d91ee93e4ed5 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include +#include + +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" +#include "enchilada.dtsi" +#include "enchilada-t0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,msm-id = <321 0x20001>; + qcom,board-id = <8 0 0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp.dts new file mode 100644 index 000000000000..7a0d7aa3a315 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp.dts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; + +#include "sdm845-v2.1.dtsi" +#include "sdm845-sde-display.dtsi" +#include "sdm845-mtp.dtsi" +#include "sdm845-audio-overlay.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM sdm845 V2.1 MTP"; + compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp"; + qcom,board-id = <8 0>; +}; + +&tlmm { + sdc2_wlan_gpio_on: sdc2_wlan_gpio_on { + mux { + pins = "gpio11"; + function = "gpio"; + }; + config { + pins = "gpio11"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + + sdc2_wlan_gpio_off: sdc2_wlan_gpio_off { + mux { + pins = "gpio11"; + function = "gpio"; + }; + config { + pins = "gpio11"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&sdhc_2 { + /delete-property/cd-gpios; + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = ; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc GIC_SPI 204 IRQ_TYPE_LEVEL_HIGH + 1 &intc GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH + 2 &tlmm 11 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq"; + + vdd-supply = <&pm8998_l23>; + qcom,vdd-voltage-level = <3200000 3300000>; + vdd-io-supply = <&pm8998_s4>; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-current-level = <15000 400000>; + qcom,vdd-io-current-level = <200 50000>; + qcom,clk-rates = <400000 25000000 50000000>; + qcom,bus-speed-mode = "SDR12"; + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on + &sdc2_wlan_gpio_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_wlan_gpio_off>; + qcom,nonremovable; + qcom,core_3_0v_support; + status = "ok"; +}; + +&pm8998_l6 { + regulator-boot-on; + regulator-always-on; +}; + +&pm8998_l23 { + regulator-boot-on; + regulator-always-on; +}; + +&soc { + qcom,cnss-sdio { + compatible = "qcom,cnss_sdio"; + reg = <0x87a00000 0x200000>; + reg-names = "ramdump"; + subsys-name = "AR6320"; + qcom,wlan-ramdump-dynamic = <0x200000>; + qcom,msm-bus,name = "msm-cnss"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <79 512 0 0>, /* No vote */ + <79 512 6250 200000>, /* 50 Mbps */ + <79 512 25000 200000>, /* 200 Mbps */ + <79 512 2048000 4096000>; /* MAX */ + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts new file mode 100644 index 000000000000..20329fc06cac --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; + +#include "sdm845-v2.1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2.1 SoC"; + compatible = "qcom,sdm845"; + qcom,board-id = <0 0>; +}; + diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi new file mode 100644 index 000000000000..c374b488c2fe --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include "sdm845-v2.dtsi" +#include "sdm845-v2-camera.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 V2.1"; + qcom,msm-id = <321 0x20001>; +}; + +&spmi_bus { + /delete-property/ qcom,enable-ahb-bus-workaround; +}; + +&clock_gcc { + compatible = "qcom,gcc-sdm845-v2.1", "syscon"; +}; + +&apps_smmu { + /delete-property/ qcom,no-asid-retention; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.dts new file mode 100644 index 000000000000..2ebff002a269 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; + +#include "sdm845-v2.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v2 SoC"; + compatible = "qcom,sdm845"; + qcom,board-id = <0 0 0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi new file mode 100644 index 000000000000..cbdc05d1b801 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include "sdm845.dtsi" +#include "sdm845-v2-camera.dtsi" +#include "sdm845_enchilada_soc.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 V2"; + qcom,msm-id = <321 0x20000>; +}; + +&sdhc_2 { + /delete-property/ qcom,sdr104-wa; +}; + +&pdc { + qcom,pdc-ranges = <0 480 94>, <94 609 15>, <115 630 7>; +}; + +&tlmm { + compatible = "qcom,sdm845-pinctrl-v2"; + irqdomain-map = <1 0 &pdc 30 0>, + <3 0 &pdc 31 0>, + <5 0 &pdc 32 0>, + <10 0 &pdc 33 0>, + <11 0 &pdc 34 0>, + <20 0 &pdc 35 0>, + <22 0 &pdc 36 0>, + <24 0 &pdc 37 0>, + <26 0 &pdc 38 0>, + <30 0 &pdc 39 0>, + <31 0 &pdc 117 0>, + <32 0 &pdc 41 0>, + <34 0 &pdc 42 0>, + <36 0 &pdc 43 0>, + <37 0 &pdc 44 0>, + <38 0 &pdc 45 0>, + <39 0 &pdc 46 0>, + <40 0 &pdc 47 0>, + <41 0 &pdc 115 0>, + <43 0 &pdc 49 0>, + <44 0 &pdc 50 0>, + <46 0 &pdc 51 0>, + <48 0 &pdc 52 0>, + <49 0 &pdc 118 0>, + <52 0 &pdc 54 0>, + <53 0 &pdc 55 0>, + <54 0 &pdc 56 0>, + <56 0 &pdc 57 0>, + <57 0 &pdc 58 0>, + <58 0 &pdc 59 0>, + <59 0 &pdc 60 0>, + <60 0 &pdc 61 0>, + <61 0 &pdc 62 0>, + <62 0 &pdc 63 0>, + <63 0 &pdc 64 0>, + <64 0 &pdc 65 0>, + <66 0 &pdc 66 0>, + <68 0 &pdc 67 0>, + <71 0 &pdc 68 0>, + <73 0 &pdc 69 0>, + <77 0 &pdc 70 0>, + <78 0 &pdc 71 0>, + <79 0 &pdc 72 0>, + <80 0 &pdc 73 0>, + <84 0 &pdc 74 0>, + <85 0 &pdc 75 0>, + <86 0 &pdc 76 0>, + <88 0 &pdc 77 0>, + <89 0 &pdc 116 0>, + <91 0 &pdc 79 0>, + <92 0 &pdc 80 0>, + <95 0 &pdc 81 0>, + <96 0 &pdc 82 0>, + <97 0 &pdc 83 0>, + <101 0 &pdc 84 0>, + <103 0 &pdc 85 0>, + //<108 0 &pdc ?? 0>, + //<112 0 &pdc ?? 0>, + //<113 0 &pdc ?? 0>, + <104 0 &pdc 86 0>, + <115 0 &pdc 90 0>, + <116 0 &pdc 91 0>, + <117 0 &pdc 92 0>, + <118 0 &pdc 93 0>, + <119 0 &pdc 94 0>, + <120 0 &pdc 95 0>, + <121 0 &pdc 96 0>, + <122 0 &pdc 97 0>, + <123 0 &pdc 98 0>, + <124 0 &pdc 99 0>, + <125 0 &pdc 100 0>, + //<126 0 &pdc ?? 0>, + <127 0 &pdc 102 0>, + <128 0 &pdc 103 0>, + <129 0 &pdc 104 0>, + <130 0 &pdc 105 0>, + <132 0 &pdc 106 0>, + <133 0 &pdc 107 0>, + <145 0 &pdc 108 0>; +}; + +&soc { + qcom,memshare { + compatible = "qcom,memshare"; + + qcom,client_1 { + compatible = "qcom,memshare-peripheral"; + qcom,peripheral-size = <0x0>; + qcom,client-id = <0>; + qcom,allocate-boot-time; + label = "modem"; + }; + + qcom,client_2 { + compatible = "qcom,memshare-peripheral"; + qcom,peripheral-size = <0x0>; + qcom,client-id = <2>; + label = "modem"; + }; + + mem_client_3_size: qcom,client_3 { + compatible = "qcom,memshare-peripheral"; + qcom,peripheral-size = <0x500000>; + qcom,client-id = <1>; + qcom,allocate-on-request; + label = "modem"; + }; + }; + + gpu_gx_domain_addr: syscon@0x5091508 { + compatible = "syscon"; + reg = <0x5091508 0x4>; + }; + + gpu_gx_sw_reset: syscon@0x5091008 { + compatible = "syscon"; + reg = <0x5091008 0x4>; + }; + + llcc_bw_opp_table_v2: llcc-bw-opp-table-v2 { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 150, 16); /* 2288 MB/s */ + BW_OPP_ENTRY( 300, 16); /* 4577 MB/s */ + BW_OPP_ENTRY( 426, 16); /* 6500 MB/s */ + BW_OPP_ENTRY( 533, 16); /* 8132 MB/s */ + BW_OPP_ENTRY( 600, 16); /* 9155 MB/s */ + BW_OPP_ENTRY( 806, 16); /* 12298 MB/s */ + BW_OPP_ENTRY( 933, 16); /* 14236 MB/s */ + }; +}; + +&pil_modem { + qcom,mss_pdc_offset = <9>; +}; + +&clock_cpucc { + compatible = "qcom,clk-cpu-osm-v2"; +}; + +&pcie1 { + qcom,phy-sequence = <0x1804 0x03 0x0 + 0x00dc 0x27 0x0 + 0x0014 0x01 0x0 + 0x0020 0x31 0x0 + 0x0024 0x01 0x0 + 0x0028 0xde 0x0 + 0x002c 0x07 0x0 + 0x0034 0x4c 0x0 + 0x0038 0x06 0x0 + 0x0054 0x18 0x0 + 0x0058 0xb0 0x0 + 0x006c 0x8c 0x0 + 0x0070 0x20 0x0 + 0x0078 0x14 0x0 + 0x007c 0x34 0x0 + 0x00b4 0x06 0x0 + 0x00b8 0x06 0x0 + 0x00c0 0x16 0x0 + 0x00c4 0x16 0x0 + 0x00cc 0x36 0x0 + 0x00d0 0x36 0x0 + 0x00f0 0x05 0x0 + 0x00f8 0x42 0x0 + 0x0100 0x82 0x0 + 0x0108 0x68 0x0 + 0x011c 0x55 0x0 + 0x0120 0x55 0x0 + 0x0124 0x03 0x0 + 0x0128 0xab 0x0 + 0x012c 0xaa 0x0 + 0x0130 0x02 0x0 + 0x0150 0x3f 0x0 + 0x0158 0x3f 0x0 + 0x0178 0x10 0x0 + 0x01cc 0x04 0x0 + 0x01d0 0x30 0x0 + 0x01e0 0x04 0x0 + 0x01e8 0x73 0x0 + 0x01f0 0x1c 0x0 + 0x01fc 0x15 0x0 + 0x021c 0x04 0x0 + 0x0224 0x01 0x0 + 0x0228 0x22 0x0 + 0x022c 0x00 0x0 + 0x0098 0x05 0x0 + 0x080c 0x00 0x0 + 0x0818 0x0d 0x0 + 0x0860 0x01 0x0 + 0x0864 0x3a 0x0 + 0x087c 0x2f 0x0 + 0x08c0 0x09 0x0 + 0x08c4 0x09 0x0 + 0x08c8 0x1a 0x0 + 0x08d0 0x01 0x0 + 0x08d4 0x07 0x0 + 0x08d8 0x31 0x0 + 0x08dc 0x31 0x0 + 0x08e0 0x03 0x0 + 0x08fc 0x02 0x0 + 0x0900 0x01 0x0 + 0x0908 0x12 0x0 + 0x0914 0x25 0x0 + 0x0918 0x00 0x0 + 0x091c 0x05 0x0 + 0x0920 0x01 0x0 + 0x0924 0x26 0x0 + 0x0928 0x12 0x0 + 0x0930 0x04 0x0 + 0x0934 0x04 0x0 + 0x0938 0x09 0x0 + 0x0954 0x15 0x0 + 0x0960 0x32 0x0 + 0x0968 0x7f 0x0 + 0x096c 0x07 0x0 + 0x0978 0x04 0x0 + 0x0980 0x70 0x0 + 0x0984 0x8b 0x0 + 0x0988 0x08 0x0 + 0x098c 0x09 0x0 + 0x0990 0x03 0x0 + 0x0994 0x04 0x0 + 0x0998 0x02 0x0 + 0x099c 0x0c 0x0 + 0x09a4 0x02 0x0 + 0x09c0 0x5c 0x0 + 0x09c4 0x3e 0x0 + 0x09c8 0x3f 0x0 + 0x0a30 0x01 0x0 + 0x0a34 0xa0 0x0 + 0x0a38 0x08 0x0 + 0x0aa4 0x01 0x0 + 0x0aac 0xc3 0x0 + 0x0ab0 0x00 0x0 + 0x0ab8 0x8c 0x0 + 0x0ac0 0x7f 0x0 + 0x0ac4 0x2a 0x0 + 0x0810 0x0c 0x0 + 0x0814 0x00 0x0 + 0x0acc 0x04 0x0 + 0x093c 0x20 0x0 + 0x100c 0x00 0x0 + 0x1018 0x0d 0x0 + 0x1060 0x01 0x0 + 0x1064 0x3a 0x0 + 0x107c 0x2f 0x0 + 0x10c0 0x09 0x0 + 0x10c4 0x09 0x0 + 0x10c8 0x1a 0x0 + 0x10d0 0x01 0x0 + 0x10d4 0x07 0x0 + 0x10d8 0x31 0x0 + 0x10dc 0x31 0x0 + 0x10e0 0x03 0x0 + 0x10fc 0x02 0x0 + 0x1100 0x01 0x0 + 0x1108 0x12 0x0 + 0x1114 0x25 0x0 + 0x1118 0x00 0x0 + 0x111c 0x05 0x0 + 0x1120 0x01 0x0 + 0x1124 0x26 0x0 + 0x1128 0x12 0x0 + 0x1130 0x04 0x0 + 0x1134 0x04 0x0 + 0x1138 0x09 0x0 + 0x1154 0x15 0x0 + 0x1160 0x32 0x0 + 0x1168 0x7f 0x0 + 0x116c 0x07 0x0 + 0x1178 0x04 0x0 + 0x1180 0x70 0x0 + 0x1184 0x8b 0x0 + 0x1188 0x08 0x0 + 0x118c 0x09 0x0 + 0x1190 0x03 0x0 + 0x1194 0x04 0x0 + 0x1198 0x02 0x0 + 0x119c 0x0c 0x0 + 0x11a4 0x02 0x0 + 0x11c0 0x5c 0x0 + 0x11c4 0x3e 0x0 + 0x11c8 0x3f 0x0 + 0x1230 0x01 0x0 + 0x1234 0xa0 0x0 + 0x1238 0x08 0x0 + 0x12a4 0x01 0x0 + 0x12ac 0xc3 0x0 + 0x12b0 0x00 0x0 + 0x12b8 0x8c 0x0 + 0x12c0 0x7f 0x0 + 0x12c4 0x2a 0x0 + 0x1010 0x0c 0x0 + 0x1014 0x0f 0x0 + 0x12cc 0x04 0x0 + 0x113c 0x20 0x0 + 0x195c 0x3f 0x0 + 0x1974 0x50 0x0 + 0x196c 0x9f 0x0 + 0x182c 0x19 0x0 + 0x1840 0x07 0x0 + 0x1854 0x17 0x0 + 0x1868 0x09 0x0 + 0x1800 0x00 0x0 + 0x0aa8 0x01 0x0 + 0x12a8 0x01 0x0 + 0x1808 0x01 0x0>; +}; + +&cpu0_cpu_l3_latmon { + qcom,core-dev-table = + < 300000 300000000 >, + < 480000 403200000 >, + < 652800 480000000 >, + < 748800 576000000 >, + < 902400 652800000 >, + < 979200 748800000 >, + < 1132800 844800000 >, + < 1228800 940800000 >, + < 1324800 1036800000 >, + < 1420800 1132800000 >, + < 1516800 1209600000 >, + < 1689600 1305600000 >, + < 1766400 1401600000 >; +}; + +&cpu4_cpu_l3_latmon { + qcom,core-dev-table = + < 300000 300000000 >, + < 825600 576000000 >, + < 1132800 748800000 >, + < 1363200 940800000 >, + < 1689600 1209600000 >, + < 1996800 1305600000 >, + < 2400000 1401600000 >, + < 2745600 1593600000 >; +}; + +&cpu_cpu_llcc_bwmon { + qcom,count-unit = <0x10000>; +}; + +&cpu_cpu_llcc_bw { + operating-points-v2 = <&llcc_bw_opp_table_v2>; +}; + +&cpu4_computemon { + qcom,core-dev-table = + < 1881600 MHZ_TO_MBPS( 200, 4) >, + < 2649600 MHZ_TO_MBPS(1017, 4) >, + < 2745600 MHZ_TO_MBPS(1804, 4) >; +}; + +&clock_gcc { + compatible = "qcom,gcc-sdm845-v2", "syscon"; +}; + +&clock_camcc { + compatible = "qcom,cam_cc-sdm845-v2", "syscon"; + qcom,cam_cc_csi3phytimer_clk_src-opp-handle = <&cam_csiphy3>; +}; + +&clock_dispcc { + compatible = "qcom,dispcc-sdm845-v2", "syscon"; +}; + +&clock_gpucc { + compatible = "qcom,gpucc-sdm845-v2", "syscon"; +}; + +&clock_gfx { + compatible = "qcom,gfxcc-sdm845-v2"; +}; + +&clock_videocc { + compatible = "qcom,video_cc-sdm845-v2", "syscon"; +}; + +&msm_vidc { + qcom,allowed-clock-rates = <100000000 200000000 330000000 + 404000000 444000000 533000000>; +}; + +&refgen { + status = "ok"; +}; + +&spss_utils { + qcom,spss-dev-firmware-name = "spss2d"; /* 8 chars max */ + qcom,spss-test-firmware-name = "spss2t"; /* 8 chars max */ + qcom,spss-prod-firmware-name = "spss2p"; /* 8 chars max */ +}; + +&mdss_mdp { + clock-max-rate = <0 0 0 0 430000000 19200000 430000000 430000000>; + qcom,sde-min-core-ib-kbps = <4800000>; + qcom,sde-max-bw-low-kbps = <9600000>; + qcom,sde-max-bw-high-kbps = <9600000>; +}; + +&mdss_dsi0 { + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; +}; + +&mdss_dsi1 { + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; +}; + +&sde_dp { + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; +}; + +&gpu_gx_gdsc { + domain-addr = <&gpu_gx_domain_addr>; + sw-reset = <&gpu_gx_sw_reset>; + qcom,reset-aon-logic; +}; + +/* GPU overrides */ +&msm_gpu { + /* Updated chip ID */ + qcom,chipid = <0x06030001>; + qcom,initial-pwrlevel = <6>; + + qcom,gpu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gpu-pwrlevels"; + + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <710000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <12>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <675000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <10>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <596000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <520000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <11>; + }; + + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <414000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <342000000>; + qcom,bus-freq = <6>; + qcom,bus-min = <5>; + qcom,bus-max = <7>; + }; + + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <257000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <0>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; +}; + +&gmu { + qcom,gmu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gmu-pwrlevels"; + + /* GMU power levels must go from lowest to highest */ + qcom,gmu-pwrlevel@0 { + reg = <0>; + qcom,gmu-freq = <0>; + }; + + qcom,gmu-pwrlevel@1 { + reg = <1>; + qcom,gmu-freq = <200000000>; + }; + + qcom,gmu-pwrlevel@2 { + reg = <2>; + qcom,gmu-freq = <500000000>; + }; + }; +}; + +&qusb_phy0 { + qcom,qusb-phy-init-seq = + /* */ + <0x23 0x210 /* PWR_CTRL1 */ + 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */ + 0x7c 0x18c /* PLL_CLOCK_INVERTERS */ + 0x80 0x2c /* PLL_CMODE */ + 0x0a 0x184 /* PLL_LOCK_DELAY */ + 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */ + 0x40 0x194 /* PLL_BIAS_CONTROL_1 */ + 0x20 0x198 /* PLL_BIAS_CONTROL_2 */ + 0x21 0x214 /* PWR_CTRL2 */ + 0x08 0x220 /* IMP_CTRL1 */ + 0x58 0x224 /* IMP_CTRL2 */ + 0x45 0x240 /* TUNE1 */ + 0x29 0x244 /* TUNE2 */ + 0xca 0x248 /* TUNE3 */ + 0x04 0x24c /* TUNE4 */ + 0x03 0x250 /* TUNE5 */ + 0x00 0x23c /* CHG_CTRL2 */ + 0x22 0x210>; /* PWR_CTRL1 */ +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi new file mode 100644 index 000000000000..c39c677574b1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +&soc { + msm_vidc: qcom,vidc@aa00000 { + compatible = "qcom,msm-vidc", "qcom,sdm845-vidc"; + status = "ok"; + reg = <0xaa00000 0x200000>; + interrupts = ; + + /* LLCC Info */ + cache-slice-names = "vidsc0", "vidsc1"; + + /* Supply */ + venus-supply = <&venus_gdsc>; + venus-core0-supply = <&vcodec0_gdsc>; + venus-core1-supply = <&vcodec1_gdsc>; + + /* Clocks */ + clock-names = "core_clk", "iface_clk", "bus_clk", + "core0_clk", "core0_bus_clk", + "core1_clk", "core1_bus_clk"; + clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&clock_videocc VIDEO_CC_VENUS_AHB_CLK>, + <&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, + <&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>, + <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>, + <&clock_videocc VIDEO_CC_VCODEC1_AXI_CLK>; + qcom,proxy-clock-names = "core_clk", "iface_clk", + "bus_clk", "core0_clk", "core0_bus_clk", + "core1_clk", "core1_bus_clk"; + qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x1 0x0>; + qcom,allowed-clock-rates = <100000000 200000000 320000000 + 380000000 444000000 533000000>; + + /* Buses */ + bus_cnoc { + compatible = "qcom,msm-vidc,bus"; + label = "cnoc"; + qcom,bus-master = ; + qcom,bus-slave = ; + qcom,mode = "performance"; + qcom,bus-range-kbps = <1000 1000>; + }; + + venus_bus_ddr { + compatible = "qcom,msm-vidc,bus"; + label = "venus-ar50-ddr"; + qcom,bus-master = ; + qcom,bus-slave = ; + qcom,mode = "venus-ar50-ddr"; + qcom,bus-range-kbps = <1000 3388000>; + }; + arm9_bus_ddr { + compatible = "qcom,msm-vidc,bus"; + label = "venus-arm9-ddr"; + qcom,bus-master = ; + qcom,bus-slave = ; + qcom,mode = "performance"; + qcom,bus-range-kbps = <1000 1000>; + }; + venus_bus_llcc { + compatible = "qcom,msm-vidc,bus"; + label = "venus-ar50-llcc"; + qcom,bus-master = ; + qcom,bus-slave = ; + qcom,mode = "venus-ar50-llcc"; + qcom,bus-range-kbps = <17000 3388000>; + }; + + /* MMUs */ + non_secure_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_ns"; + iommus = + <&apps_smmu 0x10a0 0x8>, + <&apps_smmu 0x10b0 0x0>; + qcom,iommu-dma-addr-pool = <0x70800000 0x6f800000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-pagetable = "LLC"; + buffer-types = <0xfff>; + virtual-addr-pool = <0x70800000 0x6f800000>; + }; + + secure_bitstream_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_bitstream"; + iommus = + <&apps_smmu 0x10a1 0x8>, + <&apps_smmu 0x10a5 0x8>; + qcom,iommu-dma-addr-pool = <0x4b000000 0x25800000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-pagetable = "LLC"; + qcom,iommu-vmid = <0x9>; /* VMID_CP_BITSTREAM */ + buffer-types = <0x241>; + virtual-addr-pool = <0x4b000000 0x25800000>; + qcom,secure-context-bank; + }; + + secure_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_pixel"; + iommus = + <&apps_smmu 0x10a3 0x8>; + qcom,iommu-dma-addr-pool = <0x25800000 0x25800000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-pagetable = "LLC"; + qcom,iommu-vmid = <0xA>; /* VMID_CP_PIXEL */ + buffer-types = <0x106>; + virtual-addr-pool = <0x25800000 0x25800000>; + qcom,secure-context-bank; + }; + + secure_non_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_non_pixel"; + iommus = + <&apps_smmu 0x10a4 0x8>, + <&apps_smmu 0x10b4 0x0>; + qcom,iommu-dma-addr-pool = <0x1000000 0x24800000>; + qcom,iommu-faults = "non-fatal"; + qcom,iommu-pagetable = "LLC"; + qcom,iommu-vmid = <0xB>; /* VMID_CP_NON_PIXEL */ + buffer-types = <0x480>; + virtual-addr-pool = <0x1000000 0x24800000>; + qcom,secure-context-bank; + }; + + /* Memory Heaps */ + qcom,msm-vidc,mem_cdsp { + compatible = "qcom,msm-vidc,mem-cdsp"; + memory-region = <&cdsp_mem>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-wcd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-wcd.dtsi new file mode 100644 index 000000000000..d42493becd02 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-wcd.dtsi @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +&slim_aud { + tavil_codec { + wcd: wcd_pinctrl@5 { + compatible = "qcom,wcd-pinctrl"; + qcom,num-gpios = <5>; + gpio-controller; + #gpio-cells = <2>; + + us_euro_sw_wcd_active: us_euro_sw_wcd_active { + mux { + pins = "gpio1"; + }; + + config { + pins = "gpio1"; + output-high; + }; + }; + + us_euro_sw_wcd_sleep: us_euro_sw_wcd_sleep { + mux { + pins = "gpio1"; + }; + + config { + pins = "gpio1"; + output-low; + }; + }; + + spkr_1_wcd_en_active: spkr_1_wcd_en_active { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + output-high; + }; + }; + + spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep { + mux { + pins = "gpio2"; + }; + + config { + pins = "gpio2"; + input-enable; + }; + }; + + spkr_2_wcd_en_active: spkr_2_sd_n_active { + mux { + pins = "gpio3"; + }; + + config { + pins = "gpio3"; + output-high; + }; + }; + + spkr_2_wcd_en_sleep: spkr_2_sd_n_sleep { + mux { + pins = "gpio3"; + }; + + config { + pins = "gpio3"; + input-enable; + }; + }; + + hph_en0_wcd_active: hph_en0_wcd_active { + mux { + pins = "gpio4"; + }; + + config { + pins = "gpio4"; + output-high; + }; + }; + + hph_en0_wcd_sleep: hph_en0_wcd_sleep { + mux { + pins = "gpio4"; + }; + + config { + pins = "gpio4"; + output-low; + }; + }; + + hph_en1_wcd_active: hph_en1_wcd_active { + mux { + pins = "gpio5"; + }; + + config { + pins = "gpio5"; + output-high; + }; + }; + + hph_en1_wcd_sleep: hph_en1_wcd_sleep { + mux { + pins = "gpio5"; + }; + + config { + pins = "gpio5"; + output-low; + }; + }; + }; + + wsa_spkr_wcd_sd1: msm_cdc_pinctrll { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_1_wcd_en_active>; + pinctrl-1 = <&spkr_1_wcd_en_sleep>; + }; + + wsa_spkr_wcd_sd2: msm_cdc_pinctrlr { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&spkr_2_wcd_en_active>; + pinctrl-1 = <&spkr_2_wcd_en_sleep>; + }; + + tavil_us_euro_sw: msm_cdc_pinctrl_us_euro_sw { + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&us_euro_sw_wcd_active>; + pinctrl-1 = <&us_euro_sw_wcd_sleep>; + }; + + tavil_hph_en0: msm_cdc_pinctrl_hph_en0 { + #gpio-cells = <0>; + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&hph_en0_wcd_active>; + pinctrl-1 = <&hph_en0_wcd_sleep>; + }; + + tavil_hph_en1: msm_cdc_pinctrl_hph_en1 { + #gpio-cells = <0>; + compatible = "qcom,msm-cdc-pinctrl"; + pinctrl-names = "aud_active", "aud_sleep"; + pinctrl-0 = <&hph_en1_wcd_active>; + pinctrl-1 = <&hph_en1_wcd_sleep>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845.dts b/arch/arm64/boot/dts/qcom/sdm845.dts new file mode 100644 index 000000000000..1ec7247fcf4c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +/dts-v1/; + +#include "sdm845.dtsi" +#include "sdm845_enchilada_soc.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM845 v1 SoC"; + compatible = "qcom,sdm845"; + qcom,board-id = <0 0 0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index 0c9a2aa6a1b5..91e6eb4fec3c 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -1,89 +1,47 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* - * SDM845 SoC device tree source - * - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ +#include "skeleton64.dtsi" #include +#include +#include +#include +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include + +#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024)) +#define BW_OPP_ENTRY(mhz, w) opp-mhz {opp-hz = /bits/ 64 ;} / { + model = "Qualcomm Technologies, Inc. SDM845"; + compatible = "qcom,sdm845"; + qcom,msm-id = <321 0x10000>; interrupt-parent = <&intc>; - #address-cells = <2>; - #size-cells = <2>; - aliases { - i2c0 = &i2c0; - i2c1 = &i2c1; - i2c2 = &i2c2; - i2c3 = &i2c3; - i2c4 = &i2c4; - i2c5 = &i2c5; - i2c6 = &i2c6; - i2c7 = &i2c7; - i2c8 = &i2c8; - i2c9 = &i2c9; - i2c10 = &i2c10; - i2c11 = &i2c11; - i2c12 = &i2c12; - i2c13 = &i2c13; - i2c14 = &i2c14; - i2c15 = &i2c15; - spi0 = &spi0; - spi1 = &spi1; - spi2 = &spi2; - spi3 = &spi3; - spi4 = &spi4; - spi5 = &spi5; - spi6 = &spi6; - spi7 = &spi7; - spi8 = &spi8; - spi9 = &spi9; - spi10 = &spi10; - spi11 = &spi11; - spi12 = &spi12; - spi13 = &spi13; - spi14 = &spi14; - spi15 = &spi15; - }; - - chosen { }; - - memory@80000000 { - device_type = "memory"; - /* We expect the bootloader to fill in the size */ - reg = <0 0x80000000 0 0>; + ufshc1 = &ufshc_mem; /* Embedded UFS slot */ + pci-domain0 = &pcie0; + pci-domain1 = &pcie1; + sdhc2 = &sdhc_2; /* SDC2 SD card slot */ }; - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - memory@85fc0000 { - reg = <0 0x85fc0000 0 0x20000>; - no-map; - }; - - memory@85fe0000 { - compatible = "qcom,cmd-db"; - reg = <0x0 0x85fe0000 0x0 0x20000>; - no-map; - }; - - smem_mem: memory@86000000 { - reg = <0x0 0x86000000 0x0 0x200000>; - no-map; - }; - - memory@86200000 { - reg = <0 0x86200000 0 0x2d00000>; - no-map; - }; + aliases { + serial0 = &qupv3_se9_2uart; + spi0 = &qupv3_se8_spi; + i2c0 = &qupv3_se10_i2c; + i2c1 = &qupv3_se3_i2c; + hsuart0 = &qupv3_se6_4uart; }; cpus { @@ -92,142 +50,303 @@ CPU0: cpu@0 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x0>; enable-method = "psci"; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + qcom,freq-domain = <&cpufreq_hw 0 4>; + cache-size = <0x8000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs0>; + #cooling-cells = <2>; next-level-cache = <&L2_0>; L2_0: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x20000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + L3_0: l3-cache { - compatible = "cache"; + compatible = "arm,arch-cache"; + cache-size = <0x200000>; + cache-level = <3>; }; }; + L1_I_0: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x12000>; + }; + L1_D_0: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + L1_TLB_0: l1-tlb { + qcom,dump-size = <0x6000>; + }; }; CPU1: cpu@100 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x100>; enable-method = "psci"; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + qcom,freq-domain = <&cpufreq_hw 0 4>; + cache-size = <0x8000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs0>; + #cooling-cells = <2>; next-level-cache = <&L2_100>; L2_100: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x20000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_100: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x12000>; + }; + L1_D_100: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + L1_TLB_100: l1-tlb { + qcom,dump-size = <0x6000>; }; }; CPU2: cpu@200 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x200>; enable-method = "psci"; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + qcom,freq-domain = <&cpufreq_hw 0 4>; + cache-size = <0x8000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs0>; + #cooling-cells = <2>; next-level-cache = <&L2_200>; L2_200: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x20000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_200: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x12000>; + }; + L1_D_200: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + L1_TLB_200: l1-tlb { + qcom,dump-size = <0x6000>; }; }; CPU3: cpu@300 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x300>; enable-method = "psci"; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + qcom,freq-domain = <&cpufreq_hw 0 4>; + cache-size = <0x8000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs0>; + #cooling-cells = <2>; next-level-cache = <&L2_300>; L2_300: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x20000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_300: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x12000>; + }; + L1_D_300: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + L1_TLB_300: l1-tlb { + qcom,dump-size = <0x6000>; }; }; CPU4: cpu@400 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x400>; enable-method = "psci"; + capacity-dmips-mhz = <1740>; + dynamic-power-coefficient = <396>; + qcom,freq-domain = <&cpufreq_hw 1 4>; + cache-size = <0x20000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs1>; + #cooling-cells = <2>; next-level-cache = <&L2_400>; L2_400: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x40000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_400: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x24000>; + }; + L1_D_400: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + L1_TLB_400: l1-tlb { + qcom,dump-size = <0x6800>; }; }; CPU5: cpu@500 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x500>; enable-method = "psci"; + capacity-dmips-mhz = <1740>; + dynamic-power-coefficient = <396>; + qcom,freq-domain = <&cpufreq_hw 1 4>; + cache-size = <0x20000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs1>; + #cooling-cells = <2>; next-level-cache = <&L2_500>; L2_500: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x40000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_500: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x24000>; + }; + L1_D_500: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + L1_TLB_500: l1-tlb { + qcom,dump-size = <0x6800>; }; }; CPU6: cpu@600 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x600>; enable-method = "psci"; + capacity-dmips-mhz = <1740>; + dynamic-power-coefficient = <396>; + qcom,freq-domain = <&cpufreq_hw 1 4>; + cache-size = <0x20000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs1>; + #cooling-cells = <2>; next-level-cache = <&L2_600>; L2_600: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x40000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_600: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x24000>; + }; + L1_D_600: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + L1_TLB_600: l1-tlb { + qcom,dump-size = <0x6800>; }; }; CPU7: cpu@700 { device_type = "cpu"; - compatible = "qcom,kryo385"; + compatible = "arm,armv8"; reg = <0x0 0x700>; enable-method = "psci"; + capacity-dmips-mhz = <1740>; + dynamic-power-coefficient = <396>; + qcom,freq-domain = <&cpufreq_hw 1 4>; + cache-size = <0x20000>; + cpu-release-addr = <0x0 0x90000000>; + qcom,lmh-dcvs = <&lmh_dcvs1>; + #cooling-cells = <2>; next-level-cache = <&L2_700>; L2_700: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; + compatible = "arm,arch-cache"; + cache-size = <0x40000>; + cache-level = <2>; + next-level-cache = <&L3_0>; + }; + L1_I_700: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x24000>; + }; + L1_D_700: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + L1_TLB_700: l1-tlb { + qcom,dump-size = <0x6800>; }; }; - }; - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = ; - }; + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; - timer { - compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; - }; + core1 { + cpu = <&CPU1>; + }; - clocks { - xo_board: xo-board { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <38400000>; - clock-output-names = "xo_board"; - }; + core2 { + cpu = <&CPU2>; + }; - sleep_clk: sleep-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <32764>; - }; - }; + core3 { + cpu = <&CPU3>; + }; + }; - tcsr_mutex: hwlock { - compatible = "qcom,tcsr-mutex"; - syscon = <&tcsr_mutex_regs 0 0x1000>; - #hwlock-cells = <1>; - }; + cluster1 { + core0 { + cpu = <&CPU4>; + }; - smem { - compatible = "qcom,smem"; - memory-region = <&smem_mem>; - hwlocks = <&tcsr_mutex 3>; + core1 { + cpu = <&CPU5>; + }; + + core2 { + cpu = <&CPU6>; + }; + + core3 { + cpu = <&CPU7>; + }; + }; + }; }; psci { @@ -235,872 +354,5195 @@ method = "smc"; }; - soc: soc { + chosen { + bootargs = "rcupdate.rcu_expedited=1 rcu_nocbs=0-7 cgroup.memory=nokmem,nosocket kpti=0 ssbd=force-off"; + }; + + soc: soc { }; + + vendor: vendor { #address-cells = <1>; #size-cells = <1>; ranges = <0 0 0 0xffffffff>; compatible = "simple-bus"; + }; - gcc: clock-controller@100000 { - compatible = "qcom,gcc-sdm845"; - reg = <0x100000 0x1f0000>; - #clock-cells = <1>; - #reset-cells = <1>; - #power-domain-cells = <1>; - }; - - qupv3_id_0: geniqup@8c0000 { - compatible = "qcom,geni-se-qup"; - reg = <0x8c0000 0x6000>; - clock-names = "m-ahb", "s-ahb"; - clocks = <&gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, - <&gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; - #address-cells = <1>; - #size-cells = <1>; - ranges; - status = "disabled"; - - i2c0: i2c@880000 { - compatible = "qcom,geni-i2c"; - reg = <0x880000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c0_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi0: spi@880000 { - compatible = "qcom,geni-spi"; - reg = <0x880000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi0_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c1: i2c@884000 { - compatible = "qcom,geni-i2c"; - reg = <0x884000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c1_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi1: spi@884000 { - compatible = "qcom,geni-spi"; - reg = <0x884000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi1_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c2: i2c@888000 { - compatible = "qcom,geni-i2c"; - reg = <0x888000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c2_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi2: spi@888000 { - compatible = "qcom,geni-spi"; - reg = <0x888000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi2_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c3: i2c@88c000 { - compatible = "qcom,geni-i2c"; - reg = <0x88c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c3_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi3: spi@88c000 { - compatible = "qcom,geni-spi"; - reg = <0x88c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi3_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c4: i2c@890000 { - compatible = "qcom,geni-i2c"; - reg = <0x890000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c4_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi4: spi@890000 { - compatible = "qcom,geni-spi"; - reg = <0x890000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi4_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c5: i2c@894000 { - compatible = "qcom,geni-i2c"; - reg = <0x894000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c5_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi5: spi@894000 { - compatible = "qcom,geni-spi"; - reg = <0x894000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi5_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c6: i2c@898000 { - compatible = "qcom,geni-i2c"; - reg = <0x898000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c6_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi6: spi@898000 { - compatible = "qcom,geni-spi"; - reg = <0x898000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi6_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c7: i2c@89c000 { - compatible = "qcom,geni-i2c"; - reg = <0x89c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c7_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi7: spi@89c000 { - compatible = "qcom,geni-spi"; - reg = <0x89c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi7_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - }; - - qupv3_id_1: geniqup@ac0000 { - compatible = "qcom,geni-se-qup"; - reg = <0xac0000 0x6000>; - clock-names = "m-ahb", "s-ahb"; - clocks = <&gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, - <&gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; - #address-cells = <1>; - #size-cells = <1>; - ranges; - status = "disabled"; - - i2c8: i2c@a80000 { - compatible = "qcom,geni-i2c"; - reg = <0xa80000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c8_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi8: spi@a80000 { - compatible = "qcom,geni-spi"; - reg = <0xa80000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi8_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c9: i2c@a84000 { - compatible = "qcom,geni-i2c"; - reg = <0xa84000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c9_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi9: spi@a84000 { - compatible = "qcom,geni-spi"; - reg = <0xa84000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi9_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - uart9: serial@a84000 { - compatible = "qcom,geni-debug-uart"; - reg = <0xa84000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_uart9_default>; - interrupts = ; - status = "disabled"; - }; - - i2c10: i2c@a88000 { - compatible = "qcom,geni-i2c"; - reg = <0xa88000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c10_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi10: spi@a88000 { - compatible = "qcom,geni-spi"; - reg = <0xa88000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi10_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c11: i2c@a8c000 { - compatible = "qcom,geni-i2c"; - reg = <0xa8c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c11_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi11: spi@a8c000 { - compatible = "qcom,geni-spi"; - reg = <0xa8c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi11_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c12: i2c@a90000 { - compatible = "qcom,geni-i2c"; - reg = <0xa90000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c12_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi12: spi@a90000 { - compatible = "qcom,geni-spi"; - reg = <0xa90000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi12_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c13: i2c@a94000 { - compatible = "qcom,geni-i2c"; - reg = <0xa94000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c13_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi13: spi@a94000 { - compatible = "qcom,geni-spi"; - reg = <0xa94000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi13_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c14: i2c@a98000 { - compatible = "qcom,geni-i2c"; - reg = <0xa98000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c14_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - spi14: spi@a98000 { - compatible = "qcom,geni-spi"; - reg = <0xa98000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi14_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c15: i2c@a9c000 { - compatible = "qcom,geni-i2c"; - reg = <0xa9c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_i2c15_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + firmware: firmware { + android { + compatible = "android,firmware"; + boot_devices = "soc/1d84000.ufshc"; + vbmeta { + compatible = "android,vbmeta"; + parts = "vbmeta,boot,system,vendor,dtbo"; }; - spi15: spi@a9c000 { - compatible = "qcom,geni-spi"; - reg = <0xa9c000 0x4000>; - clock-names = "se"; - clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>; - pinctrl-names = "default"; - pinctrl-0 = <&qup_spi15_default>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/1d84000.ufshc/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,slotselect,avb"; + status = "disabled"; + }; }; }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; - tcsr_mutex_regs: syscon@1f40000 { - compatible = "syscon"; - reg = <0x1f40000 0x40000>; + hyp_region: hyp_region@85700000 { + no-map; + reg = <0 0x85700000 0 0x600000>; }; - tlmm: pinctrl@3400000 { - compatible = "qcom,sdm845-pinctrl"; - reg = <0x03400000 0xc00000>; - interrupts = ; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; + xbl_region: xbl_region@85e00000 { + no-map; + reg = <0 0x85e00000 0 0x100000>; + }; - qup_i2c0_default: qup-i2c0-default { - pinmux { - pins = "gpio0", "gpio1"; - function = "qup0"; - }; - }; + removed_region: removed_region@85fc0000 { + no-map; + reg = <0 0x85fc0000 0 0x20000>; + }; - qup_i2c1_default: qup-i2c1-default { - pinmux { - pins = "gpio17", "gpio18"; - function = "qup1"; - }; - }; + cmd_db: reserved-memory@85fe0000 { + compatible = "qcom,cmd-db"; + reg = <0 0x85fe0000 0 0x20000>; + no-map; + }; - qup_i2c2_default: qup-i2c2-default { - pinmux { - pins = "gpio27", "gpio28"; - function = "qup2"; - }; - }; + smem_mem: smem_region@86000000 { + no-map; + reg = <0 0x86000000 0 0x200000>; + }; - qup_i2c3_default: qup-i2c3-default { - pinmux { - pins = "gpio41", "gpio42"; - function = "qup3"; - }; - }; + removed_region1: removed_region@86200000 { + no-map; + reg = <0 0x86200000 0 0x4900000>; /* enlarge TA memory size from 34M to 62M on 2018/09/03 */ + }; - qup_i2c4_default: qup-i2c4-default { - pinmux { - pins = "gpio89", "gpio90"; - function = "qup4"; - }; - }; + qseecom_mem: qseecom_region@0x8ab00000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0 0x8ab00000 0 0x1400000>; + }; - qup_i2c5_default: qup-i2c5-default { - pinmux { - pins = "gpio85", "gpio86"; - function = "qup5"; - }; - }; + pil_camera_mem: camera_region@0x8bf00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8bf00000 0 0x500000>; + }; - qup_i2c6_default: qup-i2c6-default { - pinmux { - pins = "gpio45", "gpio46"; - function = "qup6"; - }; - }; + pil_ipa_fw_mem: ips_fw_region@0x8c400000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8c400000 0 0x10000>; + }; - qup_i2c7_default: qup-i2c7-default { - pinmux { - pins = "gpio93", "gpio94"; - function = "qup7"; - }; - }; + pil_ipa_gsi_mem: ipa_gsi_region@0x8c410000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8c410000 0 0x5000>; + }; - qup_i2c8_default: qup-i2c8-default { - pinmux { - pins = "gpio65", "gpio66"; - function = "qup8"; - }; - }; + pil_gpu_mem: gpu_region@0x8c415000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8c415000 0 0x2000>; + }; - qup_i2c9_default: qup-i2c9-default { - pinmux { - pins = "gpio6", "gpio7"; - function = "qup9"; - }; - }; + pil_adsp_mem: adsp_region@0x8c500000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8c500000 0 0x1a00000>; + }; - qup_i2c10_default: qup-i2c10-default { - pinmux { - pins = "gpio55", "gpio56"; - function = "qup10"; - }; - }; + wlan_fw_region: wlan_fw_region@0x8df00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8df00000 0 0x100000>; + }; - qup_i2c11_default: qup-i2c11-default { - pinmux { - pins = "gpio31", "gpio32"; - function = "qup11"; - }; - }; + pil_modem_mem: modem_region@0x8e000000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x8e000000 0 0x7800000>; + }; - qup_i2c12_default: qup-i2c12-default { - pinmux { - pins = "gpio49", "gpio50"; - function = "qup12"; - }; - }; + pil_video_mem: video_region@0x95800000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x95800000 0 0x500000>; + }; - qup_i2c13_default: qup-i2c13-default { - pinmux { - pins = "gpio105", "gpio106"; - function = "qup13"; - }; - }; + pil_cdsp_mem: cdsp_region@0x95d00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x95d00000 0 0x900000>; + }; - qup_i2c14_default: qup-i2c14-default { - pinmux { - pins = "gpio33", "gpio34"; - function = "qup14"; - }; - }; + pil_mba_mem: mba_region@0x96600000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x96600000 0 0x200000>; + }; - qup_i2c15_default: qup-i2c15-default { - pinmux { - pins = "gpio81", "gpio82"; - function = "qup15"; - }; - }; + pil_slpi_mem: slpi_region@0x96800000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x96800000 0 0x1400000>; + }; - qup_spi0_default: qup-spi0-default { - pinmux { - pins = "gpio0", "gpio1", - "gpio2", "gpio3"; - function = "qup0"; - }; - }; + pil_spss_mem: pil_spss_region@0x97c00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x97c00000 0 0x100000>; + }; - qup_spi1_default: qup-spi1-default { - pinmux { - pins = "gpio17", "gpio18", - "gpio19", "gpio20"; - function = "qup1"; - }; - }; + adsp_mem: adsp_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x1000000>; + }; - qup_spi2_default: qup-spi2-default { - pinmux { - pins = "gpio27", "gpio28", - "gpio29", "gpio30"; - function = "qup2"; - }; - }; + cdsp_mem: cdsp_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x400000>; + }; - qup_spi3_default: qup-spi3-default { - pinmux { - pins = "gpio41", "gpio42", - "gpio43", "gpio44"; - function = "qup3"; - }; - }; + qseecom_ta_mem: qseecom_ta_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x1000000>; + }; - qup_spi4_default: qup-spi4-default { - pinmux { - pins = "gpio89", "gpio90", - "gpio91", "gpio92"; - function = "qup4"; - }; - }; + secure_sp_mem: secure_sp_region { /* SPSS-HLOS ION shared mem */ + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; /* 32-bit */ + reusable; + alignment = <0 0x400000>; + size = <0 0x800000>; + }; - qup_spi5_default: qup-spi5-default { - pinmux { - pins = "gpio85", "gpio86", - "gpio87", "gpio88"; - function = "qup5"; - }; - }; + cont_splash_memory: cont_splash_region@9d400000 { + reg = <0x0 0x9d400000 0x0 0x02400000>; + label = "cont_splash_region"; + }; - qup_spi6_default: qup-spi6-default { - pinmux { - pins = "gpio45", "gpio46", - "gpio47", "gpio48"; - function = "qup6"; - }; - }; + secure_display_memory: secure_display_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x5c00000>; + }; - qup_spi7_default: qup-spi7-default { - pinmux { - pins = "gpio93", "gpio94", - "gpio95", "gpio96"; - function = "qup7"; - }; - }; + dump_mem: mem_dump_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + size = <0 0x2400000>; + }; - qup_spi8_default: qup-spi8-default { - pinmux { - pins = "gpio65", "gpio66", - "gpio67", "gpio68"; - function = "qup8"; - }; - }; + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x2000000>; + linux,cma-default; + }; + }; - qup_spi9_default: qup-spi9-default { - pinmux { - pins = "gpio6", "gpio7", - "gpio4", "gpio5"; - function = "qup9"; - }; - }; + clocks { + xo_board: xo-board { + compatible = "fixed-clock"; + clock-frequency = <38400000>; + clock-output-names = "xo_board"; + #clock-cells = <0>; + }; - qup_spi10_default: qup-spi10-default { - pinmux { - pins = "gpio55", "gpio56", - "gpio53", "gpio54"; - function = "qup10"; - }; - }; + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + clock-frequency = <32764>; + clock-output-names = "sleep_clk"; + #clock-cells = <0>; + }; + }; +}; - qup_spi11_default: qup-spi11-default { - pinmux { - pins = "gpio31", "gpio32", - "gpio33", "gpio34"; - function = "qup11"; - }; - }; +#include "msm-gdsc-sdm845.dtsi" +#include "sdm845-sde-pll.dtsi" +#include "msm-rdbg.dtsi" +#include "sdm845-sde.dtsi" +#include "sdm845-qupv3.dtsi" - qup_spi12_default: qup-spi12-default { - pinmux { - pins = "gpio49", "gpio50", - "gpio51", "gpio52"; - function = "qup12"; - }; - }; +&soc { + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0 0xffffffff>; + compatible = "simple-bus"; - qup_spi13_default: qup-spi13-default { - pinmux { - pins = "gpio105", "gpio106", - "gpio107", "gpio108"; - function = "qup13"; - }; - }; + jtag_mm0: jtagmm@7040000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7040000 0x1000>; + reg-names = "etm-base"; - qup_spi14_default: qup-spi14-default { - pinmux { - pins = "gpio33", "gpio34", - "gpio31", "gpio32"; - function = "qup14"; - }; - }; + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; - qup_spi15_default: qup-spi15-default { - pinmux { - pins = "gpio81", "gpio82", - "gpio83", "gpio84"; - function = "qup15"; - }; - }; + qcom,coresight-jtagmm-cpu = <&CPU0>; + status = "disabled"; + }; - qup_uart9_default: qup-uart9-default { - pinmux { - pins = "gpio4", "gpio5"; - function = "qup9"; - }; - }; - }; + jtag_mm1: jtagmm@7140000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7140000 0x1000>; + reg-names = "etm-base"; - tsens0: thermal-sensor@c263000 { - compatible = "qcom,sdm845-tsens", "qcom,tsens-v2"; - reg = <0xc263000 0x1ff>, /* TM */ - <0xc222000 0x1ff>; /* SROT */ - #qcom,sensors = <13>; - #thermal-sensor-cells = <1>; - }; + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; - tsens1: thermal-sensor@c265000 { - compatible = "qcom,sdm845-tsens", "qcom,tsens-v2"; - reg = <0xc265000 0x1ff>, /* TM */ - <0xc223000 0x1ff>; /* SROT */ - #qcom,sensors = <8>; - #thermal-sensor-cells = <1>; - }; + qcom,coresight-jtagmm-cpu = <&CPU1>; + status = "disabled"; + }; - spmi_bus: spmi@c440000 { - compatible = "qcom,spmi-pmic-arb"; - reg = <0xc440000 0x1100>, - <0xc600000 0x2000000>, - <0xe600000 0x100000>, - <0xe700000 0xa0000>, - <0xc40a000 0x26000>; - reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; - interrupt-names = "periph_irq"; - interrupts = ; - qcom,ee = <0>; - qcom,channel = <0>; - #address-cells = <2>; - #size-cells = <0>; - interrupt-controller; - #interrupt-cells = <4>; - cell-index = <0>; - }; + jtag_mm2: jtagmm@7240000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7240000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + + qcom,coresight-jtagmm-cpu = <&CPU2>; + status = "disabled"; + }; + + jtag_mm3: jtagmm@7340000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7340000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + + qcom,coresight-jtagmm-cpu = <&CPU3>; + status = "disabled"; + }; + + jtag_mm4: jtagmm@7440000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7440000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + + qcom,coresight-jtagmm-cpu = <&CPU4>; + status = "disabled"; + }; + + jtag_mm5: jtagmm@7540000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7540000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + + qcom,coresight-jtagmm-cpu = <&CPU5>; + status = "disabled"; + }; + + jtag_mm6: jtagmm@7640000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7640000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; - apss_shared: mailbox@17990000 { - compatible = "qcom,sdm845-apss-shared"; - reg = <0x17990000 0x1000>; - #mbox-cells = <1>; - }; - - apps_rsc: rsc@179c0000 { - label = "apps_rsc"; - compatible = "qcom,rpmh-rsc"; - reg = <0x179c0000 0x10000>, - <0x179d0000 0x10000>, - <0x179e0000 0x10000>; - reg-names = "drv-0", "drv-1", "drv-2"; - interrupts = , - , - ; - qcom,tcs-offset = <0xd00>; - qcom,drv-id = <2>; - qcom,tcs-config = , - , - , - ; - - rpmhcc: clock-controller { - compatible = "qcom,sdm845-rpmh-clk"; - #clock-cells = <1>; - }; - }; - - intc: interrupt-controller@17a00000 { - compatible = "arm,gic-v3"; - #address-cells = <1>; - #size-cells = <1>; - ranges; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0x17a00000 0x10000>, /* GICD */ - <0x17a60000 0x100000>; /* GICR * 8 */ - interrupts = ; - - gic-its@17a40000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <1>; - reg = <0x17a40000 0x20000>; - status = "disabled"; - }; - }; - - timer@17c90000 { - #address-cells = <1>; - #size-cells = <1>; - ranges; - compatible = "arm,armv7-timer-mem"; - reg = <0x17c90000 0x1000>; - - frame@17ca0000 { - frame-number = <0>; - interrupts = , - ; - reg = <0x17ca0000 0x1000>, - <0x17cb0000 0x1000>; - }; - - frame@17cc0000 { - frame-number = <1>; - interrupts = ; - reg = <0x17cc0000 0x1000>; - status = "disabled"; - }; - - frame@17cd0000 { - frame-number = <2>; - interrupts = ; - reg = <0x17cd0000 0x1000>; - status = "disabled"; - }; - - frame@17ce0000 { - frame-number = <3>; - interrupts = ; - reg = <0x17ce0000 0x1000>; - status = "disabled"; - }; - - frame@17cf0000 { - frame-number = <4>; - interrupts = ; - reg = <0x17cf0000 0x1000>; - status = "disabled"; - }; - - frame@17d00000 { - frame-number = <5>; - interrupts = ; - reg = <0x17d00000 0x1000>; - status = "disabled"; - }; - - frame@17d10000 { - frame-number = <6>; - interrupts = ; - reg = <0x17d10000 0x1000>; - status = "disabled"; + qcom,coresight-jtagmm-cpu = <&CPU6>; + status = "disabled"; + }; + + jtag_mm7: jtagmm@7740000 { + compatible = "qcom,jtagv8-mm"; + reg = <0x7740000 0x1000>; + reg-names = "etm-base"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + + qcom,coresight-jtagmm-cpu = <&CPU7>; + status = "disabled"; + }; + + intc: interrupt-controller@17a00000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-controller; + #redistributor-regions = <1>; + redistributor-stride = <0x0 0x20000>; + reg = <0x17a00000 0x10000>, /* GICD */ + <0x17a60000 0x100000>; /* GICR * 8 */ + interrupts = ; + interrupt-parent = <&intc>; + ignored-save-restore-irqs = <38>; + }; + + pdc: interrupt-controller@b220000 { + compatible = "qcom,sdm845-pdc"; + reg = <0xb220000 0x400>; + qcom,pdc-ranges = <0 480 94>, <94 609 15>, <119 634 7>; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupt-controller; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + clock-frequency = <19200000>; + }; + + timer@0x17C90000{ + #address-cells = <1>; + #size-cells = <1>; + ranges; + compatible = "arm,armv7-timer-mem"; + reg = <0x17C90000 0x1000>; + clock-frequency = <19200000>; + + frame@0x17CA0000 { + frame-number = <0>; + interrupts = , + ; + reg = <0x17CA0000 0x1000>, + <0x17CB0000 0x1000>; + }; + + frame@17cc0000 { + frame-number = <1>; + interrupts = ; + reg = <0x17cc0000 0x1000>; + status = "disabled"; + }; + + frame@17cd0000 { + frame-number = <2>; + interrupts = ; + reg = <0x17cd0000 0x1000>; + status = "disabled"; + }; + + frame@17ce0000 { + frame-number = <3>; + interrupts = ; + reg = <0x17ce0000 0x1000>; + status = "disabled"; + }; + + frame@17cf0000 { + frame-number = <4>; + interrupts = ; + reg = <0x17cf0000 0x1000>; + status = "disabled"; + }; + + frame@17d00000 { + frame-number = <5>; + interrupts = ; + reg = <0x17d00000 0x1000>; + status = "disabled"; + }; + + frame@17d10000 { + frame-number = <6>; + interrupts = ; + reg = <0x17d10000 0x1000>; + status = "disabled"; + }; + }; + + restart@10ac000 { + compatible = "qcom,pshold"; + reg = <0xC264000 0x4>, + <0x1fd3000 0x4>; + reg-names = "pshold-base", "tcsr-boot-misc-detect"; + }; + + aop-msg-client { + compatible = "qcom,debugfs-qmp-client"; + mboxes = <&qmp_aop 0>; + mbox-names = "aop"; + }; + + spmi_bus: qcom,spmi@c440000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0xc440000 0x1100>, + <0xc600000 0x2000000>, + <0xe600000 0x100000>, + <0xe700000 0xa0000>, + <0xc40a000 0x26000>; + reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; + interrupt-names = "periph_irq"; + interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>; + qcom,ee = <0>; + qcom,channel = <0>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <4>; + cell-index = <0>; + qcom,enable-ahb-bus-workaround; + }; + + spmi_debug_bus: qcom,spmi-debug@6b22000 { + compatible = "qcom,spmi-pmic-arb-debug"; + reg = <0x6b22000 0x60>, <0x7820A8 4>; + reg-names = "core", "fuse"; + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; + qcom,fuse-disable-bit = <12>; + #address-cells = <2>; + #size-cells = <0>; + + qcom,pm8998-debug@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + + qcom,pm8998-debug@1 { + compatible = "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + + qcom,pmi8998-debug@2 { + compatible = "qcom,spmi-pmic"; + reg = <0x2 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + + qcom,pmi8998-debug@3 { + compatible = "qcom,spmi-pmic"; + reg = <0x3 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + + qcom,pm8005-debug@4 { + compatible = "qcom,spmi-pmic"; + reg = <0x4 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + + qcom,pm8005-debug@5 { + compatible = "qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <2>; + #size-cells = <0>; + qcom,can-sleep; + }; + }; + + qcom,devfreq-l3 { + compatible = "qcom,devfreq-fw"; + reg = <0x17d41000 0x4>, <0x17d43110 0x500>, <0x17d43920 0x4>; + reg-names = "en-base", "ftbl-base", "perf-base"; + + qcom,ftbl-row-size = <32>; + + cpu0_l3: qcom,cpu0-cpu-l3-lat { + compatible = "qcom,devfreq-fw-voter"; + }; + + cpu4_l3: qcom,cpu4-cpu-l3-lat { + compatible = "qcom,devfreq-fw-voter"; + }; + + cdsp_l3: qcom,cdsp-cdsp-l3-lat { + compatible = "qcom,devfreq-fw-voter"; + }; + }; + + llcc_bw_opp_table: llcc-bw-opp-table { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 150, 16); /* 2288 MB/s */ + BW_OPP_ENTRY( 300, 16); /* 4577 MB/s */ + BW_OPP_ENTRY( 426, 16); /* 6500 MB/s */ + BW_OPP_ENTRY( 533, 16); /* 8132 MB/s */ + BW_OPP_ENTRY( 600, 16); /* 9155 MB/s */ + BW_OPP_ENTRY( 700, 16); /* 10681 MB/s */ + }; + + cpu_cpu_llcc_bw: qcom,cpu-cpu-llcc-bw { + compatible = "qcom,devbw"; + governor = "performance"; + qcom,src-dst-ports = + ; + qcom,active-only; + operating-points-v2 = <&llcc_bw_opp_table>; + }; + + cpu_cpu_llcc_bwmon: qcom,cpu-cpu-llcc-bwmon { + compatible = "qcom,bimc-bwmon4"; + reg = <0x1436400 0x300>, <0x1436300 0x200>; + reg-names = "base", "global_base"; + interrupts = ; + qcom,mport = <0>; + qcom,hw-timer-hz = <19200000>; + qcom,target-dev = <&cpu_cpu_llcc_bw>; + }; + + ddr_bw_opp_table: ddr-bw-opp-table { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 200, 4); /* 762 MB/s */ + BW_OPP_ENTRY( 300, 4); /* 1144 MB/s */ + BW_OPP_ENTRY( 451, 4); /* 1720 MB/s */ + BW_OPP_ENTRY( 547, 4); /* 2086 MB/s */ + BW_OPP_ENTRY( 681, 4); /* 2597 MB/s */ + BW_OPP_ENTRY( 768, 4); /* 2929 MB/s */ + BW_OPP_ENTRY(1017, 4); /* 3879 MB/s */ + BW_OPP_ENTRY(1296, 4); /* 4943 MB/s */ + BW_OPP_ENTRY(1555, 4); /* 5931 MB/s */ + BW_OPP_ENTRY(1804, 4); /* 6881 MB/s */ + }; + + cpu_llcc_ddr_bw: qcom,cpu-llcc-ddr-bw { + compatible = "qcom,devbw"; + governor = "performance"; + qcom,src-dst-ports = + ; + qcom,active-only; + operating-points-v2 = <&ddr_bw_opp_table>; + }; + + cpu_llcc_ddr_bwmon: qcom,cpu-llcc-ddr-bwmon { + compatible = "qcom,bimc-bwmon5"; + reg = <0x0114A000 0x1000>; + reg-names = "base"; + interrupts = ; + qcom,hw-timer-hz = <19200000>; + qcom,target-dev = <&cpu_llcc_ddr_bw>; + qcom,count-unit = <0x400000>; + qcom,byte-mid-mask = <0xe000>; + qcom,byte-mid-match = <0xe000>; + }; + + cpu0_cpu_llcc_lat: qcom,cpu0-cpu-llcc-lat { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + operating-points-v2 = <&ddr_bw_opp_table>; + }; + + cpu0_cpu_ddr_latfloor: qcom,cpu0-cpu-ddr-latfloor { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + operating-points-v2 = <&ddr_bw_opp_table>; + }; + + cpu0_memlat_cpugrp: qcom,cpu0-cpugrp { + compatible = "qcom,arm-memlat-cpugrp"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + + cpu0_cpu_l3_latmon: qcom,cpu0-cpu-l3-latmon { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + qcom,target-dev = <&cpu0_l3>; + qcom,cachemiss-ev = <0x17>; + qcom,core-dev-table = + < 300000 300000000 >, + < 748800 576000000 >, + < 979200 652800000 >, + < 1209600 806400000 >, + < 1516800 883200000 >, + < 1593600 960000000 >, + < 1708800 1305600000 >; + }; + + cpu0_cpu_llcc_latmon: qcom,cpu0-cpu-llcc-latmon { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + qcom,target-dev = <&cpu0_cpu_llcc_lat>; + qcom,cachemiss-ev = <0x2A>; + qcom,core-dev-table = + < 300000 MHZ_TO_MBPS( 200, 4) >, + < 748800 MHZ_TO_MBPS( 451, 4) >, + < 1132800 MHZ_TO_MBPS( 547, 4) >, + < 1440000 MHZ_TO_MBPS( 768, 4) >, + < 1593600 MHZ_TO_MBPS(1017, 4) >; + }; + + cpu0_computemon: qcom,cpu0-computeon { + compatible = "qcom,arm-compute-mon"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + qcom,target-dev = <&cpu0_cpu_ddr_latfloor>; + qcom,core-dev-table = + < 1708800 MHZ_TO_MBPS(200, 4) >; + }; + }; + + cpu4_cpu_llcc_lat: qcom,cpu4-cpu-llcc-lat { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + operating-points-v2 = <&ddr_bw_opp_table>; + }; + + cpu4_cpu_ddr_latfloor: qcom,cpu4-cpu-ddr-latfloor { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 512>; + qcom,active-only; + operating-points-v2 = <&ddr_bw_opp_table>; + }; + + cpu4_memlat_cpugrp: qcom,cpu4-cpugrp { + compatible = "qcom,arm-memlat-cpugrp"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + + cpu4_cpu_l3_latmon: qcom,cpu4-cpu-l3-latmon { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,target-dev = <&cpu4_l3>; + qcom,cachemiss-ev = <0x17>; + qcom,core-dev-table = + < 300000 300000000 >, + < 1036800 576000000 >, + < 1190400 806400000 >, + < 1574400 883200000 >, + < 1804800 960000000 >, + < 1958400 1305600000 >; + }; + + cpu4_cpu_llcc_latmon: qcom,cpu4-cpu-llcc-latmon { + compatible = "qcom,arm-memlat-mon"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,target-dev = <&cpu4_cpu_llcc_lat>; + qcom,cachemiss-ev = <0x2A>; + qcom,core-dev-table = + < 300000 MHZ_TO_MBPS( 200, 4) >, + < 499200 MHZ_TO_MBPS( 451, 4) >, + < 806400 MHZ_TO_MBPS( 547, 4) >, + < 1036800 MHZ_TO_MBPS( 768, 4) >, + < 1190400 MHZ_TO_MBPS(1017, 4) >, + < 1574400 MHZ_TO_MBPS(1296, 4) >, + < 1728000 MHZ_TO_MBPS(1555, 4) >, + < 1958400 MHZ_TO_MBPS(1804, 4) >; + }; + + cpu4_computemon: qcom,cpu4-computeon { + compatible = "qcom,arm-compute-mon"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,target-dev = <&cpu4_cpu_ddr_latfloor>; + qcom,core-dev-table = + < 1881600 MHZ_TO_MBPS(200, 4) >, + < 2208000 MHZ_TO_MBPS(681, 4) >; + }; + }; + + keepalive_opp_table: keepalive-opp-table { + compatible = "operating-points-v2"; + opp-1 { + opp-hz = /bits/ 64 < 1 >; + }; + }; + + snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <139 627>; + qcom,active-only; + status = "ok"; + operating-points-v2 = <&keepalive_opp_table>; + }; + + cpu_pmu: cpu-pmu { + compatible = "arm,armv8-pmuv3"; + qcom,irq-is-percpu; + interrupts = ; + }; + + clock_gcc: qcom,gcc@100000 { + compatible = "qcom,gcc-sdm845", "syscon"; + reg = <0x100000 0x1f0000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pm8998_s9_level>; + vdd_cx_ao-supply = <&pm8998_s9_level_ao>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_videocc: qcom,videocc@ab00000 { + compatible = "qcom,video_cc-sdm845", "syscon"; + reg = <0xab00000 0x10000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pm8998_s9_level>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_camcc: qcom,camcc@ad00000 { + compatible = "qcom,cam_cc-sdm845", "syscon"; + reg = <0xad00000 0x10000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pm8998_s9_level>; + vdd_mx-supply = <&pm8998_s6_level>; + qcom,cam_cc_csi0phytimer_clk_src-opp-handle = <&cam_csiphy0>; + qcom,cam_cc_csi1phytimer_clk_src-opp-handle = <&cam_csiphy1>; + qcom,cam_cc_csi2phytimer_clk_src-opp-handle = <&cam_csiphy2>; + qcom,cam_cc_cci_clk_src-opp-handle = <&cam_cci>; + qcom,cam_cc_ife_0_csid_clk_src-opp-handle = <&cam_csid0>; + qcom,cam_cc_ife_0_clk_src-opp-handle = <&cam_vfe0>; + qcom,cam_cc_ife_1_csid_clk_src-opp-handle = <&cam_csid1>; + qcom,cam_cc_ife_1_clk_src-opp-handle = <&cam_vfe1>; + qcom,cam_cc_ife_lite_csid_clk_src-opp-handle = <&cam_csid_lite>; + qcom,cam_cc_ife_lite_clk_src-opp-handle = <&cam_vfe_lite>; + qcom,cam_cc_icp_clk_src-opp-handle = <&cam_a5>; + qcom,cam_cc_ipe_0_clk_src-opp-handle = <&cam_ipe0>; + qcom,cam_cc_ipe_1_clk_src-opp-handle = <&cam_ipe1>; + qcom,cam_cc_bps_clk_src-opp-handle = <&cam_bps>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_dispcc: qcom,dispcc@af00000 { + compatible = "qcom,dispcc-sdm845", "syscon"; + reg = <0xaf00000 0x10000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pm8998_s9_level>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_gpucc: qcom,gpucc@5090000 { + compatible = "qcom,gpucc-sdm845", "syscon"; + reg = <0x5090000 0x9000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pm8998_s9_level>; + vdd_mx-supply = <&pm8998_s6_level>; + qcom,gpu_cc_gmu_clk_src-opp-handle = <&gmu>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_gfx: qcom,gfxcc@5090000 { + compatible = "qcom,gfxcc-sdm845"; + reg = <0x5090000 0x9000>; + reg-names = "cc_base"; + vdd_gfx-supply = <&pm8005_s1_level>; + qcom,gpu_cc_gx_gfx3d_clk_src-opp-handle = <&msm_gpu>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + cpucc_debug: syscon@17970018 { + compatible = "syscon"; + reg = <0x17970018 0x4>; + }; + + clock_cpucc: qcom,cpucc { + compatible = "qcom,dummycc"; + clock-output-names = "cpucc_clocks"; + #clock-cells = <1>; + }; + + clock_debug: qcom,cc-debug@100000 { + compatible = "qcom,debugcc-sdm845"; + qcom,cc-count = <6>; + qcom,gcc = <&clock_gcc>; + qcom,videocc = <&clock_videocc>; + qcom,camcc = <&clock_camcc>; + qcom,dispcc = <&clock_dispcc>; + qcom,gpucc = <&clock_gpucc>; + qcom,cpucc = <&cpucc_debug>; + clock-names = "xo_clk_src"; + clocks = <&clock_rpmh RPMH_CXO_CLK>; + #clock-cells = <1>; + }; + + clock_aop: qcom,aopclk { + compatible = "qcom,aop-qmp-clk-v1"; + #clock-cells = <1>; + mboxes = <&qmp_aop 0>; + mbox-names = "qdss_clk"; + }; + + cpufreq_hw: qcom,cpufreq-hw { + compatible = "qcom,cpufreq-hw"; + reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>; + reg-names = "freq-domain0", "freq-domain1"; + + clocks = <&clock_rpmh RPMH_CXO_CLK>, <&clock_gcc GPLL0>; + clock-names = "xo", "alternate"; + qcom,no-accumulative-counter; + #freq-domain-cells = <2>; + }; + + ufsphy_mem: ufsphy_mem@1d87000 { + reg = <0x1d87000 0xda8>, <0x1d90000 0x8000>; /* PHY regs */ + reg-names = "phy_mem", "ufs_ice"; + #phy-cells = <0>; + + lanes-per-direction = <2>; + + clock-names = "ref_clk_src", + "ref_clk", + "ref_aux_clk"; + clocks = <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_UFS_MEM_CLKREF_CLK>, + <&clock_gcc GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK>; + + status = "disabled"; + }; + + ufshc_mem: ufshc@1d84000 { + compatible = "qcom,ufshc"; + reg = <0x1d84000 0x2500>, <0x1d90000 0x8000>; + reg-names = "ufs_mem", "ufs_ice"; + interrupts = ; + phys = <&ufsphy_mem>; + phy-names = "ufsphy"; + + lanes-per-direction = <2>; + dev-ref-clk-freq = <0>; /* 19.2 MHz */ + + clock-names = + "core_clk", + "bus_aggr_clk", + "iface_clk", + "core_clk_unipro", + "core_clk_ice", + "ref_clk", + "tx_lane0_sync_clk", + "rx_lane0_sync_clk", + "rx_lane1_sync_clk"; + clocks = + <&clock_gcc GCC_UFS_PHY_AXI_HW_CTL_CLK>, + <&clock_gcc GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK>, + <&clock_gcc GCC_UFS_PHY_AHB_CLK>, + <&clock_gcc GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK>, + <&clock_gcc GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK>, + <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>, + <&clock_gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>, + <&clock_gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>; + freq-table-hz = + <50000000 200000000>, + <0 0>, + <0 0>, + <37500000 150000000>, + <75000000 300000000>, + <0 0>, + <0 0>, + <0 0>, + <0 0>; + + non-removable; + qcom,msm-bus,name = "ufshc_mem"; + qcom,msm-bus,num-cases = <22>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + /* + * During HS G3 UFS runs at nominal voltage corner, vote + * higher bandwidth to push other buses in the data path + * to run at nominal to achieve max throughput. + * 4GBps pushes BIMC to run at nominal. + * 200MBps pushes CNOC to run at nominal. + * Vote for half of this bandwidth for HS G3 1-lane. + * For max bandwidth, vote high enough to push the buses + * to run in turbo voltage corner. + */ + <123 512 0 0>, <1 757 0 0>, /* No vote */ + <123 512 922 0>, <1 757 1000 0>, /* PWM G1 */ + <123 512 1844 0>, <1 757 1000 0>, /* PWM G2 */ + <123 512 3688 0>, <1 757 1000 0>, /* PWM G3 */ + <123 512 7376 0>, <1 757 1000 0>, /* PWM G4 */ + <123 512 1844 0>, <1 757 1000 0>, /* PWM G1 L2 */ + <123 512 3688 0>, <1 757 1000 0>, /* PWM G2 L2 */ + <123 512 7376 0>, <1 757 1000 0>, /* PWM G3 L2 */ + <123 512 14752 0>, <1 757 1000 0>, /* PWM G4 L2 */ + <123 512 127796 0>, <1 757 1000 0>, /* HS G1 RA */ + <123 512 255591 0>, <1 757 1000 0>, /* HS G2 RA */ + <123 512 2097152 0>, <1 757 102400 0>, /* HS G3 RA */ + <123 512 255591 0>, <1 757 1000 0>, /* HS G1 RA L2 */ + <123 512 511181 0>, <1 757 1000 0>, /* HS G2 RA L2 */ + <123 512 4194304 0>, <1 757 204800 0>, /* HS G3 RA L2 */ + <123 512 149422 0>, <1 757 1000 0>, /* HS G1 RB */ + <123 512 298189 0>, <1 757 1000 0>, /* HS G2 RB */ + <123 512 2097152 0>, <1 757 102400 0>, /* HS G3 RB */ + <123 512 298189 0>, <1 757 1000 0>, /* HS G1 RB L2 */ + <123 512 596378 0>, <1 757 1000 0>, /* HS G2 RB L2 */ + /* As UFS working in HS G3 RB L2 mode, aggregated + * bandwidth (AB) should take care of providing + * optimum throughput requested. However, as tested, + * in order to scale up CNOC clock, instantaneous + * bindwidth (IB) needs to be given a proper value too. + */ + <123 512 4194304 0>, <1 757 204800 409600>, /* HS G3 RB L2 */ + <123 512 7643136 0>, <1 757 307200 0>; /* Max. bandwidth */ + + qcom,bus-vector-names = "MIN", + "PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1", + "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2", + "HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1", + "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2", + "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", + "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2", + "MAX"; + + /* PM QoS */ + qcom,pm-qos-cpu-groups = <0x0f 0xf0>; + qcom,pm-qos-cpu-group-latency-us = <70 70>; + qcom,pm-qos-default-cpu = <0>; + + pinctrl-names = "dev-reset-assert", "dev-reset-deassert"; + pinctrl-0 = <&ufs_dev_reset_assert>; + pinctrl-1 = <&ufs_dev_reset_deassert>; + + resets = <&clock_gcc GCC_UFS_PHY_BCR>; + reset-names = "core_reset"; + + status = "disabled"; + }; + + sdhc_2: sdhci@8804000 { + compatible = "qcom,sdhci-msm-v5"; + reg = <0x8804000 0x1000>; + reg-names = "hc_mem"; + + interrupts = , + ; + interrupt-names = "hc_irq", "pwr_irq"; + + qcom,bus-width = <4>; + qcom,large-address-bus; + + qcom,msm-bus,name = "sdhc2"; + qcom,msm-bus,num-cases = <8>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + /* No vote */ + <81 512 0 0>, <1 608 0 0>, + /* 400 KB/s*/ + <81 512 1046 1600>, + <1 608 1600 1600>, + /* 20 MB/s */ + <81 512 52286 80000>, + <1 608 80000 80000>, + /* 25 MB/s */ + <81 512 65360 100000>, + <1 608 100000 100000>, + /* 50 MB/s */ + <81 512 130718 200000>, + <1 608 133320 133320>, + /* 100 MB/s */ + <81 512 261438 200000>, + <1 608 150000 150000>, + /* 200 MB/s */ + <81 512 261438 400000>, + <1 608 300000 300000>, + /* Max. bandwidth */ + <81 512 1338562 4096000>, + <1 608 1338562 4096000>; + qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 + 100750000 200000000 4294967295>; + + qcom,sdr104-wa; + + qcom,restore-after-cx-collapse; + + qcom,clk-rates = <400000 20000000 25000000 + 50000000 100000000 201500000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", + "SDR104"; + + qcom,devfreq,freq-table = <50000000 201500000>; + clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>, + <&clock_gcc GCC_SDCC2_APPS_CLK>; + clock-names = "iface_clk", "core_clk"; + + /* PM QoS */ + qcom,pm-qos-irq-type = "affine_irq"; + qcom,pm-qos-irq-latency = <70 70>; + qcom,pm-qos-cpu-groups = <0x3f 0xc0>; + qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>; + + status = "disabled"; + }; + + pil_modem: qcom,mss@4080000 { + compatible = "qcom,pil-q6v55-mss"; + reg = <0x4080000 0x100>, + <0x1f63000 0x008>, + <0x1f65000 0x008>, + <0x1f64000 0x008>, + <0x4180000 0x020>, + <0xc2b0000 0x004>, + <0xb2e0100 0x004>, + <0x4180044 0x004>; + reg-names = "qdsp6_base", "halt_q6", "halt_modem", + "halt_nc", "rmb_base", "restart_reg", + "pdc_sync", "alt_reset"; + + clocks = <&clock_rpmh RPMH_CXO_CLK>, + <&clock_gcc GCC_MSS_CFG_AHB_CLK>, + <&clock_gcc GCC_MSS_Q6_MEMNOC_AXI_CLK>, + <&clock_gcc GCC_BOOT_ROM_AHB_CLK>, + <&clock_gcc GCC_MSS_GPLL0_DIV_CLK_SRC>, + <&clock_gcc GCC_MSS_SNOC_AXI_CLK>, + <&clock_gcc GCC_MSS_MFAB_AXIS_CLK>, + <&clock_gcc GCC_PRNG_AHB_CLK>; + clock-names = "xo", "iface_clk", "bus_clk", + "mem_clk", "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk", "prng_clk"; + qcom,proxy-clock-names = "xo", "prng_clk"; + qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk", + "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk"; + + vdd_cx-supply = <&pm8998_s9_level>; + vdd_cx-voltage = ; + vdd_mx-supply = <&pm8998_s6_level>; + vdd_mx-uV = ; + vdd_mss-supply = <&pm8005_s2_level>; + vdd_mss-uV = ; + qcom,firmware-name = "modem"; + qcom,sequential-fw-load; + qcom,pil-self-auth; + qcom,sysmon-id = <0>; + qcom,minidump-id = <3>; + qcom,ssctl-instance-id = <0x12>; + qcom,override-acc; + qcom,signal-aop; + qcom,qdsp6v65-1-0; + qcom,mss_pdc_offset = <8>; + status = "ok"; + memory-region = <&pil_modem_mem>; + qcom,mem-protect-id = <0xF>; + + /* Inputs from mss */ + interrupts-extended = <&intc GIC_SPI 266 IRQ_TYPE_EDGE_RISING>, + <&modem_smp2p_in 0 IRQ_TYPE_LEVEL_HIGH>, + <&modem_smp2p_in 2 IRQ_TYPE_LEVEL_HIGH>, + <&modem_smp2p_in 1 IRQ_TYPE_LEVEL_HIGH>, + <&modem_smp2p_in 3 IRQ_TYPE_LEVEL_HIGH>, + <&modem_smp2p_in 7 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack", + "qcom,shutdown-ack"; + + /* Outputs to mss */ + qcom,smem-states = <&modem_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + + mboxes = <&qmp_aop 0>; + mbox-names = "mss-pil"; + qcom,mba-mem@0 { + compatible = "qcom,pil-mba-mem"; + memory-region = <&pil_mba_mem>; + }; + }; + + qcom,lpass@17300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x17300000 0x00100>; + + vdd_cx-supply = <&pm8998_s9_level>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = ; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <1>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <1>; + status = "ok"; + qcom,ssctl-instance-id = <0x14>; + qcom,firmware-name = "adsp"; + qcom,signal-aop; + memory-region = <&pil_adsp_mem>; + + /* Inputs from lpass */ + interrupts-extended = <&intc GIC_SPI 162 IRQ_TYPE_EDGE_RISING>, + <&adsp_smp2p_in 0 IRQ_TYPE_LEVEL_HIGH>, + <&adsp_smp2p_in 2 IRQ_TYPE_LEVEL_HIGH>, + <&adsp_smp2p_in 1 IRQ_TYPE_LEVEL_HIGH>, + <&adsp_smp2p_in 3 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to lpass */ + qcom,smem-states = <&adsp_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + + mboxes = <&qmp_aop 0>; + mbox-names = "adsp-pil"; + }; + + qcom,ssc@5c00000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x5c00000 0x4000>; + + vdd_cx-supply = <&pm8998_l27_level>; + qcom,vdd_cx-uV-uA = ; + vdd_mx-supply = <&pm8998_l4_level>; + qcom,vdd_mx-uV-uA = ; + qcom,proxy-reg-names = "vdd_cx", "vdd_mx"; + qcom,keep-proxy-regs-on; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <12>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <424>; + qcom,sysmon-id = <3>; + qcom,ssctl-instance-id = <0x16>; + qcom,signal-aop; + qcom,firmware-name = "slpi"; + status = "ok"; + memory-region = <&pil_slpi_mem>; + + /* Inputs from ssc */ + interrupts-extended = <&intc GIC_SPI 494 IRQ_TYPE_EDGE_RISING>, + <&dsps_smp2p_in 0 IRQ_TYPE_LEVEL_HIGH>, + <&dsps_smp2p_in 2 IRQ_TYPE_LEVEL_HIGH>, + <&dsps_smp2p_in 1 IRQ_TYPE_LEVEL_HIGH>, + <&dsps_smp2p_in 3 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to ssc */ + qcom,smem-states = <&dsps_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + + mboxes = <&qmp_aop 0>; + mbox-names = "slpi-pil"; + }; + + slim_aud: slim@171c0000 { + cell-index = <1>; + compatible = "qcom,slim-ngd"; + reg = <0x171c0000 0x2c000>, + <0x17184000 0x2a000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = , + ; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + qcom,apps-ch-pipes = <0x780000>; + qcom,ea-pc = <0x270>; + qcom,iommu-s1-bypass; + iommus = <&apps_smmu 0x1806 0x0>, + <&apps_smmu 0x180d 0x0>, + <&apps_smmu 0x180e 0x1>, + <&apps_smmu 0x1810 0x1>; + qcom,iommu-dma-addr-pool = <0x40000000 0xc0000000>; + qcom,iommu-dma = "atomic"; + }; + + slim_qca: slim@17240000 { + status = "ok"; + cell-index = <3>; + compatible = "qcom,slim-ngd"; + reg = <0x17240000 0x2c000>, + <0x17204000 0x20000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = , + ; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + qcom,iommu-s1-bypass; + iommus = <&apps_smmu 0x1813 0x0>; + qcom,iommu-dma-addr-pool = <0x40000000 0xc0000000>; + qcom,iommu-dma = "atomic"; + + /* Slimbus Slave DT for WCN3990 */ + btfmslim_codec: wcn3990 { + compatible = "qcom,btfmslim_slave"; + elemental-addr = [00 01 20 02 17 02]; + qcom,btfm-slim-ifd = "btfmslim_slave_ifd"; + qcom,btfm-slim-ifd-elemental-addr = [00 00 20 02 17 02]; + }; + }; + + eud: qcom,msm-eud@88e0000 { + compatible = "qcom,msm-eud"; + interrupt-names = "eud_irq"; + interrupts = ; + reg = <0x88e0000 0x2000>; + reg-names = "eud_base"; + clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + clock-names = "cfg_ahb_clk"; + vdda33-supply = <&pm8998_l24>; + status = "ok"; + }; + + qcom,spss@1880000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x188101c 0x4>, + <0x1881024 0x4>, + <0x1881028 0x4>, + <0x188103c 0x4>, + <0x1882014 0x4>; + reg-names = "sp2soc_irq_status", "sp2soc_irq_clr", + "sp2soc_irq_mask", "rmb_err", "rmb_err_spare2"; + interrupts = ; + + vdd_cx-supply = <&pm8998_s9_level>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = ; + vdd_mx-supply = <&pm8998_s6_level>; + vdd_mx-uV = ; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + qcom,pil-generic-irq-handler; + status = "disabled"; + + qcom,pas-id = <14>; + qcom,proxy-timeout-ms = <10000>; + qcom,signal-aop; + qcom,firmware-name = "spss"; + memory-region = <&pil_spss_mem>; + qcom,spss-scsr-bits = <24 25>; + + mboxes = <&qmp_aop 0>; + mbox-names = "spss-pil"; + }; + + wdog: qcom,wdt@17980000{ + compatible = "qcom,msm-watchdog"; + reg = <0x17980000 0x1000>; + reg-names = "wdt-base"; + interrupts = , + ; + qcom,bark-time = <11000>; + qcom,pet-time = <9360>; + qcom,ipi-ping; + qcom,wakeup-enable; + }; + + qcom,turing@8300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x8300000 0x100000>; + + vdd_cx-supply = <&pm8998_s9_level>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = ; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <18>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <601>; + qcom,sysmon-id = <7>; + qcom,ssctl-instance-id = <0x17>; + qcom,firmware-name = "cdsp"; + qcom,signal-aop; + memory-region = <&pil_cdsp_mem>; + + /* Inputs from turing */ + interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 0 IRQ_TYPE_LEVEL_HIGH>, + <&cdsp_smp2p_in 2 IRQ_TYPE_LEVEL_HIGH>, + <&cdsp_smp2p_in 1 IRQ_TYPE_LEVEL_HIGH>, + <&cdsp_smp2p_in 3 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to turing */ + qcom,smem-states = <&cdsp_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + + mboxes = <&qmp_aop 0>; + mbox-names = "cdsp-pil"; + }; + + qcom,msm-rtb { + compatible = "qcom,msm-rtb"; + qcom,rtb-size = <0x100000>; + }; + + qcom,mpm2-sleep-counter@0x0c221000 { + compatible = "qcom,mpm2-sleep-counter"; + reg = <0x0c221000 0x1000>; + clock-frequency = <32768>; + }; + + qcom,smp2p_sleepstate { + compatible = "qcom,smp2p-sleepstate"; + qcom,smem-states = <&sleepstate_smp2p_out 0>; + interrupt-parent = <&sleepstate_smp2p_in>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "smp2p-sleepstate-in"; + }; + + qcom,msm-cdsp-loader { + compatible = "qcom,cdsp-loader"; + qcom,proc-img-to-load = "cdsp"; + }; + + qcom,msm-adsprpc-mem { + compatible = "qcom,msm-adsprpc-mem-region"; + memory-region = <&adsp_mem>; + }; + + qcom,msm_fastrpc { + compatible = "qcom,msm-fastrpc-compute"; + qcom,rpc-latency-us = <611>; + + qcom,msm_fastrpc_compute_cb1 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1401 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb2 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1402 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb3 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1403 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb4 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1404 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb5 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1405 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb6 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1406 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb7 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1407 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb8 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + iommus = <&apps_smmu 0x1408 0x30>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb9 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + qcom,secure-context-bank; + iommus = <&apps_smmu 0x1409 0x30>; + qcom,iommu-dma-addr-pool = <0x60000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + qcom,iommu-vmid = <0xA>; /* VMID_CP_PIXEL */ + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb10 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "cdsprpc-smd"; + qcom,secure-context-bank; + iommus = <&apps_smmu 0x140A 0x30>; + qcom,iommu-dma-addr-pool = <0x60000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + qcom,iommu-vmid = <0xA>; /* VMID_CP_PIXEL */ + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb11 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&apps_smmu 0x1823 0x0>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + qcom,msm_fastrpc_compute_cb12 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "adsprpc-smd"; + iommus = <&apps_smmu 0x1824 0x0>; + qcom,iommu-dma-addr-pool = <0x80000000 0x78000000>; + qcom,iommu-faults = "stall-disable", "HUPCF"; + dma-coherent; + }; + }; + + qcom,msm-imem@146bf000 { + compatible = "qcom,msm-imem"; + reg = <0x146bf000 0x1000>; + ranges = <0x0 0x146bf000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + mem_dump_table@10 { + compatible = "qcom,msm-imem-mem_dump_table"; + reg = <0x10 8>; + }; + + restart_reason@65c { + compatible = "qcom,msm-imem-restart_reason"; + reg = <0x65c 4>; + }; + + dload_type@1c { + compatible = "qcom,msm-imem-dload-type"; + reg = <0x1c 0x4>; + }; + + boot_stats@6b0 { + compatible = "qcom,msm-imem-boot_stats"; + reg = <0x6b0 32>; + }; + + pil@94c { + compatible = "qcom,msm-imem-pil"; + reg = <0x94c 200>; + }; + + kaslr_offset@6d0 { + compatible = "qcom,msm-imem-kaslr_offset"; + reg = <0x6d0 12>; + }; + + diag_dload@c8 { + compatible = "qcom,msm-imem-diag-dload"; + reg = <0xc8 200>; + }; + }; + + qcom,venus@aae0000 { + compatible = "qcom,pil-tz-generic"; + reg = <0xaae0000 0x4000>; + + vdd-supply = <&venus_gdsc>; + qcom,proxy-reg-names = "vdd"; + + clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&clock_videocc VIDEO_CC_VENUS_AHB_CLK>, + <&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>; + clock-names = "core_clk", "iface_clk", "bus_clk"; + qcom,proxy-clock-names = "core_clk", "iface_clk", "bus_clk"; + + qcom,pas-id = <9>; + qcom,msm-bus,name = "pil-venus"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <63 512 0 0>, + <63 512 0 304000>; + qcom,proxy-timeout-ms = <100>; + qcom,firmware-name = "venus"; + memory-region = <&pil_video_mem>; + status = "ok"; + }; + + ssc_sensors: qcom,msm-ssc-sensors { + compatible = "qcom,msm-ssc-sensors"; + status = "ok"; + qcom,firmware-name = "slpi"; + }; + + cpuss_dump { + compatible = "qcom,cpuss-dump"; + qcom,l1_i_cache0 { + qcom,dump-node = <&L1_I_0>; + qcom,dump-id = <0x60>; + }; + qcom,l1_i_cache1 { + qcom,dump-node = <&L1_I_100>; + qcom,dump-id = <0x61>; + }; + qcom,l1_i_cache2 { + qcom,dump-node = <&L1_I_200>; + qcom,dump-id = <0x62>; + }; + qcom,l1_i_cache3 { + qcom,dump-node = <&L1_I_300>; + qcom,dump-id = <0x63>; + }; + qcom,l1_i_cache100 { + qcom,dump-node = <&L1_I_400>; + qcom,dump-id = <0x64>; + }; + qcom,l1_i_cache101 { + qcom,dump-node = <&L1_I_500>; + qcom,dump-id = <0x65>; + }; + qcom,l1_i_cache102 { + qcom,dump-node = <&L1_I_600>; + qcom,dump-id = <0x66>; + }; + qcom,l1_i_cache103 { + qcom,dump-node = <&L1_I_700>; + qcom,dump-id = <0x67>; + }; + qcom,l1_d_cache0 { + qcom,dump-node = <&L1_D_0>; + qcom,dump-id = <0x80>; + }; + qcom,l1_d_cache1 { + qcom,dump-node = <&L1_D_100>; + qcom,dump-id = <0x81>; + }; + qcom,l1_d_cache2 { + qcom,dump-node = <&L1_D_200>; + qcom,dump-id = <0x82>; + }; + qcom,l1_d_cache3 { + qcom,dump-node = <&L1_D_300>; + qcom,dump-id = <0x83>; + }; + qcom,l1_d_cache100 { + qcom,dump-node = <&L1_D_400>; + qcom,dump-id = <0x84>; + }; + qcom,l1_d_cache101 { + qcom,dump-node = <&L1_D_500>; + qcom,dump-id = <0x85>; + }; + qcom,l1_d_cache102 { + qcom,dump-node = <&L1_D_600>; + qcom,dump-id = <0x86>; + }; + qcom,l1_d_cache103 { + qcom,dump-node = <&L1_D_700>; + qcom,dump-id = <0x87>; + }; + qcom,llcc1_d_cache { + qcom,dump-node = <&LLCC_1>; + qcom,dump-id = <0x140>; + }; + qcom,llcc2_d_cache { + qcom,dump-node = <&LLCC_2>; + qcom,dump-id = <0x141>; + }; + qcom,llcc3_d_cache { + qcom,dump-node = <&LLCC_3>; + qcom,dump-id = <0x142>; + }; + qcom,llcc4_d_cache { + qcom,dump-node = <&LLCC_4>; + qcom,dump-id = <0x143>; + }; + qcom,l1_tlb_dump0 { + qcom,dump-node = <&L1_TLB_0>; + qcom,dump-id = <0x120>; + }; + qcom,l1_tlb_dump100 { + qcom,dump-node = <&L1_TLB_100>; + qcom,dump-id = <0x121>; + }; + qcom,l1_tlb_dump200 { + qcom,dump-node = <&L1_TLB_200>; + qcom,dump-id = <0x122>; + }; + qcom,l1_tlb_dump300 { + qcom,dump-node = <&L1_TLB_300>; + qcom,dump-id = <0x123>; + }; + qcom,l1_tlb_dump400 { + qcom,dump-node = <&L1_TLB_400>; + qcom,dump-id = <0x124>; + }; + qcom,l1_tlb_dump500 { + qcom,dump-node = <&L1_TLB_500>; + qcom,dump-id = <0x125>; + }; + qcom,l1_tlb_dump600 { + qcom,dump-node = <&L1_TLB_600>; + qcom,dump-id = <0x126>; + }; + qcom,l1_tlb_dump700 { + qcom,dump-node = <&L1_TLB_700>; + qcom,dump-id = <0x127>; + }; + }; + + kryo3xx-erp { + compatible = "arm,arm64-kryo-cpu-erp"; + interrupts = , + , + , + ; + + interrupt-names = "l1-l2-faultirq", + "l1-l2-errirq", + "l3-scu-errirq", + "l3-scu-faultirq"; + }; + + cache-controller@1100000 { + compatible = "qcom,sdm845-llcc"; + reg = <0x01100000 0x200000>, <0x01300000 0x50000>; + reg-names = "llcc_base", "llcc_broadcast_base"; + cap-based-alloc-and-pwr-collapse; + + LLCC_1: llcc_1_dcache { + qcom,dump-size = <0x1141c0>; + }; + + LLCC_2: llcc_2_dcache { + qcom,dump-size = <0x1141c0>; + }; + + LLCC_3: llcc_3_dcache { + qcom,dump-size = <0x1141c0>; + }; + + LLCC_4: llcc_4_dcache { + qcom,dump-size = <0x1141c0>; + }; + }; + + qcom,ipc-spinlock@1f40000 { + compatible = "qcom,ipc-spinlock-sfpb"; + reg = <0x1f40000 0x8000>; + qcom,num-locks = <8>; + }; + + tcsr_mutex_block: syscon@1f40000 { + compatible = "syscon"; + reg = <0x1f40000 0x20000>; + }; + + tcsr_mutex: hwlock { + compatible = "qcom,tcsr-mutex"; + syscon = <&tcsr_mutex_block 0 0x1000>; + #hwlock-cells = <1>; + }; + + smem: qcom,smem { + compatible = "qcom,smem"; + memory-region = <&smem_mem>; + hwlocks = <&tcsr_mutex 3>; + }; + + qmp_aop: qcom,qmp-aop@c300000 { + compatible = "qcom,qmp-mbox"; + label = "aop"; + reg = <0xc300000 0x100000>, + <0x1799000c 0x4>; + reg-names = "msgram", "irq-reg-base"; + mboxes = <&apss_shared 0>; + mbox-names = "aop_qmp"; + qcom,irq-mask = <0x1>; + interrupts = ; + qcom,early-boot; + priority = <0>; + mbox-desc-offset = <0x0>; + #mbox-cells = <1>; + }; + + apss_shared: mailbox@17990000 { + compatible = "qcom,sdm845-apss-shared"; + reg = <0x17990000 0x1000>; + #mbox-cells = <1>; + }; + + apps_rsc: rsc@179c0000 { + compatible = "qcom,rpmh-rsc"; + label = "apps_rsc"; + reg = <0x179c0000 0x10000>, + <0x179d0000 0x10000>, + <0x179e0000 0x10000>; + reg-names = "drv-0", "drv-1", "drv-2"; + interrupts = , + , + ; + qcom,tcs-offset = <0xd00>; + qcom,drv-id = <2>; + qcom,tcs-config = , + , + , + ; + + msm_bus_apps_rsc { + compatible = "qcom,msm-bus-rsc"; + qcom,msm-bus-id = ; + }; + + system_pm { + compatible = "qcom,system-pm"; + }; + + clock_rpmh: qcom,rpmhclk { + compatible = "qcom,sdm845-rpmh-clk"; + #clock-cells = <1>; + }; + }; + + disp_rsc: rsc@af20000 { + compatible = "qcom,rpmh-rsc"; + label = "display_rsc"; + reg = <0xaf20000 0x10000>; + reg-names = "drv-0"; + interrupts = ; + qcom,tcs-offset = <0x1c00>; + qcom,drv-id = <0>; + qcom,tcs-config = , + , + , + ; + + msm_bus_disp_rsc { + compatible = "qcom,msm-bus-rsc"; + qcom,msm-bus-id = ; + }; + + sde_rsc_rpmh { + compatible = "qcom,sde-rsc-rpmh"; + cell-index = <0>; + }; + }; + + sp_scsr: mailbox@188501c { + compatible = "qcom,sdm845-spcs-global"; + reg = <0x188501c 0x4>; + #mbox-cells = <1>; + }; + + sp_scsr_block: syscon@1880000 { + compatible = "syscon"; + reg = <0x1880000 0x10000>; + }; + + intsp: qcom,qsee_irq { + #address-cells = <0>; + compatible = "qcom,sdm845-qsee-irq"; + + syscon = <&sp_scsr_block>; + interrupts = , + ; + + interrupt-names = "sp_ipc0", + "sp_ipc1"; + + interrupt-controller; + #interrupt-cells = <3>; + }; + + qcom,glink { + compatible = "qcom,glink"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + glink_modem: modem { + qcom,remote-pid = <1>; + transport = "smem"; + mboxes = <&apss_shared 12>; + mbox-names = "mpss_smem"; + interrupts = ; + + label = "modem"; + qcom,glink-label = "mpss"; + + qcom,modem_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,low-latency; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,modem_ds { + qcom,glink-channels = "DS"; + qcom,intents = <0x4000 2>; + }; + + qcom,modem_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_adsp>, + <&glink_slpi>, + <&glink_cdsp>, + <&glink_spss>; + }; + }; + + glink_adsp: adsp { + qcom,remote-pid = <2>; + transport = "smem"; + mboxes = <&apss_shared 8>; + mbox-names = "adsp_smem"; + interrupts = ; + + label = "adsp"; + qcom,glink-label = "lpass"; + + qcom,adsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,low-latency; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,apr_tal_rpmsg { + qcom,glink-channels = "apr_audio_svc"; + qcom,intents = <0x200 20>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,adsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_slpi>, + <&glink_cdsp>; + }; + }; + + glink_slpi: dsps { + qcom,remote-pid = <3>; + transport = "smem"; + mboxes = <&apss_shared 24>; + mbox-names = "dsps_smem"; + interrupts = ; + + label = "slpi"; + qcom,glink-label = "dsps"; + + qcom,slpi_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,low-latency; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,slpi_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_adsp>, + <&glink_cdsp>; + }; + }; + + glink_cdsp: cdsp { + qcom,remote-pid = <5>; + transport = "smem"; + mboxes = <&apss_shared 4>; + mbox-names = "cdsp_smem"; + interrupts = ; + + label = "cdsp"; + qcom,glink-label = "cdsp"; + + qcom,cdsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,msm_cdsprm_rpmsg { + compatible = "qcom,msm-cdsprm-rpmsg"; + qcom,glink-channels = "cdsprmglink-apps-dsp"; + qcom,intents = <0x20 12>; + + qcom,cdsp-cdsp-l3-gov { + compatible = "qcom,cdsp-l3"; + qcom,target-dev = <&cdsp_l3>; + }; + + msm_cdsp_rm: qcom,msm_cdsp_rm { + compatible = "qcom,msm-cdsp-rm"; + qcom,qos-latency-us = <100>; + qcom,qos-maxhold-ms = <20>; + }; + }; + + qcom,cdsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_adsp>, + <&glink_slpi>; + }; + }; + + glink_spss: spss { + qcom,remote-pid = <8>; + transport = "spss"; + mboxes = <&sp_scsr 0>; + mbox-names = "spss_spss"; + interrupt-parent = <&intsp>; + interrupts = ; + + reg = <0x1885008 0x8>, + <0x1885010 0x4>; + reg-names = "qcom,spss-addr", + "qcom,spss-size"; + + label = "spss"; + qcom,glink-label = "spss"; + + qcom,spss_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>; + }; + }; + + glink_spi_xprt_wdsp: wdsp { + transport = "spi"; + tx-descriptors = <0x12000 0x12004>; + rx-descriptors = <0x1200c 0x12010>; + + label = "wdsp"; + qcom,glink-label = "wdsp"; + + qcom,wdsp_ctrl { + qcom,glink-channels = "g_glink_ctrl"; + qcom,intents = <0x400 1>; + }; + + qcom,wdsp_ild { + qcom,glink-channels = + "g_glink_persistent_data_ild"; + }; + + qcom,wdsp_nild { + qcom,glink-channels = + "g_glink_persistent_data_nild"; + }; + + qcom,wdsp_data { + qcom,glink-channels = "g_glink_audio_data"; + qcom,intents = <0x1000 2>; + }; + + qcom,diag_data { + qcom,glink-channels = "DIAG_DATA"; + qcom,intents = <0x4000 2>; + }; + + qcom,diag_ctrl { + qcom,glink-channels = "DIAG_CTRL"; + qcom,intents = <0x4000 1>; + }; + + qcom,diag_cmd { + qcom,glink-channels = "DIAG_CMD"; + qcom,intents = <0x4000 1 >; + }; + }; + }; + + qcom,qsee_irq_bridge { + compatible = "qcom,qsee-ipc-irq-bridge"; + + qcom,qsee-ipc-irq-spss { + qcom,dev-name = "qsee_ipc_irq_spss"; + label = "spss"; + interrupt-parent = <&intsp>; + interrupts = ; + }; + }; + + qcom,spcom { + compatible = "qcom,spcom"; + + /* predefined channels, remote side is server */ + qcom,spcom-ch-names = "sp_kernel", "sp_ssr"; + status = "ok"; + }; + + spss_utils: qcom,spss_utils { + compatible = "qcom,spss-utils"; + /* spss fuses physical address */ + qcom,spss-fuse1-addr = <0x007841c4>; + qcom,spss-fuse1-bit = <27>; + qcom,spss-fuse2-addr = <0x007841c4>; + qcom,spss-fuse2-bit = <26>; + qcom,spss-dev-firmware-name = "spss1d"; /* 8 chars max */ + qcom,spss-test-firmware-name = "spss1t"; /* 8 chars max */ + qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */ + qcom,spss-debug-reg-addr = <0x01886020>; + qcom,no-cmac-and-iar-feature-support; + status = "ok"; + }; + + qcom,glink_pkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-loopback_cntl { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl"; + }; + + qcom,glinkpkt-loopback_data { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + + qcom,glinkpkt-data1 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA1"; + qcom,glinkpkt-dev-name = "smd7"; + }; + + qcom,glinkpkt-data4 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA4"; + qcom,glinkpkt-dev-name = "smd8"; + }; + + qcom,glinkpkt-data11 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA11"; + qcom,glinkpkt-dev-name = "smd11"; + }; + }; + + qcom,sps { + compatible = "qcom,msm-sps-4k"; + qcom,pipe-attr-ee; + }; + + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 121 0>; + qcom,finger-detect-gpio = <&pm8998_gpios 5 0>; + }; + + qcom_seecom: qseecom@86d00000 { + compatible = "qcom,qseecom"; + reg = <0x86d00000 0x03E00000>; /* enlarge TA memory size from 34M to 62M on 2018/09/03 */ + reg-names = "secapp-region"; + memory-region = <&qseecom_mem>; + qcom,hlos-num-ce-hw-instances = <1>; + qcom,hlos-ce-hw-instance = <0>; + qcom,qsee-ce-hw-instance = <0>; + qcom,disk-encrypt-pipe-pair = <2>; + qcom,support-fde; + qcom,no-clock-support; + qcom,fde-key-size; + qcom,commonlib64-loaded-by-uefi; + qcom,msm-bus,name = "qseecom-noc"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <125 512 0 0>, + <125 512 200000 400000>, + <125 512 300000 800000>, + <125 512 400000 1000000>; + clock-names = "core_clk_src", "core_clk", + "iface_clk", "bus_clk"; + clocks = <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_AHB_CLK>, + <&clock_gcc GCC_CE1_AXI_CLK>; + qcom,ce-opp-freq = <171430000>; + qcom,qsee-reentrancy-support = <2>; + }; + + qcom_rng: qrng@793000 { + compatible = "qcom,msm-rng"; + reg = <0x793000 0x1000>; + qcom,msm-rng-iface-clk; + qcom,no-qrng-config; + qcom,msm-bus,name = "msm-rng-noc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <1 618 0 0>, /* No vote */ + <1 618 0 300000>; /* 75 MHz */ + clocks = <&clock_gcc GCC_PRNG_AHB_CLK>; + clock-names = "iface_clk"; + }; + + qcom_tzlog: tz-log@146bf720 { + compatible = "qcom,tz-log"; + reg = <0x146bf720 0x3000>; + qcom,hyplog-enabled; + hyplog-address-offset = <0x410>; + hyplog-size-offset = <0x414>; + }; + + qcom_cedev: qcedev@1de0000 { + compatible = "qcom,qcedev"; + reg = <0x1de0000 0x20000>, + <0x1dc4000 0x24000>; + reg-names = "crypto-base","crypto-bam-base"; + interrupts = ; + qcom,bam-pipe-pair = <3>; + qcom,ce-hw-instance = <0>; + qcom,ce-device = <0>; + qcom,ce-hw-shared; + qcom,bam-ee = <0>; + qcom,msm-bus,name = "qcedev-noc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <125 512 0 0>, + <125 512 393600 393600>; + clock-names = "core_clk_src", "core_clk", + "iface_clk", "bus_clk"; + clocks = <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_AHB_CLK>, + <&clock_gcc GCC_CE1_AXI_CLK>; + qcom,ce-opp-freq = <171430000>; + qcom,request-bw-before-clk; + + qcom,smmu-s1-enable; + iommus = <&apps_smmu 0x706 0x1>, + <&apps_smmu 0x716 0x1>; + qcom,iommu-dma = "atomic"; + + qcom_cedev_ns_cb { + compatible = "qcom,qcedev,context-bank"; + label = "ns_context"; + iommus = <&apps_smmu 0x712 0>, + <&apps_smmu 0x71f 0>; + virtual-addr = <0x60000000>; + virtual-size = <0x40000000>; + }; + + qcom_cedev_s_cb { + compatible = "qcom,qcedev,context-bank"; + label = "secure_context"; + iommus = <&apps_smmu 0x713 0>, + <&apps_smmu 0x71c 0>, + <&apps_smmu 0x71d 0>, + <&apps_smmu 0x71e 0>; + qcom,iommu-vmid = <0x9>; /* VMID_CP_BITSTREAM */ + virtual-addr = <0x60200000>; + virtual-size = <0x40000000>; + qcom,secure-context-bank; + }; + }; + + qcom_msmhdcp: qcom,msm_hdcp { + compatible = "qcom,msm-hdcp"; + }; + + qcom_crypto: qcrypto@1de0000 { + compatible = "qcom,qcrypto"; + reg = <0x1de0000 0x20000>, + <0x1dc4000 0x24000>; + reg-names = "crypto-base","crypto-bam-base"; + interrupts = ; + qcom,bam-pipe-pair = <2>; + qcom,ce-hw-instance = <0>; + qcom,ce-device = <0>; + qcom,bam-ee = <0>; + qcom,ce-hw-shared; + qcom,clk-mgmt-sus-res; + qcom,msm-bus,name = "qcrypto-noc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <125 512 0 0>, + <125 512 393600 393600>; + clock-names = "core_clk_src", "core_clk", + "iface_clk", "bus_clk"; + clocks = <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_CLK>, + <&clock_gcc GCC_CE1_AHB_CLK>, + <&clock_gcc GCC_CE1_AXI_CLK>; + qcom,ce-opp-freq = <171430000>; + qcom,request-bw-before-clk; + qcom,use-sw-aes-cbc-ecb-ctr-algo; + qcom,use-sw-aes-xts-algo; + qcom,use-sw-aes-ccm-algo; + qcom,use-sw-ahash-algo; + qcom,use-sw-aead-algo; + qcom,use-sw-hmac-algo; + qcom,smmu-s1-enable; + iommus = <&apps_smmu 0x704 0x1>, + <&apps_smmu 0x714 0x1>; + qcom,iommu-dma = "atomic"; + }; + + qcom,msm_gsi { + compatible = "qcom,msm_gsi"; + }; + + qcom,rmtfs_sharedmem@0 { + compatible = "qcom,sharedmem-uio"; + reg = <0x0 0x200000>; + reg-names = "rmtfs"; + qcom,client-id = <0x00000001>; + qcom,guard-memory; + }; + + qcom,rmnet-ipa { + compatible = "qcom,rmnet-ipa3"; + qcom,rmnet-ipa-ssr; + qcom,ipa-loaduC; + qcom,ipa-advertise-sg-support; + qcom,ipa-napi-enable; + }; + + ipa_hw: qcom,ipa@01e00000 { + compatible = "qcom,ipa"; + reg = <0x1e00000 0x34000>, + <0x1e04000 0x2c000>; + reg-names = "ipa-base", "gsi-base"; + interrupts = + , + ; + interrupt-names = "ipa-irq", "gsi-irq"; + qcom,ipa-hw-ver = <13>; /* IPA core version = IPAv3.5.1 */ + qcom,ipa-hw-mode = <0>; + qcom,platform-type = <1>; /* MSM platform */ + qcom,ee = <0>; + qcom,use-ipa-tethering-bridge; + qcom,modem-cfg-emb-pipe-flt; + qcom,ipa-wdi2; + qcom,use-64-bit-dma-mask; + qcom,arm-smmu; + qcom,smmu-fast-map; + qcom,bandwidth-vote-for-ipa; + qcom,msm-bus,name = "ipa"; + qcom,msm-bus,num-cases = <5>; + qcom,msm-bus,num-paths = <4>; + qcom,msm-bus,vectors-KBps = + /* No vote */ + <90 512 0 0>, + <90 585 0 0>, + <1 676 0 0>, + <143 777 0 0>, + /* SVS2 */ + <90 512 80000 600000>, + <90 585 80000 350000>, + <1 676 40000 40000>, /*gcc_config_noc_clk_src */ + <143 777 0 75>, /* IB defined for IPA2X_clk in MHz*/ + /* SVS */ + <90 512 80000 640000>, + <90 585 80000 640000>, + <1 676 80000 80000>, + <143 777 0 150>, /* IB defined for IPA2X_clk in MHz*/ + /* NOMINAL */ + <90 512 206000 960000>, + <90 585 206000 960000>, + <1 676 206000 160000>, + <143 777 0 300>, /* IB defined for IPA2X_clk in MHz*/ + /* TURBO */ + <90 512 206000 3600000>, + <90 585 206000 3600000>, + <1 676 206000 300000>, + <143 777 0 355>; /* IB defined for IPA clk in MHz*/ + qcom,bus-vector-names = + "MIN", "SVS2", "SVS", "NOMINAL", "TURBO"; + qcom,throughput-threshold = <310 600 1000>; + + /* smp2p information */ + qcom,smp2p_map_ipa_1_out { + compatible = "qcom,smp2p-map-ipa-1-out"; + qcom,smem-states = <&smp2p_ipa_1_out 0>; + qcom,smem-state-names = "ipa-smp2p-out"; + }; + + qcom,smp2p_map_ipa_1_in { + compatible = "qcom,smp2p-map-ipa-1-in"; + interrupts-extended = <&smp2p_ipa_1_in 0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "ipa-smp2p-in"; + }; + + ipa_smmu_ap: ipa_smmu_ap { + compatible = "qcom,ipa-smmu-ap-cb"; + iommus = <&apps_smmu 0x720 0x0>; + qcom,iommu-dma-addr-pool = <0x20000000 0x40000000>; + /* modem tables in IMEM */ + qcom,additional-mapping = <0x146BD000 0x146BD000 0x2000>; + qcom,iommu-dma = "bypass"; + }; + + ipa_smmu_wlan: ipa_smmu_wlan { + compatible = "qcom,ipa-smmu-wlan-cb"; + iommus = <&apps_smmu 0x721 0x0>; + /* ipa-uc ram */ + qcom,additional-mapping = <0x1E60000 0x1E60000 0x80000>; + qcom,iommu-dma = "bypass"; + }; + + ipa_smmu_uc: ipa_smmu_uc { + compatible = "qcom,ipa-smmu-uc-cb"; + iommus = <&apps_smmu 0x722 0x0>; + qcom,iommu-dma-addr-pool = <0x40000000 0x20000000>; + qcom,iommu-dma = "bypass"; + }; + }; + + qcom,ipa_fws { + compatible = "qcom,pil-tz-generic"; + qcom,pas-id = <0xf>; + qcom,firmware-name = "ipa_fws"; + qcom,pil-force-shutdown; + memory-region = <&pil_ipa_fw_mem>; + }; + + qcom,chd_sliver { + compatible = "qcom,core-hang-detect"; + label = "silver"; + qcom,threshold-arr = <0x17e00058 0x17e10058 + 0x17e20058 0x17e30058>; + qcom,config-arr = <0x17e00060 0x17e10060 + 0x17e20060 0x17e30060>; + }; + + qcom,chd_gold { + compatible = "qcom,core-hang-detect"; + label = "gold"; + qcom,threshold-arr = <0x17e40058 0x17e50058 + 0x17e60058 0x17e70058>; + qcom,config-arr = <0x17e40060 0x17e50060 + 0x17e60060 0x17e70060>; + }; + + qcom,ghd { + compatible = "qcom,gladiator-hang-detect-v2"; + qcom,threshold-arr = <0x1799041c 0x17990420>; + qcom,config-reg = <0x17990434>; + }; + + qcom,msm-gladiator-v3@17900000 { + compatible = "qcom,msm-gladiator-v3"; + reg = <0x17900000 0xd080>; + reg-names = "gladiator_base"; + interrupts = ; + }; + + dcc: dcc_v2@10a2000 { + compatible = "qcom,dcc-v2"; + reg = <0x10a2000 0x1000>, + <0x10ae000 0x2000>; + reg-names = "dcc-base", "dcc-ram-base"; + + dcc-ram-offset = <0x6000>; + + qcom,curr-link-list = <2>; + qcom,link-list = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + + qcom,msm-core@780000 { + compatible = "qcom,apss-core-ea"; + reg = <0x780000 0x1000>; + }; + + qcom,icnss@18800000 { + compatible = "qcom,icnss"; + reg = <0x18800000 0x800000>, + <0xb0000000 0x10000>; + reg-names = "membase", "smmu_iova_ipa"; + iommus = <&apps_smmu 0x0040 0x1>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + qcom,iommu-dma-addr-pool = <0xa0000000 0x10000000>; + qcom,iommu-dma = "bypass"; + qcom,iommu-faults = "stall-disable", "non-fatal"; + qcom,hyp_enabled; + qcom,wlan-msa-memory = <0x100000>; + + vdd-cx-mx-supply = <&pm8998_l5>; + vdd-1.8-xo-supply = <&pm8998_l7>; + vdd-1.3-rfa-supply = <&pm8998_l17>; + vdd-3.3-ch0-supply = <&pm8998_l25>; + qcom,vdd-cx-mx-config = <800000 800000>; + qcom,vdd-3.3-ch0-config = <3104000 3312000>; + + qcom,smp2p_map_wlan_1_in { + interrupts-extended = <&smp2p_wlan_1_in 0 IRQ_TYPE_LEVEL_HIGH>, + <&smp2p_wlan_1_in 1 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "qcom,smp2p-force-fatal-error", + "qcom,smp2p-early-crash-ind"; + }; + }; + + qmi-tmd-devices { + compatible = "qcom,qmi-cooling-devices"; + + modem { + qcom,instance-id = <0x0>; + + modem_pa: modem_pa { + qcom,qmi-dev-name = "pa"; + #cooling-cells = <2>; + }; + + modem_proc: modem_proc { + qcom,qmi-dev-name = "modem"; + #cooling-cells = <2>; + }; + + modem_current: modem_current { + qcom,qmi-dev-name = "modem_current"; + #cooling-cells = <2>; + }; + + modem_skin: modem_skin { + qcom,qmi-dev-name = "modem_skin"; + #cooling-cells = <2>; + }; + + modem_vdd: modem_vdd { + qcom,qmi-dev-name = "cpuv_restriction_cold"; + #cooling-cells = <2>; + }; + }; + + adsp { + qcom,instance-id = <0x1>; + + adsp_vdd: adsp_vdd { + qcom,qmi-dev-name = "cpuv_restriction_cold"; + #cooling-cells = <2>; + }; + }; + + cdsp { + qcom,instance-id = <0x43>; + + cdsp_vdd: cdsp_vdd { + qcom,qmi-dev-name = "cpuv_restriction_cold"; + #cooling-cells = <2>; + }; + }; + + slpi { + qcom,instance-id = <0x53>; + + slpi_vdd: slpi_vdd { + qcom,qmi-dev-name = "cpuv_restriction_cold"; + #cooling-cells = <2>; + }; + }; + }; + + thermal_zones: thermal-zones { + aoss0-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 0>; + wake-capable-sensor; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpu0-silver-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 1>; + wake-capable-sensor; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu1-silver-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 2>; + wake-capable-sensor; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu2-silver-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 3>; + wake-capable-sensor; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu3-silver-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 4>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + kryo-l3-0-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 5>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + kryo-l3-1-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 6>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpu0-gold-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 7>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu1-gold-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 8>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu2-gold-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 9>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + cpu3-gold-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 10>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + gpu0-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 11>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + gpu1-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 12>; + wake-capable-sensor; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + + thermal-hal-cfg { + temperature = <115000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + }; + + aoss1-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 0>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mdm-dsp-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 1>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + + + ddr-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 2>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + wlan-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 3>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + compute-hvx-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 4>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + camera-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 5>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mmss-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 6>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mdm-core-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens1 7>; + wake-capable-sensor; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + gpu-virt-max-step { + polling-delay-passive = <10>; + polling-delay = <0>; + thermal-governor = "step_wise"; + disable-thermal-zone; + wake-capable-sensor; + trips { + gpu_trip0: gpu-trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + }; + cooling-maps { + gpu_cdev0 { + trip = <&gpu_trip0>; + cooling-device = + <&msm_gpu 0 THERMAL_NO_LIMIT>; + }; + }; + }; + + silv-virt-max-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "step_wise"; + wake-capable-sensor; + trips { + silver-trip { + temperature = <120000>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; + + gold-virt-max-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "step_wise"; + wake-capable-sensor; + trips { + gold-trip { + temperature = <120000>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; + + pop-mem-step { + polling-delay-passive = <10>; + polling-delay = <0>; + thermal-sensors = <&tsens1 2>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + pop_trip: pop-trip { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + }; + cooling-maps { + pop_cdev4 { + trip = <&pop_trip>; + cooling-device = + <&CPU4 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + pop_cdev5 { + trip = <&pop_trip>; + cooling-device = + <&CPU5 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + pop_cdev6 { + trip = <&pop_trip>; + cooling-device = + <&CPU6 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + pop_cdev7 { + trip = <&pop_trip>; + cooling-device = + <&CPU7 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + }; + }; + + cpu0-silver-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 1>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config0: emerg-config0 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev0 { + trip = <&emerg_config0>; + cooling-device = <&cpu0_isolate 1 1>; + }; + }; + }; + + cpu1-silver-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 2>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config1: emerg-config1 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev1 { + trip = <&emerg_config1>; + cooling-device = <&cpu1_isolate 1 1>; + }; + }; + }; + + cpu2-silver-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 3>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config2: emerg-config2 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev2 { + trip = <&emerg_config2>; + cooling-device = <&cpu2_isolate 1 1>; + }; + }; + }; + + cpu3-silver-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 4>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config3: emerg-config3 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev3 { + trip = <&emerg_config3>; + cooling-device = <&cpu3_isolate 1 1>; + }; + }; + }; + + cpu0-gold-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 7>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config4: emerg-config4 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev4 { + trip = <&emerg_config4>; + cooling-device = <&cpu4_isolate 1 1>; + }; + }; + }; + + cpu1-gold-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 8>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config5: emerg-config5 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev5 { + trip = <&emerg_config5>; + cooling-device = <&cpu5_isolate 1 1>; + }; + }; + }; + + cpu2-gold-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 9>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config6: emerg-config6 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev6 { + trip = <&emerg_config6>; + cooling-device = <&cpu6_isolate 1 1>; + }; + }; + }; + + cpu3-gold-step { + polling-delay-passive = <100>; + polling-delay = <0>; + thermal-sensors = <&tsens0 10>; + wake-capable-sensor; + thermal-governor = "step_wise"; + trips { + emerg_config7: emerg-config7 { + temperature = <110000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + cooling-maps { + emerg_cdev7 { + trip = <&emerg_config7>; + cooling-device = <&cpu7_isolate 1 1>; + }; + }; + }; + + lmh-dcvs-01 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&lmh_dcvs1>; + wake-capable-sensor; + + trips { + active-config { + temperature = <95000>; + hysteresis = <30000>; + type = "passive"; + }; + }; + }; + + lmh-dcvs-00 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&lmh_dcvs0>; + wake-capable-sensor; + + trips { + active-config { + temperature = <95000>; + hysteresis = <30000>; + type = "passive"; + }; + }; + }; + + }; + + tsens0: tsens@c222000 { + compatible = "qcom,sdm845-tsens"; + reg = <0xc222000 0x4>, + <0xc263000 0x1ff>; + reg-names = "tsens_srot_physical", + "tsens_tm_physical"; + interrupts = , + ; + interrupt-names = "tsens-upper-lower", "tsens-critical"; + #thermal-sensor-cells = <1>; + }; + + tsens1: tsens@c223000 { + compatible = "qcom,sdm845-tsens"; + reg = <0xc223000 0x4>, + <0xc265000 0x1ff>; + reg-names = "tsens_srot_physical", + "tsens_tm_physical"; + interrupts = , + ; + interrupt-names = "tsens-upper-lower", "tsens-critical"; + #thermal-sensor-cells = <1>; + }; + + mem_dump { + compatible = "qcom,mem-dump"; + memory-region = <&dump_mem>; + + rpmh { + qcom,dump-size = <0x2000000>; + qcom,dump-id = <0xec>; + }; + + fcm { + qcom,dump-size = <0x8400>; + qcom,dump-id = <0xee>; + }; + + rpm_sw { + qcom,dump-size = <0x28000>; + qcom,dump-id = <0xea>; + }; + + pmic { + qcom,dump-size = <0x10000>; + qcom,dump-id = <0xe4>; + }; + + tmc_etf { + qcom,dump-size = <0x10000>; + qcom,dump-id = <0xf0>; + }; + + tmc_etfswao { + qcom,dump-size = <0x8400>; + qcom,dump-id = <0xf1>; + }; + + tmc_etr_reg { + qcom,dump-size = <0x1000>; + qcom,dump-id = <0x100>; + }; + + tmc_etf_reg { + qcom,dump-size = <0x1000>; + qcom,dump-id = <0x101>; + }; + + etfswao_reg { + qcom,dump-size = <0x1000>; + qcom,dump-id = <0x102>; + }; + + misc_data { + qcom,dump-size = <0x1000>; + qcom,dump-id = <0xe8>; + }; + + tpdm_swao { + qcom,dump-size = <0x512>; + qcom,dump-id = <0xf2>; + }; + }; + + gpi_dma0: qcom,gpi-dma@0x800000 { + #dma-cells = <5>; + compatible = "qcom,gpi-dma"; + reg = <0x800000 0x60000>; + reg-names = "gpi-top"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + ; + qcom,max-num-gpii = <13>; + qcom,gpii-mask = <0xfa>; + qcom,ev-factor = <2>; + iommus = <&apps_smmu 0x0016 0x0>; + qcom,smmu-cfg = <0x1>; + qcom,iommu-dma-addr-pool = <0x100000 0x100000>; + status = "ok"; + }; + + gpi_dma1: qcom,gpi-dma@0xa00000 { + #dma-cells = <5>; + compatible = "qcom,gpi-dma"; + reg = <0xa00000 0x60000>; + reg-names = "gpi-top"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + ; + qcom,max-num-gpii = <13>; + qcom,gpii-mask = <0xfa>; + qcom,ev-factor = <2>; + iommus = <&apps_smmu 0x06d6 0x0>; + qcom,smmu-cfg = <0x1>; + qcom,iommu-dma-addr-pool = <0x100000 0x100000>; + status = "ok"; + }; + + tspp: msm_tspp@0x8880000 { + compatible = "qcom,msm_tspp"; + reg = <0x088a7000 0x200>, /* MSM_TSIF0_PHYS */ + <0x088a8000 0x200>, /* MSM_TSIF1_PHYS */ + <0x088a9000 0x1000>, /* MSM_TSPP_PHYS */ + <0x08884000 0x23000>; /* MSM_TSPP_BAM_PHYS */ + reg-names = "MSM_TSIF0_PHYS", + "MSM_TSIF1_PHYS", + "MSM_TSPP_PHYS", + "MSM_TSPP_BAM_PHYS"; + interrupts = , /* TSIF_TSPP_IRQ */ + , /* TSIF0_IRQ */ + , /* TSIF1_IRQ */ + ; /* TSIF_BAM_IRQ */ + interrupt-names = "TSIF_TSPP_IRQ", + "TSIF0_IRQ", + "TSIF1_IRQ", + "TSIF_BAM_IRQ"; + + clock-names = "iface_clk", "ref_clk"; + clocks = <&clock_gcc GCC_TSIF_AHB_CLK>, + <&clock_gcc GCC_TSIF_REF_CLK>; + + qcom,msm-bus,name = "tsif"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <82 512 0 0>, /* No vote */ + <82 512 12288 24576>; + /* Max. bandwidth, 2xTSIF, each max of 96Mbps */ + + pinctrl-names = "disabled", + "tsif0-mode1", "tsif0-mode2", + "tsif1-mode1", "tsif1-mode2", + "dual-tsif-mode1", "dual-tsif-mode2"; + + pinctrl-0 = <>; /* disabled */ + pinctrl-1 = <&tsif0_signals_active>; /* tsif0-mode1 */ + pinctrl-2 = <&tsif0_signals_active + &tsif0_sync_active>; /* tsif0-mode2 */ + pinctrl-3 = <&tsif1_signals_active>; /* tsif1-mode1 */ + pinctrl-4 = <&tsif1_signals_active + &tsif1_sync_active>; /* tsif1-mode2 */ + pinctrl-5 = <&tsif0_signals_active + &tsif1_signals_active>; /* dual-tsif-mode1 */ + pinctrl-6 = <&tsif0_signals_active + &tsif0_sync_active + &tsif1_signals_active + &tsif1_sync_active>; /* dual-tsif-mode2 */ + + iommus = <&apps_smmu 0x20 0x0f>; + qcom,iommu-dma-addr-pool = <0x10000000 0x40000000>; + qcom,smmu-s1-enable; + }; +}; + +&cpufreq_hw { + lmh_dcvs0: qcom,limits-dcvs@0 { + compatible = "qcom,msm-hw-limits"; + interrupts = ; + reg = <0x17d78800 0x1000>, + <0x17d43000 0x1000>; + qcom,affinity = <0>; + #thermal-sensor-cells = <0>; + qcom,legacy-lmh-enable; + qcom,no-cooling-device-register; + }; + + lmh_dcvs1: qcom,limits-dcvs@1 { + compatible = "qcom,msm-hw-limits"; + interrupts = ; + qcom,affinity = <1>; + reg = <0x17d70800 0x1000>, + <0x17d45800 0x1000>; + #thermal-sensor-cells = <0>; + isens_vref_0p8-supply = <&pm8998_l1_ao>; + isens-vref-0p8-settings = <880000 880000 20000>; + qcom,legacy-lmh-enable; + qcom,no-cooling-device-register; + }; + + + qcom,cpu-isolation { + compatible = "qcom,cpu-isolate"; + cpu0_isolate: cpu0-isolate { + qcom,cpu = <&CPU0>; + #cooling-cells = <2>; + }; + + cpu1_isolate: cpu1-isolate { + qcom,cpu = <&CPU1>; + #cooling-cells = <2>; + }; + + cpu2_isolate: cpu2-isolate { + qcom,cpu = <&CPU2>; + #cooling-cells = <2>; + }; + + cpu3_isolate: cpu3-isolate { + qcom,cpu = <&CPU3>; + #cooling-cells = <2>; + }; + + cpu4_isolate: cpu4-isolate { + qcom,cpu = <&CPU4>; + #cooling-cells = <2>; + }; + + cpu5_isolate: cpu5-isolate { + qcom,cpu = <&CPU5>; + #cooling-cells = <2>; + }; + + cpu6_isolate: cpu6-isolate { + qcom,cpu = <&CPU6>; + #cooling-cells = <2>; + }; + + cpu7_isolate: cpu7-isolate { + qcom,cpu = <&CPU7>; + #cooling-cells = <2>; + }; + }; + + wil6210: qcom,wil6210 { + compatible = "qcom,wil6210"; + qcom,pcie-parent = <&pcie0>; + qcom,wigig-en = <&tlmm 39 0>; + qcom,msm-bus,name = "wil6210"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <45 512 0 0>, + <45 512 600000 800000>; /* ~4.6Gbps (MCS12) */ + qcom,use-ext-supply; + vdd-supply= <&pm8998_s7>; + vddio-supply= <&pm8998_s5>; + qcom,use-ext-clocks; + clocks = <&clock_rpmh RPMH_RF_CLK3>, + <&clock_rpmh RPMH_RF_CLK3_A>; + clock-names = "rf_clk3_clk", "rf_clk3_pin_clk"; + qcom,smmu-support; + qcom,smmu-mapping = <0x20000000 0xe0000000>; + qcom,smmu-s1-en; + qcom,smmu-fast-map; + qcom,smmu-coherent; + qcom,keep-radio-on-during-sleep; + status = "disabled"; + }; +}; + +&pcie_0_gdsc { + status = "ok"; +}; + +&pcie_1_gdsc { + status = "ok"; +}; + +&ufs_card_gdsc { + status = "ok"; +}; + +&ufs_phy_gdsc { + status = "ok"; +}; + +&usb30_prim_gdsc { + status = "ok"; +}; + +&usb30_sec_gdsc { + status = "ok"; +}; + +&hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc { + status = "ok"; +}; + +&hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc { + status = "ok"; +}; + +&hlos1_vote_aggre_noc_mmu_tbu1_gdsc { + status = "ok"; +}; + +&hlos1_vote_aggre_noc_mmu_tbu2_gdsc { + status = "ok"; +}; + +&hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc { + status = "ok"; +}; + +&hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc { + status = "ok"; +}; + +&hlos1_vote_mmnoc_mmu_tbu_sf_gdsc { + status = "ok"; +}; + +&bps_gdsc { + qcom,support-hw-trigger; + status = "ok"; +}; + +&ife_0_gdsc { + status = "ok"; +}; + +&ife_1_gdsc { + status = "ok"; +}; + +&ipe_0_gdsc { + qcom,support-hw-trigger; + status = "ok"; +}; + +&ipe_1_gdsc { + qcom,support-hw-trigger; + status = "ok"; +}; + +&titan_top_gdsc { + status = "ok"; +}; + +&mdss_core_gdsc { + status = "ok"; + qcom,en-few-wait-val = <6>; + qcom,en-rest-wait-val = <5>; +}; + +&gpu_cx_gdsc { + parent-supply = <&pm8998_s9_level>; + vdd_parent-supply = <&pm8998_s9_level>; + status = "ok"; +}; + +&gpu_gx_gdsc { + clock-names = "core_root_clk"; + clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK_SRC>; + qcom,force-enable-root-clk; + parent-supply = <&pm8005_s1_level>; + status = "ok"; +}; + +&vcodec0_gdsc { + qcom,support-hw-trigger; + status = "ok"; +}; + +&vcodec1_gdsc { + qcom,support-hw-trigger; + status = "ok"; +}; + +&venus_gdsc { + status = "ok"; +}; + +#include "pm8998.dtsi" +#include "pm8005.dtsi" +#include "sdm845-regulator.dtsi" +#include "msm-arm-smmu-sdm845.dtsi" +#include "sdm845-ion.dtsi" +#include "sdm845-smp2p.dtsi" +#include "sdm845-camera.dtsi" +#include "sdm845-bus.dtsi" +#include "sdm845-vidc.dtsi" +#include "sdm845-pm.dtsi" +#include "sdm845-pinctrl.dtsi" +#include "sdm845-pcie.dtsi" +#include "sdm845-audio.dtsi" +#include "sdm845-gpu.dtsi" +#include "sdm845-670-usb-common.dtsi" + +&pm8998_temp_alarm { + cooling-maps { + trip0_cpu0 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU0 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu1 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU1 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu2 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU2 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu3 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU3 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu4 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU4 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu5 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU5 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu6 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU6 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu7 { + trip = <&pm8998_trip0>; + cooling-device = + <&CPU7 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip1_cpu1 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU1 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu2 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU2 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu3 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU3 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu4 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu5 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu6 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + trip1_cpu7 { + trip = <&pm8998_trip1>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT THERMAL_MAX_LIMIT>; + }; + }; +}; + +&thermal_zones { + aoss0-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 0>; + wake-capable-sensor; + tracks-low; + trips { + aoss0_trip: aoss0-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu0-silver-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 1>; + wake-capable-sensor; + tracks-low; + trips { + cpu0_trip: cpu0-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpu0_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu1-silver-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 2>; + wake-capable-sensor; + tracks-low; + trips { + cpu1_trip: cpu1-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpu1_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu2-silver-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 3>; + wake-capable-sensor; + tracks-low; + trips { + cpu2_trip: cpu2-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpu2_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu3-silver-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 4>; + wake-capable-sensor; + tracks-low; + trips { + cpu3_trip: cpu3-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpu3_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + kryo-l3-0-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 5>; + wake-capable-sensor; + tracks-low; + trips { + l3_0_trip: l3-0-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&l3_0_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + kryo-l3-1-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 6>; + wake-capable-sensor; + tracks-low; + trips { + l3_1_trip: l3-1-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&l3_1_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu0-gold-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 7>; + wake-capable-sensor; + tracks-low; + trips { + cpug0_trip: cpug0-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpug0_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu1-gold-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 8>; + wake-capable-sensor; + tracks-low; + trips { + cpug1_trip: cpug1-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpug1_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu2-gold-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 9>; + wake-capable-sensor; + tracks-low; + trips { + cpug2_trip: cpug2-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpug2_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + cpu3-gold-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 10>; + wake-capable-sensor; + tracks-low; + trips { + cpug3_trip: cpug3-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&cpug3_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + gpu0-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 11>; + wake-capable-sensor; + tracks-low; + trips { + gpu0_trip_l: gpu0-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&gpu0_trip_l>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + gpu1-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 12>; + wake-capable-sensor; + tracks-low; + trips { + gpu1_trip_l: gpu1-trip_l { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&gpu1_trip_l>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + aoss1-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 0>; + wake-capable-sensor; + tracks-low; + trips { + aoss1_trip: aoss1-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + mdm-dsp-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 1>; + wake-capable-sensor; + tracks-low; + trips { + dsp_trip: dsp-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&dsp_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + ddr-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 2>; + wake-capable-sensor; + tracks-low; + trips { + ddr_trip: ddr-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + wlan-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 3>; + wake-capable-sensor; + tracks-low; + trips { + wlan_trip: wlan-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&wlan_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + compute-hvx-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 4>; + wake-capable-sensor; + tracks-low; + trips { + hvx_trip: hvx-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&hvx_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + camera-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 5>; + wake-capable-sensor; + tracks-low; + trips { + camera_trip: camera-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + mmss-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 6>; + wake-capable-sensor; + tracks-low; + trips { + mmss_trip: mmss-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&mmss_trip>; + cooling-device = <&slpi_vdd 0 0>; + }; + }; + }; + + mdm-core-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens1 7>; + wake-capable-sensor; + tracks-low; + trips { + mdm_trip: mdm-trip { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&CPU0 4 4>; + }; + cpu4_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&CPU4 9 9>; + }; + gpu_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&msm_gpu 1 1>; + }; + cx_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&cx_cdev 0 0>; + }; + mx_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&mx_cdev 0 0>; + }; + ebi_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&ebi_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&modem_vdd 0 0>; + }; + adsp_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&adsp_vdd 0 0>; + }; + cdsp_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&cdsp_vdd 0 0>; + }; + slpi_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&slpi_vdd 0 0>; }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845_enchilada_soc.dtsi b/arch/arm64/boot/dts/qcom/sdm845_enchilada_soc.dtsi new file mode 100644 index 000000000000..0ea3067f85b7 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845_enchilada_soc.dtsi @@ -0,0 +1,45 @@ +/* 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. + */ + + +/{ + reserved-memory { + bootloader_log_mem: bootloader_log_mem@0x9FFF7000 { + reg = <0 0x9FFF7000 0 0x00100000>; + label = "bootloader_log_mem"; + }; + param_mem: param_mem@ac200000 { + reg = <0 0xAC200000 0 0x00100000>; + label = "param_mem"; + }; + ramoops { + compatible = "removed-dma-pool", "ramoops"; + no-map; + reg = <0x0 0xAC300000 0x0 0x00400000>; + status = "ok"; + console-size = <0x200000>; + pmsg-size = <0x200000>; + }; + mtp_mem: mtp_mem@ac700000 { + reg = <0 0xAC700000 0 0x00B00000>; + label = "mtp_mem"; + }; + }; +}; + +&soc { +bootloader_log { + compatible = "bootloader_log"; + linux,contiguous-region = <&bootloader_log_mem>; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/smb1355.dtsi b/arch/arm64/boot/dts/qcom/smb1355.dtsi new file mode 100644 index 000000000000..a4a90f174bc3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/smb1355.dtsi @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +#include + +smb1355_0: qcom,smb1355@8 { + compatible = "qcom,i2c-pmic"; + reg = <0x8>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt_names = "smb1355_0"; + interrupt-controller; + #interrupt-cells = <3>; + qcom,periph-map = <0x10 0x12 0x13 0x16>; + + smb1355_revid_0: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + smb1355_charger_0: qcom,smb1355-charger@1000 { + compatible = "qcom,smb1355"; + qcom,pmic-revid = <&smb1355_revid_0>; + reg = <0x1000 0x700>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&smb1355_0>; + status = "disabled"; + + io-channels = <&pmi8998_rradc 2>, + <&pmi8998_rradc 12>; + io-channel-names = "charger_temp", + "charger_temp_max"; + + qcom,chgr@1000 { + reg = <0x1000 0x100>; + interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "chg-state-change"; + }; + + qcom,chgr-misc@1600 { + reg = <0x1600 0x100>; + interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>, + <0x16 0x6 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog-bark", + "temperature-change"; + }; + }; +}; + +smb1355_1: qcom,smb1355@c { + compatible = "qcom,i2c-pmic"; + reg = <0xc>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt_names = "smb1355_1"; + interrupt-controller; + #interrupt-cells = <3>; + qcom,periph-map = <0x10 0x12 0x13 0x16>; + + smb1355_revid_1: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + smb1355_charger_1: qcom,smb1355-charger@1000 { + compatible = "qcom,smb1355"; + qcom,pmic-revid = <&smb1355_revid_1>; + reg = <0x1000 0x700>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&smb1355_1>; + status = "disabled"; + + io-channels = <&pmi8998_rradc 2>, + <&pmi8998_rradc 12>; + io-channel-names = "charger_temp", + "charger_temp_max"; + + qcom,chgr@1000 { + reg = <0x1000 0x100>; + interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "chg-state-change"; + }; + + qcom,chgr-misc@1600 { + reg = <0x1600 0x100>; + interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>, + <0x16 0x6 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog-bark", + "temperature-change"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/vendor/bindings/platform/msm/qpnp-coincell.txt b/arch/arm64/boot/dts/vendor/bindings/platform/msm/qpnp-coincell.txt new file mode 100644 index 000000000000..4d55f0cecefe --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/platform/msm/qpnp-coincell.txt @@ -0,0 +1,44 @@ +Qualcomm Technologies, Inc. QPNP Coincell - coincell battery charger devices + +Required properties: +- compatible: Must be "qcom,qpnp-coincell". +- reg: Specifies the SPMI address and size for this coincell device. + +Required structure: +- A qcom,qpnp-coincell node must be a child of an SPMI node that has specified + the spmi-slave-container property. + +Optional properties: +- qcom,rset-ohms: Specifies the resistance of the current limiting + resistor in ohms. Four values are supported: + 800, 1200, 1700, and 2100. +- qcom,vset-millivolts: Specifies the coincell charging voltage in millivolts. + Four values are supported: 2500, 3000, 3100, and 3200. +- qcom,charge-enable: Specifies if coincell charging should be enabled or not. + 0 = disable charging, 1 = enabled charging + +If any of the optional properties are not specified, then the hardware default +values for the unspecified properties will be used instead. + +Example: + qcom,spmi@fc4c0000 { + #address-cells = <1>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <3>; + + qcom,pm8941@1 { + spmi-slave-container; + reg = <0x1>; + #address-cells = <1>; + #size-cells = <1>; + + qcom,coincell@2800 { + compatible = "qcom,qpnp-coincell"; + reg = <0x2800 0x100>; + qcom,rset-ohms = <800>; + qcom,vset-millivolts = <3100>; + qcom,charge-enable = <1>; + }; + }; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/power/supply/qcom/qpnp-qnovo.txt b/arch/arm64/boot/dts/vendor/bindings/power/supply/qcom/qpnp-qnovo.txt new file mode 100644 index 000000000000..8f35e56816ce --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/power/supply/qcom/qpnp-qnovo.txt @@ -0,0 +1,33 @@ +QPNP Qnovo pulse engine + +QPNP Qnovo is a pulse charging engine which works in tandem with the QPNP SMB2 +Charger device. It configures the QPNP SMB2 charger to charge/discharge as per +pulse characteristics. + +The QPNP Qnovo pulse engine has a single peripheral assigned to it. + +Required properties: +- compatible: Must be "qcom,qpnp-qnovo" +- qcom,pmic-revid: Should specify the phandle of PMIC + revid module. This is used to identify + the PMIC subtype. + +- reg: The address for this peripheral +- interrupts: Specifies the interrupt associated with the peripheral. +- interrupt-names: Specifies the interrupt name for the peripheral. Qnovo + peripheral has only one interrupt "ptrain-done". + +Optional Properties: +- qcom,external-rsense: To indicate whether the platform uses external or + internal rsense for measuring battery current. +- qcom,enable-for-dc: To enable qnovo for dc charging path. + +Example: + + qcom,qpnp-qnovo@1500 { + compatible = "qcom,qpnp-qnovo"; + reg = <0x1500 0x100>; + interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>; + interrupt-names = "ptrain-done"; + qcom,pmic-revid = <&pmi8998_revid>; + }; diff --git a/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi index f05fc5fc7827..d119d43d2f91 100644 --- a/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi +++ b/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi @@ -3,6 +3,10 @@ qcom,mdss-dsi-panel-name = "Dual nt35597 cmd mode dsi panel without DSC"; qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,mdss-dsi-panel-framerate = <60>; qcom,mdss-dsi-virtual-channel-id = <0>; qcom,mdss-dsi-stream = <0>; diff --git a/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi index 4059d721eb88..3a99197eb2f6 100644 --- a/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi +++ b/arch/arm64/boot/dts/vendor/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi @@ -3,6 +3,10 @@ qcom,mdss-dsi-panel-name = "Dual nt35597 video mode dsi panel without DSC"; qcom,mdss-dsi-panel-type = "dsi_video_mode"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,mdss-dsi-panel-framerate = <60>; qcom,mdss-dsi-virtual-channel-id = <0>; qcom,mdss-dsi-stream = <0>; diff --git a/arch/arm64/configs/vendor/enchilada_defconfig b/arch/arm64/configs/vendor/enchilada_defconfig new file mode 100644 index 000000000000..826de151b189 --- /dev/null +++ b/arch/arm64/configs/vendor/enchilada_defconfig @@ -0,0 +1,707 @@ +CONFIG_LOCALVERSION="-perf" +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_SCHED_WALT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_IKHEADERS=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_PID_NS is not set +CONFIG_SCHED_TUNE=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_IGNORE_SKIP_FLAG=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_FHANDLE is not set +CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_LSM=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_USERFAULTFD=y +# CONFIG_RSEQ is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +# CONFIG_ZONE_DMA32 is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SDM845=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_PCI_MSM_MSI=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_SECCOMP=y +CONFIG_OKL4_GUEST=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_ARM64_SSBD=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_ARM64_VHE is not set +CONFIG_RANDOMIZE_BASE=y +CONFIG_CMDLINE="cgroup_disable=pressure" +CONFIG_CMDLINE_EXTEND=y +# CONFIG_EFI is not set +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +CONFIG_BUILD_ARM64_DT_OVERLAY=y +CONFIG_KRYO_PMU_WORKAROUND=y +CONFIG_BUILD_ARM64_DT_OVERLAY=y +CONFIG_COMPAT=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_ENERGY_MODEL=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_TIMES=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_BOOST=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_ARM_QCOM_CPUFREQ_HW=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=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_JUMP_LABEL=y +CONFIG_PANIC_ON_REFCOUNT_ERROR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_BLK_INLINE_ENCRYPTION=y +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_GKI_HIDDEN_GPU_CONFIGS=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_HAVE_USERSPACE_LOW_MEMORY_KILLER=y +CONFIG_MEMFD_ASHMEM_SHIM=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_INTERFACE=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_XDP_SOCKETS=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_IPGRE_DEMUX=y +CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +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_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_DSCP=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_BPF=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_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +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_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_IP6_NF_IPTABLES=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_IP_SCTP=y +CONFIG_L2TP=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_TBF=y +CONFIG_NET_SCH_NETEM=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_CLS_BPF=y +CONFIG_NET_CLS_MATCHALL=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_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_NET_ACT_BPF=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_BPF_JIT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FW_CACHE is not set +CONFIG_REGMAP_WCD_IRQ=y +CONFIG_DMA_CMA=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_HDCP_QSEECOM=y +CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y +CONFIG_QPNP_MISC=y +CONFIG_OKL4_USER_VIRQ=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=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_CRYPTO=y +CONFIG_SCSI_UFS_CRYPTO_QTI=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_DEFAULT_KEY=y +CONFIG_DM_SNAPSHOT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_BOW=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_WIREGUARD=y +CONFIG_TUN=y +CONFIG_VETH=y +CONFIG_SKY2=y +CONFIG_RMNET=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPTP=y +CONFIG_PPPOL2TP=y +CONFIG_USB_RTL8150=y +CONFIG_USB_RTL8152=y +CONFIG_USB_LAN78XX=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CLD_LL_CORE=y +CONFIG_CNSS_GENL=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ST is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_TCM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_MSM_GENI=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set +CONFIG_DIAG_CHAR=y +CONFIG_MSM_ADSPRPC=y +CONFIG_OKL4_PIPE=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QCOM_GENI=y +CONFIG_SPI=y +CONFIG_SPI_QCOM_GENI=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_SDM845=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_SMB2=y +CONFIG_QPNP_QNOVO=y +CONFIG_QPNP_FG_GEN3=y +CONFIG_FG_BQ27541=y +CONFIG_ONEPLUS_FASTCHG=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_ADC_TM=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_QTI_THERMAL_LIMITS_DCVS=y +CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_REFGEN=y +CONFIG_REGULATOR_RPMH=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_VIDC_LEGACY_V4L2=y +CONFIG_SPECTRA_CAMERA=y +CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y +CONFIG_DRM=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PLANTRONICS=y +CONFIG_HID_PLAYSTATION=y +CONFIG_PLAYSTATION_FF=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y +CONFIG_HID_STEAM=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CP210X=y +CONFIG_USB_SERIAL_FTDI_SIO=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_LINK_LAYER_TEST=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=900 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_TYPEC=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_CQHCI_CRYPTO=y +CONFIG_MMC_CQHCI_CRYPTO_QTI=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_HAPTICS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_EDAC=y +CONFIG_EDAC_KRYO_ARM64=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_GPI_DMA=y +CONFIG_OEM_BOOT_MODE=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ION=y +CONFIG_ION_POOL_AUTO_REFILL=y +CONFIG_ION_LEGACY=y +CONFIG_QPNP_COINCELL=y +CONFIG_QPNP_REVID=y +CONFIG_QCA_CLD_WLAN=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA3=y +CONFIG_IPA_WDI_UNIFIED_API=y +CONFIG_RMNET_IPA3=y +CONFIG_RNDIS_IPA=y +CONFIG_USB_BAM=y +CONFIG_QCOM_GENI_SE=y +CONFIG_QCOM_CLK_RPMH=y +CONFIG_SPMI_PMIC_CLKDIV=y +CONFIG_MSM_CLK_AOP_QMP=y +CONFIG_MSM_GCC_SDM845=y +CONFIG_MSM_VIDEOCC_SDM845=y +CONFIG_MSM_DISPCC_SDM845=y +CONFIG_MSM_CAMCC_SDM845=y +CONFIG_MSM_GPUCC_SDM845=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_MAILBOX=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_MSM_QMP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_RUN_QUEUE_STATS=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_SDM845_LLCC=y +CONFIG_QCOM_MDT_LOADER=y +CONFIG_QCOM_QMI_HELPERS=y +CONFIG_QCOM_QMI_RMNET=y +CONFIG_QCOM_QMI_DFC=y +CONFIG_QCOM_QMI_POWER_COLLAPSE=y +CONFIG_QCOM_RPMH=y +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_SMP2P=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_SETUP_SSR_NOTIF_TIMEOUTS=y +CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 +CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 +CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y +CONFIG_QCOM_SECURE_BUFFER=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_DCC_V2=y +CONFIG_QCOM_EUD=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_QCOM_BUS_CONFIG_RPMH=y +CONFIG_MSM_SPCOM_LEGACY=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y +CONFIG_QCOM_SMP2P_SLEEPSTATE=y +CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_PM=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_QTEE_SHM_BRIDGE=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QCOM_CDSP_RM=y +CONFIG_QTI_CRYPTO_COMMON=y +CONFIG_QTI_CRYPTO_TZ=y +CONFIG_ICNSS=y +CONFIG_ICNSS_QMI=y +CONFIG_RF_CABLE_DETECT=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_GOV_CDSPL3=y +CONFIG_ARM_QCOM_DEVFREQ_FW=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_TRI_STATE_KEY=y +CONFIG_IIO=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_QCOM_RRADC=y +CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_QCOM_PDC=y +CONFIG_RAS=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDERFS=y +CONFIG_ANDROID_VENDOR_HOOKS=y +# CONFIG_NVMEM_SYSFS is not set +CONFIG_QCOM_QFPROM=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_SENSORS_SSC=y +CONFIG_FINGERPRINT_DETECT=y +CONFIG_FINGERPRINT_FPC=y +CONFIG_FINGERPRINT_GOODIX=y +CONFIG_QCOM_KGSL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_UNFAIR_RWSEM=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_INCREMENTAL_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_CMDLINE_APPEND_ANDROID_FORCE_NORMAL_BOOT=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y +CONFIG_EROFS_FS=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_UNICODE=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_STATIC_USERMODEHELPER=y +CONFIG_STATIC_USERMODEHELPER_PATH="" +CONFIG_SECURITY_SELINUX=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_LZ4=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_DRBG_CTR=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=-1 +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_IPC_LOGGING=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_CC_WERROR=y +CONFIG_DEBUG_ALIGN_RODATA=y diff --git a/arch/arm64/configs/vendor/sdm845-perf_defconfig b/arch/arm64/configs/vendor/sdm845-perf_defconfig new file mode 100644 index 000000000000..51b3327b36bc --- /dev/null +++ b/arch/arm64/configs/vendor/sdm845-perf_defconfig @@ -0,0 +1,687 @@ +CONFIG_LOCALVERSION="-perf" +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_SCHED_WALT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_IKHEADERS=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_PID_NS is not set +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_FHANDLE is not set +CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_LSM=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_USERFAULTFD=y +# CONFIG_RSEQ is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +# CONFIG_ZONE_DMA32 is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SDM845=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_PCI_MSM_MSI=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_SECCOMP=y +CONFIG_OKL4_GUEST=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_ARM64_SSBD=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_ARM64_VHE is not set +CONFIG_RANDOMIZE_BASE=y +CONFIG_CMDLINE="cgroup_disable=pressure" +CONFIG_CMDLINE_EXTEND=y +# CONFIG_EFI is not set +CONFIG_KRYO_PMU_WORKAROUND=y +CONFIG_BUILD_ARM64_DT_OVERLAY=y +CONFIG_COMPAT=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_ENERGY_MODEL=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_TIMES=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_BOOST=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_ARM_QCOM_CPUFREQ_HW=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=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_JUMP_LABEL=y +CONFIG_PANIC_ON_REFCOUNT_ERROR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_BLK_INLINE_ENCRYPTION=y +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_GKI_HIDDEN_GPU_CONFIGS=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_HAVE_USERSPACE_LOW_MEMORY_KILLER=y +CONFIG_MEMFD_ASHMEM_SHIM=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_INTERFACE=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_XDP_SOCKETS=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_IPGRE_DEMUX=y +CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +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_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_DSCP=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_BPF=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_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +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_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_IP6_NF_IPTABLES=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_IP_SCTP=y +CONFIG_L2TP=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_TBF=y +CONFIG_NET_SCH_NETEM=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_CLS_BPF=y +CONFIG_NET_CLS_MATCHALL=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_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_NET_ACT_BPF=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_BPF_JIT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FW_CACHE is not set +CONFIG_REGMAP_WCD_IRQ=y +CONFIG_DMA_CMA=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_HDCP_QSEECOM=y +CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y +CONFIG_QPNP_MISC=y +CONFIG_OKL4_USER_VIRQ=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=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_CRYPTO=y +CONFIG_SCSI_UFS_CRYPTO_QTI=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_DEFAULT_KEY=y +CONFIG_DM_SNAPSHOT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_BOW=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_WIREGUARD=y +CONFIG_TUN=y +CONFIG_VETH=y +CONFIG_SKY2=y +CONFIG_RMNET=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPTP=y +CONFIG_PPPOL2TP=y +CONFIG_USB_RTL8150=y +CONFIG_USB_RTL8152=y +CONFIG_USB_LAN78XX=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CLD_LL_CORE=y +CONFIG_CNSS_GENL=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_FTS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_QTI_HAPTICS=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_MSM_GENI=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set +CONFIG_DIAG_CHAR=y +CONFIG_MSM_ADSPRPC=y +CONFIG_OKL4_PIPE=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QCOM_GENI=y +CONFIG_SPI=y +CONFIG_SPI_QCOM_GENI=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_SDM845=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_SMB2=y +CONFIG_SMB1355_SLAVE_CHARGER=y +CONFIG_QPNP_QNOVO=y +CONFIG_QPNP_FG_GEN3=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_ADC_TM=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_QTI_THERMAL_LIMITS_DCVS=y +CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_REFGEN=y +CONFIG_REGULATOR_RPMH=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_VIDC_LEGACY_V4L2=y +CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y +CONFIG_DRM=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PLANTRONICS=y +CONFIG_HID_PLAYSTATION=y +CONFIG_PLAYSTATION_FF=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y +CONFIG_HID_STEAM=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CP210X=y +CONFIG_USB_SERIAL_FTDI_SIO=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_LINK_LAYER_TEST=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=900 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_TYPEC=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_CQHCI_CRYPTO=y +CONFIG_MMC_CQHCI_CRYPTO_QTI=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_EDAC=y +CONFIG_EDAC_KRYO_ARM64=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_GPI_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ION=y +CONFIG_ION_POOL_AUTO_REFILL=y +CONFIG_ION_LEGACY=y +CONFIG_QPNP_COINCELL=y +CONFIG_QPNP_REVID=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA3=y +CONFIG_IPA_WDI_UNIFIED_API=y +CONFIG_RMNET_IPA3=y +CONFIG_RNDIS_IPA=y +CONFIG_USB_BAM=y +CONFIG_QCOM_GENI_SE=y +CONFIG_QCOM_CLK_RPMH=y +CONFIG_SPMI_PMIC_CLKDIV=y +CONFIG_MSM_CLK_AOP_QMP=y +CONFIG_MSM_GCC_SDM845=y +CONFIG_MSM_VIDEOCC_SDM845=y +CONFIG_MSM_DISPCC_SDM845=y +CONFIG_MSM_CAMCC_SDM845=y +CONFIG_MSM_GPUCC_SDM845=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_MAILBOX=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_MSM_QMP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_RUN_QUEUE_STATS=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_SDM845_LLCC=y +CONFIG_QCOM_MDT_LOADER=y +CONFIG_QCOM_QMI_HELPERS=y +CONFIG_QCOM_QMI_RMNET=y +CONFIG_QCOM_QMI_DFC=y +CONFIG_QCOM_QMI_POWER_COLLAPSE=y +CONFIG_QCOM_RPMH=y +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_SMP2P=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_SETUP_SSR_NOTIF_TIMEOUTS=y +CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 +CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 +CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y +CONFIG_QCOM_SECURE_BUFFER=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_DCC_V2=y +CONFIG_QCOM_EUD=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_QCOM_BUS_CONFIG_RPMH=y +CONFIG_MSM_SPCOM_LEGACY=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y +CONFIG_QCOM_SMP2P_SLEEPSTATE=y +CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_PM=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_QTEE_SHM_BRIDGE=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QCOM_CDSP_RM=y +CONFIG_QTI_CRYPTO_COMMON=y +CONFIG_QTI_CRYPTO_TZ=y +CONFIG_ICNSS=y +CONFIG_ICNSS_QMI=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_GOV_CDSPL3=y +CONFIG_ARM_QCOM_DEVFREQ_FW=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_IIO=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_QCOM_RRADC=y +CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_QCOM_PDC=y +CONFIG_RAS=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDERFS=y +CONFIG_ANDROID_VENDOR_HOOKS=y +# CONFIG_NVMEM_SYSFS is not set +CONFIG_QCOM_QFPROM=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_SENSORS_SSC=y +CONFIG_QCOM_KGSL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_UNFAIR_RWSEM=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_INCREMENTAL_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y +CONFIG_EROFS_FS=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_UNICODE=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_STATIC_USERMODEHELPER=y +CONFIG_STATIC_USERMODEHELPER_PATH="" +CONFIG_SECURITY_SELINUX=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_LZ4=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_DRBG_CTR=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=-1 +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_IPC_LOGGING=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_CC_WERROR=y +CONFIG_DEBUG_ALIGN_RODATA=y diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index cd2040d48a4c..ad561e5f8b64 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -115,6 +115,7 @@ static inline void gic_write_bpr1(u32 val) } #define gic_read_typer(c) readq_relaxed_no_log(c) +#define gic_read_irouter(c) readq_relaxed_no_log(c) #define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c) #define gic_read_lpir(c) readq_relaxed_no_log(c) #define gic_write_lpir(v, c) writeq_relaxed_no_log(v, c) diff --git a/drivers/Kconfig b/drivers/Kconfig index e0866eda8bbd..3918ba782a80 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -232,4 +232,6 @@ source "drivers/sensors/Kconfig" source "drivers/gpu/msm/Kconfig" source "drivers/energy_model/Kconfig" + +source "drivers/oneplus/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 9a258f18ec6f..0670ba8ab248 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -192,3 +192,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_SENSORS_SSC) += sensors/ +obj-y += oneplus/ diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 94b96a9a4e66..79a2d944cf22 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -287,7 +287,8 @@ static const char * const fw_path[] = { "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, - "/lib/firmware" + "/lib/firmware", + "/vendor/etc/firmware/" }; /* diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 6677a88ced77..cb3b8f31759e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2357,6 +2357,8 @@ static int clk_change_rate(struct clk_core *core) if (core->flags & CLK_RECALC_NEW_RATES) (void)clk_calc_new_rates(core, core->new_rate); + if (core->flags & CLK_CHILD_NO_RATE_PROP) + return rc; /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. @@ -4685,6 +4687,7 @@ static int clk_add_and_print_opp(struct clk_hw *hw, unsigned long rate, int uv, int n) { struct clk_core *core = hw->core; + unsigned long rrate; int j, ret = 0; for (j = 0; j < count; j++) { @@ -4695,8 +4698,11 @@ static int clk_add_and_print_opp(struct clk_hw *hw, return ret; } - if (n == 0 || n == core->num_rate_max - 1 || - rate == clk_hw_round_rate(hw, INT_MAX)) + clk_prepare_lock(); + rrate = clk_hw_round_rate(hw, INT_MAX); + clk_prepare_unlock(); + + if (n == 0 || n == core->num_rate_max - 1 || rate == rrate) pr_info("%s: set OPP pair(%lu Hz: %u uV) on %s\n", core->name, rate, uv, dev_name(device_list[j])); @@ -4751,7 +4757,9 @@ static void clk_populate_clock_opp_table(struct device_node *np, } for (n = 0; ; n++) { + clk_prepare_lock(); rrate = clk_hw_round_rate(hw, rate + 1); + clk_prepare_unlock(); if (!rrate) { pr_err("clk_round_rate failed for %s\n", core->name); diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 61e2df623496..88b54f367984 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -237,52 +237,88 @@ config MSM_GCC_8998 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, UFS, SD/eMMC, PCIe, etc. -config SDM_GCC_845 +config SPMI_PMIC_CLKDIV + tristate "SPMI PMIC clkdiv Support" + depends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST + help + This driver supports the clkdiv functionality on the Qualcomm + Technologies, Inc. SPMI PMIC. It configures the frequency of + clkdiv outputs of the PMIC. These clocks are typically wired + through alternate functions on GPIO pins. + +config MSM_CLK_AOP_QMP + tristate "AOP QMP Clock Driver" + depends on COMMON_CLK_QCOM && MSM_QMP + help + Always On Processor manages few shared clocks on some Qualcomm + Technologies, Inc. SoCs. It accepts requests from other hardware + subsystems via QMP mailboxes. + Say Y to support the clocks managed by AOP on platforms such as sdm845. + +config MSM_GCC_SDM845 tristate "SDM845 Global Clock Controller" - select QCOM_GDSC depends on COMMON_CLK_QCOM + select QCOM_GDSC help - Support for the global clock controller on SDM845 devices. + Support for the global clock controller on Qualcomm Technologies, Inc + sdm845 devices. Say Y if you want to use peripheral devices such as UART, SPI, - i2C, USB, UFS, SDDC, PCIe, etc. + i2c, USB, UFS, SD/eMMC, PCIe, etc. -config SDM_VIDEOCC_845 - tristate "SDM845 Video Clock Controller" +config MSM_CAMCC_SDM845 + tristate "SDM845 Camera Clock Controller" depends on COMMON_CLK_QCOM - select SDM_GCC_845 - select QCOM_GDSC help - Support for the video clock controller on SDM845 devices. - Say Y if you want to support video devices and functionality such as - video encode and decode. + Support for the camera clock controller on Qualcomm Technologies, Inc + sdm845 devices. + Say Y if you want to support camera devices and functionality such as + capturing pictures. -config SDM_DISPCC_845 +config MSM_DEBUGCC_SDM845 + tristate "SDM845 Debug Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the debug clock controller on Qualcomm Technologies, Inc + sdm845 devices. + Say Y if you want to support the clock measurement functionality. + +config MSM_DISPCC_SDM845 tristate "SDM845 Display Clock Controller" - select SDM_GCC_845 depends on COMMON_CLK_QCOM help Support for the display clock controller on Qualcomm Technologies, Inc - SDM845 devices. + sdm845 devices. Say Y if you want to support display devices and functionality such as splash screen. -config SPMI_PMIC_CLKDIV - tristate "SPMI PMIC clkdiv Support" - depends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST +config MSM_GPUCC_SDM845 + tristate "SDM845 Graphics Clock Controller" + depends on MSM_GCC_SDM845 help - This driver supports the clkdiv functionality on the Qualcomm - Technologies, Inc. SPMI PMIC. It configures the frequency of - clkdiv outputs of the PMIC. These clocks are typically wired - through alternate functions on GPIO pins. + Support for the graphics clock controller on Qualcomm Technologies, Inc. + sdm845 devices. + Say Y if you want to support graphics controller devices. -config MSM_CLK_AOP_QMP - tristate "AOP QMP Clock Driver" - depends on COMMON_CLK_QCOM && MSM_QMP +config MSM_VIDEOCC_SDM845 + tristate "SDM845 Video Clock Controller" + depends on COMMON_CLK_QCOM help - Always On Processor manages few shared clocks on some Qualcomm - Technologies, Inc. SoCs. It accepts requests from other hardware - subsystems via QMP mailboxes. - Say Y to support the clocks managed by AOP on platforms such as sdm845. + Support for the video clock controller on Qualcomm Technologies, Inc + sdm845 devices. + Say Y if you want to support video devices and functionality such as + video encode/decode. + +config CLOCK_CPU_OSM_SDM845 + tristate "OSM CPU Clock Controller for SDM845" + depends on COMMON_CLK_QCOM + help + Support for the OSM clock controller for SDM845. + Operating State Manager (OSM) is a hardware engine used by some + Qualcomm Technologies, Inc. (QTI) SoCs to manage frequency and + voltage scaling in hardware. OSM is capable of controlling + frequency and voltage requests for multiple clusters via the + existence of multiple OSM domains. + Say Y if you want to support OSM clocks. config MSM_GCC_KONA tristate "KONA Global Clock Controller" diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 8834fc9bfc04..665ed6c84cc9 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -16,6 +16,15 @@ clk-qcom-y += clk-dummy.o clk-debug.o clk-qcom-y += gdsc-regulator.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +# Ported clock drivers +obj-$(CONFIG_MSM_GCC_SDM845) += gcc-sdm845.o +obj-$(CONFIG_MSM_CAMCC_SDM845) += camcc-sdm845.o +obj-$(CONFIG_MSM_DEBUGCC_SDM845) += debugcc-sdm845.o +obj-$(CONFIG_MSM_DISPCC_SDM845) += dispcc-sdm845.o +obj-$(CONFIG_MSM_GPUCC_SDM845) += gpucc-sdm845.o +obj-$(CONFIG_MSM_VIDEOCC_SDM845) += videocc-sdm845.o +obj-$(CONFIG_CLOCK_CPU_OSM_SDM845) += clk-cpu-osm-sdm845.o + # Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c new file mode 100644 index 000000000000..5f3e30dc9047 --- /dev/null +++ b/drivers/clk/qcom/camcc-sdm845.c @@ -0,0 +1,2158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-sdm845.h" + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_mx, VDD_CX_NUM, 1, vdd_corner); + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL2_OUT_EVEN, + P_CAM_CC_PLL2_OUT_ODD, + P_CAM_CC_PLL3_OUT_EVEN, + P_CORE_BI_PLL_TEST_SE, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EVEN, 1 }, + { P_CAM_CC_PLL1_OUT_EVEN, 2 }, + { P_CAM_CC_PLL3_OUT_EVEN, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_0[] = { + "bi_tcxo", + "cam_cc_pll2_out_even", + "cam_cc_pll1_out_even", + "cam_cc_pll3_out_even", + "cam_cc_pll0_out_even", + "core_bi_pll_test_se", +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EVEN, 1 }, + { P_CAM_CC_PLL1_OUT_EVEN, 2 }, + { P_CAM_CC_PLL2_OUT_ODD, 4 }, + { P_CAM_CC_PLL3_OUT_EVEN, 5 }, + { P_CAM_CC_PLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const cam_cc_parent_names_1[] = { + "bi_tcxo", + "cam_cc_pll2_out_even", + "cam_cc_pll1_out_even", + "cam_cc_pll2_out_odd", + "cam_cc_pll3_out_even", + "cam_cc_pll0_out_even", + "core_bi_pll_test_se", +}; + +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, + { 125000000, 1000000000, 1 }, +}; + +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x1f, + .alpha = 0x4000, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll0", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static const struct clk_div_table post_div_table_fabia_even[] = { + { 0x0, 1 }, + { 0x1, 2 }, + { 0x3, 4 }, + { 0x7, 8 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll0_out_even", + .parent_names = (const char *[]){ "cam_cc_pll0" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x2a, + .alpha = 0x1556, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll1", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll1_out_even", + .parent_names = (const char *[]){ "cam_cc_pll1" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_MX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll2_out_even = { + .offset = 0x2000, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2_out_even", + .parent_names = (const char *[]){ "cam_cc_pll2" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct clk_div_table post_div_table_fabia_odd[] = { + { 0x0, 1 }, + { 0x3, 3 }, + { 0x5, 5 }, + { 0x7, 7 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll2_out_odd = { + .offset = 0x2000, + .post_div_shift = 12, + .post_div_table = post_div_table_fabia_odd, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll2_out_odd", + .parent_names = (const char *[]){ "cam_cc_pll2" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x14, + .alpha = 0x0, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll3", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = { + .offset = 0x3000, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll3_out_even", + .parent_names = (const char *[]){ "cam_cc_pll3" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x600c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 200000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_clk_src = { + .cmd_rcgr = 0xb0d8, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 37500000, + LOW, 50000000, + NOMINAL, 100000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_ODD, 3, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src_sdm845_v2[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x9060, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_cphy_rx_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 300000000, + LOW, 320000000, + HIGH, 384000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(240000000, P_CAM_CC_PLL2_OUT_EVEN, 2, 0, 0), + F(269333333, P_CAM_CC_PLL1_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x5004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi0phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 240000000, + LOW, 269333333), + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x5028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi1phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 240000000, + LOW, 269333333), + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x504c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi2phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 240000000, + LOW, 269333333), + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x5070, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi3phytimer_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 240000000, + LOW, 269333333), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x6038, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_fast_ahb_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 100000000, + LOW, 200000000, + LOW_L1, 300000000, + NOMINAL, 400000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fd_core_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_cam_cc_fd_core_clk_src_sdm845_v2[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fd_core_clk_src = { + .cmd_rcgr = 0xb0b0, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fd_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_fd_core_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 320000000, + LOW, 400000000, + LOW_L1, 538666667, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src_sdm845_v2[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0xb088, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 320000000, + LOW, 400000000, + LOW_L1, 538666667, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x900c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 320000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_csid_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_csid_clk_src = { + .cmd_rcgr = 0x9038, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_csid_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 384000000, + NOMINAL, 538666667), + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_clk_src = { + .cmd_rcgr = 0xa00c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 320000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static struct clk_rcg2 cam_cc_ife_1_csid_clk_src = { + .cmd_rcgr = 0xa030, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_csid_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 384000000, + NOMINAL, 538666667), + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0xb004, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 320000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0xb024, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 384000000, + NOMINAL, 538666667), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(240000000, P_CAM_CC_PLL0_OUT_EVEN, 2.5, 0, 0), + F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EVEN, 1, 0, 0), + F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_0_clk_src = { + .cmd_rcgr = 0x700c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ipe_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP6( + MIN, 19200000, + LOWER, 240000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 538666667, + HIGH, 600000000), + }, +}; + +static struct clk_rcg2 cam_cc_ipe_1_clk_src = { + .cmd_rcgr = 0x800c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ipe_0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_1_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP6( + MIN, 19200000, + LOWER, 240000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 538666667, + HIGH, 600000000), + }, +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0xb04c, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_jpeg_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 200000000, + LOW, 404000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(384000000, P_CAM_CC_PLL2_OUT_ODD, 2.5, 0, 0), + F(480000000, P_CAM_CC_PLL2_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_cam_cc_lrme_clk_src_sdm845_v2[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(269333333, P_CAM_CC_PLL1_OUT_EVEN, 3, 0, 0), + F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_lrme_clk_src = { + .cmd_rcgr = 0xb0f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .enable_safe_config = true, + .freq_tbl = ftbl_cam_cc_lrme_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_lrme_clk_src", + .parent_names = cam_cc_parent_names_1, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 200000000, + LOW, 384000000, + LOW_L1, 480000000, + NOMINAL, 600000000), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(8000000, P_CAM_CC_PLL2_OUT_EVEN, 10, 1, 6), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_EVEN, 10, 1, 2), + F(33333333, P_CAM_CC_PLL0_OUT_EVEN, 2, 1, 9), + F(34285714, P_CAM_CC_PLL2_OUT_EVEN, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk0_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 19200000, + LOWER, 34285714), + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x4024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk1_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 19200000, + LOWER, 34285714), + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x4044, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk2_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 19200000, + LOWER, 34285714), + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x4064, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk3_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 19200000, + LOWER, 34285714), + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_CAM_CC_PLL0_OUT_EVEN, 10, 0, 0), + F(66666667, P_CAM_CC_PLL0_OUT_EVEN, 9, 0, 0), + F(73846154, P_CAM_CC_PLL2_OUT_EVEN, 6.5, 0, 0), + F(80000000, P_CAM_CC_PLL2_OUT_EVEN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x6054, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cam_cc_slow_ahb_clk_src", + .parent_names = cam_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 60000000, + LOW, 66666667, + LOW_L1, 73846154, + NOMINAL, 80000000), + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x606c, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x606c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_areg_clk = { + .halt_reg = 0x6050, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x6050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_areg_clk", + .parent_names = (const char *[]){ + "cam_cc_fast_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_axi_clk = { + .halt_reg = 0x6034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x6024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_bps_clk", + .parent_names = (const char *[]){ + "cam_cc_bps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_atb_clk = { + .halt_reg = 0xb12c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb12c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_camnoc_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_clk = { + .halt_reg = 0xb124, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb124, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_camnoc_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_clk = { + .halt_reg = 0xb0f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0f0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_cci_clk", + .parent_names = (const char *[]){ + "cam_cc_cci_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0xb11c, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0xb11c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_cpas_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x501c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi0phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi0phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x5040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi1phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi1phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x5064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi2phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi2phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x5088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csi3phytimer_clk", + .parent_names = (const char *[]){ + "cam_cc_csi3phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x5020, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x5020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy0_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x5044, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x5044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy1_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x5068, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x5068, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy2_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x508c, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x508c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_csiphy3_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_fd_core_clk = { + .halt_reg = 0xb0c8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_fd_core_clk", + .parent_names = (const char *[]){ + "cam_cc_fd_core_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_fd_core_uar_clk = { + .halt_reg = 0xb0d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0d0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_fd_core_uar_clk", + .parent_names = (const char *[]){ + "cam_cc_fd_core_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_apb_clk = { + .halt_reg = 0xb084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_apb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_atb_clk = { + .halt_reg = 0xb078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_atb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0xb0a0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0a0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_clk", + .parent_names = (const char *[]){ + "cam_cc_icp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_cti_clk = { + .halt_reg = 0xb07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb07c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_cti_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ts_clk = { + .halt_reg = 0xb080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_icp_ts_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_axi_clk = { + .halt_reg = 0x907c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x907c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x9024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_cphy_rx_clk = { + .halt_reg = 0x9078, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x9078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_csid_clk = { + .halt_reg = 0x9050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x9034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_0_dsp_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_0_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_axi_clk = { + .halt_reg = 0xa054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_clk = { + .halt_reg = 0xa024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_cphy_rx_clk = { + .halt_reg = 0xa050, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0xa050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_csid_clk = { + .halt_reg = 0xa048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_dsp_clk = { + .halt_reg = 0xa02c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa02c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_1_dsp_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_1_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0xb01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb01c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_lite_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0xb044, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0xb044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_names = (const char *[]){ + "cam_cc_cphy_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0xb03c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb03c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ife_lite_csid_clk", + .parent_names = (const char *[]){ + "cam_cc_ife_lite_csid_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_ahb_clk = { + .halt_reg = 0x703c, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x703c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_areg_clk = { + .halt_reg = 0x7038, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x7038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_areg_clk", + .parent_names = (const char *[]){ + "cam_cc_fast_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_axi_clk = { + .halt_reg = 0x7034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_0_clk = { + .halt_reg = 0x7024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_0_clk", + .parent_names = (const char *[]){ + "cam_cc_ipe_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_ahb_clk = { + .halt_reg = 0x803c, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x803c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_1_ahb_clk", + .parent_names = (const char *[]){ + "cam_cc_slow_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_areg_clk = { + .halt_reg = 0x8038, + .halt_check = BRANCH_HALT, + .aggr_sibling_rates = true, + .clkr = { + .enable_reg = 0x8038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_1_areg_clk", + .parent_names = (const char *[]){ + "cam_cc_fast_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_axi_clk = { + .halt_reg = 0x8034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_1_clk = { + .halt_reg = 0x8024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_ipe_1_clk", + .parent_names = (const char *[]){ + "cam_cc_ipe_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0xb064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_jpeg_clk", + .parent_names = (const char *[]){ + "cam_cc_jpeg_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_lrme_clk = { + .halt_reg = 0xb110, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb110, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_lrme_clk", + .parent_names = (const char *[]){ + "cam_cc_lrme_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x401c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x401c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk0_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x403c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x403c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk1_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x405c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x405c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk2_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x407c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x407c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_mclk3_clk", + .parent_names = (const char *[]){ + "cam_cc_mclk3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_pll_test_clk = { + .halt_reg = 0xc014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_pll_test_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_soc_ahb_clk = { + .halt_reg = 0xb13c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb13c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_soc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sys_tmr_clk = { + .halt_reg = 0xb0a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb0a8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "cam_cc_sys_tmr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *cam_cc_sdm845_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr, + [CAM_CC_BPS_AXI_CLK] = &cam_cc_bps_axi_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_CAMNOC_ATB_CLK] = &cam_cc_camnoc_atb_clk.clkr, + [CAM_CC_CAMNOC_AXI_CLK] = &cam_cc_camnoc_axi_clk.clkr, + [CAM_CC_CCI_CLK] = &cam_cc_cci_clk.clkr, + [CAM_CC_CCI_CLK_SRC] = &cam_cc_cci_clk_src.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = NULL, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = NULL, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = NULL, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_FD_CORE_CLK] = &cam_cc_fd_core_clk.clkr, + [CAM_CC_FD_CORE_CLK_SRC] = &cam_cc_fd_core_clk_src.clkr, + [CAM_CC_FD_CORE_UAR_CLK] = &cam_cc_fd_core_uar_clk.clkr, + [CAM_CC_ICP_APB_CLK] = &cam_cc_icp_apb_clk.clkr, + [CAM_CC_ICP_ATB_CLK] = &cam_cc_icp_atb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_ICP_CTI_CLK] = &cam_cc_icp_cti_clk.clkr, + [CAM_CC_ICP_TS_CLK] = &cam_cc_icp_ts_clk.clkr, + [CAM_CC_IFE_0_AXI_CLK] = &cam_cc_ife_0_axi_clk.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_CPHY_RX_CLK] = &cam_cc_ife_0_cphy_rx_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK] = &cam_cc_ife_0_csid_clk.clkr, + [CAM_CC_IFE_0_CSID_CLK_SRC] = &cam_cc_ife_0_csid_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_1_AXI_CLK] = &cam_cc_ife_1_axi_clk.clkr, + [CAM_CC_IFE_1_CLK] = &cam_cc_ife_1_clk.clkr, + [CAM_CC_IFE_1_CLK_SRC] = &cam_cc_ife_1_clk_src.clkr, + [CAM_CC_IFE_1_CPHY_RX_CLK] = &cam_cc_ife_1_cphy_rx_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK] = &cam_cc_ife_1_csid_clk.clkr, + [CAM_CC_IFE_1_CSID_CLK_SRC] = &cam_cc_ife_1_csid_clk_src.clkr, + [CAM_CC_IFE_1_DSP_CLK] = &cam_cc_ife_1_dsp_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_0_AHB_CLK] = &cam_cc_ipe_0_ahb_clk.clkr, + [CAM_CC_IPE_0_AREG_CLK] = &cam_cc_ipe_0_areg_clk.clkr, + [CAM_CC_IPE_0_AXI_CLK] = &cam_cc_ipe_0_axi_clk.clkr, + [CAM_CC_IPE_0_CLK] = &cam_cc_ipe_0_clk.clkr, + [CAM_CC_IPE_0_CLK_SRC] = &cam_cc_ipe_0_clk_src.clkr, + [CAM_CC_IPE_1_AHB_CLK] = &cam_cc_ipe_1_ahb_clk.clkr, + [CAM_CC_IPE_1_AREG_CLK] = &cam_cc_ipe_1_areg_clk.clkr, + [CAM_CC_IPE_1_AXI_CLK] = &cam_cc_ipe_1_axi_clk.clkr, + [CAM_CC_IPE_1_CLK] = &cam_cc_ipe_1_clk.clkr, + [CAM_CC_IPE_1_CLK_SRC] = &cam_cc_ipe_1_clk_src.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_LRME_CLK] = &cam_cc_lrme_clk.clkr, + [CAM_CC_LRME_CLK_SRC] = &cam_cc_lrme_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL2_OUT_EVEN] = &cam_cc_pll2_out_even.clkr, + [CAM_CC_PLL2_OUT_ODD] = &cam_cc_pll2_out_odd.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr, + [CAM_CC_PLL_TEST_CLK] = &cam_cc_pll_test_clk.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_SOC_AHB_CLK] = &cam_cc_soc_ahb_clk.clkr, + [CAM_CC_SYS_TMR_CLK] = &cam_cc_sys_tmr_clk.clkr, +}; + +static const struct qcom_reset_map cam_cc_sdm845_resets[] = { + [TITAN_CAM_CC_CCI_BCR] = { 0xb0d4 }, + [TITAN_CAM_CC_CPAS_BCR] = { 0xb118 }, + [TITAN_CAM_CC_CSI0PHY_BCR] = { 0x5000 }, + [TITAN_CAM_CC_CSI1PHY_BCR] = { 0x5024 }, + [TITAN_CAM_CC_CSI2PHY_BCR] = { 0x5048 }, + [TITAN_CAM_CC_MCLK0_BCR] = { 0x4000 }, + [TITAN_CAM_CC_MCLK1_BCR] = { 0x4020 }, + [TITAN_CAM_CC_MCLK2_BCR] = { 0x4040 }, + [TITAN_CAM_CC_MCLK3_BCR] = { 0x4060 }, + [TITAN_CAM_CC_TITAN_TOP_BCR] = { 0xb130 }, +}; + +static const struct regmap_config cam_cc_sdm845_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd004, + .fast_io = true, +}; + +static const struct qcom_cc_desc cam_cc_sdm845_desc = { + .config = &cam_cc_sdm845_regmap_config, + .clks = cam_cc_sdm845_clocks, + .num_clks = ARRAY_SIZE(cam_cc_sdm845_clocks), + .resets = cam_cc_sdm845_resets, + .num_resets = ARRAY_SIZE(cam_cc_sdm845_resets), +}; + +static const struct of_device_id cam_cc_sdm845_match_table[] = { + { .compatible = "qcom,cam_cc-sdm845" }, + { .compatible = "qcom,cam_cc-sdm845-v2" }, + { .compatible = "qcom,cam_cc-sdm670" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_sdm845_match_table); + +static void cam_cc_sdm845_fixup_sdm845v2(void) +{ + cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK] = + &cam_cc_csi3phytimer_clk.clkr; + cam_cc_sdm845_clocks[CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr; + cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK_SRC] = + &cam_cc_csi3phytimer_clk_src.clkr; + cam_cc_bps_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_bps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_cci_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_cci_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_cphy_rx_clk_src.freq_tbl = ftbl_cam_cc_cphy_rx_clk_src_sdm845_v2; + cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 384000000; + cam_cc_csi0phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_csi0phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_csi1phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_csi1phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_csi2phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_csi2phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_fast_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_fast_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_fd_core_clk_src.freq_tbl = ftbl_cam_cc_fd_core_clk_src_sdm845_v2; + cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_icp_clk_src.freq_tbl = ftbl_cam_cc_icp_clk_src_sdm845_v2; + cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 600000000; + cam_cc_ife_0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 384000000; + cam_cc_ife_1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 384000000; + cam_cc_ife_lite_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_lite_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 384000000; + cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000; + cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000; + cam_cc_jpeg_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_jpeg_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_lrme_clk_src.freq_tbl = ftbl_cam_cc_lrme_clk_src_sdm845_v2; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 269333333; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 320000000; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 400000000; + cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714; + cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714; + cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714; + cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 80000000; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 80000000; +} + +static void cam_cc_sdm845_fixup_sdm670(void) +{ + cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK] = + &cam_cc_csi3phytimer_clk.clkr; + cam_cc_sdm845_clocks[CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr; + cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK_SRC] = + &cam_cc_csi3phytimer_clk_src.clkr; + cam_cc_cphy_rx_clk_src.freq_tbl = ftbl_cam_cc_cphy_rx_clk_src_sdm845_v2; + cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 384000000; + cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 384000000; + cam_cc_fd_core_clk_src.freq_tbl = ftbl_cam_cc_fd_core_clk_src_sdm845_v2; + cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 384000000; + cam_cc_icp_clk_src.freq_tbl = ftbl_cam_cc_icp_clk_src_sdm845_v2; + cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 384000000; + cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 600000000; + cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000; + cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000; + cam_cc_lrme_clk_src.freq_tbl = ftbl_cam_cc_lrme_clk_src_sdm845_v2; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 269333333; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 320000000; + cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 400000000; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 80000000; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 80000000; + cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 80000000; +} + +static int cam_cc_sdm845_fixup(struct platform_device *pdev) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,cam_cc-sdm845-v2")) + cam_cc_sdm845_fixup_sdm845v2(); + else if (!strcmp(compat, "qcom,cam_cc-sdm670")) + cam_cc_sdm845_fixup_sdm670(); + + return 0; +} + +static int cam_cc_sdm845_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + int ret = 0; + + regmap = qcom_cc_map(pdev, &cam_cc_sdm845_desc); + if (IS_ERR(regmap)) { + pr_err("Failed to map the Camera CC registers\n"); + return PTR_ERR(regmap); + } + + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } + + vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx"); + if (IS_ERR(vdd_mx.regulator[0])) { + if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_mx regulator\n"); + return PTR_ERR(vdd_mx.regulator[0]); + } + + ret = cam_cc_sdm845_fixup(pdev); + if (ret) + return ret; + + clk_fabia_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); + clk_fabia_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); + clk_fabia_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); + clk_fabia_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); + + ret = qcom_cc_really_probe(pdev, &cam_cc_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register Camera CC clocks\n"); + return ret; + } + + dev_info(&pdev->dev, "Registered Camera CC clocks\n"); + return ret; +} + +static struct platform_driver cam_cc_sdm845_driver = { + .probe = cam_cc_sdm845_probe, + .driver = { + .name = "cam_cc-sdm845", + .of_match_table = cam_cc_sdm845_match_table, + }, +}; + +static int __init cam_cc_sdm845_init(void) +{ + return platform_driver_register(&cam_cc_sdm845_driver); +} +subsys_initcall(cam_cc_sdm845_init); + +static void __exit cam_cc_sdm845_exit(void) +{ + platform_driver_unregister(&cam_cc_sdm845_driver); +} +module_exit(cam_cc_sdm845_exit); + +MODULE_DESCRIPTION("QTI CAM_CC SDM845 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cam_cc-sdm845"); diff --git a/drivers/clk/qcom/clk-aop-qmp.c b/drivers/clk/qcom/clk-aop-qmp.c index 0f7b77d01228..3956cfb41726 100644 --- a/drivers/clk/qcom/clk-aop-qmp.c +++ b/drivers/clk/qcom/clk-aop-qmp.c @@ -256,6 +256,15 @@ static struct notifier_block aop_qmp_clk_panic_notifier = { .priority = 1, }; +/* + * Due to HW limitations on v1, the qdss_ao clock was not supported by the clock + * driver on AOP. + */ +static void aop_qmp_fixup_v1(void) +{ + aop_qmp_clk_hws[QDSS_AO_CLK] = NULL; +} + static int qmp_update_client(struct clk_hw *hw, struct device *dev, struct mbox_chan **mbox) { @@ -314,6 +323,9 @@ static int aop_qmp_clk_probe(struct platform_device *pdev) clk_data->clk_num = num_clks; + if (of_device_is_compatible(pdev->dev.of_node, "qcom,aop-qmp-clk-v1")) + aop_qmp_fixup_v1(); + for (i = 1; i < num_clks; i++) { if (!aop_qmp_clk_hws[i]) continue; @@ -366,6 +378,7 @@ static int aop_qmp_clk_probe(struct platform_device *pdev) static const struct of_device_id aop_qmp_clk_of_match[] = { { .compatible = "qcom,aop-qmp-clk", }, + { .compatible = "qcom,aop-qmp-clk-v1" }, {} }; diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 46e2e1861c99..7cf1d9ac9d76 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -452,6 +452,42 @@ const struct clk_ops clk_branch2_hw_ctl_ops = { }; EXPORT_SYMBOL_GPL(clk_branch2_hw_ctl_ops); +static int clk_gate_toggle(struct clk_hw *hw, bool en) +{ + struct clk_gate2 *gt = to_clk_gate2(hw); + int ret = 0; + + if (en) { + ret = clk_enable_regmap(hw); + if (ret) + return ret; + } else { + clk_disable_regmap(hw); + } + + if (gt->udelay) + udelay(gt->udelay); + + return ret; +} + +static int clk_gate2_enable(struct clk_hw *hw) +{ + return clk_gate_toggle(hw, true); +} + +static void clk_gate2_disable(struct clk_hw *hw) +{ + clk_gate_toggle(hw, false); +} + +const struct clk_ops clk_gate2_ops = { + .enable = clk_gate2_enable, + .disable = clk_gate2_disable, + .is_enabled = clk_is_enabled_regmap, +}; +EXPORT_SYMBOL_GPL(clk_gate2_ops); + const struct clk_ops clk_branch_simple_ops = { .enable = clk_enable_regmap, .disable = clk_disable_regmap, diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 43b6309cdd43..47549b8e0da2 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -41,12 +41,28 @@ struct clk_branch { struct clk_regmap clkr; }; +/** + * struct clk_gate2 - gating clock with status bit and dynamic hardware gating + * @udelay: halt delay in microseconds on clock branch Enable/Disable + * @clkr: handle between common and hardware-specific interfaces + * + * Clock which can gate its output. + */ +struct clk_gate2 { + u32 udelay; + struct clk_regmap clkr; +}; + extern const struct clk_ops clk_branch_ops; extern const struct clk_ops clk_branch2_ops; extern const struct clk_ops clk_branch2_hw_ctl_ops; +extern const struct clk_ops clk_gate2_ops; extern const struct clk_ops clk_branch_simple_ops; #define to_clk_branch(_hw) \ container_of(to_clk_regmap(_hw), struct clk_branch, clkr) +#define to_clk_gate2(_hw) \ + container_of(to_clk_regmap(_hw), struct clk_gate2, clkr) + #endif diff --git a/drivers/clk/qcom/clk-cpu-osm-sdm845.c b/drivers/clk/qcom/clk-cpu-osm-sdm845.c new file mode 100644 index 000000000000..cf46e15d305e --- /dev/null +++ b/drivers/clk/qcom/clk-cpu-osm-sdm845.c @@ -0,0 +1,1406 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "clk: %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 + +#include "common.h" +#include "clk-regmap.h" +#include "clk-voter.h" +#include "clk-debug.h" + +#define OSM_INIT_RATE 300000000UL +#define XO_RATE 19200000UL +#define OSM_TABLE_SIZE 40 +#define SINGLE_CORE 1 +#define MAX_CLUSTER_CNT 3 +#define MAX_MEM_ACC_VAL_PER_LEVEL 3 +#define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16) +#define CURRENT_LVAL(val) ((val & GENMASK(23, 16)) >> 16) + +#define OSM_REG_SIZE 32 + +#define ENABLE_REG 0x0 +#define FREQ_REG 0x110 +#define VOLT_REG 0x114 +#define CORE_DCVS_CTRL 0xbc +#define PSTATE_STATUS 0x700 + +#define DCVS_PERF_STATE_DESIRED_REG_0_V1 0x780 +#define DCVS_PERF_STATE_DESIRED_REG_0_V2 0x920 +#define DCVS_PERF_STATE_DESIRED_REG(n, v1) \ + (((v1) ? DCVS_PERF_STATE_DESIRED_REG_0_V1 \ + : DCVS_PERF_STATE_DESIRED_REG_0_V2) + 4 * (n)) + +#define OSM_CYCLE_COUNTER_STATUS_REG_0_V1 0x7d0 +#define OSM_CYCLE_COUNTER_STATUS_REG_0_V2 0x9c0 +#define OSM_CYCLE_COUNTER_STATUS_REG(n, v1) \ + (((v1) ? OSM_CYCLE_COUNTER_STATUS_REG_0_V1 \ + : OSM_CYCLE_COUNTER_STATUS_REG_0_V2) + 4 * (n)) + +static DEFINE_VDD_REGS_INIT(vdd_l3_mx_ao, 1); +static DEFINE_VDD_REGS_INIT(vdd_pwrcl_mx_ao, 1); + +struct osm_entry { + u16 virtual_corner; + u16 open_loop_volt; + unsigned long frequency; + u32 lval; + u16 ccount; +}; + +struct clk_osm { + struct clk_hw hw; + struct osm_entry osm_table[OSM_TABLE_SIZE]; + struct dentry *debugfs; + void __iomem *vbase; + phys_addr_t pbase; + spinlock_t lock; + bool per_core_dcvs; + u32 num_entries; + u32 cluster_num; + u32 core_num; + unsigned long rate; + u64 total_cycle_counter; + u32 prev_cycle_counter; + u32 max_core_count; + u32 mx_turbo_freq; + cpumask_t related_cpus; +}; + +static bool is_sdm845v1; + +static inline struct clk_osm *to_clk_osm(struct clk_hw *_hw) +{ + return container_of(_hw, struct clk_osm, hw); +} + +static inline void clk_osm_write_reg(struct clk_osm *c, u32 val, u32 offset) +{ + writel_relaxed(val, c->vbase + offset); +} + +static inline int clk_osm_read_reg(struct clk_osm *c, u32 offset) +{ + return readl_relaxed(c->vbase + offset); +} + +static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset) +{ + return readl_relaxed_no_log(c->vbase + offset); +} + +static inline int clk_osm_mb(struct clk_osm *c) +{ + return readl_relaxed_no_log(c->vbase + ENABLE_REG); +} + +static long clk_osm_list_rate(struct clk_hw *hw, unsigned int n, + unsigned long rate_max) +{ + if (n >= hw->init->num_rate_max) + return -ENXIO; + return hw->init->rate_max[n]; +} + +static inline bool is_better_rate(unsigned long req, unsigned long best, + unsigned long new) +{ + if (IS_ERR_VALUE(new)) + return false; + + return (req <= new && new < best) || (best < req && best < new); +} + +static int clk_osm_search_table(struct osm_entry *table, int entries, + unsigned long rate) +{ + int index; + + for (index = 0; index < entries; index++) { + if (rate == table[index].frequency) + return index; + } + + return -EINVAL; +} + +static int clk_osm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_osm *c = to_clk_osm(hw); + + c->rate = rate; + + return 0; +} + +static unsigned long clk_osm_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_osm *c = to_clk_osm(hw); + + return c->rate; +} + +static int clk_osm_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + unsigned long rrate = 0; + unsigned long rate = req->rate; + + /* + * If the rate passed in is 0, return the first frequency in the + * FMAX table. + */ + if (!rate) { + req->rate = hw->init->rate_max[0]; + return 0; + } + + for (i = 0; i < hw->init->num_rate_max; i++) { + if (is_better_rate(rate, rrate, hw->init->rate_max[i])) { + rrate = hw->init->rate_max[i]; + if (rate == rrate) + break; + } + } + + req->rate = rrate; + + pr_debug("clk:%s rate %lu, rrate %lu, Rate max %lu index %u\n", + hw->init->name, rate, rrate, hw->init->rate_max[i], i); + + return 0; +} + +static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_osm *c = to_clk_osm(hw); + struct clk_hw *p_hw = clk_hw_get_parent(hw); + struct clk_osm *parent = to_clk_osm(p_hw); + int core_num, index = 0; + + if (!c || !parent) + return -EINVAL; + + index = clk_osm_search_table(parent->osm_table, + parent->num_entries, rate); + if (index < 0) { + pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate); + return -EINVAL; + } + + core_num = parent->per_core_dcvs ? c->core_num : 0; + clk_osm_write_reg(parent, index, + DCVS_PERF_STATE_DESIRED_REG(core_num, + is_sdm845v1)); + + /* Make sure the write goes through before proceeding */ + clk_osm_mb(parent); + + return 0; +} + +static int clk_pwrcl_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw *p_hw = clk_hw_get_parent(hw); + struct clk_osm *parent = to_clk_osm(p_hw); + int ret, index = 0, count = 40; + u32 curr_lval; + + ret = clk_cpu_set_rate(hw, rate, parent_rate); + if (ret) + return ret; + + index = clk_osm_search_table(parent->osm_table, + parent->num_entries, rate); + if (index < 0) + return -EINVAL; + + /* + * Poll the CURRENT_FREQUENCY value of the PSTATE_STATUS register to + * check if the L_VAL has been updated. + */ + while (count-- > 0) { + curr_lval = CURRENT_LVAL(clk_osm_read_reg(parent, + PSTATE_STATUS)); + if (curr_lval <= parent->osm_table[index].lval) + return 0; + udelay(50); + } + pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate); + return -ETIMEDOUT; +} + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_osm *c = to_clk_osm(hw); + struct clk_hw *p_hw = clk_hw_get_parent(hw); + struct clk_osm *parent = to_clk_osm(p_hw); + int core_num, index = 0; + + if (!c || !parent) + return -EINVAL; + + core_num = parent->per_core_dcvs ? c->core_num : 0; + index = clk_osm_read_reg(parent, + DCVS_PERF_STATE_DESIRED_REG(core_num, + is_sdm845v1)); + return parent->osm_table[index].frequency; +} + +static int clk_cpu_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *parent_hw = clk_hw_get_parent(hw); + unsigned long rate = req->rate; + + req->rate = clk_hw_round_rate(parent_hw, rate); + + return 0; +} + +static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_osm *cpuclk = to_clk_osm(hw); + struct clk_rate_request req; + int index = 0; + int count = 40; + u32 curr_lval; + + if (!cpuclk) + return -EINVAL; + + req.rate = rate; + clk_osm_determine_rate(hw, &req); + + if (rate != req.rate) { + pr_err("invalid requested rate=%ld\n", rate); + return -EINVAL; + } + + /* Convert rate to table index */ + index = clk_osm_search_table(cpuclk->osm_table, + cpuclk->num_entries, req.rate); + if (index < 0) { + pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate); + return -EINVAL; + } + pr_debug("rate: %lu --> index %d\n", rate, index); + + clk_osm_write_reg(cpuclk, index, + DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1)); + + /* Make sure the write goes through before proceeding */ + clk_osm_mb(cpuclk); + + /* + * Poll the CURRENT_FREQUENCY value of the PSTATE_STATUS register to + * check if the L_VAL has been updated. + */ + if (cpuclk->rate >= cpuclk->mx_turbo_freq && + rate < cpuclk->mx_turbo_freq) { + while (count-- > 0) { + curr_lval = CURRENT_LVAL(clk_osm_read_reg(cpuclk, + PSTATE_STATUS)); + if (curr_lval <= cpuclk->osm_table[index].lval) { + cpuclk->rate = rate; + return 0; + } + udelay(50); + } + pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate); + return -ETIMEDOUT; + } + cpuclk->rate = rate; + return 0; +} + +static unsigned long l3_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_osm *cpuclk = to_clk_osm(hw); + int index = 0; + + if (!cpuclk) + return -EINVAL; + + index = clk_osm_read_reg(cpuclk, + DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1)); + + pr_debug("%s: Index %d, freq %ld\n", __func__, index, + cpuclk->osm_table[index].frequency); + + /* Convert index to frequency */ + return cpuclk->osm_table[index].frequency; +} + +static const struct clk_ops clk_ops_l3_osm = { + .determine_rate = clk_osm_determine_rate, + .list_rate = clk_osm_list_rate, + .recalc_rate = l3_clk_recalc_rate, + .set_rate = l3_clk_set_rate, + .debug_init = clk_debug_measure_add, +}; + +static const struct clk_ops clk_ops_pwrcl_core = { + .set_rate = clk_pwrcl_set_rate, + .determine_rate = clk_cpu_determine_rate, + .recalc_rate = clk_cpu_recalc_rate, + .debug_init = clk_debug_measure_add, +}; + +static const struct clk_ops clk_ops_perfcl_core = { + .set_rate = clk_cpu_set_rate, + .determine_rate = clk_cpu_determine_rate, + .recalc_rate = clk_cpu_recalc_rate, + .debug_init = clk_debug_measure_add, +}; + +static const struct clk_ops clk_ops_cpu_osm = { + .set_rate = clk_osm_set_rate, + .recalc_rate = clk_osm_recalc_rate, + .list_rate = clk_osm_list_rate, + .determine_rate = clk_osm_determine_rate, + .debug_init = clk_debug_measure_add, +}; + +static struct clk_init_data osm_clks_init[] = { + [0] = { + .name = "l3_clk", + .parent_names = (const char *[]){ "bi_tcxo_ao" }, + .num_parents = 1, + .flags = CLK_CHILD_NO_RATE_PROP, + .ops = &clk_ops_l3_osm, + .vdd_class = &vdd_l3_mx_ao, + }, + [1] = { + .name = "pwrcl_clk", + .parent_names = (const char *[]){ "bi_tcxo_ao" }, + .num_parents = 1, + .ops = &clk_ops_cpu_osm, + .vdd_class = &vdd_pwrcl_mx_ao, + }, + [2] = { + .name = "perfcl_clk", + .parent_names = (const char *[]){ "bi_tcxo_ao" }, + .num_parents = 1, + .ops = &clk_ops_cpu_osm, + }, +}; + +static struct clk_osm l3_clk = { + .cluster_num = 0, + .max_core_count = 4, + .hw.init = &osm_clks_init[0], +}; + +static DEFINE_CLK_VOTER(l3_cluster0_vote_clk, l3_clk, 0); +static DEFINE_CLK_VOTER(l3_cluster1_vote_clk, l3_clk, 0); +static DEFINE_CLK_VOTER(l3_misc_vote_clk, l3_clk, 0); +static DEFINE_CLK_VOTER(l3_gpu_vote_clk, l3_clk, 0); + +static struct clk_osm pwrcl_clk = { + .cluster_num = 1, + .max_core_count = 4, + .hw.init = &osm_clks_init[1], +}; + +static struct clk_osm cpu0_pwrcl_clk = { + .core_num = 0, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu0_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm cpu1_pwrcl_clk = { + .core_num = 1, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu1_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm cpu2_pwrcl_clk = { + .core_num = 2, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu2_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm cpu3_pwrcl_clk = { + .core_num = 3, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu3_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm cpu4_pwrcl_clk = { + .core_num = 4, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu4_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm cpu5_pwrcl_clk = { + .core_num = 5, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu5_pwrcl_clk", + .parent_names = (const char *[]){ "pwrcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_pwrcl_core, + }, +}; + +static struct clk_osm perfcl_clk = { + .cluster_num = 2, + .max_core_count = 4, + .hw.init = &osm_clks_init[2], +}; + + +static struct clk_osm cpu4_perfcl_clk = { + .core_num = 0, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu4_perfcl_clk", + .parent_names = (const char *[]){ "perfcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_perfcl_core, + }, +}; + +static struct clk_osm cpu5_perfcl_clk = { + .core_num = 1, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu5_perfcl_clk", + .parent_names = (const char *[]){ "perfcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_perfcl_core, + }, +}; + +static struct clk_osm cpu6_perfcl_clk = { + .core_num = 2, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu6_perfcl_clk", + .parent_names = (const char *[]){ "perfcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_perfcl_core, + }, +}; + +static struct clk_osm cpu7_perfcl_clk = { + .core_num = 3, + .total_cycle_counter = 0, + .prev_cycle_counter = 0, + .hw.init = &(struct clk_init_data){ + .name = "cpu7_perfcl_clk", + .parent_names = (const char *[]){ "perfcl_clk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_ops_perfcl_core, + }, +}; + +static struct clk_hw *osm_qcom_clk_hws[] = { + [L3_CLK] = &l3_clk.hw, + [L3_CLUSTER0_VOTE_CLK] = &l3_cluster0_vote_clk.hw, + [L3_CLUSTER1_VOTE_CLK] = &l3_cluster1_vote_clk.hw, + [L3_MISC_VOTE_CLK] = &l3_misc_vote_clk.hw, + [L3_GPU_VOTE_CLK] = &l3_gpu_vote_clk.hw, + [PWRCL_CLK] = &pwrcl_clk.hw, + [CPU0_PWRCL_CLK] = &cpu0_pwrcl_clk.hw, + [CPU1_PWRCL_CLK] = &cpu1_pwrcl_clk.hw, + [CPU2_PWRCL_CLK] = &cpu2_pwrcl_clk.hw, + [CPU3_PWRCL_CLK] = &cpu3_pwrcl_clk.hw, + [PERFCL_CLK] = &perfcl_clk.hw, + [CPU4_PERFCL_CLK] = &cpu4_perfcl_clk.hw, + [CPU5_PERFCL_CLK] = &cpu5_perfcl_clk.hw, + [CPU6_PERFCL_CLK] = &cpu6_perfcl_clk.hw, + [CPU7_PERFCL_CLK] = &cpu7_perfcl_clk.hw, + [CPU4_PWRCL_CLK] = NULL, + [CPU5_PWRCL_CLK] = NULL, +}; + +static struct clk_osm *clk_cpu_map[] = { + &cpu0_pwrcl_clk, + &cpu1_pwrcl_clk, + &cpu2_pwrcl_clk, + &cpu3_pwrcl_clk, + &cpu4_perfcl_clk, + &cpu5_perfcl_clk, + &cpu6_perfcl_clk, + &cpu7_perfcl_clk, +}; + +static struct clk_osm *logical_cpu_to_clk(int cpu) +{ + struct device_node *cpu_node; + const u32 *cell; + u64 hwid; + static struct clk_osm *cpu_clk_map[NR_CPUS]; + + if (!cpu_clk_map[cpu]) { + cpu_node = of_get_cpu_node(cpu, NULL); + if (!cpu_node) + return NULL; + + cell = of_get_property(cpu_node, "reg", NULL); + if (!cell) { + pr_err("%s: missing reg property\n", + cpu_node->full_name); + of_node_put(cpu_node); + return NULL; + } + + hwid = of_read_number(cell, of_n_addr_cells(cpu_node)); + hwid = (hwid >> 8) & 0xff; + of_node_put(cpu_node); + if (hwid >= ARRAY_SIZE(clk_cpu_map)) { + pr_err("unsupported CPU number - %d (hw_id - %llu)\n", + cpu, hwid); + return NULL; + } + + cpu_clk_map[cpu] = clk_cpu_map[hwid]; + } + + return cpu_clk_map[cpu]; +} + +static struct clk_osm *osm_configure_policy(struct cpufreq_policy *policy) +{ + int cpu; + struct clk_hw *parent, *c_parent; + struct clk_osm *first; + struct clk_osm *c, *n; + + c = logical_cpu_to_clk(policy->cpu); + if (!c) + return NULL; + + c_parent = clk_hw_get_parent(&c->hw); + if (!c_parent) + return NULL; + + /* + * Don't put any other CPUs into the policy if we're doing + * per_core_dcvs + */ + if (to_clk_osm(c_parent)->per_core_dcvs) + return c; + + first = c; + /* Find CPUs that share the same clock domain */ + for_each_possible_cpu(cpu) { + n = logical_cpu_to_clk(cpu); + if (!n) + continue; + + parent = clk_hw_get_parent(&n->hw); + if (!parent) + return NULL; + if (parent != c_parent) + continue; + + cpumask_set_cpu(cpu, &c->related_cpus); + if (n->core_num == 0) + first = n; + } + + return first; +} + +static void +osm_set_index(struct clk_osm *c, unsigned int index) +{ + struct clk_hw *p_hw = clk_hw_get_parent(&c->hw); + struct clk_osm *parent = to_clk_osm(p_hw); + unsigned long rate = 0; + + if (index >= OSM_TABLE_SIZE) { + pr_err("Passing an index (%u) that's greater than max (%d)\n", + index, OSM_TABLE_SIZE - 1); + return; + } + + rate = parent->osm_table[index].frequency; + if (!rate) + return; + + clk_set_rate(c->hw.clk, clk_round_rate(c->hw.clk, rate)); +} + +static int +osm_cpufreq_target_index(struct cpufreq_policy *policy, unsigned int index) +{ + struct clk_osm *c = policy->driver_data; + + osm_set_index(c, index); + arch_set_freq_scale(policy->related_cpus, + policy->freq_table[index].frequency, + policy->cpuinfo.max_freq); + return 0; +} + +static unsigned int osm_cpufreq_get(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); + struct clk_osm *c; + u32 index; + + if (!policy) + return 0; + + c = policy->driver_data; + index = clk_osm_read_reg(c, + DCVS_PERF_STATE_DESIRED_REG(c->core_num, is_sdm845v1)); + return policy->freq_table[index].frequency; +} + +static int osm_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *table; + struct clk_osm *c, *parent; + struct clk_hw *p_hw; + unsigned int i, prev_cc = 0; + unsigned int xo_kHz; + + c = osm_configure_policy(policy); + if (!c) { + pr_err("no clock for CPU%d\n", policy->cpu); + return -ENODEV; + } + + p_hw = clk_hw_get_parent(&c->hw); + if (!p_hw) { + pr_err("no parent clock for CPU%d\n", policy->cpu); + return -ENODEV; + } + + parent = to_clk_osm(p_hw); + c->vbase = parent->vbase; + + p_hw = clk_hw_get_parent(p_hw); + if (!p_hw) { + pr_err("no xo clock for CPU%d\n", policy->cpu); + return -ENODEV; + } + xo_kHz = clk_hw_get_rate(p_hw) / 1000; + + table = kcalloc(OSM_TABLE_SIZE + 1, sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + for (i = 0; i < OSM_TABLE_SIZE; i++) { + u32 data, src, div, lval, core_count; + + data = clk_osm_read_reg(c, FREQ_REG + i * OSM_REG_SIZE); + src = (data & GENMASK(31, 30)) >> 30; + div = (data & GENMASK(29, 28)) >> 28; + lval = data & GENMASK(7, 0); + core_count = CORE_COUNT_VAL(data); + + if (!src) + table[i].frequency = OSM_INIT_RATE / 1000; + else + table[i].frequency = xo_kHz * lval; + table[i].driver_data = table[i].frequency; + + if (core_count != parent->max_core_count) + table[i].frequency = CPUFREQ_ENTRY_INVALID; + + /* + * Two of the same frequencies with the same core counts means + * end of table. + */ + if (i > 0 && table[i - 1].driver_data == table[i].driver_data + && prev_cc == core_count) { + struct cpufreq_frequency_table *prev = &table[i - 1]; + + if (prev->frequency == CPUFREQ_ENTRY_INVALID) { + prev->flags = CPUFREQ_BOOST_FREQ; + prev->frequency = prev->driver_data; + } + + break; + } + prev_cc = core_count; + } + table[i].frequency = CPUFREQ_TABLE_END; + + policy->freq_table = table; + policy->driver_data = c; + policy->dvfs_possible_from_any_cpu = true; + cpumask_copy(policy->cpus, &c->related_cpus); + return 0; +} + +static int osm_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + kfree(policy->freq_table); + policy->freq_table = NULL; + return 0; +} + +static struct freq_attr *osm_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, + NULL +}; + +static struct cpufreq_driver qcom_osm_cpufreq_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = osm_cpufreq_target_index, + .get = osm_cpufreq_get, + .init = osm_cpufreq_cpu_init, + .exit = osm_cpufreq_cpu_exit, + .name = "osm-cpufreq", + .attr = osm_cpufreq_attr, + .boost_enabled = true, +}; + +static u32 find_voltage(struct clk_osm *c, unsigned long rate) +{ + struct osm_entry *table = c->osm_table; + int entries = c->num_entries, i; + + for (i = 0; i < entries; i++) { + if (rate == table[i].frequency) { + /* OPP table voltages have units of mV */ + return table[i].open_loop_volt * 1000; + } + } + + return -EINVAL; +} + +static int add_opp(struct clk_osm *c, struct device **device_list, int count) +{ + unsigned long rate = 0; + u32 uv; + long rc; + int i, j = 0; + unsigned long min_rate = c->hw.init->rate_max[0]; + unsigned long max_rate = + c->hw.init->rate_max[c->hw.init->num_rate_max - 1]; + + while (1) { + rate = c->hw.init->rate_max[j++]; + uv = find_voltage(c, rate); + if (uv <= 0) { + pr_warn("No voltage for %lu.\n", rate); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + rc = dev_pm_opp_add(device_list[i], rate, uv); + if (rc) { + pr_warn("failed to add OPP for %lu\n", rate); + return rc; + } + } + + /* + * Print the OPP pair for the lowest and highest frequency for + * each device that we're populating. This is important since + * this information will be used by thermal mitigation and the + * scheduler. + */ + if (rate == min_rate) { + for (i = 0; i < count; i++) { + pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n", + rate, uv, dev_name(device_list[i])); + } + } + + if (rate == max_rate && max_rate != min_rate) { + for (i = 0; i < count; i++) { + pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n", + rate, uv, dev_name(device_list[i])); + } + break; + } + + if (min_rate == max_rate) + break; + } + return 0; +} + +static int derive_device_list(struct device **device_list, + struct device_node *np, + char *phandle_name, int count) +{ + int i; + struct platform_device *pdev; + struct device_node *dev_node; + + for (i = 0; i < count; i++) { + dev_node = of_parse_phandle(np, phandle_name, i); + if (!dev_node) { + pr_err("Unable to get device_node pointer for opp-handle (%s)\n", + phandle_name); + return -ENODEV; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) { + pr_err("Unable to find platform_device node for opp-handle (%s)\n", + phandle_name); + return -ENODEV; + } + device_list[i] = &pdev->dev; + } + return 0; +} + +static void populate_l3_opp_table(struct device_node *np, char *phandle_name) +{ + struct device **device_list; + int len, count, ret = 0; + + if (of_find_property(np, phandle_name, &len)) { + count = len / sizeof(u32); + + device_list = kcalloc(count, sizeof(struct device *), + GFP_KERNEL); + if (!device_list) + return; + + ret = derive_device_list(device_list, np, phandle_name, count); + if (ret < 0) { + pr_err("Failed to fill device_list for %s\n", + phandle_name); + return; + } + } else { + pr_debug("Unable to find %s\n", phandle_name); + return; + } + + if (add_opp(&l3_clk, device_list, count)) + pr_err("Failed to add OPP levels for %s\n", phandle_name); + + kfree(device_list); +} + +static void populate_opp_table(struct platform_device *pdev) +{ + int cpu; + struct device *cpu_dev; + struct clk_osm *c, *parent; + struct clk_hw *hw_parent; + struct device_node *np = pdev->dev.of_node; + + for_each_possible_cpu(cpu) { + c = logical_cpu_to_clk(cpu); + if (!c) { + pr_err("no clock device for CPU=%d\n", cpu); + return; + } + + hw_parent = clk_hw_get_parent(&c->hw); + parent = to_clk_osm(hw_parent); + cpu_dev = get_cpu_device(cpu); + if (cpu_dev) + if (add_opp(parent, &cpu_dev, 1)) + pr_err("Failed to add OPP levels for %s\n", + dev_name(cpu_dev)); + } + + populate_l3_opp_table(np, "l3-devs"); +} + +static u64 clk_osm_get_cpu_cycle_counter(int cpu) +{ + u32 val; + int core_num; + unsigned long flags; + u64 cycle_counter_ret; + struct clk_osm *parent, *c = logical_cpu_to_clk(cpu); + + if (IS_ERR_OR_NULL(c)) { + pr_err("no clock device for CPU=%d\n", cpu); + return 0; + } + + parent = to_clk_osm(clk_hw_get_parent(&c->hw)); + + spin_lock_irqsave(&parent->lock, flags); + /* + * Use core 0's copy as proxy for the whole cluster when per + * core DCVS is disabled. + */ + core_num = parent->per_core_dcvs ? c->core_num : 0; + val = clk_osm_read_reg_no_log(parent, + OSM_CYCLE_COUNTER_STATUS_REG(core_num, is_sdm845v1)); + + if (val < c->prev_cycle_counter) { + /* Handle counter overflow */ + c->total_cycle_counter += UINT_MAX - + c->prev_cycle_counter + val; + c->prev_cycle_counter = val; + } else { + c->total_cycle_counter += val - c->prev_cycle_counter; + c->prev_cycle_counter = val; + } + cycle_counter_ret = c->total_cycle_counter; + spin_unlock_irqrestore(&parent->lock, flags); + + return cycle_counter_ret; +} + +static int clk_osm_read_lut(struct platform_device *pdev, struct clk_osm *c) +{ + u32 data, src, lval, i, j = OSM_TABLE_SIZE; + struct clk_vdd_class *vdd = osm_clks_init[c->cluster_num].vdd_class; + + for (i = 0; i < OSM_TABLE_SIZE; i++) { + data = clk_osm_read_reg(c, FREQ_REG + i * OSM_REG_SIZE); + src = ((data & GENMASK(31, 30)) >> 30); + lval = (data & GENMASK(7, 0)); + c->osm_table[i].ccount = CORE_COUNT_VAL(data); + c->osm_table[i].lval = lval; + + if (!src) + c->osm_table[i].frequency = OSM_INIT_RATE; + else + c->osm_table[i].frequency = XO_RATE * lval; + + data = clk_osm_read_reg(c, VOLT_REG + i * OSM_REG_SIZE); + c->osm_table[i].virtual_corner = + ((data & GENMASK(21, 16)) >> 16); + c->osm_table[i].open_loop_volt = (data & GENMASK(11, 0)); + + pr_debug("index=%d freq=%ld virtual_corner=%d open_loop_voltage=%u\n", + i, c->osm_table[i].frequency, + c->osm_table[i].virtual_corner, + c->osm_table[i].open_loop_volt); + + if (i > 0 && j == OSM_TABLE_SIZE && + c->osm_table[i].frequency == + c->osm_table[i - 1].frequency && + c->osm_table[i].ccount == c->osm_table[i - 1].ccount) + j = i; + } + + osm_clks_init[c->cluster_num].rate_max = devm_kcalloc(&pdev->dev, + j, sizeof(unsigned long), + GFP_KERNEL); + if (!osm_clks_init[c->cluster_num].rate_max) + return -ENOMEM; + + if (vdd) { + vdd->level_votes = devm_kcalloc(&pdev->dev, j, + sizeof(*vdd->level_votes), GFP_KERNEL); + if (!vdd->level_votes) + return -ENOMEM; + + vdd->vdd_uv = devm_kcalloc(&pdev->dev, j, sizeof(*vdd->vdd_uv), + GFP_KERNEL); + if (!vdd->vdd_uv) + return -ENOMEM; + + for (i = 0; i < j; i++) { + if (c->osm_table[i].frequency < c->mx_turbo_freq) + vdd->vdd_uv[i] = RPMH_REGULATOR_LEVEL_NOM; + else + vdd->vdd_uv[i] = RPMH_REGULATOR_LEVEL_TURBO; + } + vdd->num_levels = j; + vdd->cur_level = j; + } + + for (i = 0; i < j; i++) + osm_clks_init[c->cluster_num].rate_max[i] = + c->osm_table[i].frequency; + + c->num_entries = osm_clks_init[c->cluster_num].num_rate_max = j; + return 0; +} + +static int clk_osm_resources_init(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "osm_l3_base"); + if (!res) { + dev_err(&pdev->dev, + "Unable to get platform resource for osm_l3_base"); + return -ENOMEM; + } + + l3_clk.pbase = (unsigned long)res->start; + l3_clk.vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + + if (!l3_clk.vbase) { + dev_err(&pdev->dev, "Unable to map osm_l3_base base\n"); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "osm_pwrcl_base"); + if (!res) { + dev_err(&pdev->dev, + "Unable to get platform resource for osm_pwrcl_base"); + return -ENOMEM; + } + + pwrcl_clk.pbase = (unsigned long)res->start; + pwrcl_clk.vbase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pwrcl_clk.vbase) { + dev_err(&pdev->dev, "Unable to map osm_pwrcl_base base\n"); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "osm_perfcl_base"); + if (!res) { + dev_err(&pdev->dev, + "Unable to get platform resource for osm_perfcl_base"); + return -ENOMEM; + } + + perfcl_clk.pbase = (unsigned long)res->start; + perfcl_clk.vbase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!perfcl_clk.vbase) { + dev_err(&pdev->dev, "Unable to map osm_perfcl_base base\n"); + return -ENOMEM; + } + + return 0; +} + +static void clk_cpu_osm_driver_sdm670_fixup(void) +{ + osm_qcom_clk_hws[CPU4_PERFCL_CLK] = NULL; + osm_qcom_clk_hws[CPU5_PERFCL_CLK] = NULL; + osm_qcom_clk_hws[CPU4_PWRCL_CLK] = &cpu4_pwrcl_clk.hw; + osm_qcom_clk_hws[CPU5_PWRCL_CLK] = &cpu5_pwrcl_clk.hw; + + clk_cpu_map[4] = &cpu4_pwrcl_clk; + clk_cpu_map[5] = &cpu5_pwrcl_clk; + + cpu6_perfcl_clk.core_num = 0; + cpu7_perfcl_clk.core_num = 1; + + pwrcl_clk.max_core_count = 6; + perfcl_clk.max_core_count = 2; +} + +/* Request MX supply if configured in device tree */ +static int clk_cpu_osm_request_mx_supply(struct device *dev) +{ + u32 *array; + int rc; + + if (!of_find_property(dev->of_node, "qcom,mx-turbo-freq", NULL)) { + osm_clks_init[l3_clk.cluster_num].vdd_class = NULL; + osm_clks_init[pwrcl_clk.cluster_num].vdd_class = NULL; + return 0; + } + + vdd_l3_mx_ao.regulator[0] = devm_regulator_get(dev, "vdd_l3_mx_ao"); + if (IS_ERR(vdd_l3_mx_ao.regulator[0])) { + rc = PTR_ERR(vdd_l3_mx_ao.regulator[0]); + if (rc != -EPROBE_DEFER) + dev_err(dev, "Unable to get vdd_l3_mx_ao regulator, rc=%d\n", + rc); + return rc; + } + + vdd_pwrcl_mx_ao.regulator[0] = devm_regulator_get(dev, + "vdd_pwrcl_mx_ao"); + if (IS_ERR(vdd_pwrcl_mx_ao.regulator[0])) { + rc = PTR_ERR(vdd_pwrcl_mx_ao.regulator[0]); + if (rc != -EPROBE_DEFER) + dev_err(dev, "Unable to get vdd_pwrcl_mx_ao regulator, rc=%d\n", + rc); + return rc; + } + + array = kcalloc(MAX_CLUSTER_CNT, sizeof(*array), GFP_KERNEL); + if (!array) + return -ENOMEM; + + rc = of_property_read_u32_array(dev->of_node, "qcom,mx-turbo-freq", + array, MAX_CLUSTER_CNT); + if (rc) { + dev_err(dev, "unable to read qcom,mx-turbo-freq property, rc=%d\n", + rc); + kfree(array); + return rc; + } + + l3_clk.mx_turbo_freq = array[l3_clk.cluster_num]; + pwrcl_clk.mx_turbo_freq = array[pwrcl_clk.cluster_num]; + perfcl_clk.mx_turbo_freq = array[perfcl_clk.cluster_num]; + + kfree(array); + + return 0; +} + +static int clk_cpu_osm_driver_probe(struct platform_device *pdev) +{ + int rc = 0, i, cpu; + u32 val; + int num_clks = ARRAY_SIZE(osm_qcom_clk_hws); + struct clk *ext_xo_clk, *clk; + struct clk_osm *osm_clk; + struct device *dev = &pdev->dev; + struct clk_onecell_data *clk_data; + struct cpu_cycle_counter_cb cb = { + .get_cpu_cycle_counter = clk_osm_get_cpu_cycle_counter, + }; + + /* + * Require the RPM-XO clock to be registered before OSM. + * The cpuss_gpll0_clk_src is listed to be configured by BL. + */ + ext_xo_clk = devm_clk_get(dev, "xo_ao"); + if (IS_ERR(ext_xo_clk)) { + if (PTR_ERR(ext_xo_clk) != -EPROBE_DEFER) + dev_err(dev, "Unable to get xo clock\n"); + return PTR_ERR(ext_xo_clk); + } + + is_sdm845v1 = of_device_is_compatible(pdev->dev.of_node, + "qcom,clk-cpu-osm"); + + if (of_device_is_compatible(pdev->dev.of_node, + "qcom,clk-cpu-osm-sdm670")) + clk_cpu_osm_driver_sdm670_fixup(); + + rc = clk_cpu_osm_request_mx_supply(dev); + if (rc) + return rc; + + clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data), + GFP_KERNEL); + if (!clk_data) + goto exit; + + clk_data->clks = devm_kzalloc(&pdev->dev, (num_clks * + sizeof(struct clk *)), GFP_KERNEL); + if (!clk_data->clks) + goto clk_err; + + clk_data->clk_num = num_clks; + + rc = clk_osm_resources_init(pdev); + if (rc) { + if (rc != -EPROBE_DEFER) + dev_err(&pdev->dev, "OSM resources init failed, rc=%d\n", + rc); + return rc; + } + + /* Check if per-core DCVS is enabled/not */ + val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL); + if (val & BIT(0)) + pwrcl_clk.per_core_dcvs = true; + + val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL); + if (val & BIT(0)) + perfcl_clk.per_core_dcvs = true; + + rc = clk_osm_read_lut(pdev, &l3_clk); + if (rc) { + dev_err(&pdev->dev, "Unable to read OSM LUT for L3, rc=%d\n", + rc); + return rc; + } + + rc = clk_osm_read_lut(pdev, &pwrcl_clk); + if (rc) { + dev_err(&pdev->dev, "Unable to read OSM LUT for power cluster, rc=%d\n", + rc); + return rc; + } + + rc = clk_osm_read_lut(pdev, &perfcl_clk); + if (rc) { + dev_err(&pdev->dev, "Unable to read OSM LUT for perf cluster, rc=%d\n", + rc); + return rc; + } + + spin_lock_init(&l3_clk.lock); + spin_lock_init(&pwrcl_clk.lock); + spin_lock_init(&perfcl_clk.lock); + + /* Register OSM l3, pwr and perf clocks with Clock Framework */ + for (i = 0; i < num_clks; i++) { + if (!osm_qcom_clk_hws[i]) + continue; + + clk = devm_clk_register(&pdev->dev, osm_qcom_clk_hws[i]); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to register CPU clock at index %d\n", + i); + return PTR_ERR(clk); + } + clk_data->clks[i] = clk; + } + + rc = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, + clk_data); + if (rc) { + dev_err(&pdev->dev, "Unable to register CPU clocks\n"); + goto provider_err; + } + + get_online_cpus(); + + WARN(clk_prepare_enable(l3_cluster0_vote_clk.hw.clk), + "clk: Failed to enable cluster0 clock for L3\n"); + WARN(clk_prepare_enable(l3_cluster1_vote_clk.hw.clk), + "clk: Failed to enable cluster1 clock for L3\n"); + WARN(clk_prepare_enable(l3_misc_vote_clk.hw.clk), + "clk: Failed to enable misc clock for L3\n"); + WARN(clk_prepare_enable(l3_gpu_vote_clk.hw.clk), + "clk: Failed to enable iocoherent bwmon clock for L3\n"); + + /* + * Call clk_prepare_enable for the silver clock explicitly in order to + * place an implicit vote on MX + */ + for_each_online_cpu(cpu) { + osm_clk = logical_cpu_to_clk(cpu); + if (!osm_clk) + return -EINVAL; + clk_prepare_enable(osm_clk->hw.clk); + } + populate_opp_table(pdev); + + of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + register_cpu_cycle_counter_cb(&cb); + put_online_cpus(); + + rc = cpufreq_register_driver(&qcom_osm_cpufreq_driver); + if (rc) + goto provider_err; + + pr_info("OSM CPUFreq driver inited\n"); + return 0; + +provider_err: + if (clk_data) + devm_kfree(&pdev->dev, clk_data->clks); +clk_err: + devm_kfree(&pdev->dev, clk_data); +exit: + dev_err(&pdev->dev, "OSM CPUFreq driver failed to initialize, rc=%d\n", + rc); + panic("Unable to Setup OSM CPUFreq"); +} + +static const struct of_device_id match_table[] = { + { .compatible = "qcom,clk-cpu-osm" }, + { .compatible = "qcom,clk-cpu-osm-v2" }, + { .compatible = "qcom,clk-cpu-osm-sdm670" }, + {} +}; + +static struct platform_driver clk_cpu_osm_driver = { + .probe = clk_cpu_osm_driver_probe, + .driver = { + .name = "clk-cpu-osm", + .of_match_table = match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init clk_cpu_osm_init(void) +{ + return platform_driver_register(&clk_cpu_osm_driver); +} +subsys_initcall(clk_cpu_osm_init); + +static void __exit clk_cpu_osm_exit(void) +{ + platform_driver_unregister(&clk_cpu_osm_driver); +} +module_exit(clk_cpu_osm_exit); + +MODULE_DESCRIPTION("QTI CPU clock driver for OSM"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h index d9e70ce8caf9..ff5cf5718002 100644 --- a/drivers/clk/qcom/clk-debug.h +++ b/drivers/clk/qcom/clk-debug.h @@ -36,6 +36,63 @@ struct measure_clk_data { u32 xo_div4_cbcr; }; +#ifdef CONFIG_MSM_DEBUGCC_SDM845 +/** + * List of Debug clock controllers. + */ +enum debug_cc { + GCC, + CAM_CC, + DISP_CC, + NPU_CC, + GPU_CC, + VIDEO_CC, + CPU_CC, + MC_CC, + MAX_NUM_CC, + CPU, +}; + +/** + * struct clk_src - Structure of clock source for debug mux + * + * @parents: clock name to be used as parent for debug mux. + * @prim_mux_sel: debug mux index at global clock controller. + * @prim_mux_div_val: PLL post-divider setting for the primary mux. + * @dbg_cc: indicates the clock controller for recursive debug + * clock controllers. + * @dbg_cc_mux_sel: indicates the debug mux index at recursive debug mux. + * @mux_sel_mask: indicates the mask for the mux selection. + * @mux_sel_shift: indicates the shift required for mux selection. + * @post_div_mask: indicates the post div mask to be used at recursive + * debug mux. + * @post_div_shift: indicates the shift required for post divider + * configuration. + * @post_div_val: indicates the post div value to be used at recursive + * debug mux. + * @mux_offset: the debug mux offset. + * @post_div_offset: register with post-divider settings for the debug mux. + * @cbcr_offset: branch register to turn on debug mux. + * @misc_div_val: includes any pre-set dividers in the measurement logic. + */ +struct clk_src { + const char *parents; + int prim_mux_sel; + u32 prim_mux_div_val; + enum debug_cc dbg_cc; + int dbg_cc_mux_sel; + u32 mux_sel_mask; + u32 mux_sel_shift; + u32 post_div_mask; + u32 post_div_shift; + u32 post_div_val; + u32 mux_offset; + u32 post_div_offset; + u32 cbcr_offset; + u32 misc_div_val; +}; +#endif + /** * struct clk_debug_mux - Structure of clock debug mux * @@ -63,6 +120,9 @@ struct measure_clk_data { * @hw: handle between common and hardware-specific interfaces. */ struct clk_debug_mux { +#ifdef CONFIG_MSM_DEBUGCC_SDM845 + struct clk_src *parent; +#endif int *mux_sels; int *pre_div_vals; int num_parents; @@ -82,6 +142,12 @@ struct clk_debug_mux { struct clk_hw hw; }; +#ifdef CONFIG_MSM_DEBUGCC_SDM845 +#define MUX_SRC_LIST(...) \ + .parent = (struct clk_src[]){__VA_ARGS__}, \ + .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) +#endif + #define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) extern const struct clk_ops clk_debug_mux_ops; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index dd77d6cc2590..4bed505c54b8 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -311,6 +311,7 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return rrate; } +#if !defined(CONFIG_ARCH_SDM845) static int _determine_parent_and_update_div(struct clk_hw *hw, const struct freq_tbl *f, struct clk_hw *parent) { @@ -380,6 +381,7 @@ static int _determine_parent_and_update_div(struct clk_hw *hw, return ret; } +#endif static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, struct clk_rate_request *req, @@ -389,6 +391,9 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, struct clk_hw *p; struct clk_rcg2 *rcg = to_clk_rcg2(hw); int index, ret = 0; +#if defined(CONFIG_ARCH_SDM845) + struct clk_rate_request parent_req = { }; +#endif switch (policy) { case FLOOR: @@ -410,11 +415,15 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, clk_flags = clk_hw_get_flags(hw); p = clk_hw_get_parent_by_index(hw, index); +#if !defined(CONFIG_ARCH_SDM845) if (!p) return -EINVAL; +#endif if (clk_flags & CLK_SET_RATE_PARENT) { +#if !defined(CONFIG_ARCH_SDM845) rate = f->freq; +#endif if (f->pre_div) { if (!rate) rate = req->rate; @@ -435,6 +444,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, req->best_parent_rate = rate; req->rate = f->freq; +#if !defined(CONFIG_ARCH_SDM845) if ((rcg->flags & RCG_UPDATE_BEFORE_PLL) && (clk_flags & CLK_SET_RATE_PARENT)) { @@ -445,6 +455,22 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, if (ret) pr_err("Failed to update the div value\n"); } +#else + if (f->src_freq != FIXED_FREQ_SRC) { + rate = parent_req.rate = f->src_freq; + parent_req.best_parent_hw = p; + ret = __clk_determine_rate(p, &parent_req); + if (ret) + return ret; + + ret = clk_set_rate(p->clk, parent_req.rate); + if (ret) { + pr_err("Failed set rate(%lu) on parent for non-fixed source\n", + parent_req.rate); + return ret; + } + } +#endif return ret; } diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c new file mode 100644 index 000000000000..fc45aa657602 --- /dev/null +++ b/drivers/clk/qcom/debugcc-sdm845.c @@ -0,0 +1,965 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-debug.h" + +#define MSM_BUS_VECTOR(_src, _dst, _ab, _ib) \ +{ \ + .src = _src, \ + .dst = _dst, \ + .ab = _ab, \ + .ib = _ib, \ +} + +static struct msm_bus_vectors clk_measure_vectors[] = { + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_CAMERA_CFG, 0, 0), + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_VENUS_CFG, 0, 0), + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_DISPLAY_CFG, 0, 0), + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_CAMERA_CFG, 0, 1), + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_VENUS_CFG, 0, 1), + MSM_BUS_VECTOR(MSM_BUS_MASTER_AMPSS_M0, + MSM_BUS_SLAVE_DISPLAY_CFG, 0, 1), +}; + +static struct msm_bus_paths clk_measure_usecases[] = { + { + .num_paths = 3, + .vectors = &clk_measure_vectors[0], + }, + { + .num_paths = 3, + .vectors = &clk_measure_vectors[3], + } +}; + +static struct msm_bus_scale_pdata clk_measure_scale_table = { + .usecase = clk_measure_usecases, + .num_usecases = ARRAY_SIZE(clk_measure_usecases), + .name = "clk_measure", +}; + +static struct measure_clk_data debug_mux_priv = { + .ctl_reg = 0x62024, + .status_reg = 0x62028, + .xo_div4_cbcr = 0x43008, +}; + +static const char *const debug_mux_parent_names[] = { + "cam_cc_bps_ahb_clk", + "cam_cc_bps_areg_clk", + "cam_cc_bps_axi_clk", + "cam_cc_bps_clk", + "cam_cc_camnoc_atb_clk", + "cam_cc_camnoc_axi_clk", + "cam_cc_cci_clk", + "cam_cc_cpas_ahb_clk", + "cam_cc_csi0phytimer_clk", + "cam_cc_csi1phytimer_clk", + "cam_cc_csi2phytimer_clk", + "cam_cc_csiphy0_clk", + "cam_cc_csiphy1_clk", + "cam_cc_csiphy2_clk", + "cam_cc_csiphy3_clk", + "cam_cc_fd_core_clk", + "cam_cc_fd_core_uar_clk", + "cam_cc_icp_apb_clk", + "cam_cc_icp_atb_clk", + "cam_cc_icp_clk", + "cam_cc_icp_cti_clk", + "cam_cc_icp_ts_clk", + "cam_cc_ife_0_axi_clk", + "cam_cc_ife_0_clk", + "cam_cc_ife_0_cphy_rx_clk", + "cam_cc_ife_0_csid_clk", + "cam_cc_ife_0_dsp_clk", + "cam_cc_ife_1_axi_clk", + "cam_cc_ife_1_clk", + "cam_cc_ife_1_cphy_rx_clk", + "cam_cc_ife_1_csid_clk", + "cam_cc_ife_1_dsp_clk", + "cam_cc_ife_lite_clk", + "cam_cc_ife_lite_cphy_rx_clk", + "cam_cc_ife_lite_csid_clk", + "cam_cc_ipe_0_ahb_clk", + "cam_cc_ipe_0_areg_clk", + "cam_cc_ipe_0_axi_clk", + "cam_cc_ipe_0_clk", + "cam_cc_ipe_1_ahb_clk", + "cam_cc_ipe_1_areg_clk", + "cam_cc_ipe_1_axi_clk", + "cam_cc_ipe_1_clk", + "cam_cc_jpeg_clk", + "cam_cc_lrme_clk", + "cam_cc_mclk0_clk", + "cam_cc_mclk1_clk", + "cam_cc_mclk2_clk", + "cam_cc_mclk3_clk", + "cam_cc_soc_ahb_clk", + "cam_cc_sys_tmr_clk", + "disp_cc_mdss_ahb_clk", + "disp_cc_mdss_axi_clk", + "disp_cc_mdss_byte0_clk", + "disp_cc_mdss_byte0_intf_clk", + "disp_cc_mdss_byte1_clk", + "disp_cc_mdss_byte1_intf_clk", + "disp_cc_mdss_dp_aux_clk", + "disp_cc_mdss_dp_crypto_clk", + "disp_cc_mdss_dp_link_clk", + "disp_cc_mdss_dp_link_intf_clk", + "disp_cc_mdss_dp_pixel1_clk", + "disp_cc_mdss_dp_pixel_clk", + "disp_cc_mdss_esc0_clk", + "disp_cc_mdss_esc1_clk", + "disp_cc_mdss_mdp_clk", + "disp_cc_mdss_mdp_lut_clk", + "disp_cc_mdss_pclk0_clk", + "disp_cc_mdss_pclk1_clk", + "disp_cc_mdss_qdss_at_clk", + "disp_cc_mdss_qdss_tsctr_div8_clk", + "disp_cc_mdss_rot_clk", + "disp_cc_mdss_rscc_ahb_clk", + "disp_cc_mdss_rscc_vsync_clk", + "disp_cc_mdss_vsync_clk", + "measure_only_snoc_clk", + "measure_only_cnoc_clk", + "measure_only_bimc_clk", + "measure_only_ipa_2x_clk", + "gcc_aggre_noc_pcie_tbu_clk", + "gcc_aggre_ufs_card_axi_clk", + "gcc_aggre_ufs_phy_axi_clk", + "gcc_aggre_usb3_prim_axi_clk", + "gcc_aggre_usb3_sec_axi_clk", + "gcc_apc_vs_clk", + "gcc_boot_rom_ahb_clk", + "gcc_camera_ahb_clk", + "gcc_camera_axi_clk", + "gcc_camera_xo_clk", + "gcc_ce1_ahb_clk", + "gcc_ce1_axi_clk", + "gcc_ce1_clk", + "gcc_cfg_noc_usb3_prim_axi_clk", + "gcc_cfg_noc_usb3_sec_axi_clk", + "gcc_cpuss_ahb_clk", + "gcc_cpuss_dvm_bus_clk", + "gcc_cpuss_gnoc_clk", + "gcc_cpuss_rbcpr_clk", + "gcc_ddrss_gpu_axi_clk", + "gcc_disp_ahb_clk", + "gcc_disp_axi_clk", + "gcc_disp_gpll0_clk_src", + "gcc_disp_gpll0_div_clk_src", + "gcc_disp_xo_clk", + "gcc_gp1_clk", + "gcc_gp2_clk", + "gcc_gp3_clk", + "gcc_gpu_cfg_ahb_clk", + "gcc_gpu_gpll0_clk_src", + "gcc_gpu_gpll0_div_clk_src", + "gcc_gpu_memnoc_gfx_clk", + "gcc_gpu_snoc_dvm_gfx_clk", + "gcc_gpu_vs_clk", + "gcc_mss_axis2_clk", + "gcc_mss_cfg_ahb_clk", + "gcc_mss_gpll0_div_clk_src", + "gcc_mss_mfab_axis_clk", + "gcc_mss_q6_memnoc_axi_clk", + "gcc_mss_snoc_axi_clk", + "gcc_mss_vs_clk", + "gcc_pcie_0_aux_clk", + "gcc_pcie_0_cfg_ahb_clk", + "gcc_pcie_0_mstr_axi_clk", + "gcc_pcie_0_pipe_clk", + "gcc_pcie_0_slv_axi_clk", + "gcc_pcie_0_slv_q2a_axi_clk", + "gcc_pcie_1_aux_clk", + "gcc_pcie_1_cfg_ahb_clk", + "gcc_pcie_1_mstr_axi_clk", + "gcc_pcie_1_pipe_clk", + "gcc_pcie_1_slv_axi_clk", + "gcc_pcie_1_slv_q2a_axi_clk", + "gcc_pcie_phy_aux_clk", + "gcc_pcie_phy_refgen_clk", + "gcc_pdm2_clk", + "gcc_pdm_ahb_clk", + "gcc_pdm_xo4_clk", + "gcc_prng_ahb_clk", + "gcc_qmip_camera_ahb_clk", + "gcc_qmip_disp_ahb_clk", + "gcc_qmip_video_ahb_clk", + "gcc_qupv3_wrap0_core_2x_clk", + "gcc_qupv3_wrap0_core_clk", + "gcc_qupv3_wrap0_s0_clk", + "gcc_qupv3_wrap0_s1_clk", + "gcc_qupv3_wrap0_s2_clk", + "gcc_qupv3_wrap0_s3_clk", + "gcc_qupv3_wrap0_s4_clk", + "gcc_qupv3_wrap0_s5_clk", + "gcc_qupv3_wrap0_s6_clk", + "gcc_qupv3_wrap0_s7_clk", + "gcc_qupv3_wrap1_core_2x_clk", + "gcc_qupv3_wrap1_core_clk", + "gcc_qupv3_wrap1_s0_clk", + "gcc_qupv3_wrap1_s1_clk", + "gcc_qupv3_wrap1_s2_clk", + "gcc_qupv3_wrap1_s3_clk", + "gcc_qupv3_wrap1_s4_clk", + "gcc_qupv3_wrap1_s5_clk", + "gcc_qupv3_wrap1_s6_clk", + "gcc_qupv3_wrap1_s7_clk", + "gcc_qupv3_wrap_0_m_ahb_clk", + "gcc_qupv3_wrap_0_s_ahb_clk", + "gcc_qupv3_wrap_1_m_ahb_clk", + "gcc_qupv3_wrap_1_s_ahb_clk", + "gcc_sdcc2_ahb_clk", + "gcc_sdcc2_apps_clk", + "gcc_sdcc4_ahb_clk", + "gcc_sdcc4_apps_clk", + "gcc_sys_noc_cpuss_ahb_clk", + "gcc_tsif_ahb_clk", + "gcc_tsif_inactivity_timers_clk", + "gcc_tsif_ref_clk", + "gcc_ufs_card_ahb_clk", + "gcc_ufs_card_axi_clk", + "gcc_ufs_card_ice_core_clk", + "gcc_ufs_card_phy_aux_clk", + "gcc_ufs_card_rx_symbol_0_clk", + "gcc_ufs_card_rx_symbol_1_clk", + "gcc_ufs_card_tx_symbol_0_clk", + "gcc_ufs_card_unipro_core_clk", + "gcc_ufs_phy_ahb_clk", + "gcc_ufs_phy_axi_clk", + "gcc_ufs_phy_ice_core_clk", + "gcc_ufs_phy_phy_aux_clk", + "gcc_ufs_phy_rx_symbol_0_clk", + "gcc_ufs_phy_rx_symbol_1_clk", + "gcc_ufs_phy_tx_symbol_0_clk", + "gcc_ufs_phy_unipro_core_clk", + "gcc_usb30_prim_master_clk", + "gcc_usb30_prim_mock_utmi_clk", + "gcc_usb30_prim_sleep_clk", + "gcc_usb30_sec_master_clk", + "gcc_usb30_sec_mock_utmi_clk", + "gcc_usb30_sec_sleep_clk", + "gcc_usb3_prim_phy_aux_clk", + "gcc_usb3_prim_phy_com_aux_clk", + "gcc_usb3_prim_phy_pipe_clk", + "gcc_usb3_sec_phy_aux_clk", + "gcc_usb3_sec_phy_com_aux_clk", + "gcc_usb3_sec_phy_pipe_clk", + "gcc_usb_phy_cfg_ahb2phy_clk", + "gcc_vdda_vs_clk", + "gcc_vddcx_vs_clk", + "gcc_vddmx_vs_clk", + "gcc_video_ahb_clk", + "gcc_video_axi_clk", + "gcc_video_xo_clk", + "gcc_vs_ctrl_ahb_clk", + "gcc_vs_ctrl_clk", + "gcc_sdcc1_ahb_clk", + "gcc_sdcc1_apps_clk", + "gcc_sdcc1_ice_core_clk", + "gpu_cc_acd_cxo_clk", + "gpu_cc_crc_ahb_clk", + "gpu_cc_cx_apb_clk", + "gpu_cc_cx_gfx3d_clk", + "gpu_cc_cx_gfx3d_slv_clk", + "gpu_cc_cx_gmu_clk", + "gpu_cc_cx_qdss_at_clk", + "gpu_cc_cx_qdss_trig_clk", + "gpu_cc_cx_qdss_tsctr_clk", + "gpu_cc_cx_snoc_dvm_clk", + "gpu_cc_cxo_aon_clk", + "gpu_cc_cxo_clk", + "gpu_cc_gx_gfx3d_clk", + "gpu_cc_gx_gmu_clk", + "gpu_cc_gx_qdss_tsctr_clk", + "gpu_cc_gx_vsense_clk", + "gpu_cc_rbcpr_ahb_clk", + "gpu_cc_rbcpr_clk", + "gpu_cc_sleep_clk", + "video_cc_apb_clk", + "video_cc_at_clk", + "video_cc_qdss_trig_clk", + "video_cc_qdss_tsctr_div8_clk", + "video_cc_vcodec0_axi_clk", + "video_cc_vcodec0_core_clk", + "video_cc_vcodec1_axi_clk", + "video_cc_vcodec1_core_clk", + "video_cc_venus_ahb_clk", + "video_cc_venus_ctl_axi_clk", + "video_cc_venus_ctl_core_clk", + "l3_clk", + "pwrcl_clk", + "perfcl_clk", +}; + +static struct clk_debug_mux gcc_debug_mux = { + .priv = &debug_mux_priv, + .debug_offset = 0x62008, + .post_div_offset = 0x62000, + .cbcr_offset = 0x62004, + .src_sel_mask = 0x3FF, + .src_sel_shift = 0, + .post_div_mask = 0xF, + .post_div_shift = 0, + MUX_SRC_LIST( + { "cam_cc_bps_ahb_clk", 0x46, 4, CAM_CC, + 0xE, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_bps_areg_clk", 0x46, 4, CAM_CC, + 0xD, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_bps_axi_clk", 0x46, 4, CAM_CC, + 0xC, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_bps_clk", 0x46, 4, CAM_CC, + 0xB, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_camnoc_atb_clk", 0x46, 4, CAM_CC, + 0x34, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_camnoc_axi_clk", 0x46, 4, CAM_CC, + 0x2D, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_cci_clk", 0x46, 4, CAM_CC, + 0x2A, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_cpas_ahb_clk", 0x46, 4, CAM_CC, + 0x2C, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csi0phytimer_clk", 0x46, 4, CAM_CC, + 0x5, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csi1phytimer_clk", 0x46, 4, CAM_CC, + 0x7, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csi2phytimer_clk", 0x46, 4, CAM_CC, + 0x9, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csiphy0_clk", 0x46, 4, CAM_CC, + 0x6, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csiphy1_clk", 0x46, 4, CAM_CC, + 0x8, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csiphy2_clk", 0x46, 4, CAM_CC, + 0xA, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_csiphy3_clk", 0x46, 4, CAM_CC, + 0x36, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_fd_core_clk", 0x46, 4, CAM_CC, + 0x28, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_fd_core_uar_clk", 0x46, 4, CAM_CC, + 0x29, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_icp_apb_clk", 0x46, 4, CAM_CC, + 0x32, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_icp_atb_clk", 0x46, 4, CAM_CC, + 0x2F, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_icp_clk", 0x46, 4, CAM_CC, + 0x26, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_icp_cti_clk", 0x46, 4, CAM_CC, + 0x30, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_icp_ts_clk", 0x46, 4, CAM_CC, + 0x31, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_0_axi_clk", 0x46, 4, CAM_CC, + 0x1B, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_0_clk", 0x46, 4, CAM_CC, + 0x17, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_0_cphy_rx_clk", 0x46, 4, CAM_CC, + 0x1A, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_0_csid_clk", 0x46, 4, CAM_CC, + 0x19, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_0_dsp_clk", 0x46, 4, CAM_CC, + 0x18, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_1_axi_clk", 0x46, 4, CAM_CC, + 0x21, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_1_clk", 0x46, 4, CAM_CC, + 0x1D, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_1_cphy_rx_clk", 0x46, 4, CAM_CC, + 0x20, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_1_csid_clk", 0x46, 4, CAM_CC, + 0x1F, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_1_dsp_clk", 0x46, 4, CAM_CC, + 0x1E, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_lite_clk", 0x46, 4, CAM_CC, + 0x22, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_lite_cphy_rx_clk", 0x46, 4, CAM_CC, + 0x24, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ife_lite_csid_clk", 0x46, 4, CAM_CC, + 0x23, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_0_ahb_clk", 0x46, 4, CAM_CC, + 0x12, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_0_areg_clk", 0x46, 4, CAM_CC, + 0x11, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_0_axi_clk", 0x46, 4, CAM_CC, + 0x10, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_0_clk", 0x46, 4, CAM_CC, + 0xF, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_1_ahb_clk", 0x46, 4, CAM_CC, + 0x16, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_1_areg_clk", 0x46, 4, CAM_CC, + 0x15, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_1_axi_clk", 0x46, 4, CAM_CC, + 0x14, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_ipe_1_clk", 0x46, 4, CAM_CC, + 0x13, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_jpeg_clk", 0x46, 4, CAM_CC, + 0x25, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_lrme_clk", 0x46, 4, CAM_CC, + 0x2B, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_mclk0_clk", 0x46, 4, CAM_CC, + 0x1, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_mclk1_clk", 0x46, 4, CAM_CC, + 0x2, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_mclk2_clk", 0x46, 4, CAM_CC, + 0x3, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_mclk3_clk", 0x46, 4, CAM_CC, + 0x4, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_soc_ahb_clk", 0x46, 4, CAM_CC, + 0x2E, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "cam_cc_sys_tmr_clk", 0x46, 4, CAM_CC, + 0x33, 0xFF, 0, 0x3, 0, 1, 0xC000, 0xC004, 0xC008 }, + { "disp_cc_mdss_ahb_clk", 0x47, 4, DISP_CC, + 0x13, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_axi_clk", 0x47, 4, DISP_CC, + 0x14, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_byte0_clk", 0x47, 4, DISP_CC, + 0x7, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_byte0_intf_clk", 0x47, 4, DISP_CC, + 0x8, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_byte1_clk", 0x47, 4, DISP_CC, + 0x9, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_byte1_intf_clk", 0x47, 4, DISP_CC, + 0xA, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_aux_clk", 0x47, 4, DISP_CC, + 0x12, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_crypto_clk", 0x47, 4, DISP_CC, + 0xF, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_link_clk", 0x47, 4, DISP_CC, + 0xD, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_link_intf_clk", 0x47, 4, DISP_CC, + 0xE, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_pixel1_clk", 0x47, 4, DISP_CC, + 0x11, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_dp_pixel_clk", 0x47, 4, DISP_CC, + 0x10, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_esc0_clk", 0x47, 4, DISP_CC, + 0xB, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_esc1_clk", 0x47, 4, DISP_CC, + 0xC, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_mdp_clk", 0x47, 4, DISP_CC, + 0x3, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_mdp_lut_clk", 0x47, 4, DISP_CC, + 0x5, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_pclk0_clk", 0x47, 4, DISP_CC, + 0x1, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_pclk1_clk", 0x47, 4, DISP_CC, + 0x2, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_qdss_at_clk", 0x47, 4, DISP_CC, + 0x15, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_qdss_tsctr_div8_clk", 0x47, 4, DISP_CC, + 0x16, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_rot_clk", 0x47, 4, DISP_CC, + 0x4, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_rscc_ahb_clk", 0x47, 4, DISP_CC, + 0x17, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_rscc_vsync_clk", 0x47, 4, DISP_CC, + 0x18, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "disp_cc_mdss_vsync_clk", 0x47, 4, DISP_CC, + 0x6, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C }, + { "measure_only_snoc_clk", 0x7, 4, GCC, + 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "measure_only_cnoc_clk", 0x15, 4, GCC, + 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "measure_only_bimc_clk", 0xc2, 4, GCC, + 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "measure_only_ipa_2x_clk", 0x128, 4, GCC, + 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_aggre_noc_pcie_tbu_clk", 0x2D, 4, GCC, + 0x2D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_aggre_ufs_card_axi_clk", 0x11E, 4, GCC, + 0x11E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_aggre_ufs_phy_axi_clk", 0x11D, 4, GCC, + 0x11D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_aggre_usb3_prim_axi_clk", 0x11B, 4, GCC, + 0x11B, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_aggre_usb3_sec_axi_clk", 0x11C, 4, GCC, + 0x11C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_apc_vs_clk", 0x113, 4, GCC, + 0x113, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_boot_rom_ahb_clk", 0x94, 4, GCC, + 0x94, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_camera_ahb_clk", 0x3A, 4, GCC, + 0x3A, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_camera_axi_clk", 0x40, 4, GCC, + 0x40, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_camera_xo_clk", 0x43, 4, GCC, + 0x43, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ce1_ahb_clk", 0xA9, 4, GCC, + 0xA9, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ce1_axi_clk", 0xA8, 4, GCC, + 0xA8, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ce1_clk", 0xA7, 4, GCC, + 0xA7, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cfg_noc_usb3_prim_axi_clk", 0x1D, 4, GCC, + 0x1D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cfg_noc_usb3_sec_axi_clk", 0x1E, 4, GCC, + 0x1E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cpuss_ahb_clk", 0xCE, 4, GCC, + 0xCE, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cpuss_dvm_bus_clk", 0xD3, 4, GCC, + 0xD3, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cpuss_gnoc_clk", 0xCF, 4, GCC, + 0xCF, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_cpuss_rbcpr_clk", 0xD0, 4, GCC, + 0xD0, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ddrss_gpu_axi_clk", 0xBB, 4, GCC, + 0xBB, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_disp_ahb_clk", 0x3B, 4, GCC, + 0x3B, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_disp_axi_clk", 0x41, 4, GCC, + 0x41, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_disp_gpll0_clk_src", 0x4C, 4, GCC, + 0x4C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_disp_gpll0_div_clk_src", 0x4D, 4, GCC, + 0x4D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_disp_xo_clk", 0x44, 4, GCC, + 0x44, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gp1_clk", 0xDE, 4, GCC, + 0xDE, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gp2_clk", 0xDF, 4, GCC, + 0xDF, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gp3_clk", 0xE0, 4, GCC, + 0xE0, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_cfg_ahb_clk", 0x142, 4, GCC, + 0x142, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_gpll0_clk_src", 0x148, 4, GCC, + 0x148, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_gpll0_div_clk_src", 0x149, 4, GCC, + 0x149, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_memnoc_gfx_clk", 0x145, 4, GCC, + 0x145, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_snoc_dvm_gfx_clk", 0x147, 4, GCC, + 0x147, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_gpu_vs_clk", 0x112, 4, GCC, + 0x112, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_axis2_clk", 0x12F, 4, GCC, + 0x12F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_cfg_ahb_clk", 0x12D, 4, GCC, + 0x12D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_gpll0_div_clk_src", 0x133, 4, GCC, + 0x133, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_mfab_axis_clk", 0x12E, 4, GCC, + 0x12E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_q6_memnoc_axi_clk", 0x135, 4, GCC, + 0x135, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_snoc_axi_clk", 0x134, 4, GCC, + 0x134, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_mss_vs_clk", 0x111, 4, GCC, + 0x111, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_aux_clk", 0xE5, 4, GCC, + 0xE5, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_cfg_ahb_clk", 0xE4, 4, GCC, + 0xE4, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_mstr_axi_clk", 0xE3, 4, GCC, + 0xE3, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_pipe_clk", 0xE6, 4, GCC, + 0xE6, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_slv_axi_clk", 0xE2, 4, GCC, + 0xE2, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_0_slv_q2a_axi_clk", 0xE1, 4, GCC, + 0xE1, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_aux_clk", 0xEC, 4, GCC, + 0xEC, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_cfg_ahb_clk", 0xEB, 4, GCC, + 0xEB, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_mstr_axi_clk", 0xEA, 4, GCC, + 0xEA, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_pipe_clk", 0xED, 4, GCC, + 0xED, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_slv_axi_clk", 0xE9, 4, GCC, + 0xE9, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_1_slv_q2a_axi_clk", 0xE8, 4, GCC, + 0xE8, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_phy_aux_clk", 0xEF, 4, GCC, + 0xEF, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pcie_phy_refgen_clk", 0x160, 4, GCC, + 0x160, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pdm2_clk", 0x8E, 4, GCC, + 0x8E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pdm_ahb_clk", 0x8C, 4, GCC, + 0x8C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_pdm_xo4_clk", 0x8D, 4, GCC, + 0x8D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_prng_ahb_clk", 0x8F, 4, GCC, + 0x8F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qmip_camera_ahb_clk", 0x3D, 4, GCC, + 0x3D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qmip_disp_ahb_clk", 0x3E, 4, GCC, + 0x3E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qmip_video_ahb_clk", 0x3C, 4, GCC, + 0x3C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_core_2x_clk", 0x77, 4, GCC, + 0x77, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_core_clk", 0x76, 4, GCC, + 0x76, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s0_clk", 0x78, 4, GCC, + 0x78, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s1_clk", 0x79, 4, GCC, + 0x79, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s2_clk", 0x7A, 4, GCC, + 0x7A, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s3_clk", 0x7B, 4, GCC, + 0x7B, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s4_clk", 0x7C, 4, GCC, + 0x7C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s5_clk", 0x7D, 4, GCC, + 0x7D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s6_clk", 0x7E, 4, GCC, + 0x7E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap0_s7_clk", 0x7F, 4, GCC, + 0x7F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_core_2x_clk", 0x80, 4, GCC, + 0x80, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_core_clk", 0x81, 4, GCC, + 0x81, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s0_clk", 0x84, 4, GCC, + 0x84, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s1_clk", 0x85, 4, GCC, + 0x85, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s2_clk", 0x86, 4, GCC, + 0x86, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s3_clk", 0x87, 4, GCC, + 0x87, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s4_clk", 0x88, 4, GCC, + 0x88, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s5_clk", 0x89, 4, GCC, + 0x89, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s6_clk", 0x8A, 4, GCC, + 0x8A, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap1_s7_clk", 0x8B, 4, GCC, + 0x8B, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap_0_m_ahb_clk", 0x74, 4, GCC, + 0x74, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap_0_s_ahb_clk", 0x75, 4, GCC, + 0x75, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap_1_m_ahb_clk", 0x82, 4, GCC, + 0x82, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_qupv3_wrap_1_s_ahb_clk", 0x83, 4, GCC, + 0x83, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc2_ahb_clk", 0x71, 4, GCC, + 0x71, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc2_apps_clk", 0x70, 4, GCC, + 0x70, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc4_ahb_clk", 0x73, 4, GCC, + 0x73, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc4_apps_clk", 0x72, 4, GCC, + 0x72, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sys_noc_cpuss_ahb_clk", 0xC, 4, GCC, + 0xC, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_tsif_ahb_clk", 0x90, 4, GCC, + 0x90, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_tsif_inactivity_timers_clk", 0x92, 4, GCC, + 0x92, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_tsif_ref_clk", 0x91, 4, GCC, + 0x91, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_ahb_clk", 0xF1, 4, GCC, + 0xF1, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_axi_clk", 0xF0, 4, GCC, + 0xF0, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_ice_core_clk", 0xF7, 4, GCC, + 0xF7, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_phy_aux_clk", 0xF8, 4, GCC, + 0xF8, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_rx_symbol_0_clk", 0xF3, 4, GCC, + 0xF3, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_rx_symbol_1_clk", 0xF9, 4, GCC, + 0xF9, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_tx_symbol_0_clk", 0xF2, 4, GCC, + 0xF2, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_card_unipro_core_clk", 0xF6, 4, GCC, + 0xF6, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_ahb_clk", 0xFC, 4, GCC, + 0xFC, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_axi_clk", 0xFB, 4, GCC, + 0xFB, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_ice_core_clk", 0x102, 4, GCC, + 0x102, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_phy_aux_clk", 0x103, 4, GCC, + 0x103, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_rx_symbol_0_clk", 0xFE, 4, GCC, + 0xFE, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_rx_symbol_1_clk", 0x104, 4, GCC, + 0x104, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_tx_symbol_0_clk", 0xFD, 4, GCC, + 0xFD, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_ufs_phy_unipro_core_clk", 0x101, 4, GCC, + 0x101, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_prim_master_clk", 0x5F, 4, GCC, + 0x5F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_prim_mock_utmi_clk", 0x61, 4, GCC, + 0x61, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_prim_sleep_clk", 0x60, 4, GCC, + 0x60, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_sec_master_clk", 0x65, 4, GCC, + 0x65, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_sec_mock_utmi_clk", 0x67, 4, GCC, + 0x67, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb30_sec_sleep_clk", 0x66, 4, GCC, + 0x66, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_prim_phy_aux_clk", 0x62, 4, GCC, + 0x62, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_prim_phy_com_aux_clk", 0x63, 4, GCC, + 0x63, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_prim_phy_pipe_clk", 0x64, 4, GCC, + 0x64, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_sec_phy_aux_clk", 0x68, 4, GCC, + 0x68, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_sec_phy_com_aux_clk", 0x69, 4, GCC, + 0x69, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb3_sec_phy_pipe_clk", 0x6A, 4, GCC, + 0x6A, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_usb_phy_cfg_ahb2phy_clk", 0x6F, 4, GCC, + 0x6F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_vdda_vs_clk", 0x10E, 4, GCC, + 0x10E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_vddcx_vs_clk", 0x10C, 4, GCC, + 0x10C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_vddmx_vs_clk", 0x10D, 4, GCC, + 0x10D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_video_ahb_clk", 0x39, 4, GCC, + 0x39, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_video_axi_clk", 0x3F, 4, GCC, + 0x3F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_video_xo_clk", 0x42, 4, GCC, + 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_vs_ctrl_ahb_clk", 0x110, 4, GCC, + 0x110, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_vs_ctrl_clk", 0x10F, 4, GCC, + 0x10F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc1_ahb_clk", 0x15C, 4, GCC, + 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc1_apps_clk", 0x15B, 4, GCC, + 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gcc_sdcc1_ice_core_clk", 0x15D, 4, GCC, + 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 }, + { "gpu_cc_acd_cxo_clk", 0x144, 4, GPU_CC, + 0x1F, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_crc_ahb_clk", 0x144, 4, GPU_CC, + 0x12, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_apb_clk", 0x144, 4, GPU_CC, + 0x15, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_gfx3d_clk", 0x144, 4, GPU_CC, + 0x1A, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_gfx3d_slv_clk", 0x144, 4, GPU_CC, + 0x1B, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_gmu_clk", 0x144, 4, GPU_CC, + 0x19, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_qdss_at_clk", 0x144, 4, GPU_CC, + 0x13, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_qdss_trig_clk", 0x144, 4, GPU_CC, + 0x18, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_qdss_tsctr_clk", 0x144, 4, GPU_CC, + 0x14, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cx_snoc_dvm_clk", 0x144, 4, GPU_CC, + 0x16, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cxo_aon_clk", 0x144, 4, GPU_CC, + 0xB, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_cxo_clk", 0x144, 4, GPU_CC, + 0xA, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_gx_gfx3d_clk", 0x144, 4, GPU_CC, + 0xC, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_gx_gmu_clk", 0x144, 4, GPU_CC, + 0x10, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_gx_qdss_tsctr_clk", 0x144, 4, GPU_CC, + 0xE, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_gx_vsense_clk", 0x144, 4, GPU_CC, + 0xD, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_rbcpr_ahb_clk", 0x144, 4, GPU_CC, + 0x1D, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_rbcpr_clk", 0x144, 4, GPU_CC, + 0x1C, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "gpu_cc_sleep_clk", 0x144, 4, GPU_CC, + 0x17, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 }, + { "video_cc_apb_clk", 0x48, 4, VIDEO_CC, + 0x8, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_at_clk", 0x48, 4, VIDEO_CC, + 0xB, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_qdss_trig_clk", 0x48, 4, VIDEO_CC, + 0x7, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_qdss_tsctr_div8_clk", 0x48, 4, VIDEO_CC, + 0xA, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_vcodec0_axi_clk", 0x48, 4, VIDEO_CC, + 0x5, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_vcodec0_core_clk", 0x48, 4, VIDEO_CC, + 0x2, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_vcodec1_axi_clk", 0x48, 4, VIDEO_CC, + 0x6, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_vcodec1_core_clk", 0x48, 4, VIDEO_CC, + 0x3, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_venus_ahb_clk", 0x48, 4, VIDEO_CC, + 0x9, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_venus_ctl_axi_clk", 0x48, 4, VIDEO_CC, + 0x4, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "video_cc_venus_ctl_core_clk", 0x48, 4, VIDEO_CC, + 0x1, 0x3F, 0, 0x7, 0, 1, 0xA4C, 0xA50, 0xA58 }, + { "l3_clk", 0xD6, 4, CPU, + 0x46, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + { "pwrcl_clk", 0xD6, 4, CPU, + 0x44, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + { "perfcl_clk", 0xD6, 4, CPU, + 0x45, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + ), + .hw.init = &(struct clk_init_data){ + .name = "gcc_debug_mux", + .ops = &clk_debug_mux_ops, + .parent_names = debug_mux_parent_names, + .num_parents = ARRAY_SIZE(debug_mux_parent_names), + .flags = CLK_IS_MEASURE, + }, +}; + +static const struct of_device_id clk_debug_match_table[] = { + { .compatible = "qcom,debugcc-sdm845" }, + {} +}; + +#define GCC_REGMAP(_mux, _index) ((struct regmap **)(_mux)->regmap)[_index] +static int clk_debug_845_probe(struct platform_device *pdev) +{ + struct clk *clk; + int ret = 0, count; + + clk = devm_clk_get(&pdev->dev, "xo_clk_src"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get xo clock\n"); + return PTR_ERR(clk); + } + + debug_mux_priv.cxo = clk; + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,cc-count", + &count); + if (ret < 0) { + dev_err(&pdev->dev, "Num of debug clock controller not specified\n"); + return ret; + } + + if (!count) { + dev_err(&pdev->dev, "Count of CC cannot be zero\n"); + return -EINVAL; + } + + gcc_debug_mux.regmap = devm_kzalloc(&pdev->dev, + sizeof(struct regmap *) * count, GFP_KERNEL); + if (!gcc_debug_mux.regmap) + return -ENOMEM; + + if (of_get_property(pdev->dev.of_node, "qcom,gcc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, GCC) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,gcc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, GCC))) { + pr_err("Failed to map qcom,gcc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, GCC)); + } + } + + if (of_get_property(pdev->dev.of_node, "qcom,dispcc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, DISP_CC) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,dispcc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, DISP_CC))) { + pr_err("Failed to map qcom,dispcc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, DISP_CC)); + } + } + + if (of_get_property(pdev->dev.of_node, "qcom,videocc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, VIDEO_CC) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,videocc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, VIDEO_CC))) { + pr_err("Failed to map qcom,videocc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, VIDEO_CC)); + } + } + + if (of_get_property(pdev->dev.of_node, "qcom,camcc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, CAM_CC) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,camcc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, CAM_CC))) { + pr_err("Failed to map qcom,camcc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, CAM_CC)); + } + } + + if (of_get_property(pdev->dev.of_node, "qcom,gpucc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, GPU_CC) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,gpucc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, GPU_CC))) { + pr_err("Failed to map qcom,gpucc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, GPU_CC)); + } + } + + if (of_get_property(pdev->dev.of_node, "qcom,cpucc", NULL)) { + GCC_REGMAP(&gcc_debug_mux, CPU) = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "qcom,cpucc"); + if (IS_ERR(GCC_REGMAP(&gcc_debug_mux, CPU))) { + pr_err("Failed to map qcom,cpucc\n"); + return PTR_ERR(GCC_REGMAP(&gcc_debug_mux, CPU)); + } + } + + gcc_debug_mux.bus_cl_id = + msm_bus_scale_register_client(&clk_measure_scale_table); + + if (!gcc_debug_mux.bus_cl_id) + return -EPROBE_DEFER; + + clk = devm_clk_register(&pdev->dev, &gcc_debug_mux.hw); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to register GCC debug mux\n"); + return PTR_ERR(clk); + } + + ret = clk_debug_measure_register(&gcc_debug_mux.hw); + if (ret) + dev_err(&pdev->dev, "Could not register Measure clock\n"); + else + dev_info(&pdev->dev, "Registered debug mux successfully\n"); + + return ret; +} + +static struct platform_driver clk_debug_driver = { + .probe = clk_debug_845_probe, + .driver = { + .name = "debugcc-sdm845", + .of_match_table = clk_debug_match_table, + .owner = THIS_MODULE, + }, +}; + +int __init clk_debug_845_init(void) +{ + return platform_driver_register(&clk_debug_driver); +} +fs_initcall(clk_debug_845_init); + +MODULE_DESCRIPTION("QTI DEBUG CC SDM845 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:debugcc-sdm845"); diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c index cb7a2d9247b0..0a13e8b0dca6 100644 --- a/drivers/clk/qcom/dispcc-sdm845.c +++ b/drivers/clk/qcom/dispcc-sdm845.c @@ -1,28 +1,46 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */ -#include -#include +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include -#include "clk-alpha-pll.h" -#include "clk-branch.h" -#include "clk-rcg.h" -#include "clk-regmap-divider.h" #include "common.h" -#include "gdsc.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" #include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-sdm845.h" +#include "clk-regmap-divider.h" + +#define DISP_CC_MISC_CMD 0x8000 + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner); enum { P_BI_TCXO, P_CORE_BI_PLL_TEST_SE, P_DISP_CC_PLL0_OUT_MAIN, + P_DP_PHY_PLL_LINK_CLK, + P_DP_PHY_PLL_VCO_DIV_CLK, P_DSI0_PHY_PLL_OUT_BYTECLK, P_DSI0_PHY_PLL_OUT_DSICLK, P_DSI1_PHY_PLL_OUT_BYTECLK, @@ -45,6 +63,20 @@ static const char * const disp_cc_parent_names_0[] = { "core_bi_pll_test_se", }; +static const struct parent_map disp_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_DP_PHY_PLL_LINK_CLK, 1 }, + { P_DP_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_1[] = { + "bi_tcxo", + "dp_phy_pll_link_clk", + "dp_phy_pll_vco_div_clk", + "core_bi_pll_test_se", +}; + static const struct parent_map disp_cc_parent_map_2[] = { { P_BI_TCXO, 0 }, { P_CORE_BI_PLL_TEST_SE, 7 }, @@ -85,8 +117,25 @@ static const char * const disp_cc_parent_names_4[] = { "core_bi_pll_test_se", }; +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, + { 125000000, 1000000000, 1 }, +}; + +static const struct alpha_pll_config disp_cc_pll0_config = { + .l = 0x15, + .alpha = 0x7c00, +}; + +static const struct alpha_pll_config disp_cc_pll0_config_v2 = { + .l = 0x2c, + .alpha = 0xcaaa, +}; + static struct clk_alpha_pll disp_cc_pll0 = { .offset = 0x0, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], .clkr = { .hw.init = &(struct clk_init_data){ @@ -94,11 +143,15 @@ static struct clk_alpha_pll disp_cc_pll0 = { .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_alpha_pll_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { .cmd_rcgr = 0x20d0, .mnd_width = 0, @@ -108,12 +161,17 @@ static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { .name = "disp_cc_mdss_byte0_clk_src", .parent_names = disp_cc_parent_names_0, .num_parents = 4, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_byte2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 150000000, + LOW, 240000000, + LOW_L1, 262500000, + NOMINAL, 358000000), }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { .cmd_rcgr = 0x20ec, .mnd_width = 0, @@ -123,8 +181,132 @@ static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { .name = "disp_cc_mdss_byte1_clk_src", .parent_names = disp_cc_parent_names_0, .num_parents = 4, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_byte2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 150000000, + LOW, 240000000, + LOW_L1, 262500000, + NOMINAL, 358000000), + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_aux_clk_src = { + .cmd_rcgr = 0x219c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_crypto_clk_src[] = { + F( 108000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 180000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 360000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 540000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_crypto_clk_src = { + .cmd_rcgr = 0x2154, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_crypto_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 4, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 12800, + LOWER, 108000, + LOW, 180000, + LOW_L1, 360000, + NOMINAL, 540000), + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_link_clk_src[] = { + F( 162000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 270000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 540000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 810000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = { + .cmd_rcgr = 0x2138, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_link_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200, + LOWER, 162000, + LOW, 270000, + LOW_L1, 540000, + NOMINAL, 810000), + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel1_clk_src = { + .cmd_rcgr = 0x2184, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200, + LOWER, 202500, + LOW, 296735, + LOW_L1, 675000), + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel_clk_src = { + .cmd_rcgr = 0x216c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200, + LOWER, 202500, + LOW, 296735, + LOW_L1, 675000), }, }; @@ -138,12 +320,14 @@ static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = disp_cc_parent_map_0, - .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "disp_cc_mdss_esc0_clk_src", .parent_names = disp_cc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_esc_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -152,16 +336,31 @@ static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = disp_cc_parent_map_0, - .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "disp_cc_mdss_esc1_clk_src", .parent_names = disp_cc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_esc_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_GPLL0_OUT_MAIN, 7, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(165000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(275000000, P_DISP_CC_PLL0_OUT_MAIN, 1.5, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(412500000, P_DISP_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src_sdm845_v2[] = { F(19200000, P_BI_TCXO, 1, 0, 0), F(85714286, P_GPLL0_OUT_MAIN, 7, 0, 0), F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), @@ -174,21 +373,41 @@ static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { { } }; +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src_sdm670[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_GPLL0_OUT_MAIN, 7, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(171428571, P_GPLL0_OUT_MAIN, 3.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(286666667, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(344000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(430000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { .cmd_rcgr = 0x2088, .mnd_width = 0, .hid_width = 5, .parent_map = disp_cc_parent_map_3, .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "disp_cc_mdss_mdp_clk_src", .parent_names = disp_cc_parent_names_3, .num_parents = 5, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 165000000, + LOW, 300000000, + NOMINAL, 412500000), }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { .cmd_rcgr = 0x2058, .mnd_width = 8, @@ -198,12 +417,17 @@ static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { .name = "disp_cc_mdss_pclk0_clk_src", .parent_names = disp_cc_parent_names_4, .num_parents = 4, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_pixel_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 184000000, + LOW, 295000000, + LOW_L1, 350000000, + NOMINAL, 571428571), }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { .cmd_rcgr = 0x2070, .mnd_width = 8, @@ -213,12 +437,26 @@ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { .name = "disp_cc_mdss_pclk1_clk_src", .parent_names = disp_cc_parent_names_4, .num_parents = 4, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_pixel_ops, + VDD_CX_FMAX_MAP5( + MIN, 19200000, + LOWER, 184000000, + LOW, 295000000, + LOW_L1, 350000000, + NOMINAL, 571428571), }, }; static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(165000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(412500000, P_DISP_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src_sdm845_v2[] = { F(19200000, P_BI_TCXO, 1, 0, 0), F(171428571, P_GPLL0_OUT_MAIN, 3.5, 0, 0), F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), @@ -233,11 +471,18 @@ static struct clk_rcg2 disp_cc_mdss_rot_clk_src = { .hid_width = 5, .parent_map = disp_cc_parent_map_3, .freq_tbl = ftbl_disp_cc_mdss_rot_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "disp_cc_mdss_rot_clk_src", .parent_names = disp_cc_parent_names_3, .num_parents = 5, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 165000000, + LOW, 300000000, + NOMINAL, 412500000), }, }; @@ -251,7 +496,10 @@ static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { .name = "disp_cc_mdss_vsync_clk_src", .parent_names = disp_cc_parent_names_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -281,7 +529,6 @@ static struct clk_branch disp_cc_mdss_axi_clk = { }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_byte0_clk = { .halt_reg = 0x2028, .halt_check = BRANCH_HALT, @@ -294,13 +541,12 @@ static struct clk_branch disp_cc_mdss_byte0_clk = { "disp_cc_mdss_byte0_clk_src", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_branch2_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { .reg = 0x20e8, .shift = 0, @@ -312,12 +558,12 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { "disp_cc_mdss_byte0_clk_src", }, .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_byte0_intf_clk = { .halt_reg = 0x202c, .halt_check = BRANCH_HALT, @@ -330,13 +576,12 @@ static struct clk_branch disp_cc_mdss_byte0_intf_clk = { "disp_cc_mdss_byte0_div_clk_src", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_branch2_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_byte1_clk = { .halt_reg = 0x2030, .halt_check = BRANCH_HALT, @@ -349,13 +594,12 @@ static struct clk_branch disp_cc_mdss_byte1_clk = { "disp_cc_mdss_byte1_clk_src", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_branch2_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { .reg = 0x2104, .shift = 0, @@ -367,12 +611,12 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { "disp_cc_mdss_byte1_clk_src", }, .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, .ops = &clk_regmap_div_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_byte1_intf_clk = { .halt_reg = 0x2034, .halt_check = BRANCH_HALT, @@ -385,12 +629,121 @@ static struct clk_branch disp_cc_mdss_byte1_intf_clk = { "disp_cc_mdss_byte1_div_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_aux_clk = { + .halt_reg = 0x2054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_aux_clk_src", + }, + .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, }; +static struct clk_branch disp_cc_mdss_dp_crypto_clk = { + .halt_reg = 0x2048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_crypto_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_clk = { + .halt_reg = 0x2040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* reset state of disp_cc_mdss_dp_link_div_clk_src divider is 0x3 (div 4) */ +static struct clk_branch disp_cc_mdss_dp_link_intf_clk = { + .halt_reg = 0x2044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel1_clk = { + .halt_reg = 0x2050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_pixel1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel_clk = { + .halt_reg = 0x204c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x204c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_pixel_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch disp_cc_mdss_esc0_clk = { .halt_reg = 0x2038, .halt_check = BRANCH_HALT, @@ -462,7 +815,6 @@ static struct clk_branch disp_cc_mdss_mdp_lut_clk = { }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_pclk0_clk = { .halt_reg = 0x2004, .halt_check = BRANCH_HALT, @@ -475,13 +827,12 @@ static struct clk_branch disp_cc_mdss_pclk0_clk = { "disp_cc_mdss_pclk0_clk_src", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, .ops = &clk_branch2_ops, }, }, }; -/* Return the HW recalc rate for idle use case */ static struct clk_branch disp_cc_mdss_pclk1_clk = { .halt_reg = 0x2008, .halt_check = BRANCH_HALT, @@ -494,7 +845,33 @@ static struct clk_branch disp_cc_mdss_pclk1_clk = { "disp_cc_mdss_pclk1_clk_src", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_qdss_at_clk = { + .halt_reg = 0x4010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_qdss_at_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_qdss_tsctr_div8_clk = { + .halt_reg = 0x4014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_qdss_tsctr_div8_clk", .ops = &clk_branch2_ops, }, }, @@ -567,17 +944,6 @@ static struct clk_branch disp_cc_mdss_vsync_clk = { }, }; -static struct gdsc mdss_gdsc = { - .gdscr = 0x3000, - .en_few_wait_val = 0x6, - .en_rest_wait_val = 0x5, - .pd = { - .name = "mdss_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = HW_CTRL | POLL_CFG_GDSCR, -}; - static struct clk_regmap *disp_cc_sdm845_clocks[] = { [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, [DISP_CC_MDSS_AXI_CLK] = &disp_cc_mdss_axi_clk.clkr, @@ -591,6 +957,19 @@ static struct clk_regmap *disp_cc_sdm845_clocks[] = { [DISP_CC_MDSS_BYTE1_INTF_CLK] = &disp_cc_mdss_byte1_intf_clk.clkr, [DISP_CC_MDSS_BYTE1_DIV_CLK_SRC] = &disp_cc_mdss_byte1_div_clk_src.clkr, + [DISP_CC_MDSS_DP_AUX_CLK] = &disp_cc_mdss_dp_aux_clk.clkr, + [DISP_CC_MDSS_DP_AUX_CLK_SRC] = &disp_cc_mdss_dp_aux_clk_src.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK] = &disp_cc_mdss_dp_crypto_clk.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK_SRC] = + &disp_cc_mdss_dp_crypto_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_CLK] = &disp_cc_mdss_dp_link_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK_SRC] = &disp_cc_mdss_dp_link_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_INTF_CLK] = &disp_cc_mdss_dp_link_intf_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK] = &disp_cc_mdss_dp_pixel1_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK_SRC] = + &disp_cc_mdss_dp_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK] = &disp_cc_mdss_dp_pixel_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK_SRC] = &disp_cc_mdss_dp_pixel_clk_src.clkr, [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr, [DISP_CC_MDSS_ESC1_CLK] = &disp_cc_mdss_esc1_clk.clkr, @@ -602,6 +981,9 @@ static struct clk_regmap *disp_cc_sdm845_clocks[] = { [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr, [DISP_CC_MDSS_PCLK1_CLK] = &disp_cc_mdss_pclk1_clk.clkr, [DISP_CC_MDSS_PCLK1_CLK_SRC] = &disp_cc_mdss_pclk1_clk_src.clkr, + [DISP_CC_MDSS_QDSS_AT_CLK] = &disp_cc_mdss_qdss_at_clk.clkr, + [DISP_CC_MDSS_QDSS_TSCTR_DIV8_CLK] = + &disp_cc_mdss_qdss_tsctr_div8_clk.clkr, [DISP_CC_MDSS_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr, [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr, [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, @@ -615,10 +997,6 @@ static const struct qcom_reset_map disp_cc_sdm845_resets[] = { [DISP_CC_MDSS_RSCC_BCR] = { 0x5000 }, }; -static struct gdsc *disp_cc_sdm845_gdscs[] = { - [MDSS_GDSC] = &mdss_gdsc, -}; - static const struct regmap_config disp_cc_sdm845_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -633,34 +1011,133 @@ static const struct qcom_cc_desc disp_cc_sdm845_desc = { .num_clks = ARRAY_SIZE(disp_cc_sdm845_clocks), .resets = disp_cc_sdm845_resets, .num_resets = ARRAY_SIZE(disp_cc_sdm845_resets), - .gdscs = disp_cc_sdm845_gdscs, - .num_gdscs = ARRAY_SIZE(disp_cc_sdm845_gdscs), }; static const struct of_device_id disp_cc_sdm845_match_table[] = { - { .compatible = "qcom,sdm845-dispcc" }, + { .compatible = "qcom,dispcc-sdm845" }, + { .compatible = "qcom,dispcc-sdm845-v2" }, + { .compatible = "qcom,dispcc-sdm670" }, { } }; MODULE_DEVICE_TABLE(of, disp_cc_sdm845_match_table); +static void disp_cc_sdm845_fixup_sdm845v2(struct regmap *regmap) +{ + clk_fabia_pll_configure(&disp_cc_pll0, regmap, + &disp_cc_pll0_config_v2); + disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 180000000; + disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 275000000; + disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 328580000; + disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 180000000; + disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 275000000; + disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 328580000; + disp_cc_mdss_dp_pixel1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 337500; + disp_cc_mdss_dp_pixel_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 337500; + disp_cc_mdss_mdp_clk_src.freq_tbl = + ftbl_disp_cc_mdss_mdp_clk_src_sdm845_v2; + disp_cc_mdss_mdp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 171428571; + disp_cc_mdss_mdp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 344000000; + disp_cc_mdss_mdp_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 430000000; + disp_cc_mdss_pclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 280000000; + disp_cc_mdss_pclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 430000000; + disp_cc_mdss_pclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 430000000; + disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 280000000; + disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = + 430000000; + disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 430000000; + disp_cc_mdss_rot_clk_src.freq_tbl = + ftbl_disp_cc_mdss_rot_clk_src_sdm845_v2; + disp_cc_mdss_rot_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = + 171428571; + disp_cc_mdss_rot_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 344000000; + disp_cc_mdss_rot_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 430000000; +} + +static void disp_cc_sdm845_fixup_sdm670(struct regmap *regmap) +{ + disp_cc_sdm845_fixup_sdm845v2(regmap); + + disp_cc_mdss_mdp_clk_src.freq_tbl = + ftbl_disp_cc_mdss_mdp_clk_src_sdm670; + disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 358000000; + disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 358000000; +} + +static int disp_cc_sdm845_fixup(struct platform_device *pdev, + struct regmap *regmap) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,dispcc-sdm845-v2")) + disp_cc_sdm845_fixup_sdm845v2(regmap); + else if (!strcmp(compat, "qcom,dispcc-sdm670")) + disp_cc_sdm845_fixup_sdm670(regmap); + else + clk_fabia_pll_configure(&disp_cc_pll0, regmap, + &disp_cc_pll0_config); + + return 0; +} + static int disp_cc_sdm845_probe(struct platform_device *pdev) { struct regmap *regmap; - struct alpha_pll_config disp_cc_pll0_config = {}; + int ret = 0; regmap = qcom_cc_map(pdev, &disp_cc_sdm845_desc); - if (IS_ERR(regmap)) + if (IS_ERR(regmap)) { + pr_err("Failed to map the Display CC registers\n"); return PTR_ERR(regmap); + } + + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } - disp_cc_pll0_config.l = 0x2c; - disp_cc_pll0_config.alpha = 0xcaaa; + ret = disp_cc_sdm845_fixup(pdev, regmap); + if (ret) + return ret; - clk_fabia_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); + /* Enable clock gating for DSI and MDP clocks */ + regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10); - /* Enable hardware clock gating for DSI and MDP clocks */ - regmap_update_bits(regmap, 0x8000, 0x7f0, 0x7f0); + ret = qcom_cc_really_probe(pdev, &disp_cc_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register Display CC clocks\n"); + return ret; + } - return qcom_cc_really_probe(pdev, &disp_cc_sdm845_desc, regmap); + dev_info(&pdev->dev, "Registered Display CC clocks\n"); + return ret; } static struct platform_driver disp_cc_sdm845_driver = { @@ -683,5 +1160,6 @@ static void __exit disp_cc_sdm845_exit(void) } module_exit(disp_cc_sdm845_exit); +MODULE_DESCRIPTION("QTI DISP_CC SDM845 Driver"); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("QTI DISPCC SDM845 Driver"); +MODULE_ALIAS("platform:disp_cc-sdm845"); diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index 6bd96ddadbe3..aeca2812675b 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,9 +24,18 @@ #include "clk-pll.h" #include "clk-rcg.h" #include "clk-branch.h" -#include "clk-alpha-pll.h" -#include "gdsc.h" #include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-sdm845.h" +#include "clk-voter.h" + +#define GCC_MMSS_MISC 0x09FFC +#define GCC_GPU_MISC 0x71028 + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_cx_ao, VDD_CX_NUM, 1, vdd_corner); enum { P_BI_TCXO, @@ -31,7 +43,9 @@ enum { P_CORE_BI_PLL_TEST_SE, P_GPLL0_OUT_EVEN, P_GPLL0_OUT_MAIN, + P_GPLL1_OUT_MAIN, P_GPLL4_OUT_MAIN, + P_GPLL6_OUT_MAIN, P_SLEEP_CLK, }; @@ -131,7 +145,7 @@ static const char * const gcc_parent_names_6[] = { "core_bi_pll_test_se", }; -static const char * const gcc_parent_names_7_ao[] = { +static const char * const gcc_parent_names_7[] = { "bi_tcxo_ao", "gpll0", "gpll0_out_even", @@ -139,14 +153,22 @@ static const char * const gcc_parent_names_7_ao[] = { }; static const char * const gcc_parent_names_8[] = { - "bi_tcxo", + "bi_tcxo_ao", "gpll0", "core_bi_pll_test_se", }; -static const char * const gcc_parent_names_8_ao[] = { - "bi_tcxo_ao", +static const struct parent_map gcc_parent_map_9[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL1_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_9[] = { + "bi_tcxo", "gpll0", + "gpll1", "core_bi_pll_test_se", }; @@ -166,8 +188,63 @@ static const char * const gcc_parent_names_10[] = { "core_bi_pll_test_se", }; +static const struct parent_map gcc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL6_OUT_MAIN, 2 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_11[] = { + "bi_tcxo", + "gpll0", + "gpll6", + "gpll0_out_even", + "core_bi_pll_test_se", +}; + +static struct clk_dummy measure_only_snoc_clk = { + .rrate = 1000, + .hw.init = &(struct clk_init_data){ + .name = "measure_only_snoc_clk", + .ops = &clk_dummy_ops, + }, +}; + +static struct clk_dummy measure_only_cnoc_clk = { + .rrate = 1000, + .hw.init = &(struct clk_init_data){ + .name = "measure_only_cnoc_clk", + .ops = &clk_dummy_ops, + }, +}; + +static struct clk_dummy measure_only_bimc_clk = { + .rrate = 1000, + .hw.init = &(struct clk_init_data){ + .name = "measure_only_bimc_clk", + .ops = &clk_dummy_ops, + }, +}; + +static struct clk_dummy measure_only_ipa_2x_clk = { + .rrate = 1000, + .hw.init = &(struct clk_init_data){ + .name = "measure_only_ipa_2x_clk", + .ops = &clk_dummy_ops, + }, +}; + +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, + { 125000000, 1000000000, 1 }, +}; + static struct clk_alpha_pll gpll0 = { .offset = 0x0, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], .clkr = { .enable_reg = 0x52000, @@ -177,12 +254,19 @@ static struct clk_alpha_pll gpll0 = { .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_alpha_pll_fixed_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), }, }, }; static struct clk_alpha_pll gpll4 = { .offset = 0x76000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], .clkr = { .enable_reg = 0x52000, @@ -192,6 +276,11 @@ static struct clk_alpha_pll gpll4 = { .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_alpha_pll_fixed_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), }, }, }; @@ -219,6 +308,28 @@ static struct clk_alpha_pll_postdiv gpll0_out_even = { }, }; +static struct clk_alpha_pll gpll6 = { + .offset = 0x13000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "gpll6", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = { F(19200000, P_BI_TCXO, 1, 0, 0), { } @@ -232,9 +343,14 @@ static struct clk_rcg2 gcc_cpuss_ahb_clk_src = { .freq_tbl = ftbl_gcc_cpuss_ahb_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_cpuss_ahb_clk_src", - .parent_names = gcc_parent_names_7_ao, + .parent_names = gcc_parent_names_7, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3_AO( + MIN, 19200000, + LOW, 50000000, + NOMINAL, 100000000), }, }; @@ -243,6 +359,12 @@ static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src[] = { { } }; +static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src_sdm670[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0), + { } +}; + static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = { .cmd_rcgr = 0x4815c, .mnd_width = 0, @@ -251,9 +373,12 @@ static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = { .freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_cpuss_rbcpr_clk_src", - .parent_names = gcc_parent_names_8_ao, + .parent_names = gcc_parent_names_8, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1_AO( + MIN, 19200000), }, }; @@ -276,7 +401,13 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .name = "gcc_gp1_clk_src", .parent_names = gcc_parent_names_1, .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 50000000, + LOW, 100000000, + NOMINAL, 200000000), }, }; @@ -290,7 +421,13 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .name = "gcc_gp2_clk_src", .parent_names = gcc_parent_names_1, .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 50000000, + LOW, 100000000, + NOMINAL, 200000000), }, }; @@ -304,7 +441,13 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .name = "gcc_gp3_clk_src", .parent_names = gcc_parent_names_1, .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 50000000, + LOW, 100000000, + NOMINAL, 200000000), }, }; @@ -324,7 +467,11 @@ static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { .name = "gcc_pcie_0_aux_clk_src", .parent_names = gcc_parent_names_2, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 9600000, + LOW, 19200000), }, }; @@ -338,7 +485,11 @@ static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { .name = "gcc_pcie_1_aux_clk_src", .parent_names = gcc_parent_names_2, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 9600000, + LOW, 19200000), }, }; @@ -358,7 +509,11 @@ static struct clk_rcg2 gcc_pcie_phy_refgen_clk_src = { .name = "gcc_pcie_phy_refgen_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 19200000, + LOW, 100000000), }, }; @@ -379,11 +534,31 @@ static struct clk_rcg2 gcc_pdm2_clk_src = { .name = "gcc_pdm2_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 9600000, + LOWER, 19200000, + LOW, 60000000), }, }; static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = { + F(7372800, P_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GPLL0_OUT_EVEN, 1, 8, 75), + F(38400000, P_GPLL0_OUT_EVEN, 1, 16, 125), + F(48000000, P_GPLL0_OUT_EVEN, 1, 4, 25), + F(64000000, P_GPLL0_OUT_EVEN, 1, 16, 75), + F(80000000, P_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2[] = { F(7372800, P_GPLL0_OUT_EVEN, 1, 384, 15625), F(14745600, P_GPLL0_OUT_EVEN, 1, 768, 15625), F(19200000, P_BI_TCXO, 1, 0, 0), @@ -402,18 +577,38 @@ static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = { { } }; +static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { + .name = "gcc_qupv3_wrap0_s0_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), +}; + static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { .cmd_rcgr = 0x17034, .mnd_width = 16, .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s0_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { + .name = "gcc_qupv3_wrap0_s1_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { @@ -422,12 +617,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s1_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { + .name = "gcc_qupv3_wrap0_s2_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { @@ -436,12 +639,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s2_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { + .name = "gcc_qupv3_wrap0_s3_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { @@ -450,12 +661,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s3_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { + .name = "gcc_qupv3_wrap0_s4_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { @@ -464,12 +683,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s4_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { + .name = "gcc_qupv3_wrap0_s5_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { @@ -478,12 +705,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s5_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s6_clk_src_init = { + .name = "gcc_qupv3_wrap0_s6_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s6_clk_src = { @@ -492,12 +727,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s6_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s6_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s7_clk_src_init = { + .name = "gcc_qupv3_wrap0_s7_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap0_s7_clk_src = { @@ -506,12 +749,20 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s7_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap0_s7_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap0_s7_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { + .name = "gcc_qupv3_wrap1_s0_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { @@ -520,12 +771,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s0_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { + .name = "gcc_qupv3_wrap1_s1_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { @@ -534,12 +793,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s1_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s2_clk_src_init = { + .name = "gcc_qupv3_wrap1_s2_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s2_clk_src = { @@ -548,12 +815,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s2_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s2_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { + .name = "gcc_qupv3_wrap1_s3_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { @@ -562,12 +837,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s3_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { + .name = "gcc_qupv3_wrap1_s4_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { @@ -576,12 +859,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s4_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { + .name = "gcc_qupv3_wrap1_s5_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { @@ -590,12 +881,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s5_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s6_clk_src_init = { + .name = "gcc_qupv3_wrap1_s6_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { @@ -604,12 +903,20 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s6_clk_src", - .parent_names = gcc_parent_names_0, - .num_parents = 4, - .ops = &clk_rcg2_shared_ops, - }, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s7_clk_src_init = { + .name = "gcc_qupv3_wrap1_s7_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 75000000, + LOW, 100000000), }; static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { @@ -618,11 +925,68 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0), + F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x26010, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_qupv3_wrap1_s7_clk_src", + .name = "gcc_sdcc1_ice_core_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 75000000, + LOW, 150000000, + NOMINAL, 300000000), + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_BI_TCXO, 16, 3, 25), + F(400000, P_BI_TCXO, 12, 1, 4), + F(20000000, P_GPLL0_OUT_EVEN, 5, 1, 3), + F(25000000, P_GPLL0_OUT_EVEN, 6, 1, 2), + F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0), + F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x26028, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .enable_safe_config = true, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk_src", + .parent_names = gcc_parent_names_11, + .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 19200000, + LOWER, 50000000, + LOW, 100000000, + NOMINAL, 384000000), }, }; @@ -643,11 +1007,18 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_10, .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_sdcc2_apps_clk_src", .parent_names = gcc_parent_names_10, .num_parents = 5, - .ops = &clk_rcg2_floor_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 9600000, + LOWER, 19200000, + LOW, 100000000, + LOW_L1, 201500000), }, }; @@ -661,17 +1032,35 @@ static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src[] = { { } }; +static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src_sdm670[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), + F(33333333, P_GPLL0_OUT_EVEN, 9, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + static struct clk_rcg2 gcc_sdcc4_apps_clk_src = { .cmd_rcgr = 0x1600c, .mnd_width = 8, .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_sdcc4_apps_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_sdcc4_apps_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_floor_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 9600000, + LOWER, 19200000, + LOW, 50000000, + NOMINAL, 100000000), }, }; @@ -690,11 +1079,22 @@ static struct clk_rcg2 gcc_tsif_ref_clk_src = { .name = "gcc_tsif_ref_clk_src", .parent_names = gcc_parent_names_6, .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 105495), }, }; static const struct freq_tbl ftbl_gcc_ufs_card_axi_clk_src[] = { + F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), + F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2[] = { F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), @@ -709,11 +1109,17 @@ static struct clk_rcg2 gcc_ufs_card_axi_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_card_axi_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_axi_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 50000000, + LOW, 100000000, + NOMINAL, 200000000), }, }; @@ -731,11 +1137,17 @@ static struct clk_rcg2 gcc_ufs_card_ice_core_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_card_ice_core_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_ice_core_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 75000000, + LOW, 150000000, + NOMINAL, 300000000), }, }; @@ -745,11 +1157,15 @@ static struct clk_rcg2 gcc_ufs_card_phy_aux_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_phy_aux_clk_src", .parent_names = gcc_parent_names_4, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -766,11 +1182,17 @@ static struct clk_rcg2 gcc_ufs_card_unipro_core_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_card_unipro_core_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_unipro_core_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 37500000, + LOW, 75000000, + NOMINAL, 150000000), }, }; @@ -789,11 +1211,18 @@ static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_axi_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP4( + MIN, 50000000, + LOW, 100000000, + NOMINAL, 200000000, + HIGH, 240000000), }, }; @@ -803,11 +1232,17 @@ static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_card_ice_core_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_ice_core_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 75000000, + LOW, 150000000, + NOMINAL, 300000000), }, }; @@ -817,11 +1252,15 @@ static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_4, .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_phy_aux_clk_src", .parent_names = gcc_parent_names_4, .num_parents = 2, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -831,11 +1270,17 @@ static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_ufs_card_unipro_core_clk_src, + .flags = FORCE_ENABLE_RCG, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_unipro_core_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 37500000, + LOW, 75000000, + NOMINAL, 150000000), }, }; @@ -854,11 +1299,19 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_prim_master_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 33333333, + LOWER, 66666667, + LOW, 133333333, + NOMINAL, 200000000, + HIGH, 240000000), }, }; @@ -876,11 +1329,17 @@ static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_usb30_prim_mock_utmi_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_prim_mock_utmi_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 40000000, + LOW, 60000000), }, }; @@ -894,7 +1353,14 @@ static struct clk_rcg2 gcc_usb30_sec_master_clk_src = { .name = "gcc_usb30_sec_master_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP5( + MIN, 33333333, + LOWER, 66666667, + LOW, 133333333, + NOMINAL, 200000000, + HIGH, 240000000), }, }; @@ -908,7 +1374,12 @@ static struct clk_rcg2 gcc_usb30_sec_mock_utmi_clk_src = { .name = "gcc_usb30_sec_mock_utmi_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOWER, 40000000, + LOW, 60000000), }, }; @@ -922,7 +1393,10 @@ static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { .name = "gcc_usb3_prim_phy_aux_clk_src", .parent_names = gcc_parent_names_2, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -932,11 +1406,15 @@ static struct clk_rcg2 gcc_usb3_sec_phy_aux_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_2, .freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb3_sec_phy_aux_clk_src", .parent_names = gcc_parent_names_2, .num_parents = 3, - .ops = &clk_rcg2_shared_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -950,7 +1428,10 @@ static struct clk_rcg2 gcc_vs_ctrl_clk_src = { .name = "gcc_vs_ctrl_clk_src", .parent_names = gcc_parent_names_3, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP1( + MIN, 19200000), }, }; @@ -965,13 +1446,18 @@ static struct clk_rcg2 gcc_vsensor_clk_src = { .cmd_rcgr = 0x7a018, .mnd_width = 0, .hid_width = 5, - .parent_map = gcc_parent_map_3, + .parent_map = gcc_parent_map_9, .freq_tbl = ftbl_gcc_vsensor_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_vsensor_clk_src", - .parent_names = gcc_parent_names_8, - .num_parents = 3, + .parent_names = gcc_parent_names_9, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP3( + MIN, 19200000, + LOW, 300000000, + LOW_L1, 600000000), }, }; @@ -1008,6 +1494,23 @@ static struct clk_branch gcc_aggre_ufs_card_axi_clk = { }, }; +static struct clk_branch gcc_aggre_ufs_card_axi_hw_ctl_clk = { + .halt_reg = 0x82028, + .clkr = { + .enable_reg = 0x82028, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_aggre_ufs_card_axi_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_aggre_ufs_card_axi_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { .halt_reg = 0x82024, .halt_check = BRANCH_HALT, @@ -1028,6 +1531,28 @@ static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { }, }; +static DEFINE_CLK_VOTER(ufs_phy_axi_emmc_vote_clk, + gcc_aggre_ufs_phy_axi_clk, 0); +static DEFINE_CLK_VOTER(ufs_phy_axi_ufs_vote_clk, + gcc_aggre_ufs_phy_axi_clk, 0); + +static struct clk_branch gcc_aggre_ufs_phy_axi_hw_ctl_clk = { + .halt_reg = 0x82024, + .clkr = { + .enable_reg = 0x82024, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_aggre_ufs_phy_axi_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_aggre_ufs_phy_axi_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_aggre_usb3_prim_axi_clk = { .halt_reg = 0x8201c, .halt_check = BRANCH_HALT, @@ -1224,12 +1749,42 @@ static struct clk_branch gcc_cpuss_ahb_clk = { .enable_reg = 0x52004, .enable_mask = BIT(21), .hw.init = &(struct clk_init_data){ - .name = "gcc_cpuss_ahb_clk", - .parent_names = (const char *[]){ - "gcc_cpuss_ahb_clk_src", - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + .name = "gcc_cpuss_ahb_clk", + .parent_names = (const char *[]){ + "gcc_cpuss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cpuss_dvm_bus_clk = { + .halt_reg = 0x48190, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x48190, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_dvm_bus_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cpuss_gnoc_clk = { + .halt_reg = 0x48004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x48004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52004, + .enable_mask = BIT(22), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_gnoc_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -1295,8 +1850,8 @@ static struct clk_branch gcc_disp_axi_clk = { }, }; -static struct clk_branch gcc_disp_gpll0_clk_src = { - .halt_check = BRANCH_HALT_DELAY, +static struct clk_gate2 gcc_disp_gpll0_clk_src = { + .udelay = 500, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(18), @@ -1306,13 +1861,14 @@ static struct clk_branch gcc_disp_gpll0_clk_src = { "gpll0", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_disp_gpll0_div_clk_src = { - .halt_check = BRANCH_HALT_DELAY, +static struct clk_gate2 gcc_disp_gpll0_div_clk_src = { + .udelay = 500, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(19), @@ -1322,7 +1878,8 @@ static struct clk_branch gcc_disp_gpll0_div_clk_src = { "gpll0_out_even", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_gate2_ops, }, }, }; @@ -1411,8 +1968,8 @@ static struct clk_branch gcc_gpu_cfg_ahb_clk = { }, }; -static struct clk_branch gcc_gpu_gpll0_clk_src = { - .halt_check = BRANCH_HALT_DELAY, +static struct clk_gate2 gcc_gpu_gpll0_clk_src = { + .udelay = 500, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(15), @@ -1422,13 +1979,14 @@ static struct clk_branch gcc_gpu_gpll0_clk_src = { "gpll0", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_gpu_gpll0_div_clk_src = { - .halt_check = BRANCH_HALT_DELAY, +static struct clk_gate2 gcc_gpu_gpll0_div_clk_src = { + .udelay = 500, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(16), @@ -1438,7 +1996,8 @@ static struct clk_branch gcc_gpu_gpll0_div_clk_src = { "gpll0_out_even", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_gate2_ops, }, }, }; @@ -1528,14 +2087,14 @@ static struct clk_branch gcc_mss_cfg_ahb_clk = { }, }; -static struct clk_branch gcc_mss_gpll0_div_clk_src = { - .halt_check = BRANCH_HALT_DELAY, +static struct clk_gate2 gcc_mss_gpll0_div_clk_src = { + .udelay = 500, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(17), .hw.init = &(struct clk_init_data){ .name = "gcc_mss_gpll0_div_clk_src", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; @@ -1659,7 +2218,8 @@ static struct clk_branch gcc_pcie_0_mstr_axi_clk = { }; static struct clk_branch gcc_pcie_0_pipe_clk = { - .halt_check = BRANCH_HALT_SKIP, + .halt_reg = 0x6b020, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x5200c, .enable_mask = BIT(4), @@ -1758,7 +2318,8 @@ static struct clk_branch gcc_pcie_1_mstr_axi_clk = { }; static struct clk_branch gcc_pcie_1_pipe_clk = { - .halt_check = BRANCH_HALT_SKIP, + .halt_reg = 0x8d020, + .halt_check = BRANCH_VOTED, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(30), @@ -2283,6 +2844,55 @@ static struct clk_branch gcc_qupv3_wrap_1_s_ahb_clk = { }, }; +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x2600c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2600c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk", + .parent_names = (const char *[]){ + "gcc_sdcc1_ice_core_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x26008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x26008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x26004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x26004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", + .parent_names = (const char *[]){ + "gcc_sdcc1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_sdcc2_ahb_clk = { .halt_reg = 0x14008, .halt_check = BRANCH_HALT, @@ -2442,6 +3052,23 @@ static struct clk_branch gcc_ufs_card_axi_clk = { }, }; +static struct clk_branch gcc_ufs_card_axi_hw_ctl_clk = { + .halt_reg = 0x7500c, + .clkr = { + .enable_reg = 0x7500c, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_card_axi_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_card_axi_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_card_clkref_clk = { .halt_reg = 0x8c004, .halt_check = BRANCH_HALT, @@ -2475,6 +3102,23 @@ static struct clk_branch gcc_ufs_card_ice_core_clk = { }, }; +static struct clk_branch gcc_ufs_card_ice_core_hw_ctl_clk = { + .halt_reg = 0x75058, + .clkr = { + .enable_reg = 0x75058, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_card_ice_core_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_card_ice_core_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_card_phy_aux_clk = { .halt_reg = 0x7508c, .halt_check = BRANCH_HALT, @@ -2495,38 +3139,55 @@ static struct clk_branch gcc_ufs_card_phy_aux_clk = { }, }; -static struct clk_branch gcc_ufs_card_rx_symbol_0_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_branch gcc_ufs_card_phy_aux_hw_ctl_clk = { + .halt_reg = 0x7508c, + .clkr = { + .enable_reg = 0x7508c, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_card_phy_aux_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_card_phy_aux_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + +static struct clk_gate2 gcc_ufs_card_rx_symbol_0_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x75018, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_rx_symbol_0_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_ufs_card_rx_symbol_1_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_ufs_card_rx_symbol_1_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x750a8, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_rx_symbol_1_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_ufs_card_tx_symbol_0_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_ufs_card_tx_symbol_0_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x75014, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_tx_symbol_0_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; @@ -2551,6 +3212,23 @@ static struct clk_branch gcc_ufs_card_unipro_core_clk = { }, }; +static struct clk_branch gcc_ufs_card_unipro_core_hw_ctl_clk = { + .halt_reg = 0x75054, + .clkr = { + .enable_reg = 0x75054, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_card_unipro_core_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_card_unipro_core_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_mem_clkref_clk = { .halt_reg = 0x8c000, .halt_check = BRANCH_HALT, @@ -2599,6 +3277,23 @@ static struct clk_branch gcc_ufs_phy_axi_clk = { }, }; +static struct clk_branch gcc_ufs_phy_axi_hw_ctl_clk = { + .halt_reg = 0x7700c, + .clkr = { + .enable_reg = 0x7700c, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_axi_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_phy_axi_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_phy_ice_core_clk = { .halt_reg = 0x77058, .halt_check = BRANCH_HALT, @@ -2619,6 +3314,23 @@ static struct clk_branch gcc_ufs_phy_ice_core_clk = { }, }; +static struct clk_branch gcc_ufs_phy_ice_core_hw_ctl_clk = { + .halt_reg = 0x77058, + .clkr = { + .enable_reg = 0x77058, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_ice_core_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_phy_ice_core_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_phy_phy_aux_clk = { .halt_reg = 0x7708c, .halt_check = BRANCH_HALT, @@ -2639,38 +3351,55 @@ static struct clk_branch gcc_ufs_phy_phy_aux_clk = { }, }; -static struct clk_branch gcc_ufs_phy_rx_symbol_0_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_branch gcc_ufs_phy_phy_aux_hw_ctl_clk = { + .halt_reg = 0x7708c, + .clkr = { + .enable_reg = 0x7708c, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_phy_aux_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_phy_phy_aux_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + +static struct clk_gate2 gcc_ufs_phy_rx_symbol_0_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x77018, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_rx_symbol_0_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_ufs_phy_rx_symbol_1_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_ufs_phy_rx_symbol_1_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x770a8, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_rx_symbol_1_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; -static struct clk_branch gcc_ufs_phy_tx_symbol_0_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_ufs_phy_tx_symbol_0_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x77014, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_tx_symbol_0_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; @@ -2695,6 +3424,23 @@ static struct clk_branch gcc_ufs_phy_unipro_core_clk = { }, }; +static struct clk_branch gcc_ufs_phy_unipro_core_hw_ctl_clk = { + .halt_reg = 0x77054, + .clkr = { + .enable_reg = 0x77054, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_unipro_core_hw_ctl_clk", + .parent_names = (const char *[]){ + "gcc_ufs_phy_unipro_core_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_hw_ctl_ops, + }, + }, +}; + static struct clk_branch gcc_usb30_prim_master_clk = { .halt_reg = 0xf00c, .halt_check = BRANCH_HALT, @@ -2842,14 +3588,14 @@ static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { }, }; -static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_usb3_prim_phy_pipe_clk = { + .udelay = 500, .clkr = { .enable_reg = 0xf054, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_usb3_prim_phy_pipe_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; @@ -2903,14 +3649,14 @@ static struct clk_branch gcc_usb3_sec_phy_com_aux_clk = { }, }; -static struct clk_branch gcc_usb3_sec_phy_pipe_clk = { - .halt_check = BRANCH_HALT_SKIP, +static struct clk_gate2 gcc_usb3_sec_phy_pipe_clk = { + .udelay = 500, .clkr = { .enable_reg = 0x10054, .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_usb3_sec_phy_pipe_clk", - .ops = &clk_branch2_ops, + .ops = &clk_gate2_ops, }, }, }; @@ -3060,157 +3806,23 @@ static struct clk_branch gcc_vs_ctrl_clk = { }, }; -static struct clk_branch gcc_cpuss_dvm_bus_clk = { - .halt_reg = 0x48190, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0x48190, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_cpuss_dvm_bus_clk", - .flags = CLK_IS_CRITICAL, - .ops = &clk_branch2_ops, - }, - }, -}; - -static struct clk_branch gcc_cpuss_gnoc_clk = { - .halt_reg = 0x48004, - .halt_check = BRANCH_HALT_VOTED, - .hwcg_reg = 0x48004, - .hwcg_bit = 1, - .clkr = { - .enable_reg = 0x52004, - .enable_mask = BIT(22), - .hw.init = &(struct clk_init_data){ - .name = "gcc_cpuss_gnoc_clk", - .flags = CLK_IS_CRITICAL, - .ops = &clk_branch2_ops, - }, - }, -}; - -static struct gdsc pcie_0_gdsc = { - .gdscr = 0x6b004, - .pd = { - .name = "pcie_0_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc pcie_1_gdsc = { - .gdscr = 0x8d004, - .pd = { - .name = "pcie_1_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc ufs_card_gdsc = { - .gdscr = 0x75004, - .pd = { - .name = "ufs_card_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc ufs_phy_gdsc = { - .gdscr = 0x77004, - .pd = { - .name = "ufs_phy_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc usb30_prim_gdsc = { - .gdscr = 0xf004, - .pd = { - .name = "usb30_prim_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc usb30_sec_gdsc = { - .gdscr = 0x10004, - .pd = { - .name = "usb30_sec_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc = { - .gdscr = 0x7d030, - .pd = { - .name = "hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc = { - .gdscr = 0x7d03c, - .pd = { - .name = "hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_aggre_noc_mmu_tbu1_gdsc = { - .gdscr = 0x7d034, - .pd = { - .name = "hlos1_vote_aggre_noc_mmu_tbu1_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_aggre_noc_mmu_tbu2_gdsc = { - .gdscr = 0x7d038, - .pd = { - .name = "hlos1_vote_aggre_noc_mmu_tbu2_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { - .gdscr = 0x7d040, - .pd = { - .name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc = { - .gdscr = 0x7d048, - .pd = { - .name = "hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, -}; - -static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { - .gdscr = 0x7d044, - .pd = { - .name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, - .flags = VOTABLE, +struct clk_hw *gcc_sdm845_hws[] = { + [MEASURE_ONLY_SNOC_CLK] = &measure_only_snoc_clk.hw, + [MEASURE_ONLY_CNOC_CLK] = &measure_only_cnoc_clk.hw, + [MEASURE_ONLY_BIMC_CLK] = &measure_only_bimc_clk.hw, + [MEASURE_ONLY_IPA_2X_CLK] = &measure_only_ipa_2x_clk.hw, + [UFS_PHY_AXI_EMMC_VOTE_CLK] = &ufs_phy_axi_emmc_vote_clk.hw, + [UFS_PHY_AXI_UFS_VOTE_CLK] = &ufs_phy_axi_ufs_vote_clk.hw, }; static struct clk_regmap *gcc_sdm845_clocks[] = { [GCC_AGGRE_NOC_PCIE_TBU_CLK] = &gcc_aggre_noc_pcie_tbu_clk.clkr, [GCC_AGGRE_UFS_CARD_AXI_CLK] = &gcc_aggre_ufs_card_axi_clk.clkr, + [GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK] = + &gcc_aggre_ufs_card_axi_hw_ctl_clk.clkr, [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr, + [GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK] = + &gcc_aggre_ufs_phy_axi_hw_ctl_clk.clkr, [GCC_AGGRE_USB3_PRIM_AXI_CLK] = &gcc_aggre_usb3_prim_axi_clk.clkr, [GCC_AGGRE_USB3_SEC_AXI_CLK] = &gcc_aggre_usb3_sec_axi_clk.clkr, [GCC_APC_VS_CLK] = &gcc_apc_vs_clk.clkr, @@ -3225,6 +3837,8 @@ static struct clk_regmap *gcc_sdm845_clocks[] = { [GCC_CFG_NOC_USB3_SEC_AXI_CLK] = &gcc_cfg_noc_usb3_sec_axi_clk.clkr, [GCC_CPUSS_AHB_CLK] = &gcc_cpuss_ahb_clk.clkr, [GCC_CPUSS_AHB_CLK_SRC] = &gcc_cpuss_ahb_clk_src.clkr, + [GCC_CPUSS_DVM_BUS_CLK] = &gcc_cpuss_dvm_bus_clk.clkr, + [GCC_CPUSS_GNOC_CLK] = &gcc_cpuss_gnoc_clk.clkr, [GCC_CPUSS_RBCPR_CLK] = &gcc_cpuss_rbcpr_clk.clkr, [GCC_CPUSS_RBCPR_CLK_SRC] = &gcc_cpuss_rbcpr_clk_src.clkr, [GCC_DDRSS_GPU_AXI_CLK] = &gcc_ddrss_gpu_axi_clk.clkr, @@ -3330,30 +3944,43 @@ static struct clk_regmap *gcc_sdm845_clocks[] = { [GCC_TSIF_REF_CLK_SRC] = &gcc_tsif_ref_clk_src.clkr, [GCC_UFS_CARD_AHB_CLK] = &gcc_ufs_card_ahb_clk.clkr, [GCC_UFS_CARD_AXI_CLK] = &gcc_ufs_card_axi_clk.clkr, + [GCC_UFS_CARD_AXI_HW_CTL_CLK] = &gcc_ufs_card_axi_hw_ctl_clk.clkr, [GCC_UFS_CARD_AXI_CLK_SRC] = &gcc_ufs_card_axi_clk_src.clkr, [GCC_UFS_CARD_CLKREF_CLK] = &gcc_ufs_card_clkref_clk.clkr, [GCC_UFS_CARD_ICE_CORE_CLK] = &gcc_ufs_card_ice_core_clk.clkr, + [GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK] = + &gcc_ufs_card_ice_core_hw_ctl_clk.clkr, [GCC_UFS_CARD_ICE_CORE_CLK_SRC] = &gcc_ufs_card_ice_core_clk_src.clkr, [GCC_UFS_CARD_PHY_AUX_CLK] = &gcc_ufs_card_phy_aux_clk.clkr, + [GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK] = + &gcc_ufs_card_phy_aux_hw_ctl_clk.clkr, [GCC_UFS_CARD_PHY_AUX_CLK_SRC] = &gcc_ufs_card_phy_aux_clk_src.clkr, [GCC_UFS_CARD_RX_SYMBOL_0_CLK] = &gcc_ufs_card_rx_symbol_0_clk.clkr, [GCC_UFS_CARD_RX_SYMBOL_1_CLK] = &gcc_ufs_card_rx_symbol_1_clk.clkr, [GCC_UFS_CARD_TX_SYMBOL_0_CLK] = &gcc_ufs_card_tx_symbol_0_clk.clkr, [GCC_UFS_CARD_UNIPRO_CORE_CLK] = &gcc_ufs_card_unipro_core_clk.clkr, + [GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK] = + &gcc_ufs_card_unipro_core_hw_ctl_clk.clkr, [GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC] = &gcc_ufs_card_unipro_core_clk_src.clkr, [GCC_UFS_MEM_CLKREF_CLK] = &gcc_ufs_mem_clkref_clk.clkr, [GCC_UFS_PHY_AHB_CLK] = &gcc_ufs_phy_ahb_clk.clkr, [GCC_UFS_PHY_AXI_CLK] = &gcc_ufs_phy_axi_clk.clkr, + [GCC_UFS_PHY_AXI_HW_CTL_CLK] = &gcc_ufs_phy_axi_hw_ctl_clk.clkr, [GCC_UFS_PHY_AXI_CLK_SRC] = &gcc_ufs_phy_axi_clk_src.clkr, [GCC_UFS_PHY_ICE_CORE_CLK] = &gcc_ufs_phy_ice_core_clk.clkr, + [GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK] = + &gcc_ufs_phy_ice_core_hw_ctl_clk.clkr, [GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &gcc_ufs_phy_ice_core_clk_src.clkr, [GCC_UFS_PHY_PHY_AUX_CLK] = &gcc_ufs_phy_phy_aux_clk.clkr, + [GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK] = &gcc_ufs_phy_phy_aux_hw_ctl_clk.clkr, [GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &gcc_ufs_phy_phy_aux_clk_src.clkr, [GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &gcc_ufs_phy_rx_symbol_0_clk.clkr, [GCC_UFS_PHY_RX_SYMBOL_1_CLK] = &gcc_ufs_phy_rx_symbol_1_clk.clkr, [GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &gcc_ufs_phy_tx_symbol_0_clk.clkr, [GCC_UFS_PHY_UNIPRO_CORE_CLK] = &gcc_ufs_phy_unipro_core_clk.clkr, + [GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK] = + &gcc_ufs_phy_unipro_core_hw_ctl_clk.clkr, [GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC] = &gcc_ufs_phy_unipro_core_clk_src.clkr, [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, @@ -3392,8 +4019,12 @@ static struct clk_regmap *gcc_sdm845_clocks[] = { [GPLL0] = &gpll0.clkr, [GPLL0_OUT_EVEN] = &gpll0_out_even.clkr, [GPLL4] = &gpll4.clkr, - [GCC_CPUSS_DVM_BUS_CLK] = &gcc_cpuss_dvm_bus_clk.clkr, - [GCC_CPUSS_GNOC_CLK] = &gcc_cpuss_gnoc_clk.clkr, + [GCC_SDCC1_AHB_CLK] = NULL, + [GCC_SDCC1_APPS_CLK] = NULL, + [GCC_SDCC1_ICE_CORE_CLK] = NULL, + [GCC_SDCC1_APPS_CLK_SRC] = NULL, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = NULL, + [GPLL6] = NULL, }; static const struct qcom_reset_map gcc_sdm845_resets[] = { @@ -3423,28 +4054,27 @@ static const struct qcom_reset_map gcc_sdm845_resets[] = { [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 }, [GCC_PCIE_0_PHY_BCR] = { 0x6c01c }, [GCC_PCIE_1_PHY_BCR] = { 0x8e01c }, -}; - -static struct gdsc *gcc_sdm845_gdscs[] = { - [PCIE_0_GDSC] = &pcie_0_gdsc, - [PCIE_1_GDSC] = &pcie_1_gdsc, - [UFS_CARD_GDSC] = &ufs_card_gdsc, - [UFS_PHY_GDSC] = &ufs_phy_gdsc, - [USB30_PRIM_GDSC] = &usb30_prim_gdsc, - [USB30_SEC_GDSC] = &usb30_sec_gdsc, - [HLOS1_VOTE_AGGRE_NOC_MMU_AUDIO_TBU_GDSC] = - &hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc, - [HLOS1_VOTE_AGGRE_NOC_MMU_PCIE_TBU_GDSC] = - &hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc, - [HLOS1_VOTE_AGGRE_NOC_MMU_TBU1_GDSC] = - &hlos1_vote_aggre_noc_mmu_tbu1_gdsc, - [HLOS1_VOTE_AGGRE_NOC_MMU_TBU2_GDSC] = - &hlos1_vote_aggre_noc_mmu_tbu2_gdsc, - [HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC] = - &hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc, - [HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC] = - &hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc, - [HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_sf_gdsc, + [GCC_SDCC1_BCR] = { 0x26000 }, +}; + +/* List of RCG clocks and corresponding flags requested for DFS Mode */ +static struct clk_rcg_dfs_data gcc_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk_src), }; static const struct regmap_config gcc_sdm845_regmap_config = { @@ -3459,31 +4089,278 @@ static const struct qcom_cc_desc gcc_sdm845_desc = { .config = &gcc_sdm845_regmap_config, .clks = gcc_sdm845_clocks, .num_clks = ARRAY_SIZE(gcc_sdm845_clocks), + .hwclks = gcc_sdm845_hws, + .num_hwclks = ARRAY_SIZE(gcc_sdm845_hws), .resets = gcc_sdm845_resets, .num_resets = ARRAY_SIZE(gcc_sdm845_resets), - .gdscs = gcc_sdm845_gdscs, - .num_gdscs = ARRAY_SIZE(gcc_sdm845_gdscs), }; static const struct of_device_id gcc_sdm845_match_table[] = { { .compatible = "qcom,gcc-sdm845" }, + { .compatible = "qcom,gcc-sdm845-v2" }, + { .compatible = "qcom,gcc-sdm845-v2.1" }, + { .compatible = "qcom,gcc-sdm670" }, { } }; MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table); +static void gcc_sdm845_fixup_sdm845v2(void) +{ + gcc_qupv3_wrap0_s0_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s0_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s1_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s1_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s2_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s2_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s2_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s3_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s3_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s3_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s4_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s4_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s4_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s5_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s5_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s5_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s6_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s6_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s6_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap0_s7_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap0_s7_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap0_s7_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s0_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s0_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s1_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s1_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s2_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s2_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s2_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s3_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s3_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s3_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s4_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s4_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s4_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s5_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s5_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s5_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s6_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s6_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s6_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_qupv3_wrap1_s7_clk_src.freq_tbl = + ftbl_gcc_qupv3_wrap0_s0_clk_src_sdm845_v2; + gcc_qupv3_wrap1_s7_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = + 50000000; + gcc_qupv3_wrap1_s7_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 128000000; + gcc_ufs_card_axi_clk_src.freq_tbl = + ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2; + gcc_ufs_card_axi_clk_src.clkr.hw.init->rate_max[VDD_CX_HIGH] = + 240000000; + gcc_ufs_phy_axi_clk_src.freq_tbl = + ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2; + gcc_vsensor_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 600000000; +} + +static void gcc_sdm845_fixup_sdm670(void) +{ + gcc_sdm845_fixup_sdm845v2(); + + gcc_sdm845_clocks[GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr; + gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr; + gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK] = + &gcc_sdcc1_ice_core_clk.clkr; + gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK_SRC] = + &gcc_sdcc1_apps_clk_src.clkr; + gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK_SRC] = + &gcc_sdcc1_ice_core_clk_src.clkr; + gcc_sdm845_clocks[GPLL6] = &gpll6.clkr; + gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK] = NULL; + gcc_sdm845_clocks[GCC_AGGRE_USB3_SEC_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_AGGRE_NOC_PCIE_TBU_CLK] = NULL; + gcc_sdm845_clocks[GCC_CFG_NOC_USB3_SEC_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_CFG_AHB_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_CLKREF_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_MSTR_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_PIPE_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_SLV_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_0_SLV_Q2A_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_CFG_AHB_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_CLKREF_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_MSTR_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_PIPE_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_SLV_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_1_SLV_Q2A_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_PHY_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK] = NULL; + gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_AHB_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_AXI_HW_CTL_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_CLKREF_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_0_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_1_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_TX_SYMBOL_0_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK] = NULL; + gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_UFS_PHY_RX_SYMBOL_1_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_USB30_SEC_SLEEP_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB3_SEC_CLKREF_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK_SRC] = NULL; + gcc_sdm845_clocks[GCC_USB3_SEC_PHY_COM_AUX_CLK] = NULL; + gcc_sdm845_clocks[GCC_USB3_SEC_PHY_PIPE_CLK] = NULL; + + gcc_cpuss_rbcpr_clk_src.freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src_sdm670; + gcc_cpuss_rbcpr_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 50000000; + gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 50000000; + gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 100000000; + gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = + 201500000; + gcc_sdcc4_apps_clk_src.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src_sdm670; + gcc_sdcc4_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 33333333; +} + +static int gcc_sdm845_fixup(struct platform_device *pdev) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,gcc-sdm845-v2") || + !strcmp(compat, "qcom,gcc-sdm845-v2.1")) + gcc_sdm845_fixup_sdm845v2(); + else if (!strcmp(compat, "qcom,gcc-sdm670")) + gcc_sdm845_fixup_sdm670(); + + return 0; +} + static int gcc_sdm845_probe(struct platform_device *pdev) { struct regmap *regmap; + int ret = 0; regmap = qcom_cc_map(pdev, &gcc_sdm845_desc); if (IS_ERR(regmap)) return PTR_ERR(regmap); + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } + + vdd_cx_ao.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx_ao"); + if (IS_ERR(vdd_cx_ao.regulator[0])) { + if (!(PTR_ERR(vdd_cx_ao.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx_ao regulator\n"); + return PTR_ERR(vdd_cx_ao.regulator[0]); + } + + ret = gcc_sdm845_fixup(pdev); + if (ret) + return ret; + + /* DFS clock registration */ + ret = qcom_cc_register_rcg_dfs(regmap, gcc_dfs_clocks, + ARRAY_SIZE(gcc_dfs_clocks)); + if (ret) { + dev_err(&pdev->dev, "Failed to register with DFS!\n"); + return ret; + } + + ret = qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GCC clocks\n"); + return ret; + } + /* Disable the GPLL0 active input to MMSS and GPU via MISC registers */ - regmap_update_bits(regmap, 0x09ffc, 0x3, 0x3); - regmap_update_bits(regmap, 0x71028, 0x3, 0x3); + regmap_update_bits(regmap, GCC_MMSS_MISC, 0x3, 0x3); + regmap_update_bits(regmap, GCC_GPU_MISC, 0x3, 0x3); + + /* Keep this clock on all the times except on SDM845 v2.1 */ + if (!of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdm845-v2.1")) + clk_prepare_enable(gcc_aggre_noc_pcie_tbu_clk.clkr.hw.clk); - return qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap); + dev_info(&pdev->dev, "Registered GCC clocks\n"); + + return ret; } static struct platform_driver gcc_sdm845_driver = { @@ -3509,3 +4386,4 @@ module_exit(gcc_sdm845_exit); MODULE_DESCRIPTION("QTI GCC SDM845 Driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gcc-sdm845"); + diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index ee8b6a2cde89..08f056dfecfe 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -30,10 +30,18 @@ #define CLK_DIS_WAIT_MASK (0xF << 12) #define CLK_DIS_WAIT_SHIFT (12) #define RETAIN_FF_ENABLE_MASK BIT(11) +#define EN_FEW_WAIT_MASK (0xF << 16) +#define EN_FEW_WAIT_SHIFT (16) +#define EN_REST_WAIT_MASK (0xF << 20) +#define EN_REST_WAIT_SHIFT (20) #define SW_OVERRIDE_MASK BIT(2) #define HW_CONTROL_MASK BIT(1) #define SW_COLLAPSE_MASK BIT(0) +/* CFG_GDSCR */ +#define GDSC_POWER_UP_COMPLETE BIT(16) +#define GDSC_POWER_DOWN_COMPLETE BIT(15) + /* Domain Address */ #define GMEM_CLAMP_IO_MASK BIT(0) #define GMEM_RESET_MASK BIT(4) @@ -43,6 +51,7 @@ /* Register Offset */ #define REG_OFFSET 0x0 +#define CFG_GDSCR_OFFSET 0x4 /* Timeout Delay */ #define TIMEOUT_US 100 @@ -72,6 +81,7 @@ struct gdsc { bool allow_clear; bool reset_aon; bool is_bus_enabled; + bool poll_cfg_gdscr; int clock_count; int reset_count; int root_clk_idx; @@ -138,6 +148,31 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status) return -ETIMEDOUT; } +static int poll_cfg_gdsc_status(struct gdsc *sc, enum gdscr_status status) +{ + struct regmap *regmap = sc->regmap; + int count = sc->gds_timeout; + u32 val; + + for (; count > 0; count--) { + regmap_read(regmap, CFG_GDSCR_OFFSET, &val); + + switch (status) { + case ENABLED: + if (val & GDSC_POWER_UP_COMPLETE) + return 0; + break; + case DISABLED: + if (val & GDSC_POWER_DOWN_COMPLETE) + return 0; + break; + } + udelay(1); + } + + return -ETIMEDOUT; +} + static int gdsc_is_enabled(struct regulator_dev *rdev) { struct gdsc *sc = rdev_get_drvdata(rdev); @@ -211,7 +246,7 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) static int gdsc_enable(struct regulator_dev *rdev) { struct gdsc *sc = rdev_get_drvdata(rdev); - uint32_t regval, hw_ctrl_regval = 0x0; + uint32_t regval, cfg_regval, hw_ctrl_regval = 0x0; int i, ret = 0; if (sc->skip_disable_before_enable) @@ -305,7 +340,10 @@ static int gdsc_enable(struct regulator_dev *rdev) gdsc_mb(sc); udelay(1); - ret = poll_gdsc_status(sc, ENABLED); + if (sc->poll_cfg_gdscr) + ret = poll_cfg_gdsc_status(sc, ENABLED); + else + ret = poll_gdsc_status(sc, ENABLED); if (ret) { regmap_read(sc->regmap, REG_OFFSET, ®val); @@ -333,10 +371,21 @@ static int gdsc_enable(struct regulator_dev *rdev) regval); udelay(sc->gds_timeout); - regmap_read(sc->regmap, REG_OFFSET, ®val); - dev_err(&rdev->dev, "%s final state: 0x%x (%d us after timeout)\n", - sc->rdesc.name, regval, - sc->gds_timeout); + if (sc->poll_cfg_gdscr) { + regmap_read(sc->regmap, REG_OFFSET, + ®val); + regmap_read(sc->regmap, + CFG_GDSCR_OFFSET, &cfg_regval); + dev_err(&rdev->dev, "%s final state: gdscr - 0x%x, cfg_gdscr - 0x%x (%d us after timeout)\n", + sc->rdesc.name, regval, + cfg_regval, sc->gds_timeout); + } else { + regmap_read(sc->regmap, REG_OFFSET, + ®val); + dev_err(&rdev->dev, "%s final state: 0x%x (%d us after timeout)\n", + sc->rdesc.name, regval, + sc->gds_timeout); + } goto end; } } @@ -425,7 +474,10 @@ static int gdsc_disable(struct regulator_dev *rdev) */ udelay(TIMEOUT_US); } else { - ret = poll_gdsc_status(sc, DISABLED); + if (sc->poll_cfg_gdscr) + ret = poll_cfg_gdsc_status(sc, DISABLED); + else + ret = poll_gdsc_status(sc, DISABLED); if (ret) dev_err(&rdev->dev, "%s disable timed out: 0x%x\n", sc->rdesc.name, regval); @@ -587,7 +639,10 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode) * starts to use SW mode. */ if (sc->is_gdsc_enabled) { - ret = poll_gdsc_status(sc, ENABLED); + if (sc->poll_cfg_gdscr) + ret = poll_cfg_gdsc_status(sc, ENABLED); + else + ret = poll_gdsc_status(sc, ENABLED); if (ret) dev_err(&rdev->dev, "%s enable timed out\n", sc->rdesc.name); @@ -687,6 +742,9 @@ static int gdsc_parse_dt_data(struct gdsc *sc, struct device *dev, return PTR_ERR(sc->hw_ctrl); } + sc->poll_cfg_gdscr = of_property_read_bool(dev->of_node, + "qcom,poll-cfg-gdscr"); + sc->gds_timeout = TIMEOUT_US; of_property_read_u32(dev->of_node, "qcom,gds-timeout", &sc->gds_timeout); @@ -863,6 +921,7 @@ static int gdsc_probe(struct platform_device *pdev) struct regulator_init_data *init_data = NULL; struct gdsc *sc; uint32_t regval, clk_dis_wait_val = 0; + uint32_t en_few_wait_val, en_rest_wait_val; int i, ret; sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); @@ -908,13 +967,32 @@ static int gdsc_probe(struct platform_device *pdev) regval |= clk_dis_wait_val; } + if (!of_property_read_u32(pdev->dev.of_node, "qcom,en-few-wait-val", + &en_few_wait_val)) { + en_few_wait_val <<= EN_FEW_WAIT_SHIFT; + + regval &= ~(EN_FEW_WAIT_MASK); + regval |= en_few_wait_val; + } + + if (!of_property_read_u32(pdev->dev.of_node, "qcom,en-rest-wait-val", + &en_rest_wait_val)) { + en_rest_wait_val <<= EN_REST_WAIT_SHIFT; + + regval &= ~(EN_REST_WAIT_MASK); + regval |= en_rest_wait_val; + } + regmap_write(sc->regmap, REG_OFFSET, regval); if (!sc->toggle_logic) { regval &= ~SW_COLLAPSE_MASK; regmap_write(sc->regmap, REG_OFFSET, regval); - ret = poll_gdsc_status(sc, ENABLED); + if (sc->poll_cfg_gdscr) + ret = poll_cfg_gdsc_status(sc, ENABLED); + else + ret = poll_gdsc_status(sc, ENABLED); if (ret) { dev_err(&pdev->dev, "%s enable timed out: 0x%x\n", sc->rdesc.name, regval); diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c new file mode 100644 index 000000000000..ad1310a10a86 --- /dev/null +++ b/drivers/clk/qcom/gpucc-sdm845.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-sdm845.h" + +#define CX_GMU_CBCR_SLEEP_MASK 0xF +#define CX_GMU_CBCR_SLEEP_SHIFT 4 +#define CX_GMU_CBCR_WAKE_MASK 0xF +#define CX_GMU_CBCR_WAKE_SHIFT 8 + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static int vdd_gx_corner[] = { + 0, /* VDD_GX_NONE */ + RPMH_REGULATOR_LEVEL_MIN_SVS, /* VDD_GX_MIN */ + RPMH_REGULATOR_LEVEL_LOW_SVS, /* VDD_GX_LOWER */ + RPMH_REGULATOR_LEVEL_SVS, /* VDD_GX_LOW */ + RPMH_REGULATOR_LEVEL_SVS_L1, /* VDD_GX_LOW_L1 */ + RPMH_REGULATOR_LEVEL_NOM, /* VDD_GX_NOMINAL */ + RPMH_REGULATOR_LEVEL_NOM_L1, /* VDD_GX_NOMINAL_L1 */ + RPMH_REGULATOR_LEVEL_TURBO, /* VDD_GX_HIGH */ + RPMH_REGULATOR_LEVEL_TURBO_L1, /* VDD_GX_HIGH_L1 */ + RPMH_REGULATOR_LEVEL_MAX, /* VDD_GX_MAX */ +}; + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_mx, VDD_CX_NUM, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_gfx, VDD_GX_NUM, 1, vdd_gx_corner); + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_EVEN, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL0_OUT_ODD, + P_GPU_CC_PLL1_OUT_EVEN, + P_GPU_CC_PLL1_OUT_MAIN, + P_GPU_CC_PLL1_OUT_ODD, + P_CRC_DIV, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpu_cc_parent_names_0[] = { + "bi_tcxo", + "gpu_cc_pll0", + "gpu_cc_pll1", + "gcc_gpu_gpll0_clk_src", + "gcc_gpu_gpll0_div_clk_src", + "core_bi_pll_test_se", +}; + +static const struct parent_map gpu_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_EVEN, 1 }, + { P_GPU_CC_PLL0_OUT_ODD, 2 }, + { P_GPU_CC_PLL1_OUT_EVEN, 3 }, + { P_GPU_CC_PLL1_OUT_ODD, 4 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpu_cc_parent_names_1[] = { + "bi_tcxo", + "gpu_cc_pll0_out_even", + "gpu_cc_pll0_out_odd", + "gpu_cc_pll1_out_even", + "gpu_cc_pll1_out_odd", + "gcc_gpu_gpll0_clk_src", + "core_bi_pll_test_se", +}; + +static const struct parent_map gpu_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CRC_DIV, 1 }, + { P_GPU_CC_PLL0_OUT_ODD, 2 }, + { P_GPU_CC_PLL1_OUT_EVEN, 3 }, + { P_GPU_CC_PLL1_OUT_ODD, 4 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpu_cc_parent_names_2[] = { + "bi_tcxo", + "crc_div", + "gpu_cc_pll0_out_odd", + "gpu_cc_pll1_out_even", + "gpu_cc_pll1_out_odd", + "gcc_gpu_gpll0_clk_src", + "core_bi_pll_test_se", +}; + +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, + { 125000000, 1000000000, 1 }, +}; + +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x1d, + .alpha = 0x2aaa, +}; + +static const struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x1a, + .alpha = 0xaaaa, +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll0", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_MX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static const struct clk_div_table post_div_table_fabia_even[] = { + { 0x0, 1 }, + { 0x1, 2 }, + { 0x3, 4 }, + { 0x7, 8 }, + {}, +}; + +static struct clk_alpha_pll_postdiv gpu_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll0_out_even", + .parent_names = (const char *[]){ "gpu_cc_pll0" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll1", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + VDD_MX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), + }, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 1.5, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src_sdm845_v2[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + F(500000000, P_GPU_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src_sdm670[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .enable_safe_config = true, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gmu_clk_src", + .parent_names = gpu_cc_parent_names_0, + .num_parents = 6, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP2( + MIN, 200000000, + LOW, 400000000), + }, +}; + +static struct clk_fixed_factor crc_div = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "crc_div", + .parent_names = (const char *[]){ "gpu_cc_pll0_out_even" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src[] = { + F(147000000, P_CRC_DIV, 1, 0, 0), + F(210000000, P_CRC_DIV, 1, 0, 0), + F(280000000, P_CRC_DIV, 1, 0, 0), + F(338000000, P_CRC_DIV, 1, 0, 0), + F(425000000, P_CRC_DIV, 1, 0, 0), + F(487000000, P_CRC_DIV, 1, 0, 0), + F(548000000, P_CRC_DIV, 1, 0, 0), + F(600000000, P_CRC_DIV, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src_sdm845_v2[] = { + F(180000000, P_CRC_DIV, 1, 0, 0), + F(257000000, P_CRC_DIV, 1, 0, 0), + F(342000000, P_CRC_DIV, 1, 0, 0), + F(414000000, P_CRC_DIV, 1, 0, 0), + F(520000000, P_CRC_DIV, 1, 0, 0), + F(596000000, P_CRC_DIV, 1, 0, 0), + F(675000000, P_CRC_DIV, 1, 0, 0), + F(710000000, P_CRC_DIV, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670[] = { + F(180000000, P_CRC_DIV, 1, 0, 0), + F(267000000, P_CRC_DIV, 1, 0, 0), + F(355000000, P_CRC_DIV, 1, 0, 0), + F(430000000, P_CRC_DIV, 1, 0, 0), + F(504000000, P_CRC_DIV, 1, 0, 0), + F(565000000, P_CRC_DIV, 1, 0, 0), + F(610000000, P_CRC_DIV, 1, 0, 0), + F(650000000, P_CRC_DIV, 1, 0, 0), + F(700000000, P_CRC_DIV, 1, 0, 0), + F(750000000, P_CRC_DIV, 1, 0, 0), + F(780000000, P_CRC_DIV, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = { + .cmd_rcgr = 0x101c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_2, + .freq_tbl = ftbl_gpu_cc_gx_gfx3d_clk_src, + .flags = FORCE_ENABLE_RCG, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gfx3d_clk_src", + .parent_names = gpu_cc_parent_names_2, + .num_parents = 7, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + VDD_GX_FMAX_MAP8( + MIN, 147000000, + LOWER, 210000000, + LOW, 280000000, + LOW_L1, 338000000, + NOMINAL, 425000000, + NOMINAL_L1, 487000000, + HIGH, 548000000, + HIGH_L1, 600000000), + }, +}; + +static struct clk_branch gpu_cc_acd_ahb_clk = { + .halt_reg = 0x1168, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1168, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_acd_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_acd_cxo_clk = { + .halt_reg = 0x1164, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1164, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_acd_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_crc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_apb_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_apb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_clk = { + .halt_reg = 0x10a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10a4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gfx3d_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gfx3d_slv_clk = { + .halt_reg = 0x10a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10a8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gfx3d_slv_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gmu_clk", + .parent_names = (const char *[]){ + "gpu_cc_gmu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gfx3d_clk = { + .halt_reg = 0x1054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gfx3d_clk", + .parent_names = (const char *[]){ + "gpu_cc_gx_gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gmu_clk", + .parent_names = (const char *[]){ + "gpu_cc_gmu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_vsense_clk = { + .halt_reg = 0x1058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_vsense_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_pll_test_clk = { + .halt_reg = 0x110c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x110c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll_test_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gpu_cc_sdm845_clocks[] = { + [GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr, + [GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr, + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr, + [GPU_CC_CX_GFX3D_CLK] = &gpu_cc_cx_gfx3d_clk.clkr, + [GPU_CC_CX_GFX3D_SLV_CLK] = &gpu_cc_cx_gfx3d_slv_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_GX_VSENSE_CLK] = &gpu_cc_gx_vsense_clk.clkr, + [GPU_CC_PLL_TEST_CLK] = &gpu_cc_pll_test_clk.clkr, + [GPU_CC_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL1] = NULL, +}; + +static struct clk_regmap *gpu_cc_gfx_sdm845_clocks[] = { + [GPU_CC_PLL0_OUT_EVEN] = &gpu_cc_pll0_out_even.clkr, + [GPU_CC_GX_GFX3D_CLK_SRC] = &gpu_cc_gx_gfx3d_clk_src.clkr, + [GPU_CC_GX_GFX3D_CLK] = &gpu_cc_gx_gfx3d_clk.clkr, +}; + +static const struct qcom_reset_map gpu_cc_sdm845_resets[] = { + [GPUCC_GPU_CC_ACD_BCR] = { 0x1160 }, + [GPUCC_GPU_CC_CX_BCR] = { 0x1068 }, + [GPUCC_GPU_CC_GFX3D_AON_BCR] = { 0x10a0 }, + [GPUCC_GPU_CC_GMU_BCR] = { 0x111c }, + [GPUCC_GPU_CC_GX_BCR] = { 0x1008 }, + [GPUCC_GPU_CC_SPDM_BCR] = { 0x1110 }, + [GPUCC_GPU_CC_XO_BCR] = { 0x1000 }, +}; + +static const struct regmap_config gpu_cc_sdm845_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8008, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpu_cc_sdm845_desc = { + .config = &gpu_cc_sdm845_regmap_config, + .clks = gpu_cc_sdm845_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_sdm845_clocks), + .resets = gpu_cc_sdm845_resets, + .num_resets = ARRAY_SIZE(gpu_cc_sdm845_resets), +}; + +static const struct qcom_cc_desc gpu_cc_gfx_sdm845_desc = { + .config = &gpu_cc_sdm845_regmap_config, + .clks = gpu_cc_gfx_sdm845_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_gfx_sdm845_clocks), +}; + +static const struct of_device_id gpu_cc_sdm845_match_table[] = { + { .compatible = "qcom,gpucc-sdm845" }, + { .compatible = "qcom,gpucc-sdm845-v2" }, + { .compatible = "qcom,gpucc-sdm670" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_sdm845_match_table); + +static const struct of_device_id gpu_cc_gfx_sdm845_match_table[] = { + { .compatible = "qcom,gfxcc-sdm845" }, + { .compatible = "qcom,gfxcc-sdm845-v2" }, + { .compatible = "qcom,gfxcc-sdm670" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gpu_cc_gfx_sdm845_match_table); + +static void gpu_cc_sdm845_fixup_sdm845v2(struct regmap *regmap) +{ + gpu_cc_sdm845_clocks[GPU_CC_PLL1] = &gpu_cc_pll1.clkr; + clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + gpu_cc_gmu_clk_src.freq_tbl = ftbl_gpu_cc_gmu_clk_src_sdm845_v2; + gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 500000000; +} + +static void gpu_cc_sdm845_fixup_sdm670(struct regmap *regmap) +{ + gpu_cc_sdm845_clocks[GPU_CC_PLL1] = &gpu_cc_pll1.clkr; + clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + gpu_cc_gmu_clk_src.freq_tbl = ftbl_gpu_cc_gmu_clk_src_sdm670; + gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 200000000; +} + +static void gpu_cc_gfx_sdm845_fixup_sdm845v2(void) +{ + gpu_cc_gx_gfx3d_clk_src.freq_tbl = + ftbl_gpu_cc_gx_gfx3d_clk_src_sdm845_v2; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] = 180000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] = + 257000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] = 342000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] = + 414000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] = + 520000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] = + 596000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] = 675000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] = + 710000000; +} + +static void gpu_cc_gfx_sdm845_fixup_sdm670(void) +{ + gpu_cc_gx_gfx3d_clk_src.freq_tbl = + ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] = 180000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] = + 267000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] = 355000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] = + 430000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] = + 565000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] = + 650000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] = 750000000; + gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] = + 780000000; +} + +static int gpu_cc_gfx_sdm845_fixup(struct platform_device *pdev) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,gfxcc-sdm845-v2")) + gpu_cc_gfx_sdm845_fixup_sdm845v2(); + else if (!strcmp(compat, "qcom,gfxcc-sdm670")) + gpu_cc_gfx_sdm845_fixup_sdm670(); + + return 0; +} + +static int gpu_cc_sdm845_fixup(struct platform_device *pdev, + struct regmap *regmap) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,gpucc-sdm845-v2")) + gpu_cc_sdm845_fixup_sdm845v2(regmap); + else if (!strcmp(compat, "qcom,gpucc-sdm670")) + gpu_cc_sdm845_fixup_sdm670(regmap); + + return 0; +} + +static int gpu_cc_gfx_sdm845_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct resource *res; + void __iomem *base; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Failed to get resources for clock_gfxcc\n"); + return -EINVAL; + } + + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Failed to ioremap the GFX CC base\n"); + return PTR_ERR(base); + } + + /* Register clock fixed factor for CRC divide. */ + ret = devm_clk_hw_register(&pdev->dev, &crc_div.hw); + if (ret) { + dev_err(&pdev->dev, "Failed to register hardware clock\n"); + return ret; + } + + regmap = devm_regmap_init_mmio(&pdev->dev, base, + gpu_cc_gfx_sdm845_desc.config); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return PTR_ERR(regmap); + } + + /* GFX voltage regulators for GFX3D graphic clock. */ + vdd_gfx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_gfx"); + if (IS_ERR(vdd_gfx.regulator[0])) { + if (PTR_ERR(vdd_gfx.regulator[0]) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get vdd_gfx regulator\n"); + return PTR_ERR(vdd_gfx.regulator[0]); + } + + /* Avoid turning on the rail during clock registration */ + vdd_gfx.skip_handoff = true; + + ret = gpu_cc_gfx_sdm845_fixup(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to do GFX clock fixup\n"); + return ret; + } + + clk_fabia_pll_configure(&gpu_cc_pll0, regmap, &gpu_cc_pll0_config); + + ret = qcom_cc_really_probe(pdev, &gpu_cc_gfx_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GFX CC clocks\n"); + return ret; + } + + dev_info(&pdev->dev, "Registered GFX CC clocks\n"); + + return ret; +} + +static struct platform_driver gpu_cc_gfx_sdm845_driver = { + .probe = gpu_cc_gfx_sdm845_probe, + .driver = { + .name = "gfxcc-sdm845", + .of_match_table = gpu_cc_gfx_sdm845_match_table, + }, +}; + +static int __init gpu_cc_gfx_sdm845_init(void) +{ + return platform_driver_register(&gpu_cc_gfx_sdm845_driver); +} +subsys_initcall(gpu_cc_gfx_sdm845_init); + +static void __exit gpu_cc_gfx_sdm845_exit(void) +{ + platform_driver_unregister(&gpu_cc_gfx_sdm845_driver); +} +module_exit(gpu_cc_gfx_sdm845_exit); + +static int gpu_cc_sdm845_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + int ret = 0; + unsigned int value, mask; + + regmap = qcom_cc_map(pdev, &gpu_cc_sdm845_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Get CX voltage regulator for CX and GMU clocks. */ + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } + + /* Get MX voltage regulator for GPU PLL graphic clock. */ + vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx"); + if (IS_ERR(vdd_mx.regulator[0])) { + if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_mx regulator\n"); + return PTR_ERR(vdd_mx.regulator[0]); + } + + ret = gpu_cc_sdm845_fixup(pdev, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to do GPU CC clock fixup\n"); + return ret; + } + + ret = qcom_cc_really_probe(pdev, &gpu_cc_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GPU CC clocks\n"); + return ret; + } + + mask = CX_GMU_CBCR_WAKE_MASK << CX_GMU_CBCR_WAKE_SHIFT; + mask |= CX_GMU_CBCR_SLEEP_MASK << CX_GMU_CBCR_SLEEP_SHIFT; + value = 0xF << CX_GMU_CBCR_WAKE_SHIFT | 0xF << CX_GMU_CBCR_SLEEP_SHIFT; + regmap_update_bits(regmap, gpu_cc_cx_gmu_clk.clkr.enable_reg, + mask, value); + + dev_info(&pdev->dev, "Registered GPU CC clocks\n"); + + return ret; +} + +static struct platform_driver gpu_cc_sdm845_driver = { + .probe = gpu_cc_sdm845_probe, + .driver = { + .name = "gpu_cc-sdm845", + .of_match_table = gpu_cc_sdm845_match_table, + }, +}; + +static int __init gpu_cc_sdm845_init(void) +{ + return platform_driver_register(&gpu_cc_sdm845_driver); +} +subsys_initcall(gpu_cc_sdm845_init); + +static void __exit gpu_cc_sdm845_exit(void) +{ + platform_driver_unregister(&gpu_cc_sdm845_driver); +} +module_exit(gpu_cc_sdm845_exit); + +MODULE_DESCRIPTION("QTI GPU_CC SDM845 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpu_cc-sdm845"); diff --git a/drivers/clk/qcom/vdd-level-sdm845.h b/drivers/clk/qcom/vdd-level-sdm845.h new file mode 100644 index 000000000000..3eec510f4d8e --- /dev/null +++ b/drivers/clk/qcom/vdd-level-sdm845.h @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +#ifndef __DRIVERS_CLK_QCOM_VDD_LEVEL_COBALT_H +#define __DRIVERS_CLK_QCOM_VDD_LEVEL_COBALT_H + +#include +#include + +#define VDD_CX_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP4(l1, f1, l2, f2, l3, f3, l4, f4) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + [VDD_CX_##l4] = (f4), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP5(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + [VDD_CX_##l4] = (f4), \ + [VDD_CX_##l5] = (f5), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP6(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \ + .vdd_class = &vdd_cx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + [VDD_CX_##l4] = (f4), \ + [VDD_CX_##l5] = (f5), \ + [VDD_CX_##l6] = (f6), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP1_AO(l1, f1) \ + .vdd_class = &vdd_cx_ao, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_CX_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \ + .vdd_class = &vdd_cx_ao, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_MX_FMAX_MAP4(l1, f1, l2, f2, l3, f3, l4, f4) \ + .vdd_class = &vdd_mx, \ + .rate_max = (unsigned long[VDD_CX_NUM]) { \ + [VDD_CX_##l1] = (f1), \ + [VDD_CX_##l2] = (f2), \ + [VDD_CX_##l3] = (f3), \ + [VDD_CX_##l4] = (f4), \ + }, \ + .num_rate_max = VDD_CX_NUM + +#define VDD_GX_FMAX_MAP8(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6, \ + l7, f7, l8, f8) \ + .vdd_class = &vdd_gfx, \ + .rate_max = (unsigned long[VDD_GX_NUM]) { \ + [VDD_GX_##l1] = (f1), \ + [VDD_GX_##l2] = (f2), \ + [VDD_GX_##l3] = (f3), \ + [VDD_GX_##l4] = (f4), \ + [VDD_GX_##l5] = (f5), \ + [VDD_GX_##l6] = (f6), \ + [VDD_GX_##l7] = (f7), \ + [VDD_GX_##l8] = (f8), \ + }, \ + .num_rate_max = VDD_GX_NUM + +enum vdd_cx_levels { + VDD_CX_NONE, + VDD_CX_MIN, /* MIN SVS */ + VDD_CX_LOWER, /* SVS2 */ + VDD_CX_LOW, /* SVS */ + VDD_CX_LOW_L1, /* SVSL1 */ + VDD_CX_NOMINAL, /* NOM */ + VDD_CX_HIGH, /* TURBO */ + VDD_CX_NUM, +}; + +enum vdd_gx_levels { + VDD_GX_NONE, + VDD_GX_MIN, /* MIN SVS */ + VDD_GX_LOWER, /* SVS2 */ + VDD_GX_LOW, /* SVS */ + VDD_GX_LOW_L1, /* SVSL1 */ + VDD_GX_NOMINAL, /* NOM */ + VDD_GX_NOMINAL_L1, /* NOM1 */ + VDD_GX_HIGH, /* TURBO */ + VDD_GX_HIGH_L1, /* TURBO1 */ + VDD_GX_NUM, +}; + +/* Need to use the correct VI/VL mappings */ +static int vdd_corner[] = { + 0, /* VDD_CX_NONE */ + RPMH_REGULATOR_LEVEL_MIN_SVS, /* VDD_CX_MIN */ + RPMH_REGULATOR_LEVEL_LOW_SVS, /* VDD_CX_LOWER */ + RPMH_REGULATOR_LEVEL_SVS, /* VDD_CX_LOW */ + RPMH_REGULATOR_LEVEL_SVS_L1, /* VDD_CX_LOW_L1 */ + RPMH_REGULATOR_LEVEL_NOM, /* VDD_CX_NOMINAL */ + RPMH_REGULATOR_LEVEL_TURBO, /* VDD_CX_HIGH */ +}; + +#endif diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c index 5d6a7724a194..6cb12f892e4b 100644 --- a/drivers/clk/qcom/videocc-sdm845.c +++ b/drivers/clk/qcom/videocc-sdm845.c @@ -1,22 +1,36 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. */ -#include -#include +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include #include "common.h" -#include "clk-alpha-pll.h" -#include "clk-branch.h" -#include "clk-rcg.h" #include "clk-regmap.h" #include "clk-pll.h" -#include "gdsc.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-sdm845.h" + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner); enum { P_BI_TCXO, @@ -42,6 +56,11 @@ static const char * const video_cc_parent_names_0[] = { "core_bi_pll_test_se", }; +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, + { 125000000, 1000000000, 1 }, +}; + static const struct alpha_pll_config video_pll0_config = { .l = 0x10, .alpha = 0xaaab, @@ -49,6 +68,8 @@ static const struct alpha_pll_config video_pll0_config = { static struct clk_alpha_pll video_pll0 = { .offset = 0x42c, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], .clkr = { .hw.init = &(struct clk_init_data){ @@ -56,11 +77,26 @@ static struct clk_alpha_pll video_pll0 = { .parent_names = (const char *[]){ "bi_tcxo" }, .num_parents = 1, .ops = &clk_alpha_pll_fabia_ops, + VDD_CX_FMAX_MAP4( + MIN, 615000000, + LOW, 1066000000, + LOW_L1, 1600000000, + NOMINAL, 2000000000), }, }, }; static const struct freq_tbl ftbl_video_cc_venus_clk_src[] = { + F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0), + F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(320000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + F(380000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + F(444000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + F(533000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_video_cc_venus_clk_src_sdm845_v2[] = { F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0), F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), F(330000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), @@ -70,18 +106,37 @@ static const struct freq_tbl ftbl_video_cc_venus_clk_src[] = { { } }; +static const struct freq_tbl ftbl_video_cc_venus_clk_src_sdm670[] = { + F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0), + F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(330000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(364700000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(404000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + F(444000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + F(533000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + static struct clk_rcg2 video_cc_venus_clk_src = { .cmd_rcgr = 0x7f0, .mnd_width = 0, .hid_width = 5, .parent_map = video_cc_parent_map_0, .freq_tbl = ftbl_video_cc_venus_clk_src, + .enable_safe_config = true, .clkr.hw.init = &(struct clk_init_data){ .name = "video_cc_venus_clk_src", .parent_names = video_cc_parent_names_0, .num_parents = 5, .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_ops, + VDD_CX_FMAX_MAP6( + MIN, 100000000, + LOWER, 200000000, + LOW, 320000000, + LOW_L1, 380000000, + NOMINAL, 444000000, + HIGH, 533000000), }, }; @@ -243,39 +298,6 @@ static struct clk_branch video_cc_venus_ctl_core_clk = { }, }; -static struct gdsc venus_gdsc = { - .gdscr = 0x814, - .pd = { - .name = "venus_gdsc", - }, - .cxcs = (unsigned int []){ 0x850, 0x910 }, - .cxc_count = 2, - .pwrsts = PWRSTS_OFF_ON, - .flags = POLL_CFG_GDSCR, -}; - -static struct gdsc vcodec0_gdsc = { - .gdscr = 0x874, - .pd = { - .name = "vcodec0_gdsc", - }, - .cxcs = (unsigned int []){ 0x890, 0x930 }, - .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, - .pwrsts = PWRSTS_OFF_ON, -}; - -static struct gdsc vcodec1_gdsc = { - .gdscr = 0x8b4, - .pd = { - .name = "vcodec1_gdsc", - }, - .cxcs = (unsigned int []){ 0x8d0, 0x950 }, - .cxc_count = 2, - .flags = HW_CTRL | POLL_CFG_GDSCR, - .pwrsts = PWRSTS_OFF_ON, -}; - static struct clk_regmap *video_cc_sdm845_clocks[] = { [VIDEO_CC_APB_CLK] = &video_cc_apb_clk.clkr, [VIDEO_CC_AT_CLK] = &video_cc_at_clk.clkr, @@ -292,12 +314,6 @@ static struct clk_regmap *video_cc_sdm845_clocks[] = { [VIDEO_PLL0] = &video_pll0.clkr, }; -static struct gdsc *video_cc_sdm845_gdscs[] = { - [VENUS_GDSC] = &venus_gdsc, - [VCODEC0_GDSC] = &vcodec0_gdsc, - [VCODEC1_GDSC] = &vcodec1_gdsc, -}; - static const struct regmap_config video_cc_sdm845_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -310,33 +326,88 @@ static const struct qcom_cc_desc video_cc_sdm845_desc = { .config = &video_cc_sdm845_regmap_config, .clks = video_cc_sdm845_clocks, .num_clks = ARRAY_SIZE(video_cc_sdm845_clocks), - .gdscs = video_cc_sdm845_gdscs, - .num_gdscs = ARRAY_SIZE(video_cc_sdm845_gdscs), }; static const struct of_device_id video_cc_sdm845_match_table[] = { - { .compatible = "qcom,sdm845-videocc" }, + { .compatible = "qcom,video_cc-sdm845" }, + { .compatible = "qcom,video_cc-sdm845-v2" }, + { .compatible = "qcom,video_cc-sdm670" }, { } }; MODULE_DEVICE_TABLE(of, video_cc_sdm845_match_table); +static void video_cc_sdm845_fixup_sdm845v2(void) +{ + video_cc_venus_clk_src.freq_tbl = ftbl_video_cc_venus_clk_src_sdm845_v2; + video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 330000000; + video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 404000000; +} + +static void video_cc_sdm845_fixup_sdm670(void) +{ + video_cc_venus_clk_src.freq_tbl = ftbl_video_cc_venus_clk_src_sdm670; + video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 330000000; + video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = + 404000000; +} + +static int video_cc_sdm845_fixup(struct platform_device *pdev) +{ + const char *compat = NULL; + int compatlen = 0; + + compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen); + if (!compat || (compatlen <= 0)) + return -EINVAL; + + if (!strcmp(compat, "qcom,video_cc-sdm845-v2")) + video_cc_sdm845_fixup_sdm845v2(); + else if (!strcmp(compat, "qcom,video_cc-sdm670")) + video_cc_sdm845_fixup_sdm670(); + + return 0; +} + static int video_cc_sdm845_probe(struct platform_device *pdev) { struct regmap *regmap; + int ret = 0; regmap = qcom_cc_map(pdev, &video_cc_sdm845_desc); - if (IS_ERR(regmap)) + if (IS_ERR(regmap)) { + pr_err("Failed to map the Video CC registers\n"); return PTR_ERR(regmap); + } + + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } + + ret = video_cc_sdm845_fixup(pdev); + if (ret) + return ret; clk_fabia_pll_configure(&video_pll0, regmap, &video_pll0_config); - return qcom_cc_really_probe(pdev, &video_cc_sdm845_desc, regmap); + ret = qcom_cc_really_probe(pdev, &video_cc_sdm845_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register Video CC clocks\n"); + return ret; + } + + dev_info(&pdev->dev, "Registered Video CC clocks\n"); + return ret; } static struct platform_driver video_cc_sdm845_driver = { .probe = video_cc_sdm845_probe, .driver = { - .name = "sdm845-videocc", + .name = "video_cc-sdm845", .of_match_table = video_cc_sdm845_match_table, }, }; @@ -353,4 +424,6 @@ static void __exit video_cc_sdm845_exit(void) } module_exit(video_cc_sdm845_exit); +MODULE_DESCRIPTION("QCOM VIDEO_CC SDM845 Driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:video_cc-sdm845"); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index a87df3c9faf1..ba5973bbee89 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -125,6 +125,12 @@ module_param_named(print_parsed_dt, print_parsed_dt, bool, 0664); static bool sleep_disabled; module_param_named(sleep_disabled, sleep_disabled, bool, 0664); +void msm_cpuidle_set_sleep_disable(bool disable) +{ + sleep_disabled = disable; + pr_info("%s:sleep_disabled=%d\n", __func__, disable); +} + /** * msm_cpuidle_get_deep_idle_latency - Get deep idle latency value * diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 19a1e32c3a7e..7bd411789557 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -159,4 +159,10 @@ config EXTCON_USBC_CROS_EC Say Y here to enable USB Type C cable detection extcon support when using Chrome OS EC based USB Type-C ports. +config TRI_STATE_KEY + default n + tristate "switch Profiles by this triple key" + help + Say Y here if you want to enable the feature. + endif diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 0888fdeded72..3b47d428144a 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o +obj-$(CONFIG_TRI_STATE_KEY) += tri_state_key.o diff --git a/drivers/extcon/tri_state_key.c b/drivers/extcon/tri_state_key.c new file mode 100644 index 000000000000..d9bb8a27a344 --- /dev/null +++ b/drivers/extcon/tri_state_key.c @@ -0,0 +1,368 @@ +/* + * + * 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 "extcon.h" + +#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 + */ +enum { + MODE_UNKNOWN, + MODE_MUTE, + MODE_DO_NOT_DISTURB, + MODE_NORMAL, + MODE_MAX_NUM + } tri_mode_t; + +static const unsigned int tristate_extcon_tab[] = { + MODE_MUTE, + MODE_DO_NOT_DISTURB, + MODE_NORMAL, + EXTCON_NONE, +}; + +struct extcon_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 extcon_dev *edev; //change 1 + struct device *dev; + + struct timer_list s_timer; + struct pinctrl *key_pinctrl; + struct pinctrl_state *set_state; + +}; + +static struct extcon_dev_data *extcon_data; +static DEFINE_MUTEX(sem); +static int set_gpio_by_pinctrl(void) +{ + return pinctrl_select_state(extcon_data->key_pinctrl, + extcon_data->set_state); +} + + +static void extcon_dev_work(struct work_struct *work) +{ + int key[3]={0,0,0}; + int hw_version=0; + /*hw 13 use special tri state key no use key2*/ + hw_version=get_hw_version(); + pr_err("%s ,hw_version=%d\n",__func__, hw_version); + if (hw_version == 13) + { + key[0] = gpio_get_value(extcon_data->key1_gpio); + key[2] = gpio_get_value(extcon_data->key3_gpio); + + pr_err("%s ,key[0]=%d,key[1]=%d,key[2]=%d\n", + __func__, key[0], key[1], key[2]); + if(key[0]==1 && key[2]==1 ) + { + extcon_set_state_sync(extcon_data->edev,1, 1); + extcon_set_state_sync(extcon_data->edev,2, 0); + extcon_set_state_sync(extcon_data->edev,3, 1); + } + else if(key[0]==0 && key[2]==1 ) + { + extcon_set_state_sync(extcon_data->edev,1, 0); + extcon_set_state_sync(extcon_data->edev,2, 1); + extcon_set_state_sync(extcon_data->edev,3, 1); + } + else if(key[0]==1 && key[2]==0 ) + { + extcon_set_state_sync(extcon_data->edev,1, 1); + extcon_set_state_sync(extcon_data->edev,2, 1); + extcon_set_state_sync(extcon_data->edev,3, 0); + } + + } + else + { + + key[0] = gpio_get_value(extcon_data->key1_gpio); + key[1] = gpio_get_value(extcon_data->key2_gpio); + key[2] = gpio_get_value(extcon_data->key3_gpio); + + pr_err("%s ,key[0]=%d,key[1]=%d,key[2]=%d\n", + __func__, key[0], key[1], key[2]); + extcon_set_state_sync( + extcon_data->edev, + 1, key[0]); + extcon_set_state_sync( + extcon_data->edev, + 2, key[1]); + extcon_set_state_sync( + extcon_data->edev, + 3, key[2]); + } + +} + + +static irqreturn_t extcon_dev_interrupt(int irq, void *_dev) +{ + schedule_work(&extcon_data->work); + return IRQ_HANDLED; +} + +static void timer_handle(struct timer_list *t) +{ + schedule_work(&extcon_data->work); +} + +#ifdef CONFIG_OF +static int extcon_dev_get_devtree_pdata(struct device *dev) +{ + struct device_node *node; + + node = dev->of_node; + if (!node) + return -EINVAL; + + extcon_data->key3_gpio = + of_get_named_gpio(node, "tristate,gpio_key3", 0); + if ((!gpio_is_valid(extcon_data->key3_gpio))) + return -EINVAL; + pr_err("extcon_data->key3_gpio=%d\n", extcon_data->key3_gpio); + + extcon_data->key2_gpio = + of_get_named_gpio(node, "tristate,gpio_key2", 0); + if ((!gpio_is_valid(extcon_data->key2_gpio))) + return -EINVAL; + pr_err("extcon_data->key2_gpio=%d\n", extcon_data->key2_gpio); + + extcon_data->key1_gpio = + of_get_named_gpio(node, "tristate,gpio_key1", 0); + if ((!gpio_is_valid(extcon_data->key1_gpio))) + return -EINVAL; + pr_err("extcon_data->key1_gpio=%d\n", extcon_data->key1_gpio); + + return 0; +} +#else +static inline int +extcon_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; + + extcon_data = kzalloc(sizeof(struct extcon_dev_data), GFP_KERNEL); + if (!extcon_data) + return -ENOMEM; + + extcon_data->dev = dev; + + + extcon_data->key_pinctrl = devm_pinctrl_get(extcon_data->dev); + + if (IS_ERR_OR_NULL(extcon_data->key_pinctrl)) { + dev_err(extcon_data->dev, "Failed to get pinctrl\n"); + goto err_extcon_dev_register; + } + extcon_data->set_state = pinctrl_lookup_state(extcon_data->key_pinctrl, + "pmx_tri_state_key_active"); + if (IS_ERR_OR_NULL(extcon_data->set_state)) { + dev_err(extcon_data->dev, "Failed to lookup_state\n"); + goto err_extcon_dev_register; + } + + set_gpio_by_pinctrl(); + + ret = extcon_dev_get_devtree_pdata(dev); + if (ret) { + dev_err(dev, "parse device tree fail!!!\n"); + goto err_extcon_dev_register; + } + + + /* extcon registration */ + extcon_data->edev = + devm_extcon_dev_allocate(extcon_data->dev, tristate_extcon_tab); + extcon_data->edev->name = DRV_NAME; + + ret = devm_extcon_dev_register(extcon_data->dev, extcon_data->edev); + if (ret < 0) + goto err_extcon_dev_register; + + //config irq gpio and request irq + ret = gpio_request(extcon_data->key1_gpio, "tristate_key1"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(extcon_data->key1_gpio); + if (ret < 0) + goto err_set_gpio_input; + + extcon_data->irq_key1 = gpio_to_irq(extcon_data->key1_gpio); + if (extcon_data->irq_key1 < 0) { + ret = extcon_data->irq_key1; + goto err_detect_irq_num_failed; + } + + ret = request_irq(extcon_data->irq_key1, extcon_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key1", extcon_data); + if (ret < 0) + goto err_request_irq; + + ret = gpio_request(extcon_data->key2_gpio, + "tristate_key2"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(extcon_data->key2_gpio); + if (ret < 0) + goto err_set_gpio_input; + + extcon_data->irq_key2 = gpio_to_irq(extcon_data->key2_gpio); + if (extcon_data->irq_key2 < 0) { + ret = extcon_data->irq_key2; + goto err_detect_irq_num_failed; + } + + ret = request_irq(extcon_data->irq_key2, extcon_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key2", extcon_data); + if (ret < 0) + goto err_request_irq; + + ret = gpio_request(extcon_data->key3_gpio, + "tristate_key3"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(extcon_data->key3_gpio); + if (ret < 0) + goto err_set_gpio_input; + + extcon_data->irq_key3 = gpio_to_irq(extcon_data->key3_gpio); + if (extcon_data->irq_key3 < 0) { + ret = extcon_data->irq_key3; + goto err_detect_irq_num_failed; + } + + ret = request_irq(extcon_data->irq_key3, extcon_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key3", extcon_data); + if (ret < 0) + goto err_request_irq; + + INIT_WORK(&extcon_data->work, extcon_dev_work); + + __init_timer(&extcon_data->s_timer, timer_handle, 0); + //extcon_data->s_timer.function = &timer_handle; + extcon_data->s_timer.expires = jiffies + 5*HZ; + + add_timer(&extcon_data->s_timer); + + enable_irq_wake(extcon_data->irq_key1); + enable_irq_wake(extcon_data->irq_key2); + enable_irq_wake(extcon_data->irq_key3); + + return 0; + +err_request_gpio: + devm_extcon_dev_unregister(extcon_data->dev, extcon_data->edev); +err_request_irq: +err_detect_irq_num_failed: +err_set_gpio_input: + gpio_free(extcon_data->key2_gpio); + gpio_free(extcon_data->key1_gpio); + gpio_free(extcon_data->key3_gpio); +err_extcon_dev_register: + kfree(extcon_data); + + return ret; +} + +static int tristate_dev_remove(struct platform_device *pdev) +{ + cancel_work_sync(&extcon_data->work); + gpio_free(extcon_data->key1_gpio); + gpio_free(extcon_data->key2_gpio); + gpio_free(extcon_data->key3_gpio); + extcon_dev_unregister(extcon_data->edev); + kfree(extcon_data); + + return 0; +} +#ifdef CONFIG_OF +static const 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/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 8750f3f02b3f..0918ee4baf39 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -118,6 +118,13 @@ void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); void drm_gem_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj); +int dsi_display_set_hbm_mode(struct drm_connector *connector, int level); +int dsi_display_get_hbm_mode(struct drm_connector *connector); +int dsi_display_set_aod_mode(struct drm_connector *connector, int level); +int dsi_display_get_aod_mode(struct drm_connector *connector); +int dsi_display_set_fp_hbm_mode(struct drm_connector *connector, int level); +int dsi_display_get_fp_hbm_mode(struct drm_connector *connector); + /* drm_debugfs.c drm_debugfs_crc.c */ #if defined(CONFIG_DEBUG_FS) int drm_debugfs_init(struct drm_minor *minor, int minor_id, diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index b15d5e4320d6..6cd4053e9b25 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -1162,6 +1162,29 @@ int mipi_dsi_dcs_get_display_brightness_large(struct mipi_dsi_device *dsi, } EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness_large); +/** + * mipi_dsi_dcs_set_display_brightness_samsung() - sets the brightness value of the + * display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_brightness_samsung(struct mipi_dsi_device *dsi, + u16 brightness) +{ + u8 payload[2] = {brightness >> 8, brightness & 0xff}; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + payload, sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness_samsung); + static int mipi_dsi_drv_probe(struct device *dev) { struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 74857fafb0b8..81455fe4cec6 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -227,16 +227,291 @@ static ssize_t modes_show(struct device *device, return written; } +static ssize_t hbm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int hbm_mode = 0; + + hbm_mode = dsi_display_get_hbm_mode(connector); + + ret = scnprintf(buf, PAGE_SIZE, "%d\n", hbm_mode); + return ret; +} + +static ssize_t hbm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int hbm_mode = 0; + + ret = kstrtoint(buf, 10, &hbm_mode); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + return ret; + } + + ret = dsi_display_set_hbm_mode(connector, hbm_mode); + if (ret) + pr_err("set hbm mode(%d) fail\n", hbm_mode); + + return count; +} + +static ssize_t op_friginer_print_hbm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int op_hbm_mode = 0; + + op_hbm_mode = dsi_display_get_fp_hbm_mode(connector); + + ret = scnprintf(buf, PAGE_SIZE, "OP_FP mode = %d\n" + "0--finger-hbm mode(off)\n" + "1--finger-hbm mode(600)\n", + op_hbm_mode); + return ret; +} + +static ssize_t op_friginer_print_hbm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int op_hbm_mode = 0; + + ret = kstrtoint(buf, 10, &op_hbm_mode); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + return ret; + } + + ret = dsi_display_set_fp_hbm_mode(connector, op_hbm_mode); + if (ret) + pr_err("set hbm mode(%d) fail\n", op_hbm_mode); + + return count; +} + +static ssize_t aod_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int aod_mode = 0; + + aod_mode = dsi_display_get_aod_mode(connector); + + ret = scnprintf(buf, PAGE_SIZE, "%d\n", aod_mode); + return ret; +} + +static ssize_t aod_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + int aod_mode = 0; + + ret = kstrtoint(buf, 10, &aod_mode); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + return ret; + } + printk(KERN_ERR " node aod_mode=%d\n",aod_mode); + ret = dsi_display_set_aod_mode(connector, aod_mode); + if (ret) + pr_err("set AOD mode(%d) fail\n", aod_mode); + return count; +} + + +int oneplus_force_screenfp = 0; +int oneplus_panel_alpha = 0; +int op_dimlayer_bl_enable = 0; +int op_dp_enable = 0; +extern int oneplus_get_panel_brightness_to_alpha(void); + +static ssize_t oneplus_display_get_dim_alpha(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", oneplus_get_panel_brightness_to_alpha()); +} + +static ssize_t oneplus_display_set_dim_alpha(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + sscanf(buf, "%x", &oneplus_panel_alpha); + + return count; +} + +static ssize_t oneplus_display_get_forcescreenfp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + oneplus_force_screenfp = dsi_display_get_fp_hbm_mode(connector); + + ret = scnprintf(buf, PAGE_SIZE, "OP_FP mode = %d\n" + "0--finger-hbm mode(off)\n" + "1--finger-hbm mode(600)\n", + oneplus_force_screenfp); + return sprintf(buf, "%d\n", oneplus_force_screenfp); + +} + +static ssize_t oneplus_display_set_forcescreenfp(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + ret = kstrtoint(buf, 10, &oneplus_force_screenfp); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + return ret; + } + + ret = dsi_display_set_fp_hbm_mode(connector, oneplus_force_screenfp); + if (ret) + pr_err("set hbm mode(%d) fail\n", oneplus_force_screenfp); + return count; +} + +static ssize_t op_display_get_dimlayer_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", op_dimlayer_bl_enable); +} + +static ssize_t op_display_set_dimlayer_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = sscanf(buf, "%d", &op_dimlayer_bl_enable); + + if (err < 0) + pr_err("op_display_set_dimlayer_enable sscanf failed"); + return count; +} + +static ssize_t op_display_get_dp_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", op_dp_enable); +} + +static ssize_t op_display_set_dp_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = sscanf(buf, "%d", &op_dp_enable); + + if (err < 0) + pr_err("op_display_set_dp_enable sscanf failed"); + return count; +} + +extern ssize_t oneplus_display_notify_fp_press(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count); + +extern ssize_t oneplus_display_notify_dim(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count); + +extern ssize_t oneplus_display_notify_aod_hid(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count); + +int oneplus_auth_status = 0; +int oneplus_panel_status = 0; +static ssize_t op_display_get_auth_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", oneplus_auth_status); +} + +static ssize_t op_display_set_auth_status(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + sscanf(buf, "%d", &oneplus_auth_status); + + return count; +} + +static ssize_t op_display_get_power_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", oneplus_panel_status); +} + +static ssize_t op_display_set_power_status(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + sscanf(buf, "%d", &oneplus_panel_status); + + return count; +} + static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(enabled); static DEVICE_ATTR_RO(dpms); static DEVICE_ATTR_RO(modes); +static DEVICE_ATTR_RW(hbm); +static DEVICE_ATTR_RW(op_friginer_print_hbm); +static DEVICE_ATTR_RW(aod); +static DEVICE_ATTR(force_screenfp, S_IRUGO | S_IWUSR, oneplus_display_get_forcescreenfp, oneplus_display_set_forcescreenfp); +static DEVICE_ATTR(notify_fppress, S_IRUGO | S_IWUSR, NULL, oneplus_display_notify_fp_press); +static DEVICE_ATTR(dim_alpha, S_IRUGO | S_IWUSR, oneplus_display_get_dim_alpha, oneplus_display_set_dim_alpha); +static DEVICE_ATTR(notify_dim, S_IRUGO | S_IWUSR, NULL, oneplus_display_notify_dim); +static DEVICE_ATTR(notify_aod, S_IRUGO | S_IWUSR, NULL, oneplus_display_notify_aod_hid); +static DEVICE_ATTR(auth_status, S_IRUGO | S_IWUSR, op_display_get_auth_status, op_display_set_auth_status); +static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, op_display_get_power_status, op_display_set_power_status); +static DEVICE_ATTR(dimlayer_bl_en, S_IRUGO | S_IWUSR, op_display_get_dimlayer_enable, op_display_set_dimlayer_enable); +static DEVICE_ATTR(dp_en, S_IRUGO | S_IWUSR, op_display_get_dp_enable, op_display_set_dp_enable); static struct attribute *connector_dev_attrs[] = { &dev_attr_status.attr, &dev_attr_enabled.attr, &dev_attr_dpms.attr, &dev_attr_modes.attr, + &dev_attr_hbm.attr, + &dev_attr_op_friginer_print_hbm.attr, + &dev_attr_aod.attr, + &dev_attr_force_screenfp.attr, + &dev_attr_dim_alpha.attr, + &dev_attr_notify_fppress.attr, + &dev_attr_notify_dim.attr, + &dev_attr_notify_aod.attr, + &dev_attr_auth_status.attr, + &dev_attr_power_status.attr, + &dev_attr_dimlayer_bl_en.attr, + &dev_attr_dp_en.attr, NULL }; diff --git a/drivers/gpu/msm/adreno_coresight.c b/drivers/gpu/msm/adreno_coresight.c index 5a4d93545c23..4c0e90db086d 100644 --- a/drivers/gpu/msm/adreno_coresight.c +++ b/drivers/gpu/msm/adreno_coresight.c @@ -393,6 +393,7 @@ static const struct coresight_ops adreno_coresight_ops = { void adreno_coresight_remove(struct adreno_device *adreno_dev) { +#ifdef CONFIG_CORESIGHT int i, adreno_dev_flag = -EINVAL; for (i = 0; i < GPU_CORESIGHT_MAX; ++i) { @@ -408,10 +409,14 @@ void adreno_coresight_remove(struct adreno_device *adreno_dev) adreno_dev->csdev[i] = NULL; } } +#else + return; +#endif } int adreno_coresight_init(struct adreno_device *adreno_dev) { +#ifdef CONFIG_CORESIGHT int ret = 0; struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -464,4 +469,7 @@ int adreno_coresight_init(struct adreno_device *adreno_dev) of_node_put(node); return ret; +#else + return 0; +#endif } diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index d5f06dffb7de..d80e3ac9cda0 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -989,6 +989,10 @@ static const struct adc_channels adc_chans_rev2[ADC_MAX_CHANNEL] = { SCALE_HW_CALIB_THERM_100K_PULLUP) [ANA_IN] = ADC_CHAN_TEMP("drax_temp", 1, SCALE_HW_CALIB_PMIC_THERM) + [ADC_AMUX_THM4_PU2] = ADC_CHAN_TEMP("pa1_therm", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [VADC_LR_MUX7_HW_ID] = ADC_CHAN_VOLT("gpio12_v", 1, + SCALE_HW_CALIB_DEFAULT) }; static int adc_get_dt_channel_data(struct adc_chip *adc, diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c index d27246cb3cb6..e3b832139fe9 100644 --- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -353,9 +353,13 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc) { struct vadc_channel_prop *prop; u16 read_1, read_2; + int ratiometric_range = VADC_RATIOMETRIC_RANGE; int ret; - vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; + if (of_machine_is_compatible("qcom,sdm845")) + ratiometric_range = VADC_RATIOMETRIC_RANGE_8998; + + vadc->graph[VADC_CALIB_RATIOMETRIC].dx = ratiometric_range; vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; prop = vadc_get_channel(vadc, VADC_REF_1250MV); diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h index e0ff98bf5f15..d313b9b53b0b 100644 --- a/drivers/iio/adc/qcom-vadc-common.h +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -16,6 +16,7 @@ #define VADC_ABSOLUTE_RANGE_UV 625000 #define VADC_RATIOMETRIC_RANGE 1800 +#define VADC_RATIOMETRIC_RANGE_8998 1875 #define VADC_DEF_PRESCALING 0 /* 1:1 */ #define VADC_DEF_DECIMATION 0 /* 512 */ diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index 56918e9ef768..fbd44239815a 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -191,44 +191,6 @@ struct pon_regulator { bool enabled; }; -struct qpnp_pon { - struct device *dev; - 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; - u16 base; - u8 subtype; - u8 pon_ver; - u8 warm_reset_reason1; - u8 warm_reset_reason2; - int num_pon_config; - int num_pon_reg; - int pon_trigger_reason; - int pon_power_off_reason; - u32 dbc_time_us; - u32 uvlo; - int warm_reset_poff_type; - int hard_reset_poff_type; - int shutdown_poff_type; - int resin_warm_reset_type; - int resin_hard_reset_type; - int resin_shutdown_type; - bool is_spon; - bool store_hard_reset_reason; - bool resin_hard_reset_disable; - bool resin_shutdown_disable; - bool ps_hold_hard_reset_disable; - bool ps_hold_shutdown_disable; - bool kpdpwr_dbc_enable; - bool resin_pon_reset; - ktime_t kpdpwr_last_release_time; - bool log_kpd_event; -}; - static int pon_ship_mode_en; module_param_named( ship_mode_en, pon_ship_mode_en, int, 0600 diff --git a/drivers/input/misc/qti-haptics.c b/drivers/input/misc/qti-haptics.c index dac72b3e9e46..ad0aaf41307b 100644 --- a/drivers/input/misc/qti-haptics.c +++ b/drivers/input/misc/qti-haptics.c @@ -22,6 +22,7 @@ #include #include #include +#include enum actutor_type { ACT_LRA, @@ -33,11 +34,28 @@ enum lra_res_sig_shape { RES_SIG_SQUARE, }; +enum hap_auto_res_mode { + HAP_AUTO_RES_NONE, + HAP_AUTO_RES_ZXD, + HAP_AUTO_RES_QWD, + HAP_AUTO_RES_MAX_QWD, + HAP_AUTO_RES_ZXD_EOP, +}; + enum lra_auto_res_mode { AUTO_RES_MODE_ZXD, AUTO_RES_MODE_QWD, }; + +/* high Z option lines */ +enum hap_high_z { + HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */ + HAP_LRA_HIGH_Z_OPT1, + HAP_LRA_HIGH_Z_OPT2, + HAP_LRA_HIGH_Z_OPT3, +}; + enum wf_src { INT_WF_VMAX, INT_WF_BUFFER, @@ -111,6 +129,16 @@ enum haptics_custom_effect_param { #define HAP_WF_SOURCE_AUDIO (2 << HAP_WF_SOURCE_SHIFT) #define HAP_WF_SOURCE_PWM (3 << HAP_WF_SOURCE_SHIFT) +/* For pmi8998 */ +#define LRA_AUTO_RES_MODE_MASK GENMASK(6, 4) +#define LRA_AUTO_RES_MODE_SHIFT 4 +#define LRA_HIGH_Z_MASK GENMASK(3, 2) +#define LRA_HIGH_Z_SHIFT 2 +#define LRA_RES_CAL_MASK GENMASK(1, 0) +#define HAP_RES_CAL_PERIOD_MIN 4 +#define HAP_RES_CAL_PERIOD_MAX 32 + +/* For pm660 */ #define REG_HAP_AUTO_RES_CFG 0x4F #define HAP_AUTO_RES_MODE_BIT BIT(7) #define HAP_AUTO_RES_MODE_SHIFT 7 @@ -193,7 +221,7 @@ struct qti_hap_play_info { struct qti_hap_config { enum actutor_type act_type; enum lra_res_sig_shape lra_shape; - enum lra_auto_res_mode lra_auto_res_mode; + u8 lra_auto_res_mode; u16 vmax_mv; u16 play_rate_us; bool lra_allow_variable_play_rate; @@ -203,6 +231,7 @@ struct qti_hap_chip { struct platform_device *pdev; struct device *dev; struct regmap *regmap; + struct pmic_revid_data *revid; struct input_dev *input_dev; struct qti_hap_config config; struct qti_hap_play_info play; @@ -852,6 +881,8 @@ static int qti_haptics_upload_effect(struct input_dev *dev, if (hrtimer_active(&chip->hap_disable_timer)) { rem = hrtimer_get_remaining(&chip->hap_disable_timer); time_us = ktime_to_us(rem); + if (time_us <= 0) + time_us = 100; dev_dbg(chip->dev, "waiting for playing clear sequence: %lld us\n", time_us); usleep_range(time_us, time_us + 100); @@ -1124,10 +1155,17 @@ static int qti_haptics_hw_init(struct qti_hap_chip *chip) return rc; } - addr = REG_HAP_AUTO_RES_CFG; - mask = HAP_AUTO_RES_MODE_BIT | HAP_CAL_EOP_EN_BIT | HAP_CAL_PERIOD_MASK; - val = config->lra_auto_res_mode << HAP_AUTO_RES_MODE_SHIFT; - val |= HAP_CAL_EOP_EN_BIT | HAP_CAL_OPT3_EVERY_8_PERIOD; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + addr = REG_HAP_AUTO_RES_CFG; + mask = HAP_AUTO_RES_MODE_BIT | HAP_CAL_EOP_EN_BIT | HAP_CAL_PERIOD_MASK; + val = config->lra_auto_res_mode << HAP_AUTO_RES_MODE_SHIFT; + val |= HAP_CAL_EOP_EN_BIT | HAP_CAL_OPT3_EVERY_8_PERIOD; + } else { + addr = REG_HAP_AUTO_RES_CFG; + mask = LRA_AUTO_RES_MODE_MASK | LRA_HIGH_Z_MASK | LRA_RES_CAL_MASK; + val |= (config->lra_auto_res_mode << LRA_AUTO_RES_MODE_SHIFT); + val |= (HAP_LRA_HIGH_Z_OPT3 << LRA_HIGH_Z_SHIFT); + } rc = qti_haptics_masked_write(chip, addr, mask, val); if (rc < 0) { dev_err(chip->dev, "set AUTO_RES_CFG failed, rc=%d\n", rc); @@ -1392,14 +1430,33 @@ static int qti_haptics_lra_parse_dt(struct qti_hap_chip *chip) rc = of_property_read_string(node, "qcom,lra-auto-resonance-mode", &str); if (!rc) { - if (strcmp(str, "zxd") == 0) { - config->lra_auto_res_mode = AUTO_RES_MODE_ZXD; - } else if (strcmp(str, "qwd") == 0) { - config->lra_auto_res_mode = AUTO_RES_MODE_QWD; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + config->lra_auto_res_mode = + AUTO_RES_MODE_QWD; + if (strcmp(str, "zxd") == 0) + config->lra_auto_res_mode = + AUTO_RES_MODE_ZXD; + else if (strcmp(str, "qwd") == 0) + config->lra_auto_res_mode = + AUTO_RES_MODE_QWD; } else { - dev_err(chip->dev, "Invalid auto resonance mode: %s\n", - str); - return -EINVAL; + config->lra_auto_res_mode = + HAP_AUTO_RES_ZXD_EOP; + if (strcmp(str, "none") == 0) + config->lra_auto_res_mode = + HAP_AUTO_RES_NONE; + else if (strcmp(str, "zxd") == 0) + config->lra_auto_res_mode = + HAP_AUTO_RES_ZXD; + else if (strcmp(str, "qwd") == 0) + config->lra_auto_res_mode = + HAP_AUTO_RES_QWD; + else if (strcmp(str, "max-qwd") == 0) + config->lra_auto_res_mode = + HAP_AUTO_RES_MAX_QWD; + else + config->lra_auto_res_mode = + HAP_AUTO_RES_ZXD_EOP; } } @@ -1410,6 +1467,7 @@ static int qti_haptics_parse_dt(struct qti_hap_chip *chip) { struct qti_hap_config *config = &chip->config; const struct device_node *node = chip->dev->of_node; + struct device_node *revid_node; const char *str; int rc = 0, tmp; @@ -1420,6 +1478,25 @@ static int qti_haptics_parse_dt(struct qti_hap_chip *chip) } chip->reg_base = (u16)tmp; + revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); + if (!revid_node) { + pr_err("Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + chip->revid = get_revid_data(revid_node); + of_node_put(revid_node); + if (IS_ERR_OR_NULL(chip->revid)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(chip->revid)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + chip->sc_irq = platform_get_irq_byname(chip->pdev, "hap-sc-irq"); if (chip->sc_irq < 0) { dev_err(chip->dev, "Failed to get hap-sc-irq\n"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3ee51c5d49a7..aca45775154a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1350,6 +1350,12 @@ config TOUCHSCREEN_SYNAPTICS_TCM To compile this driver as a module, choose M here: the module will be called synaptics_tcm. +config TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI + tristate "Synaptics s3320 i2c touchscreen" + depends on I2C + help + This enables support for Synaptics s1302 RMI over I2C based touch key. + source "drivers/input/touchscreen/synaptics_tcm/Kconfig" source "drivers/input/touchscreen/focaltech_touch/Kconfig" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 8b766e754948..7af7aa4c6ab4 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -114,3 +114,6 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM) += synaptics_tcm/ obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/ obj-$(CONFIG_TOUCHSCREEN_NT36XXX) += nt36xxx/ +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_SYNAPTICS_S3320_I2C_RMI) += synaptics_dsx_fw_update.o diff --git a/drivers/input/touchscreen/fw_update_v7.if b/drivers/input/touchscreen/fw_update_v7.if new file mode 100644 index 000000000000..b853089124e1 --- /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 = 0; + + 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..8f74826fc710 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_baseline.h @@ -0,0 +1,362 @@ +#ifndef SYNAPTICS_BASELINE_H +#define SYNAPTICS_BASELINE_H + +#define TX_NUMBER (17) +#define TX_17801_NUMBER (15) +#define RX_NUMBER (30) + +#define TX_17819_NUMBER (16) +#define RX_17819_NUMBER (33) + +/*17819 s3706 TX 16 RX 33*/ +const int16_t baseline_cap_17819_data[2][TX_17819_NUMBER][RX_17819_NUMBER*2] = { +//enable cbc Notice it is opposite to the xls format. +{ + {386, 901, 618, 1441, 645, 1506, 651, 1519, 642, 1498, 638, 1489, 641, 1495, 643, 1500, 649, 1514, 637, 1485, 642, 1499, 649, 1515, 641, 1496, 648, 1512, 648, 1513, 644, 1503, 644, 1502, 644, 1502, 647, 1509, 653, 1525, 652, 1522, 658, 1535, 680, 1586, 667, 1556, 659, 1539, 663, 1547, 651, 1520, 662, 1545, 668, 1558, 663, 1546, 660, 1540, 665, 1551, 630, 1471}, + {665, 1553, 667, 1555, 669, 1560, 676, 1576, 665, 1552, 662, 1544, 664, 1549, 667, 1557, 672, 1567, 663, 1546, 666, 1553, 673, 1570, 668, 1559, 676, 1577, 671, 1566, 667, 1556, 672, 1568, 670, 1563, 669, 1562, 676, 1577, 679, 1583, 676, 1576, 681, 1588, 685, 1598, 713, 1664, 686, 1601, 676, 1578, 687, 1602, 688, 1605, 688, 1605, 681, 1589, 691, 1613, 695, 1622}, + {650, 1517, 662, 1545, 663, 1548, 671, 1565, 660, 1539, 656, 1531, 658, 1536, 661, 1543, 667, 1556, 657, 1533, 661, 1542, 668, 1559, 663, 1546, 670, 1564, 666, 1555, 662, 1544, 666, 1553, 665, 1551, 665, 1551, 671, 1566, 674, 1573, 704, 1643, 676, 1577, 678, 1582, 675, 1576, 679, 1585, 671, 1565, 681, 1590, 682, 1591, 683, 1593, 675, 1575, 686, 1601, 687, 1602}, + {644, 1503, 660, 1539, 661, 1543, 668, 1559, 657, 1532, 654, 1525, 656, 1530, 658, 1535, 664, 1549, 654, 1526, 658, 1536, 664, 1549, 660, 1540, 667, 1557, 663, 1547, 658, 1536, 663, 1546, 661, 1543, 662, 1544, 702, 1639, 670, 1564, 667, 1557, 672, 1567, 675, 1575, 673, 1571, 676, 1577, 667, 1556, 678, 1582, 679, 1584, 705, 1646, 672, 1569, 683, 1593, 683, 1594}, + {644, 1503, 659, 1538, 660, 1540, 667, 1555, 656, 1530, 653, 1523, 654, 1527, 658, 1534, 699, 1631, 653, 1525, 656, 1531, 663, 1547, 659, 1538, 667, 1556, 661, 1543, 658, 1534, 662, 1544, 660, 1540, 660, 1540, 667, 1555, 669, 1561, 665, 1553, 670, 1564, 673, 1571, 671, 1565, 674, 1573, 665, 1553, 677, 1579, 677, 1580, 677, 1579, 670, 1564, 681, 1588, 680, 1587}, + {643, 1501, 685, 1597, 659, 1537, 666, 1554, 655, 1529, 687, 1602, 654, 1526, 656, 1531, 662, 1545, 652, 1522, 656, 1530, 663, 1546, 658, 1534, 665, 1552, 661, 1543, 656, 1530, 660, 1540, 659, 1537, 658, 1536, 665, 1552, 667, 1557, 664, 1549, 668, 1559, 671, 1567, 669, 1562, 672, 1568, 664, 1550, 674, 1573, 676, 1577, 675, 1576, 669, 1560, 680, 1586, 678, 1582}, + {583, 1360, 657, 1534, 660, 1539, 666, 1554, 655, 1529, 651, 1520, 655, 1528, 656, 1532, 662, 1545, 652, 1522, 657, 1532, 663, 1546, 658, 1536, 665, 1551, 662, 1544, 693, 1616, 661, 1542, 659, 1538, 659, 1538, 666, 1554, 668, 1558, 664, 1550, 668, 1559, 671, 1566, 669, 1561, 672, 1568, 663, 1548, 675, 1575, 675, 1576, 675, 1576, 669, 1561, 679, 1584, 677, 1580}, + {398, 930, 637, 1486, 659, 1539, 667, 1556, 691, 1613, 652, 1522, 655, 1527, 656, 1532, 663, 1546, 652, 1522, 657, 1532, 663, 1546, 658, 1536, 665, 1552, 662, 1544, 657, 1532, 661, 1542, 659, 1538, 658, 1535, 666, 1554, 666, 1554, 664, 1549, 668, 1559, 671, 1567, 669, 1560, 672, 1568, 664, 1548, 674, 1574, 675, 1575, 674, 1573, 668, 1559, 678, 1582, 676, 1576}, + {416, 970, 645, 1505, 658, 1536, 665, 1551, 654, 1526, 651, 1519, 653, 1524, 656, 1530, 661, 1543, 651, 1519, 655, 1529, 662, 1545, 657, 1532, 665, 1551, 660, 1541, 656, 1531, 660, 1539, 659, 1537, 658, 1535, 665, 1552, 666, 1554, 664, 1548, 668, 1558, 706, 1647, 668, 1559, 671, 1567, 663, 1547, 673, 1571, 674, 1573, 674, 1573, 668, 1558, 679, 1583, 672, 1568}, + {603, 1406, 657, 1534, 658, 1534, 664, 1548, 654, 1525, 650, 1516, 652, 1520, 656, 1530, 660, 1541, 651, 1520, 655, 1528, 697, 1627, 657, 1533, 665, 1551, 661, 1541, 655, 1528, 660, 1539, 658, 1534, 658, 1534, 665, 1551, 665, 1552, 663, 1547, 667, 1557, 670, 1564, 668, 1560, 670, 1564, 663, 1546, 673, 1570, 674, 1572, 674, 1573, 667, 1556, 678, 1583, 667, 1556}, + {659, 1538, 658, 1535, 657, 1533, 665, 1551, 653, 1525, 651, 1518, 652, 1521, 656, 1530, 660, 1541, 651, 1518, 655, 1528, 661, 1542, 657, 1533, 664, 1549, 660, 1539, 655, 1529, 659, 1539, 658, 1535, 658, 1535, 665, 1552, 666, 1554, 663, 1547, 668, 1558, 671, 1565, 668, 1559, 707, 1650, 662, 1544, 673, 1570, 674, 1573, 672, 1569, 667, 1556, 678, 1581, 672, 1569}, + {659, 1539, 658, 1535, 658, 1534, 665, 1551, 653, 1524, 650, 1516, 653, 1524, 655, 1528, 660, 1541, 651, 1518, 655, 1528, 662, 1545, 656, 1532, 664, 1550, 660, 1540, 655, 1529, 659, 1538, 658, 1535, 695, 1622, 665, 1553, 667, 1555, 663, 1546, 667, 1557, 671, 1565, 668, 1559, 671, 1567, 663, 1547, 673, 1570, 674, 1572, 674, 1573, 667, 1557, 677, 1580, 671, 1566}, + {661, 1542, 659, 1537, 658, 1536, 665, 1552, 654, 1525, 651, 1518, 653, 1523, 656, 1530, 661, 1543, 651, 1520, 692, 1615, 662, 1545, 658, 1534, 665, 1552, 661, 1543, 656, 1531, 660, 1540, 659, 1538, 660, 1539, 667, 1555, 668, 1559, 665, 1552, 670, 1563, 673, 1571, 672, 1568, 675, 1575, 667, 1556, 678, 1582, 680, 1586, 680, 1587, 674, 1572, 685, 1599, 680, 1586}, + {666, 1555, 662, 1546, 662, 1544, 668, 1560, 657, 1534, 654, 1526, 657, 1533, 696, 1624, 666, 1553, 656, 1531, 660, 1541, 666, 1555, 662, 1546, 669, 1562, 665, 1552, 661, 1542, 665, 1551, 663, 1548, 663, 1547, 670, 1564, 671, 1566, 668, 1559, 672, 1568, 676, 1577, 673, 1570, 676, 1578, 669, 1560, 679, 1585, 680, 1587, 681, 1589, 674, 1573, 686, 1600, 681, 1589}, + {659, 1537, 663, 1546, 662, 1545, 668, 1559, 658, 1535, 654, 1526, 692, 1615, 659, 1538, 665, 1553, 655, 1528, 659, 1539, 666, 1554, 661, 1542, 669, 1560, 665, 1551, 661, 1541, 664, 1549, 663, 1547, 663, 1547, 670, 1563, 672, 1568, 668, 1559, 673, 1570, 676, 1578, 674, 1573, 677, 1579, 669, 1561, 679, 1584, 681, 1590, 681, 1589, 675, 1576, 687, 1602, 679, 1585}, + {575, 1343, 672, 1567, 677, 1579, 679, 1584, 665, 1552, 659, 1538, 660, 1539, 660, 1540, 664, 1549, 652, 1522, 656, 1530, 661, 1542, 656, 1530, 662, 1546, 694, 1619, 652, 1522, 656, 1530, 654, 1526, 653, 1524, 660, 1539, 660, 1541, 657, 1533, 661, 1543, 664, 1549, 661, 1542, 663, 1548, 655, 1528, 666, 1554, 668, 1558, 668, 1558, 663, 1548, 678, 1582, 651, 1520}, +}, +/*disable cbc*/ +{ + {397, 927, 628, 1466, 653, 1524, 660, 1539, 649, 1513, 647, 1509, 650, 1517, 651, 1518, 657, 1532, 646, 1506, 652, 1520, 657, 1533, 652, 1521, 659, 1539, 658, 1536, 651, 1519, 655, 1528, 654, 1526, 654, 1526, 662, 1545, 664, 1549, 665, 1552, 677, 1579, 674, 1572, 668, 1558, 672, 1567, 662, 1545, 673, 1569, 677, 1581, 675, 1575, 669, 1560, 672, 1567, 637, 1486}, + {672, 1568, 673, 1570, 675, 1575, 682, 1592, 671, 1566, 668, 1558, 671, 1567, 672, 1569, 678, 1583, 668, 1558, 672, 1569, 680, 1586, 674, 1572, 682, 1590, 678, 1583, 673, 1570, 678, 1582, 676, 1577, 676, 1577, 683, 1594, 684, 1597, 682, 1591, 687, 1603, 691, 1613, 708, 1652, 693, 1617, 683, 1594, 694, 1618, 695, 1621, 695, 1621, 688, 1606, 696, 1625, 701, 1635}, + {656, 1531, 668, 1559, 670, 1564, 678, 1582, 666, 1553, 664, 1549, 666, 1554, 668, 1558, 674, 1573, 664, 1548, 668, 1559, 675, 1575, 669, 1561, 678, 1581, 673, 1570, 669, 1560, 673, 1571, 671, 1566, 671, 1566, 678, 1582, 681, 1590, 699, 1630, 683, 1595, 686, 1600, 682, 1591, 686, 1602, 677, 1580, 688, 1606, 690, 1609, 690, 1611, 683, 1594, 692, 1615, 693, 1616}, + {653, 1523, 667, 1557, 670, 1562, 677, 1579, 665, 1552, 662, 1545, 665, 1552, 666, 1553, 672, 1569, 662, 1545, 667, 1555, 673, 1569, 668, 1559, 676, 1576, 672, 1568, 666, 1555, 671, 1566, 670, 1563, 670, 1564, 699, 1630, 679, 1584, 675, 1575, 680, 1587, 683, 1594, 680, 1587, 684, 1595, 676, 1577, 686, 1600, 687, 1604, 714, 1666, 681, 1589, 689, 1608, 689, 1608}, + {652, 1521, 668, 1558, 668, 1558, 675, 1574, 663, 1547, 661, 1541, 662, 1546, 666, 1554, 694, 1618, 662, 1544, 665, 1551, 671, 1567, 667, 1555, 674, 1572, 670, 1563, 665, 1551, 670, 1563, 668, 1559, 668, 1558, 675, 1576, 676, 1577, 673, 1570, 677, 1580, 682, 1590, 678, 1581, 682, 1591, 674, 1572, 684, 1596, 684, 1596, 685, 1599, 679, 1584, 687, 1602, 686, 1602}, + {650, 1518, 691, 1612, 666, 1553, 672, 1569, 662, 1546, 682, 1592, 662, 1545, 662, 1546, 668, 1560, 659, 1537, 664, 1548, 669, 1562, 665, 1551, 672, 1567, 668, 1559, 662, 1546, 667, 1557, 665, 1553, 665, 1552, 672, 1568, 674, 1574, 671, 1565, 676, 1577, 679, 1583, 676, 1576, 679, 1584, 672, 1567, 681, 1590, 683, 1594, 683, 1593, 676, 1578, 685, 1598, 684, 1595}, + {590, 1376, 664, 1549, 666, 1555, 673, 1569, 661, 1543, 659, 1537, 662, 1544, 662, 1545, 668, 1560, 658, 1536, 663, 1548, 670, 1563, 665, 1551, 672, 1569, 669, 1561, 688, 1606, 668, 1558, 665, 1553, 665, 1552, 672, 1569, 674, 1574, 671, 1566, 675, 1575, 678, 1583, 676, 1576, 679, 1584, 671, 1566, 682, 1590, 682, 1591, 682, 1592, 676, 1576, 685, 1598, 683, 1594}, + {406, 947, 644, 1502, 666, 1554, 674, 1572, 686, 1600, 660, 1540, 662, 1544, 663, 1546, 669, 1561, 659, 1538, 663, 1548, 670, 1563, 665, 1552, 673, 1570, 669, 1560, 664, 1549, 668, 1558, 667, 1556, 665, 1552, 673, 1570, 674, 1572, 671, 1566, 675, 1575, 678, 1582, 676, 1577, 679, 1584, 672, 1567, 681, 1588, 682, 1590, 683, 1593, 676, 1576, 684, 1597, 682, 1591}, + {423, 988, 653, 1524, 665, 1552, 672, 1569, 661, 1542, 659, 1537, 662, 1544, 662, 1544, 668, 1560, 659, 1537, 663, 1547, 669, 1562, 665, 1552, 672, 1569, 668, 1559, 663, 1547, 668, 1559, 666, 1553, 666, 1554, 673, 1570, 674, 1572, 671, 1566, 676, 1578, 704, 1642, 675, 1576, 679, 1584, 671, 1565, 681, 1589, 681, 1590, 682, 1591, 676, 1577, 684, 1596, 680, 1586}, + {611, 1426, 666, 1553, 666, 1553, 672, 1569, 661, 1542, 659, 1537, 661, 1542, 662, 1545, 669, 1561, 659, 1537, 663, 1547, 696, 1623, 665, 1552, 673, 1570, 669, 1560, 663, 1547, 668, 1558, 666, 1553, 665, 1552, 673, 1570, 673, 1569, 671, 1566, 675, 1575, 679, 1584, 676, 1576, 679, 1585, 671, 1566, 680, 1588, 681, 1590, 682, 1592, 675, 1576, 685, 1598, 673, 1571}, + {667, 1556, 665, 1552, 665, 1551, 672, 1567, 660, 1539, 657, 1534, 661, 1541, 661, 1542, 668, 1558, 658, 1535, 662, 1546, 669, 1561, 664, 1549, 671, 1567, 667, 1557, 662, 1545, 667, 1556, 665, 1553, 664, 1550, 672, 1568, 673, 1570, 670, 1564, 675, 1575, 678, 1581, 675, 1575, 705, 1645, 670, 1563, 680, 1586, 681, 1589, 682, 1591, 675, 1574, 683, 1594, 679, 1584}, + {667, 1556, 665, 1552, 665, 1552, 672, 1569, 661, 1542, 658, 1535, 660, 1541, 662, 1545, 668, 1558, 658, 1536, 663, 1547, 670, 1563, 665, 1551, 672, 1569, 668, 1559, 663, 1547, 667, 1556, 666, 1555, 691, 1612, 672, 1569, 674, 1573, 671, 1566, 675, 1576, 678, 1582, 675, 1576, 679, 1583, 671, 1566, 680, 1588, 682, 1591, 682, 1592, 675, 1575, 684, 1596, 678, 1581}, + {669, 1562, 667, 1556, 666, 1553, 673, 1570, 661, 1543, 659, 1537, 662, 1544, 663, 1548, 669, 1560, 659, 1539, 689, 1608, 671, 1565, 665, 1552, 673, 1570, 670, 1562, 665, 1551, 669, 1562, 668, 1558, 666, 1555, 675, 1575, 676, 1577, 674, 1572, 678, 1581, 681, 1590, 678, 1583, 683, 1594, 675, 1575, 686, 1600, 688, 1606, 688, 1606, 682, 1591, 692, 1614, 687, 1602}, + {673, 1570, 668, 1558, 668, 1558, 674, 1573, 663, 1547, 661, 1542, 664, 1550, 690, 1611, 673, 1570, 663, 1547, 668, 1558, 674, 1572, 668, 1558, 677, 1579, 673, 1569, 667, 1557, 672, 1568, 670, 1564, 669, 1562, 677, 1581, 679, 1584, 675, 1576, 680, 1587, 683, 1593, 679, 1585, 684, 1595, 675, 1576, 686, 1601, 688, 1606, 688, 1606, 682, 1591, 692, 1614, 687, 1603}, + {667, 1556, 671, 1565, 670, 1562, 676, 1577, 664, 1550, 663, 1547, 689, 1608, 667, 1556, 673, 1569, 662, 1546, 667, 1557, 674, 1573, 668, 1559, 677, 1579, 673, 1569, 668, 1558, 672, 1569, 671, 1566, 670, 1564, 678, 1582, 679, 1583, 676, 1577, 680, 1588, 684, 1596, 681, 1588, 685, 1598, 677, 1579, 688, 1604, 689, 1608, 689, 1609, 683, 1594, 693, 1618, 685, 1599}, + {582, 1359, 678, 1583, 684, 1596, 686, 1600, 671, 1567, 666, 1555, 667, 1557, 666, 1554, 671, 1566, 659, 1538, 663, 1548, 668, 1560, 662, 1546, 671, 1565, 690, 1610, 659, 1539, 663, 1548, 661, 1542, 660, 1540, 667, 1556, 668, 1558, 664, 1549, 668, 1559, 671, 1565, 667, 1556, 671, 1567, 663, 1547, 672, 1569, 674, 1574, 676, 1577, 671, 1565, 684, 1596, 658, 1535}, + +} +}; + +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 100644 index 000000000000..eba8ff2ceb11 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_driver_s3320.c @@ -0,0 +1,6429 @@ +/************************************************************************************ + ** 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: -------------------------------- + ** + ** morgan.gu@BSP.TP modified for oem 2017-10-30 s3706 tp_driver + ************************************************************************************/ +#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 + +/*modify by morgan.gu for sdm845 */ +#define CONFIG_MSM_RDM_NOTIFY +#undef CONFIG_FB + +#ifdef CONFIG_FB +#include +#include +#elif defined(CONFIG_MSM_RDM_NOTIFY) +#include +#include +#endif + +#include + +#include "synaptics_redremote.h" +#include +#include "synaptics_baseline.h" +#include "synaptics_dsx_core.h" +#include +#include +/*------------------------------------------------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; +}; + +struct fp_underscreen_info { + uint8_t touch_state; + uint8_t area_rate; + uint16_t x; + uint16_t y; +}; + +#define PM_QOS_VALUE_TP 400 +struct pm_qos_request pm_qos_req_tp; + +/******************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 FINGER_DOWN 0x0f +#define FINGER_UP 0x1f +#define SINGLE_TAP 0x10 + +// Gesture bit mask +#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) +#define BIT8 (0x1 << 8) +#define BIT8 (0x1 << 8) +#define BIT9 (0x1 << 9) +#define BITA (0x1 << 10) +#define BITB (0x1 << 11) +#define BITC (0x1 << 12) +#define BITD (0x1 << 13) +#define BITE (0x1 << 14) +#define BITF (0x1 << 15) + +// Gesture flags +#define GESTURE_NONE BIT0 +#define GESTURE_DOUBLE_TAP BIT1 // double tap +#define GESTURE_DOWN_ARROW BIT2 // V +#define GESTURE_UP_ARROW BIT3 // ^ +#define GESTURE_RIGHT_ARROW BIT4 // > +#define GESTURE_LEFT_ARROW BIT5 // < +#define GESTURE_CIRCLE BIT6 // O +#define GESTURE_DOUBLE_SWIPE BIT7 // || +#define GESTURE_RIGHT_SWIPE BIT8 // -> +#define GESTURE_LEFT_SWIPE BIT9 // <- +#define GESTURE_DOWN_SWIPE BITA // |v +#define GESTURE_UP_SWIPE BITB // |^ +#define GESTURE_M BITC // M +#define GESTURE_W BITD // W +#define GESTURE_S BITE // S +#define GESTURE_SINGLE_TAP BITF // single tap + +// Gesture key codes +#define KEY_GESTURE_W 246 // W +#define KEY_GESTURE_M 247 // M +#define KEY_GESTURE_S 248 // S +#define KEY_DOUBLE_TAP KEY_WAKEUP // 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_UP_ARROW 252 // draw up arrow to toggle flashlight +#define KEY_GESTURE_LEFT_ARROW 253 // draw left arrow for previous track +#define KEY_GESTURE_RIGHT_ARROW 254 // draw right arrow for next track +#define KEY_GESTURE_DOWN_ARROW 255 // draw down arrow to toggle flashlight +#define KEY_GESTURE_SWIPE_LEFT KEY_F5 +#define KEY_GESTURE_SWIPE_DOWN KEY_F6 +#define KEY_GESTURE_SWIPE_RIGHT KEY_F7 +#define KEY_GESTURE_SWIPE_UP KEY_F8 +#define KEY_GESTURE_SINGLE_TAP KEY_F9 + +int Enable_gesture =0; +static int gesture_switch = 0; +//ruanbanmao@BSP add for tp gesture 2015-05-06, end +#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 = 0; +static long int TP_FW; +static int tp_dev = 6; +unsigned int tp_debug; +static int button_map[3]; +static int tx_rx_num[2]; +static int16_t Rxdata[33][33];/*s3706 tx rx 16 33 s3508 tx rx 15 30*/ +static int16_t delta_baseline[33][33]; +static int16_t baseline[33][33]; +static int16_t delta[33][33]; +static int TX_NUM; +static int RX_NUM; +static int report_key_point_y = 0; +static int force_update = 0; +static int LCD_WIDTH ; +static int LCD_HEIGHT ; +static int get_tp_base = 0; +#define ENABLE_TPEDGE_LIMIT +#ifdef ENABLE_TPEDGE_LIMIT +static int F51_GRIP_CONFIGURATION; +static int limit_enable=1; +static void synaptics_tpedge_limitfunc(void); +#endif +//static int ch_getbase_status = 0; +struct timeval tpstart, tpend; +int not_getbase; +int need_reset; +int singer_touch_mun; +int recored_pointx[2] = {0, 0}; +int recored_pointy[2] = {0, 0}; + +#ifdef SUPPORT_TP_SLEEP_MODE +static int sleep_enable; +#endif +#ifdef SUPPORT_TP_TOUCHKEY +static int key_switch = 0; +static bool key_back_disable=false,key_appselect_disable=false; +#endif +static struct synaptics_ts_data *ts_g = NULL; +static struct workqueue_struct *synaptics_wq = NULL; +static struct workqueue_struct *synaptics_report = NULL; +static struct workqueue_struct *get_base_report = NULL; + + +#ifdef SUPPORT_GESTURE +static uint32_t clockwise; +static uint32_t gesture; + +/****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=0; +#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 + +/*------------------------------------------Fuction 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_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); +int tp_single_tap_en(struct synaptics_ts_data *ts, bool enable); +int gf_opticalfp_irq_handler(int event); + +#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); +#elif defined(CONFIG_MSM_RDM_NOTIFY) +static int msm_drm_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); +static void set_doze_time(int doze_time); +static int synaptics_soft_reset(struct synaptics_ts_data *ts); +static int touch_hold_en(struct synaptics_ts_data *ts, bool enable); + + +/*-------------------------------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 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 +}; + +//add by jiachenghui for boot time optimize 2015-5-13 +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()); + + //add by lifeng@bsp 2015-12-10 for only one cpu on line + for (i = 0; i < NR_CPUS; 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 != NR_CPUS?i:0,optimize_data.workqueue,&(optimize_data.work),msecs_to_jiffies(300)); + //end add by lifeng@bsp 2015-12-10 for only one cpu on line + + return probe_ret; +} +//end add by jiachenghui for boot time optimize 2015-5-13 + +static struct i2c_driver tpd_i2c_driver = { +//add by jiachenghui for boot time optimize 2015-5-13 + .probe = oem_synaptics_ts_probe, + .remove = synaptics_ts_remove, + .id_table = synaptics_ts_id, + .driver = { + // .owner = THIS_MODULE, + .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 support_1080x2340_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; + struct wakeup_source *source; + + 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; +#elif defined(CONFIG_MSM_RDM_NOTIFY) + struct notifier_block msm_drm_notif; +#endif + /******gesture*******/ + uint32_t gestures_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[20]; + char manu_name[30]; +#ifdef SUPPORT_VIRTUAL_KEY + struct kobject *properties_kobj; +#endif + uint8_t fp_up_down; + unsigned int en_up_down; + unsigned int fp_aod_cnt; + unsigned int unlock_succes; + int project_version; +}; + +static struct device_attribute attrs_oem[] = { + // __ATTR(baseline_test, 0664, synaptics_rmi4_baseline_show, NULL), + __ATTR(vendor_id, 0664, synaptics_rmi4_vendor_id_show, NULL), +}; + +static struct synaptics_rmi4_data *rmi4_data_s3706; + +static ssize_t fp_irq_get(struct device *device, + struct device_attribute *attribute, + char *buf) +{ + struct synaptics_ts_data *ts = ts_g; + + return scnprintf(buf, PAGE_SIZE, "%i\n", ts->fp_up_down); +} +static DEVICE_ATTR(fp_irq, 0400, fp_irq_get, NULL); + + +static void touch_enable (struct synaptics_ts_data *ts) +{ + spin_lock(&ts->lock); + if(0 == atomic_read(&ts->irq_enable)) + { + if(ts->irq) + enable_irq(ts->irq); + atomic_set(&ts->irq_enable,1); + //TPD_ERR("test %%%% enable irq\n"); + } + spin_unlock(&ts->lock); +} + +static void touch_disable(struct synaptics_ts_data *ts) +{ + spin_lock(&ts->lock); + if(1 == atomic_read(&ts->irq_enable)) + { + if(ts->irq) + disable_irq_nosync(ts->irq); + atomic_set(&ts->irq_enable,0); + //TPD_ERR("test ****************** disable irq\n"); + } + spin_unlock(&ts->lock); +} + +static int tpd_hw_pwron(struct synaptics_ts_data *ts) +{ + int rc = 0; + + /***enable the 2v8 power*****/ + if (!IS_ERR(ts->vdd_2v8)) { + //regulator_set_optimum_mode(ts->vdd_2v8,100000); + rc = regulator_enable(ts->vdd_2v8); + if(rc){ + dev_err(&ts->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + //return rc; + } + } + /*add for mrogan for 3V delay 10ms ,1.8v delay 10ms*/ + usleep_range(10*1000, 10*1000); + if( ts->v1p8_gpio > 0 ) { + TPD_DEBUG("synaptics:enable the v1p8_gpio\n"); + gpio_direction_output(ts->v1p8_gpio, 1); + } + //msleep(100); + + 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)) { + //regulator_set_optimum_mode(ts->vcc_i2c_1v8,100000); + rc = regulator_enable( ts->vcc_i2c_1v8 ); + if(rc) { + dev_err(&ts->client->dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); + //return 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) { + dev_err(&ts->client->dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); + return rc; + } + } + if( ts->v1p8_gpio > 0 ) { + TPD_DEBUG("synaptics: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) { + dev_err(&ts->client->dev, "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + } + if( ts->enable2v8_gpio > 0 ) { + TPD_DEBUG("synaptics: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("synaptics_read_register_map: failed for page select\n"); + return -1; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xDD, 4, &(buf[0x0])); + if( ret < 0 ){ + TPD_ERR("failed for page select!\n"); + return -1; + } + + 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; + if (version_is_s3508 == 2)/*s3706*/ + F12_2D_CTRL20 = F12_2D_CTRL_BASE + 0x06; + else + 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("synaptics_read_register_map: failed for page select\n"); + return -1; + } + 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_DEBUG("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("synaptics_read_register_map: failed for page select\n"); + return -1; + } + 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]; + //chenggang.li@BSP.TP modified for gesture + 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 -1; + } + ret = i2c_smbus_read_i2c_block_data( ts->client, F12_2D_CTRL20, 3, &(reportbuf[0x0]) ); + if( ret < 0 ) { + TPD_DEBUG("read reg F12_2D_CTRL20[0x%x] failed\n",F12_2D_CTRL20); + return -1; + } + + 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 -1; + } + gesture = GESTURE_NONE; + return 0; +} +#endif + +#ifdef SUPPORT_GLOVES_MODE +#define GLOVES_ADDR 0x001f //0x001D 0x001f +static int synaptics_glove_mode_enable(struct synaptics_ts_data *ts) +{ + int ret; + TPD_DEBUG("glove mode enable\n"); + /* page select = 0x4 */ + if( 1 == ts->glove_enable) { + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + if( ret < 0 ){ + TPD_DEBUG("i2c_smbus_write_byte_data failed for mode select\n"); + goto GLOVE_ENABLE_END; + } + ret = i2c_smbus_read_byte_data(ts->client, GLOVES_ADDR); + //TPDTM_DMESG("enable glove ret is %x ret|0x20 is %x\n", ret, ret|0x20); + ret = i2c_smbus_write_byte_data(ts->client, GLOVES_ADDR, ret | 0x01); + if( ret < 0 ){ + TPD_DEBUG("i2c_smbus_write_byte_data 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_smbus_write_byte_data 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_smbus_write_byte_data 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_smbus_write_byte_data 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( 1 == sleep_enable ){ + /*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_smbus_write_byte_data 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_smbus_write_byte_data 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("synaptics_read_product_id: failed for page select\n"); + return -1; + } + 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("synaptics_read_product_id: failed to read product info\n"); + return -1; + } + 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 -1; + } + /*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("synaptics_enable_interrupt: select page failed ret = %d\n", + ret); + return -1; + } + 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("synaptics_enable_interrupt :clear interrupt bits failed\n"); + return -1; + } + } 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: enable or disable abs \ + interrupt failed,abs_int =%d\n", __func__, abs_status_int); + return -1; + } + 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 void int_state(struct synaptics_ts_data *ts) +{ + int ret = -1; + ret = i2c_smbus_write_byte_data(ts->client, F01_RMI_CMD00, 0x01); + if(ret){ + TPD_ERR("%s:error cannot reset touch panel!\n",__func__); + return; + } + //delay_qt_ms(170); + delay_qt_ms(100); +#ifdef SUPPORT_GLOVES_MODE + synaptics_glove_mode_enable(ts); +#endif + ret = synaptics_init_panel(ts); + if( ret < 0 ){ + TPD_DEBUG("%s:error cannot change mode!\n",__func__); + return; + } + ret = synaptics_enable_interrupt(ts, 1); + if(ret){ + TPD_DEBUG("%s:error cannot enable interrupt!\n",__func__); + return; + } +} +*/ +/*Added for larger than 32 length read!*/ + +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__); + //rst_flag_counter = 1;//reset tp + retval = -5; + } else { + //rst_flag_counter = 0; + } + return retval; +} + +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[2]; + 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) { + //rst_flag_counter = 1;//rest tp + retval = -EIO; + } else { + //rst_flag_counter = 0; + } + 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; +} + +//chenggang.li@BSP.TP modified for oem 2014-08-05 gesture_judge +/***************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); + if (version_is_s3508 == 2) { + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA_BASE, + sizeof(coordinate_buf), + &(coordinate_buf[0])); + } else if (version_is_s3508 == 1 || version_is_s3508 == 0) { + 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 = GESTURE_NONE; + } + memcpy(coordinate_buf_last,coordinate_buf,sizeof(coordinate_buf)); +/* strcpy(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("synaptics TP read coordinate_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; // 1--clockwise, 0--anticlockwise, not circle, report 2 +} + +static int set_tp_info(struct synaptics_ts_data *ts, uint8_t up_down) +{ + int ret = 0; + uint8_t tp_infor[8] = {0}; + struct fp_underscreen_info tp_info; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x4); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA_BASE, + sizeof(tp_infor), + &(tp_infor[0])); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + TPD_DEBUG("%s:tp info %d %d %d %d\n", __func__, + tp_infor[0]|tp_infor[1]<<8,tp_infor[2]|tp_infor[3]<<8, + tp_infor[4]|tp_infor[5]<<8,tp_infor[6]|tp_infor[7]<<8); + tp_info.x = tp_infor[1]<<8|tp_infor[0]; + tp_info.y = tp_infor[3]<<8|tp_infor[2]; + tp_info.area_rate = tp_infor[5]<<8|tp_infor[4]; + tp_info.touch_state = up_down; + + return ret; +} + +static int recored_xy(struct synaptics_ts_data *ts, int x) +{ + int ret = 0; + uint8_t tp_info[8] = {0}; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x4); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA_BASE, + sizeof(tp_info), + &(tp_info[0])); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + TPD_DEBUG("%s:recored_xy %d %d %d %d\n", __func__, + tp_info[0]|tp_info[1]<<8, tp_info[2]|tp_info[3]<<8, + tp_info[4]|tp_info[5]<<8, tp_info[6]|tp_info[7]<<8); + recored_pointx[x] = tp_info[1]<<8|tp_info[0]; + recored_pointy[x] = tp_info[3]<<8|tp_info[2]; + TPD_DEBUG("%s:recored_xy[%d] x= %d y= %d\n", __func__, + x, recored_pointx[x], recored_pointy[x]); + + return ret; +} + +static void fp_detect(struct synaptics_ts_data *ts) +{ + int ret = 0, gesture_sign; + uint8_t gesture_buffer[10]; + + ret = i2c_smbus_write_byte_data( + ts->client, 0xff, 0x00); + ret = i2c_smbus_read_i2c_block_data( + ts->client, + 0x000A, 5, + &(gesture_buffer[0])); + + gesture_sign = gesture_buffer[0]; + switch (gesture_sign) { + case FINGER_DOWN: + ts->fp_up_down = 1; + sysfs_notify(&ts->dev->kobj, NULL, + dev_attr_fp_irq.attr.name); + TPD_DEBUG("%s:FINGER_DOWN %d\n", __func__, + ts->fp_up_down); + /*update tp info*/ + set_tp_info(ts, 1); + gf_opticalfp_irq_handler(1); + need_reset = 0; + break; + case FINGER_UP: + ts->fp_up_down = 0; + sysfs_notify(&ts->dev->kobj, NULL, + dev_attr_fp_irq.attr.name); + TPD_DEBUG("%s:FINGER_UP %d\n", __func__, + ts->fp_up_down); + /*update tp info*/ + set_tp_info(ts, 0); + gf_opticalfp_irq_handler(0); + if (ts->fp_aod_cnt > 0) + need_reset = 1; + not_getbase = 0; + ts->fp_aod_cnt = 0; + break; + } + ret = i2c_smbus_write_byte_data( + ts->client, 0xff, 0x00); +} + +static int time_recored(bool start_time) +{ + int timeuse = 0; + + if (start_time) { + do_gettimeofday(&tpstart); + } else { + do_gettimeofday(&tpend); + timeuse = 1000000 * (tpend.tv_sec-tpstart.tv_sec) + + tpend.tv_usec-tpstart.tv_usec; + TPD_DEBUG("Use time: %d uSeconds!!", timeuse); + } + if ((timeuse > 600000) || (timeuse < 0)) + return 0; + + return 1; +} + +static int double_tap(struct synaptics_ts_data *ts) +{ + int time_use = 0; + int ret = 0; + + if (singer_touch_mun == 0) { + time_recored(true); + recored_xy(ts, 0); + singer_touch_mun = 1; + } else { + time_use = time_recored(false); + recored_xy(ts, 1); + singer_touch_mun = 0; + if ((abs(recored_pointx[0] - recored_pointx[1]) < 100) + && (abs(recored_pointy[0] - recored_pointy[1]) < 120) + && (time_use)) { + ret = 1; + } else { + time_recored(true); + recored_xy(ts, 0); + singer_touch_mun = 1; + ret = -1; + } + } + + return ret; +} + + +static void gesture_judge(struct synaptics_ts_data *ts) +{ + unsigned int keyCode = 0; + int ret = 0, regswipe = 0; + uint8_t gesture_buffer[10]; + uint8_t gesture_sign = 0; + unsigned char reportbuf[3]; + uint8_t regswipe_s3706[25]; + int is_double_tap = 0; + + if (version_is_s3508 == 1) + F12_2D_DATA04 = 0x0008; + else if (version_is_s3508 == 2) /*s3706*/ + F12_2D_DATA04 = 0x000A; + 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); + if (version_is_s3508 == 2) { + ret = i2c_smbus_read_i2c_block_data( + ts->client, F51_CUSTOM_DATA_BASE, + sizeof(regswipe_s3706), &(regswipe_s3706[0])); + if (ret < 0) + TPDTM_DMESG("failed to transfer the data, ret = %d\n", ret); + } else if (version_is_s3508 == 1) + regswipe = i2c_smbus_read_byte_data(ts->client, + F51_CUSTOM_DATA04+0x18); + else + regswipe = i2c_smbus_read_byte_data(ts->client, + F51_CUSTOM_DATA04+0x18); + if (version_is_s3508 == 2) { + TPD_DEBUG("GestureType[0x%x]=[0x%x]\n", + F12_2D_DATA04, gesture_buffer[0]); + TPD_DEBUG("lpwgSwipeID[0x4%x] = [0x%x]\n", + F51_CUSTOM_DATA_BASE, regswipe_s3706[24]); + } else if (version_is_s3508 == 1) { + TPD_DEBUG("GestureType[0x%x]=[0x%x]\n", + F12_2D_DATA04, gesture_buffer[0]); + TPD_DEBUG("lpwgSwipeID[0x4%x] = [0x%x]\n", + (F51_CUSTOM_DATA04+0x18), regswipe); + } else + TPD_DEBUG("Gesture Type[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]; + + if (ts->project_version == 0x03) { + if (gesture_sign == SINGLE_TAP) { + is_double_tap = double_tap(ts); + if (is_double_tap == 1) { + gesture_sign = DTAP_DETECT; + } + } + } + //detect the gesture mode + switch (gesture_sign) { + case DTAP_DETECT: + gesture = GESTURE_DOUBLE_TAP; + break; + case SWIPE_DETECT: + if (version_is_s3508 == 2) { + gesture = + (regswipe_s3706[24] == 0x41) ? GESTURE_RIGHT_SWIPE : + (regswipe_s3706[24] == 0x42) ? GESTURE_LEFT_SWIPE : + (regswipe_s3706[24] == 0x44) ? GESTURE_DOWN_SWIPE : + (regswipe_s3706[24] == 0x48) ? GESTURE_UP_SWIPE : + (regswipe_s3706[24] == 0x80) ? GESTURE_DOUBLE_SWIPE : + GESTURE_NONE; + break; + } else if (version_is_s3508 == 1) { + gesture = (regswipe == 0x41) ? GESTURE_RIGHT_SWIPE : + (regswipe == 0x42) ? GESTURE_LEFT_SWIPE : + (regswipe == 0x44) ? GESTURE_DOWN_SWIPE : + (regswipe == 0x48) ? GESTURE_UP_SWIPE : + (regswipe == 0x80) ? GESTURE_DOUBLE_SWIPE : + GESTURE_NONE; + break; + }else{ + gesture = (regswipe == 0x41) ? GESTURE_RIGHT_SWIPE : + (regswipe == 0x42) ? GESTURE_LEFT_SWIPE : + (regswipe == 0x44) ? GESTURE_DOWN_SWIPE : + (regswipe == 0x48) ? GESTURE_UP_SWIPE : + (regswipe == 0x84) ? GESTURE_DOUBLE_SWIPE : + GESTURE_NONE; + break; + } + case CIRCLE_DETECT: + gesture = GESTURE_CIRCLE; + break; + case VEE_DETECT: + gesture = (gesture_buffer[2] == 0x01) ? GESTURE_UP_ARROW : + (gesture_buffer[2] == 0x02) ? GESTURE_DOWN_ARROW : + (gesture_buffer[2] == 0x04) ? GESTURE_LEFT_ARROW : + (gesture_buffer[2] == 0x08) ? GESTURE_RIGHT_ARROW : + GESTURE_NONE; + break; + case FINGER_DOWN: + if (ts->project_version == 0x03) { + ts->fp_up_down = 1; + ts->fp_aod_cnt = 1; + sysfs_notify(&ts->dev->kobj, NULL, + dev_attr_fp_irq.attr.name); + TPD_DEBUG("%s:FINGER_DOWN %d\n", __func__, + ts->fp_up_down); + /*update tp info*/ + set_tp_info(ts, 1); + gf_opticalfp_irq_handler(1); + not_getbase = 1; + need_reset = 0; + } + gesture = GESTURE_NONE; + break; + case FINGER_UP: + if (ts->project_version == 0x03) { + ts->fp_up_down = 0; + ts->fp_aod_cnt = 0; + sysfs_notify(&ts->dev->kobj, NULL, + dev_attr_fp_irq.attr.name); + TPD_DEBUG("%s:FINGER_UP %d\n", __func__, + ts->fp_up_down); + /*update tp info*/ + set_tp_info(ts, 0); + gf_opticalfp_irq_handler(0); + } + gesture = GESTURE_NONE; + break; + case SINGLE_TAP: + if (ts->project_version == 0x03) { + TPD_DEBUG("%s:SINGLE TAP\n", __func__); + gesture = GESTURE_SINGLE_TAP; + } + break; + case UNICODE_DETECT: + gesture = (gesture_buffer[2] == 0x77) ? GESTURE_W : + (gesture_buffer[2] == 0x6d) ? GESTURE_M : + (gesture_buffer[2] == 0x73) ? GESTURE_S : + GESTURE_NONE; + break; + default: + gesture = GESTURE_NONE; + break; + } + + // Get key code based on registered gesture. + switch (gesture) { + case GESTURE_DOUBLE_TAP: + keyCode = KEY_DOUBLE_TAP; + break; + case GESTURE_UP_ARROW: + keyCode = KEY_GESTURE_UP_ARROW; + break; + case GESTURE_DOWN_ARROW: + keyCode = KEY_GESTURE_DOWN_ARROW; + break; + case GESTURE_LEFT_ARROW: + keyCode = KEY_GESTURE_LEFT_ARROW; + break; + case GESTURE_RIGHT_ARROW: + keyCode = KEY_GESTURE_RIGHT_ARROW; + break; + case GESTURE_CIRCLE: + keyCode = KEY_GESTURE_CIRCLE; + break; + case GESTURE_DOUBLE_SWIPE: + keyCode = KEY_GESTURE_TWO_SWIPE; + break; + case GESTURE_LEFT_SWIPE: + keyCode = KEY_GESTURE_SWIPE_LEFT; + break; + case GESTURE_RIGHT_SWIPE: + keyCode = KEY_GESTURE_SWIPE_RIGHT; + break; + case GESTURE_UP_SWIPE: + keyCode = KEY_GESTURE_SWIPE_UP; + break; + case GESTURE_DOWN_SWIPE: + keyCode = KEY_GESTURE_SWIPE_DOWN; + break; + case GESTURE_W: + keyCode = KEY_GESTURE_W; + break; + case GESTURE_M: + keyCode = KEY_GESTURE_M; + break; + case GESTURE_S: + keyCode = KEY_GESTURE_S; + break; + case GESTURE_SINGLE_TAP: + keyCode = KEY_GESTURE_SINGLE_TAP; + break; + default: + break; + } + + if ((gesture != GESTURE_SINGLE_TAP) && (gesture != GESTURE_DOUBLE_TAP)) + synaptics_get_coordinate_point(ts); + + if((gesture & ts->gestures_enable) != 0){ + 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]) ); //enable gesture + if (ret < 0) + TPD_ERR("%s :Failed to write report buffer\n", __func__); + } + TPD_DEBUG("%s end!\n",__func__); +} +#endif +/***************end****************/ +static char prlog_count = 0; +#ifdef REPORT_2D_PRESSURE +static unsigned char pres_value = 1; +#endif +#ifdef SUPPORT_VIRTUAL_KEY //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system +//extern struct completion key_cm; +bool key_back_pressed = 0; +bool key_appselect_pressed = 0; +bool key_home_pressed =0; +extern bool virtual_key_enable; +#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 = 0; + uint8_t last_status = 0 ; +#ifdef SUPPORT_VIRTUAL_KEY //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system + bool key_appselect_check = false; + bool key_back_check = false; + bool key_home_check = false; + bool key_pressed = key_appselect_pressed || key_back_pressed;// || key_home_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 (0 == points.pressure)//workaround for have no pressure value input reader into hover mode + { + pres_value++; + if (255 == pres_value) + pres_value = 1; + } + else + { + pres_value = points.pressure; + } + } +#endif + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + if (version_is_s3508 == 1) + F12_2D_DATA15 = 0x0009; + else if (version_is_s3508 == 0) + F12_2D_DATA15 = 0x000C; + else if (version_is_s3508 == 2) + F12_2D_DATA15 = 0x000B;/*s3706 for 17819*/ + ret = synaptics_rmi4_i2c_read_block(ts->client, F12_2D_DATA15, 2, object_attention); + if (ret < 0) { + TPD_ERR("synaptics_int_touch 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("synaptics_int_touch 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 //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system + 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; + }else{ + //TPD_DEBUG(" finger %d with !finger_statue and no key match\n",i); + } + } + } +#endif + if (version_is_s3508 == 0){//for 15811 panel + 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); + //#ifdef REPORT_2D_W + 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 //WayneChang, 2015/12/02, add for key to abs, simulate key in abs through virtual key system + if(virtual_key_enable){ + // complete(&key_cm); + } +#endif + finger_num++; + finger_info |= 1 ; + //TPD_DEBUG("%s: Finger %d: status = 0x%02x " + //"x = %4d, y = %4d, wx = %2d, wy = %2d\n", + //__func__, i, points.status, points.x, points.y, points.raw_x, points.raw_y); + + } + } + + 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"); + if (ts->project_version == 0x03) { + if ((ts->unlock_succes == 1) && (need_reset ==1) && (ts->is_suspended == 0)) { + TPD_DEBUG("touch hold reset %d\n", need_reset); + need_reset = 0; + not_getbase = 0; + ts->unlock_succes = 0; + mutex_lock(&ts->mutex); + ret = synaptics_soft_reset(ts); + if (ret < 0) + TPD_ERR("%s reset failed\n", __func__); + mutex_unlock(&ts->mutex); + } + } + 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)){//all finger up do get base once + get_tp_base = 1; + TPD_ERR("start get base data:%d\n",get_tp_base); + if (!ts->en_up_down) + tp_baseline_get(ts, false); + } + +INT_TOUCH_END: + mutex_unlock(&ts->mutexreport); +} +static char log_count = 0; +#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 (1 == (++log_count % 4)) + 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)//back + { + 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)//menu + { + 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; + //input_sync(ts->input_dev); + 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; + } + 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; + + pm_qos_add_request(&pm_qos_req_tp, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_VALUE_TP); + if (atomic_read(&ts->is_stop) == 1) + { + touch_disable(ts); + goto EXIT; + } + + 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); + if (ts->is_suspended == 1 && ts->gestures_enable == 0) { + touch_disable(ts); + goto EXIT; + } + goto END; + } + status = ret & 0xff; + inte = (ret & 0x7f00)>>8; + //TPD_ERR("%s status[0x%x],inte[0x%x]\n",__func__,status,inte); + if(status & 0x80){ + TPD_DEBUG("enter reset tp status,and ts->in_gesture_mode is:%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->gestures_enable != 0)){ + synaptics_enable_interrupt_for_gesture(ts, 1); + if (ts->project_version == 0x03) { + mutex_lock(&ts->mutex); + tp_single_tap_en(ts, true); + if (ts->en_up_down) + touch_hold_en(ts, true); + mutex_unlock(&ts->mutex); + } + } + } +/* + if(0 != status && 1 != status) {//0:no error;1: after hard reset;the two state don't need soft reset + TPD_ERR("%s status[0x%x],inte[0x%x]\n",__func__,status,inte); + int_state(ts); + goto END; + } +*/ + 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 ) { + + if (ts->project_version == 0x03) { + if (ts->en_up_down && ts->in_gesture_mode == 0) + fp_detect(ts); + } + + if (ts->is_suspended == 1) { + #ifdef SUPPORT_GESTURE + if (ts->in_gesture_mode == 1) { + mutex_lock(&ts->mutex); + gesture_judge(ts); + mutex_unlock(&ts->mutex); + } + #endif + } else { + int_touch(); + } + } + if( inte & 0x10 ){ + int_key_report_s3508(ts); + } + + +END: + //ret = set_changer_bit(ts); + touch_enable(ts); +EXIT: + pm_qos_remove_request(&pm_qos_req_tp); + return; +} + +#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); + __pm_stay_awake(ts->source); //avoid system enter suspend lead to i2c error + synaptics_ts_work_func(&ts->report_work); + __pm_relax(ts->source); + return IRQ_HANDLED; +} +#endif + +//wangwenxue@BSP add for change baseline_test to "proc\touchpanel\baseline_test" begin +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; +} +//wangwenxue@BSP add for change baseline_test to "proc\touchpanel\baseline_test" end + +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->gestures_enable); + ret = sprintf(page, "%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 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); + ret = sprintf(page, "%d,%d:%d,%d:%d,%d:%d,%d:%d,%d:%d,%d:%d,%d\n", + gesture, 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 = sprintf(page, "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; + } + __pm_stay_awake(ts->source); //avoid system enter suspend lead to i2c error + mutex_lock(&ts->mutex); + ret = sscanf(buf,"%d",&write_flag); + gesture_switch = write_flag; + TPD_ERR("gesture_switch:%d,suspend:%d,gesture:%d\n",gesture_switch,ts->is_suspended,ts->gestures_enable); + if (1 == gesture_switch){ + if ((ts->is_suspended == 1) && (ts->gestures_enable != 0)){ + i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + synaptics_mode_change(0x80); + //synaptics_enable_interrupt_for_gesture(ts, 1); + //change active mode no need to write gesture mode. + touch_enable(ts); + } + }else if(2 == gesture_switch){ + if ((ts->is_suspended == 1) && (ts->gestures_enable != 0)){ + i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + synaptics_mode_change(0x81); + touch_disable(ts); + //synaptics_enable_interrupt_for_gesture(ts, 0); + //change slepp mode no need to write gesture mode. + } + } + mutex_unlock(&ts->mutex); + __pm_relax(ts->source); + + return count; +} + +// chenggang.li@BSP.TP modified for oem 2014-08-08 create node +/******************************start****************************/ +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 + +#define GESTURE_ATTR(name, flag)\ + static ssize_t name##_enable_read_func(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)\ + {\ + int ret = 0;\ + char page[PAGESIZE];\ + ret = sprintf(page, "%d\n", (ts_g->gestures_enable & flag) != 0);\ + ret = simple_read_from_buffer(user_buf, count, ppos, page, strlen(page));\ + return ret;\ + }\ + static ssize_t name##_enable_write_func(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)\ + {\ + int ret, write_flag = 0;\ + char page[PAGESIZE] = {0};\ + ret = copy_from_user(page, user_buf, count);\ + ret = sscanf(page, "%d", &write_flag);\ + if (write_flag) {\ + ts_g->gestures_enable |= flag;\ + } else {\ + ts_g->gestures_enable &= ~flag;\ + }\ + return count;\ + }\ + static const struct file_operations name##_enable_proc_fops = {\ + .write = name##_enable_write_func,\ + .read = name##_enable_read_func,\ + .open = simple_open,\ + .owner = THIS_MODULE,\ + }; + +GESTURE_ATTR(single_tap, GESTURE_SINGLE_TAP); +GESTURE_ATTR(double_tap, GESTURE_DOUBLE_TAP); +GESTURE_ATTR(up_arrow, GESTURE_UP_ARROW); +GESTURE_ATTR(down_arrow, GESTURE_DOWN_ARROW); +GESTURE_ATTR(left_arrow, GESTURE_LEFT_ARROW); +GESTURE_ATTR(right_arrow, GESTURE_RIGHT_ARROW); +GESTURE_ATTR(double_swipe, GESTURE_DOUBLE_SWIPE); +GESTURE_ATTR(up_swipe, GESTURE_UP_SWIPE); +GESTURE_ATTR(down_swipe, GESTURE_DOWN_SWIPE); +GESTURE_ATTR(left_swipe, GESTURE_RIGHT_SWIPE); +GESTURE_ATTR(right_swipe, GESTURE_LEFT_SWIPE); +GESTURE_ATTR(letter_o, GESTURE_CIRCLE); +GESTURE_ATTR(letter_w, GESTURE_W); +GESTURE_ATTR(letter_m, GESTURE_M); +GESTURE_ATTR(letter_s, GESTURE_S); + +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 += sprintf(&(buffer[cnt]), "page=0x%x,address=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 += sprintf(&(buffer[cnt]), "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]; + char buffer_local[128]; + int ret, i; + struct synaptics_ts_data *ts = ts_g; + int temp_block, wbyte; + char reg[30]; + + if (count > 128) + return count; + if (copy_from_user(buffer_local, buffer, count)) { + TPD_ERR("%s: write proc error.\n", __func__); + return count; + } + + ret = sscanf(buffer_local, + "%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 (0xFF == temp_block)//the mark is to write register else read register + { + 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 = sprintf(page, "%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}; + + 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; + } + sscanf(buf, "%d", &ret); + 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("Please enter 0 or 1 to open or close the glove function\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 = sprintf(page, "%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 ; + 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; + } + sscanf(buf, "%d", &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("Please enter 0 or 1 to open or close the sleep function\n"); + } + return count; +} +#endif + +static ssize_t tp_debug_log_show(struct device_driver *ddri, char *buf) +{ + // uint8_t ret = 0; + 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_debug_log_show read i2c err\n"); + b = synaptics_rmi4_i2c_read_byte(ts->client, F01_RMI_DATA01); + if( b < 0 ) + TPD_ERR("tp_debug_log_show read i2c err\n"); + c = synaptics_rmi4_i2c_read_byte(ts->client, F12_2D_DATA_BASE); + if( c < 0 ) + TPD_ERR("tp_debug_log_show read i2c err\n"); + + return sprintf(buf, "F01_RMI_DATA_BASE[0x%x]=0x%x;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 tp_debug_log_store(struct device_driver *ddri, const char *buf, size_t count) +{ + int tmp = 0; + if( 1 == sscanf(buf, "%d", &tmp) ){ + 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 = sprintf(page, "%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("synaptics_rmi4_i2c_write_byte failed for page select\n"); + return -1; + } + 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 < 60)); + if (ret > 0x00 || flag_err >= 60) + 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 { + if (version_is_s3508 == 2) + delay_qt_ms(80); + else + 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_image_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_hard_reset(ts); + synaptics_read_register_map_page1(ts); + + TPD_DEBUG("\nstep 1:select report type 0x03 baseline\n"); + msm_cpuidle_set_sleep_disable(true); + /*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); + + if (version_is_s3508 != 2) { + 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); + } else if (version_is_s3508 == 2) { + /* s3706 no nedd 0D CBC Settings */ + 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+23);/* Analog Control 1 */ + tmp_new = ret & 0xdf; + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23, 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); + //TPDTM_DMESG("forbid Forbid NoiseMitigation oK\n"); + ret = i2c_smbus_write_byte_data(ts->client, F54_ANALOG_COMMAND_BASE, 0X02);//force Cal + checkCMD(10); + //TPDTM_DMESG("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);//get report + checkCMD(10); + count = 0; + for( x = 0; x < TX_NUM; x++ ){ + //printk("\n[%d]", x); + num_read_chars += sprintf(&(buf[num_read_chars]), "\n[%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; + //printk("%d,", delta_baseline[x][y]); + num_read_chars += sprintf(&(buf[num_read_chars]), "%5d", delta_baseline[x][y]); + } + } + num_read_chars += sprintf(&(buf[num_read_chars]), "\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); + msm_cpuidle_set_sleep_disable(false); +#ifdef SUPPORT_GLOVES_MODE + synaptics_glove_mode_enable(ts); +#endif + synaptics_init_panel(ts); +//modify by zhouwenping for solve cat tp_baseline_image node cause touch disable 20160225 start + 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); +//modify by zhouwenping 20160225 end + return num_read_chars; + +} + +static ssize_t tp_delta_image_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); + + //TPD_DEBUG("\nstep 2:report type2 delta image\n"); + 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++ ){ + //printk("\n[%d]", x); + num_read_chars += sprintf(&(buf[num_read_chars]), "\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; + //printk("%3d,", delta_baseline[x][y]); + num_read_chars += sprintf(&(buf[num_read_chars]), "%3d ", delta_baseline[x][y]); + } + } + num_read_chars += sprintf(&(buf[num_read_chars]), "\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_image_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 tp_baseline_image_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; + int fd = -1; + struct timespec now_time; + struct rtc_time rtc_now_time; + uint8_t data_buf[64]; + mm_segment_t old_fs; + uint32_t CURRENT_FIRMWARE_ID = 0 ; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + /* + const struct firmware *fw = NULL; + struct test_header *ph = NULL; + 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); + error_count++; + num_read_chars += sprintf(&(buf[num_read_chars]), "imageid=0x%x,deviceid=0x%x\n",TP_FW,TP_FW); + num_read_chars += sprintf(&(buf[num_read_chars]), "%d error(s). %s\n", error_count, error_count?"":"All test passed."); + return num_read_chars; + } + + //ph = (struct test_header *)(fw->data); + */ + + 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); + sprintf(ts->fw_id,"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); + //TPDTM_DMESG("step 1:select report type 0x03\n"); + + if(savefile) { + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + sprintf(data_buf, "/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); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = ksys_open(data_buf, O_WRONLY | O_CREAT | O_TRUNC, 0); + if (fd < 0) { + TPD_ERR("Open log file '%s' failed.\n", data_buf); + set_fs(old_fs); + } + } + + //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);//get 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 (fd >= 0){ + sprintf(data_buf, "%d,", baseline_data); + ksys_write(fd, data_buf, strlen(data_buf)); + } + if( (y < RX_NUM ) && (x < TX_NUM) ){ + //printk("%4d ,",baseline_data); + 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)))//the last tx and rx last two line for touchkey,others no need take care + { + count++; + continue; + } + TPD_ERR("touchpanel failed,RX_NUM:%d,TX_NUM:%d,baseline_data is %d,TPK_array_limit[%d*2]=%d,TPK_array_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 += sprintf(&(buf[num_read_chars]), "0 raw data erro 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; + } + } + /* + //test virtual key + if( (y==(RX_NUM-1)) && (x>= TX_NUM-3) ){ + TPD_DEBUG("synaptics:test virtual key,y= %d ,x = %d\n",y,x); + TPD_DEBUG("Synaptic:test virtual key;baseline_data is %d,TPK_array_limit[%d*2]=%d,TPK_array_limit[%d*2+1]=%d\n ",baseline_data,count,*(baseline_data_test+count*2),count,*(baseline_data_test+count*2+1)); + if((baseline_data < *(baseline_data_test+count*2)) || (baseline_data > *(baseline_data_test+count*2+1))){ + TPD_ERR("Synaptic:test virtual key failed------------;baseline_data is %d,TPK_array_limit[%d*2]=%d,TPK_array_limit[%d*2+1]=%d\n ",baseline_data,count,*(baseline_data_test+count*2),count,*(baseline_data_test+count*2+1)); + num_read_chars += sprintf(&(buf[num_read_chars]), "0 raw data erro 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++; + } + //printk("\n synaptics:s3320 TX_NUM:%d\n",x); + if (fd >= 0){ + ksys_write(fd, "\n", 1); + } + } + + if(!enable_cbc){ + enable_cbc = 1; + if (fd >= 0){ + ksys_write(fd, "\n", 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(0xff != buffer[x]){ + 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("step 4:error_count[%d] baseline_data%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)) { /*red zone, test capacitance,if half num large than 20, fail*/ + if (++err_RT251 > ((TX_NUM - 1) * (RX_NUM - 2) / 2)) { + error_count++; + TPD_ERR("Step 5 : Check RT251 for random touch event error, capacitance, err_RT251 is %d\n", err_RT251); + goto END; + } + } + + if ((x != TX_NUM - 1) && (y == RX_NUM - 1) && baseline_data > 500) { /*blue zone, test self capaccitance, if half num large than 500, fail*/ + if (++err_RT251_self > (TX_NUM - 1) / 2) { + error_count++; + TPD_ERR("Step 5 : Check RT251 for random touch event error, self capacitance, err_RT251_self is %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("Step 6 : Check RT252 for random touch event, 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("Step 7 : Check RT253 for random touch event, self capacitance, err_RT253 is %d\n", err_RT253); + goto END; + } + } + } + } + TPD_ERR("ROLAND----> err_RT253 is %d\n", err_RT253); +END: + if (fd >= 0) { + ksys_close(fd); + set_fs(old_fs); + } + //release_firmware(fw); + 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]), 128, + "imageid=0x%lx,deviceid=0x%lx\n", TP_FW, TP_FW); + num_read_chars += snprintf(&(buf[num_read_chars]), 128, + "left:=%d,right:%d\n", left_ramdata, right_ramdata); + num_read_chars += snprintf(&(buf[num_read_chars]), 128, + "%d error(s). %s\n", error_count, + error_count?"":"All test passed."); + return num_read_chars; +} + +static ssize_t synaptics_rmi4_baseline_show_s3706( + 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 baseline_data = 0; + uint32_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; + int fd = -1; + struct timespec now_time; + struct rtc_time rtc_now_time; + uint8_t data_buf[64]; + mm_segment_t old_fs; + unsigned long int CURRENT_FIRMWARE_ID = 0; + + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + msm_cpuidle_set_sleep_disable(true); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + synaptics_rmi4_i2c_read_block(ts->client, + F34_FLASH_CTRL00, 8, buf); + CURRENT_FIRMWARE_ID = (buf[0] << 24) | + (buf[1] << 16) | (buf[2] << 8) | buf[3]; + CURRENT_FIRMWARE_ID = CURRENT_FIRMWARE_ID << 32 | + ((buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]); + TPD_ERR("[sk]CURRENT_FIRMWARE_ID = 0x%lx\n", + CURRENT_FIRMWARE_ID); + snprintf(ts->fw_id, 20, "0x%lx", 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_hard_reset(ts); + 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); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = ksys_open(data_buf, O_WRONLY | O_CREAT | O_TRUNC, 0); + if (fd < 0) { + TPD_ERR("Open log file '%s' failed.\n", data_buf); + set_fs(old_fs); + } + } + + /*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) { + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + + 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); + + 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+23); + tmp_new = ret | 0x20;/*CBC Xmtr carrier select bit 5*/ + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + checkCMD(30); + TPD_DEBUG("Test open cbc\n"); + baseline_data_test = (int16_t *)baseline_cap_17819_data[0]; + } else { + 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+23); + tmp_new = ret & 0xdf;/*CBC Xmtr carrier select bit 5*/ + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + /* force update*/ + checkCMD(30); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+7, 0x01); + /* Forbid NoiseMitigation*/ + baseline_data_test = (int16_t *)baseline_cap_17819_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);//get 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; + usleep_range(1000, 1001); + baseline_data = (tmp_h << 8) | tmp_l; + if (fd >= 0) { + snprintf(data_buf, 20, "%d,", baseline_data); + ksys_write(fd, data_buf, strlen(data_buf)); + } + if ((y < RX_NUM) && (x < TX_NUM)) { + /*printk("%4d ,",baseline_data);*/ + 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 (ts->support_1080x2340_tp) { + if ((y == 0) && (x >= 7) && (x <= 8)) { + count++; + TPD_ERR("RX_NU:%dTX_NU:%dbaslin_da%d\n", + y, x, baseline_data); + continue; + } + } else { + if ((y == 0) && (x >= 6) && (x <= 9)) { + count++; + TPD_ERR("RX_NU:%dTX_NU:%dbaslin_da%d\n", + y, x, baseline_data); + continue; + } + } + TPD_ERR("failed,RX_NU:%d,TX_NU:%d,baslin_da is%d\n", + y, x, baseline_data); + TPD_ERR("failed,TPK_arrlimit[%d*2]=%d,TPK_arrlimit[%d*2+1]=%d\n", + 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]), 60, + "0 raw data erro 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++; + } + /*printk("\n synaptics:s3320 TX_NUM:%d\n",x);*/ + if (fd >= 0) + ksys_write(fd, "\n", 1); + } + + if (!enable_cbc) { + enable_cbc = 1; + if (fd >= 0) + ksys_write(fd, "\n", 1); + TPD_ERR("enable cbc baseline test again\n"); + goto TEST_WITH_CBC_s3508; + } + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + + 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); + + 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; + } + + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + + 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(80); + tx_datal = i2c_smbus_read_i2c_block_data(ts->client, + F54_ANALOG_DATA_BASE+3, 7, buffer); + buffer[0] &= 0xFC;/*buf = 03*/ + buffer[4] &= 0xFC;/*buf = 03*/ + 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; + } + + /*hard reset TP*/ + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + + 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("step 4:error_count[%d] baseline_data%d[0x%x] ERROR!\n", + error_count, y, baseline_data); + goto END; + } + } + /*must add reset for RT150 */ + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + /*Step 5 : Check RT150 for random touch event*/ + TPD_ERR("Step 5 : Check RT150 for Hybrid Absolute\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x96);/*select report type 0x96*/ + 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); + /*all channel 16+33 */ + for (y = 0; y < RX_NUM + TX_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; + unsigned_baseline_data = (tmp_h << 8) | tmp_l; + + if (unsigned_baseline_data < 10000) { + error_count++; + TPD_ERR("Step5 RT150 ,error_line is y =%d,data = %hu\n", + y, unsigned_baseline_data); + goto END; + } + } +END: + if (fd >= 0) { + ksys_close(fd); + set_fs(old_fs); + } + + 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); + /*hard reset TP*/ + synaptics_hard_reset(ts); + synaptics_read_register_map_page1(ts); + +#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); + msm_cpuidle_set_sleep_disable(false); + num_read_chars += snprintf(&(buf[num_read_chars]), 128, + "imageid=0x%lx,deviceid=0x%lx\n", + TP_FW, TP_FW); + num_read_chars += snprintf(&(buf[num_read_chars]), 128, + "left:=%d,right:%d\n", left_ramdata, + right_ramdata); + num_read_chars += snprintf(&(buf[num_read_chars]), 128, + "%d error(s). %s\n", error_count, + error_count?"":"All test passed."); + return num_read_chars; +} + + +static ssize_t tp_baseline_image_with_cbc_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; + 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_hard_reset(ts); + synaptics_read_register_map_page1(ts); + TPD_DEBUG("\nstep 1:select report type 0x03 baseline\n"); + msm_cpuidle_set_sleep_disable(true); + //step 1:check raw capacitance. + //select report type 0x03/* Report Type */ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_DATA_BASE, 0x03); + if (ret < 0) { + TPDTM_DMESG("step 1: select report type 0x03 failed \n"); + //return sprintf(buf, "i2c err!"); + } + if (version_is_s3508 == 2) { + /* Multi Metric Noise Mitigation Control */ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+20, 0x01); + /* Analog Command 0 *//*force update*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + checkCMD(10); + TPD_DEBUG("open CBC oK\n"); + /* Analog Control 1 s3508 is 27 s3706 is 27*/ + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23); + tmp_new = ret | 0x20;/*CBC Xmtr carrier select*/ + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23, tmp_new); + } else { + /* Multi Metric Noise Mitigation Control */ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+20, 0x01); + /* 0D CBC Settings */ + 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)); + /* Analog Command 0 *//*force update*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + checkCMD(10); + TPD_DEBUG("open CBC oK\n"); + /* Analog Control 1 s3508 is 27 s3706 is 27*/ + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+27); + tmp_new = ret | 0x20;/*CBC Xmtr carrier select*/ + ret = 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 F54_ANALOG_CMD00*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0X04); + checkCMD(10); + TPD_DEBUG("forbid Forbid NoiseMitigation oK\n"); + /*Force Cal, F54_ANALOG_CMD00*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02); + checkCMD(10); + TPDTM_DMESG("Force Cal oK\n"); + /* Report Index LSB //set fifo 00 */ + ret = synaptics_rmi4_i2c_write_word(ts->client, + F54_ANALOG_DATA_BASE+1, 0x00); + /*get report*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01); + checkCMD(10); + count = 0; + for(x = 0;x < TX_NUM; x++) { + TPD_DEBUG("\n[%d]",x); + num_read_chars += sprintf(&(buf[num_read_chars]), "\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; + TPD_DEBUG("%d,",delta_baseline[x][y]); + num_read_chars += sprintf(&(buf[num_read_chars]), "%d ",delta_baseline[x][y]); + } + } + + ret = synaptics_rmi4_i2c_write_byte(ts->client,F54_ANALOG_COMMAND_BASE,0X02); + delay_qt_ms(60); + msm_cpuidle_set_sleep_disable(false); + 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) +{ + if (version_is_s3508 == 2) { + return synaptics_rmi4_baseline_show_s3706(dev, buf, savefile); + } else { + return synaptics_rmi4_baseline_show_s3508(dev, buf, savefile); + } +} + +static ssize_t tp_baseline_image_with_cbc_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 sprintf(buf, "%d\n", TP_TPK); + if(tp_dev == TP_TRULY) + return sprintf(buf, "%d\n", TP_TRULY); + if(tp_dev == TP_OFILM) + return sprintf(buf, "%d\n", TP_OFILM); + return sprintf(buf, "%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("synaptics_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); + set_bit(KEY_GESTURE_W, ts->input_dev->keybit); + set_bit(KEY_GESTURE_M, ts->input_dev->keybit); + set_bit(KEY_GESTURE_S, ts->input_dev->keybit); + set_bit(KEY_DOUBLE_TAP, ts->input_dev->keybit); + set_bit(KEY_GESTURE_CIRCLE, ts->input_dev->keybit); + set_bit(KEY_GESTURE_TWO_SWIPE, ts->input_dev->keybit); + set_bit(KEY_GESTURE_UP_ARROW, ts->input_dev->keybit); + set_bit(KEY_GESTURE_LEFT_ARROW, ts->input_dev->keybit); + set_bit(KEY_GESTURE_RIGHT_ARROW, ts->input_dev->keybit); + set_bit(KEY_GESTURE_DOWN_ARROW, ts->input_dev->keybit); + set_bit(KEY_GESTURE_SWIPE_LEFT, ts->input_dev->keybit); + set_bit(KEY_GESTURE_SWIPE_DOWN, ts->input_dev->keybit); + set_bit(KEY_GESTURE_SWIPE_RIGHT, ts->input_dev->keybit); + set_bit(KEY_GESTURE_SWIPE_UP, ts->input_dev->keybit); + set_bit(KEY_GESTURE_SINGLE_TAP, 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 -1; + } + 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 -1; + } + } + 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 firmware failed - %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 -1; + else + return ret; +} +static int check_version = 0; + +/*FW Update Func for s3706*/ +static int synaptics_rmi4_sw_reset(struct i2c_client *client) +{ + int retval; + unsigned char command = 0x01; + + retval = synaptics_rmi4_i2c_write_block(client, + F01_RMI_CTRL_BASE, + sizeof(command), + &command); + + return retval; +} + +static int synaptics_rmi4_reset_device(struct i2c_client *client, + bool rebuild) +{ + int retval; + + rebuild = 1; + /*mutex_lock(&(rmi4_data->rmi4_reset_mutex));*/ + + retval = synaptics_rmi4_sw_reset(client); + if (retval < 0) { + TPD_ERR("%s: Failed to issue reset command\n", + __func__); + return retval; + } + /*mutex_unlock(&(rmi4_data->rmi4_reset_mutex));*/ + return retval; +} + +/*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[20]; + uint8_t buf[8]; + unsigned long int 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, "S3706", 5)) { + TPD_ERR("enter version 17819 update mode\n"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request firmware failed - %s (%d)\n", + ts->fw_name, ret); + return ret; + } + } else 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 -1; + } + } + + if(1 == check_version ) { + TPD_DEBUG("enter version 15801 update mode\n"); + strcpy(ts->fw_name,"tp/fw_synaptics_15801.img"); + //push_component_info(TP, ts->fw_id, "S3718_vA"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request firmware failed - %s (%d)\n",ts->fw_name, ret); + return ret; + } + + }else{ + TPD_DEBUG("enter version 15801 vb update mode\n"); + //push_component_info(TP, ts->fw_id, "S3718_vB"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request firmware failed - %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"); + //push_component_info(TP, ts->fw_id, "s3508"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request firmware failed - %s (%d)\n",ts->fw_name, ret); + return ret; + } + }else{ + TPD_ERR("firmware name not match\n"); + return -1; + } + + 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 failed twice, quit updating 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, 8, buf); + CURRENT_FIRMWARE_ID = (buf[0]<<24) | (buf[1]<<16) | + (buf[2]<<8) | buf[3]; + if (version_is_s3508 == 2) + CURRENT_FIRMWARE_ID = CURRENT_FIRMWARE_ID << 32 | + ((buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]); + TPD_ERR("CURRENT_FIRMWARE_ID 0x%lx!\n", CURRENT_FIRMWARE_ID); + snprintf(fw_id_temp, 20, "0x%lx", CURRENT_FIRMWARE_ID); + strcpy(ts->fw_id,fw_id_temp); + 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, 2, "%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; + + int bootmode; + + bootmode = get_boot_mode(); + TPD_ERR("synaptics bootmode %d !\n", bootmode); + if ((bootmode == MSM_BOOT_MODE__FACTORY) + || (bootmode == MSM_BOOT_MODE__RF) + || (bootmode == MSM_BOOT_MODE__WLAN)) { + TPD_ERR("synaptics disable tp update firmware update\n"); + return size; + } + + if (ts->is_suspended && ts->support_hw_poweroff){ + TPD_ERR("power off firmware abort!\n"); + return size; + } + if (version_is_s3508 == 1) { + if (strncmp(ts->manu_name,"s3508",5) && strncmp(ts->manu_name,"15811",5)){ + TPD_ERR("product name[%s] do not update!\n",ts->manu_name); + return size; + } + } else if (version_is_s3508 == 2) { + if (strncmp(ts->manu_name, "S3706", 5)) { + TPD_ERR("product name[%s] do not update!\n", + ts->manu_name); + return size; + } + }else{ + if (strncmp(ts->manu_name,"S3718",5)){ + TPD_ERR("product 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 = 0; + 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 + sprintf(&buf[temp],"Request failed,Check the path %d",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("synaptics_test_limit_show:array_limit_offset = %x array_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 += sprintf(buf, "Without cbc:"); + for(i = 0 ;i < (ph->array_limit_size/2 ); i++){ + if(i % (2*RX_NUM) == 0) + temp += sprintf(&(buf[temp]), "\n[%d] ",(i/RX_NUM)/2); + temp += sprintf(&buf[temp],"%d,",prow[i]); + printk("%d,",prow[i]); + } + cat_cbc_change = 1; + }else{ + temp += sprintf(buf, "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 += sprintf(&(buf[temp]), "\n[%d] ",(i/RX_NUM)/2); + temp += sprintf(&buf[temp],"%d,",prowcbc[i]); + printk("%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, 16, "switch page err\n"); + + doze_time = i2c_smbus_read_byte_data(ts->client, F01_RMI_CTRL02); + return snprintf(buf, 2, "%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 int touch_hold_en(struct synaptics_ts_data *ts, bool enable) +{ + int ret = 0; + int touch_hold_enable = 0; + + /* SYNA_F51_CUSTOM_CTRL20_00 0x0428*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x04); + if (ret < 0) + TPD_ERR("%s: set page 0x04 fail!\n", __func__); + touch_hold_enable = i2c_smbus_read_byte_data(ts->client, 0x2c); + + if (enable == 1) + touch_hold_enable = touch_hold_enable | 0x01; + else if (enable == 0) + touch_hold_enable = touch_hold_enable & 0xfe; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x2c, touch_hold_enable); + if (ret < 0) + TPD_ERR("%s: set reg fail!\n", __func__); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + TPD_ERR("%s: set page 00 fail!\n", __func__); + + return ret; + +} + +int tp_single_tap_en(struct synaptics_ts_data *ts, bool enable) +{ + uint8_t ret = 0; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x04); + if (ret < 0) + return ret; + if (enable) { + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x22, 0x01); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x23, 0x1e); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x24, 0x1e); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x25, 0x32); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); // force update + TPD_DEBUG("%s: force update %x\n", + __func__, F54_ANALOG_COMMAND_BASE); + } else + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x22, 0x00); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + TPD_ERR("%s: set page 00 fail!\n", __func__); + + return ret; +} + +static ssize_t tp_gesture_touch_hold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + int touch_hold_enable = 0; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + /* SYNA_F51_CUSTOM_CTRL20_00 0x0428*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x04); + if (ret < 0) + return snprintf(buf, 16, "switch page err\n"); + touch_hold_enable = i2c_smbus_read_byte_data(ts->client, 0x2c); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + TPD_ERR("%s: set page 00 fail!\n", __func__); + return snprintf(buf, 6, "0x%x\n", touch_hold_enable); +} + +static ssize_t tp_gesture_touch_hold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int tmp = 0; + int touch_hold_enable = 0; + int touchhold_tmp = 0; + int ret = 0; + int touch_hold_retry = 0; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + ret = kstrtoint(buf, 10, &tmp); + if (ret < 0) { + TPDTM_DMESG("invalid content: '%s', length = %zd\n", buf, size); + return size; + } + + TPD_ERR("%s: set %d\n", __func__, tmp); + if (tmp == 2 && ts->fp_aod_cnt > 0) { + ts->fp_up_down = 0; + ts->unlock_succes = 1; + return size; + } + + if (tmp == 3) { + ts->unlock_succes = 0; + return size; + } + + mutex_lock(&ts->mutex); + /* SYNA_F51_CUSTOM_CTRL20_00 0x0428*/ + while (1) { + if((tmp != 1) && (tmp != 0)) + break; + touch_hold_retry ++; + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x04); + if (ret < 0) + TPD_ERR("set page first fail!\n"); + + touch_hold_enable = i2c_smbus_read_byte_data(ts->client, 0x2c); + TPDTM_DMESG("%s:read reg 0x%x\n", + __func__, touch_hold_enable); + + if (tmp == 1) { + touch_hold_enable = touch_hold_enable | 0x01; + ts->en_up_down = 1; + } else if (tmp == 0) { + touch_hold_enable = touch_hold_enable & 0xfe; + ts->en_up_down = 0; + } + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x2c, touch_hold_enable); + if (ret < 0) + TPD_ERR("set first fail!\n"); + touchhold_tmp = i2c_smbus_read_byte_data(ts->client, 0x2c); + TPDTM_DMESG("%s:read reg again 0x%x,0x%x,%d\n", __func__, + touch_hold_enable, touchhold_tmp, touch_hold_retry); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x00); + if (ret < 0) + TPD_ERR("set page 00 fail!\n"); + if (touch_hold_enable == touchhold_tmp) + break; + if (touch_hold_retry == 5) + break; + msleep(10); + } + mutex_unlock(&ts->mutex); + + return size; +} + +static DEVICE_ATTR(test_limit, 0664, synaptics_test_limit_show, synaptics_test_limit_store); +static DRIVER_ATTR_RW(tp_baseline_image); +static DRIVER_ATTR_RW(tp_baseline_image_with_cbc); +static DRIVER_ATTR_RW(tp_delta_image); +static DRIVER_ATTR_RW(tp_debug_log); +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 DEVICE_ATTR(tp_gesture_touch_hold, 0664, + tp_gesture_touch_hold_show, tp_gesture_touch_hold_store); +static int synaptics_dsx_pinctrl_init(struct synaptics_ts_data *ts); + +static ssize_t tp_debug_log_write_func( + struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + int ret, tmp = 0; + char buf[4] = {0}; + + if (count > 4) + return count; + if (copy_from_user(buf, buffer, count)) { + TPD_ERR(KERN_INFO "%s: read proc input error.\n", __func__); + return count; + } + + 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 tp_debug_log_read_func( + struct file *file, char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[4]; + + ret = snprintf(page, 4, "%d\n", tp_debug); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static const struct file_operations tp_debug_log_proc_fops = { + .write = tp_debug_log_write_func, + .read = tp_debug_log_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static ssize_t synaptics_main_reg_read_func( + struct file *file, char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + int device_contrl = 0; + int doze_interval = 0; + unsigned char reportbuf[4] = {0}; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + TPD_ERR("%s: chage page failed:%d\n", __func__, ret); + + ret = i2c_smbus_read_byte_data(ts->client, F01_RMI_CTRL00); + if (ret < 0) + TPD_ERR("%s: read failed:%d\n", __func__, ret); + + TPD_ERR("%s Device Control 0x%x=0x%x\n", __func__, F01_RMI_CTRL00, ret); + device_contrl = ret; + + ret = i2c_smbus_read_byte_data(ts->client, F01_RMI_CTRL02); + if (ret < 0) + TPD_ERR("%s: read failed:%d\n", __func__, ret); + + TPD_ERR("Doze Interval 0x%x=0x%x\n", F01_RMI_CTRL02, ret); + doze_interval = ret; + + ret = i2c_smbus_read_i2c_block_data(ts->client, F12_2D_CTRL20, + 3, &(reportbuf[0x0])); + if (ret < 0) + TPD_ERR("read reg F12_2D_CTRL20[0x%x] failed\n", F12_2D_CTRL20); + + TPD_ERR("Gesture Report Flag 0x%x=[2]:0x%x\n", + F12_2D_CTRL20, reportbuf[2]); + + ret = snprintf(page, 128, "0x%x:0x%x:0x%x\n", + device_contrl, doze_interval, reportbuf[2]); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static const struct file_operations tp_main_reg_proc_fops = { + .read = synaptics_main_reg_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + + +static ssize_t tp_reset_write_func (struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int ret, write_flag, i; + char buf[10] = {0}; + struct synaptics_ts_data *ts = ts_g; + + if (count > 10) + return count; + if (!ts) + return count; + if (ts->loading_fw) { + TPD_ERR("%s FW is updating break!!\n", __func__); + return count; + } + if (copy_from_user(buf, buffer, count)) { + TPD_ERR(KERN_INFO "%s: read proc input error.\n", __func__); + return count; + } + ret = sscanf(buf, "%d", &write_flag); + + TPD_ERR("%s write [%d]\n",__func__,write_flag); + if (1 == write_flag) + { + ret = synaptics_soft_reset(ts); + } + else if(2 == write_flag) + { + synaptics_hard_reset(ts); + } + else if(3 == write_flag) + { + disable_irq_nosync(ts->irq); + } + else if(4 == write_flag) + { + enable_irq(ts->irq); + } + else if(8 == write_flag) + { + touch_enable(ts); + } + else if(9 == write_flag) + { + touch_disable(ts); + } + else if(5 == write_flag) + { + synaptics_read_register_map(ts); + } + else if(6 == write_flag) + { + 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; +} + +//chenggang.li@bsp add for 14045 +static const struct file_operations radd_proc_fops = { + .write = synap_write_address, + .read = synap_read_address, + .open = simple_open, + .owner = THIS_MODULE, +}; + + +//wangwenxue@BSP add for change baseline_test to "proc\touchpanel\baseline_test" begin +static const struct file_operations i2c_device_test_proc_fops = { + .read = i2c_device_test_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +//wangwenxue@BSP add for change baseline_test to "proc\touchpanel\baseline_test" begin +static const struct file_operations baseline_test_proc_fops = { + .read = tp_baseline_test_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +//wangwenxue@BSP add for change baseline_test to "proc\touchpanel\baseline_test" end + +#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, + //.read = tp_sleep_read_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 = sprintf(page, "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 ; + char buf[4] = {0}; + + if (count > 2) + return count; + + if (copy_from_user(buf, buffer, count)) { + TPD_ERR(KERN_INFO "%s: write proc input error.\n", __func__); + return count; + } + + if (-1 == sscanf(buf, "%d", &ret)) { + TPD_ERR("%s sscanf error\n", __func__); + return count; + } + 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_connet_proc_fops = { + .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; + int touch_hold_enable = 0; + int touch_hold_retry = 0; + + if(!ts) + return -1; + + 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->gestures_enable) { + synaptics_enable_interrupt_for_gesture(ts,false); + synaptics_mode_change(0x00); + //change to active later getbase data + } else { + synaptics_mode_change(0x00); + //change to active later 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); + + if (ts->project_version != 0x03) { + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x0); + ret = i2c_smbus_write_byte_data(ts->client, + F01_RMI_CMD_BASE, 0x01);//soft reset + msleep(100); + TPD_DEBUG("resume soft reset"); + } else { + if ((not_getbase == 0) && (ts->unlock_succes == 0)) { + TPD_DEBUG("not touchhold status reset\n"); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x0); + ret = i2c_smbus_write_byte_data(ts->client, + F01_RMI_CMD_BASE, 0x01);//soft reset + msleep(100); + } + } + mutex_unlock(&ts->mutex); + atomic_set(&ts->is_stop, 0); + if (ts->gestures_enable) + set_doze_time(1); + touch_enable(ts); +#ifdef ENABLE_TPEDGE_LIMIT + synaptics_tpedge_limitfunc(); +#endif + if (ts->project_version == 0x03) { + ts->fp_up_down = 0; + TPD_DEBUG("%s:fp_down_up %d gesture %d, en_down_up %d\n", + __func__, ts->fp_up_down, + ts->in_gesture_mode, ts->en_up_down); + while (ts->en_up_down) { + touch_hold_enable = 0; + touch_hold_retry++; + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x04); + if (ret < 0) + TPD_ERR("set page first fail!\n"); + touch_hold_enable = + i2c_smbus_read_byte_data(ts->client, 0x2c); + TPDTM_DMESG("%s:read reg 0x%x\n", + __func__, touch_hold_enable); + touch_hold_enable = touch_hold_enable | 0x01; + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0x2c, touch_hold_enable); + if (ret < 0) + TPD_ERR("set first fail!\n"); + ret = i2c_smbus_read_byte_data(ts->client, 0x2c); + TPDTM_DMESG("%s:read reg again 0x%x,0x%x\n", __func__, + touch_hold_enable, ret); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + 0xff, 0x00); + if (ret < 0) + TPD_ERR("set page 00 fail!\n"); + if (touch_hold_enable == ret) + break; + if (touch_hold_retry == 3) + break; + } + } + 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 += sprintf(&page[str_n], "\n%s %d points delta > [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 ; + char buf[4] = {0}; + + if (count > 2) + return count; + + if (copy_from_user(buf, buffer, count)) { + TPD_ERR("%s write error\n", __func__); + return count; + } + if (-1 == sscanf(buf, "%d", &ret)) { + TPD_ERR("%s sscanf error\n", __func__); + return count; + } + 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 (0 == ts->gestures_enable) + 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_proc_fops = { + .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 = sprintf(page, "%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 (version_is_s3508 == 2) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x0a; + } else if (version_is_s3508 == 0) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x34; + } else if (version_is_s3508 == 1) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x1b; + } else + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x0a; + + if( count > 2) + count = 2; + if(ts_g == NULL) + { + TPD_ERR("ts_g is NULL!\n"); + return -1; + } + 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_GRIP_CONFIGURATION); + TPD_ERR("%s_proc limit_enable =%d,mode:0x%x,grip:0x%x!\n", + __func__, limit_enable, limit_mode, + F51_GRIP_CONFIGURATION); + if(limit_mode){ + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + if(0 == limit_enable) + { + limit_mode = limit_mode & 0xFE; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_GRIP_CONFIGURATION, limit_mode); + } + else if(1 == limit_enable) + { + limit_mode = limit_mode | 0x1; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_GRIP_CONFIGURATION, limit_mode); + } + } + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x0); + mutex_unlock(&ts_g->mutex); + return count; +} + +static const struct file_operations tpedge_limit_enable_proc_fops = +{ + .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, sizeof(page), "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}; + 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; + } + sscanf(&buf[0], "%d", &key_switch); + 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 = sprintf(page, "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 (NULL != strstr(buf,"disable")) + { + key_back_disable =true; + key_appselect_disable = true; + } + else if (NULL != strstr(buf,"enable")) + { + 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 + +#define CREATE_PROC_NODE(PARENT, NAME, MODE)\ + node = proc_create(#NAME, MODE, PARENT, &NAME##_proc_fops);\ + if (node == NULL) {\ + ret = -ENOMEM;\ + TPD_ERR("Couldn't create " #NAME " in " #PARENT "\n");\ + } + +#define CREATE_GESTURE_NODE(NAME)\ + CREATE_PROC_NODE(touchpanel, NAME##_enable, 0666) + +static int init_synaptics_proc(struct synaptics_ts_data *ts) +{ + int ret = 0; + struct proc_dir_entry *node = NULL; + struct proc_dir_entry *touchpanel = NULL; + + touchpanel = proc_mkdir("touchpanel", NULL); + if (touchpanel == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create touchpanel\n"); + } + + CREATE_PROC_NODE(touchpanel, coordinate, 0444); + +#ifdef SUPPORT_GESTURE + // single_tap is only available on fajita + if (ts->project_version == 0x03) { + CREATE_GESTURE_NODE(single_tap); + } + CREATE_GESTURE_NODE(double_tap); + CREATE_GESTURE_NODE(up_arrow); + CREATE_GESTURE_NODE(down_arrow); + CREATE_GESTURE_NODE(left_arrow); + CREATE_GESTURE_NODE(right_arrow); + CREATE_GESTURE_NODE(double_swipe); + CREATE_GESTURE_NODE(up_swipe); + CREATE_GESTURE_NODE(down_swipe); + CREATE_GESTURE_NODE(left_swipe); + CREATE_GESTURE_NODE(right_swipe); + CREATE_GESTURE_NODE(letter_o); + CREATE_GESTURE_NODE(letter_w); + CREATE_GESTURE_NODE(letter_m); + CREATE_GESTURE_NODE(letter_s); +#endif + +#ifdef SUPPORT_GLOVES_MODE + CREATE_PROC_NODE(touchpanel, glove_mode_enable, 0666); +#endif + +#ifdef SUPPORT_TP_SLEEP_MODE + CREATE_PROC_NODE(touchpanel, sleep_mode_enable, 0666); +#endif + +#ifdef RESET_ONESECOND + CREATE_PROC_NODE(touchpanel, tp_reset, 0666); +#endif + +#ifdef ENABLE_TPEDGE_LIMIT + CREATE_PROC_NODE(touchpanel, tpedge_limit_enable, 0666); +#endif + + CREATE_PROC_NODE(touchpanel, baseline_test, 0666); + CREATE_PROC_NODE(touchpanel, i2c_device_test, 0666); + CREATE_PROC_NODE(touchpanel, radd, 0777); + CREATE_PROC_NODE(touchpanel, vendor_id, 0444); + CREATE_PROC_NODE(touchpanel, changer_connet, 0666); + CREATE_PROC_NODE(touchpanel, touch_press, 0666); + +#ifdef SUPPORT_TP_TOUCHKEY + CREATE_PROC_NODE(touchpanel, key_switch, 0666); + CREATE_PROC_NODE(touchpanel, key_disable, 0666); +#endif + + /*morgan.gu add for logkit to dump main registor*/ + CREATE_PROC_NODE(touchpanel, tp_main_reg, 0444); + + /*morgan.gu add for logkit to open more log*/ + CREATE_PROC_NODE(touchpanel, tp_debug_log, 0644); + + 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); + + return; +} + +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(3); //wait 3ms + 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 -1; + } + + 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 -1; + } + /*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("synaptics_rmi4_i2c_write_byte failed for page select\n"); + return -1; + } + } + + 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,program memory self-check bootloader_mode = 0x%x\n",bootloader_mode); + + if((max_x_ic == 0)||(max_y_ic == 0)||(bootloader_mode == 0x40)) { + TPD_ERR("Something terrible wrong \n Trying Update the Firmware again\n"); + return -1; + } + 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; +} + +#define F01_BUID_ID_OFFSET 18 + +static int synaptics_rmi4_query_firmware_id_s3706( + struct i2c_client *client, + unsigned int *firmware_id_s3706, + unsigned char *conifg_id_s3706) +{ + int retval; + unsigned char build_id[3]; + unsigned int firmware_id; + unsigned char config_id[32]; + + retval = synaptics_rmi4_i2c_read_block(client, + F01_RMI_QUERY_BASE + F01_BUID_ID_OFFSET, + sizeof(build_id), build_id); + if (retval < 0) + return retval; + firmware_id = (unsigned int)build_id[0] + + (unsigned int)build_id[1] * 0x100 + + (unsigned int)build_id[2] * 0x10000; + *firmware_id_s3706 = firmware_id; + + retval = synaptics_rmi4_i2c_read_block(client, + F34_FLASH_CTRL_BASE, + sizeof(config_id), config_id); + if (retval < 0) + return retval; + conifg_id_s3706 = config_id; + return 0; +} + + +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; + unsigned char CURRENT_CONFIG_ID[32] = {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 -1; + if (!strncmp(ts->manu_name, "S3706", 5)) { + synaptics_rmi4_query_firmware_id_s3706( + client, + &CURRENT_FIRMWARE_ID, + CURRENT_CONFIG_ID); + TPD_ERR("FW_ID:%d--CONFIG_ID %s FW_NAME:%s\n", + CURRENT_FIRMWARE_ID, + CURRENT_CONFIG_ID, + ts->fw_name); + rmi4_data_s3706->firmware_id = CURRENT_FIRMWARE_ID; + rmi4_data_s3706->force = force; + ret = synaptics_rmi4_fwu_init(rmi4_data_s3706, data, client); + if (ret < 0) { + return ret; + } + } else 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(1 == check_version) + TPD_ERR("15801CURRENT_FW_ID:%x----, FW_ID:%x----,FW_NAME:%s\n", CURRENT_FIRMWARE_ID, FIRMWARE_ID,ts->fw_name); + else + TPD_ERR("15801CURRENT_FW_ID:%xvB----, FW_ID:%xvB----,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; + } + } + ret = fwu_start_reflash(data,client); + if (ret){ + return -1; + } + }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("firmware_size + config_size + 0x100 > data_len data_len = %d \n",data_len); + return -1; + } + 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 bootloader id SynaF34_FlashControl is 0x00%x ret is %d\n",SynaF34_FlashControl,ret); + + i2c_smbus_write_byte_data(client,SynaF34_FlashControl,0x0F); + msleep(10); + TPD_DEBUG("attn step 4\n"); + ret=checkFlashState(client); + if(ret > 0) { + TPD_ERR("Get in prog:The status(Image) of flashstate is %x\n",ret); + return -1; + } + 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>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:The status(Image) of flash data3 is %x,time =%d\n",ret,j); + return -1; + } + } + //step 7 configure data + //TPD_ERR("going to flash configuration area\n"); + //TPD_ERR("header.firmware_size is 0x%x\n", header.firmware_size); + //TPD_ERR("bootloader_size is 0x%x\n", bootloader_size); + for(j=0;j>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:The status(Image) of flash data3 is %x,time =%d\n",ret,j); + return -1; + } + } + //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("firmware_size + config_size + 0x100 > data_len data_len = %d \n",data_len); + return -1; + } + + 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]; + + //TPD_ERR("synaptics force is %d\n", force); + 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 bootloader id SynaF34_FlashControl is 0x00%x ret is %d\n",SynaF34_FlashControl,ret); + + synaptics_rmi4_i2c_write_byte(client,SynaF34_FlashControl,0x0F); + msleep(10); + TPD_DEBUG("attn step 4\n"); + ret=checkFlashState(client); + if(ret > 0) { + TPD_ERR("Get in prog:The status(Image) of flashstate is %x\n",ret); + return -1; + } + 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 flash firmware area synaF34_FlashControl %d\n",ret); + + TPD_ERR("update-----------------firmware ------------------update!\n"); + TPD_DEBUG("cnt %d\n",firmware); + for(j=0; j>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:The status(Image) of flash data3 is %x,time =%d\n",ret,j); + return -1; + } + } + //step 7 configure data + //TPD_ERR("going to flash configuration area\n"); + //TPD_ERR("header.firmware_size is 0x%x\n", header.firmware_size); + //TPD_ERR("bootloader_size is 0x%x\n", bootloader_size); + TPD_ERR("update-----------------configuration ------------------update!\n"); + for(j=0;j>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:The status(Image) of flash data3 is %x,time =%d\n",ret,j); + return -1; + } + } + + //step 1 issue reset + synaptics_rmi4_i2c_write_byte(client,SynaF01CommandBase,0x01); + } + //step2 wait ATTN + //delay_qt_ms(1000); + mdelay(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 -1; + } + TPD_ERR("Firmware self check success\n"); + return 0; +} +#ifdef ENABLE_TPEDGE_LIMIT +static void synaptics_tpedge_limitfunc(void) +{ + int limit_mode=0; + int ret; + + if (version_is_s3508 == 2) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x0a; + } else if (version_is_s3508 == 0) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x34; + } else if (version_is_s3508 == 1) { + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x1b; + } else + F51_GRIP_CONFIGURATION = F51_CUSTOM_CTRL_BASE+0x0a; + + TPD_DEBUG("%s line %d F51_GRIP_CONFIGURATION = 0x%x\n", + __func__, __LINE__, F51_GRIP_CONFIGURATION); + //msleep(60); + ret = i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + limit_mode = i2c_smbus_read_byte_data(ts_g->client, + F51_GRIP_CONFIGURATION); + 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(0 == limit_enable) + { + if(limit_mode & 0x1){ + //TPD_ERR("000 limit_enable:0x%xs !\n",limit_mode); + limit_mode = limit_mode & 0xFE; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_GRIP_CONFIGURATION, limit_mode); + } + } + else if(1 == limit_enable) + { + if(!(limit_mode & 0x1)){ + //TPD_ERR("111 limit_enable:x%xs !\n",limit_mode); + limit_mode = limit_mode | 0x1; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_GRIP_CONFIGURATION, 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 -1; + } + 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(5); + 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_1080x2340_tp")) + ts->support_1080x2340_tp = true; + else + ts->support_1080x2340_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"); + //button_map[0] = 180; + //button_map[1] = 180; + //button_map[2] = 2021; + } + 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];/*s3706 16 33*/ + 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 avdd current\n", __func__); + + } else { + ts->regulator_avdd_current = current_supply; + TPD_ERR("%s: avdd current = %d\n", + __func__, ts->regulator_avdd_current); + rc = regulator_set_load(ts->vdd_2v8, + ts->regulator_avdd_current); + if (rc < 0) + TPD_ERR("%s: Failed to set avdd\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__); + } else { + ts->regulator_avdd_vmin = voltage_supply[0]; + ts->regulator_avdd_vmax = voltage_supply[1]; + TPD_ERR("%s:avdd_vmin=%d,avdd_vmax=%d\n", __func__, + ts->regulator_avdd_vmin, ts->regulator_avdd_vmax); + + rc = regulator_set_voltage(ts->vdd_2v8, + ts->regulator_avdd_vmin, ts->regulator_avdd_vmax); + if (rc < 0) + TPD_ERR("%s:Failed to set avdd\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__); + } else { + ts->regulator_vdd_current = current_supply; + TPD_ERR("%s: vdd current = %d\n", __func__, + ts->regulator_vdd_current); + rc = regulator_set_load(ts->vcc_i2c_1v8, + ts->regulator_vdd_current); + if (rc < 0) + TPD_ERR("%s: Failed to set 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__); + } else { + ts->regulator_vdd_vmin = voltage_supply[0]; + ts->regulator_vdd_vmax = voltage_supply[1]; + TPD_ERR("%s:vdd_vmin=%d,vdd_vmax=%d\n", __func__, + ts->regulator_vdd_vmin, ts->regulator_vdd_vmax); + + rc = regulator_set_voltage(ts->vcc_i2c_1v8, + ts->regulator_vdd_vmin, ts->regulator_vdd_vmax); + if (rc < 0) + TPD_ERR("%s:Failed to set 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 enable2v8_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//2260 +#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 = sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_APPSELECT) ":%d:%d:%d:%d" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOMEPAGE) ":%d:%d:%d:%d" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":%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[8]; + unsigned long int CURRENT_FIRMWARE_ID = 0; + uint32_t bootloader_mode; + uint32_t bootmode; + + 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); + ts->project_version = 0x00; + if (of_property_read_bool(ts->dev->of_node, "oem,fajta")) + ts->project_version = 0x03; + /***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 power on tp 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;//if touch is no valid report key + #endif + TPD_ERR("tp is no exist!\n"); + goto err_check_functionality_failed; + } + } + + ts->i2c_device_test = ret; + rmi4_data_s3706 = kzalloc(sizeof(struct synaptics_rmi4_data), + GFP_KERNEL); + if (rmi4_data_s3706 == NULL) { + TPD_ERR("Request rmi4_data_s3706 failed\n"); + goto err_check_functionality_failed; + } + rmi4_data_s3706->reset_device = synaptics_rmi4_reset_device; + mutex_init(&(rmi4_data_s3706->rmi4_reset_mutex)); + mutex_init(&(rmi4_data_s3706->rmi4_report_mutex)); + mutex_init(&(rmi4_data_s3706->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data_s3706->rmi4_exp_init_mutex)); + mutex_init(&(rmi4_data_s3706->rmi4_irq_enable_mutex)); + + 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); + + memset(ts->fw_name, 0, TP_FW_NAME_MAX_LEN); + memset(ts->test_limit_name, 0, TP_FW_NAME_MAX_LEN); + + //sprintf(ts->manu_name, "TP_SYNAPTICS"); + synaptics_rmi4_i2c_read_block(ts->client, F01_RMI_QUERY11,\ + sizeof(ts->manu_name), ts->manu_name); + if (!strncmp(ts->manu_name,"S3718",5)){ + strcpy(ts->fw_name,"tp/fw_synaptics_15801b.img"); + version_is_s3508 = 0; + } else if (!strncmp(ts->manu_name, "S3706", 5)) { + strlcpy(ts->fw_name, "tp/fw_synaptics_17819.img", + sizeof(ts->fw_name)); + version_is_s3508 = 2;/*for s3706*/ + TX_NUM = 16;/*s3706 16 33*/ + RX_NUM = 33; + if (ts->support_1080x2340_tp) { + strlcpy(ts->fw_name, "tp/fw_synaptics_18801.img", + sizeof(ts->fw_name)); + ts->max_x = 1080; + ts->max_y = 2340; + } else { + ts->max_x = 1080; + ts->max_y = 2280; + } + F12_2D_CTRL20 = F12_2D_CTRL_BASE + 0x06;/*0x07 for s3508*/ + F12_2D_CTRL27 = F12_2D_CTRL_BASE + 0x09; + } 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; + } + strcpy(ts->test_limit_name,"tp/14049/14049_Limit_jdi.img"); + + TPD_DEBUG("synatpitcs fw_name=%s\n", + ts->fw_name); + TPD_DEBUG("synatpitcs manu_name:%s\n", + ts->manu_name); + + synaptics_rmi4_i2c_read_block(ts->client, + F34_FLASH_CTRL00, 8, buf); + CURRENT_FIRMWARE_ID = (buf[0]<<24) | (buf[1]<<16) | + (buf[2]<<8) | buf[3]; + if (version_is_s3508 == 2) + CURRENT_FIRMWARE_ID = CURRENT_FIRMWARE_ID << 32 | + ((buf[4]<<24) | (buf[5]<<16) | + (buf[6]<<8) | buf[7]); + TPD_ERR("CURRENT_FIRMWARE_ID = 0x%lx\n", + CURRENT_FIRMWARE_ID); + TP_FW = CURRENT_FIRMWARE_ID; + snprintf(ts->fw_id, 20, "0x%lx", TP_FW); + + 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); + ts->source = wakeup_source_register(NULL, "tp_syna"); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + TPD_ERR("synaptics_init_panel failed\n"); + } + + //Detect whether TP FW is error, max_x,max_y may be incoorect while it has been damaged! + 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); +#elif defined(CONFIG_MSM_RDM_NOTIFY) + ts->msm_drm_notif.notifier_call = msm_drm_notifier_callback; + ret = msm_drm_register_client(&ts->msm_drm_notif); + if (ret) + TPD_ERR("Unable to register msm_drm_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 + /**************** + shoud 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(5); + 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; + } + if (ts->project_version == 0x03) { + if (device_create_file(&client->dev, + &dev_attr_tp_gesture_touch_hold)) { + TPDTM_DMESG("tp_gesture_touch_hold failt\n"); + goto exit_init_failed; + } + if (device_create_file(&client->dev, &dev_attr_fp_irq)) { + TPDTM_DMESG("driver_create_file fail fp_irq\n"); + goto exit_init_failed; + } + ts->fp_up_down = 0; + ts->en_up_down = 0; + ts->fp_aod_cnt = 0; + ts->unlock_succes = 0; + } + +#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(ts); + TPDTM_DMESG("synaptics_ts_probe 3203: normal end\n"); + + bootmode = get_boot_mode(); + TPD_ERR("synaptics bootmode %d !\n", bootmode); + if ((bootmode == MSM_BOOT_MODE__FACTORY) + || (bootmode == MSM_BOOT_MODE__RF) + || (bootmode == MSM_BOOT_MODE__WLAN)) { + touch_disable(ts); + TPD_ERR("synaptics ftm mode disable int \n"); + return 0; + } + + 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); + /*add for morgan for if no tp no pull gpio*/ + if (ts->pinctrl) { + ret = pinctrl_select_state(ts->pinctrl, + ts->pinctrl_state_suspend); + } +err_alloc_data_failed: + tpd_i2c_driver.driver.pm=NULL; + kfree(ts); + ts = NULL; + kfree(rmi4_data_s3706); + rmi4_data_s3706 = 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"); +#elif defined(CONFIG_MSM_RDM_NOTIFY) + if (msm_drm_unregister_client(&ts->msm_drm_notif)) + dev_err(&client->dev, "Error occurred while unregistering msm_drm_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 -1; + } + 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->gestures_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); /*change dozeinterval by firmware*/ + //just for fajita + if (ts->project_version == 0x03) { + mutex_lock(&ts->mutex); + tp_single_tap_en(ts, true); + mutex_unlock(&ts->mutex); + } + } + else{ + if (!ts->loading_fw) { + //loading_fw tp cannot enter sleep mode; + 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->gestures_enable == 0) { + ret = tpd_power(ts, 1); + if (ret < 0) + TPD_ERR("%s power on err\n", __func__); + if (ts->pinctrl) { + ret = pinctrl_select_state(ts->pinctrl, + ts->pinctrl_state_active); + } + } + } + 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 -1; + } + + 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->gestures_enable != 0){ + /*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 -1; + } + if(ts->support_hw_poweroff && (ts->gestures_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); + } + } + 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->gestures_enable != 0){ + /*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; + + 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 (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_POWERDOWN && (event == FB_EARLY_EVENT_BLANK )){ + 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->gestures_enable == 0) + touch_disable(ts); + synaptics_ts_suspend(&ts->client->dev); + TPD_DEBUG("%s : going TP suspend end\n", __func__); + } + } + } + return 0; +} +#elif defined(CONFIG_MSM_RDM_NOTIFY) +static int msm_drm_notifier_callback( + struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + + struct synaptics_ts_data *ts = container_of( + self, struct synaptics_ts_data, msm_drm_notif); + + if (event != MSM_DRM_EARLY_EVENT_BLANK + && MSM_DRM_EVENT_BLANK != event) + return 0; + if ((evdata) && (evdata->data) && (ts) && (ts->client)) { + blank = evdata->data; + TPD_ERR("%s blank[%d],event[0x%lx],evdata->id[%d]\n", + __func__, *blank, event, evdata->id); + + if ((*blank == MSM_DRM_BLANK_UNBLANK_CUST) + && (event == MSM_DRM_EARLY_EVENT_BLANK)) { + if (ts->is_suspended == 1) { + TPD_DEBUG("%s TP resume start\n", __func__); + ts->is_suspended = 0; + queue_delayed_work(get_base_report, + &ts->base_work, msecs_to_jiffies(10)); + synaptics_ts_resume(&ts->client->dev); + //atomic_set(&ts->is_stop,0); + TPD_DEBUG("%sTP resume end\n", __func__); + } + } else if (((*blank == MSM_DRM_BLANK_POWERDOWN_CUST) + && (event == MSM_DRM_EARLY_EVENT_BLANK)) + || (*blank == MSM_DRM_BLANK_NORMAL)) { + if (ts->is_suspended == 0) { + TPD_DEBUG("%s:TP suspend start\n", __func__); + ts->is_suspended = 1; + atomic_set(&ts->is_stop, 1); + cancel_delayed_work_sync(&ts->base_work); + flush_workqueue(get_base_report); + if (ts->gestures_enable == 0) + touch_disable(ts); + synaptics_ts_suspend(&ts->client->dev); + TPD_DEBUG("%s:TP suspend end\n", __func__); + } + } + if (ts->project_version == 0x03) + if (*blank == MSM_DRM_BLANK_POWERDOWN && + event == MSM_DRM_EVENT_BLANK && + ts->fp_aod_cnt > 0) { + set_tp_info(ts, 0); + gf_opticalfp_irq_handler(0); + } + } + 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 -1; + } + 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; + } + return; +} + +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_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_core.h new file mode 100644 index 000000000000..3ef54391a715 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx_core.h @@ -0,0 +1,552 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DS4 (1 << 0) +#define SYNAPTICS_DS5 (1 << 1) +#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5) +#define SYNAPTICS_DSX_DRIVER_VERSION 0x206a + +#include +/*delete by morgan.gu for sdm845 */ +#undef CONFIG_FB + +#ifdef CONFIG_FB +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (KERNEL_VERSION(2, 6, 38) < LINUX_VERSION_CODE) +#define KERNEL_ABOVE_2_6_38 +#endif + +#if (KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE) +#define KERNEL_ABOVE_3_6 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#endif +/* + *#define F51_DISCRETE_FORCE + *#ifdef F51_DISCRETE_FORCE + *#define FORCE_LEVEL_ADDR 0x0419 + *#define FORCE_LEVEL_MAX 255 + *#define CAL_DATA_SIZE 144 + *#endif + */ + +#define PDT_PROPS (0X00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x00D0) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define ADDRESS_LEN (2) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1A) +#define SYNAPTICS_RMI4_F21 (0x21) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xDB) + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 +#define BUILD_ID_SIZE 3 + +#define F12_FINGERS_TO_SUPPORT 10 +#define F12_NO_OBJECT_STATUS 0x00 +#define F12_FINGER_STATUS 0x01 +#define F12_ACTIVE_STYLUS_STATUS 0x02 +#define F12_PALM_STATUS 0x03 +#define F12_HOVERING_FINGER_STATUS 0x05 +#define F12_GLOVED_FINGER_STATUS 0x06 +#define F12_NARROW_OBJECT_STATUS 0x07 +#define F12_HAND_EDGE_STATUS 0x08 +#define F12_COVER_STATUS 0x0A +#define F12_STYLUS_STATUS 0x0B +#define F12_ERASER_STATUS 0x0C +#define F12_SMALL_OBJECT_STATUS 0x0D + +#define F12_GESTURE_DETECTION_LEN 5 + +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +enum exp_fn { + RMI_DEV = 0, + RMI_FW_UPDATER, + RMI_TEST_REPORTING, + RMI_PROXIMITY, + RMI_ACTIVE_PEN, + RMI_GESTURE, + RMI_VIDEO, + RMI_DEBUG, + RMI_LAST, +}; + +/* + * struct synaptics_rmi4_fn_desc2- function descriptor fields in PDT entry + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_version: version of function + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc2 { + 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]; + }; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for command registers + * @ctrl_base: 16-bit base address for control registers + * @data_base: 16-bit base address for data registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_f11_extra_data - extra data of F$11 + * @data38_offset: offset to F11_2D_DATA38 register + */ +struct synaptics_rmi4_f11_extra_data { + unsigned char data38_offset; +}; + +/* + * struct synaptics_rmi4_f12_extra_data - extra data of F$12 + * @data1_offset: offset to F12_2D_DATA01 register + * @data4_offset: offset to F12_2D_DATA04 register + * @data15_offset: offset to F12_2D_DATA15 register + * @data15_size: size of F12_2D_DATA15 register + * @data15_data: buffer for reading F12_2D_DATA15 register + * @data29_offset: offset to F12_2D_DATA29 register + * @data29_size: size of F12_2D_DATA29 register + * @data29_data: buffer for reading F12_2D_DATA29 register + * @ctrl20_offset: offset to F12_2D_CTRL20 register + */ +struct synaptics_rmi4_f12_extra_data { + unsigned char data1_offset; + unsigned char data4_offset; + unsigned char data15_offset; + unsigned char data15_size; + unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8]; + unsigned char data29_offset; + unsigned char data29_size; + unsigned char data29_data[F12_FINGERS_TO_SUPPORT]; + unsigned char ctrl20_offset; +}; + +/* + * struct synaptics_rmi4_fn - RMI function handler + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + * @extra: pointer to extra data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; + void *extra; +}; + +/* + * struct synaptics_rmi4_input_settings - current input settings + * @num_of_fingers: maximum number of fingers for 2D touch + * @valid_button_count: number of valid 0D buttons + * @max_touch_width: maximum touch width + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + */ +struct synaptics_rmi4_input_settings { + unsigned char num_of_fingers; + unsigned char valid_button_count; + unsigned char max_touch_width; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + bool stylus_enable; + bool eraser_enable; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: RMI protocol major version number + * @version_minor: RMI protocol minor version number + * @manufacturer_id: manufacturer ID + * @product_props: product properties + * @product_info: product information + * @product_id_string: product ID + * @build_id: firmware build ID + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[PRODUCT_INFO_SIZE]; + unsigned char product_id_string[PRODUCT_ID_SIZE + 1]; + unsigned char build_id[BUILD_ID_SIZE]; + struct list_head support_fn_list; +}; + +/* + * struct synaptics_rmi4_data - RMI4 device instance data + * @pdev: pointer to platform device + * @input_dev: pointer to associated input device + * @stylus_dev: pointer to associated stylus device + * @hw_if: pointer to hardware interface data + * @rmi4_mod_info: device information + * @board_prop_dir: /sys/board_properties directory for virtual key map file + * @pwr_reg: pointer to regulator for power control + * @bus_reg: pointer to regulator for bus pullup control + * @rmi4_reset_mutex: mutex for software reset + * @rmi4_report_mutex: mutex for input event reporting + * @rmi4_io_ctrl_mutex: mutex for communication interface I/O + * @rmi4_exp_init_mutex: mutex for expansion function module initialization + * @rmi4_irq_enable_mutex: mutex for enabling/disabling interrupt + * @rb_work: work for rebuilding input device + * @rb_workqueue: workqueue for rebuilding input device + * @fb_notifier: framebuffer notifier client + * @reset_work: work for issuing reset after display framebuffer ready + * @reset_workqueue: workqueue for issuing reset after display framebuffer ready + * @early_suspend: early suspend power management + * @current_page: current RMI page for register access + * @button_0d_enabled: switch for enabling 0d button support + * @num_of_tx: number of Tx channels for 2D touch + * @num_of_rx: number of Rx channels for 2D touch + * @num_of_fingers: maximum number of fingers for 2D touch + * @max_touch_width: maximum touch width + * @valid_button_count: number of valid 0D buttons + * @report_enable: input data to report for F$12 + * @no_sleep_setting: default setting of NoSleep in F01_RMI_CTRL00 register + * @gesture_detection: detected gesture type and properties + * @intr_mask: interrupt enable mask + * @button_txrx_mapping: Tx Rx mapping of 0D buttons + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f$01 + * @f01_cmd_base_addr: command base address for f$01 + * @f01_ctrl_base_addr: control base address for f$01 + * @f01_data_base_addr: data base address for f$01 + * @f51_query_base_addr: query base address for f$51 + * @firmware_id: firmware build ID + * @irq: attention interrupt + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @flash_prog_mode: flag to indicate flash programming mode status + * @irq_enabled: flag to indicate attention interrupt enable status + * @fingers_on_2d: flag to indicate presence of fingers in 2D area + * @suspend: flag to indicate whether in suspend state + * @sensor_sleep: flag to indicate sleep state of sensor + * @stay_awake: flag to indicate whether to stay awake during suspend + * @fb_ready: flag to indicate whether display framebuffer in ready state + * @f11_wakeup_gesture: flag to indicate support for wakeup gestures in F$11 + * @f12_wakeup_gesture: flag to indicate support for wakeup gestures in F$12 + * @enable_wakeup_gesture: flag to indicate usage of wakeup gestures + * @wedge_sensor: flag to indicate use of wedge sensor + * @report_pressure: flag to indicate reporting of pressure data + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + * @external_afe_buttons: flag to indicate presence of external AFE buttons + * @reset_device: pointer to device reset function + * @irq_enable: pointer to interrupt enable function + * @sleep_enable: pointer to sleep enable function + * @report_touch: pointer to touch reporting function + */ +struct synaptics_rmi4_data { + struct platform_device *pdev; + struct input_dev *input_dev; + struct input_dev *stylus_dev; + const struct synaptics_dsx_hw_interface *hw_if; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct synaptics_rmi4_input_settings input_settings; + struct kobject *board_prop_dir; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct mutex rmi4_reset_mutex; + struct mutex rmi4_report_mutex; + struct mutex rmi4_io_ctrl_mutex; + struct mutex rmi4_exp_init_mutex; + struct mutex rmi4_irq_enable_mutex; + struct delayed_work rb_work; + struct workqueue_struct *rb_workqueue; +#ifdef CONFIG_FB + struct notifier_block fb_notifier; + struct work_struct reset_work; + struct workqueue_struct *reset_workqueue; +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_tx; + unsigned char num_of_rx; + unsigned char num_of_fingers; + unsigned char max_touch_width; + unsigned char valid_button_count; + unsigned char report_enable; + unsigned char no_sleep_setting; + unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN]; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned char *button_txrx_mapping; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; +#ifdef F51_DISCRETE_FORCE + unsigned short f51_query_base_addr; +#endif + unsigned int firmware_id; + int irq; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + bool flash_prog_mode; + bool irq_enabled; + bool fingers_on_2d; + bool suspend; + bool sensor_sleep; + bool stay_awake; + bool fb_ready; + bool f11_wakeup_gesture; + bool f12_wakeup_gesture; + bool enable_wakeup_gesture; + bool wedge_sensor; + bool report_pressure; + bool stylus_enable; + bool eraser_enable; + bool external_afe_buttons; + int (*reset_device)(struct i2c_client *client, + bool rebuild); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable, + bool attn_only); + void (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data, + bool enable); + void (*report_touch)(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler); + struct device *s3706_dev; + bool force; +}; + +struct synaptics_dsx_bus_access { + unsigned char type; + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); +}; + +struct synaptics_dsx_hw_interface { + struct synaptics_dsx_board_data *board_data; + const struct synaptics_dsx_bus_access *bus_access; + int (*bl_hw_init)(struct synaptics_rmi4_data *rmi4_data); + int (*ui_hw_init)(struct synaptics_rmi4_data *rmi4_data); +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + int (*init)(struct synaptics_rmi4_data *rmi4_data); + void (*remove)(struct synaptics_rmi4_data *rmi4_data); + void (*reset)(struct synaptics_rmi4_data *rmi4_data); + void (*reinit)(struct synaptics_rmi4_data *rmi4_data); + void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*resume)(struct synaptics_rmi4_data *rmi4_data); + void (*late_resume)(struct synaptics_rmi4_data *rmi4_data); + void (*attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); +}; + +extern unsigned int tp_debug; +int synaptics_rmi4_bus_init(void); +void synaptics_rmi4_bus_exit(void); +void synaptics_rmi4_new_function( + struct synaptics_rmi4_exp_fn *exp_fn_module, + bool insert); +int synaptics_fw_updater( + const unsigned char *fw_data, + const unsigned char *fw_image); +int synaptics_rmi4_fwu_init( + struct synaptics_rmi4_data *rmi4_data, + const unsigned char *fw_image, struct i2c_client *client); +/*Added for larger than 32 length read!*/ +extern int synaptics_rmi4_i2c_read_block( + struct i2c_client *client, + unsigned char addr, + unsigned short length, + unsigned char *data); +extern int synaptics_rmi4_i2c_write_block( + struct i2c_client *client, + unsigned char addr, + unsigned short length, + unsigned char const *data); +static inline int synaptics_rmi4_reg_read( + struct i2c_client *i2c, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return synaptics_rmi4_i2c_read_block(i2c, addr, len, data); +} + +static inline int synaptics_rmi4_reg_write( + struct i2c_client *i2c, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return synaptics_rmi4_i2c_write_block(i2c, addr, len, data); +} + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + dev_warn(dev, "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + dev_warn(dev, "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +#endif diff --git a/drivers/input/touchscreen/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_fw_update.c new file mode 100644 index 000000000000..93db8d645bd8 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx_fw_update.c @@ -0,0 +1,4090 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define FW_IHEX_NAME "tp/fw_synaptics_17819.img" +#define FW_IMAGE_NAME "tp/fw_synaptics_17819.img" + +/*#define DO_STARTUP_FW_UPDATE*/ +/*delete by morgan.gu for sdm845 */ +#undef CONFIG_FB + +#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 + +/*********************for Debug LOG switch*******************/ +#define TPD_DEVICE "synaptics,s3320_firmware" +#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) + +#define MAX_WRITE_SIZE 4096 + +#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 MAX_UTILITY_PARAMS 20 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#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_CONFIG_ID_SIZE 32 + +#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_FLASH_STATUS_OFFSET 5 +#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 5000 +#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 + +static int fwu_do_reflash(void); + +static int fwu_recovery_check_status(void); + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +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, + UPP_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +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, + EXTERNAL_TOUCH_AFE_CONFIG_PARTITION, + UTILITY_PARAMETER_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_WRITE_BOOTLOADER, + CMD_WRITE_UTILITY_PARAM, + 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_ERASE_BOOTLOADER, + CMD_ERASE_UTILITY_PARAMETER, + 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, + EXTERNAL_TOUCH_AFE_CONFIG_CONTAINER, + UTILITY_CONTAINER, + UTILITY_PARAMETER_CONTAINER, +}; + +enum utility_parameter_id { + UNUSED = 0, + FORCE_PARAMETER, + ANTI_BENDING_PARAMETER, +}; + +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_data0 { + union { + struct { + unsigned char operation_status:5; + unsigned char device_cfg_status:2; + unsigned char bl_mode:1; + } __packed; + unsigned char data[1]; + }; +}; + +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 f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char reserved:7; + } __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 pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; + unsigned short total_count; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; +}; + +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 image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_guest_code; + bool contains_disp_config; + bool contains_perm_config; + bool contains_flash_config; + bool contains_utility_param; + 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]; + unsigned char utility_param_id[MAX_UTILITY_PARAMS]; + struct block_data bootloader; + struct block_data utility; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data pm_config; + struct block_data fl_config; + struct block_data bl_image; + struct block_data bl_config; + struct block_data utility_param[MAX_UTILITY_PARAMS]; + struct block_data lockdown; + struct block_data guest_code; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool bl_mode_device; + bool force_update; + bool do_lockdown; + bool has_guest_code; + bool has_utility_param; + bool new_partition_table; + bool incompatible_partition_tables; + bool write_bootloader; + 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 config_id[32]; + unsigned char flash_status; + unsigned char partitions; +#ifdef F51_DISCRETE_FORCE + unsigned char *cal_data; + unsigned short cal_data_off; + unsigned short cal_data_size; + unsigned short cal_data_buf_size; + unsigned short cal_packet_data_size; +#endif + 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; + 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_desc2 f34_fd; + struct synaptics_rmi4_fn_desc2 f35_fd; + struct synaptics_rmi4_data *rmi4_data; + struct workqueue_struct *fwu_workqueue; + struct work_struct fwu_work; + struct i2c_client *client; + unsigned char bl_reset_add; +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +DECLARE_COMPLETION(fwu_remove_complete); + +static void calculate_checksum(unsigned short *data, unsigned long len, + unsigned long *result) +{ + unsigned long temp; + unsigned long sum1 = 0xffff; + unsigned long sum2 = 0xffff; + + *result = 0xffffffff; + + while (len--) { + temp = *data; + sum1 += temp; + sum2 += sum1; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + data++; + } + + *result = sum2 << 16 | sum1; + +} + +static void convert_to_little_endian(unsigned char *dest, unsigned long src) +{ + dest[0] = (unsigned char)(src & 0xff); + dest[1] = (unsigned char)((src >> 8) & 0xff); + dest[2] = (unsigned char)((src >> 16) & 0xff); + dest[3] = (unsigned char)((src >> 24) & 0xff); + +} + +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; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_f51_force_data_init(void) +{ + int retval; + unsigned char query_count; + unsigned char packet_info; + unsigned char offset[2]; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(fwu->client, + rmi4_data->f51_query_base_addr + 7, + offset, + sizeof(offset)); + if (retval < 0) { + TPD_ERR("%s: Failed to read force data offset\n", + __func__); + return retval; + } + + fwu->cal_data_off = offset[0] | offset[1] << 8; + + retval = synaptics_rmi4_reg_read(fwu->client, + rmi4_data->f51_query_base_addr, + &query_count, + sizeof(query_count)); + if (retval < 0) { + TPD_ERR("%s: Failed to read number of F51 query registers\n", + __func__); + return retval; + } + + if (query_count >= 10) { + retval = synaptics_rmi4_reg_read(fwu->client, + rmi4_data->f51_query_base_addr + 9, + &packet_info, + sizeof(packet_info)); + if (retval < 0) { + TPD_ERR("%s: Failed to read F51 packet register info\n", + __func__); + return retval; + } + + if (packet_info & MASK_1BIT) { + fwu->cal_packet_data_size = packet_info >> 1; + fwu->cal_packet_data_size *= 2; + } else { + fwu->cal_packet_data_size = 0; + } + } else { + fwu->cal_packet_data_size = 0; + } + + fwu->cal_data_size = CAL_DATA_SIZE + fwu->cal_packet_data_size; + if (fwu->cal_data_size > fwu->cal_data_buf_size) { + kfree(fwu->cal_data); + fwu->cal_data_buf_size = fwu->cal_data_size; + fwu->cal_data = kmalloc(fwu->cal_data_buf_size, GFP_KERNEL); + if (!fwu->cal_data) { + TPD_ERR("%s: Failed to alloc mem for fwu->cal_data\n", + __func__); + fwu->cal_data_buf_size = 0; + return -ENOMEM; + } + } + + return 0; +} +#endif + +static int fwu_allocate_read_config_buf(unsigned int count) +{ + + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + TPD_ERR("%s:Failed to alloc mem read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + + return 0; +} + +static void fwu_compare_partition_tables(void) +{ + fwu->incompatible_partition_tables = false; + + if (fwu->phyaddr.bl_image != fwu->img.phyaddr.bl_image) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.lockdown != fwu->img.phyaddr.lockdown) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.bl_config != fwu->img.phyaddr.bl_config) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.utility_param != fwu->img.phyaddr.utility_param) + fwu->incompatible_partition_tables = true; + + if (fwu->bl_version == BL_V7) { + if (fwu->phyaddr.fl_config != fwu->img.phyaddr.fl_config) + fwu->incompatible_partition_tables = true; + } + + fwu->new_partition_table = false; + + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) + fwu->new_partition_table = true; + else if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) + fwu->new_partition_table = true; + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) + fwu->new_partition_table = true; + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) + fwu->new_partition_table = true; + } + +} + +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); + blkcount->total_count += partition_length; + 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); + blkcount->total_count += partition_length; + break; + case BOOTLOADER_PARTITION: + blkcount->bl_image = partition_length; + phyaddr->bl_image = physical_address; + TPD_DEBUG("%s: Bootloader block count: %d\n", + __func__, blkcount->bl_image); + blkcount->total_count += partition_length; + break; + case UTILITY_PARAMETER_PARTITION: + blkcount->utility_param = partition_length; + phyaddr->utility_param = physical_address; + TPD_DEBUG("%s: Utility parameter block count: %d\n", + __func__, blkcount->utility_param); + blkcount->total_count += partition_length; + 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); + blkcount->total_count += partition_length; + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + phyaddr->fl_config = physical_address; + TPD_DEBUG("%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + blkcount->total_count += partition_length; + 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); + blkcount->total_count += partition_length; + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + phyaddr->pm_config = physical_address; + TPD_DEBUG("%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + blkcount->total_count += partition_length; + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + phyaddr->bl_config = physical_address; + TPD_DEBUG("%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + blkcount->total_count += partition_length; + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + phyaddr->lockdown = physical_address; + TPD_DEBUG("%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + blkcount->total_count += partition_length; + break; + }; + } + +} + +static void fwu_parse_image_header_10_utility(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.utility.size / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + if (ii >= MAX_UTILITY_PARAMS) + continue; + addr = le_to_uint(fwu->img.utility.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 UTILITY_PARAMETER_CONTAINER: + fwu->img.utility_param[ii].data = content; + fwu->img.utility_param[ii].size = length; + fwu->img.utility_param_id[ii] = content[0]; + break; + default: + break; + }; + } + +} + +static void fwu_parse_image_header_10_bootloader(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_IMAGE_CONTAINER: + fwu->img.bl_image.data = content; + fwu->img.bl_image.size = length; + break; + 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; + }; + } + +} + +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; + + TPD_DEBUG("%s: enter\n", __func__); + 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; + 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_bootloader(image); + break; + case UTILITY_CONTAINER: + fwu->img.utility.data = content; + fwu->img.utility.size = length; + fwu_parse_image_header_10_utility(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 PERMANENT_CONFIG_CONTAINER: + case GUEST_SERIALIZATION_CONTAINER: + fwu->img.contains_perm_config = true; + fwu->img.pm_config.data = content; + fwu->img.pm_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; + } + } + +} + +static void fwu_parse_image_header_05_06(void) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + + TPD_ERR("%s: enter\n", __func__); + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + TPD_ERR("%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + TPD_ERR("%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; + +} + +static int fwu_parse_image_info(void) +{ + struct image_header_10 *header; + + TPD_DEBUG("%s: enter\n", __func__); + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(); + break; + default: + TPD_ERR("%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (!fwu->img.contains_flash_config) { + TPD_ERR("%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + if (fwu->img.blkcount.utility_param) + fwu->img.contains_utility_param = true; + + fwu_compare_partition_tables(); + } else { + fwu->new_partition_table = false; + fwu->incompatible_partition_tables = false; + } + + return 0; +} + +static int fwu_read_flash_status(void) +{ + int retval; + unsigned char status; + unsigned char command; + + TPD_DEBUG("%s: enter\n", __func__); + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash status\n", + __func__); + return retval; + } + + 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->bl_version == BL_V8) + fwu->flash_status = status & MASK_5BIT; + + if (fwu->write_bootloader) + fwu->flash_status = 0x00; + + if (fwu->flash_status != 0x00) { + TPD_ERR("%s: Flash status = %d, command = 0x%02x\n", + __func__, fwu->flash_status, fwu->command); + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (fwu->flash_status == 0x08) + fwu->flash_status = 0x00; + } + + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash command\n", + __func__); + return retval; + } + + 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->bl_version == BL_V8) + fwu->command = command; + + if (fwu->write_bootloader) + fwu->command = 0x00; + + 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; + + TPD_DEBUG("%s: enter\n", __func__); + 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\n", + __func__); + + return -ETIMEDOUT; +} + +static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd) +{ + int retval; + unsigned char data_base; + struct f34_v7_data_1_5 data_1_5; + + + data_base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + 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_ERASE_BOOTLOADER: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UTILITY_PARAMETER: + data_1_5.partition_id = UTILITY_PARAMETER_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_reg_write(fwu->client, + data_base + fwu->off.partition_id, + data_1_5.data, + sizeof(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 data_base; + unsigned char command; + + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_LOCKDOWN: + case CMD_WRITE_GUEST_CODE: + case CMD_WRITE_BOOTLOADER: + case CMD_WRITE_UTILITY_PARAM: + 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: + case CMD_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + 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_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + 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_reg_write(fwu->client, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char command; + + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; + default: + TPD_ERR("%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + TPD_ERR("%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_command(cmd); + else + retval = fwu_write_f34_v5v6_command(cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char partition = 0; + + data_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; + else if (fwu->config_area == UPP_AREA) + partition = UTILITY_PARAMETER_PARTITION; + break; + case CMD_WRITE_LOCKDOWN: + partition = DEVICE_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_WRITE_BOOTLOADER: + partition = BOOTLOADER_PARTITION; + break; + case CMD_WRITE_UTILITY_PARAM: + partition = UTILITY_PARAMETER_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_ERASE_BOOTLOADER: + partition = BOOTLOADER_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_reg_write(fwu->client, + data_base + fwu->off.partition_id, + &partition, + sizeof(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; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_partition_id(cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table(unsigned char *partition_table) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short block_number = 0; + + data_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_reg_write(fwu->client, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(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_reg_write(fwu->client, + data_base + fwu->off.transfer_length, + length, + sizeof(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_reg_read(fwu->client, + data_base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + 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 query_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; + + query_base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(fwu->client, + query_base, + query_0.data, + sizeof(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_reg_read(fwu->client, + query_base + offset, + query_1_7.data, + sizeof(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; + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + + 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; + + 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++; + } + + TPD_DEBUG("%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + TPD_ERR("%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(ptable); + if (retval < 0) { + TPD_ERR("%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr); + + if (fwu->blkcount.dp_config) + fwu->flash_properties.has_disp_config = 1; + else + fwu->flash_properties.has_disp_config = 0; + + if (fwu->blkcount.pm_config) + fwu->flash_properties.has_pm_config = 1; + else + fwu->flash_properties.has_pm_config = 0; + + if (fwu->blkcount.bl_config) + fwu->flash_properties.has_bl_config = 1; + else + fwu->flash_properties.has_bl_config = 0; + + if (fwu->blkcount.guest_code) + fwu->has_guest_code = 1; + else + fwu->has_guest_code = 0; + + if (fwu->blkcount.utility_param) + fwu->has_utility_param = 1; + else + fwu->has_utility_param = 0; + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries(void) +{ + int retval; + unsigned char count; + unsigned char query_base; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + + query_base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + TPD_ERR("%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + TPD_ERR("%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + TPD_ERR("%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(fwu->client, + query_base + fwu->off.gc_block_count, + buf, + 2); + if (retval < 0) { + TPD_ERR("%s: Failed to read guest count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + fwu->has_guest_code = true; + } + } + + fwu->has_utility_param = false; + + 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)); + + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(); + else + retval = fwu_read_f34_v5v6_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 data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short left_bytes; + unsigned short write_size; + unsigned short max_write_size; + + TPD_DEBUG("%s enter\n", __func__); + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + TPD_ERR("%s:Failed write transfer length remaining %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s:Failedwrite command remaining%d)\n", + __func__, remaining); + return retval; + } + +#ifdef MAX_WRITE_SIZE + max_write_size = MAX_WRITE_SIZE; + if (max_write_size >= transfer * fwu->block_size) + max_write_size = transfer * fwu->block_size; + else if (max_write_size > fwu->block_size) + max_write_size -= max_write_size % fwu->block_size; + else + max_write_size = fwu->block_size; +#else + max_write_size = transfer * fwu->block_size; +#endif + left_bytes = transfer * fwu->block_size; + + do { + if (left_bytes / max_write_size) + write_size = max_write_size; + else + write_size = left_bytes; + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.payload, + block_ptr, + write_size); + if (retval < 0) { + TPD_ERR("%s: Failed to write (remaining = %d)\n", + __func__, remaining); + return retval; + } + + block_ptr += write_size; + left_bytes -= write_size; + } while (left_bytes); + /*moragan the pollingwap to increase firmware time */ + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + /*#endif */ + if (retval < 0) { + TPD_ERR("%s: Failed to wait idle (remaining %d)\n", + __func__, remaining); + return retval; + } + + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + TPD_ERR("%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + TPD_ERR("%s Failed wait idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_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 data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + TPD_ERR("%s: Failed to writeblocknumber\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + TPD_ERR("%s Failed write transferlengthremaining%d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s:Failedwrite command remaining %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + TPD_ERR("%s:Failed to wait dle remaining %d)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(fwu->client, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + TPD_ERR("%s:Failed read dataremaining%d)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(fwu->client, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + TPD_ERR("%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(fwu->client, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + TPD_ERR("%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_read_f34_v7_blocks(block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(block_cnt, cmd); + + return retval; +} + +static int fwu_get_image_firmware_id(unsigned int *fw_id) +{ + int retval; + unsigned char index = 0; + char *strptr; + char *firmware_id; + + TPD_DEBUG("%s enter\n", __func__); + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + } else { + strptr = strnstr(fwu->image_name, "PR", MAX_IMAGE_NAME_LEN); + if (!strptr) { + TPD_ERR("%s: No valid PR number found in image name(%s)\n", + __func__, fwu->image_name); + return -EINVAL; + } + TPD_DEBUG("%s enter 2 strptr %s\n", __func__, strptr); + strptr += 2; + firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL); + if (!firmware_id) { + TPD_ERR("%s: Failed to alloc mem for firmware_id\n", + __func__); + return -ENOMEM; + } + while (strptr[index] >= '0' && strptr[index] <= '9') { + firmware_id[index] = strptr[index]; + index++; + } + TPD_DEBUG("%s enter 3 firmware_id %s\n", + __func__, firmware_id); + retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id); + kfree(firmware_id); + if (retval) { + TPD_ERR("%s: Failed to obtain image firmware ID\n", + __func__); + return -EINVAL; + } + } + + return 0; +} + +static int fwu_get_device_config_id(void) +{ + int retval; + unsigned char config_id_size; + + TPD_DEBUG("%s enter\n", __func__); + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(void) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%s enter\n", __func__); + + if (rmi4_data->force) { + flash_area = UI_FIRMWARE; + TPD_ERR("%s force update firmware\n", __func__); + goto exit; + } + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->bl_mode_device) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device firmware ID */ + device_fw_id = rmi4_data->firmware_id; + TPD_ERR("%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(&image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + TPD_ERR("%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id != device_fw_id) { + flash_area = UI_FIRMWARE; + TPD_ERR("%s Device firmware ID & Image firmware ID aren't same, updating firmware\n", + __func__); + goto exit; + } + /* Get device config ID */ + retval = fwu_get_device_config_id(); + if (retval < 0) { + TPD_ERR("%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + TPD_ERR("%s: No need to do reflash\n", + __func__); + } else { + TPD_ERR("%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc2 rmi_fd; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + char add_buf[6] = { 0 }; + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(fwu->client, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + TPD_DEBUG("%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + TPD_ERR("%s:Unrecog F34 V\n", + __func__); + return -EINVAL; + } + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + fwu->f35_fd.cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + TPD_ERR("%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + TPD_ERR("%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + TPD_ERR("%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(); + return 0; + } + } + + rmi4_data->intr_mask[0] |= fwu->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(fwu->client, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + TPD_ERR("%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + retval = synaptics_rmi4_i2c_read_block(fwu->client, + 0xE3, sizeof(add_buf), add_buf); + fwu->bl_reset_add = add_buf[1];/*0xe4*/ + return 0; +} + +static int fwu_enter_flash_prog(void) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%s enter\n", __func__); + retval = fwu_read_flash_status(); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) + return 0; + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + return retval; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false); + if (retval < 0) + return retval; + + if (!fwu->in_bl_mode) { + TPD_ERR("%s: BL mode not entered\n", + __func__); + return -EINVAL; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return retval; + + retval = fwu_read_f34_queries(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(fwu->client, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read F01 device control\n", + __func__); + return retval; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(fwu->client, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to write F01 device control\n", + __func__); + return retval; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); + + 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_dp_configuration_size(void) +{ + unsigned short block_count; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + TPD_ERR("%s: Display 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_check_guest_code_size(void) +{ + unsigned short block_count; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + TPD_ERR("%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +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; + case FLASH_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + break; + case UPP_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UTILITY_PARAMETER); + if (retval < 0) + return retval; + default: + TPD_ERR("%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + TPD_DEBUG("%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_DEBUG("%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_bootloader(void) +{ + int retval; + + retval = fwu_write_f34_command(CMD_ERASE_BOOTLOADER); + if (retval < 0) + return retval; + + TPD_DEBUG("%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_DEBUG("%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_guest_code(void) +{ + int retval; + + retval = fwu_write_f34_command(CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + TPD_DEBUG("%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_DEBUG("%s: Idle status detected\n", + __func__); + + return 0; +} + +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_DEBUG("%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_DEBUG("%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_DEBUG("%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + TPD_DEBUG("%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code) { + retval = fwu_erase_guest_code(); + if (retval < 0) + return retval; + } + + 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_write_bootloader(void) +{ + int retval; + unsigned short bootloader_block_count; + + bootloader_block_count = fwu->img.bl_image.size / fwu->block_size; + + fwu->write_bootloader = true; + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.bl_image.data, + bootloader_block_count, CMD_WRITE_BOOTLOADER); + fwu->write_bootloader = false; + + return retval; +} + +static int fwu_write_utility_parameter(void) +{ + int retval; + unsigned char ii; + unsigned char checksum_array[4]; + unsigned char *pbuf; + unsigned short remaining_size; + unsigned short utility_param_size; + unsigned long checksum; + + utility_param_size = fwu->blkcount.utility_param * fwu->block_size; + retval = fwu_allocate_read_config_buf(utility_param_size); + if (retval < 0) + return retval; + memset(fwu->read_config_buf, 0x00, utility_param_size); + + pbuf = fwu->read_config_buf; + remaining_size = utility_param_size - 4; + + for (ii = 0; ii < MAX_UTILITY_PARAMS; ii++) { + if (fwu->img.utility_param_id[ii] == UNUSED) + continue; + +#ifdef F51_DISCRETE_FORCE + if (fwu->img.utility_param_id[ii] == FORCE_PARAMETER) { + if (fwu->bl_mode_device) { + TPD_ERR("%s:skipping calibration data restoration\n", + __func__); + goto image_param; + } + retval = secure_memcpy(&(pbuf[4]), + remaining_size - 4, + fwu->cal_data, + fwu->cal_data_buf_size, + fwu->cal_data_size); + if (retval < 0) { + TPD_ERR("%s:Failed to copy force calibration data\n", + __func__); + return retval; + } + pbuf[0] = FORCE_PARAMETER; + pbuf[1] = 0x00; + pbuf[2] = (4 + fwu->cal_data_size) / 2; + pbuf += (fwu->cal_data_size + 4); + remaining_size -= (fwu->cal_data_size + 4); + continue; + } +image_param: +#endif + + retval = secure_memcpy(pbuf, + remaining_size, + fwu->img.utility_param[ii].data, + fwu->img.utility_param[ii].size, + fwu->img.utility_param[ii].size); + if (retval < 0) { + TPD_ERR("%s: Failed to copy utility parameter data\n", + __func__); + return retval; + } + pbuf += fwu->img.utility_param[ii].size; + remaining_size -= fwu->img.utility_param[ii].size; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((utility_param_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[utility_param_size - 4] = checksum_array[0]; + fwu->read_config_buf[utility_param_size - 3] = checksum_array[1]; + fwu->read_config_buf[utility_param_size - 2] = checksum_array[2]; + fwu->read_config_buf[utility_param_size - 1] = checksum_array[3]; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->read_config_buf, + fwu->blkcount.utility_param, CMD_WRITE_UTILITY_PARAM); + 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_dp_configuration(void) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_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; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + 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_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + rmi4_data->reset_device(fwu->client, false); + + return 0; +} + +static int fwu_write_guest_code(void) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(void) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%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_configuration(); + if (retval < 0) + return retval; + TPD_ERR("%s enter 1\n ", __func__); + rmi4_data->reset_device(fwu->client, false); + + return 0; +} + +static int fwu_write_partition_table_v7(void) +{ + int retval; + unsigned short block_count; + + TPD_DEBUG("%s enter\n", __func__); + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(); + 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_write_bl_area_v7(void) +{ + int retval; + bool has_utility_param; + + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%s enter\n ", __func__); + + has_utility_param = fwu->has_utility_param; + + if (fwu->has_utility_param) { + fwu->config_area = UPP_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + fwu->config_area = BL_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = FLASH_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_erase_bootloader(); + if (retval < 0) + return retval; + + retval = fwu_write_bootloader(); + if (retval < 0) + return retval; + /*msleep(rmi4_data->hw_if->board_data->reset_delay_ms);*/ + rmi4_data->reset_device(fwu->client, false); + + 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; + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + rmi4_data->reset_device(fwu->client, false); + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->img.bl_config.data; + 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; + + if (fwu->img.contains_utility_param) { + retval = fwu_write_utility_parameter(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_do_reflash(void) +{ + int retval; + bool do_bl_update = false; + + TPD_DEBUG("%s enter\n", __func__); + 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; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(); + if (retval < 0) + return retval; + } + + if (!fwu->has_utility_param && fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (fwu->has_utility_param && !fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (!do_bl_update && fwu->incompatible_partition_tables) { + TPD_ERR("%s: Incompatible partition tables\n", + __func__); + return -EINVAL; + } else if (!do_bl_update && fwu->new_partition_table) { + if (!fwu->force_update) { + TPD_ERR("%s: Partition table mismatch\n", + __func__); + return -EINVAL; + } + } + + retval = fwu_erase_all(); + if (retval < 0) + return retval; + + if (do_bl_update) { + retval = fwu_write_bl_area_v7(); + if (retval < 0) + return retval; + TPD_ERR("%s: Bootloader area programmed\n", __func__); + } else if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(); + if (retval < 0) + return retval; + TPD_ERR("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(); + if (retval < 0) + return retval; + TPD_ERR("%s: Partition table programmed\n", __func__); + } + + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + TPD_ERR("%s: Firmware programmed\n", __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_write_ui_configuration(); + if (retval < 0) + return retval; + TPD_ERR("%s: Configuration programmed\n", __func__); + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(); + if (retval < 0) + return retval; + TPD_ERR("%s: Display configuration programmed\n", __func__); + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(); + if (retval < 0) + return retval; + TPD_ERR("%s: Guest code programmed\n", __func__); + } + + return retval; +} + +static int fwu_do_lockdown_v7(void) +{ + int retval; + struct f34_v7_data0 status; + + TPD_DEBUG("%s enter\n", __func__); + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + status.data, + sizeof(status.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash status\n", + __func__); + return retval; + } + + if (status.device_cfg_status == 2) { + TPD_ERR("%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +static int fwu_do_lockdown_v5v6(void) +{ + int retval; + + TPD_DEBUG("%s enter\n", __func__); + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + TPD_ERR("%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_do_restore_f51_cal_data(void) +{ + int retval; + unsigned char checksum_array[4]; + unsigned short block_count; + unsigned long checksum; + + block_count = fwu->blkcount.ui_config; + fwu->config_size = fwu->block_size * block_count; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = secure_memcpy(&fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data, + fwu->cal_data_buf_size, fwu->cal_data_size); + if (retval < 0) { + TPD_ERR("%s: Failed to restore calibration data\n", + __func__); + return retval; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((fwu->config_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[fwu->config_size - 4] = checksum_array[0]; + fwu->read_config_buf[fwu->config_size - 3] = checksum_array[1]; + fwu->read_config_buf[fwu->config_size - 2] = checksum_array[2]; + fwu->read_config_buf[fwu->config_size - 1] = checksum_array[3]; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} +#endif + +static int fwu_start_reflash(const unsigned char *fw_image) +{ + int retval = 0; + enum flash_area flash_area; + bool do_rebuild = false; + /*const struct firmware *fw_entry = NULL;*/ + unsigned char temp; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%s: enter\n", __func__); + if (rmi4_data->sensor_sleep) { + TPD_ERR("%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + /*mutex_lock(&rmi4_data->rmi4_exp_init_mutex);*/ + + TPD_ERR("%s: Start of reflash process\n", __func__); + + fwu->image = fw_image; + + #ifdef OLD_WAY_SYNA_REQ_FIRM + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IMAGE_NAME, sizeof(FW_IMAGE_NAME), + sizeof(FW_IMAGE_NAME)); + if (retval < 0) { + TPD_ERR("%s: Failed to copy image file name\n", + __func__); + goto exit; + } + TPD_ERR("%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->s3706_dev); + if (retval != 0) { + TPD_ERR("%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + TPD_ERR("%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + } + #endif + + retval = fwu_parse_image_info(); + if (retval < 0) + goto exit; + + if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) { + TPD_ERR("%s: Flash size mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (fwu->bl_version != fwu->img.bl_version) { + TPD_ERR("%s: Bootloader version mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_read_flash_status(); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + fwu->bl_mode_device = true; + TPD_ERR("%s: Device in bootloader mode\n", + __func__); + } else { + fwu->bl_mode_device = false; + } + + flash_area = fwu_go_nogo(); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(); + if (retval < 0) { + rmi4_data->reset_device(fwu->client, false); + goto exit; + } + } +#ifdef F51_DISCRETE_FORCE + if (flash_area != NONE && !fwu->bl_mode_device) { + fwu->config_size = fwu->block_size * fwu->blkcount.ui_config; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) { + rmi4_data->reset_device(fwu->client, false); + goto exit; + } + + retval = fwu_read_f34_blocks(fwu->blkcount.ui_config, + CMD_READ_CONFIG); + if (retval < 0) { + rmi4_data->reset_device(fwu->client, false); + goto exit; + } + + retval = secure_memcpy(fwu->cal_data, fwu->cal_data_buf_size, + &fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data_size); + if (retval < 0) { + TPD_ERR("%s: Failed to save calibration data\n", + __func__); + rmi4_data->reset_device(fwu->client, false); + goto exit; + } + } +#endif + + switch (flash_area) { + case UI_FIRMWARE: + do_rebuild = true; + retval = fwu_do_reflash(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param || fwu->img.contains_utility_param) + break; + + rmi4_data->reset_device(fwu->client, false); + + if (fwu->bl_mode_device || fwu->in_bl_mode) { + TPD_ERR("%s:bl mode skip calibration data restoration\n", + __func__); + break; + } + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case UI_CONFIG: + do_rebuild = true; + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param) + break; + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case NONE: + default: + break; + } + + if (retval < 0) { + do_rebuild = false; + rmi4_data->reset_device(fwu->client, false); + TPD_ERR("%s: Failed to do reflash\n", + __func__); + goto exit; + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown_v5v6(); + if (retval < 0) { + TPD_ERR("%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(fwu->client, false); + break; + case BL_V7: + case BL_V8: + retval = fwu_do_lockdown_v7(); + if (retval < 0) { + TPD_ERR("%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(fwu->client, false); + break; + default: + break; + } + } + +exit: + #ifdef OLD_WAY_SYNA_REQ_FIRM + if (fw_entry) + release_firmware(fw_entry); + #endif + /*if (do_rebuild)*/ + rmi4_data->reset_device(fwu->client, true); + temp = 0x01; + synaptics_rmi4_reg_write(fwu->client, + fwu->bl_reset_add, &temp, 1);/*reset tp*/ + + TPD_ERR("%s: End of reflash process\n", __func__); + + /*mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);*/ + + rmi4_data->stay_awake = false; + + return retval; +} + +static int fwu_recovery_check_status(void) +{ + int retval; + unsigned char data_base; + unsigned char status; + + data_base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(fwu->client, + data_base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + TPD_ERR("%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_5BIT; + + if (status != 0x00) { + TPD_ERR("%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +static int fwu_recovery_erase_completion(void) +{ + int retval; + unsigned char data_base; + unsigned char command; + unsigned char status; + unsigned int timeout = F35_ERASE_ALL_WAIT_MS / 20; + + data_base = fwu->f35_fd.data_base_addr; + TPD_DEBUG("%s enter\n", __func__); + do { + command = 0x01; + retval = synaptics_rmi4_reg_write(fwu->client, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to issue command\n", + __func__); + return retval; + } + + do { + retval = synaptics_rmi4_reg_read(fwu->client, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to read command status\n", + __func__); + return retval; + } + + if ((command & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + + if (timeout == 0) + goto exit; + + retval = synaptics_rmi4_reg_read(fwu->client, + data_base + F35_FLASH_STATUS_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash status\n", + __func__); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + +exit: + if (timeout == 0) { + TPD_ERR("%s: Timed out waiting for flash erase completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int fwu_recovery_erase_all(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_ERASE_ALL; + + TPD_DEBUG("%s enter\n", __func__); + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(fwu->client, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + if (fwu->f35_fd.cmd_base_addr) { + retval = fwu_recovery_erase_completion(); + if (retval < 0) + return retval; + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = fwu_recovery_check_status(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(fwu->client, + ctrl_base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + TPD_ERR("%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(fwu->client, + ctrl_base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + TPD_ERR("%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(); + if (retval < 0) { + TPD_ERR("%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_RESET; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(fwu->client, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + TPD_ERR("%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery(void) +{ + int retval = 0; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + TPD_DEBUG("%s enter", __func__); + if (rmi4_data->sensor_sleep) { + TPD_ERR("%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + /*mutex_lock(&rmi4_data->rmi4_exp_init_mutex);*/ + + pr_notice("%s: Start of recovery process\n", __func__); + + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IHEX_NAME, sizeof(FW_IHEX_NAME), + sizeof(FW_IHEX_NAME)); + if (retval < 0) { + TPD_ERR("%s: Failed to copy ihex file name\n", + __func__); + goto exit; + } + TPD_ERR("%s: Requesting firmware ihex %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + TPD_ERR("%s: Firmware ihex %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + TPD_ERR("%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + /*retval = rmi4_data->irq_enable(rmi4_data, false, false);*/ + if (retval < 0) { + TPD_ERR("%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + retval = fwu_recovery_erase_all(); + if (retval < 0) { + TPD_ERR("%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(); + if (retval < 0) { + TPD_ERR("%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(); + if (retval < 0) { + TPD_ERR("%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + rmi4_data->reset_device(fwu->client, true); + + retval = 0; + +exit: + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of recovery process\n", __func__); + + /*mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);*/ + + rmi4_data->stay_awake = false; + + return retval; +} + +int synaptics_fw_updater(const unsigned char *fw_data, + const unsigned char *fw_image) +{ + int retval; + + TPD_DEBUG("%s enter\n", __func__); + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) { + fwu->image = NULL; + retval = fwu_start_recovery(); + if (retval < 0) + return retval; + } + + fwu->image = fw_data; + + retval = fwu_start_reflash(fw_image); + + fwu->image = NULL; + + return retval; +} +EXPORT_SYMBOL(synaptics_fw_updater); + +static int fwu_startup_fw_update_work(const unsigned char *fw_image) +{ + int retval; + +#ifdef WAIT_FOR_FB_READY + unsigned int timeout; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; +#endif + +#ifdef WAIT_FOR_FB_READY + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1; + + while (!rmi4_data->fb_ready) { + msleep(FB_READY_WAIT_MS); + timeout--; + if (timeout == 0) { + TPD_ERR("%s: Timed out waiting for FB ready\n", + __func__); + return -EINVAL; + } + } +#endif + + retval = synaptics_fw_updater(NULL, fw_image); + + return retval; +} + +int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data, + const unsigned char *fw_image, struct i2c_client *client) +{ + int retval; + /*unsigned char attr_count;*/ + struct pdt_properties pdt_props; + + if (!fw_image || !rmi4_data || !client) { + TPD_ERR("%s: fw_image rmi4_data client is null\n", __func__); + retval = -ENOMEM; + goto exit; + } + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + TPD_ERR("%s: Failed to alloc mem for fwu\n", __func__); + retval = -ENOMEM; + goto exit; + } + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + TPD_ERR("%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + fwu->rmi4_data = rmi4_data; + fwu->client = client; + retval = synaptics_rmi4_reg_read(fwu->client, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + TPD_ERR("%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + TPD_ERR("%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + retval = fwu_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(); + if (retval < 0) { + TPD_ERR("%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->initialized = true; + + retval = fwu_startup_fw_update_work(fw_image); +#ifdef F51_DISCRETE_FORCE + fwu_read_flash_status(); + if (!fwu->in_bl_mode) { + retval = fwu_f51_force_data_init(); + if (retval < 0) + goto exit_free_mem; + } +#endif + + return retval; + +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + fwu = NULL; + +exit: + return retval; +} + diff --git a/drivers/input/touchscreen/synaptics_redremote.h b/drivers/input/touchscreen/synaptics_redremote.h new file mode 100644 index 000000000000..57428a319d47 --- /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 \ No newline at end of file diff --git a/drivers/input/touchscreen/synaptics_s3320_redremote.c b/drivers/input/touchscreen/synaptics_s3320_redremote.c new file mode 100644 index 000000000000..23d35341d225 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_s3320_redremote.c @@ -0,0 +1,984 @@ +/* + * 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 (sscanf(buf, "%u", &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 (sscanf(buf, "%u", &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 (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_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(5); + } + + 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(5); + } + + 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(5); + } + + 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[2]; + 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(5); + } + msleep(10); + 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; + //printk("synap %s\n",__func__); + 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[16]; + struct rmidev_data *dev_data = filp->private_data; + //printk("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; + + 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[16]; + struct rmidev_data *dev_data = filp->private_data; + printk("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(); + + printk("synap %s\n",__func__); + if(!create) + goto err_sysfs_attrs ; + + if(rmidev->sysfs_dir) + return 0 ; + + if(!input_dev) + return -1; + /* + retval = gpio_export(remote_rmi4_get_irq_gpio(), false); + if (retval < 0) { + dev_err(device_ptr, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(input_dev->dev), + "attn", remote_rmi4_get_irq_gpio()); + if (retval < 0) { + dev_err(device_ptr, + "%s Failed to create gpio symlink\n", + __func__); + } + } + */ + 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 -1; + } + + + 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); + printk("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__); + } + + return; +} + +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) + { + printk("%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) +{ + if(pdata) + kfree(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) + { + printk("%s:pdata is null\n",__func__); + return -1; + } + if(rmidev) + { + printk("%s:remote device has register already null\n",__func__); + return -1; + } + 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); + + + return; +} + +/* +static int __init rmidev_module_init(void) +{ + rmidev_init_device(); + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + rmidev_remove_device(); + + return; +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); +*/ + diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 469649eb78e7..51fa7d753ab4 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -268,6 +269,7 @@ struct arm_smmu_device { #define ARM_SMMU_OPT_DISABLE_ATOS (1 << 7) #define ARM_SMMU_OPT_NO_DYNAMIC_ASID (1 << 8) #define ARM_SMMU_OPT_HALT (1 << 9) +#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 10) u32 options; enum arm_smmu_arch_version version; enum arm_smmu_implementation model; @@ -401,6 +403,9 @@ struct arm_smmu_domain { struct list_head nonsecure_pool; struct iommu_debug_attachment *logger; struct iommu_domain domain; + + bool qsmmuv500_errata1_init; + bool qsmmuv500_errata1_client; }; struct arm_smmu_option_prop { @@ -420,6 +425,10 @@ struct qsmmuv500_archdata { struct actlr_setting *actlrs; u32 actlr_tbl_size; u32 testbus_version; + struct arm_smmu_smr *errata1_clients; + u32 num_errata1_clients; + struct hwspinlock *errata1_lock; + ktime_t last_tlbi_ktime; }; #define get_qsmmuv500_archdata(smmu) \ ((struct qsmmuv500_archdata *)(smmu->archdata)) @@ -455,6 +464,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { { ARM_SMMU_OPT_DISABLE_ATOS, "qcom,disable-atos" }, { ARM_SMMU_OPT_NO_DYNAMIC_ASID, "qcom,no-dynamic-asid" }, { ARM_SMMU_OPT_HALT, "qcom,enable-smmu-halt"}, + { ARM_SMMU_OPT_MMU500_ERRATA1, "qcom,mmu500-errata-1" }, { 0, NULL}, }; @@ -477,6 +487,7 @@ static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain); static int arm_smmu_alloc_cb(struct iommu_domain *domain, struct arm_smmu_device *smmu, struct device *dev); +static struct iommu_gather_ops qsmmuv500_errata1_smmu_gather_ops; static bool arm_smmu_is_static_cb(struct arm_smmu_device *smmu); static bool arm_smmu_is_master_side_secure(struct arm_smmu_domain *smmu_domain); @@ -2597,6 +2608,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, (smmu->model == QCOM_SMMUV500)) quirks |= IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE; + if (smmu->options & ARM_SMMU_OPT_MMU500_ERRATA1) + smmu_domain->tlb_ops = &qsmmuv500_errata1_smmu_gather_ops; + if (arm_smmu_is_slave_side_secure(smmu_domain)) smmu_domain->tlb_ops = &msm_smmu_gather_ops; @@ -6028,6 +6042,136 @@ static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec, return false; } +static bool qsmmuv500_errata1_required(struct arm_smmu_domain *smmu_domain, + struct qsmmuv500_archdata *data) +{ + bool ret = false; + int j; + struct arm_smmu_smr *smr; + struct iommu_fwspec *fwspec; + + if (smmu_domain->qsmmuv500_errata1_init) + return smmu_domain->qsmmuv500_errata1_client; + + fwspec = smmu_domain->dev->iommu_fwspec; + for (j = 0; j < data->num_errata1_clients; j++) { + smr = &data->errata1_clients[j]; + if (arm_smmu_fwspec_match_smr(fwspec, smr)) { + ret = true; + break; + } + } + + smmu_domain->qsmmuv500_errata1_init = true; + smmu_domain->qsmmuv500_errata1_client = ret; + return ret; +} + +#define SCM_CONFIG_ERRATA1_CLIENT_ALL 0x2 +#define SCM_CONFIG_ERRATA1 0x3 +static void __qsmmuv500_errata1_tlbiall(struct arm_smmu_domain *smmu_domain) +{ + struct arm_smmu_device *smmu = smmu_domain->smmu; + struct device *dev = smmu_domain->dev; + struct arm_smmu_cfg *cfg = &smmu_domain->cfg; + void __iomem *base; + int ret; + ktime_t cur; + u32 val; + struct scm_desc desc = { + .args[0] = SCM_CONFIG_ERRATA1_CLIENT_ALL, + .args[1] = false, + .arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL), + }; + + base = ARM_SMMU_CB(smmu, cfg->cbndx); + writel_relaxed(0, base + ARM_SMMU_CB_S1_TLBIALL); + writel_relaxed(0, base + ARM_SMMU_CB_TLBSYNC); + if (!readl_poll_timeout_atomic(base + ARM_SMMU_CB_TLBSTATUS, val, + !(val & TLBSTATUS_SACTIVE), 0, 100)) + return; + + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_SMMU_PROGRAM, + SCM_CONFIG_ERRATA1), + &desc); + if (ret) { + dev_err(smmu->dev, "Calling into TZ to disable ERRATA1 failed - IOMMU hardware in bad state\n"); + BUG(); + return; + } + + cur = ktime_get(); + trace_tlbi_throttle_start(dev, 0); + msm_bus_noc_throttle_wa(true); + + if (readl_poll_timeout_atomic(base + ARM_SMMU_CB_TLBSTATUS, val, + !(val & TLBSTATUS_SACTIVE), 0, 10000)) { + dev_err(smmu->dev, "ERRATA1 TLBSYNC timeout - IOMMU hardware in bad state"); + trace_tlbsync_timeout(dev, 0); + BUG(); + } + + msm_bus_noc_throttle_wa(false); + trace_tlbi_throttle_end(dev, ktime_us_delta(ktime_get(), cur)); + + desc.args[1] = true; + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_SMMU_PROGRAM, + SCM_CONFIG_ERRATA1), + &desc); + if (ret) { + dev_err(smmu->dev, "Calling into TZ to reenable ERRATA1 failed - IOMMU hardware in bad state\n"); + BUG(); + } +} + +/* Must be called with clocks/regulators enabled */ +#define ERRATA1_TLBI_INTERVAL_US 10 +/* Timeout (ms) for the trylock of remote spinlocks */ +#define HWSPINLOCK_TIMEOUT 1000 +static void qsmmuv500_errata1_tlb_inv_context(void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + struct device *dev = smmu_domain->dev; + struct qsmmuv500_archdata *data = + get_qsmmuv500_archdata(smmu_domain->smmu); + ktime_t cur; + unsigned long flags; + bool errata; + int ret; + + cur = ktime_get(); + trace_tlbi_start(dev, 0); + + errata = qsmmuv500_errata1_required(smmu_domain, data); + ret = hwspin_lock_timeout_irqsave(data->errata1_lock, + HWSPINLOCK_TIMEOUT, &flags); + if (ret) + WARN(1, "%s: get the hw lock failed", dev_name(dev)); + + if (errata) { + s64 delta; + + delta = ktime_us_delta(ktime_get(), data->last_tlbi_ktime); + if (delta < ERRATA1_TLBI_INTERVAL_US) + udelay(ERRATA1_TLBI_INTERVAL_US - delta); + + __qsmmuv500_errata1_tlbiall(smmu_domain); + + data->last_tlbi_ktime = ktime_get(); + } else { + __qsmmuv500_errata1_tlbiall(smmu_domain); + } + hwspin_unlock_irqrestore(data->errata1_lock, &flags); + + trace_tlbi_end(dev, ktime_us_delta(ktime_get(), cur)); +} + +static struct iommu_gather_ops qsmmuv500_errata1_smmu_gather_ops = { + .tlb_flush_all = qsmmuv500_errata1_tlb_inv_context, + .alloc_pages_exact = arm_smmu_alloc_pages_exact, + .free_pages_exact = arm_smmu_free_pages_exact, +}; + static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu, struct arm_smmu_domain *smmu_domain) { @@ -6460,6 +6604,48 @@ static int qsmmuv500_tbu_register(struct device *dev, void *cookie) return 0; } +static int qsmmuv500_parse_errata1(struct arm_smmu_device *smmu) +{ + int len, i, hwlock_id; + struct device *dev = smmu->dev; + struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu); + struct arm_smmu_smr *smrs; + const __be32 *cell; + + cell = of_get_property(dev->of_node, "qcom,mmu500-errata-1", NULL); + if (!cell) + return 0; + + hwlock_id = of_hwspin_lock_get_id(dev->of_node, 0); + if (hwlock_id < 0) { + if (hwlock_id != -EPROBE_DEFER) + dev_err(dev, "failed to retrieve hwlock\n"); + return hwlock_id; + } + + data->errata1_lock = hwspin_lock_request_specific(hwlock_id); + if (!data->errata1_lock) + return -ENXIO; + + len = of_property_count_elems_of_size( + dev->of_node, "qcom,mmu500-errata-1", sizeof(u32) * 2); + if (len < 0) + return 0; + + smrs = devm_kzalloc(dev, sizeof(*smrs) * len, GFP_KERNEL); + if (!smrs) + return -ENOMEM; + + for (i = 0; i < len; i++) { + smrs[i].id = of_read_number(cell++, 1); + smrs[i].mask = of_read_number(cell++, 1); + } + + data->errata1_clients = smrs; + data->num_errata1_clients = len; + return 0; +} + static int qsmmuv500_read_actlr_tbl(struct arm_smmu_device *smmu) { int len, i; @@ -7110,6 +7296,10 @@ static int qsmmuv500_arch_init(struct arm_smmu_device *smmu) if (arm_smmu_is_static_cb(smmu)) return 0; + ret = qsmmuv500_parse_errata1(smmu); + if (ret) + return ret; + ret = qsmmuv500_read_actlr_tbl(smmu); if (ret) return ret; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index cd5e3285a01a..baad4c87ec2f 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -45,6 +45,34 @@ #include "irq-gic-common.h" +#define MAX_IRQ 1020U /* Max number of SGI+PPI+SPI */ +#define SPI_START_IRQ 32 /* SPI start irq number */ +#define GICD_ICFGR_BITS 2 /* 2 bits per irq in GICD_ICFGR */ +#define GICD_ISENABLER_BITS 1 /* 1 bit per irq in GICD_ISENABLER */ +#define GICD_IPRIORITYR_BITS 8 /* 8 bits per irq in GICD_IPRIORITYR */ + +/* 32 bit mask with lower n bits set */ +#define UMASK_LOW(n) (~0U >> (32 - (n))) + +/* Number of 32-bit words required to store all irqs, for + * registers where each word stores configuration for each irq + * in bits_per_irq bits. + */ +#define NUM_IRQ_WORDS(bits_per_irq) (DIV_ROUND_UP(MAX_IRQ, \ + 32 / (bits_per_irq))) +#define MAX_IRQS_IGNORE 10 + +#define IRQ_NR_BOUND(nr) min((nr), MAX_IRQ) + +/* Bitmap to irqs, which are restored */ +static DECLARE_BITMAP(irqs_restore, MAX_IRQ); + +/* Bitmap to irqs, for which restore is ignored. + * Presently, only GICD_IROUTER mismatches are + * ignored. + */ +static DECLARE_BITMAP(irqs_ignore_restore, MAX_IRQ); + struct redist_region { void __iomem *redist_base; phys_addr_t phys_base; @@ -62,6 +90,16 @@ struct gic_chip_data { bool has_rss; unsigned int irq_nr; struct partition_desc *ppi_descs[16]; + + u64 saved_spi_router[MAX_IRQ]; + u32 saved_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; + u32 saved_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; + u32 saved_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; + + u64 changed_spi_router[MAX_IRQ]; + u32 changed_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; + u32 changed_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; + u32 changed_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; }; static struct gic_chip_data gic_data __read_mostly; @@ -70,6 +108,58 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key); static struct gic_kvm_info gic_v3_kvm_info; static DEFINE_PER_CPU(bool, has_rss); +enum gicd_save_restore_reg { + SAVED_ICFGR, + SAVED_IS_ENABLER, + SAVED_IPRIORITYR, + NUM_SAVED_GICD_REGS, +}; + +/* Stores start address of spi config for saved gicd regs */ +static u32 *saved_spi_regs_start[NUM_SAVED_GICD_REGS] = { + [SAVED_ICFGR] = gic_data.saved_spi_cfg, + [SAVED_IS_ENABLER] = gic_data.saved_spi_enable, + [SAVED_IPRIORITYR] = gic_data.saved_spi_priority, +}; + +/* Stores start address of spi config for changed gicd regs */ +static u32 *changed_spi_regs_start[NUM_SAVED_GICD_REGS] = { + [SAVED_ICFGR] = gic_data.changed_spi_cfg, + [SAVED_IS_ENABLER] = gic_data.changed_spi_enable, + [SAVED_IPRIORITYR] = gic_data.changed_spi_priority, +}; + +/* GICD offset for saved registers */ +static u32 gicd_offset[NUM_SAVED_GICD_REGS] = { + [SAVED_ICFGR] = GICD_ICFGR, + [SAVED_IS_ENABLER] = GICD_ISENABLER, + [SAVED_IPRIORITYR] = GICD_IPRIORITYR, +}; + +/* Bits per irq word, for gicd saved registers */ +static u32 gicd_reg_bits_per_irq[NUM_SAVED_GICD_REGS] = { + [SAVED_ICFGR] = GICD_ICFGR_BITS, + [SAVED_IS_ENABLER] = GICD_ISENABLER_BITS, + [SAVED_IPRIORITYR] = GICD_IPRIORITYR_BITS, +}; + +#define for_each_spi_irq_word(i, reg) \ + for (i = 0; \ + i < DIV_ROUND_UP(IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ, \ + 32 / gicd_reg_bits_per_irq[reg]); \ + i++) + +#define read_spi_word_offset(base, reg, i) \ + readl_relaxed_no_log( \ + base + gicd_offset[reg] + i * 4 + \ + SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) + +#define restore_spi_word_offset(base, reg, i) \ + writel_relaxed_no_log( \ + saved_spi_regs_start[reg][i],\ + base + gicd_offset[reg] + i * 4 + \ + SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) + #define MPIDR_RS(mpidr) (((mpidr) & 0xF0UL) >> 4) #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) @@ -137,6 +227,240 @@ static u64 __maybe_unused gic_read_iar(void) } #endif +void gic_v3_dist_save(void) +{ + void __iomem *base = gic_data.dist_base; + int reg, i; + + if (!base) + return; + + bitmap_zero(irqs_restore, MAX_IRQ); + + for (reg = SAVED_ICFGR; reg < NUM_SAVED_GICD_REGS; reg++) { + for_each_spi_irq_word(i, reg) { + saved_spi_regs_start[reg][i] = + read_spi_word_offset(base, reg, i); + changed_spi_regs_start[reg][i] = 0; + } + } + + for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { + gic_data.saved_spi_router[i] = + gic_read_irouter(base + GICD_IROUTER + i * 8); + gic_data.changed_spi_router[i] = 0; + } +} + +static void _gicd_check_reg(enum gicd_save_restore_reg reg) +{ + void __iomem *base = gic_data.dist_base; + u32 *saved_spi_cfg = saved_spi_regs_start[reg]; + u32 *changed_spi_cfg = changed_spi_regs_start[reg]; + u32 bits_per_irq = gicd_reg_bits_per_irq[reg]; + u32 current_cfg = 0; + int i, j = SPI_START_IRQ, l; + u32 k; + + for_each_spi_irq_word(i, reg) { + current_cfg = read_spi_word_offset(base, reg, i); + if (current_cfg != saved_spi_cfg[i]) { + for (k = current_cfg ^ saved_spi_cfg[i], + l = 0; k ; k >>= bits_per_irq, l++) { + if (k & UMASK_LOW(bits_per_irq)) + set_bit(j+l, irqs_restore); + } + changed_spi_cfg[i] = current_cfg ^ saved_spi_cfg[i]; + } + j += 32 / bits_per_irq; + } +} + +#define _gic_v3_dist_check_icfgr() \ + _gicd_check_reg(SAVED_ICFGR) +#define _gic_v3_dist_check_ipriorityr() \ + _gicd_check_reg(SAVED_IPRIORITYR) +#define _gic_v3_dist_check_isenabler() \ + _gicd_check_reg(SAVED_IS_ENABLER) + +static void _gic_v3_dist_check_irouter(void) +{ + void __iomem *base = gic_data.dist_base; + u64 current_irouter_cfg = 0; + int i; + + for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { + if (test_bit(i, irqs_ignore_restore)) + continue; + current_irouter_cfg = gic_read_irouter( + base + GICD_IROUTER + i * 8); + if (current_irouter_cfg != gic_data.saved_spi_router[i]) { + set_bit(i, irqs_restore); + gic_data.changed_spi_router[i] = + current_irouter_cfg ^ gic_data.saved_spi_router[i]; + } + } +} + +static void _gic_v3_dist_restore_reg(enum gicd_save_restore_reg reg) +{ + void __iomem *base = gic_data.dist_base; + int i; + + for_each_spi_irq_word(i, reg) { + if (changed_spi_regs_start[reg][i]) + restore_spi_word_offset(base, reg, i); + } + + /* Commit all restored configurations before subsequent writes */ + wmb(); +} + +#define _gic_v3_dist_restore_icfgr() _gic_v3_dist_restore_reg(SAVED_ICFGR) +#define _gic_v3_dist_restore_ipriorityr() \ + _gic_v3_dist_restore_reg(SAVED_IPRIORITYR) + +static void _gic_v3_dist_restore_set_reg(u32 offset) +{ + void __iomem *base = gic_data.dist_base; + int i, j = SPI_START_IRQ, l; + int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; + + for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { + u32 reg_val = readl_relaxed_no_log(base + offset + i * 4 + 4); + bool irqs_restore_updated = 0; + + for (l = 0; l < 32; l++) { + if (test_bit(j+l, irqs_restore)) { + reg_val |= BIT(l); + irqs_restore_updated = 1; + } + } + + if (irqs_restore_updated) { + writel_relaxed_no_log( + reg_val, base + offset + i * 4 + 4); + } + } + + /* Commit restored configuration updates before subsequent writes */ + wmb(); +} + +#define _gic_v3_dist_restore_isenabler() \ + _gic_v3_dist_restore_reg(SAVED_IS_ENABLER) + +#define _gic_v3_dist_restore_ispending() \ + _gic_v3_dist_restore_set_reg(GICD_ISPENDR) + +static void _gic_v3_dist_restore_irouter(void) +{ + void __iomem *base = gic_data.dist_base; + int i; + + for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { + if (test_bit(i, irqs_ignore_restore)) + continue; + if (gic_data.changed_spi_router[i]) { + gic_write_irouter(gic_data.saved_spi_router[i], + base + GICD_IROUTER + i * 8); + } + } + + /* Commit GICD_IROUTER writes before subsequent writes */ + wmb(); +} + +static void _gic_v3_dist_clear_reg(u32 offset) +{ + void __iomem *base = gic_data.dist_base; + int i, j = SPI_START_IRQ, l; + int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; + + for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { + u32 clear = 0; + bool irqs_restore_updated = 0; + + for (l = 0; l < 32; l++) { + if (test_bit(j+l, irqs_restore)) { + clear |= BIT(l); + irqs_restore_updated = 1; + } + } + + if (irqs_restore_updated) { + writel_relaxed_no_log( + clear, base + offset + i * 4 + 4); + } + } + + /* Commit clearing of irq config before subsequent writes */ + wmb(); +} + +#define _gic_v3_dist_set_icenabler() \ + _gic_v3_dist_clear_reg(GICD_ICENABLER) + +#define _gic_v3_dist_set_icpending() \ + _gic_v3_dist_clear_reg(GICD_ICPENDR) + +#define _gic_v3_dist_set_icactive() \ + _gic_v3_dist_clear_reg(GICD_ICACTIVER) + +/* Restore GICD state for SPIs. SPI configuration is restored + * for GICD_ICFGR, GICD_ISENABLER, GICD_IPRIORITYR, GICD_IROUTER + * registers. Following is the sequence for restore: + * + * 1. For SPIs, check whether any of GICD_ICFGR, GICD_ISENABLER, + * GICD_IPRIORITYR, GICD_IROUTER, current configuration is + * different from saved configuration. + * + * For all irqs, with mismatched configurations, + * + * 2. Set GICD_ICENABLER and wait for its completion. + * + * 3. Restore any changed GICD_ICFGR, GICD_IPRIORITYR, GICD_IROUTER + * configurations. + * + * 4. Set GICD_ICACTIVER. + * + * 5. Set pending for the interrupt. + * + * 6. Restore Enable bit of interrupt and wait for its completion. + * + */ +void gic_v3_dist_restore(void) +{ + if (!gic_data.dist_base) + return; + + _gic_v3_dist_check_icfgr(); + _gic_v3_dist_check_ipriorityr(); + _gic_v3_dist_check_isenabler(); + _gic_v3_dist_check_irouter(); + + if (bitmap_empty(irqs_restore, IRQ_NR_BOUND(gic_data.irq_nr))) + return; + + _gic_v3_dist_set_icenabler(); + gic_dist_wait_for_rwp(); + + _gic_v3_dist_restore_icfgr(); + _gic_v3_dist_restore_ipriorityr(); + _gic_v3_dist_restore_irouter(); + + _gic_v3_dist_set_icactive(); + + _gic_v3_dist_set_icpending(); + _gic_v3_dist_restore_ispending(); + + _gic_v3_dist_restore_isenabler(); + gic_dist_wait_for_rwp(); + + /* Commit all writes before proceeding */ + wmb(); +} + static void gic_enable_redist(bool enable) { void __iomem *rbase; @@ -1382,7 +1706,8 @@ static int __init gicv3_of_init(struct device_node *node, struct device_node *pa struct redist_region *rdist_regs; u64 redist_stride; u32 nr_redist_regions; - int err, i; + int err, i, ignore_irqs_len; + u32 ignore_restore_irqs[MAX_IRQS_IGNORE] = {0}; dist_base = of_iomap(node, 0); if (!dist_base) { @@ -1432,6 +1757,14 @@ static int __init gicv3_of_init(struct device_node *node, struct device_node *pa if (static_branch_likely(&supports_deactivate_key)) gic_of_setup_kvm_info(node); + + ignore_irqs_len = of_property_read_variable_u32_array(node, + "ignored-save-restore-irqs", + ignore_restore_irqs, + 0, MAX_IRQS_IGNORE); + for (i = 0; i < ignore_irqs_len; i++) + set_bit(ignore_restore_irqs[i], irqs_ignore_restore); + return 0; out_unmap_rdist: diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e2b8820d125b..9a49d5c2f9f5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -798,6 +798,16 @@ config LEDS_QPNP_FLASH_V2 flash LED target current for several independent channels. It also supports various over current and over temperature mitigation features. +config LEDS_QPNP_HAPTICS + tristate "Haptics support for QPNP PMIC" + depends on LEDS_CLASS && MFD_SPMI_PMIC + help + This option enables device driver support for the haptics peripheral + found on Qualcomm Technologies, Inc. QPNP PMICs. The haptic + peripheral is capable of driving both LRA and ERM vibrators. This + module provides haptic feedback for user actions such as a long press + on the touch screen. + config LEDS_QPNP_VIBRATOR_LDO tristate "Vibrator-LDO support for QPNP PMIC" depends on LEDS_CLASS && MFD_SPMI_PMIC diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index b306664b7932..485a5973e4d3 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_QTI_TRI_LED) += leds-qti-tri-led.o obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o leds-qpnp-flash-common.o +obj-$(CONFIG_LEDS_QPNP_HAPTICS) += leds-qpnp-haptics.o obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o obj-$(CONFIG_LEDS_QPNP_VIBRATOR) += leds-qpnp-vibrator.o diff --git a/drivers/leds/leds-qpnp-haptics.c b/drivers/leds/leds-qpnp-haptics.c new file mode 100644 index 000000000000..5786b3c04dc0 --- /dev/null +++ b/drivers/leds/leds-qpnp-haptics.c @@ -0,0 +1,2528 @@ +/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "haptics: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define HAP_STATUS_1_REG(chip) (chip->base + 0x0A) +#define HAP_BUSY_BIT BIT(1) +#define SC_FLAG_BIT BIT(3) +#define AUTO_RES_ERROR_BIT BIT(4) + +#define HAP_LRA_AUTO_RES_LO_REG(chip) (chip->base + 0x0B) +#define HAP_LRA_AUTO_RES_HI_REG(chip) (chip->base + 0x0C) + +#define HAP_INT_RT_STS_REG(chip) (chip->base + 0x10) +#define SC_INT_RT_STS_BIT BIT(0) +#define PLAY_INT_RT_STS_BIT BIT(1) + +#define HAP_EN_CTL_REG(chip) (chip->base + 0x46) +#define HAP_EN_BIT BIT(7) + +#define HAP_EN_CTL2_REG(chip) (chip->base + 0x48) +#define BRAKE_EN_BIT BIT(0) + +#define HAP_AUTO_RES_CTRL_REG(chip) (chip->base + 0x4B) +#define AUTO_RES_EN_BIT BIT(7) +#define AUTO_RES_ERR_RECOVERY_BIT BIT(3) + +#define HAP_CFG1_REG(chip) (chip->base + 0x4C) +#define HAP_ACT_TYPE_MASK BIT(0) +#define HAP_LRA 0 +#define HAP_ERM 1 + +#define HAP_CFG2_REG(chip) (chip->base + 0x4D) +#define HAP_WAVE_SINE 0 +#define HAP_WAVE_SQUARE 1 +#define HAP_LRA_RES_TYPE_MASK BIT(0) + +#define HAP_SEL_REG(chip) (chip->base + 0x4E) +#define HAP_WF_SOURCE_MASK GENMASK(5, 4) +#define HAP_WF_SOURCE_SHIFT 4 + +#define HAP_LRA_AUTO_RES_REG(chip) (chip->base + 0x4F) +/* For pmi8998 */ +#define LRA_AUTO_RES_MODE_MASK GENMASK(6, 4) +#define LRA_AUTO_RES_MODE_SHIFT 4 +#define LRA_HIGH_Z_MASK GENMASK(3, 2) +#define LRA_HIGH_Z_SHIFT 2 +#define LRA_RES_CAL_MASK GENMASK(1, 0) +#define HAP_RES_CAL_PERIOD_MIN 4 +#define HAP_RES_CAL_PERIOD_MAX 32 +/* For pm660 */ +#define PM660_AUTO_RES_MODE_BIT BIT(7) +#define PM660_AUTO_RES_MODE_SHIFT 7 +#define PM660_CAL_DURATION_MASK GENMASK(6, 5) +#define PM660_CAL_DURATION_SHIFT 5 +#define PM660_QWD_DRIVE_DURATION_BIT BIT(4) +#define PM660_QWD_DRIVE_DURATION_SHIFT 4 +#define PM660_CAL_EOP_BIT BIT(3) +#define PM660_CAL_EOP_SHIFT 3 +#define PM660_LRA_RES_CAL_MASK GENMASK(2, 0) +#define HAP_PM660_RES_CAL_PERIOD_MAX 256 + +#define HAP_VMAX_CFG_REG(chip) (chip->base + 0x51) +#define HAP_VMAX_OVD_BIT BIT(6) +#define HAP_VMAX_MASK GENMASK(5, 1) +#define HAP_VMAX_SHIFT 1 +#define HAP_VMAX_MIN_MV 116 +#define HAP_VMAX_MAX_MV 3596 + +#define HAP_ILIM_CFG_REG(chip) (chip->base + 0x52) +#define HAP_ILIM_SEL_MASK BIT(0) +#define HAP_ILIM_400_MA 0 +#define HAP_ILIM_800_MA 1 + +#define HAP_SC_DEB_REG(chip) (chip->base + 0x53) +#define HAP_SC_DEB_MASK GENMASK(2, 0) +#define HAP_SC_DEB_CYCLES_MIN 0 +#define HAP_DEF_SC_DEB_CYCLES 8 +#define HAP_SC_DEB_CYCLES_MAX 32 + +#define HAP_RATE_CFG1_REG(chip) (chip->base + 0x54) +#define HAP_RATE_CFG1_MASK GENMASK(7, 0) + +#define HAP_RATE_CFG2_REG(chip) (chip->base + 0x55) +#define HAP_RATE_CFG2_MASK GENMASK(3, 0) +/* Shift needed to convert drive period upper bits [11:8] */ +#define HAP_RATE_CFG2_SHIFT 8 + +#define HAP_INT_PWM_REG(chip) (chip->base + 0x56) +#define INT_PWM_FREQ_SEL_MASK GENMASK(1, 0) +#define INT_PWM_FREQ_253_KHZ 0 +#define INT_PWM_FREQ_505_KHZ 1 +#define INT_PWM_FREQ_739_KHZ 2 +#define INT_PWM_FREQ_1076_KHZ 3 + +#define HAP_EXT_PWM_REG(chip) (chip->base + 0x57) +#define EXT_PWM_FREQ_SEL_MASK GENMASK(1, 0) +#define EXT_PWM_FREQ_25_KHZ 0 +#define EXT_PWM_FREQ_50_KHZ 1 +#define EXT_PWM_FREQ_75_KHZ 2 +#define EXT_PWM_FREQ_100_KHZ 3 + +#define HAP_PWM_CAP_REG(chip) (chip->base + 0x58) + +#define HAP_SC_CLR_REG(chip) (chip->base + 0x59) +#define SC_CLR_BIT BIT(0) + +#define HAP_BRAKE_REG(chip) (chip->base + 0x5C) +#define HAP_BRAKE_PAT_MASK 0x3 + +#define HAP_WF_REPEAT_REG(chip) (chip->base + 0x5E) +#define WF_REPEAT_MASK GENMASK(6, 4) +#define WF_REPEAT_SHIFT 4 +#define WF_REPEAT_MIN 1 +#define WF_REPEAT_MAX 128 +#define WF_S_REPEAT_MASK GENMASK(1, 0) +#define WF_S_REPEAT_MIN 1 +#define WF_S_REPEAT_MAX 8 + +#define HAP_WF_S1_REG(chip) (chip->base + 0x60) +#define HAP_WF_SIGN_BIT BIT(7) +#define HAP_WF_OVD_BIT BIT(6) +#define HAP_WF_SAMP_MAX GENMASK(5, 1) +#define HAP_WF_SAMPLE_LEN 8 + +#define HAP_PLAY_REG(chip) (chip->base + 0x70) +#define PLAY_BIT BIT(7) +#define PAUSE_BIT BIT(0) + +#define HAP_SEC_ACCESS_REG(chip) (chip->base + 0xD0) + +#define HAP_TEST2_REG(chip) (chip->base + 0xE3) +#define HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4) +#define HAP_EXT_PWM_DTEST_SHIFT 4 +#define PWM_MAX_DTEST_LINES 4 +#define HAP_EXT_PWM_PEAK_DATA 0x7F +#define HAP_EXT_PWM_HALF_DUTY 50 +#define HAP_EXT_PWM_FULL_DUTY 100 +#define HAP_EXT_PWM_DATA_FACTOR 39 + +/* Other definitions */ +#define HAP_BRAKE_PAT_LEN 4 +#define HAP_WAVE_SAMP_LEN 8 +#define NUM_WF_SET 4 +#define HAP_WAVE_SAMP_SET_LEN (HAP_WAVE_SAMP_LEN * NUM_WF_SET) +#define HAP_RATE_CFG_STEP_US 5 +#define HAP_WAVE_PLAY_RATE_US_MIN 0 +#define HAP_DEF_WAVE_PLAY_RATE_US 5715 +#define HAP_WAVE_PLAY_RATE_US_MAX 20475 +#define HAP_MAX_PLAY_TIME_MS 15000 + +enum hap_brake_pat { + NO_BRAKE = 0, + BRAKE_VMAX_4, + BRAKE_VMAX_2, + BRAKE_VMAX, +}; + +enum hap_auto_res_mode { + HAP_AUTO_RES_NONE, + HAP_AUTO_RES_ZXD, + HAP_AUTO_RES_QWD, + HAP_AUTO_RES_MAX_QWD, + HAP_AUTO_RES_ZXD_EOP, +}; + +enum hap_pm660_auto_res_mode { + HAP_PM660_AUTO_RES_ZXD, + HAP_PM660_AUTO_RES_QWD, +}; + +/* high Z option lines */ +enum hap_high_z { + HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */ + HAP_LRA_HIGH_Z_OPT1, + HAP_LRA_HIGH_Z_OPT2, + HAP_LRA_HIGH_Z_OPT3, +}; + +/* play modes */ +enum hap_mode { + HAP_DIRECT, + HAP_BUFFER, + HAP_AUDIO, + HAP_PWM, +}; + +/* wave/sample repeat */ +enum hap_rep_type { + HAP_WAVE_REPEAT = 1, + HAP_WAVE_SAMP_REPEAT, +}; + +/* status flags */ +enum hap_status { + AUTO_RESONANCE_ENABLED = BIT(0), +}; + +enum hap_play_control { + HAP_STOP, + HAP_PAUSE, + HAP_PLAY, +}; + +/* pwm channel parameters */ +struct pwm_param { + struct pwm_device *pwm_dev; + u32 duty_us; + u32 period_us; +}; + +/* + * hap_lra_ares_param - Haptic auto_resonance parameters + * @ 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 hap_lra_ares_param { + int lra_qwd_drive_duration; + int calibrate_at_eop; + enum hap_high_z lra_high_z; + u16 lra_res_cal_period; + u8 auto_res_mode; +}; + +/* + * hap_chip - Haptics data structure + * @ pdev - platform device pointer + * @ regmap - regmap pointer + * @ bus_lock - spin lock for bus read/write + * @ play_lock - mutex lock for haptics play/enable control + * @ haptics_work - haptics worker + * @ stop_timer - hrtimer for stopping haptics + * @ auto_res_err_poll_timer - hrtimer for auto-resonance error + * @ base - base address + * @ play_irq - irq for play + * @ sc_irq - irq for short circuit + * @ pwm_data - pwm configuration + * @ ares_cfg - auto resonance configuration + * @ play_time_ms - play time set by the user in ms + * @ max_play_time_ms - max play time in ms + * @ vmax_mv - max voltage in mv + * @ ilim_ma - limiting current in ma + * @ sc_deb_cycles - short circuit debounce cycles + * @ wave_play_rate_us - play rate for waveform + * @ last_rate_cfg - Last rate config updated + * @ wave_rep_cnt - waveform repeat count + * @ wave_s_rep_cnt - waveform sample repeat count + * @ ext_pwm_freq_khz - external pwm frequency in KHz + * @ ext_pwm_dtest_line - DTEST line for external pwm + * @ status_flags - status + * @ play_mode - play mode + * @ act_type - actuator type + * @ wave_shape - waveform shape + * @ wave_samp_idx - wave sample id used to refer start of a sample set + * @ wave_samp - array of wave samples + * @ brake_pat - pattern for active breaking + * @ en_brake - brake state + * @ misc_clk_trim_error_reg - MISC clock trim error register if present + * @ clk_trim_error_code - MISC clock trim error 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 + * @ drive_period_code_max_var_pct - maximum limit of percentage variation of + drive period code + * @ drive_period_code_min_var_pct - minimum limit of percentage variation of + drive period code + * @ last_sc_time - Last time short circuit was detected + * @ sc_count - counter to determine the duration of short circuit + condition + * @ perm_disable - Flag to disable module permanently + * @ state - current state of haptics + * @ module_en - module enable status of haptics + * @ lra_auto_mode - Auto mode selection + * @ play_irq_en - Play interrupt enable status + * @ auto_res_err_recovery_hw - Enable auto resonance error recovery by HW + */ +struct hap_chip { + struct platform_device *pdev; + struct regmap *regmap; + struct pmic_revid_data *revid; + struct led_classdev cdev; + spinlock_t bus_lock; + struct mutex play_lock; + struct mutex param_lock; + struct work_struct haptics_work; + struct hrtimer stop_timer; + struct hrtimer auto_res_err_poll_timer; + u16 base; + int play_irq; + int sc_irq; + struct pwm_param pwm_data; + struct hap_lra_ares_param ares_cfg; + u32 play_time_ms; + u32 max_play_time_ms; + u32 vmax_mv; + u8 ilim_ma; + u32 sc_deb_cycles; + u32 wave_play_rate_us; + u16 last_rate_cfg; + u32 wave_rep_cnt; + u32 wave_s_rep_cnt; + u32 ext_pwm_freq_khz; + u8 ext_pwm_dtest_line; + u32 status_flags; + enum hap_mode play_mode; + u8 act_type; + u8 wave_shape; + u8 wave_samp_idx; + u32 wave_samp[HAP_WAVE_SAMP_SET_LEN]; + u32 brake_pat[HAP_BRAKE_PAT_LEN]; + bool en_brake; + u32 misc_clk_trim_error_reg; + u8 clk_trim_error_code; + u16 drive_period_code_max_limit; + u16 drive_period_code_min_limit; + u8 drive_period_code_max_var_pct; + u8 drive_period_code_min_var_pct; + ktime_t last_sc_time; + u8 sc_count; + bool perm_disable; + atomic_t state; + bool module_en; + bool lra_auto_mode; + bool play_irq_en; + bool auto_res_err_recovery_hw; +}; + +static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip); +static int qpnp_haptics_parse_pwm_dt(struct hap_chip *chip); + +static int qpnp_haptics_read_reg(struct hap_chip *chip, u16 addr, u8 *val, + int len) +{ + int rc; + + rc = regmap_bulk_read(chip->regmap, addr, val, len); + if (rc < 0) + pr_err("Error reading address: 0x%x - rc %d\n", addr, rc); + + return rc; +} + +static inline bool is_secure(u16 addr) +{ + return ((addr & 0xFF) > 0xD0); +} + +static int qpnp_haptics_write_reg(struct hap_chip *chip, u16 addr, u8 *val, + int len) +{ + unsigned long flags; + unsigned int unlock = 0xA5; + int rc = 0, i; + + spin_lock_irqsave(&chip->bus_lock, flags); + + if (is_secure(addr)) { + for (i = 0; i < len; i++) { + rc = regmap_write(chip->regmap, + HAP_SEC_ACCESS_REG(chip), unlock); + if (rc < 0) { + pr_err("Error writing unlock code - rc %d\n", + rc); + goto out; + } + + rc = regmap_write(chip->regmap, addr + i, val[i]); + if (rc < 0) { + pr_err("Error writing address 0x%x - rc %d\n", + addr + i, rc); + goto out; + } + } + } else { + if (len > 1) + rc = regmap_bulk_write(chip->regmap, addr, val, len); + else + rc = regmap_write(chip->regmap, addr, *val); + } + + if (rc < 0) + pr_err("Error writing address: 0x%x - rc %d\n", addr, rc); + +out: + spin_unlock_irqrestore(&chip->bus_lock, flags); + return rc; +} + +static int qpnp_haptics_masked_write_reg(struct hap_chip *chip, u16 addr, + u8 mask, u8 val) +{ + unsigned long flags; + unsigned int unlock = 0xA5; + int rc; + + spin_lock_irqsave(&chip->bus_lock, flags); + if (is_secure(addr)) { + rc = regmap_write(chip->regmap, HAP_SEC_ACCESS_REG(chip), + unlock); + if (rc < 0) { + pr_err("Error writing unlock code - rc %d\n", rc); + goto out; + } + } + + rc = regmap_update_bits(chip->regmap, addr, mask, val); + if (rc < 0) + pr_err("Error writing address: 0x%x - rc %d\n", addr, rc); + + if (!rc) + pr_debug("wrote to address 0x%x = 0x%x\n", addr, val); +out: + spin_unlock_irqrestore(&chip->bus_lock, flags); + return rc; +} + +static bool is_sw_lra_auto_resonance_control(struct hap_chip *chip) +{ + if (chip->act_type != HAP_LRA) + return false; + + if (chip->auto_res_err_recovery_hw) + return false; + + /* + * For short pattern in auto mode, we use buffer mode and auto + * resonance is not needed. + */ + if (chip->lra_auto_mode && chip->play_mode == HAP_BUFFER) + return false; + + return true; +} + +#define HAPTICS_BACK_EMF_DELAY_US 20000 +static int qpnp_haptics_auto_res_enable(struct hap_chip *chip, bool enable) +{ + int rc = 0; + u32 delay_us = HAPTICS_BACK_EMF_DELAY_US; + u8 val; + bool auto_res_mode_qwd; + + if (chip->act_type != HAP_LRA) + return 0; + + if (chip->revid->pmic_subtype == PM660_SUBTYPE) + auto_res_mode_qwd = (chip->ares_cfg.auto_res_mode == + HAP_PM660_AUTO_RES_QWD); + else + auto_res_mode_qwd = (chip->ares_cfg.auto_res_mode == + HAP_AUTO_RES_QWD); + + /* + * Do not enable auto resonance if auto mode is enabled and auto + * resonance mode is QWD, meaning long pattern. + */ + if (chip->lra_auto_mode && auto_res_mode_qwd && enable) { + pr_debug("auto_mode enabled, not enabling auto_res\n"); + return 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 (auto_res_mode_qwd && enable) + usleep_range(delay_us, delay_us + 1); + + val = enable ? AUTO_RES_EN_BIT : 0; + + if (chip->revid->pmic_subtype == PM660_SUBTYPE) + rc = qpnp_haptics_masked_write_reg(chip, + HAP_AUTO_RES_CTRL_REG(chip), + AUTO_RES_EN_BIT, val); + else + rc = qpnp_haptics_masked_write_reg(chip, HAP_TEST2_REG(chip), + AUTO_RES_EN_BIT, val); + if (rc < 0) + return rc; + + if (enable) + chip->status_flags |= AUTO_RESONANCE_ENABLED; + else + chip->status_flags &= ~AUTO_RESONANCE_ENABLED; + + pr_debug("auto_res %sabled\n", enable ? "en" : "dis"); + return rc; +} + +static int qpnp_haptics_update_rate_cfg(struct hap_chip *chip, u16 play_rate) +{ + int rc; + u8 val[2]; + + if (chip->last_rate_cfg == play_rate) { + pr_debug("Same rate_cfg %x\n", play_rate); + return 0; + } + + val[0] = play_rate & HAP_RATE_CFG1_MASK; + val[1] = (play_rate >> HAP_RATE_CFG2_SHIFT) & HAP_RATE_CFG2_MASK; + rc = qpnp_haptics_write_reg(chip, HAP_RATE_CFG1_REG(chip), val, 2); + if (rc < 0) + return rc; + + pr_debug("Play rate code 0x%x\n", play_rate); + chip->last_rate_cfg = play_rate; + return 0; +} + +static void qpnp_haptics_update_lra_frequency(struct hap_chip *chip) +{ + u8 lra_auto_res[2], val; + u32 play_rate_code; + u16 rate_cfg; + int rc; + + rc = qpnp_haptics_read_reg(chip, HAP_LRA_AUTO_RES_LO_REG(chip), + lra_auto_res, 2); + if (rc < 0) { + pr_err("Error in reading LRA_AUTO_RES_LO/HI, rc=%d\n", rc); + return; + } + + play_rate_code = + (lra_auto_res[1] & 0xF0) << 4 | (lra_auto_res[0] & 0xFF); + + 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_haptics_read_reg(chip, HAP_STATUS_1_REG(chip), &val, 1); + 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_ERROR_BIT) || + ((play_rate_code <= chip->drive_period_code_min_limit) || + (play_rate_code >= chip->drive_period_code_max_limit))) { + if (val & AUTO_RES_ERROR_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, + chip->drive_period_code_min_limit, + chip->drive_period_code_max_limit); + rc = qpnp_haptics_auto_res_enable(chip, false); + if (rc < 0) + pr_debug("Auto-resonance disable failed\n"); + return; + } + + /* + * bits[7:4] of AUTO_RES_HI should be written to bits[3:0] of RATE_CFG2 + */ + lra_auto_res[1] >>= 4; + rate_cfg = lra_auto_res[1] << 8 | lra_auto_res[0]; + rc = qpnp_haptics_update_rate_cfg(chip, rate_cfg); + if (rc < 0) + pr_debug("Error in updating rate_cfg\n"); +} + +#define MAX_RETRIES 5 +#define HAP_CYCLES 4 +static bool is_haptics_idle(struct hap_chip *chip) +{ + unsigned long wait_time_us; + int rc, i; + u8 val; + + rc = qpnp_haptics_read_reg(chip, HAP_STATUS_1_REG(chip), &val, 1); + if (rc < 0) + return false; + + if (!(val & HAP_BUSY_BIT)) + return true; + + if (chip->play_time_ms <= 20) + wait_time_us = chip->play_time_ms * 1000; + else + wait_time_us = chip->wave_play_rate_us * HAP_CYCLES; + + for (i = 0; i < MAX_RETRIES; i++) { + /* wait for play_rate cycles */ + usleep_range(wait_time_us, wait_time_us + 1); + + if (chip->play_mode == HAP_DIRECT || + chip->play_mode == HAP_PWM) + return true; + + rc = qpnp_haptics_read_reg(chip, HAP_STATUS_1_REG(chip), &val, + 1); + if (rc < 0) + return false; + + if (!(val & HAP_BUSY_BIT)) + return true; + } + + if (i >= MAX_RETRIES && (val & HAP_BUSY_BIT)) { + pr_debug("Haptics Busy after %d retries\n", i); + return false; + } + + return true; +} + +static int qpnp_haptics_mod_enable(struct hap_chip *chip, bool enable) +{ + u8 val; + int rc; + + if (chip->module_en == enable) + return 0; + + if (!enable) { + if (!is_haptics_idle(chip)) + pr_debug("Disabling module forcibly\n"); + } + + val = enable ? HAP_EN_BIT : 0; + rc = qpnp_haptics_write_reg(chip, HAP_EN_CTL_REG(chip), &val, 1); + if (rc < 0) + return rc; + + chip->module_en = enable; + return 0; +} + +static int qpnp_haptics_play_control(struct hap_chip *chip, + enum hap_play_control ctrl) +{ + u8 val; + int rc; + + switch (ctrl) { + case HAP_STOP: + val = 0; + break; + case HAP_PAUSE: + val = PAUSE_BIT; + break; + case HAP_PLAY: + val = PLAY_BIT; + break; + default: + return 0; + } + + rc = qpnp_haptics_write_reg(chip, HAP_PLAY_REG(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing to PLAY_REG, rc=%d\n", rc); + return rc; + } + + pr_debug("haptics play ctrl: %d\n", ctrl); + return rc; +} + +#define AUTO_RES_ERR_POLL_TIME_NS (20 * NSEC_PER_MSEC) +static int qpnp_haptics_play(struct hap_chip *chip, bool enable) +{ + int rc = 0, time_ms = chip->play_time_ms; + + if (chip->perm_disable && enable) + return 0; + + mutex_lock(&chip->play_lock); + + if (enable) { + if (chip->play_mode == HAP_PWM) { + rc = pwm_enable(chip->pwm_data.pwm_dev); + if (rc < 0) { + pr_err("Error in enabling PWM, rc=%d\n", rc); + goto out; + } + } + + rc = qpnp_haptics_auto_res_enable(chip, false); + if (rc < 0) { + pr_err("Error in disabling auto_res, rc=%d\n", rc); + goto out; + } + + rc = qpnp_haptics_mod_enable(chip, true); + if (rc < 0) { + pr_err("Error in enabling module, rc=%d\n", rc); + goto out; + } + + rc = qpnp_haptics_play_control(chip, HAP_PLAY); + if (rc < 0) { + pr_err("Error in enabling play, rc=%d\n", rc); + goto out; + } + + hrtimer_start(&chip->stop_timer, + ktime_set(time_ms / MSEC_PER_SEC, + (time_ms % MSEC_PER_SEC) * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + + rc = qpnp_haptics_auto_res_enable(chip, true); + if (rc < 0) { + pr_err("Error in enabling auto_res, rc=%d\n", rc); + goto out; + } + + if (is_sw_lra_auto_resonance_control(chip)) + hrtimer_start(&chip->auto_res_err_poll_timer, + ktime_set(0, AUTO_RES_ERR_POLL_TIME_NS), + HRTIMER_MODE_REL); + } else { + rc = qpnp_haptics_play_control(chip, HAP_STOP); + if (rc < 0) { + pr_err("Error in disabling play, rc=%d\n", rc); + goto out; + } + + if (is_sw_lra_auto_resonance_control(chip)) { + if (chip->status_flags & AUTO_RESONANCE_ENABLED) + qpnp_haptics_update_lra_frequency(chip); + hrtimer_cancel(&chip->auto_res_err_poll_timer); + } + + if (chip->play_mode == HAP_PWM) + pwm_disable(chip->pwm_data.pwm_dev); + } + +out: + mutex_unlock(&chip->play_lock); + return rc; +} + +static void qpnp_haptics_work(struct work_struct *work) +{ + struct hap_chip *chip = container_of(work, struct hap_chip, + haptics_work); + int rc; + bool enable; + + enable = atomic_read(&chip->state); + pr_debug("state: %d\n", enable); + rc = qpnp_haptics_play(chip, enable); + if (rc < 0) + pr_err("Error in %sing haptics, rc=%d\n", + enable ? "play" : "stopp", rc); +} + +static enum hrtimer_restart hap_stop_timer(struct hrtimer *timer) +{ + struct hap_chip *chip = container_of(timer, struct hap_chip, + stop_timer); + + atomic_set(&chip->state, 0); + schedule_work(&chip->haptics_work); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart hap_auto_res_err_poll_timer(struct hrtimer *timer) +{ + struct hap_chip *chip = container_of(timer, struct hap_chip, + auto_res_err_poll_timer); + + if (!(chip->status_flags & AUTO_RESONANCE_ENABLED)) + return HRTIMER_NORESTART; + + qpnp_haptics_update_lra_frequency(chip); + hrtimer_forward(&chip->auto_res_err_poll_timer, ktime_get(), + ktime_set(0, AUTO_RES_ERR_POLL_TIME_NS)); + + return HRTIMER_NORESTART; +} + +static int qpnp_haptics_suspend(struct device *dev) +{ + struct hap_chip *chip = dev_get_drvdata(dev); + int rc; + + rc = qpnp_haptics_play(chip, false); + if (rc < 0) + pr_err("Error in stopping haptics, rc=%d\n", rc); + + rc = qpnp_haptics_mod_enable(chip, false); + if (rc < 0) + pr_err("Error in disabling module, rc=%d\n", rc); + + return 0; +} + +static int qpnp_haptics_wave_rep_config(struct hap_chip *chip, + enum hap_rep_type type) +{ + int rc; + u8 val = 0, mask = 0; + + if (type & HAP_WAVE_REPEAT) { + if (chip->wave_rep_cnt < WF_REPEAT_MIN) + chip->wave_rep_cnt = WF_REPEAT_MIN; + else if (chip->wave_rep_cnt > WF_REPEAT_MAX) + chip->wave_rep_cnt = WF_REPEAT_MAX; + mask = WF_REPEAT_MASK; + val = ilog2(chip->wave_rep_cnt) << WF_REPEAT_SHIFT; + } + + if (type & HAP_WAVE_SAMP_REPEAT) { + if (chip->wave_s_rep_cnt < WF_S_REPEAT_MIN) + chip->wave_s_rep_cnt = WF_S_REPEAT_MIN; + else if (chip->wave_s_rep_cnt > WF_S_REPEAT_MAX) + chip->wave_s_rep_cnt = WF_S_REPEAT_MAX; + mask |= WF_S_REPEAT_MASK; + val |= ilog2(chip->wave_s_rep_cnt); + } + + rc = qpnp_haptics_masked_write_reg(chip, HAP_WF_REPEAT_REG(chip), + mask, val); + return rc; +} + +/* configuration api for buffer mode */ +static int qpnp_haptics_buffer_config(struct hap_chip *chip, u32 *wave_samp, + bool overdrive) +{ + u8 buf[HAP_WAVE_SAMP_LEN]; + u32 *ptr; + int rc, i; + + if (wave_samp) { + ptr = wave_samp; + } else { + if (chip->wave_samp_idx >= ARRAY_SIZE(chip->wave_samp)) { + pr_err("Incorrect wave_samp_idx %d\n", + chip->wave_samp_idx); + return -EINVAL; + } + + ptr = &chip->wave_samp[chip->wave_samp_idx]; + } + + /* Don't set override bit in waveform sample for PM660 */ + if (chip->revid->pmic_subtype == PM660_SUBTYPE) + overdrive = false; + + /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ + for (i = 0; i < HAP_WAVE_SAMP_LEN; i++) { + buf[i] = ptr[i]; + if (buf[i]) + buf[i] |= (overdrive ? HAP_WF_OVD_BIT : 0); + } + + rc = qpnp_haptics_write_reg(chip, HAP_WF_S1_REG(chip), buf, + HAP_WAVE_SAMP_LEN); + return rc; +} + +/* configuration api for pwm */ +static int qpnp_haptics_pwm_config(struct hap_chip *chip) +{ + u8 val = 0; + int rc; + + if (chip->ext_pwm_freq_khz == 0) + return 0; + + /* Configure the EXTERNAL_PWM register */ + if (chip->ext_pwm_freq_khz <= EXT_PWM_FREQ_25_KHZ) { + chip->ext_pwm_freq_khz = EXT_PWM_FREQ_25_KHZ; + val = 0; + } else if (chip->ext_pwm_freq_khz <= EXT_PWM_FREQ_50_KHZ) { + chip->ext_pwm_freq_khz = EXT_PWM_FREQ_50_KHZ; + val = 1; + } else if (chip->ext_pwm_freq_khz <= EXT_PWM_FREQ_75_KHZ) { + chip->ext_pwm_freq_khz = EXT_PWM_FREQ_75_KHZ; + val = 2; + } else { + chip->ext_pwm_freq_khz = EXT_PWM_FREQ_100_KHZ; + val = 3; + } + + rc = qpnp_haptics_masked_write_reg(chip, HAP_EXT_PWM_REG(chip), + EXT_PWM_FREQ_SEL_MASK, val); + if (rc < 0) + return rc; + + if (chip->ext_pwm_dtest_line < 0 || + chip->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { + pr_err("invalid dtest line\n"); + return -EINVAL; + } + + if (chip->ext_pwm_dtest_line > 0) { + /* disable auto res for PWM mode */ + val = chip->ext_pwm_dtest_line << HAP_EXT_PWM_DTEST_SHIFT; + rc = qpnp_haptics_masked_write_reg(chip, HAP_TEST2_REG(chip), + HAP_EXT_PWM_DTEST_MASK | AUTO_RES_EN_BIT, val); + if (rc < 0) + return rc; + } + + rc = pwm_config(chip->pwm_data.pwm_dev, + chip->pwm_data.duty_us * NSEC_PER_USEC, + chip->pwm_data.period_us * NSEC_PER_USEC); + if (rc < 0) { + pr_err("pwm_config failed, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int qpnp_haptics_lra_auto_res_config(struct hap_chip *chip, + struct hap_lra_ares_param *tmp_cfg) +{ + struct hap_lra_ares_param *ares_cfg; + int rc; + u8 val = 0, mask = 0; + + /* disable auto resonance for ERM */ + if (chip->act_type == HAP_ERM) { + val = 0x00; + rc = qpnp_haptics_write_reg(chip, HAP_LRA_AUTO_RES_REG(chip), + &val, 1); + return rc; + } + + if (chip->auto_res_err_recovery_hw) { + rc = qpnp_haptics_masked_write_reg(chip, + HAP_AUTO_RES_CTRL_REG(chip), + AUTO_RES_ERR_RECOVERY_BIT, AUTO_RES_ERR_RECOVERY_BIT); + if (rc < 0) + return rc; + } + + if (tmp_cfg) + ares_cfg = tmp_cfg; + else + ares_cfg = &chip->ares_cfg; + + if (ares_cfg->lra_res_cal_period < HAP_RES_CAL_PERIOD_MIN) + ares_cfg->lra_res_cal_period = HAP_RES_CAL_PERIOD_MIN; + + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + if (ares_cfg->lra_res_cal_period > + HAP_PM660_RES_CAL_PERIOD_MAX) + ares_cfg->lra_res_cal_period = + HAP_PM660_RES_CAL_PERIOD_MAX; + + if (ares_cfg->auto_res_mode == 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 / + HAP_RES_CAL_PERIOD_MIN) + 1; + } else { + if (ares_cfg->lra_res_cal_period > HAP_RES_CAL_PERIOD_MAX) + ares_cfg->lra_res_cal_period = + HAP_RES_CAL_PERIOD_MAX; + + if (ares_cfg->lra_res_cal_period) + val = ilog2(ares_cfg->lra_res_cal_period / + HAP_RES_CAL_PERIOD_MIN); + } + + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + val |= ares_cfg->auto_res_mode << PM660_AUTO_RES_MODE_SHIFT; + mask = PM660_AUTO_RES_MODE_BIT; + val |= ares_cfg->lra_high_z << PM660_CAL_DURATION_SHIFT; + mask |= PM660_CAL_DURATION_MASK; + if (ares_cfg->lra_qwd_drive_duration != -EINVAL) { + val |= ares_cfg->lra_qwd_drive_duration << + PM660_QWD_DRIVE_DURATION_SHIFT; + mask |= PM660_QWD_DRIVE_DURATION_BIT; + } + if (ares_cfg->calibrate_at_eop != -EINVAL) { + val |= ares_cfg->calibrate_at_eop << + PM660_CAL_EOP_SHIFT; + mask |= PM660_CAL_EOP_BIT; + } + mask |= PM660_LRA_RES_CAL_MASK; + } else { + val |= (ares_cfg->auto_res_mode << LRA_AUTO_RES_MODE_SHIFT); + val |= (ares_cfg->lra_high_z << LRA_HIGH_Z_SHIFT); + mask = LRA_AUTO_RES_MODE_MASK | LRA_HIGH_Z_MASK | + LRA_RES_CAL_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_haptics_masked_write_reg(chip, HAP_LRA_AUTO_RES_REG(chip), + mask, val); + return rc; +} + +/* configuration api for play mode */ +static int qpnp_haptics_play_mode_config(struct hap_chip *chip) +{ + u8 val = 0; + int rc; + + if (!is_haptics_idle(chip)) + return -EBUSY; + + val = chip->play_mode << HAP_WF_SOURCE_SHIFT; + rc = qpnp_haptics_masked_write_reg(chip, HAP_SEL_REG(chip), + HAP_WF_SOURCE_MASK, val); + return rc; +} + +/* configuration api for max voltage */ +static int qpnp_haptics_vmax_config(struct hap_chip *chip, int vmax_mv, + bool overdrive) +{ + u8 val = 0; + int rc; + + if (vmax_mv < 0) + return -EINVAL; + + /* Allow setting override bit in VMAX_CFG only for PM660 */ + if (chip->revid->pmic_subtype != PM660_SUBTYPE) + overdrive = false; + + if (vmax_mv < HAP_VMAX_MIN_MV) + vmax_mv = HAP_VMAX_MIN_MV; + else if (vmax_mv > HAP_VMAX_MAX_MV) + vmax_mv = HAP_VMAX_MAX_MV; + + val = DIV_ROUND_CLOSEST(vmax_mv, HAP_VMAX_MIN_MV); + val <<= HAP_VMAX_SHIFT; + if (overdrive) + val |= HAP_VMAX_OVD_BIT; + + rc = qpnp_haptics_masked_write_reg(chip, HAP_VMAX_CFG_REG(chip), + HAP_VMAX_MASK | HAP_VMAX_OVD_BIT, val); + return rc; +} + +/* configuration api for ilim */ +static int qpnp_haptics_ilim_config(struct hap_chip *chip) +{ + int rc; + + if (chip->ilim_ma < HAP_ILIM_400_MA) + chip->ilim_ma = HAP_ILIM_400_MA; + else if (chip->ilim_ma > HAP_ILIM_800_MA) + chip->ilim_ma = HAP_ILIM_800_MA; + + rc = qpnp_haptics_masked_write_reg(chip, HAP_ILIM_CFG_REG(chip), + HAP_ILIM_SEL_MASK, chip->ilim_ma); + return rc; +} + +/* configuration api for short circuit debounce */ +static int qpnp_haptics_sc_deb_config(struct hap_chip *chip) +{ + u8 val = 0; + int rc; + + if (chip->sc_deb_cycles < HAP_SC_DEB_CYCLES_MIN) + chip->sc_deb_cycles = HAP_SC_DEB_CYCLES_MIN; + else if (chip->sc_deb_cycles > HAP_SC_DEB_CYCLES_MAX) + chip->sc_deb_cycles = HAP_SC_DEB_CYCLES_MAX; + + if (chip->sc_deb_cycles != HAP_SC_DEB_CYCLES_MIN) + val = ilog2(chip->sc_deb_cycles / + HAP_DEF_SC_DEB_CYCLES) + 1; + else + val = HAP_SC_DEB_CYCLES_MIN; + + rc = qpnp_haptics_masked_write_reg(chip, HAP_SC_DEB_REG(chip), + HAP_SC_DEB_MASK, val); + + return rc; +} + +static int qpnp_haptics_brake_config(struct hap_chip *chip, u32 *brake_pat) +{ + int rc, i; + u32 temp, *ptr; + u8 val; + + /* Configure BRAKE register */ + rc = qpnp_haptics_masked_write_reg(chip, HAP_EN_CTL2_REG(chip), + BRAKE_EN_BIT, (u8)chip->en_brake); + if (rc < 0) + return rc; + + /* If braking is not enabled, skip configuring brake pattern */ + if (!chip->en_brake) + return 0; + + if (!brake_pat) + ptr = chip->brake_pat; + else + ptr = brake_pat; + + for (i = HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { + ptr[i] &= HAP_BRAKE_PAT_MASK; + temp = i << 1; + val |= ptr[i] << temp; + } + + rc = qpnp_haptics_write_reg(chip, HAP_BRAKE_REG(chip), &val, 1); + if (rc < 0) + return rc; + + return 0; +} + +static int qpnp_haptics_auto_mode_config(struct hap_chip *chip, int time_ms) +{ + struct hap_lra_ares_param ares_cfg; + enum hap_mode old_play_mode; + u8 old_ares_mode; + u32 brake_pat[HAP_BRAKE_PAT_LEN] = {0}; + u32 wave_samp[HAP_WAVE_SAMP_LEN] = {0}; + int rc, vmax_mv; + + if (!chip->lra_auto_mode) + return false; + + /* For now, this is for LRA only */ + if (chip->act_type == HAP_ERM) + return 0; + + old_ares_mode = chip->ares_cfg.auto_res_mode; + old_play_mode = chip->play_mode; + pr_debug("auto_mode, time_ms: %d\n", time_ms); + if (time_ms <= 20) { + wave_samp[0] = 0x7e; + wave_samp[1] = 0x7e; + wave_samp[2] = 0x7e; + wave_samp[3] = 0x7e; + wave_samp[4] = 0x7e; + wave_samp[5] = 0x28; + wave_samp[6] = 0x28; + wave_samp[7] = 0x28; + + /* short pattern */ + rc = qpnp_haptics_parse_buffer_dt(chip); + if (!rc) { + rc = qpnp_haptics_wave_rep_config(chip, + HAP_WAVE_REPEAT | HAP_WAVE_SAMP_REPEAT); + if (rc < 0) { + pr_err("Error in configuring wave_rep config %d\n", + rc); + return rc; + } + + rc = qpnp_haptics_buffer_config(chip, wave_samp, false); + if (rc < 0) { + pr_err("Error in configuring buffer mode %d\n", + rc); + return rc; + } + } + + ares_cfg.lra_high_z = HAP_LRA_HIGH_Z_OPT1; + ares_cfg.lra_res_cal_period = HAP_RES_CAL_PERIOD_MIN; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + ares_cfg.auto_res_mode = HAP_PM660_AUTO_RES_QWD; + ares_cfg.lra_qwd_drive_duration = 0; + ares_cfg.calibrate_at_eop = 0; + } else { + ares_cfg.auto_res_mode = HAP_AUTO_RES_QWD; + ares_cfg.lra_qwd_drive_duration = -EINVAL; + ares_cfg.calibrate_at_eop = -EINVAL; + } + + if (chip->vmax_mv >= 2320) + vmax_mv = HAP_VMAX_MAX_MV; + else + vmax_mv = chip->vmax_mv; + rc = qpnp_haptics_vmax_config(chip, vmax_mv, true); + if (rc < 0) + return rc; + + brake_pat[0] = 0x3; + brake_pat[1] = 0x3; + brake_pat[2] = 0x3; + brake_pat[3] = 0x3; + rc = qpnp_haptics_brake_config(chip, brake_pat); + if (rc < 0) + return rc; + + /* enable play_irq for buffer mode */ + if (chip->play_irq >= 0 && !chip->play_irq_en) { + enable_irq(chip->play_irq); + chip->play_irq_en = true; + } + + chip->play_mode = HAP_BUFFER; + chip->wave_shape = HAP_WAVE_SINE; + } else { + wave_samp[0] = 0x28; + wave_samp[1] = 0x28; + wave_samp[2] = 0x28; + wave_samp[3] = 0x28; + wave_samp[4] = 0x28; + wave_samp[5] = 0x28; + wave_samp[6] = 0x28; + wave_samp[7] = 0x28; + rc = qpnp_haptics_parse_buffer_dt(chip); + if (!rc) { + rc = qpnp_haptics_wave_rep_config(chip, + HAP_WAVE_REPEAT | HAP_WAVE_SAMP_REPEAT); + if (rc < 0) { + pr_err("Error in configuring wave_rep config %d\n", + rc); + return rc; + } + + rc = qpnp_haptics_buffer_config(chip, wave_samp, false); + if (rc < 0) { + pr_err("Error in configuring buffer mode %d\n", + rc); + return rc; + } + } + /* long pattern */ + ares_cfg.lra_high_z = HAP_LRA_HIGH_Z_OPT1; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + ares_cfg.auto_res_mode = HAP_PM660_AUTO_RES_ZXD; + ares_cfg.lra_res_cal_period = + 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 = HAP_AUTO_RES_ZXD_EOP; + ares_cfg.lra_res_cal_period = HAP_RES_CAL_PERIOD_MAX; + ares_cfg.lra_qwd_drive_duration = -EINVAL; + ares_cfg.calibrate_at_eop = -EINVAL; + } + + vmax_mv = chip->vmax_mv; + rc = qpnp_haptics_vmax_config(chip, vmax_mv, false); + if (rc < 0) + return rc; + + brake_pat[0] = 0x3; + brake_pat[1] = 0x3; + brake_pat[2] = 0x3; + brake_pat[3] = 0x3; + rc = qpnp_haptics_brake_config(chip, brake_pat); + if (rc < 0) + return rc; + + /* enable play_irq for direct mode */ + if (chip->play_irq >= 0 && chip->play_irq_en) { + disable_irq(chip->play_irq); + chip->play_irq_en = false; + } + + chip->play_mode = HAP_BUFFER; + chip->wave_shape = HAP_WAVE_SINE; + } + + chip->ares_cfg.auto_res_mode = ares_cfg.auto_res_mode; + rc = qpnp_haptics_lra_auto_res_config(chip, &ares_cfg); + if (rc < 0) { + chip->ares_cfg.auto_res_mode = old_ares_mode; + return rc; + } + + rc = qpnp_haptics_play_mode_config(chip); + if (rc < 0) { + chip->play_mode = old_play_mode; + return rc; + } + + rc = qpnp_haptics_masked_write_reg(chip, HAP_CFG2_REG(chip), + HAP_LRA_RES_TYPE_MASK, chip->wave_shape); + if (rc < 0) + return rc; + + return 0; +} + +static irqreturn_t qpnp_haptics_play_irq_handler(int irq, void *data) +{ + struct hap_chip *chip = data; + int rc; + + if (chip->play_mode != HAP_BUFFER) + goto irq_handled; + + if (chip->wave_samp[chip->wave_samp_idx + HAP_WAVE_SAMP_LEN] > 0) { + chip->wave_samp_idx += HAP_WAVE_SAMP_LEN; + if (chip->wave_samp_idx >= ARRAY_SIZE(chip->wave_samp)) { + pr_debug("Samples over\n"); + /* fall through to stop playing */ + } else { + pr_debug("moving to next sample set %d\n", + chip->wave_samp_idx); + + rc = qpnp_haptics_buffer_config(chip, NULL, false); + if (rc < 0) { + pr_err("Error in configuring buffer, rc=%d\n", + rc); + goto irq_handled; + } + + /* + * Moving to next set of wave sample. No need to stop + * or change the play control. Just return. + */ + goto irq_handled; + } + } + + rc = qpnp_haptics_play_control(chip, HAP_STOP); + if (rc < 0) { + pr_err("Error in disabling play, rc=%d\n", rc); + goto irq_handled; + } + chip->wave_samp_idx = 0; + +irq_handled: + return IRQ_HANDLED; +} + +#define SC_MAX_COUNT 5 +#define SC_COUNT_RST_DELAY_US 1000000 +static irqreturn_t qpnp_haptics_sc_irq_handler(int irq, void *data) +{ + struct hap_chip *chip = data; + int rc; + u8 val; + s64 sc_delta_time_us; + ktime_t temp; + + rc = qpnp_haptics_read_reg(chip, HAP_STATUS_1_REG(chip), &val, 1); + if (rc < 0) + goto irq_handled; + + if (!(val & SC_FLAG_BIT)) { + chip->sc_count = 0; + goto irq_handled; + } + + pr_debug("SC irq fired\n"); + temp = ktime_get(); + sc_delta_time_us = ktime_us_delta(temp, chip->last_sc_time); + chip->last_sc_time = temp; + + if (sc_delta_time_us > SC_COUNT_RST_DELAY_US) + chip->sc_count = 0; + else + chip->sc_count++; + + val = SC_CLR_BIT; + rc = qpnp_haptics_write_reg(chip, HAP_SC_CLR_REG(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing to SC_CLR_REG, rc=%d\n", rc); + goto irq_handled; + } + + /* Permanently disable module if SC condition persists */ + if (chip->sc_count > SC_MAX_COUNT) { + pr_crit("SC persists, permanently disabling haptics\n"); + rc = qpnp_haptics_mod_enable(chip, false); + if (rc < 0) { + pr_err("Error in disabling module, rc=%d\n", rc); + goto irq_handled; + } + chip->perm_disable = true; + } + +irq_handled: + return IRQ_HANDLED; +} + +/* All sysfs show/store functions below */ + +#define HAP_STR_SIZE 128 +static int parse_string(const char *in_buf, char *out_buf) +{ + int i; + + if (snprintf(out_buf, HAP_STR_SIZE, "%s", in_buf) > 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; +} + +static ssize_t qpnp_haptics_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->module_en); +} + +static ssize_t qpnp_haptics_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + + /* At present, nothing to do with setting state */ + return count; +} + +static ssize_t qpnp_haptics_show_duration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + ktime_t time_rem; + s64 time_us = 0; + + if (hrtimer_active(&chip->stop_timer)) { + time_rem = hrtimer_get_remaining(&chip->stop_timer); + time_us = ktime_to_us(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", div_s64(time_us, 1000)); +} + +static ssize_t qpnp_haptics_store_duration(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + u32 val; + int rc; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + if (val > chip->max_play_time_ms) + return -EINVAL; + + mutex_lock(&chip->param_lock); + rc = qpnp_haptics_auto_mode_config(chip, val); + if (rc < 0) { + pr_err("Unable to do auto mode config\n"); + mutex_unlock(&chip->param_lock); + return rc; + } + + chip->play_time_ms = val; + mutex_unlock(&chip->param_lock); + + return count; +} + +static ssize_t qpnp_haptics_show_activate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", 0); +} + +static ssize_t qpnp_haptics_store_activate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + u32 val; + int rc; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + if (val) { + hrtimer_cancel(&chip->stop_timer); + if (is_sw_lra_auto_resonance_control(chip)) + hrtimer_cancel(&chip->auto_res_err_poll_timer); + cancel_work_sync(&chip->haptics_work); + + atomic_set(&chip->state, 1); + schedule_work(&chip->haptics_work); + } else { + rc = qpnp_haptics_mod_enable(chip, false); + if (rc < 0) { + pr_err("Error in disabling module, rc=%d\n", rc); + return rc; + } + } + + return count; +} + +static ssize_t qpnp_haptics_show_play_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + char *str; + + if (chip->play_mode == HAP_BUFFER) + str = "buffer"; + else if (chip->play_mode == HAP_DIRECT) + str = "direct"; + else if (chip->play_mode == HAP_AUDIO) + str = "audio"; + else if (chip->play_mode == HAP_PWM) + str = "pwm"; + else + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qpnp_haptics_store_play_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + char str[HAP_STR_SIZE + 1]; + int rc = 0, temp, old_mode; + + rc = parse_string(buf, str); + if (rc < 0) + return rc; + + if (strcmp(str, "buffer") == 0) + temp = HAP_BUFFER; + else if (strcmp(str, "direct") == 0) + temp = HAP_DIRECT; + else if (strcmp(str, "audio") == 0) + temp = HAP_AUDIO; + else if (strcmp(str, "pwm") == 0) + temp = HAP_PWM; + else + return -EINVAL; + + if (temp == chip->play_mode) + return count; + + if (temp == HAP_BUFFER) { + rc = qpnp_haptics_parse_buffer_dt(chip); + if (!rc) { + rc = qpnp_haptics_wave_rep_config(chip, + HAP_WAVE_REPEAT | HAP_WAVE_SAMP_REPEAT); + if (rc < 0) { + pr_err("Error in configuring wave_rep config %d\n", + rc); + return rc; + } + } + + rc = qpnp_haptics_buffer_config(chip, NULL, true); + } else if (temp == HAP_PWM) { + rc = qpnp_haptics_parse_pwm_dt(chip); + if (!rc) + rc = qpnp_haptics_pwm_config(chip); + } + + if (rc < 0) + return rc; + + rc = qpnp_haptics_mod_enable(chip, false); + if (rc < 0) + return rc; + + old_mode = chip->play_mode; + chip->play_mode = temp; + rc = qpnp_haptics_play_mode_config(chip); + if (rc < 0) { + chip->play_mode = old_mode; + return rc; + } + + if (chip->play_mode == HAP_AUDIO) { + rc = qpnp_haptics_mod_enable(chip, true); + if (rc < 0) { + chip->play_mode = old_mode; + return rc; + } + } + + return count; +} + +static ssize_t qpnp_haptics_show_wf_samp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + char str[HAP_STR_SIZE + 1]; + char *ptr = str; + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(chip->wave_samp); i++) { + len = scnprintf(ptr, HAP_STR_SIZE, "%x ", chip->wave_samp[i]); + ptr += len; + } + ptr[len] = '\0'; + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static ssize_t qpnp_haptics_store_wf_samp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + u8 samp[HAP_WAVE_SAMP_SET_LEN] = {0}; + int bytes_read, rc; + unsigned int data, pos = 0, i = 0; + + while (pos < count && i < ARRAY_SIZE(samp) && + sscanf(buf + pos, "%x%n", &data, &bytes_read) == 1) { + /* bit 0 is not used in WF_Sx */ + samp[i++] = data & GENMASK(7, 1); + pos += bytes_read; + } + + for (i = 0; i < ARRAY_SIZE(chip->wave_samp); i++) + chip->wave_samp[i] = samp[i]; + + rc = qpnp_haptics_buffer_config(chip, NULL, false); + if (rc < 0) { + pr_err("Error in configuring buffer mode %d\n", rc); + return rc; + } + + return count; +} + +static ssize_t qpnp_haptics_show_wf_rep_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->wave_rep_cnt); +} + +static ssize_t qpnp_haptics_store_wf_rep_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + int data, rc, old_wave_rep_cnt; + + rc = kstrtoint(buf, 10, &data); + if (rc < 0) + return rc; + + old_wave_rep_cnt = chip->wave_rep_cnt; + chip->wave_rep_cnt = data; + rc = qpnp_haptics_wave_rep_config(chip, HAP_WAVE_REPEAT); + if (rc < 0) { + chip->wave_rep_cnt = old_wave_rep_cnt; + return rc; + } + + return count; +} + +static ssize_t qpnp_haptics_show_wf_s_rep_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->wave_s_rep_cnt); +} + +static ssize_t qpnp_haptics_store_wf_s_rep_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + int data, rc, old_wave_s_rep_cnt; + + rc = kstrtoint(buf, 10, &data); + if (rc < 0) + return rc; + + old_wave_s_rep_cnt = chip->wave_s_rep_cnt; + chip->wave_s_rep_cnt = data; + rc = qpnp_haptics_wave_rep_config(chip, HAP_WAVE_SAMP_REPEAT); + if (rc < 0) { + chip->wave_s_rep_cnt = old_wave_s_rep_cnt; + return rc; + } + + return count; +} + +static ssize_t qpnp_haptics_show_vmax(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->vmax_mv); +} + +static ssize_t qpnp_haptics_store_vmax(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + int data, rc, old_vmax_mv; + + rc = kstrtoint(buf, 10, &data); + if (rc < 0) + return rc; + + old_vmax_mv = chip->vmax_mv; + chip->vmax_mv = data; + rc = qpnp_haptics_vmax_config(chip, chip->vmax_mv, false); + if (rc < 0) { + chip->vmax_mv = old_vmax_mv; + return rc; + } + + return count; +} + +static ssize_t qpnp_haptics_show_lra_auto_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->lra_auto_mode); +} + +static ssize_t qpnp_haptics_store_lra_auto_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev); + int rc, data; + + rc = kstrtoint(buf, 10, &data); + if (rc < 0) + return rc; + + if (data != 0 && data != 1) + return count; + + chip->lra_auto_mode = !!data; + return count; +} + +static struct device_attribute qpnp_haptics_attrs[] = { + __ATTR(state, 0664, qpnp_haptics_show_state, qpnp_haptics_store_state), + __ATTR(duration, 0664, qpnp_haptics_show_duration, + qpnp_haptics_store_duration), + __ATTR(activate, 0664, qpnp_haptics_show_activate, + qpnp_haptics_store_activate), + __ATTR(play_mode, 0664, qpnp_haptics_show_play_mode, + qpnp_haptics_store_play_mode), + __ATTR(wf_samp, 0664, qpnp_haptics_show_wf_samp, + qpnp_haptics_store_wf_samp), + __ATTR(wf_rep_count, 0664, qpnp_haptics_show_wf_rep_count, + qpnp_haptics_store_wf_rep_count), + __ATTR(wf_s_rep_count, 0664, qpnp_haptics_show_wf_s_rep_count, + qpnp_haptics_store_wf_s_rep_count), + __ATTR(vmax_mv, 0664, qpnp_haptics_show_vmax, qpnp_haptics_store_vmax), + __ATTR(lra_auto_mode, 0664, qpnp_haptics_show_lra_auto_mode, + qpnp_haptics_store_lra_auto_mode), +}; + +/* Dummy functions for brightness */ +static +enum led_brightness qpnp_haptics_brightness_get(struct led_classdev *cdev) +{ + return 0; +} + +static void qpnp_haptics_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ +} + +static int qpnp_haptics_config(struct hap_chip *chip) +{ + u8 rc_clk_err_deci_pct; + u16 play_rate = 0; + int rc; + + /* Configure the CFG1 register for actuator type */ + rc = qpnp_haptics_masked_write_reg(chip, HAP_CFG1_REG(chip), + HAP_ACT_TYPE_MASK, chip->act_type); + if (rc < 0) + return rc; + + /* Configure auto resonance parameters */ + rc = qpnp_haptics_lra_auto_res_config(chip, NULL); + if (rc < 0) + return rc; + + /* Configure the PLAY MODE register */ + rc = qpnp_haptics_play_mode_config(chip); + if (rc < 0) + return rc; + + /* Configure the VMAX register */ + rc = qpnp_haptics_vmax_config(chip, chip->vmax_mv, false); + if (rc < 0) + return rc; + + /* Configure the ILIM register */ + rc = qpnp_haptics_ilim_config(chip); + if (rc < 0) + return rc; + + /* Configure the short circuit debounce register */ + rc = qpnp_haptics_sc_deb_config(chip); + if (rc < 0) + return rc; + + /* Configure the WAVE SHAPE register */ + rc = qpnp_haptics_masked_write_reg(chip, HAP_CFG2_REG(chip), + HAP_LRA_RES_TYPE_MASK, chip->wave_shape); + if (rc < 0) + return rc; + + play_rate = chip->wave_play_rate_us / HAP_RATE_CFG_STEP_US; + + /* + * The frequency of 19.2 MHz RC clock is subject to variation. Currently + * some PMI chips have MISC_TRIM_ERROR_RC19P2_CLK register present in + * MISC peripheral. This register holds the trim error of RC clock. + */ + if (chip->act_type == HAP_LRA && chip->misc_clk_trim_error_reg) { + /* + * Error is available in bits[3:0] and each LSB is 0.7%. + * Bit 7 is the sign bit for error code. If it is set, then a + * negative error correction needs to be made. Otherwise, a + * positive error correction needs to be made. + */ + rc_clk_err_deci_pct = (chip->clk_trim_error_code & 0x0F) * 7; + if (chip->clk_trim_error_code & BIT(7)) + play_rate = (play_rate * + (1000 - rc_clk_err_deci_pct)) / 1000; + else + play_rate = (play_rate * + (1000 + rc_clk_err_deci_pct)) / 1000; + + pr_debug("TRIM register = 0x%x, play_rate=%d\n", + chip->clk_trim_error_code, play_rate); + } + + /* + * Configure RATE_CFG1 and RATE_CFG2 registers. + * Note: For ERM these registers act as play rate and + * for LRA these represent resonance period + */ + rc = qpnp_haptics_update_rate_cfg(chip, play_rate); + if (chip->act_type == HAP_LRA) { + chip->drive_period_code_max_limit = (play_rate * + (100 + chip->drive_period_code_max_var_pct)) / 100; + chip->drive_period_code_min_limit = (play_rate * + (100 - chip->drive_period_code_min_var_pct)) / 100; + pr_debug("Drive period code max limit %x min limit %x\n", + chip->drive_period_code_max_limit, + chip->drive_period_code_min_limit); + } + + rc = qpnp_haptics_brake_config(chip, NULL); + if (rc < 0) + return rc; + + if (chip->play_mode == HAP_BUFFER) { + rc = qpnp_haptics_wave_rep_config(chip, + HAP_WAVE_REPEAT | HAP_WAVE_SAMP_REPEAT); + if (rc < 0) + return rc; + + rc = qpnp_haptics_buffer_config(chip, NULL, false); + } else if (chip->play_mode == HAP_PWM) { + rc = qpnp_haptics_pwm_config(chip); + } else if (chip->play_mode == HAP_AUDIO) { + rc = qpnp_haptics_mod_enable(chip, true); + } + + if (rc < 0) + return rc; + + /* setup play irq */ + if (chip->play_irq >= 0) { + rc = devm_request_threaded_irq(&chip->pdev->dev, chip->play_irq, + NULL, qpnp_haptics_play_irq_handler, IRQF_ONESHOT, + "haptics_play_irq", chip); + if (rc < 0) { + pr_err("Unable to request play(%d) IRQ(err:%d)\n", + chip->play_irq, rc); + return rc; + } + + chip->play_irq_en = true; + /* use play_irq only for buffer mode */ + if (chip->play_mode != HAP_BUFFER) { + disable_irq(chip->play_irq); + chip->play_irq_en = false; + } + } + + /* setup short circuit irq */ + if (chip->sc_irq >= 0) { + rc = devm_request_threaded_irq(&chip->pdev->dev, chip->sc_irq, + NULL, qpnp_haptics_sc_irq_handler, IRQF_ONESHOT, + "haptics_sc_irq", chip); + if (rc < 0) { + pr_err("Unable to request sc(%d) IRQ(err:%d)\n", + chip->sc_irq, rc); + return rc; + } + } + + return rc; +} + +static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip) +{ + struct device_node *node = chip->pdev->dev.of_node; + u32 temp; + int rc, i, wf_samp_len; + + if (chip->wave_rep_cnt > 0 || chip->wave_s_rep_cnt > 0) + return 0; + + chip->wave_rep_cnt = WF_REPEAT_MIN; + rc = of_property_read_u32(node, "qcom,wave-rep-cnt", &temp); + if (!rc) { + chip->wave_rep_cnt = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read rep cnt rc=%d\n", rc); + return rc; + } + + chip->wave_s_rep_cnt = WF_S_REPEAT_MIN; + rc = of_property_read_u32(node, + "qcom,wave-samp-rep-cnt", &temp); + if (!rc) { + chip->wave_s_rep_cnt = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read samp rep cnt rc=%d\n", rc); + return rc; + } + + wf_samp_len = of_property_count_elems_of_size(node, + "qcom,wave-samples", sizeof(u32)); + if (wf_samp_len > 0) { + if (wf_samp_len > HAP_WAVE_SAMP_SET_LEN) { + pr_err("Invalid length for wave samples\n"); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, "qcom,wave-samples", + chip->wave_samp, wf_samp_len); + if (rc < 0) { + pr_err("Error in reading qcom,wave-samples, rc=%d\n", + rc); + return rc; + } + } else { + /* Use default values */ + for (i = 0; i < HAP_WAVE_SAMP_LEN; i++) + chip->wave_samp[i] = HAP_WF_SAMP_MAX; + } + + return 0; +} + +static int qpnp_haptics_parse_pwm_dt(struct hap_chip *chip) +{ + struct device_node *node = chip->pdev->dev.of_node; + u32 temp; + int rc; + + if (chip->pwm_data.period_us > 0 && chip->pwm_data.duty_us > 0) + return 0; + + chip->pwm_data.pwm_dev = of_pwm_get(node, NULL); + if (IS_ERR(chip->pwm_data.pwm_dev)) { + rc = PTR_ERR(chip->pwm_data.pwm_dev); + pr_err("Cannot get PWM device rc=%d\n", rc); + chip->pwm_data.pwm_dev = NULL; + return rc; + } + + rc = of_property_read_u32(node, "qcom,period-us", &temp); + if (!rc) { + chip->pwm_data.period_us = temp; + } else { + pr_err("Cannot read PWM period rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,duty-us", &temp); + if (!rc) { + chip->pwm_data.duty_us = temp; + } else { + pr_err("Cannot read PWM duty rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,ext-pwm-dtest-line", &temp); + if (!rc) + chip->ext_pwm_dtest_line = temp; + + rc = of_property_read_u32(node, "qcom,ext-pwm-freq-khz", &temp); + if (!rc) { + chip->ext_pwm_freq_khz = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read ext pwm freq rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int qpnp_haptics_parse_dt(struct hap_chip *chip) +{ + struct device_node *node = chip->pdev->dev.of_node; + struct device_node *revid_node, *misc_node; + const char *temp_str; + int rc, temp; + + rc = of_property_read_u32(node, "reg", &temp); + if (rc < 0) { + pr_err("Couldn't find reg in node = %s rc = %d\n", + node->full_name, rc); + return rc; + } + + if (temp <= 0) { + pr_err("Invalid base address %x\n", temp); + return -EINVAL; + } + chip->base = (u16)temp; + + revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); + if (!revid_node) { + pr_err("Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + chip->revid = get_revid_data(revid_node); + of_node_put(revid_node); + if (IS_ERR_OR_NULL(chip->revid)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(chip->revid)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + if (of_find_property(node, "qcom,pmic-misc", NULL)) { + misc_node = of_parse_phandle(node, "qcom,pmic-misc", 0); + if (!misc_node) + return -EINVAL; + + rc = of_property_read_u32(node, "qcom,misc-clk-trim-error-reg", + &chip->misc_clk_trim_error_reg); + if (rc < 0 || !chip->misc_clk_trim_error_reg) { + pr_err("Invalid or missing misc-clk-trim-error-reg\n"); + of_node_put(misc_node); + return rc; + } + + rc = qpnp_misc_read_reg(misc_node, + chip->misc_clk_trim_error_reg, + &chip->clk_trim_error_code); + if (rc < 0) { + pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc); + of_node_put(misc_node); + return -EPROBE_DEFER; + } + of_node_put(misc_node); + } + + chip->play_irq = platform_get_irq_byname(chip->pdev, "hap-play-irq"); + if (chip->play_irq < 0) { + pr_err("Unable to get play irq\n"); + return chip->play_irq; + } + + chip->sc_irq = platform_get_irq_byname(chip->pdev, "hap-sc-irq"); + if (chip->sc_irq < 0) { + pr_err("Unable to get sc irq\n"); + return chip->sc_irq; + } + + chip->act_type = HAP_LRA; + rc = of_property_read_u32(node, "qcom,actuator-type", &temp); + if (!rc) { + if (temp != HAP_LRA && temp != HAP_ERM) { + pr_err("Incorrect actuator type\n"); + return -EINVAL; + } + chip->act_type = temp; + } + + chip->lra_auto_mode = of_property_read_bool(node, "qcom,lra-auto-mode"); + + rc = of_property_read_string(node, "qcom,play-mode", &temp_str); + if (!rc) { + if (strcmp(temp_str, "direct") == 0) + chip->play_mode = HAP_DIRECT; + else if (strcmp(temp_str, "buffer") == 0) + chip->play_mode = HAP_BUFFER; + else if (strcmp(temp_str, "pwm") == 0) + chip->play_mode = HAP_PWM; + else if (strcmp(temp_str, "audio") == 0) + chip->play_mode = HAP_AUDIO; + else { + pr_err("Invalid play mode\n"); + return -EINVAL; + } + } else { + if (rc == -EINVAL && chip->act_type == HAP_LRA) { + pr_info("Play mode not specified, using auto mode\n"); + chip->lra_auto_mode = true; + } else { + pr_err("Unable to read play mode\n"); + return rc; + } + } + + chip->max_play_time_ms = HAP_MAX_PLAY_TIME_MS; + rc = of_property_read_u32(node, "qcom,max-play-time-ms", &temp); + if (!rc) { + chip->max_play_time_ms = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read max-play-time rc=%d\n", rc); + return rc; + } + + chip->vmax_mv = HAP_VMAX_MAX_MV; + rc = of_property_read_u32(node, "qcom,vmax-mv", &temp); + if (!rc) { + chip->vmax_mv = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read Vmax rc=%d\n", rc); + return rc; + } + + chip->ilim_ma = HAP_ILIM_400_MA; + rc = of_property_read_u32(node, "qcom,ilim-ma", &temp); + if (!rc) { + chip->ilim_ma = (u8)temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read ILIM rc=%d\n", rc); + return rc; + } + + chip->sc_deb_cycles = HAP_DEF_SC_DEB_CYCLES; + rc = of_property_read_u32(node, "qcom,sc-dbc-cycles", &temp); + if (!rc) { + chip->sc_deb_cycles = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read sc debounce rc=%d\n", rc); + return rc; + } + + chip->wave_shape = HAP_WAVE_SQUARE; + rc = of_property_read_string(node, "qcom,wave-shape", &temp_str); + if (!rc) { + if (strcmp(temp_str, "sine") == 0) + chip->wave_shape = HAP_WAVE_SINE; + else if (strcmp(temp_str, "square") == 0) + chip->wave_shape = HAP_WAVE_SQUARE; + else { + pr_err("Unsupported wave shape\n"); + return -EINVAL; + } + } else if (rc != -EINVAL) { + pr_err("Unable to read wave shape rc=%d\n", rc); + return rc; + } + + chip->wave_play_rate_us = HAP_DEF_WAVE_PLAY_RATE_US; + rc = of_property_read_u32(node, + "qcom,wave-play-rate-us", &temp); + if (!rc) { + chip->wave_play_rate_us = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read play rate rc=%d\n", rc); + return rc; + } + + if (chip->wave_play_rate_us < HAP_WAVE_PLAY_RATE_US_MIN) + chip->wave_play_rate_us = HAP_WAVE_PLAY_RATE_US_MIN; + else if (chip->wave_play_rate_us > HAP_WAVE_PLAY_RATE_US_MAX) + chip->wave_play_rate_us = HAP_WAVE_PLAY_RATE_US_MAX; + + chip->en_brake = of_property_read_bool(node, "qcom,en-brake"); + + rc = of_property_count_elems_of_size(node, + "qcom,brake-pattern", sizeof(u32)); + if (rc > 0) { + if (rc != HAP_BRAKE_PAT_LEN) { + pr_err("Invalid length for brake pattern\n"); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, "qcom,brake-pattern", + chip->brake_pat, HAP_BRAKE_PAT_LEN); + if (rc < 0) { + pr_err("Error in reading qcom,brake-pattern, rc=%d\n", + rc); + return rc; + } + } + + /* Read the following properties only for LRA */ + if (chip->act_type == HAP_LRA) { + rc = of_property_read_string(node, "qcom,lra-auto-res-mode", + &temp_str); + if (!rc) { + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + chip->ares_cfg.auto_res_mode = + HAP_PM660_AUTO_RES_QWD; + if (strcmp(temp_str, "zxd") == 0) + chip->ares_cfg.auto_res_mode = + HAP_PM660_AUTO_RES_ZXD; + else if (strcmp(temp_str, "qwd") == 0) + chip->ares_cfg.auto_res_mode = + HAP_PM660_AUTO_RES_QWD; + } else { + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_ZXD_EOP; + if (strcmp(temp_str, "none") == 0) + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_NONE; + else if (strcmp(temp_str, "zxd") == 0) + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_ZXD; + else if (strcmp(temp_str, "qwd") == 0) + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_QWD; + else if (strcmp(temp_str, "max-qwd") == 0) + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_MAX_QWD; + else + chip->ares_cfg.auto_res_mode = + HAP_AUTO_RES_ZXD_EOP; + } + } else if (rc != -EINVAL) { + pr_err("Unable to read auto res mode rc=%d\n", rc); + return rc; + } + + chip->ares_cfg.lra_high_z = HAP_LRA_HIGH_Z_OPT3; + rc = of_property_read_string(node, "qcom,lra-high-z", + &temp_str); + if (!rc) { + if (strcmp(temp_str, "none") == 0) + chip->ares_cfg.lra_high_z = + HAP_LRA_HIGH_Z_NONE; + else if (strcmp(temp_str, "opt1") == 0) + chip->ares_cfg.lra_high_z = + HAP_LRA_HIGH_Z_OPT1; + else if (strcmp(temp_str, "opt2") == 0) + chip->ares_cfg.lra_high_z = + HAP_LRA_HIGH_Z_OPT2; + else + chip->ares_cfg.lra_high_z = + HAP_LRA_HIGH_Z_OPT3; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + if (strcmp(temp_str, "opt0") == 0) + chip->ares_cfg.lra_high_z = + HAP_LRA_HIGH_Z_NONE; + } + } else if (rc != -EINVAL) { + pr_err("Unable to read LRA high-z rc=%d\n", rc); + return rc; + } + + chip->ares_cfg.lra_res_cal_period = HAP_RES_CAL_PERIOD_MAX; + rc = of_property_read_u32(node, + "qcom,lra-res-cal-period", &temp); + if (!rc) { + chip->ares_cfg.lra_res_cal_period = temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read cal period rc=%d\n", rc); + return rc; + } + + chip->ares_cfg.lra_qwd_drive_duration = -EINVAL; + chip->ares_cfg.calibrate_at_eop = -EINVAL; + if (chip->revid->pmic_subtype == PM660_SUBTYPE) { + rc = of_property_read_u32(node, + "qcom,lra-qwd-drive-duration", + &chip->ares_cfg.lra_qwd_drive_duration); + if (rc && rc != -EINVAL) { + pr_err("Unable to read LRA QWD drive duration rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, + "qcom,lra-calibrate-at-eop", + &chip->ares_cfg.calibrate_at_eop); + if (rc && rc != -EINVAL) { + pr_err("Unable to read Calibrate at EOP rc=%d\n", + rc); + return rc; + } + } + + chip->drive_period_code_max_var_pct = 25; + rc = of_property_read_u32(node, + "qcom,drive-period-code-max-variation-pct", &temp); + if (!rc) { + if (temp > 0 && temp < 100) + chip->drive_period_code_max_var_pct = (u8)temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read drive period code max var pct rc=%d\n", + rc); + return rc; + } + + chip->drive_period_code_min_var_pct = 25; + rc = of_property_read_u32(node, + "qcom,drive-period-code-min-variation-pct", &temp); + if (!rc) { + if (temp > 0 && temp < 100) + chip->drive_period_code_min_var_pct = (u8)temp; + } else if (rc != -EINVAL) { + pr_err("Unable to read drive period code min var pct rc=%d\n", + rc); + return rc; + } + + chip->auto_res_err_recovery_hw = + of_property_read_bool(node, + "qcom,auto-res-err-recovery-hw"); + + if (chip->revid->pmic_subtype != PM660_SUBTYPE) + chip->auto_res_err_recovery_hw = false; + } + + if (rc == -EINVAL) + rc = 0; + + if (chip->play_mode == HAP_BUFFER) + rc = qpnp_haptics_parse_buffer_dt(chip); + else if (chip->play_mode == HAP_PWM) + rc = qpnp_haptics_parse_pwm_dt(chip); + + return rc; +} + +static int qpnp_haptics_probe(struct platform_device *pdev) +{ + struct hap_chip *chip; + int rc, i; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + chip->pdev = pdev; + rc = qpnp_haptics_parse_dt(chip); + if (rc < 0) { + dev_err(&pdev->dev, "Error in parsing DT parameters, rc=%d\n", + rc); + return rc; + } + + spin_lock_init(&chip->bus_lock); + mutex_init(&chip->play_lock); + mutex_init(&chip->param_lock); + INIT_WORK(&chip->haptics_work, qpnp_haptics_work); + + rc = qpnp_haptics_config(chip); + if (rc < 0) { + dev_err(&pdev->dev, "Error in configuring haptics, rc=%d\n", + rc); + goto fail; + } + + hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->stop_timer.function = hap_stop_timer; + hrtimer_init(&chip->auto_res_err_poll_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + chip->auto_res_err_poll_timer.function = hap_auto_res_err_poll_timer; + dev_set_drvdata(&pdev->dev, chip); + + chip->cdev.name = "vibrator"; + chip->cdev.brightness_get = qpnp_haptics_brightness_get; + chip->cdev.brightness_set = qpnp_haptics_brightness_set; + chip->cdev.max_brightness = 100; + rc = devm_led_classdev_register(&pdev->dev, &chip->cdev); + if (rc < 0) { + dev_err(&pdev->dev, "Error in registering led class device, rc=%d\n", + rc); + goto register_fail; + } + + for (i = 0; i < ARRAY_SIZE(qpnp_haptics_attrs); i++) { + rc = sysfs_create_file(&chip->cdev.dev->kobj, + &qpnp_haptics_attrs[i].attr); + if (rc < 0) { + dev_err(&pdev->dev, "Error in creating sysfs file, rc=%d\n", + rc); + goto sysfs_fail; + } + } + + return 0; + +sysfs_fail: + for (--i; i >= 0; i--) + sysfs_remove_file(&chip->cdev.dev->kobj, + &qpnp_haptics_attrs[i].attr); +register_fail: + cancel_work_sync(&chip->haptics_work); + hrtimer_cancel(&chip->auto_res_err_poll_timer); + hrtimer_cancel(&chip->stop_timer); +fail: + mutex_destroy(&chip->play_lock); + mutex_destroy(&chip->param_lock); + if (chip->pwm_data.pwm_dev) + pwm_put(chip->pwm_data.pwm_dev); + dev_set_drvdata(&pdev->dev, NULL); + return rc; +} + +static int qpnp_haptics_remove(struct platform_device *pdev) +{ + struct hap_chip *chip = dev_get_drvdata(&pdev->dev); + + cancel_work_sync(&chip->haptics_work); + hrtimer_cancel(&chip->auto_res_err_poll_timer); + hrtimer_cancel(&chip->stop_timer); + mutex_destroy(&chip->play_lock); + mutex_destroy(&chip->param_lock); + if (chip->pwm_data.pwm_dev) + pwm_put(chip->pwm_data.pwm_dev); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct dev_pm_ops qpnp_haptics_pm_ops = { + .suspend = qpnp_haptics_suspend, +}; + +static const struct of_device_id hap_match_table[] = { + { .compatible = "qcom,qpnp-haptics" }, + { }, +}; + +static struct platform_driver qpnp_haptics_driver = { + .driver = { + .name = "qcom,qpnp-haptics", + .of_match_table = hap_match_table, + .pm = &qpnp_haptics_pm_ops, + }, + .probe = qpnp_haptics_probe, + .remove = qpnp_haptics_remove, +}; +module_platform_driver(qpnp_haptics_driver); + +MODULE_DESCRIPTION("QPNP haptics driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index f3deddf176bc..eb0b38f15204 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -134,6 +134,7 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = { { .compatible = "qcom,msm8998-apcs-hmss-global", .data = (void *)8 }, { .compatible = "qcom,sdm845-apss-shared", .data = (void *)12 }, { .compatible = "qcom,sm8150-apcs-hmss-global", .data = (void *) 12 }, + { .compatible = "qcom,sdm845-spcs-global", .data = (void *)0 }, { .compatible = "qcom,sm8150-spcs-global", .data = (void *)0 }, { .compatible = "qcom,kona-spcs-global", .data = (void *)0 }, { .compatible = "qcom,bengal-apcs-hmss-global", .data = (void *)8 }, diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig index 9de7fb2b9de1..97c36197a9c5 100644 --- a/drivers/media/platform/msm/Kconfig +++ b/drivers/media/platform/msm/Kconfig @@ -54,9 +54,19 @@ if MSMB_CAMERA source "drivers/media/platform/msm/camera_v2/Kconfig" endif # MSMB_CAMERA +menuconfig SPECTRA_CAMERA + bool "Qualcomm Technologies, Inc. Spectra camera and video capture support" + depends on ARCH_QCOM && VIDEO_V4L2 && I2C + ---help--- + Say Y here to enable selecting the video adapters for + Qualcomm Technologies, Inc. Spectra camera and video capture. + Enabling this adds support for the camera driver stack including sensor, + IFE and postprocessing drivers. + source "drivers/media/platform/msm/cvp/Kconfig" source "drivers/media/platform/msm/npu/Kconfig" source "drivers/media/platform/msm/synx/Kconfig" source "drivers/media/platform/msm/dvb/Kconfig" source "drivers/media/platform/msm/broadcast/Kconfig" source "drivers/media/platform/msm/sde/Kconfig" +source "drivers/media/platform/msm/vidc/Kconfig" diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile index 042de7015758..4d031f377e09 100644 --- a/drivers/media/platform/msm/Makefile +++ b/drivers/media/platform/msm/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx/ obj-$(CONFIG_TSPP) += broadcast/ obj-$(CONFIG_DVB_MPQ) += dvb/ obj-$(CONFIG_MSMB_CAMERA) += camera_v2/ +obj-$(CONFIG_MSM_VIDC_LEGACY_V4L2) += vidc/ +obj-$(CONFIG_SPECTRA_CAMERA) += camera_oneplus/ diff --git a/drivers/media/platform/msm/camera_oneplus/Makefile b/drivers/media/platform/msm/camera_oneplus/Makefile new file mode 100644 index 000000000000..9e0aee9f69e3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_utils/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_core/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sync/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_smmu/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cpas/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cdm/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_module/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_jpeg/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_cdm/Makefile new file mode 100644 index 000000000000..a8dfde110e42 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cdm_soc.o cam_cdm_util.o cam_cdm_intf.o \ + cam_cdm_core_common.o cam_cdm_virtual_core.o \ + cam_cdm_hw_core.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm.h new file mode 100644 index 000000000000..ee7b442561bb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm.h @@ -0,0 +1,260 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CDM_H_ +#define _CAM_CDM_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "cam_cdm_intf_api.h" +#include "cam_soc_util.h" +#include "cam_cpas_api.h" +#include "cam_hw_intf.h" +#include "cam_hw.h" +#include "cam_debug_util.h" + +#define CAM_MAX_SW_CDM_VERSION_SUPPORTED 1 +#define CAM_SW_CDM_INDEX 0 +#define CAM_CDM_INFLIGHT_WORKS 5 +#define CAM_CDM_HW_RESET_TIMEOUT 300 + +#define CAM_CDM_HW_ID_MASK 0xF +#define CAM_CDM_HW_ID_SHIFT 0x5 +#define CAM_CDM_CLIENTS_ID_MASK 0x1F + +#define CAM_CDM_GET_HW_IDX(x) (((x) >> CAM_CDM_HW_ID_SHIFT) & \ + CAM_CDM_HW_ID_MASK) +#define CAM_CDM_CREATE_CLIENT_HANDLE(hw_idx, client_idx) \ + ((((hw_idx) & CAM_CDM_HW_ID_MASK) << CAM_CDM_HW_ID_SHIFT) | \ + ((client_idx) & CAM_CDM_CLIENTS_ID_MASK)) +#define CAM_CDM_GET_CLIENT_IDX(x) ((x) & CAM_CDM_CLIENTS_ID_MASK) +#define CAM_PER_CDM_MAX_REGISTERED_CLIENTS (CAM_CDM_CLIENTS_ID_MASK + 1) +#define CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM (CAM_CDM_HW_ID_MASK + 1) + +/* enum cam_cdm_reg_attr - read, write, read and write permissions.*/ +enum cam_cdm_reg_attr { + CAM_REG_ATTR_READ, + CAM_REG_ATTR_WRITE, + CAM_REG_ATTR_READ_WRITE, +}; + +/* enum cam_cdm_hw_process_intf_cmd - interface commands.*/ +enum cam_cdm_hw_process_intf_cmd { + CAM_CDM_HW_INTF_CMD_ACQUIRE, + CAM_CDM_HW_INTF_CMD_RELEASE, + CAM_CDM_HW_INTF_CMD_SUBMIT_BL, + CAM_CDM_HW_INTF_CMD_RESET_HW, + CAM_CDM_HW_INTF_CMD_INVALID, +}; + +/* enum cam_cdm_regs - CDM driver offset enums.*/ +enum cam_cdm_regs { + /*cfg_offsets 0*/ + CDM_CFG_HW_VERSION, + CDM_CFG_TITAN_VERSION, + CDM_CFG_RST_CMD, + CDM_CFG_CGC_CFG, + CDM_CFG_CORE_CFG, + CDM_CFG_CORE_EN, + CDM_CFG_FE_CFG, + /*irq_offsets 7*/ + CDM_IRQ_MASK, + CDM_IRQ_CLEAR, + CDM_IRQ_CLEAR_CMD, + CDM_IRQ_SET, + CDM_IRQ_SET_CMD, + CDM_IRQ_STATUS, + CDM_IRQ_USR_DATA, + /*BL FIFO Registers 14*/ + CDM_BL_FIFO_BASE_REG, + CDM_BL_FIFO_LEN_REG, + CDM_BL_FIFO_STORE_REG, + CDM_BL_FIFO_CFG, + CDM_BL_FIFO_RB, + CDM_BL_FIFO_BASE_RB, + CDM_BL_FIFO_LEN_RB, + CDM_BL_FIFO_PENDING_REQ_RB, + /*CDM System Debug Registers 22*/ + CDM_DBG_WAIT_STATUS, + CDM_DBG_SCRATCH_0_REG, + CDM_DBG_SCRATCH_1_REG, + CDM_DBG_SCRATCH_2_REG, + CDM_DBG_SCRATCH_3_REG, + CDM_DBG_SCRATCH_4_REG, + CDM_DBG_SCRATCH_5_REG, + CDM_DBG_SCRATCH_6_REG, + CDM_DBG_SCRATCH_7_REG, + CDM_DBG_LAST_AHB_ADDR, + CDM_DBG_LAST_AHB_DATA, + CDM_DBG_CORE_DBUG, + CDM_DBG_LAST_AHB_ERR_ADDR, + CDM_DBG_LAST_AHB_ERR_DATA, + CDM_DBG_CURRENT_BL_BASE, + CDM_DBG_CURRENT_BL_LEN, + CDM_DBG_CURRENT_USED_AHB_BASE, + CDM_DBG_DEBUG_STATUS, + /*FE Bus Miser Registers 40*/ + CDM_BUS_MISR_CFG_0, + CDM_BUS_MISR_CFG_1, + CDM_BUS_MISR_RD_VAL, + /*Performance Counter registers 43*/ + CDM_PERF_MON_CTRL, + CDM_PERF_MON_0, + CDM_PERF_MON_1, + CDM_PERF_MON_2, + /*Spare registers 47*/ + CDM_SPARE, +}; + +/* struct cam_cdm_reg_offset - struct for offset with attribute.*/ +struct cam_cdm_reg_offset { + uint32_t offset; + enum cam_cdm_reg_attr attribute; +}; + +/* struct cam_cdm_reg_offset_table - struct for whole offset table.*/ +struct cam_cdm_reg_offset_table { + uint32_t first_offset; + uint32_t last_offset; + uint32_t reg_count; + const struct cam_cdm_reg_offset *offsets; + uint32_t offset_max_size; +}; + +/* enum cam_cdm_flags - Bit fields for CDM flags used */ +enum cam_cdm_flags { + CAM_CDM_FLAG_SHARED_CDM, + CAM_CDM_FLAG_PRIVATE_CDM, +}; + +/* enum cam_cdm_type - Enum for possible CAM CDM types */ +enum cam_cdm_type { + CAM_VIRTUAL_CDM, + CAM_HW_CDM, +}; + +/* enum cam_cdm_mem_base_index - Enum for possible CAM CDM types */ +enum cam_cdm_mem_base_index { + CAM_HW_CDM_BASE_INDEX, + CAM_HW_CDM_MAX_INDEX = CAM_SOC_MAX_BLOCK, +}; + +/* struct cam_cdm_client - struct for cdm clients data.*/ +struct cam_cdm_client { + struct cam_cdm_acquire_data data; + void __iomem *changebase_addr; + uint32_t stream_on; + uint32_t refcount; + struct mutex lock; + uint32_t handle; +}; + +/* struct cam_cdm_work_payload - struct for cdm work payload data.*/ +struct cam_cdm_work_payload { + struct cam_hw_info *hw; + uint32_t irq_status; + uint32_t irq_data; + struct work_struct work; +}; + +/* enum cam_cdm_bl_cb_type - Enum for possible CAM CDM cb request types */ +enum cam_cdm_bl_cb_type { + CAM_HW_CDM_BL_CB_CLIENT = 1, + CAM_HW_CDM_BL_CB_INTERNAL, +}; + +/* struct cam_cdm_bl_cb_request_entry - callback entry for work to process.*/ +struct cam_cdm_bl_cb_request_entry { + uint8_t bl_tag; + enum cam_cdm_bl_cb_type request_type; + uint32_t client_hdl; + void *userdata; + uint32_t cookie; + struct list_head entry; +}; + +/* struct cam_cdm_hw_intf_cmd_submit_bl - cdm interface submit command.*/ +struct cam_cdm_hw_intf_cmd_submit_bl { + uint32_t handle; + struct cam_cdm_bl_request *data; +}; + +/* struct cam_cdm_hw_mem - CDM hw memory struct */ +struct cam_cdm_hw_mem { + int32_t handle; + uint32_t vaddr; + uintptr_t kmdvaddr; + size_t size; +}; + +/* struct cam_cdm - CDM hw device struct */ +struct cam_cdm { + uint32_t index; + char name[128]; + enum cam_cdm_id id; + enum cam_cdm_flags flags; + struct completion reset_complete; + struct completion bl_complete; + struct workqueue_struct *work_queue; + struct list_head bl_request_list; + struct cam_hw_version version; + uint32_t hw_version; + uint32_t hw_family_version; + struct cam_iommu_handle iommu_hdl; + struct cam_cdm_reg_offset_table *offset_tbl; + struct cam_cdm_utils_ops *ops; + struct cam_cdm_client *clients[CAM_PER_CDM_MAX_REGISTERED_CLIENTS]; + uint8_t bl_tag; + atomic_t error; + atomic_t bl_done; + struct cam_cdm_hw_mem gen_irq; + uint32_t cpas_handle; +}; + +/* struct cam_cdm_private_dt_data - CDM hw custom dt data */ +struct cam_cdm_private_dt_data { + bool dt_cdm_shared; + uint32_t dt_num_supported_clients; + const char *dt_cdm_client_name[CAM_PER_CDM_MAX_REGISTERED_CLIENTS]; +}; + +/* struct cam_cdm_intf_devices - CDM mgr interface devices */ +struct cam_cdm_intf_devices { + struct mutex lock; + uint32_t refcount; + struct cam_hw_intf *device; + struct cam_cdm_private_dt_data *data; +}; + +/* struct cam_cdm_intf_mgr - CDM mgr interface device struct */ +struct cam_cdm_intf_mgr { + bool probe_done; + struct cam_cdm_intf_devices nodes[CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM]; + uint32_t cdm_count; + uint32_t dt_supported_hw_cdm; + int32_t refcount; +}; + +int cam_cdm_intf_register_hw_cdm(struct cam_hw_intf *hw, + struct cam_cdm_private_dt_data *data, enum cam_cdm_type type, + uint32_t *index); +int cam_cdm_intf_deregister_hw_cdm(struct cam_hw_intf *hw, + struct cam_cdm_private_dt_data *data, enum cam_cdm_type type, + uint32_t index); + +#endif /* _CAM_CDM_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.c new file mode 100644 index 000000000000..c57853cb04e7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.c @@ -0,0 +1,567 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_soc_util.h" +#include "cam_smmu_api.h" +#include "cam_io_util.h" +#include "cam_cdm_intf_api.h" +#include "cam_cdm.h" +#include "cam_cdm_soc.h" +#include "cam_cdm_core_common.h" + +static void cam_cdm_get_client_refcount(struct cam_cdm_client *client) +{ + mutex_lock(&client->lock); + CAM_DBG(CAM_CDM, "CDM client get refcount=%d", + client->refcount); + client->refcount++; + mutex_unlock(&client->lock); +} + +static void cam_cdm_put_client_refcount(struct cam_cdm_client *client) +{ + mutex_lock(&client->lock); + CAM_DBG(CAM_CDM, "CDM client put refcount=%d", + client->refcount); + if (client->refcount > 0) { + client->refcount--; + } else { + CAM_ERR(CAM_CDM, "Refcount put when zero"); + WARN_ON(1); + } + mutex_unlock(&client->lock); +} + +bool cam_cdm_set_cam_hw_version( + uint32_t ver, struct cam_hw_version *cam_version) +{ + switch (ver) { + case CAM_CDM170_VERSION: + cam_version->major = (ver & 0xF0000000); + cam_version->minor = (ver & 0xFFF0000); + cam_version->incr = (ver & 0xFFFF); + cam_version->reserved = 0; + return true; + default: + CAM_ERR(CAM_CDM, "CDM Version=%x not supported in util", ver); + break; + } + return false; +} + +bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + if (!irq_data) + return false; + + CAM_DBG(CAM_CDM, "CPAS error callback type=%d", irq_data->irq_type); + + return false; +} + +struct cam_cdm_utils_ops *cam_cdm_get_ops( + uint32_t ver, struct cam_hw_version *cam_version, bool by_cam_version) +{ + if (by_cam_version == false) { + switch (ver) { + case CAM_CDM170_VERSION: + return &CDM170_ops; + default: + CAM_ERR(CAM_CDM, "CDM Version=%x not supported in util", + ver); + } + } else if (cam_version) { + if ((cam_version->major == 1) && (cam_version->minor == 0) && + (cam_version->incr == 0)) + return &CDM170_ops; + CAM_ERR(CAM_CDM, "cam_hw_version=%x:%x:%x not supported", + cam_version->major, cam_version->minor, + cam_version->incr); + } + + return NULL; +} + +struct cam_cdm_bl_cb_request_entry *cam_cdm_find_request_by_bl_tag( + uint32_t tag, struct list_head *bl_list) +{ + struct cam_cdm_bl_cb_request_entry *node; + + list_for_each_entry(node, bl_list, entry) { + if (node->bl_tag == tag) + return node; + } + CAM_ERR(CAM_CDM, "Could not find the bl request for tag=%x", tag); + + return NULL; +} + +int cam_cdm_get_caps(void *hw_priv, + void *get_hw_cap_args, uint32_t arg_size) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_cdm *cdm_core; + + if ((cdm_hw) && (cdm_hw->core_info) && (get_hw_cap_args) && + (sizeof(struct cam_iommu_handle) == arg_size)) { + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + *((struct cam_iommu_handle *)get_hw_cap_args) = + cdm_core->iommu_hdl; + return 0; + } + + return -EINVAL; +} + +int cam_cdm_find_free_client_slot(struct cam_cdm *hw) +{ + int i; + + for (i = 0; i < CAM_PER_CDM_MAX_REGISTERED_CLIENTS; i++) { + if (hw->clients[i] == NULL) { + CAM_DBG(CAM_CDM, "Found client slot %d", i); + return i; + } + } + CAM_ERR(CAM_CDM, "No more client slots"); + + return -EBUSY; +} + + +void cam_cdm_notify_clients(struct cam_hw_info *cdm_hw, + enum cam_cdm_cb_status status, void *data) +{ + int i; + struct cam_cdm *core = NULL; + struct cam_cdm_client *client = NULL; + + if (!cdm_hw) { + CAM_ERR(CAM_CDM, "CDM Notify called with NULL hw info"); + return; + } + core = (struct cam_cdm *)cdm_hw->core_info; + + if (status == CAM_CDM_CB_STATUS_BL_SUCCESS) { + int client_idx; + struct cam_cdm_bl_cb_request_entry *node = + (struct cam_cdm_bl_cb_request_entry *)data; + + client_idx = CAM_CDM_GET_CLIENT_IDX(node->client_hdl); + client = core->clients[client_idx]; + if ((!client) || (client->handle != node->client_hdl)) { + CAM_ERR(CAM_CDM, "Invalid client %pK hdl=%x", client, + node->client_hdl); + return; + } + cam_cdm_get_client_refcount(client); + if (client->data.cam_cdm_callback) { + CAM_DBG(CAM_CDM, "Calling client=%s cb cookie=%d", + client->data.identifier, node->cookie); + client->data.cam_cdm_callback(node->client_hdl, + node->userdata, CAM_CDM_CB_STATUS_BL_SUCCESS, + node->cookie); + CAM_DBG(CAM_CDM, "Exit client cb cookie=%d", + node->cookie); + } else { + CAM_ERR(CAM_CDM, "No cb registered for client hdl=%x", + node->client_hdl); + } + cam_cdm_put_client_refcount(client); + return; + } + + for (i = 0; i < CAM_PER_CDM_MAX_REGISTERED_CLIENTS; i++) { + if (core->clients[i] != NULL) { + client = core->clients[i]; + mutex_lock(&client->lock); + CAM_DBG(CAM_CDM, "Found client slot %d", i); + if (client->data.cam_cdm_callback) { + if (status == CAM_CDM_CB_STATUS_PAGEFAULT) { + unsigned long iova = + (unsigned long)data; + + client->data.cam_cdm_callback( + client->handle, + client->data.userdata, + CAM_CDM_CB_STATUS_PAGEFAULT, + (iova & 0xFFFFFFFF)); + } + } else { + CAM_ERR(CAM_CDM, + "No cb registered for client hdl=%x", + client->handle); + } + mutex_unlock(&client->lock); + } + } +} + +int cam_cdm_stream_ops_internal(void *hw_priv, + void *start_args, bool operation) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_cdm *core = NULL; + int rc = -EPERM; + int client_idx; + struct cam_cdm_client *client; + uint32_t *handle = start_args; + + if (!hw_priv) + return -EINVAL; + + core = (struct cam_cdm *)cdm_hw->core_info; + client_idx = CAM_CDM_GET_CLIENT_IDX(*handle); + client = core->clients[client_idx]; + if (!client) { + CAM_ERR(CAM_CDM, "Invalid client %pK hdl=%x", client, *handle); + return -EINVAL; + } + cam_cdm_get_client_refcount(client); + if (*handle != client->handle) { + CAM_ERR(CAM_CDM, "client id given handle=%x invalid", *handle); + cam_cdm_put_client_refcount(client); + return -EINVAL; + } + if (operation == true) { + if (true == client->stream_on) { + CAM_ERR(CAM_CDM, + "Invalid CDM client is already streamed ON"); + cam_cdm_put_client_refcount(client); + return rc; + } + } else { + if (client->stream_on == false) { + CAM_ERR(CAM_CDM, + "Invalid CDM client is already streamed Off"); + cam_cdm_put_client_refcount(client); + return rc; + } + } + + mutex_lock(&cdm_hw->hw_mutex); + if (operation == true) { + if (!cdm_hw->open_count) { + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(core->cpas_handle, + &ahb_vote, &axi_vote); + if (rc != 0) { + CAM_ERR(CAM_CDM, "CPAS start failed"); + goto end; + } + CAM_DBG(CAM_CDM, "CDM init first time"); + if (core->id == CAM_CDM_VIRTUAL) { + CAM_DBG(CAM_CDM, + "Virtual CDM HW init first time"); + rc = 0; + } else { + CAM_DBG(CAM_CDM, "CDM HW init first time"); + rc = cam_hw_cdm_init(hw_priv, NULL, 0); + if (rc == 0) { + rc = cam_hw_cdm_alloc_genirq_mem( + hw_priv); + if (rc != 0) { + CAM_ERR(CAM_CDM, + "Genirqalloc failed"); + cam_hw_cdm_deinit(hw_priv, + NULL, 0); + } + } else { + CAM_ERR(CAM_CDM, "CDM HW init failed"); + } + } + if (rc == 0) { + cdm_hw->open_count++; + client->stream_on = true; + } else { + if (cam_cpas_stop(core->cpas_handle)) + CAM_ERR(CAM_CDM, "CPAS stop failed"); + } + } else { + cdm_hw->open_count++; + CAM_DBG(CAM_CDM, "CDM HW already ON count=%d", + cdm_hw->open_count); + rc = 0; + client->stream_on = true; + } + } else { + if (cdm_hw->open_count) { + cdm_hw->open_count--; + CAM_DBG(CAM_CDM, "stream OFF CDM %d", + cdm_hw->open_count); + if (!cdm_hw->open_count) { + CAM_DBG(CAM_CDM, "CDM Deinit now"); + if (core->id == CAM_CDM_VIRTUAL) { + CAM_DBG(CAM_CDM, + "Virtual CDM HW Deinit"); + rc = 0; + } else { + CAM_DBG(CAM_CDM, "CDM HW Deinit now"); + rc = cam_hw_cdm_deinit( + hw_priv, NULL, 0); + if (cam_hw_cdm_release_genirq_mem( + hw_priv)) + CAM_ERR(CAM_CDM, + "Genirq release fail"); + } + if (rc) { + CAM_ERR(CAM_CDM, + "Deinit failed in streamoff"); + } else { + client->stream_on = false; + rc = cam_cpas_stop(core->cpas_handle); + if (rc) + CAM_ERR(CAM_CDM, + "CPAS stop failed"); + } + } else { + client->stream_on = false; + rc = 0; + CAM_DBG(CAM_CDM, + "Client stream off success =%d", + cdm_hw->open_count); + } + } else { + CAM_DBG(CAM_CDM, "stream OFF CDM Invalid %d", + cdm_hw->open_count); + rc = -ENXIO; + } + } +end: + cam_cdm_put_client_refcount(client); + mutex_unlock(&cdm_hw->hw_mutex); + return rc; +} + +int cam_cdm_stream_start(void *hw_priv, + void *start_args, uint32_t size) +{ + int rc = 0; + + if (!hw_priv) + return -EINVAL; + + rc = cam_cdm_stream_ops_internal(hw_priv, start_args, true); + return rc; + +} + +int cam_cdm_stream_stop(void *hw_priv, + void *start_args, uint32_t size) +{ + int rc = 0; + + if (!hw_priv) + return -EINVAL; + + rc = cam_cdm_stream_ops_internal(hw_priv, start_args, false); + return rc; + +} + +int cam_cdm_process_cmd(void *hw_priv, + uint32_t cmd, void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_hw_soc_info *soc_data = NULL; + struct cam_cdm *core = NULL; + int rc = -EINVAL; + + if ((!hw_priv) || (!cmd_args) || + (cmd >= CAM_CDM_HW_INTF_CMD_INVALID)) + return rc; + + soc_data = &cdm_hw->soc_info; + core = (struct cam_cdm *)cdm_hw->core_info; + switch (cmd) { + case CAM_CDM_HW_INTF_CMD_SUBMIT_BL: { + struct cam_cdm_hw_intf_cmd_submit_bl *req; + int idx; + struct cam_cdm_client *client; + + if (sizeof(struct cam_cdm_hw_intf_cmd_submit_bl) != arg_size) { + CAM_ERR(CAM_CDM, "Invalid CDM cmd %d arg size=%x", cmd, + arg_size); + break; + } + req = (struct cam_cdm_hw_intf_cmd_submit_bl *)cmd_args; + if ((req->data->type < 0) || + (req->data->type > CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA)) { + CAM_ERR(CAM_CDM, "Invalid req bl cmd addr type=%d", + req->data->type); + break; + } + idx = CAM_CDM_GET_CLIENT_IDX(req->handle); + client = core->clients[idx]; + if ((!client) || (req->handle != client->handle)) { + CAM_ERR(CAM_CDM, "Invalid client %pK hdl=%x", client, + req->handle); + break; + } + cam_cdm_get_client_refcount(client); + if ((req->data->flag == true) && + (!client->data.cam_cdm_callback)) { + CAM_ERR(CAM_CDM, + "CDM request cb without registering cb"); + cam_cdm_put_client_refcount(client); + break; + } + if (client->stream_on != true) { + CAM_ERR(CAM_CDM, + "Invalid CDM needs to be streamed ON first"); + cam_cdm_put_client_refcount(client); + break; + } + if (core->id == CAM_CDM_VIRTUAL) + rc = cam_virtual_cdm_submit_bl(cdm_hw, req, client); + else + rc = cam_hw_cdm_submit_bl(cdm_hw, req, client); + + cam_cdm_put_client_refcount(client); + break; + } + case CAM_CDM_HW_INTF_CMD_ACQUIRE: { + struct cam_cdm_acquire_data *data; + int idx; + struct cam_cdm_client *client; + + if (sizeof(struct cam_cdm_acquire_data) != arg_size) { + CAM_ERR(CAM_CDM, "Invalid CDM cmd %d arg size=%x", cmd, + arg_size); + break; + } + + mutex_lock(&cdm_hw->hw_mutex); + data = (struct cam_cdm_acquire_data *)cmd_args; + CAM_DBG(CAM_CDM, "Trying to acquire client=%s in hw idx=%d", + data->identifier, core->index); + idx = cam_cdm_find_free_client_slot(core); + if ((idx < 0) || (core->clients[idx])) { + mutex_unlock(&cdm_hw->hw_mutex); + CAM_ERR(CAM_CDM, + "Fail to client slots, client=%s in hw idx=%d", + data->identifier, core->index); + break; + } + core->clients[idx] = kzalloc(sizeof(struct cam_cdm_client), + GFP_KERNEL); + if (!core->clients[idx]) { + mutex_unlock(&cdm_hw->hw_mutex); + rc = -ENOMEM; + break; + } + + mutex_unlock(&cdm_hw->hw_mutex); + client = core->clients[idx]; + mutex_init(&client->lock); + data->ops = core->ops; + if (core->id == CAM_CDM_VIRTUAL) { + data->cdm_version.major = 1; + data->cdm_version.minor = 0; + data->cdm_version.incr = 0; + data->cdm_version.reserved = 0; + data->ops = cam_cdm_get_ops(0, + &data->cdm_version, true); + if (!data->ops) { + mutex_destroy(&client->lock); + mutex_lock(&cdm_hw->hw_mutex); + kfree(core->clients[idx]); + core->clients[idx] = NULL; + mutex_unlock( + &cdm_hw->hw_mutex); + rc = -EPERM; + CAM_ERR(CAM_CDM, "Invalid ops for virtual cdm"); + break; + } + } else { + data->cdm_version = core->version; + } + + cam_cdm_get_client_refcount(client); + mutex_lock(&client->lock); + memcpy(&client->data, data, + sizeof(struct cam_cdm_acquire_data)); + client->handle = CAM_CDM_CREATE_CLIENT_HANDLE( + core->index, + idx); + client->stream_on = false; + data->handle = client->handle; + CAM_DBG(CAM_CDM, "Acquired client=%s in hwidx=%d", + data->identifier, core->index); + mutex_unlock(&client->lock); + rc = 0; + break; + } + case CAM_CDM_HW_INTF_CMD_RELEASE: { + uint32_t *handle = cmd_args; + int idx; + struct cam_cdm_client *client; + + if (sizeof(uint32_t) != arg_size) { + CAM_ERR(CAM_CDM, + "Invalid CDM cmd %d size=%x for handle=%x", + cmd, arg_size, *handle); + return -EINVAL; + } + idx = CAM_CDM_GET_CLIENT_IDX(*handle); + mutex_lock(&cdm_hw->hw_mutex); + client = core->clients[idx]; + if ((!client) || (*handle != client->handle)) { + CAM_ERR(CAM_CDM, "Invalid client %pK hdl=%x", + client, *handle); + mutex_unlock(&cdm_hw->hw_mutex); + break; + } + cam_cdm_put_client_refcount(client); + mutex_lock(&client->lock); + if (client->refcount != 0) { + CAM_ERR(CAM_CDM, "CDM Client refcount not zero %d", + client->refcount); + rc = -EPERM; + mutex_unlock(&client->lock); + mutex_unlock(&cdm_hw->hw_mutex); + break; + } + core->clients[idx] = NULL; + mutex_unlock(&client->lock); + mutex_destroy(&client->lock); + kfree(client); + mutex_unlock(&cdm_hw->hw_mutex); + rc = 0; + break; + } + case CAM_CDM_HW_INTF_CMD_RESET_HW: { + CAM_ERR(CAM_CDM, "CDM HW reset not supported for handle =%x", + *((uint32_t *)cmd_args)); + break; + } + default: + CAM_ERR(CAM_CDM, "CDM HW intf command not valid =%d", cmd); + break; + } + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.h new file mode 100644 index 000000000000..497832b4eeff --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_core_common.h @@ -0,0 +1,50 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CDM_CORE_COMMON_H_ +#define _CAM_CDM_CORE_COMMON_H_ + +#include "cam_mem_mgr.h" + +#define CAM_CDM170_VERSION 0x10000000 + +extern struct cam_cdm_utils_ops CDM170_ops; + +int cam_hw_cdm_init(void *hw_priv, void *init_hw_args, uint32_t arg_size); +int cam_hw_cdm_deinit(void *hw_priv, void *init_hw_args, uint32_t arg_size); +int cam_hw_cdm_alloc_genirq_mem(void *hw_priv); +int cam_hw_cdm_release_genirq_mem(void *hw_priv); +int cam_cdm_get_caps(void *hw_priv, void *get_hw_cap_args, uint32_t arg_size); +int cam_cdm_stream_ops_internal(void *hw_priv, void *start_args, + bool operation); +int cam_cdm_stream_start(void *hw_priv, void *start_args, uint32_t size); +int cam_cdm_stream_stop(void *hw_priv, void *start_args, uint32_t size); +int cam_cdm_process_cmd(void *hw_priv, uint32_t cmd, void *cmd_args, + uint32_t arg_size); +bool cam_cdm_set_cam_hw_version( + uint32_t ver, struct cam_hw_version *cam_version); +bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data); +struct cam_cdm_utils_ops *cam_cdm_get_ops( + uint32_t ver, struct cam_hw_version *cam_version, bool by_cam_version); +int cam_virtual_cdm_submit_bl(struct cam_hw_info *cdm_hw, + struct cam_cdm_hw_intf_cmd_submit_bl *req, + struct cam_cdm_client *client); +int cam_hw_cdm_submit_bl(struct cam_hw_info *cdm_hw, + struct cam_cdm_hw_intf_cmd_submit_bl *req, + struct cam_cdm_client *client); +struct cam_cdm_bl_cb_request_entry *cam_cdm_find_request_by_bl_tag( + uint32_t tag, struct list_head *bl_list); +void cam_cdm_notify_clients(struct cam_hw_info *cdm_hw, + enum cam_cdm_cb_status status, void *data); + +#endif /* _CAM_CDM_CORE_COMMON_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_hw_core.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_hw_core.c new file mode 100644 index 000000000000..61ade5ce7c62 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_hw_core.c @@ -0,0 +1,1149 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include +#include "cam_soc_util.h" +#include "cam_smmu_api.h" +#include "cam_cdm_intf_api.h" +#include "cam_cdm.h" +#include "cam_cdm_core_common.h" +#include "cam_cdm_soc.h" +#include "cam_io_util.h" +#include "cam_hw_cdm170_reg.h" + +#define CAM_HW_CDM_CPAS_0_NAME "qcom,cam170-cpas-cdm0" +#define CAM_HW_CDM_IPE_0_NAME "qcom,cam170-ipe0-cdm" +#define CAM_HW_CDM_IPE_1_NAME "qcom,cam170-ipe1-cdm" +#define CAM_HW_CDM_BPS_NAME "qcom,cam170-bps-cdm" + +#define CAM_CDM_BL_FIFO_WAIT_TIMEOUT 2000 + +static void cam_hw_cdm_work(struct work_struct *work); + +/* DT match table entry for all CDM variants*/ +static const struct of_device_id msm_cam_hw_cdm_dt_match[] = { + { + .compatible = CAM_HW_CDM_CPAS_0_NAME, + .data = &cam170_cpas_cdm_offset_table, + }, + {} +}; + +static enum cam_cdm_id cam_hw_cdm_get_id_by_name(char *name) +{ + if (!strcmp(CAM_HW_CDM_CPAS_0_NAME, name)) + return CAM_CDM_CPAS_0; + + return CAM_CDM_MAX; +} + +int cam_hw_cdm_bl_fifo_pending_bl_rb(struct cam_hw_info *cdm_hw, + uint32_t *pending_bl) +{ + int rc = 0; + + if (cam_cdm_read_hw_reg(cdm_hw, CDM_BL_FIFO_PENDING_REQ_RB, + pending_bl)) { + CAM_ERR(CAM_CDM, "Failed to read CDM pending BL's"); + rc = -EIO; + } + + return rc; +} + +static int cam_hw_cdm_enable_bl_done_irq(struct cam_hw_info *cdm_hw, + bool enable) +{ + int rc = -EIO; + uint32_t irq_mask = 0; + struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info; + + if (cam_cdm_read_hw_reg(cdm_hw, CDM_IRQ_MASK, + &irq_mask)) { + CAM_ERR(CAM_CDM, "Failed to read CDM IRQ mask"); + return rc; + } + + if (enable == true) { + if (cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_MASK, + (irq_mask | 0x4))) { + CAM_ERR(CAM_CDM, "Write failed to enable BL done irq"); + } else { + atomic_inc(&core->bl_done); + rc = 0; + CAM_DBG(CAM_CDM, "BL done irq enabled =%d", + atomic_read(&core->bl_done)); + } + } else { + if (cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_MASK, + (irq_mask & 0x70003))) { + CAM_ERR(CAM_CDM, "Write failed to disable BL done irq"); + } else { + atomic_dec(&core->bl_done); + rc = 0; + CAM_DBG(CAM_CDM, "BL done irq disable =%d", + atomic_read(&core->bl_done)); + } + } + return rc; +} + +static int cam_hw_cdm_enable_core(struct cam_hw_info *cdm_hw, bool enable) +{ + int rc = 0; + + if (enable == true) { + if (cam_cdm_write_hw_reg(cdm_hw, CDM_CFG_CORE_EN, 0x01)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW core enable"); + rc = -EIO; + } + } else { + if (cam_cdm_write_hw_reg(cdm_hw, CDM_CFG_CORE_EN, 0x02)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW core disable"); + rc = -EIO; + } + } + return rc; +} + +int cam_hw_cdm_enable_core_dbg(struct cam_hw_info *cdm_hw) +{ + int rc = 0; + + if (cam_cdm_write_hw_reg(cdm_hw, CDM_DBG_CORE_DBUG, 0x10100)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW core debug"); + rc = -EIO; + } + + return rc; +} + +int cam_hw_cdm_disable_core_dbg(struct cam_hw_info *cdm_hw) +{ + int rc = 0; + + if (cam_cdm_write_hw_reg(cdm_hw, CDM_DBG_CORE_DBUG, 0)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW core debug"); + rc = -EIO; + } + + return rc; +} + +void cam_hw_cdm_dump_scratch_registors(struct cam_hw_info *cdm_hw) +{ + uint32_t dump_reg = 0; + + cam_cdm_read_hw_reg(cdm_hw, CDM_CFG_CORE_EN, &dump_reg); + CAM_ERR(CAM_CDM, "dump core en=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_0_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch0=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_1_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch1=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_2_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch2=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_3_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch3=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_4_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch4=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_5_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch5=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_6_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch6=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_SCRATCH_7_REG, &dump_reg); + CAM_ERR(CAM_CDM, "dump scratch7=%x", dump_reg); + +} + +void cam_hw_cdm_dump_core_debug_registers( + struct cam_hw_info *cdm_hw) +{ + uint32_t dump_reg, core_dbg, loop_cnt; + + mutex_lock(&cdm_hw->hw_mutex); + cam_cdm_read_hw_reg(cdm_hw, CDM_CFG_CORE_EN, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW core status=%x", dump_reg); + /* First pause CDM, If it fails still proceed to dump debug info */ + cam_hw_cdm_enable_core(cdm_hw, false); + cam_hw_cdm_bl_fifo_pending_bl_rb(cdm_hw, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW current pending BL=%x", dump_reg); + loop_cnt = dump_reg; + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_DEBUG_STATUS, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW Debug status reg=%x", dump_reg); + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_CORE_DBUG, &core_dbg); + if (core_dbg & 0x100) { + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_LAST_AHB_ADDR, &dump_reg); + CAM_ERR(CAM_CDM, "AHB dump reglastaddr=%x", dump_reg); + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_LAST_AHB_DATA, &dump_reg); + CAM_ERR(CAM_CDM, "AHB dump reglastdata=%x", dump_reg); + } else { + CAM_ERR(CAM_CDM, "CDM HW AHB dump not enable"); + } + + if (core_dbg & 0x10000) { + int i; + + CAM_ERR(CAM_CDM, "CDM HW BL FIFO dump with loop count=%d", + loop_cnt); + for (i = 0 ; i < loop_cnt ; i++) { + cam_cdm_write_hw_reg(cdm_hw, CDM_BL_FIFO_RB, i); + cam_cdm_read_hw_reg(cdm_hw, CDM_BL_FIFO_BASE_RB, + &dump_reg); + CAM_ERR(CAM_CDM, "BL(%d) base addr =%x", i, dump_reg); + cam_cdm_read_hw_reg(cdm_hw, CDM_BL_FIFO_LEN_RB, + &dump_reg); + CAM_ERR(CAM_CDM, "BL(%d) len=%d tag=%d", i, + (dump_reg & 0xFFFFF), (dump_reg & 0xFF000000)); + } + } else { + CAM_ERR(CAM_CDM, "CDM HW BL FIFO readback not enable"); + } + + CAM_ERR(CAM_CDM, "CDM HW default dump"); + cam_cdm_read_hw_reg(cdm_hw, CDM_CFG_CORE_CFG, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW core cfg=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_IRQ_STATUS, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW irq status=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_IRQ_SET, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW irq set reg=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_CURRENT_BL_BASE, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW current BL base=%x", dump_reg); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_CURRENT_BL_LEN, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW current BL len=%d tag=%d", + (dump_reg & 0xFFFFF), (dump_reg & 0xFF000000)); + + cam_cdm_read_hw_reg(cdm_hw, CDM_DBG_CURRENT_USED_AHB_BASE, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW current AHB base=%x", dump_reg); + + cam_hw_cdm_bl_fifo_pending_bl_rb(cdm_hw, &dump_reg); + CAM_ERR(CAM_CDM, "CDM HW current pending BL=%x", dump_reg); + + /* Enable CDM back */ + cam_hw_cdm_enable_core(cdm_hw, true); + mutex_unlock(&cdm_hw->hw_mutex); + +} + +int cam_hw_cdm_wait_for_bl_fifo(struct cam_hw_info *cdm_hw, + uint32_t bl_count) +{ + uint32_t pending_bl = 0; + int32_t available_bl_slots = 0; + int rc = -EIO; + long time_left; + struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info; + + do { + if (cam_cdm_read_hw_reg(cdm_hw, CDM_BL_FIFO_PENDING_REQ_RB, + &pending_bl)) { + CAM_ERR(CAM_CDM, "Failed to read CDM pending BL's"); + rc = -EIO; + break; + } + available_bl_slots = CAM_CDM_HWFIFO_SIZE - pending_bl; + if (available_bl_slots < 0) { + CAM_ERR(CAM_CDM, "Invalid available slots %d:%d:%d", + available_bl_slots, CAM_CDM_HWFIFO_SIZE, + pending_bl); + break; + } + if (bl_count < (available_bl_slots - 1)) { + CAM_DBG(CAM_CDM, + "BL slot available_cnt=%d requested=%d", + (available_bl_slots - 1), bl_count); + rc = bl_count; + break; + } else if (0 == (available_bl_slots - 1)) { + rc = cam_hw_cdm_enable_bl_done_irq(cdm_hw, true); + if (rc) { + CAM_ERR(CAM_CDM, "Enable BL done irq failed"); + break; + } + time_left = wait_for_completion_timeout( + &core->bl_complete, msecs_to_jiffies( + CAM_CDM_BL_FIFO_WAIT_TIMEOUT)); + if (time_left <= 0) { + CAM_ERR(CAM_CDM, + "CDM HW BL Wait timed out failed"); + if (cam_hw_cdm_enable_bl_done_irq(cdm_hw, + false)) + CAM_ERR(CAM_CDM, + "Disable BL done irq failed"); + rc = -EIO; + break; + } + if (cam_hw_cdm_enable_bl_done_irq(cdm_hw, false)) + CAM_ERR(CAM_CDM, "Disable BL done irq failed"); + rc = 0; + CAM_DBG(CAM_CDM, "CDM HW is ready for data"); + } else { + rc = (bl_count - (available_bl_slots - 1)); + break; + } + } while (1); + + return rc; +} + +bool cam_hw_cdm_bl_write(struct cam_hw_info *cdm_hw, uint32_t src, + uint32_t len, uint32_t tag) +{ + if (cam_cdm_write_hw_reg(cdm_hw, CDM_BL_FIFO_BASE_REG, src)) { + CAM_ERR(CAM_CDM, "Failed to write CDM base to BL base"); + return true; + } + if (cam_cdm_write_hw_reg(cdm_hw, CDM_BL_FIFO_LEN_REG, + ((len & 0xFFFFF) | ((tag & 0xFF) << 20)))) { + CAM_ERR(CAM_CDM, "Failed to write CDM BL len"); + return true; + } + return false; +} + +bool cam_hw_cdm_commit_bl_write(struct cam_hw_info *cdm_hw) +{ + if (cam_cdm_write_hw_reg(cdm_hw, CDM_BL_FIFO_STORE_REG, 1)) { + CAM_ERR(CAM_CDM, "Failed to write CDM commit BL"); + return true; + } + return false; +} + +int cam_hw_cdm_submit_gen_irq(struct cam_hw_info *cdm_hw, + struct cam_cdm_hw_intf_cmd_submit_bl *req) +{ + struct cam_cdm_bl_cb_request_entry *node; + struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info; + uint32_t len; + int rc; + + if (core->bl_tag > 63) { + CAM_ERR(CAM_CDM, "bl_tag invalid =%d", core->bl_tag); + rc = -EINVAL; + goto end; + } + CAM_DBG(CAM_CDM, "CDM write BL last cmd tag=%x total=%d cookie=%d", + core->bl_tag, req->data->cmd_arrary_count, req->data->cookie); + node = kzalloc(sizeof(struct cam_cdm_bl_cb_request_entry), + GFP_KERNEL); + if (!node) { + rc = -ENOMEM; + goto end; + } + node->request_type = CAM_HW_CDM_BL_CB_CLIENT; + node->client_hdl = req->handle; + node->cookie = req->data->cookie; + node->bl_tag = core->bl_tag; + node->userdata = req->data->userdata; + list_add_tail(&node->entry, &core->bl_request_list); + len = core->ops->cdm_required_size_genirq() * core->bl_tag; + core->ops->cdm_write_genirq(((uint32_t *)core->gen_irq.kmdvaddr + len), + core->bl_tag); + rc = cam_hw_cdm_bl_write(cdm_hw, (core->gen_irq.vaddr + (4*len)), + ((4 * core->ops->cdm_required_size_genirq()) - 1), + core->bl_tag); + if (rc) { + CAM_ERR(CAM_CDM, "CDM hw bl write failed for gen irq bltag=%d", + core->bl_tag); + list_del_init(&node->entry); + kfree(node); + rc = -EIO; + goto end; + } + + if (cam_hw_cdm_commit_bl_write(cdm_hw)) { + CAM_ERR(CAM_CDM, "Cannot commit the genirq BL with tag tag=%d", + core->bl_tag); + list_del_init(&node->entry); + kfree(node); + rc = -EIO; + } + +end: + return rc; +} + +int cam_hw_cdm_submit_bl(struct cam_hw_info *cdm_hw, + struct cam_cdm_hw_intf_cmd_submit_bl *req, + struct cam_cdm_client *client) +{ + int i, rc; + struct cam_cdm_bl_request *cdm_cmd = req->data; + struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info; + uint32_t pending_bl = 0; + int write_count = 0; + + if (req->data->cmd_arrary_count > CAM_CDM_HWFIFO_SIZE) { + pr_info("requested BL more than max size, cnt=%d max=%d", + req->data->cmd_arrary_count, CAM_CDM_HWFIFO_SIZE); + } + + if (atomic_read(&core->error)) + return -EIO; + + mutex_lock(&cdm_hw->hw_mutex); + mutex_lock(&client->lock); + rc = cam_hw_cdm_bl_fifo_pending_bl_rb(cdm_hw, &pending_bl); + if (rc) { + CAM_ERR(CAM_CDM, "Cannot read the current BL depth"); + mutex_unlock(&client->lock); + mutex_unlock(&cdm_hw->hw_mutex); + return rc; + } + + for (i = 0; i < req->data->cmd_arrary_count ; i++) { + dma_addr_t hw_vaddr_ptr = 0; + size_t len = 0; + + if ((!cdm_cmd->cmd[i].len) && + (cdm_cmd->cmd[i].len > 0x100000)) { + CAM_ERR(CAM_CDM, + "cmd len(%d) is invalid cnt=%d total cnt=%d", + cdm_cmd->cmd[i].len, i, + req->data->cmd_arrary_count); + rc = -EINVAL; + break; + } + if (atomic_read(&core->error)) { + CAM_ERR_RATE_LIMIT(CAM_CDM, + "In error state cnt=%d total cnt=%d\n", + i, req->data->cmd_arrary_count); + rc = -EIO; + break; + } + if (write_count == 0) { + write_count = cam_hw_cdm_wait_for_bl_fifo(cdm_hw, + (req->data->cmd_arrary_count - i)); + if (write_count < 0) { + CAM_ERR(CAM_CDM, + "wait for bl fifo failed %d:%d", + i, req->data->cmd_arrary_count); + rc = -EIO; + break; + } + } else { + write_count--; + } + + if (req->data->type == CAM_CDM_BL_CMD_TYPE_MEM_HANDLE) { + rc = cam_mem_get_io_buf( + cdm_cmd->cmd[i].bl_addr.mem_handle, + core->iommu_hdl.non_secure, &hw_vaddr_ptr, + &len); + } else if (req->data->type == CAM_CDM_BL_CMD_TYPE_HW_IOVA) { + if (!cdm_cmd->cmd[i].bl_addr.hw_iova) { + CAM_ERR(CAM_CDM, + "Hw bl hw_iova is invalid %d:%d", + i, req->data->cmd_arrary_count); + rc = -EINVAL; + break; + } + rc = 0; + hw_vaddr_ptr = + (uint64_t)cdm_cmd->cmd[i].bl_addr.hw_iova; + len = cdm_cmd->cmd[i].len + cdm_cmd->cmd[i].offset; + } else { + CAM_ERR(CAM_CDM, + "Only mem hdl/hw va type is supported %d", + req->data->type); + rc = -EINVAL; + break; + } + + if ((!rc) && (hw_vaddr_ptr) && (len) && + (len >= cdm_cmd->cmd[i].offset)) { + + if ((len - cdm_cmd->cmd[i].offset) < + cdm_cmd->cmd[i].len) { + CAM_ERR(CAM_CDM, + "Not enough buffer cmd offset: %u cmd length: %u", + cdm_cmd->cmd[i].offset, + cdm_cmd->cmd[i].len); + rc = -EINVAL; + break; + } + + CAM_DBG(CAM_CDM, "Got the HW VA"); + if (core->bl_tag >= + (CAM_CDM_HWFIFO_SIZE - 1)) + core->bl_tag = 0; + rc = cam_hw_cdm_bl_write(cdm_hw, + ((uint32_t)hw_vaddr_ptr + + cdm_cmd->cmd[i].offset), + (cdm_cmd->cmd[i].len - 1), core->bl_tag); + if (rc) { + CAM_ERR(CAM_CDM, "Hw bl write failed %d:%d", + i, req->data->cmd_arrary_count); + rc = -EIO; + break; + } + } else { + CAM_ERR(CAM_CDM, + "Sanity check failed for hdl=%x len=%zu:%d", + cdm_cmd->cmd[i].bl_addr.mem_handle, len, + cdm_cmd->cmd[i].offset); + CAM_ERR(CAM_CDM, "Sanity check failed for %d:%d", + i, req->data->cmd_arrary_count); + rc = -EINVAL; + break; + } + + if (!rc) { + CAM_DBG(CAM_CDM, + "write BL success for cnt=%d with tag=%d total_cnt=%d", + i, core->bl_tag, req->data->cmd_arrary_count); + + CAM_DBG(CAM_CDM, "Now commit the BL"); + if (cam_hw_cdm_commit_bl_write(cdm_hw)) { + CAM_ERR(CAM_CDM, + "Cannot commit the BL %d tag=%d", + i, core->bl_tag); + rc = -EIO; + break; + } + CAM_DBG(CAM_CDM, "BL commit success BL %d tag=%d", i, + core->bl_tag); + core->bl_tag++; + if ((req->data->flag == true) && + (i == (req->data->cmd_arrary_count - + 1))) { + rc = cam_hw_cdm_submit_gen_irq( + cdm_hw, req); + if (rc == 0) + core->bl_tag++; + } + } + } + mutex_unlock(&client->lock); + mutex_unlock(&cdm_hw->hw_mutex); + return rc; + +} + +static void cam_hw_cdm_work(struct work_struct *work) +{ + struct cam_cdm_work_payload *payload; + struct cam_hw_info *cdm_hw; + struct cam_cdm *core; + + payload = container_of(work, struct cam_cdm_work_payload, work); + if (payload) { + cdm_hw = payload->hw; + core = (struct cam_cdm *)cdm_hw->core_info; + + CAM_DBG(CAM_CDM, "IRQ status=%x", payload->irq_status); + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_INFO_INLINE_IRQ_MASK) { + struct cam_cdm_bl_cb_request_entry *node; + + CAM_DBG(CAM_CDM, "inline IRQ data=%x", + payload->irq_data); + mutex_lock(&cdm_hw->hw_mutex); + node = cam_cdm_find_request_by_bl_tag( + payload->irq_data, + &core->bl_request_list); + if (node) { + if (node->request_type == + CAM_HW_CDM_BL_CB_CLIENT) { + cam_cdm_notify_clients(cdm_hw, + CAM_CDM_CB_STATUS_BL_SUCCESS, + (void *)node); + } else if (node->request_type == + CAM_HW_CDM_BL_CB_INTERNAL) { + CAM_ERR(CAM_CDM, + "Invalid node=%pK %d", node, + node->request_type); + } + list_del_init(&node->entry); + kfree(node); + } else { + CAM_ERR(CAM_CDM, + "Inval node, inline_irq st=%x data=%x", + payload->irq_status, payload->irq_data); + } + mutex_unlock(&cdm_hw->hw_mutex); + } + + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_INFO_RST_DONE_MASK) { + CAM_DBG(CAM_CDM, "CDM HW reset done IRQ"); + complete(&core->reset_complete); + } + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_INFO_BL_DONE_MASK) { + if (atomic_read(&core->bl_done)) { + CAM_DBG(CAM_CDM, "CDM HW BL done IRQ"); + complete(&core->bl_complete); + } + } + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_ERROR_INV_CMD_MASK) { + CAM_ERR_RATE_LIMIT(CAM_CDM, + "Invalid command IRQ, Need HW reset\n"); + atomic_inc(&core->error); + cam_hw_cdm_dump_core_debug_registers(cdm_hw); + } + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_ERROR_AHB_BUS_MASK) { + CAM_ERR_RATE_LIMIT(CAM_CDM, "AHB Error IRQ\n"); + atomic_inc(&core->error); + cam_hw_cdm_dump_core_debug_registers(cdm_hw); + atomic_dec(&core->error); + } + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_ERROR_OVER_FLOW_MASK) { + CAM_ERR_RATE_LIMIT(CAM_CDM, "Overflow Error IRQ\n"); + atomic_inc(&core->error); + cam_hw_cdm_dump_core_debug_registers(cdm_hw); + atomic_dec(&core->error); + } + kfree(payload); + } else { + CAM_ERR(CAM_CDM, "NULL payload"); + } + +} + +static void cam_hw_cdm_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags, void *token, + uint32_t buf_info) +{ + struct cam_hw_info *cdm_hw = NULL; + struct cam_cdm *core = NULL; + + if (token) { + cdm_hw = (struct cam_hw_info *)token; + core = (struct cam_cdm *)cdm_hw->core_info; + atomic_inc(&core->error); + cam_hw_cdm_dump_core_debug_registers(cdm_hw); + CAM_ERR_RATE_LIMIT(CAM_CDM, "Page fault iova addr %pK\n", + (void *)iova); + cam_cdm_notify_clients(cdm_hw, CAM_CDM_CB_STATUS_PAGEFAULT, + (void *)iova); + atomic_dec(&core->error); + } else { + CAM_ERR(CAM_CDM, "Invalid token"); + } + +} + +irqreturn_t cam_hw_cdm_irq(int irq_num, void *data) +{ + struct cam_hw_info *cdm_hw = data; + struct cam_cdm *cdm_core = cdm_hw->core_info; + struct cam_cdm_work_payload *payload; + bool work_status; + + CAM_DBG(CAM_CDM, "Got irq"); + payload = kzalloc(sizeof(struct cam_cdm_work_payload), GFP_ATOMIC); + if (payload) { + if (cam_cdm_read_hw_reg(cdm_hw, CDM_IRQ_STATUS, + &payload->irq_status)) { + CAM_ERR(CAM_CDM, "Failed to read CDM HW IRQ status"); + } + if (!payload->irq_status) { + CAM_ERR_RATE_LIMIT(CAM_CDM, "Invalid irq received\n"); + kfree(payload); + return IRQ_HANDLED; + } + if (payload->irq_status & + CAM_CDM_IRQ_STATUS_INFO_INLINE_IRQ_MASK) { + if (cam_cdm_read_hw_reg(cdm_hw, CDM_IRQ_USR_DATA, + &payload->irq_data)) { + CAM_ERR(CAM_CDM, + "Failed to read CDM HW IRQ data"); + } + } + CAM_DBG(CAM_CDM, "Got payload=%d", payload->irq_status); + payload->hw = cdm_hw; + INIT_WORK((struct work_struct *)&payload->work, + cam_hw_cdm_work); + if (cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_CLEAR, + payload->irq_status)) + CAM_ERR(CAM_CDM, "Failed to Write CDM HW IRQ Clear"); + if (cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_CLEAR_CMD, 0x01)) + CAM_ERR(CAM_CDM, "Failed to Write CDM HW IRQ cmd"); + work_status = queue_work(cdm_core->work_queue, &payload->work); + if (work_status == false) { + CAM_ERR(CAM_CDM, "Failed to queue work for irq=%x", + payload->irq_status); + kfree(payload); + } + } + + return IRQ_HANDLED; +} + +int cam_hw_cdm_alloc_genirq_mem(void *hw_priv) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_mem_mgr_request_desc genirq_alloc_cmd; + struct cam_mem_mgr_memory_desc genirq_alloc_out; + struct cam_cdm *cdm_core = NULL; + int rc = -EINVAL; + + if (!hw_priv) + return rc; + + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + genirq_alloc_cmd.align = 0; + genirq_alloc_cmd.size = (8 * CAM_CDM_HWFIFO_SIZE); + genirq_alloc_cmd.smmu_hdl = cdm_core->iommu_hdl.non_secure; + genirq_alloc_cmd.flags = CAM_MEM_FLAG_HW_READ_WRITE; + rc = cam_mem_mgr_request_mem(&genirq_alloc_cmd, + &genirq_alloc_out); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to get genirq cmd space rc=%d", rc); + goto end; + } + cdm_core->gen_irq.handle = genirq_alloc_out.mem_handle; + cdm_core->gen_irq.vaddr = (genirq_alloc_out.iova & 0xFFFFFFFF); + cdm_core->gen_irq.kmdvaddr = genirq_alloc_out.kva; + cdm_core->gen_irq.size = genirq_alloc_out.len; + +end: + return rc; +} + +int cam_hw_cdm_release_genirq_mem(void *hw_priv) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_cdm *cdm_core = NULL; + struct cam_mem_mgr_memory_desc genirq_release_cmd; + int rc = -EINVAL; + + if (!hw_priv) + return rc; + + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + genirq_release_cmd.mem_handle = cdm_core->gen_irq.handle; + rc = cam_mem_mgr_release_mem(&genirq_release_cmd); + if (rc) + CAM_ERR(CAM_CDM, "Failed to put genirq cmd space for hw"); + + return rc; +} + +int cam_hw_cdm_init(void *hw_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_cdm *cdm_core = NULL; + int rc; + long time_left; + + if (!hw_priv) + return -EINVAL; + + soc_info = &cdm_hw->soc_info; + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) { + CAM_ERR(CAM_CDM, "Enable platform failed"); + goto end; + } + + CAM_DBG(CAM_CDM, "Enable soc done"); + +/* Before triggering the reset to HW, clear the reset complete */ + atomic_set(&cdm_core->error, 0); + atomic_set(&cdm_core->bl_done, 0); + reinit_completion(&cdm_core->reset_complete); + reinit_completion(&cdm_core->bl_complete); + + if (cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_MASK, 0x70003)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW IRQ mask"); + goto disable_return; + } + if (cam_cdm_write_hw_reg(cdm_hw, CDM_CFG_RST_CMD, 0x9)) { + CAM_ERR(CAM_CDM, "Failed to Write CDM HW reset"); + goto disable_return; + } + + CAM_DBG(CAM_CDM, "Waiting for CDM HW resetdone"); + time_left = wait_for_completion_timeout(&cdm_core->reset_complete, + msecs_to_jiffies(CAM_CDM_HW_RESET_TIMEOUT)); + + if (time_left <= 0) { + CAM_ERR(CAM_CDM, "CDM HW reset Wait failed rc=%d", rc); + goto disable_return; + } else { + CAM_DBG(CAM_CDM, "CDM Init success"); + cdm_hw->hw_state = CAM_HW_STATE_POWER_UP; + cam_cdm_write_hw_reg(cdm_hw, CDM_IRQ_MASK, 0x70003); + rc = 0; + goto end; + } + +disable_return: + rc = -EIO; + cam_soc_util_disable_platform_resource(soc_info, true, true); +end: + return rc; +} + +int cam_hw_cdm_deinit(void *hw_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *cdm_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_cdm *cdm_core = NULL; + int rc = 0; + + if (!hw_priv) + return -EINVAL; + + soc_info = &cdm_hw->soc_info; + cdm_core = cdm_hw->core_info; + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) { + CAM_ERR(CAM_CDM, "disable platform failed"); + } else { + CAM_DBG(CAM_CDM, "CDM Deinit success"); + cdm_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + } + + return rc; +} + +int cam_hw_cdm_probe(struct platform_device *pdev) +{ + int rc; + struct cam_hw_info *cdm_hw = NULL; + struct cam_hw_intf *cdm_hw_intf = NULL; + struct cam_cdm *cdm_core = NULL; + struct cam_cdm_private_dt_data *soc_private = NULL; + struct cam_cpas_register_params cpas_parms; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + + cdm_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!cdm_hw_intf) + return -ENOMEM; + + cdm_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!cdm_hw) { + kfree(cdm_hw_intf); + return -ENOMEM; + } + + cdm_hw->core_info = kzalloc(sizeof(struct cam_cdm), GFP_KERNEL); + if (!cdm_hw->core_info) { + kfree(cdm_hw); + kfree(cdm_hw_intf); + return -ENOMEM; + } + + cdm_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + cdm_hw->soc_info.pdev = pdev; + cdm_hw->soc_info.dev = &pdev->dev; + cdm_hw->soc_info.dev_name = pdev->name; + cdm_hw_intf->hw_type = CAM_HW_CDM; + cdm_hw->open_count = 0; + mutex_init(&cdm_hw->hw_mutex); + spin_lock_init(&cdm_hw->hw_lock); + init_completion(&cdm_hw->hw_complete); + + rc = cam_hw_cdm_soc_get_dt_properties(cdm_hw, msm_cam_hw_cdm_dt_match); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to get dt properties"); + goto release_mem; + } + cdm_hw_intf->hw_idx = cdm_hw->soc_info.index; + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + soc_private = (struct cam_cdm_private_dt_data *) + cdm_hw->soc_info.soc_private; + if (soc_private->dt_cdm_shared == true) + cdm_core->flags = CAM_CDM_FLAG_SHARED_CDM; + else + cdm_core->flags = CAM_CDM_FLAG_PRIVATE_CDM; + + cdm_core->bl_tag = 0; + cdm_core->id = cam_hw_cdm_get_id_by_name(cdm_core->name); + if (cdm_core->id >= CAM_CDM_MAX) { + CAM_ERR(CAM_CDM, "Failed to get CDM HW name for %s", + cdm_core->name); + goto release_private_mem; + } + INIT_LIST_HEAD(&cdm_core->bl_request_list); + init_completion(&cdm_core->reset_complete); + init_completion(&cdm_core->bl_complete); + cdm_hw_intf->hw_priv = cdm_hw; + cdm_hw_intf->hw_ops.get_hw_caps = cam_cdm_get_caps; + cdm_hw_intf->hw_ops.init = cam_hw_cdm_init; + cdm_hw_intf->hw_ops.deinit = cam_hw_cdm_deinit; + cdm_hw_intf->hw_ops.start = cam_cdm_stream_start; + cdm_hw_intf->hw_ops.stop = cam_cdm_stream_stop; + cdm_hw_intf->hw_ops.read = NULL; + cdm_hw_intf->hw_ops.write = NULL; + cdm_hw_intf->hw_ops.process_cmd = cam_cdm_process_cmd; + mutex_lock(&cdm_hw->hw_mutex); + + CAM_DBG(CAM_CDM, "type %d index %d", cdm_hw_intf->hw_type, + cdm_hw_intf->hw_idx); + + platform_set_drvdata(pdev, cdm_hw_intf); + + rc = cam_smmu_get_handle("cpas-cdm0", &cdm_core->iommu_hdl.non_secure); + if (rc < 0) { + CAM_ERR(CAM_CDM, "cpas-cdm get iommu handle failed"); + goto unlock_release_mem; + } + cam_smmu_set_client_page_fault_handler(cdm_core->iommu_hdl.non_secure, + cam_hw_cdm_iommu_fault_handler, cdm_hw); + + cdm_core->iommu_hdl.secure = -1; + + cdm_core->work_queue = alloc_workqueue(cdm_core->name, + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, + CAM_CDM_INFLIGHT_WORKS); + + rc = cam_soc_util_request_platform_resource(&cdm_hw->soc_info, + cam_hw_cdm_irq, cdm_hw); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to request platform resource"); + goto destroy_non_secure_hdl; + } + + cpas_parms.cam_cpas_client_cb = cam_cdm_cpas_cb; + cpas_parms.cell_index = cdm_hw->soc_info.index; + cpas_parms.dev = &pdev->dev; + cpas_parms.userdata = cdm_hw_intf; + strlcpy(cpas_parms.identifier, "cpas-cdm", CAM_HW_IDENTIFIER_LENGTH); + rc = cam_cpas_register_client(&cpas_parms); + if (rc) { + CAM_ERR(CAM_CDM, "Virtual CDM CPAS registration failed"); + goto release_platform_resource; + } + CAM_DBG(CAM_CDM, "CPAS registration successful handle=%d", + cpas_parms.client_handle); + cdm_core->cpas_handle = cpas_parms.client_handle; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + rc = cam_cpas_start(cdm_core->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_CDM, "CPAS start failed"); + goto cpas_unregister; + } + + rc = cam_hw_cdm_init(cdm_hw, NULL, 0); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to Init CDM HW"); + goto cpas_stop; + } + cdm_hw->open_count++; + + if (cam_cdm_read_hw_reg(cdm_hw, CDM_CFG_HW_VERSION, + &cdm_core->hw_version)) { + CAM_ERR(CAM_CDM, "Failed to read CDM HW Version"); + goto deinit; + } + + if (cam_cdm_read_hw_reg(cdm_hw, CDM_CFG_TITAN_VERSION, + &cdm_core->hw_family_version)) { + CAM_ERR(CAM_CDM, "Failed to read CDM family Version"); + goto deinit; + } + + CAM_DBG(CAM_CDM, "CDM Hw version read success family =%x hw =%x", + cdm_core->hw_family_version, cdm_core->hw_version); + cdm_core->ops = cam_cdm_get_ops(cdm_core->hw_version, NULL, + false); + if (!cdm_core->ops) { + CAM_ERR(CAM_CDM, "Failed to util ops for hw"); + goto deinit; + } + + if (!cam_cdm_set_cam_hw_version(cdm_core->hw_version, + &cdm_core->version)) { + CAM_ERR(CAM_CDM, "Failed to set cam he version for hw"); + goto deinit; + } + + rc = cam_hw_cdm_deinit(cdm_hw, NULL, 0); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to Deinit CDM HW"); + cdm_hw->open_count--; + goto cpas_stop; + } + + rc = cam_cpas_stop(cdm_core->cpas_handle); + if (rc) { + CAM_ERR(CAM_CDM, "CPAS stop failed"); + cdm_hw->open_count--; + goto cpas_unregister; + } + + rc = cam_cdm_intf_register_hw_cdm(cdm_hw_intf, + soc_private, CAM_HW_CDM, &cdm_core->index); + if (rc) { + CAM_ERR(CAM_CDM, "HW CDM Interface registration failed"); + cdm_hw->open_count--; + goto cpas_unregister; + } + cdm_hw->open_count--; + mutex_unlock(&cdm_hw->hw_mutex); + + CAM_DBG(CAM_CDM, "CDM%d probe successful", cdm_hw_intf->hw_idx); + + return rc; + +deinit: + if (cam_hw_cdm_deinit(cdm_hw, NULL, 0)) + CAM_ERR(CAM_CDM, "Deinit failed for hw"); + cdm_hw->open_count--; +cpas_stop: + if (cam_cpas_stop(cdm_core->cpas_handle)) + CAM_ERR(CAM_CDM, "CPAS stop failed"); +cpas_unregister: + if (cam_cpas_unregister_client(cdm_core->cpas_handle)) + CAM_ERR(CAM_CDM, "CPAS unregister failed"); +release_platform_resource: + if (cam_soc_util_release_platform_resource(&cdm_hw->soc_info)) + CAM_ERR(CAM_CDM, "Release platform resource failed"); + + flush_workqueue(cdm_core->work_queue); + destroy_workqueue(cdm_core->work_queue); +destroy_non_secure_hdl: + cam_smmu_set_client_page_fault_handler(cdm_core->iommu_hdl.non_secure, + NULL, cdm_hw); + if (cam_smmu_destroy_handle(cdm_core->iommu_hdl.non_secure)) + CAM_ERR(CAM_CDM, "Release iommu secure hdl failed"); +unlock_release_mem: + mutex_unlock(&cdm_hw->hw_mutex); +release_private_mem: + kfree(cdm_hw->soc_info.soc_private); +release_mem: + mutex_destroy(&cdm_hw->hw_mutex); + kfree(cdm_hw_intf); + kfree(cdm_hw->core_info); + kfree(cdm_hw); + return rc; +} + +int cam_hw_cdm_remove(struct platform_device *pdev) +{ + int rc = -EBUSY; + struct cam_hw_info *cdm_hw = NULL; + struct cam_hw_intf *cdm_hw_intf = NULL; + struct cam_cdm *cdm_core = NULL; + + cdm_hw_intf = platform_get_drvdata(pdev); + if (!cdm_hw_intf) { + CAM_ERR(CAM_CDM, "Failed to get dev private data"); + return rc; + } + + cdm_hw = cdm_hw_intf->hw_priv; + if (!cdm_hw) { + CAM_ERR(CAM_CDM, + "Failed to get hw private data for type=%d idx=%d", + cdm_hw_intf->hw_type, cdm_hw_intf->hw_idx); + return rc; + } + + cdm_core = cdm_hw->core_info; + if (!cdm_core) { + CAM_ERR(CAM_CDM, + "Failed to get hw core data for type=%d idx=%d", + cdm_hw_intf->hw_type, cdm_hw_intf->hw_idx); + return rc; + } + + if (cdm_hw->open_count != 0) { + CAM_ERR(CAM_CDM, "Hw open count invalid type=%d idx=%d cnt=%d", + cdm_hw_intf->hw_type, cdm_hw_intf->hw_idx, + cdm_hw->open_count); + return rc; + } + + rc = cam_hw_cdm_deinit(cdm_hw, NULL, 0); + if (rc) { + CAM_ERR(CAM_CDM, "Deinit failed for hw"); + return rc; + } + + rc = cam_cpas_unregister_client(cdm_core->cpas_handle); + if (rc) { + CAM_ERR(CAM_CDM, "CPAS unregister failed"); + return rc; + } + + if (cam_soc_util_release_platform_resource(&cdm_hw->soc_info)) + CAM_ERR(CAM_CDM, "Release platform resource failed"); + + flush_workqueue(cdm_core->work_queue); + destroy_workqueue(cdm_core->work_queue); + + if (cam_smmu_destroy_handle(cdm_core->iommu_hdl.non_secure)) + CAM_ERR(CAM_CDM, "Release iommu secure hdl failed"); + cam_smmu_unset_client_page_fault_handler( + cdm_core->iommu_hdl.non_secure, cdm_hw); + + mutex_destroy(&cdm_hw->hw_mutex); + kfree(cdm_hw->soc_info.soc_private); + kfree(cdm_hw_intf); + kfree(cdm_hw->core_info); + kfree(cdm_hw); + + return 0; +} + +static struct platform_driver cam_hw_cdm_driver = { + .probe = cam_hw_cdm_probe, + .remove = cam_hw_cdm_remove, + .driver = { + .name = "msm_cam_cdm", + .owner = THIS_MODULE, + .of_match_table = msm_cam_hw_cdm_dt_match, + }, +}; + +static int __init cam_hw_cdm_init_module(void) +{ + return platform_driver_register(&cam_hw_cdm_driver); +} + +static void __exit cam_hw_cdm_exit_module(void) +{ + platform_driver_unregister(&cam_hw_cdm_driver); +} + +module_init(cam_hw_cdm_init_module); +module_exit(cam_hw_cdm_exit_module); +MODULE_DESCRIPTION("MSM Camera HW CDM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf.c new file mode 100644 index 000000000000..da4fc3cc587a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf.c @@ -0,0 +1,577 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_cdm_intf_api.h" +#include "cam_cdm.h" +#include "cam_cdm_virtual.h" +#include "cam_soc_util.h" +#include "cam_cdm_soc.h" + +static struct cam_cdm_intf_mgr cdm_mgr; +static DEFINE_MUTEX(cam_cdm_mgr_lock); + +static const struct of_device_id msm_cam_cdm_intf_dt_match[] = { + { .compatible = "qcom,cam-cdm-intf", }, + {} +}; + +static int get_cdm_mgr_refcount(void) +{ + int rc = 0; + + mutex_lock(&cam_cdm_mgr_lock); + if (cdm_mgr.probe_done == false) { + CAM_ERR(CAM_CDM, "CDM intf mgr not probed yet"); + rc = -EPERM; + } else { + CAM_DBG(CAM_CDM, "CDM intf mgr get refcount=%d", + cdm_mgr.refcount); + cdm_mgr.refcount++; + } + mutex_unlock(&cam_cdm_mgr_lock); + return rc; +} + +static void put_cdm_mgr_refcount(void) +{ + mutex_lock(&cam_cdm_mgr_lock); + if (cdm_mgr.probe_done == false) { + CAM_ERR(CAM_CDM, "CDM intf mgr not probed yet"); + } else { + CAM_DBG(CAM_CDM, "CDM intf mgr put refcount=%d", + cdm_mgr.refcount); + if (cdm_mgr.refcount > 0) { + cdm_mgr.refcount--; + } else { + CAM_ERR(CAM_CDM, "Refcount put when zero"); + WARN_ON(1); + } + } + mutex_unlock(&cam_cdm_mgr_lock); +} + +static int get_cdm_iommu_handle(struct cam_iommu_handle *cdm_handles, + uint32_t hw_idx) +{ + int rc = -EPERM; + struct cam_hw_intf *hw = cdm_mgr.nodes[hw_idx].device; + + if (hw->hw_ops.get_hw_caps) { + rc = hw->hw_ops.get_hw_caps(hw->hw_priv, cdm_handles, + sizeof(struct cam_iommu_handle)); + } + + return rc; +} + +static int get_cdm_index_by_id(char *identifier, + uint32_t cell_index, uint32_t *hw_index) +{ + int rc = -EPERM, i, j; + char client_name[128]; + + CAM_DBG(CAM_CDM, "Looking for HW id of =%s and index=%d", + identifier, cell_index); + snprintf(client_name, sizeof(client_name), "%s", identifier); + CAM_DBG(CAM_CDM, "Looking for HW id of %s count:%d", client_name, + cdm_mgr.cdm_count); + mutex_lock(&cam_cdm_mgr_lock); + for (i = 0; i < cdm_mgr.cdm_count; i++) { + mutex_lock(&cdm_mgr.nodes[i].lock); + CAM_DBG(CAM_CDM, "dt_num_supported_clients=%d", + cdm_mgr.nodes[i].data->dt_num_supported_clients); + + for (j = 0; j < + cdm_mgr.nodes[i].data->dt_num_supported_clients; j++) { + CAM_DBG(CAM_CDM, "client name:%s", + cdm_mgr.nodes[i].data->dt_cdm_client_name[j]); + if (!strcmp( + cdm_mgr.nodes[i].data->dt_cdm_client_name[j], + client_name)) { + rc = 0; + *hw_index = i; + break; + } + } + mutex_unlock(&cdm_mgr.nodes[i].lock); + if (rc == 0) + break; + } + mutex_unlock(&cam_cdm_mgr_lock); + + return rc; +} + +int cam_cdm_get_iommu_handle(char *identifier, + struct cam_iommu_handle *cdm_handles) +{ + int i, j, rc = -EPERM; + + if ((!identifier) || (!cdm_handles)) + return -EINVAL; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + return rc; + } + CAM_DBG(CAM_CDM, "Looking for Iommu handle of %s", identifier); + + for (i = 0; i < cdm_mgr.cdm_count; i++) { + mutex_lock(&cdm_mgr.nodes[i].lock); + if (!cdm_mgr.nodes[i].data) { + mutex_unlock(&cdm_mgr.nodes[i].lock); + continue; + } + for (j = 0; j < + cdm_mgr.nodes[i].data->dt_num_supported_clients; + j++) { + if (!strcmp( + cdm_mgr.nodes[i].data->dt_cdm_client_name[j], + identifier)) { + rc = get_cdm_iommu_handle(cdm_handles, i); + break; + } + } + mutex_unlock(&cdm_mgr.nodes[i].lock); + if (rc == 0) + break; + } + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_get_iommu_handle); + +int cam_cdm_acquire(struct cam_cdm_acquire_data *data) +{ + int rc = -EPERM; + struct cam_hw_intf *hw; + uint32_t hw_index = 0; + + if ((!data) || (!data->base_array_cnt)) + return -EINVAL; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + return rc; + } + + if (data->id > CAM_CDM_HW_ANY) { + CAM_ERR(CAM_CDM, + "only CAM_CDM_VIRTUAL/CAM_CDM_HW_ANY is supported"); + rc = -EPERM; + goto end; + } + rc = get_cdm_index_by_id(data->identifier, data->cell_index, + &hw_index); + if ((rc < 0) && (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM)) { + CAM_ERR(CAM_CDM, "Failed to identify associated hw id"); + goto end; + } else { + CAM_DBG(CAM_CDM, "hw_index:%d", hw_index); + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.process_cmd) { + rc = hw->hw_ops.process_cmd(hw->hw_priv, + CAM_CDM_HW_INTF_CMD_ACQUIRE, data, + sizeof(struct cam_cdm_acquire_data)); + if (rc < 0) { + CAM_ERR(CAM_CDM, "CDM hw acquire failed"); + goto end; + } + } else { + CAM_ERR(CAM_CDM, "idx %d doesn't have acquire ops", + hw_index); + rc = -EPERM; + } + } +end: + if (rc < 0) { + CAM_ERR(CAM_CDM, "CDM acquire failed for id=%d name=%s, idx=%d", + data->id, data->identifier, data->cell_index); + put_cdm_mgr_refcount(); + } + return rc; +} +EXPORT_SYMBOL(cam_cdm_acquire); + +int cam_cdm_release(uint32_t handle) +{ + uint32_t hw_index; + int rc = -EPERM; + struct cam_hw_intf *hw; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + return rc; + } + + hw_index = CAM_CDM_GET_HW_IDX(handle); + if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) { + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.process_cmd) { + rc = hw->hw_ops.process_cmd(hw->hw_priv, + CAM_CDM_HW_INTF_CMD_RELEASE, &handle, + sizeof(handle)); + if (rc < 0) + CAM_ERR(CAM_CDM, + "hw release failed for handle=%x", + handle); + } else + CAM_ERR(CAM_CDM, "hw idx %d doesn't have release ops", + hw_index); + } + put_cdm_mgr_refcount(); + if (rc == 0) + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_release); + + +int cam_cdm_submit_bls(uint32_t handle, struct cam_cdm_bl_request *data) +{ + uint32_t hw_index; + int rc = -EINVAL; + struct cam_hw_intf *hw; + + if (!data) + return rc; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + rc = -EPERM; + return rc; + } + + hw_index = CAM_CDM_GET_HW_IDX(handle); + if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) { + struct cam_cdm_hw_intf_cmd_submit_bl req; + + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.process_cmd) { + req.data = data; + req.handle = handle; + rc = hw->hw_ops.process_cmd(hw->hw_priv, + CAM_CDM_HW_INTF_CMD_SUBMIT_BL, &req, + sizeof(struct cam_cdm_hw_intf_cmd_submit_bl)); + if (rc < 0) + CAM_ERR(CAM_CDM, + "hw submit bl failed for handle=%x", + handle); + } else { + CAM_ERR(CAM_CDM, "hw idx %d doesn't have submit ops", + hw_index); + } + } + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_submit_bls); + +int cam_cdm_stream_on(uint32_t handle) +{ + uint32_t hw_index; + int rc = -EINVAL; + struct cam_hw_intf *hw; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + rc = -EPERM; + return rc; + } + + hw_index = CAM_CDM_GET_HW_IDX(handle); + if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) { + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.start) { + rc = hw->hw_ops.start(hw->hw_priv, &handle, + sizeof(uint32_t)); + if (rc < 0) + CAM_ERR(CAM_CDM, + "hw start failed handle=%x", + handle); + } else { + CAM_ERR(CAM_CDM, + "hw idx %d doesn't have start ops", + hw_index); + } + } + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_stream_on); + +int cam_cdm_stream_off(uint32_t handle) +{ + uint32_t hw_index; + int rc = -EINVAL; + struct cam_hw_intf *hw; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + rc = -EPERM; + return rc; + } + + hw_index = CAM_CDM_GET_HW_IDX(handle); + if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) { + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.stop) { + rc = hw->hw_ops.stop(hw->hw_priv, &handle, + sizeof(uint32_t)); + if (rc < 0) + CAM_ERR(CAM_CDM, "hw stop failed handle=%x", + handle); + } else { + CAM_ERR(CAM_CDM, "hw idx %d doesn't have stop ops", + hw_index); + } + } + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_stream_off); + +int cam_cdm_reset_hw(uint32_t handle) +{ + uint32_t hw_index; + int rc = -EINVAL; + struct cam_hw_intf *hw; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + rc = -EPERM; + return rc; + } + + hw_index = CAM_CDM_GET_HW_IDX(handle); + if (hw_index < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM) { + hw = cdm_mgr.nodes[hw_index].device; + if (hw && hw->hw_ops.process_cmd) { + rc = hw->hw_ops.process_cmd(hw->hw_priv, + CAM_CDM_HW_INTF_CMD_RESET_HW, &handle, + sizeof(handle)); + if (rc < 0) + CAM_ERR(CAM_CDM, + "CDM hw release failed for handle=%x", + handle); + } else { + CAM_ERR(CAM_CDM, "hw idx %d doesn't have release ops", + hw_index); + } + } + put_cdm_mgr_refcount(); + + return rc; +} +EXPORT_SYMBOL(cam_cdm_reset_hw); + +int cam_cdm_intf_register_hw_cdm(struct cam_hw_intf *hw, + struct cam_cdm_private_dt_data *data, enum cam_cdm_type type, + uint32_t *index) +{ + int rc = -EINVAL; + + if ((!hw) || (!data) || (!index)) + return rc; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + return rc; + } + + mutex_lock(&cam_cdm_mgr_lock); + if ((type == CAM_VIRTUAL_CDM) && + (!cdm_mgr.nodes[CAM_SW_CDM_INDEX].device)) { + mutex_lock(&cdm_mgr.nodes[CAM_SW_CDM_INDEX].lock); + cdm_mgr.nodes[CAM_SW_CDM_INDEX].device = hw; + cdm_mgr.nodes[CAM_SW_CDM_INDEX].data = data; + *index = cdm_mgr.cdm_count; + mutex_unlock(&cdm_mgr.nodes[CAM_SW_CDM_INDEX].lock); + cdm_mgr.cdm_count++; + rc = 0; + } else if ((type == CAM_HW_CDM) && (cdm_mgr.cdm_count > 0)) { + mutex_lock(&cdm_mgr.nodes[cdm_mgr.cdm_count].lock); + cdm_mgr.nodes[cdm_mgr.cdm_count].device = hw; + cdm_mgr.nodes[cdm_mgr.cdm_count].data = data; + *index = cdm_mgr.cdm_count; + mutex_unlock(&cdm_mgr.nodes[cdm_mgr.cdm_count].lock); + cdm_mgr.cdm_count++; + rc = 0; + } else { + CAM_ERR(CAM_CDM, "CDM registration failed type=%d count=%d", + type, cdm_mgr.cdm_count); + } + mutex_unlock(&cam_cdm_mgr_lock); + put_cdm_mgr_refcount(); + + return rc; +} + +int cam_cdm_intf_deregister_hw_cdm(struct cam_hw_intf *hw, + struct cam_cdm_private_dt_data *data, enum cam_cdm_type type, + uint32_t index) +{ + int rc = -EINVAL; + + if ((!hw) || (!data)) + return rc; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + rc = -EPERM; + return rc; + } + + mutex_lock(&cam_cdm_mgr_lock); + if ((type == CAM_VIRTUAL_CDM) && + (hw == cdm_mgr.nodes[CAM_SW_CDM_INDEX].device) && + (index == CAM_SW_CDM_INDEX)) { + mutex_lock(&cdm_mgr.nodes[cdm_mgr.cdm_count].lock); + cdm_mgr.nodes[CAM_SW_CDM_INDEX].device = NULL; + cdm_mgr.nodes[CAM_SW_CDM_INDEX].data = NULL; + mutex_unlock(&cdm_mgr.nodes[cdm_mgr.cdm_count].lock); + rc = 0; + } else if ((type == CAM_HW_CDM) && + (hw == cdm_mgr.nodes[index].device)) { + mutex_lock(&cdm_mgr.nodes[index].lock); + cdm_mgr.nodes[index].device = NULL; + cdm_mgr.nodes[index].data = NULL; + mutex_unlock(&cdm_mgr.nodes[index].lock); + cdm_mgr.cdm_count--; + rc = 0; + } else { + CAM_ERR(CAM_CDM, "CDM Deregistration failed type=%d index=%d", + type, index); + } + mutex_unlock(&cam_cdm_mgr_lock); + put_cdm_mgr_refcount(); + + return rc; +} + +static int cam_cdm_intf_probe(struct platform_device *pdev) +{ + int i, rc; + + rc = cam_cdm_intf_mgr_soc_get_dt_properties(pdev, &cdm_mgr); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to get dt properties"); + return rc; + } + mutex_lock(&cam_cdm_mgr_lock); + for (i = 0 ; i < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM; i++) { + mutex_init(&cdm_mgr.nodes[i].lock); + cdm_mgr.nodes[i].device = NULL; + cdm_mgr.nodes[i].data = NULL; + cdm_mgr.nodes[i].refcount = 0; + } + cdm_mgr.probe_done = true; + cdm_mgr.refcount = 0; + mutex_unlock(&cam_cdm_mgr_lock); + rc = cam_virtual_cdm_probe(pdev); + if (rc) { + mutex_lock(&cam_cdm_mgr_lock); + cdm_mgr.probe_done = false; + for (i = 0 ; i < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM; i++) { + if (cdm_mgr.nodes[i].device || cdm_mgr.nodes[i].data || + (cdm_mgr.nodes[i].refcount != 0)) + CAM_ERR(CAM_CDM, + "Valid node present in index=%d", i); + mutex_destroy(&cdm_mgr.nodes[i].lock); + cdm_mgr.nodes[i].device = NULL; + cdm_mgr.nodes[i].data = NULL; + cdm_mgr.nodes[i].refcount = 0; + } + mutex_unlock(&cam_cdm_mgr_lock); + } + + return rc; +} + +static int cam_cdm_intf_remove(struct platform_device *pdev) +{ + int i, rc = -EBUSY; + + if (get_cdm_mgr_refcount()) { + CAM_ERR(CAM_CDM, "CDM intf mgr get refcount failed"); + return rc; + } + + if (cam_virtual_cdm_remove(pdev)) { + CAM_ERR(CAM_CDM, "Virtual CDM remove failed"); + goto end; + } + put_cdm_mgr_refcount(); + + mutex_lock(&cam_cdm_mgr_lock); + if (cdm_mgr.refcount != 0) { + CAM_ERR(CAM_CDM, "cdm manger refcount not zero %d", + cdm_mgr.refcount); + goto end; + } + + for (i = 0 ; i < CAM_CDM_INTF_MGR_MAX_SUPPORTED_CDM; i++) { + if (cdm_mgr.nodes[i].device || cdm_mgr.nodes[i].data || + (cdm_mgr.nodes[i].refcount != 0)) { + CAM_ERR(CAM_CDM, "Valid node present in index=%d", i); + mutex_unlock(&cam_cdm_mgr_lock); + goto end; + } + mutex_destroy(&cdm_mgr.nodes[i].lock); + cdm_mgr.nodes[i].device = NULL; + cdm_mgr.nodes[i].data = NULL; + cdm_mgr.nodes[i].refcount = 0; + } + cdm_mgr.probe_done = false; + rc = 0; + +end: + mutex_unlock(&cam_cdm_mgr_lock); + return rc; +} + +static struct platform_driver cam_cdm_intf_driver = { + .probe = cam_cdm_intf_probe, + .remove = cam_cdm_intf_remove, + .driver = { + .name = "msm_cam_cdm_intf", + .owner = THIS_MODULE, + .of_match_table = msm_cam_cdm_intf_dt_match, + }, +}; + +static int __init cam_cdm_intf_init_module(void) +{ + return platform_driver_register(&cam_cdm_intf_driver); +} + +static void __exit cam_cdm_intf_exit_module(void) +{ + platform_driver_unregister(&cam_cdm_intf_driver); +} + +module_init(cam_cdm_intf_init_module); +module_exit(cam_cdm_intf_exit_module); +MODULE_DESCRIPTION("MSM Camera CDM Intf driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf_api.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf_api.h new file mode 100644 index 000000000000..6aa6e6d2bc4c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_intf_api.h @@ -0,0 +1,209 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CDM_API_H_ +#define _CAM_CDM_API_H_ + +#include +#include "cam_cdm_util.h" +#include "cam_soc_util.h" + +/* enum cam_cdm_id - Enum for possible CAM CDM hardwares */ +enum cam_cdm_id { + CAM_CDM_VIRTUAL, + CAM_CDM_HW_ANY, + CAM_CDM_CPAS_0, + CAM_CDM_IPE0, + CAM_CDM_IPE1, + CAM_CDM_BPS, + CAM_CDM_VFE, + CAM_CDM_MAX +}; + +/* enum cam_cdm_cb_status - Enum for possible CAM CDM callback */ +enum cam_cdm_cb_status { + CAM_CDM_CB_STATUS_BL_SUCCESS, + CAM_CDM_CB_STATUS_INVALID_BL_CMD, + CAM_CDM_CB_STATUS_PAGEFAULT, + CAM_CDM_CB_STATUS_HW_RESET_ONGOING, + CAM_CDM_CB_STATUS_HW_RESET_DONE, + CAM_CDM_CB_STATUS_UNKNOWN_ERROR, +}; + +/* enum cam_cdm_bl_cmd_addr_type - Enum for possible CDM bl cmd addr types */ +enum cam_cdm_bl_cmd_addr_type { + CAM_CDM_BL_CMD_TYPE_MEM_HANDLE, + CAM_CDM_BL_CMD_TYPE_HW_IOVA, + CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA, +}; + +/** + * struct cam_cdm_acquire_data - Cam CDM acquire data structure + * + * @identifier : Input identifier string which is the device label from dt + * like vfe, ife, jpeg etc + * @cell_index : Input integer identifier pointing to the cell index from dt + * of the device. This can be used to form a unique string + * with @identifier like vfe0, ife1, jpeg0 etc + * @id : ID of a specific or any CDM HW which needs to be acquired. + * @userdata : Input private data which will be returned as part + * of callback. + * @cam_cdm_callback : Input callback pointer for triggering the + * callbacks from CDM driver + * @handle : CDM Client handle + * @userdata : Private data given at the time of acquire + * @status : Callback status + * @cookie : Cookie if the callback is gen irq status + * @base_array_cnt : Input number of ioremapped address pair pointing + * in base_array, needed only if selected cdm is a virtual. + * @base_array : Input pointer to ioremapped address pair arrary + * needed only if selected cdm is a virtual. + * @cdm_version : CDM version is output while acquiring HW cdm and + * it is Input while acquiring virtual cdm, Currently fixing it + * to one version below acquire API. + * @ops : Output pointer updated by cdm driver to the CDM + * util ops for this HW version of CDM acquired. + * @handle : Output Unique handle generated for this acquire + * + */ +struct cam_cdm_acquire_data { + char identifier[128]; + uint32_t cell_index; + enum cam_cdm_id id; + void *userdata; + void (*cam_cdm_callback)(uint32_t handle, void *userdata, + enum cam_cdm_cb_status status, uint64_t cookie); + uint32_t base_array_cnt; + struct cam_soc_reg_map *base_array[CAM_SOC_MAX_BLOCK]; + struct cam_hw_version cdm_version; + struct cam_cdm_utils_ops *ops; + uint32_t handle; +}; + +/** + * struct cam_cdm_bl_cmd - Cam CDM HW bl command + * + * @bl_addr : Union of all three type for CDM BL commands + * @mem_handle : Input mem handle of bl cmd + * @offset : Input offset of the actual bl cmd in the memory pointed + * by mem_handle + * @len : Input length of the BL command, Cannot be more than 1MB and + * this is will be validated with offset+size of the memory pointed + * by mem_handle + * + */ +struct cam_cdm_bl_cmd { + union { + int32_t mem_handle; + uint32_t *hw_iova; + uintptr_t kernel_iova; + } bl_addr; + uint32_t offset; + uint32_t len; +}; + +/** + * struct cam_cdm_bl_request - Cam CDM HW base & length (BL) request + * + * @flag : 1 for callback needed and 0 for no callback when this BL + * request is done + * @userdata :Input private data which will be returned as part + * of callback if request for this bl request in flags. + * @cookie : Cookie if the callback is gen irq status + * @type : type of the submitted bl cmd address. + * @cmd_arrary_count : Input number of BL commands to be submitted to CDM + * @bl_cmd_array : Input payload holding the BL cmd's arrary + * to be sumbitted. + * + */ +struct cam_cdm_bl_request { + int flag; + void *userdata; + uint64_t cookie; + enum cam_cdm_bl_cmd_addr_type type; + uint32_t cmd_arrary_count; + struct cam_cdm_bl_cmd cmd[1]; +}; + +/** + * @brief : API to get the CDM capabilities for a camera device type + * + * @identifier : Input pointer to a string which is the device label from dt + * like vfe, ife, jpeg etc, We do not need cell index + * assuming all devices of a single type maps to one SMMU + * client + * @cdm_handles : Input iommu handle memory pointer to update handles + * + * @return 0 on success + */ +int cam_cdm_get_iommu_handle(char *identifier, + struct cam_iommu_handle *cdm_handles); + +/** + * @brief : API to acquire a CDM + * + * @data : Input data for the CDM to be acquired + * + * @return 0 on success + */ +int cam_cdm_acquire(struct cam_cdm_acquire_data *data); + +/** + * @brief : API to release a previously acquired CDM + * + * @handle : Input handle for the CDM to be released + * + * @return 0 on success + */ +int cam_cdm_release(uint32_t handle); + +/** + * @brief : API to submit the base & length (BL's) for acquired CDM + * + * @handle : Input cdm handle to which the BL's needs to be sumbitted. + * @data : Input pointer to the BL's to be sumbitted + * + * @return 0 on success + */ +int cam_cdm_submit_bls(uint32_t handle, struct cam_cdm_bl_request *data); + +/** + * @brief : API to stream ON a previously acquired CDM, + * during this we turn on/off clocks/power based on active clients. + * + * @handle : Input handle for the CDM to be released + * + * @return 0 on success + */ +int cam_cdm_stream_on(uint32_t handle); + +/** + * @brief : API to stream OFF a previously acquired CDM, + * during this we turn on/off clocks/power based on active clients. + * + * @handle : Input handle for the CDM to be released + * + * @return 0 on success + */ +int cam_cdm_stream_off(uint32_t handle); + +/** + * @brief : API to reset previously acquired CDM, + * this can be only performed only the CDM is private. + * + * @handle : Input handle of the CDM to reset + * + * @return 0 on success + */ +int cam_cdm_reset_hw(uint32_t handle); + +#endif /* _CAM_CDM_API_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.c new file mode 100644 index 000000000000..92ab9a7df4a7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.c @@ -0,0 +1,206 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_soc_util.h" +#include "cam_smmu_api.h" +#include "cam_cdm.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" + +#define CAM_CDM_OFFSET_FROM_REG(x, y) ((x)->offsets[y].offset) +#define CAM_CDM_ATTR_FROM_REG(x, y) ((x)->offsets[y].attribute) + +bool cam_cdm_read_hw_reg(struct cam_hw_info *cdm_hw, + enum cam_cdm_regs reg, uint32_t *value) +{ + void __iomem *reg_addr; + struct cam_cdm *cdm = (struct cam_cdm *)cdm_hw->core_info; + void __iomem *base = + cdm_hw->soc_info.reg_map[CAM_HW_CDM_BASE_INDEX].mem_base; + resource_size_t mem_len = + cdm_hw->soc_info.reg_map[CAM_HW_CDM_BASE_INDEX].size; + + CAM_DBG(CAM_CDM, "E: b=%pK blen=%d reg=%x off=%x", (void __iomem *)base, + (int)mem_len, reg, (CAM_CDM_OFFSET_FROM_REG(cdm->offset_tbl, + reg))); + CAM_DBG(CAM_CDM, "E: b=%pK reg=%x off=%x", (void __iomem *)base, + reg, (CAM_CDM_OFFSET_FROM_REG(cdm->offset_tbl, reg))); + + if ((reg > cdm->offset_tbl->offset_max_size) || + (reg > cdm->offset_tbl->last_offset)) { + CAM_ERR_RATE_LIMIT(CAM_CDM, "Invalid reg=%d\n", reg); + goto permission_error; + } else { + reg_addr = (base + (CAM_CDM_OFFSET_FROM_REG( + cdm->offset_tbl, reg))); + if (reg_addr > (base + mem_len)) { + CAM_ERR_RATE_LIMIT(CAM_CDM, + "Invalid mapped region %d", reg); + goto permission_error; + } + *value = cam_io_r_mb(reg_addr); + CAM_DBG(CAM_CDM, "X b=%pK reg=%x off=%x val=%x", + (void __iomem *)base, reg, + (CAM_CDM_OFFSET_FROM_REG(cdm->offset_tbl, reg)), + *value); + return false; + } +permission_error: + *value = 0; + return true; + +} + +bool cam_cdm_write_hw_reg(struct cam_hw_info *cdm_hw, + enum cam_cdm_regs reg, uint32_t value) +{ + void __iomem *reg_addr; + struct cam_cdm *cdm = (struct cam_cdm *)cdm_hw->core_info; + void __iomem *base = + cdm_hw->soc_info.reg_map[CAM_HW_CDM_BASE_INDEX].mem_base; + resource_size_t mem_len = + cdm_hw->soc_info.reg_map[CAM_HW_CDM_BASE_INDEX].size; + + CAM_DBG(CAM_CDM, "E: b=%pK reg=%x off=%x val=%x", (void __iomem *)base, + reg, (CAM_CDM_OFFSET_FROM_REG(cdm->offset_tbl, reg)), value); + + if ((reg > cdm->offset_tbl->offset_max_size) || + (reg > cdm->offset_tbl->last_offset)) { + CAM_ERR_RATE_LIMIT(CAM_CDM, "CDM accessing invalid reg=%d\n", + reg); + goto permission_error; + } else { + reg_addr = (base + CAM_CDM_OFFSET_FROM_REG( + cdm->offset_tbl, reg)); + if (reg_addr > (base + mem_len)) { + CAM_ERR_RATE_LIMIT(CAM_CDM, + "Accessing invalid region %d:%d\n", + reg, (CAM_CDM_OFFSET_FROM_REG( + cdm->offset_tbl, reg))); + goto permission_error; + } + cam_io_w_mb(value, reg_addr); + return false; + } +permission_error: + return true; + +} + +int cam_cdm_soc_load_dt_private(struct platform_device *pdev, + struct cam_cdm_private_dt_data *ptr) +{ + int i, rc = -EINVAL; + + ptr->dt_num_supported_clients = of_property_count_strings( + pdev->dev.of_node, + "cdm-client-names"); + CAM_DBG(CAM_CDM, "Num supported cdm_client = %d", + ptr->dt_num_supported_clients); + if (ptr->dt_num_supported_clients > + CAM_PER_CDM_MAX_REGISTERED_CLIENTS) { + CAM_ERR(CAM_CDM, "Invalid count of client names count=%d", + ptr->dt_num_supported_clients); + rc = -EINVAL; + return rc; + } + if (ptr->dt_num_supported_clients < 0) { + CAM_DBG(CAM_CDM, "No cdm client names found"); + ptr->dt_num_supported_clients = 0; + ptr->dt_cdm_shared = false; + } else { + ptr->dt_cdm_shared = true; + } + for (i = 0; i < ptr->dt_num_supported_clients; i++) { + rc = of_property_read_string_index(pdev->dev.of_node, + "cdm-client-names", i, &(ptr->dt_cdm_client_name[i])); + CAM_DBG(CAM_CDM, "cdm-client-names[%d] = %s", i, + ptr->dt_cdm_client_name[i]); + if (rc < 0) { + CAM_ERR(CAM_CDM, "Reading cdm-client-names failed"); + break; + } + } + + return rc; +} + +int cam_hw_cdm_soc_get_dt_properties(struct cam_hw_info *cdm_hw, + const struct of_device_id *table) +{ + int rc; + struct cam_hw_soc_info *soc_ptr; + const struct of_device_id *id; + + if (!cdm_hw || (cdm_hw->soc_info.soc_private) + || !(cdm_hw->soc_info.pdev)) + return -EINVAL; + + soc_ptr = &cdm_hw->soc_info; + + rc = cam_soc_util_get_dt_properties(soc_ptr); + if (rc != 0) { + CAM_ERR(CAM_CDM, "Failed to retrieve the CDM dt properties"); + } else { + soc_ptr->soc_private = kzalloc( + sizeof(struct cam_cdm_private_dt_data), + GFP_KERNEL); + if (!soc_ptr->soc_private) + return -ENOMEM; + + rc = cam_cdm_soc_load_dt_private(soc_ptr->pdev, + soc_ptr->soc_private); + if (rc != 0) { + CAM_ERR(CAM_CDM, "Failed to load CDM dt private data"); + goto error; + } + id = of_match_node(table, soc_ptr->pdev->dev.of_node); + if ((!id) || !(id->data)) { + CAM_ERR(CAM_CDM, "Failed to retrieve the CDM id table"); + goto error; + } + CAM_DBG(CAM_CDM, "CDM Hw Id compatible =%s", id->compatible); + ((struct cam_cdm *)cdm_hw->core_info)->offset_tbl = + (struct cam_cdm_reg_offset_table *)id->data; + strlcpy(((struct cam_cdm *)cdm_hw->core_info)->name, + id->compatible, + sizeof(((struct cam_cdm *)cdm_hw->core_info)->name)); + } + + return rc; + +error: + rc = -EINVAL; + kfree(soc_ptr->soc_private); + soc_ptr->soc_private = NULL; + return rc; +} + +int cam_cdm_intf_mgr_soc_get_dt_properties( + struct platform_device *pdev, struct cam_cdm_intf_mgr *mgr) +{ + int rc; + + rc = of_property_read_u32(pdev->dev.of_node, + "num-hw-cdm", &mgr->dt_supported_hw_cdm); + CAM_DBG(CAM_CDM, "Number of HW cdm supported =%d", + mgr->dt_supported_hw_cdm); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.h new file mode 100644 index 000000000000..765aba4d5bac --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_soc.h @@ -0,0 +1,28 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CDM_SOC_H_ +#define _CAM_CDM_SOC_H_ + +int cam_hw_cdm_soc_get_dt_properties(struct cam_hw_info *cdm_hw, + const struct of_device_id *table); +bool cam_cdm_read_hw_reg(struct cam_hw_info *cdm_hw, + enum cam_cdm_regs reg, uint32_t *value); +bool cam_cdm_write_hw_reg(struct cam_hw_info *cdm_hw, + enum cam_cdm_regs reg, uint32_t value); +int cam_cdm_intf_mgr_soc_get_dt_properties( + struct platform_device *pdev, + struct cam_cdm_intf_mgr *mgr); +int cam_cdm_soc_load_dt_private(struct platform_device *pdev, + struct cam_cdm_private_dt_data *ptr); + +#endif /* _CAM_CDM_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.c new file mode 100644 index 000000000000..c8b830ff82ea --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.c @@ -0,0 +1,542 @@ +/* Copyright (c) 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 +#include +#include +#include + +#include "cam_cdm_intf_api.h" +#include "cam_cdm_util.h" +#include "cam_cdm.h" +#include "cam_io_util.h" + +#define CAM_CDM_DWORD 4 + +static unsigned int CDMCmdHeaderSizes[ + CAM_CDM_CMD_PRIVATE_BASE + CAM_CDM_SW_CMD_COUNT] = { + 0, /* UNUSED*/ + 3, /* DMI*/ + 0, /* UNUSED*/ + 2, /* RegContinuous*/ + 1, /* RegRandom*/ + 2, /* BUFFER_INDIREC*/ + 2, /* GenerateIRQ*/ + 3, /* WaitForEvent*/ + 1, /* ChangeBase*/ + 1, /* PERF_CONTINUOUSROL*/ + 3, /* DMI32*/ + 3, /* DMI64*/ +}; + +/** + * struct cdm_regrandom_cmd - Definition for CDM random register command. + * @count: Number of register writes + * @reserved: reserved bits + * @cmd: Command ID (CDMCmd) + */ +struct cdm_regrandom_cmd { + unsigned int count : 16; + unsigned int reserved : 8; + unsigned int cmd : 8; +} __attribute__((__packed__)); + +/** + * struct cdm_regcontinuous_cmd - Definition for a CDM register range command. + * @count: Number of register writes + * @reserved0: reserved bits + * @cmd: Command ID (CDMCmd) + * @offset: Start address of the range of registers + * @reserved1: reserved bits + */ +struct cdm_regcontinuous_cmd { + unsigned int count : 16; + unsigned int reserved0 : 8; + unsigned int cmd : 8; + unsigned int offset : 24; + unsigned int reserved1 : 8; +} __attribute__((__packed__)); + +/** + * struct cdm_dmi_cmd - Definition for a CDM DMI command. + * @length: Number of bytes in LUT - 1 + * @reserved: reserved bits + * @cmd: Command ID (CDMCmd) + * @addr: Address of the LUT in memory + * @DMIAddr: Address of the target DMI config register + * @DMISel: DMI identifier + */ +struct cdm_dmi_cmd { + unsigned int length : 16; + unsigned int reserved : 8; + unsigned int cmd : 8; + unsigned int addr; + unsigned int DMIAddr : 24; + unsigned int DMISel : 8; +} __attribute__((__packed__)); + +/** + * struct cdm_indirect_cmd - Definition for a CDM indirect buffer command. + * @length: Number of bytes in buffer - 1 + * @reserved: reserved bits + * @cmd: Command ID (CDMCmd) + * @addr: Device address of the indirect buffer + */ +struct cdm_indirect_cmd { + unsigned int length : 16; + unsigned int reserved : 8; + unsigned int cmd : 8; + unsigned int addr; +} __attribute__((__packed__)); + +/** + * struct cdm_changebase_cmd - Definition for CDM base address change command. + * @base: Base address to be changed to + * @cmd:Command ID (CDMCmd) + */ +struct cdm_changebase_cmd { + unsigned int base : 24; + unsigned int cmd : 8; +} __attribute__((__packed__)); + +/** + * struct cdm_wait_event_cmd - Definition for a CDM Gen IRQ command. + * @mask: Mask for the events + * @id: ID to read back for debug + * @iw_reserved: reserved bits + * @iw: iw AHB write bit + * @cmd:Command ID (CDMCmd) + * @offset: Offset to where data is written + * @offset_reserved: reserved bits + * @data: data returned in IRQ_USR_DATA + */ +struct cdm_wait_event_cmd { + unsigned int mask : 8; + unsigned int id : 8; + unsigned int iw_reserved : 7; + unsigned int iw : 1; + unsigned int cmd : 8; + unsigned int offset : 24; + unsigned int offset_reserved : 8; + unsigned int data; +} __attribute__((__packed__)); + +/** + * struct cdm_genirq_cmd - Definition for a CDM Wait event command. + * @reserved: reserved bits + * @cmd:Command ID (CDMCmd) + * @userdata: userdata returned in IRQ_USR_DATA + */ +struct cdm_genirq_cmd { + unsigned int reserved : 24; + unsigned int cmd : 8; + unsigned int userdata; +} __attribute__((__packed__)); + +/** + * struct cdm_perf_ctrl_cmd_t - Definition for CDM perf control command. + * @perf: perf command + * @reserved: reserved bits + * @cmd:Command ID (CDMCmd) + */ +struct cdm_perf_ctrl_cmd { + unsigned int perf : 2; + unsigned int reserved : 22; + unsigned int cmd : 8; +} __attribute__((__packed__)); + +uint32_t cdm_get_cmd_header_size(unsigned int command) +{ + return CDMCmdHeaderSizes[command]; +} + +uint32_t cdm_required_size_reg_continuous(uint32_t numVals) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT) + numVals; +} + +uint32_t cdm_required_size_reg_random(uint32_t numRegVals) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM) + + (2 * numRegVals); +} + +uint32_t cdm_required_size_dmi(void) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_DMI); +} + +uint32_t cdm_required_size_genirq(void) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_GEN_IRQ); +} + +uint32_t cdm_required_size_indirect(void) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_BUFF_INDIRECT); +} + +uint32_t cdm_required_size_changebase(void) +{ + return cdm_get_cmd_header_size(CAM_CDM_CMD_CHANGE_BASE); +} + +uint32_t cdm_offsetof_dmi_addr(void) +{ + return offsetof(struct cdm_dmi_cmd, addr); +} + +uint32_t cdm_offsetof_indirect_addr(void) +{ + return offsetof(struct cdm_indirect_cmd, addr); +} + +uint32_t *cdm_write_regcontinuous(uint32_t *pCmdBuffer, uint32_t reg, + uint32_t numVals, uint32_t *pVals) +{ + uint32_t i; + struct cdm_regcontinuous_cmd *pHeader = + (struct cdm_regcontinuous_cmd *)pCmdBuffer; + + pHeader->count = numVals; + pHeader->cmd = CAM_CDM_CMD_REG_CONT; + pHeader->reserved0 = 0; + pHeader->reserved1 = 0; + pHeader->offset = reg; + + pCmdBuffer += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); + + for (i = 0; i < numVals; i++) + (((uint32_t *)pCmdBuffer)[i]) = (((uint32_t *)pVals)[i]); + + pCmdBuffer += numVals; + + return pCmdBuffer; +} + +uint32_t *cdm_write_regrandom(uint32_t *pCmdBuffer, uint32_t numRegVals, + uint32_t *pRegVals) +{ + uint32_t i; + uint32_t *dst, *src; + struct cdm_regrandom_cmd *pHeader = + (struct cdm_regrandom_cmd *)pCmdBuffer; + + pHeader->count = numRegVals; + pHeader->cmd = CAM_CDM_CMD_REG_RANDOM; + pHeader->reserved = 0; + + pCmdBuffer += cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); + dst = pCmdBuffer; + src = pRegVals; + for (i = 0; i < numRegVals; i++) { + *dst++ = *src++; + *dst++ = *src++; + } + + return dst; +} + +uint32_t *cdm_write_dmi(uint32_t *pCmdBuffer, uint8_t dmiCmd, + uint32_t DMIAddr, uint8_t DMISel, uint32_t dmiBufferAddr, + uint32_t length) +{ + struct cdm_dmi_cmd *pHeader = (struct cdm_dmi_cmd *)pCmdBuffer; + + pHeader->cmd = dmiCmd; + pHeader->addr = dmiBufferAddr; + pHeader->length = length - 1; + pHeader->DMIAddr = DMIAddr; + pHeader->DMISel = DMISel; + + pCmdBuffer += cdm_get_cmd_header_size(CAM_CDM_CMD_DMI); + + return pCmdBuffer; +} + +uint32_t *cdm_write_indirect(uint32_t *pCmdBuffer, uint32_t indirectBufAddr, + uint32_t length) +{ + struct cdm_indirect_cmd *pHeader = + (struct cdm_indirect_cmd *)pCmdBuffer; + + pHeader->cmd = CAM_CDM_CMD_BUFF_INDIRECT; + pHeader->addr = indirectBufAddr; + pHeader->length = length - 1; + + pCmdBuffer += cdm_get_cmd_header_size(CAM_CDM_CMD_BUFF_INDIRECT); + + return pCmdBuffer; +} + +uint32_t *cdm_write_changebase(uint32_t *pCmdBuffer, uint32_t base) +{ + struct cdm_changebase_cmd *pHeader = + (struct cdm_changebase_cmd *)pCmdBuffer; + + pHeader->cmd = CAM_CDM_CMD_CHANGE_BASE; + pHeader->base = base; + pCmdBuffer += cdm_get_cmd_header_size(CAM_CDM_CMD_CHANGE_BASE); + + return pCmdBuffer; +} + +void cdm_write_genirq(uint32_t *pCmdBuffer, uint32_t userdata) +{ + struct cdm_genirq_cmd *pHeader = (struct cdm_genirq_cmd *)pCmdBuffer; + + pHeader->cmd = CAM_CDM_CMD_GEN_IRQ; + pHeader->userdata = userdata; +} + +struct cam_cdm_utils_ops CDM170_ops = { + cdm_get_cmd_header_size, + cdm_required_size_reg_continuous, + cdm_required_size_reg_random, + cdm_required_size_dmi, + cdm_required_size_genirq, + cdm_required_size_indirect, + cdm_required_size_changebase, + cdm_offsetof_dmi_addr, + cdm_offsetof_indirect_addr, + cdm_write_regcontinuous, + cdm_write_regrandom, + cdm_write_dmi, + cdm_write_indirect, + cdm_write_changebase, + cdm_write_genirq, +}; + +int cam_cdm_get_ioremap_from_base(uint32_t hw_base, + uint32_t base_array_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK], + void __iomem **device_base) +{ + int ret = -EINVAL, i; + + for (i = 0; i < base_array_size; i++) { + if (base_table[i]) + CAM_DBG(CAM_CDM, "In loop %d ioremap for %x addr=%x", + i, (base_table[i])->mem_cam_base, hw_base); + if ((base_table[i]) && + ((base_table[i])->mem_cam_base == hw_base)) { + *device_base = (base_table[i])->mem_base; + ret = 0; + break; + } + } + + return ret; +} + +static int cam_cdm_util_reg_cont_write(void __iomem *base_addr, + uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes) +{ + int ret = 0; + uint32_t *data; + struct cdm_regcontinuous_cmd *reg_cont; + + if ((cmd_buf_size < cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) || + (!base_addr)) { + CAM_ERR(CAM_CDM, "invalid base addr and data length %d %pK", + cmd_buf_size, base_addr); + return -EINVAL; + } + + reg_cont = (struct cdm_regcontinuous_cmd *)cmd_buf; + if ((!reg_cont->count) || (reg_cont->count > 0x10000) || + (((reg_cont->count * sizeof(uint32_t)) + + cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) > + cmd_buf_size)) { + CAM_ERR(CAM_CDM, "buffer size %d is not sufficient for count%d", + cmd_buf_size, reg_cont->count); + return -EINVAL; + } + data = cmd_buf + cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); + cam_io_memcpy(base_addr + reg_cont->offset, data, + reg_cont->count * sizeof(uint32_t)); + + *used_bytes = (reg_cont->count * sizeof(uint32_t)) + + (4 * cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)); + + return ret; +} + +static int cam_cdm_util_reg_random_write(void __iomem *base_addr, + uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes) +{ + uint32_t i; + struct cdm_regrandom_cmd *reg_random; + uint32_t *data; + + if (!base_addr) { + CAM_ERR(CAM_CDM, "invalid base address"); + return -EINVAL; + } + + reg_random = (struct cdm_regrandom_cmd *) cmd_buf; + if ((!reg_random->count) || (reg_random->count > 0x10000) || + (((reg_random->count * (sizeof(uint32_t) * 2)) + + cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)) > + cmd_buf_size)) { + CAM_ERR(CAM_CDM, "invalid reg_count %d cmd_buf_size %d", + reg_random->count, cmd_buf_size); + return -EINVAL; + } + data = cmd_buf + cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); + + for (i = 0; i < reg_random->count; i++) { + CAM_DBG(CAM_CDM, "reg random: offset %pK, value 0x%x", + ((void __iomem *)(base_addr + data[0])), + data[1]); + cam_io_w(data[1], base_addr + data[0]); + data += 2; + } + + *used_bytes = ((reg_random->count * (sizeof(uint32_t) * 2)) + + (4 * cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM))); + + return 0; +} + +static int cam_cdm_util_swd_dmi_write(uint32_t cdm_cmd_type, + void __iomem *base_addr, uint32_t *cmd_buf, uint32_t cmd_buf_size, + uint32_t *used_bytes) +{ + uint32_t i; + struct cdm_dmi_cmd *swd_dmi; + uint32_t *data; + + swd_dmi = (struct cdm_dmi_cmd *)cmd_buf; + + if (cmd_buf_size < (cdm_required_size_dmi() + swd_dmi->length + 1)) { + CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d", + swd_dmi->length + 1); + return -EINVAL; + } + data = cmd_buf + cdm_required_size_dmi(); + + if (cdm_cmd_type == CAM_CDM_CMD_SWD_DMI_64) { + for (i = 0; i < (swd_dmi->length + 1)/8; i++) { + cam_io_w_mb(data[0], base_addr + + swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + cam_io_w_mb(data[1], base_addr + + swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET); + data += 2; + } + } else { + for (i = 0; i < (swd_dmi->length + 1)/4; i++) { + cam_io_w_mb(data[0], base_addr + + swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + data += 1; + } + } + *used_bytes = (4 * cdm_required_size_dmi()) + swd_dmi->length + 1; + + return 0; +} + +int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, + uint32_t *cmd_buf, uint32_t cmd_buf_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK], + uint32_t base_array_size, uint8_t bl_tag) +{ + int ret = 0; + uint32_t cdm_cmd_type = 0, total_cmd_buf_size = 0; + uint32_t used_bytes = 0; + + total_cmd_buf_size = cmd_buf_size; + + while (cmd_buf_size > 0) { + CAM_DBG(CAM_CDM, "cmd data=%x", *cmd_buf); + cdm_cmd_type = (*cmd_buf >> CAM_CDM_COMMAND_OFFSET); + switch (cdm_cmd_type) { + case CAM_CDM_CMD_REG_CONT: { + ret = cam_cdm_util_reg_cont_write(*current_device_base, + cmd_buf, cmd_buf_size, &used_bytes); + if (ret) + break; + + if (used_bytes > 0) { + cmd_buf_size -= used_bytes; + cmd_buf += used_bytes/4; + } + } + break; + case CAM_CDM_CMD_REG_RANDOM: { + ret = cam_cdm_util_reg_random_write( + *current_device_base, cmd_buf, cmd_buf_size, + &used_bytes); + if (ret) + break; + + if (used_bytes > 0) { + cmd_buf_size -= used_bytes; + cmd_buf += used_bytes / 4; + } + } + break; + case CAM_CDM_CMD_SWD_DMI_32: + case CAM_CDM_CMD_SWD_DMI_64: { + if (*current_device_base == 0) { + CAM_ERR(CAM_CDM, + "Got SWI DMI cmd =%d for invalid hw", + cdm_cmd_type); + ret = -EINVAL; + break; + } + ret = cam_cdm_util_swd_dmi_write(cdm_cmd_type, + *current_device_base, cmd_buf, cmd_buf_size, + &used_bytes); + if (ret) + break; + + if (used_bytes > 0) { + cmd_buf_size -= used_bytes; + cmd_buf += used_bytes / 4; + } + } + break; + case CAM_CDM_CMD_CHANGE_BASE: { + struct cdm_changebase_cmd *change_base_cmd = + (struct cdm_changebase_cmd *)cmd_buf; + + ret = cam_cdm_get_ioremap_from_base( + change_base_cmd->base, base_array_size, + base_table, current_device_base); + if (ret != 0) { + CAM_ERR(CAM_CDM, + "Get ioremap change base failed %x", + change_base_cmd->base); + break; + } + CAM_DBG(CAM_CDM, "Got ioremap for %x addr=%pK", + change_base_cmd->base, + current_device_base); + cmd_buf_size -= (4 * + cdm_required_size_changebase()); + cmd_buf += cdm_required_size_changebase(); + } + break; + default: + CAM_ERR(CAM_CDM, "unsupported cdm_cmd_type type 0%x", + cdm_cmd_type); + ret = -EINVAL; + break; + } + + if (ret < 0) + break; + } + + return ret; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.h new file mode 100644 index 000000000000..09d0d638c987 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_util.h @@ -0,0 +1,161 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CDM_UTIL_H_ +#define _CAM_CDM_UTIL_H_ + +#define CAM_CDM_SW_CMD_COUNT 2 +#define CAM_CMD_LENGTH_MASK 0xFFFF +#define CAM_CDM_COMMAND_OFFSET 24 + +#define CAM_CDM_DMI_DATA_HI_OFFSET 8 +#define CAM_CDM_DMI_DATA_LO_OFFSET 12 + +enum cam_cdm_command { + CAM_CDM_CMD_UNUSED = 0x0, + CAM_CDM_CMD_DMI = 0x1, + CAM_CDM_CMD_NOT_DEFINED = 0x2, + CAM_CDM_CMD_REG_CONT = 0x3, + CAM_CDM_CMD_REG_RANDOM = 0x4, + CAM_CDM_CMD_BUFF_INDIRECT = 0x5, + CAM_CDM_CMD_GEN_IRQ = 0x6, + CAM_CDM_CMD_WAIT_EVENT = 0x7, + CAM_CDM_CMD_CHANGE_BASE = 0x8, + CAM_CDM_CMD_PERF_CTRL = 0x9, + CAM_CDM_CMD_DMI_32 = 0xa, + CAM_CDM_CMD_DMI_64 = 0xb, + CAM_CDM_CMD_PRIVATE_BASE = 0xc, + CAM_CDM_CMD_SWD_DMI_32 = (CAM_CDM_CMD_PRIVATE_BASE + 0x64), + CAM_CDM_CMD_SWD_DMI_64 = (CAM_CDM_CMD_PRIVATE_BASE + 0x65), + CAM_CDM_CMD_PRIVATE_BASE_MAX = 0x7F +}; + +/** + * struct cam_cdm_utils_ops - Camera CDM util ops + * + * @cdm_get_cmd_header_size: Returns the size of the given command header + * in DWORDs. + * @command Command ID + * @return Size of the command in DWORDs + * + * @cdm_required_size_reg_continuous: Calculates the size of a reg-continuous + * command in dwords. + * @numVals Number of continuous values + * @return Size in dwords + * + * @cdm_required_size_reg_random: Calculates the size of a reg-random command + * in dwords. + * @numRegVals Number of register/value pairs + * @return Size in dwords + * + * @cdm_required_size_dmi: Calculates the size of a DMI command in dwords. + * @return Size in dwords + * + * @cdm_required_size_genirq: Calculates size of a Genirq command in dwords. + * @return Size in dwords + * + * @cdm_required_size_indirect: Calculates the size of an indirect command + * in dwords. + * @return Size in dwords + * + * @cdm_required_size_changebase: Calculates the size of a change-base command + * in dwords. + * @return Size in dwords + * + * @cdm_offsetof_dmi_addr: Returns the offset of address field in the DMI + * command header. + * @return Offset of addr field + * + * @cdm_offsetof_indirect_addr: Returns the offset of address field in the + * indirect command header. + * @return Offset of addr field + * + * @cdm_write_regcontinuous: Writes a command into the command buffer. + * @pCmdBuffer: Pointer to command buffer + * @reg: Beginning of the register address range where + * values will be written. + * @numVals: Number of values (registers) that will be written + * @pVals : An array of values that will be written + * @return Pointer in command buffer pointing past the written commands + * + * @cdm_write_regrandom: Writes a command into the command buffer in + * register/value pairs. + * @pCmdBuffer: Pointer to command buffer + * @numRegVals: Number of register/value pairs that will be written + * @pRegVals: An array of register/value pairs that will be written + * The even indices are registers and the odd indices + * arevalues, e.g., {reg1, val1, reg2, val2, ...}. + * @return Pointer in command buffer pointing past the written commands + * + * @cdm_write_dmi: Writes a DMI command into the command bufferM. + * @pCmdBuffer: Pointer to command buffer + * @dmiCmd: DMI command + * @DMIAddr: Address of the DMI + * @DMISel: Selected bank that the DMI will write to + * @length: Size of data in bytes + * @return Pointer in command buffer pointing past the written commands + * + * @cdm_write_indirect: Writes a indirect command into the command buffer. + * @pCmdBuffer: Pointer to command buffer + * @indirectBufferAddr: Device address of the indirect cmd buffer. + * @length: Size of data in bytes + * @return Pointer in command buffer pointing past the written commands + * + * @cdm_write_changebase: Writes a changing CDM (address) base command into + * the command buffer. + * @pCmdBuffer: Pointer to command buffer + * @base: New base (device) address + * @return Pointer in command buffer pointing past the written commands + * + * @cdm_write_genirq: Writes a gen irq command into the command buffer. + * @pCmdBuffer: Pointer to command buffer + * @userdata: userdata or cookie return by hardware during irq. + */ +struct cam_cdm_utils_ops { +uint32_t (*cdm_get_cmd_header_size)(unsigned int command); +uint32_t (*cdm_required_size_reg_continuous)(uint32_t numVals); +uint32_t (*cdm_required_size_reg_random)(uint32_t numRegVals); +uint32_t (*cdm_required_size_dmi)(void); +uint32_t (*cdm_required_size_genirq)(void); +uint32_t (*cdm_required_size_indirect)(void); +uint32_t (*cdm_required_size_changebase)(void); +uint32_t (*cdm_offsetof_dmi_addr)(void); +uint32_t (*cdm_offsetof_indirect_addr)(void); +uint32_t* (*cdm_write_regcontinuous)( + uint32_t *pCmdBuffer, + uint32_t reg, + uint32_t numVals, + uint32_t *pVals); +uint32_t *(*cdm_write_regrandom)( + uint32_t *pCmdBuffer, + uint32_t numRegVals, + uint32_t *pRegVals); +uint32_t *(*cdm_write_dmi)( + uint32_t *pCmdBuffer, + uint8_t dmiCmd, + uint32_t DMIAddr, + uint8_t DMISel, + uint32_t dmiBufferAddr, + uint32_t length); +uint32_t *(*cdm_write_indirect)( + uint32_t *pCmdBuffer, + uint32_t indirectBufferAddr, + uint32_t length); +uint32_t *(*cdm_write_changebase)( + uint32_t *pCmdBuffer, + uint32_t base); +void (*cdm_write_genirq)( + uint32_t *pCmdBuffer, + uint32_t userdata); +}; + +#endif /* _CAM_CDM_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual.h new file mode 100644 index 000000000000..ed0721862a74 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CDM_VIRTUAL_H_ +#define _CAM_CDM_VIRTUAL_H_ + +#include "cam_cdm_intf_api.h" + +int cam_virtual_cdm_probe(struct platform_device *pdev); +int cam_virtual_cdm_remove(struct platform_device *pdev); +int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, + uint32_t *cmd_buf, uint32_t cmd_buf_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK], + uint32_t base_array_size, uint8_t bl_tag); + +#endif /* _CAM_CDM_VIRTUAL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual_core.c b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual_core.c new file mode 100644 index 000000000000..2ac2504c8bd5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_cdm_virtual_core.c @@ -0,0 +1,389 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_soc_util.h" +#include "cam_smmu_api.h" +#include "cam_cdm_intf_api.h" +#include "cam_cdm.h" +#include "cam_cdm_util.h" +#include "cam_cdm_virtual.h" +#include "cam_cdm_core_common.h" +#include "cam_cdm_soc.h" +#include "cam_io_util.h" + +#define CAM_CDM_VIRTUAL_NAME "qcom,cam_virtual_cdm" + +static void cam_virtual_cdm_work(struct work_struct *work) +{ + struct cam_cdm_work_payload *payload; + struct cam_hw_info *cdm_hw; + struct cam_cdm *core; + + payload = container_of(work, struct cam_cdm_work_payload, work); + if (payload) { + cdm_hw = payload->hw; + core = (struct cam_cdm *)cdm_hw->core_info; + if (payload->irq_status & 0x2) { + struct cam_cdm_bl_cb_request_entry *node; + + CAM_DBG(CAM_CDM, "CDM HW Gen/inline IRQ with data=%x", + payload->irq_data); + mutex_lock(&cdm_hw->hw_mutex); + node = cam_cdm_find_request_by_bl_tag( + payload->irq_data, + &core->bl_request_list); + if (node) { + if (node->request_type == + CAM_HW_CDM_BL_CB_CLIENT) { + cam_cdm_notify_clients(cdm_hw, + CAM_CDM_CB_STATUS_BL_SUCCESS, + (void *)node); + } else if (node->request_type == + CAM_HW_CDM_BL_CB_INTERNAL) { + CAM_ERR(CAM_CDM, "Invalid node=%pK %d", + node, node->request_type); + } + list_del_init(&node->entry); + kfree(node); + } else { + CAM_ERR(CAM_CDM, "Invalid node for inline irq"); + } + mutex_unlock(&cdm_hw->hw_mutex); + } + if (payload->irq_status & 0x1) { + CAM_DBG(CAM_CDM, "CDM HW reset done IRQ"); + complete(&core->reset_complete); + } + kfree(payload); + } + +} + +int cam_virtual_cdm_submit_bl(struct cam_hw_info *cdm_hw, + struct cam_cdm_hw_intf_cmd_submit_bl *req, + struct cam_cdm_client *client) +{ + int i, rc = -EINVAL; + struct cam_cdm_bl_request *cdm_cmd = req->data; + struct cam_cdm *core = (struct cam_cdm *)cdm_hw->core_info; + + mutex_lock(&client->lock); + for (i = 0; i < req->data->cmd_arrary_count ; i++) { + uintptr_t vaddr_ptr = 0; + size_t len = 0; + + if ((!cdm_cmd->cmd[i].len) && + (cdm_cmd->cmd[i].len > 0x100000)) { + CAM_ERR(CAM_CDM, + "len(%d) is invalid count=%d total cnt=%d", + cdm_cmd->cmd[i].len, i, + req->data->cmd_arrary_count); + rc = -EINVAL; + break; + } + if (req->data->type == CAM_CDM_BL_CMD_TYPE_MEM_HANDLE) { + rc = cam_mem_get_cpu_buf( + cdm_cmd->cmd[i].bl_addr.mem_handle, &vaddr_ptr, + &len); + } else if (req->data->type == + CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA) { + rc = 0; + vaddr_ptr = cdm_cmd->cmd[i].bl_addr.kernel_iova; + len = cdm_cmd->cmd[i].offset + cdm_cmd->cmd[i].len; + } else { + CAM_ERR(CAM_CDM, + "Only mem hdl/Kernel va type is supported %d", + req->data->type); + rc = -EINVAL; + break; + } + + if ((!rc) && (vaddr_ptr) && (len) && + (len >= cdm_cmd->cmd[i].offset)) { + + + if ((len - cdm_cmd->cmd[i].offset) < + cdm_cmd->cmd[i].len) { + CAM_ERR(CAM_CDM, "Not enough buffer"); + rc = -EINVAL; + break; + } + CAM_DBG(CAM_CDM, + "hdl=%x vaddr=%pK offset=%d cmdlen=%d:%zu", + cdm_cmd->cmd[i].bl_addr.mem_handle, + (void *)vaddr_ptr, cdm_cmd->cmd[i].offset, + cdm_cmd->cmd[i].len, len); + rc = cam_cdm_util_cmd_buf_write( + &client->changebase_addr, + ((uint32_t *)vaddr_ptr + + ((cdm_cmd->cmd[i].offset)/4)), + cdm_cmd->cmd[i].len, client->data.base_array, + client->data.base_array_cnt, core->bl_tag); + if (rc) { + CAM_ERR(CAM_CDM, + "write failed for cnt=%d:%d len %u", + i, req->data->cmd_arrary_count, + cdm_cmd->cmd[i].len); + break; + } + } else { + CAM_ERR(CAM_CDM, + "Sanity check failed for hdl=%x len=%zu:%d", + cdm_cmd->cmd[i].bl_addr.mem_handle, len, + cdm_cmd->cmd[i].offset); + CAM_ERR(CAM_CDM, + "Sanity check failed for cmd_count=%d cnt=%d", + i, req->data->cmd_arrary_count); + rc = -EINVAL; + break; + } + if (!rc) { + struct cam_cdm_work_payload *payload; + + CAM_DBG(CAM_CDM, + "write BL success for cnt=%d with tag=%d", + i, core->bl_tag); + if ((true == req->data->flag) && + (i == req->data->cmd_arrary_count)) { + struct cam_cdm_bl_cb_request_entry *node; + + node = kzalloc(sizeof( + struct cam_cdm_bl_cb_request_entry), + GFP_KERNEL); + if (!node) { + rc = -ENOMEM; + break; + } + node->request_type = CAM_HW_CDM_BL_CB_CLIENT; + node->client_hdl = req->handle; + node->cookie = req->data->cookie; + node->bl_tag = core->bl_tag; + node->userdata = req->data->userdata; + mutex_lock(&cdm_hw->hw_mutex); + list_add_tail(&node->entry, + &core->bl_request_list); + mutex_unlock(&cdm_hw->hw_mutex); + + payload = kzalloc(sizeof( + struct cam_cdm_work_payload), + GFP_ATOMIC); + if (payload) { + payload->irq_status = 0x2; + payload->irq_data = core->bl_tag; + payload->hw = cdm_hw; + INIT_WORK((struct work_struct *) + &payload->work, + cam_virtual_cdm_work); + queue_work(core->work_queue, + &payload->work); + } + } + core->bl_tag++; + CAM_DBG(CAM_CDM, + "Now commit the BL nothing for virtual"); + if (!rc && (core->bl_tag == 63)) + core->bl_tag = 0; + } + } + mutex_unlock(&client->lock); + return rc; +} + +int cam_virtual_cdm_probe(struct platform_device *pdev) +{ + struct cam_hw_info *cdm_hw = NULL; + struct cam_hw_intf *cdm_hw_intf = NULL; + struct cam_cdm *cdm_core = NULL; + struct cam_cdm_private_dt_data *soc_private = NULL; + int rc; + struct cam_cpas_register_params cpas_parms; + + cdm_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!cdm_hw_intf) + return -ENOMEM; + + cdm_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!cdm_hw) { + kfree(cdm_hw_intf); + return -ENOMEM; + } + + cdm_hw->core_info = kzalloc(sizeof(struct cam_cdm), GFP_KERNEL); + if (!cdm_hw->core_info) { + kfree(cdm_hw); + kfree(cdm_hw_intf); + return -ENOMEM; + } + cdm_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + cdm_hw->soc_info.pdev = pdev; + cdm_hw_intf->hw_type = CAM_VIRTUAL_CDM; + cdm_hw->soc_info.soc_private = kzalloc( + sizeof(struct cam_cdm_private_dt_data), GFP_KERNEL); + if (!cdm_hw->soc_info.soc_private) { + rc = -ENOMEM; + goto soc_load_failed; + } + + rc = cam_cdm_soc_load_dt_private(pdev, cdm_hw->soc_info.soc_private); + if (rc) { + CAM_ERR(CAM_CDM, "Failed to load CDM dt private data"); + kfree(cdm_hw->soc_info.soc_private); + cdm_hw->soc_info.soc_private = NULL; + goto soc_load_failed; + } + + cdm_core = (struct cam_cdm *)cdm_hw->core_info; + soc_private = (struct cam_cdm_private_dt_data *) + cdm_hw->soc_info.soc_private; + if (soc_private->dt_cdm_shared == true) + cdm_core->flags = CAM_CDM_FLAG_SHARED_CDM; + else + cdm_core->flags = CAM_CDM_FLAG_PRIVATE_CDM; + + cdm_core->bl_tag = 0; + INIT_LIST_HEAD(&cdm_core->bl_request_list); + init_completion(&cdm_core->reset_complete); + cdm_hw_intf->hw_priv = cdm_hw; + cdm_hw_intf->hw_ops.get_hw_caps = cam_cdm_get_caps; + cdm_hw_intf->hw_ops.init = NULL; + cdm_hw_intf->hw_ops.deinit = NULL; + cdm_hw_intf->hw_ops.start = cam_cdm_stream_start; + cdm_hw_intf->hw_ops.stop = cam_cdm_stream_stop; + cdm_hw_intf->hw_ops.read = NULL; + cdm_hw_intf->hw_ops.write = NULL; + cdm_hw_intf->hw_ops.process_cmd = cam_cdm_process_cmd; + + CAM_DBG(CAM_CDM, "type %d index %d", cdm_hw_intf->hw_type, + cdm_hw_intf->hw_idx); + + platform_set_drvdata(pdev, cdm_hw_intf); + + cdm_hw->open_count = 0; + cdm_core->iommu_hdl.non_secure = -1; + cdm_core->iommu_hdl.secure = -1; + mutex_init(&cdm_hw->hw_mutex); + spin_lock_init(&cdm_hw->hw_lock); + init_completion(&cdm_hw->hw_complete); + mutex_lock(&cdm_hw->hw_mutex); + cdm_core->id = CAM_CDM_VIRTUAL; + memcpy(cdm_core->name, CAM_CDM_VIRTUAL_NAME, + sizeof(CAM_CDM_VIRTUAL_NAME)); + cdm_core->work_queue = alloc_workqueue(cdm_core->name, + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, + CAM_CDM_INFLIGHT_WORKS); + cdm_core->ops = NULL; + + cpas_parms.cam_cpas_client_cb = cam_cdm_cpas_cb; + cpas_parms.cell_index = cdm_hw->soc_info.index; + cpas_parms.dev = &pdev->dev; + cpas_parms.userdata = cdm_hw_intf; + strlcpy(cpas_parms.identifier, "cam-cdm-intf", + CAM_HW_IDENTIFIER_LENGTH); + rc = cam_cpas_register_client(&cpas_parms); + if (rc) { + CAM_ERR(CAM_CDM, "Virtual CDM CPAS registration failed"); + goto cpas_registration_failed; + } + CAM_DBG(CAM_CDM, "CPAS registration successful handle=%d", + cpas_parms.client_handle); + cdm_core->cpas_handle = cpas_parms.client_handle; + + CAM_DBG(CAM_CDM, "CDM%d probe successful", cdm_hw_intf->hw_idx); + + rc = cam_cdm_intf_register_hw_cdm(cdm_hw_intf, + soc_private, CAM_VIRTUAL_CDM, &cdm_core->index); + if (rc) { + CAM_ERR(CAM_CDM, "Virtual CDM Interface registration failed"); + goto intf_registration_failed; + } + CAM_DBG(CAM_CDM, "CDM%d registered to intf successful", + cdm_hw_intf->hw_idx); + mutex_unlock(&cdm_hw->hw_mutex); + + return 0; +intf_registration_failed: + cam_cpas_unregister_client(cdm_core->cpas_handle); +cpas_registration_failed: + kfree(cdm_hw->soc_info.soc_private); + flush_workqueue(cdm_core->work_queue); + destroy_workqueue(cdm_core->work_queue); + mutex_unlock(&cdm_hw->hw_mutex); + mutex_destroy(&cdm_hw->hw_mutex); +soc_load_failed: + kfree(cdm_hw->core_info); + kfree(cdm_hw); + kfree(cdm_hw_intf); + return rc; +} + +int cam_virtual_cdm_remove(struct platform_device *pdev) +{ + struct cam_hw_info *cdm_hw = NULL; + struct cam_hw_intf *cdm_hw_intf = NULL; + struct cam_cdm *cdm_core = NULL; + int rc = -EBUSY; + + cdm_hw_intf = platform_get_drvdata(pdev); + if (!cdm_hw_intf) { + CAM_ERR(CAM_CDM, "Failed to get dev private data"); + return rc; + } + + cdm_hw = cdm_hw_intf->hw_priv; + if (!cdm_hw) { + CAM_ERR(CAM_CDM, + "Failed to get virtual private data for type=%d idx=%d", + cdm_hw_intf->hw_type, cdm_hw_intf->hw_idx); + return rc; + } + + cdm_core = cdm_hw->core_info; + if (!cdm_core) { + CAM_ERR(CAM_CDM, + "Failed to get virtual core data for type=%d idx=%d", + cdm_hw_intf->hw_type, cdm_hw_intf->hw_idx); + return rc; + } + + rc = cam_cpas_unregister_client(cdm_core->cpas_handle); + if (rc) { + CAM_ERR(CAM_CDM, "CPAS unregister failed"); + return rc; + } + + rc = cam_cdm_intf_deregister_hw_cdm(cdm_hw_intf, + cdm_hw->soc_info.soc_private, CAM_VIRTUAL_CDM, + cdm_core->index); + if (rc) { + CAM_ERR(CAM_CDM, + "Virtual CDM Interface de-registration failed"); + return rc; + } + + flush_workqueue(cdm_core->work_queue); + destroy_workqueue(cdm_core->work_queue); + mutex_destroy(&cdm_hw->hw_mutex); + kfree(cdm_hw->soc_info.soc_private); + kfree(cdm_hw->core_info); + kfree(cdm_hw); + kfree(cdm_hw_intf); + rc = 0; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_hw_cdm170_reg.h b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_hw_cdm170_reg.h new file mode 100644 index 000000000000..183b657100d2 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cdm/cam_hw_cdm170_reg.h @@ -0,0 +1,142 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_HW_CDM170_REG_H_ +#define _CAM_HW_CDM170_REG_H_ + +#define CAM_CDM_REG_OFFSET_FIRST 0x0 +#define CAM_CDM_REG_OFFSET_LAST 0x200 +#define CAM_CDM_REGS_COUNT 0x30 +#define CAM_CDM_HWFIFO_SIZE 0x40 + +#define CAM_CDM_OFFSET_HW_VERSION 0x0 +#define CAM_CDM_OFFSET_TITAN_VERSION 0x4 +#define CAM_CDM_OFFSET_RST_CMD 0x10 +#define CAM_CDM_OFFSET_CGC_CFG 0x14 +#define CAM_CDM_OFFSET_CORE_CFG 0x18 +#define CAM_CDM_OFFSET_CORE_EN 0x1c +#define CAM_CDM_OFFSET_FE_CFG 0x20 +#define CAM_CDM_OFFSET_IRQ_MASK 0x30 +#define CAM_CDM_OFFSET_IRQ_CLEAR 0x34 +#define CAM_CDM_OFFSET_IRQ_CLEAR_CMD 0x38 +#define CAM_CDM_OFFSET_IRQ_SET 0x3c +#define CAM_CDM_OFFSET_IRQ_SET_CMD 0x40 + +#define CAM_CDM_OFFSET_IRQ_STATUS 0x44 +#define CAM_CDM_IRQ_STATUS_INFO_RST_DONE_MASK 0x1 +#define CAM_CDM_IRQ_STATUS_INFO_INLINE_IRQ_MASK 0x2 +#define CAM_CDM_IRQ_STATUS_INFO_BL_DONE_MASK 0x4 +#define CAM_CDM_IRQ_STATUS_ERROR_INV_CMD_MASK 0x10000 +#define CAM_CDM_IRQ_STATUS_ERROR_OVER_FLOW_MASK 0x20000 +#define CAM_CDM_IRQ_STATUS_ERROR_AHB_BUS_MASK 0x40000 + +#define CAM_CDM_OFFSET_BL_FIFO_BASE_REG 0x50 +#define CAM_CDM_OFFSET_BL_FIFO_LEN_REG 0x54 +#define CAM_CDM_OFFSET_BL_FIFO_STORE_REG 0x58 +#define CAM_CDM_OFFSET_BL_FIFO_CFG 0x5c +#define CAM_CDM_OFFSET_BL_FIFO_RB 0x60 +#define CAM_CDM_OFFSET_BL_FIFO_BASE_RB 0x64 +#define CAM_CDM_OFFSET_BL_FIFO_LEN_RB 0x68 +#define CAM_CDM_OFFSET_BL_FIFO_PENDING_REQ_RB 0x6c +#define CAM_CDM_OFFSET_IRQ_USR_DATA 0x80 +#define CAM_CDM_OFFSET_WAIT_STATUS 0x84 +#define CAM_CDM_OFFSET_SCRATCH_0_REG 0x90 +#define CAM_CDM_OFFSET_SCRATCH_1_REG 0x94 +#define CAM_CDM_OFFSET_SCRATCH_2_REG 0x98 +#define CAM_CDM_OFFSET_SCRATCH_3_REG 0x9c +#define CAM_CDM_OFFSET_SCRATCH_4_REG 0xa0 +#define CAM_CDM_OFFSET_SCRATCH_5_REG 0xa4 +#define CAM_CDM_OFFSET_SCRATCH_6_REG 0xa8 +#define CAM_CDM_OFFSET_SCRATCH_7_REG 0xac +#define CAM_CDM_OFFSET_LAST_AHB_ADDR 0xd0 +#define CAM_CDM_OFFSET_LAST_AHB_DATA 0xd4 +#define CAM_CDM_OFFSET_CORE_DBUG 0xd8 +#define CAM_CDM_OFFSET_LAST_AHB_ERR_ADDR 0xe0 +#define CAM_CDM_OFFSET_LAST_AHB_ERR_DATA 0xe4 +#define CAM_CDM_OFFSET_CURRENT_BL_BASE 0xe8 +#define CAM_CDM_OFFSET_CURRENT_BL_LEN 0xec +#define CAM_CDM_OFFSET_CURRENT_USED_AHB_BASE 0xf0 +#define CAM_CDM_OFFSET_DEBUG_STATUS 0xf4 +#define CAM_CDM_OFFSET_BUS_MISR_CFG_0 0x100 +#define CAM_CDM_OFFSET_BUS_MISR_CFG_1 0x104 +#define CAM_CDM_OFFSET_BUS_MISR_RD_VAL 0x108 +#define CAM_CDM_OFFSET_PERF_MON_CTRL 0x110 +#define CAM_CDM_OFFSET_PERF_MON_0 0x114 +#define CAM_CDM_OFFSET_PERF_MON_1 0x118 +#define CAM_CDM_OFFSET_PERF_MON_2 0x11c +#define CAM_CDM_OFFSET_SPARE 0x200 + +/* + * Always make sure below register offsets are aligned with + * enum cam_cdm_regs offsets + */ +struct cam_cdm_reg_offset cam170_cpas_cdm_register_offsets[] = { + { CAM_CDM_OFFSET_HW_VERSION, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_TITAN_VERSION, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_RST_CMD, CAM_REG_ATTR_WRITE }, + { CAM_CDM_OFFSET_CGC_CFG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_CORE_CFG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_CORE_EN, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_FE_CFG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_IRQ_MASK, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_IRQ_CLEAR, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_IRQ_CLEAR_CMD, CAM_REG_ATTR_WRITE }, + { CAM_CDM_OFFSET_IRQ_SET, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_IRQ_SET_CMD, CAM_REG_ATTR_WRITE }, + { CAM_CDM_OFFSET_IRQ_STATUS, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_IRQ_USR_DATA, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_BASE_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_LEN_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_STORE_REG, CAM_REG_ATTR_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_CFG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_RB, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BL_FIFO_BASE_RB, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_BL_FIFO_LEN_RB, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_BL_FIFO_PENDING_REQ_RB, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_WAIT_STATUS, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_SCRATCH_0_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_1_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_2_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_3_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_4_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_5_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_6_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_SCRATCH_7_REG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_LAST_AHB_ADDR, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_LAST_AHB_DATA, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_CORE_DBUG, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_LAST_AHB_ERR_ADDR, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_LAST_AHB_ERR_DATA, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_CURRENT_BL_BASE, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_CURRENT_BL_LEN, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_CURRENT_USED_AHB_BASE, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_DEBUG_STATUS, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_BUS_MISR_CFG_0, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BUS_MISR_CFG_1, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_BUS_MISR_RD_VAL, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_PERF_MON_CTRL, CAM_REG_ATTR_READ_WRITE }, + { CAM_CDM_OFFSET_PERF_MON_0, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_PERF_MON_1, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_PERF_MON_2, CAM_REG_ATTR_READ }, + { CAM_CDM_OFFSET_SPARE, CAM_REG_ATTR_READ_WRITE } +}; + +struct cam_cdm_reg_offset_table cam170_cpas_cdm_offset_table = { + .first_offset = 0x0, + .last_offset = 0x200, + .reg_count = 0x30, + .offsets = cam170_cpas_cdm_register_offsets, + .offset_max_size = (sizeof(cam170_cpas_cdm_register_offsets)/ + sizeof(struct cam_cdm_reg_offset)), +}; + +#endif /* _CAM_HW_CDM170_REG_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_core/Makefile new file mode 100644 index 000000000000..d08bc1a11ea6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/Makefile @@ -0,0 +1,6 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_context.o cam_context_utils.o cam_node.o cam_subdev.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.c b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.c new file mode 100644 index 000000000000..78971a932a78 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.c @@ -0,0 +1,529 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include + +#include "cam_context.h" +#include "cam_debug_util.h" +#include "cam_node.h" + +static int cam_context_handle_hw_event(void *context, uint32_t evt_id, + void *evt_data) +{ + int rc = 0; + struct cam_context *ctx = (struct cam_context *)context; + + if (!ctx || !ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (ctx->state_machine[ctx->state].irq_ops) + rc = ctx->state_machine[ctx->state].irq_ops(ctx, evt_id, + evt_data); + else + CAM_DBG(CAM_CORE, + "No function to handle event %d in dev %d, state %d", + evt_id, ctx->dev_hdl, ctx->state); + return rc; +} + +int cam_context_shutdown(struct cam_context *ctx) +{ + int rc = 0; + + if (ctx->state_machine[ctx->state].ioctl_ops.stop_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.stop_dev( + ctx, NULL); + if (rc < 0) + CAM_ERR(CAM_CORE, "Error while dev stop %d", rc); + } + if (ctx->state_machine[ctx->state].ioctl_ops.release_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.release_dev( + ctx, NULL); + if (rc < 0) + CAM_ERR(CAM_CORE, "Error while dev release %d", rc); + } + + return rc; +} + +int cam_context_handle_crm_get_dev_info(struct cam_context *ctx, + struct cam_req_mgr_device_info *info) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!info) { + CAM_ERR(CAM_CORE, "Invalid get device info payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.get_dev_info) { + rc = ctx->state_machine[ctx->state].crm_ops.get_dev_info( + ctx, info); + } else { + CAM_ERR(CAM_CORE, "No get device info in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_crm_link(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *link) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!link) { + CAM_ERR(CAM_CORE, "Invalid link payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.link) { + rc = ctx->state_machine[ctx->state].crm_ops.link(ctx, link); + } else { + CAM_ERR(CAM_CORE, "No crm link in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_crm_unlink(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!unlink) { + CAM_ERR(CAM_CORE, "Invalid unlink payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.unlink) { + rc = ctx->state_machine[ctx->state].crm_ops.unlink( + ctx, unlink); + } else { + CAM_ERR(CAM_CORE, "No crm unlink in dev %d, name %s, state %d", + ctx->dev_hdl, ctx->dev_name, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_crm_apply_req(struct cam_context *ctx, + struct cam_req_mgr_apply_request *apply) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!apply) { + CAM_ERR(CAM_CORE, "Invalid apply request payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.apply_req) { + rc = ctx->state_machine[ctx->state].crm_ops.apply_req(ctx, + apply); + } else { + CAM_ERR(CAM_CORE, "No crm apply req in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_crm_flush_req(struct cam_context *ctx, + struct cam_req_mgr_flush_request *flush) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.flush_req) { + rc = ctx->state_machine[ctx->state].crm_ops.flush_req(ctx, + flush); + } else { + CAM_ERR(CAM_CORE, "No crm flush req in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_crm_process_evt(struct cam_context *ctx, + struct cam_req_mgr_link_evt_data *process_evt) +{ + int rc = 0; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].crm_ops.process_evt) { + rc = ctx->state_machine[ctx->state].crm_ops.process_evt(ctx, + process_evt); + } else { + /* handling of this message is optional */ + CAM_DBG(CAM_CORE, "No crm process evt in dev %d, state %d", + ctx->dev_hdl, ctx->state); + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_dump_pf_info(struct cam_context *ctx, unsigned long iova, + uint32_t buf_info) +{ + int rc = 0; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if ((ctx->state > CAM_CTX_AVAILABLE) && + (ctx->state < CAM_CTX_STATE_MAX)) { + if (ctx->state_machine[ctx->state].pagefault_ops) { + rc = ctx->state_machine[ctx->state].pagefault_ops( + ctx, iova, buf_info); + } else { + CAM_WARN(CAM_CORE, "No dump ctx in dev %d, state %d", + ctx->dev_hdl, ctx->state); + } + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_acquire_dev(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc; + int i; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid acquire device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.acquire_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.acquire_dev( + ctx, cmd); + } else { + CAM_ERR(CAM_CORE, "No acquire device in dev %d, state %d", + cmd->dev_handle, ctx->state); + rc = -EPROTO; + } + + INIT_LIST_HEAD(&ctx->active_req_list); + INIT_LIST_HEAD(&ctx->wait_req_list); + INIT_LIST_HEAD(&ctx->pending_req_list); + INIT_LIST_HEAD(&ctx->free_req_list); + + for (i = 0; i < ctx->req_size; i++) { + INIT_LIST_HEAD(&ctx->req_list[i].list); + list_add_tail(&ctx->req_list[i].list, &ctx->free_req_list); + } + + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_release_dev(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid release device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.release_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.release_dev( + ctx, cmd); + } else { + CAM_ERR(CAM_CORE, "No release device in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_flush_dev(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + int rc = 0; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid flush device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.flush_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.flush_dev( + ctx, cmd); + } else { + CAM_WARN(CAM_CORE, "No flush device in dev %d, state %d", + ctx->dev_hdl, ctx->state); + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_config_dev(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc; + + if (!ctx->state_machine) { + CAM_ERR(CAM_CORE, "context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid config device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.config_dev) { + rc = ctx->state_machine[ctx->state].ioctl_ops.config_dev( + ctx, cmd); + } else { + CAM_ERR(CAM_CORE, "No config device in dev %d, state %d", + ctx->dev_hdl, ctx->state); + rc = -EPROTO; + } + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_start_dev(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + + if (!ctx || !ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid start device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.start_dev) + rc = ctx->state_machine[ctx->state].ioctl_ops.start_dev( + ctx, cmd); + else + /* start device can be optional for some driver */ + CAM_DBG(CAM_CORE, "No start device in dev %d, state %d", + ctx->dev_hdl, ctx->state); + + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_handle_stop_dev(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + + if (!ctx || !ctx->state_machine) { + CAM_ERR(CAM_CORE, "Context is not ready"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_CORE, "Invalid stop device command payload"); + return -EINVAL; + } + + mutex_lock(&ctx->ctx_mutex); + if (ctx->state_machine[ctx->state].ioctl_ops.stop_dev) + rc = ctx->state_machine[ctx->state].ioctl_ops.stop_dev( + ctx, cmd); + else + /* stop device can be optional for some driver */ + CAM_WARN(CAM_CORE, "No stop device in dev %d, name %s state %d", + ctx->dev_hdl, ctx->dev_name, ctx->state); + mutex_unlock(&ctx->ctx_mutex); + + return rc; +} + +int cam_context_init(struct cam_context *ctx, + const char *dev_name, + uint64_t dev_id, + uint32_t ctx_id, + struct cam_req_mgr_kmd_ops *crm_node_intf, + struct cam_hw_mgr_intf *hw_mgr_intf, + struct cam_ctx_request *req_list, + uint32_t req_size) +{ + int i; + + /* crm_node_intf is optinal */ + if (!ctx || !hw_mgr_intf || !req_list) { + CAM_ERR(CAM_CORE, "Invalid input parameters"); + return -EINVAL; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->dev_hdl = -1; + ctx->link_hdl = -1; + ctx->session_hdl = -1; + INIT_LIST_HEAD(&ctx->list); + mutex_init(&ctx->ctx_mutex); + mutex_init(&ctx->sync_mutex); + spin_lock_init(&ctx->lock); + + ctx->dev_name = dev_name; + ctx->dev_id = dev_id; + ctx->ctx_id = ctx_id; + ctx->ctx_crm_intf = NULL; + ctx->crm_ctx_intf = crm_node_intf; + ctx->hw_mgr_intf = hw_mgr_intf; + ctx->irq_cb_intf = cam_context_handle_hw_event; + + INIT_LIST_HEAD(&ctx->active_req_list); + INIT_LIST_HEAD(&ctx->wait_req_list); + INIT_LIST_HEAD(&ctx->pending_req_list); + INIT_LIST_HEAD(&ctx->free_req_list); + ctx->req_list = req_list; + ctx->req_size = req_size; + for (i = 0; i < req_size; i++) { + INIT_LIST_HEAD(&ctx->req_list[i].list); + list_add_tail(&ctx->req_list[i].list, &ctx->free_req_list); + ctx->req_list[i].ctx = ctx; + } + ctx->state = CAM_CTX_AVAILABLE; + ctx->state_machine = NULL; + ctx->ctx_priv = NULL; + + return 0; +} + +int cam_context_deinit(struct cam_context *ctx) +{ + if (!ctx) + return -EINVAL; + + /** + * This is called from platform device remove. + * Everyting should be released at this moment. + * so we just free the memory for the context + */ + if (ctx->state != CAM_CTX_AVAILABLE) + CAM_ERR(CAM_CORE, "Device did not shutdown cleanly"); + + memset(ctx, 0, sizeof(*ctx)); + + return 0; +} + +void cam_context_putref(struct cam_context *ctx) +{ + if (kref_read(&ctx->refcount)) + kref_put(&ctx->refcount, cam_node_put_ctxt_to_free_list); + else + WARN(1, "ctx %s %d state %d devhdl %X\n", ctx->dev_name, + ctx->ctx_id, ctx->state, ctx->dev_hdl); + + CAM_DBG(CAM_CORE, + "ctx device hdl %ld, ref count %d, dev_name %s", + ctx->dev_hdl, refcount_read(&(ctx->refcount.refcount)), + ctx->dev_name); +} + +void cam_context_getref(struct cam_context *ctx) +{ + if (kref_get_unless_zero(&ctx->refcount) == 0) { + /* should never happen */ + WARN(1, "%s fail\n", __func__); + } + CAM_DBG(CAM_CORE, + "ctx device hdl %ld, ref count %d, dev_name %s", + ctx->dev_hdl, refcount_read(&(ctx->refcount.refcount)), + ctx->dev_name); +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.h new file mode 100644 index 000000000000..25bcd8a2541a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context.h @@ -0,0 +1,436 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CONTEXT_H_ +#define _CAM_CONTEXT_H_ + +#include +#include +#include +#include "cam_req_mgr_interface.h" +#include "cam_hw_mgr_intf.h" + +/* Forward declarations */ +struct cam_context; + +/* max request number */ +#define CAM_CTX_REQ_MAX 20 +#define CAM_CTX_CFG_MAX 20 +#define CAM_CTX_RES_MAX 20 + +/** + * enum cam_ctx_state - context top level states + * + */ +enum cam_context_state { + CAM_CTX_UNINIT = 0, + CAM_CTX_AVAILABLE = 1, + CAM_CTX_ACQUIRED = 2, + CAM_CTX_READY = 3, + CAM_CTX_ACTIVATED = 4, + CAM_CTX_STATE_MAX = 5, +}; + +/** + * struct cam_ctx_request - Common request structure for the context + * + * @list: Link list entry + * @status: Request status + * @request_id: Request id + * @req_priv: Derived request object + * @hw_update_entries: Hardware update entries + * @num_hw_update_entries: Number of hardware update entries + * @in_map_entries: Entries for in fences + * @num_in_map_entries: Number of in map entries + * @out_map_entries: Entries for out fences + * @num_out_map_entries: Number of out map entries + * @num_in_acked: Number of in fence acked + * @num_out_acked: Number of out fence acked + * @flushed: Request is flushed + * @ctx: The context to which this request belongs + * + */ +struct cam_ctx_request { + struct list_head list; + uint32_t status; + uint64_t request_id; + void *req_priv; + struct cam_hw_update_entry hw_update_entries[CAM_CTX_CFG_MAX]; + uint32_t num_hw_update_entries; + struct cam_hw_fence_map_entry in_map_entries[CAM_CTX_CFG_MAX]; + uint32_t num_in_map_entries; + struct cam_hw_fence_map_entry out_map_entries[CAM_CTX_CFG_MAX]; + uint32_t num_out_map_entries; + atomic_t num_in_acked; + uint32_t num_out_acked; + int flushed; + struct cam_context *ctx; +}; + +/** + * struct cam_ctx_ioctl_ops - Function table for handling IOCTL calls + * + * @acquire_dev: Function pointer for acquire device + * @release_dev: Function pointer for release device + * @config_dev: Function pointer for config device + * @start_dev: Function pointer for start device + * @stop_dev: Function pointer for stop device + * @flush_dev: Function pointer for flush device + * + */ +struct cam_ctx_ioctl_ops { + int (*acquire_dev)(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd); + int (*release_dev)(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd); + int (*config_dev)(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd); + int (*start_dev)(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd); + int (*stop_dev)(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd); + int (*flush_dev)(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd); +}; + +/** + * struct cam_ctx_crm_ops - Function table for handling CRM to context calls + * + * @get_dev_info: Get device informaiton + * @link: Link the context + * @unlink: Unlink the context + * @apply_req: Apply setting for the context + * @flush_req: Flush request to remove request ids + * @process_evt: Handle event notification from CRM.(optional) + * + */ +struct cam_ctx_crm_ops { + int (*get_dev_info)(struct cam_context *ctx, + struct cam_req_mgr_device_info *); + int (*link)(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *link); + int (*unlink)(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink); + int (*apply_req)(struct cam_context *ctx, + struct cam_req_mgr_apply_request *apply); + int (*flush_req)(struct cam_context *ctx, + struct cam_req_mgr_flush_request *flush); + int (*process_evt)(struct cam_context *ctx, + struct cam_req_mgr_link_evt_data *evt_data); +}; + + +/** + * struct cam_ctx_ops - Collection of the interface funciton tables + * + * @ioctl_ops: Ioctl funciton table + * @crm_ops: CRM to context interface function table + * @irq_ops: Hardware event handle function + * @pagefault_ops: Function to be called on page fault + * + */ +struct cam_ctx_ops { + struct cam_ctx_ioctl_ops ioctl_ops; + struct cam_ctx_crm_ops crm_ops; + cam_hw_event_cb_func irq_ops; + cam_hw_pagefault_cb_func pagefault_ops; + cam_ctx_info_dump_cb_func dumpinfo_ops; +}; + +/** + * struct cam_context - camera context object for the subdevice node + * + * @dev_name: String giving name of device associated + * @dev_id: ID of device associated + * @ctx_id: ID for this context + * @list: Link list entry + * @sessoin_hdl: Session handle + * @dev_hdl: Device handle + * @link_hdl: Link handle + * @ctx_mutex: Mutex for ioctl calls + * @lock: Spin lock + * @active_req_list: Requests pending for done event + * @pending_req_list: Requests pending for reg upd event + * @wait_req_list: Requests waiting for apply + * @free_req_list: Requests that are free + * @req_list: Reference to the request storage + * @req_size: Size of the request storage + * @hw_mgr_intf: Context to HW interface + * @ctx_crm_intf: Context to CRM interface + * @crm_ctx_intf: CRM to context interface + * @irq_cb_intf: HW to context callback interface + * @state: Current state for top level state machine + * @state_machine: Top level state machine + * @ctx_priv: Private context pointer + * @ctxt_to_hw_map: Context to hardware mapping pointer + * @refcount: Context object refcount + * @node: The main node to which this context belongs + * @sync_mutex: mutex to sync with sync cb thread + * + */ +struct cam_context { + const char *dev_name; + uint64_t dev_id; + uint32_t ctx_id; + struct list_head list; + int32_t session_hdl; + int32_t dev_hdl; + int32_t link_hdl; + + struct mutex ctx_mutex; + spinlock_t lock; + + struct list_head active_req_list; + struct list_head pending_req_list; + struct list_head wait_req_list; + struct list_head free_req_list; + struct cam_ctx_request *req_list; + uint32_t req_size; + + struct cam_hw_mgr_intf *hw_mgr_intf; + struct cam_req_mgr_crm_cb *ctx_crm_intf; + struct cam_req_mgr_kmd_ops *crm_ctx_intf; + cam_hw_event_cb_func irq_cb_intf; + + enum cam_context_state state; + struct cam_ctx_ops *state_machine; + + void *ctx_priv; + void *ctxt_to_hw_map; + + struct kref refcount; + void *node; + struct mutex sync_mutex; +}; + +/** + * cam_context_shutdown() + * + * @brief: Calls while device close or shutdown + * + * @ctx: Object pointer for cam_context + * + */ +int cam_context_shutdown(struct cam_context *ctx); + +/** + * cam_context_handle_crm_get_dev_info() + * + * @brief: Handle get device information command + * + * @ctx: Object pointer for cam_context + * @info: Device information returned + * + */ +int cam_context_handle_crm_get_dev_info(struct cam_context *ctx, + struct cam_req_mgr_device_info *info); + +/** + * cam_context_handle_crm_link() + * + * @brief: Handle link command + * + * @ctx: Object pointer for cam_context + * @link: Link command payload + * + */ +int cam_context_handle_crm_link(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *link); + +/** + * cam_context_handle_crm_unlink() + * + * @brief: Handle unlink command + * + * @ctx: Object pointer for cam_context + * @unlink: Unlink command payload + * + */ +int cam_context_handle_crm_unlink(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink); + +/** + * cam_context_handle_crm_apply_req() + * + * @brief: Handle apply request command + * + * @ctx: Object pointer for cam_context + * @apply: Apply request command payload + * + */ +int cam_context_handle_crm_apply_req(struct cam_context *ctx, + struct cam_req_mgr_apply_request *apply); + +/** + * cam_context_handle_crm_flush_req() + * + * @brief: Handle flush request command + * + * @ctx: Object pointer for cam_context + * @apply: Flush request command payload + * + */ +int cam_context_handle_crm_flush_req(struct cam_context *ctx, + struct cam_req_mgr_flush_request *apply); + +/** + * cam_context_handle_crm_process_evt() + * + * @brief: Handle process event command + * + * @ctx: Object pointer for cam_context + * @process_evt: process event command payload + * + */ +int cam_context_handle_crm_process_evt(struct cam_context *ctx, + struct cam_req_mgr_link_evt_data *process_evt); + +/** + * cam_context_dump_pf_info() + * + * @brief: Handle dump active request request command + * + * @ctx: Object pointer for cam_context + * @iova: Page fault address + * @buf_info: Information about closest memory handle + * + */ +int cam_context_dump_pf_info(struct cam_context *ctx, unsigned long iova, + uint32_t buf_info); + +/** + * cam_context_handle_acquire_dev() + * + * @brief: Handle acquire device command + * + * @ctx: Object pointer for cam_context + * @cmd: Acquire device command payload + * + */ +int cam_context_handle_acquire_dev(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd); + +/** + * cam_context_handle_release_dev() + * + * @brief: Handle release device command + * + * @ctx: Object pointer for cam_context + * @cmd: Release device command payload + * + */ +int cam_context_handle_release_dev(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd); + +/** + * cam_context_handle_config_dev() + * + * @brief: Handle config device command + * + * @ctx: Object pointer for cam_context + * @cmd: Config device command payload + * + */ +int cam_context_handle_config_dev(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd); + +/** + * cam_context_handle_flush_dev() + * + * @brief: Handle flush device command + * + * @ctx: Object pointer for cam_context + * @cmd: Flush device command payload + * + */ +int cam_context_handle_flush_dev(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd); + +/** + * cam_context_handle_start_dev() + * + * @brief: Handle start device command + * + * @ctx: Object pointer for cam_context + * @cmd: Start device command payload + * + */ +int cam_context_handle_start_dev(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd); + +/** + * cam_context_handle_stop_dev() + * + * @brief: Handle stop device command + * + * @ctx: Object pointer for cam_context + * @cmd: Stop device command payload + * + */ +int cam_context_handle_stop_dev(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd); + +/** + * cam_context_deinit() + * + * @brief: Camera context deinitialize function + * + * @ctx: Object pointer for cam_context + * + */ +int cam_context_deinit(struct cam_context *ctx); + +/** + * cam_context_init() + * + * @brief: Camera context initialize function + * + * @ctx: Object pointer for cam_context + * @dev_name: String giving name of device associated + * @dev_id: ID of the device associated + * @ctx_id: ID for this context + * @crm_node_intf: Function table for crm to context interface + * @hw_mgr_intf: Function table for context to hw interface + * @req_list: Requests storage + * @req_size: Size of the request storage + * + */ +int cam_context_init(struct cam_context *ctx, + const char *dev_name, + uint64_t dev_id, + uint32_t ctx_id, + struct cam_req_mgr_kmd_ops *crm_node_intf, + struct cam_hw_mgr_intf *hw_mgr_intf, + struct cam_ctx_request *req_list, + uint32_t req_size); + +/** + * cam_context_putref() + * + * @brief: Put back context reference. + * + * @ctx: Context for which ref is returned + * + */ +void cam_context_putref(struct cam_context *ctx); + +/** + * cam_context_getref() + * + * @brief: Get back context reference. + * + * @ctx: Context for which ref is taken + * + */ +void cam_context_getref(struct cam_context *ctx); + +#endif /* _CAM_CONTEXT_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.c new file mode 100644 index 000000000000..310ce94c0a0a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.c @@ -0,0 +1,902 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include + +#include "cam_context.h" +#include "cam_context_utils.h" +#include "cam_mem_mgr.h" +#include "cam_node.h" +#include "cam_req_mgr_util.h" +#include "cam_sync_api.h" +#include "cam_trace.h" +#include "cam_debug_util.h" + +static uint cam_debug_ctx_req_list; +module_param(cam_debug_ctx_req_list, uint, 0644); + +static inline int cam_context_validate_thread(void) +{ + if (in_interrupt()) { + WARN(1, "Invalid execution context\n"); + return -EINVAL; + } + return 0; +} + +int cam_context_buf_done_from_hw(struct cam_context *ctx, + void *done_event_data, uint32_t bubble_state) +{ + int j; + int result; + struct cam_ctx_request *req; + struct cam_hw_done_event_data *done = + (struct cam_hw_done_event_data *)done_event_data; + int rc; + + if (!ctx || !done) { + CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, done); + return -EINVAL; + } + + rc = cam_context_validate_thread(); + if (rc) + return rc; + + spin_lock(&ctx->lock); + if (list_empty(&ctx->active_req_list)) { + CAM_ERR(CAM_CTXT, "[%s][%d] no active request", + ctx->dev_name, ctx->ctx_id); + spin_unlock(&ctx->lock); + return -EIO; + } + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + + trace_cam_buf_done("UTILS", ctx, req); + + if (done->request_id != req->request_id) { + CAM_ERR(CAM_CTXT, + "[%s][%d] mismatch: done req[%lld], active req[%lld]", + ctx->dev_name, ctx->ctx_id, + done->request_id, req->request_id); + spin_unlock(&ctx->lock); + return -EIO; + } + + if (!req->num_out_map_entries) { + CAM_ERR(CAM_CTXT, "[%s][%d] no output fence to signal", + ctx->dev_name, ctx->ctx_id); + spin_unlock(&ctx->lock); + return -EIO; + } + + /* + * since another thread may be adding/removing from active + * list, so hold the lock + */ + list_del_init(&req->list); + spin_unlock(&ctx->lock); + if (!bubble_state) { + result = CAM_SYNC_STATE_SIGNALED_SUCCESS; + } else { + CAM_DBG(CAM_REQ, + "[%s][ctx_id %d] : req[%llu] is done with error", + ctx->dev_name, ctx->ctx_id, req->request_id); + + for (j = 0; j < req->num_out_map_entries; j++) + CAM_DBG(CAM_REQ, "fence %d signaled with error", + req->out_map_entries[j].sync_id); + + result = CAM_SYNC_STATE_SIGNALED_ERROR; + } + + for (j = 0; j < req->num_out_map_entries; j++) { + cam_sync_signal(req->out_map_entries[j].sync_id, result); + req->out_map_entries[j].sync_id = -1; + } + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from active_list to free_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + + /* + * another thread may be adding/removing from free list, + * so hold the lock + */ + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + req->ctx = NULL; + spin_unlock(&ctx->lock); + + return 0; +} + +static int cam_context_apply_req_to_hw(struct cam_ctx_request *req, + struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_context *ctx = req->ctx; + struct cam_hw_config_args cfg; + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto end; + } + + spin_lock(&ctx->lock); + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->active_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from pending_list to active_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + + cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + cfg.request_id = req->request_id; + cfg.hw_update_entries = req->hw_update_entries; + cfg.num_hw_update_entries = req->num_hw_update_entries; + cfg.out_map_entries = req->out_map_entries; + cfg.num_out_map_entries = req->num_out_map_entries; + cfg.priv = req->req_priv; + + rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg); + if (rc) { + spin_lock(&ctx->lock); + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from active_list to free_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + } + +end: + return rc; +} + +static void cam_context_sync_callback(int32_t sync_obj, int status, void *data) +{ + struct cam_ctx_request *req = data; + struct cam_context *ctx = NULL; + struct cam_flush_dev_cmd flush_cmd; + struct cam_req_mgr_apply_request apply; + int rc; + + if (!req) { + CAM_ERR(CAM_CTXT, "Invalid input param"); + return; + } + rc = cam_context_validate_thread(); + if (rc) + return; + + ctx = req->ctx; + if (!ctx) { + CAM_ERR(CAM_CTXT, "Invalid ctx for req %llu", req->request_id); + return; + } + + if (atomic_inc_return(&req->num_in_acked) == req->num_in_map_entries) { + apply.request_id = req->request_id; + /* + * take mutex to ensure that another thread does + * not flush the request while this + * thread is submitting it to h/w. The submit to + * h/w and adding to the active list should happen + * in a critical section which is provided by this + * mutex. + */ + if (status == CAM_SYNC_STATE_SIGNALED_ERROR) { + CAM_DBG(CAM_CTXT, "fence error: %d", sync_obj); + flush_cmd.req_id = req->request_id; + cam_context_flush_req_to_hw(ctx, &flush_cmd); + } + + mutex_lock(&ctx->sync_mutex); + if (!req->flushed) { + cam_context_apply_req_to_hw(req, &apply); + mutex_unlock(&ctx->sync_mutex); + } else { + req->flushed = 0; + req->ctx = NULL; + mutex_unlock(&ctx->sync_mutex); + spin_lock(&ctx->lock); + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from pending_list to free_list", + ctx->dev_name, ctx->ctx_id, + req->request_id); + } + } + cam_context_putref(ctx); +} + +int32_t cam_context_release_dev_to_hw(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + struct cam_hw_release_args arg; + + if (!ctx) { + CAM_ERR(CAM_CTXT, "Invalid input param"); + return -EINVAL; + } + + if ((!ctx->hw_mgr_intf) || (!ctx->hw_mgr_intf->hw_release)) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + return -EINVAL; + } + + arg.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + arg.active_req = false; + + ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &arg); + ctx->ctxt_to_hw_map = NULL; + + ctx->session_hdl = -1; + ctx->dev_hdl = -1; + ctx->link_hdl = -1; + + return 0; +} + +int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc = 0; + struct cam_ctx_request *req = NULL; + struct cam_hw_prepare_update_args cfg; + uintptr_t packet_addr; + struct cam_packet *packet; + size_t len = 0; + int32_t i = 0, j = 0; + + if (!ctx || !cmd) { + CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, cmd); + return -EINVAL; + } + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + return -EFAULT; + } + rc = cam_context_validate_thread(); + if (rc) + return rc; + + spin_lock(&ctx->lock); + if (!list_empty(&ctx->free_req_list)) { + req = list_first_entry(&ctx->free_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + } + spin_unlock(&ctx->lock); + + if (!req) { + CAM_ERR(CAM_CTXT, "[%s][%d] No more request obj free", + ctx->dev_name, ctx->ctx_id); + return -ENOMEM; + } + + memset(req, 0, sizeof(*req)); + INIT_LIST_HEAD(&req->list); + req->ctx = ctx; + + /* for config dev, only memory handle is supported */ + /* map packet from the memhandle */ + rc = cam_mem_get_cpu_buf((int32_t) cmd->packet_handle, + &packet_addr, &len); + if (rc != 0) { + CAM_ERR(CAM_CTXT, "[%s][%d] Can not get packet address", + ctx->dev_name, ctx->ctx_id); + rc = -EINVAL; + goto free_req; + } + + packet = (struct cam_packet *) ((uint8_t *)packet_addr + + (uint32_t)cmd->offset); + + /* preprocess the configuration */ + memset(&cfg, 0, sizeof(cfg)); + cfg.packet = packet; + cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + cfg.max_hw_update_entries = CAM_CTX_CFG_MAX; + cfg.num_hw_update_entries = req->num_hw_update_entries; + cfg.hw_update_entries = req->hw_update_entries; + cfg.max_out_map_entries = CAM_CTX_CFG_MAX; + cfg.out_map_entries = req->out_map_entries; + cfg.max_in_map_entries = CAM_CTX_CFG_MAX; + cfg.in_map_entries = req->in_map_entries; + + rc = ctx->hw_mgr_intf->hw_prepare_update( + ctx->hw_mgr_intf->hw_mgr_priv, &cfg); + if (rc != 0) { + CAM_ERR(CAM_CTXT, + "[%s][%d] Prepare config packet failed in HW layer", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto free_req; + } + req->num_hw_update_entries = cfg.num_hw_update_entries; + req->num_out_map_entries = cfg.num_out_map_entries; + req->num_in_map_entries = cfg.num_in_map_entries; + atomic_set(&req->num_in_acked, 0); + req->request_id = packet->header.request_id; + req->status = 1; + req->req_priv = cfg.priv; + + for (i = 0; i < req->num_out_map_entries; i++) { + rc = cam_sync_get_obj_ref(req->out_map_entries[i].sync_id); + if (rc) { + CAM_ERR(CAM_CTXT, "Can't get ref for sync %d", + req->out_map_entries[i].sync_id); + goto put_ref; + } + } + + if (req->num_in_map_entries > 0) { + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->pending_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from free_list to pending_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + + for (j = 0; j < req->num_in_map_entries; j++) { + cam_context_getref(ctx); + rc = cam_sync_register_callback( + cam_context_sync_callback, + (void *)req, + req->in_map_entries[j].sync_id); + if (rc) { + CAM_ERR(CAM_CTXT, + "[%s][%d] Failed register fence cb: %d ret = %d", + ctx->dev_name, ctx->ctx_id, + req->in_map_entries[j].sync_id, rc); + spin_lock(&ctx->lock); + list_del_init(&req->list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from pending_list to free_list", + ctx->dev_name, ctx->ctx_id, + req->request_id); + + goto put_ctx_ref; + } + CAM_DBG(CAM_CTXT, "register in fence cb: %d ret = %d", + req->in_map_entries[j].sync_id, rc); + } + } + + return rc; +put_ctx_ref: + for (; j >= 0; j--) + cam_context_putref(ctx); +put_ref: + for (--i; i >= 0; i--) { + if (cam_sync_put_obj_ref(req->out_map_entries[i].sync_id)) + CAM_ERR(CAM_CTXT, "Failed to put ref of fence %d", + req->out_map_entries[i].sync_id); + } +free_req: + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + req->ctx = NULL; + spin_unlock(&ctx->lock); + + return rc; +} + +int32_t cam_context_acquire_dev_to_hw(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc; + struct cam_hw_acquire_args param; + struct cam_create_dev_hdl req_hdl_param; + struct cam_hw_release_args release; + + if (!ctx || !cmd) { + CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, cmd); + rc = -EINVAL; + goto end; + } + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto end; + } + + CAM_DBG(CAM_CTXT, "ses hdl: %x, num_res: %d, type: %d, res: %lld", + cmd->session_handle, cmd->num_resources, cmd->handle_type, + cmd->resource_hdl); + + if (cmd->num_resources > CAM_CTX_RES_MAX) { + CAM_ERR(CAM_CTXT, "[%s][%d] resource limit exceeded", + ctx->dev_name, ctx->ctx_id); + rc = -ENOMEM; + goto end; + } + + /* for now we only support user pointer */ + if (cmd->handle_type != 1) { + CAM_ERR(CAM_CTXT, "[%s][%d] Only user pointer is supported", + ctx->dev_name, ctx->ctx_id); + rc = -EINVAL; + goto end; + } + + /* fill in parameters */ + param.context_data = ctx; + param.event_cb = ctx->irq_cb_intf; + param.num_acq = cmd->num_resources; + param.acquire_info = cmd->resource_hdl; + + /* call HW manager to reserve the resource */ + rc = ctx->hw_mgr_intf->hw_acquire(ctx->hw_mgr_intf->hw_mgr_priv, + ¶m); + if (rc != 0) { + CAM_ERR(CAM_CTXT, "[%s][%d] Acquire device failed", + ctx->dev_name, ctx->ctx_id); + goto end; + } + + ctx->ctxt_to_hw_map = param.ctxt_to_hw_map; + + /* if hw resource acquire successful, acquire dev handle */ + req_hdl_param.session_hdl = cmd->session_handle; + /* bridge is not ready for these flags. so false for now */ + req_hdl_param.v4l2_sub_dev_flag = 0; + req_hdl_param.media_entity_flag = 0; + req_hdl_param.priv = ctx; + req_hdl_param.ops = ctx->crm_ctx_intf; + + ctx->dev_hdl = cam_create_device_hdl(&req_hdl_param); + if (ctx->dev_hdl <= 0) { + rc = -EFAULT; + CAM_ERR(CAM_CTXT, "[%s][%d] Can not create device handle", + ctx->dev_name, ctx->ctx_id); + goto free_hw; + } + cmd->dev_handle = ctx->dev_hdl; + + /* store session information */ + ctx->session_hdl = cmd->session_handle; + + return rc; + +free_hw: + release.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &release); + ctx->ctxt_to_hw_map = NULL; + ctx->dev_hdl = -1; +end: + return rc; +} + +int32_t cam_context_flush_ctx_to_hw(struct cam_context *ctx) +{ + struct cam_hw_flush_args flush_args; + struct list_head temp_list; + struct cam_ctx_request *req; + uint32_t i; + int rc = 0; + bool free_req; + + CAM_DBG(CAM_CTXT, "[%s] E: NRT flush ctx", ctx->dev_name); + memset(&flush_args, 0, sizeof(flush_args)); + + /* + * flush pending requests, take the sync lock to synchronize with the + * sync callback thread so that the sync cb thread does not try to + * submit request to h/w while the request is being flushed + */ + mutex_lock(&ctx->sync_mutex); + INIT_LIST_HEAD(&temp_list); + spin_lock(&ctx->lock); + list_splice_init(&ctx->pending_req_list, &temp_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving all pending requests from pending_list to temp_list", + ctx->dev_name, ctx->ctx_id); + + flush_args.num_req_pending = 0; + while (true) { + spin_lock(&ctx->lock); + if (list_empty(&temp_list)) { + spin_unlock(&ctx->lock); + break; + } + + req = list_first_entry(&temp_list, + struct cam_ctx_request, list); + + list_del_init(&req->list); + spin_unlock(&ctx->lock); + req->flushed = 1; + + flush_args.flush_req_pending[flush_args.num_req_pending++] = + req->req_priv; + + free_req = false; + for (i = 0; i < req->num_in_map_entries; i++) { + rc = cam_sync_deregister_callback( + cam_context_sync_callback, + (void *)req, + req->in_map_entries[i].sync_id); + if (!rc) { + cam_context_putref(ctx); + if (atomic_inc_return(&req->num_in_acked) == + req->num_in_map_entries) + free_req = true; + } + } + + for (i = 0; i < req->num_out_map_entries; i++) { + if (req->out_map_entries[i].sync_id != -1) { + rc = cam_sync_signal( + req->out_map_entries[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc == -EALREADY) { + CAM_ERR(CAM_CTXT, + "Req: %llu already signalled, sync_id:%d", + req->request_id, + req->out_map_entries[i].sync_id); + break; + } + } + } + + /* + * If we have deregistered the last sync callback, req will + * not be put on the free list. So put it on the free list here + */ + if (free_req) { + req->ctx = NULL; + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock(&ctx->lock); + } + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Deleting req[%llu] from temp_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + } + mutex_unlock(&ctx->sync_mutex); + + if (ctx->hw_mgr_intf->hw_flush) { + flush_args.num_req_active = 0; + spin_lock(&ctx->lock); + list_for_each_entry(req, &ctx->active_req_list, list) { + flush_args.flush_req_active[flush_args.num_req_active++] + = req->req_priv; + } + spin_unlock(&ctx->lock); + + if (flush_args.num_req_pending || flush_args.num_req_active) { + flush_args.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + flush_args.flush_type = CAM_FLUSH_TYPE_ALL; + ctx->hw_mgr_intf->hw_flush( + ctx->hw_mgr_intf->hw_mgr_priv, &flush_args); + } + } + + INIT_LIST_HEAD(&temp_list); + spin_lock(&ctx->lock); + list_splice_init(&ctx->active_req_list, &temp_list); + INIT_LIST_HEAD(&ctx->active_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving all requests from active_list to temp_list", + ctx->dev_name, ctx->ctx_id); + + while (true) { + spin_lock(&ctx->lock); + if (list_empty(&temp_list)) { + spin_unlock(&ctx->lock); + break; + } + req = list_first_entry(&temp_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + spin_unlock(&ctx->lock); + + for (i = 0; i < req->num_out_map_entries; i++) { + if (req->out_map_entries[i].sync_id != -1) { + rc = cam_sync_signal( + req->out_map_entries[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc == -EALREADY) { + CAM_ERR(CAM_CTXT, + "Req: %llu already signalled ctx: %pK dev_name: %s dev_handle: %d ctx_state: %d", + req->request_id, req->ctx, + req->ctx->dev_name, + req->ctx->dev_hdl, + req->ctx->state); + break; + } + } + } + + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock(&ctx->lock); + req->ctx = NULL; + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from temp_list to free_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + } + + CAM_DBG(CAM_CTXT, "[%s] X: NRT flush ctx", ctx->dev_name); + + return 0; +} + +int32_t cam_context_flush_req_to_hw(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + struct cam_ctx_request *req = NULL; + struct cam_hw_flush_args flush_args; + uint32_t i; + int32_t sync_id = 0; + int rc = 0; + bool free_req = false; + + CAM_DBG(CAM_CTXT, "[%s] E: NRT flush req", ctx->dev_name); + + memset(&flush_args, 0, sizeof(flush_args)); + flush_args.num_req_pending = 0; + flush_args.num_req_active = 0; + mutex_lock(&ctx->sync_mutex); + spin_lock(&ctx->lock); + list_for_each_entry(req, &ctx->pending_req_list, list) { + if (req->request_id != cmd->req_id) + continue; + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Deleting req[%llu] from pending_list", + ctx->dev_name, ctx->ctx_id, req->request_id); + + list_del_init(&req->list); + req->flushed = 1; + + flush_args.flush_req_pending[flush_args.num_req_pending++] = + req->req_priv; + break; + } + spin_unlock(&ctx->lock); + mutex_unlock(&ctx->sync_mutex); + + if (ctx->hw_mgr_intf->hw_flush) { + if (!flush_args.num_req_pending) { + spin_lock(&ctx->lock); + list_for_each_entry(req, &ctx->active_req_list, list) { + if (req->request_id != cmd->req_id) + continue; + + list_del_init(&req->list); + + flush_args.flush_req_active[ + flush_args.num_req_active++] = + req->req_priv; + break; + } + spin_unlock(&ctx->lock); + } + + if (flush_args.num_req_pending || flush_args.num_req_active) { + flush_args.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + flush_args.flush_type = CAM_FLUSH_TYPE_REQ; + ctx->hw_mgr_intf->hw_flush( + ctx->hw_mgr_intf->hw_mgr_priv, &flush_args); + } + } + + if (req) { + if (flush_args.num_req_pending) { + for (i = 0; i < req->num_in_map_entries; i++) { + rc = cam_sync_deregister_callback( + cam_context_sync_callback, + (void *)req, + req->in_map_entries[i].sync_id); + if (rc) + continue; + + cam_context_putref(ctx); + if (atomic_inc_return(&req->num_in_acked) == + req->num_in_map_entries) + free_req = true; + } + } + + if (flush_args.num_req_pending || flush_args.num_req_active) { + for (i = 0; i < req->num_out_map_entries; i++) { + sync_id = + req->out_map_entries[i].sync_id; + if (sync_id != -1) { + rc = cam_sync_signal(sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc == -EALREADY) { + CAM_ERR(CAM_CTXT, + "Req: %llu already signalled, sync_id:%d", + req->request_id, sync_id); + break; + } + } + } + if (flush_args.num_req_active || free_req) { + req->ctx = NULL; + spin_lock(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock(&ctx->lock); + + if (cam_debug_ctx_req_list & ctx->dev_id) + CAM_INFO(CAM_CTXT, + "[%s][%d] : Moving req[%llu] from %s to free_list", + ctx->dev_name, ctx->ctx_id, + req->request_id, + flush_args.num_req_active ? + "active_list" : + "pending_list"); + } + } + } + CAM_DBG(CAM_CTXT, "[%s] X: NRT flush req", ctx->dev_name); + + return 0; +} + +int32_t cam_context_flush_dev_to_hw(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + + int rc = 0; + + if (!ctx || !cmd) { + CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, cmd); + rc = -EINVAL; + goto end; + } + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto end; + } + + if (cmd->flush_type == CAM_FLUSH_TYPE_ALL) + rc = cam_context_flush_ctx_to_hw(ctx); + else if (cmd->flush_type == CAM_FLUSH_TYPE_REQ) + rc = cam_context_flush_req_to_hw(ctx, cmd); + else { + rc = -EINVAL; + CAM_ERR(CAM_CORE, "[%s][%d] Invalid flush type %d", + ctx->dev_name, ctx->ctx_id, cmd->flush_type); + } + +end: + return rc; +} + +int32_t cam_context_start_dev_to_hw(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + struct cam_hw_start_args arg; + + if (!ctx || !cmd) { + CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, cmd); + rc = -EINVAL; + goto end; + } + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto end; + } + + if ((cmd->session_handle != ctx->session_hdl) || + (cmd->dev_handle != ctx->dev_hdl)) { + CAM_ERR(CAM_CTXT, + "[%s][%d] Invalid session hdl[%d], dev_handle[%d]", + ctx->dev_name, ctx->ctx_id, + cmd->session_handle, cmd->dev_handle); + rc = -EPERM; + goto end; + } + + if (ctx->hw_mgr_intf->hw_start) { + arg.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + rc = ctx->hw_mgr_intf->hw_start(ctx->hw_mgr_intf->hw_mgr_priv, + &arg); + if (rc) { + /* HW failure. user need to clean up the resource */ + CAM_ERR(CAM_CTXT, "[%s][%d] Start HW failed", + ctx->dev_name, ctx->ctx_id); + goto end; + } + } + +end: + return rc; +} + +int32_t cam_context_stop_dev_to_hw(struct cam_context *ctx) +{ + int rc = 0; + struct cam_hw_stop_args stop; + + if (!ctx) { + CAM_ERR(CAM_CTXT, "Invalid input param"); + rc = -EINVAL; + goto end; + } + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready", + ctx->dev_name, ctx->ctx_id); + rc = -EFAULT; + goto end; + } + + rc = cam_context_validate_thread(); + if (rc) + goto end; + + rc = cam_context_flush_ctx_to_hw(ctx); + if (rc) + goto end; + + /* stop hw first */ + if (ctx->hw_mgr_intf->hw_stop) { + stop.ctxt_to_hw_map = ctx->ctxt_to_hw_map; + ctx->hw_mgr_intf->hw_stop(ctx->hw_mgr_intf->hw_mgr_priv, + &stop); + } + +end: + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.h new file mode 100644 index 000000000000..9b95eaddb3c7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_context_utils.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CONTEXT_UTILS_H_ +#define _CAM_CONTEXT_UTILS_H_ + +#include + +int cam_context_buf_done_from_hw(struct cam_context *ctx, + void *done_event_data, uint32_t bubble_state); +int32_t cam_context_release_dev_to_hw(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd); +int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd); +int32_t cam_context_acquire_dev_to_hw(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd); +int32_t cam_context_start_dev_to_hw(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd); +int32_t cam_context_stop_dev_to_hw(struct cam_context *ctx); +int32_t cam_context_flush_dev_to_hw(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd); +int32_t cam_context_flush_ctx_to_hw(struct cam_context *ctx); +int32_t cam_context_flush_req_to_hw(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd); + +#endif /* _CAM_CONTEXT_UTILS_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_core_defs.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_core_defs.h new file mode 100644 index 000000000000..45d989f9b6e4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_core_defs.h @@ -0,0 +1,45 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CORE_DEFS_H_ +#define _CAM_CORE_DEFS_H_ + +#define CAM_CORE_TRACE_ENABLE 0 + +#if (CAM_CORE_TRACE_ENABLE == 1) + #define CAM_CORE_DBG(fmt, args...) do { \ + trace_printk("%d: [cam_core_dbg] "fmt"\n", __LINE__, ##args); \ + pr_debug("%s:%d "fmt"\n", __func__, __LINE__, ##args); \ + } while (0) + + #define CAM_CORE_WARN(fmt, args...) do { \ + trace_printk("%d: [cam_core_warn] "fmt"\n", __LINE__, ##args); \ + pr_warn("%s:%d "fmt"\n", __func__, __LINE__, ##args); \ + } while (0) + + #define CAM_CORE_ERR(fmt, args...) do { \ + trace_printk("%d: [cam_core_err] "fmt"\n", __LINE__, ##args); \ + pr_err("%s:%d "fmt"\n", __func__, __LINE__, ##args);\ + } while (0) +#else + #define CAM_CORE_DBG(fmt, args...) pr_debug("%s:%d "fmt"\n", \ + __func__, __LINE__, ##args) + + #define CAM_CORE_WARN(fmt, args...) pr_warn("%s:%d "fmt"\n", \ + __func__, __LINE__, ##args) + + #define CAM_CORE_ERR(fmt, args...) pr_err("%s:%d "fmt"\n", \ + __func__, __LINE__, ##args) +#endif + +#endif /* _CAM_CORE_DEFS_H_ */ + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw.h new file mode 100644 index 000000000000..d01a84ae33e6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw.h @@ -0,0 +1,53 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_HW_H_ +#define _CAM_HW_H_ + +#include "cam_soc_util.h" + +/* + * This file declares Enums, Structures and APIs to be used as template + * when writing any HW driver in the camera subsystem. + */ + +/* Hardware state enum */ +enum cam_hw_state { + CAM_HW_STATE_POWER_DOWN, + CAM_HW_STATE_POWER_UP, +}; + +/** + * struct cam_hw_info - Common hardware information + * + * @hw_mutex: Hardware mutex + * @hw_lock: Hardware spinlock + * @hw_complete: Hardware Completion + * @open_count: Count to track the HW enable from the client + * @hw_state: Hardware state + * @soc_info: Platform SOC properties for hardware + * @node_info: Private HW data related to nodes + * @core_info: Private HW data related to core logic + * + */ +struct cam_hw_info { + struct mutex hw_mutex; + spinlock_t hw_lock; + struct completion hw_complete; + uint32_t open_count; + enum cam_hw_state hw_state; + struct cam_hw_soc_info soc_info; + void *node_info; + void *core_info; +}; + +#endif /* _CAM_HW_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_intf.h new file mode 100644 index 000000000000..bd2b789101bb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_intf.h @@ -0,0 +1,83 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_HW_INTF_H_ +#define _CAM_HW_INTF_H_ + +#include + +/* + * This file declares Constants, Enums, Structures and APIs to be used as + * Interface between HW driver and HW Manager. + */ + +/** + * struct cam_hw_ops - Hardware layer interface functions + * + * @get_hw_caps: Function pointer for get hw caps + * @init: Function poniter for initialize hardware + * @deinit: Function pointer for deinitialize hardware + * @reset: Function pointer for reset hardware + * @reserve: Function pointer for reserve hardware + * @release: Function pointer for release hardware + * @start: Function pointer for start hardware + * @stop: Function pointer for stop hardware + * @read: Function pointer for read hardware registers + * @write: Function pointer for Write hardware registers + * @process_cmd: Function pointer for additional hardware controls + * @flush_cmd: Function pointer for flush requests + * + */ +struct cam_hw_ops { + int (*get_hw_caps)(void *hw_priv, + void *get_hw_cap_args, uint32_t arg_size); + int (*init)(void *hw_priv, + void *init_hw_args, uint32_t arg_size); + int (*deinit)(void *hw_priv, + void *init_hw_args, uint32_t arg_size); + int (*reset)(void *hw_priv, + void *reset_core_args, uint32_t arg_size); + int (*reserve)(void *hw_priv, + void *reserve_args, uint32_t arg_size); + int (*release)(void *hw_priv, + void *release_args, uint32_t arg_size); + int (*start)(void *hw_priv, + void *start_args, uint32_t arg_size); + int (*stop)(void *hw_priv, + void *stop_args, uint32_t arg_size); + int (*read)(void *hw_priv, + void *read_args, uint32_t arg_size); + int (*write)(void *hw_priv, + void *write_args, uint32_t arg_size); + int (*process_cmd)(void *hw_priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size); + int (*flush)(void *hw_priv, + void *flush_args, uint32_t arg_size); +}; + +/** + * struct cam_hw_intf - Common hardware node + * + * @hw_type: Hardware type + * @hw_idx: Hardware ID + * @hw_ops: Hardware interface function table + * @hw_priv: Private hardware node pointer + * + */ +struct cam_hw_intf { + uint32_t hw_type; + uint32_t hw_idx; + struct cam_hw_ops hw_ops; + void *hw_priv; +}; + +#endif /* _CAM_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_mgr_intf.h new file mode 100644 index 000000000000..c950336b3c61 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_hw_mgr_intf.h @@ -0,0 +1,273 @@ +/* Copyright (c) 2017-2018, 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 _CAM_HW_MGR_INTF_H_ +#define _CAM_HW_MGR_INTF_H_ + +/* + * This file declares Constants, Enums, Structures and APIs to be used as + * Interface between HW Manager and Context. + */ + + +/* maximum context numbers */ +#define CAM_CTX_MAX 8 + +/* maximum buf done irqs */ +#define CAM_NUM_OUT_PER_COMP_IRQ_MAX 12 + +/** + * enum cam_context_dump_id - + * context dump type + * + */ +enum cam_context_dump_id { + CAM_CTX_DUMP_TYPE_NONE, + CAM_CTX_DUMP_ACQ_INFO, + CAM_CTX_DUMP_TYPE_MAX, +}; + +/* hardware event callback function type */ +typedef int (*cam_hw_event_cb_func)(void *context, uint32_t evt_id, + void *evt_data); + +/* hardware page fault callback function type */ +typedef int (*cam_hw_pagefault_cb_func)(void *context, unsigned long iova, + uint32_t buf_info); + +/* ctx dump callback function type */ +typedef int (*cam_ctx_info_dump_cb_func)(void *context, + enum cam_context_dump_id dump_id); + +/** + * struct cam_hw_update_entry - Entry for hardware config + * + * @handle: Memory handle for the configuration + * @offset: Memory offset + * @len: Size of the configuration + * @flags: Flags for the config entry(eg. DMI) + * @addr: Address of hardware update entry + * + */ +struct cam_hw_update_entry { + int handle; + uint32_t offset; + uint32_t len; + uint32_t flags; + uint64_t addr; +}; + +/** + * struct cam_hw_fence_map_entry - Entry for the resource to sync id map + * + * @resrouce_handle: Resource port id for the buffer + * @sync_id: Sync id + * + */ +struct cam_hw_fence_map_entry { + uint32_t resource_handle; + int32_t sync_id; +}; + +/** + * struct cam_hw_done_event_data - Payload for hw done event + * + * @num_handles: number of handles in the event + * @resrouce_handle: list of the resource handle + * @timestamp: time stamp + * @request_id: request identifier + * + */ +struct cam_hw_done_event_data { + uint32_t num_handles; + uint32_t resource_handle[CAM_NUM_OUT_PER_COMP_IRQ_MAX]; + struct timeval timestamp; + uint64_t request_id; +}; + +/** + * struct cam_hw_acquire_args - Payload for acquire command + * + * @context_data: Context data pointer for the callback function + * @event_cb: Callback function array + * @num_acq: Total number of acquire in the payload + * @acquire_info: Acquired resource array pointer + * @ctxt_to_hw_map: HW context (returned) + * + */ +struct cam_hw_acquire_args { + void *context_data; + cam_hw_event_cb_func event_cb; + uint32_t num_acq; + uint64_t acquire_info; + void *ctxt_to_hw_map; +}; + +/** + * struct cam_hw_release_args - Payload for release command + * + * @ctxt_to_hw_map: HW context from the acquire + * @active_req: Active request flag + * + */ +struct cam_hw_release_args { + void *ctxt_to_hw_map; + bool active_req; +}; + +/** + * struct cam_hw_start_args - Payload for start command + * + * @ctxt_to_hw_map: HW context from the acquire + * @num_hw_update_entries: Number of Hardware configuration + * @hw_update_entries: Hardware configuration list + * + */ +struct cam_hw_start_args { + void *ctxt_to_hw_map; + uint32_t num_hw_update_entries; + struct cam_hw_update_entry *hw_update_entries; +}; + +/** + * struct cam_hw_stop_args - Payload for stop command + * + * @ctxt_to_hw_map: HW context from the acquire + * @args: Arguments to pass for stop + * + */ +struct cam_hw_stop_args { + void *ctxt_to_hw_map; + void *args; +}; + +/** + * struct cam_hw_prepare_update_args - Payload for prepare command + * + * @packet: CSL packet from user mode driver + * @ctxt_to_hw_map: HW context from the acquire + * @max_hw_update_entries: Maximum hardware update entries supported + * @hw_update_entries: Actual hardware update configuration (returned) + * @num_hw_update_entries: Number of actual hardware update entries (returned) + * @max_out_map_entries: Maximum output fence mapping supported + * @out_map_entries: Actual output fence mapping list (returned) + * @num_out_map_entries: Number of actual output fence mapping (returned) + * @max_in_map_entries: Maximum input fence mapping supported + * @in_map_entries: Actual input fence mapping list (returned) + * @num_in_map_entries: Number of acutal input fence mapping (returned) + * @priv: Private pointer of hw update + * + */ +struct cam_hw_prepare_update_args { + struct cam_packet *packet; + void *ctxt_to_hw_map; + uint32_t max_hw_update_entries; + struct cam_hw_update_entry *hw_update_entries; + uint32_t num_hw_update_entries; + uint32_t max_out_map_entries; + struct cam_hw_fence_map_entry *out_map_entries; + uint32_t num_out_map_entries; + uint32_t max_in_map_entries; + struct cam_hw_fence_map_entry *in_map_entries; + uint32_t num_in_map_entries; + void *priv; +}; + +/** + * struct cam_hw_config_args - Payload for config command + * + * @ctxt_to_hw_map: HW context from the acquire + * @num_hw_update_entries: Number of hardware update entries + * @hw_update_entries: Hardware update list + * @out_map_entries: Out map info + * @num_out_map_entries: Number of out map entries + * @priv: Private pointer + * @request_id: Request ID + * + */ +struct cam_hw_config_args { + void *ctxt_to_hw_map; + uint32_t num_hw_update_entries; + struct cam_hw_update_entry *hw_update_entries; + struct cam_hw_fence_map_entry *out_map_entries; + uint32_t num_out_map_entries; + void *priv; + uint64_t request_id; + bool init_packet; +}; + +/** + * struct cam_hw_flush_args - Flush arguments + * + * @ctxt_to_hw_map: HW context from the acquire + * @num_req_pending: Num request to flush, valid when flush type is REQ + * @flush_req_pending: Request pending pointers to flush + * @num_req_active: Num request to flush, valid when flush type is REQ + * @flush_req_active: Request active pointers to flush + * @flush_type: The flush type + * + */ +struct cam_hw_flush_args { + void *ctxt_to_hw_map; + uint32_t num_req_pending; + void *flush_req_pending[20]; + uint32_t num_req_active; + void *flush_req_active[20]; + enum flush_type_t flush_type; +}; + +/** + * cam_hw_mgr_intf - HW manager interface + * + * @hw_mgr_priv: HW manager object + * @hw_get_caps: Function pointer for get hw caps + * args = cam_query_cap_cmd + * @hw_acquire: Function poniter for acquire hw resources + * args = cam_hw_acquire_args + * @hw_release: Function pointer for release hw device resource + * args = cam_hw_release_args + * @hw_start: Function pointer for start hw devices + * args = cam_hw_start_args + * @hw_stop: Function pointer for stop hw devices + * args = cam_hw_stop_args + * @hw_prepare_update: Function pointer for prepare hw update for hw devices + * args = cam_hw_prepare_update_args + * @hw_config: Function pointer for configure hw devices + * args = cam_hw_config_args + * @hw_read: Function pointer for read hardware registers + * @hw_write: Function pointer for Write hardware registers + * @hw_cmd: Function pointer for any customized commands for the + * hardware manager + * @hw_open: Function pointer for HW init + * @hw_close: Function pointer for HW deinit + * @hw_flush: Function pointer for HW flush + * + */ +struct cam_hw_mgr_intf { + void *hw_mgr_priv; + + int (*hw_get_caps)(void *hw_priv, void *hw_caps_args); + int (*hw_acquire)(void *hw_priv, void *hw_acquire_args); + int (*hw_release)(void *hw_priv, void *hw_release_args); + int (*hw_start)(void *hw_priv, void *hw_start_args); + int (*hw_stop)(void *hw_priv, void *hw_stop_args); + int (*hw_prepare_update)(void *hw_priv, void *hw_prepare_update_args); + int (*hw_config)(void *hw_priv, void *hw_config_args); + int (*hw_read)(void *hw_priv, void *read_args); + int (*hw_write)(void *hw_priv, void *write_args); + int (*hw_cmd)(void *hw_priv, void *write_args); + int (*hw_open)(void *hw_priv, void *fw_download_args); + int (*hw_close)(void *hw_priv, void *hw_close_args); + int (*hw_flush)(void *hw_priv, void *hw_flush_args); +}; + +#endif /* _CAM_HW_MGR_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.c b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.c new file mode 100644 index 000000000000..9fe331de53a5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.c @@ -0,0 +1,579 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include + +#include "cam_node.h" +#include "cam_trace.h" +#include "cam_debug_util.h" + +static struct cam_context *cam_node_get_ctxt_from_free_list( + struct cam_node *node) +{ + struct cam_context *ctx = NULL; + + mutex_lock(&node->list_mutex); + if (!list_empty(&node->free_ctx_list)) { + ctx = list_first_entry(&node->free_ctx_list, + struct cam_context, list); + list_del_init(&ctx->list); + } + mutex_unlock(&node->list_mutex); + if (ctx) + kref_init(&ctx->refcount); + return ctx; +} + +void cam_node_put_ctxt_to_free_list(struct kref *ref) +{ + struct cam_context *ctx = + container_of(ref, struct cam_context, refcount); + struct cam_node *node = ctx->node; + + mutex_lock(&node->list_mutex); + list_add_tail(&ctx->list, &node->free_ctx_list); + mutex_unlock(&node->list_mutex); +} + +static int __cam_node_handle_query_cap(struct cam_node *node, + struct cam_query_cap_cmd *query) +{ + int rc = -EFAULT; + + if (!query) { + CAM_ERR(CAM_CORE, "Invalid params"); + return -EINVAL; + } + + if (node->hw_mgr_intf.hw_get_caps) { + rc = node->hw_mgr_intf.hw_get_caps( + node->hw_mgr_intf.hw_mgr_priv, query); + } + + return rc; +} +static uint64_t LrmeaRequreCount; +static uint64_t LrmeaReleaseCount; +static int __cam_node_handle_acquire_dev(struct cam_node *node, + struct cam_acquire_dev_cmd *acquire) +{ + int rc = 0; + struct cam_context *ctx = NULL; + + if (!acquire) + return -EINVAL; + if (!strcmp(node->name, "cam-lrme")) { + CAM_ERR(CAM_CORE, "Acquire device for node %s session_handle=%d,%lld", + node->name, acquire->session_handle, + ++LrmeaRequreCount); + } + ctx = cam_node_get_ctxt_from_free_list(node); + if (!ctx) { + rc = -ENOMEM; + if (!strcmp(node->name, "cam-lrme")) + CAM_ERR(CAM_CORE, "Can not get context for node %s", + node->name); + goto err; + } + + rc = cam_context_handle_acquire_dev(ctx, acquire); + if (rc) { + CAM_ERR(CAM_CORE, "Acquire device failed for node %s", + node->name); + goto free_ctx; + } + + return 0; +free_ctx: + cam_context_putref(ctx); +err: + return rc; +} + +static int __cam_node_handle_start_dev(struct cam_node *node, + struct cam_start_stop_dev_cmd *start) +{ + struct cam_context *ctx = NULL; + int rc; + + if (!start) + return -EINVAL; + + if (start->dev_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid device handle for context"); + return -EINVAL; + } + + if (start->session_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid session handle for context"); + return -EINVAL; + } + + ctx = (struct cam_context *)cam_get_device_priv(start->dev_handle); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + start->dev_handle); + return -EINVAL; + } + + rc = cam_context_handle_start_dev(ctx, start); + if (rc) + CAM_ERR(CAM_CORE, "Start failure for node %s", node->name); + + return rc; +} + +static int __cam_node_handle_stop_dev(struct cam_node *node, + struct cam_start_stop_dev_cmd *stop) +{ + struct cam_context *ctx = NULL; + int rc; + + if (!stop) + return -EINVAL; + + if (stop->dev_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid device handle for context"); + return -EINVAL; + } + + if (stop->session_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid session handle for context"); + return -EINVAL; + } + + ctx = (struct cam_context *)cam_get_device_priv(stop->dev_handle); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + stop->dev_handle); + return -EINVAL; + } + + rc = cam_context_handle_stop_dev(ctx, stop); + if (rc) + CAM_ERR(CAM_CORE, "Stop failure for node %s", node->name); + + return rc; +} + +static int __cam_node_handle_config_dev(struct cam_node *node, + struct cam_config_dev_cmd *config) +{ + struct cam_context *ctx = NULL; + int rc; + + if (!config) + return -EINVAL; + + if (config->dev_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid device handle for context"); + return -EINVAL; + } + + if (config->session_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid session handle for context"); + return -EINVAL; + } + + ctx = (struct cam_context *)cam_get_device_priv(config->dev_handle); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + config->dev_handle); + return -EINVAL; + } + + rc = cam_context_handle_config_dev(ctx, config); + if (rc) + CAM_ERR(CAM_CORE, "Config failure for node %s", node->name); + + return rc; +} + +static int __cam_node_handle_flush_dev(struct cam_node *node, + struct cam_flush_dev_cmd *flush) +{ + struct cam_context *ctx = NULL; + int rc; + + if (!flush) + return -EINVAL; + + if (flush->dev_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid device handle for context"); + return -EINVAL; + } + + if (flush->session_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid session handle for context"); + return -EINVAL; + } + + ctx = (struct cam_context *)cam_get_device_priv(flush->dev_handle); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + flush->dev_handle); + return -EINVAL; + } + + rc = cam_context_handle_flush_dev(ctx, flush); + if (rc) + CAM_ERR(CAM_CORE, "Flush failure for node %s", node->name); + + return rc; +} + +static int __cam_node_handle_release_dev(struct cam_node *node, + struct cam_release_dev_cmd *release) +{ + int rc = 0; + struct cam_context *ctx = NULL; + + if (!release) + return -EINVAL; + + if (release->dev_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid device handle for context"); + return -EINVAL; + } + + if (release->session_handle <= 0) { + CAM_ERR(CAM_CORE, "Invalid session handle for context"); + return -EINVAL; + } + if (!strcmp(node->name, "cam-lrme")) { + CAM_ERR(CAM_CORE, "cam_node_handle_release_dev %d, node %s, %lld", + release->dev_handle, + node->name, ++LrmeaReleaseCount); + } + ctx = (struct cam_context *)cam_get_device_priv(release->dev_handle); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d node %s", + release->dev_handle, node->name); + return -EINVAL; + } + + rc = cam_context_handle_release_dev(ctx, release); + if (rc) + CAM_ERR(CAM_CORE, "context release failed node %s", node->name); + + rc = cam_destroy_device_hdl(release->dev_handle); + if (rc) + CAM_ERR(CAM_CORE, "destroy device handle is failed node %s", + node->name); + + cam_context_putref(ctx); + return rc; +} + +static int __cam_node_crm_get_dev_info(struct cam_req_mgr_device_info *info) +{ + struct cam_context *ctx = NULL; + + if (!info) + return -EINVAL; + + ctx = (struct cam_context *) cam_get_device_priv(info->dev_hdl); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + info->dev_hdl); + return -EINVAL; + } + return cam_context_handle_crm_get_dev_info(ctx, info); +} + +static int __cam_node_crm_link_setup( + struct cam_req_mgr_core_dev_link_setup *setup) +{ + int rc; + struct cam_context *ctx = NULL; + + if (!setup) + return -EINVAL; + + ctx = (struct cam_context *) cam_get_device_priv(setup->dev_hdl); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + setup->dev_hdl); + return -EINVAL; + } + + if (setup->link_enable) + rc = cam_context_handle_crm_link(ctx, setup); + else + rc = cam_context_handle_crm_unlink(ctx, setup); + + return rc; +} + +static int __cam_node_crm_apply_req(struct cam_req_mgr_apply_request *apply) +{ + struct cam_context *ctx = NULL; + + if (!apply) + return -EINVAL; + + ctx = (struct cam_context *) cam_get_device_priv(apply->dev_hdl); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + apply->dev_hdl); + return -EINVAL; + } + + trace_cam_apply_req("Node", apply->request_id); + + return cam_context_handle_crm_apply_req(ctx, apply); +} + +static int __cam_node_crm_flush_req(struct cam_req_mgr_flush_request *flush) +{ + struct cam_context *ctx = NULL; + + if (!flush) { + CAM_ERR(CAM_CORE, "Invalid flush request payload"); + return -EINVAL; + } + + ctx = (struct cam_context *) cam_get_device_priv(flush->dev_hdl); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + flush->dev_hdl); + return -EINVAL; + } + + return cam_context_handle_crm_flush_req(ctx, flush); +} + +static int __cam_node_crm_process_evt( + struct cam_req_mgr_link_evt_data *evt_data) +{ + struct cam_context *ctx = NULL; + + if (!evt_data) { + CAM_ERR(CAM_CORE, "Invalid process event request payload"); + return -EINVAL; + } + + ctx = (struct cam_context *) cam_get_device_priv(evt_data->dev_hdl); + if (!ctx) { + CAM_ERR(CAM_CORE, "Can not get context for handle %d", + evt_data->dev_hdl); + return -EINVAL; + } + return cam_context_handle_crm_process_evt(ctx, evt_data); +} + +int cam_node_deinit(struct cam_node *node) +{ + if (node) + memset(node, 0, sizeof(*node)); + + CAM_DBG(CAM_CORE, "deinit complete"); + + return 0; +} + +int cam_node_shutdown(struct cam_node *node) +{ + int i = 0; + + if (!node) + return -EINVAL; + + for (i = 0; i < node->ctx_size; i++) { + if (node->ctx_list[i].dev_hdl >= 0) { + cam_context_shutdown(&(node->ctx_list[i])); + cam_destroy_device_hdl(node->ctx_list[i].dev_hdl); + cam_context_putref(&(node->ctx_list[i])); + } + } + + if (node->hw_mgr_intf.hw_close) + node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv, + NULL); + + return 0; +} + +int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf, + struct cam_context *ctx_list, uint32_t ctx_size, char *name) +{ + int rc = 0; + int i; + + if (!node || !hw_mgr_intf || + sizeof(node->hw_mgr_intf) != sizeof(*hw_mgr_intf)) { + return -EINVAL; + } + + memset(node, 0, sizeof(*node)); + + strlcpy(node->name, name, sizeof(node->name)); + + memcpy(&node->hw_mgr_intf, hw_mgr_intf, sizeof(node->hw_mgr_intf)); + node->crm_node_intf.apply_req = __cam_node_crm_apply_req; + node->crm_node_intf.get_dev_info = __cam_node_crm_get_dev_info; + node->crm_node_intf.link_setup = __cam_node_crm_link_setup; + node->crm_node_intf.flush_req = __cam_node_crm_flush_req; + node->crm_node_intf.process_evt = __cam_node_crm_process_evt; + + mutex_init(&node->list_mutex); + INIT_LIST_HEAD(&node->free_ctx_list); + node->ctx_list = ctx_list; + node->ctx_size = ctx_size; + for (i = 0; i < ctx_size; i++) { + if (!ctx_list[i].state_machine) { + CAM_ERR(CAM_CORE, + "camera context %d is not initialized", i); + rc = -1; + goto err; + } + INIT_LIST_HEAD(&ctx_list[i].list); + list_add_tail(&ctx_list[i].list, &node->free_ctx_list); + ctx_list[i].node = node; + } + + node->state = CAM_NODE_STATE_INIT; +err: + CAM_DBG(CAM_CORE, "Exit. (rc = %d)", rc); + return rc; +} + +int cam_node_handle_ioctl(struct cam_node *node, struct cam_control *cmd) +{ + int rc = 0; + + if (!cmd) + return -EINVAL; + + CAM_DBG(CAM_CORE, "handle cmd %d", cmd->op_code); + + switch (cmd->op_code) { + case CAM_QUERY_CAP: { + struct cam_query_cap_cmd query; + + if (copy_from_user(&query, u64_to_user_ptr(cmd->handle), + sizeof(query))) { + rc = -EFAULT; + break; + } + + rc = __cam_node_handle_query_cap(node, &query); + if (rc) { + CAM_ERR(CAM_CORE, "querycap is failed(rc = %d)", + rc); + break; + } + + if (copy_to_user(u64_to_user_ptr(cmd->handle), &query, + sizeof(query))) + rc = -EFAULT; + + break; + } + case CAM_ACQUIRE_DEV: { + struct cam_acquire_dev_cmd acquire; + + if (copy_from_user(&acquire, u64_to_user_ptr(cmd->handle), + sizeof(acquire))) { + rc = -EFAULT; + break; + } + rc = __cam_node_handle_acquire_dev(node, &acquire); + if (rc) { + CAM_ERR(CAM_CORE, "acquire device failed(rc = %d)", + rc); + break; + } + if (copy_to_user(u64_to_user_ptr(cmd->handle), &acquire, + sizeof(acquire))) + rc = -EFAULT; + break; + } + case CAM_START_DEV: { + struct cam_start_stop_dev_cmd start; + + if (copy_from_user(&start, u64_to_user_ptr(cmd->handle), + sizeof(start))) + rc = -EFAULT; + else { + rc = __cam_node_handle_start_dev(node, &start); + if (rc) + CAM_ERR(CAM_CORE, + "start device failed(rc = %d)", rc); + } + break; + } + case CAM_STOP_DEV: { + struct cam_start_stop_dev_cmd stop; + + if (copy_from_user(&stop, u64_to_user_ptr(cmd->handle), + sizeof(stop))) + rc = -EFAULT; + else { + rc = __cam_node_handle_stop_dev(node, &stop); + if (rc) + CAM_ERR(CAM_CORE, + "stop device failed(rc = %d)", rc); + } + break; + } + case CAM_CONFIG_DEV: { + struct cam_config_dev_cmd config; + + if (copy_from_user(&config, u64_to_user_ptr(cmd->handle), + sizeof(config))) + rc = -EFAULT; + else { + rc = __cam_node_handle_config_dev(node, &config); + if (rc) + CAM_ERR(CAM_CORE, + "config device failed(rc = %d)", rc); + } + break; + } + case CAM_RELEASE_DEV: { + struct cam_release_dev_cmd release; + + if (copy_from_user(&release, u64_to_user_ptr(cmd->handle), + sizeof(release))) + rc = -EFAULT; + else { + rc = __cam_node_handle_release_dev(node, &release); + if (rc) + CAM_ERR(CAM_CORE, + "release device failed(rc = %d)", rc); + } + break; + } + case CAM_FLUSH_REQ: { + struct cam_flush_dev_cmd flush; + + if (copy_from_user(&flush, u64_to_user_ptr(cmd->handle), + sizeof(flush))) + rc = -EFAULT; + else { + rc = __cam_node_handle_flush_dev(node, &flush); + if (rc) + CAM_ERR(CAM_CORE, + "flush device failed(rc = %d)", rc); + } + break; + } + default: + CAM_ERR(CAM_CORE, "Unknown op code %d", cmd->op_code); + rc = -EINVAL; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.h b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.h new file mode 100644 index 000000000000..4303ee38dd54 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_node.h @@ -0,0 +1,111 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_NODE_H_ +#define _CAM_NODE_H_ + +#include +#include "cam_context.h" +#include "cam_hw_mgr_intf.h" +#include "cam_req_mgr_interface.h" + +#define CAM_NODE_NAME_LENGTH_MAX 256 + +#define CAM_NODE_STATE_UNINIT 0 +#define CAM_NODE_STATE_INIT 1 + +/** + * struct cam_node - Singleton Node for camera HW devices + * + * @name: Name for struct cam_node + * @state: Node state: + * 0 = uninitialized, 1 = initialized + * @list_mutex: Mutex for the context pool + * @free_ctx_list: Free context pool list + * @ctx_list: Context list + * @ctx_size: Context list size + * @hw_mgr_intf: Interface for cam_node to HW + * @crm_node_intf: Interface for the CRM to cam_node + * + */ +struct cam_node { + char name[CAM_NODE_NAME_LENGTH_MAX]; + uint32_t state; + + /* context pool */ + struct mutex list_mutex; + struct list_head free_ctx_list; + struct cam_context *ctx_list; + uint32_t ctx_size; + + /* interfaces */ + struct cam_hw_mgr_intf hw_mgr_intf; + struct cam_req_mgr_kmd_ops crm_node_intf; +}; + +/** + * cam_node_handle_ioctl() + * + * @brief: Handle ioctl commands + * + * @node: Node handle + * @cmd: IOCTL command + * + */ +int cam_node_handle_ioctl(struct cam_node *node, struct cam_control *cmd); + +/** + * cam_node_deinit() + * + * @brief: Deinitialization function for the Node interface + * + * @node: Node handle + * + */ +int cam_node_deinit(struct cam_node *node); + +/** + * cam_node_shutdown() + * + * @brief: Shutdowns/Closes the cam node. + * + * @node: Cam_node pointer + * + */ +int cam_node_shutdown(struct cam_node *node); + +/** + * cam_node_init() + * + * @brief: Initialization function for the Node interface. + * + * @node: Cam_node pointer + * @hw_mgr_intf: HW manager interface blob + * @ctx_list: List of cam_contexts to be added + * @ctx_size: Size of the cam_context + * @name: Name for the node + * + */ +int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf, + struct cam_context *ctx_list, uint32_t ctx_size, char *name); + +/** + * cam_node_put_ctxt_to_free_list() + * + * @brief: Put context in node free list. + * + * @ref: Context's kref object + * + */ +void cam_node_put_ctxt_to_free_list(struct kref *ref); + +#endif /* _CAM_NODE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_core/cam_subdev.c b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_subdev.c new file mode 100644 index 000000000000..d690508b85c0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_core/cam_subdev.c @@ -0,0 +1,161 @@ +/* Copyright (c) 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 "cam_subdev.h" +#include "cam_node.h" +#include "cam_debug_util.h" + +/** + * cam_subdev_subscribe_event() + * + * @brief: function to subscribe to v4l2 events + * + * @sd: Pointer to struct v4l2_subdev. + * @fh: Pointer to struct v4l2_fh. + * @sub: Pointer to struct v4l2_event_subscription. + */ +static int cam_subdev_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_subscribe(fh, sub, CAM_SUBDEVICE_EVENT_MAX, NULL); +} + +/** + * cam_subdev_unsubscribe_event() + * + * @brief: function to unsubscribe from v4l2 events + * + * @sd: Pointer to struct v4l2_subdev. + * @fh: Pointer to struct v4l2_fh. + * @sub: Pointer to struct v4l2_event_subscription. + */ +static int cam_subdev_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} + +static long cam_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, + void *arg) +{ + long rc; + struct cam_node *node = + (struct cam_node *) v4l2_get_subdevdata(sd); + + if (!node || node->state == CAM_NODE_STATE_UNINIT) { + rc = -EINVAL; + goto end; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_node_handle_ioctl(node, + (struct cam_control *) arg); + break; + default: + CAM_ERR(CAM_CORE, "Invalid command %d for %s", cmd, + node->name); + rc = -EINVAL; + } +end: + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_subdev_compat_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int rc; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_CORE, "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + rc = cam_subdev_ioctl(sd, cmd, &cmd_data); + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_CORE, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + +const struct v4l2_subdev_core_ops cam_subdev_core_ops = { + .ioctl = cam_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_subdev_compat_ioctl, +#endif + .subscribe_event = cam_subdev_subscribe_event, + .unsubscribe_event = cam_subdev_unsubscribe_event, +}; + +static const struct v4l2_subdev_ops cam_subdev_ops = { + .core = &cam_subdev_core_ops, +}; + +int cam_subdev_remove(struct cam_subdev *sd) +{ + if (!sd) + return -EINVAL; + + cam_unregister_subdev(sd); + cam_node_deinit((struct cam_node *)sd->token); + kfree(sd->token); + + return 0; +} + +int cam_subdev_probe(struct cam_subdev *sd, struct platform_device *pdev, + char *name, uint32_t dev_type) +{ + int rc; + struct cam_node *node = NULL; + + if (!sd || !pdev || !name) + return -EINVAL; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + /* Setup camera v4l2 subdevice */ + sd->pdev = pdev; + sd->name = name; + sd->ops = &cam_subdev_ops; + sd->token = node; + sd->sd_flags = + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->ent_function = dev_type; + + rc = cam_register_subdev(sd); + if (rc) { + CAM_ERR(CAM_CORE, "cam_register_subdev() failed for dev: %s", + sd->name); + goto err; + } + platform_set_drvdata(sd->pdev, sd); + return rc; +err: + kfree(node); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_cpas/Makefile new file mode 100644 index 000000000000..68e4a1ceff9b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/Makefile @@ -0,0 +1,10 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top + +obj-$(CONFIG_SPECTRA_CAMERA) += cpas_top/ +obj-$(CONFIG_SPECTRA_CAMERA) += camss_top/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cpas_soc.o cam_cpas_intf.o cam_cpas_hw.o \ No newline at end of file diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.c new file mode 100644 index 000000000000..5e4ff0d73f67 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.c @@ -0,0 +1,1692 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include + +#include "cam_cpas_hw.h" +#include "cam_cpas_hw_intf.h" +#include "cam_cpas_soc.h" + +#define CAM_CPAS_AXI_MIN_MNOC_AB_BW (2048 * 1024) +#define CAM_CPAS_AXI_MIN_MNOC_IB_BW (2048 * 1024) +#define CAM_CPAS_AXI_MIN_CAMNOC_AB_BW (2048 * 1024) +#define CAM_CPAS_AXI_MIN_CAMNOC_IB_BW (3000000000L) + +static uint cam_min_camnoc_ib_bw; +module_param(cam_min_camnoc_ib_bw, uint, 0644); + +int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw, + enum cam_cpas_reg_base reg_base, struct cam_cpas_reg *reg_info) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + uint32_t value; + int reg_base_index; + + if (reg_info->enable == false) + return 0; + + reg_base_index = cpas_core->regbase_index[reg_base]; + if (reg_base_index == -1) + return -EINVAL; + + if (reg_info->masked_value) { + value = cam_io_r_mb( + soc_info->reg_map[reg_base_index].mem_base + + reg_info->offset); + value = value & (~reg_info->mask); + value = value | (reg_info->value << reg_info->shift); + } else { + value = reg_info->value; + } + + CAM_DBG(CAM_CPAS, "Base[%d] Offset[0x%8x] Value[0x%8x]", + reg_base, reg_info->offset, value); + + cam_io_w_mb(value, soc_info->reg_map[reg_base_index].mem_base + + reg_info->offset); + + return 0; +} + +static int cam_cpas_util_vote_bus_client_level( + struct cam_cpas_bus_client *bus_client, unsigned int level) +{ + if (!bus_client->valid || (bus_client->dyn_vote == true)) { + CAM_ERR(CAM_CPAS, "Invalid params %d %d", bus_client->valid, + bus_client->dyn_vote); + return -EINVAL; + } + + if (level >= bus_client->num_usecases) { + CAM_ERR(CAM_CPAS, "Invalid vote level=%d, usecases=%d", level, + bus_client->num_usecases); + return -EINVAL; + } + + if (level == bus_client->curr_vote_level) + return 0; + + CAM_DBG(CAM_CPAS, "Bus client[%d] index[%d]", bus_client->client_id, + level); + msm_bus_scale_client_update_request(bus_client->client_id, level); + bus_client->curr_vote_level = level; + + return 0; +} + +static int cam_cpas_util_vote_bus_client_bw( + struct cam_cpas_bus_client *bus_client, uint64_t ab, uint64_t ib, + bool camnoc_bw) +{ + struct msm_bus_paths *path; + struct msm_bus_scale_pdata *pdata; + int idx = 0; + uint64_t min_camnoc_ib_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW; + + if (cam_min_camnoc_ib_bw > 0) + min_camnoc_ib_bw = (uint64_t)cam_min_camnoc_ib_bw * 1000000L; + + CAM_DBG(CAM_CPAS, "cam_min_camnoc_ib_bw = %d, min_camnoc_ib_bw=%llu", + cam_min_camnoc_ib_bw, min_camnoc_ib_bw); + + if (!bus_client->valid) { + CAM_ERR(CAM_CPAS, "bus client not valid"); + return -EINVAL; + } + + if ((bus_client->num_usecases != 2) || + (bus_client->num_paths != 1) || + (bus_client->dyn_vote != true)) { + CAM_ERR(CAM_CPAS, "dynamic update not allowed %d %d %d", + bus_client->num_usecases, bus_client->num_paths, + bus_client->dyn_vote); + return -EINVAL; + } + + mutex_lock(&bus_client->lock); + + if (bus_client->curr_vote_level > 1) { + CAM_ERR(CAM_CPAS, "curr_vote_level %d cannot be greater than 1", + bus_client->curr_vote_level); + mutex_unlock(&bus_client->lock); + return -EINVAL; + } + + idx = bus_client->curr_vote_level; + idx = 1 - idx; + bus_client->curr_vote_level = idx; + mutex_unlock(&bus_client->lock); + + if (camnoc_bw == true) { + if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_CAMNOC_AB_BW)) + ab = CAM_CPAS_AXI_MIN_CAMNOC_AB_BW; + + if ((ib > 0) && (ib < min_camnoc_ib_bw)) + ib = min_camnoc_ib_bw; + } else { + if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_MNOC_AB_BW)) + ab = CAM_CPAS_AXI_MIN_MNOC_AB_BW; + + if ((ib > 0) && (ib < CAM_CPAS_AXI_MIN_MNOC_IB_BW)) + ib = CAM_CPAS_AXI_MIN_MNOC_IB_BW; + } + + pdata = bus_client->pdata; + path = &(pdata->usecase[idx]); + path->vectors[0].ab = ab; + path->vectors[0].ib = ib; + + CAM_DBG(CAM_CPAS, "Bus client[%d] :ab[%llu] ib[%llu], index[%d]", + bus_client->client_id, ab, ib, idx); + msm_bus_scale_client_update_request(bus_client->client_id, idx); + + return 0; +} + +static int cam_cpas_util_register_bus_client( + struct cam_hw_soc_info *soc_info, struct device_node *dev_node, + struct cam_cpas_bus_client *bus_client) +{ + struct msm_bus_scale_pdata *pdata = NULL; + uint32_t client_id; + int rc; + + pdata = msm_bus_pdata_from_node(soc_info->pdev, + dev_node); + if (!pdata) { + CAM_ERR(CAM_CPAS, "failed get_pdata"); + return -EINVAL; + } + + if ((pdata->num_usecases == 0) || + (pdata->usecase[0].num_paths == 0)) { + CAM_ERR(CAM_CPAS, "usecase=%d", pdata->num_usecases); + rc = -EINVAL; + goto error; + } + + client_id = msm_bus_scale_register_client(pdata); + if (!client_id) { + CAM_ERR(CAM_CPAS, "failed in register ahb bus client"); + rc = -EINVAL; + goto error; + } + + bus_client->dyn_vote = of_property_read_bool(dev_node, + "qcom,msm-bus-vector-dyn-vote"); + + if (bus_client->dyn_vote && (pdata->num_usecases != 2)) { + CAM_ERR(CAM_CPAS, "Excess or less vectors %d", + pdata->num_usecases); + rc = -EINVAL; + goto fail_unregister_client; + } + + msm_bus_scale_client_update_request(client_id, 0); + + bus_client->src = pdata->usecase[0].vectors[0].src; + bus_client->dst = pdata->usecase[0].vectors[0].dst; + bus_client->pdata = pdata; + bus_client->client_id = client_id; + bus_client->num_usecases = pdata->num_usecases; + bus_client->num_paths = pdata->usecase[0].num_paths; + bus_client->curr_vote_level = 0; + bus_client->valid = true; + mutex_init(&bus_client->lock); + + CAM_DBG(CAM_CPAS, "Bus Client : src=%d, dst=%d, bus_client=%d", + bus_client->src, bus_client->dst, bus_client->client_id); + + return 0; +fail_unregister_client: + msm_bus_scale_unregister_client(bus_client->client_id); +error: + return rc; + +} + +static int cam_cpas_util_unregister_bus_client( + struct cam_cpas_bus_client *bus_client) +{ + if (!bus_client->valid) + return -EINVAL; + + if (bus_client->dyn_vote) + cam_cpas_util_vote_bus_client_bw(bus_client, 0, 0, false); + else + cam_cpas_util_vote_bus_client_level(bus_client, 0); + + msm_bus_scale_unregister_client(bus_client->client_id); + bus_client->valid = false; + + mutex_destroy(&bus_client->lock); + + return 0; +} + +static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core, + struct cam_hw_soc_info *soc_info) +{ + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *)soc_info->soc_private; + struct cam_cpas_axi_port *curr_port; + struct cam_cpas_axi_port *temp_port; + + list_for_each_entry_safe(curr_port, temp_port, + &cpas_core->axi_ports_list_head, sibling_port) { + cam_cpas_util_unregister_bus_client(&curr_port->mnoc_bus); + of_node_put(curr_port->axi_port_mnoc_node); + if (soc_private->axi_camnoc_based) { + cam_cpas_util_unregister_bus_client( + &curr_port->camnoc_bus); + of_node_put(curr_port->axi_port_camnoc_node); + } + of_node_put(curr_port->axi_port_node); + list_del(&curr_port->sibling_port); + mutex_destroy(&curr_port->lock); + kfree(curr_port); + } + + of_node_put(soc_private->axi_port_list_node); + + return 0; +} + +static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core, + struct cam_hw_soc_info *soc_info) +{ + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *)soc_info->soc_private; + struct cam_cpas_axi_port *axi_port; + int rc; + struct device_node *axi_port_list_node; + struct device_node *axi_port_node = NULL; + struct device_node *axi_port_mnoc_node = NULL; + struct device_node *axi_port_camnoc_node = NULL; + + INIT_LIST_HEAD(&cpas_core->axi_ports_list_head); + + axi_port_list_node = of_find_node_by_name(soc_info->pdev->dev.of_node, + "qcom,axi-port-list"); + if (!axi_port_list_node) { + CAM_ERR(CAM_CPAS, "Node qcom,axi-port-list not found."); + return -EINVAL; + } + + soc_private->axi_port_list_node = axi_port_list_node; + + for_each_available_child_of_node(axi_port_list_node, axi_port_node) { + axi_port = kzalloc(sizeof(*axi_port), GFP_KERNEL); + if (!axi_port) { + rc = -ENOMEM; + goto error_previous_axi_cleanup; + } + axi_port->axi_port_node = axi_port_node; + + rc = of_property_read_string_index(axi_port_node, + "qcom,axi-port-name", 0, + (const char **)&axi_port->axi_port_name); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to read qcom,axi-port-name rc=%d", rc); + goto port_name_fail; + } + + axi_port_mnoc_node = of_find_node_by_name(axi_port_node, + "qcom,axi-port-mnoc"); + if (!axi_port_mnoc_node) { + CAM_ERR(CAM_CPAS, "Node qcom,axi-port-mnoc not found."); + rc = -EINVAL; + goto mnoc_node_get_fail; + } + axi_port->axi_port_mnoc_node = axi_port_mnoc_node; + + rc = cam_cpas_util_register_bus_client(soc_info, + axi_port_mnoc_node, &axi_port->mnoc_bus); + if (rc) + goto mnoc_register_fail; + + if (soc_private->axi_camnoc_based) { + axi_port_camnoc_node = of_find_node_by_name( + axi_port_node, "qcom,axi-port-camnoc"); + if (!axi_port_camnoc_node) { + CAM_ERR(CAM_CPAS, + "Node qcom,axi-port-camnoc not found"); + rc = -EINVAL; + goto camnoc_node_get_fail; + } + axi_port->axi_port_camnoc_node = axi_port_camnoc_node; + + rc = cam_cpas_util_register_bus_client(soc_info, + axi_port_camnoc_node, &axi_port->camnoc_bus); + if (rc) + goto camnoc_register_fail; + } + + mutex_init(&axi_port->lock); + + INIT_LIST_HEAD(&axi_port->sibling_port); + list_add_tail(&axi_port->sibling_port, + &cpas_core->axi_ports_list_head); + INIT_LIST_HEAD(&axi_port->clients_list_head); + } + + return 0; +camnoc_register_fail: + of_node_put(axi_port->axi_port_camnoc_node); +camnoc_node_get_fail: + cam_cpas_util_unregister_bus_client(&axi_port->mnoc_bus); +mnoc_register_fail: + of_node_put(axi_port->axi_port_mnoc_node); +mnoc_node_get_fail: +port_name_fail: + of_node_put(axi_port->axi_port_node); + kfree(axi_port); +error_previous_axi_cleanup: + cam_cpas_util_axi_cleanup(cpas_core, soc_info); + return rc; +} + +static int cam_cpas_util_vote_default_ahb_axi(struct cam_hw_info *cpas_hw, + int enable) +{ + int rc; + struct cam_cpas *cpas_core = (struct cam_cpas *)cpas_hw->core_info; + struct cam_cpas_axi_port *curr_port; + struct cam_cpas_axi_port *temp_port; + uint64_t camnoc_bw, mnoc_bw; + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + + rc = cam_cpas_util_vote_bus_client_level(&cpas_core->ahb_bus_client, + (enable == true) ? CAM_SVS_VOTE : CAM_SUSPEND_VOTE); + if (rc) { + CAM_ERR(CAM_CPAS, "Failed in AHB vote, enable=%d, rc=%d", + enable, rc); + return rc; + } + + if (enable) { + mnoc_bw = CAM_CPAS_DEFAULT_AXI_BW; + camnoc_bw = CAM_CPAS_DEFAULT_AXI_BW; + } else { + mnoc_bw = 0; + camnoc_bw = 0; + } + + list_for_each_entry_safe(curr_port, temp_port, + &cpas_core->axi_ports_list_head, sibling_port) { + rc = cam_cpas_util_vote_bus_client_bw(&curr_port->mnoc_bus, + mnoc_bw, mnoc_bw, false); + if (rc) { + CAM_ERR(CAM_CPAS, + "Failed in mnoc vote, enable=%d, rc=%d", + enable, rc); + goto remove_ahb_vote; + } + + if (soc_private->axi_camnoc_based) { + cam_cpas_util_vote_bus_client_bw( + &curr_port->camnoc_bus, 0, camnoc_bw, true); + if (rc) { + CAM_ERR(CAM_CPAS, + "Failed in mnoc vote, enable=%d, %d", + enable, rc); + cam_cpas_util_vote_bus_client_bw( + &curr_port->mnoc_bus, 0, 0, false); + goto remove_ahb_vote; + } + } + } + + return 0; +remove_ahb_vote: + cam_cpas_util_vote_bus_client_level(&cpas_core->ahb_bus_client, + CAM_SUSPEND_VOTE); + return rc; +} + +static int cam_cpas_util_insert_client_to_axi_port(struct cam_cpas *cpas_core, + struct cam_cpas_private_soc *soc_private, + struct cam_cpas_client *cpas_client, int32_t client_indx) +{ + struct cam_cpas_axi_port *curr_port; + struct cam_cpas_axi_port *temp_port; + + list_for_each_entry_safe(curr_port, temp_port, + &cpas_core->axi_ports_list_head, sibling_port) { + if (strnstr(curr_port->axi_port_name, + soc_private->client_axi_port_name[client_indx], + strlen(curr_port->axi_port_name))) { + + cpas_client->axi_port = curr_port; + INIT_LIST_HEAD(&cpas_client->axi_sibling_client); + + mutex_lock(&curr_port->lock); + list_add_tail(&cpas_client->axi_sibling_client, + &cpas_client->axi_port->clients_list_head); + mutex_unlock(&curr_port->lock); + break; + } + } + + return 0; +} + +static void cam_cpas_util_remove_client_from_axi_port( + struct cam_cpas_client *cpas_client) +{ + mutex_lock(&cpas_client->axi_port->lock); + list_del(&cpas_client->axi_sibling_client); + mutex_unlock(&cpas_client->axi_port->lock); +} + +static int cam_cpas_hw_reg_write(struct cam_hw_info *cpas_hw, + uint32_t client_handle, enum cam_cpas_reg_base reg_base, + uint32_t offset, bool mb, uint32_t value) +{ + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + int reg_base_index = cpas_core->regbase_index[reg_base]; + uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle); + int rc = 0; + + if (reg_base_index < 0 || reg_base_index >= soc_info->num_reg_map) { + CAM_ERR(CAM_CPAS, + "Invalid reg_base=%d, reg_base_index=%d, num_map=%d", + reg_base, reg_base_index, soc_info->num_reg_map); + return -EINVAL; + } + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client has not started%d", client_indx); + rc = -EPERM; + goto unlock_client; + } + + if (mb) + cam_io_w_mb(value, + soc_info->reg_map[reg_base_index].mem_base + offset); + else + cam_io_w(value, + soc_info->reg_map[reg_base_index].mem_base + offset); + +unlock_client: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + return rc; +} + +static int cam_cpas_hw_reg_read(struct cam_hw_info *cpas_hw, + uint32_t client_handle, enum cam_cpas_reg_base reg_base, + uint32_t offset, bool mb, uint32_t *value) +{ + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + int reg_base_index = cpas_core->regbase_index[reg_base]; + uint32_t reg_value; + uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle); + int rc = 0; + + if (!value) + return -EINVAL; + + if (reg_base_index < 0 || reg_base_index >= soc_info->num_reg_map) { + CAM_ERR(CAM_CPAS, + "Invalid reg_base=%d, reg_base_index=%d, num_map=%d", + reg_base, reg_base_index, soc_info->num_reg_map); + return -EINVAL; + } + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client has not started%d", client_indx); + rc = -EPERM; + goto unlock_client; + } + + if (mb) + reg_value = cam_io_r_mb( + soc_info->reg_map[reg_base_index].mem_base + offset); + else + reg_value = cam_io_r( + soc_info->reg_map[reg_base_index].mem_base + offset); + + *value = reg_value; + +unlock_client: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + return rc; +} + +static int cam_cpas_util_set_camnoc_axi_clk_rate( + struct cam_hw_info *cpas_hw) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + int rc = 0; + + CAM_DBG(CAM_CPAS, "control_camnoc_axi_clk=%d", + soc_private->control_camnoc_axi_clk); + + if (soc_private->control_camnoc_axi_clk) { + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + struct cam_cpas_axi_port *curr_axi_port = NULL; + struct cam_cpas_axi_port *temp_axi_port = NULL; + uint64_t required_camnoc_bw = 0; + int32_t clk_rate = 0; + + list_for_each_entry_safe(curr_axi_port, temp_axi_port, + &cpas_core->axi_ports_list_head, sibling_port) { + + if (curr_axi_port->consolidated_axi_vote.uncompressed_bw + > required_camnoc_bw) + required_camnoc_bw = curr_axi_port-> + consolidated_axi_vote.uncompressed_bw; + + CAM_DBG(CAM_CPAS, "[%s] : curr=%llu, overal=%llu", + curr_axi_port->axi_port_name, + curr_axi_port->consolidated_axi_vote. + uncompressed_bw, + required_camnoc_bw); + } + + required_camnoc_bw += (required_camnoc_bw * + soc_private->camnoc_axi_clk_bw_margin) / 100; + + if ((required_camnoc_bw > 0) && + (required_camnoc_bw < CAM_CPAS_AXI_MIN_CAMNOC_IB_BW)) + required_camnoc_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW; + + clk_rate = required_camnoc_bw / soc_private->camnoc_bus_width; + + CAM_DBG(CAM_CPAS, "Setting camnoc axi clk rate : %llu %d", + required_camnoc_bw, clk_rate); + + rc = cam_soc_util_set_clk_rate( + soc_info->clk[soc_info->src_clk_idx], + soc_info->clk_name[soc_info->src_clk_idx], + clk_rate); + if (!rc) + CAM_ERR(CAM_CPAS, + "Failed in setting camnoc axi clk %llu %d %d", + required_camnoc_bw, clk_rate, rc); + } + + return rc; +} + +static int cam_cpas_util_apply_client_axi_vote( + struct cam_hw_info *cpas_hw, + struct cam_cpas_client *cpas_client, + struct cam_axi_vote *axi_vote) +{ + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + struct cam_cpas_client *curr_client; + struct cam_cpas_client *temp_client; + struct cam_axi_vote req_axi_vote = *axi_vote; + struct cam_cpas_axi_port *axi_port = cpas_client->axi_port; + uint64_t camnoc_bw = 0, mnoc_bw = 0; + int rc = 0; + + if (!axi_port) { + CAM_ERR(CAM_CPAS, "axi port does not exists"); + return -EINVAL; + } + + /* + * Make sure we use same bw for both compressed, uncompressed + * in case client has requested either of one only + */ + if (req_axi_vote.compressed_bw == 0) + req_axi_vote.compressed_bw = req_axi_vote.uncompressed_bw; + + if (req_axi_vote.uncompressed_bw == 0) + req_axi_vote.uncompressed_bw = req_axi_vote.compressed_bw; + + if ((cpas_client->axi_vote.compressed_bw == + req_axi_vote.compressed_bw) && + (cpas_client->axi_vote.uncompressed_bw == + req_axi_vote.uncompressed_bw)) + return 0; + + mutex_lock(&axi_port->lock); + cpas_client->axi_vote = req_axi_vote; + + list_for_each_entry_safe(curr_client, temp_client, + &axi_port->clients_list_head, axi_sibling_client) { + camnoc_bw += curr_client->axi_vote.uncompressed_bw; + mnoc_bw += curr_client->axi_vote.compressed_bw; + } + + if ((!soc_private->axi_camnoc_based) && (mnoc_bw < camnoc_bw)) + mnoc_bw = camnoc_bw; + + axi_port->consolidated_axi_vote.compressed_bw = mnoc_bw; + axi_port->consolidated_axi_vote.uncompressed_bw = camnoc_bw; + + CAM_DBG(CAM_CPAS, + "axi[(%d, %d),(%d, %d)] : camnoc_bw[%llu], mnoc_bw[%llu]", + axi_port->mnoc_bus.src, axi_port->mnoc_bus.dst, + axi_port->camnoc_bus.src, axi_port->camnoc_bus.dst, + camnoc_bw, mnoc_bw); + + rc = cam_cpas_util_vote_bus_client_bw(&axi_port->mnoc_bus, + mnoc_bw, mnoc_bw, false); + if (rc) { + CAM_ERR(CAM_CPAS, + "Failed in mnoc vote ab[%llu] ib[%llu] rc=%d", + mnoc_bw, mnoc_bw, rc); + goto unlock_axi_port; + } + + if (soc_private->axi_camnoc_based) { + rc = cam_cpas_util_vote_bus_client_bw(&axi_port->camnoc_bus, + 0, camnoc_bw, true); + if (rc) { + CAM_ERR(CAM_CPAS, + "Failed camnoc vote ab[%llu] ib[%llu] rc=%d", + (uint64_t)0, camnoc_bw, rc); + goto unlock_axi_port; + } + } + + mutex_unlock(&axi_port->lock); + + rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc); + + return rc; + +unlock_axi_port: + mutex_unlock(&axi_port->lock); + return rc; +} + +static int cam_cpas_hw_update_axi_vote(struct cam_hw_info *cpas_hw, + uint32_t client_handle, struct cam_axi_vote *client_axi_vote) +{ + struct cam_axi_vote axi_vote; + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle); + int rc = 0; + + if (!client_axi_vote) { + CAM_ERR(CAM_CPAS, "Invalid arg client_handle=%d", + client_handle); + return -EINVAL; + } + + axi_vote = *client_axi_vote; + + if ((axi_vote.compressed_bw == 0) && + (axi_vote.uncompressed_bw == 0)) { + CAM_DBG(CAM_CPAS, "0 vote from client_handle=%d", + client_handle); + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + } + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_hw->hw_mutex); + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client has not started %d", client_indx); + rc = -EPERM; + goto unlock_client; + } + + CAM_DBG(CAM_CPAS, + "Client[%d] Requested compressed[%llu], uncompressed[%llu]", + client_indx, axi_vote.compressed_bw, + axi_vote.uncompressed_bw); + + rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, + cpas_core->cpas_client[client_indx], &axi_vote); + +unlock_client: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + mutex_unlock(&cpas_hw->hw_mutex); + return rc; +} + +static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw, + struct device *dev, unsigned long freq, enum cam_vote_level *req_level) +{ + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + struct dev_pm_opp *opp; + unsigned int corner; + enum cam_vote_level level = CAM_SVS_VOTE; + unsigned long corner_freq = freq; + int i; + + if (!dev || !req_level) { + CAM_ERR(CAM_CPAS, "Invalid params %pK, %pK", dev, req_level); + return -EINVAL; + } + + opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq); + if (IS_ERR(opp)) { + CAM_DBG(CAM_CPAS, "OPP Ceil not available for freq :%ld, %pK", + corner_freq, opp); + *req_level = CAM_TURBO_VOTE; + return 0; + } + + corner = dev_pm_opp_get_voltage(opp); + + for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++) + if (corner == soc_private->vdd_ahb[i].vdd_corner) + level = soc_private->vdd_ahb[i].ahb_level; + + CAM_DBG(CAM_CPAS, + "From OPP table : freq=[%ld][%ld], corner=%d, level=%d", + freq, corner_freq, corner, level); + + *req_level = level; + + return 0; +} + +static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw, + struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote, + enum cam_vote_level *applied_level) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client; + enum cam_vote_level required_level; + enum cam_vote_level highest_level; + int i, rc = 0; + + if (!ahb_bus_client->valid) { + CAM_ERR(CAM_CPAS, "AHB Bus client not valid"); + return -EINVAL; + } + + if (ahb_vote->type == CAM_VOTE_DYNAMIC) { + rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev, + ahb_vote->vote.freq, &required_level); + if (rc) + return rc; + } else { + required_level = ahb_vote->vote.level; + } + + if (cpas_client->ahb_level == required_level) + return 0; + + mutex_lock(&ahb_bus_client->lock); + cpas_client->ahb_level = required_level; + + CAM_DBG(CAM_CPAS, "Clients required level[%d], curr_level[%d]", + required_level, ahb_bus_client->curr_vote_level); + + if (required_level == ahb_bus_client->curr_vote_level) + goto unlock_bus_client; + + highest_level = required_level; + for (i = 0; i < cpas_core->num_clients; i++) { + if (cpas_core->cpas_client[i] && (highest_level < + cpas_core->cpas_client[i]->ahb_level)) + highest_level = cpas_core->cpas_client[i]->ahb_level; + } + + CAM_DBG(CAM_CPAS, "Required highest_level[%d]", highest_level); + + rc = cam_cpas_util_vote_bus_client_level(ahb_bus_client, + highest_level); + if (rc) { + CAM_ERR(CAM_CPAS, "Failed in ahb vote, level=%d, rc=%d", + highest_level, rc); + goto unlock_bus_client; + } + + rc = cam_soc_util_set_clk_rate_level(&cpas_hw->soc_info, highest_level); + if (rc) { + CAM_ERR(CAM_CPAS, + "Failed in scaling clock rate level %d for AHB", + highest_level); + goto unlock_bus_client; + } + + if (applied_level) + *applied_level = highest_level; + +unlock_bus_client: + mutex_unlock(&ahb_bus_client->lock); + return rc; +} + +static int cam_cpas_hw_update_ahb_vote(struct cam_hw_info *cpas_hw, + uint32_t client_handle, struct cam_ahb_vote *client_ahb_vote) +{ + struct cam_ahb_vote ahb_vote; + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle); + int rc = 0; + + if (!client_ahb_vote) { + CAM_ERR(CAM_CPAS, "Invalid input arg"); + return -EINVAL; + } + + ahb_vote = *client_ahb_vote; + + if (ahb_vote.vote.level == 0) { + CAM_DBG(CAM_CPAS, "0 ahb vote from client %d", + client_handle); + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + } + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_hw->hw_mutex); + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client has not started %d", client_indx); + rc = -EPERM; + goto unlock_client; + } + + CAM_DBG(CAM_CPAS, + "client[%d] : type[%d], level[%d], freq[%ld], applied[%d]", + client_indx, ahb_vote.type, ahb_vote.vote.level, + ahb_vote.vote.freq, + cpas_core->cpas_client[client_indx]->ahb_level); + + rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, + cpas_core->cpas_client[client_indx], &ahb_vote, NULL); + +unlock_client: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + mutex_unlock(&cpas_hw->hw_mutex); + return rc; +} + +static int cam_cpas_hw_start(void *hw_priv, void *start_args, + uint32_t arg_size) +{ + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + uint32_t client_indx; + struct cam_cpas_hw_cmd_start *cmd_hw_start; + struct cam_cpas_client *cpas_client; + struct cam_ahb_vote *ahb_vote; + struct cam_axi_vote *axi_vote; + enum cam_vote_level applied_level = CAM_SVS_VOTE; + int rc; + struct cam_cpas_private_soc *soc_private = NULL; + + if (!hw_priv || !start_args) { + CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK", + hw_priv, start_args); + return -EINVAL; + } + + if (sizeof(struct cam_cpas_hw_cmd_start) != arg_size) { + CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d", + sizeof(struct cam_cpas_hw_cmd_start), arg_size); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *)hw_priv; + cpas_core = (struct cam_cpas *) cpas_hw->core_info; + soc_private = (struct cam_cpas_private_soc *) + cpas_hw->soc_info.soc_private; + cmd_hw_start = (struct cam_cpas_hw_cmd_start *)start_args; + client_indx = CAM_CPAS_GET_CLIENT_IDX(cmd_hw_start->client_handle); + ahb_vote = cmd_hw_start->ahb_vote; + axi_vote = cmd_hw_start->axi_vote; + + if (!ahb_vote || !axi_vote) + return -EINVAL; + + if ((ahb_vote->vote.level == 0) || ((axi_vote->compressed_bw == 0) && + (axi_vote->uncompressed_bw == 0))) { + CAM_ERR(CAM_CPAS, "Invalid vote ahb[%d], axi[%llu], [%llu]", + ahb_vote->vote.level, axi_vote->compressed_bw, + axi_vote->uncompressed_bw); + return -EINVAL; + } + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_hw->hw_mutex); + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client is not registered %d", client_indx); + rc = -EPERM; + goto done; + } + + if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "Client %d is in start state", client_indx); + rc = -EPERM; + goto done; + } + + cpas_client = cpas_core->cpas_client[client_indx]; + + CAM_DBG(CAM_CPAS, "AHB :client[%d] type[%d], level[%d], applied[%d]", + client_indx, ahb_vote->type, ahb_vote->vote.level, + cpas_client->ahb_level); + rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, + ahb_vote, &applied_level); + if (rc) + goto done; + + CAM_DBG(CAM_CPAS, + "AXI client[%d] compressed_bw[%llu], uncompressed_bw[%llu]", + client_indx, axi_vote->compressed_bw, + axi_vote->uncompressed_bw); + rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, + cpas_client, axi_vote); + if (rc) + goto done; + + if (cpas_core->streamon_clients == 0) { + atomic_set(&cpas_core->irq_count, 1); + rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info, + applied_level); + if (rc) { + atomic_set(&cpas_core->irq_count, 0); + CAM_ERR(CAM_CPAS, "enable_resorce failed, rc=%d", rc); + goto done; + } + + if (cpas_core->internal_ops.power_on) { + rc = cpas_core->internal_ops.power_on(cpas_hw); + if (rc) { + atomic_set(&cpas_core->irq_count, 0); + cam_cpas_soc_disable_resources( + &cpas_hw->soc_info, true, true); + CAM_ERR(CAM_CPAS, + "failed in power_on settings rc=%d", + rc); + goto done; + } + } + CAM_DBG(CAM_CPAS, "irq_count=%d\n", + atomic_read(&cpas_core->irq_count)); + cpas_hw->hw_state = CAM_HW_STATE_POWER_UP; + } + + cpas_client->started = true; + cpas_core->streamon_clients++; + + CAM_DBG(CAM_CPAS, "client=%s, streamon_clients=%d", + soc_private->client_name[client_indx], + cpas_core->streamon_clients); +done: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + mutex_unlock(&cpas_hw->hw_mutex); + return rc; +} + +static int _check_irq_count(struct cam_cpas *cpas_core) +{ + return (atomic_read(&cpas_core->irq_count) > 0) ? 0 : 1; +} + +static int cam_cpas_hw_stop(void *hw_priv, void *stop_args, + uint32_t arg_size) +{ + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + uint32_t client_indx; + struct cam_cpas_hw_cmd_stop *cmd_hw_stop; + struct cam_cpas_client *cpas_client; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + struct cam_cpas_private_soc *soc_private = NULL; + int rc = 0; + long result; + + if (!hw_priv || !stop_args) { + CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK", + hw_priv, stop_args); + return -EINVAL; + } + + if (sizeof(struct cam_cpas_hw_cmd_stop) != arg_size) { + CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d", + sizeof(struct cam_cpas_hw_cmd_stop), arg_size); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *)hw_priv; + cpas_core = (struct cam_cpas *) cpas_hw->core_info; + soc_private = (struct cam_cpas_private_soc *) + cpas_hw->soc_info.soc_private; + cmd_hw_stop = (struct cam_cpas_hw_cmd_stop *)stop_args; + client_indx = CAM_CPAS_GET_CLIENT_IDX(cmd_hw_stop->client_handle); + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_hw->hw_mutex); + mutex_lock(&cpas_core->client_mutex[client_indx]); + + CAM_DBG(CAM_CPAS, "client=%s, streamon_clients=%d", + soc_private->client_name[client_indx], + cpas_core->streamon_clients); + + if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "Client %d is not started", client_indx); + rc = -EPERM; + goto done; + } + + cpas_client = cpas_core->cpas_client[client_indx]; + cpas_client->started = false; + cpas_core->streamon_clients--; + + if (cpas_core->streamon_clients == 0) { + if (cpas_core->internal_ops.power_off) { + rc = cpas_core->internal_ops.power_off(cpas_hw); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed in power_off settings rc=%d", + rc); + /* Do not return error, passthrough */ + } + } + + rc = cam_cpas_soc_disable_irq(&cpas_hw->soc_info); + if (rc) { + CAM_ERR(CAM_CPAS, "disable_irq failed, rc=%d", rc); + goto done; + } + + /* Wait for any IRQs still being handled */ + atomic_dec(&cpas_core->irq_count); + result = wait_event_timeout(cpas_core->irq_count_wq, + _check_irq_count(cpas_core), HZ); + if (result == 0) { + CAM_ERR(CAM_CPAS, "Wait failed: irq_count=%d", + atomic_read(&cpas_core->irq_count)); + } + + rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info, + true, false); + if (rc) { + CAM_ERR(CAM_CPAS, "disable_resorce failed, rc=%d", rc); + goto done; + } + CAM_DBG(CAM_CPAS, "Disabled all the resources: irq_count=%d\n", + atomic_read(&cpas_core->irq_count)); + cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + } + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SUSPEND_VOTE; + rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, + &ahb_vote, NULL); + if (rc) + goto done; + + axi_vote.uncompressed_bw = 0; + axi_vote.compressed_bw = 0; + rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, + cpas_client, &axi_vote); + +done: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + mutex_unlock(&cpas_hw->hw_mutex); + return rc; +} + +static int cam_cpas_hw_init(void *hw_priv, void *init_hw_args, + uint32_t arg_size) +{ + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + int rc = 0; + + if (!hw_priv || !init_hw_args) { + CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK", + hw_priv, init_hw_args); + return -EINVAL; + } + + if (sizeof(struct cam_cpas_hw_caps) != arg_size) { + CAM_ERR(CAM_CPAS, "INIT HW size mismatch %ld %d", + sizeof(struct cam_cpas_hw_caps), arg_size); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *)hw_priv; + cpas_core = (struct cam_cpas *)cpas_hw->core_info; + + if (cpas_core->internal_ops.init_hw_version) { + rc = cpas_core->internal_ops.init_hw_version(cpas_hw, + (struct cam_cpas_hw_caps *)init_hw_args); + } + + return rc; +} + +static int cam_cpas_hw_register_client(struct cam_hw_info *cpas_hw, + struct cam_cpas_register_params *register_params) +{ + int rc; + struct cam_cpas_client *cpas_client; + char client_name[CAM_HW_IDENTIFIER_LENGTH + 3]; + int32_t client_indx = -1; + struct cam_cpas *cpas_core = (struct cam_cpas *)cpas_hw->core_info; + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + + CAM_DBG(CAM_CPAS, "Register params : identifier=%s, cell_index=%d", + register_params->identifier, register_params->cell_index); + + if (soc_private->client_id_based) + snprintf(client_name, sizeof(client_name), "%s%d", + register_params->identifier, + register_params->cell_index); + else + snprintf(client_name, sizeof(client_name), "%s", + register_params->identifier); + + mutex_lock(&cpas_hw->hw_mutex); + + rc = cam_common_util_get_string_index(soc_private->client_name, + soc_private->num_clients, client_name, &client_indx); + if (rc || !CAM_CPAS_CLIENT_VALID(client_indx) || + CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "Invalid Client register : %s %d, %d", + register_params->identifier, + register_params->cell_index, client_indx); + mutex_unlock(&cpas_hw->hw_mutex); + return -EPERM; + } + + cpas_client = kzalloc(sizeof(struct cam_cpas_client), GFP_KERNEL); + if (!cpas_client) { + mutex_unlock(&cpas_hw->hw_mutex); + return -ENOMEM; + } + + rc = cam_cpas_util_insert_client_to_axi_port(cpas_core, soc_private, + cpas_client, client_indx); + if (rc) { + CAM_ERR(CAM_CPAS, + "axi_port_insert failed client_indx=%d, rc=%d", + client_indx, rc); + kfree(cpas_client); + mutex_unlock(&cpas_hw->hw_mutex); + return -EINVAL; + } + + register_params->client_handle = + CAM_CPAS_GET_CLIENT_HANDLE(client_indx); + memcpy(&cpas_client->data, register_params, + sizeof(struct cam_cpas_register_params)); + cpas_core->cpas_client[client_indx] = cpas_client; + cpas_core->registered_clients++; + + mutex_unlock(&cpas_hw->hw_mutex); + + CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d", + client_indx, cpas_core->registered_clients); + + return 0; +} + +static int cam_cpas_hw_unregister_client(struct cam_hw_info *cpas_hw, + uint32_t client_handle) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle); + int rc = 0; + + if (!CAM_CPAS_CLIENT_VALID(client_indx)) + return -EINVAL; + + mutex_lock(&cpas_hw->hw_mutex); + mutex_lock(&cpas_core->client_mutex[client_indx]); + + if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "client not registered %d", client_indx); + rc = -EPERM; + goto done; + } + + if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) { + CAM_ERR(CAM_CPAS, "Client %d is not stopped", client_indx); + rc = -EPERM; + goto done; + } + + cam_cpas_util_remove_client_from_axi_port( + cpas_core->cpas_client[client_indx]); + + CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d", + client_indx, cpas_core->registered_clients); + + kfree(cpas_core->cpas_client[client_indx]); + cpas_core->cpas_client[client_indx] = NULL; + cpas_core->registered_clients--; +done: + mutex_unlock(&cpas_core->client_mutex[client_indx]); + mutex_unlock(&cpas_hw->hw_mutex); + return rc; +} + +static int cam_cpas_hw_get_hw_info(void *hw_priv, + void *get_hw_cap_args, uint32_t arg_size) +{ + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + struct cam_cpas_hw_caps *hw_caps; + + if (!hw_priv || !get_hw_cap_args) { + CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK", + hw_priv, get_hw_cap_args); + return -EINVAL; + } + + if (sizeof(struct cam_cpas_hw_caps) != arg_size) { + CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d", + sizeof(struct cam_cpas_hw_caps), arg_size); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *)hw_priv; + cpas_core = (struct cam_cpas *) cpas_hw->core_info; + hw_caps = (struct cam_cpas_hw_caps *)get_hw_cap_args; + + *hw_caps = cpas_core->hw_caps; + + return 0; +} + + +static int cam_cpas_hw_process_cmd(void *hw_priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + int rc = -EINVAL; + + if (!hw_priv || !cmd_args || + (cmd_type >= CAM_CPAS_HW_CMD_INVALID)) { + CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK %d", + hw_priv, cmd_args, cmd_type); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_CPAS_HW_CMD_REGISTER_CLIENT: { + struct cam_cpas_register_params *register_params; + + if (sizeof(struct cam_cpas_register_params) != arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + register_params = (struct cam_cpas_register_params *)cmd_args; + rc = cam_cpas_hw_register_client(hw_priv, register_params); + break; + } + case CAM_CPAS_HW_CMD_UNREGISTER_CLIENT: { + uint32_t *client_handle; + + if (sizeof(uint32_t) != arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + client_handle = (uint32_t *)cmd_args; + rc = cam_cpas_hw_unregister_client(hw_priv, *client_handle); + break; + } + case CAM_CPAS_HW_CMD_REG_WRITE: { + struct cam_cpas_hw_cmd_reg_read_write *reg_write; + + if (sizeof(struct cam_cpas_hw_cmd_reg_read_write) != + arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + reg_write = + (struct cam_cpas_hw_cmd_reg_read_write *)cmd_args; + rc = cam_cpas_hw_reg_write(hw_priv, reg_write->client_handle, + reg_write->reg_base, reg_write->offset, reg_write->mb, + reg_write->value); + break; + } + case CAM_CPAS_HW_CMD_REG_READ: { + struct cam_cpas_hw_cmd_reg_read_write *reg_read; + + if (sizeof(struct cam_cpas_hw_cmd_reg_read_write) != + arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + reg_read = + (struct cam_cpas_hw_cmd_reg_read_write *)cmd_args; + rc = cam_cpas_hw_reg_read(hw_priv, + reg_read->client_handle, reg_read->reg_base, + reg_read->offset, reg_read->mb, ®_read->value); + + break; + } + case CAM_CPAS_HW_CMD_AHB_VOTE: { + struct cam_cpas_hw_cmd_ahb_vote *cmd_ahb_vote; + + if (sizeof(struct cam_cpas_hw_cmd_ahb_vote) != arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + cmd_ahb_vote = (struct cam_cpas_hw_cmd_ahb_vote *)cmd_args; + rc = cam_cpas_hw_update_ahb_vote(hw_priv, + cmd_ahb_vote->client_handle, cmd_ahb_vote->ahb_vote); + break; + } + case CAM_CPAS_HW_CMD_AXI_VOTE: { + struct cam_cpas_hw_cmd_axi_vote *cmd_axi_vote; + + if (sizeof(struct cam_cpas_hw_cmd_axi_vote) != arg_size) { + CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + cmd_axi_vote = (struct cam_cpas_hw_cmd_axi_vote *)cmd_args; + rc = cam_cpas_hw_update_axi_vote(hw_priv, + cmd_axi_vote->client_handle, cmd_axi_vote->axi_vote); + break; + } + default: + CAM_ERR(CAM_CPAS, "CPAS HW command not valid =%d", cmd_type); + break; + } + + return rc; +} + +static int cam_cpas_util_client_setup(struct cam_hw_info *cpas_hw) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + int i; + + for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) { + mutex_init(&cpas_core->client_mutex[i]); + cpas_core->cpas_client[i] = NULL; + } + + return 0; +} + +static int cam_cpas_util_client_cleanup(struct cam_hw_info *cpas_hw) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + int i; + + for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) { + if (cpas_core->cpas_client[i]) { + cam_cpas_hw_unregister_client(cpas_hw, i); + cpas_core->cpas_client[i] = NULL; + } + mutex_destroy(&cpas_core->client_mutex[i]); + } + + return 0; +} + +static int cam_cpas_util_get_internal_ops(struct platform_device *pdev, + struct cam_hw_intf *hw_intf, struct cam_cpas_internal_ops *internal_ops) +{ + struct device_node *of_node = pdev->dev.of_node; + int rc; + const char *compat_str = NULL; + + rc = of_property_read_string_index(of_node, "arch-compat", 0, + (const char **)&compat_str); + if (rc) { + CAM_ERR(CAM_CPAS, "failed to get arch-compat rc=%d", rc); + return -EINVAL; + } + + if (strnstr(compat_str, "camss_top", strlen(compat_str))) { + hw_intf->hw_type = CAM_HW_CAMSSTOP; + rc = cam_camsstop_get_internal_ops(internal_ops); + } else if (strnstr(compat_str, "cpas_top", strlen(compat_str))) { + hw_intf->hw_type = CAM_HW_CPASTOP; + rc = cam_cpastop_get_internal_ops(internal_ops); + } else { + CAM_ERR(CAM_CPAS, "arch-compat %s not supported", compat_str); + rc = -EINVAL; + } + + return rc; +} + +static int cam_cpas_util_get_hw_version(struct platform_device *pdev, + struct cam_hw_soc_info *soc_info) +{ + struct device_node *of_node = pdev->dev.of_node; + int rc; + + soc_info->hw_version = 0; + + rc = of_property_read_u32(of_node, + "qcom,cpas-hw-ver", &soc_info->hw_version); + + CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_info->hw_version); + + if (rc) { + CAM_ERR(CAM_CPAS, "failed to get CPAS HW Version rc=%d", rc); + return -EINVAL; + } + + return rc; +} + +int cam_cpas_hw_probe(struct platform_device *pdev, + struct cam_hw_intf **hw_intf) +{ + int rc = 0; + int i; + struct cam_hw_info *cpas_hw = NULL; + struct cam_hw_intf *cpas_hw_intf = NULL; + struct cam_cpas *cpas_core = NULL; + struct cam_cpas_private_soc *soc_private; + struct cam_cpas_internal_ops *internal_ops; + + cpas_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!cpas_hw_intf) + return -ENOMEM; + + cpas_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!cpas_hw) { + kfree(cpas_hw_intf); + return -ENOMEM; + } + + cpas_core = kzalloc(sizeof(struct cam_cpas), GFP_KERNEL); + if (!cpas_core) { + kfree(cpas_hw); + kfree(cpas_hw_intf); + return -ENOMEM; + } + + for (i = 0; i < CAM_CPAS_REG_MAX; i++) + cpas_core->regbase_index[i] = -1; + + cpas_hw_intf->hw_priv = cpas_hw; + cpas_hw->core_info = cpas_core; + + cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + cpas_hw->soc_info.pdev = pdev; + cpas_hw->soc_info.dev = &pdev->dev; + cpas_hw->soc_info.dev_name = pdev->name; + cpas_hw->open_count = 0; + mutex_init(&cpas_hw->hw_mutex); + spin_lock_init(&cpas_hw->hw_lock); + init_completion(&cpas_hw->hw_complete); + + cpas_hw_intf->hw_ops.get_hw_caps = cam_cpas_hw_get_hw_info; + cpas_hw_intf->hw_ops.init = cam_cpas_hw_init; + cpas_hw_intf->hw_ops.deinit = NULL; + cpas_hw_intf->hw_ops.reset = NULL; + cpas_hw_intf->hw_ops.reserve = NULL; + cpas_hw_intf->hw_ops.release = NULL; + cpas_hw_intf->hw_ops.start = cam_cpas_hw_start; + cpas_hw_intf->hw_ops.stop = cam_cpas_hw_stop; + cpas_hw_intf->hw_ops.read = NULL; + cpas_hw_intf->hw_ops.write = NULL; + cpas_hw_intf->hw_ops.process_cmd = cam_cpas_hw_process_cmd; + + cpas_core->work_queue = alloc_workqueue("cam-cpas", + WQ_UNBOUND | WQ_MEM_RECLAIM, CAM_CPAS_INFLIGHT_WORKS); + if (!cpas_core->work_queue) { + rc = -ENOMEM; + goto release_mem; + } + + internal_ops = &cpas_core->internal_ops; + rc = cam_cpas_util_get_internal_ops(pdev, cpas_hw_intf, internal_ops); + if (rc) + goto release_workq; + + rc = cam_cpas_soc_init_resources(&cpas_hw->soc_info, + internal_ops->handle_irq, cpas_hw); + if (rc) + goto release_workq; + + soc_private = (struct cam_cpas_private_soc *) + cpas_hw->soc_info.soc_private; + cpas_core->num_clients = soc_private->num_clients; + atomic_set(&cpas_core->irq_count, 0); + init_waitqueue_head(&cpas_core->irq_count_wq); + + if (internal_ops->setup_regbase) { + rc = internal_ops->setup_regbase(&cpas_hw->soc_info, + cpas_core->regbase_index, CAM_CPAS_REG_MAX); + if (rc) + goto deinit_platform_res; + } + + rc = cam_cpas_util_client_setup(cpas_hw); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in client setup, rc=%d", rc); + goto deinit_platform_res; + } + + rc = cam_cpas_util_register_bus_client(&cpas_hw->soc_info, + cpas_hw->soc_info.pdev->dev.of_node, + &cpas_core->ahb_bus_client); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in ahb setup, rc=%d", rc); + goto client_cleanup; + } + + rc = cam_cpas_util_axi_setup(cpas_core, &cpas_hw->soc_info); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in axi setup, rc=%d", rc); + goto ahb_cleanup; + } + + /* Need to vote first before enabling clocks */ + rc = cam_cpas_util_vote_default_ahb_axi(cpas_hw, true); + if (rc) + goto axi_cleanup; + + rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info, CAM_SVS_VOTE); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in soc_enable_resources, rc=%d", rc); + goto remove_default_vote; + } + + if (internal_ops->get_hw_info) { + rc = internal_ops->get_hw_info(cpas_hw, &cpas_core->hw_caps); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in get_hw_info, rc=%d", rc); + goto disable_soc_res; + } + } else { + CAM_ERR(CAM_CPAS, "Invalid get_hw_info"); + goto disable_soc_res; + } + + rc = cam_cpas_hw_init(cpas_hw_intf->hw_priv, + &cpas_core->hw_caps, sizeof(struct cam_cpas_hw_caps)); + if (rc) + goto disable_soc_res; + + rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info, true, true); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in soc_disable_resources, rc=%d", rc); + goto remove_default_vote; + } + + rc = cam_cpas_util_vote_default_ahb_axi(cpas_hw, false); + if (rc) + goto axi_cleanup; + + rc = cam_cpas_util_get_hw_version(pdev, &cpas_hw->soc_info); + if (rc) + goto axi_cleanup; + + *hw_intf = cpas_hw_intf; + return 0; + +disable_soc_res: + cam_cpas_soc_disable_resources(&cpas_hw->soc_info, true, true); +remove_default_vote: + cam_cpas_util_vote_default_ahb_axi(cpas_hw, false); +axi_cleanup: + cam_cpas_util_axi_cleanup(cpas_core, &cpas_hw->soc_info); +ahb_cleanup: + cam_cpas_util_unregister_bus_client(&cpas_core->ahb_bus_client); +client_cleanup: + cam_cpas_util_client_cleanup(cpas_hw); +deinit_platform_res: + cam_cpas_soc_deinit_resources(&cpas_hw->soc_info); +release_workq: + flush_workqueue(cpas_core->work_queue); + destroy_workqueue(cpas_core->work_queue); +release_mem: + mutex_destroy(&cpas_hw->hw_mutex); + kfree(cpas_core); + kfree(cpas_hw); + kfree(cpas_hw_intf); + CAM_ERR(CAM_CPAS, "failed in hw probe"); + return rc; +} + +int cam_cpas_hw_remove(struct cam_hw_intf *cpas_hw_intf) +{ + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + + if (!cpas_hw_intf) { + CAM_ERR(CAM_CPAS, "cpas interface not initialized"); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *)cpas_hw_intf->hw_priv; + cpas_core = (struct cam_cpas *)cpas_hw->core_info; + + if (cpas_hw->hw_state == CAM_HW_STATE_POWER_UP) { + CAM_ERR(CAM_CPAS, "cpas hw is in power up state"); + return -EINVAL; + } + + cam_cpas_util_axi_cleanup(cpas_core, &cpas_hw->soc_info); + cam_cpas_util_unregister_bus_client(&cpas_core->ahb_bus_client); + cam_cpas_util_client_cleanup(cpas_hw); + cam_cpas_soc_deinit_resources(&cpas_hw->soc_info); + flush_workqueue(cpas_core->work_queue); + destroy_workqueue(cpas_core->work_queue); + mutex_destroy(&cpas_hw->hw_mutex); + kfree(cpas_core); + kfree(cpas_hw); + kfree(cpas_hw_intf); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.h new file mode 100644 index 000000000000..2e660b1adb95 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw.h @@ -0,0 +1,202 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CPAS_HW_H_ +#define _CAM_CPAS_HW_H_ + +#include "cam_cpas_api.h" +#include "cam_cpas_hw_intf.h" +#include "cam_common_util.h" + +#define CAM_CPAS_MAX_CLIENTS 30 +#define CAM_CPAS_INFLIGHT_WORKS 5 + +#define CAM_CPAS_GET_CLIENT_IDX(handle) (handle) +#define CAM_CPAS_GET_CLIENT_HANDLE(indx) (indx) + +#define CAM_CPAS_CLIENT_VALID(indx) \ + ((indx >= 0) && (indx < CAM_CPAS_MAX_CLIENTS)) +#define CAM_CPAS_CLIENT_REGISTERED(cpas_core, indx) \ + ((CAM_CPAS_CLIENT_VALID(indx)) && \ + (cpas_core->cpas_client[indx])) +#define CAM_CPAS_CLIENT_STARTED(cpas_core, indx) \ + ((CAM_CPAS_CLIENT_REGISTERED(cpas_core, indx)) && \ + (cpas_core->cpas_client[indx]->started)) + +/** + * enum cam_cpas_access_type - Enum for Register access type + */ +enum cam_cpas_access_type { + CAM_REG_TYPE_READ, + CAM_REG_TYPE_WRITE, + CAM_REG_TYPE_READ_WRITE, +}; + +/** + * struct cam_cpas_internal_ops - CPAS Hardware layer internal ops + * + * @get_hw_info: Function pointer for get hw info + * @init_hw_version: Function pointer for hw init based on version + * @handle_irq: Function poniter for irq handling + * @setup_regbase: Function pointer for setup rebase indices + * @power_on: Function pointer for hw core specific power on settings + * @power_off: Function pointer for hw core specific power off settings + * + */ +struct cam_cpas_internal_ops { + int (*get_hw_info)(struct cam_hw_info *cpas_hw, + struct cam_cpas_hw_caps *hw_caps); + int (*init_hw_version)(struct cam_hw_info *cpas_hw, + struct cam_cpas_hw_caps *hw_caps); + irqreturn_t (*handle_irq)(int irq_num, void *data); + int (*setup_regbase)(struct cam_hw_soc_info *soc_info, + int32_t regbase_index[], int32_t num_reg_map); + int (*power_on)(struct cam_hw_info *cpas_hw); + int (*power_off)(struct cam_hw_info *cpas_hw); +}; + +/** + * struct cam_cpas_reg : CPAS register info + * + * @enable: Whether this reg info need to be enabled + * @access_type: Register access type + * @masked_value: Whether this register write/read is based on mask, shift + * @mask: Mask for this register value + * @shift: Shift for this register value + * @value: Register value + * + */ +struct cam_cpas_reg { + bool enable; + enum cam_cpas_access_type access_type; + bool masked_value; + uint32_t offset; + uint32_t mask; + uint32_t shift; + uint32_t value; +}; + +/** + * struct cam_cpas_client : CPAS Client structure info + * + * @data: Client register params + * @started: Whether client has streamed on + * @ahb_level: Determined/Applied ahb level for the client + * @axi_vote: Determined/Applied axi vote for the client + * @axi_port: Client's parent axi port + * @axi_sibling_client: Client's sibllings sharing the same axi port + * + */ +struct cam_cpas_client { + struct cam_cpas_register_params data; + bool started; + enum cam_vote_level ahb_level; + struct cam_axi_vote axi_vote; + struct cam_cpas_axi_port *axi_port; + struct list_head axi_sibling_client; +}; + +/** + * struct cam_cpas_bus_client : Bus client information + * + * @src: Bus master/src id + * @dst: Bus slave/dst id + * @pdata: Bus pdata information + * @client_id: Bus client id + * @num_usecases: Number of use cases for this client + * @num_paths: Number of paths for this client + * @curr_vote_level: current voted index + * @dyn_vote: Whether dynamic voting enabled + * @lock: Mutex lock used while voting on this client + * @valid: Whether bus client is valid + * + */ +struct cam_cpas_bus_client { + int src; + int dst; + struct msm_bus_scale_pdata *pdata; + uint32_t client_id; + int num_usecases; + int num_paths; + unsigned int curr_vote_level; + bool dyn_vote; + struct mutex lock; + bool valid; +}; + +/** + * struct cam_cpas_axi_port : AXI port information + * + * @sibling_port: Sibling AXI ports + * @clients_list_head: List head pointing to list of clients sharing this port + * @lock: Mutex lock for accessing this port + * @camnoc_bus: CAMNOC bus client info for this port + * @mnoc_bus: MNOC bus client info for this port + * @axi_port_name: Name of this AXI port + * @axi_port_node: Node representing this AXI Port + * @axi_port_mnoc_node: Node representing mnoc in this AXI Port + * @axi_port_camnoc_node: Node representing camnoc in this AXI Port + * @consolidated_axi_vote: Consolidated axi bw values for this AXI port + * + */ +struct cam_cpas_axi_port { + struct list_head sibling_port; + struct list_head clients_list_head; + struct mutex lock; + struct cam_cpas_bus_client camnoc_bus; + struct cam_cpas_bus_client mnoc_bus; + const char *axi_port_name; + struct device_node *axi_port_node; + struct device_node *axi_port_mnoc_node; + struct device_node *axi_port_camnoc_node; + struct cam_axi_vote consolidated_axi_vote; +}; + +/** + * struct cam_cpas : CPAS core data structure info + * + * @hw_caps: CPAS hw capabilities + * @cpas_client: Array of pointers to CPAS clients info + * @client_mutex: Mutex for accessing client info + * @num_clients: Total number of clients that CPAS supports + * @registered_clients: Number of Clients registered currently + * @streamon_clients: Number of Clients that are in start state currently + * @regbase_index: Register base indices for CPAS register base IDs + * @ahb_bus_client: AHB Bus client info + * @axi_ports_list_head: Head pointing to list of AXI ports + * @internal_ops: CPAS HW internal ops + * @work_queue: Work queue handle + * + */ +struct cam_cpas { + struct cam_cpas_hw_caps hw_caps; + struct cam_cpas_client *cpas_client[CAM_CPAS_MAX_CLIENTS]; + struct mutex client_mutex[CAM_CPAS_MAX_CLIENTS]; + uint32_t num_clients; + uint32_t registered_clients; + uint32_t streamon_clients; + int32_t regbase_index[CAM_CPAS_REG_MAX]; + struct cam_cpas_bus_client ahb_bus_client; + struct list_head axi_ports_list_head; + struct cam_cpas_internal_ops internal_ops; + struct workqueue_struct *work_queue; + atomic_t irq_count; + wait_queue_head_t irq_count_wq; +}; + +int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops); +int cam_cpastop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops); + +int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw, + enum cam_cpas_reg_base reg_base, struct cam_cpas_reg *reg_info); + +#endif /* _CAM_CPAS_HW_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw_intf.h new file mode 100644 index 000000000000..fa4018e27c71 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_hw_intf.h @@ -0,0 +1,135 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CPAS_HW_INTF_H_ +#define _CAM_CPAS_HW_INTF_H_ + +#include + +#include "cam_cpas_api.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_debug_util.h" + +/* Number of times to retry while polling */ +#define CAM_CPAS_POLL_RETRY_CNT 5 +/* Minimum usecs to sleep while polling */ +#define CAM_CPAS_POLL_MIN_USECS 200 +/* Maximum usecs to sleep while polling */ +#define CAM_CPAS_POLL_MAX_USECS 250 + +/** + * enum cam_cpas_hw_type - Enum for CPAS HW type + */ +enum cam_cpas_hw_type { + CAM_HW_CPASTOP, + CAM_HW_CAMSSTOP, +}; + +/** + * enum cam_cpas_hw_cmd_process - Enum for CPAS HW process command type + */ +enum cam_cpas_hw_cmd_process { + CAM_CPAS_HW_CMD_REGISTER_CLIENT, + CAM_CPAS_HW_CMD_UNREGISTER_CLIENT, + CAM_CPAS_HW_CMD_REG_WRITE, + CAM_CPAS_HW_CMD_REG_READ, + CAM_CPAS_HW_CMD_AHB_VOTE, + CAM_CPAS_HW_CMD_AXI_VOTE, + CAM_CPAS_HW_CMD_INVALID, +}; + +/** + * struct cam_cpas_hw_cmd_reg_read_write : CPAS cmd struct for reg read, write + * + * @client_handle: Client handle + * @reg_base: Register base type + * @offset: Register offset + * @value: Register value + * @mb: Whether to do operation with memory barrier + * + */ +struct cam_cpas_hw_cmd_reg_read_write { + uint32_t client_handle; + enum cam_cpas_reg_base reg_base; + uint32_t offset; + uint32_t value; + bool mb; +}; + +/** + * struct cam_cpas_hw_cmd_ahb_vote : CPAS cmd struct for AHB vote + * + * @client_handle: Client handle + * @ahb_vote: AHB voting info + * + */ +struct cam_cpas_hw_cmd_ahb_vote { + uint32_t client_handle; + struct cam_ahb_vote *ahb_vote; +}; + +/** + * struct cam_cpas_hw_cmd_axi_vote : CPAS cmd struct for AXI vote + * + * @client_handle: Client handle + * @axi_vote: axi bandwidth vote + * + */ +struct cam_cpas_hw_cmd_axi_vote { + uint32_t client_handle; + struct cam_axi_vote *axi_vote; +}; + +/** + * struct cam_cpas_hw_cmd_start : CPAS cmd struct for start + * + * @client_handle: Client handle + * + */ +struct cam_cpas_hw_cmd_start { + uint32_t client_handle; + struct cam_ahb_vote *ahb_vote; + struct cam_axi_vote *axi_vote; +}; + +/** + * struct cam_cpas_hw_cmd_stop : CPAS cmd struct for stop + * + * @client_handle: Client handle + * + */ +struct cam_cpas_hw_cmd_stop { + uint32_t client_handle; +}; + +/** + * struct cam_cpas_hw_caps : CPAS HW capabilities + * + * @camera_family: Camera family type + * @camera_version: Camera version + * @cpas_version: CPAS version + * @camera_capability: Camera hw capabilities + * + */ +struct cam_cpas_hw_caps { + uint32_t camera_family; + struct cam_hw_version camera_version; + struct cam_hw_version cpas_version; + uint32_t camera_capability; +}; + +int cam_cpas_hw_probe(struct platform_device *pdev, + struct cam_hw_intf **hw_intf); +int cam_cpas_hw_remove(struct cam_hw_intf *cpas_hw_intf); + +#endif /* _CAM_CPAS_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_intf.c new file mode 100644 index 000000000000..95b550a9eeae --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_intf.c @@ -0,0 +1,660 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam_subdev.h" +#include "cam_cpas_hw_intf.h" + +#define CAM_CPAS_DEV_NAME "cam-cpas" +#define CAM_CPAS_INTF_INITIALIZED() (g_cpas_intf && g_cpas_intf->probe_done) + +/** + * struct cam_cpas_intf : CPAS interface + * + * @pdev: Platform device + * @subdev: Subdev info + * @hw_intf: CPAS HW interface + * @hw_caps: CPAS HW capabilities + * @intf_lock: CPAS interface mutex + * @open_cnt: CPAS subdev open count + * @probe_done: Whether CPAS prove completed + * + */ +struct cam_cpas_intf { + struct platform_device *pdev; + struct cam_subdev subdev; + struct cam_hw_intf *hw_intf; + struct cam_cpas_hw_caps hw_caps; + struct mutex intf_lock; + uint32_t open_cnt; + bool probe_done; +}; + +static struct cam_cpas_intf *g_cpas_intf; + +int cam_cpas_get_cpas_hw_version(uint32_t *hw_version) +{ + struct cam_hw_info *cpas_hw = NULL; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (!hw_version) { + CAM_ERR(CAM_CPAS, "invalid input %pK", hw_version); + return -EINVAL; + } + + cpas_hw = (struct cam_hw_info *) g_cpas_intf->hw_intf->hw_priv; + + *hw_version = cpas_hw->soc_info.hw_version; + + if (*hw_version == CAM_CPAS_TITAN_NONE) { + CAM_DBG(CAM_CPAS, "Didn't find a valid HW Version %d", + *hw_version); + } + + return 0; +} + + +int cam_cpas_get_hw_info(uint32_t *camera_family, + struct cam_hw_version *camera_version, + struct cam_hw_version *cpas_version, + uint32_t *cam_caps) +{ + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (!camera_family || !camera_version || !cpas_version || !cam_caps) { + CAM_ERR(CAM_CPAS, "invalid input %pK %pK %pK %pK", + camera_family, camera_version, cpas_version, cam_caps); + return -EINVAL; + } + + *camera_family = g_cpas_intf->hw_caps.camera_family; + *camera_version = g_cpas_intf->hw_caps.camera_version; + *cpas_version = g_cpas_intf->hw_caps.cpas_version; + *cam_caps = g_cpas_intf->hw_caps.camera_capability; + + return 0; +} +EXPORT_SYMBOL(cam_cpas_get_hw_info); + +int cam_cpas_reg_write(uint32_t client_handle, + enum cam_cpas_reg_base reg_base, uint32_t offset, bool mb, + uint32_t value) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + struct cam_cpas_hw_cmd_reg_read_write cmd_reg_write; + + cmd_reg_write.client_handle = client_handle; + cmd_reg_write.reg_base = reg_base; + cmd_reg_write.offset = offset; + cmd_reg_write.value = value; + cmd_reg_write.mb = mb; + + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_REG_WRITE, &cmd_reg_write, + sizeof(struct cam_cpas_hw_cmd_reg_read_write)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_reg_write); + +int cam_cpas_reg_read(uint32_t client_handle, + enum cam_cpas_reg_base reg_base, uint32_t offset, bool mb, + uint32_t *value) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (!value) { + CAM_ERR(CAM_CPAS, "Invalid arg value"); + return -EINVAL; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + struct cam_cpas_hw_cmd_reg_read_write cmd_reg_read; + + cmd_reg_read.client_handle = client_handle; + cmd_reg_read.reg_base = reg_base; + cmd_reg_read.offset = offset; + cmd_reg_read.mb = mb; + cmd_reg_read.value = 0; + + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_REG_READ, &cmd_reg_read, + sizeof(struct cam_cpas_hw_cmd_reg_read_write)); + if (rc) { + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + return rc; + } + + *value = cmd_reg_read.value; + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_reg_read); + +int cam_cpas_update_axi_vote(uint32_t client_handle, + struct cam_axi_vote *axi_vote) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + struct cam_cpas_hw_cmd_axi_vote cmd_axi_vote; + + cmd_axi_vote.client_handle = client_handle; + cmd_axi_vote.axi_vote = axi_vote; + + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_AXI_VOTE, &cmd_axi_vote, + sizeof(struct cam_cpas_hw_cmd_axi_vote)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_update_axi_vote); + +int cam_cpas_update_ahb_vote(uint32_t client_handle, + struct cam_ahb_vote *ahb_vote) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + struct cam_cpas_hw_cmd_ahb_vote cmd_ahb_vote; + + cmd_ahb_vote.client_handle = client_handle; + cmd_ahb_vote.ahb_vote = ahb_vote; + + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_AHB_VOTE, &cmd_ahb_vote, + sizeof(struct cam_cpas_hw_cmd_ahb_vote)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_update_ahb_vote); + +int cam_cpas_stop(uint32_t client_handle) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.stop) { + struct cam_cpas_hw_cmd_stop cmd_hw_stop; + + cmd_hw_stop.client_handle = client_handle; + + rc = g_cpas_intf->hw_intf->hw_ops.stop( + g_cpas_intf->hw_intf->hw_priv, &cmd_hw_stop, + sizeof(struct cam_cpas_hw_cmd_stop)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in stop, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid stop ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_stop); + +int cam_cpas_start(uint32_t client_handle, + struct cam_ahb_vote *ahb_vote, struct cam_axi_vote *axi_vote) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.start) { + struct cam_cpas_hw_cmd_start cmd_hw_start; + + cmd_hw_start.client_handle = client_handle; + cmd_hw_start.ahb_vote = ahb_vote; + cmd_hw_start.axi_vote = axi_vote; + + rc = g_cpas_intf->hw_intf->hw_ops.start( + g_cpas_intf->hw_intf->hw_priv, &cmd_hw_start, + sizeof(struct cam_cpas_hw_cmd_start)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in start, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid start ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_start); + +int cam_cpas_unregister_client(uint32_t client_handle) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_UNREGISTER_CLIENT, + &client_handle, sizeof(uint32_t)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_unregister_client); + +int cam_cpas_register_client( + struct cam_cpas_register_params *register_params) +{ + int rc; + + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + if (g_cpas_intf->hw_intf->hw_ops.process_cmd) { + rc = g_cpas_intf->hw_intf->hw_ops.process_cmd( + g_cpas_intf->hw_intf->hw_priv, + CAM_CPAS_HW_CMD_REGISTER_CLIENT, register_params, + sizeof(struct cam_cpas_register_params)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in process_cmd, rc=%d", rc); + } else { + CAM_ERR(CAM_CPAS, "Invalid process_cmd ops"); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(cam_cpas_register_client); + +int cam_cpas_subdev_cmd(struct cam_cpas_intf *cpas_intf, + struct cam_control *cmd) +{ + int rc = 0; + + if (!cmd) { + CAM_ERR(CAM_CPAS, "Invalid input cmd"); + return -EINVAL; + } + + switch (cmd->op_code) { + case CAM_QUERY_CAP: { + struct cam_cpas_query_cap query; + + rc = copy_from_user(&query, u64_to_user_ptr(cmd->handle), + sizeof(query)); + if (rc) { + CAM_ERR(CAM_CPAS, "Failed in copy from user, rc=%d", + rc); + break; + } + + rc = cam_cpas_get_hw_info(&query.camera_family, + &query.camera_version, &query.cpas_version, + &query.reserved); + if (rc) + break; + + rc = copy_to_user(u64_to_user_ptr(cmd->handle), &query, + sizeof(query)); + if (rc) + CAM_ERR(CAM_CPAS, "Failed in copy to user, rc=%d", rc); + + break; + } + case CAM_SD_SHUTDOWN: + break; + default: + CAM_ERR(CAM_CPAS, "Unknown op code %d for CPAS", cmd->op_code); + rc = -EINVAL; + break; + } + + return rc; +} + +static int cam_cpas_subdev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_cpas_intf *cpas_intf = v4l2_get_subdevdata(sd); + + if (!cpas_intf || !cpas_intf->probe_done) { + CAM_ERR(CAM_CPAS, "CPAS not initialized"); + return -ENODEV; + } + + mutex_lock(&cpas_intf->intf_lock); + cpas_intf->open_cnt++; + CAM_DBG(CAM_CPAS, "CPAS Subdev open count %d", cpas_intf->open_cnt); + mutex_unlock(&cpas_intf->intf_lock); + + return 0; +} + +static int cam_cpas_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_cpas_intf *cpas_intf = v4l2_get_subdevdata(sd); + + if (!cpas_intf || !cpas_intf->probe_done) { + CAM_ERR(CAM_CPAS, "CPAS not initialized"); + return -ENODEV; + } + + mutex_lock(&cpas_intf->intf_lock); + cpas_intf->open_cnt--; + CAM_DBG(CAM_CPAS, "CPAS Subdev close count %d", cpas_intf->open_cnt); + mutex_unlock(&cpas_intf->intf_lock); + + return 0; +} + +static long cam_cpas_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int32_t rc; + struct cam_cpas_intf *cpas_intf = v4l2_get_subdevdata(sd); + + if (!cpas_intf || !cpas_intf->probe_done) { + CAM_ERR(CAM_CPAS, "CPAS not initialized"); + return -ENODEV; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_cpas_subdev_cmd(cpas_intf, (struct cam_control *) arg); + break; + default: + CAM_ERR(CAM_CPAS, "Invalid command %d for CPAS!", cmd); + rc = -EINVAL; + break; + } + + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_cpas_subdev_compat_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc; + struct cam_cpas_intf *cpas_intf = v4l2_get_subdevdata(sd); + + if (!cpas_intf || !cpas_intf->probe_done) { + CAM_ERR(CAM_CPAS, "CPAS not initialized"); + return -ENODEV; + } + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_CPAS, "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_cpas_subdev_cmd(cpas_intf, &cmd_data); + break; + default: + CAM_ERR(CAM_CPAS, "Invalid command %d for CPAS!", cmd); + rc = -EINVAL; + break; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_CPAS, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + +static struct v4l2_subdev_core_ops cpas_subdev_core_ops = { + .ioctl = cam_cpas_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_cpas_subdev_compat_ioctl, +#endif +}; + +static const struct v4l2_subdev_ops cpas_subdev_ops = { + .core = &cpas_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cpas_subdev_intern_ops = { + .open = cam_cpas_subdev_open, + .close = cam_cpas_subdev_close, +}; + +static int cam_cpas_subdev_register(struct platform_device *pdev) +{ + int rc; + struct cam_subdev *subdev; + + if (!g_cpas_intf) + return -EINVAL; + + subdev = &g_cpas_intf->subdev; + + subdev->name = CAM_CPAS_DEV_NAME; + subdev->pdev = pdev; + subdev->ops = &cpas_subdev_ops; + subdev->internal_ops = &cpas_subdev_intern_ops; + subdev->token = g_cpas_intf; + subdev->sd_flags = + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + subdev->ent_function = CAM_CPAS_DEVICE_TYPE; + + rc = cam_register_subdev(subdev); + if (rc) { + CAM_ERR(CAM_CPAS, "failed register subdev: %s!", + CAM_CPAS_DEV_NAME); + return rc; + } + + platform_set_drvdata(g_cpas_intf->pdev, g_cpas_intf); + return rc; +} + +static int cam_cpas_dev_probe(struct platform_device *pdev) +{ + struct cam_cpas_hw_caps *hw_caps; + struct cam_hw_intf *hw_intf; + int rc; + + if (g_cpas_intf) { + CAM_ERR(CAM_CPAS, "cpas dev proble already done"); + return -EALREADY; + } + + g_cpas_intf = kzalloc(sizeof(*g_cpas_intf), GFP_KERNEL); + if (!g_cpas_intf) + return -ENOMEM; + + mutex_init(&g_cpas_intf->intf_lock); + g_cpas_intf->pdev = pdev; + + rc = cam_cpas_hw_probe(pdev, &g_cpas_intf->hw_intf); + if (rc || (g_cpas_intf->hw_intf == NULL)) { + CAM_ERR(CAM_CPAS, "Failed in hw probe, rc=%d", rc); + goto error_destroy_mem; + } + + hw_intf = g_cpas_intf->hw_intf; + hw_caps = &g_cpas_intf->hw_caps; + if (hw_intf->hw_ops.get_hw_caps) { + rc = hw_intf->hw_ops.get_hw_caps(hw_intf->hw_priv, + hw_caps, sizeof(struct cam_cpas_hw_caps)); + if (rc) { + CAM_ERR(CAM_CPAS, "Failed in get_hw_caps, rc=%d", rc); + goto error_hw_remove; + } + } else { + CAM_ERR(CAM_CPAS, "Invalid get_hw_caps ops"); + goto error_hw_remove; + } + + rc = cam_cpas_subdev_register(pdev); + if (rc) + goto error_hw_remove; + + g_cpas_intf->probe_done = true; + CAM_DBG(CAM_CPAS, + "CPAS INTF Probe success %d, %d.%d.%d, %d.%d.%d, 0x%x", + hw_caps->camera_family, hw_caps->camera_version.major, + hw_caps->camera_version.minor, hw_caps->camera_version.incr, + hw_caps->cpas_version.major, hw_caps->cpas_version.minor, + hw_caps->cpas_version.incr, hw_caps->camera_capability); + + return rc; + +error_hw_remove: + cam_cpas_hw_remove(g_cpas_intf->hw_intf); +error_destroy_mem: + mutex_destroy(&g_cpas_intf->intf_lock); + kfree(g_cpas_intf); + g_cpas_intf = NULL; + CAM_ERR(CAM_CPAS, "CPAS probe failed"); + return rc; +} + +static int cam_cpas_dev_remove(struct platform_device *dev) +{ + if (!CAM_CPAS_INTF_INITIALIZED()) { + CAM_ERR(CAM_CPAS, "cpas intf not initialized"); + return -ENODEV; + } + + mutex_lock(&g_cpas_intf->intf_lock); + cam_unregister_subdev(&g_cpas_intf->subdev); + cam_cpas_hw_remove(g_cpas_intf->hw_intf); + mutex_unlock(&g_cpas_intf->intf_lock); + mutex_destroy(&g_cpas_intf->intf_lock); + kfree(g_cpas_intf); + g_cpas_intf = NULL; + + return 0; +} + +static const struct of_device_id cam_cpas_dt_match[] = { + {.compatible = "qcom,cam-cpas"}, + {} +}; + +static struct platform_driver cam_cpas_driver = { + .probe = cam_cpas_dev_probe, + .remove = cam_cpas_dev_remove, + .driver = { + .name = CAM_CPAS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_cpas_dt_match, + }, +}; + +static int __init cam_cpas_dev_init_module(void) +{ + return platform_driver_register(&cam_cpas_driver); +} + +static void __exit cam_cpas_dev_exit_module(void) +{ + platform_driver_unregister(&cam_cpas_driver); +} + +module_init(cam_cpas_dev_init_module); +module_exit(cam_cpas_dev_exit_module); +MODULE_DESCRIPTION("MSM CPAS driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.c new file mode 100644 index 000000000000..46ee90c5f9c4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.c @@ -0,0 +1,275 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include + +#include "cam_cpas_api.h" +#include "cam_cpas_hw_intf.h" +#include "cam_cpas_hw.h" +#include "cam_cpas_soc.h" + +int cam_cpas_get_custom_dt_info(struct platform_device *pdev, + struct cam_cpas_private_soc *soc_private) +{ + struct device_node *of_node; + int count = 0, i = 0, rc = 0; + + if (!soc_private || !pdev) { + CAM_ERR(CAM_CPAS, "invalid input arg %pK %pK", + soc_private, pdev); + return -EINVAL; + } + + of_node = pdev->dev.of_node; + + rc = of_property_read_string_index(of_node, "arch-compat", 0, + (const char **)&soc_private->arch_compat); + if (rc) { + CAM_ERR(CAM_CPAS, "device %s failed to read arch-compat", + pdev->name); + return rc; + } + + + soc_private->hw_version = 0; + rc = of_property_read_u32(of_node, + "qcom,cpas-hw-ver", &soc_private->hw_version); + if (rc) { + CAM_ERR(CAM_CPAS, "device %s failed to read cpas-hw-ver", + pdev->name); + return rc; + } + + CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_private->hw_version); + + soc_private->client_id_based = of_property_read_bool(of_node, + "client-id-based"); + + count = of_property_count_strings(of_node, "client-names"); + if (count <= 0) { + CAM_ERR(CAM_CPAS, "no client-names found"); + count = 0; + return -EINVAL; + } + soc_private->num_clients = count; + CAM_DBG(CAM_CPAS, + "arch-compat=%s, client_id_based = %d, num_clients=%d", + soc_private->arch_compat, soc_private->client_id_based, + soc_private->num_clients); + + for (i = 0; i < soc_private->num_clients; i++) { + rc = of_property_read_string_index(of_node, + "client-names", i, &soc_private->client_name[i]); + if (rc) { + CAM_ERR(CAM_CPAS, "no client-name at cnt=%d", i); + return -ENODEV; + } + CAM_DBG(CAM_CPAS, "Client[%d] : %s", i, + soc_private->client_name[i]); + } + + count = of_property_count_strings(of_node, "client-axi-port-names"); + if ((count <= 0) || (count != soc_private->num_clients)) { + CAM_ERR(CAM_CPAS, "incorrect client-axi-port-names info %d %d", + count, soc_private->num_clients); + count = 0; + return -EINVAL; + } + + for (i = 0; i < soc_private->num_clients; i++) { + rc = of_property_read_string_index(of_node, + "client-axi-port-names", i, + &soc_private->client_axi_port_name[i]); + if (rc) { + CAM_ERR(CAM_CPAS, "no client-name at cnt=%d", i); + return -ENODEV; + } + CAM_DBG(CAM_CPAS, "Client AXI Port[%d] : %s", i, + soc_private->client_axi_port_name[i]); + } + + soc_private->axi_camnoc_based = of_property_read_bool(of_node, + "client-bus-camnoc-based"); + + soc_private->control_camnoc_axi_clk = of_property_read_bool(of_node, + "control-camnoc-axi-clk"); + + if (soc_private->control_camnoc_axi_clk == true) { + rc = of_property_read_u32(of_node, "camnoc-bus-width", + &soc_private->camnoc_bus_width); + if (rc || (soc_private->camnoc_bus_width == 0)) { + CAM_ERR(CAM_CPAS, "Bus width not found rc=%d, %d", + rc, soc_private->camnoc_bus_width); + return rc; + } + + rc = of_property_read_u32(of_node, + "camnoc-axi-clk-bw-margin-perc", + &soc_private->camnoc_axi_clk_bw_margin); + + if (rc) { + /* this is not fatal, overwrite rc */ + rc = 0; + soc_private->camnoc_axi_clk_bw_margin = 0; + } + } + + CAM_DBG(CAM_CPAS, + "control_camnoc_axi_clk=%d, width=%d, margin=%d", + soc_private->control_camnoc_axi_clk, + soc_private->camnoc_bus_width, + soc_private->camnoc_axi_clk_bw_margin); + + count = of_property_count_u32_elems(of_node, "vdd-corners"); + if ((count > 0) && (count <= CAM_REGULATOR_LEVEL_MAX) && + (of_property_count_strings(of_node, "vdd-corner-ahb-mapping") == + count)) { + const char *ahb_string; + + for (i = 0; i < count; i++) { + rc = of_property_read_u32_index(of_node, "vdd-corners", + i, &soc_private->vdd_ahb[i].vdd_corner); + if (rc) { + CAM_ERR(CAM_CPAS, + "vdd-corners failed at index=%d", i); + return -ENODEV; + } + + rc = of_property_read_string_index(of_node, + "vdd-corner-ahb-mapping", i, &ahb_string); + if (rc) { + CAM_ERR(CAM_CPAS, + "no ahb-mapping at index=%d", i); + return -ENODEV; + } + + rc = cam_soc_util_get_level_from_string(ahb_string, + &soc_private->vdd_ahb[i].ahb_level); + if (rc) { + CAM_ERR(CAM_CPAS, + "invalid ahb-string at index=%d", i); + return -EINVAL; + } + + CAM_DBG(CAM_CPAS, + "Vdd-AHB mapping [%d] : [%d] [%s] [%d]", i, + soc_private->vdd_ahb[i].vdd_corner, + ahb_string, soc_private->vdd_ahb[i].ahb_level); + } + + soc_private->num_vdd_ahb_mapping = count; + } + + return 0; +} + +int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in get_dt_properties, rc=%d", rc); + return rc; + } + + if (soc_info->irq_line && !irq_handler) { + CAM_ERR(CAM_CPAS, "Invalid IRQ handler"); + return -EINVAL; + } + + rc = cam_soc_util_request_platform_resource(soc_info, irq_handler, + irq_data); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in request_platform_resource, rc=%d", + rc); + return rc; + } + + soc_info->soc_private = kzalloc(sizeof(struct cam_cpas_private_soc), + GFP_KERNEL); + if (!soc_info->soc_private) { + rc = -ENOMEM; + goto release_res; + } + + rc = cam_cpas_get_custom_dt_info(soc_info->pdev, soc_info->soc_private); + if (rc) { + CAM_ERR(CAM_CPAS, "failed in get_custom_info, rc=%d", rc); + goto free_soc_private; + } + + return rc; + +free_soc_private: + kfree(soc_info->soc_private); +release_res: + cam_soc_util_release_platform_resource(soc_info); + return rc; +} + +int cam_cpas_soc_deinit_resources(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_ERR(CAM_CPAS, "release platform failed, rc=%d", rc); + + kfree(soc_info->soc_private); + soc_info->soc_private = NULL; + + return rc; +} + +int cam_cpas_soc_enable_resources(struct cam_hw_soc_info *soc_info, + enum cam_vote_level default_level) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + default_level, true); + if (rc) + CAM_ERR(CAM_CPAS, "enable platform resource failed, rc=%d", rc); + + return rc; +} + +int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info, + bool disable_clocks, bool disble_irq) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, + disable_clocks, disble_irq); + if (rc) + CAM_ERR(CAM_CPAS, "disable platform failed, rc=%d", rc); + + return rc; +} + +int cam_cpas_soc_disable_irq(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_irq_disable(soc_info); + if (rc) + CAM_ERR(CAM_CPAS, "disable irq failed, rc=%d", rc); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.h new file mode 100644 index 000000000000..70c49bdd28df --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cam_cpas_soc.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CPAS_SOC_H_ +#define _CAM_CPAS_SOC_H_ + +#include "cam_soc_util.h" +#include "cam_cpas_hw.h" + +#define CAM_REGULATOR_LEVEL_MAX 16 + +/** + * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping + * + * @vdd_corner : Voltage corner value + * @ahb_level : AHB vote level corresponds to this vdd_corner + * + */ +struct cam_cpas_vdd_ahb_mapping { + unsigned int vdd_corner; + enum cam_vote_level ahb_level; +}; + +/** + * struct cam_cpas_private_soc : CPAS private DT info + * + * @arch_compat: ARCH compatible string + * @hw_version: Camera HW version + * @client_id_based: Whether clients are id based + * @num_clients: Number of clients supported + * @client_name: Client names + * @axi_camnoc_based: Whether AXi access is camnoc based + * @client_axi_port_name: AXI Port name for each client + * @axi_port_list_node : Node representing AXI Ports list + * @num_vdd_ahb_mapping : Number of vdd to ahb level mapping supported + * @vdd_ahb : AHB level mapping info for the supported vdd levels + * @control_camnoc_axi_clk : Whether CPAS driver need to set camnoc axi clk freq + * @camnoc_bus_width : CAMNOC Bus width + * @camnoc_axi_clk_bw_margin : BW Margin in percentage to add while calculating + * camnoc axi clock + * + */ +struct cam_cpas_private_soc { + const char *arch_compat; + uint32_t hw_version; + bool client_id_based; + uint32_t num_clients; + const char *client_name[CAM_CPAS_MAX_CLIENTS]; + bool axi_camnoc_based; + const char *client_axi_port_name[CAM_CPAS_MAX_CLIENTS]; + struct device_node *axi_port_list_node; + uint32_t num_vdd_ahb_mapping; + struct cam_cpas_vdd_ahb_mapping vdd_ahb[CAM_REGULATOR_LEVEL_MAX]; + bool control_camnoc_axi_clk; + uint32_t camnoc_bus_width; + uint32_t camnoc_axi_clk_bw_margin; +}; + +int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t vfe_irq_handler, void *irq_data); +int cam_cpas_soc_deinit_resources(struct cam_hw_soc_info *soc_info); +int cam_cpas_soc_enable_resources(struct cam_hw_soc_info *soc_info, + enum cam_vote_level default_level); +int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info, + bool disable_clocks, bool disble_irq); +int cam_cpas_soc_disable_irq(struct cam_hw_soc_info *soc_info); +#endif /* _CAM_CPAS_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/Makefile new file mode 100644 index 000000000000..a6cc9c2cb3f9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/Makefile @@ -0,0 +1,6 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_camsstop_hw.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/cam_camsstop_hw.c b/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/cam_camsstop_hw.c new file mode 100644 index 000000000000..0669070506bd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/camss_top/cam_camsstop_hw.c @@ -0,0 +1,89 @@ +/* Copyright (c) 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 "cam_cpas_hw_intf.h" +#include "cam_cpas_hw.h" +#include "cam_cpas_soc.h" + +int cam_camsstop_get_hw_info(struct cam_hw_info *cpas_hw, + struct cam_cpas_hw_caps *hw_caps) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + int32_t reg_indx = cpas_core->regbase_index[CAM_CPAS_REG_CAMSS]; + uint32_t reg_value; + + if (reg_indx == -1) + return -EINVAL; + + hw_caps->camera_family = CAM_FAMILY_CAMERA_SS; + + reg_value = cam_io_r_mb(soc_info->reg_map[reg_indx].mem_base + 0x0); + hw_caps->camera_version.major = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1c); + hw_caps->camera_version.minor = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->camera_version.incr = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + CAM_DBG(CAM_FD, "Family %d, version %d.%d.%d", + hw_caps->camera_family, hw_caps->camera_version.major, + hw_caps->camera_version.minor, hw_caps->camera_version.incr); + + return 0; +} + +int cam_camsstop_setup_regbase_indices(struct cam_hw_soc_info *soc_info, + int32_t regbase_index[], int32_t num_reg_map) +{ + uint32_t index; + int rc; + + if (num_reg_map > CAM_CPAS_REG_MAX) { + CAM_ERR(CAM_CPAS, "invalid num_reg_map=%d", num_reg_map); + return -EINVAL; + } + + if (soc_info->num_mem_block > CAM_SOC_MAX_BLOCK) { + CAM_ERR(CAM_CPAS, "invalid num_mem_block=%d", + soc_info->num_mem_block); + return -EINVAL; + } + + rc = cam_common_util_get_string_index(soc_info->mem_block_name, + soc_info->num_mem_block, "cam_camss", &index); + if ((rc == 0) && (index < num_reg_map)) { + regbase_index[CAM_CPAS_REG_CAMSS] = index; + } else { + CAM_ERR(CAM_CPAS, "regbase not found for CAM_CPAS_REG_CAMSS"); + return -EINVAL; + } + + return 0; +} + +int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops) +{ + if (!internal_ops) { + CAM_ERR(CAM_CPAS, "invalid NULL param"); + return -EINVAL; + } + + internal_ops->get_hw_info = cam_camsstop_get_hw_info; + internal_ops->init_hw_version = NULL; + internal_ops->handle_irq = NULL; + internal_ops->setup_regbase = cam_camsstop_setup_regbase_indices; + internal_ops->power_on = NULL; + internal_ops->power_off = NULL; + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/Makefile new file mode 100644 index 000000000000..d2ca9ff0cdcb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/Makefile @@ -0,0 +1,6 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cpastop_hw.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.c new file mode 100644 index 000000000000..d6187ce210ce --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.c @@ -0,0 +1,574 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include + +#include "cam_cpas_hw_intf.h" +#include "cam_cpas_hw.h" +#include "cam_cpastop_hw.h" +#include "cam_io_util.h" +#include "cam_cpas_soc.h" +#include "cpastop100.h" +#include "cpastop_v170_110.h" +#include "cpastop_v175_100.h" +#include "cpastop_v175_101.h" + +struct cam_camnoc_info *camnoc_info; + +#define CAMNOC_SLAVE_MAX_ERR_CODE 7 +static const char * const camnoc_salve_err_code[] = { + "Target Error", /* err code 0 */ + "Address decode error", /* err code 1 */ + "Unsupported request", /* err code 2 */ + "Disconnected target", /* err code 3 */ + "Security violation", /* err code 4 */ + "Hidden security violation", /* err code 5 */ + "Timeout Error", /* err code 6 */ + "Unknown Error", /* unknown err code */ +}; + +static int cam_cpastop_get_hw_info(struct cam_hw_info *cpas_hw, + struct cam_cpas_hw_caps *hw_caps) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + int32_t reg_indx = cpas_core->regbase_index[CAM_CPAS_REG_CPASTOP]; + uint32_t reg_value; + + if (reg_indx == -1) + return -EINVAL; + + hw_caps->camera_family = CAM_FAMILY_CPAS_SS; + + reg_value = cam_io_r_mb(soc_info->reg_map[reg_indx].mem_base + 0x0); + hw_caps->camera_version.major = + CAM_BITS_MASK_SHIFT(reg_value, 0xff0000, 0x10); + hw_caps->camera_version.minor = + CAM_BITS_MASK_SHIFT(reg_value, 0xff00, 0x8); + hw_caps->camera_version.incr = + CAM_BITS_MASK_SHIFT(reg_value, 0xff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[reg_indx].mem_base + 0x4); + hw_caps->cpas_version.major = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1c); + hw_caps->cpas_version.minor = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->cpas_version.incr = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[reg_indx].mem_base + 0x8); + hw_caps->camera_capability = reg_value; + + CAM_DBG(CAM_FD, "Family %d, version %d.%d.%d, cpas %d.%d.%d, cap 0x%x", + hw_caps->camera_family, hw_caps->camera_version.major, + hw_caps->camera_version.minor, hw_caps->camera_version.incr, + hw_caps->cpas_version.major, hw_caps->cpas_version.minor, + hw_caps->cpas_version.incr, hw_caps->camera_capability); + + return 0; +} + +static int cam_cpastop_setup_regbase_indices(struct cam_hw_soc_info *soc_info, + int32_t regbase_index[], int32_t num_reg_map) +{ + uint32_t index; + int rc; + + if (num_reg_map > CAM_CPAS_REG_MAX) { + CAM_ERR(CAM_CPAS, "invalid num_reg_map=%d", num_reg_map); + return -EINVAL; + } + + if (soc_info->num_mem_block > CAM_SOC_MAX_BLOCK) { + CAM_ERR(CAM_CPAS, "invalid num_mem_block=%d", + soc_info->num_mem_block); + return -EINVAL; + } + + rc = cam_common_util_get_string_index(soc_info->mem_block_name, + soc_info->num_mem_block, "cam_cpas_top", &index); + if ((rc == 0) && (index < num_reg_map)) { + regbase_index[CAM_CPAS_REG_CPASTOP] = index; + } else { + CAM_ERR(CAM_CPAS, "regbase not found for CPASTOP, rc=%d, %d %d", + rc, index, num_reg_map); + return -EINVAL; + } + + rc = cam_common_util_get_string_index(soc_info->mem_block_name, + soc_info->num_mem_block, "cam_camnoc", &index); + if ((rc == 0) && (index < num_reg_map)) { + regbase_index[CAM_CPAS_REG_CAMNOC] = index; + } else { + CAM_ERR(CAM_CPAS, "regbase not found for CAMNOC, rc=%d, %d %d", + rc, index, num_reg_map); + return -EINVAL; + } + + return 0; +} + +static int cam_cpastop_handle_errlogger(struct cam_cpas *cpas_core, + struct cam_hw_soc_info *soc_info, + struct cam_camnoc_irq_slave_err_data *slave_err) +{ + int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC]; + int err_code_index = 0; + + if (!camnoc_info->err_logger) { + CAM_ERR_RATE_LIMIT(CAM_CPAS, "Invalid err logger info"); + return -EINVAL; + } + + slave_err->mainctrl.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->mainctrl); + + slave_err->errvld.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errvld); + + slave_err->errlog0_low.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog0_low); + + slave_err->errlog0_high.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog0_high); + + slave_err->errlog1_low.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog1_low); + + slave_err->errlog1_high.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog1_high); + + slave_err->errlog2_low.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog2_low); + + slave_err->errlog2_high.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog2_high); + + slave_err->errlog3_low.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog3_low); + + slave_err->errlog3_high.value = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->err_logger->errlog3_high); + + CAM_ERR_RATE_LIMIT(CAM_CPAS, + "Possible memory configuration issue, fault at SMMU raised as CAMNOC SLAVE_IRQ"); + + CAM_ERR_RATE_LIMIT(CAM_CPAS, + "mainctrl[0x%x 0x%x] errvld[0x%x 0x%x] stall_en=%d, fault_en=%d, err_vld=%d", + camnoc_info->err_logger->mainctrl, + slave_err->mainctrl.value, + camnoc_info->err_logger->errvld, + slave_err->errvld.value, + slave_err->mainctrl.stall_en, + slave_err->mainctrl.fault_en, + slave_err->errvld.err_vld); + + err_code_index = slave_err->errlog0_low.err_code; + if (err_code_index > CAMNOC_SLAVE_MAX_ERR_CODE) + err_code_index = CAMNOC_SLAVE_MAX_ERR_CODE; + + CAM_ERR_RATE_LIMIT(CAM_CPAS, + "errlog0 low[0x%x 0x%x] high[0x%x 0x%x] loginfo_vld=%d, word_error=%d, non_secure=%d, device=%d, opc=%d, err_code=%d(%s) sizef=%d, addr_space=%d, len1=%d", + camnoc_info->err_logger->errlog0_low, + slave_err->errlog0_low.value, + camnoc_info->err_logger->errlog0_high, + slave_err->errlog0_high.value, + slave_err->errlog0_low.loginfo_vld, + slave_err->errlog0_low.word_error, + slave_err->errlog0_low.non_secure, + slave_err->errlog0_low.device, + slave_err->errlog0_low.opc, + slave_err->errlog0_low.err_code, + camnoc_salve_err_code[err_code_index], + slave_err->errlog0_low.sizef, + slave_err->errlog0_low.addr_space, + slave_err->errlog0_high.len1); + + CAM_ERR_RATE_LIMIT(CAM_CPAS, + "errlog1_low[0x%x 0x%x] errlog1_high[0x%x 0x%x] errlog2_low[0x%x 0x%x] errlog2_high[0x%x 0x%x] errlog3_low[0x%x 0x%x] errlog3_high[0x%x 0x%x]", + camnoc_info->err_logger->errlog1_low, + slave_err->errlog1_low.value, + camnoc_info->err_logger->errlog1_high, + slave_err->errlog1_high.value, + camnoc_info->err_logger->errlog2_low, + slave_err->errlog2_low.value, + camnoc_info->err_logger->errlog2_high, + slave_err->errlog2_high.value, + camnoc_info->err_logger->errlog3_low, + slave_err->errlog3_low.value, + camnoc_info->err_logger->errlog3_high, + slave_err->errlog3_high.value); + + return 0; +} + +static int cam_cpastop_handle_ubwc_enc_err(struct cam_cpas *cpas_core, + struct cam_hw_soc_info *soc_info, int i, + struct cam_camnoc_irq_ubwc_enc_data *enc_err) +{ + int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC]; + + enc_err->encerr_status.value = + cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->irq_err[i].err_status.offset); + + /* Let clients handle the UBWC errors */ + CAM_DBG(CAM_CPAS, + "ubwc enc err [%d]: offset[0x%x] value[0x%x]", + i, camnoc_info->irq_err[i].err_status.offset, + enc_err->encerr_status.value); + + return 0; +} + +static int cam_cpastop_handle_ubwc_dec_err(struct cam_cpas *cpas_core, + struct cam_hw_soc_info *soc_info, int i, + struct cam_camnoc_irq_ubwc_dec_data *dec_err) +{ + int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC]; + + dec_err->decerr_status.value = + cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->irq_err[i].err_status.offset); + + /* Let clients handle the UBWC errors */ + CAM_DBG(CAM_CPAS, + "ubwc dec err status [%d]: offset[0x%x] value[0x%x] thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d", + i, camnoc_info->irq_err[i].err_status.offset, + dec_err->decerr_status.value, + dec_err->decerr_status.thr_err, + dec_err->decerr_status.fcl_err, + dec_err->decerr_status.len_md_err, + dec_err->decerr_status.format_err); + + return 0; +} + +static int cam_cpastop_handle_ahb_timeout_err(struct cam_hw_info *cpas_hw, + struct cam_camnoc_irq_ahb_timeout_data *ahb_err) +{ + CAM_ERR_RATE_LIMIT(CAM_CPAS, "ahb timout error"); + + return 0; +} + +static int cam_cpastop_disable_test_irq(struct cam_hw_info *cpas_hw) +{ + camnoc_info->irq_sbm->sbm_clear.value &= ~0x4; + camnoc_info->irq_sbm->sbm_enable.value &= ~0x100; + camnoc_info->irq_err[CAM_CAMNOC_HW_IRQ_CAMNOC_TEST].enable = false; + + return 0; +} + +static int cam_cpastop_reset_irq(struct cam_hw_info *cpas_hw) +{ + int i; + + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->irq_sbm->sbm_clear); + for (i = 0; i < camnoc_info->irq_err_size; i++) { + if (camnoc_info->irq_err[i].enable) + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->irq_err[i].err_clear); + } + + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->irq_sbm->sbm_enable); + for (i = 0; i < camnoc_info->irq_err_size; i++) { + if (camnoc_info->irq_err[i].enable) + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->irq_err[i].err_enable); + } + + return 0; +} + +static void cam_cpastop_notify_clients(struct cam_cpas *cpas_core, + struct cam_cpas_irq_data *irq_data) +{ + int i; + struct cam_cpas_client *cpas_client; + bool error_handled = false; + + CAM_DBG(CAM_CPAS, + "Notify CB : num_clients=%d, registered=%d, started=%d", + cpas_core->num_clients, cpas_core->registered_clients, + cpas_core->streamon_clients); + + for (i = 0; i < cpas_core->num_clients; i++) { + if (CAM_CPAS_CLIENT_STARTED(cpas_core, i)) { + cpas_client = cpas_core->cpas_client[i]; + if (cpas_client->data.cam_cpas_client_cb) { + CAM_DBG(CAM_CPAS, + "Calling client CB %d : %d", + i, irq_data->irq_type); + error_handled = + cpas_client->data.cam_cpas_client_cb( + cpas_client->data.client_handle, + cpas_client->data.userdata, + irq_data); + if (error_handled) + break; + } + } + } +} + +static void cam_cpastop_work(struct work_struct *work) +{ + struct cam_cpas_work_payload *payload; + struct cam_hw_info *cpas_hw; + struct cam_cpas *cpas_core; + struct cam_hw_soc_info *soc_info; + int i; + enum cam_camnoc_hw_irq_type irq_type; + struct cam_cpas_irq_data irq_data; + + payload = container_of(work, struct cam_cpas_work_payload, work); + if (!payload) { + CAM_ERR(CAM_CPAS, "NULL payload"); + return; + } + + cpas_hw = payload->hw; + cpas_core = (struct cam_cpas *) cpas_hw->core_info; + soc_info = &cpas_hw->soc_info; + + if (!atomic_inc_not_zero(&cpas_core->irq_count)) { + CAM_ERR(CAM_CPAS, "CPAS off"); + return; + } + + for (i = 0; i < camnoc_info->irq_err_size; i++) { + if ((payload->irq_status & camnoc_info->irq_err[i].sbm_port) && + (camnoc_info->irq_err[i].enable)) { + irq_type = camnoc_info->irq_err[i].irq_type; + CAM_ERR(CAM_CPAS, "Error occurred, type=%d", irq_type); + memset(&irq_data, 0x0, sizeof(irq_data)); + irq_data.irq_type = (enum cam_camnoc_irq_type)irq_type; + + switch (irq_type) { + case CAM_CAMNOC_HW_IRQ_SLAVE_ERROR: + cam_cpastop_handle_errlogger( + cpas_core, soc_info, + &irq_data.u.slave_err); + break; + case CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR: + case CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR: + case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: + cam_cpastop_handle_ubwc_enc_err( + cpas_core, soc_info, i, + &irq_data.u.enc_err); + break; + case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR: + cam_cpastop_handle_ubwc_dec_err( + cpas_core, soc_info, i, + &irq_data.u.dec_err); + break; + case CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT: + cam_cpastop_handle_ahb_timeout_err( + cpas_hw, &irq_data.u.ahb_err); + break; + case CAM_CAMNOC_HW_IRQ_CAMNOC_TEST: + CAM_DBG(CAM_CPAS, "TEST IRQ"); + break; + default: + CAM_ERR(CAM_CPAS, "Invalid IRQ type"); + break; + } + + cam_cpastop_notify_clients(cpas_core, &irq_data); + + payload->irq_status &= + ~camnoc_info->irq_err[i].sbm_port; + } + } + atomic_dec(&cpas_core->irq_count); + wake_up(&cpas_core->irq_count_wq); + CAM_DBG(CAM_CPAS, "irq_count=%d\n", atomic_read(&cpas_core->irq_count)); + + if (payload->irq_status) + CAM_ERR(CAM_CPAS, "IRQ not handled irq_status=0x%x", + payload->irq_status); + + kfree(payload); +} + +static irqreturn_t cam_cpastop_handle_irq(int irq_num, void *data) +{ + struct cam_hw_info *cpas_hw = (struct cam_hw_info *)data; + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC]; + struct cam_cpas_work_payload *payload; + + if (!atomic_inc_not_zero(&cpas_core->irq_count)) { + CAM_ERR(CAM_CPAS, "CPAS off"); + return IRQ_HANDLED; + } + + payload = kzalloc(sizeof(struct cam_cpas_work_payload), GFP_ATOMIC); + if (!payload) + goto done; + + payload->irq_status = cam_io_r_mb( + soc_info->reg_map[camnoc_index].mem_base + + camnoc_info->irq_sbm->sbm_status.offset); + + CAM_DBG(CAM_CPAS, "IRQ callback, irq_status=0x%x", payload->irq_status); + + payload->hw = cpas_hw; + INIT_WORK((struct work_struct *)&payload->work, cam_cpastop_work); + + if (TEST_IRQ_ENABLE) + cam_cpastop_disable_test_irq(cpas_hw); + + cam_cpastop_reset_irq(cpas_hw); + + queue_work(cpas_core->work_queue, &payload->work); +done: + atomic_dec(&cpas_core->irq_count); + wake_up(&cpas_core->irq_count_wq); + + return IRQ_HANDLED; +} + +static int cam_cpastop_poweron(struct cam_hw_info *cpas_hw) +{ + int i; + for (i = 0; i < camnoc_info->specific_size; i++) { + if (camnoc_info->specific[i].enable) { + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].priority_lut_low); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].priority_lut_high); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].urgency); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].danger_lut); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].safe_lut); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].ubwc_ctl); + cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC, + &camnoc_info->specific[i].flag_out_set0_low); + } + } + + return 0; +} + +static int cam_cpastop_poweroff(struct cam_hw_info *cpas_hw) +{ + struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; + struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info; + int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC]; + int rc = 0; + struct cam_cpas_hw_errata_wa_list *errata_wa_list = + camnoc_info->errata_wa_list; + + if (!errata_wa_list) + return 0; + + if (errata_wa_list->camnoc_flush_slave_pending_trans.enable) { + struct cam_cpas_hw_errata_wa *errata_wa = + &errata_wa_list->camnoc_flush_slave_pending_trans; + + rc = cam_io_poll_value_wmask( + soc_info->reg_map[camnoc_index].mem_base + + errata_wa->data.reg_info.offset, + errata_wa->data.reg_info.value, + errata_wa->data.reg_info.mask, + CAM_CPAS_POLL_RETRY_CNT, + CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS); + if (rc) { + CAM_DBG(CAM_CPAS, + "camnoc flush slave pending trans failed"); + /* Do not return error, passthrough */ + rc = 0; + } + } + + return rc; +} + +static int cam_cpastop_init_hw_version(struct cam_hw_info *cpas_hw, + struct cam_cpas_hw_caps *hw_caps) +{ + int rc = 0; + struct cam_cpas_private_soc *soc_private = + (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; + + CAM_DBG(CAM_CPAS, + "hw_version=0x%x Camera Version %d.%d.%d, cpas version %d.%d.%d", + soc_private->hw_version, + hw_caps->camera_version.major, + hw_caps->camera_version.minor, + hw_caps->camera_version.incr, + hw_caps->cpas_version.major, + hw_caps->cpas_version.minor, + hw_caps->cpas_version.incr); + + switch (soc_private->hw_version) { + case CAM_CPAS_TITAN_170_V100: + camnoc_info = &cam170_cpas100_camnoc_info; + break; + case CAM_CPAS_TITAN_170_V110: + camnoc_info = &cam170_cpas110_camnoc_info; + break; + case CAM_CPAS_TITAN_175_V100: + camnoc_info = &cam175_cpas100_camnoc_info; + break; + case CAM_CPAS_TITAN_175_V101: + camnoc_info = &cam175_cpas101_camnoc_info; + break; + default: + CAM_ERR(CAM_CPAS, "Camera Version not supported %d.%d.%d", + hw_caps->camera_version.major, + hw_caps->camera_version.minor, + hw_caps->camera_version.incr); + rc = -EINVAL; + break; + } + + return 0; +} + +int cam_cpastop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops) +{ + if (!internal_ops) { + CAM_ERR(CAM_CPAS, "invalid NULL param"); + return -EINVAL; + } + + internal_ops->get_hw_info = cam_cpastop_get_hw_info; + internal_ops->init_hw_version = cam_cpastop_init_hw_version; + internal_ops->handle_irq = cam_cpastop_handle_irq; + internal_ops->setup_regbase = cam_cpastop_setup_regbase_indices; + internal_ops->power_on = cam_cpastop_poweron; + internal_ops->power_off = cam_cpastop_poweroff; + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.h new file mode 100644 index 000000000000..73f7e9be3eb7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cam_cpastop_hw.h @@ -0,0 +1,241 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CPASTOP_HW_H_ +#define _CAM_CPASTOP_HW_H_ + +#include "cam_cpas_api.h" +#include "cam_cpas_hw.h" + +/** + * enum cam_camnoc_hw_irq_type - Enum for camnoc error types + * + * @CAM_CAMNOC_HW_IRQ_SLAVE_ERROR: Each slave port in CAMNOC (3 QSB ports and + * 1 QHB port) has an error logger. The error + * observed at any slave port is logged into + * the error logger register and an IRQ is + * triggered + * @CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR : Triggered if any error + * detected in the IFE0 UBWC + * encoder instance + * @CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR : Triggered if any error + * detected in the IFE1 or IFE3 + * UBWC encoder instance + * @CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR: Triggered if any error + * detected in the IPE/BPS + * UBWC decoder instance + * @CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: Triggered if any error + * detected in the IPE/BPS UBWC + * encoder instance + * @CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT : Triggered when the QHS_ICP + * slave times out after 4000 + * AHB cycles + * @CAM_CAMNOC_HW_IRQ_RESERVED1 : Reserved + * @CAM_CAMNOC_HW_IRQ_RESERVED2 : Reserved + * @CAM_CAMNOC_HW_IRQ_CAMNOC_TEST : To test the IRQ logic + */ +enum cam_camnoc_hw_irq_type { + CAM_CAMNOC_HW_IRQ_SLAVE_ERROR = + CAM_CAMNOC_IRQ_SLAVE_ERROR, + CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR = + CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR, + CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR = + CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR, + CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR = + CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR = + CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT = + CAM_CAMNOC_IRQ_AHB_TIMEOUT, + CAM_CAMNOC_HW_IRQ_RESERVED1, + CAM_CAMNOC_HW_IRQ_RESERVED2, + CAM_CAMNOC_HW_IRQ_CAMNOC_TEST, +}; + +/** + * enum cam_camnoc_port_type - Enum for different camnoc hw ports. All CAMNOC + * settings like QoS, LUT mappings need to be configured for + * each of these ports. + * + * @CAM_CAMNOC_CDM: Indicates CDM HW connection to camnoc + * @CAM_CAMNOC_IFE02: Indicates IFE0, IFE2 HW connection to camnoc + * @CAM_CAMNOC_IFE13: Indicates IFE1, IFE3 HW connection to camnoc + * @CAM_CAMNOC_IPE_BPS_LRME_READ: Indicates IPE, BPS, LRME Read HW + * connection to camnoc + * @CAM_CAMNOC_IPE_BPS_LRME_WRITE: Indicates IPE, BPS, LRME Write HW + * connection to camnoc + * @CAM_CAMNOC_JPEG: Indicates JPEG HW connection to camnoc + * @CAM_CAMNOC_FD: Indicates FD HW connection to camnoc + * @CAM_CAMNOC_ICP: Indicates ICP HW connection to camnoc + */ +enum cam_camnoc_port_type { + CAM_CAMNOC_CDM, + CAM_CAMNOC_IFE02, + CAM_CAMNOC_IFE13, + CAM_CAMNOC_IPE_BPS_LRME_READ, + CAM_CAMNOC_IPE_BPS_LRME_WRITE, + CAM_CAMNOC_JPEG, + CAM_CAMNOC_FD, + CAM_CAMNOC_ICP, +}; + +/** + * struct cam_camnoc_specific : CPAS camnoc specific settings + * + * @port_type: Port type + * @enable: Whether to enable settings for this connection + * @priority_lut_low: Priority Low LUT mapping for this connection + * @priority_lut_high: Priority High LUT mapping for this connection + * @urgency: Urgency (QoS) settings for this connection + * @danger_lut: Danger LUT mapping for this connection + * @safe_lut: Safe LUT mapping for this connection + * @ubwc_ctl: UBWC control settings for this connection + * + */ +struct cam_camnoc_specific { + enum cam_camnoc_port_type port_type; + bool enable; + struct cam_cpas_reg priority_lut_low; + struct cam_cpas_reg priority_lut_high; + struct cam_cpas_reg urgency; + struct cam_cpas_reg danger_lut; + struct cam_cpas_reg safe_lut; + struct cam_cpas_reg ubwc_ctl; + struct cam_cpas_reg flag_out_set0_low; +}; + +/** + * struct cam_camnoc_irq_sbm : Sideband manager settings for all CAMNOC IRQs + * + * @sbm_enable: SBM settings for IRQ enable + * @sbm_status: SBM settings for IRQ status + * @sbm_clear: SBM settings for IRQ clear + * + */ +struct cam_camnoc_irq_sbm { + struct cam_cpas_reg sbm_enable; + struct cam_cpas_reg sbm_status; + struct cam_cpas_reg sbm_clear; +}; + +/** + * struct cam_camnoc_irq_err : Error settings specific to each CAMNOC IRQ + * + * @irq_type: Type of IRQ + * @enable: Whether to enable error settings for this IRQ + * @sbm_port: Corresponding SBM port for this IRQ + * @err_enable: Error enable settings for this IRQ + * @err_status: Error status settings for this IRQ + * @err_clear: Error clear settings for this IRQ + * + */ +struct cam_camnoc_irq_err { + enum cam_camnoc_hw_irq_type irq_type; + bool enable; + uint32_t sbm_port; + struct cam_cpas_reg err_enable; + struct cam_cpas_reg err_status; + struct cam_cpas_reg err_clear; +}; + +/** + * struct cam_cpas_hw_errata_wa : Struct for HW errata workaround info + * + * @enable: Whether to enable this errata workround + * @data: HW Errata workaround data + * + */ +struct cam_cpas_hw_errata_wa { + bool enable; + union { + struct cam_cpas_reg reg_info; + } data; +}; + +/** + * struct cam_cpas_hw_errata_wa_list : List of HW Errata workaround info + * + * @camnoc_flush_slave_pending_trans: Errata workaround info for flushing + * camnoc slave pending transactions before turning off CPAS_TOP gdsc + * + */ +struct cam_cpas_hw_errata_wa_list { + struct cam_cpas_hw_errata_wa camnoc_flush_slave_pending_trans; +}; + +/** + * struct cam_camnoc_err_logger_info : CAMNOC error logger register offsets + * + * @mainctrl: Register offset for mainctrl + * @errvld: Register offset for errvld + * @errlog0_low: Register offset for errlog0_low + * @errlog0_high: Register offset for errlog0_high + * @errlog1_low: Register offset for errlog1_low + * @errlog1_high: Register offset for errlog1_high + * @errlog2_low: Register offset for errlog2_low + * @errlog2_high: Register offset for errlog2_high + * @errlog3_low: Register offset for errlog3_low + * @errlog3_high: Register offset for errlog3_high + * + */ +struct cam_camnoc_err_logger_info { + uint32_t mainctrl; + uint32_t errvld; + uint32_t errlog0_low; + uint32_t errlog0_high; + uint32_t errlog1_low; + uint32_t errlog1_high; + uint32_t errlog2_low; + uint32_t errlog2_high; + uint32_t errlog3_low; + uint32_t errlog3_high; +}; + +/** + * struct cam_camnoc_info : Overall CAMNOC settings info + * + * @specific: Pointer to CAMNOC SPECIFICTONTTPTR settings + * @specific_size: Array size of SPECIFICTONTTPTR settings + * @irq_sbm: Pointer to CAMNOC IRQ SBM settings + * @irq_err: Pointer to CAMNOC IRQ Error settings + * @irq_err_size: Array size of IRQ Error settings + * @err_logger: Pointer to CAMNOC IRQ Error logger read registers + * @errata_wa_list: HW Errata workaround info + * + */ +struct cam_camnoc_info { + struct cam_camnoc_specific *specific; + int specific_size; + struct cam_camnoc_irq_sbm *irq_sbm; + struct cam_camnoc_irq_err *irq_err; + int irq_err_size; + struct cam_camnoc_err_logger_info *err_logger; + struct cam_cpas_hw_errata_wa_list *errata_wa_list; +}; + +/** + * struct cam_cpas_work_payload : Struct for cpas work payload data + * + * @hw: Pointer to HW info + * @irq_status: IRQ status value + * @irq_data: IRQ data + * @work: Work handle + * + */ +struct cam_cpas_work_payload { + struct cam_hw_info *hw; + uint32_t irq_status; + uint32_t irq_data; + struct work_struct work; +}; + +#endif /* _CAM_CPASTOP_HW_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop100.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop100.h new file mode 100644 index 000000000000..2654b4770b34 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop100.h @@ -0,0 +1,538 @@ +/* Copyright (c) 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. + */ + +#ifndef _CPASTOP100_H_ +#define _CPASTOP100_H_ + +#define TEST_IRQ_ENABLE 0 + +static struct cam_camnoc_irq_sbm cam_cpas100_irq_sbm = { + .sbm_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2040, /* SBM_FAULTINEN0_LOW */ + .value = 0x1 | /* SBM_FAULTINEN0_LOW_PORT0_MASK*/ + 0x2 | /* SBM_FAULTINEN0_LOW_PORT1_MASK */ + 0x4 | /* SBM_FAULTINEN0_LOW_PORT2_MASK */ + 0x8 | /* SBM_FAULTINEN0_LOW_PORT3_MASK */ + 0x10 | /* SBM_FAULTINEN0_LOW_PORT4_MASK */ + 0x20 | /* SBM_FAULTINEN0_LOW_PORT5_MASK */ + (TEST_IRQ_ENABLE ? + 0x100 : /* SBM_FAULTINEN0_LOW_PORT8_MASK */ + 0x0), + }, + .sbm_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2048, /* SBM_FAULTINSTATUS0_LOW */ + }, + .sbm_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2080, /* SBM_FLAGOUTCLR0_LOW */ + .value = TEST_IRQ_ENABLE ? 0x6 : 0x2, + } +}; + +static struct cam_camnoc_irq_err + cam_cpas100_irq_err[] = { + { + .irq_type = CAM_CAMNOC_HW_IRQ_SLAVE_ERROR, + .enable = true, + .sbm_port = 0x1, /* SBM_FAULTINSTATUS0_LOW_PORT0_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2718, /* ERRLOGGER_ERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x2, /* SBM_FAULTINSTATUS0_LOW_PORT1_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x5a0, /* SPECIFIC_IFE02_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x590, /* SPECIFIC_IFE02_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x598, /* SPECIFIC_IFE02_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x4, /* SBM_FAULTINSTATUS0_LOW_PORT2_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x9a0, /* SPECIFIC_IFE13_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x990, /* SPECIFIC_IFE13_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x998, /* SPECIFIC_IFE13_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + .enable = true, + .sbm_port = 0x8, /* SBM_FAULTINSTATUS0_LOW_PORT3_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0xd20, /* SPECIFIC_IBL_RD_DECERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0xd10, /* SPECIFIC_IBL_RD_DECERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0xd18, /* SPECIFIC_IBL_RD_DECERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x10, /* SBM_FAULTINSTATUS0_LOW_PORT4_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x11a0, /* SPECIFIC_IBL_WR_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x1190, + /* SPECIFIC_IBL_WR_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x1198, /* SPECIFIC_IBL_WR_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT, + .enable = true, + .sbm_port = 0x20, /* SBM_FAULTINSTATUS0_LOW_PORT5_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED1, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED2, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_CAMNOC_TEST, + .enable = TEST_IRQ_ENABLE ? true : false, + .sbm_port = 0x100, /* SBM_FAULTINSTATUS0_LOW_PORT8_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x5, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, +}; + +static struct cam_camnoc_specific + cam_cpas100_camnoc_specific[] = { + { + .port_type = CAM_CAMNOC_CDM, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x30, /* SPECIFIC_CDM_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x34, /* SPECIFIC_CDM_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x38, /* SPECIFIC_CDM_URGENCY_LOW */ + .mask = 0x7, /* SPECIFIC_CDM_URGENCY_LOW_READ_MASK */ + .shift = 0x0, /* SPECIFIC_CDM_URGENCY_LOW_READ_SHIFT */ + .value = 0, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x40, /* SPECIFIC_CDM_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x48, /* SPECIFIC_CDM_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_IFE02, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x430, /* SPECIFIC_IFE02_PRIORITYLUT_LOW */ + .value = 0x66665433, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x434, /* SPECIFIC_IFE02_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x438, /* SPECIFIC_IFE02_URGENCY_LOW */ + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x440, /* SPECIFIC_IFE02_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x448, /* SPECIFIC_IFE02_SAFELUT_LOW */ + .value = 0x3, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x588, /* SPECIFIC_IFE02_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IFE13, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x830, /* SPECIFIC_IFE13_PRIORITYLUT_LOW */ + .value = 0x66665433, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x834, /* SPECIFIC_IFE13_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x838, /* SPECIFIC_IFE13_URGENCY_LOW */ + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x840, /* SPECIFIC_IFE13_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x848, /* SPECIFIC_IFE13_SAFELUT_LOW */ + .value = 0x3, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x988, /* SPECIFIC_IFE13_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_READ, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc30, /* SPECIFIC_IBL_RD_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc34, /* SPECIFIC_IBL_RD_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0xc38, /* SPECIFIC_IBL_RD_URGENCY_LOW */ + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_MASK */ + .mask = 0x7, + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_SHIFT */ + .shift = 0x0, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc40, /* SPECIFIC_IBL_RD_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc48, /* SPECIFIC_IBL_RD_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xd08, /* SPECIFIC_IBL_RD_DECCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_WRITE, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1030, /* SPECIFIC_IBL_WR_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1034, /* SPECIFIC_IBL_WR_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x1038, /* SPECIFIC_IBL_WR_URGENCY_LOW */ + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1040, /* SPECIFIC_IBL_WR_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1048, /* SPECIFIC_IBL_WR_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1188, /* SPECIFIC_IBL_WR_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_JPEG, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1430, /* SPECIFIC_JPEG_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1434, /* SPECIFIC_JPEG_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1438, /* SPECIFIC_JPEG_URGENCY_LOW */ + .value = 0x22, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1440, /* SPECIFIC_JPEG_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1448, /* SPECIFIC_JPEG_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_FD, + .enable = false, + }, + { + .port_type = CAM_CAMNOC_ICP, + .enable = false, + } +}; + +static struct cam_camnoc_err_logger_info cam170_cpas100_err_logger_offsets = { + .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */ + .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */ + .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */ + .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */ + .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */ + .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */ + .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */ + .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */ +}; + +static struct cam_cpas_hw_errata_wa_list cam170_cpas100_errata_wa_list = { + .camnoc_flush_slave_pending_trans = { + .enable = true, + .data.reg_info = { + .access_type = CAM_REG_TYPE_READ, + .offset = 0x2100, /* SidebandManager_SenseIn0_Low */ + .mask = 0xE0000, /* Bits 17, 18, 19 */ + .value = 0, /* expected to be 0 */ + }, + }, +}; + +struct cam_camnoc_info cam170_cpas100_camnoc_info = { + .specific = &cam_cpas100_camnoc_specific[0], + .specific_size = sizeof(cam_cpas100_camnoc_specific) / + sizeof(cam_cpas100_camnoc_specific[0]), + .irq_sbm = &cam_cpas100_irq_sbm, + .irq_err = &cam_cpas100_irq_err[0], + .irq_err_size = sizeof(cam_cpas100_irq_err) / + sizeof(cam_cpas100_irq_err[0]), + .err_logger = &cam170_cpas100_err_logger_offsets, + .errata_wa_list = &cam170_cpas100_errata_wa_list, +}; + +#endif /* _CPASTOP100_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v170_110.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v170_110.h new file mode 100644 index 000000000000..e1af22b9f504 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v170_110.h @@ -0,0 +1,545 @@ +/* Copyright (c) 2017-2018, 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 _CPASTOP_V170_110_H_ +#define _CPASTOP_V170_110_H_ + +#define TEST_IRQ_ENABLE 0 + +static struct cam_camnoc_irq_sbm cam_cpas110_irq_sbm = { + .sbm_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2040, /* SBM_FAULTINEN0_LOW */ + .value = 0x1 | /* SBM_FAULTINEN0_LOW_PORT0_MASK*/ + 0x2 | /* SBM_FAULTINEN0_LOW_PORT1_MASK */ + 0x4 | /* SBM_FAULTINEN0_LOW_PORT2_MASK */ + 0x8 | /* SBM_FAULTINEN0_LOW_PORT3_MASK */ + 0x10 | /* SBM_FAULTINEN0_LOW_PORT4_MASK */ + 0x20 | /* SBM_FAULTINEN0_LOW_PORT5_MASK */ + (TEST_IRQ_ENABLE ? + 0x100 : /* SBM_FAULTINEN0_LOW_PORT8_MASK */ + 0x0), + }, + .sbm_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2048, /* SBM_FAULTINSTATUS0_LOW */ + }, + .sbm_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2080, /* SBM_FLAGOUTCLR0_LOW */ + .value = TEST_IRQ_ENABLE ? 0x6 : 0x2, + } +}; + +static struct cam_camnoc_irq_err + cam_cpas110_irq_err[] = { + { + .irq_type = CAM_CAMNOC_HW_IRQ_SLAVE_ERROR, + .enable = true, + .sbm_port = 0x1, /* SBM_FAULTINSTATUS0_LOW_PORT0_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2718, /* ERRLOGGER_ERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x2, /* SBM_FAULTINSTATUS0_LOW_PORT1_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x5a0, /* SPECIFIC_IFE02_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x590, /* SPECIFIC_IFE02_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x598, /* SPECIFIC_IFE02_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x4, /* SBM_FAULTINSTATUS0_LOW_PORT2_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x9a0, /* SPECIFIC_IFE13_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x990, /* SPECIFIC_IFE13_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x998, /* SPECIFIC_IFE13_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + .enable = true, + .sbm_port = 0x8, /* SBM_FAULTINSTATUS0_LOW_PORT3_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0xd20, /* SPECIFIC_IBL_RD_DECERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0xd10, /* SPECIFIC_IBL_RD_DECERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0xd18, /* SPECIFIC_IBL_RD_DECERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x10, /* SBM_FAULTINSTATUS0_LOW_PORT4_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x11a0, /* SPECIFIC_IBL_WR_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x1190, + /* SPECIFIC_IBL_WR_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x1198, /* SPECIFIC_IBL_WR_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT, + .enable = true, + .sbm_port = 0x20, /* SBM_FAULTINSTATUS0_LOW_PORT5_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED1, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED2, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_CAMNOC_TEST, + .enable = TEST_IRQ_ENABLE ? true : false, + .sbm_port = 0x100, /* SBM_FAULTINSTATUS0_LOW_PORT8_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x5, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, +}; + +static struct cam_camnoc_specific + cam_cpas110_camnoc_specific[] = { + { + .port_type = CAM_CAMNOC_CDM, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x30, /* SPECIFIC_CDM_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x34, /* SPECIFIC_CDM_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x38, /* SPECIFIC_CDM_URGENCY_LOW */ + .mask = 0x7, /* SPECIFIC_CDM_URGENCY_LOW_READ_MASK */ + .shift = 0x0, /* SPECIFIC_CDM_URGENCY_LOW_READ_SHIFT */ + .value = 0x2, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x40, /* SPECIFIC_CDM_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x48, /* SPECIFIC_CDM_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_IFE02, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x430, /* SPECIFIC_IFE02_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x434, /* SPECIFIC_IFE02_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x438, /* SPECIFIC_IFE02_URGENCY_LOW */ + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x440, /* SPECIFIC_IFE02_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x448, /* SPECIFIC_IFE02_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x588, /* SPECIFIC_IFE02_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IFE13, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x830, /* SPECIFIC_IFE13_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x834, /* SPECIFIC_IFE13_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x838, /* SPECIFIC_IFE13_URGENCY_LOW */ + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x840, /* SPECIFIC_IFE13_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x848, /* SPECIFIC_IFE13_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x988, /* SPECIFIC_IFE13_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_READ, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc30, /* SPECIFIC_IBL_RD_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc34, /* SPECIFIC_IBL_RD_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0xc38, /* SPECIFIC_IBL_RD_URGENCY_LOW */ + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_MASK */ + .mask = 0x7, + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_SHIFT */ + .shift = 0x0, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc40, /* SPECIFIC_IBL_RD_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc48, /* SPECIFIC_IBL_RD_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xd08, /* SPECIFIC_IBL_RD_DECCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_WRITE, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1030, /* SPECIFIC_IBL_WR_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1034, /* SPECIFIC_IBL_WR_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x1038, /* SPECIFIC_IBL_WR_URGENCY_LOW */ + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1040, /* SPECIFIC_IBL_WR_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1048, /* SPECIFIC_IBL_WR_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1188, /* SPECIFIC_IBL_WR_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_JPEG, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1430, /* SPECIFIC_JPEG_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1434, /* SPECIFIC_JPEG_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1438, /* SPECIFIC_JPEG_URGENCY_LOW */ + .value = 0x22, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1440, /* SPECIFIC_JPEG_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1448, /* SPECIFIC_JPEG_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_FD, + .enable = false, + }, + { + .port_type = CAM_CAMNOC_ICP, + .enable = true, + .flag_out_set0_low = { + .enable = true, + .access_type = CAM_REG_TYPE_WRITE, + .masked_value = 0, + .offset = 0x2088, + .value = 0x100000, + }, + }, +}; + +static struct cam_camnoc_err_logger_info cam170_cpas110_err_logger_offsets = { + .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */ + .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */ + .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */ + .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */ + .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */ + .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */ + .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */ + .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */ +}; + +static struct cam_cpas_hw_errata_wa_list cam170_cpas110_errata_wa_list = { + .camnoc_flush_slave_pending_trans = { + .enable = false, + .data.reg_info = { + .access_type = CAM_REG_TYPE_READ, + .offset = 0x2100, /* SidebandManager_SenseIn0_Low */ + .mask = 0xE0000, /* Bits 17, 18, 19 */ + .value = 0, /* expected to be 0 */ + }, + }, +}; + +static struct cam_camnoc_info cam170_cpas110_camnoc_info = { + .specific = &cam_cpas110_camnoc_specific[0], + .specific_size = sizeof(cam_cpas110_camnoc_specific) / + sizeof(cam_cpas110_camnoc_specific[0]), + .irq_sbm = &cam_cpas110_irq_sbm, + .irq_err = &cam_cpas110_irq_err[0], + .irq_err_size = sizeof(cam_cpas110_irq_err) / + sizeof(cam_cpas110_irq_err[0]), + .err_logger = &cam170_cpas110_err_logger_offsets, + .errata_wa_list = &cam170_cpas110_errata_wa_list, +}; + +#endif /* _CPASTOP_V170_110_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_100.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_100.h new file mode 100644 index 000000000000..e2aad09aa8e7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_100.h @@ -0,0 +1,545 @@ +/* Copyright (c) 2018, 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 _CPASTOP_V175_100_H_ +#define _CPASTOP_V175_100_H_ + +#define TEST_IRQ_ENABLE 0 + +static struct cam_camnoc_irq_sbm cam_cpas_v175_100_irq_sbm = { + .sbm_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2040, /* SBM_FAULTINEN0_LOW */ + .value = 0x1 | /* SBM_FAULTINEN0_LOW_PORT0_MASK*/ + 0x2 | /* SBM_FAULTINEN0_LOW_PORT1_MASK */ + 0x4 | /* SBM_FAULTINEN0_LOW_PORT2_MASK */ + 0x8 | /* SBM_FAULTINEN0_LOW_PORT3_MASK */ + 0x10 | /* SBM_FAULTINEN0_LOW_PORT4_MASK */ + 0x20 | /* SBM_FAULTINEN0_LOW_PORT5_MASK */ + (TEST_IRQ_ENABLE ? + 0x100 : /* SBM_FAULTINEN0_LOW_PORT8_MASK */ + 0x0), + }, + .sbm_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2048, /* SBM_FAULTINSTATUS0_LOW */ + }, + .sbm_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2080, /* SBM_FLAGOUTCLR0_LOW */ + .value = TEST_IRQ_ENABLE ? 0x6 : 0x2, + } +}; + +static struct cam_camnoc_irq_err + cam_cpas_v175_100_irq_err[] = { + { + .irq_type = CAM_CAMNOC_HW_IRQ_SLAVE_ERROR, + .enable = true, + .sbm_port = 0x1, /* SBM_FAULTINSTATUS0_LOW_PORT0_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2718, /* ERRLOGGER_ERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x2, /* SBM_FAULTINSTATUS0_LOW_PORT1_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x5a0, /* SPECIFIC_IFE02_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x590, /* SPECIFIC_IFE02_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x598, /* SPECIFIC_IFE02_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x4, /* SBM_FAULTINSTATUS0_LOW_PORT2_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x9a0, /* SPECIFIC_IFE13_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x990, /* SPECIFIC_IFE13_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x998, /* SPECIFIC_IFE13_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + .enable = true, + .sbm_port = 0x8, /* SBM_FAULTINSTATUS0_LOW_PORT3_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0xd20, /* SPECIFIC_IBL_RD_DECERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0xd10, /* SPECIFIC_IBL_RD_DECERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0xd18, /* SPECIFIC_IBL_RD_DECERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x10, /* SBM_FAULTINSTATUS0_LOW_PORT4_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x11a0, /* SPECIFIC_IBL_WR_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x1190, + /* SPECIFIC_IBL_WR_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x1198, /* SPECIFIC_IBL_WR_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT, + .enable = true, + .sbm_port = 0x20, /* SBM_FAULTINSTATUS0_LOW_PORT5_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED1, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED2, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_CAMNOC_TEST, + .enable = TEST_IRQ_ENABLE ? true : false, + .sbm_port = 0x100, /* SBM_FAULTINSTATUS0_LOW_PORT8_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x5, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, +}; + +static struct cam_camnoc_specific + cam_cpas_v175_100_camnoc_specific[] = { + { + .port_type = CAM_CAMNOC_CDM, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x30, /* SPECIFIC_CDM_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x34, /* SPECIFIC_CDM_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x38, /* SPECIFIC_CDM_URGENCY_LOW */ + .mask = 0x7, /* SPECIFIC_CDM_URGENCY_LOW_READ_MASK */ + .shift = 0x0, /* SPECIFIC_CDM_URGENCY_LOW_READ_SHIFT */ + .value = 0x2, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x40, /* SPECIFIC_CDM_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x48, /* SPECIFIC_CDM_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_IFE02, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x430, /* SPECIFIC_IFE02_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x434, /* SPECIFIC_IFE02_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x438, /* SPECIFIC_IFE02_URGENCY_LOW */ + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x440, /* SPECIFIC_IFE02_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x448, /* SPECIFIC_IFE02_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x588, /* SPECIFIC_IFE02_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IFE13, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x830, /* SPECIFIC_IFE13_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x834, /* SPECIFIC_IFE13_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x838, /* SPECIFIC_IFE13_URGENCY_LOW */ + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x840, /* SPECIFIC_IFE13_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x848, /* SPECIFIC_IFE13_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x988, /* SPECIFIC_IFE13_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_READ, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc30, /* SPECIFIC_IBL_RD_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc34, /* SPECIFIC_IBL_RD_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0xc38, /* SPECIFIC_IBL_RD_URGENCY_LOW */ + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_MASK */ + .mask = 0x7, + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_SHIFT */ + .shift = 0x0, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc40, /* SPECIFIC_IBL_RD_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc48, /* SPECIFIC_IBL_RD_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xd08, /* SPECIFIC_IBL_RD_DECCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_WRITE, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1030, /* SPECIFIC_IBL_WR_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1034, /* SPECIFIC_IBL_WR_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x1038, /* SPECIFIC_IBL_WR_URGENCY_LOW */ + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1040, /* SPECIFIC_IBL_WR_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1048, /* SPECIFIC_IBL_WR_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1188, /* SPECIFIC_IBL_WR_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_JPEG, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1430, /* SPECIFIC_JPEG_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1434, /* SPECIFIC_JPEG_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1438, /* SPECIFIC_JPEG_URGENCY_LOW */ + .value = 0x22, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1440, /* SPECIFIC_JPEG_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1448, /* SPECIFIC_JPEG_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_FD, + .enable = false, + }, + { + .port_type = CAM_CAMNOC_ICP, + .enable = true, + .flag_out_set0_low = { + .enable = true, + .access_type = CAM_REG_TYPE_WRITE, + .masked_value = 0, + .offset = 0x2088, + .value = 0x100000, + }, + }, +}; + +static struct cam_camnoc_err_logger_info cam175_cpas100_err_logger_offsets = { + .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */ + .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */ + .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */ + .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */ + .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */ + .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */ + .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */ + .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */ +}; + +static struct cam_cpas_hw_errata_wa_list cam175_cpas100_errata_wa_list = { + .camnoc_flush_slave_pending_trans = { + .enable = false, + .data.reg_info = { + .access_type = CAM_REG_TYPE_READ, + .offset = 0x2100, /* SidebandManager_SenseIn0_Low */ + .mask = 0xE0000, /* Bits 17, 18, 19 */ + .value = 0, /* expected to be 0 */ + }, + }, +}; + +static struct cam_camnoc_info cam175_cpas100_camnoc_info = { + .specific = &cam_cpas_v175_100_camnoc_specific[0], + .specific_size = sizeof(cam_cpas_v175_100_camnoc_specific) / + sizeof(cam_cpas_v175_100_camnoc_specific[0]), + .irq_sbm = &cam_cpas_v175_100_irq_sbm, + .irq_err = &cam_cpas_v175_100_irq_err[0], + .irq_err_size = sizeof(cam_cpas_v175_100_irq_err) / + sizeof(cam_cpas_v175_100_irq_err[0]), + .err_logger = &cam175_cpas100_err_logger_offsets, + .errata_wa_list = &cam175_cpas100_errata_wa_list, +}; + +#endif /* _CPASTOP_V175_100_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_101.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_101.h new file mode 100644 index 000000000000..e5e967380c23 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/cpas_top/cpastop_v175_101.h @@ -0,0 +1,545 @@ +/* Copyright (c) 2018, 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 _CPASTOP_V175_101_H_ +#define _CPASTOP_V175_101_H_ + +#define TEST_IRQ_ENABLE 0 + +static struct cam_camnoc_irq_sbm cam_cpas_v175_101_irq_sbm = { + .sbm_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2040, /* SBM_FAULTINEN0_LOW */ + .value = 0x1 | /* SBM_FAULTINEN0_LOW_PORT0_MASK*/ + 0x2 | /* SBM_FAULTINEN0_LOW_PORT1_MASK */ + 0x4 | /* SBM_FAULTINEN0_LOW_PORT2_MASK */ + 0x8 | /* SBM_FAULTINEN0_LOW_PORT3_MASK */ + 0x10 | /* SBM_FAULTINEN0_LOW_PORT4_MASK */ + 0x20 | /* SBM_FAULTINEN0_LOW_PORT5_MASK */ + (TEST_IRQ_ENABLE ? + 0x100 : /* SBM_FAULTINEN0_LOW_PORT8_MASK */ + 0x0), + }, + .sbm_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2048, /* SBM_FAULTINSTATUS0_LOW */ + }, + .sbm_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2080, /* SBM_FLAGOUTCLR0_LOW */ + .value = TEST_IRQ_ENABLE ? 0x6 : 0x2, + } +}; + +static struct cam_camnoc_irq_err + cam_cpas_v175_101_irq_err[] = { + { + .irq_type = CAM_CAMNOC_HW_IRQ_SLAVE_ERROR, + .enable = true, + .sbm_port = 0x1, /* SBM_FAULTINSTATUS0_LOW_PORT0_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x2718, /* ERRLOGGER_ERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x2, /* SBM_FAULTINSTATUS0_LOW_PORT1_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x5a0, /* SPECIFIC_IFE02_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x590, /* SPECIFIC_IFE02_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x598, /* SPECIFIC_IFE02_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x4, /* SBM_FAULTINSTATUS0_LOW_PORT2_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x9a0, /* SPECIFIC_IFE13_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x990, /* SPECIFIC_IFE13_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x998, /* SPECIFIC_IFE13_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + .enable = true, + .sbm_port = 0x8, /* SBM_FAULTINSTATUS0_LOW_PORT3_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0xd20, /* SPECIFIC_IBL_RD_DECERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0xd10, /* SPECIFIC_IBL_RD_DECERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0xd18, /* SPECIFIC_IBL_RD_DECERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + .enable = true, + .sbm_port = 0x10, /* SBM_FAULTINSTATUS0_LOW_PORT4_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x11a0, /* SPECIFIC_IBL_WR_ENCERREN_LOW */ + .value = 1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x1190, + /* SPECIFIC_IBL_WR_ENCERRSTATUS_LOW */ + }, + .err_clear = { + .access_type = CAM_REG_TYPE_WRITE, + .enable = true, + .offset = 0x1198, /* SPECIFIC_IBL_WR_ENCERRCLR_LOW */ + .value = 1, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT, + .enable = true, + .sbm_port = 0x20, /* SBM_FAULTINSTATUS0_LOW_PORT5_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x1, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED1, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_RESERVED2, + .enable = false, + }, + { + .irq_type = CAM_CAMNOC_HW_IRQ_CAMNOC_TEST, + .enable = TEST_IRQ_ENABLE ? true : false, + .sbm_port = 0x100, /* SBM_FAULTINSTATUS0_LOW_PORT8_MASK */ + .err_enable = { + .access_type = CAM_REG_TYPE_READ_WRITE, + .enable = true, + .offset = 0x2088, /* SBM_FLAGOUTSET0_LOW */ + .value = 0x5, + }, + .err_status = { + .access_type = CAM_REG_TYPE_READ, + .enable = true, + .offset = 0x2090, /* SBM_FLAGOUTSTATUS0_LOW */ + }, + .err_clear = { + .enable = false, + }, + }, +}; + +static struct cam_camnoc_specific + cam_cpas_v175_101_camnoc_specific[] = { + { + .port_type = CAM_CAMNOC_CDM, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x30, /* SPECIFIC_CDM_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x34, /* SPECIFIC_CDM_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x38, /* SPECIFIC_CDM_URGENCY_LOW */ + .mask = 0x7, /* SPECIFIC_CDM_URGENCY_LOW_READ_MASK */ + .shift = 0x0, /* SPECIFIC_CDM_URGENCY_LOW_READ_SHIFT */ + .value = 0x2, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x40, /* SPECIFIC_CDM_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x48, /* SPECIFIC_CDM_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_IFE02, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x430, /* SPECIFIC_IFE02_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x434, /* SPECIFIC_IFE02_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x438, /* SPECIFIC_IFE02_URGENCY_LOW */ + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE02_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x440, /* SPECIFIC_IFE02_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x448, /* SPECIFIC_IFE02_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x588, /* SPECIFIC_IFE02_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IFE13, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x830, /* SPECIFIC_IFE13_PRIORITYLUT_LOW */ + .value = 0x66666543, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x834, /* SPECIFIC_IFE13_PRIORITYLUT_HIGH */ + .value = 0x66666666, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x838, /* SPECIFIC_IFE13_URGENCY_LOW */ + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IFE13_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x840, /* SPECIFIC_IFE13_DANGERLUT_LOW */ + .value = 0xFFFFFF00, + }, + .safe_lut = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .offset = 0x848, /* SPECIFIC_IFE13_SAFELUT_LOW */ + .value = 0x1, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x988, /* SPECIFIC_IFE13_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_READ, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc30, /* SPECIFIC_IBL_RD_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc34, /* SPECIFIC_IBL_RD_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0xc38, /* SPECIFIC_IBL_RD_URGENCY_LOW */ + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_MASK */ + .mask = 0x7, + /* SPECIFIC_IBL_RD_URGENCY_LOW_READ_SHIFT */ + .shift = 0x0, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc40, /* SPECIFIC_IBL_RD_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xc48, /* SPECIFIC_IBL_RD_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0xd08, /* SPECIFIC_IBL_RD_DECCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_IPE_BPS_LRME_WRITE, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1030, /* SPECIFIC_IBL_WR_PRIORITYLUT_LOW */ + .value = 0x33333333, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1034, /* SPECIFIC_IBL_WR_PRIORITYLUT_HIGH */ + .value = 0x33333333, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 1, + .offset = 0x1038, /* SPECIFIC_IBL_WR_URGENCY_LOW */ + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_MASK */ + .mask = 0x70, + /* SPECIFIC_IBL_WR_URGENCY_LOW_WRITE_SHIFT */ + .shift = 0x4, + .value = 3, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1040, /* SPECIFIC_IBL_WR_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1048, /* SPECIFIC_IBL_WR_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1188, /* SPECIFIC_IBL_WR_ENCCTL_LOW */ + .value = 1, + }, + }, + { + .port_type = CAM_CAMNOC_JPEG, + .enable = true, + .priority_lut_low = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1430, /* SPECIFIC_JPEG_PRIORITYLUT_LOW */ + .value = 0x22222222, + }, + .priority_lut_high = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1434, /* SPECIFIC_JPEG_PRIORITYLUT_HIGH */ + .value = 0x22222222, + }, + .urgency = { + .enable = true, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1438, /* SPECIFIC_JPEG_URGENCY_LOW */ + .value = 0x22, + }, + .danger_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1440, /* SPECIFIC_JPEG_DANGERLUT_LOW */ + .value = 0x0, + }, + .safe_lut = { + .enable = false, + .access_type = CAM_REG_TYPE_READ_WRITE, + .masked_value = 0, + .offset = 0x1448, /* SPECIFIC_JPEG_SAFELUT_LOW */ + .value = 0x0, + }, + .ubwc_ctl = { + .enable = false, + }, + }, + { + .port_type = CAM_CAMNOC_FD, + .enable = false, + }, + { + .port_type = CAM_CAMNOC_ICP, + .enable = true, + .flag_out_set0_low = { + .enable = true, + .access_type = CAM_REG_TYPE_WRITE, + .masked_value = 0, + .offset = 0x2088, + .value = 0x100000, + }, + }, +}; + +static struct cam_camnoc_err_logger_info cam175_cpas101_err_logger_offsets = { + .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */ + .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */ + .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */ + .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */ + .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */ + .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */ + .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */ + .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */ + .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */ + .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */ +}; + +static struct cam_cpas_hw_errata_wa_list cam175_cpas101_errata_wa_list = { + .camnoc_flush_slave_pending_trans = { + .enable = false, + .data.reg_info = { + .access_type = CAM_REG_TYPE_READ, + .offset = 0x2100, /* SidebandManager_SenseIn0_Low */ + .mask = 0xE0000, /* Bits 17, 18, 19 */ + .value = 0, /* expected to be 0 */ + }, + }, +}; + +static struct cam_camnoc_info cam175_cpas101_camnoc_info = { + .specific = &cam_cpas_v175_101_camnoc_specific[0], + .specific_size = sizeof(cam_cpas_v175_101_camnoc_specific) / + sizeof(cam_cpas_v175_101_camnoc_specific[0]), + .irq_sbm = &cam_cpas_v175_101_irq_sbm, + .irq_err = &cam_cpas_v175_101_irq_err[0], + .irq_err_size = sizeof(cam_cpas_v175_101_irq_err) / + sizeof(cam_cpas_v175_101_irq_err[0]), + .err_logger = &cam175_cpas101_err_logger_offsets, + .errata_wa_list = &cam175_cpas101_errata_wa_list, +}; + +#endif /* _CPASTOP_V175_101_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_cpas/include/cam_cpas_api.h b/drivers/media/platform/msm/camera_oneplus/cam_cpas/include/cam_cpas_api.h new file mode 100644 index 000000000000..c844ef78a3c3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_cpas/include/cam_cpas_api.h @@ -0,0 +1,517 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CPAS_API_H_ +#define _CAM_CPAS_API_H_ + +#include +#include + +#include +#include "cam_soc_util.h" + +#define CAM_HW_IDENTIFIER_LENGTH 128 + +/* Default AXI Bandwidth vote */ +#define CAM_CPAS_DEFAULT_AXI_BW 1024 + +/** + * enum cam_cpas_reg_base - Enum for register base identifier. These + * are the identifiers used in generic register + * write/read APIs provided by cpas driver. + */ +enum cam_cpas_reg_base { + CAM_CPAS_REG_CPASTOP, + CAM_CPAS_REG_CAMNOC, + CAM_CPAS_REG_CAMSS, + CAM_CPAS_REG_MAX +}; + +/** + * enum cam_cpas_hw_version - Enum for Titan CPAS HW Versions + */ +enum cam_cpas_hw_version { + CAM_CPAS_TITAN_NONE = 0, + CAM_CPAS_TITAN_170_V100 = 0x170100, + CAM_CPAS_TITAN_170_V110 = 0x170110, + CAM_CPAS_TITAN_170_V120 = 0x170120, + CAM_CPAS_TITAN_175_V100 = 0x175100, + CAM_CPAS_TITAN_175_V101 = 0x175101, + CAM_CPAS_TITAN_MAX +}; + + +/** + * enum cam_camnoc_irq_type - Enum for camnoc irq types + * + * @CAM_CAMNOC_IRQ_SLAVE_ERROR: Each slave port in CAMNOC (3 QSB ports and + * 1 QHB port) has an error logger. The error + * observed at any slave port is logged into + * the error logger register and an IRQ is + * triggered + * @CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR : Triggered if any error detected + * in the IFE0 UBWC encoder instance + * @CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR : Triggered if any error detected + * in the IFE1 or IFE3 UBWC encoder + * instance + * @CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR: Triggered if any error detected + * in the IPE/BPS UBWC decoder + * instance + * @CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: Triggered if any error detected + * in the IPE/BPS UBWC encoder + * instance + * @CAM_CAMNOC_IRQ_AHB_TIMEOUT : Triggered when the QHS_ICP slave + * times out after 4000 AHB cycles + */ +enum cam_camnoc_irq_type { + CAM_CAMNOC_IRQ_SLAVE_ERROR, + CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR, + CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR, + CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR, + CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR, + CAM_CAMNOC_IRQ_AHB_TIMEOUT, +}; + +/** + * struct cam_camnoc_irq_slave_err_data : Data for Slave error. + * + * @mainctrl : Err logger mainctrl info + * @errvld : Err logger errvld info + * @errlog0_low : Err logger errlog0_low info + * @errlog0_high : Err logger errlog0_high info + * @errlog1_low : Err logger errlog1_low info + * @errlog1_high : Err logger errlog1_high info + * @errlog2_low : Err logger errlog2_low info + * @errlog2_high : Err logger errlog2_high info + * @errlog3_low : Err logger errlog3_low info + * @errlog3_high : Err logger errlog3_high info + * + */ +struct cam_camnoc_irq_slave_err_data { + union { + struct { + uint32_t stall_en : 1; /* bit 0 */ + uint32_t fault_en : 1; /* bit 1 */ + uint32_t rsv : 30; /* bits 2-31 */ + }; + uint32_t value; + } mainctrl; + union { + struct { + uint32_t err_vld : 1; /* bit 0 */ + uint32_t rsv : 31; /* bits 1-31 */ + }; + uint32_t value; + } errvld; + union { + struct { + uint32_t loginfo_vld : 1; /* bit 0 */ + uint32_t word_error : 1; /* bit 1 */ + uint32_t non_secure : 1; /* bit 2 */ + uint32_t device : 1; /* bit 3 */ + uint32_t opc : 3; /* bits 4 - 6 */ + uint32_t rsv0 : 1; /* bit 7 */ + uint32_t err_code : 3; /* bits 8 - 10 */ + uint32_t sizef : 3; /* bits 11 - 13 */ + uint32_t rsv1 : 2; /* bits 14 - 15 */ + uint32_t addr_space : 6; /* bits 16 - 21 */ + uint32_t rsv2 : 10; /* bits 22 - 31 */ + }; + uint32_t value; + } errlog0_low; + union { + struct { + uint32_t len1 : 10; /* bits 0 - 9 */ + uint32_t rsv : 22; /* bits 10 - 31 */ + }; + uint32_t value; + } errlog0_high; + union { + struct { + uint32_t path : 16; /* bits 0 - 15 */ + uint32_t rsv : 16; /* bits 16 - 31 */ + }; + uint32_t value; + } errlog1_low; + union { + struct { + uint32_t extid : 18; /* bits 0 - 17 */ + uint32_t rsv : 14; /* bits 18 - 31 */ + }; + uint32_t value; + } errlog1_high; + union { + struct { + uint32_t errlog2_lsb : 32; /* bits 0 - 31 */ + }; + uint32_t value; + } errlog2_low; + union { + struct { + uint32_t errlog2_msb : 16; /* bits 0 - 16 */ + uint32_t rsv : 16; /* bits 16 - 31 */ + }; + uint32_t value; + } errlog2_high; + union { + struct { + uint32_t errlog3_lsb : 32; /* bits 0 - 31 */ + }; + uint32_t value; + } errlog3_low; + union { + struct { + uint32_t errlog3_msb : 32; /* bits 0 - 31 */ + }; + uint32_t value; + } errlog3_high; +}; + +/** + * struct cam_camnoc_irq_ubwc_enc_data : Data for UBWC Encode error. + * + * @encerr_status : Encode error status + * + */ +struct cam_camnoc_irq_ubwc_enc_data { + union { + struct { + uint32_t encerrstatus : 3; /* bits 0 - 2 */ + uint32_t rsv : 29; /* bits 3 - 31 */ + }; + uint32_t value; + } encerr_status; +}; + +/** + * struct cam_camnoc_irq_ubwc_dec_data : Data for UBWC Decode error. + * + * @decerr_status : Decoder error status + * @thr_err : Set to 1 if + * At least one of the bflc_len fields in the bit steam exceeds + * its threshold value. This error is possible only for + * RGBA1010102, TP10, and RGB565 formats + * @fcl_err : Set to 1 if + * Fast clear with a legal non-RGB format + * @len_md_err : Set to 1 if + * The calculated burst length does not match burst length + * specified by the metadata value + * @format_err : Set to 1 if + * Illegal format + * 1. bad format :2,3,6 + * 2. For 32B MAL, metadata=6 + * 3. For 32B MAL RGB565, Metadata != 0,1,7 + * 4. For 64B MAL RGB565, metadata[3:1] == 1,2 + * + */ +struct cam_camnoc_irq_ubwc_dec_data { + union { + struct { + uint32_t thr_err : 1; /* bit 0 */ + uint32_t fcl_err : 1; /* bit 1 */ + uint32_t len_md_err : 1; /* bit 2 */ + uint32_t format_err : 1; /* bit 3 */ + uint32_t rsv : 28; /* bits 4 - 31 */ + }; + uint32_t value; + } decerr_status; +}; + +struct cam_camnoc_irq_ahb_timeout_data { + uint32_t data; +}; + +/** + * struct cam_cpas_irq_data : CAMNOC IRQ data + * + * @irq_type : To identify the type of IRQ + * @u : Union of irq err data information + * @slave_err : Data for Slave error. + * Valid if type is CAM_CAMNOC_IRQ_SLAVE_ERROR + * @enc_err : Data for UBWC Encode error. + * Valid if type is one of below: + * CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR + * CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR + * CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR + * @dec_err : Data for UBWC Decode error. + * Valid if type is CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR + * @ahb_err : Data for Slave error. + * Valid if type is CAM_CAMNOC_IRQ_AHB_TIMEOUT + * + */ +struct cam_cpas_irq_data { + enum cam_camnoc_irq_type irq_type; + union { + struct cam_camnoc_irq_slave_err_data slave_err; + struct cam_camnoc_irq_ubwc_enc_data enc_err; + struct cam_camnoc_irq_ubwc_dec_data dec_err; + struct cam_camnoc_irq_ahb_timeout_data ahb_err; + } u; +}; + +/** + * struct cam_cpas_register_params : Register params for cpas client + * + * @identifier : Input identifier string which is the device label + * from dt like vfe, ife, jpeg etc + * @cell_index : Input integer identifier pointing to the cell index + * from dt of the device. This can be used to form a + * unique string with @identifier like vfe0, ife1, + * jpeg0, etc + * @dev : device handle + * @userdata : Input private data which will be passed as + * an argument while callback. + * @cam_cpas_callback : Input callback pointer for triggering the + * callbacks from CPAS driver. + * @client_handle : CPAS client handle + * @userdata : User data given at the time of register + * @event_type : event type + * @event_data : event data + * @client_handle : Output Unique handle generated for this register + * + */ +struct cam_cpas_register_params { + char identifier[CAM_HW_IDENTIFIER_LENGTH]; + uint32_t cell_index; + struct device *dev; + void *userdata; + bool (*cam_cpas_client_cb)( + uint32_t client_handle, + void *userdata, + struct cam_cpas_irq_data *irq_data); + uint32_t client_handle; +}; + +/** + * enum cam_vote_type - Enum for voting type + * + * @CAM_VOTE_ABSOLUTE : Absolute vote + * @CAM_VOTE_DYNAMIC : Dynamic vote + */ +enum cam_vote_type { + CAM_VOTE_ABSOLUTE, + CAM_VOTE_DYNAMIC, +}; + +/** + * struct cam_ahb_vote : AHB vote + * + * @type : AHB voting type. + * CAM_VOTE_ABSOLUTE : vote based on the value 'level' is set + * CAM_VOTE_DYNAMIC : vote calculated dynamically using 'freq' + * and 'dev' handle is set + * @level : AHB vote level + * @freq : AHB vote dynamic frequency + * + */ +struct cam_ahb_vote { + enum cam_vote_type type; + union { + enum cam_vote_level level; + unsigned long freq; + } vote; +}; + +/** + * struct cam_axi_vote : AXI vote + * + * @uncompressed_bw : Bus bandwidth required in Bytes for uncompressed data + * This is the required bandwidth for uncompressed + * data traffic between hw core and camnoc. + * @compressed_bw : Bus bandwidth required in Bytes for compressed data. + * This is the required bandwidth for compressed + * data traffic between camnoc and mmnoc. + * + * If one of the above is not applicable to a hw client, it has to + * fill the same values in both. + * + */ +struct cam_axi_vote { + uint64_t uncompressed_bw; + uint64_t compressed_bw; +}; + +/** + * cam_cpas_register_client() + * + * @brief: API to register cpas client + * + * @register_params: Input params to register as a client to CPAS + * + * @return 0 on success. + * + */ +int cam_cpas_register_client( + struct cam_cpas_register_params *register_params); + +/** + * cam_cpas_unregister_client() + * + * @brief: API to unregister cpas client + * + * @client_handle: Client handle to be unregistered + * + * @return 0 on success. + * + */ +int cam_cpas_unregister_client(uint32_t client_handle); + +/** + * cam_cpas_start() + * + * @brief: API to start cpas client hw. Clients have to vote for minimal + * bandwidth requirements for AHB, AXI. Use cam_cpas_update_ahb_vote + * to scale bandwidth after start. + * + * @client_handle: client cpas handle + * @ahb_vote : Pointer to ahb vote info + * @axi_vote : Pointer to axi bandwidth vote info + * + * If AXI vote is not applicable to a particular client, use the value exposed + * by CAM_CPAS_DEFAULT_AXI_BW as the default vote request. + * + * @return 0 on success. + * + */ +int cam_cpas_start( + uint32_t client_handle, + struct cam_ahb_vote *ahb_vote, + struct cam_axi_vote *axi_vote); + +/** + * cam_cpas_stop() + * + * @brief: API to stop cpas client hw. Bandwidth for AHB, AXI votes + * would be removed for this client on this call. Clients should not + * use cam_cpas_update_ahb_vote or cam_cpas_update_axi_vote + * to remove their bandwidth vote. + * + * @client_handle: client cpas handle + * + * @return 0 on success. + * + */ +int cam_cpas_stop(uint32_t client_handle); + +/** + * cam_cpas_update_ahb_vote() + * + * @brief: API to update AHB vote requirement. Use this function only + * between cam_cpas_start and cam_cpas_stop in case clients wants + * to scale to different vote level. Do not use this function to de-vote, + * removing client's vote is implicit on cam_cpas_stop + * + * @client_handle : Client cpas handle + * @ahb_vote : Pointer to ahb vote info + * + * @return 0 on success. + * + */ +int cam_cpas_update_ahb_vote( + uint32_t client_handle, + struct cam_ahb_vote *ahb_vote); + +/** + * cam_cpas_update_axi_vote() + * + * @brief: API to update AXI vote requirement. Use this function only + * between cam_cpas_start and cam_cpas_stop in case clients wants + * to scale to different vote level. Do not use this function to de-vote, + * removing client's vote is implicit on cam_cpas_stop + * + * @client_handle : Client cpas handle + * @axi_vote : Pointer to axi bandwidth vote info + * + * @return 0 on success. + * + */ +int cam_cpas_update_axi_vote( + uint32_t client_handle, + struct cam_axi_vote *axi_vote); + +/** + * cam_cpas_reg_write() + * + * @brief: API to write a register value in CPAS register space + * + * @client_handle : Client cpas handle + * @reg_base : Register base identifier + * @offset : Offset from the register base address + * @mb : Whether to do reg write with memory barrier + * @value : Value to be written in register + * + * @return 0 on success. + * + */ +int cam_cpas_reg_write( + uint32_t client_handle, + enum cam_cpas_reg_base reg_base, + uint32_t offset, + bool mb, + uint32_t value); + +/** + * cam_cpas_reg_read() + * + * @brief: API to read a register value from CPAS register space + * + * @client_handle : Client cpas handle + * @reg_base : Register base identifier + * @offset : Offset from the register base address + * @mb : Whether to do reg read with memory barrier + * @value : Value to be red from register + * + * @return 0 on success. + * + */ +int cam_cpas_reg_read( + uint32_t client_handle, + enum cam_cpas_reg_base reg_base, + uint32_t offset, + bool mb, + uint32_t *value); + +/** + * cam_cpas_get_hw_info() + * + * @brief: API to get camera hw information + * + * @camera_family : Camera family type. One of + * CAM_FAMILY_CAMERA_SS + * CAM_FAMILY_CPAS_SS + * @camera_version : Camera platform version + * @cpas_version : Camera cpas version + * @cam_caps : Camera capability + * + * @return 0 on success. + * + */ +int cam_cpas_get_hw_info( + uint32_t *camera_family, + struct cam_hw_version *camera_version, + struct cam_hw_version *cpas_version, + uint32_t *cam_caps); + +/** + * cam_cpas_get_cpas_hw_version() + * + * @brief: API to get camera cpas hw version + * + * @hw_version : Camera cpas hw version + * + * @return 0 on success. + * + */ +int cam_cpas_get_cpas_hw_version( + uint32_t *hw_version); + +#endif /* _CAM_CPAS_API_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_fd/Makefile new file mode 100644 index 000000000000..e5117b2cbe50 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += fd_hw_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_dev.o cam_fd_context.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.c new file mode 100644 index 000000000000..99c509c62809 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2017-2018, 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 +#include + +#include "cam_debug_util.h" +#include "cam_fd_context.h" +#include "cam_trace.h" + +static const char fd_dev_name[] = "fd"; + +/* Functions in Available state */ +static int __cam_fd_ctx_acquire_dev_in_available(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_acquire_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Acquire dev, rc=%d", rc); + return rc; + } + + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("FD", ctx); + + return rc; +} + +/* Functions in Acquired state */ +static int __cam_fd_ctx_release_dev_in_acquired(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_release_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Release dev, rc=%d", rc); + return rc; + } + + ctx->state = CAM_CTX_AVAILABLE; + trace_cam_context_state("FD", ctx); + + return rc; +} + +static int __cam_fd_ctx_config_dev_in_acquired(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_prepare_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Prepare dev, rc=%d", rc); + return rc; + } + + return rc; +} + +static int __cam_fd_ctx_start_dev_in_acquired(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_start_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Start dev, rc=%d", rc); + return rc; + } + + ctx->state = CAM_CTX_ACTIVATED; + trace_cam_context_state("FD", ctx); + + return rc; +} + +/* Functions in Activated state */ +static int __cam_fd_ctx_stop_dev_in_activated(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_stop_dev_to_hw(ctx); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Stop dev, rc=%d", rc); + return rc; + } + + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("FD", ctx); + + return rc; +} + +static int __cam_fd_ctx_release_dev_in_activated(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + rc = __cam_fd_ctx_stop_dev_in_activated(ctx, NULL); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Stop dev, rc=%d", rc); + return rc; + } + + rc = __cam_fd_ctx_release_dev_in_acquired(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Release dev, rc=%d", rc); + return rc; + } + + return rc; +} + +static int __cam_fd_ctx_flush_dev_in_activated(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_flush_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Failed to flush device, rc=%d", rc); + + return rc; +} +static int __cam_fd_ctx_config_dev_in_activated( + struct cam_context *ctx, struct cam_config_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_prepare_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_FD, "Failed in Prepare dev, rc=%d", rc); + return rc; + } + + return rc; +} + +static int __cam_fd_ctx_handle_irq_in_activated(void *context, + uint32_t evt_id, void *evt_data) +{ + int rc; + + rc = cam_context_buf_done_from_hw(context, evt_data, evt_id); + if (rc) { + CAM_ERR(CAM_FD, "Failed in buf done, rc=%d", rc); + return rc; + } + + return rc; +} + +/* top state machine */ +static struct cam_ctx_ops + cam_fd_ctx_state_machine[CAM_CTX_STATE_MAX] = { + /* Uninit */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Available */ + { + .ioctl_ops = { + .acquire_dev = __cam_fd_ctx_acquire_dev_in_available, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Acquired */ + { + .ioctl_ops = { + .release_dev = __cam_fd_ctx_release_dev_in_acquired, + .config_dev = __cam_fd_ctx_config_dev_in_acquired, + .start_dev = __cam_fd_ctx_start_dev_in_acquired, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Ready */ + { + .ioctl_ops = { }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Activated */ + { + .ioctl_ops = { + .stop_dev = __cam_fd_ctx_stop_dev_in_activated, + .release_dev = __cam_fd_ctx_release_dev_in_activated, + .config_dev = __cam_fd_ctx_config_dev_in_activated, + .flush_dev = __cam_fd_ctx_flush_dev_in_activated, + }, + .crm_ops = {}, + .irq_ops = __cam_fd_ctx_handle_irq_in_activated, + }, +}; + + +int cam_fd_context_init(struct cam_fd_context *fd_ctx, + struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id) +{ + int rc; + + if (!base_ctx || !fd_ctx) { + CAM_ERR(CAM_FD, "Invalid Context %pK %pK", base_ctx, fd_ctx); + return -EINVAL; + } + + memset(fd_ctx, 0, sizeof(*fd_ctx)); + + rc = cam_context_init(base_ctx, fd_dev_name, CAM_FD, ctx_id, + NULL, hw_intf, fd_ctx->req_base, CAM_CTX_REQ_MAX); + if (rc) { + CAM_ERR(CAM_FD, "Camera Context Base init failed, rc=%d", rc); + return rc; + } + + fd_ctx->base = base_ctx; + base_ctx->ctx_priv = fd_ctx; + base_ctx->state_machine = cam_fd_ctx_state_machine; + + return rc; +} + +int cam_fd_context_deinit(struct cam_fd_context *fd_ctx) +{ + int rc = 0; + + if (!fd_ctx || !fd_ctx->base) { + CAM_ERR(CAM_FD, "Invalid inputs %pK", fd_ctx); + return -EINVAL; + } + + rc = cam_context_deinit(fd_ctx->base); + if (rc) + CAM_ERR(CAM_FD, "Error in base deinit, rc=%d", rc); + + memset(fd_ctx, 0, sizeof(*fd_ctx)); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.h new file mode 100644 index 000000000000..a8b5d159f265 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_context.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2017-2018, 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 _CAM_FD_CONTEXT_H_ +#define _CAM_FD_CONTEXT_H_ + +#include "cam_context.h" +#include "cam_context_utils.h" +#include "cam_hw_mgr_intf.h" +#include "cam_req_mgr_interface.h" + +/** + * struct cam_fd_context - Face Detection context information + * + * @base : Base context pointer for this FD context + * @req_base : List of base requests for this FD context + */ +struct cam_fd_context { + struct cam_context *base; + struct cam_ctx_request req_base[CAM_CTX_REQ_MAX]; +}; + +int cam_fd_context_init(struct cam_fd_context *fd_ctx, + struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id); +int cam_fd_context_deinit(struct cam_fd_context *ctx); + +#endif /* _CAM_FD_CONTEXT_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_dev.c new file mode 100644 index 000000000000..3f0124436b9e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/cam_fd_dev.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include + +#include "cam_subdev.h" +#include "cam_node.h" +#include "cam_fd_context.h" +#include "cam_fd_hw_mgr.h" +#include "cam_fd_hw_mgr_intf.h" + +#define CAM_FD_DEV_NAME "cam-fd" + +/** + * struct cam_fd_dev - FD device information + * + * @sd: Subdev information + * @base_ctx: List of base contexts + * @fd_ctx: List of FD contexts + * @lock: Mutex handle + * @open_cnt: FD subdev open count + * @probe_done: Whether FD probe is completed + */ +struct cam_fd_dev { + struct cam_subdev sd; + struct cam_context base_ctx[CAM_CTX_MAX]; + struct cam_fd_context fd_ctx[CAM_CTX_MAX]; + struct mutex lock; + uint32_t open_cnt; + bool probe_done; +}; + +static struct cam_fd_dev g_fd_dev; + +static int cam_fd_dev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_fd_dev *fd_dev = &g_fd_dev; + + if (!fd_dev->probe_done) { + CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev); + return -ENODEV; + } + + mutex_lock(&fd_dev->lock); + fd_dev->open_cnt++; + CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt); + mutex_unlock(&fd_dev->lock); + + return 0; +} + +static int cam_fd_dev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_fd_dev *fd_dev = &g_fd_dev; + struct cam_node *node = v4l2_get_subdevdata(sd); + + if (!fd_dev->probe_done) { + CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev); + return -ENODEV; + } + + mutex_lock(&fd_dev->lock); + fd_dev->open_cnt--; + CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt); + mutex_unlock(&fd_dev->lock); + + if (!node) { + CAM_ERR(CAM_FD, "Node ptr is NULL"); + return -EINVAL; + } + + cam_node_shutdown(node); + + return 0; +} + +static const struct v4l2_subdev_internal_ops cam_fd_subdev_internal_ops = { + .open = cam_fd_dev_open, + .close = cam_fd_dev_close, +}; + +static int cam_fd_dev_probe(struct platform_device *pdev) +{ + int rc; + int i; + struct cam_hw_mgr_intf hw_mgr_intf; + struct cam_node *node; + + g_fd_dev.sd.internal_ops = &cam_fd_subdev_internal_ops; + + /* Initialze the v4l2 subdevice first. (create cam_node) */ + rc = cam_subdev_probe(&g_fd_dev.sd, pdev, CAM_FD_DEV_NAME, + CAM_FD_DEVICE_TYPE); + if (rc) { + CAM_ERR(CAM_FD, "FD cam_subdev_probe failed, rc=%d", rc); + return rc; + } + node = (struct cam_node *) g_fd_dev.sd.token; + + rc = cam_fd_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf); + if (rc) { + CAM_ERR(CAM_FD, "Failed in initializing FD HW manager, rc=%d", + rc); + goto unregister_subdev; + } + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_fd_context_init(&g_fd_dev.fd_ctx[i], + &g_fd_dev.base_ctx[i], &node->hw_mgr_intf, i); + if (rc) { + CAM_ERR(CAM_FD, "FD context init failed i=%d, rc=%d", + i, rc); + goto deinit_ctx; + } + } + + rc = cam_node_init(node, &hw_mgr_intf, g_fd_dev.base_ctx, CAM_CTX_MAX, + CAM_FD_DEV_NAME); + if (rc) { + CAM_ERR(CAM_FD, "FD node init failed, rc=%d", rc); + goto deinit_ctx; + } + + mutex_init(&g_fd_dev.lock); + g_fd_dev.probe_done = true; + + CAM_DBG(CAM_FD, "Camera FD probe complete"); + + return 0; + +deinit_ctx: + for (--i; i >= 0; i--) { + if (cam_fd_context_deinit(&g_fd_dev.fd_ctx[i])) + CAM_ERR(CAM_FD, "FD context %d deinit failed", i); + } +unregister_subdev: + if (cam_subdev_remove(&g_fd_dev.sd)) + CAM_ERR(CAM_FD, "Failed in subdev remove"); + + return rc; +} + +static int cam_fd_dev_remove(struct platform_device *pdev) +{ + int i, rc; + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_fd_context_deinit(&g_fd_dev.fd_ctx[i]); + if (rc) + CAM_ERR(CAM_FD, "FD context %d deinit failed, rc=%d", + i, rc); + } + + rc = cam_fd_hw_mgr_deinit(pdev->dev.of_node); + if (rc) + CAM_ERR(CAM_FD, "Failed in hw mgr deinit, rc=%d", rc); + + rc = cam_subdev_remove(&g_fd_dev.sd); + if (rc) + CAM_ERR(CAM_FD, "Unregister failed, rc=%d", rc); + + mutex_destroy(&g_fd_dev.lock); + g_fd_dev.probe_done = false; + + return rc; +} + +static const struct of_device_id cam_fd_dt_match[] = { + { + .compatible = "qcom,cam-fd" + }, + {} +}; + +static struct platform_driver cam_fd_driver = { + .probe = cam_fd_dev_probe, + .remove = cam_fd_dev_remove, + .driver = { + .name = "cam_fd", + .owner = THIS_MODULE, + .of_match_table = cam_fd_dt_match, + }, +}; + +static int __init cam_fd_dev_init_module(void) +{ + return platform_driver_register(&cam_fd_driver); +} + +static void __exit cam_fd_dev_exit_module(void) +{ + platform_driver_unregister(&cam_fd_driver); +} + +module_init(cam_fd_dev_init_module); +module_exit(cam_fd_dev_exit_module); +MODULE_DESCRIPTION("MSM FD driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/Makefile new file mode 100644 index 000000000000..fa486052c9d8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += fd_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_hw_mgr.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c new file mode 100644 index 000000000000..a080028edeea --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c @@ -0,0 +1,1953 @@ +/* Copyright (c) 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 +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_soc_util.h" +#include "cam_mem_mgr_api.h" +#include "cam_smmu_api.h" +#include "cam_packet_util.h" +#include "cam_fd_context.h" +#include "cam_fd_hw_intf.h" +#include "cam_fd_hw_core.h" +#include "cam_fd_hw_soc.h" +#include "cam_fd_hw_mgr_intf.h" +#include "cam_fd_hw_mgr.h" +#include "cam_trace.h" + +static struct cam_fd_hw_mgr g_fd_hw_mgr; + +static int cam_fd_mgr_util_packet_validate(struct cam_packet *packet) +{ + struct cam_cmd_buf_desc *cmd_desc = NULL; + int i, rc; + + if (!packet) + return -EINVAL; + + CAM_DBG(CAM_FD, "Packet request=%d, op_code=0x%x, size=%d, flags=%d", + packet->header.request_id, packet->header.op_code, + packet->header.size, packet->header.flags); + CAM_DBG(CAM_FD, + "Packet cmdbuf(offset=%d, num=%d) io(offset=%d, num=%d)", + packet->cmd_buf_offset, packet->num_cmd_buf, + packet->io_configs_offset, packet->num_io_configs); + CAM_DBG(CAM_FD, + "Packet Patch(offset=%d, num=%d) kmd(offset=%d, num=%d)", + packet->patch_offset, packet->num_patches, + packet->kmd_cmd_buf_offset, packet->kmd_cmd_buf_index); + + if (cam_packet_util_validate_packet(packet)) { + CAM_ERR(CAM_FD, "invalid packet:%d %d %d %d %d", + packet->kmd_cmd_buf_index, + packet->num_cmd_buf, packet->cmd_buf_offset, + packet->io_configs_offset, packet->header.size); + return -EINVAL; + } + + /* All buffers must come through io config, do not support patching */ + if (packet->num_patches || !packet->num_io_configs) { + CAM_ERR(CAM_FD, "wrong number of cmd/patch info: %u %u", + packet->num_cmd_buf, packet->num_patches); + return -EINVAL; + } + + /* KMD Buf index can never be greater than or equal to num cmd bufs */ + if (packet->kmd_cmd_buf_index >= packet->num_cmd_buf) { + CAM_ERR(CAM_FD, "Invalid kmd index %d (%d)", + packet->kmd_cmd_buf_index, packet->num_cmd_buf); + return -EINVAL; + } + + if ((packet->header.op_code & 0xff) != + CAM_PACKET_OPCODES_FD_FRAME_UPDATE) { + CAM_ERR(CAM_FD, "Invalid op_code %u", + packet->header.op_code & 0xff); + return -EINVAL; + } + + cmd_desc = (struct cam_cmd_buf_desc *) ((uint8_t *)&packet->payload + + packet->cmd_buf_offset); + + for (i = 0; i < packet->num_cmd_buf; i++) { + /* + * We can allow 0 length cmd buffer. This can happen in case + * umd gives an empty cmd buffer as kmd buffer + */ + if (!cmd_desc[i].length) + continue; + + if ((cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_GENERIC) && + (cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_CDM)) { + CAM_ERR(CAM_FD, "Invalid meta_data [%d] %u", i, + cmd_desc[i].meta_data); + return -EINVAL; + } + + CAM_DBG(CAM_FD, + "CmdBuf[%d] hdl=%d, offset=%d, size=%d, len=%d, type=%d, meta_data=%d", + i, + cmd_desc[i].mem_handle, cmd_desc[i].offset, + cmd_desc[i].size, cmd_desc[i].length, cmd_desc[i].type, + cmd_desc[i].meta_data); + + rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]); + if (rc) { + CAM_ERR(CAM_FD, "Invalid cmd buffer %d", i); + return rc; + } + } + + return 0; +} + +static int cam_fd_mgr_util_put_ctx( + struct list_head *src_list, + struct cam_fd_hw_mgr_ctx **fd_ctx) +{ + int rc = 0; + struct cam_fd_hw_mgr_ctx *ctx_ptr = NULL; + + mutex_lock(&g_fd_hw_mgr.ctx_mutex); + ctx_ptr = *fd_ctx; + if (ctx_ptr) + list_add_tail(&ctx_ptr->list, src_list); + *fd_ctx = NULL; + mutex_unlock(&g_fd_hw_mgr.ctx_mutex); + + return rc; +} + +static int cam_fd_mgr_util_get_ctx( + struct list_head *src_list, + struct cam_fd_hw_mgr_ctx **fd_ctx) +{ + int rc = 0; + struct cam_fd_hw_mgr_ctx *ctx_ptr = NULL; + + mutex_lock(&g_fd_hw_mgr.ctx_mutex); + if (!list_empty(src_list)) { + ctx_ptr = list_first_entry(src_list, + struct cam_fd_hw_mgr_ctx, list); + list_del_init(&ctx_ptr->list); + } else { + CAM_ERR(CAM_FD, "No more free fd hw mgr ctx"); + rc = -1; + } + *fd_ctx = ctx_ptr; + mutex_unlock(&g_fd_hw_mgr.ctx_mutex); + + return rc; +} + +static int cam_fd_mgr_util_put_frame_req( + struct list_head *src_list, + struct cam_fd_mgr_frame_request **frame_req) +{ + int rc = 0; + struct cam_fd_mgr_frame_request *req_ptr = NULL; + + mutex_lock(&g_fd_hw_mgr.frame_req_mutex); + req_ptr = *frame_req; + if (req_ptr) + list_add_tail(&req_ptr->list, src_list); + *frame_req = NULL; + mutex_unlock(&g_fd_hw_mgr.frame_req_mutex); + + return rc; +} + +static int cam_fd_mgr_util_get_frame_req( + struct list_head *src_list, + struct cam_fd_mgr_frame_request **frame_req) +{ + int rc = 0; + struct cam_fd_mgr_frame_request *req_ptr = NULL; + + mutex_lock(&g_fd_hw_mgr.frame_req_mutex); + if (!list_empty(src_list)) { + req_ptr = list_first_entry(src_list, + struct cam_fd_mgr_frame_request, list); + list_del_init(&req_ptr->list); + } else { + CAM_DBG(CAM_FD, "Frame req not available"); + rc = -EPERM; + } + *frame_req = req_ptr; + mutex_unlock(&g_fd_hw_mgr.frame_req_mutex); + + return rc; +} + +static int cam_fd_mgr_util_get_device(struct cam_fd_hw_mgr *hw_mgr, + struct cam_fd_hw_mgr_ctx *hw_ctx, struct cam_fd_device **hw_device) +{ + if (!hw_mgr || !hw_ctx || !hw_device) { + CAM_ERR(CAM_FD, "Invalid input %pK %pK %pK", hw_mgr, hw_ctx, + hw_device); + return -EINVAL; + } + + if ((hw_ctx->device_index < 0) || + (hw_ctx->device_index >= CAM_FD_HW_MAX)) { + CAM_ERR(CAM_FD, "Invalid device indx %d", hw_ctx->device_index); + return -EINVAL; + } + + CAM_DBG(CAM_FD, "ctx_index=%u, hw_ctx=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + *hw_device = &hw_mgr->hw_device[hw_ctx->device_index]; + + return 0; +} + +static int cam_fd_mgr_util_release_device(struct cam_fd_hw_mgr *hw_mgr, + struct cam_fd_hw_mgr_ctx *hw_ctx) +{ + struct cam_fd_device *hw_device; + struct cam_fd_hw_release_args hw_release_args; + int rc; + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + if (hw_device->hw_intf->hw_ops.release) { + hw_release_args.hw_ctx = hw_ctx; + hw_release_args.ctx_hw_private = hw_ctx->ctx_hw_private; + rc = hw_device->hw_intf->hw_ops.release( + hw_device->hw_intf->hw_priv, &hw_release_args, + sizeof(hw_release_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW release %d", rc); + return rc; + } + } else { + CAM_ERR(CAM_FD, "Invalid release function"); + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + hw_device->num_ctxts--; + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + hw_ctx->device_index = -1; + + return rc; +} + +static int cam_fd_mgr_util_select_device(struct cam_fd_hw_mgr *hw_mgr, + struct cam_fd_hw_mgr_ctx *hw_ctx, + struct cam_fd_acquire_dev_info *fd_acquire_args) +{ + int i, rc; + struct cam_fd_hw_reserve_args hw_reserve_args; + struct cam_fd_device *hw_device = NULL; + + if (!hw_mgr || !hw_ctx || !fd_acquire_args) { + CAM_ERR(CAM_FD, "Invalid input %pK %pK %pK", hw_mgr, hw_ctx, + fd_acquire_args); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + + /* Check if a device is free which can satisfy the requirements */ + for (i = 0; i < hw_mgr->num_devices; i++) { + hw_device = &hw_mgr->hw_device[i]; + CAM_DBG(CAM_FD, + "[%d] : num_ctxts=%d, modes=%d, raw_results=%d", + i, hw_device->num_ctxts, + hw_device->hw_caps.supported_modes, + hw_device->hw_caps.raw_results_available); + if ((hw_device->num_ctxts == 0) && + (fd_acquire_args->mode & + hw_device->hw_caps.supported_modes) && + (!fd_acquire_args->get_raw_results || + hw_device->hw_caps.raw_results_available)) { + CAM_DBG(CAM_FD, "Found dedicated HW Index=%d", i); + hw_device->num_ctxts++; + break; + } + } + + /* + * We couldn't find a free HW which meets requirement, now check if + * there is a HW which meets acquire requirements + */ + if (i == hw_mgr->num_devices) { + for (i = 0; i < hw_mgr->num_devices; i++) { + hw_device = &hw_mgr->hw_device[i]; + if ((fd_acquire_args->mode & + hw_device->hw_caps.supported_modes) && + (!fd_acquire_args->get_raw_results || + hw_device->hw_caps.raw_results_available)) { + hw_device->num_ctxts++; + CAM_DBG(CAM_FD, "Found sharing HW Index=%d", i); + break; + } + } + } + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + if ((i == hw_mgr->num_devices) || !hw_device) { + CAM_ERR(CAM_FD, "Couldn't acquire HW %d %d", + fd_acquire_args->mode, + fd_acquire_args->get_raw_results); + return -EBUSY; + } + + CAM_DBG(CAM_FD, "Device index %d selected for this acquire", i); + + /* Check if we can reserve this HW */ + if (hw_device->hw_intf->hw_ops.reserve) { + hw_reserve_args.hw_ctx = hw_ctx; + hw_reserve_args.mode = fd_acquire_args->mode; + rc = hw_device->hw_intf->hw_ops.reserve( + hw_device->hw_intf->hw_priv, &hw_reserve_args, + sizeof(hw_reserve_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW reserve %d", rc); + return rc; + } + hw_ctx->ctx_hw_private = hw_reserve_args.ctx_hw_private; + } else { + CAM_ERR(CAM_FD, "Invalid reserve function"); + return -EPERM; + } + + /* Update required info in hw context */ + hw_ctx->device_index = i; + + CAM_DBG(CAM_FD, "ctx index=%u, device_index=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + return 0; +} + +static int cam_fd_mgr_util_pdev_get_hw_intf(struct device_node *of_node, + int i, struct cam_hw_intf **device_hw_intf) +{ + struct device_node *device_node = NULL; + struct platform_device *child_pdev = NULL; + struct cam_hw_intf *hw_intf = NULL; + const char *name = NULL; + int rc; + + rc = of_property_read_string_index(of_node, "compat-hw-name", i, &name); + if (rc) { + CAM_ERR(CAM_FD, "Getting dev object name failed %d %d", i, rc); + goto put_node; + } + + device_node = of_find_node_by_name(NULL, name); + if (!device_node) { + CAM_ERR(CAM_FD, "Cannot find node in dtsi %s", name); + return -ENODEV; + } + + child_pdev = of_find_device_by_node(device_node); + if (!child_pdev) { + CAM_ERR(CAM_FD, "Failed to find device on bus %s", + device_node->name); + rc = -ENODEV; + goto put_node; + } + + hw_intf = (struct cam_hw_intf *)platform_get_drvdata(child_pdev); + if (!hw_intf) { + CAM_ERR(CAM_FD, "No driver data for child device"); + rc = -ENODEV; + goto put_node; + } + + CAM_DBG(CAM_FD, "child type %d index %d child_intf %pK", + hw_intf->hw_type, hw_intf->hw_idx, hw_intf); + + if (hw_intf->hw_idx >= CAM_FD_HW_MAX) { + CAM_ERR(CAM_FD, "hw_idx invalid %d", hw_intf->hw_idx); + rc = -ENODEV; + goto put_node; + } + + rc = 0; + *device_hw_intf = hw_intf; + +put_node: + of_node_put(device_node); + + return rc; +} + +static int cam_fd_packet_generic_blob_handler(void *user_data, + uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data) +{ + struct cam_fd_hw_cmd_prestart_args *prestart_args = + (struct cam_fd_hw_cmd_prestart_args *)user_data; + + if (!blob_data || (blob_size == 0)) { + CAM_ERR(CAM_FD, "Invalid blob info %pK %u", blob_data, + blob_size); + return -EINVAL; + } + + if (!prestart_args) { + CAM_ERR(CAM_FD, "Invalid user data"); + return -EINVAL; + } + + switch (blob_type) { + case CAM_FD_BLOB_TYPE_RAW_RESULTS_REQUIRED: { + uint32_t *get_raw_results = (uint32_t *)blob_data; + + if (sizeof(uint32_t) != blob_size) { + CAM_ERR(CAM_FD, "Invalid blob size %lu %u", + sizeof(uint32_t), blob_size); + return -EINVAL; + } + + prestart_args->get_raw_results = *get_raw_results; + break; + } + case CAM_FD_BLOB_TYPE_SOC_CLOCK_BW_REQUEST: { + struct cam_fd_soc_clock_bw_request *clk_req = + (struct cam_fd_soc_clock_bw_request *)blob_data; + + if (sizeof(struct cam_fd_soc_clock_bw_request) != blob_size) { + CAM_ERR(CAM_FD, "Invalid blob size %lu %u", + sizeof(struct cam_fd_soc_clock_bw_request), + blob_size); + return -EINVAL; + } + + CAM_DBG(CAM_FD, "SOC Clk Request clock=%lld, bw=%lld", + clk_req->clock_rate, clk_req->bandwidth); + + break; + } + default: + CAM_WARN(CAM_FD, "Unknown blob type %d", blob_type); + break; + } + + return 0; +} + +static int cam_fd_mgr_util_parse_generic_cmd_buffer( + struct cam_fd_hw_mgr_ctx *hw_ctx, struct cam_packet *packet, + struct cam_fd_hw_cmd_prestart_args *prestart_args) +{ + struct cam_cmd_buf_desc *cmd_desc = NULL; + int i, rc = 0; + + cmd_desc = (struct cam_cmd_buf_desc *) ((uint8_t *)&packet->payload + + packet->cmd_buf_offset); + + for (i = 0; i < packet->num_cmd_buf; i++) { + if (!cmd_desc[i].length) + continue; + + if (cmd_desc[i].meta_data == CAM_FD_CMD_BUFFER_ID_CDM) + continue; + + rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]); + if (rc) + return rc; + + rc = cam_packet_util_process_generic_cmd_buffer(&cmd_desc[i], + cam_fd_packet_generic_blob_handler, prestart_args); + if (rc) + CAM_ERR(CAM_FD, "Failed in processing blobs %d", rc); + + break; + } + + return rc; +} + +static int cam_fd_mgr_util_get_buf_map_requirement(uint32_t direction, + uint32_t resource_type, bool *need_io_map, bool *need_cpu_map) +{ + if (!need_io_map || !need_cpu_map) { + CAM_ERR(CAM_FD, "Invalid input pointers %pK %pK", need_io_map, + need_cpu_map); + return -EINVAL; + } + + if (direction == CAM_BUF_INPUT) { + switch (resource_type) { + case CAM_FD_INPUT_PORT_ID_IMAGE: + *need_io_map = true; + *need_cpu_map = false; + break; + default: + CAM_WARN(CAM_FD, "Invalid port: dir %d, id %d", + direction, resource_type); + return -EINVAL; + } + } else if (direction == CAM_BUF_OUTPUT) { + switch (resource_type) { + case CAM_FD_OUTPUT_PORT_ID_RESULTS: + *need_io_map = true; + *need_cpu_map = true; + break; + case CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS: + *need_io_map = true; + *need_cpu_map = true; + break; + case CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER: + *need_io_map = true; + *need_cpu_map = false; + break; + default: + CAM_WARN(CAM_FD, "Invalid port: dir %d, id %d", + direction, resource_type); + return -EINVAL; + } + } else { + CAM_WARN(CAM_FD, "Invalid direction %d", direction); + return -EINVAL; + } + + return 0; +} + +static int cam_fd_mgr_util_prepare_io_buf_info(int32_t iommu_hdl, + struct cam_hw_prepare_update_args *prepare, + struct cam_fd_hw_io_buffer *input_buf, + struct cam_fd_hw_io_buffer *output_buf, uint32_t io_buf_size) +{ + int rc = -EINVAL; + uint32_t plane, num_out_buf, num_in_buf; + int i, j; + struct cam_buf_io_cfg *io_cfg; + dma_addr_t io_addr[CAM_PACKET_MAX_PLANES]; + uintptr_t cpu_addr[CAM_PACKET_MAX_PLANES]; + size_t size; + bool need_io_map, need_cpu_map; + + /* Get IO Buf information */ + num_out_buf = 0; + num_in_buf = 0; + io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *) + &prepare->packet->payload + prepare->packet->io_configs_offset); + + for (i = 0; i < prepare->packet->num_io_configs; i++) { + CAM_DBG(CAM_FD, + "IOConfig[%d] : handle[%d] Dir[%d] Res[%d] Fence[%d], Format[%d]", + i, io_cfg[i].mem_handle[0], io_cfg[i].direction, + io_cfg[i].resource_type, + io_cfg[i].fence, io_cfg[i].format); + + if ((num_in_buf >= io_buf_size) || + (num_out_buf >= io_buf_size)) { + CAM_ERR(CAM_FD, "Invalid number of buffers %d %d %d", + num_in_buf, num_out_buf, io_buf_size); + return -EINVAL; + } + + rc = cam_fd_mgr_util_get_buf_map_requirement( + io_cfg[i].direction, io_cfg[i].resource_type, + &need_io_map, &need_cpu_map); + if (rc) { + CAM_WARN(CAM_FD, "Invalid io buff [%d] : %d %d %d", + i, io_cfg[i].direction, + io_cfg[i].resource_type, rc); + continue; + } + + memset(io_addr, 0x0, sizeof(io_addr)); + for (plane = 0; plane < CAM_PACKET_MAX_PLANES; plane++) { + if (!io_cfg[i].mem_handle[plane]) + break; + + io_addr[plane] = 0x0; + cpu_addr[plane] = 0x0; + + if (need_io_map) { + rc = cam_mem_get_io_buf( + io_cfg[i].mem_handle[plane], + iommu_hdl, &io_addr[plane], &size); + if (rc) { + CAM_ERR(CAM_FD, + "Failed to get io buf %u %u %u %d", + io_cfg[i].direction, + io_cfg[i].resource_type, plane, + rc); + return -ENOMEM; + } + + if (io_cfg[i].offsets[plane] >= size) { + CAM_ERR(CAM_FD, + "Invalid io buf %u %u %u %d %u %zu", + io_cfg[i].direction, + io_cfg[i].resource_type, plane, + i, io_cfg[i].offsets[plane], + size); + return -EINVAL; + } + + io_addr[plane] += io_cfg[i].offsets[plane]; + } + + if (need_cpu_map) { + rc = cam_mem_get_cpu_buf( + io_cfg[i].mem_handle[plane], + &cpu_addr[plane], &size); + if (rc || ((io_addr[plane] & 0xFFFFFFFF) + != io_addr[plane])) { + CAM_ERR(CAM_FD, + "Invalid cpu buf %d %d %d %d", + io_cfg[i].direction, + io_cfg[i].resource_type, plane, + rc); + return rc; + } + if (io_cfg[i].offsets[plane] >= size) { + CAM_ERR(CAM_FD, + "Invalid cpu buf %d %d %d", + io_cfg[i].direction, + io_cfg[i].resource_type, plane); + rc = -EINVAL; + return rc; + } + cpu_addr[plane] += io_cfg[i].offsets[plane]; + } + + CAM_DBG(CAM_FD, "IO Address[%d][%d] : %pK, %pK", + io_cfg[i].direction, plane, io_addr[plane], + cpu_addr[plane]); + } + + switch (io_cfg[i].direction) { + case CAM_BUF_INPUT: { + prepare->in_map_entries[num_in_buf].resource_handle = + io_cfg[i].resource_type; + prepare->in_map_entries[num_in_buf].sync_id = + io_cfg[i].fence; + + input_buf[num_in_buf].valid = true; + for (j = 0; j < plane; j++) { + input_buf[num_in_buf].io_addr[j] = io_addr[j]; + input_buf[num_in_buf].cpu_addr[j] = cpu_addr[j]; + } + input_buf[num_in_buf].num_buf = plane; + input_buf[num_in_buf].io_cfg = &io_cfg[i]; + + num_in_buf++; + break; + } + case CAM_BUF_OUTPUT: { + prepare->out_map_entries[num_out_buf].resource_handle = + io_cfg[i].resource_type; + prepare->out_map_entries[num_out_buf].sync_id = + io_cfg[i].fence; + + output_buf[num_out_buf].valid = true; + for (j = 0; j < plane; j++) { + output_buf[num_out_buf].io_addr[j] = io_addr[j]; + output_buf[num_out_buf].cpu_addr[j] = + cpu_addr[j]; + } + output_buf[num_out_buf].num_buf = plane; + output_buf[num_out_buf].io_cfg = &io_cfg[i]; + + num_out_buf++; + break; + } + default: + CAM_ERR(CAM_FD, "Unsupported io direction %d", + io_cfg[i].direction); + rc = -EINVAL; + break; + } + } + + prepare->num_in_map_entries = num_in_buf; + prepare->num_out_map_entries = num_out_buf; + return rc; +} + +static int cam_fd_mgr_util_prepare_hw_update_entries( + struct cam_fd_hw_mgr *hw_mgr, + struct cam_hw_prepare_update_args *prepare, + struct cam_fd_hw_cmd_prestart_args *prestart_args, + struct cam_kmd_buf_info *kmd_buf_info) +{ + int i, rc; + struct cam_hw_update_entry *hw_entry; + uint32_t num_ent; + struct cam_fd_hw_mgr_ctx *hw_ctx = + (struct cam_fd_hw_mgr_ctx *)prepare->ctxt_to_hw_map; + struct cam_fd_device *hw_device; + uint32_t kmd_buf_max_size, kmd_buf_used_bytes = 0; + uint32_t *kmd_buf_addr; + struct cam_cmd_buf_desc *cmd_desc = NULL; + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + kmd_buf_addr = (uint32_t *)((uint8_t *)kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes); + kmd_buf_max_size = kmd_buf_info->size - kmd_buf_info->used_bytes; + + prestart_args->cmd_buf_addr = kmd_buf_addr; + prestart_args->size = kmd_buf_max_size; + prestart_args->pre_config_buf_size = 0; + prestart_args->post_config_buf_size = 0; + + if (hw_device->hw_intf->hw_ops.process_cmd) { + rc = hw_device->hw_intf->hw_ops.process_cmd( + hw_device->hw_intf->hw_priv, CAM_FD_HW_CMD_PRESTART, + prestart_args, + sizeof(struct cam_fd_hw_cmd_prestart_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in CMD_PRESTART %d", rc); + return rc; + } + } + + kmd_buf_used_bytes += prestart_args->pre_config_buf_size; + kmd_buf_used_bytes += prestart_args->post_config_buf_size; + + /* HW layer is expected to add commands */ + if (!kmd_buf_used_bytes || (kmd_buf_used_bytes > kmd_buf_max_size)) { + CAM_ERR(CAM_FD, "Invalid kmd used bytes %d (%d)", + kmd_buf_used_bytes, kmd_buf_max_size); + return -ENOMEM; + } + + hw_entry = prepare->hw_update_entries; + num_ent = 0; + + if (prestart_args->pre_config_buf_size) { + if ((num_ent + 1) >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d", + num_ent, prepare->max_hw_update_entries); + return -EINVAL; + } + + hw_entry[num_ent].handle = kmd_buf_info->handle; + hw_entry[num_ent].len = prestart_args->pre_config_buf_size; + hw_entry[num_ent].offset = kmd_buf_info->offset; + + kmd_buf_info->used_bytes += prestart_args->pre_config_buf_size; + kmd_buf_info->offset += prestart_args->pre_config_buf_size; + num_ent++; + } + + /* + * set the cmd_desc to point the first command descriptor in the + * packet and update hw entries with CDM command buffers + */ + cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *) + &prepare->packet->payload + prepare->packet->cmd_buf_offset); + + for (i = 0; i < prepare->packet->num_cmd_buf; i++) { + if (!cmd_desc[i].length) + continue; + + if (cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_CDM) + continue; + + if (num_ent + 1 >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d", + num_ent, prepare->max_hw_update_entries); + return -EINVAL; + } + + hw_entry[num_ent].handle = cmd_desc[i].mem_handle; + hw_entry[num_ent].len = cmd_desc[i].length; + hw_entry[num_ent].offset = cmd_desc[i].offset; + num_ent++; + } + + if (prestart_args->post_config_buf_size) { + if (num_ent + 1 >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d", + num_ent, prepare->max_hw_update_entries); + return -EINVAL; + } + + hw_entry[num_ent].handle = kmd_buf_info->handle; + hw_entry[num_ent].len = prestart_args->post_config_buf_size; + hw_entry[num_ent].offset = kmd_buf_info->offset; + + kmd_buf_info->used_bytes += prestart_args->post_config_buf_size; + kmd_buf_info->offset += prestart_args->post_config_buf_size; + + num_ent++; + } + + prepare->num_hw_update_entries = num_ent; + + CAM_DBG(CAM_FD, "FinalConfig : hw_entries=%d, Sync(in=%d, out=%d)", + prepare->num_hw_update_entries, prepare->num_in_map_entries, + prepare->num_out_map_entries); + + return rc; +} + +static int cam_fd_mgr_util_submit_frame(void *priv, void *data) +{ + struct cam_fd_device *hw_device; + struct cam_fd_hw_mgr *hw_mgr; + struct cam_fd_mgr_frame_request *frame_req; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_hw_cmd_start_args start_args; + int rc; + + if (!priv) { + CAM_ERR(CAM_FD, "Invalid data"); + return -EINVAL; + } + + hw_mgr = (struct cam_fd_hw_mgr *)priv; + mutex_lock(&hw_mgr->frame_req_mutex); + + /* Check if we have any frames pending in high priority list */ + if (!list_empty(&hw_mgr->frame_pending_list_high)) { + CAM_DBG(CAM_FD, "Pending frames in high priority list"); + frame_req = list_first_entry(&hw_mgr->frame_pending_list_high, + struct cam_fd_mgr_frame_request, list); + } else if (!list_empty(&hw_mgr->frame_pending_list_normal)) { + CAM_DBG(CAM_FD, "Pending frames in normal priority list"); + frame_req = list_first_entry(&hw_mgr->frame_pending_list_normal, + struct cam_fd_mgr_frame_request, list); + } else { + mutex_unlock(&hw_mgr->frame_req_mutex); + CAM_DBG(CAM_FD, "No pending frames"); + return 0; + } + + CAM_DBG(CAM_FD, "FrameSubmit : Frame[%lld]", frame_req->request_id); + hw_ctx = frame_req->hw_ctx; + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + mutex_unlock(&hw_mgr->frame_req_mutex); + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + mutex_lock(&hw_device->lock); + if (hw_device->ready_to_process == false) { + mutex_unlock(&hw_device->lock); + mutex_unlock(&hw_mgr->frame_req_mutex); + return -EBUSY; + } + + trace_cam_submit_to_hw("FD", frame_req->request_id); + + list_del_init(&frame_req->list); + mutex_unlock(&hw_mgr->frame_req_mutex); + + if (hw_device->hw_intf->hw_ops.start) { + start_args.hw_ctx = hw_ctx; + start_args.ctx_hw_private = hw_ctx->ctx_hw_private; + start_args.hw_req_private = &frame_req->hw_req_private; + start_args.hw_update_entries = frame_req->hw_update_entries; + start_args.num_hw_update_entries = + frame_req->num_hw_update_entries; + + rc = hw_device->hw_intf->hw_ops.start( + hw_device->hw_intf->hw_priv, &start_args, + sizeof(start_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW Start %d", rc); + mutex_unlock(&hw_device->lock); + goto put_req_into_free_list; + } + } else { + CAM_ERR(CAM_FD, "Invalid hw_ops.start"); + mutex_unlock(&hw_device->lock); + rc = -EPERM; + goto put_req_into_free_list; + } + + hw_device->ready_to_process = false; + hw_device->cur_hw_ctx = hw_ctx; + hw_device->req_id = frame_req->request_id; + mutex_unlock(&hw_device->lock); + + rc = cam_fd_mgr_util_put_frame_req( + &hw_mgr->frame_processing_list, &frame_req); + if (rc) { + CAM_ERR(CAM_FD, + "Failed in putting frame req in processing list"); + goto stop_unlock; + } + + return rc; + +stop_unlock: + if (hw_device->hw_intf->hw_ops.stop) { + struct cam_fd_hw_stop_args stop_args; + + stop_args.hw_ctx = hw_ctx; + stop_args.ctx_hw_private = hw_ctx->ctx_hw_private; + stop_args.hw_req_private = &frame_req->hw_req_private; + if (hw_device->hw_intf->hw_ops.stop( + hw_device->hw_intf->hw_priv, &stop_args, + sizeof(stop_args))) + CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc); + } +put_req_into_free_list: + cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, &frame_req); + + return rc; +} + +static int cam_fd_mgr_util_schedule_frame_worker_task( + struct cam_fd_hw_mgr *hw_mgr) +{ + int32_t rc = 0; + struct crm_workq_task *task; + struct cam_fd_mgr_work_data *work_data; + + task = cam_req_mgr_workq_get_task(hw_mgr->work); + if (!task) { + CAM_ERR(CAM_FD, "no empty task available"); + return -ENOMEM; + } + + work_data = (struct cam_fd_mgr_work_data *)task->payload; + work_data->type = CAM_FD_WORK_FRAME; + + task->process_cb = cam_fd_mgr_util_submit_frame; + rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0); + + return rc; +} + +static int32_t cam_fd_mgr_workq_irq_cb(void *priv, void *data) +{ + struct cam_fd_device *hw_device = NULL; + struct cam_fd_hw_mgr *hw_mgr; + struct cam_fd_mgr_work_data *work_data; + struct cam_fd_mgr_frame_request *frame_req = NULL; + enum cam_fd_hw_irq_type irq_type; + bool frame_abort = true; + int rc; + + if (!data || !priv) { + CAM_ERR(CAM_FD, "Invalid data %pK %pK", data, priv); + return -EINVAL; + } + + hw_mgr = (struct cam_fd_hw_mgr *)priv; + work_data = (struct cam_fd_mgr_work_data *)data; + irq_type = work_data->irq_type; + + CAM_DBG(CAM_FD, "FD IRQ type=%d", irq_type); + + if (irq_type == CAM_FD_IRQ_HALT_DONE) { + /* HALT would be followed by a RESET, ignore this */ + CAM_DBG(CAM_FD, "HALT IRQ callback"); + return 0; + } + + /* Get the frame from processing list */ + rc = cam_fd_mgr_util_get_frame_req(&hw_mgr->frame_processing_list, + &frame_req); + if (rc || !frame_req) { + /* + * This can happen if reset is triggered while no frames + * were pending, so not an error, just continue to check if + * there are any pending frames and submit + */ + CAM_DBG(CAM_FD, "No Frame in processing list, rc=%d", rc); + goto submit_next_frame; + } + + if (!frame_req->hw_ctx) { + CAM_ERR(CAM_FD, "Invalid Frame request %lld", + frame_req->request_id); + goto put_req_in_free_list; + } + + rc = cam_fd_mgr_util_get_device(hw_mgr, frame_req->hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + goto put_req_in_free_list; + } + + /* Read frame results first */ + if (irq_type == CAM_FD_IRQ_FRAME_DONE) { + struct cam_fd_hw_frame_done_args frame_done_args; + + CAM_DBG(CAM_FD, "FrameDone : Frame[%lld]", + frame_req->request_id); + + frame_done_args.hw_ctx = frame_req->hw_ctx; + frame_done_args.ctx_hw_private = + frame_req->hw_ctx->ctx_hw_private; + frame_done_args.request_id = frame_req->request_id; + frame_done_args.hw_req_private = &frame_req->hw_req_private; + + if (hw_device->hw_intf->hw_ops.process_cmd) { + rc = hw_device->hw_intf->hw_ops.process_cmd( + hw_device->hw_intf->hw_priv, + CAM_FD_HW_CMD_FRAME_DONE, + &frame_done_args, sizeof(frame_done_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in CMD_PRESTART %d", + rc); + frame_abort = true; + goto notify_context; + } + } + + frame_abort = false; + } + + trace_cam_irq_handled("FD", irq_type); + +notify_context: + /* Do a callback to inform frame done or stop done */ + if (frame_req->hw_ctx->event_cb) { + struct cam_hw_done_event_data buf_data; + + CAM_DBG(CAM_FD, "FrameHALT : Frame[%lld]", + frame_req->request_id); + + buf_data.num_handles = frame_req->num_hw_update_entries; + buf_data.request_id = frame_req->request_id; + + rc = frame_req->hw_ctx->event_cb(frame_req->hw_ctx->cb_priv, + frame_abort, &buf_data); + if (rc) + CAM_ERR(CAM_FD, "Error in event cb handling %d", rc); + } + + /* + * Now we can set hw device is free to process further frames. + * Note - Do not change state to IDLE until we read the frame results, + * Otherwise, other thread may schedule frame processing before + * reading current frame's results. Also, we need to set to IDLE state + * in case some error happens after getting this irq callback + */ + mutex_lock(&hw_device->lock); + hw_device->ready_to_process = true; + hw_device->req_id = -1; + hw_device->cur_hw_ctx = NULL; + CAM_DBG(CAM_FD, "ready_to_process=%d", hw_device->ready_to_process); + mutex_unlock(&hw_device->lock); + +put_req_in_free_list: + rc = cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &frame_req); + if (rc) { + CAM_ERR(CAM_FD, "Failed in putting frame req in free list"); + /* continue */ + } + +submit_next_frame: + /* Check if there are any frames pending for processing and submit */ + rc = cam_fd_mgr_util_submit_frame(hw_mgr, NULL); + if (rc) { + CAM_ERR(CAM_FD, "Error while submit frame, rc=%d", rc); + return rc; + } + + return rc; +} + +static int cam_fd_mgr_irq_cb(void *data, enum cam_fd_hw_irq_type irq_type) +{ + struct cam_fd_hw_mgr *hw_mgr = &g_fd_hw_mgr; + int rc = 0; + unsigned long flags; + struct crm_workq_task *task; + struct cam_fd_mgr_work_data *work_data; + + spin_lock_irqsave(&hw_mgr->hw_mgr_slock, flags); + task = cam_req_mgr_workq_get_task(hw_mgr->work); + if (!task) { + CAM_ERR(CAM_FD, "no empty task available"); + spin_unlock_irqrestore(&hw_mgr->hw_mgr_slock, flags); + return -ENOMEM; + } + + work_data = (struct cam_fd_mgr_work_data *)task->payload; + work_data->type = CAM_FD_WORK_IRQ; + work_data->irq_type = irq_type; + + task->process_cb = cam_fd_mgr_workq_irq_cb; + rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0); + if (rc) + CAM_ERR(CAM_FD, "Failed in enqueue work task, rc=%d", rc); + + spin_unlock_irqrestore(&hw_mgr->hw_mgr_slock, flags); + + return rc; +} + +static int cam_fd_mgr_hw_get_caps(void *hw_mgr_priv, void *hw_get_caps_args) +{ + int rc = 0; + struct cam_fd_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_query_cap_cmd *query = hw_get_caps_args; + struct cam_fd_query_cap_cmd query_fd; + void __user *caps_handle = + u64_to_user_ptr(query->caps_handle); + + if (copy_from_user(&query_fd, caps_handle, + sizeof(struct cam_fd_query_cap_cmd))) { + CAM_ERR(CAM_FD, "Failed in copy from user, rc=%d", rc); + return -EFAULT; + } + + query_fd = hw_mgr->fd_caps; + + CAM_DBG(CAM_FD, + "IOMMU device(%d, %d), CDM(%d, %d), versions %d.%d, %d.%d", + query_fd.device_iommu.secure, query_fd.device_iommu.non_secure, + query_fd.cdm_iommu.secure, query_fd.cdm_iommu.non_secure, + query_fd.hw_caps.core_version.major, + query_fd.hw_caps.core_version.minor, + query_fd.hw_caps.wrapper_version.major, + query_fd.hw_caps.wrapper_version.minor); + + if (copy_to_user(caps_handle, &query_fd, + sizeof(struct cam_fd_query_cap_cmd))) + rc = -EFAULT; + + return rc; +} + +static int cam_fd_mgr_hw_acquire(void *hw_mgr_priv, void *hw_acquire_args) +{ + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_acquire_args *acquire_args = + (struct cam_hw_acquire_args *)hw_acquire_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_acquire_dev_info fd_acquire_args; + int rc; + + if (!acquire_args || acquire_args->num_acq <= 0) { + CAM_ERR(CAM_FD, "Invalid acquire args %pK", acquire_args); + return -EINVAL; + } + + if (copy_from_user(&fd_acquire_args, + (void __user *)acquire_args->acquire_info, + sizeof(struct cam_fd_acquire_dev_info))) { + CAM_ERR(CAM_FD, "Copy from user failed"); + return -EFAULT; + } + + CAM_DBG(CAM_FD, "Acquire : mode=%d, get_raw_results=%d, priority=%d", + fd_acquire_args.mode, fd_acquire_args.get_raw_results, + fd_acquire_args.priority); + + /* get a free fd hw mgr ctx */ + rc = cam_fd_mgr_util_get_ctx(&hw_mgr->free_ctx_list, &hw_ctx); + if (rc || !hw_ctx) { + CAM_ERR(CAM_FD, "Get hw context failed, rc=%d, hw_ctx=%pK", + rc, hw_ctx); + return -EINVAL; + } + + if (fd_acquire_args.get_raw_results && !hw_mgr->raw_results_available) { + CAM_ERR(CAM_FD, "HW cannot support raw results %d (%d)", + fd_acquire_args.get_raw_results, + hw_mgr->raw_results_available); + goto put_ctx; + } + + if (!(fd_acquire_args.mode & hw_mgr->supported_modes)) { + CAM_ERR(CAM_FD, "HW cannot support requested mode 0x%x (0x%x)", + fd_acquire_args.mode, hw_mgr->supported_modes); + rc = -EPERM; + goto put_ctx; + } + + rc = cam_fd_mgr_util_select_device(hw_mgr, hw_ctx, &fd_acquire_args); + if (rc) { + CAM_ERR(CAM_FD, "Failed in selecting device, rc=%d", rc); + goto put_ctx; + } + + hw_ctx->ctx_in_use = true; + hw_ctx->hw_mgr = hw_mgr; + hw_ctx->get_raw_results = fd_acquire_args.get_raw_results; + hw_ctx->mode = fd_acquire_args.mode; + + /* Save incoming cam core info into hw ctx*/ + hw_ctx->cb_priv = acquire_args->context_data; + hw_ctx->event_cb = acquire_args->event_cb; + + /* Update out args */ + acquire_args->ctxt_to_hw_map = hw_ctx; + + cam_fd_mgr_util_put_ctx(&hw_mgr->used_ctx_list, &hw_ctx); + + return 0; +put_ctx: + list_del_init(&hw_ctx->list); + cam_fd_mgr_util_put_ctx(&hw_mgr->free_ctx_list, &hw_ctx); + return rc; +} + +static int cam_fd_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args) +{ + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_release_args *release_args = hw_release_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + int rc; + + if (!hw_mgr_priv || !hw_release_args) { + CAM_ERR(CAM_FD, "Invalid arguments %pK, %pK", + hw_mgr_priv, hw_release_args); + return -EINVAL; + } + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)release_args->ctxt_to_hw_map; + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + + rc = cam_fd_mgr_util_release_device(hw_mgr, hw_ctx); + if (rc) + CAM_ERR(CAM_FD, "Failed in release device, rc=%d", rc); + + hw_ctx->ctx_in_use = false; + list_del_init(&hw_ctx->list); + cam_fd_mgr_util_put_ctx(&hw_mgr->free_ctx_list, &hw_ctx); + + return 0; +} + +static int cam_fd_mgr_hw_start(void *hw_mgr_priv, void *mgr_start_args) +{ + int rc = 0; + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_start_args *hw_mgr_start_args = + (struct cam_hw_start_args *)mgr_start_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_device *hw_device; + struct cam_fd_hw_init_args hw_init_args; + + if (!hw_mgr_priv || !hw_mgr_start_args) { + CAM_ERR(CAM_FD, "Invalid arguments %pK %pK", + hw_mgr_priv, hw_mgr_start_args); + return -EINVAL; + } + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)hw_mgr_start_args->ctxt_to_hw_map; + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + + CAM_DBG(CAM_FD, "ctx index=%u, device_index=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + if (hw_device->hw_intf->hw_ops.init) { + hw_init_args.hw_ctx = hw_ctx; + hw_init_args.ctx_hw_private = hw_ctx->ctx_hw_private; + rc = hw_device->hw_intf->hw_ops.init( + hw_device->hw_intf->hw_priv, &hw_init_args, + sizeof(hw_init_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW Init %d", rc); + return rc; + } + } else { + CAM_ERR(CAM_FD, "Invalid init function"); + return -EINVAL; + } + + return rc; +} + +static int cam_fd_mgr_hw_flush_req(void *hw_mgr_priv, + struct cam_hw_flush_args *flush_args) +{ + int rc = 0; + struct cam_fd_mgr_frame_request *frame_req, *req_temp, *flush_req; + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_fd_device *hw_device; + struct cam_fd_hw_stop_args hw_stop_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + uint32_t i = 0; + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)flush_args->ctxt_to_hw_map; + + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + CAM_DBG(CAM_FD, "ctx index=%u, hw_ctx=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + mutex_lock(&hw_mgr->frame_req_mutex); + for (i = 0; i < flush_args->num_req_active; i++) { + flush_req = (struct cam_fd_mgr_frame_request *) + flush_args->flush_req_active[i]; + + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_pending_list_high, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + if (frame_req->request_id != flush_req->request_id) + continue; + + list_del_init(&frame_req->list); + break; + } + + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_pending_list_normal, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + if (frame_req->request_id != flush_req->request_id) + continue; + + list_del_init(&frame_req->list); + break; + } + + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_processing_list, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + if (frame_req->request_id != flush_req->request_id) + continue; + + list_del_init(&frame_req->list); + + mutex_lock(&hw_device->lock); + if ((hw_device->ready_to_process == true) || + (hw_device->cur_hw_ctx != hw_ctx)) + goto unlock_dev_flush_req; + + if (hw_device->hw_intf->hw_ops.stop) { + hw_stop_args.hw_ctx = hw_ctx; + rc = hw_device->hw_intf->hw_ops.stop( + hw_device->hw_intf->hw_priv, + &hw_stop_args, + sizeof(hw_stop_args)); + if (rc) { + CAM_ERR(CAM_FD, + "Failed in HW Stop %d", rc); + goto unlock_dev_flush_req; + } + hw_device->ready_to_process = true; + } + +unlock_dev_flush_req: + mutex_unlock(&hw_device->lock); + break; + } + } + mutex_unlock(&hw_mgr->frame_req_mutex); + + for (i = 0; i < flush_args->num_req_pending; i++) { + flush_req = (struct cam_fd_mgr_frame_request *) + flush_args->flush_req_pending[i]; + cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &flush_req); + } + + return rc; +} + +static int cam_fd_mgr_hw_flush_ctx(void *hw_mgr_priv, + struct cam_hw_flush_args *flush_args) +{ + int rc = 0; + struct cam_fd_mgr_frame_request *frame_req, *req_temp, *flush_req; + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_fd_device *hw_device; + struct cam_fd_hw_stop_args hw_stop_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + uint32_t i = 0; + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)flush_args->ctxt_to_hw_map; + + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + CAM_DBG(CAM_FD, "ctx index=%u, hw_ctx=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + mutex_lock(&hw_mgr->frame_req_mutex); + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_pending_list_high, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + list_del_init(&frame_req->list); + } + + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_pending_list_normal, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + list_del_init(&frame_req->list); + } + + list_for_each_entry_safe(frame_req, req_temp, + &hw_mgr->frame_processing_list, list) { + if (frame_req->hw_ctx != hw_ctx) + continue; + + list_del_init(&frame_req->list); + mutex_lock(&hw_device->lock); + if ((hw_device->ready_to_process == true) || + (hw_device->cur_hw_ctx != hw_ctx)) + goto unlock_dev_flush_ctx; + + if (hw_device->hw_intf->hw_ops.stop) { + hw_stop_args.hw_ctx = hw_ctx; + rc = hw_device->hw_intf->hw_ops.stop( + hw_device->hw_intf->hw_priv, &hw_stop_args, + sizeof(hw_stop_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc); + goto unlock_dev_flush_ctx; + } + hw_device->ready_to_process = true; + } + +unlock_dev_flush_ctx: + mutex_unlock(&hw_device->lock); + } + mutex_unlock(&hw_mgr->frame_req_mutex); + + for (i = 0; i < flush_args->num_req_pending; i++) { + flush_req = (struct cam_fd_mgr_frame_request *) + flush_args->flush_req_pending[i]; + cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &flush_req); + } + + return rc; +} + +static int cam_fd_mgr_hw_flush(void *hw_mgr_priv, + void *hw_flush_args) +{ + int rc = 0; + struct cam_hw_flush_args *flush_args = + (struct cam_hw_flush_args *)hw_flush_args; + + if (!hw_mgr_priv || !hw_flush_args) { + CAM_ERR(CAM_FD, "Invalid arguments %pK %pK", + hw_mgr_priv, hw_flush_args); + return -EINVAL; + } + + switch (flush_args->flush_type) { + case CAM_FLUSH_TYPE_REQ: + rc = cam_fd_mgr_hw_flush_req(hw_mgr_priv, flush_args); + break; + case CAM_FLUSH_TYPE_ALL: + rc = cam_fd_mgr_hw_flush_ctx(hw_mgr_priv, flush_args); + break; + default: + rc = -EINVAL; + CAM_ERR(CAM_FD, "Invalid flush type %d", + flush_args->flush_type); + break; + } + return rc; +} + +static int cam_fd_mgr_hw_stop(void *hw_mgr_priv, void *mgr_stop_args) +{ + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_stop_args *hw_mgr_stop_args = + (struct cam_hw_stop_args *)mgr_stop_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_device *hw_device; + struct cam_fd_hw_deinit_args hw_deinit_args; + int rc = 0; + + if (!hw_mgr_priv || !hw_mgr_stop_args) { + CAM_ERR(CAM_FD, "Invalid arguments %pK %pK", + hw_mgr_priv, hw_mgr_stop_args); + return -EINVAL; + } + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)hw_mgr_stop_args->ctxt_to_hw_map; + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + CAM_DBG(CAM_FD, "ctx index=%u, hw_ctx=%d", hw_ctx->ctx_index, + hw_ctx->device_index); + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + return rc; + } + + CAM_DBG(CAM_FD, "FD Device ready_to_process = %d", + hw_device->ready_to_process); + + if (hw_device->hw_intf->hw_ops.deinit) { + hw_deinit_args.hw_ctx = hw_ctx; + hw_deinit_args.ctx_hw_private = hw_ctx->ctx_hw_private; + rc = hw_device->hw_intf->hw_ops.deinit( + hw_device->hw_intf->hw_priv, &hw_deinit_args, + sizeof(hw_deinit_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HW DeInit %d", rc); + return rc; + } + } + + return rc; +} + +static int cam_fd_mgr_hw_prepare_update(void *hw_mgr_priv, + void *hw_prepare_update_args) +{ + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_prepare_update_args *prepare = + (struct cam_hw_prepare_update_args *) hw_prepare_update_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_device *hw_device; + struct cam_kmd_buf_info kmd_buf; + int rc; + struct cam_fd_hw_cmd_prestart_args prestart_args; + struct cam_fd_mgr_frame_request *frame_req; + + if (!hw_mgr_priv || !hw_prepare_update_args) { + CAM_ERR(CAM_FD, "Invalid args %pK %pK", + hw_mgr_priv, hw_prepare_update_args); + return -EINVAL; + } + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)prepare->ctxt_to_hw_map; + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + + rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device); + if (rc) { + CAM_ERR(CAM_FD, "Error in getting device %d", rc); + goto error; + } + + rc = cam_fd_mgr_util_packet_validate(prepare->packet); + if (rc) { + CAM_ERR(CAM_FD, "Error in packet validation %d", rc); + goto error; + } + + rc = cam_packet_util_get_kmd_buffer(prepare->packet, &kmd_buf); + if (rc) { + CAM_ERR(CAM_FD, "Error in get kmd buf buffer %d", rc); + goto error; + } + + CAM_DBG(CAM_FD, + "KMD Buf : hdl=%d, cpu_addr=%pK, offset=%d, size=%d, used=%d", + kmd_buf.handle, kmd_buf.cpu_addr, kmd_buf.offset, + kmd_buf.size, kmd_buf.used_bytes); + + /* We do not expect any patching, but just do it anyway */ + rc = cam_packet_util_process_patches(prepare->packet, + hw_mgr->device_iommu.non_secure, -1); + if (rc) { + CAM_ERR(CAM_FD, "Patch FD packet failed, rc=%d", rc); + return rc; + } + + memset(&prestart_args, 0x0, sizeof(prestart_args)); + prestart_args.ctx_hw_private = hw_ctx->ctx_hw_private; + prestart_args.hw_ctx = hw_ctx; + prestart_args.request_id = prepare->packet->header.request_id; + + rc = cam_fd_mgr_util_parse_generic_cmd_buffer(hw_ctx, prepare->packet, + &prestart_args); + if (rc) { + CAM_ERR(CAM_FD, "Error in parsing gerneric cmd buffer %d", rc); + goto error; + } + + rc = cam_fd_mgr_util_prepare_io_buf_info( + hw_mgr->device_iommu.non_secure, prepare, + prestart_args.input_buf, prestart_args.output_buf, + CAM_FD_MAX_IO_BUFFERS); + if (rc) { + CAM_ERR(CAM_FD, "Error in prepare IO Buf %d", rc); + goto error; + } + + rc = cam_fd_mgr_util_prepare_hw_update_entries(hw_mgr, prepare, + &prestart_args, &kmd_buf); + if (rc) { + CAM_ERR(CAM_FD, "Error in hw update entries %d", rc); + goto error; + } + + /* get a free frame req from free list */ + rc = cam_fd_mgr_util_get_frame_req(&hw_mgr->frame_free_list, + &frame_req); + if (rc || !frame_req) { + CAM_ERR(CAM_FD, "Get frame_req failed, rc=%d, hw_ctx=%pK", + rc, hw_ctx); + return -ENOMEM; + } + + /* Setup frame request info and queue to pending list */ + frame_req->hw_ctx = hw_ctx; + frame_req->request_id = prepare->packet->header.request_id; + /* This has to be passed to HW while calling hw_ops->start */ + frame_req->hw_req_private = prestart_args.hw_req_private; + + /* + * Save the current frame_req into priv, + * this will come as priv while hw_config + */ + prepare->priv = frame_req; + + CAM_DBG(CAM_FD, "FramePrepare : Frame[%lld]", frame_req->request_id); + + return 0; +error: + return rc; +} + +static int cam_fd_mgr_hw_config(void *hw_mgr_priv, void *hw_config_args) +{ + struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv; + struct cam_hw_config_args *config = + (struct cam_hw_config_args *) hw_config_args; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_mgr_frame_request *frame_req; + int rc; + int i; + + if (!hw_mgr || !config) { + CAM_ERR(CAM_FD, "Invalid arguments %pK %pK", hw_mgr, config); + return -EINVAL; + } + + if (!config->num_hw_update_entries) { + CAM_ERR(CAM_FD, "No hw update enteries are available"); + return -EINVAL; + } + + hw_ctx = (struct cam_fd_hw_mgr_ctx *)config->ctxt_to_hw_map; + if (!hw_ctx || !hw_ctx->ctx_in_use) { + CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx); + return -EPERM; + } + + frame_req = config->priv; + + trace_cam_apply_req("FD", frame_req->request_id); + CAM_DBG(CAM_FD, "FrameHWConfig : Frame[%lld]", frame_req->request_id); + + frame_req->num_hw_update_entries = config->num_hw_update_entries; + for (i = 0; i < config->num_hw_update_entries; i++) { + frame_req->hw_update_entries[i] = config->hw_update_entries[i]; + CAM_DBG(CAM_FD, "PreStart HWEntry[%d] : %d %d %d %d %pK", + frame_req->hw_update_entries[i].handle, + frame_req->hw_update_entries[i].offset, + frame_req->hw_update_entries[i].len, + frame_req->hw_update_entries[i].flags, + frame_req->hw_update_entries[i].addr); + } + + if (hw_ctx->priority == CAM_FD_PRIORITY_HIGH) { + CAM_DBG(CAM_FD, "Insert frame into prio0 queue"); + rc = cam_fd_mgr_util_put_frame_req( + &hw_mgr->frame_pending_list_high, &frame_req); + } else { + CAM_DBG(CAM_FD, "Insert frame into prio1 queue"); + rc = cam_fd_mgr_util_put_frame_req( + &hw_mgr->frame_pending_list_normal, &frame_req); + } + if (rc) { + CAM_ERR(CAM_FD, "Failed in queuing frame req, rc=%d", rc); + goto put_free_list; + } + + rc = cam_fd_mgr_util_schedule_frame_worker_task(hw_mgr); + if (rc) { + CAM_ERR(CAM_FD, "Worker task scheduling failed %d", rc); + goto remove_and_put_free_list; + } + + return 0; + +remove_and_put_free_list: + + if (hw_ctx->priority == CAM_FD_PRIORITY_HIGH) { + CAM_DBG(CAM_FD, "Removing frame into prio0 queue"); + cam_fd_mgr_util_get_frame_req( + &hw_mgr->frame_pending_list_high, &frame_req); + } else { + CAM_DBG(CAM_FD, "Removing frame into prio1 queue"); + cam_fd_mgr_util_get_frame_req( + &hw_mgr->frame_pending_list_normal, &frame_req); + } +put_free_list: + cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &frame_req); + + return rc; +} + +int cam_fd_hw_mgr_deinit(struct device_node *of_node) +{ + CAM_DBG(CAM_FD, "HW Mgr Deinit"); + + cam_req_mgr_workq_destroy(&g_fd_hw_mgr.work); + + cam_smmu_destroy_handle(g_fd_hw_mgr.device_iommu.non_secure); + g_fd_hw_mgr.device_iommu.non_secure = -1; + + mutex_destroy(&g_fd_hw_mgr.ctx_mutex); + mutex_destroy(&g_fd_hw_mgr.frame_req_mutex); + mutex_destroy(&g_fd_hw_mgr.hw_mgr_mutex); + + return 0; +} + +int cam_fd_hw_mgr_init(struct device_node *of_node, + struct cam_hw_mgr_intf *hw_mgr_intf) +{ + int count, i, rc = 0; + struct cam_hw_intf *hw_intf = NULL; + struct cam_fd_hw_mgr_ctx *hw_mgr_ctx; + struct cam_fd_device *hw_device; + struct cam_fd_mgr_frame_request *frame_req; + + if (!of_node || !hw_mgr_intf) { + CAM_ERR(CAM_FD, "Invalid args of_node %pK hw_mgr_intf %pK", + of_node, hw_mgr_intf); + return -EINVAL; + } + + memset(&g_fd_hw_mgr, 0x0, sizeof(g_fd_hw_mgr)); + memset(hw_mgr_intf, 0x0, sizeof(*hw_mgr_intf)); + + mutex_init(&g_fd_hw_mgr.ctx_mutex); + mutex_init(&g_fd_hw_mgr.frame_req_mutex); + mutex_init(&g_fd_hw_mgr.hw_mgr_mutex); + spin_lock_init(&g_fd_hw_mgr.hw_mgr_slock); + + count = of_property_count_strings(of_node, "compat-hw-name"); + if (!count || (count > CAM_FD_HW_MAX)) { + CAM_ERR(CAM_FD, "Invalid compat names in dev tree %d", count); + return -EINVAL; + } + g_fd_hw_mgr.num_devices = count; + + g_fd_hw_mgr.raw_results_available = false; + g_fd_hw_mgr.supported_modes = 0; + + for (i = 0; i < count; i++) { + hw_device = &g_fd_hw_mgr.hw_device[i]; + + rc = cam_fd_mgr_util_pdev_get_hw_intf(of_node, i, &hw_intf); + if (rc) { + CAM_ERR(CAM_FD, "hw intf from pdev failed, rc=%d", rc); + return rc; + } + + mutex_init(&hw_device->lock); + + hw_device->valid = true; + hw_device->hw_intf = hw_intf; + hw_device->ready_to_process = true; + + if (hw_device->hw_intf->hw_ops.process_cmd) { + struct cam_fd_hw_cmd_set_irq_cb irq_cb_args; + + irq_cb_args.cam_fd_hw_mgr_cb = cam_fd_mgr_irq_cb; + irq_cb_args.data = hw_device; + + rc = hw_device->hw_intf->hw_ops.process_cmd( + hw_device->hw_intf->hw_priv, + CAM_FD_HW_CMD_REGISTER_CALLBACK, + &irq_cb_args, sizeof(irq_cb_args)); + if (rc) { + CAM_ERR(CAM_FD, + "Failed in REGISTER_CALLBACK %d", rc); + return rc; + } + } + + if (hw_device->hw_intf->hw_ops.get_hw_caps) { + rc = hw_device->hw_intf->hw_ops.get_hw_caps( + hw_intf->hw_priv, &hw_device->hw_caps, + sizeof(hw_device->hw_caps)); + if (rc) { + CAM_ERR(CAM_FD, "Failed in get_hw_caps %d", rc); + return rc; + } + + g_fd_hw_mgr.raw_results_available |= + hw_device->hw_caps.raw_results_available; + g_fd_hw_mgr.supported_modes |= + hw_device->hw_caps.supported_modes; + + CAM_DBG(CAM_FD, + "Device[mode=%d, raw=%d], Mgr[mode=%d, raw=%d]", + hw_device->hw_caps.supported_modes, + hw_device->hw_caps.raw_results_available, + g_fd_hw_mgr.supported_modes, + g_fd_hw_mgr.raw_results_available); + } + } + + INIT_LIST_HEAD(&g_fd_hw_mgr.free_ctx_list); + INIT_LIST_HEAD(&g_fd_hw_mgr.used_ctx_list); + INIT_LIST_HEAD(&g_fd_hw_mgr.frame_free_list); + INIT_LIST_HEAD(&g_fd_hw_mgr.frame_pending_list_high); + INIT_LIST_HEAD(&g_fd_hw_mgr.frame_pending_list_normal); + INIT_LIST_HEAD(&g_fd_hw_mgr.frame_processing_list); + + g_fd_hw_mgr.device_iommu.non_secure = -1; + g_fd_hw_mgr.device_iommu.secure = -1; + g_fd_hw_mgr.cdm_iommu.non_secure = -1; + g_fd_hw_mgr.cdm_iommu.secure = -1; + + rc = cam_smmu_get_handle("fd", + &g_fd_hw_mgr.device_iommu.non_secure); + if (rc) { + CAM_ERR(CAM_FD, "Get iommu handle failed, rc=%d", rc); + goto destroy_mutex; + } + + rc = cam_cdm_get_iommu_handle("fd", &g_fd_hw_mgr.cdm_iommu); + if (rc) + CAM_DBG(CAM_FD, "Failed to acquire the CDM iommu handles"); + + CAM_DBG(CAM_FD, "iommu handles : device(%d, %d), cdm(%d, %d)", + g_fd_hw_mgr.device_iommu.non_secure, + g_fd_hw_mgr.device_iommu.secure, + g_fd_hw_mgr.cdm_iommu.non_secure, + g_fd_hw_mgr.cdm_iommu.secure); + + /* Init hw mgr contexts and add to free list */ + for (i = 0; i < CAM_CTX_MAX; i++) { + hw_mgr_ctx = &g_fd_hw_mgr.ctx_pool[i]; + + memset(hw_mgr_ctx, 0x0, sizeof(*hw_mgr_ctx)); + INIT_LIST_HEAD(&hw_mgr_ctx->list); + + hw_mgr_ctx->ctx_index = i; + hw_mgr_ctx->device_index = -1; + hw_mgr_ctx->hw_mgr = &g_fd_hw_mgr; + + list_add_tail(&hw_mgr_ctx->list, &g_fd_hw_mgr.free_ctx_list); + } + + /* Init hw mgr frame requests and add to free list */ + for (i = 0; i < CAM_CTX_REQ_MAX; i++) { + frame_req = &g_fd_hw_mgr.frame_req[i]; + + memset(frame_req, 0x0, sizeof(*frame_req)); + INIT_LIST_HEAD(&frame_req->list); + + list_add_tail(&frame_req->list, &g_fd_hw_mgr.frame_free_list); + } + + rc = cam_req_mgr_workq_create("cam_fd_worker", CAM_FD_WORKQ_NUM_TASK, + &g_fd_hw_mgr.work, CRM_WORKQ_USAGE_IRQ); + if (rc) { + CAM_ERR(CAM_FD, "Unable to create a worker, rc=%d", rc); + goto detach_smmu; + } + + for (i = 0; i < CAM_FD_WORKQ_NUM_TASK; i++) + g_fd_hw_mgr.work->task.pool[i].payload = + &g_fd_hw_mgr.work_data[i]; + + /* Setup hw cap so that we can just return the info when requested */ + memset(&g_fd_hw_mgr.fd_caps, 0, sizeof(g_fd_hw_mgr.fd_caps)); + g_fd_hw_mgr.fd_caps.device_iommu = g_fd_hw_mgr.device_iommu; + g_fd_hw_mgr.fd_caps.cdm_iommu = g_fd_hw_mgr.cdm_iommu; + g_fd_hw_mgr.fd_caps.hw_caps = g_fd_hw_mgr.hw_device[0].hw_caps; + + CAM_DBG(CAM_FD, + "IOMMU device(%d, %d), CDM(%d, %d) versions core[%d.%d], wrapper[%d.%d]", + g_fd_hw_mgr.fd_caps.device_iommu.secure, + g_fd_hw_mgr.fd_caps.device_iommu.non_secure, + g_fd_hw_mgr.fd_caps.cdm_iommu.secure, + g_fd_hw_mgr.fd_caps.cdm_iommu.non_secure, + g_fd_hw_mgr.fd_caps.hw_caps.core_version.major, + g_fd_hw_mgr.fd_caps.hw_caps.core_version.minor, + g_fd_hw_mgr.fd_caps.hw_caps.wrapper_version.major, + g_fd_hw_mgr.fd_caps.hw_caps.wrapper_version.minor); + + hw_mgr_intf->hw_mgr_priv = &g_fd_hw_mgr; + hw_mgr_intf->hw_get_caps = cam_fd_mgr_hw_get_caps; + hw_mgr_intf->hw_acquire = cam_fd_mgr_hw_acquire; + hw_mgr_intf->hw_release = cam_fd_mgr_hw_release; + hw_mgr_intf->hw_start = cam_fd_mgr_hw_start; + hw_mgr_intf->hw_stop = cam_fd_mgr_hw_stop; + hw_mgr_intf->hw_prepare_update = cam_fd_mgr_hw_prepare_update; + hw_mgr_intf->hw_config = cam_fd_mgr_hw_config; + hw_mgr_intf->hw_read = NULL; + hw_mgr_intf->hw_write = NULL; + hw_mgr_intf->hw_close = NULL; + hw_mgr_intf->hw_flush = cam_fd_mgr_hw_flush; + + return rc; + +detach_smmu: + cam_smmu_destroy_handle(g_fd_hw_mgr.device_iommu.non_secure); + g_fd_hw_mgr.device_iommu.non_secure = -1; +destroy_mutex: + mutex_destroy(&g_fd_hw_mgr.ctx_mutex); + mutex_destroy(&g_fd_hw_mgr.frame_req_mutex); + mutex_destroy(&g_fd_hw_mgr.hw_mgr_mutex); + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h new file mode 100644 index 000000000000..db5d1002262a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h @@ -0,0 +1,186 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FD_HW_MGR_H_ +#define _CAM_FD_HW_MGR_H_ + +#include +#include + +#include +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "cam_hw_mgr_intf.h" +#include "cam_req_mgr_workq.h" +#include "cam_fd_hw_intf.h" + +#define CAM_FD_HW_MAX 1 +#define CAM_FD_WORKQ_NUM_TASK 10 + +struct cam_fd_hw_mgr; + +/** + * enum cam_fd_mgr_work_type - Type of worker task + * + * @CAM_FD_WORK_FRAME : Work type indicating frame task + * @CAM_FD_WORK_IRQ : Work type indicating irq task + */ +enum cam_fd_mgr_work_type { + CAM_FD_WORK_FRAME, + CAM_FD_WORK_IRQ, +}; + +/** + * struct cam_fd_hw_mgr_ctx : FD HW Mgr context + * + * @list : List pointer used to maintain this context + * in free, used list + * @ctx_index : Index of this context + * @ctx_in_use : Whether this context is in use + * @event_cb : Event callback pointer to notify cam core context + * @cb_priv : Event callback private pointer + * @hw_mgr : Pointer to hw manager + * @get_raw_results : Whether this context needs raw results + * @mode : Mode in which this context runs + * @device_index : HW Device used by this context + * @ctx_hw_private : HW layer's private context pointer for this context + * @priority : Priority of this context + */ +struct cam_fd_hw_mgr_ctx { + struct list_head list; + uint32_t ctx_index; + bool ctx_in_use; + cam_hw_event_cb_func event_cb; + void *cb_priv; + struct cam_fd_hw_mgr *hw_mgr; + bool get_raw_results; + enum cam_fd_hw_mode mode; + int32_t device_index; + void *ctx_hw_private; + uint32_t priority; +}; + +/** + * struct cam_fd_device : FD HW Device + * + * @hw_caps : This FD device's capabilities + * @hw_intf : FD device's interface information + * @ready_to_process : Whether this device is ready to process next frame + * @num_ctxts : Number of context currently running on this device + * @valid : Whether this device is valid + * @lock : Lock used for protectin + * @cur_hw_ctx : current hw context running in the device + * @req_id : current processing req id + */ +struct cam_fd_device { + struct cam_fd_hw_caps hw_caps; + struct cam_hw_intf *hw_intf; + bool ready_to_process; + uint32_t num_ctxts; + bool valid; + struct mutex lock; + struct cam_fd_hw_mgr_ctx *cur_hw_ctx; + int64_t req_id; +}; + +/** + * struct cam_fd_mgr_frame_request : Frame request information maintained + * in HW Mgr layer + * + * @list : List pointer used to maintain this request in + * free, pending, processing request lists + * @request_id : Request ID corresponding to this request + * @hw_ctx : HW context from which this request is coming + * @hw_req_private : HW layer's private information specific to + * this request + * @hw_update_entries : HW update entries corresponding to this request + * which needs to be submitted to HW through CDM + * @num_hw_update_entries : Number of HW update entries + */ +struct cam_fd_mgr_frame_request { + struct list_head list; + uint64_t request_id; + struct cam_fd_hw_mgr_ctx *hw_ctx; + struct cam_fd_hw_req_private hw_req_private; + struct cam_hw_update_entry hw_update_entries[CAM_FD_MAX_HW_ENTRIES]; + uint32_t num_hw_update_entries; +}; + +/** + * struct cam_fd_mgr_work_data : HW Mgr work data information + * + * @type : Type of work + * @irq_type : IRQ type when this work is queued because of irq callback + */ +struct cam_fd_mgr_work_data { + enum cam_fd_mgr_work_type type; + enum cam_fd_hw_irq_type irq_type; +}; + +/** + * struct cam_fd_hw_mgr : FD HW Mgr information + * + * @free_ctx_list : List of free contexts available for acquire + * @used_ctx_list : List of contexts that are acquired + * @frame_free_list : List of free frame requests available + * @frame_pending_list_high : List of high priority frame requests pending + * for processing + * @frame_pending_list_normal : List of normal priority frame requests pending + * for processing + * @frame_processing_list : List of frame requests currently being + * processed currently. Generally maximum one + * request would be present in this list + * @hw_mgr_mutex : Mutex to protect hw mgr data when accessed + * from multiple threads + * @hw_mgr_slock : Spin lock to protect hw mgr data when accessed + * from multiple threads + * @ctx_mutex : Mutex to protect context list + * @frame_req_mutex : Mutex to protect frame request list + * @device_iommu : Device IOMMU information + * @cdm_iommu : CDM IOMMU information + * @hw_device : Underlying HW device information + * @num_devices : Number of HW devices available + * @raw_results_available : Whether raw results available in this driver + * @supported_modes : Supported modes by this driver + * @ctx_pool : List of context + * @frame_req : List of frame requests + * @work : Worker handle + * @work_data : Worker data + * @fd_caps : FD driver capabilities + */ +struct cam_fd_hw_mgr { + struct list_head free_ctx_list; + struct list_head used_ctx_list; + struct list_head frame_free_list; + struct list_head frame_pending_list_high; + struct list_head frame_pending_list_normal; + struct list_head frame_processing_list; + struct mutex hw_mgr_mutex; + spinlock_t hw_mgr_slock; + struct mutex ctx_mutex; + struct mutex frame_req_mutex; + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + struct cam_fd_device hw_device[CAM_FD_HW_MAX]; + uint32_t num_devices; + bool raw_results_available; + uint32_t supported_modes; + struct cam_fd_hw_mgr_ctx ctx_pool[CAM_CTX_MAX]; + struct cam_fd_mgr_frame_request frame_req[CAM_CTX_REQ_MAX]; + struct cam_req_mgr_core_workq *work; + struct cam_fd_mgr_work_data work_data[CAM_FD_WORKQ_NUM_TASK]; + struct cam_fd_query_cap_cmd fd_caps; +}; + +#endif /* _CAM_FD_HW_MGR_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h new file mode 100644 index 000000000000..58cba4fad125 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FD_HW_MGR_INTF_H_ +#define _CAM_FD_HW_MGR_INTF_H_ + +#include + +#include "cam_debug_util.h" +#include "cam_hw_mgr_intf.h" + +int cam_fd_hw_mgr_init(struct device_node *of_node, + struct cam_hw_mgr_intf *hw_mgr_intf); +int cam_fd_hw_mgr_deinit(struct device_node *of_node); + +#endif /* _CAM_FD_HW_MGR_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/Makefile new file mode 100644 index 000000000000..1e89b80ea286 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/Makefile @@ -0,0 +1,13 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_hw_dev.o cam_fd_hw_core.o cam_fd_hw_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c new file mode 100644 index 000000000000..a18afc6d7acc --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c @@ -0,0 +1,1168 @@ +/* Copyright (c) 2017-2018, 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 "cam_fd_hw_core.h" +#include "cam_fd_hw_soc.h" +#include "cam_trace.h" + +#define CAM_FD_REG_VAL_PAIR_SIZE 256 + +static uint32_t cam_fd_cdm_write_reg_val_pair(uint32_t *buffer, + uint32_t index, uint32_t reg_offset, uint32_t reg_value) +{ + buffer[index++] = reg_offset; + buffer[index++] = reg_value; + + CAM_DBG(CAM_FD, "FD_CDM_CMD: Base[FD_CORE] Offset[0x%8x] Value[0x%8x]", + reg_offset, reg_value); + + return index; +} + +static void cam_fd_hw_util_cdm_callback(uint32_t handle, void *userdata, + enum cam_cdm_cb_status status, uint64_t cookie) +{ + trace_cam_cdm_cb("FD", status); + CAM_DBG(CAM_FD, "CDM hdl=%x, udata=%pK, status=%d, cookie=%llu", + handle, userdata, status, cookie); +} + +static void cam_fd_hw_util_enable_power_on_settings(struct cam_hw_info *fd_hw) +{ + struct cam_hw_soc_info *soc_info = &fd_hw->soc_info; + struct cam_fd_hw_static_info *hw_static_info = + ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info; + + if (hw_static_info->enable_errata_wa.single_irq_only == false) { + /* Enable IRQs here */ + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_mask, + hw_static_info->irq_mask); + } + + /* QoS settings */ + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.vbif_req_priority, + hw_static_info->qos_priority); + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.vbif_priority_level, + hw_static_info->qos_priority_level); +} + +int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw, + struct cam_fd_hw_caps *hw_caps) +{ + struct cam_hw_soc_info *soc_info = &fd_hw->soc_info; + struct cam_fd_hw_static_info *hw_static_info = + ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info; + uint32_t reg_value; + + if (!hw_static_info) { + CAM_ERR(CAM_FD, "Invalid hw info data"); + return -EINVAL; + } + + reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_CORE, + hw_static_info->core_regs.version); + hw_caps->core_version.major = + CAM_BITS_MASK_SHIFT(reg_value, 0xf00, 0x8); + hw_caps->core_version.minor = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0, 0x4); + hw_caps->core_version.incr = + CAM_BITS_MASK_SHIFT(reg_value, 0xf, 0x0); + + reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.wrapper_version); + hw_caps->wrapper_version.major = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1c); + hw_caps->wrapper_version.minor = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->wrapper_version.incr = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + hw_caps->raw_results_available = + hw_static_info->results.raw_results_available; + hw_caps->supported_modes = hw_static_info->supported_modes; + + CAM_DBG(CAM_FD, "core:%d.%d.%d wrapper:%d.%d.%d intermediate:%d", + hw_caps->core_version.major, hw_caps->core_version.minor, + hw_caps->core_version.incr, hw_caps->wrapper_version.major, + hw_caps->wrapper_version.minor, hw_caps->wrapper_version.incr, + hw_caps->raw_results_available); + + return 0; +} + +static int cam_fd_hw_util_fdwrapper_sync_reset(struct cam_hw_info *fd_hw) +{ + struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info; + struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info; + struct cam_hw_soc_info *soc_info = &fd_hw->soc_info; + long time_left; + + /* Before triggering reset to HW, clear the reset complete */ + reinit_completion(&fd_core->reset_complete); + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE, + hw_static_info->core_regs.control, 0x1); + + if (hw_static_info->enable_errata_wa.single_irq_only) { + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_mask, + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE)); + } + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.sw_reset, 0x1); + + time_left = wait_for_completion_timeout(&fd_core->reset_complete, + msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT)); + if (time_left <= 0) + CAM_WARN(CAM_FD, "HW reset timeout time_left=%ld", time_left); + + CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete"); + + return 0; +} + + +static int cam_fd_hw_util_fdwrapper_halt(struct cam_hw_info *fd_hw) +{ + struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info; + struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info; + struct cam_hw_soc_info *soc_info = &fd_hw->soc_info; + long time_left; + + /* Before triggering halt to HW, clear halt complete */ + reinit_completion(&fd_core->halt_complete); + + if (hw_static_info->enable_errata_wa.single_irq_only) { + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_mask, + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE)); + } + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.hw_stop, 0x1); + + time_left = wait_for_completion_timeout(&fd_core->halt_complete, + msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT)); + if (time_left <= 0) + CAM_WARN(CAM_FD, "HW halt timeout time_left=%ld", time_left); + + CAM_DBG(CAM_FD, "FD Wrapper Halt complete"); + + return 0; +} + +static int cam_fd_hw_util_processcmd_prestart(struct cam_hw_info *fd_hw, + struct cam_fd_hw_cmd_prestart_args *prestart_args) +{ + struct cam_hw_soc_info *soc_info = &fd_hw->soc_info; + struct cam_fd_hw_static_info *hw_static_info = + ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info; + struct cam_fd_ctx_hw_private *ctx_hw_private = + prestart_args->ctx_hw_private; + uint32_t size, size_required = 0; + uint32_t mem_base; + uint32_t *cmd_buf_addr = prestart_args->cmd_buf_addr; + uint32_t reg_val_pair[CAM_FD_REG_VAL_PAIR_SIZE]; + uint32_t num_cmds = 0; + int i; + struct cam_fd_hw_io_buffer *io_buf; + struct cam_fd_hw_req_private *req_private; + uint32_t available_size = prestart_args->size; + bool work_buffer_configured = false; + + if (!ctx_hw_private || !cmd_buf_addr) { + CAM_ERR(CAM_FD, "Invalid input prestart args %pK %pK", + ctx_hw_private, cmd_buf_addr); + return -EINVAL; + } + + if (prestart_args->get_raw_results && + !hw_static_info->results.raw_results_available) { + CAM_ERR(CAM_FD, "Raw results not supported %d %d", + prestart_args->get_raw_results, + hw_static_info->results.raw_results_available); + return -EINVAL; + } + + req_private = &prestart_args->hw_req_private; + req_private->ctx_hw_private = prestart_args->ctx_hw_private; + req_private->request_id = prestart_args->request_id; + req_private->get_raw_results = prestart_args->get_raw_results; + req_private->fd_results = NULL; + req_private->raw_results = NULL; + + /* Start preparing CDM register values that KMD has to insert */ + num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds, + hw_static_info->core_regs.control, 0x1); + num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds, + hw_static_info->core_regs.control, 0x0); + + for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) { + io_buf = &prestart_args->input_buf[i]; + + if (io_buf->valid == false) + break; + + if (io_buf->io_cfg->direction != CAM_BUF_INPUT) { + CAM_ERR(CAM_FD, "Incorrect direction %d %d", + io_buf->io_cfg->direction, CAM_BUF_INPUT); + return -EINVAL; + } + + switch (io_buf->io_cfg->resource_type) { + case CAM_FD_INPUT_PORT_ID_IMAGE: { + if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) { + CAM_ERR(CAM_FD, + "Invalid reg_val pair size %d, %d", + num_cmds, CAM_FD_REG_VAL_PAIR_SIZE); + return -EINVAL; + } + + num_cmds = cam_fd_cdm_write_reg_val_pair( + reg_val_pair, num_cmds, + hw_static_info->core_regs.image_addr, + io_buf->io_addr[0]); + break; + } + default: + CAM_ERR(CAM_FD, "Invalid resource type %d", + io_buf->io_cfg->resource_type); + return -EINVAL; + } + } + + for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) { + io_buf = &prestart_args->output_buf[i]; + + if (io_buf->valid == false) + break; + + if (io_buf->io_cfg->direction != CAM_BUF_OUTPUT) { + CAM_ERR(CAM_FD, "Incorrect direction %d %d", + io_buf->io_cfg->direction, CAM_BUF_INPUT); + return -EINVAL; + } + + switch (io_buf->io_cfg->resource_type) { + case CAM_FD_OUTPUT_PORT_ID_RESULTS: { + uint32_t face_results_offset; + + size_required = hw_static_info->results.max_faces * + hw_static_info->results.per_face_entries * 4; + + if (io_buf->io_cfg->planes[0].plane_stride < + size_required) { + CAM_ERR(CAM_FD, "Invalid results size %d %d", + io_buf->io_cfg->planes[0].plane_stride, + size_required); + return -EINVAL; + } + + req_private->fd_results = + (struct cam_fd_results *)io_buf->cpu_addr[0]; + + face_results_offset = + (uint8_t *)&req_private->fd_results->faces[0] - + (uint8_t *)req_private->fd_results; + + if (hw_static_info->ro_mode_supported) { + if ((num_cmds + 4) > CAM_FD_REG_VAL_PAIR_SIZE) { + CAM_ERR(CAM_FD, + "Invalid reg_val size %d, %d", + num_cmds, + CAM_FD_REG_VAL_PAIR_SIZE); + return -EINVAL; + } + /* + * Face data actually starts 16bytes later in + * the io buffer Check cam_fd_results. + */ + num_cmds = cam_fd_cdm_write_reg_val_pair( + reg_val_pair, num_cmds, + hw_static_info->core_regs.result_addr, + io_buf->io_addr[0] + + face_results_offset); + num_cmds = cam_fd_cdm_write_reg_val_pair( + reg_val_pair, num_cmds, + hw_static_info->core_regs.ro_mode, + 0x1); + + req_private->ro_mode_enabled = true; + } else { + req_private->ro_mode_enabled = false; + } + break; + } + case CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS: { + size_required = + hw_static_info->results.raw_results_entries * + sizeof(uint32_t); + + if (io_buf->io_cfg->planes[0].plane_stride < + size_required) { + CAM_ERR(CAM_FD, "Invalid results size %d %d", + io_buf->io_cfg->planes[0].plane_stride, + size_required); + return -EINVAL; + } + + req_private->raw_results = + (uint32_t *)io_buf->cpu_addr[0]; + break; + } + case CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER: { + if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) { + CAM_ERR(CAM_FD, + "Invalid reg_val pair size %d, %d", + num_cmds, CAM_FD_REG_VAL_PAIR_SIZE); + return -EINVAL; + } + + num_cmds = cam_fd_cdm_write_reg_val_pair( + reg_val_pair, num_cmds, + hw_static_info->core_regs.work_addr, + io_buf->io_addr[0]); + + work_buffer_configured = true; + break; + } + default: + CAM_ERR(CAM_FD, "Invalid resource type %d", + io_buf->io_cfg->resource_type); + return -EINVAL; + } + } + + if (!req_private->fd_results || !work_buffer_configured) { + CAM_ERR(CAM_FD, "Invalid IO Buffers results=%pK work=%d", + req_private->fd_results, work_buffer_configured); + return -EINVAL; + } + + /* First insert CHANGE_BASE command */ + size = ctx_hw_private->cdm_ops->cdm_required_size_changebase(); + /* since cdm returns dwords, we need to convert it into bytes */ + if ((size * 4) > available_size) { + CAM_ERR(CAM_FD, "buf size:%d is not sufficient, expected: %d", + prestart_args->size, size); + return -EINVAL; + } + + mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(soc_info, + ((struct cam_fd_soc_private *)soc_info->soc_private)-> + regbase_index[CAM_FD_REG_CORE]); + + ctx_hw_private->cdm_ops->cdm_write_changebase(cmd_buf_addr, mem_base); + cmd_buf_addr += size; + available_size -= (size * 4); + + size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random( + num_cmds/2); + /* cdm util returns dwords, need to convert to bytes */ + if ((size * 4) > available_size) { + CAM_ERR(CAM_FD, "Insufficient size:%d , expected size:%d", + available_size, size); + return -ENOMEM; + } + ctx_hw_private->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmds/2, + reg_val_pair); + cmd_buf_addr += size; + available_size -= (size * 4); + + /* Update pre_config_buf_size in bytes */ + prestart_args->pre_config_buf_size = + prestart_args->size - available_size; + + /* Insert start trigger command into CDM as post config commands. */ + num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, 0, + hw_static_info->core_regs.control, 0x2); + size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random( + num_cmds/2); + if ((size * 4) > available_size) { + CAM_ERR(CAM_FD, "Insufficient size:%d , expected size:%d", + available_size, size); + return -ENOMEM; + } + ctx_hw_private->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmds/2, + reg_val_pair); + cmd_buf_addr += size; + available_size -= (size * 4); + + prestart_args->post_config_buf_size = size * 4; + + CAM_DBG(CAM_FD, "PreConfig [%pK %d], PostConfig[%pK %d]", + prestart_args->cmd_buf_addr, prestart_args->pre_config_buf_size, + cmd_buf_addr, prestart_args->post_config_buf_size); + + for (i = 0; i < (prestart_args->pre_config_buf_size + + prestart_args->post_config_buf_size) / 4; i++) + CAM_DBG(CAM_FD, "CDM KMD Commands [%d] : [%pK] [0x%x]", i, + &prestart_args->cmd_buf_addr[i], + prestart_args->cmd_buf_addr[i]); + + return 0; +} + +static int cam_fd_hw_util_processcmd_frame_done(struct cam_hw_info *fd_hw, + struct cam_fd_hw_frame_done_args *frame_done_args) +{ + struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info; + struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info; + struct cam_fd_hw_req_private *req_private; + uint32_t base, face_cnt; + uint32_t *buffer; + unsigned long flags; + int i; + + spin_lock_irqsave(&fd_core->spin_lock, flags); + if ((fd_core->core_state != CAM_FD_CORE_STATE_IDLE) || + (fd_core->results_valid == false) || + !fd_core->hw_req_private) { + CAM_ERR(CAM_FD, + "Invalid state for results state=%d, results=%d %pK", + fd_core->core_state, fd_core->results_valid, + fd_core->hw_req_private); + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + return -EINVAL; + } + fd_core->core_state = CAM_FD_CORE_STATE_READING_RESULTS; + req_private = fd_core->hw_req_private; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + /* + * Copy the register value as is into output buffers. + * Wehter we are copying the output data by reading registers or + * programming output buffer directly to HW must be transparent to UMD. + * In case HW supports writing face count value directly into + * DDR memory in future, these values should match. + */ + req_private->fd_results->face_count = + cam_fd_soc_register_read(&fd_hw->soc_info, CAM_FD_REG_CORE, + hw_static_info->core_regs.result_cnt); + + face_cnt = req_private->fd_results->face_count & 0x3F; + + if (face_cnt > hw_static_info->results.max_faces) { + CAM_WARN(CAM_FD, "Face count greater than max %d %d", + face_cnt, hw_static_info->results.max_faces); + face_cnt = hw_static_info->results.max_faces; + } + + CAM_DBG(CAM_FD, "ReqID[%lld] Faces Detected = %d", + req_private->request_id, face_cnt); + + /* + * We need to read the face data information from registers only + * if one of below is true + * 1. RO mode is not set. i.e FD HW doesn't write face data into + * DDR memory + * 2. On the current chipset, results written into DDR memory by FD HW + * are not gauranteed to be correct + */ + if (!req_private->ro_mode_enabled || + hw_static_info->enable_errata_wa.ro_mode_results_invalid) { + buffer = (uint32_t *)&req_private->fd_results->faces[0]; + base = hw_static_info->core_regs.results_reg_base; + + /* + * Write register values as is into face data buffer. Its UMD + * driver responsibility to interpret the data and extract face + * properties from output buffer. Think in case output buffer + * is directly programmed to HW, then KMD has no control to + * extract the face properties and UMD anyway has to extract + * face properties. So we follow the same approach and keep + * this transparent to UMD. + */ + for (i = 0; + i < (face_cnt * + hw_static_info->results.per_face_entries); i++) { + *buffer = cam_fd_soc_register_read(&fd_hw->soc_info, + CAM_FD_REG_CORE, base + (i * 0x4)); + CAM_DBG(CAM_FD, "FaceData[%d] : 0x%x", i / 4, *buffer); + buffer++; + } + } + + if (req_private->get_raw_results && + req_private->raw_results && + hw_static_info->results.raw_results_available) { + buffer = req_private->raw_results; + base = hw_static_info->core_regs.raw_results_reg_base; + + for (i = 0; + i < hw_static_info->results.raw_results_entries; + i++) { + *buffer = cam_fd_soc_register_read(&fd_hw->soc_info, + CAM_FD_REG_CORE, base + (i * 0x4)); + CAM_DBG(CAM_FD, "RawData[%d] : 0x%x", i, *buffer); + buffer++; + } + } + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_core->hw_req_private = NULL; + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + return 0; +} + +irqreturn_t cam_fd_hw_irq(int irq_num, void *data) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)data; + struct cam_fd_core *fd_core; + struct cam_hw_soc_info *soc_info; + struct cam_fd_hw_static_info *hw_static_info; + uint32_t reg_value; + enum cam_fd_hw_irq_type irq_type = CAM_FD_IRQ_FRAME_DONE; + uint32_t num_irqs = 0; + + if (!fd_hw) { + CAM_ERR(CAM_FD, "Invalid data in IRQ callback"); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *) fd_hw->core_info; + soc_info = &fd_hw->soc_info; + hw_static_info = fd_core->hw_static_info; + + reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_status); + + CAM_DBG(CAM_FD, "FD IRQ status 0x%x", reg_value); + + if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE)) { + complete_all(&fd_core->halt_complete); + irq_type = CAM_FD_IRQ_HALT_DONE; + num_irqs++; + } + + if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE)) { + complete_all(&fd_core->reset_complete); + irq_type = CAM_FD_IRQ_RESET_DONE; + num_irqs++; + } + + if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE)) { + complete_all(&fd_core->processing_complete); + irq_type = CAM_FD_IRQ_FRAME_DONE; + num_irqs++; + } + + /* + * We should never get an IRQ callback with no or more than one mask. + * Validate first to make sure nothing going wrong. + */ + if (num_irqs != 1) { + CAM_ERR(CAM_FD, + "Invalid number of IRQs, value=0x%x, num_irqs=%d", + reg_value, num_irqs); + return -EINVAL; + } + + trace_cam_irq_activated("FD", irq_type); + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_clear, + hw_static_info->irq_mask); + + if (irq_type == CAM_FD_IRQ_HALT_DONE) { + /* + * Do not send HALT IRQ callback to Hw Mgr, + * a reset would always follow + */ + return IRQ_HANDLED; + } + + spin_lock(&fd_core->spin_lock); + /* Do not change state to IDLE on HALT IRQ. Reset must follow halt */ + if ((irq_type == CAM_FD_IRQ_RESET_DONE) || + (irq_type == CAM_FD_IRQ_FRAME_DONE)) { + + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + if (irq_type == CAM_FD_IRQ_FRAME_DONE) + fd_core->results_valid = true; + + CAM_DBG(CAM_FD, "FD IRQ type %d, state=%d", + irq_type, fd_core->core_state); + } + spin_unlock(&fd_core->spin_lock); + + if (fd_core->irq_cb.cam_fd_hw_mgr_cb) + fd_core->irq_cb.cam_fd_hw_mgr_cb(fd_core->irq_cb.data, + irq_type); + + return IRQ_HANDLED; +} + +int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args, + uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + struct cam_fd_core *fd_core; + struct cam_fd_hw_caps *fd_hw_caps = + (struct cam_fd_hw_caps *)get_hw_cap_args; + + if (!hw_priv || !get_hw_cap_args) { + CAM_ERR(CAM_FD, "Invalid input pointers %pK %pK", + hw_priv, get_hw_cap_args); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + *fd_hw_caps = fd_core->hw_caps; + + CAM_DBG(CAM_FD, "core:%d.%d wrapper:%d.%d mode:%d, raw:%d", + fd_hw_caps->core_version.major, + fd_hw_caps->core_version.minor, + fd_hw_caps->wrapper_version.major, + fd_hw_caps->wrapper_version.minor, + fd_hw_caps->supported_modes, + fd_hw_caps->raw_results_available); + + return 0; +} + +int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + struct cam_fd_core *fd_core; + struct cam_fd_hw_init_args *init_args = + (struct cam_fd_hw_init_args *)init_hw_args; + int rc = 0; + unsigned long flags; + + if (!fd_hw || !init_args) { + CAM_ERR(CAM_FD, "Invalid argument %pK %pK", fd_hw, init_args); + return -EINVAL; + } + + if (arg_size != sizeof(struct cam_fd_hw_init_args)) { + CAM_ERR(CAM_FD, "Invalid arg size %u, %lu", arg_size, + sizeof(struct cam_fd_hw_init_args)); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + + mutex_lock(&fd_hw->hw_mutex); + CAM_DBG(CAM_FD, "FD HW Init ref count before %d", fd_hw->open_count); + + if (fd_hw->open_count > 0) { + rc = 0; + goto cdm_streamon; + } + + rc = cam_fd_soc_enable_resources(&fd_hw->soc_info); + if (rc) { + CAM_ERR(CAM_FD, "Enable SOC failed, rc=%d", rc); + goto unlock_return; + } + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_hw->hw_state = CAM_HW_STATE_POWER_UP; + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + rc = cam_fd_hw_reset(hw_priv, NULL, 0); + if (rc) { + CAM_ERR(CAM_FD, "Reset Failed, rc=%d", rc); + goto disable_soc; + } + + cam_fd_hw_util_enable_power_on_settings(fd_hw); + +cdm_streamon: + fd_hw->open_count++; + CAM_DBG(CAM_FD, "FD HW Init ref count after %d", fd_hw->open_count); + + if (init_args->ctx_hw_private) { + struct cam_fd_ctx_hw_private *ctx_hw_private = + init_args->ctx_hw_private; + + rc = cam_cdm_stream_on(ctx_hw_private->cdm_handle); + if (rc) { + CAM_ERR(CAM_FD, "CDM StreamOn fail :handle=0x%x, rc=%d", + ctx_hw_private->cdm_handle, rc); + fd_hw->open_count--; + if (!fd_hw->open_count) + goto disable_soc; + } + } + + mutex_unlock(&fd_hw->hw_mutex); + + return rc; + +disable_soc: + if (cam_fd_soc_disable_resources(&fd_hw->soc_info)) + CAM_ERR(CAM_FD, "Error in disable soc resources"); + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); +unlock_return: + mutex_unlock(&fd_hw->hw_mutex); + return rc; +} + +int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = hw_priv; + struct cam_fd_core *fd_core = NULL; + struct cam_fd_hw_deinit_args *deinit_args = + (struct cam_fd_hw_deinit_args *)deinit_hw_args; + int rc = 0; + unsigned long flags; + + if (!fd_hw || !deinit_hw_args) { + CAM_ERR(CAM_FD, "Invalid argument"); + return -EINVAL; + } + + if (arg_size != sizeof(struct cam_fd_hw_deinit_args)) { + CAM_ERR(CAM_FD, "Invalid arg size %u, %lu", arg_size, + sizeof(struct cam_fd_hw_deinit_args)); + return -EINVAL; + } + + mutex_lock(&fd_hw->hw_mutex); + if (fd_hw->open_count == 0) { + mutex_unlock(&fd_hw->hw_mutex); + CAM_ERR(CAM_FD, "Error Unbalanced deinit"); + return -EFAULT; + } + + fd_hw->open_count--; + CAM_DBG(CAM_FD, "FD HW ref count=%d", fd_hw->open_count); + + if (fd_hw->open_count > 0) { + rc = 0; + goto positive_ref_cnt; + } + + rc = cam_fd_soc_disable_resources(&fd_hw->soc_info); + if (rc) + CAM_ERR(CAM_FD, "Failed in Disable SOC, rc=%d", rc); + + fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + fd_core = (struct cam_fd_core *)fd_hw->core_info; + + /* With the ref_cnt correct, this should never happen */ + WARN_ON(!fd_core); + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); +positive_ref_cnt: + if (deinit_args->ctx_hw_private) { + struct cam_fd_ctx_hw_private *ctx_hw_private = + deinit_args->ctx_hw_private; + + rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle); + if (rc) { + CAM_ERR(CAM_FD, + "Failed in CDM StreamOff, handle=0x%x, rc=%d", + ctx_hw_private->cdm_handle, rc); + } + } + + mutex_unlock(&fd_hw->hw_mutex); + return rc; +} + +int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + struct cam_fd_core *fd_core; + struct cam_fd_hw_static_info *hw_static_info; + struct cam_hw_soc_info *soc_info; + unsigned long flags; + int rc; + + if (!fd_hw) { + CAM_ERR(CAM_FD, "Invalid input handle"); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + hw_static_info = fd_core->hw_static_info; + soc_info = &fd_hw->soc_info; + + spin_lock_irqsave(&fd_core->spin_lock, flags); + if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) || + (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) { + CAM_ERR(CAM_FD, "Reset not allowed in %d state", + fd_core->core_state); + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + return -EINVAL; + } + + fd_core->results_valid = false; + fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.cgc_disable, 0x1); + + rc = cam_fd_hw_util_fdwrapper_halt(fd_hw); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc); + return rc; + } + + rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw); + if (rc) { + CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc); + return rc; + } + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.cgc_disable, 0x0); + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + return rc; +} + +int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + struct cam_fd_core *fd_core; + struct cam_fd_hw_static_info *hw_static_info; + struct cam_fd_hw_cmd_start_args *start_args = + (struct cam_fd_hw_cmd_start_args *)hw_start_args; + struct cam_fd_ctx_hw_private *ctx_hw_private; + unsigned long flags; + int rc; + + if (!hw_priv || !start_args) { + CAM_ERR(CAM_FD, "Invalid input args %pK %pK", hw_priv, + start_args); + return -EINVAL; + } + + if (arg_size != sizeof(struct cam_fd_hw_cmd_start_args)) { + CAM_ERR(CAM_FD, "Invalid arg size %u, %lu", arg_size, + sizeof(struct cam_fd_hw_cmd_start_args)); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + hw_static_info = fd_core->hw_static_info; + + spin_lock_irqsave(&fd_core->spin_lock, flags); + if (fd_core->core_state != CAM_FD_CORE_STATE_IDLE) { + CAM_ERR(CAM_FD, "Cannot start in %d state", + fd_core->core_state); + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + return -EINVAL; + } + + /* + * We are about to start FD HW processing, save the request + * private data which is being processed by HW. Once the frame + * processing is finished, process_cmd(FRAME_DONE) should be called + * with same hw_req_private as input. + */ + fd_core->hw_req_private = start_args->hw_req_private; + fd_core->core_state = CAM_FD_CORE_STATE_PROCESSING; + fd_core->results_valid = false; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + ctx_hw_private = start_args->ctx_hw_private; + + /* Before starting HW process, clear processing complete */ + reinit_completion(&fd_core->processing_complete); + + if (hw_static_info->enable_errata_wa.single_irq_only) { + cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.irq_mask, + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE)); + } + + if (start_args->num_hw_update_entries > 0) { + struct cam_cdm_bl_request *cdm_cmd = ctx_hw_private->cdm_cmd; + struct cam_hw_update_entry *cmd; + int i; + + cdm_cmd->cmd_arrary_count = start_args->num_hw_update_entries; + cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE; + cdm_cmd->flag = false; + cdm_cmd->userdata = NULL; + cdm_cmd->cookie = 0; + + for (i = 0 ; i <= start_args->num_hw_update_entries; i++) { + cmd = (start_args->hw_update_entries + i); + cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle; + cdm_cmd->cmd[i].offset = cmd->offset; + cdm_cmd->cmd[i].len = cmd->len; + } + + rc = cam_cdm_submit_bls(ctx_hw_private->cdm_handle, cdm_cmd); + if (rc) { + CAM_ERR(CAM_FD, + "Failed to submit cdm commands, rc=%d", rc); + goto error; + } + } else { + CAM_ERR(CAM_FD, "Invalid number of hw update entries"); + rc = -EINVAL; + goto error; + } + + return 0; +error: + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + return rc; +} + +int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + struct cam_fd_core *fd_core; + struct cam_fd_hw_static_info *hw_static_info; + struct cam_hw_soc_info *soc_info; + unsigned long flags; + int rc; + + if (!fd_hw) { + CAM_ERR(CAM_FD, "Invalid input handle"); + return -EINVAL; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + hw_static_info = fd_core->hw_static_info; + soc_info = &fd_hw->soc_info; + + spin_lock_irqsave(&fd_core->spin_lock, flags); + if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) || + (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) { + CAM_ERR(CAM_FD, "Reset not allowed in %d state", + fd_core->core_state); + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + return -EINVAL; + } + + fd_core->results_valid = false; + fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.cgc_disable, 0x1); + + rc = cam_fd_hw_util_fdwrapper_halt(fd_hw); + if (rc) { + CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc); + return rc; + } + + /* HALT must be followed by RESET */ + rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw); + if (rc) { + CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc); + return rc; + } + + cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER, + hw_static_info->wrapper_regs.cgc_disable, 0x0); + + spin_lock_irqsave(&fd_core->spin_lock, flags); + fd_core->core_state = CAM_FD_CORE_STATE_IDLE; + spin_unlock_irqrestore(&fd_core->spin_lock, flags); + + return rc; +} + +int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + int rc = -EINVAL; + struct cam_fd_ctx_hw_private *ctx_hw_private; + struct cam_fd_hw_reserve_args *reserve_args = + (struct cam_fd_hw_reserve_args *)hw_reserve_args; + struct cam_cdm_acquire_data cdm_acquire; + struct cam_cdm_bl_request *cdm_cmd; + int i; + + if (!fd_hw || !reserve_args) { + CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, reserve_args); + return -EINVAL; + } + + if (arg_size != sizeof(struct cam_fd_hw_reserve_args)) { + CAM_ERR(CAM_FD, "Invalid arg size %u, %lu", arg_size, + sizeof(struct cam_fd_hw_reserve_args)); + return -EINVAL; + } + + cdm_cmd = kzalloc(((sizeof(struct cam_cdm_bl_request)) + + ((CAM_FD_MAX_HW_ENTRIES - 1) * + sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL); + if (!cdm_cmd) + return -ENOMEM; + + ctx_hw_private = kzalloc(sizeof(struct cam_fd_ctx_hw_private), + GFP_KERNEL); + if (!ctx_hw_private) { + kfree(cdm_cmd); + return -ENOMEM; + } + + memset(&cdm_acquire, 0, sizeof(cdm_acquire)); + strlcpy(cdm_acquire.identifier, "fd", sizeof("fd")); + cdm_acquire.cell_index = fd_hw->soc_info.index; + cdm_acquire.handle = 0; + cdm_acquire.userdata = ctx_hw_private; + cdm_acquire.cam_cdm_callback = cam_fd_hw_util_cdm_callback; + cdm_acquire.id = CAM_CDM_VIRTUAL; + cdm_acquire.base_array_cnt = fd_hw->soc_info.num_reg_map; + for (i = 0; i < fd_hw->soc_info.num_reg_map; i++) + cdm_acquire.base_array[i] = &fd_hw->soc_info.reg_map[i]; + + rc = cam_cdm_acquire(&cdm_acquire); + if (rc) { + CAM_ERR(CAM_FD, "Failed to acquire the CDM HW"); + goto error; + } + + ctx_hw_private->hw_ctx = reserve_args->hw_ctx; + ctx_hw_private->fd_hw = fd_hw; + ctx_hw_private->mode = reserve_args->mode; + ctx_hw_private->cdm_handle = cdm_acquire.handle; + ctx_hw_private->cdm_ops = cdm_acquire.ops; + ctx_hw_private->cdm_cmd = cdm_cmd; + + reserve_args->ctx_hw_private = ctx_hw_private; + + CAM_DBG(CAM_FD, "private=%pK, hw_ctx=%pK, mode=%d, cdm_handle=0x%x", + ctx_hw_private, ctx_hw_private->hw_ctx, ctx_hw_private->mode, + ctx_hw_private->cdm_handle); + + return 0; +error: + kfree(ctx_hw_private); + kfree(cdm_cmd); + return rc; +} + +int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + int rc = -EINVAL; + struct cam_fd_ctx_hw_private *ctx_hw_private; + struct cam_fd_hw_release_args *release_args = + (struct cam_fd_hw_release_args *)hw_release_args; + + if (!fd_hw || !release_args) { + CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, release_args); + return -EINVAL; + } + + if (arg_size != sizeof(struct cam_fd_hw_release_args)) { + CAM_ERR(CAM_FD, "Invalid arg size %u, %lu", arg_size, + sizeof(struct cam_fd_hw_release_args)); + return -EINVAL; + } + + ctx_hw_private = + (struct cam_fd_ctx_hw_private *)release_args->ctx_hw_private; + + rc = cam_cdm_release(ctx_hw_private->cdm_handle); + if (rc) + CAM_ERR(CAM_FD, "Release cdm handle failed, handle=0x%x, rc=%d", + ctx_hw_private->cdm_handle, rc); + + kfree(ctx_hw_private->cdm_cmd); + kfree(ctx_hw_private); + release_args->ctx_hw_private = NULL; + + return 0; +} + +int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv; + int rc = -EINVAL; + + if (!hw_priv || !cmd_args || + (cmd_type >= CAM_FD_HW_CMD_MAX)) { + CAM_ERR(CAM_FD, "Invalid arguments %pK %pK %d", hw_priv, + cmd_args, cmd_type); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_FD_HW_CMD_REGISTER_CALLBACK: { + struct cam_fd_hw_cmd_set_irq_cb *irq_cb_args; + struct cam_fd_core *fd_core = + (struct cam_fd_core *)fd_hw->core_info; + + if (sizeof(struct cam_fd_hw_cmd_set_irq_cb) != arg_size) { + CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + irq_cb_args = (struct cam_fd_hw_cmd_set_irq_cb *)cmd_args; + fd_core->irq_cb.cam_fd_hw_mgr_cb = + irq_cb_args->cam_fd_hw_mgr_cb; + fd_core->irq_cb.data = irq_cb_args->data; + rc = 0; + break; + } + case CAM_FD_HW_CMD_PRESTART: { + struct cam_fd_hw_cmd_prestart_args *prestart_args; + + if (sizeof(struct cam_fd_hw_cmd_prestart_args) != arg_size) { + CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + prestart_args = (struct cam_fd_hw_cmd_prestart_args *)cmd_args; + rc = cam_fd_hw_util_processcmd_prestart(fd_hw, prestart_args); + break; + } + case CAM_FD_HW_CMD_FRAME_DONE: { + struct cam_fd_hw_frame_done_args *cmd_frame_results; + + if (sizeof(struct cam_fd_hw_frame_done_args) != + arg_size) { + CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d", + cmd_type, arg_size); + break; + } + + cmd_frame_results = + (struct cam_fd_hw_frame_done_args *)cmd_args; + rc = cam_fd_hw_util_processcmd_frame_done(fd_hw, + cmd_frame_results); + break; + } + default: + break; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h new file mode 100644 index 000000000000..3d9c5f0e90f8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h @@ -0,0 +1,244 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FD_HW_CORE_H_ +#define _CAM_FD_HW_CORE_H_ + +#include +#include +#include +#include +#include + +#include "cam_common_util.h" +#include "cam_debug_util.h" +#include "cam_io_util.h" +#include "cam_cpas_api.h" +#include "cam_cdm_intf_api.h" +#include "cam_fd_hw_intf.h" +#include "cam_fd_hw_soc.h" + +#define CAM_FD_IRQ_TO_MASK(irq) (1 << (irq)) +#define CAM_FD_MASK_TO_IRQ(mask, irq) ((mask) >> (irq)) + +#define CAM_FD_HW_HALT_RESET_TIMEOUT 750 + +/** + * enum cam_fd_core_state - FD Core internal states + * + * @CAM_FD_CORE_STATE_POWERDOWN : Indicates FD core is powered down + * @CAM_FD_CORE_STATE_IDLE : Indicates FD HW is in idle state. + * Core can be in this state when it is + * ready to process frames or when + * processing is finished and results are + * available + * @CAM_FD_CORE_STATE_PROCESSING : Indicates FD core is processing frame + * @CAM_FD_CORE_STATE_READING_RESULTS : Indicates results are being read from + * FD core + * @CAM_FD_CORE_STATE_RESET_PROGRESS : Indicates FD Core is in reset state + */ +enum cam_fd_core_state { + CAM_FD_CORE_STATE_POWERDOWN, + CAM_FD_CORE_STATE_IDLE, + CAM_FD_CORE_STATE_PROCESSING, + CAM_FD_CORE_STATE_READING_RESULTS, + CAM_FD_CORE_STATE_RESET_PROGRESS, +}; + +/** + * struct cam_fd_ctx_hw_private : HW private information for a specific hw ctx. + * This information is populated by HW layer on + * reserve() and given back to HW Mgr as private + * data for the hw context. This private_data + * has to be passed by HW Mgr layer while + * further HW layer calls + * + * @hw_ctx : Corresponding hw_ctx pointer + * @fd_hw : FD HW info pointer + * @cdm_handle : CDM Handle for this context + * @cdm_ops : CDM Ops + * @cdm_cmd : CDM command pointer + * @mode : Mode this context is running + * @curr_req_private : Current Request information + * + */ +struct cam_fd_ctx_hw_private { + void *hw_ctx; + struct cam_hw_info *fd_hw; + uint32_t cdm_handle; + struct cam_cdm_utils_ops *cdm_ops; + struct cam_cdm_bl_request *cdm_cmd; + enum cam_fd_hw_mode mode; + struct cam_fd_hw_req_private *curr_req_private; +}; + +/** + * struct cam_fd_core_regs : FD HW Core register offsets info + * + * @version : Offset of version register + * @control : Offset of control register + * @result_cnt : Offset of result count register + * @result_addr : Offset of results address register + * @image_addr : Offset of image address register + * @work_addr : Offset of work address register + * @ro_mode : Offset of ro_mode register + * @results_reg_base : Offset of results_reg_base register + * @raw_results_reg_base : Offset of raw_results_reg_base register + * + */ +struct cam_fd_core_regs { + uint32_t version; + uint32_t control; + uint32_t result_cnt; + uint32_t result_addr; + uint32_t image_addr; + uint32_t work_addr; + uint32_t ro_mode; + uint32_t results_reg_base; + uint32_t raw_results_reg_base; +}; + +/** + * struct cam_fd_core_regs : FD HW Wrapper register offsets info + * + * @wrapper_version : Offset of wrapper_version register + * @cgc_disable : Offset of cgc_disable register + * @hw_stop : Offset of hw_stop register + * @sw_reset : Offset of sw_reset register + * @vbif_req_priority : Offset of vbif_req_priority register + * @vbif_priority_level : Offset of vbif_priority_level register + * @vbif_done_status : Offset of vbif_done_status register + * @irq_mask : Offset of irq mask register + * @irq_status : Offset of irq status register + * @irq_clear : Offset of irq clear register + * + */ +struct cam_fd_wrapper_regs { + uint32_t wrapper_version; + uint32_t cgc_disable; + uint32_t hw_stop; + uint32_t sw_reset; + uint32_t vbif_req_priority; + uint32_t vbif_priority_level; + uint32_t vbif_done_status; + uint32_t irq_mask; + uint32_t irq_status; + uint32_t irq_clear; +}; + +/** + * struct cam_fd_hw_errata_wa : FD HW Errata workaround enable/dsiable info + * + * @single_irq_only : Whether to enable only one irq at any time + * @ro_mode_enable_always : Whether to enable ro mode always + * @ro_mode_results_invalid : Whether results written directly into output + * memory by HW are valid or not + */ +struct cam_fd_hw_errata_wa { + bool single_irq_only; + bool ro_mode_enable_always; + bool ro_mode_results_invalid; +}; + +/** + * struct cam_fd_hw_results_prop : FD HW Results properties + * + * @max_faces : Maximum number of faces supported + * @per_face_entries : Number of register with properties for each face + * @raw_results_entries : Number of raw results entries for the full search + * @raw_results_available : Whether raw results available on this HW + * + */ +struct cam_fd_hw_results_prop { + uint32_t max_faces; + uint32_t per_face_entries; + uint32_t raw_results_entries; + bool raw_results_available; +}; + +/** + * struct cam_fd_hw_static_info : FD HW information based on HW version + * + * @core_version : Core version of FD HW + * @wrapper_version : Wrapper version of FD HW + * @core_regs : Register offset information for core registers + * @wrapper_regs : Register offset information for wrapper registers + * @results : Information about results available on this HW + * @enable_errata_wa : Errata workaround information + * @irq_mask : IRQ mask to enable + * @qos_priority : QoS priority setting for this chipset + * @qos_priority_level : QoS priority level setting for this chipset + * @supported_modes : Supported HW modes on this HW version + * @ro_mode_supported : Whether RO mode is supported on this HW + * + */ +struct cam_fd_hw_static_info { + struct cam_hw_version core_version; + struct cam_hw_version wrapper_version; + struct cam_fd_core_regs core_regs; + struct cam_fd_wrapper_regs wrapper_regs; + struct cam_fd_hw_results_prop results; + struct cam_fd_hw_errata_wa enable_errata_wa; + uint32_t irq_mask; + uint32_t qos_priority; + uint32_t qos_priority_level; + uint32_t supported_modes; + bool ro_mode_supported; +}; + +/** + * struct cam_fd_core : FD HW core data structure + * + * @hw_static_info : HW information specific to version + * @hw_caps : HW capabilities + * @core_state : Current HW state + * @processing_complete : Whether processing is complete + * @reset_complete : Whether reset is complete + * @halt_complete : Whether halt is complete + * @hw_req_private : Request that is being currently processed by HW + * @results_valid : Whether HW frame results are available to get + * @spin_lock : Mutex to protect shared data in hw layer + * @irq_cb : HW Manager callback information + * + */ +struct cam_fd_core { + struct cam_fd_hw_static_info *hw_static_info; + struct cam_fd_hw_caps hw_caps; + enum cam_fd_core_state core_state; + struct completion processing_complete; + struct completion reset_complete; + struct completion halt_complete; + struct cam_fd_hw_req_private *hw_req_private; + bool results_valid; + spinlock_t spin_lock; + struct cam_fd_hw_cmd_set_irq_cb irq_cb; +}; + +int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw, + struct cam_fd_hw_caps *hw_caps); +irqreturn_t cam_fd_hw_irq(int irq_num, void *data); + +int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args, + uint32_t arg_size); +int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size); +int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size); +int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size); +int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size); +int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size); +int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size); +int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size); +int cam_fd_hw_read(void *hw_priv, void *read_args, uint32_t arg_size); +int cam_fd_hw_write(void *hw_priv, void *write_args, uint32_t arg_size); +int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); + +#endif /* _CAM_FD_HW_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c new file mode 100644 index 000000000000..6d9d330f7838 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include + +#include "cam_subdev.h" +#include "cam_fd_hw_intf.h" +#include "cam_fd_hw_core.h" +#include "cam_fd_hw_soc.h" +#include "cam_fd_hw_v41.h" +#include "cam_fd_hw_v501.h" + +static int cam_fd_hw_dev_probe(struct platform_device *pdev) +{ + struct cam_hw_info *fd_hw; + struct cam_hw_intf *fd_hw_intf; + struct cam_fd_core *fd_core; + const struct of_device_id *match_dev = NULL; + struct cam_fd_hw_static_info *hw_static_info = NULL; + int rc = 0; + struct cam_fd_hw_init_args init_args; + struct cam_fd_hw_deinit_args deinit_args; + + fd_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!fd_hw_intf) + return -ENOMEM; + + fd_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!fd_hw) { + kfree(fd_hw_intf); + return -ENOMEM; + } + + fd_core = kzalloc(sizeof(struct cam_fd_core), GFP_KERNEL); + if (!fd_core) { + kfree(fd_hw); + kfree(fd_hw_intf); + return -ENOMEM; + } + + fd_hw_intf->hw_priv = fd_hw; + fd_hw->core_info = fd_core; + + fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + fd_hw->soc_info.pdev = pdev; + fd_hw->soc_info.dev = &pdev->dev; + fd_hw->soc_info.dev_name = pdev->name; + fd_hw->open_count = 0; + mutex_init(&fd_hw->hw_mutex); + spin_lock_init(&fd_hw->hw_lock); + init_completion(&fd_hw->hw_complete); + + spin_lock_init(&fd_core->spin_lock); + init_completion(&fd_core->processing_complete); + init_completion(&fd_core->halt_complete); + init_completion(&fd_core->reset_complete); + + fd_hw_intf->hw_ops.get_hw_caps = cam_fd_hw_get_hw_caps; + fd_hw_intf->hw_ops.init = cam_fd_hw_init; + fd_hw_intf->hw_ops.deinit = cam_fd_hw_deinit; + fd_hw_intf->hw_ops.reset = cam_fd_hw_reset; + fd_hw_intf->hw_ops.reserve = cam_fd_hw_reserve; + fd_hw_intf->hw_ops.release = cam_fd_hw_release; + fd_hw_intf->hw_ops.start = cam_fd_hw_start; + fd_hw_intf->hw_ops.stop = cam_fd_hw_halt_reset; + fd_hw_intf->hw_ops.read = NULL; + fd_hw_intf->hw_ops.write = NULL; + fd_hw_intf->hw_ops.process_cmd = cam_fd_hw_process_cmd; + fd_hw_intf->hw_type = CAM_HW_FD; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev || !match_dev->data) { + CAM_ERR(CAM_FD, "No Of_match data, %pK", match_dev); + rc = -EINVAL; + goto free_memory; + } + hw_static_info = (struct cam_fd_hw_static_info *)match_dev->data; + fd_core->hw_static_info = hw_static_info; + + CAM_DBG(CAM_FD, "HW Static Info : version core[%d.%d] wrapper[%d.%d]", + hw_static_info->core_version.major, + hw_static_info->core_version.minor, + hw_static_info->wrapper_version.major, + hw_static_info->wrapper_version.minor); + + rc = cam_fd_soc_init_resources(&fd_hw->soc_info, cam_fd_hw_irq, fd_hw); + if (rc) { + CAM_ERR(CAM_FD, "Failed to init soc, rc=%d", rc); + goto free_memory; + } + + fd_hw_intf->hw_idx = fd_hw->soc_info.index; + + memset(&init_args, 0x0, sizeof(init_args)); + memset(&deinit_args, 0x0, sizeof(deinit_args)); + rc = cam_fd_hw_init(fd_hw, &init_args, sizeof(init_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed to hw init, rc=%d", rc); + goto deinit_platform_res; + } + + rc = cam_fd_hw_util_get_hw_caps(fd_hw, &fd_core->hw_caps); + if (rc) { + CAM_ERR(CAM_FD, "Failed to get hw caps, rc=%d", rc); + goto deinit_hw; + } + + rc = cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args)); + if (rc) { + CAM_ERR(CAM_FD, "Failed to deinit hw, rc=%d", rc); + goto deinit_platform_res; + } + + platform_set_drvdata(pdev, fd_hw_intf); + CAM_DBG(CAM_FD, "FD-%d probe successful", fd_hw_intf->hw_idx); + + return rc; + +deinit_hw: + if (cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args))) + CAM_ERR(CAM_FD, "Failed in hw deinit"); +deinit_platform_res: + if (cam_fd_soc_deinit_resources(&fd_hw->soc_info)) + CAM_ERR(CAM_FD, "Failed in soc deinit"); + mutex_destroy(&fd_hw->hw_mutex); +free_memory: + kfree(fd_hw); + kfree(fd_hw_intf); + kfree(fd_core); + + return rc; +} + +static int cam_fd_hw_dev_remove(struct platform_device *pdev) +{ + int rc = 0; + struct cam_hw_intf *fd_hw_intf; + struct cam_hw_info *fd_hw; + struct cam_fd_core *fd_core; + + fd_hw_intf = platform_get_drvdata(pdev); + if (!fd_hw_intf) { + CAM_ERR(CAM_FD, "Invalid fd_hw_intf from pdev"); + return -EINVAL; + } + + fd_hw = fd_hw_intf->hw_priv; + if (!fd_hw) { + CAM_ERR(CAM_FD, "Invalid fd_hw from fd_hw_intf"); + rc = -ENODEV; + goto free_fd_hw_intf; + } + + fd_core = (struct cam_fd_core *)fd_hw->core_info; + if (!fd_core) { + CAM_ERR(CAM_FD, "Invalid fd_core from fd_hw"); + rc = -EINVAL; + goto deinit_platform_res; + } + + kfree(fd_core); + +deinit_platform_res: + rc = cam_fd_soc_deinit_resources(&fd_hw->soc_info); + if (rc) + CAM_ERR(CAM_FD, "Error in FD soc deinit, rc=%d", rc); + + mutex_destroy(&fd_hw->hw_mutex); + kfree(fd_hw); + +free_fd_hw_intf: + kfree(fd_hw_intf); + + return rc; +} + +static const struct of_device_id cam_fd_hw_dt_match[] = { + { + .compatible = "qcom,fd41", + .data = &cam_fd_wrapper120_core410_info, + }, + { + .compatible = "qcom,fd501", + .data = &cam_fd_wrapper200_core501_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_fd_hw_dt_match); + +static struct platform_driver cam_fd_hw_driver = { + .probe = cam_fd_hw_dev_probe, + .remove = cam_fd_hw_dev_remove, + .driver = { + .name = "cam_fd_hw", + .owner = THIS_MODULE, + .of_match_table = cam_fd_hw_dt_match, + }, +}; + +static int __init cam_fd_hw_init_module(void) +{ + return platform_driver_register(&cam_fd_hw_driver); +} + +static void __exit cam_fd_hw_exit_module(void) +{ + platform_driver_unregister(&cam_fd_hw_driver); +} + +module_init(cam_fd_hw_init_module); +module_exit(cam_fd_hw_exit_module); +MODULE_DESCRIPTION("CAM FD HW driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h new file mode 100644 index 000000000000..aae7648ba1e2 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h @@ -0,0 +1,289 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FD_HW_INTF_H_ +#define _CAM_FD_HW_INTF_H_ + +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_soc_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_subdev.h" +#include "cam_cpas_api.h" +#include "cam_hw_mgr_intf.h" +#include "cam_debug_util.h" + +#define CAM_FD_MAX_IO_BUFFERS 5 +#define CAM_FD_MAX_HW_ENTRIES 5 + +/** + * enum cam_fd_hw_type - Enum for FD HW type + * + * @CAM_HW_FD : FaceDetection HW type + */ +enum cam_fd_hw_type { + CAM_HW_FD, +}; + +/** + * enum cam_fd_hw_mode - Mode in which HW can run + * + * @CAM_FD_MODE_FACEDETECTION : Face Detection mode in which face search + * is done on the given frame + * @CAM_FD_MODE_PYRAMID : Pyramid mode where a pyramid image is generated + * from an input image + */ +enum cam_fd_hw_mode { + CAM_FD_MODE_FACEDETECTION = 0x1, + CAM_FD_MODE_PYRAMID = 0x2, +}; + +/** + * enum cam_fd_priority - FD priority levels + * + * @CAM_FD_PRIORITY_HIGH : Indicates high priority client, driver prioritizes + * frame requests coming from contexts with HIGH + * priority compared to context with normal priority + * @CAM_FD_PRIORITY_NORMAL : Indicates normal priority client + */ +enum cam_fd_priority { + CAM_FD_PRIORITY_HIGH = 0x0, + CAM_FD_PRIORITY_NORMAL, +}; + +/** + * enum cam_fd_hw_irq_type - FD HW IRQ types + * + * @CAM_FD_IRQ_FRAME_DONE : Indicates frame processing is finished + * @CAM_FD_IRQ_HALT_DONE : Indicates HW halt is finished + * @CAM_FD_IRQ_RESET_DONE : Indicates HW reset is finished + */ +enum cam_fd_hw_irq_type { + CAM_FD_IRQ_FRAME_DONE, + CAM_FD_IRQ_HALT_DONE, + CAM_FD_IRQ_RESET_DONE, +}; + +/** + * enum cam_fd_hw_cmd_type - FD HW layer custom commands + * + * @CAM_FD_HW_CMD_PRESTART : Command to process pre-start settings + * @CAM_FD_HW_CMD_FRAME_DONE : Command to process frame done settings + * @CAM_FD_HW_CMD_UPDATE_SOC : Command to process soc update + * @CAM_FD_HW_CMD_REGISTER_CALLBACK : Command to set hw mgr callback + * @CAM_FD_HW_CMD_MAX : Indicates max cmd + */ +enum cam_fd_hw_cmd_type { + CAM_FD_HW_CMD_PRESTART, + CAM_FD_HW_CMD_FRAME_DONE, + CAM_FD_HW_CMD_UPDATE_SOC, + CAM_FD_HW_CMD_REGISTER_CALLBACK, + CAM_FD_HW_CMD_MAX, +}; + +/** + * struct cam_fd_hw_io_buffer : FD HW IO Buffer information + * + * @valid : Whether this IO Buf configuration is valid + * @io_cfg : IO Configuration information + * @num_buf : Number planes in io_addr, cpu_addr array + * @io_addr : Array of IO address information for planes + * @cpu_addr : Array of CPU address information for planes + */ +struct cam_fd_hw_io_buffer { + bool valid; + struct cam_buf_io_cfg *io_cfg; + uint32_t num_buf; + uint64_t io_addr[CAM_PACKET_MAX_PLANES]; + uint64_t cpu_addr[CAM_PACKET_MAX_PLANES]; +}; + +/** + * struct cam_fd_hw_req_private : FD HW layer's private information + * specific to a request + * + * @ctx_hw_private : FD HW layer's ctx specific private data + * @request_id : Request ID corresponding to this private information + * @get_raw_results : Whether to get raw results for this request + * @ro_mode_enabled : Whether RO mode is enabled for this request + * @fd_results : Pointer to save face detection results + * @raw_results : Pointer to save face detection raw results + */ +struct cam_fd_hw_req_private { + void *ctx_hw_private; + uint64_t request_id; + bool get_raw_results; + bool ro_mode_enabled; + struct cam_fd_results *fd_results; + uint32_t *raw_results; +}; + +/** + * struct cam_fd_hw_reserve_args : Reserve args for this HW context + * + * @hw_ctx : HW context for which reserve is requested + * @mode : Mode for which this reserve is requested + * @ctx_hw_private : Pointer to save HW layer's private information specific + * to this hw context. This has to be passed while calling + * further HW layer calls + */ +struct cam_fd_hw_reserve_args { + void *hw_ctx; + enum cam_fd_hw_mode mode; + void *ctx_hw_private; +}; + +/** + * struct cam_fd_hw_release_args : Release args for this HW context + * + * @hw_ctx : HW context for which release is requested + * @ctx_hw_private : HW layer's private information specific to this hw context + */ +struct cam_fd_hw_release_args { + void *hw_ctx; + void *ctx_hw_private; +}; + +/** + * struct cam_fd_hw_init_args : Init args for this HW context + * + * @hw_ctx : HW context for which init is requested + * @ctx_hw_private : HW layer's private information specific to this hw context + */ +struct cam_fd_hw_init_args { + void *hw_ctx; + void *ctx_hw_private; +}; + +/** + * struct cam_fd_hw_deinit_args : Deinit args for this HW context + * + * @hw_ctx : HW context for which deinit is requested + * @ctx_hw_private : HW layer's private information specific to this hw context + */ +struct cam_fd_hw_deinit_args { + void *hw_ctx; + void *ctx_hw_private; +}; + +/** + * struct cam_fd_hw_cmd_prestart_args : Prestart command args + * + * @hw_ctx : HW context which submitted this prestart + * @ctx_hw_private : HW layer's private information specific to + * this hw context + * @request_id : Request ID corresponds to this pre-start command + * @get_raw_results : Whether to get raw results for this request + * @input_buf : Input IO Buffer information for this request + * @output_buf : Output IO Buffer information for this request + * @cmd_buf_addr : Command buffer address to fill kmd commands + * @size : Size available in command buffer + * @pre_config_buf_size : Buffer size filled with commands by KMD that has + * to be inserted before umd commands + * @post_config_buf_size : Buffer size filled with commands by KMD that has + * to be inserted after umd commands + * @hw_req_private : HW layer's private information specific to + * this request + */ +struct cam_fd_hw_cmd_prestart_args { + void *hw_ctx; + void *ctx_hw_private; + uint64_t request_id; + bool get_raw_results; + struct cam_fd_hw_io_buffer input_buf[CAM_FD_MAX_IO_BUFFERS]; + struct cam_fd_hw_io_buffer output_buf[CAM_FD_MAX_IO_BUFFERS]; + uint32_t *cmd_buf_addr; + uint32_t size; + uint32_t pre_config_buf_size; + uint32_t post_config_buf_size; + struct cam_fd_hw_req_private hw_req_private; +}; + +/** + * struct cam_fd_hw_cmd_start_args : Start command args + * + * @hw_ctx : HW context which submitting start command + * @ctx_hw_private : HW layer's private information specific to + * this hw context + * @hw_req_private : HW layer's private information specific to + * this request + * @hw_update_entries : HW update entries corresponds to this request + * @num_hw_update_entries : Number of hw update entries + */ +struct cam_fd_hw_cmd_start_args { + void *hw_ctx; + void *ctx_hw_private; + struct cam_fd_hw_req_private *hw_req_private; + struct cam_hw_update_entry *hw_update_entries; + uint32_t num_hw_update_entries; +}; + +/** + * struct cam_fd_hw_stop_args : Stop command args + * + * @hw_ctx : HW context which submitting stop command + * @ctx_hw_private : HW layer's private information specific to this hw context + * @request_id : Request ID that need to be stopped + * @hw_req_private : HW layer's private information specific to this request + */ +struct cam_fd_hw_stop_args { + void *hw_ctx; + void *ctx_hw_private; + uint64_t request_id; + struct cam_fd_hw_req_private *hw_req_private; +}; + +/** + * struct cam_fd_hw_frame_done_args : Frame done command args + * + * @hw_ctx : HW context which submitting frame done request + * @ctx_hw_private : HW layer's private information specific to this hw context + * @request_id : Request ID that need to be stopped + * @hw_req_private : HW layer's private information specific to this request + */ +struct cam_fd_hw_frame_done_args { + void *hw_ctx; + void *ctx_hw_private; + uint64_t request_id; + struct cam_fd_hw_req_private *hw_req_private; +}; + +/** + * struct cam_fd_hw_reset_args : Reset command args + * + * @hw_ctx : HW context which submitting reset command + * @ctx_hw_private : HW layer's private information specific to this hw context + */ +struct cam_fd_hw_reset_args { + void *hw_ctx; + void *ctx_hw_private; +}; + +/** + * struct cam_fd_hw_cmd_set_irq_cb : Set IRQ callback command args + * + * @cam_fd_hw_mgr_cb : HW Mgr's callback pointer + * @data : HW Mgr's private data + */ +struct cam_fd_hw_cmd_set_irq_cb { + int (*cam_fd_hw_mgr_cb)(void *data, enum cam_fd_hw_irq_type irq_type); + void *data; +}; + +#endif /* _CAM_FD_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c new file mode 100644 index 000000000000..f27d016ae199 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c @@ -0,0 +1,290 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_fd_hw_core.h" +#include "cam_fd_hw_soc.h" + +static bool cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + if (!irq_data) + return false; + + CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, irq_type=%d", + handle, userdata, irq_data->irq_type); + + return false; +} + +static int cam_fd_hw_soc_util_setup_regbase_indices( + struct cam_hw_soc_info *soc_info) +{ + struct cam_fd_soc_private *soc_private = + (struct cam_fd_soc_private *)soc_info->soc_private; + uint32_t index; + int rc, i; + + for (i = 0; i < CAM_FD_REG_MAX; i++) + soc_private->regbase_index[i] = -1; + + if ((soc_info->num_mem_block > CAM_SOC_MAX_BLOCK) || + (soc_info->num_mem_block != CAM_FD_REG_MAX)) { + CAM_ERR(CAM_FD, "Invalid num_mem_block=%d", + soc_info->num_mem_block); + return -EINVAL; + } + + rc = cam_common_util_get_string_index(soc_info->mem_block_name, + soc_info->num_mem_block, "fd_core", &index); + if ((rc == 0) && (index < CAM_FD_REG_MAX)) { + soc_private->regbase_index[CAM_FD_REG_CORE] = index; + } else { + CAM_ERR(CAM_FD, "regbase not found for FD_CORE, rc=%d, %d %d", + rc, index, CAM_FD_REG_MAX); + return -EINVAL; + } + + rc = cam_common_util_get_string_index(soc_info->mem_block_name, + soc_info->num_mem_block, "fd_wrapper", &index); + if ((rc == 0) && (index < CAM_FD_REG_MAX)) { + soc_private->regbase_index[CAM_FD_REG_WRAPPER] = index; + } else { + CAM_ERR(CAM_FD, "regbase not found FD_WRAPPER, rc=%d, %d %d", + rc, index, CAM_FD_REG_MAX); + return -EINVAL; + } + + CAM_DBG(CAM_FD, "Reg indices : CORE=%d, WRAPPER=%d", + soc_private->regbase_index[CAM_FD_REG_CORE], + soc_private->regbase_index[CAM_FD_REG_WRAPPER]); + + return 0; +} + +static int cam_fd_soc_set_clk_flags(struct cam_hw_soc_info *soc_info) +{ + int i, rc = 0; + + if (soc_info->num_clk > CAM_SOC_MAX_CLK) { + CAM_ERR(CAM_FD, "Invalid num clk %d", soc_info->num_clk); + return -EINVAL; + } + + /* set memcore and mem periphery logic flags to 0 */ + for (i = 0; i < soc_info->num_clk; i++) { + if ((strcmp(soc_info->clk_name[i], "fd_core_clk") == 0) || + (strcmp(soc_info->clk_name[i], "fd_core_uar_clk") == + 0)) { + rc = cam_soc_util_set_clk_flags(soc_info, i, + CLKFLAG_NORETAIN_MEM); + if (rc) + CAM_ERR(CAM_FD, + "Failed in NORETAIN_MEM i=%d, rc=%d", + i, rc); + + cam_soc_util_set_clk_flags(soc_info, i, + CLKFLAG_NORETAIN_PERIPH); + if (rc) + CAM_ERR(CAM_FD, + "Failed in NORETAIN_PERIPH i=%d, rc=%d", + i, rc); + } + } + + return rc; +} + +void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info, + enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value) +{ + struct cam_fd_soc_private *soc_private = + (struct cam_fd_soc_private *)soc_info->soc_private; + int32_t reg_index = soc_private->regbase_index[reg_base]; + + CAM_DBG(CAM_FD, "FD_REG_WRITE: Base[%d] Offset[0x%8x] Value[0x%8x]", + reg_base, reg_offset, reg_value); + + cam_io_w_mb(reg_value, + soc_info->reg_map[reg_index].mem_base + reg_offset); +} + +uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info, + enum cam_fd_reg_base reg_base, uint32_t reg_offset) +{ + struct cam_fd_soc_private *soc_private = + (struct cam_fd_soc_private *)soc_info->soc_private; + int32_t reg_index = soc_private->regbase_index[reg_base]; + uint32_t reg_value; + + reg_value = cam_io_r_mb( + soc_info->reg_map[reg_index].mem_base + reg_offset); + + CAM_DBG(CAM_FD, "FD_REG_READ: Base[%d] Offset[0x%8x] Value[0x%8x]", + reg_base, reg_offset, reg_value); + + return reg_value; +} + +int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info) +{ + struct cam_fd_soc_private *soc_private = soc_info->soc_private; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + int rc; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = 7200000; + axi_vote.uncompressed_bw = 7200000; + rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_FD, "Error in CPAS START, rc=%d", rc); + return -EFAULT; + } + + rc = cam_soc_util_enable_platform_resource(soc_info, true, CAM_SVS_VOTE, + true); + if (rc) { + CAM_ERR(CAM_FD, "Error enable platform failed, rc=%d", rc); + goto stop_cpas; + } + + return rc; + +stop_cpas: + if (cam_cpas_stop(soc_private->cpas_handle)) + CAM_ERR(CAM_FD, "Error in CPAS STOP"); + + return rc; +} + + +int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info) +{ + struct cam_fd_soc_private *soc_private; + int rc = 0; + + if (!soc_info) { + CAM_ERR(CAM_FD, "Invalid soc_info param"); + return -EINVAL; + } + soc_private = soc_info->soc_private; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) { + CAM_ERR(CAM_FD, "disable platform resources failed, rc=%d", rc); + return rc; + } + + rc = cam_cpas_stop(soc_private->cpas_handle); + if (rc) { + CAM_ERR(CAM_FD, "Error in CPAS STOP, handle=0x%x, rc=%d", + soc_private->cpas_handle, rc); + return rc; + } + + return rc; +} + +int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, void *private_data) +{ + struct cam_fd_soc_private *soc_private; + struct cam_cpas_register_params cpas_register_param; + int rc; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) { + CAM_ERR(CAM_FD, "Failed in get_dt_properties, rc=%d", rc); + return rc; + } + + rc = cam_soc_util_request_platform_resource(soc_info, irq_handler, + private_data); + if (rc) { + CAM_ERR(CAM_FD, "Failed in request_platform_resource rc=%d", + rc); + return rc; + } + + rc = cam_fd_soc_set_clk_flags(soc_info); + if (rc) { + CAM_ERR(CAM_FD, "failed in set_clk_flags rc=%d", rc); + goto release_res; + } + + soc_private = kzalloc(sizeof(struct cam_fd_soc_private), GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto release_res; + } + soc_info->soc_private = soc_private; + + rc = cam_fd_hw_soc_util_setup_regbase_indices(soc_info); + if (rc) { + CAM_ERR(CAM_FD, "Failed in setup regbase, rc=%d", rc); + goto free_soc_private; + } + + memset(&cpas_register_param, 0, sizeof(cpas_register_param)); + strlcpy(cpas_register_param.identifier, "fd", CAM_HW_IDENTIFIER_LENGTH); + cpas_register_param.cell_index = soc_info->index; + cpas_register_param.dev = &soc_info->pdev->dev; + cpas_register_param.userdata = private_data; + cpas_register_param.cam_cpas_client_cb = cam_fd_hw_util_cpas_callback; + + rc = cam_cpas_register_client(&cpas_register_param); + if (rc) { + CAM_ERR(CAM_FD, "CPAS registration failed"); + goto free_soc_private; + } + soc_private->cpas_handle = cpas_register_param.client_handle; + CAM_DBG(CAM_FD, "CPAS handle=%d", soc_private->cpas_handle); + + return rc; + +free_soc_private: + kfree(soc_info->soc_private); + soc_info->soc_private = NULL; +release_res: + cam_soc_util_release_platform_resource(soc_info); + + return rc; +} + +int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info) +{ + struct cam_fd_soc_private *soc_private = + (struct cam_fd_soc_private *)soc_info->soc_private; + int rc; + + rc = cam_cpas_unregister_client(soc_private->cpas_handle); + if (rc) + CAM_ERR(CAM_FD, "Unregister cpas failed, handle=%d, rc=%d", + soc_private->cpas_handle, rc); + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_ERR(CAM_FD, "release platform failed, rc=%d", rc); + + kfree(soc_info->soc_private); + soc_info->soc_private = NULL; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h new file mode 100644 index 000000000000..4a22293fab17 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h @@ -0,0 +1,53 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FD_HW_SOC_H_ +#define _CAM_FD_HW_SOC_H_ + +#include "cam_soc_util.h" + +/** + * enum cam_fd_reg_base - Enum for FD register sets + * + * @CAM_FD_REG_CORE : Indicates FD Core register space + * @CAM_FD_REG_WRAPPER : Indicates FD Wrapper register space + * @CAM_FD_REG_MAX : Max number of register sets supported + */ +enum cam_fd_reg_base { + CAM_FD_REG_CORE, + CAM_FD_REG_WRAPPER, + CAM_FD_REG_MAX +}; + +/** + * struct cam_fd_soc_private : FD private SOC information + * + * @regbase_index : Mapping between Register base enum to register index in SOC + * @cpas_handle : CPAS handle + * + */ +struct cam_fd_soc_private { + int32_t regbase_index[CAM_FD_REG_MAX]; + uint32_t cpas_handle; +}; + +int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, void *private_data); +int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info); +int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info); +int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info); +uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info, + enum cam_fd_reg_base reg_base, uint32_t reg_offset); +void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info, + enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value); + +#endif /* _CAM_FD_HW_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h new file mode 100644 index 000000000000..78257a591a31 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2017-2018, 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 _CAM_FD_HW_V41_H_ +#define _CAM_FD_HW_V41_H_ + +static struct cam_fd_hw_static_info cam_fd_wrapper120_core410_info = { + .core_version = { + .major = 4, + .minor = 1, + .incr = 0, + }, + .wrapper_version = { + .major = 1, + .minor = 2, + .incr = 0, + }, + .core_regs = { + .version = 0x38, + .control = 0x0, + .result_cnt = 0x4, + .result_addr = 0x20, + .image_addr = 0x24, + .work_addr = 0x28, + .ro_mode = 0x34, + .results_reg_base = 0x400, + .raw_results_reg_base = 0x800, + }, + .wrapper_regs = { + .wrapper_version = 0x0, + .cgc_disable = 0x4, + .hw_stop = 0x8, + .sw_reset = 0x10, + .vbif_req_priority = 0x20, + .vbif_priority_level = 0x24, + .vbif_done_status = 0x34, + .irq_mask = 0x50, + .irq_status = 0x54, + .irq_clear = 0x58, + }, + .results = { + .max_faces = 35, + .per_face_entries = 4, + .raw_results_available = true, + .raw_results_entries = 512, + }, + .enable_errata_wa = { + .single_irq_only = true, + .ro_mode_enable_always = true, + .ro_mode_results_invalid = true, + }, + .irq_mask = CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE) | + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE) | + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE), + .qos_priority = 4, + .qos_priority_level = 4, + .supported_modes = CAM_FD_MODE_FACEDETECTION, + .ro_mode_supported = true, +}; + +#endif /* _CAM_FD_HW_V41_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v501.h b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v501.h new file mode 100644 index 000000000000..44b9ab58e566 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v501.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2018, 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 _CAM_FD_HW_V501_H_ +#define _CAM_FD_HW_V501_H_ + +static struct cam_fd_hw_static_info cam_fd_wrapper200_core501_info = { + .core_version = { + .major = 5, + .minor = 0, + .incr = 1, + }, + .wrapper_version = { + .major = 2, + .minor = 0, + .incr = 0, + }, + .core_regs = { + .version = 0x38, + .control = 0x0, + .result_cnt = 0x4, + .result_addr = 0x20, + .image_addr = 0x24, + .work_addr = 0x28, + .ro_mode = 0x34, + .results_reg_base = 0x400, + .raw_results_reg_base = 0x800, + }, + .wrapper_regs = { + .wrapper_version = 0x0, + .cgc_disable = 0x4, + .hw_stop = 0x8, + .sw_reset = 0x10, + .vbif_req_priority = 0x20, + .vbif_priority_level = 0x24, + .vbif_done_status = 0x34, + .irq_mask = 0x50, + .irq_status = 0x54, + .irq_clear = 0x58, + }, + .results = { + .max_faces = 35, + .per_face_entries = 4, + .raw_results_available = true, + .raw_results_entries = 512, + }, + .enable_errata_wa = { + .single_irq_only = true, + .ro_mode_enable_always = true, + .ro_mode_results_invalid = true, + }, + .irq_mask = CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE) | + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE) | + CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE), + .qos_priority = 4, + .qos_priority_level = 4, + .supported_modes = CAM_FD_MODE_FACEDETECTION | CAM_FD_MODE_PYRAMID, + .ro_mode_supported = true, +}; + +#endif /* _CAM_FD_HW_V501_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/Makefile new file mode 100644 index 000000000000..7f7d68debd05 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += icp_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp_subdev.o cam_icp_context.o hfi.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.c new file mode 100644 index 000000000000..502c95d4c60e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include "cam_sync_api.h" +#include "cam_node.h" +#include "cam_context.h" +#include "cam_context_utils.h" +#include "cam_icp_context.h" +#include "cam_req_mgr_util.h" +#include "cam_mem_mgr.h" +#include "cam_trace.h" +#include "cam_debug_util.h" + +static const char icp_dev_name[] = "icp"; + +static int __cam_icp_acquire_dev_in_available(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_acquire_dev_to_hw(ctx, cmd); + if (!rc) { + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("ICP", ctx); + } + + return rc; +} + +static int __cam_icp_release_dev_in_acquired(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_release_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Unable to release device"); + + ctx->state = CAM_CTX_AVAILABLE; + trace_cam_context_state("ICP", ctx); + return rc; +} + +static int __cam_icp_start_dev_in_acquired(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_start_dev_to_hw(ctx, cmd); + if (!rc) { + ctx->state = CAM_CTX_READY; + trace_cam_context_state("ICP", ctx); + } + + return rc; +} + +static int __cam_icp_flush_dev_in_ready(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_flush_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Failed to flush device"); + + return rc; +} + +static int __cam_icp_config_dev_in_ready(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_prepare_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Failed to prepare device"); + + return rc; +} + +static int __cam_icp_stop_dev_in_ready(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_stop_dev_to_hw(ctx); + if (rc) + CAM_ERR(CAM_ICP, "Failed to stop device"); + + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("ICP", ctx); + return rc; +} + +static int __cam_icp_release_dev_in_ready(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + rc = __cam_icp_stop_dev_in_ready(ctx, NULL); + if (rc) + CAM_ERR(CAM_ICP, "Failed to stop device"); + + rc = __cam_icp_release_dev_in_acquired(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Failed to release device"); + + return rc; +} + +static int __cam_icp_handle_buf_done_in_ready(void *ctx, + uint32_t evt_id, void *done) +{ + return cam_context_buf_done_from_hw(ctx, done, evt_id); +} + +static struct cam_ctx_ops + cam_icp_ctx_state_machine[CAM_CTX_STATE_MAX] = { + /* Uninit */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Available */ + { + .ioctl_ops = { + .acquire_dev = __cam_icp_acquire_dev_in_available, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Acquired */ + { + .ioctl_ops = { + .release_dev = __cam_icp_release_dev_in_acquired, + .start_dev = __cam_icp_start_dev_in_acquired, + .config_dev = __cam_icp_config_dev_in_ready, + .flush_dev = __cam_icp_flush_dev_in_ready, + }, + .crm_ops = {}, + .irq_ops = __cam_icp_handle_buf_done_in_ready, + }, + /* Ready */ + { + .ioctl_ops = { + .stop_dev = __cam_icp_stop_dev_in_ready, + .release_dev = __cam_icp_release_dev_in_ready, + .config_dev = __cam_icp_config_dev_in_ready, + .flush_dev = __cam_icp_flush_dev_in_ready, + }, + .crm_ops = {}, + .irq_ops = __cam_icp_handle_buf_done_in_ready, + }, + /* Activated */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, +}; + +int cam_icp_context_init(struct cam_icp_context *ctx, + struct cam_hw_mgr_intf *hw_intf, uint32_t ctx_id) +{ + int rc; + + if ((!ctx) || (!ctx->base) || (!hw_intf)) { + CAM_ERR(CAM_ICP, "Invalid params: %pK %pK", ctx, hw_intf); + rc = -EINVAL; + goto err; + } + + rc = cam_context_init(ctx->base, icp_dev_name, CAM_ICP, ctx_id, + NULL, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX); + if (rc) { + CAM_ERR(CAM_ICP, "Camera Context Base init failed"); + goto err; + } + + ctx->base->state_machine = cam_icp_ctx_state_machine; + ctx->base->ctx_priv = ctx; + ctx->ctxt_to_hw_map = NULL; + +err: + return rc; +} + +int cam_icp_context_deinit(struct cam_icp_context *ctx) +{ + if ((!ctx) || (!ctx->base)) { + CAM_ERR(CAM_ICP, "Invalid params: %pK", ctx); + return -EINVAL; + } + + cam_context_deinit(ctx->base); + memset(ctx, 0, sizeof(*ctx)); + + return 0; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.h new file mode 100644 index 000000000000..0c3a360c7de5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_context.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2017-2018, 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 _CAM_ICP_CONTEXT_H_ +#define _CAM_ICP_CONTEXT_H_ + +#include "cam_context.h" + +/** + * struct cam_icp_context - icp context + * @base: icp context object + * @state_machine: state machine for ICP context + * @req_base: common request structure + * @state: icp context state + * @ctxt_to_hw_map: context to FW handle mapping + */ +struct cam_icp_context { + struct cam_context *base; + struct cam_ctx_ops *state_machine; + struct cam_ctx_request req_base[CAM_CTX_REQ_MAX]; + uint32_t state; + void *ctxt_to_hw_map; +}; + +/** + * cam_icp_context_init() - ICP context init + * @ctx: Pointer to context + * @hw_intf: Pointer to ICP hardware interface + * @ctx_id: ID for this context + */ +int cam_icp_context_init(struct cam_icp_context *ctx, + struct cam_hw_mgr_intf *hw_intf, uint32_t ctx_id); + +/** + * cam_icp_context_deinit() - ICP context deinit + * @ctx: Pointer to context + */ +int cam_icp_context_deinit(struct cam_icp_context *ctx); + +#endif /* _CAM_ICP_CONTEXT_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_subdev.c new file mode 100644 index 000000000000..25bfc5a2182a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/cam_icp_subdev.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_req_mgr_dev.h" +#include "cam_subdev.h" +#include "cam_node.h" +#include "cam_context.h" +#include "cam_icp_context.h" +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_debug_util.h" +#include "cam_smmu_api.h" + +#define CAM_ICP_DEV_NAME "cam-icp" + +struct cam_icp_subdev { + struct cam_subdev sd; + struct cam_node *node; + struct cam_context ctx[CAM_ICP_CTX_MAX]; + struct cam_icp_context ctx_icp[CAM_ICP_CTX_MAX]; + struct mutex icp_lock; + int32_t open_cnt; + int32_t reserved; +}; + +static struct cam_icp_subdev g_icp_dev; + +static const struct of_device_id cam_icp_dt_match[] = { + {.compatible = "qcom,cam-icp"}, + {} +}; + +static void cam_icp_dev_iommu_fault_handler( + struct iommu_domain *domain, struct device *dev, unsigned long iova, + int flags, void *token, uint32_t buf_info) +{ + int i = 0; + struct cam_node *node = NULL; + + if (!token) { + CAM_ERR(CAM_ICP, "invalid token in page handler cb"); + return; + } + + node = (struct cam_node *)token; + + for (i = 0; i < node->ctx_size; i++) + cam_context_dump_pf_info(&(node->ctx_list[i]), iova, + buf_info); +} + +static int cam_icp_subdev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_hw_mgr_intf *hw_mgr_intf = NULL; + struct cam_node *node = v4l2_get_subdevdata(sd); + int rc = 0; + + mutex_lock(&g_icp_dev.icp_lock); + if (g_icp_dev.open_cnt >= 1) { + CAM_ERR(CAM_ICP, "ICP subdev is already opened"); + rc = -EALREADY; + goto end; + } + + if (!node) { + CAM_ERR(CAM_ICP, "Invalid args"); + rc = -EINVAL; + goto end; + } + + hw_mgr_intf = &node->hw_mgr_intf; + rc = hw_mgr_intf->hw_open(hw_mgr_intf->hw_mgr_priv, NULL); + if (rc < 0) { + CAM_ERR(CAM_ICP, "FW download failed"); + goto end; + } + g_icp_dev.open_cnt++; +end: + mutex_unlock(&g_icp_dev.icp_lock); + return rc; +} + +static int cam_icp_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + int rc = 0; + struct cam_hw_mgr_intf *hw_mgr_intf = NULL; + struct cam_node *node = v4l2_get_subdevdata(sd); + + mutex_lock(&g_icp_dev.icp_lock); + if (g_icp_dev.open_cnt <= 0) { + CAM_ERR(CAM_ICP, "ICP subdev is already closed"); + rc = -EINVAL; + goto end; + } + g_icp_dev.open_cnt--; + if (!node) { + CAM_ERR(CAM_ICP, "Invalid args"); + rc = -EINVAL; + goto end; + } + + hw_mgr_intf = &node->hw_mgr_intf; + if (!hw_mgr_intf) { + CAM_ERR(CAM_ICP, "hw_mgr_intf is not initialized"); + rc = -EINVAL; + goto end; + } + + rc = cam_node_shutdown(node); + if (rc < 0) { + CAM_ERR(CAM_ICP, "HW close failed"); + goto end; + } + +end: + mutex_unlock(&g_icp_dev.icp_lock); + return 0; +} + +const struct v4l2_subdev_internal_ops cam_icp_subdev_internal_ops = { + .open = cam_icp_subdev_open, + .close = cam_icp_subdev_close, +}; + +static int cam_icp_probe(struct platform_device *pdev) +{ + int rc = 0, i = 0; + struct cam_node *node; + struct cam_hw_mgr_intf *hw_mgr_intf; + int iommu_hdl = -1; + + if (!pdev) { + CAM_ERR(CAM_ICP, "pdev is NULL"); + return -EINVAL; + } + + g_icp_dev.sd.pdev = pdev; + g_icp_dev.sd.internal_ops = &cam_icp_subdev_internal_ops; + rc = cam_subdev_probe(&g_icp_dev.sd, pdev, CAM_ICP_DEV_NAME, + CAM_ICP_DEVICE_TYPE); + if (rc) { + CAM_ERR(CAM_ICP, "ICP cam_subdev_probe failed"); + goto probe_fail; + } + + node = (struct cam_node *) g_icp_dev.sd.token; + + hw_mgr_intf = kzalloc(sizeof(*hw_mgr_intf), GFP_KERNEL); + if (!hw_mgr_intf) { + rc = -EINVAL; + goto hw_alloc_fail; + } + + rc = cam_icp_hw_mgr_init(pdev->dev.of_node, (uint64_t *)hw_mgr_intf, + &iommu_hdl); + if (rc) { + CAM_ERR(CAM_ICP, "ICP HW manager init failed: %d", rc); + goto hw_init_fail; + } + + for (i = 0; i < CAM_ICP_CTX_MAX; i++) { + g_icp_dev.ctx_icp[i].base = &g_icp_dev.ctx[i]; + rc = cam_icp_context_init(&g_icp_dev.ctx_icp[i], + hw_mgr_intf, i); + if (rc) { + CAM_ERR(CAM_ICP, "ICP context init failed"); + goto ctx_fail; + } + } + + rc = cam_node_init(node, hw_mgr_intf, g_icp_dev.ctx, + CAM_ICP_CTX_MAX, CAM_ICP_DEV_NAME); + if (rc) { + CAM_ERR(CAM_ICP, "ICP node init failed"); + goto ctx_fail; + } + + cam_smmu_set_client_page_fault_handler(iommu_hdl, + cam_icp_dev_iommu_fault_handler, node); + + g_icp_dev.open_cnt = 0; + mutex_init(&g_icp_dev.icp_lock); + + CAM_DBG(CAM_ICP, "ICP probe complete"); + + return rc; + +ctx_fail: + for (--i; i >= 0; i--) + cam_icp_context_deinit(&g_icp_dev.ctx_icp[i]); +hw_init_fail: + kfree(hw_mgr_intf); +hw_alloc_fail: + cam_subdev_remove(&g_icp_dev.sd); +probe_fail: + return rc; +} + +static int cam_icp_remove(struct platform_device *pdev) +{ + int i; + struct v4l2_subdev *sd; + struct cam_subdev *subdev; + + if (!pdev) { + CAM_ERR(CAM_ICP, "pdev is NULL"); + return -ENODEV; + } + + sd = platform_get_drvdata(pdev); + if (!sd) { + CAM_ERR(CAM_ICP, "V4l2 subdev is NULL"); + return -ENODEV; + } + + subdev = v4l2_get_subdevdata(sd); + if (!subdev) { + CAM_ERR(CAM_ICP, "cam subdev is NULL"); + return -ENODEV; + } + + for (i = 0; i < CAM_ICP_CTX_MAX; i++) + cam_icp_context_deinit(&g_icp_dev.ctx_icp[i]); + cam_node_deinit(g_icp_dev.node); + cam_subdev_remove(&g_icp_dev.sd); + mutex_destroy(&g_icp_dev.icp_lock); + + return 0; +} + +static struct platform_driver cam_icp_driver = { + .probe = cam_icp_probe, + .remove = cam_icp_remove, + .driver = { + .name = "cam_icp", + .owner = THIS_MODULE, + .of_match_table = cam_icp_dt_match, + }, +}; + +static int __init cam_icp_init_module(void) +{ + return platform_driver_register(&cam_icp_driver); +} + +static void __exit cam_icp_exit_module(void) +{ + platform_driver_unregister(&cam_icp_driver); +} +module_init(cam_icp_init_module); +module_exit(cam_icp_exit_module); +MODULE_DESCRIPTION("MSM ICP driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_intf.h new file mode 100644 index 000000000000..cff838653e8c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_intf.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2017-2018, 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 _HFI_INTF_H_ +#define _HFI_INTF_H_ + +#include + +/** + * struct hfi_mem + * @len: length of memory + * @kva: kernel virtual address + * @iova: IO virtual address + * @reserved: reserved field + */ +struct hfi_mem { + uint64_t len; + uintptr_t kva; + uint32_t iova; + uint32_t reserved; +}; + +/** + * struct hfi_mem_info + * @qtbl: qtable hfi memory + * @cmd_q: command queue hfi memory for host to firmware communication + * @msg_q: message queue hfi memory for firmware to host communication + * @dbg_q: debug queue hfi memory for firmware debug information + * @sec_heap: secondary heap hfi memory for firmware + * @qdss: qdss mapped memory for fw + * @icp_base: icp base address + */ +struct hfi_mem_info { + struct hfi_mem qtbl; + struct hfi_mem cmd_q; + struct hfi_mem msg_q; + struct hfi_mem dbg_q; + struct hfi_mem sec_heap; + struct hfi_mem shmem; + struct hfi_mem qdss; + void __iomem *icp_base; +}; + +/** + * hfi_write_cmd() - function for hfi write + * @cmd_ptr: pointer to command data for hfi write + * + * Returns success(zero)/failure(non zero) + */ +int hfi_write_cmd(void *cmd_ptr); + +/** + * hfi_read_message() - function for hfi read + * @pmsg: buffer to place read message for hfi queue + * @q_id: queue id + * @words_read: total number of words read from the queue + * returned as output to the caller + * + * Returns success(zero)/failure(non zero) + */ +int hfi_read_message(uint32_t *pmsg, uint8_t q_id, uint32_t *words_read); + +/** + * hfi_init() - function initialize hfi after firmware download + * @event_driven_mode: event mode + * @hfi_mem: hfi memory info + * @icp_base: icp base address + * @debug: debug flag + * + * Returns success(zero)/failure(non zero) + */ +int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem, + void *__iomem icp_base, bool debug); + +/** + * hfi_get_hw_caps() - hardware capabilities from firmware + * @query_caps: holds query information from hfi + * + * Returns success(zero)/failure(non zero) + */ +int hfi_get_hw_caps(void *query_caps); + +/** + * hfi_send_system_cmd() - send hfi system command to firmware + * @type: type of system command + * @data: command data + * @size: size of command data + */ +void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size); + +/** + * cam_hfi_enable_cpu() - enable A5 CPU + * @icp_base: icp base address + */ +void cam_hfi_enable_cpu(void __iomem *icp_base); + +/** + * cam_hfi_disable_cpu() - disable A5 CPU + * @icp_base: icp base address + */ +void cam_hfi_disable_cpu(void __iomem *icp_base); + +/** + * cam_hfi_deinit() - cleanup HFI + */ +void cam_hfi_deinit(void __iomem *icp_base); +/** + * hfi_set_debug_level() - set debug level + * @a5_dbg_type: 1 for debug_q & 2 for qdss + * @lvl: FW debug message level + */ +int hfi_set_debug_level(u64 a5_dbg_type, uint32_t lvl); + +/** + * hfi_enable_ipe_bps_pc() - Enable interframe pc + * Host sends a command to firmware to enable interframe + * power collapse for IPE and BPS hardware. + * + * @enable: flag to enable/disable + * @core_info: Core information to firmware + */ +int hfi_enable_ipe_bps_pc(bool enable, uint32_t core_info); + +/** + * hfi_cmd_ubwc_config() - UBWC configuration to firmware + * @ubwc_cfg: UBWC configuration parameters + */ +int hfi_cmd_ubwc_config(uint32_t *ubwc_cfg); + +/** + * cam_hfi_resume() - function to resume + * @hfi_mem: hfi memory info + * @icp_base: icp base address + * @debug: debug flag + * + * Returns success(zero)/failure(non zero) + */ +int cam_hfi_resume(struct hfi_mem_info *hfi_mem, + void __iomem *icp_base, bool debug); + +#endif /* _HFI_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_reg.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_reg.h new file mode 100644 index 000000000000..2153ceacbb83 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_reg.h @@ -0,0 +1,324 @@ +/* Copyright (c) 2018, 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 _CAM_HFI_REG_H_ +#define _CAM_HFI_REG_H_ + +#include +#include "hfi_intf.h" + + +/* start of ICP CSR registers */ +#define HFI_REG_A5_HW_VERSION 0x0 +#define HFI_REG_A5_CSR_NSEC_RESET 0x4 +#define HFI_REG_A5_CSR_A5_CONTROL 0x8 +#define HFI_REG_A5_CSR_ETM 0xC +#define HFI_REG_A5_CSR_A2HOSTINTEN 0x10 +#define HFI_REG_A5_CSR_A2HOSTINT 0x14 +#define HFI_REG_A5_CSR_A2HOSTINTCLR 0x18 +#define HFI_REG_A5_CSR_A2HOSTINTSTATUS 0x1C +#define HFI_REG_A5_CSR_A2HOSTINTSET 0x20 +#define HFI_REG_A5_CSR_HOST2ICPINT 0x30 +#define HFI_REG_A5_CSR_A5_STATUS 0x200 +#define HFI_REG_A5_QGIC2_LM_ID 0x204 +#define HFI_REG_A5_SPARE 0x400 + +/* general purpose registers from */ +#define HFI_REG_FW_VERSION 0x44 +#define HFI_REG_HOST_ICP_INIT_REQUEST 0x48 +#define HFI_REG_ICP_HOST_INIT_RESPONSE 0x4C +#define HFI_REG_SHARED_MEM_PTR 0x50 +#define HFI_REG_SHARED_MEM_SIZE 0x54 +#define HFI_REG_QTBL_PTR 0x58 +#define HFI_REG_UNCACHED_HEAP_PTR 0x5C +#define HFI_REG_UNCACHED_HEAP_SIZE 0x60 +#define HFI_REG_QDSS_IOVA 0x6C +#define HFI_REG_QDSS_IOVA_SIZE 0x70 +/* end of ICP CSR registers */ + +/* flags for ICP CSR registers */ +#define ICP_FLAG_CSR_WAKE_UP_EN (1 << 4) +#define ICP_FLAG_CSR_A5_EN (1 << 9) +#define ICP_CSR_EN_CLKGATE_WFI (1 << 12) +#define ICP_CSR_EDBGRQ (1 << 14) +#define ICP_CSR_DBGSWENABLE (1 << 22) +#define ICP_CSR_A5_STATUS_WFI (1 << 7) + +#define ICP_FLAG_A5_CTRL_DBG_EN (ICP_FLAG_CSR_WAKE_UP_EN|\ + ICP_FLAG_CSR_A5_EN|\ + ICP_CSR_EDBGRQ|\ + ICP_CSR_DBGSWENABLE) + +#define ICP_FLAG_A5_CTRL_EN (ICP_FLAG_CSR_WAKE_UP_EN|\ + ICP_FLAG_CSR_A5_EN|\ + ICP_CSR_EN_CLKGATE_WFI) + +/* start of Queue table and queues */ +#define MAX_ICP_HFI_QUEUES 4 +#define ICP_QHDR_TX_TYPE_MASK 0xFF000000 +#define ICP_QHDR_RX_TYPE_MASK 0x00FF0000 +#define ICP_QHDR_PRI_TYPE_MASK 0x0000FF00 +#define ICP_QHDR_Q_ID_MASK 0x000000FF + +#define ICP_CMD_Q_SIZE_IN_BYTES 4096 +#define ICP_MSG_Q_SIZE_IN_BYTES 4096 +#define ICP_DBG_Q_SIZE_IN_BYTES 102400 + +#define ICP_SHARED_MEM_IN_BYTES (1024 * 1024) +#define ICP_UNCACHED_HEAP_SIZE_IN_BYTES (2 * 1024 * 1024) +#define ICP_HFI_MAX_PKT_SIZE_IN_WORDS 25600 +#define ICP_HFI_MAX_PKT_SIZE_MSGQ_IN_WORDS 256 + +#define ICP_HFI_QTBL_HOSTID1 0x01000000 +#define ICP_HFI_QTBL_STATUS_ENABLED 0x00000001 +#define ICP_HFI_NUMBER_OF_QS 3 +#define ICP_HFI_NUMBER_OF_ACTIVE_QS 3 +#define ICP_HFI_QTBL_OFFSET 0 +#define ICP_HFI_VAR_SIZE_PKT 0 +#define ICP_HFI_MAX_MSG_SIZE_IN_WORDS 128 + + +/* Queue Header type masks. Use these to access bitfields in qhdr_type */ +#define HFI_MASK_QHDR_TX_TYPE 0xFF000000 +#define HFI_MASK_QHDR_RX_TYPE 0x00FF0000 +#define HFI_MASK_QHDR_PRI_TYPE 0x0000FF00 +#define HFI_MASK_QHDR_Q_ID_TYPE 0x000000FF + + +#define TX_EVENT_DRIVEN_MODE_1 0 +#define RX_EVENT_DRIVEN_MODE_1 0 +#define TX_EVENT_DRIVEN_MODE_2 0x01000000 +#define RX_EVENT_DRIVEN_MODE_2 0x00010000 +#define TX_EVENT_POLL_MODE_2 0x02000000 +#define RX_EVENT_POLL_MODE_2 0x00020000 +#define U32_OFFSET 0x1 +#define BYTE_WORD_SHIFT 2 + +/** + * @INVALID: Invalid state + * @HFI_DEINIT: HFI is not initialized yet + * @HFI_INIT: HFI is initialized + * @HFI_READY: HFI is ready to send/receive commands/messages + */ +enum hfi_state { + HFI_DEINIT, + HFI_INIT, + HFI_READY +}; + +/** + * @RESET: init success + * @SET: init failed + */ +enum reg_settings { + RESET, + SET, + SET_WM = 1024 +}; + +/** + * @INTR_DISABLE: Disable interrupt + * @INTR_ENABLE: Enable interrupt + */ +enum intr_status { + INTR_DISABLE, + INTR_ENABLE +}; + +/** + * @ICP_INIT_RESP_RESET: reset init state + * @ICP_INIT_RESP_SUCCESS: init success + * @ICP_INIT_RESP_FAILED: init failed + */ +enum host_init_resp { + ICP_INIT_RESP_RESET, + ICP_INIT_RESP_SUCCESS, + ICP_INIT_RESP_FAILED +}; + +/** + * @ICP_INIT_REQUEST_RESET: reset init request + * @ICP_INIT_REQUEST_SET: set init request + */ +enum host_init_request { + ICP_INIT_REQUEST_RESET, + ICP_INIT_REQUEST_SET +}; + +/** + * @QHDR_INACTIVE: Queue is inactive + * @QHDR_ACTIVE: Queue is active + */ +enum qhdr_status { + QHDR_INACTIVE, + QHDR_ACTIVE +}; + +/** + * @INTR_MODE: event driven mode 1, each send and receive generates interrupt + * @WM_MODE: event driven mode 2, interrupts based on watermark mechanism + * @POLL_MODE: poll method + */ +enum qhdr_event_drv_type { + INTR_MODE, + WM_MODE, + POLL_MODE +}; + +/** + * @TX_INT: event driven mode 1, each send and receive generates interrupt + * @TX_INT_WM: event driven mode 2, interrupts based on watermark mechanism + * @TX_POLL: poll method + * @ICP_QHDR_TX_TYPE_MASK defines position in qhdr_type + */ +enum qhdr_tx_type { + TX_INT, + TX_INT_WM, + TX_POLL +}; + +/** + * @RX_INT: event driven mode 1, each send and receive generates interrupt + * @RX_INT_WM: event driven mode 2, interrupts based on watermark mechanism + * @RX_POLL: poll method + * @ICP_QHDR_RX_TYPE_MASK defines position in qhdr_type + */ +enum qhdr_rx_type { + RX_INT, + RX_INT_WM, + RX_POLL +}; + +/** + * @Q_CMD: Host to FW command queue + * @Q_MSG: FW to Host message queue + * @Q_DEBUG: FW to Host debug queue + * @ICP_QHDR_Q_ID_MASK defines position in qhdr_type + */ +enum qhdr_q_id { + Q_CMD, + Q_MSG, + Q_DBG +}; + +/** + * struct hfi_qtbl_hdr + * @qtbl_version: Queue table version number + * Higher 16 bits: Major version + * Lower 16 bits: Minor version + * @qtbl_size: Queue table size from version to last parametr in qhdr entry + * @qtbl_qhdr0_offset: Offset to the start of first qhdr + * @qtbl_qhdr_size: Queue header size in bytes + * @qtbl_num_q: Total number of queues in Queue table + * @qtbl_num_active_q: Total number of active queues + */ +struct hfi_qtbl_hdr { + uint32_t qtbl_version; + uint32_t qtbl_size; + uint32_t qtbl_qhdr0_offset; + uint32_t qtbl_qhdr_size; + uint32_t qtbl_num_q; + uint32_t qtbl_num_active_q; +} __packed; + +/** + * struct hfi_q_hdr + * @qhdr_status: Queue status, qhdr_state define possible status + * @qhdr_start_addr: Queue start address in non cached memory + * @qhdr_type: qhdr_tx, qhdr_rx, qhdr_q_id and priority defines qhdr type + * @qhdr_q_size: Queue size + * Number of queue packets if qhdr_pkt_size is non-zero + * Queue size in bytes if qhdr_pkt_size is zero + * @qhdr_pkt_size: Size of queue packet entries + * 0x0: variable queue packet size + * non zero: size of queue packet entry, fixed + * @qhdr_pkt_drop_cnt: Number of packets dropped by sender + * @qhdr_rx_wm: Receiver watermark, applicable in event driven mode + * @qhdr_tx_wm: Sender watermark, applicable in event driven mode + * @qhdr_rx_req: Receiver sets this bit if queue is empty + * @qhdr_tx_req: Sender sets this bit if queue is full + * @qhdr_rx_irq_status: Receiver sets this bit and triggers an interrupt to + * the sender after packets are dequeued. Sender clears this bit + * @qhdr_tx_irq_status: Sender sets this bit and triggers an interrupt to + * the receiver after packets are queued. Receiver clears this bit + * @qhdr_read_idx: Read index + * @qhdr_write_idx: Write index + */ +struct hfi_q_hdr { + uint32_t dummy[15]; + uint32_t qhdr_status; + uint32_t dummy1[15]; + uint32_t qhdr_start_addr; + uint32_t dummy2[15]; + uint32_t qhdr_type; + uint32_t dummy3[15]; + uint32_t qhdr_q_size; + uint32_t dummy4[15]; + uint32_t qhdr_pkt_size; + uint32_t dummy5[15]; + uint32_t qhdr_pkt_drop_cnt; + uint32_t dummy6[15]; + uint32_t qhdr_rx_wm; + uint32_t dummy7[15]; + uint32_t qhdr_tx_wm; + uint32_t dummy8[15]; + uint32_t qhdr_rx_req; + uint32_t dummy9[15]; + uint32_t qhdr_tx_req; + uint32_t dummy10[15]; + uint32_t qhdr_rx_irq_status; + uint32_t dummy11[15]; + uint32_t qhdr_tx_irq_status; + uint32_t dummy12[15]; + uint32_t qhdr_read_idx; + uint32_t dummy13[15]; + uint32_t qhdr_write_idx; + uint32_t dummy14[15]; +}; + +/** + * struct hfi_q_tbl + * @q_tbl_hdr: Queue table header + * @q_hdr: Queue header info, it holds info of cmd, msg and debug queues + */ +struct hfi_qtbl { + struct hfi_qtbl_hdr q_tbl_hdr; + struct hfi_q_hdr q_hdr[MAX_ICP_HFI_QUEUES]; +}; + +/** + * struct hfi_info + * @map: Hfi shared memory info + * @smem_size: Shared memory size + * @uncachedheap_size: uncached heap size + * @msgpacket_buf: message buffer + * @hfi_state: State machine for hfi + * @cmd_q_lock: Lock for command queue + * @cmd_q_state: State of command queue + * @mutex msg_q_lock: Lock for message queue + * @msg_q_state: State of message queue + * @csr_base: CSR base address + */ +struct hfi_info { + struct hfi_mem_info map; + uint32_t smem_size; + uint32_t uncachedheap_size; + uint32_t msgpacket_buf[ICP_HFI_MAX_MSG_SIZE_IN_WORDS]; + uint8_t hfi_state; + struct mutex cmd_q_lock; + bool cmd_q_state; + struct mutex msg_q_lock; + bool msg_q_state; + void __iomem *csr_base; +}; + +#endif /* _CAM_HFI_REG_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_session_defs.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_session_defs.h new file mode 100644 index 000000000000..7b2cb8b1a412 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_session_defs.h @@ -0,0 +1,530 @@ +/* Copyright (c) 2017-2018, 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 _CAM_HFI_SESSION_DEFS_H +#define _CAM_HFI_SESSION_DEFS_H + +#include + +#define HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO 0x1 +#define HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS 0x2 +#define HFI_IPEBPS_CMD_OPCODE_BPS_ABORT 0x3 +#define HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY 0x4 + +#define HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO 0x5 +#define HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS 0x6 +#define HFI_IPEBPS_CMD_OPCODE_IPE_ABORT 0x7 +#define HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY 0x8 + +#define HFI_IPEBPS_CMD_OPCODE_BPS_WAIT_FOR_IPE 0x9 +#define HFI_IPEBPS_CMD_OPCODE_BPS_WAIT_FOR_BPS 0xa +#define HFI_IPEBPS_CMD_OPCODE_IPE_WAIT_FOR_BPS 0xb +#define HFI_IPEBPS_CMD_OPCODE_IPE_WAIT_FOR_IPE 0xc + +#define HFI_IPEBPS_HANDLE_TYPE_BPS 0x1 +#define HFI_IPEBPS_HANDLE_TYPE_IPE_RT 0x2 +#define HFI_IPEBPS_HANDLE_TYPE_IPE_NON_RT 0x3 + +/** + * struct abort_data + * @num_req_ids: Number of req ids + * @num_req_id: point to specific req id + * + * create abort data + */ +struct abort_data { + uint32_t num_req_ids; + uint32_t num_req_id[1]; +}; + +/** + * struct hfi_cmd_data + * @abort: abort data + * @user data: user supplied argument + * + * create session abort data + */ +struct hfi_cmd_abort { + struct abort_data abort; + uint64_t user_data; +} __packed; + +/** + * struct hfi_cmd_abort_destroy + * @user_data: user supplied data + * + * IPE/BPS destroy/abort command + * @HFI_IPEBPS_CMD_OPCODE_IPE_ABORT + * @HFI_IPEBPS_CMD_OPCODE_BPS_ABORT + * @HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY + * @HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY + */ +struct hfi_cmd_abort_destroy { + uint64_t user_data; +} __packed; + +/** + * struct hfi_cmd_chaining_ops + * @wait_hdl: current session handle waits on wait_hdl to complete operation + * @user_data: user supplied argument + * + * this structure for chaining opcodes + * BPS_WAITS_FOR_IPE + * BPS_WAITS_FOR_BPS + * IPE_WAITS_FOR_BPS + * IPE_WAITS_FOR_IPE + */ +struct hfi_cmd_chaining_ops { + uint32_t wait_hdl; + uint64_t user_data; +} __packed; + +/** + * struct hfi_cmd_create_handle + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @handle_type: IPE/BPS firmware session handle type + * @user_data1: caller provided data1 + * @user_data2: caller provided data2 + * + * create firmware session handle + */ +struct hfi_cmd_create_handle { + uint32_t size; + uint32_t pkt_type; + uint32_t handle_type; + uint64_t user_data1; + uint64_t user_data2; +} __packed; + +/** + * struct hfi_cmd_ipebps_async + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @opcode: opcode for IPE/BPS async operation + * CONFIG_IO: configures I/O for IPE/BPS handle + * FRAME_PROCESS: image frame to be processed by IPE/BPS + * ABORT: abort all processing frames of IPE/BPS handle + * DESTROY: destroy earlier created IPE/BPS handle + * BPS_WAITS_FOR_IPE: sync for BPS to wait for IPE + * BPS_WAITS_FOR_BPS: sync for BPS to wait for BPS + * IPE_WAITS_FOR_IPE: sync for IPE to wait for IPE + * IPE_WAITS_FOR_BPS: sync for IPE to wait for BPS + * @num_fw_handles: number of IPE/BPS firmware handles in fw_handles array + * @fw_handles: IPE/BPS handles array + * @payload: command payload for IPE/BPS opcodes + * @direct: points to actual payload + * @indirect: points to address of payload + * + * sends async command to the earlier created IPE or BPS handle + * for asynchronous operation. + */ +struct hfi_cmd_ipebps_async { + uint32_t size; + uint32_t pkt_type; + uint32_t opcode; + uint64_t user_data1; + uint64_t user_data2; + uint32_t num_fw_handles; + uint32_t fw_handles[1]; + union { + uint32_t direct[1]; + uint32_t indirect; + } payload; +} __packed; + +/** + * struct hfi_msg_create_handle_ack + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @err_type: error code + * @fw_handle: output param for IPE/BPS handle + * @user_data1: user provided data1 + * @user_data2: user provided data2 + * + * ack for create handle command of IPE/BPS + * @HFI_MSG_IPEBPS_CREATE_HANDLE_ACK + */ +struct hfi_msg_create_handle_ack { + uint32_t size; + uint32_t pkt_type; + uint32_t err_type; + uint32_t fw_handle; + uint64_t user_data1; + uint64_t user_data2; +} __packed; + +/** + * struct hfi_msg_ipebps_async + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @opcode: opcode of IPE/BPS async operation + * @user_data1: user provided data1 + * @user_data2: user provided data2 + * @err_type: error code + * @msg_data: IPE/BPS async done message data + * + * result of IPE/BPS async command + * @HFI_MSG_IPEBPS_ASYNC_COMMAND_ACK + */ +struct hfi_msg_ipebps_async_ack { + uint32_t size; + uint32_t pkt_type; + uint32_t opcode; + uint64_t user_data1; + uint64_t user_data2; + uint32_t err_type; + uint32_t msg_data[1]; +} __packed; + +/** + * struct hfi_msg_frame_process_done + * @result: result of frame process command + * @scratch_buffer_address: address of scratch buffer + */ +struct hfi_msg_frame_process_done { + uint32_t result; + uint32_t scratch_buffer_address; +}; + +/** + * struct hfi_msg_chaining_op + * @status: return status + * @user_data: user data provided as part of chaining ops + * + * IPE/BPS wait response + */ +struct hfi_msg_chaining_op { + uint32_t status; + uint64_t user_data; +} __packed; + +/** + * struct hfi_msg_abort_destroy + * @status: return status + * @user_data: user data provided as part of abort/destroy ops + * + * IPE/BPS abort/destroy response + */ +struct hfi_msg_abort_destroy { + uint32_t status; + uint64_t user_data; +} __packed; + +#define MAX_NUM_OF_IMAGE_PLANES 2 +#define MAX_HFR_GROUP 16 + +enum hfi_ipe_io_images { + IPE_INPUT_IMAGE_FULL, + IPE_INPUT_IMAGE_DS4, + IPE_INPUT_IMAGE_DS16, + IPE_INPUT_IMAGE_DS64, + IPE_INPUT_IMAGE_FULL_REF, + IPE_INPUT_IMAGE_DS4_REF, + IPE_INPUT_IMAGE_DS16_REF, + IPE_INPUT_IMAGE_DS64_REF, + IPE_OUTPUT_IMAGE_DISPLAY, + IPE_OUTPUT_IMAGE_VIDEO, + IPE_OUTPUT_IMAGE_FULL_REF, + IPE_OUTPUT_IMAGE_DS4_REF, + IPE_OUTPUT_IMAGE_DS16_REF, + IPE_OUTPUT_IMAGE_DS64_REF, + IPE_INPUT_IMAGE_FIRST = IPE_INPUT_IMAGE_FULL, + IPE_INPUT_IMAGE_LAST = IPE_INPUT_IMAGE_DS64_REF, + IPE_OUTPUT_IMAGE_FIRST = IPE_OUTPUT_IMAGE_DISPLAY, + IPE_OUTPUT_IMAGE_LAST = IPE_OUTPUT_IMAGE_DS64_REF, + IPE_IO_IMAGES_MAX +}; + +enum bps_io_images { + BPS_INPUT_IMAGE, + BPS_OUTPUT_IMAGE_FULL, + BPS_OUTPUT_IMAGE_DS4, + BPS_OUTPUT_IMAGE_DS16, + BPS_OUTPUT_IMAGE_DS64, + BPS_OUTPUT_IMAGE_STATS_BG, + BPS_OUTPUT_IMAGE_STATS_BHIST, + BPS_OUTPUT_IMAGE_REG1, + BPS_OUTPUT_IMAGE_REG2, + BPS_OUTPUT_IMAGE_FIRST = BPS_OUTPUT_IMAGE_FULL, + BPS_OUTPUT_IMAGE_LAST = BPS_OUTPUT_IMAGE_REG2, + BPS_IO_IMAGES_MAX +}; + +struct frame_buffer { + uint32_t buffer_ptr[MAX_NUM_OF_IMAGE_PLANES]; + uint32_t meta_buffer_ptr[MAX_NUM_OF_IMAGE_PLANES]; +} __packed; + +struct bps_frame_process_data { + struct frame_buffer buffers[BPS_IO_IMAGES_MAX]; + uint32_t max_num_cores; + uint32_t target_time; + uint32_t ubwc_stats_buffer_addr; + uint32_t ubwc_stats_buffer_size; + uint32_t cdm_buffer_addr; + uint32_t cdm_buffer_size; + uint32_t iq_settings_addr; + uint32_t strip_lib_out_addr; + uint32_t cdm_prog_addr; + uint32_t request_id; +}; + +enum hfi_ipe_image_format { + IMAGE_FORMAT_INVALID, + IMAGE_FORMAT_MIPI_8, + IMAGE_FORMAT_MIPI_10, + IMAGE_FORMAT_MIPI_12, + IMAGE_FORMAT_MIPI_14, + IMAGE_FORMAT_BAYER_8, + IMAGE_FORMAT_BAYER_10, + IMAGE_FORMAT_BAYER_12, + IMAGE_FORMAT_BAYER_14, + IMAGE_FORMAT_PDI_10, + IMAGE_FORMAT_PD_10, + IMAGE_FORMAT_PD_8, + IMAGE_FORMAT_INDICATIONS, + IMAGE_FORMAT_REFINEMENT, + IMAGE_FORMAT_UBWC_TP_10, + IMAGE_FORMAT_UBWC_NV_12, + IMAGE_FORMAT_UBWC_NV12_4R, + IMAGE_FORMAT_UBWC_P010, + IMAGE_FORMAT_LINEAR_TP_10, + IMAGE_FORMAT_LINEAR_P010, + IMAGE_FORMAT_LINEAR_NV12, + IMAGE_FORMAT_LINEAR_PLAIN_16, + IMAGE_FORMAT_YUV422_8, + IMAGE_FORMAT_YUV422_10, + IMAGE_FORMAT_STATISTICS_BAYER_GRID, + IMAGE_FORMAT_STATISTICS_BAYER_HISTOGRAM, + IMAGE_FORMAT_MAX +}; + +enum hfi_ipe_plane_format { + PLANE_FORMAT_INVALID = 0, + PLANE_FORMAT_MIPI_8, + PLANE_FORMAT_MIPI_10, + PLANE_FORMAT_MIPI_12, + PLANE_FORMAT_MIPI_14, + PLANE_FORMAT_BAYER_8, + PLANE_FORMAT_BAYER_10, + PLANE_FORMAT_BAYER_12, + PLANE_FORMAT_BAYER_14, + PLANE_FORMAT_PDI_10, + PLANE_FORMAT_PD_10, + PLANE_FORMAT_PD_8, + PLANE_FORMAT_INDICATIONS, + PLANE_FORMAT_REFINEMENT, + PLANE_FORMAT_UBWC_TP_10_Y, + PLANE_FORMAT_UBWC_TP_10_C, + PLANE_FORMAT_UBWC_NV_12_Y, + PLANE_FORMAT_UBWC_NV_12_C, + PLANE_FORMAT_UBWC_NV_12_4R_Y, + PLANE_FORMAT_UBWC_NV_12_4R_C, + PLANE_FORMAT_UBWC_P010_Y, + PLANE_FORMAT_UBWC_P010_C, + PLANE_FORMAT_LINEAR_TP_10_Y, + PLANE_FORMAT_LINEAR_TP_10_C, + PLANE_FORMAT_LINEAR_P010_Y, + PLANE_FORMAT_LINEAR_P010_C, + PLANE_FORMAT_LINEAR_NV12_Y, + PLANE_FORMAT_LINEAR_NV12_C, + PLANE_FORMAT_LINEAR_PLAIN_16_Y, + PLANE_FORMAT_LINEAR_PLAIN_16_C, + PLANE_FORMAT_YUV422_8, + PLANE_FORMAT_YUV422_10, + PLANE_FORMAT_STATISTICS_BAYER_GRID, + PLANE_FORMAT_STATISTICS_BAYER_HISTOGRAM, + PLANE_FORMAT_MAX +}; + +enum hfi_ipe_bayer_pixel_order { + FIRST_PIXEL_R, + FIRST_PIXEL_GR, + FIRST_PIXEL_B, + FIRST_PIXEL_GB, + FIRST_PIXEL_MAX +}; + +enum hfi_ipe_pixel_pack_alignment { + PIXEL_LSB_ALIGNED, + PIXEL_MSB_ALIGNED, +}; + +enum hfi_ipe_yuv_422_order { + PIXEL_ORDER_Y_U_Y_V, + PIXEL_ORDER_Y_V_Y_U, + PIXEL_ORDER_U_Y_V_Y, + PIXEL_ORDER_V_Y_U_Y, + PIXEL_ORDER_YUV422_MAX +}; + +enum ubwc_write_client { + IPE_WR_CLIENT_0 = 0, + IPE_WR_CLIENT_1, + IPE_WR_CLIENT_5, + IPE_WR_CLIENT_6, + IPE_WR_CLIENT_7, + IPE_WR_CLIENT_8, + IPE_WR_CLIENT_MAX +}; + +/** + * struct image_info + * @format: image format + * @img_width: image width + * @img_height: image height + * @bayer_order: pixel order + * @pix_align: alignment + * @yuv422_order: YUV order + * @byte_swap: byte swap + */ +struct image_info { + enum hfi_ipe_image_format format; + uint32_t img_width; + uint32_t img_height; + enum hfi_ipe_bayer_pixel_order bayer_order; + enum hfi_ipe_pixel_pack_alignment pix_align; + enum hfi_ipe_yuv_422_order yuv422_order; + uint32_t byte_swap; +} __packed; + +/** + * struct buffer_layout + * @buf_stride: buffer stride + * @buf_height: buffer height + */ +struct buffer_layout { + uint32_t buf_stride; + uint32_t buf_height; +} __packed; + +/** + * struct image_desc + * @info: image info + * @buf_layout: buffer layout + * @meta_buf_layout: meta buffer layout + */ +struct image_desc { + struct image_info info; + struct buffer_layout buf_layout[MAX_NUM_OF_IMAGE_PLANES]; + struct buffer_layout meta_buf_layout[MAX_NUM_OF_IMAGE_PLANES]; +} __packed; + +struct ica_stab_coeff { + uint32_t coeffs[8]; +} __packed; + +struct ica_stab_params { + uint32_t mode; + struct ica_stab_coeff transforms[3]; +} __packed; + +struct frame_set { + struct frame_buffer buffers[IPE_IO_IMAGES_MAX]; + struct ica_stab_params ica_params; + uint32_t cdm_ica1_addr; + uint32_t cdm_ica2_addr; +} __packed; + +struct ipe_frame_process_data { + uint32_t strip_lib_out_addr; + uint32_t iq_settings_addr; + uint32_t scratch_buffer_addr; + uint32_t scratch_buffer_size; + uint32_t ubwc_stats_buffer_addr; + uint32_t ubwc_stats_buffer_size; + uint32_t cdm_buffer_addr; + uint32_t cdm_buffer_size; + uint32_t max_num_cores; + uint32_t target_time; + uint32_t cdm_prog_base; + uint32_t cdm_pre_ltm; + uint32_t cdm_post_ltm; + uint32_t cdm_anr_full_pass; + uint32_t cdm_anr_ds4; + uint32_t cdm_anr_ds16; + uint32_t cdm_anr_ds64; + uint32_t cdm_tf_full_pass; + uint32_t cdm_tf_ds4; + uint32_t cdm_tf_ds16; + uint32_t cdm_tf_ds64; + uint32_t request_id; + uint32_t frames_in_batch; + struct frame_set framesets[MAX_HFR_GROUP]; +} __packed; + +/** + * struct hfi_cmd_ipe_config + * @images: images descreptions + * @user_data: user supplied data + * + * payload for IPE async command + */ +struct hfi_cmd_ipe_config { + struct image_desc images[IPE_IO_IMAGES_MAX]; + uint64_t user_data; +} __packed; + +/** + * struct frame_buffers + * @buf_ptr: buffer pointers for all planes + * @meta_buf_ptr: meta buffer pointers for all planes + */ +struct frame_buffers { + uint32_t buf_ptr[MAX_NUM_OF_IMAGE_PLANES]; + uint32_t meta_buf_ptr[MAX_NUM_OF_IMAGE_PLANES]; +} __packed; + +/** + * struct hfi_msg_ipe_config + * @rc: result of ipe config command + * @scratch_mem_size: scratch mem size for a config + * @user_data: user data + */ +struct hfi_msg_ipe_config { + uint32_t rc; + uint32_t scratch_mem_size; + uint64_t user_data; +} __packed; + +/** + * struct hfi_msg_bps_common + * @rc: result of ipe config command + * @user_data: user data + */ +struct hfi_msg_bps_common { + uint32_t rc; + uint64_t user_data; +} __packed; + +/** + * struct ipe_bps_destroy + * @user_data: user data + */ +struct ipe_bps_destroy { + uint64_t userdata; +}; + +/** + * struct hfi_msg_ipe_frame_process + * @status: result of ipe frame process command + * @scratch_buf_addr: address of scratch buffer + * @user_data: user data + */ +struct hfi_msg_ipe_frame_process { + uint32_t status; + uint32_t scratch_buf_addr; + uint64_t user_data; +} __packed; + +#endif /* _CAM_HFI_SESSION_DEFS_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_sys_defs.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_sys_defs.h new file mode 100644 index 000000000000..91190b6fa420 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/hfi_sys_defs.h @@ -0,0 +1,522 @@ +/* Copyright (c) 2017-2018, 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 _HFI_DEFS_H_ +#define _HFI_DEFS_H_ + +#include + +/* + * Following base acts as common starting points + * for all enumerations. + */ +#define HFI_COMMON_BASE 0x0 + +/* HFI Domain base offset for commands and messages */ +#define HFI_DOMAIN_SHFT (24) +#define HFI_DOMAIN_BMSK (0x7 << HFI_DOMAIN_SHFT) +#define HFI_DOMAIN_BASE_ICP (0x0 << HFI_DOMAIN_SHFT) +#define HFI_DOMAIN_BASE_IPE_BPS (0x1 << HFI_DOMAIN_SHFT) +#define HFI_DOMAIN_BASE_CDM (0x2 << HFI_DOMAIN_SHFT) +#define HFI_DOMAIN_BASE_DBG (0x3 << HFI_DOMAIN_SHFT) + +/* Command base offset for commands */ +#define HFI_CMD_START_OFFSET 0x10000 + +/* Command base offset for messages */ +#define HFI_MSG_START_OFFSET 0x20000 + +/* System Level Error types */ +#define HFI_ERR_SYS_NONE (HFI_COMMON_BASE) +#define HFI_ERR_SYS_FATAL (HFI_COMMON_BASE + 0x1) +#define HFI_ERR_SYS_VERSION_MISMATCH (HFI_COMMON_BASE + 0x2) +#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x3) +#define HFI_ERR_SYS_UNSUPPORT_CMD (HFI_COMMON_BASE + 0x4) +#define HFI_ERR_SYS_CMDFAILED (HFI_COMMON_BASE + 0x5) +#define HFI_ERR_SYS_CMDSIZE (HFI_COMMON_BASE + 0x6) + +/* System Level Event types */ +#define HFI_EVENT_SYS_ERROR (HFI_COMMON_BASE + 0x1) +#define HFI_EVENT_ICP_ERROR (HFI_COMMON_BASE + 0x2) +#define HFI_EVENT_IPE_BPS_ERROR (HFI_COMMON_BASE + 0x3) +#define HFI_EVENT_CDM_ERROR (HFI_COMMON_BASE + 0x4) +#define HFI_EVENT_DBG_ERROR (HFI_COMMON_BASE + 0x5) + +/* Core level start Ranges for errors */ +#define HFI_ERR_ICP_START (HFI_COMMON_BASE + 0x64) +#define HFI_ERR_IPE_BPS_START (HFI_ERR_ICP_START + 0x64) +#define HFI_ERR_CDM_START (HFI_ERR_IPE_BPS_START + 0x64) +#define HFI_ERR_DBG_START (HFI_ERR_CDM_START + 0x64) + +/*ICP Core level error messages */ +#define HFI_ERR_NO_RES (HFI_ERR_ICP_START + 0x1) +#define HFI_ERR_UNSUPPORTED_RES (HFI_ERR_ICP_START + 0x2) +#define HFI_ERR_UNSUPPORTED_PROP (HFI_ERR_ICP_START + 0x3) +#define HFI_ERR_INIT_EXPECTED (HFI_ERR_ICP_START + 0x4) +#define HFI_ERR_INIT_IGNORED (HFI_ERR_ICP_START + 0x5) + +/* System level commands */ +#define HFI_CMD_COMMON_START \ + (HFI_DOMAIN_BASE_ICP + HFI_CMD_START_OFFSET + 0x0) +#define HFI_CMD_SYS_INIT (HFI_CMD_COMMON_START + 0x1) +#define HFI_CMD_SYS_PC_PREP (HFI_CMD_COMMON_START + 0x2) +#define HFI_CMD_SYS_SET_PROPERTY (HFI_CMD_COMMON_START + 0x3) +#define HFI_CMD_SYS_GET_PROPERTY (HFI_CMD_COMMON_START + 0x4) +#define HFI_CMD_SYS_PING (HFI_CMD_COMMON_START + 0x5) +#define HFI_CMD_SYS_RESET (HFI_CMD_COMMON_START + 0x6) + +/* Core level commands */ +/* IPE/BPS core Commands */ +#define HFI_CMD_IPE_BPS_COMMON_START \ + (HFI_DOMAIN_BASE_IPE_BPS + HFI_CMD_START_OFFSET + 0x0) +#define HFI_CMD_IPEBPS_CREATE_HANDLE \ + (HFI_CMD_IPE_BPS_COMMON_START + 0x8) +#define HFI_CMD_IPEBPS_ASYNC_COMMAND_DIRECT \ + (HFI_CMD_IPE_BPS_COMMON_START + 0xa) +#define HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT \ + (HFI_CMD_IPE_BPS_COMMON_START + 0xe) + +/* CDM core Commands */ +#define HFI_CMD_CDM_COMMON_START \ + (HFI_DOMAIN_BASE_CDM + HFI_CMD_START_OFFSET + 0x0) +#define HFI_CMD_CDM_TEST_START (HFI_CMD_CDM_COMMON_START + 0x800) +#define HFI_CMD_CDM_END (HFI_CMD_CDM_COMMON_START + 0xFFF) + +/* Debug/Test Commands */ +#define HFI_CMD_DBG_COMMON_START \ + (HFI_DOMAIN_BASE_DBG + HFI_CMD_START_OFFSET + 0x0) +#define HFI_CMD_DBG_TEST_START (HFI_CMD_DBG_COMMON_START + 0x800) +#define HFI_CMD_DBG_END (HFI_CMD_DBG_COMMON_START + 0xFFF) + +/* System level messages */ +#define HFI_MSG_ICP_COMMON_START \ + (HFI_DOMAIN_BASE_ICP + HFI_MSG_START_OFFSET + 0x0) +#define HFI_MSG_SYS_INIT_DONE (HFI_MSG_ICP_COMMON_START + 0x1) +#define HFI_MSG_SYS_PC_PREP_DONE (HFI_MSG_ICP_COMMON_START + 0x2) +#define HFI_MSG_SYS_DEBUG (HFI_MSG_ICP_COMMON_START + 0x3) +#define HFI_MSG_SYS_IDLE (HFI_MSG_ICP_COMMON_START + 0x4) +#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_ICP_COMMON_START + 0x5) +#define HFI_MSG_SYS_PING_ACK (HFI_MSG_ICP_COMMON_START + 0x6) +#define HFI_MSG_SYS_RESET_ACK (HFI_MSG_ICP_COMMON_START + 0x7) +#define HFI_MSG_EVENT_NOTIFY (HFI_MSG_ICP_COMMON_START + 0x8) + +/* Core level Messages */ +/* IPE/BPS core Messages */ +#define HFI_MSG_IPE_BPS_COMMON_START \ + (HFI_DOMAIN_BASE_IPE_BPS + HFI_MSG_START_OFFSET + 0x0) +#define HFI_MSG_IPEBPS_CREATE_HANDLE_ACK \ + (HFI_MSG_IPE_BPS_COMMON_START + 0x08) +#define HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK \ + (HFI_MSG_IPE_BPS_COMMON_START + 0x0a) +#define HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK \ + (HFI_MSG_IPE_BPS_COMMON_START + 0x0e) +#define HFI_MSG_IPE_BPS_TEST_START \ + (HFI_MSG_IPE_BPS_COMMON_START + 0x800) +#define HFI_MSG_IPE_BPS_END \ + (HFI_MSG_IPE_BPS_COMMON_START + 0xFFF) + +/* CDM core Messages */ +#define HFI_MSG_CDM_COMMON_START \ + (HFI_DOMAIN_BASE_CDM + HFI_MSG_START_OFFSET + 0x0) +#define HFI_MSG_PRI_CDM_PAYLOAD_ACK (HFI_MSG_CDM_COMMON_START + 0xa) +#define HFI_MSG_PRI_LLD_PAYLOAD_ACK (HFI_MSG_CDM_COMMON_START + 0xb) +#define HFI_MSG_CDM_TEST_START (HFI_MSG_CDM_COMMON_START + 0x800) +#define HFI_MSG_CDM_END (HFI_MSG_CDM_COMMON_START + 0xFFF) + +/* core level test command ranges */ +/* ICP core level test command range */ +#define HFI_CMD_ICP_TEST_START (HFI_CMD_ICP_COMMON_START + 0x800) +#define HFI_CMD_ICP_END (HFI_CMD_ICP_COMMON_START + 0xFFF) + +/* IPE/BPS core level test command range */ +#define HFI_CMD_IPE_BPS_TEST_START \ + (HFI_CMD_IPE_BPS_COMMON_START + 0x800) +#define HFI_CMD_IPE_BPS_END (HFI_CMD_IPE_BPS_COMMON_START + 0xFFF) + +/* ICP core level test message range */ +#define HFI_MSG_ICP_TEST_START (HFI_MSG_ICP_COMMON_START + 0x800) +#define HFI_MSG_ICP_END (HFI_MSG_ICP_COMMON_START + 0xFFF) + +/* ICP core level Debug test message range */ +#define HFI_MSG_DBG_COMMON_START \ + (HFI_DOMAIN_BASE_DBG + 0x0) +#define HFI_MSG_DBG_TEST_START (HFI_MSG_DBG_COMMON_START + 0x800) +#define HFI_MSG_DBG_END (HFI_MSG_DBG_COMMON_START + 0xFFF) + +/* System level property base offset */ +#define HFI_PROPERTY_ICP_COMMON_START (HFI_DOMAIN_BASE_ICP + 0x0) + +#define HFI_PROP_SYS_DEBUG_CFG (HFI_PROPERTY_ICP_COMMON_START + 0x1) +#define HFI_PROP_SYS_UBWC_CFG (HFI_PROPERTY_ICP_COMMON_START + 0x2) +#define HFI_PROP_SYS_IMAGE_VER (HFI_PROPERTY_ICP_COMMON_START + 0x3) +#define HFI_PROP_SYS_SUPPORTED (HFI_PROPERTY_ICP_COMMON_START + 0x4) +#define HFI_PROP_SYS_IPEBPS_PC (HFI_PROPERTY_ICP_COMMON_START + 0x5) + +/* Capabilities reported at sys init */ +#define HFI_CAPS_PLACEHOLDER_1 (HFI_COMMON_BASE + 0x1) +#define HFI_CAPS_PLACEHOLDER_2 (HFI_COMMON_BASE + 0x2) + +/* Section describes different debug levels (HFI_DEBUG_MSG_X) + * available for debug messages from FW + */ +#define HFI_DEBUG_MSG_LOW 0x00000001 +#define HFI_DEBUG_MSG_MEDIUM 0x00000002 +#define HFI_DEBUG_MSG_HIGH 0x00000004 +#define HFI_DEBUG_MSG_ERROR 0x00000008 +#define HFI_DEBUG_MSG_FATAL 0x00000010 +/* Messages containing performance data */ +#define HFI_DEBUG_MSG_PERF 0x00000020 +/* Disable ARM9 WFI in low power mode. */ +#define HFI_DEBUG_CFG_WFI 0x01000000 +/* Disable ARM9 watchdog. */ +#define HFI_DEBUG_CFG_ARM9WD 0x10000000 + +/* Debug Msg Communication types: + * Section describes different modes (HFI_DEBUG_MODE_X) + * available to communicate the debug messages + */ + /* Debug message output through the interface debug queue. */ +#define HFI_DEBUG_MODE_QUEUE 0x00000001 + /* Debug message output through QDSS. */ +#define HFI_DEBUG_MODE_QDSS 0x00000002 + /* Number of debug modes available. */ +#define NUM_HFI_DEBUG_MODE 0x00000002 + +#define HFI_DEBUG_MSG_LOW 0x00000001 +#define HFI_DEBUG_MSG_MEDIUM 0x00000002 +#define HFI_DEBUG_MSG_HIGH 0x00000004 +#define HFI_DEBUG_MSG_ERROR 0x00000008 +#define HFI_DEBUG_MSG_FATAL 0x00000010 +#define HFI_DEBUG_MSG_PERF 0x00000020 +#define HFI_DEBUG_CFG_WFI 0x01000000 +#define HFI_DEBUG_CFG_ARM9WD 0x10000000 + +#define HFI_DEV_VERSION_MAX 0x5 + +/** + * start of sys command packet types + * These commands are used to get system level information + * from firmware + */ + +/** + * struct hfi_caps_support + * payload to report caps through HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED + * @type: capability type + * @min: minimum supported value for the capability + * @max: maximum supported value for the capability + * @step_size: supported steps between min-max + */ +struct hfi_caps_support { + uint32_t type; + uint32_t min; + uint32_t max; + uint32_t step_size; +} __packed; + +/** + * struct hfi_caps_support_info + * capability report through HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED + * @num_caps: number of capabilities listed + * @caps_data: capabilities info array + */ +struct hfi_caps_support_info { + uint32_t num_caps; + struct hfi_caps_support caps_data[1]; +} __packed; + +/** + * struct hfi_debug + * payload structure to configure HFI_PROPERTY_SYS_DEBUG_CONFIG + * @debug_config: it is a result of HFI_DEBUG_MSG_X values that + * are OR-ed together to specify the debug message types + * to otput + * @debug_mode: debug message output through debug queue/qdss + * @HFI_PROPERTY_SYS_DEBUG_CONFIG + */ +struct hfi_debug { + uint32_t debug_config; + uint32_t debug_mode; +} __packed; + +/** + * struct hfi_ipe_bps_pc + * payload structure to configure HFI_PROPERTY_SYS_IPEBPS_PC + * @enable: Flag to enable IPE, BPS interfrane power collapse + * @core_info: Core information to firmware + */ +struct hfi_ipe_bps_pc { + uint32_t enable; + uint32_t core_info; +} __packed; + +/** + * struct hfi_cmd_ubwc_cfg + * Payload structure to configure HFI_PROP_SYS_UBWC_CFG + * @ubwc_fetch_cfg: UBWC configuration for fecth + * @ubwc_write_cfg: UBWC configuration for write + */ +struct hfi_cmd_ubwc_cfg { + uint32_t ubwc_fetch_cfg; + uint32_t ubwc_write_cfg; +}; + +/** + * struct hfi_cmd_sys_init + * command to initialization of system session + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @HFI_CMD_SYS_INIT + */ +struct hfi_cmd_sys_init { + uint32_t size; + uint32_t pkt_type; +} __packed; + +/** + * struct hfi_cmd_pc_prep + * command to firmware to prepare for power collapse + * @eize: packet size in bytes + * @pkt_type: opcode of a packet + * @HFI_CMD_SYS_PC_PREP + */ +struct hfi_cmd_pc_prep { + uint32_t size; + uint32_t pkt_type; +} __packed; + +/** + * struct hfi_cmd_prop + * command to get/set properties of firmware + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @num_prop: number of properties queried/set + * @prop_data: array of property IDs being queried. size depends on num_prop + * array of property IDs and associated structure pairs in set + * @HFI_CMD_SYS_GET_PROPERTY + * @HFI_CMD_SYS_SET_PROPERTY + */ +struct hfi_cmd_prop { + uint32_t size; + uint32_t pkt_type; + uint32_t num_prop; + uint32_t prop_data[1]; +} __packed; + +/** + * struct hfi_cmd_ping_pkt + * ping command pings the firmware to confirm whether + * it is alive. + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @user_data: client data, firmware returns this data + * as part of HFI_MSG_SYS_PING_ACK + * @HFI_CMD_SYS_PING + */ +struct hfi_cmd_ping_pkt { + uint32_t size; + uint32_t pkt_type; + uint64_t user_data; +} __packed; + +/** + * struct hfi_cmd_sys_reset_pkt + * sends the reset command to FW. FW responds in the same type + * of packet. so can be used for reset_ack_pkt type also + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @user_data: client data, firmware returns this data + * as part of HFI_MSG_SYS_RESET_ACK + * @HFI_CMD_SYS_RESET + */ + +struct hfi_cmd_sys_reset_pkt { + uint32_t size; + uint32_t pkt_type; + uint64_t user_data; +} __packed; + +/* end of sys command packet types */ + +/* start of sys message packet types */ + +/** + * struct hfi_prop + * structure to report maximum supported features of firmware. + */ +struct hfi_sys_support { + uint32_t place_holder; +} __packed; + +/** + * struct hfi_supported_prop + * structure to report HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED + * for a session + * @num_prop: number of properties supported + * @prop_data: array of supported property IDs + */ +struct hfi_supported_prop { + uint32_t num_prop; + uint32_t prop_data[1]; +} __packed; + +/** + * struct hfi_image_version + * system image version + * @major: major version number + * @minor: minor version number + * @ver_name_size: size of version name + * @ver_name: image version name + */ +struct hfi_image_version { + uint32_t major; + uint32_t minor; + uint32_t ver_name_size; + uint8_t ver_name[1]; +} __packed; + +/** + * struct hfi_msg_init_done_data + * @api_ver: Firmware API version + * @dev_ver: Device version + * @num_icp_hw: Number of ICP hardware information + * @dev_hw_ver: Supported hardware version information + * @reserved: Reserved field + */ +struct hfi_msg_init_done_data { + uint32_t api_ver; + uint32_t dev_ver; + uint32_t num_icp_hw; + uint32_t dev_hw_ver[HFI_DEV_VERSION_MAX]; + uint32_t reserved; +}; + +/** + * struct hfi_msg_init_done + * system init done message from firmware. Many system level properties + * are returned with the packet + * @size: Packet size in bytes + * @pkt_type: Opcode of a packet + * @err_type: Error code associated with response + * @num_prop: Number of default capability info + * @prop_data: Array of property ids and corresponding structure pairs + */ +struct hfi_msg_init_done { + uint32_t size; + uint32_t pkt_type; + uint32_t err_type; + uint32_t num_prop; + uint32_t prop_data[1]; +} __packed; + +/** + * struct hfi_msg_pc_prep_done + * system power collapse preperation done message + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @err_type: error code associated with the response + */ +struct hfi_msg_pc_prep_done { + uint32_t size; + uint32_t pkt_type; + uint32_t err_type; +} __packed; + +/** + * struct hfi_msg_prop + * system property info from firmware + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @num_prop: number of property info structures + * @prop_data: array of property IDs and associated structure pairs + */ +struct hfi_msg_prop { + uint32_t size; + uint32_t pkt_type; + uint32_t num_prop; + uint32_t prop_data[1]; +} __packed; + +/** + * struct hfi_msg_idle + * system idle message from firmware + * @size: packet size in bytes + * @pkt_type: opcode of a packet + */ +struct hfi_msg_idle { + uint32_t size; + uint32_t pkt_type; +} __packed; + +/** + * struct hfi_msg_ping_ack + * system ping ack message + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @user_data: this data is sent as part of ping command from host + */ +struct hfi_msg_ping_ack { + uint32_t size; + uint32_t pkt_type; + uint64_t user_data; +} __packed; + +/** + * struct hfi_msg_debug + * system debug message defination + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @msg_type: debug message type + * @msg_size: size of debug message in bytes + * @timestamp_hi: most significant 32 bits of the 64 bit timestamp field. + * timestamp shall be interpreted as a signed 64-bit value + * representing microseconds. + * @timestamp_lo: least significant 32 bits of the 64 bit timestamp field. + * timestamp shall be interpreted as a signed 64-bit value + * representing microseconds. + * @msg_data: message data in string form + */ +struct hfi_msg_debug { + uint32_t size; + uint32_t pkt_type; + uint32_t msg_type; + uint32_t msg_size; + uint32_t timestamp_hi; + uint32_t timestamp_lo; + uint8_t msg_data[1]; +} __packed; +/** + * struct hfi_msg_event_notify + * event notify message + * @size: packet size in bytes + * @pkt_type: opcode of a packet + * @fw_handle: firmware session handle + * @event_id: session event id + * @event_data1: event data corresponding to event ID + * @event_data2: event data corresponding to event ID + * @ext_event_data: info array, interpreted based on event_data1 + * and event_data2 + */ +struct hfi_msg_event_notify { + uint32_t size; + uint32_t pkt_type; + uint32_t fw_handle; + uint32_t event_id; + uint32_t event_data1; + uint32_t event_data2; + uint32_t ext_event_data[1]; +} __packed; +/** + * end of sys message packet types + */ + +#endif /* _HFI_DEFS_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/hfi.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/hfi.c new file mode 100644 index 000000000000..b75719b78c82 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/hfi.c @@ -0,0 +1,808 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" +#include "hfi_reg.h" +#include "hfi_sys_defs.h" +#include "hfi_session_defs.h" +#include "hfi_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_debug_util.h" +#include "cam_soc_util.h" + +#define HFI_VERSION_INFO_MAJOR_VAL 1 +#define HFI_VERSION_INFO_MINOR_VAL 1 +#define HFI_VERSION_INFO_STEP_VAL 0 +#define HFI_VERSION_INFO_STEP_VAL 0 +#define HFI_VERSION_INFO_MAJOR_BMSK 0xFF000000 +#define HFI_VERSION_INFO_MAJOR_SHFT 24 +#define HFI_VERSION_INFO_MINOR_BMSK 0xFFFF00 +#define HFI_VERSION_INFO_MINOR_SHFT 8 +#define HFI_VERSION_INFO_STEP_BMSK 0xFF +#define HFI_VERSION_INFO_STEP_SHFT 0 + +#define HFI_MAX_POLL_TRY 5 + +static struct hfi_info *g_hfi; +unsigned int g_icp_mmu_hdl; +static DEFINE_MUTEX(hfi_cmd_q_mutex); +static DEFINE_MUTEX(hfi_msg_q_mutex); + +int hfi_write_cmd(void *cmd_ptr) +{ + uint32_t size_in_words, empty_space, new_write_idx, read_idx, temp; + uint32_t *write_q, *write_ptr; + struct hfi_qtbl *q_tbl; + struct hfi_q_hdr *q; + int rc = 0; + + if (!cmd_ptr) { + CAM_ERR(CAM_HFI, "command is null"); + return -EINVAL; + } + + mutex_lock(&hfi_cmd_q_mutex); + if (!g_hfi) { + CAM_ERR(CAM_HFI, "HFI interface not setup"); + rc = -ENODEV; + goto err; + } + + if (g_hfi->hfi_state != HFI_READY || + !g_hfi->cmd_q_state) { + CAM_ERR(CAM_HFI, "HFI state: %u, cmd q state: %u", + g_hfi->hfi_state, g_hfi->cmd_q_state); + rc = -ENODEV; + goto err; + } + + q_tbl = (struct hfi_qtbl *)g_hfi->map.qtbl.kva; + q = &q_tbl->q_hdr[Q_CMD]; + + write_q = (uint32_t *)g_hfi->map.cmd_q.kva; + + size_in_words = (*(uint32_t *)cmd_ptr) >> BYTE_WORD_SHIFT; + if (!size_in_words) { + CAM_DBG(CAM_HFI, "failed"); + rc = -EINVAL; + goto err; + } + + read_idx = q->qhdr_read_idx; + empty_space = (q->qhdr_write_idx >= read_idx) ? + (q->qhdr_q_size - (q->qhdr_write_idx - read_idx)) : + (read_idx - q->qhdr_write_idx); + if (empty_space <= size_in_words) { + CAM_ERR(CAM_HFI, "failed"); + rc = -EIO; + goto err; + } + + new_write_idx = q->qhdr_write_idx + size_in_words; + write_ptr = (uint32_t *)(write_q + q->qhdr_write_idx); + + if (new_write_idx < q->qhdr_q_size) { + memcpy(write_ptr, (uint8_t *)cmd_ptr, + size_in_words << BYTE_WORD_SHIFT); + } else { + new_write_idx -= q->qhdr_q_size; + temp = (size_in_words - new_write_idx) << BYTE_WORD_SHIFT; + memcpy(write_ptr, (uint8_t *)cmd_ptr, temp); + memcpy(write_q, (uint8_t *)cmd_ptr + temp, + new_write_idx << BYTE_WORD_SHIFT); + } + + /* + * To make sure command data in a command queue before + * updating write index + */ + wmb(); + + q->qhdr_write_idx = new_write_idx; + + /* + * Before raising interrupt make sure command data is ready for + * firmware to process + */ + wmb(); + cam_io_w_mb((uint32_t)INTR_ENABLE, + g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT); +err: + mutex_unlock(&hfi_cmd_q_mutex); + return rc; +} + +int hfi_read_message(uint32_t *pmsg, uint8_t q_id, + uint32_t *words_read) +{ + struct hfi_qtbl *q_tbl_ptr; + struct hfi_q_hdr *q; + uint32_t new_read_idx, size_in_words, word_diff, temp; + uint32_t *read_q, *read_ptr, *write_ptr; + uint32_t size_upper_bound = 0; + int rc = 0; + + if (!pmsg) { + CAM_ERR(CAM_HFI, "Invalid msg"); + return -EINVAL; + } + + if (q_id > Q_DBG) { + CAM_ERR(CAM_HFI, "Inavlid q :%u", q_id); + return -EINVAL; + } + + mutex_lock(&hfi_msg_q_mutex); + if (!g_hfi) { + CAM_ERR(CAM_HFI, "hfi not set up yet"); + rc = -ENODEV; + goto err; + } + + if ((g_hfi->hfi_state != HFI_READY) || + !g_hfi->msg_q_state) { + CAM_ERR(CAM_HFI, "hfi state: %u, msg q state: %u", + g_hfi->hfi_state, g_hfi->msg_q_state); + rc = -ENODEV; + goto err; + } + + q_tbl_ptr = (struct hfi_qtbl *)g_hfi->map.qtbl.kva; + q = &q_tbl_ptr->q_hdr[q_id]; + + if (q->qhdr_read_idx == q->qhdr_write_idx) { + CAM_DBG(CAM_HFI, "Q not ready, state:%u, r idx:%u, w idx:%u", + g_hfi->hfi_state, q->qhdr_read_idx, q->qhdr_write_idx); + rc = -EIO; + goto err; + } + + if (q_id == Q_MSG) { + read_q = (uint32_t *)g_hfi->map.msg_q.kva; + size_upper_bound = ICP_HFI_MAX_PKT_SIZE_MSGQ_IN_WORDS; + } else { + read_q = (uint32_t *)g_hfi->map.dbg_q.kva; + size_upper_bound = ICP_HFI_MAX_PKT_SIZE_IN_WORDS; + } + + read_ptr = (uint32_t *)(read_q + q->qhdr_read_idx); + write_ptr = (uint32_t *)(read_q + q->qhdr_write_idx); + + if (write_ptr > read_ptr) + size_in_words = write_ptr - read_ptr; + else { + word_diff = read_ptr - write_ptr; + if (q_id == Q_MSG) + size_in_words = (ICP_MSG_Q_SIZE_IN_BYTES >> + BYTE_WORD_SHIFT) - word_diff; + else + size_in_words = (ICP_DBG_Q_SIZE_IN_BYTES >> + BYTE_WORD_SHIFT) - word_diff; + } + + if ((size_in_words == 0) || + (size_in_words > size_upper_bound)) { + CAM_ERR(CAM_HFI, "invalid HFI message packet size - 0x%08x", + size_in_words << BYTE_WORD_SHIFT); + q->qhdr_read_idx = q->qhdr_write_idx; + rc = -EIO; + goto err; + } + + new_read_idx = q->qhdr_read_idx + size_in_words; + + if (new_read_idx < q->qhdr_q_size) { + memcpy(pmsg, read_ptr, size_in_words << BYTE_WORD_SHIFT); + } else { + new_read_idx -= q->qhdr_q_size; + temp = (size_in_words - new_read_idx) << BYTE_WORD_SHIFT; + memcpy(pmsg, read_ptr, temp); + memcpy((uint8_t *)pmsg + temp, read_q, + new_read_idx << BYTE_WORD_SHIFT); + } + + q->qhdr_read_idx = new_read_idx; + *words_read = size_in_words; + /* Memory Barrier to make sure message + * queue parameters are updated after read + */ + wmb(); +err: + mutex_unlock(&hfi_msg_q_mutex); + return rc; +} + +int hfi_cmd_ubwc_config(uint32_t *ubwc_cfg) +{ + uint8_t *prop; + struct hfi_cmd_prop *dbg_prop; + uint32_t size = 0; + + size = sizeof(struct hfi_cmd_prop) + + sizeof(struct hfi_cmd_ubwc_cfg); + + prop = kzalloc(size, GFP_KERNEL); + if (!prop) + return -ENOMEM; + + dbg_prop = (struct hfi_cmd_prop *)prop; + dbg_prop->size = size; + dbg_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY; + dbg_prop->num_prop = 1; + dbg_prop->prop_data[0] = HFI_PROP_SYS_UBWC_CFG; + dbg_prop->prop_data[1] = ubwc_cfg[0]; + dbg_prop->prop_data[2] = ubwc_cfg[1]; + + hfi_write_cmd(prop); + kfree(prop); + + return 0; +} + +int hfi_enable_ipe_bps_pc(bool enable, uint32_t core_info) +{ + uint8_t *prop; + struct hfi_cmd_prop *dbg_prop; + uint32_t size = 0; + + size = sizeof(struct hfi_cmd_prop) + + sizeof(struct hfi_ipe_bps_pc); + + prop = kzalloc(size, GFP_KERNEL); + if (!prop) + return -ENOMEM; + + dbg_prop = (struct hfi_cmd_prop *)prop; + dbg_prop->size = size; + dbg_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY; + dbg_prop->num_prop = 1; + dbg_prop->prop_data[0] = HFI_PROP_SYS_IPEBPS_PC; + dbg_prop->prop_data[1] = enable; + dbg_prop->prop_data[2] = core_info; + + hfi_write_cmd(prop); + kfree(prop); + + return 0; +} + +int hfi_set_debug_level(u64 a5_dbg_type, uint32_t lvl) +{ + uint8_t *prop; + struct hfi_cmd_prop *dbg_prop; + uint32_t size = 0, val; + + val = HFI_DEBUG_MSG_LOW | + HFI_DEBUG_MSG_MEDIUM | + HFI_DEBUG_MSG_HIGH | + HFI_DEBUG_MSG_ERROR | + HFI_DEBUG_MSG_FATAL | + HFI_DEBUG_MSG_PERF | + HFI_DEBUG_CFG_WFI | + HFI_DEBUG_CFG_ARM9WD; + + if (lvl > val) + return -EINVAL; + + size = sizeof(struct hfi_cmd_prop) + + sizeof(struct hfi_debug); + + prop = kzalloc(size, GFP_KERNEL); + if (!prop) + return -ENOMEM; + + dbg_prop = (struct hfi_cmd_prop *)prop; + dbg_prop->size = size; + dbg_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY; + dbg_prop->num_prop = 1; + dbg_prop->prop_data[0] = HFI_PROP_SYS_DEBUG_CFG; + dbg_prop->prop_data[1] = lvl; + dbg_prop->prop_data[2] = a5_dbg_type; + hfi_write_cmd(prop); + + kfree(prop); + + return 0; +} + +void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size) +{ + switch (type) { + case HFI_CMD_SYS_INIT: { + struct hfi_cmd_sys_init init; + + memset(&init, 0, sizeof(init)); + + init.size = sizeof(struct hfi_cmd_sys_init); + init.pkt_type = type; + hfi_write_cmd(&init); + } + break; + case HFI_CMD_SYS_PC_PREP: { + struct hfi_cmd_pc_prep prep; + + prep.size = sizeof(struct hfi_cmd_pc_prep); + prep.pkt_type = type; + hfi_write_cmd(&prep); + } + break; + case HFI_CMD_SYS_SET_PROPERTY: { + struct hfi_cmd_prop prop; + + if ((uint32_t)data == (uint32_t)HFI_PROP_SYS_DEBUG_CFG) { + prop.size = sizeof(struct hfi_cmd_prop); + prop.pkt_type = type; + prop.num_prop = 1; + prop.prop_data[0] = HFI_PROP_SYS_DEBUG_CFG; + hfi_write_cmd(&prop); + } + } + break; + case HFI_CMD_SYS_GET_PROPERTY: + break; + case HFI_CMD_SYS_PING: { + struct hfi_cmd_ping_pkt ping; + + ping.size = sizeof(struct hfi_cmd_ping_pkt); + ping.pkt_type = type; + ping.user_data = (uint64_t)data; + hfi_write_cmd(&ping); + } + break; + case HFI_CMD_SYS_RESET: { + struct hfi_cmd_sys_reset_pkt reset; + + reset.size = sizeof(struct hfi_cmd_sys_reset_pkt); + reset.pkt_type = type; + reset.user_data = (uint64_t)data; + hfi_write_cmd(&reset); + } + break; + case HFI_CMD_IPEBPS_CREATE_HANDLE: { + struct hfi_cmd_create_handle handle; + + handle.size = sizeof(struct hfi_cmd_create_handle); + handle.pkt_type = type; + handle.handle_type = (uint32_t)data; + handle.user_data1 = 0; + hfi_write_cmd(&handle); + } + break; + case HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT: + break; + default: + CAM_ERR(CAM_HFI, "command not supported :%d", type); + break; + } +} + + +int hfi_get_hw_caps(void *query_buf) +{ + int i = 0; + struct cam_icp_query_cap_cmd *query_cmd = NULL; + + if (!query_buf) { + CAM_ERR(CAM_HFI, "query buf is NULL"); + return -EINVAL; + } + + query_cmd = (struct cam_icp_query_cap_cmd *)query_buf; + query_cmd->fw_version.major = 0x12; + query_cmd->fw_version.minor = 0x12; + query_cmd->fw_version.revision = 0x12; + + query_cmd->api_version.major = 0x13; + query_cmd->api_version.minor = 0x13; + query_cmd->api_version.revision = 0x13; + + query_cmd->num_ipe = 2; + query_cmd->num_bps = 1; + + for (i = 0; i < CAM_ICP_DEV_TYPE_MAX; i++) { + query_cmd->dev_ver[i].dev_type = i; + query_cmd->dev_ver[i].hw_ver.major = 0x34 + i; + query_cmd->dev_ver[i].hw_ver.minor = 0x34 + i; + query_cmd->dev_ver[i].hw_ver.incr = 0x34 + i; + } + return 0; +} + +void cam_hfi_disable_cpu(void __iomem *icp_base) +{ + uint32_t data; + uint32_t val; + uint32_t try = 0; + + while (try < HFI_MAX_POLL_TRY) { + data = cam_io_r(icp_base + HFI_REG_A5_CSR_A5_STATUS); + CAM_DBG(CAM_HFI, "wfi status = %x\n", (int)data); + + if (data & ICP_CSR_A5_STATUS_WFI) + break; + /* Need to poll here to confirm that FW is going trigger wfi + * and Host can the proceed. No interrupt is expected from FW + * at this time. + */ + msleep(100); + try++; + } + + val = cam_io_r(icp_base + HFI_REG_A5_CSR_A5_CONTROL); + val &= ~(ICP_FLAG_CSR_A5_EN | ICP_FLAG_CSR_WAKE_UP_EN); + cam_io_w_mb(val, icp_base + HFI_REG_A5_CSR_A5_CONTROL); + + val = cam_io_r(icp_base + HFI_REG_A5_CSR_NSEC_RESET); + cam_io_w_mb(val, icp_base + HFI_REG_A5_CSR_NSEC_RESET); +} + +void cam_hfi_enable_cpu(void __iomem *icp_base) +{ + cam_io_w_mb((uint32_t)ICP_FLAG_CSR_A5_EN, + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + cam_io_w_mb((uint32_t)0x10, icp_base + HFI_REG_A5_CSR_NSEC_RESET); +} + +int cam_hfi_resume(struct hfi_mem_info *hfi_mem, + void __iomem *icp_base, bool debug) +{ + int rc = 0; + uint32_t data; + uint32_t fw_version, status = 0; + uint32_t retry_cnt = 0; + + cam_hfi_enable_cpu(icp_base); + g_hfi->csr_base = icp_base; + + if (debug) { + cam_io_w_mb(ICP_FLAG_A5_CTRL_DBG_EN, + (icp_base + HFI_REG_A5_CSR_A5_CONTROL)); + + /* Barrier needed as next write should be done after + * sucessful previous write. Next write enable clock + * gating + */ + wmb(); + + cam_io_w_mb((uint32_t)ICP_FLAG_A5_CTRL_EN, + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + + } else { + cam_io_w_mb((uint32_t)ICP_FLAG_A5_CTRL_EN, + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + } + + while (retry_cnt < HFI_MAX_POLL_TRY) { + readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE), + status, (status == ICP_INIT_RESP_SUCCESS), 100, 10000); + + CAM_DBG(CAM_HFI, "1: status = %u", status); + status = cam_io_r_mb(icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE); + CAM_DBG(CAM_HFI, "2: status = %u", status); + if (status == ICP_INIT_RESP_SUCCESS) + break; + + if (status == ICP_INIT_RESP_FAILED) { + CAM_ERR(CAM_HFI, "ICP Init Failed. status = %u", + status); + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version); + return -EINVAL; + } + retry_cnt++; + } + + if ((retry_cnt == HFI_MAX_POLL_TRY) && + (status == ICP_INIT_RESP_RESET)) { + CAM_ERR(CAM_HFI, "Reached Max retries. status = %u", + status); + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version); + return -EINVAL; + } + + cam_io_w_mb((uint32_t)INTR_ENABLE, + icp_base + HFI_REG_A5_CSR_A2HOSTINTEN); + + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_DBG(CAM_HFI, "fw version : [%x]", fw_version); + + data = cam_io_r(icp_base + HFI_REG_A5_CSR_A5_STATUS); + CAM_DBG(CAM_HFI, "wfi status = %x", (int)data); + + cam_io_w_mb((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR); + cam_io_w_mb((uint32_t)hfi_mem->shmem.iova, + icp_base + HFI_REG_SHARED_MEM_PTR); + cam_io_w_mb((uint32_t)hfi_mem->shmem.len, + icp_base + HFI_REG_SHARED_MEM_SIZE); + cam_io_w_mb((uint32_t)hfi_mem->sec_heap.iova, + icp_base + HFI_REG_UNCACHED_HEAP_PTR); + cam_io_w_mb((uint32_t)hfi_mem->sec_heap.len, + icp_base + HFI_REG_UNCACHED_HEAP_SIZE); + cam_io_w_mb((uint32_t)hfi_mem->qdss.iova, + icp_base + HFI_REG_QDSS_IOVA); + cam_io_w_mb((uint32_t)hfi_mem->qdss.len, + icp_base + HFI_REG_QDSS_IOVA_SIZE); + + return rc; +} + +int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem, + void __iomem *icp_base, bool debug) +{ + int rc = 0; + struct hfi_qtbl *qtbl; + struct hfi_qtbl_hdr *qtbl_hdr; + struct hfi_q_hdr *cmd_q_hdr, *msg_q_hdr, *dbg_q_hdr; + uint32_t hw_version, soc_version, fw_version, status = 0; + uint32_t retry_cnt = 0; + + mutex_lock(&hfi_cmd_q_mutex); + mutex_lock(&hfi_msg_q_mutex); + + if (!g_hfi) { + g_hfi = kzalloc(sizeof(struct hfi_info), GFP_KERNEL); + if (!g_hfi) { + rc = -ENOMEM; + goto alloc_fail; + } + } + + if (g_hfi->hfi_state != HFI_DEINIT) { + CAM_ERR(CAM_HFI, "hfi_init: invalid state"); + return -EINVAL; + } + + memcpy(&g_hfi->map, hfi_mem, sizeof(g_hfi->map)); + g_hfi->hfi_state = HFI_DEINIT; + soc_version = socinfo_get_version(); + if (debug) { + cam_io_w_mb( + (uint32_t)(ICP_FLAG_CSR_A5_EN | ICP_FLAG_CSR_WAKE_UP_EN | + ICP_CSR_EDBGRQ | ICP_CSR_DBGSWENABLE), + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + msleep(100); + cam_io_w_mb((uint32_t)(ICP_FLAG_CSR_A5_EN | + ICP_FLAG_CSR_WAKE_UP_EN | ICP_CSR_EN_CLKGATE_WFI), + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + } else { + /* Due to hardware bug in V1 ICP clock gating has to be + * disabled, this is supposed to be fixed in V-2. But enabling + * the clock gating is causing the firmware hang, hence + * disabling the clock gating on both V1 and V2 until the + * hardware team root causes this + */ + cam_io_w_mb((uint32_t)ICP_FLAG_CSR_A5_EN | + ICP_FLAG_CSR_WAKE_UP_EN | + ICP_CSR_EN_CLKGATE_WFI, + icp_base + HFI_REG_A5_CSR_A5_CONTROL); + } + + qtbl = (struct hfi_qtbl *)hfi_mem->qtbl.kva; + qtbl_hdr = &qtbl->q_tbl_hdr; + qtbl_hdr->qtbl_version = 0xFFFFFFFF; + qtbl_hdr->qtbl_size = sizeof(struct hfi_qtbl); + qtbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_qtbl_hdr); + qtbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_q_hdr); + qtbl_hdr->qtbl_num_q = ICP_HFI_NUMBER_OF_QS; + qtbl_hdr->qtbl_num_active_q = ICP_HFI_NUMBER_OF_QS; + + /* setup host-to-firmware command queue */ + cmd_q_hdr = &qtbl->q_hdr[Q_CMD]; + cmd_q_hdr->qhdr_status = QHDR_ACTIVE; + cmd_q_hdr->qhdr_start_addr = hfi_mem->cmd_q.iova; + cmd_q_hdr->qhdr_q_size = ICP_CMD_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT; + cmd_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT; + cmd_q_hdr->qhdr_pkt_drop_cnt = RESET; + cmd_q_hdr->qhdr_read_idx = RESET; + cmd_q_hdr->qhdr_write_idx = RESET; + + /* setup firmware-to-Host message queue */ + msg_q_hdr = &qtbl->q_hdr[Q_MSG]; + msg_q_hdr->qhdr_status = QHDR_ACTIVE; + msg_q_hdr->qhdr_start_addr = hfi_mem->msg_q.iova; + msg_q_hdr->qhdr_q_size = ICP_MSG_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT; + msg_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT; + msg_q_hdr->qhdr_pkt_drop_cnt = RESET; + msg_q_hdr->qhdr_read_idx = RESET; + msg_q_hdr->qhdr_write_idx = RESET; + + /* setup firmware-to-Host message queue */ + dbg_q_hdr = &qtbl->q_hdr[Q_DBG]; + dbg_q_hdr->qhdr_status = QHDR_ACTIVE; + dbg_q_hdr->qhdr_start_addr = hfi_mem->dbg_q.iova; + dbg_q_hdr->qhdr_q_size = ICP_DBG_Q_SIZE_IN_BYTES >> BYTE_WORD_SHIFT; + dbg_q_hdr->qhdr_pkt_size = ICP_HFI_VAR_SIZE_PKT; + dbg_q_hdr->qhdr_pkt_drop_cnt = RESET; + dbg_q_hdr->qhdr_read_idx = RESET; + dbg_q_hdr->qhdr_write_idx = RESET; + + switch (event_driven_mode) { + case INTR_MODE: + cmd_q_hdr->qhdr_type = Q_CMD; + cmd_q_hdr->qhdr_rx_wm = SET; + cmd_q_hdr->qhdr_tx_wm = SET; + cmd_q_hdr->qhdr_rx_req = SET; + cmd_q_hdr->qhdr_tx_req = RESET; + cmd_q_hdr->qhdr_rx_irq_status = RESET; + cmd_q_hdr->qhdr_tx_irq_status = RESET; + + msg_q_hdr->qhdr_type = Q_MSG; + msg_q_hdr->qhdr_rx_wm = SET; + msg_q_hdr->qhdr_tx_wm = SET; + msg_q_hdr->qhdr_rx_req = SET; + msg_q_hdr->qhdr_tx_req = RESET; + msg_q_hdr->qhdr_rx_irq_status = RESET; + msg_q_hdr->qhdr_tx_irq_status = RESET; + + dbg_q_hdr->qhdr_type = Q_DBG; + dbg_q_hdr->qhdr_rx_wm = SET; + dbg_q_hdr->qhdr_tx_wm = SET_WM; + dbg_q_hdr->qhdr_rx_req = RESET; + dbg_q_hdr->qhdr_tx_req = RESET; + dbg_q_hdr->qhdr_rx_irq_status = RESET; + dbg_q_hdr->qhdr_tx_irq_status = RESET; + + break; + + case POLL_MODE: + cmd_q_hdr->qhdr_type = Q_CMD | TX_EVENT_POLL_MODE_2 | + RX_EVENT_POLL_MODE_2; + msg_q_hdr->qhdr_type = Q_MSG | TX_EVENT_POLL_MODE_2 | + RX_EVENT_POLL_MODE_2; + dbg_q_hdr->qhdr_type = Q_DBG | TX_EVENT_POLL_MODE_2 | + RX_EVENT_POLL_MODE_2; + break; + + case WM_MODE: + cmd_q_hdr->qhdr_type = Q_CMD | TX_EVENT_DRIVEN_MODE_2 | + RX_EVENT_DRIVEN_MODE_2; + cmd_q_hdr->qhdr_rx_wm = SET; + cmd_q_hdr->qhdr_tx_wm = SET; + cmd_q_hdr->qhdr_rx_req = RESET; + cmd_q_hdr->qhdr_tx_req = SET; + cmd_q_hdr->qhdr_rx_irq_status = RESET; + cmd_q_hdr->qhdr_tx_irq_status = RESET; + + msg_q_hdr->qhdr_type = Q_MSG | TX_EVENT_DRIVEN_MODE_2 | + RX_EVENT_DRIVEN_MODE_2; + msg_q_hdr->qhdr_rx_wm = SET; + msg_q_hdr->qhdr_tx_wm = SET; + msg_q_hdr->qhdr_rx_req = SET; + msg_q_hdr->qhdr_tx_req = RESET; + msg_q_hdr->qhdr_rx_irq_status = RESET; + msg_q_hdr->qhdr_tx_irq_status = RESET; + + dbg_q_hdr->qhdr_type = Q_DBG | TX_EVENT_DRIVEN_MODE_2 | + RX_EVENT_DRIVEN_MODE_2; + dbg_q_hdr->qhdr_rx_wm = SET; + dbg_q_hdr->qhdr_tx_wm = SET_WM; + dbg_q_hdr->qhdr_rx_req = RESET; + dbg_q_hdr->qhdr_tx_req = RESET; + dbg_q_hdr->qhdr_rx_irq_status = RESET; + dbg_q_hdr->qhdr_tx_irq_status = RESET; + break; + + default: + CAM_ERR(CAM_HFI, "Invalid event driven mode :%u", + event_driven_mode); + break; + } + + cam_io_w_mb((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR); + cam_io_w_mb((uint32_t)hfi_mem->shmem.iova, + icp_base + HFI_REG_SHARED_MEM_PTR); + cam_io_w_mb((uint32_t)hfi_mem->shmem.len, + icp_base + HFI_REG_SHARED_MEM_SIZE); + cam_io_w_mb((uint32_t)hfi_mem->sec_heap.iova, + icp_base + HFI_REG_UNCACHED_HEAP_PTR); + cam_io_w_mb((uint32_t)hfi_mem->sec_heap.len, + icp_base + HFI_REG_UNCACHED_HEAP_SIZE); + cam_io_w_mb((uint32_t)ICP_INIT_REQUEST_SET, + icp_base + HFI_REG_HOST_ICP_INIT_REQUEST); + cam_io_w_mb((uint32_t)hfi_mem->qdss.iova, + icp_base + HFI_REG_QDSS_IOVA); + cam_io_w_mb((uint32_t)hfi_mem->qdss.len, + icp_base + HFI_REG_QDSS_IOVA_SIZE); + + hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION); + + while (retry_cnt < HFI_MAX_POLL_TRY) { + readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE), + status, (status == ICP_INIT_RESP_SUCCESS), 100, 10000); + + CAM_DBG(CAM_HFI, "1: status = %u rc = %d", status, rc); + status = cam_io_r_mb(icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE); + CAM_DBG(CAM_HFI, "2: status = %u rc = %d", status, rc); + if (status == ICP_INIT_RESP_SUCCESS) + break; + + if (status == ICP_INIT_RESP_FAILED) { + CAM_ERR(CAM_HFI, "ICP Init Failed. status = %u", + status); + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version); + goto regions_fail; + } + retry_cnt++; + } + + if ((retry_cnt == HFI_MAX_POLL_TRY) && + (status == ICP_INIT_RESP_RESET)) { + CAM_ERR(CAM_HFI, "Reached Max retries. status = %u", + status); + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_ERR(CAM_HFI, + "hw version : : [%x], fw version : [%x]", + hw_version, fw_version); + goto regions_fail; + } + + fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION); + CAM_DBG(CAM_HFI, "hw version : : [%x], fw version : [%x]", + hw_version, fw_version); + + g_hfi->csr_base = icp_base; + g_hfi->hfi_state = HFI_READY; + g_hfi->cmd_q_state = true; + g_hfi->msg_q_state = true; + cam_io_w_mb((uint32_t)INTR_ENABLE, + icp_base + HFI_REG_A5_CSR_A2HOSTINTEN); + + mutex_unlock(&hfi_cmd_q_mutex); + mutex_unlock(&hfi_msg_q_mutex); + + return rc; +regions_fail: + kfree(g_hfi); + g_hfi = NULL; +alloc_fail: + mutex_unlock(&hfi_cmd_q_mutex); + mutex_unlock(&hfi_msg_q_mutex); + return rc; +} + +void cam_hfi_deinit(void __iomem *icp_base) +{ + mutex_lock(&hfi_cmd_q_mutex); + mutex_lock(&hfi_msg_q_mutex); + + if (!g_hfi) { + CAM_ERR(CAM_HFI, "hfi path not established yet"); + goto err; + } + + g_hfi->cmd_q_state = false; + g_hfi->msg_q_state = false; + + cam_io_w_mb((uint32_t)ICP_INIT_REQUEST_RESET, + icp_base + HFI_REG_HOST_ICP_INIT_REQUEST); + + cam_io_w_mb((uint32_t)INTR_DISABLE, + g_hfi->csr_base + HFI_REG_A5_CSR_A2HOSTINTEN); + kzfree(g_hfi); + g_hfi = NULL; + +err: + mutex_unlock(&hfi_cmd_q_mutex); + mutex_unlock(&hfi_msg_q_mutex); +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/Makefile new file mode 100644 index 000000000000..f4fad8b012f7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += icp_hw_mgr/ a5_hw/ ipe_hw/ bps_hw/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/Makefile new file mode 100644 index 000000000000..5183c5172774 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += a5_dev.o a5_core.o a5_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.c new file mode 100644 index 000000000000..4b5f22eda762 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.c @@ -0,0 +1,478 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_io_util.h" +#include "cam_a5_hw_intf.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "a5_core.h" +#include "a5_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "hfi_intf.h" +#include "hfi_sys_defs.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static int cam_a5_cpas_vote(struct cam_a5_device_core_info *core_info, + struct cam_icp_cpas_vote *cpas_vote) +{ + int rc = 0; + + if (cpas_vote->ahb_vote_valid) + rc = cam_cpas_update_ahb_vote(core_info->cpas_handle, + &cpas_vote->ahb_vote); + + if (cpas_vote->axi_vote_valid) + rc = cam_cpas_update_axi_vote(core_info->cpas_handle, + &cpas_vote->axi_vote); + + if (rc) + CAM_ERR(CAM_ICP, "cpas vote is failed: %d", rc); + + return rc; +} + +static int32_t cam_icp_validate_fw(const uint8_t *elf) +{ + struct elf32_hdr *elf_hdr; + + if (!elf) { + CAM_ERR(CAM_ICP, "Invalid params"); + return -EINVAL; + } + + elf_hdr = (struct elf32_hdr *)elf; + + if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG)) { + CAM_ERR(CAM_ICP, "ICP elf identifier is failed"); + return -EINVAL; + } + + /* check architecture */ + if (elf_hdr->e_machine != EM_ARM) { + CAM_ERR(CAM_ICP, "unsupported arch"); + return -EINVAL; + } + + /* check elf bit format */ + if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) { + CAM_ERR(CAM_ICP, "elf doesn't support 32 bit format"); + return -EINVAL; + } + + return 0; +} + +static int32_t cam_icp_get_fw_size(const uint8_t *elf, uint32_t *fw_size) +{ + int32_t rc = 0; + int32_t i = 0; + uint32_t num_prg_hdrs; + unsigned char *icp_prg_hdr_tbl; + uint32_t seg_mem_size = 0; + struct elf32_hdr *elf_hdr; + struct elf32_phdr *prg_hdr; + + if (!elf || !fw_size) { + CAM_ERR(CAM_ICP, "invalid args"); + return -EINVAL; + } + + *fw_size = 0; + + elf_hdr = (struct elf32_hdr *)elf; + num_prg_hdrs = elf_hdr->e_phnum; + icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff; + prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0]; + + if (!prg_hdr) { + CAM_ERR(CAM_ICP, "failed to get elf program header attr"); + return -EINVAL; + } + + CAM_DBG(CAM_ICP, "num_prg_hdrs = %d", num_prg_hdrs); + for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) { + if (prg_hdr->p_flags == 0) + continue; + + seg_mem_size = (prg_hdr->p_memsz + prg_hdr->p_align - 1) & + ~(prg_hdr->p_align - 1); + seg_mem_size += prg_hdr->p_vaddr; + CAM_DBG(CAM_ICP, "memsz:%x align:%x addr:%x seg_mem_size:%x", + (int)prg_hdr->p_memsz, (int)prg_hdr->p_align, + (int)prg_hdr->p_vaddr, (int)seg_mem_size); + if (*fw_size < seg_mem_size) + *fw_size = seg_mem_size; + + } + + if (*fw_size == 0) { + CAM_ERR(CAM_ICP, "invalid elf fw file"); + return -EINVAL; + } + + return rc; +} + +static int32_t cam_icp_program_fw(const uint8_t *elf, + struct cam_a5_device_core_info *core_info) +{ + int32_t rc = 0; + uint32_t num_prg_hdrs; + unsigned char *icp_prg_hdr_tbl; + int32_t i = 0; + u8 *dest; + u8 *src; + struct elf32_hdr *elf_hdr; + struct elf32_phdr *prg_hdr; + + elf_hdr = (struct elf32_hdr *)elf; + num_prg_hdrs = elf_hdr->e_phnum; + icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff; + prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0]; + + if (!prg_hdr) { + CAM_ERR(CAM_ICP, "failed to get elf program header attr"); + return -EINVAL; + } + + for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) { + if (prg_hdr->p_flags == 0) + continue; + + CAM_DBG(CAM_ICP, "Loading FW header size: %u", + prg_hdr->p_filesz); + if (prg_hdr->p_filesz != 0) { + src = (u8 *)((u8 *)elf + prg_hdr->p_offset); + dest = (u8 *)(((u8 *)core_info->fw_kva_addr) + + prg_hdr->p_vaddr); + + memcpy_toio(dest, src, prg_hdr->p_filesz); + } + } + + return rc; +} + +static int32_t cam_a5_download_fw(void *device_priv) +{ + int32_t rc = 0; + uint32_t fw_size; + const uint8_t *fw_start = NULL; + struct cam_hw_info *a5_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_a5_device_core_info *core_info = NULL; + struct cam_a5_device_hw_info *hw_info = NULL; + struct platform_device *pdev = NULL; + struct a5_soc_info *cam_a5_soc_info = NULL; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &a5_dev->soc_info; + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + hw_info = core_info->a5_hw_info; + pdev = soc_info->pdev; + cam_a5_soc_info = soc_info->soc_private; + + rc = request_firmware(&core_info->fw_elf, "CAMERA_ICP.elf", &pdev->dev); + if (rc) { + CAM_ERR(CAM_ICP, "Failed to locate fw: %d", rc); + return rc; + } + + if (!core_info->fw_elf) { + CAM_ERR(CAM_ICP, "Invalid elf size"); + rc = -EINVAL; + goto fw_download_failed; + } + + fw_start = core_info->fw_elf->data; + rc = cam_icp_validate_fw(fw_start); + if (rc) { + CAM_ERR(CAM_ICP, "fw elf validation failed"); + goto fw_download_failed; + } + + rc = cam_icp_get_fw_size(fw_start, &fw_size); + if (rc) { + CAM_ERR(CAM_ICP, "unable to get fw size"); + goto fw_download_failed; + } + + if (core_info->fw_buf_len < fw_size) { + CAM_ERR(CAM_ICP, "mismatch in fw size: %u %llu", + fw_size, core_info->fw_buf_len); + rc = -EINVAL; + goto fw_download_failed; + } + + rc = cam_icp_program_fw(fw_start, core_info); + if (rc) { + CAM_ERR(CAM_ICP, "fw program is failed"); + goto fw_download_failed; + } + +fw_download_failed: + release_firmware(core_info->fw_elf); + return rc; +} + +int cam_a5_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *a5_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_a5_device_core_info *core_info = NULL; + struct cam_icp_cpas_vote cpas_vote; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &a5_dev->soc_info; + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info: %pK core_info: %pK", + soc_info, core_info); + return -EINVAL; + } + + cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE; + cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE; + cpas_vote.axi_vote.compressed_bw = CAM_ICP_A5_BW_BYTES_VOTE; + cpas_vote.axi_vote.uncompressed_bw = CAM_ICP_A5_BW_BYTES_VOTE; + + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote.ahb_vote, &cpas_vote.axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "cpass start failed: %d", rc); + return rc; + } + core_info->cpas_start = true; + + rc = cam_a5_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "soc enable is failed: %d", rc); + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } + + return rc; +} + +int cam_a5_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *a5_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_a5_device_core_info *core_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &a5_dev->soc_info; + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + rc = cam_a5_disable_soc_resources(soc_info); + if (rc) + CAM_ERR(CAM_ICP, "soc disable is failed: %d", rc); + + if (core_info->cpas_start) { + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } + + return rc; +} + +irqreturn_t cam_a5_irq(int irq_num, void *data) +{ + struct cam_hw_info *a5_dev = data; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_a5_device_core_info *core_info = NULL; + struct cam_a5_device_hw_info *hw_info = NULL; + uint32_t irq_status = 0; + + if (!data) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info or query_cap args"); + return IRQ_HANDLED; + } + + soc_info = &a5_dev->soc_info; + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + hw_info = core_info->a5_hw_info; + + irq_status = cam_io_r_mb(soc_info->reg_map[A5_SIERRA_BASE].mem_base + + core_info->a5_hw_info->a5_host_int_status); + + cam_io_w_mb(irq_status, + soc_info->reg_map[A5_SIERRA_BASE].mem_base + + core_info->a5_hw_info->a5_host_int_clr); + + if ((irq_status & A5_WDT_0) || + (irq_status & A5_WDT_1)) { + CAM_ERR_RATE_LIMIT(CAM_ICP, "watch dog interrupt from A5"); + } + + if (core_info->irq_cb.icp_hw_mgr_cb) + core_info->irq_cb.icp_hw_mgr_cb(irq_status, + core_info->irq_cb.data); + + return IRQ_HANDLED; +} + +int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *a5_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_a5_device_core_info *core_info = NULL; + struct cam_a5_device_hw_info *hw_info = NULL; + struct a5_soc_info *a5_soc = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_ICP_A5_CMD_MAX) { + CAM_ERR(CAM_ICP, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + soc_info = &a5_dev->soc_info; + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + hw_info = core_info->a5_hw_info; + + switch (cmd_type) { + case CAM_ICP_A5_CMD_FW_DOWNLOAD: + rc = cam_a5_download_fw(device_priv); + break; + case CAM_ICP_A5_CMD_SET_FW_BUF: { + struct cam_icp_a5_set_fw_buf_info *fw_buf_info = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + core_info->fw_buf = fw_buf_info->iova; + core_info->fw_kva_addr = fw_buf_info->kva; + core_info->fw_buf_len = fw_buf_info->len; + + CAM_DBG(CAM_ICP, "fw buf info = %x %llx %lld", + core_info->fw_buf, core_info->fw_kva_addr, + core_info->fw_buf_len); + break; + } + case CAM_ICP_A5_SET_IRQ_CB: { + struct cam_icp_a5_set_irq_cb *irq_cb = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + core_info->irq_cb.icp_hw_mgr_cb = irq_cb->icp_hw_mgr_cb; + core_info->irq_cb.data = irq_cb->data; + break; + } + + case CAM_ICP_A5_SEND_INIT: + hfi_send_system_cmd(HFI_CMD_SYS_INIT, 0, 0); + break; + + case CAM_ICP_A5_CMD_PC_PREP: + hfi_send_system_cmd(HFI_CMD_SYS_PC_PREP, 0, 0); + break; + + case CAM_ICP_A5_CMD_VOTE_CPAS: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + cam_a5_cpas_vote(core_info, cpas_vote); + break; + } + + case CAM_ICP_A5_CMD_CPAS_START: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + if (!core_info->cpas_start) { + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote->ahb_vote, + &cpas_vote->axi_vote); + core_info->cpas_start = true; + } + break; + } + + case CAM_ICP_A5_CMD_CPAS_STOP: + if (core_info->cpas_start) { + cam_cpas_stop(core_info->cpas_handle); + core_info->cpas_start = false; + } + break; + case CAM_ICP_A5_CMD_UBWC_CFG: + a5_soc = soc_info->soc_private; + if (!a5_soc) { + CAM_ERR(CAM_ICP, "A5 private soc info is NULL"); + return -EINVAL; + } + rc = hfi_cmd_ubwc_config(a5_soc->ubwc_cfg); + break; + default: + break; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.h new file mode 100644 index 000000000000..4aa6b4bd570e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_core.h @@ -0,0 +1,89 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_A5_CORE_H +#define CAM_A5_CORE_H + +#include +#include +#include +#include +#include +#include "cam_a5_hw_intf.h" + +#define A5_QGIC_BASE 0 +#define A5_SIERRA_BASE 1 +#define A5_CSR_BASE 2 + +#define A5_HOST_INT 0x1 +#define A5_WDT_0 0x10 +#define A5_WDT_1 0x100 + +#define ELF_GUARD_PAGE (2 * 1024 * 1024) + +struct cam_a5_device_hw_info { + uint32_t hw_ver; + uint32_t nsec_reset; + uint32_t a5_control; + uint32_t a5_host_int_en; + uint32_t a5_host_int; + uint32_t a5_host_int_clr; + uint32_t a5_host_int_status; + uint32_t a5_host_int_set; + uint32_t host_a5_int; + uint32_t fw_version; + uint32_t init_req; + uint32_t init_response; + uint32_t shared_mem_ptr; + uint32_t shared_mem_size; + uint32_t qtbl_ptr; + uint32_t uncached_heap_ptr; + uint32_t uncached_heap_size; + uint32_t a5_status; +}; + +/** + * struct cam_a5_device_hw_info + * @a5_hw_info: A5 hardware info + * @fw_elf: start address of fw start with elf header + * @fw: start address of fw blob + * @fw_buf: smmu alloc/mapped fw buffer + * @fw_buf_len: fw buffer length + * @query_cap: A5 query info from firmware + * @a5_acquire: Acquire information of A5 + * @irq_cb: IRQ callback + * @cpas_handle: CPAS handle for A5 + * @cpast_start: state variable for cpas + */ +struct cam_a5_device_core_info { + struct cam_a5_device_hw_info *a5_hw_info; + const struct firmware *fw_elf; + void *fw; + uint32_t fw_buf; + uint64_t fw_kva_addr; + uint64_t fw_buf_len; + struct cam_icp_a5_query_cap query_cap; + struct cam_icp_a5_acquire_dev a5_acquire[8]; + struct cam_icp_a5_set_irq_cb irq_cb; + uint32_t cpas_handle; + bool cpas_start; +}; + +int cam_a5_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_a5_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); + +irqreturn_t cam_a5_irq(int irq_num, void *data); +#endif /* CAM_A5_CORE_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_dev.c new file mode 100644 index 000000000000..14c3c9c5815b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_dev.c @@ -0,0 +1,234 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include "a5_core.h" +#include "a5_soc.h" +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_a5_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +struct a5_soc_info cam_a5_soc_info; +EXPORT_SYMBOL(cam_a5_soc_info); + +struct cam_a5_device_hw_info cam_a5_hw_info = { + .hw_ver = 0x0, + .nsec_reset = 0x4, + .a5_control = 0x8, + .a5_host_int_en = 0x10, + .a5_host_int = 0x14, + .a5_host_int_clr = 0x18, + .a5_host_int_status = 0x1c, + .a5_host_int_set = 0x20, + .host_a5_int = 0x30, + .fw_version = 0x44, + .init_req = 0x48, + .init_response = 0x4c, + .shared_mem_ptr = 0x50, + .shared_mem_size = 0x54, + .qtbl_ptr = 0x58, + .uncached_heap_ptr = 0x5c, + .uncached_heap_size = 0x60, + .a5_status = 0x200, +}; +EXPORT_SYMBOL(cam_a5_hw_info); + +static bool cam_a5_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + bool error_handled = false; + + if (!irq_data) + return error_handled; + + switch (irq_data->irq_type) { + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d", + irq_data->irq_type, + irq_data->u.dec_err.decerr_status.value, + irq_data->u.dec_err.decerr_status.thr_err, + irq_data->u.dec_err.decerr_status.fcl_err, + irq_data->u.dec_err.decerr_status.len_md_err, + irq_data->u.dec_err.decerr_status.format_err); + error_handled = true; + break; + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC Encode error type=%d status=%x", + irq_data->irq_type, + irq_data->u.enc_err.encerr_status.value); + error_handled = true; + break; + default: + break; + } + + return error_handled; +} + +int cam_a5_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_a5_device_core_info *core_info, + uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = &soc_info->pdev->dev; + memcpy(cpas_register_params.identifier, "icp", sizeof("icp")); + cpas_register_params.cam_cpas_client_cb = cam_a5_cpas_cb; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed: %d", rc); + return rc; + } + + core_info->cpas_handle = cpas_register_params.client_handle; + return rc; +} + +int cam_a5_probe(struct platform_device *pdev) +{ + int rc = 0; + struct cam_hw_info *a5_dev = NULL; + struct cam_hw_intf *a5_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_a5_device_core_info *core_info = NULL; + struct cam_a5_device_hw_info *hw_info = NULL; + + a5_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!a5_dev_intf) + return -ENOMEM; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &a5_dev_intf->hw_idx); + + a5_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!a5_dev) { + rc = -ENOMEM; + goto a5_dev_alloc_failure; + } + + a5_dev->soc_info.pdev = pdev; + a5_dev->soc_info.dev = &pdev->dev; + a5_dev->soc_info.dev_name = pdev->name; + a5_dev_intf->hw_priv = a5_dev; + a5_dev_intf->hw_ops.init = cam_a5_init_hw; + a5_dev_intf->hw_ops.deinit = cam_a5_deinit_hw; + a5_dev_intf->hw_ops.process_cmd = cam_a5_process_cmd; + a5_dev_intf->hw_type = CAM_ICP_DEV_A5; + + CAM_DBG(CAM_ICP, "type %d index %d", + a5_dev_intf->hw_type, + a5_dev_intf->hw_idx); + + platform_set_drvdata(pdev, a5_dev_intf); + + a5_dev->core_info = kzalloc(sizeof(struct cam_a5_device_core_info), + GFP_KERNEL); + if (!a5_dev->core_info) { + rc = -ENOMEM; + goto core_info_alloc_failure; + } + core_info = (struct cam_a5_device_core_info *)a5_dev->core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_ICP, "No a5 hardware info"); + rc = -EINVAL; + goto match_err; + } + hw_info = (struct cam_a5_device_hw_info *)match_dev->data; + core_info->a5_hw_info = hw_info; + + a5_dev->soc_info.soc_private = &cam_a5_soc_info; + + rc = cam_a5_init_soc_resources(&a5_dev->soc_info, cam_a5_irq, + a5_dev); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed to init_soc"); + goto init_soc_failure; + } + + CAM_DBG(CAM_ICP, "soc info : %pK", + (void *)&a5_dev->soc_info); + rc = cam_a5_register_cpas(&a5_dev->soc_info, + core_info, a5_dev_intf->hw_idx); + if (rc < 0) { + CAM_ERR(CAM_ICP, "a5 cpas registration failed"); + goto cpas_reg_failed; + } + a5_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&a5_dev->hw_mutex); + spin_lock_init(&a5_dev->hw_lock); + init_completion(&a5_dev->hw_complete); + + CAM_DBG(CAM_ICP, "A5%d probe successful", + a5_dev_intf->hw_idx); + return 0; + +cpas_reg_failed: +init_soc_failure: +match_err: + kfree(a5_dev->core_info); +core_info_alloc_failure: + kfree(a5_dev); +a5_dev_alloc_failure: + kfree(a5_dev_intf); + + return rc; +} + +static const struct of_device_id cam_a5_dt_match[] = { + { + .compatible = "qcom,cam-a5", + .data = &cam_a5_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_a5_dt_match); + +static struct platform_driver cam_a5_driver = { + .probe = cam_a5_probe, + .driver = { + .name = "cam-a5", + .owner = THIS_MODULE, + .of_match_table = cam_a5_dt_match, + }, +}; + +static int __init cam_a5_init_module(void) +{ + return platform_driver_register(&cam_a5_driver); +} + +static void __exit cam_a5_exit_module(void) +{ + platform_driver_unregister(&cam_a5_driver); +} + +module_init(cam_a5_init_module); +module_exit(cam_a5_exit_module); +MODULE_DESCRIPTION("CAM A5 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.c new file mode 100644 index 000000000000..f38e4089825b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.c @@ -0,0 +1,119 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include "a5_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +static int cam_a5_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + int rc = 0, i; + const char *fw_name; + struct a5_soc_info *camp_a5_soc_info; + struct device_node *of_node = NULL; + struct platform_device *pdev = NULL; + int num_ubwc_cfg; + + pdev = soc_info->pdev; + of_node = pdev->dev.of_node; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_ICP, "get a5 dt prop is failed"); + return rc; + } + + camp_a5_soc_info = soc_info->soc_private; + fw_name = camp_a5_soc_info->fw_name; + + rc = of_property_read_string(of_node, "fw_name", &fw_name); + if (rc < 0) { + CAM_ERR(CAM_ICP, "fw_name read failed"); + goto end; + } + + num_ubwc_cfg = of_property_count_u32_elems(of_node, "ubwc-cfg"); + if ((num_ubwc_cfg < 0) || (num_ubwc_cfg > ICP_UBWC_MAX)) { + CAM_ERR(CAM_ICP, "wrong ubwc_cfg: %d", num_ubwc_cfg); + rc = num_ubwc_cfg; + goto end; + } + + for (i = 0; i < num_ubwc_cfg; i++) { + rc = of_property_read_u32_index(of_node, "ubwc-cfg", + i, &camp_a5_soc_info->ubwc_cfg[i]); + if (rc < 0) { + CAM_ERR(CAM_ICP, "unable to read ubwc cfg values"); + break; + } + } + +end: + return rc; +} + +static int cam_a5_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t a5_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, a5_irq_handler, + irq_data); + + return rc; +} + +int cam_a5_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t a5_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_a5_get_dt_properties(soc_info); + if (rc < 0) + return rc; + + rc = cam_a5_request_platform_resource(soc_info, a5_irq_handler, + irq_data); + if (rc < 0) + return rc; + + return rc; +} + +int cam_a5_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) + CAM_ERR(CAM_ICP, "enable platform failed"); + + return rc; +} + +int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) + CAM_ERR(CAM_ICP, "disable platform failed"); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.h new file mode 100644 index 000000000000..3593cfb4a80a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/a5_soc.h @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_A5_SOC_H +#define CAM_A5_SOC_H + +#include "cam_soc_util.h" + +#define ICP_UBWC_MAX 2 + +struct a5_soc_info { + char *fw_name; + uint32_t ubwc_cfg[ICP_UBWC_MAX]; +}; + +int cam_a5_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t a5_irq_handler, void *irq_data); + +int cam_a5_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info); + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/Makefile new file mode 100644 index 000000000000..62e453f03917 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += bps_dev.o bps_core.o bps_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.c new file mode 100644 index 000000000000..5bd7f1c91729 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.c @@ -0,0 +1,323 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include +#include +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "bps_core.h" +#include "bps_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_bps_hw_intf.h" +#include "cam_icp_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static int cam_bps_cpas_vote(struct cam_bps_device_core_info *core_info, + struct cam_icp_cpas_vote *cpas_vote) +{ + int rc = 0; + + if (cpas_vote->ahb_vote_valid) + rc = cam_cpas_update_ahb_vote(core_info->cpas_handle, + &cpas_vote->ahb_vote); + if (cpas_vote->axi_vote_valid) + rc = cam_cpas_update_axi_vote(core_info->cpas_handle, + &cpas_vote->axi_vote); + + if (rc < 0) + CAM_ERR(CAM_ICP, "cpas vote is failed: %d", rc); + + return rc; +} + + +int cam_bps_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *bps_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_bps_device_core_info *core_info = NULL; + struct cam_icp_cpas_vote cpas_vote; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &bps_dev->soc_info; + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE; + cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE; + cpas_vote.axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + cpas_vote.axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote.ahb_vote, &cpas_vote.axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "cpass start failed: %d", rc); + return rc; + } + core_info->cpas_start = true; + + rc = cam_bps_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "soc enable is failed: %d", rc); + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } else { + core_info->clk_enable = true; + } + + return rc; +} + +int cam_bps_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *bps_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_bps_device_core_info *core_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &bps_dev->soc_info; + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + rc = cam_bps_disable_soc_resources(soc_info, core_info->clk_enable); + if (rc) + CAM_ERR(CAM_ICP, "soc disable is failed: %d", rc); + core_info->clk_enable = false; + + if (core_info->cpas_start) { + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } + + return rc; +} + +static int cam_bps_handle_pc(struct cam_hw_info *bps_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_bps_device_core_info *core_info = NULL; + struct cam_bps_device_hw_info *hw_info = NULL; + int pwr_ctrl; + int pwr_status; + + soc_info = &bps_dev->soc_info; + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + hw_info = core_info->bps_hw_info; + + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (!(pwr_ctrl & BPS_COLLAPSE_MASK)) { + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x1); + + if ((pwr_status >> BPS_PWR_ON_MASK)) { + CAM_ERR(CAM_ICP, "BPS: pwr_status(%x):pwr_ctrl(%x)", + pwr_status, pwr_ctrl); + return -EINVAL; + } + } + cam_bps_get_gdsc_control(soc_info); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, true, + &pwr_ctrl); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + CAM_DBG(CAM_ICP, "pwr_ctrl = %x pwr_status = %x", + pwr_ctrl, pwr_status); + + return 0; +} + +static int cam_bps_handle_resume(struct cam_hw_info *bps_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_bps_device_core_info *core_info = NULL; + struct cam_bps_device_hw_info *hw_info = NULL; + int pwr_ctrl; + int pwr_status; + int rc = 0; + + soc_info = &bps_dev->soc_info; + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + hw_info = core_info->bps_hw_info; + + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, true, &pwr_ctrl); + if (pwr_ctrl & BPS_COLLAPSE_MASK) { + CAM_ERR(CAM_ICP, "BPS: pwr_ctrl(%x)", pwr_ctrl); + return -EINVAL; + } + + rc = cam_bps_transfer_gdsc_control(soc_info); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, true, &pwr_ctrl); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, true, &pwr_status); + CAM_DBG(CAM_ICP, "pwr_ctrl = %x pwr_status = %x", + pwr_ctrl, pwr_status); + + return rc; +} + +int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *bps_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_bps_device_core_info *core_info = NULL; + struct cam_bps_device_hw_info *hw_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_ICP_BPS_CMD_MAX) { + CAM_ERR(CAM_ICP, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + soc_info = &bps_dev->soc_info; + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + hw_info = core_info->bps_hw_info; + + switch (cmd_type) { + case CAM_ICP_BPS_CMD_VOTE_CPAS: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + cam_bps_cpas_vote(core_info, cpas_vote); + break; + } + + case CAM_ICP_BPS_CMD_CPAS_START: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_ICP, "cmd args NULL"); + return -EINVAL; + } + + if (!core_info->cpas_start) { + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote->ahb_vote, + &cpas_vote->axi_vote); + core_info->cpas_start = true; + } + break; + } + + case CAM_ICP_BPS_CMD_CPAS_STOP: + if (core_info->cpas_start) { + cam_cpas_stop(core_info->cpas_handle); + core_info->cpas_start = false; + } + break; + case CAM_ICP_BPS_CMD_POWER_COLLAPSE: + rc = cam_bps_handle_pc(bps_dev); + break; + case CAM_ICP_BPS_CMD_POWER_RESUME: + rc = cam_bps_handle_resume(bps_dev); + break; + case CAM_ICP_BPS_CMD_UPDATE_CLK: { + struct cam_a5_clk_update_cmd *clk_upd_cmd = + (struct cam_a5_clk_update_cmd *)cmd_args; + uint32_t clk_rate = clk_upd_cmd->curr_clk_rate; + + CAM_DBG(CAM_ICP, "bps_src_clk rate = %d", (int)clk_rate); + + if (!core_info->clk_enable) { + if (clk_upd_cmd->ipe_bps_pc_enable) { + cam_bps_handle_pc(bps_dev); + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x0); + } + rc = cam_bps_toggle_clk(soc_info, true); + if (rc) + CAM_ERR(CAM_ICP, "Enable failed"); + else + core_info->clk_enable = true; + if (clk_upd_cmd->ipe_bps_pc_enable) { + rc = cam_bps_handle_resume(bps_dev); + if (rc) + CAM_ERR(CAM_ICP, "BPS resume failed"); + } + } + CAM_DBG(CAM_ICP, "clock rate %d", clk_rate); + rc = cam_bps_update_clk_rate(soc_info, clk_rate); + if (rc) + CAM_ERR(CAM_ICP, "Failed to update clk"); + } + break; + case CAM_ICP_BPS_CMD_DISABLE_CLK: + if (core_info->clk_enable == true) + cam_bps_toggle_clk(soc_info, false); + core_info->clk_enable = false; + break; + default: + break; + } + return rc; +} + +irqreturn_t cam_bps_irq(int irq_num, void *data) +{ + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.h new file mode 100644 index 000000000000..8d775487c179 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_core.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_BPS_CORE_H +#define CAM_BPS_CORE_H + +#include +#include +#include +#include + +#define BPS_COLLAPSE_MASK 0x1 +#define BPS_PWR_ON_MASK 0x2 + +struct cam_bps_device_hw_info { + uint32_t hw_idx; + uint32_t pwr_ctrl; + uint32_t pwr_status; + uint32_t reserved; +}; + +struct cam_bps_device_core_info { + struct cam_bps_device_hw_info *bps_hw_info; + uint32_t cpas_handle; + bool cpas_start; + bool clk_enable; +}; + +int cam_bps_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_bps_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); + +irqreturn_t cam_bps_irq(int irq_num, void *data); +#endif /* CAM_BPS_CORE_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_dev.c new file mode 100644 index 000000000000..feb0bd899d87 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_dev.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include "bps_core.h" +#include "bps_soc.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_io_util.h" +#include "cam_icp_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static struct cam_bps_device_hw_info cam_bps_hw_info = { + .hw_idx = 0, + .pwr_ctrl = 0x5c, + .pwr_status = 0x58, + .reserved = 0, +}; +EXPORT_SYMBOL(cam_bps_hw_info); + +static bool cam_bps_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + bool error_handled = false; + + if (!irq_data) + return error_handled; + + switch (irq_data->irq_type) { + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d", + irq_data->irq_type, + irq_data->u.dec_err.decerr_status.value, + irq_data->u.dec_err.decerr_status.thr_err, + irq_data->u.dec_err.decerr_status.fcl_err, + irq_data->u.dec_err.decerr_status.len_md_err, + irq_data->u.dec_err.decerr_status.format_err); + error_handled = true; + break; + case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ICP, + "IPE/BPS UBWC Encode error type=%d status=%x", + irq_data->irq_type, + irq_data->u.enc_err.encerr_status.value); + error_handled = true; + break; + default: + break; + } + + return error_handled; +} + +int cam_bps_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_bps_device_core_info *core_info, + uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = &soc_info->pdev->dev; + memcpy(cpas_register_params.identifier, "bps", sizeof("bps")); + cpas_register_params.cam_cpas_client_cb = cam_bps_cpas_cb; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed: %d", rc); + return rc; + } + core_info->cpas_handle = cpas_register_params.client_handle; + + return rc; +} + +int cam_bps_probe(struct platform_device *pdev) +{ + struct cam_hw_info *bps_dev = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_bps_device_core_info *core_info = NULL; + struct cam_bps_device_hw_info *hw_info = NULL; + int rc = 0; + + bps_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!bps_dev_intf) + return -ENOMEM; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &bps_dev_intf->hw_idx); + + bps_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!bps_dev) { + kfree(bps_dev_intf); + return -ENOMEM; + } + bps_dev->soc_info.pdev = pdev; + bps_dev->soc_info.dev = &pdev->dev; + bps_dev->soc_info.dev_name = pdev->name; + bps_dev_intf->hw_priv = bps_dev; + bps_dev_intf->hw_ops.init = cam_bps_init_hw; + bps_dev_intf->hw_ops.deinit = cam_bps_deinit_hw; + bps_dev_intf->hw_ops.process_cmd = cam_bps_process_cmd; + bps_dev_intf->hw_type = CAM_ICP_DEV_BPS; + platform_set_drvdata(pdev, bps_dev_intf); + bps_dev->core_info = kzalloc(sizeof(struct cam_bps_device_core_info), + GFP_KERNEL); + if (!bps_dev->core_info) { + kfree(bps_dev); + kfree(bps_dev_intf); + return -ENOMEM; + } + core_info = (struct cam_bps_device_core_info *)bps_dev->core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_ICP, "No bps hardware info"); + kfree(bps_dev->core_info); + kfree(bps_dev); + kfree(bps_dev_intf); + rc = -EINVAL; + return rc; + } + hw_info = &cam_bps_hw_info; + core_info->bps_hw_info = hw_info; + + rc = cam_bps_init_soc_resources(&bps_dev->soc_info, cam_bps_irq, + bps_dev); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed to init_soc"); + kfree(bps_dev->core_info); + kfree(bps_dev); + kfree(bps_dev_intf); + return rc; + } + CAM_DBG(CAM_ICP, "soc info : %pK", + (void *)&bps_dev->soc_info); + + rc = cam_bps_register_cpas(&bps_dev->soc_info, + core_info, bps_dev_intf->hw_idx); + if (rc < 0) { + kfree(bps_dev->core_info); + kfree(bps_dev); + kfree(bps_dev_intf); + return rc; + } + bps_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&bps_dev->hw_mutex); + spin_lock_init(&bps_dev->hw_lock); + init_completion(&bps_dev->hw_complete); + CAM_DBG(CAM_ICP, "BPS%d probe successful", + bps_dev_intf->hw_idx); + + return rc; +} + +static const struct of_device_id cam_bps_dt_match[] = { + { + .compatible = "qcom,cam-bps", + .data = &cam_bps_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_bps_dt_match); + +static struct platform_driver cam_bps_driver = { + .probe = cam_bps_probe, + .driver = { + .name = "cam-bps", + .owner = THIS_MODULE, + .of_match_table = cam_bps_dt_match, + }, +}; + +static int __init cam_bps_init_module(void) +{ + return platform_driver_register(&cam_bps_driver); +} + +static void __exit cam_bps_exit_module(void) +{ + platform_driver_unregister(&cam_bps_driver); +} + +module_init(cam_bps_init_module); +module_exit(cam_bps_exit_module); +MODULE_DESCRIPTION("CAM BPS driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.c new file mode 100644 index 000000000000..f3bfdf1b4d12 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include "bps_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +static int cam_bps_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) + CAM_ERR(CAM_ICP, "get bps dt prop is failed"); + + return rc; +} + +static int cam_bps_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t bps_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, bps_irq_handler, + irq_data); + + return rc; +} + +int cam_bps_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t bps_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_bps_get_dt_properties(soc_info); + if (rc < 0) + return rc; + + rc = cam_bps_request_platform_resource(soc_info, bps_irq_handler, + irq_data); + if (rc < 0) + return rc; + + return rc; +} + +int cam_bps_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, false); + if (rc) + CAM_ERR(CAM_ICP, "enable platform failed"); + + return rc; +} + +int cam_bps_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, disable_clk, + false); + if (rc) + CAM_ERR(CAM_ICP, "disable platform failed"); + + return rc; +} + +int cam_bps_transfer_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + int i; + int rc; + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_FAST); + if (rc) { + CAM_ERR(CAM_ICP, "Regulator set mode %s failed", + soc_info->rgltr_name[i]); + goto rgltr_set_mode_failed; + } + } + return 0; + +rgltr_set_mode_failed: + for (i = i - 1; i >= 0; i--) + if (soc_info->rgltr[i]) + regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_NORMAL); + + return rc; +} + +int cam_bps_get_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + int i; + int rc; + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_NORMAL); + if (rc) { + CAM_ERR(CAM_ICP, "Regulator set mode %s failed", + soc_info->rgltr_name[i]); + goto rgltr_set_mode_failed; + } + } + return 0; + +rgltr_set_mode_failed: + for (i = i - 1; i >= 0; i--) + if (soc_info->rgltr[i]) + regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_FAST); + + return rc; +} + +int cam_bps_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate) +{ + int32_t src_clk_idx; + + if (!soc_info) + return -EINVAL; + + src_clk_idx = soc_info->src_clk_idx; + + if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) && + (soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) && + (clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) { + CAM_WARN(CAM_ICP, "clk_rate %d greater than max, reset to %d", + clk_rate, + soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]); + clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]; + } + + return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx], + soc_info->clk_name[soc_info->src_clk_idx], clk_rate); +} + +int cam_bps_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable) +{ + int rc = 0; + + if (clk_enable) + rc = cam_soc_util_clk_enable_default(soc_info, CAM_SVS_VOTE); + else + cam_soc_util_clk_disable_default(soc_info); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.h new file mode 100644 index 000000000000..18f3015855ac --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/bps_hw/bps_soc.h @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_BPS_SOC_H_ +#define _CAM_BPS_SOC_H_ + +#include "cam_soc_util.h" + +int cam_bps_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t bps_irq_handler, void *irq_data); + +int cam_bps_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_bps_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk); + +int cam_bps_get_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_bps_transfer_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_bps_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate); +int cam_bps_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable); +#endif /* _CAM_BPS_SOC_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/Makefile new file mode 100644 index 000000000000..fd9202eb13e7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/Makefile @@ -0,0 +1,16 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/isp/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/isp/isp_hw/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/isp/isp_hw/isp_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/a5_hw/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp_hw_mgr.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c new file mode 100644 index 000000000000..63cc0bbecfef --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c @@ -0,0 +1,4348 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam_sync_api.h" +#include "cam_packet_util.h" +#include "cam_hw.h" +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_icp_hw_mgr.h" +#include "cam_a5_hw_intf.h" +#include "cam_bps_hw_intf.h" +#include "cam_ipe_hw_intf.h" +#include "cam_smmu_api.h" +#include "cam_mem_mgr.h" +#include "hfi_intf.h" +#include "hfi_reg.h" +#include "hfi_session_defs.h" +#include "hfi_sys_defs.h" +#include "cam_req_mgr_workq.h" +#include "cam_mem_mgr.h" +#include "a5_core.h" +#include "hfi_sys_defs.h" +#include "cam_debug_util.h" +#include "cam_soc_util.h" +#include "cam_trace.h" +#include "cam_cpas_api.h" + +#define ICP_WORKQ_TASK_CMD_TYPE 1 +#define ICP_WORKQ_TASK_MSG_TYPE 2 + +#define ICP_DEV_TYPE_TO_CLK_TYPE(dev_type) \ + ((dev_type == CAM_ICP_RES_TYPE_BPS) ? ICP_CLK_HW_BPS : ICP_CLK_HW_IPE) + +static struct cam_icp_hw_mgr icp_hw_mgr; + +static int cam_icp_send_ubwc_cfg(struct cam_icp_hw_mgr *hw_mgr) +{ + struct cam_hw_intf *a5_dev_intf = NULL; + int rc; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is NULL"); + return -EINVAL; + } + + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_CMD_UBWC_CFG, NULL, 0); + if (rc) + CAM_ERR(CAM_ICP, "CAM_ICP_A5_CMD_UBWC_CFG is failed"); + + return rc; +} + +static void cam_icp_hw_mgr_clk_info_update(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + struct cam_icp_clk_info *hw_mgr_clk_info; + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS]; + else + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE]; + + if (hw_mgr_clk_info->base_clk >= ctx_data->clk_info.base_clk) + hw_mgr_clk_info->base_clk -= ctx_data->clk_info.base_clk; +} + +static void cam_icp_hw_mgr_reset_clk_info(struct cam_icp_hw_mgr *hw_mgr) +{ + int i; + + for (i = 0; i < ICP_CLK_HW_MAX; i++) { + hw_mgr->clk_info[i].base_clk = 0; + hw_mgr->clk_info[i].curr_clk = ICP_CLK_SVS_HZ; + hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD; + hw_mgr->clk_info[i].over_clked = 0; + hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + } + hw_mgr->icp_default_clk = ICP_CLK_SVS_HZ; +} + +static int cam_icp_get_actual_clk_rate_idx( + struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk) +{ + int i; + + for (i = 0; i < CAM_MAX_VOTE; i++) + if (ctx_data->clk_info.clk_rate[i] >= base_clk) + return i; + + /* + * Caller has to ensure returned index is within array + * size bounds while accessing that index. + */ + + return i; +} + +static bool cam_icp_is_over_clk(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_icp_clk_info *hw_mgr_clk_info) +{ + int base_clk_idx; + int curr_clk_idx; + + base_clk_idx = cam_icp_get_actual_clk_rate_idx(ctx_data, + hw_mgr_clk_info->base_clk); + + curr_clk_idx = cam_icp_get_actual_clk_rate_idx(ctx_data, + hw_mgr_clk_info->curr_clk); + + CAM_DBG(CAM_ICP, "bc_idx = %d cc_idx = %d %d %d", + base_clk_idx, curr_clk_idx, hw_mgr_clk_info->base_clk, + hw_mgr_clk_info->curr_clk); + + if (curr_clk_idx > base_clk_idx) + return true; + + return false; +} + +static int cam_icp_get_lower_clk_rate(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk) +{ + int i; + + i = cam_icp_get_actual_clk_rate_idx(ctx_data, base_clk); + + if (i > 0) + return ctx_data->clk_info.clk_rate[i - 1]; + + CAM_DBG(CAM_ICP, "Already clk at lower level"); + return base_clk; +} + +static int cam_icp_get_next_clk_rate(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk) +{ + int i; + + i = cam_icp_get_actual_clk_rate_idx(ctx_data, base_clk); + + if (i < CAM_MAX_VOTE - 1) + return ctx_data->clk_info.clk_rate[i + 1]; + + CAM_DBG(CAM_ICP, "Already clk at higher level"); + + return base_clk; +} + +static int cam_icp_get_actual_clk_rate(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk) +{ + int i; + + for (i = 0; i < CAM_MAX_VOTE; i++) + if (ctx_data->clk_info.clk_rate[i] >= base_clk) + return ctx_data->clk_info.clk_rate[i]; + + return base_clk; +} + +static int cam_icp_supported_clk_rates(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + int i; + struct cam_hw_soc_info *soc_info; + struct cam_hw_intf *dev_intf = NULL; + struct cam_hw_info *dev = NULL; + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + dev_intf = hw_mgr->bps_dev_intf; + else + dev_intf = hw_mgr->ipe0_dev_intf; + + if (!dev_intf) { + CAM_ERR(CAM_ICP, "dev_intf is invalid"); + return -EINVAL; + } + dev = (struct cam_hw_info *)dev_intf->hw_priv; + soc_info = &dev->soc_info; + + for (i = 0; i < CAM_MAX_VOTE; i++) { + ctx_data->clk_info.clk_rate[i] = + soc_info->clk_rate[i][soc_info->src_clk_idx]; + CAM_DBG(CAM_ICP, "clk_info[%d] = %d", + i, ctx_data->clk_info.clk_rate[i]); + } + + return 0; +} + +static int cam_icp_clk_idx_from_req_id(struct cam_icp_hw_ctx_data *ctx_data, + uint64_t req_id) +{ + struct hfi_frame_process_info *frame_process; + int i; + + frame_process = &ctx_data->hfi_frame_process; + + for (i = 0; i < CAM_FRAME_CMD_MAX; i++) + if (frame_process->request_id[i] == req_id) + return i; + + return 0; +} + +static int cam_icp_ctx_clk_info_init(struct cam_icp_hw_ctx_data *ctx_data) +{ + ctx_data->clk_info.curr_fc = 0; + ctx_data->clk_info.base_clk = 0; + ctx_data->clk_info.uncompressed_bw = 0; + ctx_data->clk_info.compressed_bw = 0; + cam_icp_supported_clk_rates(&icp_hw_mgr, ctx_data); + + return 0; +} + +static bool cam_icp_frame_pending(struct cam_icp_hw_ctx_data *ctx_data) +{ + return !bitmap_empty(ctx_data->hfi_frame_process.bitmap, + CAM_FRAME_CMD_MAX); +} + +static int cam_icp_ctx_timer_reset(struct cam_icp_hw_ctx_data *ctx_data) +{ + if (ctx_data && ctx_data->watch_dog) { + ctx_data->watch_dog_reset_counter++; + CAM_DBG(CAM_ICP, "reset timer : ctx_id = %d, counter=%d", + ctx_data->ctx_id, ctx_data->watch_dog_reset_counter); + crm_timer_reset(ctx_data->watch_dog); + } + + return 0; +} + +static void cam_icp_device_timer_reset(struct cam_icp_hw_mgr *hw_mgr, + int device_index) +{ + if ((device_index >= ICP_CLK_HW_MAX) || (!hw_mgr)) + return; + + if (hw_mgr->clk_info[device_index].watch_dog) { + CAM_DBG(CAM_ICP, "reset timer : device_index = %d", + device_index); + crm_timer_reset(hw_mgr->clk_info[device_index].watch_dog); + hw_mgr->clk_info[device_index].watch_dog_reset_counter++; + } +} + +static int32_t cam_icp_deinit_idle_clk(void *priv, void *data) +{ + struct cam_icp_hw_mgr *hw_mgr = (struct cam_icp_hw_mgr *)priv; + struct clk_work_data *task_data = (struct clk_work_data *)data; + struct cam_icp_clk_info *clk_info = + (struct cam_icp_clk_info *)task_data->data; + uint32_t id; + uint32_t i; + struct cam_icp_hw_ctx_data *ctx_data; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + struct cam_hw_intf *dev_intf = NULL; + struct cam_a5_clk_update_cmd clk_upd_cmd; + int rc = 0; + bool busy = false; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + clk_info->base_clk = 0; + clk_info->curr_clk = 0; + clk_info->over_clked = 0; + mutex_lock(&hw_mgr->hw_mgr_mutex); + + for (i = 0; i < CAM_ICP_CTX_MAX; i++) { + ctx_data = &hw_mgr->ctx_data[i]; + mutex_lock(&ctx_data->ctx_mutex); + if ((ctx_data->state == CAM_ICP_CTX_STATE_ACQUIRED) && + (ICP_DEV_TYPE_TO_CLK_TYPE(ctx_data-> + icp_dev_acquire_info->dev_type) == clk_info->hw_type)) { + busy = cam_icp_frame_pending(ctx_data); + if (busy) { + mutex_unlock(&ctx_data->ctx_mutex); + break; + } + cam_icp_ctx_clk_info_init(ctx_data); + } + mutex_unlock(&ctx_data->ctx_mutex); + } + if (busy) { + cam_icp_device_timer_reset(hw_mgr, clk_info->hw_type); + rc = -EBUSY; + goto done; + } + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk"); + rc = -EINVAL; + goto done; + } + + if (clk_info->hw_type == ICP_CLK_HW_BPS) { + dev_intf = bps_dev_intf; + id = CAM_ICP_BPS_CMD_DISABLE_CLK; + } else if (clk_info->hw_type == ICP_CLK_HW_IPE) { + dev_intf = ipe0_dev_intf; + id = CAM_ICP_IPE_CMD_DISABLE_CLK; + } else { + CAM_ERR(CAM_ICP, "Error"); + goto done; + } + + CAM_DBG(CAM_ICP, "Disable %d", clk_info->hw_type); + + clk_upd_cmd.ipe_bps_pc_enable = icp_hw_mgr.ipe_bps_pc_flag; + + dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id, + &clk_upd_cmd, sizeof(struct cam_a5_clk_update_cmd)); + + if (clk_info->hw_type != ICP_CLK_HW_BPS) + if (ipe1_dev_intf) + ipe1_dev_intf->hw_ops.process_cmd( + ipe1_dev_intf->hw_priv, id, + &clk_upd_cmd, + sizeof(struct cam_a5_clk_update_cmd)); + +done: + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int32_t cam_icp_ctx_timer(void *priv, void *data) +{ + struct clk_work_data *task_data = (struct clk_work_data *)data; + struct cam_icp_hw_ctx_data *ctx_data = + (struct cam_icp_hw_ctx_data *)task_data->data; + struct cam_icp_hw_mgr *hw_mgr = &icp_hw_mgr; + uint32_t id; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + struct cam_hw_intf *dev_intf = NULL; + struct cam_icp_clk_info *clk_info; + struct cam_icp_cpas_vote clk_update; + + if (!ctx_data) { + CAM_ERR(CAM_ICP, "ctx_data is NULL, failed to update clk"); + return -EINVAL; + } + + mutex_lock(&ctx_data->ctx_mutex); + if ((ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) || + (ctx_data->watch_dog_reset_counter == 0)) { + CAM_DBG(CAM_ICP, "state %d, counter=%d", + ctx_data->state, ctx_data->watch_dog_reset_counter); + mutex_unlock(&ctx_data->ctx_mutex); + return 0; + } + if (cam_icp_frame_pending(ctx_data)) { + cam_icp_ctx_timer_reset(ctx_data); + mutex_unlock(&ctx_data->ctx_mutex); + return -EBUSY; + } + + CAM_DBG(CAM_ICP, + "E :ctx_id = %d ubw = %lld cbw = %lld curr_fc = %u bc = %u", + ctx_data->ctx_id, + ctx_data->clk_info.uncompressed_bw, + ctx_data->clk_info.compressed_bw, + ctx_data->clk_info.curr_fc, ctx_data->clk_info.base_clk); + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk"); + mutex_unlock(&ctx_data->ctx_mutex); + return -EINVAL; + } + + if (!ctx_data->icp_dev_acquire_info) { + CAM_WARN(CAM_ICP, "NULL acquire info"); + mutex_unlock(&ctx_data->ctx_mutex); + return -EINVAL; + } + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) { + dev_intf = bps_dev_intf; + clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS]; + id = CAM_ICP_BPS_CMD_VOTE_CPAS; + } else { + dev_intf = ipe0_dev_intf; + clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE]; + id = CAM_ICP_IPE_CMD_VOTE_CPAS; + } + + clk_info->compressed_bw -= ctx_data->clk_info.compressed_bw; + clk_info->uncompressed_bw -= ctx_data->clk_info.uncompressed_bw; + ctx_data->clk_info.uncompressed_bw = 0; + ctx_data->clk_info.compressed_bw = 0; + ctx_data->clk_info.curr_fc = 0; + ctx_data->clk_info.base_clk = 0; + + clk_update.ahb_vote.type = CAM_VOTE_DYNAMIC; + clk_update.ahb_vote.vote.freq = clk_info->curr_clk; + clk_update.ahb_vote_valid = true; + clk_update.axi_vote.compressed_bw = clk_info->compressed_bw; + clk_update.axi_vote.uncompressed_bw = clk_info->uncompressed_bw; + clk_update.axi_vote_valid = true; + dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id, + &clk_update, sizeof(clk_update)); + + CAM_DBG(CAM_ICP, + "X :ctx_id = %d ubw = %lld cbw = %lld curr_fc = %u bc = %u", + ctx_data->ctx_id, + ctx_data->clk_info.uncompressed_bw, + ctx_data->clk_info.compressed_bw, + ctx_data->clk_info.curr_fc, ctx_data->clk_info.base_clk); + + mutex_unlock(&ctx_data->ctx_mutex); + + return 0; +} + +static void cam_icp_ctx_timer_cb(struct timer_list *timer_data) +{ + unsigned long flags; + struct crm_workq_task *task; + struct clk_work_data *task_data; + struct cam_req_mgr_timer *timer = + container_of(timer_data, struct cam_req_mgr_timer, sys_timer); + + spin_lock_irqsave(&icp_hw_mgr.hw_mgr_lock, flags); + task = cam_req_mgr_workq_get_task(icp_hw_mgr.timer_work); + if (!task) { + CAM_ERR(CAM_ICP, "no empty task"); + spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags); + return; + } + + task_data = (struct clk_work_data *)task->payload; + task_data->data = timer->parent; + task_data->type = ICP_WORKQ_TASK_MSG_TYPE; + task->process_cb = cam_icp_ctx_timer; + cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags); +} + +static void cam_icp_device_timer_cb(struct timer_list *timer_data) +{ + unsigned long flags; + struct crm_workq_task *task; + struct clk_work_data *task_data; + struct cam_req_mgr_timer *timer = + container_of(timer_data, struct cam_req_mgr_timer, sys_timer); + + spin_lock_irqsave(&icp_hw_mgr.hw_mgr_lock, flags); + task = cam_req_mgr_workq_get_task(icp_hw_mgr.timer_work); + if (!task) { + CAM_ERR(CAM_ICP, "no empty task"); + spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags); + return; + } + + task_data = (struct clk_work_data *)task->payload; + task_data->data = timer->parent; + task_data->type = ICP_WORKQ_TASK_MSG_TYPE; + task->process_cb = cam_icp_deinit_idle_clk; + cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags); +} + +static int cam_icp_clk_info_init(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + int i; + + for (i = 0; i < ICP_CLK_HW_MAX; i++) { + hw_mgr->clk_info[i].base_clk = ICP_CLK_SVS_HZ; + hw_mgr->clk_info[i].curr_clk = ICP_CLK_SVS_HZ; + hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD; + hw_mgr->clk_info[i].over_clked = 0; + hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + hw_mgr->clk_info[i].hw_type = i; + hw_mgr->clk_info[i].watch_dog_reset_counter = 0; + } + hw_mgr->icp_default_clk = ICP_CLK_SVS_HZ; + + return 0; +} + +static int cam_icp_ctx_timer_start(struct cam_icp_hw_ctx_data *ctx_data) +{ + int rc = 0; + + rc = crm_timer_init(&ctx_data->watch_dog, + 200, ctx_data, &cam_icp_ctx_timer_cb); + if (rc) + CAM_ERR(CAM_ICP, "Failed to start timer"); + + ctx_data->watch_dog_reset_counter = 0; + + CAM_DBG(CAM_ICP, "stop timer : ctx_id = %d", ctx_data->ctx_id); + return rc; +} + +static int cam_icp_device_timer_start(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc = 0; + int i; + + for (i = 0; i < ICP_CLK_HW_MAX; i++) { + if (!hw_mgr->clk_info[i].watch_dog) { + rc = crm_timer_init(&hw_mgr->clk_info[i].watch_dog, + 3000, &hw_mgr->clk_info[i], + &cam_icp_device_timer_cb); + + if (rc) + CAM_ERR(CAM_ICP, "Failed to start timer %d", i); + + hw_mgr->clk_info[i].watch_dog_reset_counter = 0; + } + } + + return rc; +} + +static int cam_icp_ctx_timer_stop(struct cam_icp_hw_ctx_data *ctx_data) +{ + if (ctx_data->watch_dog) { + CAM_DBG(CAM_ICP, "stop timer : ctx_id = %d", ctx_data->ctx_id); + ctx_data->watch_dog_reset_counter = 0; + crm_timer_exit(&ctx_data->watch_dog); + ctx_data->watch_dog = NULL; + } + + return 0; +} + +static void cam_icp_device_timer_stop(struct cam_icp_hw_mgr *hw_mgr) +{ + if (!hw_mgr->bps_ctxt_cnt && + hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog) { + hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog_reset_counter = 0; + crm_timer_exit(&hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog); + hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog = NULL; + } + + if (!hw_mgr->ipe_ctxt_cnt && + hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog) { + hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog_reset_counter = 0; + crm_timer_exit(&hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog); + hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog = NULL; + } +} + +static uint32_t cam_icp_mgr_calc_base_clk(uint32_t frame_cycles, + uint64_t budget) +{ + uint64_t base_clk; + uint64_t mul = 1000000000; + + base_clk = (frame_cycles * mul) / budget; + + CAM_DBG(CAM_ICP, "budget = %lld fc = %d ib = %lld base_clk = %lld", + budget, frame_cycles, + (long long int)(frame_cycles * mul), base_clk); + + return base_clk; +} + +static bool cam_icp_busy_prev_reqs(struct hfi_frame_process_info *frm_process, + uint64_t req_id) +{ + int i; + int cnt; + + for (i = 0, cnt = 0; i < CAM_FRAME_CMD_MAX; i++) { + if (frm_process->request_id[i]) { + if (frm_process->fw_process_flag[i]) { + CAM_DBG(CAM_ICP, "r id = %lld busy = %d", + frm_process->request_id[i], + frm_process->fw_process_flag[i]); + cnt++; + } + } + } + if (cnt > 1) + return true; + + return false; +} + +static int cam_icp_calc_total_clk(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_clk_info *hw_mgr_clk_info, uint32_t dev_type) +{ + int i; + struct cam_icp_hw_ctx_data *ctx_data; + + hw_mgr_clk_info->base_clk = 0; + for (i = 0; i < CAM_ICP_CTX_MAX; i++) { + ctx_data = &hw_mgr->ctx_data[i]; + if (ctx_data->state == CAM_ICP_CTX_STATE_ACQUIRED && + ICP_DEV_TYPE_TO_CLK_TYPE( + ctx_data->icp_dev_acquire_info->dev_type) == + ICP_DEV_TYPE_TO_CLK_TYPE(dev_type)) + hw_mgr_clk_info->base_clk += + ctx_data->clk_info.base_clk; + } + + return 0; +} + +static bool cam_icp_update_clk_busy(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_icp_clk_info *hw_mgr_clk_info, + struct cam_icp_clk_bw_request *clk_info, + uint32_t base_clk) +{ + uint32_t next_clk_level; + uint32_t actual_clk; + bool rc = false; + + /* 1. if current request frame cycles(fc) are more than previous + * frame fc + * Calculate the new base clock. + * if sum of base clocks are more than next available clk level + * Update clock rate, change curr_clk_rate to sum of base clock + * rates and make over_clked to zero + * else + * Update clock rate to next level, update curr_clk_rate and make + * overclked cnt to zero + * 2. if current fc is less than or equal to previous frame fc + * Still Bump up the clock to next available level + * if it is available, then update clock, make overclk cnt to + * zero. If the clock is already at highest clock rate then + * no need to update the clock + */ + ctx_data->clk_info.base_clk = base_clk; + hw_mgr_clk_info->over_clked = 0; + if (clk_info->frame_cycles > ctx_data->clk_info.curr_fc) { + cam_icp_calc_total_clk(hw_mgr, hw_mgr_clk_info, + ctx_data->icp_dev_acquire_info->dev_type); + actual_clk = cam_icp_get_actual_clk_rate(hw_mgr, + ctx_data, base_clk); + if (hw_mgr_clk_info->base_clk > actual_clk) { + hw_mgr_clk_info->curr_clk = hw_mgr_clk_info->base_clk; + } else { + next_clk_level = cam_icp_get_next_clk_rate(hw_mgr, + ctx_data, hw_mgr_clk_info->curr_clk); + hw_mgr_clk_info->curr_clk = next_clk_level; + } + rc = true; + } else { + next_clk_level = + cam_icp_get_next_clk_rate(hw_mgr, ctx_data, + hw_mgr_clk_info->curr_clk); + if (hw_mgr_clk_info->curr_clk < next_clk_level) { + hw_mgr_clk_info->curr_clk = next_clk_level; + rc = true; + } + } + ctx_data->clk_info.curr_fc = clk_info->frame_cycles; + + return rc; +} + +static bool cam_icp_update_clk_overclk_free(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_icp_clk_info *hw_mgr_clk_info, + struct cam_icp_clk_bw_request *clk_info, + uint32_t base_clk) +{ + int rc = false; + + /* + * In caseof no pending packets case + * 1. In caseof overclk cnt is less than threshold, increase + * overclk count and no update in the clock rate + * 2. In caseof overclk cnt is greater than or equal to threshold + * then lower clock rate by one level and update hw_mgr current + * clock value. + * a. In case of new clock rate greater than sum of clock + * rates, reset overclk count value to zero if it is + * overclock + * b. if it is less than sum of base clocks then go to next + * level of clock and make overclk count to zero + * c. if it is same as sum of base clock rates update overclock + * cnt to 0 + */ + if (hw_mgr_clk_info->over_clked < hw_mgr_clk_info->threshold) { + hw_mgr_clk_info->over_clked++; + rc = false; + } else { + hw_mgr_clk_info->curr_clk = + cam_icp_get_lower_clk_rate(hw_mgr, ctx_data, + hw_mgr_clk_info->curr_clk); + if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk) { + if (cam_icp_is_over_clk(hw_mgr, ctx_data, + hw_mgr_clk_info)) + hw_mgr_clk_info->over_clked = 0; + } else if (hw_mgr_clk_info->curr_clk < + hw_mgr_clk_info->base_clk) { + hw_mgr_clk_info->curr_clk = + cam_icp_get_next_clk_rate(hw_mgr, ctx_data, + hw_mgr_clk_info->curr_clk); + hw_mgr_clk_info->over_clked = 0; + } else if (hw_mgr_clk_info->curr_clk == + hw_mgr_clk_info->base_clk) { + hw_mgr_clk_info->over_clked = 0; + } + rc = true; + } + + return rc; +} + +static bool cam_icp_update_clk_free(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_icp_clk_info *hw_mgr_clk_info, + struct cam_icp_clk_bw_request *clk_info, + uint32_t base_clk) +{ + int rc = false; + bool over_clocked = false; + + ctx_data->clk_info.curr_fc = clk_info->frame_cycles; + ctx_data->clk_info.base_clk = base_clk; + cam_icp_calc_total_clk(hw_mgr, hw_mgr_clk_info, + ctx_data->icp_dev_acquire_info->dev_type); + + /* + * Current clock is not always sum of base clocks, due to + * clock scales update to next higher or lower levels, it + * equals to one of discrete clock values supported by hardware. + * So even current clock is higher than sum of base clocks, we + * can not consider it is over clocked. if it is greater than + * discrete clock level then only it is considered as over clock. + * 1. Handle over clock case + * 2. If current clock is less than sum of base clocks + * update current clock + * 3. If current clock is same as sum of base clocks no action + */ + + over_clocked = cam_icp_is_over_clk(hw_mgr, ctx_data, + hw_mgr_clk_info); + + if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk && + over_clocked) { + rc = cam_icp_update_clk_overclk_free(hw_mgr, ctx_data, + hw_mgr_clk_info, clk_info, base_clk); + } else if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk) { + hw_mgr_clk_info->over_clked = 0; + rc = false; + } else if (hw_mgr_clk_info->curr_clk < hw_mgr_clk_info->base_clk) { + hw_mgr_clk_info->curr_clk = cam_icp_get_actual_clk_rate(hw_mgr, + ctx_data, hw_mgr_clk_info->base_clk); + rc = true; + } + + return rc; +} + +static bool cam_icp_debug_clk_update(struct cam_icp_clk_info *hw_mgr_clk_info) +{ + if (icp_hw_mgr.icp_debug_clk < ICP_CLK_TURBO_HZ && + icp_hw_mgr.icp_debug_clk && + icp_hw_mgr.icp_debug_clk != hw_mgr_clk_info->curr_clk) { + hw_mgr_clk_info->base_clk = icp_hw_mgr.icp_debug_clk; + hw_mgr_clk_info->curr_clk = icp_hw_mgr.icp_debug_clk; + hw_mgr_clk_info->uncompressed_bw = icp_hw_mgr.icp_debug_clk; + hw_mgr_clk_info->compressed_bw = icp_hw_mgr.icp_debug_clk; + CAM_DBG(CAM_ICP, "bc = %d cc = %d", + hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk); + return true; + } + + return false; +} + +static bool cam_icp_default_clk_update(struct cam_icp_clk_info *hw_mgr_clk_info) +{ + if (icp_hw_mgr.icp_default_clk != hw_mgr_clk_info->curr_clk) { + hw_mgr_clk_info->base_clk = icp_hw_mgr.icp_default_clk; + hw_mgr_clk_info->curr_clk = icp_hw_mgr.icp_default_clk; + hw_mgr_clk_info->uncompressed_bw = icp_hw_mgr.icp_default_clk; + hw_mgr_clk_info->compressed_bw = icp_hw_mgr.icp_default_clk; + CAM_DBG(CAM_ICP, "bc = %d cc = %d", + hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk); + return true; + } + + return false; +} + +static bool cam_icp_update_bw(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_icp_clk_info *hw_mgr_clk_info, + struct cam_icp_clk_bw_request *clk_info, + bool busy) +{ + int i; + struct cam_icp_hw_ctx_data *ctx; + + /* + * If current request bandwidth is different from previous frames, then + * recalculate bandwidth of all contexts of same hardware and update + * voting of bandwidth + */ + CAM_DBG(CAM_ICP, "ubw ctx = %lld clk_info ubw = %lld busy = %d", + ctx_data->clk_info.uncompressed_bw, + clk_info->uncompressed_bw, busy); + + if ((clk_info->uncompressed_bw == ctx_data->clk_info.uncompressed_bw) && + (ctx_data->clk_info.uncompressed_bw == + hw_mgr_clk_info->uncompressed_bw)) + return false; + + if (busy && + ctx_data->clk_info.uncompressed_bw > clk_info->uncompressed_bw) + return false; + + ctx_data->clk_info.uncompressed_bw = clk_info->uncompressed_bw; + ctx_data->clk_info.compressed_bw = clk_info->compressed_bw; + hw_mgr_clk_info->uncompressed_bw = 0; + hw_mgr_clk_info->compressed_bw = 0; + for (i = 0; i < CAM_ICP_CTX_MAX; i++) { + ctx = &hw_mgr->ctx_data[i]; + if (ctx->state == CAM_ICP_CTX_STATE_ACQUIRED && + ICP_DEV_TYPE_TO_CLK_TYPE( + ctx->icp_dev_acquire_info->dev_type) == + ICP_DEV_TYPE_TO_CLK_TYPE( + ctx_data->icp_dev_acquire_info->dev_type)) { + hw_mgr_clk_info->uncompressed_bw += + ctx->clk_info.uncompressed_bw; + hw_mgr_clk_info->compressed_bw += + ctx->clk_info.compressed_bw; + CAM_DBG(CAM_ICP, "ubw = %lld, cbw = %lld", + hw_mgr_clk_info->uncompressed_bw, + hw_mgr_clk_info->compressed_bw); + } + } + + return true; +} + +static bool cam_icp_check_clk_update(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, int idx) +{ + bool busy, rc = false; + uint32_t base_clk; + struct cam_icp_clk_bw_request *clk_info; + struct hfi_frame_process_info *frame_info; + uint64_t req_id; + struct cam_icp_clk_info *hw_mgr_clk_info; + + cam_icp_ctx_timer_reset(ctx_data); + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) { + cam_icp_device_timer_reset(hw_mgr, ICP_CLK_HW_BPS); + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS]; + CAM_DBG(CAM_ICP, "Reset bps timer"); + } else { + cam_icp_device_timer_reset(hw_mgr, ICP_CLK_HW_IPE); + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE]; + CAM_DBG(CAM_ICP, "Reset ipe timer"); + } + + if (icp_hw_mgr.icp_debug_clk) + return cam_icp_debug_clk_update(hw_mgr_clk_info); + + /* Check is there any pending frames in this context */ + frame_info = &ctx_data->hfi_frame_process; + req_id = frame_info->request_id[idx]; + busy = cam_icp_busy_prev_reqs(frame_info, req_id); + CAM_DBG(CAM_ICP, "busy = %d req_id = %lld", busy, req_id); + + clk_info = &ctx_data->hfi_frame_process.clk_info[idx]; + if (!clk_info->frame_cycles) + return cam_icp_default_clk_update(hw_mgr_clk_info); + + /* Calculate base clk rate */ + base_clk = cam_icp_mgr_calc_base_clk( + clk_info->frame_cycles, clk_info->budget_ns); + ctx_data->clk_info.rt_flag = clk_info->rt_flag; + + if (busy) + rc = cam_icp_update_clk_busy(hw_mgr, ctx_data, + hw_mgr_clk_info, clk_info, base_clk); + else + rc = cam_icp_update_clk_free(hw_mgr, ctx_data, + hw_mgr_clk_info, clk_info, base_clk); + + CAM_DBG(CAM_ICP, "bc = %d cc = %d busy = %d overclk = %d uc = %d", + hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk, + busy, hw_mgr_clk_info->over_clked, rc); + + return rc; +} + +static bool cam_icp_check_bw_update(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, int idx) +{ + bool busy, rc = false; + struct cam_icp_clk_bw_request *clk_info; + struct cam_icp_clk_info *hw_mgr_clk_info; + struct hfi_frame_process_info *frame_info; + uint64_t req_id; + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS]; + else + hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE]; + + clk_info = &ctx_data->hfi_frame_process.clk_info[idx]; + frame_info = &ctx_data->hfi_frame_process; + req_id = frame_info->request_id[idx]; + busy = cam_icp_busy_prev_reqs(frame_info, req_id); + rc = cam_icp_update_bw(hw_mgr, ctx_data, hw_mgr_clk_info, + clk_info, busy); + + CAM_DBG(CAM_ICP, "ubw = %lld, cbw = %lld, update_bw = %d", + hw_mgr_clk_info->uncompressed_bw, + hw_mgr_clk_info->compressed_bw, rc); + + return rc; +} + +static int cam_icp_update_clk_rate(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + uint32_t id; + uint32_t curr_clk_rate; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + struct cam_hw_intf *dev_intf = NULL; + struct cam_a5_clk_update_cmd clk_upd_cmd; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk"); + return -EINVAL; + } + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) { + dev_intf = bps_dev_intf; + curr_clk_rate = hw_mgr->clk_info[ICP_CLK_HW_BPS].curr_clk; + id = CAM_ICP_BPS_CMD_UPDATE_CLK; + } else { + dev_intf = ipe0_dev_intf; + curr_clk_rate = hw_mgr->clk_info[ICP_CLK_HW_IPE].curr_clk; + id = CAM_ICP_IPE_CMD_UPDATE_CLK; + } + + clk_upd_cmd.curr_clk_rate = curr_clk_rate; + clk_upd_cmd.ipe_bps_pc_enable = icp_hw_mgr.ipe_bps_pc_flag; + + dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id, + &clk_upd_cmd, sizeof(struct cam_a5_clk_update_cmd)); + + if (ctx_data->icp_dev_acquire_info->dev_type != CAM_ICP_RES_TYPE_BPS) + if (ipe1_dev_intf) + ipe1_dev_intf->hw_ops.process_cmd( + ipe1_dev_intf->hw_priv, id, + &clk_upd_cmd, + sizeof(struct cam_a5_clk_update_cmd)); + + return 0; +} + +static int cam_icp_update_cpas_vote(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + uint32_t id; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + struct cam_hw_intf *dev_intf = NULL; + struct cam_icp_clk_info *clk_info; + struct cam_icp_cpas_vote clk_update; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk"); + return -EINVAL; + } + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) { + dev_intf = bps_dev_intf; + clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS]; + id = CAM_ICP_BPS_CMD_VOTE_CPAS; + } else { + dev_intf = ipe0_dev_intf; + clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE]; + id = CAM_ICP_IPE_CMD_VOTE_CPAS; + } + + clk_update.ahb_vote.type = CAM_VOTE_DYNAMIC; + clk_update.ahb_vote.vote.freq = clk_info->curr_clk; + clk_update.ahb_vote_valid = true; + clk_update.axi_vote.compressed_bw = clk_info->compressed_bw; + clk_update.axi_vote.uncompressed_bw = clk_info->uncompressed_bw; + clk_update.axi_vote_valid = true; + dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id, + &clk_update, sizeof(clk_update)); + + /* + * Consolidated bw needs to be voted on only one IPE client. Otherwise + * total bw that we vote at bus client would be doubled. So either + * remove voting on IPE1 or divide the vote for each IPE client + * and vote to cpas - cpas will add up and vote full bw to sf client + * anyway. + */ + + return 0; +} + +static int cam_icp_mgr_ipe_bps_clk_update(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, int idx) +{ + int rc = 0; + + if (cam_icp_check_clk_update(hw_mgr, ctx_data, idx)) + rc = cam_icp_update_clk_rate(hw_mgr, ctx_data); + + if (cam_icp_check_bw_update(hw_mgr, ctx_data, idx)) + rc |= cam_icp_update_cpas_vote(hw_mgr, ctx_data); + + return rc; +} + +static int cam_icp_mgr_ipe_bps_resume(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data) +{ + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + int rc = 0; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to close"); + return -EINVAL; + } + + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) { + if (hw_mgr->bps_ctxt_cnt++) + goto end; + if (!hw_mgr->bps_clk_state) { + bps_dev_intf->hw_ops.init( + bps_dev_intf->hw_priv, NULL, 0); + hw_mgr->bps_clk_state = true; + } + if (icp_hw_mgr.ipe_bps_pc_flag) { + bps_dev_intf->hw_ops.process_cmd( + bps_dev_intf->hw_priv, + CAM_ICP_BPS_CMD_POWER_RESUME, NULL, 0); + hw_mgr->core_info = hw_mgr->core_info | ICP_PWR_CLP_BPS; + } + } else { + if (hw_mgr->ipe_ctxt_cnt++) + goto end; + if (!hw_mgr->ipe_clk_state) + ipe0_dev_intf->hw_ops.init( + ipe0_dev_intf->hw_priv, NULL, 0); + if (icp_hw_mgr.ipe_bps_pc_flag) { + ipe0_dev_intf->hw_ops.process_cmd( + ipe0_dev_intf->hw_priv, + CAM_ICP_IPE_CMD_POWER_RESUME, NULL, 0); + } + + if ((icp_hw_mgr.ipe1_enable) && + (ipe1_dev_intf) && + (!hw_mgr->ipe_clk_state)) { + ipe1_dev_intf->hw_ops.init(ipe1_dev_intf->hw_priv, + NULL, 0); + + if (icp_hw_mgr.ipe_bps_pc_flag) { + ipe1_dev_intf->hw_ops.process_cmd( + ipe1_dev_intf->hw_priv, + CAM_ICP_IPE_CMD_POWER_RESUME, + NULL, 0); + } + } + hw_mgr->ipe_clk_state = true; + if (icp_hw_mgr.ipe_bps_pc_flag) { + hw_mgr->core_info = hw_mgr->core_info | + (ICP_PWR_CLP_IPE0 | ICP_PWR_CLP_IPE1); + } + } + + CAM_DBG(CAM_ICP, "core_info %X", hw_mgr->core_info); + if (icp_hw_mgr.ipe_bps_pc_flag) + rc = hfi_enable_ipe_bps_pc(true, hw_mgr->core_info); + else if (icp_hw_mgr.icp_pc_flag) + rc = hfi_enable_ipe_bps_pc(false, hw_mgr->core_info); + else + rc = hfi_enable_ipe_bps_pc(false, hw_mgr->core_info); +end: + return rc; +} + +static int cam_icp_mgr_ipe_bps_power_collapse(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, int dev_type) +{ + int rc = 0, dev; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to close"); + return -EINVAL; + } + + if (!ctx_data) + dev = dev_type; + else + dev = ctx_data->icp_dev_acquire_info->dev_type; + + if (dev == CAM_ICP_RES_TYPE_BPS) { + CAM_DBG(CAM_ICP, "bps ctx cnt %d", hw_mgr->bps_ctxt_cnt); + if (ctx_data) + --hw_mgr->bps_ctxt_cnt; + + if (hw_mgr->bps_ctxt_cnt) + goto end; + + if (icp_hw_mgr.ipe_bps_pc_flag) { + rc = bps_dev_intf->hw_ops.process_cmd( + bps_dev_intf->hw_priv, + CAM_ICP_BPS_CMD_POWER_COLLAPSE, + NULL, 0); + hw_mgr->core_info = + hw_mgr->core_info & (~ICP_PWR_CLP_BPS); + } + + if (hw_mgr->bps_clk_state) { + bps_dev_intf->hw_ops.deinit + (bps_dev_intf->hw_priv, NULL, 0); + hw_mgr->bps_clk_state = false; + } + } else { + CAM_DBG(CAM_ICP, "ipe ctx cnt %d", hw_mgr->ipe_ctxt_cnt); + if (ctx_data) + --hw_mgr->ipe_ctxt_cnt; + + if (hw_mgr->ipe_ctxt_cnt) + goto end; + + if (icp_hw_mgr.ipe_bps_pc_flag) { + rc = ipe0_dev_intf->hw_ops.process_cmd( + ipe0_dev_intf->hw_priv, + CAM_ICP_IPE_CMD_POWER_COLLAPSE, NULL, 0); + + } + + if (hw_mgr->ipe_clk_state) + ipe0_dev_intf->hw_ops.deinit( + ipe0_dev_intf->hw_priv, NULL, 0); + + if (ipe1_dev_intf) { + if (icp_hw_mgr.ipe_bps_pc_flag) { + rc = ipe1_dev_intf->hw_ops.process_cmd( + ipe1_dev_intf->hw_priv, + CAM_ICP_IPE_CMD_POWER_COLLAPSE, + NULL, 0); + } + + if (hw_mgr->ipe_clk_state) + ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv, + NULL, 0); + } + + hw_mgr->ipe_clk_state = false; + if (icp_hw_mgr.ipe_bps_pc_flag) { + hw_mgr->core_info = hw_mgr->core_info & + (~(ICP_PWR_CLP_IPE0 | ICP_PWR_CLP_IPE1)); + } + } + + CAM_DBG(CAM_ICP, "Exit: core_info = %x", hw_mgr->core_info); +end: + return rc; +} + +static int cam_icp_set_dbg_default_clk(void *data, u64 val) +{ + icp_hw_mgr.icp_debug_clk = val; + return 0; +} + +static int cam_icp_get_dbg_default_clk(void *data, u64 *val) +{ + *val = icp_hw_mgr.icp_debug_clk; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_default_clk, + cam_icp_get_dbg_default_clk, + cam_icp_set_dbg_default_clk, "%16llu"); + +static int cam_icp_set_a5_dbg_lvl(void *data, u64 val) +{ + icp_hw_mgr.a5_dbg_lvl = val; + return 0; +} + +static int cam_icp_get_a5_dbg_lvl(void *data, u64 *val) +{ + *val = icp_hw_mgr.a5_dbg_lvl; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_fs, cam_icp_get_a5_dbg_lvl, + cam_icp_set_a5_dbg_lvl, "%08llu"); + +static int cam_icp_set_a5_dbg_type(void *data, u64 val) +{ + if (val <= NUM_HFI_DEBUG_MODE) + icp_hw_mgr.a5_debug_type = val; + return 0; +} + +static int cam_icp_get_a5_dbg_type(void *data, u64 *val) +{ + *val = icp_hw_mgr.a5_debug_type; + return 0; +} + + +DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_type_fs, cam_icp_get_a5_dbg_type, + cam_icp_set_a5_dbg_type, "%08llu"); + +static int cam_icp_hw_mgr_create_debugfs_entry(void) +{ + int rc = 0; + + icp_hw_mgr.dentry = debugfs_create_dir("camera_icp", NULL); + if (!icp_hw_mgr.dentry) + return -ENOMEM; + + if (!debugfs_create_bool("icp_pc", + 0644, + icp_hw_mgr.dentry, + &icp_hw_mgr.icp_pc_flag)) { + CAM_ERR(CAM_ICP, "failed to create icp_pc entry"); + rc = -ENOMEM; + goto err; + } + icp_hw_mgr.icp_pc_flag = false; + + if (!debugfs_create_bool("ipe_bps_pc", + 0644, + icp_hw_mgr.dentry, + &icp_hw_mgr.ipe_bps_pc_flag)) { + CAM_ERR(CAM_ICP, "failed to create ipe_bps_pc entry"); + rc = -ENOMEM; + goto err; + } + + icp_hw_mgr.ipe_bps_pc_flag = false; + + if (!debugfs_create_file("icp_debug_clk", + 0644, + icp_hw_mgr.dentry, NULL, + &cam_icp_debug_default_clk)) { + CAM_ERR(CAM_ICP, "failed to create icp_debug_clk entry"); + rc = -ENOMEM; + goto err; + } + + if (!debugfs_create_bool("a5_jtag_debug", + 0644, + icp_hw_mgr.dentry, + &icp_hw_mgr.a5_jtag_debug)) { + rc = -ENOMEM; + goto err; + } + + if (!debugfs_create_file("a5_debug_type", + 0644, + icp_hw_mgr.dentry, + NULL, &cam_icp_debug_type_fs)) { + CAM_ERR(CAM_ICP, "failed to create a5_debug_type\n"); + rc = -ENOMEM; + goto err; + } + + if (!debugfs_create_file("a5_debug_lvl", + 0644, + icp_hw_mgr.dentry, + NULL, &cam_icp_debug_fs)) { + CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl\n"); + rc = -ENOMEM; + goto err; + } + + return rc; +err: + debugfs_remove_recursive(icp_hw_mgr.dentry); + return rc; +} + +static int cam_icp_mgr_process_cmd(void *priv, void *data) +{ + int rc; + struct hfi_cmd_work_data *task_data = NULL; + struct cam_icp_hw_mgr *hw_mgr; + + if (!data || !priv) { + CAM_ERR(CAM_ICP, "Invalid params%pK %pK", data, priv); + return -EINVAL; + } + + hw_mgr = priv; + task_data = (struct hfi_cmd_work_data *)data; + + rc = hfi_write_cmd(task_data->data); + + return rc; +} + +static int cam_icp_mgr_cleanup_ctx(struct cam_icp_hw_ctx_data *ctx_data) +{ + int i; + struct hfi_frame_process_info *hfi_frame_process; + struct cam_hw_done_event_data buf_data; + + hfi_frame_process = &ctx_data->hfi_frame_process; + for (i = 0; i < CAM_FRAME_CMD_MAX; i++) { + if (!hfi_frame_process->request_id[i]) + continue; + buf_data.request_id = hfi_frame_process->request_id[i]; + ctx_data->ctxt_event_cb(ctx_data->context_priv, + false, &buf_data); + hfi_frame_process->request_id[i] = 0; + if (ctx_data->hfi_frame_process.in_resource[i] > 0) { + CAM_DBG(CAM_ICP, "Delete merged sync in object: %d", + ctx_data->hfi_frame_process.in_resource[i]); + cam_sync_destroy( + ctx_data->hfi_frame_process.in_resource[i]); + ctx_data->hfi_frame_process.in_resource[i] = 0; + } + hfi_frame_process->fw_process_flag[i] = false; + clear_bit(i, ctx_data->hfi_frame_process.bitmap); + } + + for (i = 0; i < CAM_FRAME_CMD_MAX; i++) { + if (!hfi_frame_process->in_free_resource[i]) + continue; + + CAM_DBG(CAM_ICP, "Delete merged sync in object: %d", + ctx_data->hfi_frame_process.in_free_resource[i]); + cam_sync_destroy( + ctx_data->hfi_frame_process.in_free_resource[i]); + ctx_data->hfi_frame_process.in_free_resource[i] = 0; + } + + return 0; +} + +static int cam_icp_mgr_handle_frame_process(uint32_t *msg_ptr, int flag) +{ + int i; + uint32_t idx; + uint64_t request_id; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL; + struct hfi_frame_process_info *hfi_frame_process; + struct cam_hw_done_event_data buf_data; + uint32_t clk_type; + + ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr; + request_id = ioconfig_ack->user_data2; + ctx_data = (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "Invalid Context"); + return -EINVAL; + } + CAM_DBG(CAM_ICP, "ctx : %pK, request_id :%lld", + (void *)ctx_data->context_priv, request_id); + + clk_type = ICP_DEV_TYPE_TO_CLK_TYPE(ctx_data->icp_dev_acquire_info-> + dev_type); + cam_icp_device_timer_reset(&icp_hw_mgr, clk_type); + + mutex_lock(&ctx_data->ctx_mutex); + cam_icp_ctx_timer_reset(ctx_data); + if (ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) { + CAM_DBG(CAM_ICP, "ctx %u is in %d state", + ctx_data->ctx_id, ctx_data->state); + mutex_unlock(&ctx_data->ctx_mutex); + return 0; + } + + hfi_frame_process = &ctx_data->hfi_frame_process; + for (i = 0; i < CAM_FRAME_CMD_MAX; i++) + if (hfi_frame_process->request_id[i] == request_id) + break; + + if (i >= CAM_FRAME_CMD_MAX) { + CAM_ERR(CAM_ICP, "pkt not found in ctx data for req_id =%lld", + request_id); + mutex_unlock(&ctx_data->ctx_mutex); + return -EINVAL; + } + idx = i; + + buf_data.request_id = hfi_frame_process->request_id[idx]; + ctx_data->ctxt_event_cb(ctx_data->context_priv, flag, &buf_data); + hfi_frame_process->request_id[idx] = 0; + if (ctx_data->hfi_frame_process.in_resource[idx] > 0) { + CAM_DBG(CAM_ICP, "Delete merged sync in object: %d", + ctx_data->hfi_frame_process.in_resource[idx]); + cam_sync_destroy(ctx_data->hfi_frame_process.in_resource[idx]); + ctx_data->hfi_frame_process.in_resource[idx] = 0; + } + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + hfi_frame_process->fw_process_flag[idx] = false; + mutex_unlock(&ctx_data->ctx_mutex); + + return 0; +} + +static int cam_icp_mgr_process_msg_frame_process(uint32_t *msg_ptr) +{ + struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL; + struct hfi_msg_frame_process_done *frame_done; + + if (!msg_ptr) { + CAM_ERR(CAM_ICP, "msg ptr is NULL"); + return -EINVAL; + } + + ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr; + if (ioconfig_ack->err_type != HFI_ERR_SYS_NONE) { + CAM_ERR(CAM_ICP, "failed with error : %u", + ioconfig_ack->err_type); + return -EIO; + } + + frame_done = + (struct hfi_msg_frame_process_done *)ioconfig_ack->msg_data; + if (!frame_done) { + cam_icp_mgr_handle_frame_process(msg_ptr, + ICP_FRAME_PROCESS_FAILURE); + return -EINVAL; + } + + if (frame_done->result) + return cam_icp_mgr_handle_frame_process(msg_ptr, + ICP_FRAME_PROCESS_FAILURE); + else + return cam_icp_mgr_handle_frame_process(msg_ptr, + ICP_FRAME_PROCESS_SUCCESS); +} + +static int cam_icp_mgr_process_msg_config_io(uint32_t *msg_ptr) +{ + struct cam_icp_hw_ctx_data *ctx_data = NULL; + struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL; + struct hfi_msg_ipe_config *ipe_config_ack = NULL; + struct hfi_msg_bps_common *bps_config_ack = NULL; + + if (!msg_ptr) { + CAM_ERR(CAM_ICP, "msg ptr is NULL"); + return -EINVAL; + } + + ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr; + + if (ioconfig_ack->opcode == HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO) { + ipe_config_ack = + (struct hfi_msg_ipe_config *)(ioconfig_ack->msg_data); + if (ipe_config_ack->rc) { + CAM_ERR(CAM_ICP, "rc = %d err = %u", + ipe_config_ack->rc, ioconfig_ack->err_type); + return -EIO; + } + ctx_data = + (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "wrong ctx data from IPE response"); + return -EINVAL; + } + ctx_data->scratch_mem_size = ipe_config_ack->scratch_mem_size; + } else { + bps_config_ack = + (struct hfi_msg_bps_common *)(ioconfig_ack->msg_data); + if (bps_config_ack->rc) { + CAM_ERR(CAM_ICP, "rc : %u, opcode :%u", + bps_config_ack->rc, ioconfig_ack->opcode); + return -EIO; + } + ctx_data = + (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "wrong ctx data from BPS response"); + return -EINVAL; + } + } + complete(&ctx_data->wait_complete); + + return 0; +} + +static int cam_icp_mgr_process_msg_create_handle(uint32_t *msg_ptr) +{ + struct hfi_msg_create_handle_ack *create_handle_ack = NULL; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + int rc = 0; + + create_handle_ack = (struct hfi_msg_create_handle_ack *)msg_ptr; + if (!create_handle_ack) { + CAM_ERR(CAM_ICP, "Invalid create_handle_ack"); + return -EINVAL; + } + + ctx_data = (struct cam_icp_hw_ctx_data *)create_handle_ack->user_data1; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "Invalid ctx_data"); + return -EINVAL; + } + + if (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE) { + ctx_data->fw_handle = create_handle_ack->fw_handle; + CAM_DBG(CAM_ICP, "fw_handle = %x", ctx_data->fw_handle); + } else { + CAM_WARN(CAM_ICP, + "This ctx is no longer in use current state: %d", + ctx_data->state); + ctx_data->fw_handle = 0; + rc = -EPERM; + } + complete(&ctx_data->wait_complete); + return rc; +} + +static int cam_icp_mgr_process_msg_ping_ack(uint32_t *msg_ptr) +{ + struct hfi_msg_ping_ack *ping_ack = NULL; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + + ping_ack = (struct hfi_msg_ping_ack *)msg_ptr; + if (!ping_ack) { + CAM_ERR(CAM_ICP, "Empty ping ack message"); + return -EINVAL; + } + + ctx_data = (struct cam_icp_hw_ctx_data *)ping_ack->user_data; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "Invalid ctx_data"); + return -EINVAL; + } + + if (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE) + complete(&ctx_data->wait_complete); + + return 0; +} + +static int cam_icp_mgr_process_indirect_ack_msg(uint32_t *msg_ptr) +{ + int rc; + + if (!msg_ptr) { + CAM_ERR(CAM_ICP, "msg ptr is NULL"); + return -EINVAL; + } + + switch (msg_ptr[ICP_PACKET_OPCODE]) { + case HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO: + case HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO: + CAM_DBG(CAM_ICP, "received IPE/BPS_CONFIG_IO:"); + rc = cam_icp_mgr_process_msg_config_io(msg_ptr); + if (rc) + return rc; + break; + + case HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS: + case HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS: + rc = cam_icp_mgr_process_msg_frame_process(msg_ptr); + if (rc) + return rc; + break; + default: + CAM_ERR(CAM_ICP, "Invalid opcode : %u", + msg_ptr[ICP_PACKET_OPCODE]); + rc = -EINVAL; + break; + } + + return rc; +} + +static int cam_icp_mgr_process_direct_ack_msg(uint32_t *msg_ptr) +{ + struct cam_icp_hw_ctx_data *ctx_data = NULL; + struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL; + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + int rc = 0; + + a5_dev_intf = icp_hw_mgr.a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + switch (msg_ptr[ICP_PACKET_OPCODE]) { + case HFI_IPEBPS_CMD_OPCODE_IPE_ABORT: + case HFI_IPEBPS_CMD_OPCODE_BPS_ABORT: + ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr; + ctx_data = + (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1; + if (ctx_data->state != CAM_ICP_CTX_STATE_FREE) + complete(&ctx_data->wait_complete); + CAM_DBG(CAM_ICP, "received IPE/BPS/ ABORT: ctx_state =%d", + ctx_data->state); + break; + case HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY: + case HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY: + ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr; + ctx_data = + (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1; + if ((ctx_data->state == CAM_ICP_CTX_STATE_RELEASE) || + (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE)) { + complete(&ctx_data->wait_complete); + } + CAM_DBG(CAM_ICP, "received IPE/BPS/ DESTROY: ctx_state =%d", + ctx_data->state); + break; + default: + CAM_ERR(CAM_ICP, "Invalid opcode : %u", + msg_ptr[ICP_PACKET_OPCODE]); + rc = -EINVAL; + break; + } + return rc; +} + +static void cam_icp_mgr_process_dbg_buf(void) +{ + uint32_t *msg_ptr = NULL, *pkt_ptr = NULL; + struct hfi_msg_debug *dbg_msg; + uint32_t read_len, size_processed = 0; + char *dbg_buf; + int rc = 0; + + rc = hfi_read_message(icp_hw_mgr.dbg_buf, Q_DBG, &read_len); + if (rc) + return; + + msg_ptr = (uint32_t *)icp_hw_mgr.dbg_buf; + while (true) { + pkt_ptr = msg_ptr; + if (pkt_ptr[ICP_PACKET_TYPE] == HFI_MSG_SYS_DEBUG) { + dbg_msg = (struct hfi_msg_debug *)pkt_ptr; + dbg_buf = (char *)&dbg_msg->msg_data; + trace_cam_icp_fw_dbg(dbg_buf); + } + size_processed += (pkt_ptr[ICP_PACKET_SIZE] >> + BYTE_WORD_SHIFT); + if (size_processed >= read_len) + return; + msg_ptr += (pkt_ptr[ICP_PACKET_SIZE] >> + BYTE_WORD_SHIFT); + pkt_ptr = NULL; + dbg_msg = NULL; + dbg_buf = NULL; + } +} + +static int cam_icp_process_msg_pkt_type( + struct cam_icp_hw_mgr *hw_mgr, + uint32_t *msg_ptr, + uint32_t *msg_processed_len) +{ + int rc = 0; + int size_processed = 0; + + switch (msg_ptr[ICP_PACKET_TYPE]) { + case HFI_MSG_SYS_INIT_DONE: + CAM_DBG(CAM_ICP, "received SYS_INIT_DONE"); + complete(&hw_mgr->a5_complete); + size_processed = ( + (struct hfi_msg_init_done *)msg_ptr)->size; + break; + + case HFI_MSG_SYS_PC_PREP_DONE: + CAM_DBG(CAM_ICP, "HFI_MSG_SYS_PC_PREP_DONE is received\n"); + complete(&hw_mgr->a5_complete); + size_processed = sizeof(struct hfi_msg_pc_prep_done); + break; + + case HFI_MSG_SYS_PING_ACK: + CAM_DBG(CAM_ICP, "received SYS_PING_ACK"); + rc = cam_icp_mgr_process_msg_ping_ack(msg_ptr); + size_processed = sizeof(struct hfi_msg_ping_ack); + break; + + case HFI_MSG_IPEBPS_CREATE_HANDLE_ACK: + CAM_DBG(CAM_ICP, "received IPEBPS_CREATE_HANDLE_ACK"); + rc = cam_icp_mgr_process_msg_create_handle(msg_ptr); + size_processed = sizeof(struct hfi_msg_create_handle_ack); + break; + + case HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK: + CAM_DBG(CAM_ICP, "received ASYNC_INDIRECT_ACK"); + rc = cam_icp_mgr_process_indirect_ack_msg(msg_ptr); + size_processed = ( + (struct hfi_msg_ipebps_async_ack *)msg_ptr)->size; + break; + + case HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK: + CAM_DBG(CAM_ICP, "received ASYNC_DIRECT_ACK"); + rc = cam_icp_mgr_process_direct_ack_msg(msg_ptr); + size_processed = ( + (struct hfi_msg_ipebps_async_ack *)msg_ptr)->size; + break; + + case HFI_MSG_EVENT_NOTIFY: + CAM_DBG(CAM_ICP, "received EVENT_NOTIFY"); + size_processed = ( + (struct hfi_msg_event_notify *)msg_ptr)->size; + break; + + default: + CAM_ERR(CAM_ICP, "invalid msg : %u", + msg_ptr[ICP_PACKET_TYPE]); + rc = -EINVAL; + break; + } + + *msg_processed_len = size_processed; + return rc; +} + +static int32_t cam_icp_mgr_process_msg(void *priv, void *data) +{ + uint32_t read_len, msg_processed_len; + uint32_t *msg_ptr = NULL; + struct hfi_msg_work_data *task_data; + struct cam_icp_hw_mgr *hw_mgr; + int rc = 0; + + if (!data || !priv) { + CAM_ERR(CAM_ICP, "Invalid data"); + return -EINVAL; + } + + task_data = data; + hw_mgr = priv; + + rc = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG, &read_len); + if (rc) { + CAM_DBG(CAM_ICP, "Unable to read msg q"); + } else { + read_len = read_len << BYTE_WORD_SHIFT; + msg_ptr = (uint32_t *)icp_hw_mgr.msg_buf; + while (true) { + cam_icp_process_msg_pkt_type(hw_mgr, msg_ptr, + &msg_processed_len); + + if (!msg_processed_len) { + CAM_ERR(CAM_ICP, "Failed to read"); + rc = -EINVAL; + break; + } + + read_len -= msg_processed_len; + if (read_len > 0) { + msg_ptr += (msg_processed_len >> + BYTE_WORD_SHIFT); + msg_processed_len = 0; + } + else + break; + } + } + + if (icp_hw_mgr.a5_debug_type == + HFI_DEBUG_MODE_QUEUE) + cam_icp_mgr_process_dbg_buf(); + + return rc; +} + +int32_t cam_icp_hw_mgr_cb(uint32_t irq_status, void *data) +{ + int32_t rc = 0; + unsigned long flags; + struct cam_icp_hw_mgr *hw_mgr = data; + struct crm_workq_task *task; + struct hfi_msg_work_data *task_data; + + spin_lock_irqsave(&hw_mgr->hw_mgr_lock, flags); + task = cam_req_mgr_workq_get_task(icp_hw_mgr.msg_work); + if (!task) { + CAM_ERR(CAM_ICP, "no empty task"); + spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags); + return -ENOMEM; + } + + task_data = (struct hfi_msg_work_data *)task->payload; + task_data->data = hw_mgr; + task_data->irq_status = irq_status; + task_data->type = ICP_WORKQ_TASK_MSG_TYPE; + task->process_cb = cam_icp_mgr_process_msg; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags); + + return rc; +} + +static void cam_icp_free_hfi_mem(void) +{ + int rc; + cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl); + rc = cam_mem_mgr_free_memory_region(&icp_hw_mgr.hfi_mem.sec_heap); + if (rc) + CAM_ERR(CAM_ICP, "failed to unreserve sec heap"); + + cam_smmu_dealloc_qdss(icp_hw_mgr.iommu_hdl); + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl); + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.cmd_q); + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q); + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q); +} + +static int cam_icp_alloc_secheap_mem(struct cam_mem_mgr_memory_desc *secheap) +{ + int rc; + struct cam_mem_mgr_request_desc alloc; + struct cam_mem_mgr_memory_desc out; + struct cam_smmu_region_info secheap_info; + + memset(&alloc, 0, sizeof(alloc)); + memset(&out, 0, sizeof(out)); + + rc = cam_smmu_get_region_info(icp_hw_mgr.iommu_hdl, + CAM_SMMU_REGION_SECHEAP, + &secheap_info); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to get secheap memory info"); + return rc; + } + + alloc.size = secheap_info.iova_len; + alloc.align = 0; + alloc.flags = 0; + alloc.smmu_hdl = icp_hw_mgr.iommu_hdl; + rc = cam_mem_mgr_reserve_memory_region(&alloc, + CAM_SMMU_REGION_SECHEAP, + &out); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to reserve secheap memory"); + return rc; + } + + *secheap = out; + CAM_DBG(CAM_ICP, "kva: %llX, iova: %x, hdl: %x, len: %lld", + out.kva, out.iova, out.mem_handle, out.len); + + return rc; +} + +static int cam_icp_alloc_shared_mem(struct cam_mem_mgr_memory_desc *qtbl) +{ + int rc; + struct cam_mem_mgr_request_desc alloc; + struct cam_mem_mgr_memory_desc out; + + memset(&alloc, 0, sizeof(alloc)); + memset(&out, 0, sizeof(out)); + alloc.size = SZ_1M; + alloc.align = 0; + alloc.flags = CAM_MEM_FLAG_HW_READ_WRITE | + CAM_MEM_FLAG_HW_SHARED_ACCESS; + alloc.smmu_hdl = icp_hw_mgr.iommu_hdl; + rc = cam_mem_mgr_request_mem(&alloc, &out); + if (rc) + return rc; + + *qtbl = out; + CAM_DBG(CAM_ICP, "kva: %llX, iova: %x, hdl: %x, len: %lld", + out.kva, out.iova, out.mem_handle, out.len); + + return rc; +} + +static int cam_icp_allocate_fw_mem(void) +{ + int rc; + uintptr_t kvaddr; + size_t len; + dma_addr_t iova; + + rc = cam_smmu_alloc_firmware(icp_hw_mgr.iommu_hdl, + &iova, &kvaddr, &len); + if (rc) + return -ENOMEM; + + icp_hw_mgr.hfi_mem.fw_buf.len = len; + icp_hw_mgr.hfi_mem.fw_buf.kva = kvaddr; + icp_hw_mgr.hfi_mem.fw_buf.iova = iova; + icp_hw_mgr.hfi_mem.fw_buf.smmu_hdl = icp_hw_mgr.iommu_hdl; + + CAM_DBG(CAM_ICP, "kva: %zX, iova: %llx, len: %zu", + kvaddr, iova, len); + + return rc; +} + +static int cam_icp_allocate_qdss_mem(void) +{ + int rc; + size_t len; + dma_addr_t iova; + + rc = cam_smmu_alloc_qdss(icp_hw_mgr.iommu_hdl, + &iova, &len); + if (rc) + return rc; + + icp_hw_mgr.hfi_mem.qdss_buf.len = len; + icp_hw_mgr.hfi_mem.qdss_buf.iova = iova; + icp_hw_mgr.hfi_mem.qdss_buf.smmu_hdl = icp_hw_mgr.iommu_hdl; + + CAM_DBG(CAM_ICP, "iova: %llx, len: %zu", iova, len); + + return rc; +} + +static int cam_icp_get_io_mem_info(void) +{ + int rc; + size_t len, discard_iova_len; + dma_addr_t iova, discard_iova_start; + + rc = cam_smmu_get_io_region_info(icp_hw_mgr.iommu_hdl, + &iova, &len, &discard_iova_start, &discard_iova_len); + if (rc) + return rc; + + icp_hw_mgr.hfi_mem.io_mem.iova_len = len; + icp_hw_mgr.hfi_mem.io_mem.iova_start = iova; + icp_hw_mgr.hfi_mem.io_mem.discard_iova_start = discard_iova_start; + icp_hw_mgr.hfi_mem.io_mem.discard_iova_len = discard_iova_len; + + CAM_DBG(CAM_ICP, "iova: %llx, len: %zu discard iova %llx len %llx", + iova, len, discard_iova_start, discard_iova_len); + + return rc; +} + +static int cam_icp_allocate_hfi_mem(void) +{ + int rc; + + rc = cam_smmu_get_region_info(icp_hw_mgr.iommu_hdl, + CAM_SMMU_REGION_SHARED, + &icp_hw_mgr.hfi_mem.shmem); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to get shared memory info"); + return rc; + } + + rc = cam_icp_allocate_fw_mem(); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate FW memory"); + return rc; + } + + rc = cam_icp_allocate_qdss_mem(); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate qdss memory"); + goto fw_alloc_failed; + } + + rc = cam_icp_alloc_shared_mem(&icp_hw_mgr.hfi_mem.qtbl); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate qtbl memory"); + goto qtbl_alloc_failed; + } + + rc = cam_icp_alloc_shared_mem(&icp_hw_mgr.hfi_mem.cmd_q); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate cmd q memory"); + goto cmd_q_alloc_failed; + } + + rc = cam_icp_alloc_shared_mem(&icp_hw_mgr.hfi_mem.msg_q); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate msg q memory"); + goto msg_q_alloc_failed; + } + + rc = cam_icp_alloc_shared_mem(&icp_hw_mgr.hfi_mem.dbg_q); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate dbg q memory"); + goto dbg_q_alloc_failed; + } + + rc = cam_icp_alloc_secheap_mem(&icp_hw_mgr.hfi_mem.sec_heap); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to allocate sec heap memory"); + goto sec_heap_alloc_failed; + } + + rc = cam_icp_get_io_mem_info(); + if (rc) { + CAM_ERR(CAM_ICP, "Unable to get I/O region info"); + goto get_io_mem_failed; + } + + return rc; +get_io_mem_failed: + cam_mem_mgr_free_memory_region(&icp_hw_mgr.hfi_mem.sec_heap); +sec_heap_alloc_failed: + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q); +dbg_q_alloc_failed: + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q); +msg_q_alloc_failed: + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.cmd_q); +cmd_q_alloc_failed: + cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl); +qtbl_alloc_failed: + cam_smmu_dealloc_qdss(icp_hw_mgr.iommu_hdl); +fw_alloc_failed: + cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl); + return rc; +} + +static int cam_icp_mgr_get_free_ctx(struct cam_icp_hw_mgr *hw_mgr) +{ + int i = 0; + + for (i = 0; i < CAM_ICP_CTX_MAX; i++) { + mutex_lock(&hw_mgr->ctx_data[i].ctx_mutex); + if (hw_mgr->ctx_data[i].state == CAM_ICP_CTX_STATE_FREE) { + hw_mgr->ctx_data[i].state = CAM_ICP_CTX_STATE_IN_USE; + mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex); + break; + } + mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex); + } + + return i; +} + +static void cam_icp_mgr_put_ctx(struct cam_icp_hw_ctx_data *ctx_data) +{ + ctx_data->state = CAM_ICP_CTX_STATE_FREE; +} + +static int cam_icp_mgr_send_pc_prep(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc; + struct cam_hw_intf *a5_dev_intf = NULL; + unsigned long rem_jiffies; + int timeout = 5000; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid\n"); + return -EINVAL; + } + + reinit_completion(&hw_mgr->a5_complete); + CAM_DBG(CAM_ICP, "Sending HFI init command"); + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, CAM_ICP_A5_CMD_PC_PREP, NULL, 0); + if (rc) + return rc; + + CAM_DBG(CAM_ICP, "Wait for PC_PREP_DONE Message\n"); + rem_jiffies = wait_for_completion_timeout(&icp_hw_mgr.a5_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "PC_PREP response timed out %d\n", rc); + } + CAM_DBG(CAM_ICP, "Done Waiting for PC_PREP Message\n"); + + return rc; +} + +static int cam_ipe_bps_deint(struct cam_icp_hw_mgr *hw_mgr) +{ + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + if ((!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to close"); + return 0; + } + + if (ipe1_dev_intf && hw_mgr->ipe_clk_state) { + ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv, + NULL, 0); + } + + if (hw_mgr->ipe_clk_state) + ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0); + if (hw_mgr->bps_clk_state) + bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0); + + + hw_mgr->bps_clk_state = false; + hw_mgr->ipe_clk_state = false; + + return 0; +} + +static int cam_icp_mgr_hw_close_u(void *hw_priv, void *hw_close_args) +{ + struct cam_icp_hw_mgr *hw_mgr = hw_priv; + int rc = 0; + + if (!hw_mgr) { + CAM_ERR(CAM_ICP, "Null hw mgr"); + return 0; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + rc = cam_icp_mgr_hw_close(hw_mgr, NULL); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_icp_mgr_hw_close_k(void *hw_priv, void *hw_close_args) +{ + struct cam_icp_hw_mgr *hw_mgr = hw_priv; + + if (!hw_mgr) { + CAM_ERR(CAM_ICP, "Null hw mgr"); + return 0; + } + + return cam_icp_mgr_hw_close(hw_mgr, NULL); + +} + +static int cam_icp_mgr_icp_power_collapse(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc; + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + + CAM_DBG(CAM_ICP, "ENTER"); + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid\n"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + + if (!hw_mgr->icp_pc_flag) { + cam_hfi_disable_cpu( + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); + rc = cam_icp_mgr_hw_close_k(hw_mgr, NULL); + } else { + rc = cam_icp_mgr_send_pc_prep(hw_mgr); + cam_hfi_disable_cpu( + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); + } + a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0); + CAM_DBG(CAM_ICP, "EXIT"); + + return rc; +} + +static int cam_icp_mgr_hfi_resume(struct cam_icp_hw_mgr *hw_mgr) +{ + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + struct hfi_mem_info hfi_mem; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid\n"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + + hfi_mem.qtbl.kva = icp_hw_mgr.hfi_mem.qtbl.kva; + hfi_mem.qtbl.iova = icp_hw_mgr.hfi_mem.qtbl.iova; + hfi_mem.qtbl.len = icp_hw_mgr.hfi_mem.qtbl.len; + CAM_DBG(CAM_ICP, "kva = %llX IOVA = %X length = %lld\n", + hfi_mem.qtbl.kva, hfi_mem.qtbl.iova, hfi_mem.qtbl.len); + + hfi_mem.cmd_q.kva = icp_hw_mgr.hfi_mem.cmd_q.kva; + hfi_mem.cmd_q.iova = icp_hw_mgr.hfi_mem.cmd_q.iova; + hfi_mem.cmd_q.len = icp_hw_mgr.hfi_mem.cmd_q.len; + CAM_DBG(CAM_ICP, "kva = %llX IOVA = %X length = %lld\n", + hfi_mem.cmd_q.kva, hfi_mem.cmd_q.iova, hfi_mem.cmd_q.len); + + hfi_mem.msg_q.kva = icp_hw_mgr.hfi_mem.msg_q.kva; + hfi_mem.msg_q.iova = icp_hw_mgr.hfi_mem.msg_q.iova; + hfi_mem.msg_q.len = icp_hw_mgr.hfi_mem.msg_q.len; + CAM_DBG(CAM_ICP, "kva = %llX IOVA = %X length = %lld\n", + hfi_mem.msg_q.kva, hfi_mem.msg_q.iova, hfi_mem.msg_q.len); + + hfi_mem.dbg_q.kva = icp_hw_mgr.hfi_mem.dbg_q.kva; + hfi_mem.dbg_q.iova = icp_hw_mgr.hfi_mem.dbg_q.iova; + hfi_mem.dbg_q.len = icp_hw_mgr.hfi_mem.dbg_q.len; + CAM_DBG(CAM_ICP, "kva = %llX IOVA = %X length = %lld\n", + hfi_mem.dbg_q.kva, hfi_mem.dbg_q.iova, hfi_mem.dbg_q.len); + + hfi_mem.sec_heap.kva = icp_hw_mgr.hfi_mem.sec_heap.kva; + hfi_mem.sec_heap.iova = icp_hw_mgr.hfi_mem.sec_heap.iova; + hfi_mem.sec_heap.len = icp_hw_mgr.hfi_mem.sec_heap.len; + + hfi_mem.shmem.iova = icp_hw_mgr.hfi_mem.shmem.iova_start; + hfi_mem.shmem.len = icp_hw_mgr.hfi_mem.shmem.iova_len; + + hfi_mem.qdss.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova; + hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len; + return cam_hfi_resume(&hfi_mem, + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base, + hw_mgr->a5_jtag_debug); +} + +static int cam_icp_mgr_abort_handle( + struct cam_icp_hw_ctx_data *ctx_data) +{ + int rc = 0; + unsigned long rem_jiffies; + size_t packet_size; + int timeout = 1000; + struct hfi_cmd_work_data *task_data; + struct hfi_cmd_ipebps_async *abort_cmd; + struct crm_workq_task *task; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) + return -ENOMEM; + + packet_size = + sizeof(struct hfi_cmd_ipebps_async) + + sizeof(struct hfi_cmd_abort) - + sizeof(((struct hfi_cmd_ipebps_async *)0)->payload.direct); + abort_cmd = kzalloc(packet_size, GFP_KERNEL); + CAM_DBG(CAM_ICP, "abort pkt size = %d", (int) packet_size); + if (!abort_cmd) { + rc = -ENOMEM; + return rc; + } + + abort_cmd->size = packet_size; + abort_cmd->pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_DIRECT; + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + abort_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_BPS_ABORT; + else + abort_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_IPE_ABORT; + + reinit_completion(&ctx_data->wait_complete); + abort_cmd->num_fw_handles = 1; + abort_cmd->fw_handles[0] = ctx_data->fw_handle; + abort_cmd->user_data1 = (uint64_t)ctx_data; + abort_cmd->user_data2 = (uint64_t)0x0; + + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)abort_cmd; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) { + kfree(abort_cmd); + return rc; + } + CAM_DBG(CAM_ICP, "fw_handle = %x ctx_data = %pK", + ctx_data->fw_handle, ctx_data); + rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW timeout/err in abort handle command"); + } else { + //workq won't do free,if no timeout means + //cmd execution finished,so just release it. + kfree(abort_cmd); + } + + return rc; +} + +static int cam_icp_mgr_destroy_handle( + struct cam_icp_hw_ctx_data *ctx_data) +{ + int rc = 0; + int timeout = 1000; + unsigned long rem_jiffies; + size_t packet_size; + struct hfi_cmd_work_data *task_data; + struct hfi_cmd_ipebps_async *destroy_cmd; + struct crm_workq_task *task; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) + return -ENOMEM; + + packet_size = + sizeof(struct hfi_cmd_ipebps_async) + + sizeof(struct hfi_cmd_abort_destroy) - + sizeof(((struct hfi_cmd_ipebps_async *)0)->payload.direct); + destroy_cmd = kzalloc(packet_size, GFP_KERNEL); + if (!destroy_cmd) { + rc = -ENOMEM; + return rc; + } + + destroy_cmd->size = packet_size; + destroy_cmd->pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_DIRECT; + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + destroy_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY; + else + destroy_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY; + + reinit_completion(&ctx_data->wait_complete); + destroy_cmd->num_fw_handles = 1; + destroy_cmd->fw_handles[0] = ctx_data->fw_handle; + destroy_cmd->user_data1 = (uint64_t)ctx_data; + destroy_cmd->user_data2 = (uint64_t)0x0; + memcpy(destroy_cmd->payload.direct, &ctx_data->temp_payload, + sizeof(uint64_t)); + + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)destroy_cmd; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) { + kfree(destroy_cmd); + return rc; + } + CAM_DBG(CAM_ICP, "fw_handle = %x ctx_data = %pK", + ctx_data->fw_handle, ctx_data); + rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW response timeout: %d for %u", + rc, ctx_data->ctx_id); + if (icp_hw_mgr.a5_debug_type == + HFI_DEBUG_MODE_QUEUE) + cam_icp_mgr_process_dbg_buf(); + } else { + //workq won't do free,if no timeout means + //cmd execution finished,so just release it. + kfree(destroy_cmd); + } + + return rc; +} + +static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id) +{ + int i = 0; + + if (ctx_id >= CAM_ICP_CTX_MAX) { + CAM_ERR(CAM_ICP, "ctx_id is wrong: %d", ctx_id); + return -EINVAL; + } + + mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + if (hw_mgr->ctx_data[ctx_id].state != + CAM_ICP_CTX_STATE_ACQUIRED) { + mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + CAM_DBG(CAM_ICP, + "ctx with id: %d not in right state to release: %d", + ctx_id, hw_mgr->ctx_data[ctx_id].state); + return 0; + } + cam_icp_mgr_ipe_bps_power_collapse(hw_mgr, + &hw_mgr->ctx_data[ctx_id], 0); + hw_mgr->ctx_data[ctx_id].state = CAM_ICP_CTX_STATE_RELEASE; + CAM_DBG(CAM_ICP, "E: ctx_id = %d", ctx_id); + cam_icp_mgr_abort_handle(&hw_mgr->ctx_data[ctx_id]); + cam_icp_mgr_destroy_handle(&hw_mgr->ctx_data[ctx_id]); + cam_icp_mgr_cleanup_ctx(&hw_mgr->ctx_data[ctx_id]); + + hw_mgr->ctx_data[ctx_id].fw_handle = 0; + hw_mgr->ctx_data[ctx_id].scratch_mem_size = 0; + for (i = 0; i < CAM_FRAME_CMD_MAX; i++) + clear_bit(i, hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap); + kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap); + hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap = NULL; + cam_icp_hw_mgr_clk_info_update(hw_mgr, &hw_mgr->ctx_data[ctx_id]); + hw_mgr->ctx_data[ctx_id].clk_info.curr_fc = 0; + hw_mgr->ctx_data[ctx_id].clk_info.base_clk = 0; + hw_mgr->ctxt_cnt--; + kfree(hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info); + hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info = NULL; + hw_mgr->ctx_data[ctx_id].state = CAM_ICP_CTX_STATE_FREE; + cam_icp_ctx_timer_stop(&hw_mgr->ctx_data[ctx_id]); + mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + + CAM_DBG(CAM_ICP, "X: ctx_id = %d", ctx_id); + return 0; +} + +static void cam_icp_mgr_device_deinit(struct cam_icp_hw_mgr *hw_mgr) +{ + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + + a5_dev_intf = hw_mgr->a5_dev_intf; + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to close"); + return; + } + + if (ipe1_dev_intf) + ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv, NULL, 0); + ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0); + bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0); + a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0); + hw_mgr->bps_clk_state = false; + hw_mgr->ipe_clk_state = false; +} + +static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args) +{ + struct cam_icp_hw_mgr *hw_mgr = hw_priv; + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + struct cam_icp_a5_set_irq_cb irq_cb; + struct cam_icp_a5_set_fw_buf_info fw_buf_info; + int rc = 0; + + CAM_DBG(CAM_ICP, "E"); + if (hw_mgr->fw_download == false) { + CAM_DBG(CAM_ICP, "hw mgr is already closed"); + return 0; + } + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_DBG(CAM_ICP, "a5_dev_intf is NULL"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + fw_buf_info.kva = 0; + fw_buf_info.iova = 0; + fw_buf_info.len = 0; + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_CMD_SET_FW_BUF, + &fw_buf_info, + sizeof(fw_buf_info)); + if (rc) + CAM_ERR(CAM_ICP, "nullify the fw buf failed"); + cam_hfi_deinit( + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); + irq_cb.icp_hw_mgr_cb = NULL; + irq_cb.data = NULL; + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) + CAM_ERR(CAM_ICP, "deregister irq call back failed"); + + cam_icp_free_hfi_mem(); + hw_mgr->fw_download = false; + hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE; + + CAM_DBG(CAM_ICP, "Exit"); + return rc; +} + +static int cam_icp_mgr_device_init(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc = 0; + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_intf *ipe0_dev_intf = NULL; + struct cam_hw_intf *ipe1_dev_intf = NULL; + struct cam_hw_intf *bps_dev_intf = NULL; + + a5_dev_intf = hw_mgr->a5_dev_intf; + ipe0_dev_intf = hw_mgr->ipe0_dev_intf; + ipe1_dev_intf = hw_mgr->ipe1_dev_intf; + bps_dev_intf = hw_mgr->bps_dev_intf; + + if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) { + CAM_ERR(CAM_ICP, "dev intfs are wrong"); + return -EINVAL; + } + + rc = a5_dev_intf->hw_ops.init(a5_dev_intf->hw_priv, NULL, 0); + if (rc) + goto a5_dev_init_failed; + + rc = bps_dev_intf->hw_ops.init(bps_dev_intf->hw_priv, NULL, 0); + if (rc) + goto bps_dev_init_failed; + + rc = ipe0_dev_intf->hw_ops.init(ipe0_dev_intf->hw_priv, NULL, 0); + if (rc) + goto ipe0_dev_init_failed; + + if (ipe1_dev_intf) { + rc = ipe1_dev_intf->hw_ops.init(ipe1_dev_intf->hw_priv, + NULL, 0); + if (rc) + goto ipe1_dev_init_failed; + } + + hw_mgr->bps_clk_state = true; + hw_mgr->ipe_clk_state = true; + + return rc; +ipe1_dev_init_failed: + ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0); + hw_mgr->ipe_clk_state = false; +ipe0_dev_init_failed: + bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0); + hw_mgr->bps_clk_state = false; +bps_dev_init_failed: + a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0); +a5_dev_init_failed: + return rc; +} + +static int cam_icp_mgr_fw_download(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc; + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + struct cam_icp_a5_set_irq_cb irq_cb; + struct cam_icp_a5_set_fw_buf_info fw_buf_info; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + + irq_cb.icp_hw_mgr_cb = cam_icp_hw_mgr_cb; + irq_cb.data = hw_mgr; + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) + goto set_irq_failed; + + fw_buf_info.kva = icp_hw_mgr.hfi_mem.fw_buf.kva; + fw_buf_info.iova = icp_hw_mgr.hfi_mem.fw_buf.iova; + fw_buf_info.len = icp_hw_mgr.hfi_mem.fw_buf.len; + + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_CMD_SET_FW_BUF, + &fw_buf_info, sizeof(fw_buf_info)); + if (rc) + goto set_irq_failed; + + cam_hfi_enable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); + + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_CMD_FW_DOWNLOAD, + NULL, 0); + if (rc) + goto fw_download_failed; + + return rc; +fw_download_failed: + cam_hfi_disable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); +set_irq_failed: + return rc; +} + +static int cam_icp_mgr_hfi_init(struct cam_icp_hw_mgr *hw_mgr) +{ + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + struct hfi_mem_info hfi_mem; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + + hfi_mem.qtbl.kva = icp_hw_mgr.hfi_mem.qtbl.kva; + hfi_mem.qtbl.iova = icp_hw_mgr.hfi_mem.qtbl.iova; + hfi_mem.qtbl.len = icp_hw_mgr.hfi_mem.qtbl.len; + + hfi_mem.cmd_q.kva = icp_hw_mgr.hfi_mem.cmd_q.kva; + hfi_mem.cmd_q.iova = icp_hw_mgr.hfi_mem.cmd_q.iova; + hfi_mem.cmd_q.len = icp_hw_mgr.hfi_mem.cmd_q.len; + + hfi_mem.msg_q.kva = icp_hw_mgr.hfi_mem.msg_q.kva; + hfi_mem.msg_q.iova = icp_hw_mgr.hfi_mem.msg_q.iova; + hfi_mem.msg_q.len = icp_hw_mgr.hfi_mem.msg_q.len; + + hfi_mem.dbg_q.kva = icp_hw_mgr.hfi_mem.dbg_q.kva; + hfi_mem.dbg_q.iova = icp_hw_mgr.hfi_mem.dbg_q.iova; + hfi_mem.dbg_q.len = icp_hw_mgr.hfi_mem.dbg_q.len; + + hfi_mem.sec_heap.kva = icp_hw_mgr.hfi_mem.sec_heap.kva; + hfi_mem.sec_heap.iova = icp_hw_mgr.hfi_mem.sec_heap.iova; + hfi_mem.sec_heap.len = icp_hw_mgr.hfi_mem.sec_heap.len; + + hfi_mem.shmem.iova = icp_hw_mgr.hfi_mem.shmem.iova_start; + hfi_mem.shmem.len = icp_hw_mgr.hfi_mem.shmem.iova_len; + + hfi_mem.qdss.iova = icp_hw_mgr.hfi_mem.qdss_buf.iova; + hfi_mem.qdss.len = icp_hw_mgr.hfi_mem.qdss_buf.len; + + return cam_hfi_init(0, &hfi_mem, + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base, + hw_mgr->a5_jtag_debug); +} + +static int cam_icp_mgr_send_fw_init(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc; + struct cam_hw_intf *a5_dev_intf = NULL; + unsigned long rem_jiffies; + int timeout = 5000; + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid"); + return -EINVAL; + } + + reinit_completion(&hw_mgr->a5_complete); + CAM_DBG(CAM_ICP, "Sending HFI init command"); + rc = a5_dev_intf->hw_ops.process_cmd( + a5_dev_intf->hw_priv, + CAM_ICP_A5_SEND_INIT, + NULL, 0); + if (rc) + return rc; + + rem_jiffies = wait_for_completion_timeout(&icp_hw_mgr.a5_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW response timed out %d", rc); + } + CAM_DBG(CAM_ICP, "Done Waiting for INIT DONE Message"); + + return rc; +} + +static int cam_icp_mgr_hw_open_u(void *hw_mgr_priv, void *download_fw_args) +{ + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + int rc = 0; + + if (!hw_mgr) { + CAM_ERR(CAM_ICP, "Null hw mgr"); + return 0; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + rc = cam_icp_mgr_hw_open(hw_mgr, download_fw_args); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_icp_mgr_hw_open_k(void *hw_mgr_priv, void *download_fw_args) +{ + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + + if (!hw_mgr) { + CAM_ERR(CAM_ICP, "Null hw mgr"); + return 0; + } + + return cam_icp_mgr_hw_open(hw_mgr, download_fw_args); +} + +static int cam_icp_mgr_icp_resume(struct cam_icp_hw_mgr *hw_mgr) +{ + int rc = 0; + struct cam_hw_intf *a5_dev_intf = NULL; + bool downloadFromResume = true; + + CAM_DBG(CAM_ICP, "Enter"); + a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0]; + + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5 dev intf is wrong"); + return -EINVAL; + } + + if (hw_mgr->fw_download == false) { + CAM_DBG(CAM_ICP, "Downloading FW"); + rc = cam_icp_mgr_hw_open_k(hw_mgr, &downloadFromResume); + CAM_DBG(CAM_ICP, "FW Download Done Exit"); + return rc; + } + + rc = a5_dev_intf->hw_ops.init(a5_dev_intf->hw_priv, NULL, 0); + if (rc) + return -EINVAL; + + rc = cam_icp_mgr_hfi_resume(hw_mgr); + if (rc) + goto hfi_resume_failed; + + CAM_DBG(CAM_ICP, "Exit"); + return rc; +hfi_resume_failed: + cam_icp_mgr_icp_power_collapse(hw_mgr); + return rc; +} + +static int cam_icp_mgr_hw_open(void *hw_mgr_priv, void *download_fw_args) +{ + struct cam_hw_intf *a5_dev_intf = NULL; + struct cam_hw_info *a5_dev = NULL; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + bool icp_pc = false; + int rc = 0; + + if (!hw_mgr) { + CAM_ERR(CAM_ICP, "hw_mgr is NULL"); + return -EINVAL; + } + + if (hw_mgr->fw_download) { + CAM_DBG(CAM_ICP, "FW already downloaded"); + return rc; + } + + a5_dev_intf = hw_mgr->a5_dev_intf; + if (!a5_dev_intf) { + CAM_ERR(CAM_ICP, "a5_dev_intf is invalid"); + return -EINVAL; + } + a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv; + rc = cam_icp_allocate_hfi_mem(); + if (rc) + goto alloc_hfi_mem_failed; + + rc = cam_icp_mgr_device_init(hw_mgr); + if (rc) + goto dev_init_fail; + + rc = cam_icp_mgr_fw_download(hw_mgr); + if (rc) + goto fw_download_failed; + + rc = cam_icp_mgr_hfi_init(hw_mgr); + if (rc) + goto hfi_init_failed; + + rc = cam_icp_mgr_send_fw_init(hw_mgr); + if (rc) + goto fw_init_failed; + + hw_mgr->ctxt_cnt = 0; + hw_mgr->fw_download = true; + + CAM_INFO(CAM_ICP, "FW download done successfully"); + + rc = cam_ipe_bps_deint(hw_mgr); + if (download_fw_args) + icp_pc = *((bool *)download_fw_args); + + if (download_fw_args && icp_pc == true && hw_mgr->icp_pc_flag) { + rc = cam_ipe_bps_deint(hw_mgr); + CAM_DBG(CAM_ICP, "deinit all clocks"); + } + + if (download_fw_args && icp_pc == true) + return rc; + + rc = cam_ipe_bps_deint(hw_mgr); + rc = cam_icp_mgr_icp_power_collapse(hw_mgr); + CAM_DBG(CAM_ICP, "deinit all clocks at boot up"); + + return rc; + +fw_init_failed: + cam_hfi_deinit( + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); +hfi_init_failed: + cam_hfi_disable_cpu( + a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base); +fw_download_failed: + cam_icp_mgr_device_deinit(hw_mgr); +dev_init_fail: + cam_icp_free_hfi_mem(); +alloc_hfi_mem_failed: + return rc; +} + +static int cam_icp_mgr_handle_config_err( + struct cam_hw_config_args *config_args, + struct cam_icp_hw_ctx_data *ctx_data, + int idx) +{ + struct cam_hw_done_event_data buf_data; + + buf_data.request_id = *(uint64_t *)config_args->priv; + ctx_data->ctxt_event_cb(ctx_data->context_priv, false, &buf_data); + + ctx_data->hfi_frame_process.request_id[idx] = 0; + ctx_data->hfi_frame_process.fw_process_flag[idx] = false; + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + + return 0; +} + +static int cam_icp_mgr_enqueue_config(struct cam_icp_hw_mgr *hw_mgr, + struct cam_hw_config_args *config_args) +{ + int rc = 0; + uint64_t request_id = 0; + struct crm_workq_task *task; + struct hfi_cmd_work_data *task_data; + struct hfi_cmd_ipebps_async *hfi_cmd; + struct cam_hw_update_entry *hw_update_entries; + + request_id = *(uint64_t *)config_args->priv; + hw_update_entries = config_args->hw_update_entries; + CAM_DBG(CAM_ICP, "req_id = %lld %pK", request_id, config_args->priv); + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) { + CAM_ERR(CAM_ICP, "no empty task"); + return -ENOMEM; + } + + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)hw_update_entries->addr; + hfi_cmd = (struct hfi_cmd_ipebps_async *)hw_update_entries->addr; + task_data->request_id = request_id; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + + return rc; +} + +static int cam_icp_mgr_config_hw(void *hw_mgr_priv, void *config_hw_args) +{ + int rc = 0; + int idx; + uint64_t req_id; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_config_args *config_args = config_hw_args; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + + if (!hw_mgr || !config_args) { + CAM_ERR(CAM_ICP, "Invalid arguments %pK %pK", + hw_mgr, config_args); + return -EINVAL; + } + + if (!config_args->num_hw_update_entries) { + CAM_ERR(CAM_ICP, "No hw update enteries are available"); + return -EINVAL; + } + + ctx_data = config_args->ctxt_to_hw_map; + mutex_lock(&hw_mgr->hw_mgr_mutex); + mutex_lock(&ctx_data->ctx_mutex); + if (ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) { + mutex_unlock(&ctx_data->ctx_mutex); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_ERR(CAM_ICP, "ctx id :%u is not in use", + ctx_data->ctx_id); + return -EINVAL; + } + + req_id = *(uint64_t *)config_args->priv; + idx = cam_icp_clk_idx_from_req_id(ctx_data, req_id); + ctx_data->hfi_frame_process.fw_process_flag[idx] = true; + cam_icp_mgr_ipe_bps_clk_update(hw_mgr, ctx_data, idx); + + rc = cam_icp_mgr_enqueue_config(hw_mgr, config_args); + if (rc) + goto config_err; + CAM_DBG(CAM_ICP, "req_id = %lld %u", + req_id, ctx_data->ctx_id); + mutex_unlock(&ctx_data->ctx_mutex); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return 0; +config_err: + cam_icp_mgr_handle_config_err(config_args, ctx_data, idx); + mutex_unlock(&ctx_data->ctx_mutex); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int cam_icp_mgr_prepare_frame_process_cmd( + struct cam_icp_hw_ctx_data *ctx_data, + struct hfi_cmd_ipebps_async *hfi_cmd, + uint64_t request_id, + uint32_t fw_cmd_buf_iova_addr) +{ + hfi_cmd->size = sizeof(struct hfi_cmd_ipebps_async); + hfi_cmd->pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT; + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + hfi_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_BPS_FRAME_PROCESS; + else + hfi_cmd->opcode = HFI_IPEBPS_CMD_OPCODE_IPE_FRAME_PROCESS; + hfi_cmd->num_fw_handles = 1; + hfi_cmd->fw_handles[0] = ctx_data->fw_handle; + hfi_cmd->payload.indirect = fw_cmd_buf_iova_addr; + hfi_cmd->user_data1 = (uint64_t)ctx_data; + hfi_cmd->user_data2 = request_id; + + CAM_DBG(CAM_ICP, "ctx_data : %pK, request_id :%lld cmd_buf %x", + (void *)ctx_data->context_priv, request_id, + fw_cmd_buf_iova_addr); + + return 0; +} + +static int cam_icp_mgr_pkt_validation(struct cam_packet *packet) +{ + if (((packet->header.op_code & 0xff) != + CAM_ICP_OPCODE_IPE_UPDATE) && + ((packet->header.op_code & 0xff) != + CAM_ICP_OPCODE_BPS_UPDATE)) { + CAM_ERR(CAM_ICP, "Invalid Opcode in pkt: %d", + packet->header.op_code & 0xff); + return -EINVAL; + } + + if (packet->num_io_configs > IPE_IO_IMAGES_MAX) { + CAM_ERR(CAM_ICP, "Invalid number of io configs: %d %d", + IPE_IO_IMAGES_MAX, packet->num_io_configs); + return -EINVAL; + } + + if (packet->num_cmd_buf > CAM_ICP_CTX_MAX_CMD_BUFFERS) { + CAM_ERR(CAM_ICP, "Invalid number of cmd buffers: %d %d", + CAM_ICP_CTX_MAX_CMD_BUFFERS, packet->num_cmd_buf); + return -EINVAL; + } + + CAM_DBG(CAM_ICP, "number of cmd/patch info: %u %u %u %u", + packet->num_cmd_buf, + packet->num_io_configs, IPE_IO_IMAGES_MAX, + packet->num_patches); + return 0; +} + +static int cam_icp_mgr_process_cmd_desc(struct cam_icp_hw_mgr *hw_mgr, + struct cam_packet *packet, struct cam_icp_hw_ctx_data *ctx_data, + uint32_t *fw_cmd_buf_iova_addr) +{ + int rc = 0; + int i, j, k; + int num_cmd_buf = 0; + dma_addr_t addr; + size_t len; + struct cam_cmd_buf_desc *cmd_desc = NULL; + uintptr_t cpu_addr = 0; + struct ipe_frame_process_data *frame_process_data = NULL; + struct bps_frame_process_data *bps_frame_process_data = NULL; + + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *) &packet->payload + packet->cmd_buf_offset/4); + + *fw_cmd_buf_iova_addr = 0; + for (i = 0; i < packet->num_cmd_buf; i++, num_cmd_buf++) { + if (cmd_desc[i].type == CAM_CMD_BUF_FW) { + rc = cam_mem_get_io_buf(cmd_desc[i].mem_handle, + hw_mgr->iommu_hdl, &addr, &len); + if (rc) { + CAM_ERR(CAM_ICP, "get cmd buf failed %x", + hw_mgr->iommu_hdl); + + if (num_cmd_buf > 0) + num_cmd_buf--; + return rc; + } + *fw_cmd_buf_iova_addr = addr; + + if ((cmd_desc[i].offset >= len) || + ((len - cmd_desc[i].offset) < + cmd_desc[i].size)){ + CAM_ERR(CAM_ICP, + "Invalid offset, i: %d offset: %u len: %zu size: %zu", + i, cmd_desc[i].offset, + len, cmd_desc[i].size); + return -EINVAL; + } + + *fw_cmd_buf_iova_addr = + (*fw_cmd_buf_iova_addr + cmd_desc[i].offset); + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &cpu_addr, &len); + if (rc || !cpu_addr) { + CAM_ERR(CAM_ICP, "get cmd buf failed %x", + hw_mgr->iommu_hdl); + *fw_cmd_buf_iova_addr = 0; + + if (num_cmd_buf > 0) + num_cmd_buf--; + return rc; + } + if ((len <= cmd_desc[i].offset) || + (cmd_desc[i].size < cmd_desc[i].length) || + ((len - cmd_desc[i].offset) < + cmd_desc[i].length)) { + CAM_ERR(CAM_ICP, "Invalid offset or length"); + return -EINVAL; + } + cpu_addr = cpu_addr + cmd_desc[i].offset; + } + } + + if (!cpu_addr) { + CAM_ERR(CAM_ICP, "invalid number of cmd buf"); + return -EINVAL; + } + + if (ctx_data->icp_dev_acquire_info->dev_type != + CAM_ICP_RES_TYPE_BPS) { + CAM_DBG(CAM_ICP, "cpu addr = %llx", cpu_addr); + frame_process_data = (struct ipe_frame_process_data *)cpu_addr; + CAM_DBG(CAM_ICP, "%u %u %u", frame_process_data->max_num_cores, + frame_process_data->target_time, + frame_process_data->frames_in_batch); + frame_process_data->strip_lib_out_addr = 0; + frame_process_data->iq_settings_addr = 0; + frame_process_data->scratch_buffer_addr = 0; + frame_process_data->ubwc_stats_buffer_addr = 0; + frame_process_data->cdm_buffer_addr = 0; + frame_process_data->cdm_prog_base = 0; + for (i = 0; i < frame_process_data->frames_in_batch; i++) { + for (j = 0; j < IPE_IO_IMAGES_MAX; j++) { + for (k = 0; k < MAX_NUM_OF_IMAGE_PLANES; k++) { + frame_process_data-> + framesets[i].buffers[j]. + buffer_ptr[k] = 0; + frame_process_data-> + framesets[i].buffers[j]. + meta_buffer_ptr[k] = 0; + } + } + } + } else { + CAM_DBG(CAM_ICP, "cpu addr = %llx", cpu_addr); + bps_frame_process_data = + (struct bps_frame_process_data *)cpu_addr; + CAM_DBG(CAM_ICP, "%u %u", + bps_frame_process_data->max_num_cores, + bps_frame_process_data->target_time); + bps_frame_process_data->ubwc_stats_buffer_addr = 0; + bps_frame_process_data->cdm_buffer_addr = 0; + bps_frame_process_data->iq_settings_addr = 0; + bps_frame_process_data->strip_lib_out_addr = 0; + bps_frame_process_data->cdm_prog_addr = 0; + for (i = 0; i < BPS_IO_IMAGES_MAX; i++) { + for (j = 0; j < MAX_NUM_OF_IMAGE_PLANES; j++) { + bps_frame_process_data-> + buffers[i].buffer_ptr[j] = 0; + bps_frame_process_data-> + buffers[i].meta_buffer_ptr[j] = 0; + } + } + } + + return rc; +} + +static int cam_icp_mgr_process_io_cfg(struct cam_icp_hw_mgr *hw_mgr, + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_packet *packet, + struct cam_hw_prepare_update_args *prepare_args, + int32_t index) +{ + int i, j, k, rc = 0; + struct cam_buf_io_cfg *io_cfg_ptr = NULL; + int32_t sync_in_obj[CAM_MAX_IN_RES]; + int32_t merged_sync_in_obj; + + io_cfg_ptr = (struct cam_buf_io_cfg *) ((uint32_t *) &packet->payload + + packet->io_configs_offset/4); + prepare_args->num_out_map_entries = 0; + prepare_args->num_in_map_entries = 0; + + for (i = 0, j = 0, k = 0; i < packet->num_io_configs; i++) { + if (io_cfg_ptr[i].direction == CAM_BUF_INPUT) { + sync_in_obj[j++] = io_cfg_ptr[i].fence; + prepare_args->num_in_map_entries++; + } else { + prepare_args->out_map_entries[k++].sync_id = + io_cfg_ptr[i].fence; + prepare_args->num_out_map_entries++; + } + CAM_DBG(CAM_ICP, "dir[%d]: %u, fence: %u", + i, io_cfg_ptr[i].direction, io_cfg_ptr[i].fence); + } + + if (prepare_args->num_in_map_entries > 1) { + rc = cam_sync_merge(&sync_in_obj[0], + prepare_args->num_in_map_entries, &merged_sync_in_obj); + if (rc) { + prepare_args->num_out_map_entries = 0; + prepare_args->num_in_map_entries = 0; + return rc; + } + + ctx_data->hfi_frame_process.in_resource[index] = + merged_sync_in_obj; + prepare_args->in_map_entries[0].sync_id = merged_sync_in_obj; + prepare_args->num_in_map_entries = 1; + CAM_DBG(CAM_ICP, "Merged Sync obj = %d", merged_sync_in_obj); + } else if (prepare_args->num_in_map_entries == 1) { + prepare_args->in_map_entries[0].sync_id = sync_in_obj[0]; + prepare_args->num_in_map_entries = 1; + ctx_data->hfi_frame_process.in_resource[index] = 0; + } else { + CAM_ERR(CAM_ICP, "No input fences"); + prepare_args->num_in_map_entries = 0; + ctx_data->hfi_frame_process.in_resource[index] = 0; + rc = -EINVAL; + } + + return rc; +} + +static int cam_icp_packet_generic_blob_handler(void *user_data, + uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data) +{ + struct cam_icp_clk_bw_request *soc_req; + struct cam_icp_clk_bw_request *clk_info; + struct icp_cmd_generic_blob *blob; + struct cam_icp_hw_ctx_data *ctx_data; + uint32_t index; + int rc = 0; + + if (!blob_data || (blob_size == 0)) { + CAM_ERR(CAM_ICP, "Invalid blob info %pK %d", blob_data, + blob_size); + return -EINVAL; + } + + blob = (struct icp_cmd_generic_blob *)user_data; + ctx_data = blob->ctx; + index = blob->frame_info_idx; + + switch (blob_type) { + case CAM_ICP_CMD_GENERIC_BLOB_CLK: + if (blob_size != sizeof(struct cam_icp_clk_bw_request)) { + rc = -EINVAL; + break; + } + clk_info = &ctx_data->hfi_frame_process.clk_info[index]; + memset(clk_info, 0, sizeof(struct cam_icp_clk_bw_request)); + + soc_req = (struct cam_icp_clk_bw_request *)blob_data; + *clk_info = *soc_req; + CAM_DBG(CAM_ICP, "%llu %llu %d %d %d", + clk_info->budget_ns, clk_info->frame_cycles, + clk_info->rt_flag, clk_info->uncompressed_bw, + clk_info->compressed_bw); + break; + + default: + CAM_WARN(CAM_ICP, "Invalid blob type %d", blob_type); + break; + } + return rc; +} + +static int cam_icp_process_generic_cmd_buffer( + struct cam_packet *packet, + struct cam_icp_hw_ctx_data *ctx_data, + int32_t index) +{ + int i, rc = 0; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct icp_cmd_generic_blob cmd_generic_blob; + + cmd_generic_blob.ctx = ctx_data; + cmd_generic_blob.frame_info_idx = index; + + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *) &packet->payload + packet->cmd_buf_offset/4); + for (i = 0; i < packet->num_cmd_buf; i++) { + if (!cmd_desc[i].length) + continue; + + if (cmd_desc[i].meta_data != CAM_ICP_CMD_META_GENERIC_BLOB) + continue; + + rc = cam_packet_util_process_generic_cmd_buffer(&cmd_desc[i], + cam_icp_packet_generic_blob_handler, &cmd_generic_blob); + if (rc) + CAM_ERR(CAM_ICP, "Failed in processing blobs %d", rc); + } + + return rc; +} + +static int cam_icp_mgr_update_hfi_frame_process( + struct cam_icp_hw_ctx_data *ctx_data, + struct cam_packet *packet, + struct cam_hw_prepare_update_args *prepare_args, + int32_t *idx) +{ + int32_t index, rc; + + index = find_first_zero_bit(ctx_data->hfi_frame_process.bitmap, + ctx_data->hfi_frame_process.bits); + if (index < 0 || index >= CAM_FRAME_CMD_MAX) { + CAM_ERR(CAM_ICP, "request idx is wrong: %d", index); + return -EINVAL; + } + set_bit(index, ctx_data->hfi_frame_process.bitmap); + + ctx_data->hfi_frame_process.request_id[index] = + packet->header.request_id; + rc = cam_icp_process_generic_cmd_buffer(packet, ctx_data, index); + if (rc) { + clear_bit(index, ctx_data->hfi_frame_process.bitmap); + ctx_data->hfi_frame_process.request_id[index] = -1; + return rc; + } + *idx = index; + + return 0; +} + +static int cam_icp_mgr_prepare_hw_update(void *hw_mgr_priv, + void *prepare_hw_update_args) +{ + int rc = 0; + int32_t idx; + uint32_t fw_cmd_buf_iova_addr; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + struct cam_packet *packet = NULL; + struct hfi_cmd_ipebps_async *hfi_cmd = NULL; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_prepare_update_args *prepare_args = + prepare_hw_update_args; + + if ((!prepare_args) || (!hw_mgr) || (!prepare_args->packet)) { + CAM_ERR(CAM_ICP, "Invalid args"); + return -EINVAL; + } + + ctx_data = prepare_args->ctxt_to_hw_map; + mutex_lock(&ctx_data->ctx_mutex); + if (ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) { + mutex_unlock(&ctx_data->ctx_mutex); + CAM_ERR(CAM_ICP, "ctx id: %u is not in use", + ctx_data->ctx_id); + return -EINVAL; + } + + packet = prepare_args->packet; + + rc = cam_icp_mgr_pkt_validation(packet); + if (rc) { + mutex_unlock(&ctx_data->ctx_mutex); + return rc; + } + + rc = cam_icp_mgr_process_cmd_desc(hw_mgr, packet, + ctx_data, &fw_cmd_buf_iova_addr); + if (rc) { + mutex_unlock(&ctx_data->ctx_mutex); + return rc; + } + + CAM_DBG(CAM_ICP, "E: req id = %lld", packet->header.request_id); + /* Update Buffer Address from handles and patch information */ + rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl, + hw_mgr->iommu_sec_hdl); + if (rc) { + mutex_unlock(&ctx_data->ctx_mutex); + return rc; + } + + rc = cam_icp_mgr_update_hfi_frame_process(ctx_data, packet, + prepare_args, &idx); + if (rc) { + mutex_unlock(&ctx_data->ctx_mutex); + return rc; + } + + rc = cam_icp_mgr_process_io_cfg(hw_mgr, ctx_data, + packet, prepare_args, idx); + if (rc) { + if (ctx_data->hfi_frame_process.in_resource[idx] > 0) + cam_sync_destroy( + ctx_data->hfi_frame_process.in_resource[idx]); + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + ctx_data->hfi_frame_process.request_id[idx] = -1; + mutex_unlock(&ctx_data->ctx_mutex); + return rc; + } + + hfi_cmd = (struct hfi_cmd_ipebps_async *) + &ctx_data->hfi_frame_process.hfi_frame_cmd[idx]; + cam_icp_mgr_prepare_frame_process_cmd( + ctx_data, hfi_cmd, packet->header.request_id, + fw_cmd_buf_iova_addr); + + prepare_args->num_hw_update_entries = 1; + prepare_args->hw_update_entries[0].addr = (uint64_t)hfi_cmd; + prepare_args->priv = &ctx_data->hfi_frame_process.request_id[idx]; + + CAM_DBG(CAM_ICP, "X: req id = %lld ctx_id = %u", + packet->header.request_id, ctx_data->ctx_id); + mutex_unlock(&ctx_data->ctx_mutex); + return rc; +} + +static int cam_icp_mgr_send_abort_status(struct cam_icp_hw_ctx_data *ctx_data) +{ + struct hfi_frame_process_info *hfi_frame_process; + int idx; + + mutex_lock(&ctx_data->ctx_mutex); + hfi_frame_process = &ctx_data->hfi_frame_process; + for (idx = 0; idx < CAM_FRAME_CMD_MAX; idx++) { + if (!hfi_frame_process->request_id[idx]) + continue; + + ctx_data->ctxt_event_cb(ctx_data->context_priv, true, + &hfi_frame_process->request_id[idx]); + + /* now release memory for hfi frame process command */ + hfi_frame_process->request_id[idx] = 0; + if (ctx_data->hfi_frame_process.in_resource[idx] > 0) { + CAM_DBG(CAM_ICP, "Delete merged sync in object: %d", + ctx_data->hfi_frame_process.in_resource[idx]); + cam_sync_destroy( + ctx_data->hfi_frame_process.in_resource[idx]); + ctx_data->hfi_frame_process.in_resource[idx] = 0; + } + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + } + mutex_unlock(&ctx_data->ctx_mutex); + return 0; +} + +static int cam_icp_mgr_delete_sync(void *priv, void *data) +{ + struct hfi_cmd_work_data *task_data = NULL; + struct cam_icp_hw_ctx_data *ctx_data; + struct hfi_frame_process_info *hfi_frame_process; + int idx; + + if (!data || !priv) { + CAM_ERR(CAM_ICP, "Invalid params%pK %pK", data, priv); + return -EINVAL; + } + + task_data = (struct hfi_cmd_work_data *)data; + ctx_data = task_data->data; + + if (!ctx_data) { + CAM_ERR(CAM_ICP, "Null Context"); + return -EINVAL; + } + + mutex_lock(&ctx_data->ctx_mutex); + hfi_frame_process = &ctx_data->hfi_frame_process; + for (idx = 0; idx < CAM_FRAME_CMD_MAX; idx++) { + if (!hfi_frame_process->in_free_resource[idx]) + continue; + //cam_sync_destroy( + //ctx_data->hfi_frame_process.in_free_resource[idx]); + ctx_data->hfi_frame_process.in_resource[idx] = 0; + } + mutex_unlock(&ctx_data->ctx_mutex); + return 0; +} + +static int cam_icp_mgr_delete_sync_obj(struct cam_icp_hw_ctx_data *ctx_data) +{ + int rc = 0; + struct crm_workq_task *task; + struct hfi_cmd_work_data *task_data; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) { + CAM_ERR(CAM_ICP, "no empty task"); + return -ENOMEM; + } + + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)ctx_data; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_delete_sync; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + + return rc; +} + +static int cam_icp_mgr_flush_all(struct cam_icp_hw_ctx_data *ctx_data, + struct cam_hw_flush_args *flush_args) +{ + struct hfi_frame_process_info *hfi_frame_process; + int idx; + bool clear_in_resource = false; + + hfi_frame_process = &ctx_data->hfi_frame_process; + for (idx = 0; idx < CAM_FRAME_CMD_MAX; idx++) { + if (!hfi_frame_process->request_id[idx]) + continue; + + /* now release memory for hfi frame process command */ + hfi_frame_process->request_id[idx] = 0; + if (ctx_data->hfi_frame_process.in_resource[idx] > 0) { + ctx_data->hfi_frame_process.in_free_resource[idx] = + ctx_data->hfi_frame_process.in_resource[idx]; + ctx_data->hfi_frame_process.in_resource[idx] = 0; + } + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + clear_in_resource = true; + } + + if (clear_in_resource) + cam_icp_mgr_delete_sync_obj(ctx_data); + + return 0; +} + +static int cam_icp_mgr_flush_req(struct cam_icp_hw_ctx_data *ctx_data, + struct cam_hw_flush_args *flush_args) +{ + int64_t request_id; + struct hfi_frame_process_info *hfi_frame_process; + int idx; + bool clear_in_resource = false; + + hfi_frame_process = &ctx_data->hfi_frame_process; + request_id = *(int64_t *)flush_args->flush_req_pending[0]; + for (idx = 0; idx < CAM_FRAME_CMD_MAX; idx++) { + if (!hfi_frame_process->request_id[idx]) + continue; + + if (hfi_frame_process->request_id[idx] != request_id) + continue; + + /* now release memory for hfi frame process command */ + hfi_frame_process->request_id[idx] = 0; + if (ctx_data->hfi_frame_process.in_resource[idx] > 0) { + ctx_data->hfi_frame_process.in_free_resource[idx] = + ctx_data->hfi_frame_process.in_resource[idx]; + ctx_data->hfi_frame_process.in_resource[idx] = 0; + } + clear_bit(idx, ctx_data->hfi_frame_process.bitmap); + clear_in_resource = true; + } + + if (clear_in_resource) + cam_icp_mgr_delete_sync_obj(ctx_data); + + return 0; +} + +static int cam_icp_mgr_hw_flush(void *hw_priv, void *hw_flush_args) +{ + struct cam_hw_flush_args *flush_args = hw_flush_args; + struct cam_icp_hw_ctx_data *ctx_data; + + if ((!hw_priv) || (!hw_flush_args)) { + CAM_ERR(CAM_ICP, "Input params are Null:"); + return -EINVAL; + } + + ctx_data = flush_args->ctxt_to_hw_map; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "Ctx data is NULL"); + return -EINVAL; + } + + if ((flush_args->flush_type >= CAM_FLUSH_TYPE_MAX) || + (flush_args->flush_type < CAM_FLUSH_TYPE_REQ)) { + CAM_ERR(CAM_ICP, "Invalid lush type: %d", + flush_args->flush_type); + return -EINVAL; + } + + switch (flush_args->flush_type) { + case CAM_FLUSH_TYPE_ALL: + if (flush_args->num_req_active) + cam_icp_mgr_abort_handle(ctx_data); + mutex_lock(&ctx_data->ctx_mutex); + cam_icp_mgr_flush_all(ctx_data, flush_args); + mutex_unlock(&ctx_data->ctx_mutex); + break; + case CAM_FLUSH_TYPE_REQ: + mutex_lock(&ctx_data->ctx_mutex); + if (flush_args->num_req_active) { + CAM_ERR(CAM_ICP, "Flush request is not supported"); + mutex_unlock(&ctx_data->ctx_mutex); + return -EINVAL; + } + if (flush_args->num_req_pending) + cam_icp_mgr_flush_req(ctx_data, flush_args); + mutex_unlock(&ctx_data->ctx_mutex); + break; + default: + CAM_ERR(CAM_ICP, "Invalid flush type: %d", + flush_args->flush_type); + return -EINVAL; + } + + return 0; +} + +static int cam_icp_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args) +{ + int rc = 0; + int ctx_id = 0; + struct cam_hw_release_args *release_hw = release_hw_args; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + + if (!release_hw || !hw_mgr) { + CAM_ERR(CAM_ICP, "Invalid args: %pK %pK", release_hw, hw_mgr); + return -EINVAL; + } + + CAM_DBG(CAM_ICP, "Enter"); + ctx_data = release_hw->ctxt_to_hw_map; + if (!ctx_data) { + CAM_ERR(CAM_ICP, "NULL ctx data"); + return -EINVAL; + } + + ctx_id = ctx_data->ctx_id; + if (ctx_id < 0 || ctx_id >= CAM_ICP_CTX_MAX) { + CAM_ERR(CAM_ICP, "Invalid ctx id: %d", ctx_id); + return -EINVAL; + } + + mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + if (hw_mgr->ctx_data[ctx_id].state != CAM_ICP_CTX_STATE_ACQUIRED) { + CAM_DBG(CAM_ICP, "ctx is not in use: %d", ctx_id); + mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + return -EINVAL; + } + mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex); + + if (release_hw->active_req) { + cam_icp_mgr_abort_handle(ctx_data); + cam_icp_mgr_send_abort_status(ctx_data); + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + rc = cam_icp_mgr_release_ctx(hw_mgr, ctx_id); + if (!hw_mgr->ctxt_cnt) { + CAM_DBG(CAM_ICP, "Last Release"); + cam_icp_mgr_icp_power_collapse(hw_mgr); + cam_icp_hw_mgr_reset_clk_info(hw_mgr); + hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE; + rc = cam_ipe_bps_deint(hw_mgr); + } + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + if (!hw_mgr->bps_ctxt_cnt || !hw_mgr->ipe_ctxt_cnt) + cam_icp_device_timer_stop(hw_mgr); + + CAM_DBG(CAM_ICP, "Exit"); + return rc; +} + +static int cam_icp_mgr_send_config_io(struct cam_icp_hw_ctx_data *ctx_data, + uint32_t io_buf_addr) +{ + int rc = 0; + struct hfi_cmd_work_data *task_data; + struct hfi_cmd_ipebps_async ioconfig_cmd; + unsigned long rem_jiffies; + int timeout = 5000; + struct crm_workq_task *task; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) + return -ENOMEM; + + ioconfig_cmd.size = sizeof(struct hfi_cmd_ipebps_async); + ioconfig_cmd.pkt_type = HFI_CMD_IPEBPS_ASYNC_COMMAND_INDIRECT; + if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) + ioconfig_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_BPS_CONFIG_IO; + else + ioconfig_cmd.opcode = HFI_IPEBPS_CMD_OPCODE_IPE_CONFIG_IO; + + reinit_completion(&ctx_data->wait_complete); + ioconfig_cmd.num_fw_handles = 1; + ioconfig_cmd.fw_handles[0] = ctx_data->fw_handle; + ioconfig_cmd.payload.indirect = io_buf_addr; + ioconfig_cmd.user_data1 = (uint64_t)ctx_data; + ioconfig_cmd.user_data2 = (uint64_t)0x0; + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)&ioconfig_cmd; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) + return rc; + + rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW response timed out %d", rc); + } + + return rc; +} + +static int cam_icp_mgr_create_handle(uint32_t dev_type, + struct cam_icp_hw_ctx_data *ctx_data) +{ + struct hfi_cmd_create_handle create_handle; + struct hfi_cmd_work_data *task_data; + unsigned long rem_jiffies; + int timeout = 5000; + struct crm_workq_task *task; + int rc = 0; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) + return -ENOMEM; + + create_handle.size = sizeof(struct hfi_cmd_create_handle); + create_handle.pkt_type = HFI_CMD_IPEBPS_CREATE_HANDLE; + create_handle.handle_type = dev_type; + create_handle.user_data1 = (uint64_t)ctx_data; + reinit_completion(&ctx_data->wait_complete); + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)&create_handle; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) + return rc; + + rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW response timed out %d", rc); + } + + if (ctx_data->fw_handle == 0) { + CAM_ERR(CAM_ICP, "Invalid handle created"); + rc = -EINVAL; + } + + return rc; +} + +static int cam_icp_mgr_send_ping(struct cam_icp_hw_ctx_data *ctx_data) +{ + struct hfi_cmd_ping_pkt ping_pkt; + struct hfi_cmd_work_data *task_data; + unsigned long rem_jiffies; + int timeout = 5000; + struct crm_workq_task *task; + int rc = 0; + + task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work); + if (!task) { + CAM_ERR(CAM_ICP, "No free task to send ping command"); + return -ENOMEM; + } + + ping_pkt.size = sizeof(struct hfi_cmd_ping_pkt); + ping_pkt.pkt_type = HFI_CMD_SYS_PING; + ping_pkt.user_data = (uint64_t)ctx_data; + init_completion(&ctx_data->wait_complete); + task_data = (struct hfi_cmd_work_data *)task->payload; + task_data->data = (void *)&ping_pkt; + task_data->request_id = 0; + task_data->type = ICP_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_icp_mgr_process_cmd; + + rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) + return rc; + + rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete, + msecs_to_jiffies((timeout))); + if (!rem_jiffies) { + rc = -ETIMEDOUT; + CAM_ERR(CAM_ICP, "FW response timed out %d", rc); + } + + return rc; +} + +static int cam_icp_get_acquire_info(struct cam_icp_hw_mgr *hw_mgr, + struct cam_hw_acquire_args *args, + struct cam_icp_hw_ctx_data *ctx_data) +{ + int i; + int acquire_size; + struct cam_icp_acquire_dev_info icp_dev_acquire_info; + struct cam_icp_res_info *p_icp_out = NULL; + + if (copy_from_user(&icp_dev_acquire_info, + (void __user *)args->acquire_info, + sizeof(struct cam_icp_acquire_dev_info))) { + CAM_ERR(CAM_ICP, "Failed in acquire"); + return -EFAULT; + } + + if (icp_dev_acquire_info.secure_mode > CAM_SECURE_MODE_SECURE) { + CAM_ERR(CAM_ICP, "Invalid mode:%d", + icp_dev_acquire_info.secure_mode); + return -EINVAL; + } + + if (icp_dev_acquire_info.num_out_res > ICP_MAX_OUTPUT_SUPPORTED) { + CAM_ERR(CAM_ICP, "num of out resources exceeding : %u", + icp_dev_acquire_info.num_out_res); + return -EINVAL; + } + + if (icp_dev_acquire_info.dev_type >= CAM_ICP_RES_TYPE_MAX) { + CAM_ERR(CAM_ICP, "Invalid device type: %d", + icp_dev_acquire_info.dev_type); + return -EFAULT; + } + + if (!hw_mgr->ctxt_cnt) { + hw_mgr->secure_mode = icp_dev_acquire_info.secure_mode; + } else { + if (hw_mgr->secure_mode != icp_dev_acquire_info.secure_mode) { + CAM_ERR(CAM_ICP, + "secure mode mismatch driver:%d, context:%d", + hw_mgr->secure_mode, + icp_dev_acquire_info.secure_mode); + return -EINVAL; + } + } + + acquire_size = sizeof(struct cam_icp_acquire_dev_info) + + ((icp_dev_acquire_info.num_out_res - 1) * + sizeof(struct cam_icp_res_info)); + ctx_data->icp_dev_acquire_info = kzalloc(acquire_size, GFP_KERNEL); + if (!ctx_data->icp_dev_acquire_info) { + if (!hw_mgr->ctxt_cnt) + hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE; + return -ENOMEM; + } + + if (copy_from_user(ctx_data->icp_dev_acquire_info, + (void __user *)args->acquire_info, acquire_size)) { + CAM_ERR(CAM_ICP, "Failed in acquire: size = %d", acquire_size); + if (!hw_mgr->ctxt_cnt) + hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE; + kfree(ctx_data->icp_dev_acquire_info); + ctx_data->icp_dev_acquire_info = NULL; + return -EFAULT; + } + + CAM_DBG(CAM_ICP, "%x %x %x %x %x %x %x %u", + ctx_data->icp_dev_acquire_info->dev_type, + ctx_data->icp_dev_acquire_info->in_res.format, + ctx_data->icp_dev_acquire_info->in_res.width, + ctx_data->icp_dev_acquire_info->in_res.height, + ctx_data->icp_dev_acquire_info->in_res.fps, + ctx_data->icp_dev_acquire_info->num_out_res, + ctx_data->icp_dev_acquire_info->scratch_mem_size, + hw_mgr->secure_mode); + + p_icp_out = ctx_data->icp_dev_acquire_info->out_res; + for (i = 0; i < icp_dev_acquire_info.num_out_res; i++) + CAM_DBG(CAM_ICP, "out[i] %x %x %x %x", + p_icp_out[i].format, + p_icp_out[i].width, + p_icp_out[i].height, + p_icp_out[i].fps); + + return 0; +} + +static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args) +{ + int rc = 0, bitmap_size = 0; + uint32_t ctx_id = 0; + dma_addr_t io_buf_addr; + size_t io_buf_size; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_icp_hw_ctx_data *ctx_data = NULL; + struct cam_hw_acquire_args *args = acquire_hw_args; + struct cam_icp_acquire_dev_info *icp_dev_acquire_info; + + if ((!hw_mgr_priv) || (!acquire_hw_args)) { + CAM_ERR(CAM_ICP, "Invalid params: %pK %pK", hw_mgr_priv, + acquire_hw_args); + return -EINVAL; + } + + if (args->num_acq > 1) { + CAM_ERR(CAM_ICP, "number of resources are wrong: %u", + args->num_acq); + return -EINVAL; + } + + CAM_DBG(CAM_ICP, "ENTER"); + mutex_lock(&hw_mgr->hw_mgr_mutex); + ctx_id = cam_icp_mgr_get_free_ctx(hw_mgr); + if (ctx_id >= CAM_ICP_CTX_MAX) { + CAM_ERR(CAM_ICP, "No free ctx space in hw_mgr"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -ENOSPC; + } + ctx_data = &hw_mgr->ctx_data[ctx_id]; + ctx_data->ctx_id = ctx_id; + + mutex_lock(&ctx_data->ctx_mutex); + rc = cam_icp_get_acquire_info(hw_mgr, args, ctx_data); + if (rc) + goto acquire_info_failed; + + icp_dev_acquire_info = ctx_data->icp_dev_acquire_info; + + rc = cam_mem_get_io_buf( + icp_dev_acquire_info->io_config_cmd_handle, + hw_mgr->iommu_hdl, + &io_buf_addr, &io_buf_size); + if (rc) { + CAM_ERR(CAM_ICP, "unable to get src buf info from io desc"); + goto get_io_buf_failed; + } + + CAM_DBG(CAM_ICP, "hdl: %d, addr: %pK, size: %zu", + icp_dev_acquire_info->io_config_cmd_handle, + (void *)io_buf_addr, io_buf_size); + + if (!hw_mgr->ctxt_cnt) { + rc = cam_icp_clk_info_init(hw_mgr, ctx_data); + if (rc) + goto get_io_buf_failed; + + rc = cam_icp_mgr_icp_resume(hw_mgr); + if (rc) + goto get_io_buf_failed; + + if (icp_hw_mgr.a5_debug_type) + hfi_set_debug_level(icp_hw_mgr.a5_debug_type, + icp_hw_mgr.a5_dbg_lvl); + + rc = cam_icp_send_ubwc_cfg(hw_mgr); + if (rc) + goto ubwc_cfg_failed; + } + + + rc = cam_icp_mgr_ipe_bps_resume(hw_mgr, ctx_data); + if (rc) + goto ipe_bps_resume_failed; + + rc = cam_icp_mgr_send_ping(ctx_data); + if (rc) { + CAM_ERR(CAM_ICP, "ping ack not received"); + goto send_ping_failed; + } + CAM_DBG(CAM_ICP, "ping ack received"); + + rc = cam_icp_mgr_create_handle(icp_dev_acquire_info->dev_type, + ctx_data); + if (rc) { + CAM_ERR(CAM_ICP, "create handle failed"); + goto create_handle_failed; + } + + rc = cam_icp_mgr_send_config_io(ctx_data, io_buf_addr); + if (rc) { + CAM_ERR(CAM_ICP, "IO Config command failed"); + goto ioconfig_failed; + } + + ctx_data->context_priv = args->context_data; + args->ctxt_to_hw_map = ctx_data; + + bitmap_size = BITS_TO_LONGS(CAM_FRAME_CMD_MAX) * sizeof(long); + ctx_data->hfi_frame_process.bitmap = + kzalloc(bitmap_size, GFP_KERNEL); + if (!ctx_data->hfi_frame_process.bitmap) + goto ioconfig_failed; + + ctx_data->hfi_frame_process.bits = bitmap_size * BITS_PER_BYTE; + hw_mgr->ctx_data[ctx_id].ctxt_event_cb = args->event_cb; + icp_dev_acquire_info->scratch_mem_size = ctx_data->scratch_mem_size; + + if (copy_to_user((void __user *)args->acquire_info, + icp_dev_acquire_info, sizeof(struct cam_icp_acquire_dev_info))) + goto copy_to_user_failed; + + cam_icp_ctx_clk_info_init(ctx_data); + ctx_data->state = CAM_ICP_CTX_STATE_ACQUIRED; + mutex_unlock(&ctx_data->ctx_mutex); + CAM_DBG(CAM_ICP, "scratch size = %x fw_handle = %x", + (unsigned int)icp_dev_acquire_info->scratch_mem_size, + (unsigned int)ctx_data->fw_handle); + /* Start device timer*/ + if (((hw_mgr->bps_ctxt_cnt == 1) || (hw_mgr->ipe_ctxt_cnt == 1))) + cam_icp_device_timer_start(hw_mgr); + /* Start context timer*/ + cam_icp_ctx_timer_start(ctx_data); + hw_mgr->ctxt_cnt++; + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_DBG(CAM_ICP, "Acquire Done"); + + return 0; + +copy_to_user_failed: + kfree(ctx_data->hfi_frame_process.bitmap); + ctx_data->hfi_frame_process.bitmap = NULL; +ioconfig_failed: + cam_icp_mgr_destroy_handle(ctx_data); +create_handle_failed: +send_ping_failed: + cam_icp_mgr_ipe_bps_power_collapse(hw_mgr, ctx_data, 0); +ipe_bps_resume_failed: +ubwc_cfg_failed: + if (!hw_mgr->ctxt_cnt) + cam_icp_mgr_icp_power_collapse(hw_mgr); +get_io_buf_failed: + kfree(hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info); + hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info = NULL; +acquire_info_failed: + cam_icp_mgr_put_ctx(ctx_data); + mutex_unlock(&ctx_data->ctx_mutex); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int cam_icp_mgr_get_hw_caps(void *hw_mgr_priv, void *hw_caps_args) +{ + int rc = 0; + struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_query_cap_cmd *query_cap = hw_caps_args; + + if ((!hw_mgr_priv) || (!hw_caps_args)) { + CAM_ERR(CAM_ICP, "Invalid params: %pK %pK", + hw_mgr_priv, hw_caps_args); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + if (copy_from_user(&icp_hw_mgr.icp_caps, + u64_to_user_ptr(query_cap->caps_handle), + sizeof(struct cam_icp_query_cap_cmd))) { + CAM_ERR(CAM_ICP, "copy_from_user failed"); + rc = -EFAULT; + goto end; + } + + rc = hfi_get_hw_caps(&icp_hw_mgr.icp_caps); + if (rc) + goto end; + + icp_hw_mgr.icp_caps.dev_iommu_handle.non_secure = hw_mgr->iommu_hdl; + icp_hw_mgr.icp_caps.dev_iommu_handle.secure = hw_mgr->iommu_sec_hdl; + + if (copy_to_user(u64_to_user_ptr(query_cap->caps_handle), + &icp_hw_mgr.icp_caps, sizeof(struct cam_icp_query_cap_cmd))) { + CAM_ERR(CAM_ICP, "copy_to_user failed"); + rc = -EFAULT; + } +end: + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int cam_icp_mgr_alloc_devs(struct device_node *of_node) +{ + int rc; + uint32_t num_dev; + + rc = of_property_read_u32(of_node, "num-a5", &num_dev); + if (rc) { + CAM_ERR(CAM_ICP, "getting num of a5 failed"); + goto num_a5_failed; + } + + icp_hw_mgr.devices[CAM_ICP_DEV_A5] = kzalloc( + sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL); + if (!icp_hw_mgr.devices[CAM_ICP_DEV_A5]) { + rc = -ENOMEM; + goto num_a5_failed; + } + + rc = of_property_read_u32(of_node, "num-ipe", &num_dev); + if (rc) { + CAM_ERR(CAM_ICP, "getting number of ipe dev nodes failed"); + goto num_ipe_failed; + } + + if (!icp_hw_mgr.ipe1_enable) + num_dev = 1; + + icp_hw_mgr.devices[CAM_ICP_DEV_IPE] = kzalloc( + sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL); + if (!icp_hw_mgr.devices[CAM_ICP_DEV_IPE]) { + rc = -ENOMEM; + goto num_ipe_failed; + } + + rc = of_property_read_u32(of_node, "num-bps", &num_dev); + if (rc) { + CAM_ERR(CAM_ICP, "read num bps devices failed"); + goto num_bps_failed; + } + icp_hw_mgr.devices[CAM_ICP_DEV_BPS] = kzalloc( + sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL); + if (!icp_hw_mgr.devices[CAM_ICP_DEV_BPS]) { + rc = -ENOMEM; + goto num_bps_failed; + } + + return 0; +num_bps_failed: + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_IPE]); +num_ipe_failed: + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_A5]); +num_a5_failed: + return rc; +} + +static int cam_icp_mgr_init_devs(struct device_node *of_node) +{ + int rc = 0; + int count, i; + const char *name = NULL; + struct device_node *child_node = NULL; + struct platform_device *child_pdev = NULL; + struct cam_hw_intf *child_dev_intf = NULL; + + rc = cam_icp_mgr_alloc_devs(of_node); + if (rc) + return rc; + + count = of_property_count_strings(of_node, "compat-hw-name"); + if (!count) { + CAM_ERR(CAM_ICP, "no compat hw found in dev tree, cnt = %d", + count); + rc = -EINVAL; + goto compat_hw_name_failed; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, "compat-hw-name", + i, &name); + if (rc) { + CAM_ERR(CAM_ICP, "getting dev object name failed"); + goto compat_hw_name_failed; + } + + child_node = of_find_node_by_name(NULL, name); + if (!child_node) { + CAM_ERR(CAM_ICP, "Cannot find node in dtsi %s", name); + rc = -ENODEV; + goto compat_hw_name_failed; + } + + child_pdev = of_find_device_by_node(child_node); + if (!child_pdev) { + CAM_ERR(CAM_ICP, "failed to find device on bus %s", + child_node->name); + rc = -ENODEV; + of_node_put(child_node); + goto compat_hw_name_failed; + } + + child_dev_intf = (struct cam_hw_intf *)platform_get_drvdata( + child_pdev); + if (!child_dev_intf) { + CAM_ERR(CAM_ICP, "no child device"); + of_node_put(child_node); + if (!icp_hw_mgr.ipe1_enable) + continue; + goto compat_hw_name_failed; + } + icp_hw_mgr.devices[child_dev_intf->hw_type] + [child_dev_intf->hw_idx] = child_dev_intf; + + if (!child_dev_intf->hw_ops.process_cmd) + goto compat_hw_name_failed; + + of_node_put(child_node); + } + + icp_hw_mgr.a5_dev_intf = icp_hw_mgr.devices[CAM_ICP_DEV_A5][0]; + icp_hw_mgr.bps_dev_intf = icp_hw_mgr.devices[CAM_ICP_DEV_BPS][0]; + icp_hw_mgr.ipe0_dev_intf = icp_hw_mgr.devices[CAM_ICP_DEV_IPE][0]; + if (icp_hw_mgr.ipe1_enable) + icp_hw_mgr.ipe1_dev_intf = + icp_hw_mgr.devices[CAM_ICP_DEV_IPE][1]; + + return 0; +compat_hw_name_failed: + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_BPS]); + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_IPE]); + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_A5]); + return rc; +} + +static int cam_icp_mgr_create_wq(void) +{ + int rc; + int i; + + rc = cam_req_mgr_workq_create("icp_command_queue", ICP_WORKQ_NUM_TASK, + &icp_hw_mgr.cmd_work, CRM_WORKQ_USAGE_NON_IRQ); + if (rc) { + CAM_ERR(CAM_ICP, "unable to create a command worker"); + goto cmd_work_failed; + } + + rc = cam_req_mgr_workq_create("icp_message_queue", ICP_WORKQ_NUM_TASK, + &icp_hw_mgr.msg_work, CRM_WORKQ_USAGE_IRQ); + if (rc) { + CAM_ERR(CAM_ICP, "unable to create a message worker"); + goto msg_work_failed; + } + + rc = cam_req_mgr_workq_create("icp_timer_queue", ICP_WORKQ_NUM_TASK, + &icp_hw_mgr.timer_work, CRM_WORKQ_USAGE_IRQ); + if (rc) { + CAM_ERR(CAM_ICP, "unable to create a timer worker"); + goto timer_work_failed; + } + + icp_hw_mgr.cmd_work_data = (struct hfi_cmd_work_data *) + kzalloc(sizeof(struct hfi_cmd_work_data) * ICP_WORKQ_NUM_TASK, + GFP_KERNEL); + if (!icp_hw_mgr.cmd_work_data) + goto cmd_work_data_failed; + + icp_hw_mgr.msg_work_data = (struct hfi_msg_work_data *) + kzalloc(sizeof(struct hfi_msg_work_data) * ICP_WORKQ_NUM_TASK, + GFP_KERNEL); + if (!icp_hw_mgr.msg_work_data) + goto msg_work_data_failed; + + icp_hw_mgr.timer_work_data = (struct hfi_msg_work_data *) + kzalloc(sizeof(struct hfi_msg_work_data) * ICP_WORKQ_NUM_TASK, + GFP_KERNEL); + if (!icp_hw_mgr.timer_work_data) + goto timer_work_data_failed; + + rc = cam_icp_hw_mgr_create_debugfs_entry(); + if (rc) + goto debugfs_create_failed; + + for (i = 0; i < ICP_WORKQ_NUM_TASK; i++) + icp_hw_mgr.msg_work->task.pool[i].payload = + &icp_hw_mgr.msg_work_data[i]; + + for (i = 0; i < ICP_WORKQ_NUM_TASK; i++) + icp_hw_mgr.cmd_work->task.pool[i].payload = + &icp_hw_mgr.cmd_work_data[i]; + + for (i = 0; i < ICP_WORKQ_NUM_TASK; i++) + icp_hw_mgr.timer_work->task.pool[i].payload = + &icp_hw_mgr.timer_work_data[i]; + return 0; + +debugfs_create_failed: + kfree(icp_hw_mgr.timer_work_data); +timer_work_data_failed: + kfree(icp_hw_mgr.msg_work_data); +msg_work_data_failed: + kfree(icp_hw_mgr.cmd_work_data); +cmd_work_data_failed: + cam_req_mgr_workq_destroy(&icp_hw_mgr.timer_work); +timer_work_failed: + cam_req_mgr_workq_destroy(&icp_hw_mgr.msg_work); +msg_work_failed: + cam_req_mgr_workq_destroy(&icp_hw_mgr.cmd_work); +cmd_work_failed: + return rc; +} + +int cam_icp_hw_mgr_init(struct device_node *of_node, uint64_t *hw_mgr_hdl, + int *iommu_hdl) +{ + int i, rc = 0; + struct cam_hw_mgr_intf *hw_mgr_intf; + struct cam_cpas_query_cap query; + uint32_t cam_caps; + + hw_mgr_intf = (struct cam_hw_mgr_intf *)hw_mgr_hdl; + if (!of_node || !hw_mgr_intf) { + CAM_ERR(CAM_ICP, "Invalid args of_node %pK hw_mgr %pK", + of_node, hw_mgr_intf); + return -EINVAL; + } + + hw_mgr_intf->hw_mgr_priv = &icp_hw_mgr; + hw_mgr_intf->hw_get_caps = cam_icp_mgr_get_hw_caps; + hw_mgr_intf->hw_acquire = cam_icp_mgr_acquire_hw; + hw_mgr_intf->hw_release = cam_icp_mgr_release_hw; + hw_mgr_intf->hw_prepare_update = cam_icp_mgr_prepare_hw_update; + hw_mgr_intf->hw_config = cam_icp_mgr_config_hw; + hw_mgr_intf->hw_open = cam_icp_mgr_hw_open_u; + hw_mgr_intf->hw_close = cam_icp_mgr_hw_close_u; + hw_mgr_intf->hw_flush = cam_icp_mgr_hw_flush; + + icp_hw_mgr.secure_mode = CAM_SECURE_MODE_NON_SECURE; + mutex_init(&icp_hw_mgr.hw_mgr_mutex); + spin_lock_init(&icp_hw_mgr.hw_mgr_lock); + + for (i = 0; i < CAM_ICP_CTX_MAX; i++) + mutex_init(&icp_hw_mgr.ctx_data[i].ctx_mutex); + + cam_cpas_get_hw_info(&query.camera_family, + &query.camera_version, &query.cpas_version, &cam_caps); + if (cam_caps & CPAS_IPE0_BIT) + icp_hw_mgr.ipe0_enable = true; + if (cam_caps & CPAS_IPE1_BIT) + icp_hw_mgr.ipe1_enable = true; + if (cam_caps & CPAS_BPS_BIT) + icp_hw_mgr.bps_enable = true; + + rc = cam_icp_mgr_init_devs(of_node); + if (rc) + goto dev_init_failed; + + rc = cam_smmu_get_handle("icp", &icp_hw_mgr.iommu_hdl); + if (rc) { + CAM_ERR(CAM_ICP, "get mmu handle failed: %d", rc); + goto icp_get_hdl_failed; + } + + rc = cam_smmu_get_handle("cam-secure", &icp_hw_mgr.iommu_sec_hdl); + if (rc) { + CAM_ERR(CAM_ICP, "get secure mmu handle failed: %d", rc); + goto secure_hdl_failed; + } + + rc = cam_icp_mgr_create_wq(); + if (rc) + goto icp_wq_create_failed; + + if (iommu_hdl) + *iommu_hdl = icp_hw_mgr.iommu_hdl; + + init_completion(&icp_hw_mgr.a5_complete); + return rc; + +icp_wq_create_failed: + cam_smmu_destroy_handle(icp_hw_mgr.iommu_sec_hdl); + icp_hw_mgr.iommu_sec_hdl = -1; +secure_hdl_failed: + cam_smmu_destroy_handle(icp_hw_mgr.iommu_hdl); + icp_hw_mgr.iommu_hdl = -1; +icp_get_hdl_failed: + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_BPS]); + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_IPE]); + kfree(icp_hw_mgr.devices[CAM_ICP_DEV_A5]); +dev_init_failed: + mutex_destroy(&icp_hw_mgr.hw_mgr_mutex); + for (i = 0; i < CAM_ICP_CTX_MAX; i++) + mutex_destroy(&icp_hw_mgr.ctx_data[i].ctx_mutex); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h new file mode 100644 index 000000000000..25f51fbb343b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h @@ -0,0 +1,347 @@ +/* Copyright (c) 2017-2018, 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 CAM_ICP_HW_MGR_H +#define CAM_ICP_HW_MGR_H + +#include +#include +#include +#include "cam_icp_hw_intf.h" +#include "cam_hw_mgr_intf.h" +#include "cam_hw_intf.h" +#include "cam_a5_hw_intf.h" +#include "hfi_session_defs.h" +#include "cam_req_mgr_workq.h" +#include "cam_mem_mgr.h" +#include "cam_smmu_api.h" +#include "cam_soc_util.h" +#include "cam_req_mgr_timer.h" + +#define CAM_ICP_ROLE_PARENT 1 +#define CAM_ICP_ROLE_CHILD 2 + +#define CAM_FRAME_CMD_MAX 20 + +#define CAM_MAX_OUT_RES 6 +#define CAM_MAX_IN_RES 8 + +#define ICP_WORKQ_NUM_TASK 100 +#define ICP_WORKQ_TASK_CMD_TYPE 1 +#define ICP_WORKQ_TASK_MSG_TYPE 2 + +#define ICP_PACKET_SIZE 0 +#define ICP_PACKET_TYPE 1 +#define ICP_PACKET_OPCODE 2 +#define ICP_MAX_OUTPUT_SUPPORTED 6 + +#define ICP_FRAME_PROCESS_SUCCESS 0 +#define ICP_FRAME_PROCESS_FAILURE 1 +#define ICP_MSG_BUF_SIZE 256 +#define ICP_DBG_BUF_SIZE 102400 + +#define ICP_CLK_HW_IPE 0x0 +#define ICP_CLK_HW_BPS 0x1 +#define ICP_CLK_HW_MAX 0x2 + +#define ICP_OVER_CLK_THRESHOLD 15 + +#define CPAS_IPE0_BIT 0x1000 +#define CPAS_IPE1_BIT 0x2000 +#define CPAS_BPS_BIT 0x400 + +#define ICP_PWR_CLP_BPS 0x00000001 +#define ICP_PWR_CLP_IPE0 0x00010000 +#define ICP_PWR_CLP_IPE1 0x00020000 + +#define CAM_ICP_CTX_STATE_FREE 0x0 +#define CAM_ICP_CTX_STATE_IN_USE 0x1 +#define CAM_ICP_CTX_STATE_ACQUIRED 0x2 +#define CAM_ICP_CTX_STATE_RELEASE 0x3 + +#define CAM_ICP_CTX_MAX_CMD_BUFFERS 0x2 + +/** + * struct icp_hfi_mem_info + * @qtbl: Memory info of queue table + * @cmd_q: Memory info of command queue + * @msg_q: Memory info of message queue + * @dbg_q: Memory info of debug queue + * @sec_heap: Memory info of secondary heap + * @fw_buf: Memory info of firmware + * @qdss_buf: Memory info of qdss + * @shmem: Memory info for shared region + * @io_mem: Memory info for io region + */ +struct icp_hfi_mem_info { + struct cam_mem_mgr_memory_desc qtbl; + struct cam_mem_mgr_memory_desc cmd_q; + struct cam_mem_mgr_memory_desc msg_q; + struct cam_mem_mgr_memory_desc dbg_q; + struct cam_mem_mgr_memory_desc sec_heap; + struct cam_mem_mgr_memory_desc fw_buf; + struct cam_mem_mgr_memory_desc qdss_buf; + struct cam_smmu_region_info shmem; + struct cam_smmu_region_info io_mem; +}; + +/** + * struct hfi_cmd_work_data + * @type: Task type + * @data: Pointer to command data + * @request_id: Request id + */ +struct hfi_cmd_work_data { + uint32_t type; + void *data; + int32_t request_id; +}; + +/** + * struct hfi_msg_work_data + * @type: Task type + * @data: Pointer to message data + * @irq_status: IRQ status + */ +struct hfi_msg_work_data { + uint32_t type; + void *data; + uint32_t irq_status; +}; + +/** + * struct clk_work_data + * @type: Task type + * @data: Pointer to clock info + */ +struct clk_work_data { + uint32_t type; + void *data; +}; + +/** + * struct hfi_frame_process_info + * @hfi_frame_cmd: Frame process command info + * @bitmap: Bitmap for hfi_frame_cmd + * @bits: Used in hfi_frame_cmd bitmap + * @lock: Lock for hfi_frame_cmd + * @request_id: Request id list + * @num_out_resources: Number of out syncs + * @out_resource: Out sync info + * @fw_process_flag: Frame process flag + * @clk_info: Clock information for a request + */ +struct hfi_frame_process_info { + struct hfi_cmd_ipebps_async hfi_frame_cmd[CAM_FRAME_CMD_MAX]; + void *bitmap; + size_t bits; + struct mutex lock; + uint64_t request_id[CAM_FRAME_CMD_MAX]; + uint32_t num_out_resources[CAM_FRAME_CMD_MAX]; + uint32_t out_resource[CAM_FRAME_CMD_MAX][CAM_MAX_OUT_RES]; + uint32_t in_resource[CAM_FRAME_CMD_MAX]; + uint32_t in_free_resource[CAM_FRAME_CMD_MAX]; + uint32_t fw_process_flag[CAM_FRAME_CMD_MAX]; + struct cam_icp_clk_bw_request clk_info[CAM_FRAME_CMD_MAX]; +}; + +/** + * struct cam_ctx_clk_info + * @curr_fc: Context latest request frame cycles + * @rt_flag: Flag to indicate real time request + * @base_clk: Base clock to process the request + * @reserved: Reserved field + * #uncompressed_bw: Current bandwidth voting + * @compressed_bw: Current compressed bandwidth voting + * @clk_rate: Supported clock rates for the context + */ +struct cam_ctx_clk_info { + uint32_t curr_fc; + uint32_t rt_flag; + uint32_t base_clk; + uint32_t reserved; + uint64_t uncompressed_bw; + uint64_t compressed_bw; + int32_t clk_rate[CAM_MAX_VOTE]; +}; +/** + * struct cam_icp_hw_ctx_data + * @context_priv: Context private data + * @ctx_mutex: Mutex for context + * @fw_handle: Firmware handle + * @scratch_mem_size: Scratch memory size + * @acquire_dev_cmd: Acquire command + * @icp_dev_acquire_info: Acquire device info + * @ctxt_event_cb: Context callback function + * @state: context state + * @role: Role of a context in case of chaining + * @chain_ctx: Peer context + * @hfi_frame_process: Frame process command + * @wait_complete: Completion info + * @temp_payload: Payload for destroy handle data + * @ctx_id: Context Id + * @clk_info: Current clock info of a context + * @watch_dog: watchdog timer handle + * @watch_dog_reset_counter: Counter for watch dog reset + */ +struct cam_icp_hw_ctx_data { + void *context_priv; + struct mutex ctx_mutex; + uint32_t fw_handle; + uint32_t scratch_mem_size; + struct cam_acquire_dev_cmd acquire_dev_cmd; + struct cam_icp_acquire_dev_info *icp_dev_acquire_info; + cam_hw_event_cb_func ctxt_event_cb; + uint32_t state; + uint32_t role; + struct cam_icp_hw_ctx_data *chain_ctx; + struct hfi_frame_process_info hfi_frame_process; + struct completion wait_complete; + struct ipe_bps_destroy temp_payload; + uint32_t ctx_id; + struct cam_ctx_clk_info clk_info; + struct cam_req_mgr_timer *watch_dog; + uint32_t watch_dog_reset_counter; +}; + +/** + * struct icp_cmd_generic_blob + * @ctx: Current context info + * @frame_info_idx: Index used for frame process info + */ +struct icp_cmd_generic_blob { + struct cam_icp_hw_ctx_data *ctx; + uint32_t frame_info_idx; +}; + +/** + * struct cam_icp_clk_info + * @base_clk: Base clock to process request + * @curr_clk: Current clock of hadrware + * @threshold: Threshold for overclk count + * @over_clked: Over clock count + * @uncompressed_bw: Current bandwidth voting + * @compressed_bw: Current compressed bandwidth voting + * @hw_type: IPE/BPS device type + * @watch_dog: watchdog timer handle + * @watch_dog_reset_counter: Counter for watch dog reset + */ +struct cam_icp_clk_info { + uint32_t base_clk; + uint32_t curr_clk; + uint32_t threshold; + uint32_t over_clked; + uint64_t uncompressed_bw; + uint64_t compressed_bw; + uint32_t hw_type; + struct cam_req_mgr_timer *watch_dog; + uint32_t watch_dog_reset_counter; +}; + +/** + * struct cam_icp_hw_mgr + * @hw_mgr_mutex: Mutex for ICP hardware manager + * @hw_mgr_lock: Spinlock for ICP hardware manager + * @devices: Devices of ICP hardware manager + * @ctx_data: Context data + * @icp_caps: ICP capabilities + * @fw_download: Firmware download state + * @iommu_hdl: Non secure IOMMU handle + * @iommu_sec_hdl: Secure IOMMU handle + * @hfi_mem: Memory for hfi + * @cmd_work: Work queue for hfi commands + * @msg_work: Work queue for hfi messages + * @timer_work: Work queue for timer watchdog + * @msg_buf: Buffer for message data from firmware + * @dbg_buf: Buffer for debug data from firmware + * @a5_complete: Completion info + * @cmd_work_data: Pointer to command work queue task + * @msg_work_data: Pointer to message work queue task + * @timer_work_data: Pointer to timer work queue task + * @ctxt_cnt: Active context count + * @ipe_ctxt_cnt: IPE Active context count + * @bps_ctxt_cnt: BPS Active context count + * @dentry: Debugfs entry + * @a5_debug: A5 debug flag + * @icp_pc_flag: Flag to enable/disable power collapse + * @ipe_bps_pc_flag: Flag to enable/disable + * power collapse for ipe & bps + * @icp_debug_clk: Set clock based on debug value + * @icp_default_clk: Set this clok if user doesn't supply + * @clk_info: Clock info of hardware + * @secure_mode: Flag to enable/disable secure camera + * @a5_jtag_debug: entry to enable A5 JTAG debugging + * @a5_debug_type : entry to enable FW debug message/qdss + * @a5_dbg_lvl : debug level set to FW. + * @ipe0_enable: Flag for IPE0 + * @ipe1_enable: Flag for IPE1 + * @bps_enable: Flag for BPS + * @core_info: 32 bit value , tells IPE0/1 and BPS + * @a5_dev_intf : Device interface for A5 + * @ipe0_dev_intf: Device interface for IPE0 + * @ipe1_dev_intf: Device interface for IPE1 + * @bps_dev_intf: Device interface for BPS + * @ipe_clk_state: IPE clock state flag + * @bps_clk_state: BPS clock state flag + */ +struct cam_icp_hw_mgr { + struct mutex hw_mgr_mutex; + spinlock_t hw_mgr_lock; + + struct cam_hw_intf **devices[CAM_ICP_DEV_MAX]; + struct cam_icp_hw_ctx_data ctx_data[CAM_ICP_CTX_MAX]; + struct cam_icp_query_cap_cmd icp_caps; + + bool fw_download; + int32_t iommu_hdl; + int32_t iommu_sec_hdl; + struct icp_hfi_mem_info hfi_mem; + struct cam_req_mgr_core_workq *cmd_work; + struct cam_req_mgr_core_workq *msg_work; + struct cam_req_mgr_core_workq *timer_work; + uint32_t msg_buf[ICP_MSG_BUF_SIZE]; + uint32_t dbg_buf[ICP_DBG_BUF_SIZE]; + struct completion a5_complete; + struct hfi_cmd_work_data *cmd_work_data; + struct hfi_msg_work_data *msg_work_data; + struct hfi_msg_work_data *timer_work_data; + uint32_t ctxt_cnt; + uint32_t ipe_ctxt_cnt; + uint32_t bps_ctxt_cnt; + struct dentry *dentry; + bool a5_debug; + bool icp_pc_flag; + bool ipe_bps_pc_flag; + uint64_t icp_debug_clk; + uint64_t icp_default_clk; + struct cam_icp_clk_info clk_info[ICP_CLK_HW_MAX]; + bool secure_mode; + bool a5_jtag_debug; + u64 a5_debug_type; + u64 a5_dbg_lvl; + bool ipe0_enable; + bool ipe1_enable; + bool bps_enable; + uint32_t core_info; + struct cam_hw_intf *a5_dev_intf; + struct cam_hw_intf *ipe0_dev_intf; + struct cam_hw_intf *ipe1_dev_intf; + struct cam_hw_intf *bps_dev_intf; + bool ipe_clk_state; + bool bps_clk_state; +}; + +static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args); +static int cam_icp_mgr_hw_open(void *hw_mgr_priv, void *download_fw_args); +static int cam_icp_mgr_icp_resume(struct cam_icp_hw_mgr *hw_mgr); +static int cam_icp_mgr_icp_power_collapse(struct cam_icp_hw_mgr *hw_mgr); +#endif /* CAM_ICP_HW_MGR_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h new file mode 100644 index 000000000000..fd0482ce2b97 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h @@ -0,0 +1,81 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_A5_HW_INTF_H +#define CAM_A5_HW_INTF_H + +#include +#include +#include +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_intf.h" + +enum cam_icp_a5_cmd_type { + CAM_ICP_A5_CMD_FW_DOWNLOAD, + CAM_ICP_A5_CMD_POWER_COLLAPSE, + CAM_ICP_A5_CMD_POWER_RESUME, + CAM_ICP_A5_CMD_SET_FW_BUF, + CAM_ICP_A5_CMD_ACQUIRE, + CAM_ICP_A5_SET_IRQ_CB, + CAM_ICP_A5_TEST_IRQ, + CAM_ICP_A5_SEND_INIT, + CAM_ICP_A5_CMD_VOTE_CPAS, + CAM_ICP_A5_CMD_CPAS_START, + CAM_ICP_A5_CMD_CPAS_STOP, + CAM_ICP_A5_CMD_UBWC_CFG, + CAM_ICP_A5_CMD_PC_PREP, + CAM_ICP_A5_CMD_MAX, +}; + +struct cam_icp_a5_set_fw_buf_info { + uint32_t iova; + uint64_t kva; + uint64_t len; +}; + +/** + * struct cam_icp_a5_query_cap - ICP query device capability payload + * @fw_version: firmware version info + * @api_version: api version info + * @num_ipe: number of ipes + * @num_bps: number of bps + * @num_dev: number of device capabilities in dev_caps + * @reserved: reserved + * @dev_ver: returned device capability array + * @CAM_QUERY_CAP IOCTL + */ +struct cam_icp_a5_query_cap { + struct cam_icp_ver fw_version; + struct cam_icp_ver api_version; + uint32_t num_ipe; + uint32_t num_bps; + uint32_t num_dev; + uint32_t reserved; + struct cam_icp_dev_ver dev_ver[CAM_ICP_DEV_TYPE_MAX]; +}; + +struct cam_icp_a5_acquire_dev { + uint32_t ctx_id; + struct cam_icp_acquire_dev_info icp_acquire_info; + struct cam_icp_res_info icp_out_acquire_info[2]; + uint32_t fw_handle; +}; + +struct cam_icp_a5_set_irq_cb { + int32_t (*icp_hw_mgr_cb)(uint32_t irq_status, void *data); + void *data; +}; + +struct cam_icp_a5_test_irq { + uint32_t test_irq; +}; +#endif /* CAM_A5_HW_INTF_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h new file mode 100644 index 000000000000..4f0717290b06 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h @@ -0,0 +1,34 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_BPS_HW_INTF_H +#define CAM_BPS_HW_INTF_H + +#include +#include +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_intf.h" + +enum cam_icp_bps_cmd_type { + CAM_ICP_BPS_CMD_FW_DOWNLOAD, + CAM_ICP_BPS_CMD_POWER_COLLAPSE, + CAM_ICP_BPS_CMD_POWER_RESUME, + CAM_ICP_BPS_CMD_SET_FW_BUF, + CAM_ICP_BPS_CMD_VOTE_CPAS, + CAM_ICP_BPS_CMD_CPAS_START, + CAM_ICP_BPS_CMD_CPAS_STOP, + CAM_ICP_BPS_CMD_UPDATE_CLK, + CAM_ICP_BPS_CMD_DISABLE_CLK, + CAM_ICP_BPS_CMD_MAX, +}; + +#endif /* CAM_BPS_HW_INTF_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h new file mode 100644 index 000000000000..9e05f2bd6c9c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h @@ -0,0 +1,36 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_ICP_HW_INTF_H +#define CAM_ICP_HW_INTF_H + +#define CAM_ICP_CMD_BUF_MAX_SIZE 128 +#define CAM_ICP_MSG_BUF_MAX_SIZE CAM_ICP_CMD_BUF_MAX_SIZE + +enum cam_a5_hw_type { + CAM_ICP_DEV_A5, + CAM_ICP_DEV_IPE, + CAM_ICP_DEV_BPS, + CAM_ICP_DEV_MAX, +}; + +/** + * struct cam_a5_clk_update_cmd - Payload for hw manager command + * + * @curr_clk_rate: clk rate to HW + * @ipe_bps_pc_enable power collpase enable flag + */ +struct cam_a5_clk_update_cmd { + uint32_t curr_clk_rate; + bool ipe_bps_pc_enable; +}; +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h new file mode 100644 index 000000000000..0943bef0836d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h @@ -0,0 +1,34 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_IPE_HW_INTF_H +#define CAM_IPE_HW_INTF_H + +#include +#include +#include "cam_hw_mgr_intf.h" +#include "cam_icp_hw_intf.h" + +enum cam_icp_ipe_cmd_type { + CAM_ICP_IPE_CMD_FW_DOWNLOAD, + CAM_ICP_IPE_CMD_POWER_COLLAPSE, + CAM_ICP_IPE_CMD_POWER_RESUME, + CAM_ICP_IPE_CMD_SET_FW_BUF, + CAM_ICP_IPE_CMD_VOTE_CPAS, + CAM_ICP_IPE_CMD_CPAS_START, + CAM_ICP_IPE_CMD_CPAS_STOP, + CAM_ICP_IPE_CMD_UPDATE_CLK, + CAM_ICP_IPE_CMD_DISABLE_CLK, + CAM_ICP_IPE_CMD_MAX, +}; + +#endif /* CAM_IPE_HW_INTF_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h new file mode 100644 index 000000000000..2ebe41417d1d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2017-2019, 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 CAM_ICP_HW_MGR_INTF_H +#define CAM_ICP_HW_MGR_INTF_H + +#include +#include +#include +#include "cam_cpas_api.h" + +#define ICP_CLK_TURBO_HZ 600000000 +#define ICP_CLK_SVS_HZ 400000000 + +#define CAM_ICP_A5_BW_BYTES_VOTE 40000000 + +#define CAM_ICP_CTX_MAX 54 + +#define CPAS_IPE1_BIT 0x2000 + +int cam_icp_hw_mgr_init(struct device_node *of_node, + uint64_t *hw_mgr_hdl, int *iommu_hdl); + +/** + * struct cam_icp_cpas_vote + * @ahb_vote: AHB vote info + * @axi_vote: AXI vote info + * @ahb_vote_valid: Flag for ahb vote data + * @axi_vote_valid: flag for axi vote data + */ +struct cam_icp_cpas_vote { + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + uint32_t ahb_vote_valid; + uint32_t axi_vote_valid; +}; + +#endif /* CAM_ICP_HW_MGR_INTF_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/Makefile new file mode 100644 index 000000000000..756eac3e64d4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/icp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_icp/fw_inc +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += ipe_dev.o ipe_core.o ipe_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.c new file mode 100644 index 000000000000..87478af551b7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.c @@ -0,0 +1,314 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include +#include +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "ipe_core.h" +#include "ipe_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_ipe_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static int cam_ipe_caps_vote(struct cam_ipe_device_core_info *core_info, + struct cam_icp_cpas_vote *cpas_vote) +{ + int rc = 0; + + if (cpas_vote->ahb_vote_valid) + rc = cam_cpas_update_ahb_vote(core_info->cpas_handle, + &cpas_vote->ahb_vote); + if (cpas_vote->axi_vote_valid) + rc = cam_cpas_update_axi_vote(core_info->cpas_handle, + &cpas_vote->axi_vote); + + if (rc) + CAM_ERR(CAM_ICP, "cpas vote is failed: %d", rc); + + return rc; +} + +int cam_ipe_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *ipe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + struct cam_icp_cpas_vote cpas_vote; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &ipe_dev->soc_info; + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE; + cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE; + cpas_vote.axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + cpas_vote.axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote.ahb_vote, &cpas_vote.axi_vote); + if (rc) { + CAM_ERR(CAM_ICP, "cpass start failed: %d", rc); + return rc; + } + core_info->cpas_start = true; + + rc = cam_ipe_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ICP, "soc enable is failed : %d", rc); + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } else { + core_info->clk_enable = true; + } + + return rc; +} + +int cam_ipe_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *ipe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &ipe_dev->soc_info; + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + if ((!soc_info) || (!core_info)) { + CAM_ERR(CAM_ICP, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + rc = cam_ipe_disable_soc_resources(soc_info, core_info->clk_enable); + if (rc) + CAM_ERR(CAM_ICP, "soc disable is failed : %d", rc); + core_info->clk_enable = false; + + if (core_info->cpas_start) { + if (cam_cpas_stop(core_info->cpas_handle)) + CAM_ERR(CAM_ICP, "cpas stop is failed"); + else + core_info->cpas_start = false; + } + + return rc; +} + +static int cam_ipe_handle_pc(struct cam_hw_info *ipe_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + struct cam_ipe_device_hw_info *hw_info = NULL; + int pwr_ctrl; + int pwr_status; + + soc_info = &ipe_dev->soc_info; + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + hw_info = core_info->ipe_hw_info; + + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (!(pwr_ctrl & IPE_COLLAPSE_MASK)) { + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x1); + + if (pwr_status >> IPE_PWR_ON_MASK) + return -EINVAL; + + } + cam_ipe_get_gdsc_control(soc_info); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + CAM_DBG(CAM_ICP, "pwr_ctrl = %x pwr_status = %x", + pwr_ctrl, pwr_status); + + return 0; +} + +static int cam_ipe_handle_resume(struct cam_hw_info *ipe_dev) +{ + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + struct cam_ipe_device_hw_info *hw_info = NULL; + int pwr_ctrl; + int pwr_status; + int rc = 0; + + soc_info = &ipe_dev->soc_info; + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + hw_info = core_info->ipe_hw_info; + + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, + true, &pwr_ctrl); + if (pwr_ctrl & IPE_COLLAPSE_MASK) { + CAM_ERR(CAM_ICP, "IPE: resume failed : %d", pwr_ctrl); + return -EINVAL; + } + rc = cam_ipe_transfer_gdsc_control(soc_info); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_ctrl, true, &pwr_ctrl); + cam_cpas_reg_read(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, hw_info->pwr_status, + true, &pwr_status); + CAM_DBG(CAM_ICP, "pwr_ctrl = %x pwr_status = %x", + pwr_ctrl, pwr_status); + + return rc; +} + +int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *ipe_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + struct cam_ipe_device_hw_info *hw_info = NULL; + int rc = 0; + + if (!device_priv) { + CAM_ERR(CAM_ICP, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_ICP_IPE_CMD_MAX) { + CAM_ERR(CAM_ICP, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + soc_info = &ipe_dev->soc_info; + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + hw_info = core_info->ipe_hw_info; + + switch (cmd_type) { + case CAM_ICP_IPE_CMD_VOTE_CPAS: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) + return -EINVAL; + + cam_ipe_caps_vote(core_info, cpas_vote); + break; + } + + case CAM_ICP_IPE_CMD_CPAS_START: { + struct cam_icp_cpas_vote *cpas_vote = cmd_args; + + if (!cmd_args) + return -EINVAL; + + if (!core_info->cpas_start) { + rc = cam_cpas_start(core_info->cpas_handle, + &cpas_vote->ahb_vote, &cpas_vote->axi_vote); + core_info->cpas_start = true; + } + break; + } + + case CAM_ICP_IPE_CMD_CPAS_STOP: + if (core_info->cpas_start) { + cam_cpas_stop(core_info->cpas_handle); + core_info->cpas_start = false; + } + break; + case CAM_ICP_IPE_CMD_POWER_COLLAPSE: + rc = cam_ipe_handle_pc(ipe_dev); + break; + case CAM_ICP_IPE_CMD_POWER_RESUME: + rc = cam_ipe_handle_resume(ipe_dev); + break; + case CAM_ICP_IPE_CMD_UPDATE_CLK: { + struct cam_a5_clk_update_cmd *clk_upd_cmd = + (struct cam_a5_clk_update_cmd *)cmd_args; + uint32_t clk_rate = clk_upd_cmd->curr_clk_rate; + + CAM_DBG(CAM_ICP, "ipe_src_clk rate = %d", (int)clk_rate); + if (!core_info->clk_enable) { + if (clk_upd_cmd->ipe_bps_pc_enable) { + cam_ipe_handle_pc(ipe_dev); + cam_cpas_reg_write(core_info->cpas_handle, + CAM_CPAS_REG_CPASTOP, + hw_info->pwr_ctrl, true, 0x0); + } + rc = cam_ipe_toggle_clk(soc_info, true); + if (rc) + CAM_ERR(CAM_ICP, "Enable failed"); + else + core_info->clk_enable = true; + if (clk_upd_cmd->ipe_bps_pc_enable) { + rc = cam_ipe_handle_resume(ipe_dev); + if (rc) + CAM_ERR(CAM_ICP, "bps resume failed"); + } + } + CAM_DBG(CAM_ICP, "clock rate %d", clk_rate); + + rc = cam_ipe_update_clk_rate(soc_info, clk_rate); + if (rc) + CAM_ERR(CAM_ICP, "Failed to update clk"); + } + break; + case CAM_ICP_IPE_CMD_DISABLE_CLK: + if (core_info->clk_enable == true) + cam_ipe_toggle_clk(soc_info, false); + core_info->clk_enable = false; + break; + default: + break; + } + return rc; +} + +irqreturn_t cam_ipe_irq(int irq_num, void *data) +{ + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.h new file mode 100644 index 000000000000..e04dece956b7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_core.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_IPE_CORE_H +#define CAM_IPE_CORE_H + +#include +#include +#include +#include + +#define IPE_COLLAPSE_MASK 0x1 +#define IPE_PWR_ON_MASK 0x2 + +struct cam_ipe_device_hw_info { + uint32_t hw_idx; + uint32_t pwr_ctrl; + uint32_t pwr_status; + uint32_t reserved; +}; + +struct cam_ipe_device_core_info { + struct cam_ipe_device_hw_info *ipe_hw_info; + uint32_t cpas_handle; + bool cpas_start; + bool clk_enable; +}; + +int cam_ipe_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_ipe_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); +irqreturn_t cam_ipe_irq(int irq_num, void *data); + +#endif /* CAM_IPE_CORE_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_dev.c new file mode 100644 index 000000000000..cc2b1b1f88a5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_dev.c @@ -0,0 +1,198 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include "ipe_core.h" +#include "ipe_soc.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_io_util.h" +#include "cam_icp_hw_intf.h" +#include "cam_icp_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static struct cam_ipe_device_hw_info cam_ipe_hw_info[] = { + { + .hw_idx = 0, + .pwr_ctrl = 0x4c, + .pwr_status = 0x48, + .reserved = 0, + }, + { + .hw_idx = 1, + .pwr_ctrl = 0x54, + .pwr_status = 0x50, + .reserved = 0, + }, +}; +EXPORT_SYMBOL(cam_ipe_hw_info); + +int cam_ipe_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_ipe_device_core_info *core_info, + uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = &soc_info->pdev->dev; + memcpy(cpas_register_params.identifier, "ipe", sizeof("ipe")); + cpas_register_params.cam_cpas_client_cb = NULL; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed: %d", rc); + return rc; + } + core_info->cpas_handle = cpas_register_params.client_handle; + + return rc; +} + +int cam_ipe_probe(struct platform_device *pdev) +{ + struct cam_hw_info *ipe_dev = NULL; + struct cam_hw_intf *ipe_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_ipe_device_core_info *core_info = NULL; + struct cam_ipe_device_hw_info *hw_info = NULL; + int rc = 0; + struct cam_cpas_query_cap query; + uint32_t cam_caps; + uint32_t hw_idx; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &hw_idx); + + cam_cpas_get_hw_info(&query.camera_family, + &query.camera_version, &query.cpas_version, &cam_caps); + if ((!(cam_caps & CPAS_IPE1_BIT)) && (hw_idx)) { + CAM_ERR(CAM_ICP, "IPE1 hw idx = %d\n", hw_idx); + return -EINVAL; + } + + ipe_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!ipe_dev_intf) + return -ENOMEM; + + ipe_dev_intf->hw_idx = hw_idx; + ipe_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!ipe_dev) { + kfree(ipe_dev_intf); + return -ENOMEM; + } + ipe_dev->soc_info.pdev = pdev; + ipe_dev->soc_info.dev = &pdev->dev; + ipe_dev->soc_info.dev_name = pdev->name; + ipe_dev_intf->hw_priv = ipe_dev; + ipe_dev_intf->hw_ops.init = cam_ipe_init_hw; + ipe_dev_intf->hw_ops.deinit = cam_ipe_deinit_hw; + ipe_dev_intf->hw_ops.process_cmd = cam_ipe_process_cmd; + ipe_dev_intf->hw_type = CAM_ICP_DEV_IPE; + + CAM_DBG(CAM_ICP, "type %d index %d", + ipe_dev_intf->hw_type, + ipe_dev_intf->hw_idx); + + platform_set_drvdata(pdev, ipe_dev_intf); + + ipe_dev->core_info = kzalloc(sizeof(struct cam_ipe_device_core_info), + GFP_KERNEL); + if (!ipe_dev->core_info) { + kfree(ipe_dev); + kfree(ipe_dev_intf); + return -ENOMEM; + } + core_info = (struct cam_ipe_device_core_info *)ipe_dev->core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_DBG(CAM_ICP, "No ipe hardware info"); + kfree(ipe_dev->core_info); + kfree(ipe_dev); + kfree(ipe_dev_intf); + rc = -EINVAL; + return rc; + } + hw_info = &cam_ipe_hw_info[ipe_dev_intf->hw_idx]; + core_info->ipe_hw_info = hw_info; + + rc = cam_ipe_init_soc_resources(&ipe_dev->soc_info, cam_ipe_irq, + ipe_dev); + if (rc < 0) { + CAM_ERR(CAM_ICP, "failed to init_soc"); + kfree(ipe_dev->core_info); + kfree(ipe_dev); + kfree(ipe_dev_intf); + return rc; + } + + CAM_DBG(CAM_ICP, "cam_ipe_init_soc_resources : %pK", + (void *)&ipe_dev->soc_info); + rc = cam_ipe_register_cpas(&ipe_dev->soc_info, + core_info, ipe_dev_intf->hw_idx); + if (rc < 0) { + kfree(ipe_dev->core_info); + kfree(ipe_dev); + kfree(ipe_dev_intf); + return rc; + } + ipe_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&ipe_dev->hw_mutex); + spin_lock_init(&ipe_dev->hw_lock); + init_completion(&ipe_dev->hw_complete); + + CAM_DBG(CAM_ICP, "IPE%d probe successful", + ipe_dev_intf->hw_idx); + + return rc; +} + +static const struct of_device_id cam_ipe_dt_match[] = { + { + .compatible = "qcom,cam-ipe", + .data = &cam_ipe_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_ipe_dt_match); + +static struct platform_driver cam_ipe_driver = { + .probe = cam_ipe_probe, + .driver = { + .name = "cam-ipe", + .owner = THIS_MODULE, + .of_match_table = cam_ipe_dt_match, + }, +}; + +static int __init cam_ipe_init_module(void) +{ + return platform_driver_register(&cam_ipe_driver); +} + +static void __exit cam_ipe_exit_module(void) +{ + platform_driver_unregister(&cam_ipe_driver); +} + +module_init(cam_ipe_init_module); +module_exit(cam_ipe_exit_module); +MODULE_DESCRIPTION("CAM IPE driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.c new file mode 100644 index 000000000000..c1d02f4a9710 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include "ipe_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + + +int cam_ipe_transfer_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + int i; + int rc; + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_FAST); + if (rc) { + CAM_ERR(CAM_ICP, "Regulator set mode %s failed", + soc_info->rgltr_name[i]); + goto rgltr_set_mode_failed; + } + } + return 0; + +rgltr_set_mode_failed: + for (i = i - 1; i >= 0; i--) + if (soc_info->rgltr[i]) + regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_NORMAL); + + return rc; +} + +int cam_ipe_get_gdsc_control(struct cam_hw_soc_info *soc_info) +{ + int i; + int rc; + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_NORMAL); + if (rc) { + CAM_ERR(CAM_ICP, "Regulator set mode %s failed", + soc_info->rgltr_name[i]); + goto rgltr_set_mode_failed; + } + } + return 0; + +rgltr_set_mode_failed: + for (i = i - 1; i >= 0; i--) + if (soc_info->rgltr[i]) + regulator_set_mode(soc_info->rgltr[i], + REGULATOR_MODE_FAST); + + return rc; +} + +static int cam_ipe_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) + CAM_ERR(CAM_ICP, "get ipe dt prop is failed"); + + return rc; +} + +static int cam_ipe_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t ipe_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, ipe_irq_handler, + irq_data); + + return rc; +} + +int cam_ipe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t ipe_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_ipe_get_dt_properties(soc_info); + if (rc < 0) + return rc; + + rc = cam_ipe_request_platform_resource(soc_info, ipe_irq_handler, + irq_data); + if (rc < 0) + return rc; + + return rc; +} + +int cam_ipe_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, false); + if (rc) { + CAM_ERR(CAM_ICP, "enable platform failed"); + return rc; + } + + return rc; +} + +int cam_ipe_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk) +{ + int rc = 0; + + rc = cam_soc_util_disable_platform_resource(soc_info, disable_clk, + false); + if (rc) + CAM_ERR(CAM_ICP, "enable platform failed"); + + return rc; +} + +int cam_ipe_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate) +{ + int32_t src_clk_idx; + + if (!soc_info) + return -EINVAL; + + src_clk_idx = soc_info->src_clk_idx; + + if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) && + (soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) && + (clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) { + CAM_WARN(CAM_ICP, "clk_rate %d greater than max, reset to %d", + clk_rate, + soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]); + clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]; + } + + return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx], + soc_info->clk_name[soc_info->src_clk_idx], clk_rate); +} + +int cam_ipe_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable) +{ + int rc = 0; + + if (clk_enable) + rc = cam_soc_util_clk_enable_default(soc_info, CAM_SVS_VOTE); + else + cam_soc_util_clk_disable_default(soc_info); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.h new file mode 100644 index 000000000000..5385bde371e7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_icp/icp_hw/ipe_hw/ipe_soc.h @@ -0,0 +1,33 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_IPE_SOC_H +#define CAM_IPE_SOC_H + +#include "cam_soc_util.h" + +int cam_ipe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t ipe_irq_handler, void *irq_data); + +int cam_ipe_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_ipe_disable_soc_resources(struct cam_hw_soc_info *soc_info, + bool disable_clk); + +int cam_ipe_get_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_ipe_transfer_gdsc_control(struct cam_hw_soc_info *soc_info); + +int cam_ipe_update_clk_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_rate); +int cam_ipe_toggle_clk(struct cam_hw_soc_info *soc_info, bool clk_enable); +#endif /* CAM_IPE_SOC_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/Makefile new file mode 100644 index 000000000000..69cbfac3106e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += isp_hw_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp_dev.o cam_isp_context.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.c new file mode 100644 index 000000000000..92fdf1cac415 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.c @@ -0,0 +1,2586 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include + +#include "cam_isp_context.h" +#include "cam_isp_log.h" +#include "cam_mem_mgr.h" +#include "cam_sync_api.h" +#include "cam_req_mgr_dev.h" +#include "cam_trace.h" +#include "cam_debug_util.h" + +static const char isp_dev_name[] = "isp"; + +static int __cam_isp_ctx_enqueue_request_in_order( + struct cam_context *ctx, struct cam_ctx_request *req) +{ + struct cam_ctx_request *req_current; + struct cam_ctx_request *req_prev; + struct list_head temp_list; + + INIT_LIST_HEAD(&temp_list); + spin_lock_bh(&ctx->lock); + if (list_empty(&ctx->pending_req_list)) { + list_add_tail(&req->list, &ctx->pending_req_list); + } else { + list_for_each_entry_safe_reverse( + req_current, req_prev, &ctx->pending_req_list, list) { + if (req->request_id < req_current->request_id) { + list_del_init(&req_current->list); + list_add(&req_current->list, &temp_list); + continue; + } else if (req->request_id == req_current->request_id) { + CAM_WARN(CAM_ISP, + "Received duplicated request %lld", + req->request_id); + } + break; + } + list_add_tail(&req->list, &ctx->pending_req_list); + + if (!list_empty(&temp_list)) { + list_for_each_entry_safe( + req_current, req_prev, &temp_list, list) { + list_del_init(&req_current->list); + list_add_tail(&req_current->list, + &ctx->pending_req_list); + } + } + } + spin_unlock_bh(&ctx->lock); + return 0; +} + +static int __cam_isp_ctx_enqueue_init_request( + struct cam_context *ctx, struct cam_ctx_request *req) +{ + int rc = 0; + struct cam_ctx_request *req_old; + struct cam_isp_ctx_req *req_isp_old; + struct cam_isp_ctx_req *req_isp_new; + + spin_lock_bh(&ctx->lock); + if (list_empty(&ctx->pending_req_list)) { + list_add_tail(&req->list, &ctx->pending_req_list); + CAM_DBG(CAM_ISP, "INIT packet added req id= %d", + req->request_id); + goto end; + } + + req_old = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + req_isp_old = (struct cam_isp_ctx_req *) req_old->req_priv; + req_isp_new = (struct cam_isp_ctx_req *) req->req_priv; + if (req_isp_old->hw_update_data.packet_opcode_type == + CAM_ISP_PACKET_INIT_DEV) { + if ((req_isp_old->num_cfg + req_isp_new->num_cfg) >= + CAM_ISP_CTX_CFG_MAX) { + CAM_WARN(CAM_ISP, "Can not merge INIT pkt"); + rc = -ENOMEM; + } + + if (req_isp_old->num_fence_map_out != 0 || + req_isp_old->num_fence_map_in != 0) { + CAM_WARN(CAM_ISP, "Invalid INIT pkt sequence"); + rc = -EINVAL; + } + + if (!rc) { + memcpy(req_isp_old->fence_map_out, + req_isp_new->fence_map_out, + sizeof(req_isp_new->fence_map_out[0])* + req_isp_new->num_fence_map_out); + req_isp_old->num_fence_map_out = + req_isp_new->num_fence_map_out; + + memcpy(req_isp_old->fence_map_in, + req_isp_new->fence_map_in, + sizeof(req_isp_new->fence_map_in[0])* + req_isp_new->num_fence_map_in); + req_isp_old->num_fence_map_in = + req_isp_new->num_fence_map_in; + + memcpy(&req_isp_old->cfg[req_isp_old->num_cfg], + req_isp_new->cfg, + sizeof(req_isp_new->cfg[0])* + req_isp_new->num_cfg); + req_isp_old->num_cfg += req_isp_new->num_cfg; + + list_add_tail(&req->list, &ctx->free_req_list); + } + } else { + CAM_WARN(CAM_ISP, + "Received Update pkt before INIT pkt. req_id= %lld", + req->request_id); + rc = -EINVAL; + } +end: + spin_unlock_bh(&ctx->lock); + return rc; +} + +static const char *__cam_isp_resource_handle_id_to_type + (uint32_t resource_handle) +{ + switch (resource_handle) { + case CAM_ISP_IFE_OUT_RES_FULL: + return "CAM_ISP_IFE_OUT_RES_FULL"; + case CAM_ISP_IFE_OUT_RES_DS4: + return "CAM_ISP_IFE_OUT_RES_DS4"; + case CAM_ISP_IFE_OUT_RES_DS16: + return "CAM_ISP_IFE_OUT_RES_DS16"; + case CAM_ISP_IFE_OUT_RES_RAW_DUMP: + return "CAM_ISP_IFE_OUT_RES_RAW_DUMP"; + case CAM_ISP_IFE_OUT_RES_FD: + return "CAM_ISP_IFE_OUT_RES_FD"; + case CAM_ISP_IFE_OUT_RES_PDAF: + return "CAM_ISP_IFE_OUT_RES_PDAF"; + case CAM_ISP_IFE_OUT_RES_RDI_0: + return "CAM_ISP_IFE_OUT_RES_RDI_0"; + case CAM_ISP_IFE_OUT_RES_RDI_1: + return "CAM_ISP_IFE_OUT_RES_RDI_1"; + case CAM_ISP_IFE_OUT_RES_RDI_2: + return "CAM_ISP_IFE_OUT_RES_RDI_2"; + case CAM_ISP_IFE_OUT_RES_RDI_3: + return "CAM_ISP_IFE_OUT_RES_RDI_3"; + case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE: + return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BE"; + case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST: + return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST"; + case CAM_ISP_IFE_OUT_RES_STATS_TL_BG: + return "CAM_ISP_IFE_OUT_RES_STATS_TL_BG"; + case CAM_ISP_IFE_OUT_RES_STATS_BF: + return "CAM_ISP_IFE_OUT_RES_STATS_BF"; + case CAM_ISP_IFE_OUT_RES_STATS_AWB_BG: + return "CAM_ISP_IFE_OUT_RES_STATS_AWB_BG"; + case CAM_ISP_IFE_OUT_RES_STATS_BHIST: + return "CAM_ISP_IFE_OUT_RES_STATS_BHIST"; + case CAM_ISP_IFE_OUT_RES_STATS_RS: + return "CAM_ISP_IFE_OUT_RES_STATS_RS"; + case CAM_ISP_IFE_OUT_RES_STATS_CS: + return "CAM_ISP_IFE_OUT_RES_STATS_CS"; + default: + return "CAM_ISP_Invalid_Resource_Type"; + } +} + +static uint64_t __cam_isp_ctx_get_event_ts(uint32_t evt_id, void *evt_data) +{ + uint64_t ts = 0; + + if (!evt_data) + return 0; + + switch (evt_id) { + case CAM_ISP_HW_EVENT_ERROR: + ts = ((struct cam_isp_hw_error_event_data *)evt_data)-> + timestamp; + break; + case CAM_ISP_HW_EVENT_SOF: + ts = ((struct cam_isp_hw_sof_event_data *)evt_data)-> + timestamp; + break; + case CAM_ISP_HW_EVENT_REG_UPDATE: + ts = ((struct cam_isp_hw_reg_update_event_data *)evt_data)-> + timestamp; + break; + case CAM_ISP_HW_EVENT_EPOCH: + ts = ((struct cam_isp_hw_epoch_event_data *)evt_data)-> + timestamp; + break; + case CAM_ISP_HW_EVENT_EOF: + ts = ((struct cam_isp_hw_eof_event_data *)evt_data)-> + timestamp; + break; + case CAM_ISP_HW_EVENT_DONE: + break; + default: + CAM_DBG(CAM_ISP, "Invalid Event Type %d", evt_id); + } + + return ts; +} + +static void __cam_isp_ctx_handle_buf_done_fail_log( + struct cam_isp_ctx_req *req_isp) +{ + int i; + + if (req_isp->num_fence_map_out >= CAM_ISP_CTX_RES_MAX) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Num Resources exceed mMAX %d >= %d ", + req_isp->num_fence_map_out, CAM_ISP_CTX_RES_MAX); + return; + } + + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Resource Handles that fail to generate buf_done in prev frame"); + for (i = 0; i < req_isp->num_fence_map_out; i++) { + if (req_isp->fence_map_out[i].sync_id != -1) + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Resource_Handle: [%s] Sync_ID: [0x%x]", + __cam_isp_resource_handle_id_to_type( + req_isp->fence_map_out[i].resource_handle), + req_isp->fence_map_out[i].sync_id); + } +} + +static int __cam_isp_ctx_handle_buf_done_in_activated_state( + struct cam_isp_context *ctx_isp, + struct cam_isp_hw_done_event_data *done, + uint32_t bubble_state) +{ + int rc = 0; + int i, j; + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_context *ctx = ctx_isp->base; + + if (list_empty(&ctx->active_req_list)) { + CAM_DBG(CAM_ISP, "Buf done with no active request!"); + goto end; + } + + CAM_DBG(CAM_ISP, "Enter with bubble_state %d", bubble_state); + + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + + trace_cam_buf_done("ISP", ctx, req); + + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + for (i = 0; i < done->num_handles; i++) { + for (j = 0; j < req_isp->num_fence_map_out; j++) { + if (done->resource_handle[i] == + req_isp->fence_map_out[j].resource_handle) + break; + } + + if (j == req_isp->num_fence_map_out) { + CAM_ERR(CAM_ISP, + "Can not find matching lane handle 0x%x!", + done->resource_handle[i]); + rc = -EINVAL; + continue; + } + + if (req_isp->fence_map_out[j].sync_id == -1) { + __cam_isp_ctx_handle_buf_done_fail_log(req_isp); + continue; + } + + if (!bubble_state) { + CAM_DBG(CAM_ISP, + "Sync with success: req %lld res 0x%x fd 0x%x", + req->request_id, + req_isp->fence_map_out[j].resource_handle, + req_isp->fence_map_out[j].sync_id); + + rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id, + CAM_SYNC_STATE_SIGNALED_SUCCESS); + if (rc) + CAM_DBG(CAM_ISP, "Sync failed with rc = %d", + rc); + } else if (!req_isp->bubble_report) { + CAM_DBG(CAM_ISP, + "Sync with failure: req %lld res 0x%x fd 0x%x", + req->request_id, + req_isp->fence_map_out[j].resource_handle, + req_isp->fence_map_out[j].sync_id); + + rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc) + CAM_ERR(CAM_ISP, "Sync failed with rc = %d", + rc); + } else { + /* + * Ignore the buffer done if bubble detect is on + * In most case, active list should be empty when + * bubble detects. But for safety, we just move the + * current active request to the pending list here. + */ + CAM_DBG(CAM_ISP, + "buf done with bubble state %d recovery %d", + bubble_state, req_isp->bubble_report); + list_del_init(&req->list); + list_add(&req->list, &ctx->pending_req_list); + continue; + } + + CAM_DBG(CAM_ISP, "req %lld, reset sync id 0x%x", + req->request_id, + req_isp->fence_map_out[j].sync_id); + if (!rc) { + req_isp->num_acked++; + req_isp->fence_map_out[j].sync_id = -1; + } + } + + if (req_isp->num_acked > req_isp->num_fence_map_out) { + /* Should not happen */ + CAM_ERR(CAM_ISP, + "WARNING: req_id %lld num_acked %d > map_out %d", + req->request_id, req_isp->num_acked, + req_isp->num_fence_map_out); + WARN_ON(req_isp->num_acked > req_isp->num_fence_map_out); + } + if (req_isp->num_acked == req_isp->num_fence_map_out) { + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + ctx_isp->active_req_cnt--; + CAM_DBG(CAM_ISP, + "Move active request %lld to free list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + } + +end: + return rc; +} + +static void __cam_isp_ctx_send_sof_timestamp( + struct cam_isp_context *ctx_isp, uint64_t request_id, + uint32_t sof_event_status) +{ + struct cam_req_mgr_message req_msg; + + req_msg.session_hdl = ctx_isp->base->session_hdl; + req_msg.u.frame_msg.frame_id = ctx_isp->frame_id; + req_msg.u.frame_msg.request_id = request_id; + req_msg.u.frame_msg.timestamp = ctx_isp->sof_timestamp_val; + req_msg.u.frame_msg.link_hdl = ctx_isp->base->link_hdl; + req_msg.u.frame_msg.sof_status = sof_event_status; + + CAM_DBG(CAM_ISP, + "request id:%lld frame number:%lld SOF time stamp:0x%llx", + request_id, ctx_isp->frame_id, + ctx_isp->sof_timestamp_val); + CAM_DBG(CAM_ISP, " sof status:%d", sof_event_status); + + if (cam_req_mgr_notify_message(&req_msg, + V4L_EVENT_CAM_REQ_MGR_SOF, V4L_EVENT_CAM_REQ_MGR_EVENT)) + CAM_ERR(CAM_ISP, + "Error in notifying the sof time for req id:%lld", + request_id); +} + +static int __cam_isp_ctx_reg_upd_in_activated_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_ctx_request *req; + struct cam_context *ctx = ctx_isp->base; + struct cam_isp_ctx_req *req_isp; + + if (list_empty(&ctx->pending_req_list)) { + CAM_ERR(CAM_ISP, "Reg upd ack with no pending request"); + goto end; + } + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + if (req_isp->num_fence_map_out != 0) { + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, "move request %lld to active list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + } else { + /* no io config, so the request is completed. */ + list_add_tail(&req->list, &ctx->free_req_list); + CAM_DBG(CAM_ISP, + "move active request %lld to free list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + } + + /* + * This function only called directly from applied and bubble applied + * state so change substate here. + */ + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH; + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); + +end: + return rc; +} + +static int __cam_isp_ctx_notify_sof_in_actived_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_req_mgr_trigger_notify notify; + struct cam_context *ctx = ctx_isp->base; + struct cam_ctx_request *req; + uint64_t request_id = 0; + + /* + * notify reqmgr with sof signal. Note, due to scheduling delay + * we can run into situation that two active requests has already + * be in the active queue while we try to do the notification. + * In this case, we need to skip the current notification. This + * helps the state machine to catch up the delay. + */ + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger && + ctx_isp->active_req_cnt <= 2) { + if (ctx_isp->subscribe_event & CAM_TRIGGER_POINT_SOF) { + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.frame_id = ctx_isp->frame_id; + notify.trigger = CAM_TRIGGER_POINT_SOF; + + ctx->ctx_crm_intf->notify_trigger(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld", + ctx_isp->frame_id); + } + + list_for_each_entry(req, &ctx->active_req_list, list) { + if (req->request_id > ctx_isp->reported_req_id) { + request_id = req->request_id; + ctx_isp->reported_req_id = request_id; + break; + } + } + + if (ctx_isp->substate_activated == CAM_ISP_CTX_ACTIVATED_BUBBLE) + request_id = 0; + + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify SOF to CRM"); + rc = -EFAULT; + } + + return 0; +} + +static int __cam_isp_ctx_notify_eof_in_actived_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_req_mgr_trigger_notify notify; + struct cam_context *ctx = ctx_isp->base; + + if (!(ctx_isp->subscribe_event & CAM_TRIGGER_POINT_EOF)) + return rc; + + /* notify reqmgr with eof signal */ + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger) { + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.frame_id = ctx_isp->frame_id; + notify.trigger = CAM_TRIGGER_POINT_EOF; + + ctx->ctx_crm_intf->notify_trigger(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM EOF frame %lld\n", + ctx_isp->frame_id); + } else { + CAM_ERR(CAM_ISP, "Can not notify EOF to CRM"); + rc = -EFAULT; + } + + return rc; +} + +static int __cam_isp_ctx_reg_upd_in_hw_error( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + return 0; +} + +static int __cam_isp_ctx_sof_in_activated_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + + return rc; +} + +static int __cam_isp_ctx_reg_upd_in_sof(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + int rc = 0; + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_context *ctx = ctx_isp->base; + + if (ctx->state != CAM_CTX_ACTIVATED) { + CAM_DBG(CAM_ISP, "invalid RUP"); + goto end; + } + + /* + * This is for the first update. The initial setting will + * cause the reg_upd in the first frame. + */ + if (!list_empty(&ctx->pending_req_list)) { + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + if (req_isp->num_fence_map_out == req_isp->num_acked) { + list_add_tail(&req->list, &ctx->free_req_list); + } else { + /* need to handle the buf done */ + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, + "move request %lld to active list(cnt = %d)", + req->request_id, + ctx_isp->active_req_cnt); + ctx_isp->substate_activated = + CAM_ISP_CTX_ACTIVATED_EPOCH; + } + } +end: + return rc; +} + +static int __cam_isp_ctx_epoch_in_applied(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_context *ctx = ctx_isp->base; + uint64_t request_id = 0; + + if (list_empty(&ctx->pending_req_list)) { + /* + * If no pending req in epoch, this is an error case. + * The recovery is to go back to sof state + */ + CAM_ERR(CAM_ISP, "No pending request"); + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + + /* Send SOF event as empty frame*/ + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + goto end; + } + + req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request, + list); + req_isp = (struct cam_isp_ctx_req *)req->req_priv; + + CAM_DBG(CAM_ISP, "Report Bubble flag %d", req_isp->bubble_report); + if (req_isp->bubble_report && ctx->ctx_crm_intf && + ctx->ctx_crm_intf->notify_err) { + struct cam_req_mgr_error_notify notify; + + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.req_id = req->request_id; + notify.error = CRM_KMD_ERR_BUBBLE; + ctx->ctx_crm_intf->notify_err(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM about Bubble frame %lld", + ctx_isp->frame_id); + } else { + /* + * Since can not bubble report, always move the request to + * active list. + */ + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, "move request %lld to active list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + req_isp->bubble_report = 0; + } + + if (req->request_id > ctx_isp->reported_req_id) { + request_id = req->request_id; + ctx_isp->reported_req_id = request_id; + } + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_ERROR); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE; + CAM_DBG(CAM_ISP, "next substate %d", + ctx_isp->substate_activated); +end: + return 0; +} + + +static int __cam_isp_ctx_buf_done_in_applied(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_done_event_data *done = + (struct cam_isp_hw_done_event_data *) evt_data; + + rc = __cam_isp_ctx_handle_buf_done_in_activated_state(ctx_isp, done, 0); + return rc; +} + + +static int __cam_isp_ctx_sof_in_epoch(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + int rc = 0; + struct cam_context *ctx = ctx_isp->base; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + + if (list_empty(&ctx->active_req_list)) + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + else + CAM_DBG(CAM_ISP, "Still need to wait for the buf done"); + + CAM_DBG(CAM_ISP, "next substate %d", + ctx_isp->substate_activated); + + return rc; +} + +static int __cam_isp_ctx_buf_done_in_epoch(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_done_event_data *done = + (struct cam_isp_hw_done_event_data *) evt_data; + + rc = __cam_isp_ctx_handle_buf_done_in_activated_state(ctx_isp, done, 0); + return rc; +} + +static int __cam_isp_ctx_buf_done_in_bubble( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_done_event_data *done = + (struct cam_isp_hw_done_event_data *) evt_data; + + rc = __cam_isp_ctx_handle_buf_done_in_activated_state(ctx_isp, done, 1); + return rc; +} + +static int __cam_isp_ctx_epoch_in_bubble_applied( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_context *ctx = ctx_isp->base; + uint64_t request_id = 0; + + /* + * This means we missed the reg upd ack. So we need to + * transition to BUBBLE state again. + */ + + if (list_empty(&ctx->pending_req_list)) { + /* + * If no pending req in epoch, this is an error case. + * Just go back to the bubble state. + */ + CAM_ERR(CAM_ISP, "No pending request."); + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE; + goto end; + } + + req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request, + list); + req_isp = (struct cam_isp_ctx_req *)req->req_priv; + + if (req_isp->bubble_report && ctx->ctx_crm_intf && + ctx->ctx_crm_intf->notify_err) { + struct cam_req_mgr_error_notify notify; + + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.req_id = req->request_id; + notify.error = CRM_KMD_ERR_BUBBLE; + ctx->ctx_crm_intf->notify_err(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM about Bubble frame %lld", + ctx_isp->frame_id); + } else { + /* + * If we can not report bubble, then treat it as if no bubble + * report. Just move the req to active list. + */ + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, "move request %lld to active list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + req_isp->bubble_report = 0; + } + + if (!req_isp->bubble_report) { + if (req->request_id > ctx_isp->reported_req_id) { + request_id = req->request_id; + ctx_isp->reported_req_id = request_id; + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_ERROR); + } else + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + } else + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE; + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); +end: + return 0; +} + +static int __cam_isp_ctx_buf_done_in_bubble_applied( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_done_event_data *done = + (struct cam_isp_hw_done_event_data *) evt_data; + + rc = __cam_isp_ctx_handle_buf_done_in_activated_state(ctx_isp, done, 1); + return rc; +} + +static int __cam_isp_ctx_handle_error(struct cam_isp_context *ctx_isp, + void *evt_data) +{ + int rc = 0; + uint32_t i = 0; + bool found = 0; + struct cam_ctx_request *req = NULL; + struct cam_ctx_request *req_temp; + struct cam_isp_ctx_req *req_isp = NULL; + struct cam_req_mgr_error_notify notify = {}; + uint64_t error_request_id; + + struct cam_context *ctx = ctx_isp->base; + struct cam_isp_hw_error_event_data *error_event_data = + (struct cam_isp_hw_error_event_data *)evt_data; + + uint32_t error_type = error_event_data->error_type; + + CAM_DBG(CAM_ISP, "Enter error_type = %d", error_type); + if ((error_type == CAM_ISP_HW_ERROR_OVERFLOW) || + (error_type == CAM_ISP_HW_ERROR_BUSIF_OVERFLOW)) + notify.error = CRM_KMD_ERR_OVERFLOW; + + /* + * Need to check the active req + * move all of them to the pending request list + * Note this funciton need revisit! + */ + + if (list_empty(&ctx->active_req_list)) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "handling error with no active request"); + } else { + list_for_each_entry_safe(req, req_temp, + &ctx->active_req_list, list) { + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + if (!req_isp->bubble_report) { + for (i = 0; i < req_isp->num_fence_map_out; + i++) { + CAM_ERR(CAM_ISP, "req %llu, Sync fd %x", + req->request_id, + req_isp->fence_map_out[i]. + sync_id); + if (req_isp->fence_map_out[i].sync_id + != -1) { + rc = cam_sync_signal( + req_isp->fence_map_out[i]. + sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + req_isp->fence_map_out[i]. + sync_id = -1; + } + } + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + ctx_isp->active_req_cnt--; + } else { + found = 1; + break; + } + } + } + + if (found) { + list_for_each_entry_safe_reverse(req, req_temp, + &ctx->active_req_list, list) { + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + list_del_init(&req->list); + list_add(&req->list, &ctx->pending_req_list); + ctx_isp->active_req_cnt--; + } + } + + do { + if (list_empty(&ctx->pending_req_list)) { + error_request_id = ctx_isp->last_applied_req_id + 1; + req_isp = NULL; + break; + } + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + error_request_id = ctx_isp->last_applied_req_id; + + if (req_isp->bubble_report) + break; + + for (i = 0; i < req_isp->num_fence_map_out; i++) { + if (req_isp->fence_map_out[i].sync_id != -1) + rc = cam_sync_signal( + req_isp->fence_map_out[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + req_isp->fence_map_out[i].sync_id = -1; + } + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + + } while (req->request_id < ctx_isp->last_applied_req_id); + + + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_err) { + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.req_id = error_request_id; + + if (req_isp && req_isp->bubble_report) + notify.error = CRM_KMD_ERR_BUBBLE; + + CAM_WARN(CAM_ISP, "Notify CRM: req %lld, frame %lld\n", + error_request_id, ctx_isp->frame_id); + + ctx->ctx_crm_intf->notify_err(¬ify); + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_HW_ERROR; + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify ERRROR to CRM"); + rc = -EFAULT; + } + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +static int __cam_isp_ctx_sof_in_flush( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + + if (--ctx_isp->frame_skip_count == 0) + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + else + CAM_ERR(CAM_ISP, "Skip currect SOF"); + + return rc; +} + +static struct cam_isp_ctx_irq_ops + cam_isp_ctx_activated_state_machine_irq[CAM_ISP_CTX_ACTIVATED_MAX] = { + /* SOF */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_sof_in_activated_state, + __cam_isp_ctx_reg_upd_in_sof, + __cam_isp_ctx_notify_sof_in_actived_state, + __cam_isp_ctx_notify_eof_in_actived_state, + NULL, + }, + }, + /* APPLIED */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_sof_in_activated_state, + __cam_isp_ctx_reg_upd_in_activated_state, + __cam_isp_ctx_epoch_in_applied, + __cam_isp_ctx_notify_eof_in_actived_state, + __cam_isp_ctx_buf_done_in_applied, + }, + }, + /* EPOCH */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_sof_in_epoch, + NULL, + __cam_isp_ctx_notify_sof_in_actived_state, + __cam_isp_ctx_notify_eof_in_actived_state, + __cam_isp_ctx_buf_done_in_epoch, + }, + }, + /* BUBBLE */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_sof_in_activated_state, + NULL, + __cam_isp_ctx_notify_sof_in_actived_state, + __cam_isp_ctx_notify_eof_in_actived_state, + __cam_isp_ctx_buf_done_in_bubble, + }, + }, + /* Bubble Applied */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_sof_in_activated_state, + __cam_isp_ctx_reg_upd_in_activated_state, + __cam_isp_ctx_epoch_in_bubble_applied, + NULL, + __cam_isp_ctx_buf_done_in_bubble_applied, + }, + }, + /* HW ERROR */ + { + .irq_ops = { + NULL, + __cam_isp_ctx_sof_in_activated_state, + __cam_isp_ctx_reg_upd_in_hw_error, + NULL, + NULL, + NULL, + }, + }, + /* HALT */ + { + }, + /* FLUSH */ + { + .irq_ops = { + NULL, + __cam_isp_ctx_sof_in_flush, + NULL, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_applied, + }, + }, +}; + +static int __cam_isp_ctx_apply_req_in_activated_state( + struct cam_context *ctx, struct cam_req_mgr_apply_request *apply, + uint32_t next_state) +{ + int rc = 0; + struct cam_ctx_request *req; + struct cam_ctx_request *active_req; + struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *active_req_isp; + struct cam_isp_context *ctx_isp; + struct cam_hw_config_args cfg; + + if (list_empty(&ctx->pending_req_list)) { + CAM_ERR(CAM_ISP, "No available request for Apply id %lld", + apply->request_id); + rc = -EFAULT; + goto end; + } + + /* + * When the pipeline has issue, the requests can be queued up in the + * pipeline. In this case, we should reject the additional request. + * The maximum number of request allowed to be outstanding is 2. + * + */ + ctx_isp = (struct cam_isp_context *) ctx->ctx_priv; + req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request, + list); + + /* + * Check whehter the request id is matching the tip, if not, this means + * we are in the middle of the error handling. Need to reject this apply + */ + if (req->request_id != apply->request_id) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Invalid Request Id asking %llu existing %llu", + apply->request_id, req->request_id); + rc = -EFAULT; + goto end; + } + + CAM_DBG(CAM_ISP, "Apply request %lld", req->request_id); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + + if (ctx_isp->active_req_cnt >= 2) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Reject apply request (id %lld) due to congestion(cnt = %d)", + req->request_id, + ctx_isp->active_req_cnt); + if (!list_empty(&ctx->active_req_list)) { + active_req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + active_req_isp = + (struct cam_isp_ctx_req *) active_req->req_priv; + __cam_isp_ctx_handle_buf_done_fail_log(active_req_isp); + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "WARNING: should not happen (cnt = %d) but active_list empty", + ctx_isp->active_req_cnt); + } + rc = -EFAULT; + goto end; + } + req_isp->bubble_report = apply->report_if_bubble; + + cfg.ctxt_to_hw_map = ctx_isp->hw_ctx; + cfg.request_id = req->request_id; + cfg.hw_update_entries = req_isp->cfg; + cfg.num_hw_update_entries = req_isp->num_cfg; + cfg.priv = &req_isp->hw_update_data; + cfg.init_packet = 0; + + rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not apply the configuration"); + } else { + spin_lock_bh(&ctx->lock); + ctx_isp->substate_activated = next_state; + ctx_isp->last_applied_req_id = apply->request_id; + CAM_DBG(CAM_ISP, "new substate state %d, applied req %lld", + next_state, ctx_isp->last_applied_req_id); + spin_unlock_bh(&ctx->lock); + } +end: + return rc; +} + +static int __cam_isp_ctx_apply_req_in_sof( + struct cam_context *ctx, struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "current substate %d", + ctx_isp->substate_activated); + rc = __cam_isp_ctx_apply_req_in_activated_state(ctx, apply, + CAM_ISP_CTX_ACTIVATED_APPLIED); + CAM_DBG(CAM_ISP, "new substate %d", ctx_isp->substate_activated); + + return rc; +} + +static int __cam_isp_ctx_apply_req_in_epoch( + struct cam_context *ctx, struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "current substate %d", + ctx_isp->substate_activated); + rc = __cam_isp_ctx_apply_req_in_activated_state(ctx, apply, + CAM_ISP_CTX_ACTIVATED_APPLIED); + CAM_DBG(CAM_ISP, "new substate %d", ctx_isp->substate_activated); + + return rc; +} + +static int __cam_isp_ctx_apply_req_in_bubble( + struct cam_context *ctx, struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "current substate %d", + ctx_isp->substate_activated); + rc = __cam_isp_ctx_apply_req_in_activated_state(ctx, apply, + CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED); + CAM_DBG(CAM_ISP, "new substate %d", ctx_isp->substate_activated); + + return rc; +} + +static int __cam_isp_ctx_flush_req(struct cam_context *ctx, + struct list_head *req_list, struct cam_req_mgr_flush_request *flush_req) +{ + int i, rc; + uint32_t cancel_req_id_found = 0; + struct cam_ctx_request *req; + struct cam_ctx_request *req_temp; + struct cam_isp_ctx_req *req_isp; + struct list_head flush_list; + + INIT_LIST_HEAD(&flush_list); + spin_lock_bh(&ctx->lock); + if (list_empty(req_list)) { + spin_unlock_bh(&ctx->lock); + CAM_DBG(CAM_ISP, "request list is empty"); + return 0; + } + + list_for_each_entry_safe(req, req_temp, req_list, list) { + if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) { + if (req->request_id != flush_req->req_id) { + continue; + } else { + list_del_init(&req->list); + list_add_tail(&req->list, &flush_list); + cancel_req_id_found = 1; + break; + } + } + list_del_init(&req->list); + list_add_tail(&req->list, &flush_list); + } + spin_unlock_bh(&ctx->lock); + + list_for_each_entry_safe(req, req_temp, &flush_list, list) { + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + for (i = 0; i < req_isp->num_fence_map_out; i++) { + if (req_isp->fence_map_out[i].sync_id != -1) { + CAM_DBG(CAM_ISP, "Flush req 0x%llx, fence %d", + req->request_id, + req_isp->fence_map_out[i].sync_id); + rc = cam_sync_signal( + req_isp->fence_map_out[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc) + CAM_ERR_RATE_LIMIT(CAM_ISP, + "signal fence failed\n"); + req_isp->fence_map_out[i].sync_id = -1; + } + } + spin_lock_bh(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock_bh(&ctx->lock); + } + + if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ && + !cancel_req_id_found) + CAM_DBG(CAM_ISP, + "Flush request id:%lld is not found in the list", + flush_req->req_id); + + return 0; +} + +static int __cam_isp_ctx_flush_req_in_top_state( + struct cam_context *ctx, + struct cam_req_mgr_flush_request *flush_req) +{ + int rc = 0; + + CAM_DBG(CAM_ISP, "try to flush pending list"); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req); + CAM_DBG(CAM_ISP, "Flush request in top state %d", + ctx->state); + return rc; +} + +static int __cam_isp_ctx_flush_req_in_activated( + struct cam_context *ctx, + struct cam_req_mgr_flush_request *flush_req) +{ + int rc = 0; + struct cam_isp_context *ctx_isp; + + ctx_isp = (struct cam_isp_context *) ctx->ctx_priv; + spin_lock_bh(&ctx->lock); + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_FLUSH; + ctx_isp->frame_skip_count = 2; + spin_unlock_bh(&ctx->lock); + + CAM_DBG(CAM_ISP, "Flush request in state %d", ctx->state); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req); + return rc; +} + +static int __cam_isp_ctx_flush_req_in_ready( + struct cam_context *ctx, + struct cam_req_mgr_flush_request *flush_req) +{ + int rc = 0; + + CAM_DBG(CAM_ISP, "try to flush pending list"); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req); + + /* if nothing is in pending req list, change state to acquire*/ + spin_lock_bh(&ctx->lock); + if (list_empty(&ctx->pending_req_list)) + ctx->state = CAM_CTX_ACQUIRED; + spin_unlock_bh(&ctx->lock); + + trace_cam_context_state("ISP", ctx); + + CAM_DBG(CAM_ISP, "Flush request in ready state. next state %d", + ctx->state); + return rc; +} + +static struct cam_ctx_ops + cam_isp_ctx_activated_state_machine[CAM_ISP_CTX_ACTIVATED_MAX] = { + /* SOF */ + { + .ioctl_ops = {}, + .crm_ops = { + .apply_req = __cam_isp_ctx_apply_req_in_sof, + }, + .irq_ops = NULL, + }, + /* APPLIED */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* EPOCH */ + { + .ioctl_ops = {}, + .crm_ops = { + .apply_req = __cam_isp_ctx_apply_req_in_epoch, + }, + .irq_ops = NULL, + }, + /* BUBBLE */ + { + .ioctl_ops = {}, + .crm_ops = { + .apply_req = __cam_isp_ctx_apply_req_in_bubble, + }, + .irq_ops = NULL, + }, + /* Bubble Applied */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* HW ERROR */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* HALT */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* FLUSH */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, +}; + +static int __cam_isp_ctx_rdi_only_sof_in_top_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + int rc = 0; + struct cam_context *ctx = ctx_isp->base; + struct cam_req_mgr_trigger_notify notify; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + uint64_t request_id = 0; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + + /* + * notify reqmgr with sof signal. Note, due to scheduling delay + * we can run into situation that two active requests has already + * be in the active queue while we try to do the notification. + * In this case, we need to skip the current notification. This + * helps the state machine to catch up the delay. + */ + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger && + ctx_isp->active_req_cnt <= 2) { + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.frame_id = ctx_isp->frame_id; + notify.trigger = CAM_TRIGGER_POINT_SOF; + + ctx->ctx_crm_intf->notify_trigger(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld", + ctx_isp->frame_id); + + /* + * It is idle frame with out any applied request id, send + * request id as zero + */ + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify SOF to CRM"); + } + + if (list_empty(&ctx->active_req_list)) + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + else + CAM_DBG(CAM_ISP, "Still need to wait for the buf done"); + + CAM_DBG(CAM_ISP, "next substate %d", + ctx_isp->substate_activated); + return rc; +} + +static int __cam_isp_ctx_rdi_only_sof_in_applied_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED; + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); + + return 0; +} + +static int __cam_isp_ctx_rdi_only_sof_in_bubble_applied( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_context *ctx = ctx_isp->base; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + uint64_t request_id = 0; + + /* + * Sof in bubble applied state means, reg update not received. + * before increment frame id and override time stamp value, send + * the previous sof time stamp that got captured in the + * sof in applied state. + */ + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + + if (list_empty(&ctx->pending_req_list)) { + /* + * If no pending req in epoch, this is an error case. + * The recovery is to go back to sof state + */ + CAM_ERR(CAM_ISP, "No pending request"); + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + + /* Send SOF event as empty frame*/ + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + goto end; + } + + req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request, + list); + req_isp = (struct cam_isp_ctx_req *)req->req_priv; + + CAM_DBG(CAM_ISP, "Report Bubble flag %d", req_isp->bubble_report); + if (req_isp->bubble_report && ctx->ctx_crm_intf && + ctx->ctx_crm_intf->notify_err) { + struct cam_req_mgr_error_notify notify; + + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.req_id = req->request_id; + notify.error = CRM_KMD_ERR_BUBBLE; + ctx->ctx_crm_intf->notify_err(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM about Bubble frame %lld", + ctx_isp->frame_id); + } else { + /* + * Since can not bubble report, always move the request to + * active list. + */ + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, "move request %lld to active list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + req_isp->bubble_report = 0; + } + + if (!req_isp->bubble_report) { + if (req->request_id > ctx_isp->reported_req_id) { + request_id = req->request_id; + ctx_isp->reported_req_id = request_id; + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_ERROR); + } else + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + } else + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + /* change the state to bubble, as reg update has not come */ + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE; + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); +end: + return 0; +} + +static int __cam_isp_ctx_rdi_only_sof_in_bubble_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + uint32_t i; + struct cam_ctx_request *req; + struct cam_context *ctx = ctx_isp->base; + struct cam_req_mgr_trigger_notify notify; + struct cam_isp_hw_sof_event_data *sof_event_data = evt_data; + struct cam_isp_ctx_req *req_isp; + uint64_t request_id = 0; + + if (!evt_data) { + CAM_ERR(CAM_ISP, "in valid sof event data"); + return -EINVAL; + } + + ctx_isp->frame_id++; + ctx_isp->sof_timestamp_val = sof_event_data->timestamp; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx", + ctx_isp->frame_id, ctx_isp->sof_timestamp_val); + /* + * Signal all active requests with error and move the all the active + * requests to free list + */ + while (!list_empty(&ctx->active_req_list)) { + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + CAM_DBG(CAM_ISP, "signal fence in active list. fence num %d", + req_isp->num_fence_map_out); + for (i = 0; i < req_isp->num_fence_map_out; i++) + if (req_isp->fence_map_out[i].sync_id != -1) { + cam_sync_signal( + req_isp->fence_map_out[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + } + list_add_tail(&req->list, &ctx->free_req_list); + ctx_isp->active_req_cnt--; + } + + /* notify reqmgr with sof signal */ + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger) { + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.frame_id = ctx_isp->frame_id; + notify.trigger = CAM_TRIGGER_POINT_SOF; + + ctx->ctx_crm_intf->notify_trigger(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld", + ctx_isp->frame_id); + + } else { + CAM_ERR(CAM_ISP, "Can not notify SOF to CRM"); + } + + /* + * It is idle frame with out any applied request id, send + * request id as zero + */ + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + + CAM_DBG(CAM_ISP, "next substate %d", + ctx_isp->substate_activated); + + return 0; +} + +static int __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state( + struct cam_isp_context *ctx_isp, void *evt_data) +{ + struct cam_ctx_request *req; + struct cam_context *ctx = ctx_isp->base; + struct cam_isp_ctx_req *req_isp; + struct cam_req_mgr_trigger_notify notify; + uint64_t request_id = 0; + + /* notify reqmgr with sof signal*/ + if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger) { + if (list_empty(&ctx->pending_req_list)) { + CAM_ERR(CAM_ISP, "Reg upd ack with no pending request"); + goto error; + } + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + request_id = req->request_id; + if (req_isp->num_fence_map_out != 0) { + list_add_tail(&req->list, &ctx->active_req_list); + ctx_isp->active_req_cnt++; + CAM_DBG(CAM_ISP, + "move request %lld to active list(cnt = %d)", + req->request_id, ctx_isp->active_req_cnt); + } else { + /* no io config, so the request is completed. */ + list_add_tail(&req->list, &ctx->free_req_list); + CAM_DBG(CAM_ISP, + "move active req %lld to free list(cnt=%d)", + req->request_id, ctx_isp->active_req_cnt); + } + + notify.link_hdl = ctx->link_hdl; + notify.dev_hdl = ctx->dev_hdl; + notify.frame_id = ctx_isp->frame_id; + notify.trigger = CAM_TRIGGER_POINT_SOF; + + ctx->ctx_crm_intf->notify_trigger(¬ify); + CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld", + ctx_isp->frame_id); + } else { + CAM_ERR(CAM_ISP, "Can not notify SOF to CRM"); + } + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH; + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); + + return 0; +error: + /* Send SOF event as idle frame*/ + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, + CAM_REQ_MGR_SOF_EVENT_SUCCESS); + + /* + * There is no request in the pending list, move the sub state machine + * to SOF sub state + */ + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + + return 0; +} + +static struct cam_isp_ctx_irq_ops + cam_isp_ctx_rdi_only_activated_state_machine_irq + [CAM_ISP_CTX_ACTIVATED_MAX] = { + /* SOF */ + { + .irq_ops = { + NULL, + __cam_isp_ctx_rdi_only_sof_in_top_state, + __cam_isp_ctx_reg_upd_in_sof, + NULL, + NULL, + NULL, + }, + }, + /* APPLIED */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_rdi_only_sof_in_applied_state, + NULL, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_applied, + }, + }, + /* EPOCH */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_rdi_only_sof_in_top_state, + NULL, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_epoch, + }, + }, + /* BUBBLE*/ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_rdi_only_sof_in_bubble_state, + NULL, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_bubble, + }, + }, + /* BUBBLE APPLIED ie PRE_BUBBLE */ + { + .irq_ops = { + __cam_isp_ctx_handle_error, + __cam_isp_ctx_rdi_only_sof_in_bubble_applied, + __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_bubble_applied, + }, + }, + /* HW ERROR */ + { + }, + /* HALT */ + { + }, + /* FLUSH */ + { + .irq_ops = { + NULL, + __cam_isp_ctx_sof_in_flush, + NULL, + NULL, + NULL, + __cam_isp_ctx_buf_done_in_applied, + }, + }, +}; + +static int __cam_isp_ctx_rdi_only_apply_req_top_state( + struct cam_context *ctx, struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "current substate %d", + ctx_isp->substate_activated); + rc = __cam_isp_ctx_apply_req_in_activated_state(ctx, apply, + CAM_ISP_CTX_ACTIVATED_APPLIED); + CAM_DBG(CAM_ISP, "new substate %d", ctx_isp->substate_activated); + + return rc; +} + +static struct cam_ctx_ops + cam_isp_ctx_rdi_only_activated_state_machine + [CAM_ISP_CTX_ACTIVATED_MAX] = { + /* SOF */ + { + .ioctl_ops = {}, + .crm_ops = { + .apply_req = __cam_isp_ctx_rdi_only_apply_req_top_state, + }, + .irq_ops = NULL, + }, + /* APPLIED */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* EPOCH */ + { + .ioctl_ops = {}, + .crm_ops = { + .apply_req = __cam_isp_ctx_rdi_only_apply_req_top_state, + }, + .irq_ops = NULL, + }, + /* PRE BUBBLE */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* BUBBLE */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* HW ERROR */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* HALT */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* FLUSHED */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, +}; + +/* top level state machine */ +static int __cam_isp_ctx_release_dev_in_top_state(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc = 0; + struct cam_hw_release_args rel_arg; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + struct cam_req_mgr_flush_request flush_req; + + if (ctx_isp->hw_ctx) { + rel_arg.ctxt_to_hw_map = ctx_isp->hw_ctx; + ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, + &rel_arg); + ctx_isp->hw_ctx = NULL; + } + + ctx->session_hdl = -1; + ctx->dev_hdl = -1; + ctx->link_hdl = -1; + ctx->ctx_crm_intf = NULL; + ctx_isp->frame_id = 0; + ctx_isp->active_req_cnt = 0; + ctx_isp->reported_req_id = 0; + + /* + * Ideally, we should never have any active request here. + * But we still add some sanity check code here to help the debug + */ + if (!list_empty(&ctx->active_req_list)) + CAM_ERR(CAM_ISP, "Active list is not empty"); + + /* Flush all the pending request list */ + flush_req.type = CAM_REQ_MGR_FLUSH_TYPE_ALL; + flush_req.link_hdl = ctx->link_hdl; + flush_req.dev_hdl = ctx->dev_hdl; + + CAM_DBG(CAM_ISP, "try to flush pending list"); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, &flush_req); + + ctx->state = CAM_CTX_AVAILABLE; + + trace_cam_context_state("ISP", ctx); + CAM_DBG(CAM_ISP, "next state %d", ctx->state); + return rc; +} + +static int __cam_isp_ctx_config_dev_in_top_state( + struct cam_context *ctx, struct cam_config_dev_cmd *cmd) +{ + int rc = 0, i; + struct cam_ctx_request *req = NULL; + struct cam_isp_ctx_req *req_isp; + uintptr_t packet_addr; + struct cam_packet *packet; + size_t len = 0; + struct cam_hw_prepare_update_args cfg; + struct cam_req_mgr_add_request add_req; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "get free request object......"); + + /* get free request */ + spin_lock_bh(&ctx->lock); + if (!list_empty(&ctx->free_req_list)) { + req = list_first_entry(&ctx->free_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + } + spin_unlock_bh(&ctx->lock); + + if (!req) { + CAM_ERR(CAM_ISP, "No more request obj free"); + return -ENOMEM; + } + + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + + /* for config dev, only memory handle is supported */ + /* map packet from the memhandle */ + rc = cam_mem_get_cpu_buf((int32_t) cmd->packet_handle, + &packet_addr, &len); + if (rc != 0) { + CAM_ERR(CAM_ISP, "Can not get packet address"); + rc = -EINVAL; + goto free_req; + } + + packet = (struct cam_packet *)(packet_addr + (uint32_t)cmd->offset); + CAM_DBG(CAM_ISP, "pack_handle %llx", cmd->packet_handle); + CAM_DBG(CAM_ISP, "packet address is 0x%llx", packet_addr); + CAM_DBG(CAM_ISP, "packet with length %zu, offset 0x%llx", + len, cmd->offset); + CAM_DBG(CAM_ISP, "Packet request id %lld", + packet->header.request_id); + CAM_DBG(CAM_ISP, "Packet size 0x%x", packet->header.size); + CAM_DBG(CAM_ISP, "packet op %d", packet->header.op_code); + + /* preprocess the configuration */ + memset(&cfg, 0, sizeof(cfg)); + cfg.packet = packet; + cfg.ctxt_to_hw_map = ctx_isp->hw_ctx; + cfg.max_hw_update_entries = CAM_ISP_CTX_CFG_MAX; + cfg.hw_update_entries = req_isp->cfg; + cfg.max_out_map_entries = CAM_ISP_CTX_RES_MAX; + cfg.max_in_map_entries = CAM_ISP_CTX_RES_MAX; + cfg.out_map_entries = req_isp->fence_map_out; + cfg.in_map_entries = req_isp->fence_map_in; + cfg.priv = &req_isp->hw_update_data; + + CAM_DBG(CAM_ISP, "try to prepare config packet......"); + + rc = ctx->hw_mgr_intf->hw_prepare_update( + ctx->hw_mgr_intf->hw_mgr_priv, &cfg); + if (rc != 0) { + CAM_ERR(CAM_ISP, "Prepare config packet failed in HW layer"); + rc = -EFAULT; + goto free_req; + } + req_isp->num_cfg = cfg.num_hw_update_entries; + req_isp->num_fence_map_out = cfg.num_out_map_entries; + req_isp->num_fence_map_in = cfg.num_in_map_entries; + req_isp->num_acked = 0; + + for (i = 0; i < req_isp->num_fence_map_out; i++) { + rc = cam_sync_get_obj_ref(req_isp->fence_map_out[i].sync_id); + if (rc) { + CAM_ERR(CAM_ISP, "Can't get ref for fence %d", + req_isp->fence_map_out[i].sync_id); + goto put_ref; + } + } + + CAM_DBG(CAM_ISP, "num_entry: %d, num fence out: %d, num fence in: %d", + req_isp->num_cfg, req_isp->num_fence_map_out, + req_isp->num_fence_map_in); + + req->request_id = packet->header.request_id; + req->status = 1; + + CAM_DBG(CAM_ISP, "Packet request id 0x%llx packet opcode:%d", + packet->header.request_id, + req_isp->hw_update_data.packet_opcode_type); + + if (req_isp->hw_update_data.packet_opcode_type == + CAM_ISP_PACKET_INIT_DEV) { + if (ctx->state < CAM_CTX_ACTIVATED) { + rc = __cam_isp_ctx_enqueue_init_request(ctx, req); + if (rc) + CAM_ERR(CAM_ISP, "Enqueue INIT pkt failed"); + } else { + rc = -EINVAL; + CAM_ERR(CAM_ISP, "Recevied INIT pkt in wrong state"); + } + } else { + if (ctx->state >= CAM_CTX_READY && ctx->ctx_crm_intf->add_req) { + add_req.link_hdl = ctx->link_hdl; + add_req.dev_hdl = ctx->dev_hdl; + add_req.req_id = req->request_id; + add_req.skip_before_applying = 0; + rc = ctx->ctx_crm_intf->add_req(&add_req); + if (rc) { + CAM_ERR(CAM_ISP, "Add req failed: req id=%llu", + req->request_id); + } else { + __cam_isp_ctx_enqueue_request_in_order( + ctx, req); + } + } else { + rc = -EINVAL; + CAM_ERR(CAM_ISP, "Recevied Update in wrong state"); + } + } + if (rc) + goto put_ref; + + CAM_DBG(CAM_ISP, "Preprocessing Config %lld successful", + req->request_id); + + return rc; + +put_ref: + for (--i; i >= 0; i--) { + if (cam_sync_put_obj_ref(req_isp->fence_map_out[i].sync_id)) + CAM_ERR(CAM_CTXT, "Failed to put ref of fence %d", + req_isp->fence_map_out[i].sync_id); + } +free_req: + spin_lock_bh(&ctx->lock); + list_add_tail(&req->list, &ctx->free_req_list); + spin_unlock_bh(&ctx->lock); + + return rc; +} + +static int __cam_isp_ctx_acquire_dev_in_available(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc = 0; + struct cam_hw_acquire_args param; + struct cam_isp_resource *isp_res = NULL; + struct cam_create_dev_hdl req_hdl_param; + struct cam_hw_release_args release; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + struct cam_isp_hw_cmd_args hw_cmd_args; + + if (!ctx->hw_mgr_intf) { + CAM_ERR(CAM_ISP, "HW interface is not ready"); + rc = -EFAULT; + goto end; + } + + CAM_DBG(CAM_ISP, + "session_hdl 0x%x, num_resources %d, hdl type %d, res %lld", + cmd->session_handle, cmd->num_resources, + cmd->handle_type, cmd->resource_hdl); + + if (cmd->num_resources > CAM_ISP_CTX_RES_MAX) { + CAM_ERR(CAM_ISP, "Too much resources in the acquire"); + rc = -ENOMEM; + goto end; + } + + /* for now we only support user pointer */ + if (cmd->handle_type != 1) { + CAM_ERR(CAM_ISP, "Only user pointer is supported"); + rc = -EINVAL; + goto end; + } + + isp_res = kzalloc( + sizeof(*isp_res)*cmd->num_resources, GFP_KERNEL); + if (!isp_res) { + rc = -ENOMEM; + goto end; + } + + CAM_DBG(CAM_ISP, "start copy %d resources from user", + cmd->num_resources); + + if (copy_from_user(isp_res, u64_to_user_ptr(cmd->resource_hdl), + sizeof(*isp_res)*cmd->num_resources)) { + rc = -EFAULT; + goto free_res; + } + + param.context_data = ctx; + param.event_cb = ctx->irq_cb_intf; + param.num_acq = cmd->num_resources; + param.acquire_info = (uint64_t) isp_res; + + /* call HW manager to reserve the resource */ + rc = ctx->hw_mgr_intf->hw_acquire(ctx->hw_mgr_intf->hw_mgr_priv, + ¶m); + if (rc != 0) { + CAM_ERR(CAM_ISP, "Acquire device failed"); + goto free_res; + } + + /* Query the context has rdi only resource */ + hw_cmd_args.ctxt_to_hw_map = param.ctxt_to_hw_map; + hw_cmd_args.cmd_type = CAM_ISP_HW_MGR_CMD_IS_RDI_ONLY_CONTEXT; + rc = ctx->hw_mgr_intf->hw_cmd(ctx->hw_mgr_intf->hw_mgr_priv, + &hw_cmd_args); + if (rc) { + CAM_ERR(CAM_ISP, "HW command failed"); + goto free_hw; + } + + if (hw_cmd_args.u.is_rdi_only_context) { + /* + * this context has rdi only resource assign rdi only + * state machine + */ + CAM_DBG(CAM_ISP, "RDI only session Context"); + + ctx_isp->substate_machine_irq = + cam_isp_ctx_rdi_only_activated_state_machine_irq; + ctx_isp->substate_machine = + cam_isp_ctx_rdi_only_activated_state_machine; + } else { + CAM_DBG(CAM_ISP, "Session has PIX or PIX and RDI resources"); + ctx_isp->substate_machine_irq = + cam_isp_ctx_activated_state_machine_irq; + ctx_isp->substate_machine = + cam_isp_ctx_activated_state_machine; + } + + ctx_isp->hw_ctx = param.ctxt_to_hw_map; + + req_hdl_param.session_hdl = cmd->session_handle; + /* bridge is not ready for these flags. so false for now */ + req_hdl_param.v4l2_sub_dev_flag = 0; + req_hdl_param.media_entity_flag = 0; + req_hdl_param.ops = ctx->crm_ctx_intf; + req_hdl_param.priv = ctx; + + CAM_DBG(CAM_ISP, "get device handle form bridge"); + ctx->dev_hdl = cam_create_device_hdl(&req_hdl_param); + if (ctx->dev_hdl <= 0) { + rc = -EFAULT; + CAM_ERR(CAM_ISP, "Can not create device handle"); + goto free_hw; + } + cmd->dev_handle = ctx->dev_hdl; + + /* store session information */ + ctx->session_hdl = cmd->session_handle; + + ctx->state = CAM_CTX_ACQUIRED; + + trace_cam_context_state("ISP", ctx); + CAM_DBG(CAM_ISP, "Acquire success."); + kfree(isp_res); + return rc; + +free_hw: + release.ctxt_to_hw_map = ctx_isp->hw_ctx; + ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &release); + ctx_isp->hw_ctx = NULL; +free_res: + kfree(isp_res); +end: + return rc; +} + +static int __cam_isp_ctx_config_dev_in_acquired(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc = 0; + + rc = __cam_isp_ctx_config_dev_in_top_state(ctx, cmd); + + if (!rc && (ctx->link_hdl >= 0)) { + ctx->state = CAM_CTX_READY; + trace_cam_context_state("ISP", ctx); + } + + CAM_DBG(CAM_ISP, "next state %d", ctx->state); + return rc; +} + +static int __cam_isp_ctx_link_in_acquired(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *link) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + CAM_DBG(CAM_ISP, "Enter........."); + + ctx->link_hdl = link->link_hdl; + ctx->ctx_crm_intf = link->crm_cb; + ctx_isp->subscribe_event = link->subscribe_event; + + /* change state only if we had the init config */ + if (!list_empty(&ctx->pending_req_list)) { + ctx->state = CAM_CTX_READY; + trace_cam_context_state("ISP", ctx); + } + + CAM_DBG(CAM_ISP, "next state %d", ctx->state); + + return rc; +} + +static int __cam_isp_ctx_unlink_in_acquired(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink) +{ + int rc = 0; + + ctx->link_hdl = -1; + ctx->ctx_crm_intf = NULL; + + return rc; +} + +static int __cam_isp_ctx_get_dev_info_in_acquired(struct cam_context *ctx, + struct cam_req_mgr_device_info *dev_info) +{ + int rc = 0; + + dev_info->dev_hdl = ctx->dev_hdl; + strlcpy(dev_info->name, CAM_ISP_DEV_NAME, sizeof(dev_info->name)); + dev_info->dev_id = CAM_REQ_MGR_DEVICE_IFE; + dev_info->p_delay = 1; + dev_info->trigger = CAM_TRIGGER_POINT_SOF; + + return rc; +} + +static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + struct cam_hw_config_args arg; + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + if (cmd->session_handle != ctx->session_hdl || + cmd->dev_handle != ctx->dev_hdl) { + rc = -EPERM; + goto end; + } + + if (list_empty(&ctx->pending_req_list)) { + /* should never happen */ + CAM_ERR(CAM_ISP, "Start device with empty configuration"); + rc = -EFAULT; + goto end; + } else { + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + } + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + + if (!ctx_isp->hw_ctx) { + CAM_ERR(CAM_ISP, "Wrong hw context pointer."); + rc = -EFAULT; + goto end; + } + + arg.ctxt_to_hw_map = ctx_isp->hw_ctx; + arg.request_id = req->request_id; + arg.hw_update_entries = req_isp->cfg; + arg.num_hw_update_entries = req_isp->num_cfg; + arg.priv = &req_isp->hw_update_data; + arg.init_packet = 1; + + ctx_isp->frame_id = 0; + ctx_isp->active_req_cnt = 0; + ctx_isp->reported_req_id = 0; + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + + /* + * Only place to change state before calling the hw due to + * hardware tasklet has higher priority that can cause the + * irq handling comes early + */ + ctx->state = CAM_CTX_ACTIVATED; + trace_cam_context_state("ISP", ctx); + rc = ctx->hw_mgr_intf->hw_start(ctx->hw_mgr_intf->hw_mgr_priv, &arg); + if (rc) { + /* HW failure. user need to clean up the resource */ + CAM_ERR(CAM_ISP, "Start HW failed"); + ctx->state = CAM_CTX_READY; + trace_cam_context_state("ISP", ctx); + goto end; + } + CAM_DBG(CAM_ISP, "start device success"); +end: + return rc; +} + +static int __cam_isp_ctx_unlink_in_ready(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink) +{ + int rc = 0; + + ctx->link_hdl = -1; + ctx->ctx_crm_intf = NULL; + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("ISP", ctx); + + return rc; +} + +static int __cam_isp_ctx_stop_dev_in_activated_unlock( + struct cam_context *ctx, struct cam_start_stop_dev_cmd *stop_cmd) +{ + int rc = 0; + uint32_t i; + struct cam_hw_stop_args stop; + struct cam_ctx_request *req; + struct cam_isp_ctx_req *req_isp; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + /* Mask off all the incoming hardware events */ + spin_lock_bh(&ctx->lock); + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_HALT; + spin_unlock_bh(&ctx->lock); + CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); + + /* stop hw first */ + if (ctx_isp->hw_ctx) { + stop.ctxt_to_hw_map = ctx_isp->hw_ctx; + stop.args = stop_cmd; + ctx->hw_mgr_intf->hw_stop(ctx->hw_mgr_intf->hw_mgr_priv, + &stop); + } + + while (!list_empty(&ctx->pending_req_list)) { + req = list_first_entry(&ctx->pending_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + CAM_DBG(CAM_ISP, "signal fence in pending list. fence num %d", + req_isp->num_fence_map_out); + for (i = 0; i < req_isp->num_fence_map_out; i++) + if (req_isp->fence_map_out[i].sync_id != -1) { + cam_sync_signal( + req_isp->fence_map_out[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + } + list_add_tail(&req->list, &ctx->free_req_list); + } + + while (!list_empty(&ctx->active_req_list)) { + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + list_del_init(&req->list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + CAM_DBG(CAM_ISP, "signal fence in active list. fence num %d", + req_isp->num_fence_map_out); + for (i = 0; i < req_isp->num_fence_map_out; i++) + if (req_isp->fence_map_out[i].sync_id != -1) { + cam_sync_signal( + req_isp->fence_map_out[i].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + } + list_add_tail(&req->list, &ctx->free_req_list); + } + ctx_isp->frame_id = 0; + ctx_isp->active_req_cnt = 0; + ctx_isp->reported_req_id = 0; + + CAM_DBG(CAM_ISP, "next state %d", ctx->state); + return rc; +} + +static int __cam_isp_ctx_stop_dev_in_activated(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + + __cam_isp_ctx_stop_dev_in_activated_unlock(ctx, cmd); + ctx->state = CAM_CTX_ACQUIRED; + trace_cam_context_state("ISP", ctx); + return rc; +} + +static int __cam_isp_ctx_release_dev_in_activated(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc = 0; + + rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx, NULL); + if (rc) + CAM_ERR(CAM_ISP, "Stop device failed rc=%d", rc); + + rc = __cam_isp_ctx_release_dev_in_top_state(ctx, cmd); + if (rc) + CAM_ERR(CAM_ISP, "Release device failed rc=%d", rc); + + return rc; +} + +static int __cam_isp_ctx_link_pause(struct cam_context *ctx) +{ + int rc = 0; + struct cam_isp_hw_cmd_args hw_cmd_args; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + hw_cmd_args.ctxt_to_hw_map = ctx_isp->hw_ctx; + hw_cmd_args.cmd_type = CAM_ISP_HW_MGR_CMD_PAUSE_HW; + rc = ctx->hw_mgr_intf->hw_cmd(ctx->hw_mgr_intf->hw_mgr_priv, + &hw_cmd_args); + + return rc; +} + +static int __cam_isp_ctx_link_resume(struct cam_context *ctx) +{ + int rc = 0; + struct cam_isp_hw_cmd_args hw_cmd_args; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + hw_cmd_args.ctxt_to_hw_map = ctx_isp->hw_ctx; + hw_cmd_args.cmd_type = CAM_ISP_HW_MGR_CMD_RESUME_HW; + rc = ctx->hw_mgr_intf->hw_cmd(ctx->hw_mgr_intf->hw_mgr_priv, + &hw_cmd_args); + + return rc; +} + +static int __cam_isp_ctx_process_evt(struct cam_context *ctx, + struct cam_req_mgr_link_evt_data *link_evt_data) +{ + int rc = 0; + + switch (link_evt_data->evt_type) { + case CAM_REQ_MGR_LINK_EVT_ERR: + /* No need to handle this message now */ + break; + case CAM_REQ_MGR_LINK_EVT_PAUSE: + __cam_isp_ctx_link_pause(ctx); + break; + case CAM_REQ_MGR_LINK_EVT_RESUME: + __cam_isp_ctx_link_resume(ctx); + break; + default: + CAM_WARN(CAM_ISP, "Unknown event from CRM"); + break; + } + return rc; +} + +static int __cam_isp_ctx_unlink_in_activated(struct cam_context *ctx, + struct cam_req_mgr_core_dev_link_setup *unlink) +{ + int rc = 0; + + CAM_WARN(CAM_ISP, + "Received unlink in activated state. It's unexpected"); + + rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx, NULL); + if (rc) + CAM_WARN(CAM_ISP, "Stop device failed rc=%d", rc); + + rc = __cam_isp_ctx_unlink_in_ready(ctx, unlink); + if (rc) + CAM_ERR(CAM_ISP, "Unlink failed rc=%d", rc); + + return rc; +} + +static int __cam_isp_ctx_apply_req(struct cam_context *ctx, + struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + + trace_cam_apply_req("ISP", apply->request_id); + CAM_DBG(CAM_ISP, "Enter: apply req in Substate %d request _id:%lld", + ctx_isp->substate_activated, apply->request_id); + if (ctx_isp->substate_machine[ctx_isp->substate_activated]. + crm_ops.apply_req) { + rc = ctx_isp->substate_machine[ctx_isp->substate_activated]. + crm_ops.apply_req(ctx, apply); + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "No handle function in activated substate %d", + ctx_isp->substate_activated); + rc = -EFAULT; + } + + if (rc) + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Apply failed in active substate %d", + ctx_isp->substate_activated); + return rc; +} + + + +static int __cam_isp_ctx_handle_irq_in_activated(void *context, + uint32_t evt_id, void *evt_data) +{ + int rc = 0; + struct cam_context *ctx = (struct cam_context *)context; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *)ctx->ctx_priv; + + spin_lock_bh(&ctx->lock); + + trace_cam_isp_activated_irq(ctx, ctx_isp->substate_activated, evt_id, + __cam_isp_ctx_get_event_ts(evt_id, evt_data)); + + CAM_DBG(CAM_ISP, "Enter: State %d, Substate %d, evt id %d", + ctx->state, ctx_isp->substate_activated, evt_id); + if (ctx_isp->substate_machine_irq[ctx_isp->substate_activated]. + irq_ops[evt_id]) { + rc = ctx_isp->substate_machine_irq[ctx_isp->substate_activated]. + irq_ops[evt_id](ctx_isp, evt_data); + } else { + CAM_DBG(CAM_ISP, "No handle function for substate %d", + ctx_isp->substate_activated); + } + CAM_DBG(CAM_ISP, "Exit: State %d Substate %d", + ctx->state, ctx_isp->substate_activated); + spin_unlock_bh(&ctx->lock); + return rc; +} + +/* top state machine */ +static struct cam_ctx_ops + cam_isp_ctx_top_state_machine[CAM_CTX_STATE_MAX] = { + /* Uninit */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Available */ + { + .ioctl_ops = { + .acquire_dev = __cam_isp_ctx_acquire_dev_in_available, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Acquired */ + { + .ioctl_ops = { + .release_dev = __cam_isp_ctx_release_dev_in_top_state, + .config_dev = __cam_isp_ctx_config_dev_in_acquired, + }, + .crm_ops = { + .link = __cam_isp_ctx_link_in_acquired, + .unlink = __cam_isp_ctx_unlink_in_acquired, + .get_dev_info = __cam_isp_ctx_get_dev_info_in_acquired, + .flush_req = __cam_isp_ctx_flush_req_in_top_state, + }, + .irq_ops = NULL, + }, + /* Ready */ + { + .ioctl_ops = { + .start_dev = __cam_isp_ctx_start_dev_in_ready, + .release_dev = __cam_isp_ctx_release_dev_in_top_state, + .config_dev = __cam_isp_ctx_config_dev_in_top_state, + }, + .crm_ops = { + .unlink = __cam_isp_ctx_unlink_in_ready, + .flush_req = __cam_isp_ctx_flush_req_in_ready, + }, + .irq_ops = NULL, + }, + /* Activated */ + { + .ioctl_ops = { + .stop_dev = __cam_isp_ctx_stop_dev_in_activated, + .release_dev = __cam_isp_ctx_release_dev_in_activated, + .config_dev = __cam_isp_ctx_config_dev_in_top_state, + }, + .crm_ops = { + .unlink = __cam_isp_ctx_unlink_in_activated, + .apply_req = __cam_isp_ctx_apply_req, + .flush_req = __cam_isp_ctx_flush_req_in_activated, + .process_evt = __cam_isp_ctx_process_evt, + }, + .irq_ops = __cam_isp_ctx_handle_irq_in_activated, + }, +}; + + +int cam_isp_context_init(struct cam_isp_context *ctx, + struct cam_context *ctx_base, + struct cam_req_mgr_kmd_ops *crm_node_intf, + struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id) + +{ + int rc = -1; + int i; + + if (!ctx || !ctx_base) { + CAM_ERR(CAM_ISP, "Invalid Context"); + goto err; + } + + /* ISP context setup */ + memset(ctx, 0, sizeof(*ctx)); + + ctx->base = ctx_base; + ctx->frame_id = 0; + ctx->active_req_cnt = 0; + ctx->reported_req_id = 0; + ctx->hw_ctx = NULL; + ctx->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; + ctx->substate_machine = cam_isp_ctx_activated_state_machine; + ctx->substate_machine_irq = cam_isp_ctx_activated_state_machine_irq; + + for (i = 0; i < CAM_CTX_REQ_MAX; i++) { + ctx->req_base[i].req_priv = &ctx->req_isp[i]; + ctx->req_isp[i].base = &ctx->req_base[i]; + } + + /* camera context setup */ + rc = cam_context_init(ctx_base, isp_dev_name, CAM_ISP, ctx_id, + crm_node_intf, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX); + if (rc) { + CAM_ERR(CAM_ISP, "Camera Context Base init failed"); + goto err; + } + + /* link camera context with isp context */ + ctx_base->state_machine = cam_isp_ctx_top_state_machine; + ctx_base->ctx_priv = ctx; + +err: + return rc; +} + +int cam_isp_context_deinit(struct cam_isp_context *ctx) +{ + int rc = 0; + + if (ctx->base) + cam_context_deinit(ctx->base); + + if (ctx->substate_activated != CAM_ISP_CTX_ACTIVATED_SOF) + CAM_ERR(CAM_ISP, "ISP context substate is invalid"); + + memset(ctx, 0, sizeof(*ctx)); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.h new file mode 100644 index 000000000000..38ea58de4a16 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_context.h @@ -0,0 +1,171 @@ +/* Copyright (c) 2017-2018, 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 _CAM_ISP_CONTEXT_H_ +#define _CAM_ISP_CONTEXT_H_ + + +#include +#include + +#include "cam_context.h" +#include "cam_isp_hw_mgr_intf.h" + +/* + * Maximum hw resource - This number is based on the maximum + * output port resource. The current maximum resource number + * is 20. + */ +#define CAM_ISP_CTX_RES_MAX 20 + +/* + * Maxiimum configuration entry size - This is based on the + * worst case DUAL IFE use case plus some margin. + */ +#define CAM_ISP_CTX_CFG_MAX 22 + +/* forward declaration */ +struct cam_isp_context; + +/* cam isp context irq handling function type */ +typedef int (*cam_isp_hw_event_cb_func)(struct cam_isp_context *ctx_isp, + void *evt_data); + +/** + * enum cam_isp_ctx_activated_substate - sub states for activated + * + */ +enum cam_isp_ctx_activated_substate { + CAM_ISP_CTX_ACTIVATED_SOF, + CAM_ISP_CTX_ACTIVATED_APPLIED, + CAM_ISP_CTX_ACTIVATED_EPOCH, + CAM_ISP_CTX_ACTIVATED_BUBBLE, + CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED, + CAM_ISP_CTX_ACTIVATED_HW_ERROR, + CAM_ISP_CTX_ACTIVATED_HALT, + CAM_ISP_CTX_ACTIVATED_FLUSH, + CAM_ISP_CTX_ACTIVATED_MAX, +}; + + +/** + * struct cam_isp_ctx_irq_ops - Function table for handling IRQ callbacks + * + * @irq_ops: Array of handle function pointers. + * + */ +struct cam_isp_ctx_irq_ops { + cam_isp_hw_event_cb_func irq_ops[CAM_ISP_HW_EVENT_MAX]; +}; + +/** + * struct cam_isp_ctx_req - ISP context request object + * + * @base: Common request object ponter + * @cfg: ISP hardware configuration array + * @num_cfg: Number of ISP hardware configuration entries + * @fence_map_out: Output fence mapping array + * @num_fence_map_out: Number of the output fence map + * @fence_map_in: Input fence mapping array + * @num_fence_map_in: Number of input fence map + * @num_acked: Count to track acked entried for output. + * If count equals the number of fence out, it means + * the request has been completed. + * @bubble_report: Flag to track if bubble report is active on + * current request + * @hw_update_data: HW update data for this request + * + */ +struct cam_isp_ctx_req { + struct cam_ctx_request *base; + + struct cam_hw_update_entry cfg[CAM_ISP_CTX_CFG_MAX]; + uint32_t num_cfg; + struct cam_hw_fence_map_entry fence_map_out + [CAM_ISP_CTX_RES_MAX]; + uint32_t num_fence_map_out; + struct cam_hw_fence_map_entry fence_map_in[CAM_ISP_CTX_RES_MAX]; + uint32_t num_fence_map_in; + uint32_t num_acked; + int32_t bubble_report; + struct cam_isp_prepare_hw_update_data hw_update_data; +}; + +/** + * struct cam_isp_context - ISP context object + * + * @base: Common context object pointer + * @frame_id: Frame id tracking for the isp context + * @substate_actiavted: Current substate for the activated state. + * @substate_machine: ISP substate machine for external interface + * @substate_machine_irq: ISP substate machine for irq handling + * @req_base: Common request object storage + * @req_isp: ISP private request object storage + * @hw_ctx: HW object returned by the acquire device command + * @sof_timestamp_val: Captured time stamp value at sof hw event + * @active_req_cnt: Counter for the active request + * @reported_req_id: Last reported request id + * @subscribe_event: The irq event mask that CRM subscribes to, IFE will + * invoke CRM cb at those event. + * @last_applied_req_id: Last applied request id + * @frame_skip_count: Number of frame to skip before change state + * + */ +struct cam_isp_context { + struct cam_context *base; + + int64_t frame_id; + uint32_t substate_activated; + struct cam_ctx_ops *substate_machine; + struct cam_isp_ctx_irq_ops *substate_machine_irq; + + struct cam_ctx_request req_base[CAM_CTX_REQ_MAX]; + struct cam_isp_ctx_req req_isp[CAM_CTX_REQ_MAX]; + + void *hw_ctx; + uint64_t sof_timestamp_val; + int32_t active_req_cnt; + int64_t reported_req_id; + uint32_t subscribe_event; + int64_t last_applied_req_id; + uint32_t frame_skip_count; +}; + +/** + * cam_isp_context_init() + * + * @brief: Initialization function for the ISP context + * + * @ctx: ISP context obj to be initialized + * @bridge_ops: Bridge call back funciton + * @hw_intf: ISP hw manager interface + * @ctx_id: ID for this context + * + */ +int cam_isp_context_init(struct cam_isp_context *ctx, + struct cam_context *ctx_base, + struct cam_req_mgr_kmd_ops *bridge_ops, + struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id); + +/** + * cam_isp_context_deinit() + * + * @brief: Deinitialize function for the ISP context + * + * @ctx: ISP context obj to be deinitialized + * + */ +int cam_isp_context_deinit(struct cam_isp_context *ctx); + + +#endif /* __CAM_ISP_CONTEXT_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.c new file mode 100644 index 000000000000..fac8f857dae7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.c @@ -0,0 +1,178 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include + +#include +#include "cam_isp_dev.h" +#include "cam_isp_log.h" +#include "cam_hw_mgr_intf.h" +#include "cam_isp_hw_mgr_intf.h" +#include "cam_node.h" +#include "cam_debug_util.h" +#include "cam_smmu_api.h" + +static struct cam_isp_dev g_isp_dev; + +static void cam_isp_dev_iommu_fault_handler( + struct iommu_domain *domain, struct device *dev, unsigned long iova, + int flags, void *token, uint32_t buf_info) +{ + int i = 0; + struct cam_node *node = NULL; + + if (!token) { + CAM_ERR(CAM_ISP, "invalid token in page handler cb"); + return; + } + + node = (struct cam_node *)token; + + for (i = 0; i < node->ctx_size; i++) + cam_context_dump_pf_info(&(node->ctx_list[i]), iova, + buf_info); +} + +static const struct of_device_id cam_isp_dt_match[] = { + { + .compatible = "qcom,cam-isp" + }, + {} +}; + +static int cam_isp_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_node *node = v4l2_get_subdevdata(sd); + + if (!node) { + CAM_ERR(CAM_ISP, "Node ptr is NULL"); + return -EINVAL; + } + + cam_node_shutdown(node); + + return 0; +} + +static const struct v4l2_subdev_internal_ops cam_isp_subdev_internal_ops = { + .close = cam_isp_subdev_close, +}; + +static int cam_isp_dev_remove(struct platform_device *pdev) +{ + int rc = 0; + int i; + + /* clean up resources */ + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_isp_context_deinit(&g_isp_dev.ctx_isp[i]); + if (rc) + CAM_ERR(CAM_ISP, "ISP context %d deinit failed", + i); + } + + rc = cam_subdev_remove(&g_isp_dev.sd); + if (rc) + CAM_ERR(CAM_ISP, "Unregister failed"); + + memset(&g_isp_dev, 0, sizeof(g_isp_dev)); + return 0; +} + +static int cam_isp_dev_probe(struct platform_device *pdev) +{ + int rc = -1; + int i; + struct cam_hw_mgr_intf hw_mgr_intf; + struct cam_node *node; + int iommu_hdl = -1; + + g_isp_dev.sd.internal_ops = &cam_isp_subdev_internal_ops; + /* Initialize the v4l2 subdevice first. (create cam_node) */ + rc = cam_subdev_probe(&g_isp_dev.sd, pdev, CAM_ISP_DEV_NAME, + CAM_IFE_DEVICE_TYPE); + if (rc) { + CAM_ERR(CAM_ISP, "ISP cam_subdev_probe failed!"); + goto err; + } + node = (struct cam_node *) g_isp_dev.sd.token; + + memset(&hw_mgr_intf, 0, sizeof(hw_mgr_intf)); + rc = cam_isp_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf, &iommu_hdl); + if (rc != 0) { + CAM_ERR(CAM_ISP, "Can not initialized ISP HW manager!"); + goto unregister; + } + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_isp_context_init(&g_isp_dev.ctx_isp[i], + &g_isp_dev.ctx[i], + &node->crm_node_intf, + &node->hw_mgr_intf, + i); + if (rc) { + CAM_ERR(CAM_ISP, "ISP context init failed!"); + goto unregister; + } + } + + rc = cam_node_init(node, &hw_mgr_intf, g_isp_dev.ctx, CAM_CTX_MAX, + CAM_ISP_DEV_NAME); + if (rc) { + CAM_ERR(CAM_ISP, "ISP node init failed!"); + goto unregister; + } + + cam_smmu_set_client_page_fault_handler(iommu_hdl, + cam_isp_dev_iommu_fault_handler, node); + + CAM_INFO(CAM_ISP, "Camera ISP probe complete"); + + return 0; +unregister: + rc = cam_subdev_remove(&g_isp_dev.sd); +err: + return rc; +} + + +static struct platform_driver isp_driver = { + .probe = cam_isp_dev_probe, + .remove = cam_isp_dev_remove, + .driver = { + .name = "cam_isp", + .owner = THIS_MODULE, + .of_match_table = cam_isp_dt_match, + }, +}; + +static int __init cam_isp_dev_init_module(void) +{ + return platform_driver_register(&isp_driver); +} + +static void __exit cam_isp_dev_exit_module(void) +{ + platform_driver_unregister(&isp_driver); +} + +module_init(cam_isp_dev_init_module); +module_exit(cam_isp_dev_exit_module); +MODULE_DESCRIPTION("MSM ISP driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.h new file mode 100644 index 000000000000..95463ca37a13 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_dev.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ISP_DEV_H_ +#define _CAM_ISP_DEV_H_ + +#include "cam_subdev.h" +#include "cam_hw_mgr_intf.h" +#include "cam_context.h" +#include "cam_isp_context.h" + +/** + * struct cam_isp_dev - Camera ISP V4l2 device node + * + * @sd: Commone camera subdevice node + * @ctx: Isp base context storage + * @ctx_isp: Isp private context storage + * + */ +struct cam_isp_dev { + struct cam_subdev sd; + struct cam_context ctx[CAM_CTX_MAX]; + struct cam_isp_context ctx_isp[CAM_CTX_MAX]; +}; + +#endif /* __CAM_ISP_DEV_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_log.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_log.h new file mode 100644 index 000000000000..3d8d8aa194c8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/cam_isp_log.h @@ -0,0 +1,26 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ISP_LOG_H_ +#define _CAM_ISP_LOG_H_ + +#include + +#define ISP_TRACE_ENABLE 0 + +#if (ISP_TRACE_ENABLE == 1) + #define ISP_TRACE(args...) trace_printk(args) +#else + #define ISP_TRACE(arg...) +#endif + +#endif /* __CAM_ISP_LOG_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/Makefile new file mode 100644 index 000000000000..f66f64749e82 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/Makefile @@ -0,0 +1,15 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += hw_utils/ isp_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp_hw_mgr.o cam_ife_hw_mgr.o + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c new file mode 100644 index 000000000000..9dc68e5ee7d3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c @@ -0,0 +1,4221 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include "cam_smmu_api.h" +#include "cam_req_mgr_workq.h" +#include "cam_isp_hw_mgr_intf.h" +#include "cam_isp_hw.h" +#include "cam_ife_csid_hw_intf.h" +#include "cam_vfe_hw_intf.h" +#include "cam_isp_packet_parser.h" +#include "cam_ife_hw_mgr.h" +#include "cam_cdm_intf_api.h" +#include "cam_packet_util.h" +#include "cam_debug_util.h" +#include "cam_cpas_api.h" + +#define CAM_IFE_HW_ENTRIES_MAX 20 + +#define TZ_SVC_SMMU_PROGRAM 0x15 +#define TZ_SAFE_SYSCALL_ID 0x3 +#define CAM_IFE_SAFE_DISABLE 0 +#define CAM_IFE_SAFE_ENABLE 1 +#define SMMU_SE_IFE 0 + +#define CAM_ISP_PACKET_META_MAX \ + (CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON + 1) + +#define CAM_ISP_GENERIC_BLOB_TYPE_MAX \ + (CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG + 1) + +static uint32_t blob_type_hw_cmd_map[CAM_ISP_GENERIC_BLOB_TYPE_MAX] = { + CAM_ISP_HW_CMD_GET_HFR_UPDATE, + CAM_ISP_HW_CMD_CLOCK_UPDATE, + CAM_ISP_HW_CMD_BW_UPDATE, +}; + +static struct cam_ife_hw_mgr g_ife_hw_mgr; + +static int cam_ife_notify_safe_lut_scm(bool safe_trigger) +{ + uint32_t camera_hw_version, rc = 0; + struct scm_desc desc = {0}; + + rc = cam_cpas_get_cpas_hw_version(&camera_hw_version); + if (!rc) { + switch (camera_hw_version) { + case CAM_CPAS_TITAN_170_V100: + case CAM_CPAS_TITAN_170_V110: + case CAM_CPAS_TITAN_175_V100: + + desc.arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL); + desc.args[0] = SMMU_SE_IFE; + desc.args[1] = safe_trigger; + + CAM_DBG(CAM_ISP, "Safe scm call %d", safe_trigger); + if (scm_call2(SCM_SIP_FNID(TZ_SVC_SMMU_PROGRAM, + TZ_SAFE_SYSCALL_ID), &desc)) { + CAM_ERR(CAM_ISP, + "scm call to Enable Safe failed"); + rc = -EINVAL; + } + break; + default: + break; + } + } + + return rc; +} + +static int cam_ife_mgr_get_hw_caps(void *hw_mgr_priv, + void *hw_caps_args) +{ + int rc = 0; + int i; + struct cam_ife_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_query_cap_cmd *query = hw_caps_args; + struct cam_isp_query_cap_cmd query_isp; + + CAM_DBG(CAM_ISP, "enter"); + + if (copy_from_user(&query_isp, + u64_to_user_ptr(query->caps_handle), + sizeof(struct cam_isp_query_cap_cmd))) { + rc = -EFAULT; + return rc; + } + + query_isp.device_iommu.non_secure = hw_mgr->mgr_common.img_iommu_hdl; + query_isp.device_iommu.secure = hw_mgr->mgr_common.img_iommu_hdl_secure; + query_isp.cdm_iommu.non_secure = hw_mgr->mgr_common.cmd_iommu_hdl; + query_isp.cdm_iommu.secure = hw_mgr->mgr_common.cmd_iommu_hdl_secure; + query_isp.num_dev = 2; + for (i = 0; i < query_isp.num_dev; i++) { + query_isp.dev_caps[i].hw_type = CAM_ISP_HW_IFE; + query_isp.dev_caps[i].hw_version.major = 1; + query_isp.dev_caps[i].hw_version.minor = 7; + query_isp.dev_caps[i].hw_version.incr = 0; + query_isp.dev_caps[i].hw_version.reserved = 0; + } + + if (copy_to_user(u64_to_user_ptr(query->caps_handle), + &query_isp, sizeof(struct cam_isp_query_cap_cmd))) + rc = -EFAULT; + + CAM_DBG(CAM_ISP, "exit rc :%d", rc); + + return rc; +} + +static int cam_ife_hw_mgr_is_rdi_res(uint32_t res_id) +{ + int rc = 0; + + switch (res_id) { + case CAM_ISP_IFE_OUT_RES_RDI_0: + case CAM_ISP_IFE_OUT_RES_RDI_1: + case CAM_ISP_IFE_OUT_RES_RDI_2: + case CAM_ISP_IFE_OUT_RES_RDI_3: + rc = 1; + break; + default: + break; + } + + return rc; +} + +static int cam_ife_hw_mgr_reset_csid_res( + struct cam_ife_hw_mgr_res *isp_hw_res) +{ + int i; + int rc = 0; + struct cam_hw_intf *hw_intf; + struct cam_csid_reset_cfg_args csid_reset_args; + + csid_reset_args.reset_type = CAM_IFE_CSID_RESET_PATH; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + csid_reset_args.node_res = isp_hw_res->hw_res[i]; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + CAM_DBG(CAM_ISP, "Resetting csid hardware %d", + hw_intf->hw_idx); + if (hw_intf->hw_ops.reset) { + rc = hw_intf->hw_ops.reset(hw_intf->hw_priv, + &csid_reset_args, + sizeof(struct cam_csid_reset_cfg_args)); + if (rc <= 0) + goto err; + } + } + + return 0; +err: + CAM_ERR(CAM_ISP, "RESET HW res failed: (type:%d, id:%d)", + isp_hw_res->res_type, isp_hw_res->res_id); + return rc; +} + +static int cam_ife_hw_mgr_init_hw_res( + struct cam_ife_hw_mgr_res *isp_hw_res) +{ + int i; + int rc = -1; + struct cam_hw_intf *hw_intf; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + CAM_DBG(CAM_ISP, "enabled vfe hardware %d", + hw_intf->hw_idx); + if (hw_intf->hw_ops.init) { + rc = hw_intf->hw_ops.init(hw_intf->hw_priv, + isp_hw_res->hw_res[i], + sizeof(struct cam_isp_resource_node)); + if (rc) + goto err; + } + } + + return 0; +err: + CAM_ERR(CAM_ISP, "INIT HW res failed: (type:%d, id:%d)", + isp_hw_res->res_type, isp_hw_res->res_id); + return rc; +} + +static int cam_ife_hw_mgr_start_hw_res( + struct cam_ife_hw_mgr_res *isp_hw_res, + struct cam_ife_hw_mgr_ctx *ctx) +{ + int i; + int rc = -1; + struct cam_hw_intf *hw_intf; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + if (hw_intf->hw_ops.start) { + isp_hw_res->hw_res[i]->rdi_only_ctx = + ctx->is_rdi_only_context; + rc = hw_intf->hw_ops.start(hw_intf->hw_priv, + isp_hw_res->hw_res[i], + sizeof(struct cam_isp_resource_node)); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start HW resources"); + goto err; + } + } else { + CAM_ERR(CAM_ISP, "function null"); + goto err; + } + } + + return 0; +err: + CAM_ERR(CAM_ISP, "Start hw res failed (type:%d, id:%d)", + isp_hw_res->res_type, isp_hw_res->res_id); + return rc; +} + +static void cam_ife_hw_mgr_stop_hw_res( + struct cam_ife_hw_mgr_res *isp_hw_res) +{ + int i; + struct cam_hw_intf *hw_intf; + uint32_t dummy_args; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + if (hw_intf->hw_ops.stop) + hw_intf->hw_ops.stop(hw_intf->hw_priv, + isp_hw_res->hw_res[i], + sizeof(struct cam_isp_resource_node)); + else + CAM_ERR(CAM_ISP, "stop null"); + if (hw_intf->hw_ops.process_cmd && + isp_hw_res->res_type == CAM_IFE_HW_MGR_RES_IFE_OUT) { + hw_intf->hw_ops.process_cmd(hw_intf->hw_priv, + CAM_ISP_HW_CMD_STOP_BUS_ERR_IRQ, + &dummy_args, sizeof(dummy_args)); + } + } +} + +static void cam_ife_hw_mgr_deinit_hw_res( + struct cam_ife_hw_mgr_res *isp_hw_res) +{ + int i; + struct cam_hw_intf *hw_intf; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + if (hw_intf->hw_ops.deinit) + hw_intf->hw_ops.deinit(hw_intf->hw_priv, + isp_hw_res->hw_res[i], + sizeof(struct cam_isp_resource_node)); + } +} + +static int cam_ife_hw_mgr_put_res( + struct list_head *src_list, + struct cam_ife_hw_mgr_res **res) +{ + int rc = 0; + struct cam_ife_hw_mgr_res *res_ptr = NULL; + + res_ptr = *res; + if (res_ptr) + list_add_tail(&res_ptr->list, src_list); + + return rc; +} + +static int cam_ife_hw_mgr_get_res( + struct list_head *src_list, + struct cam_ife_hw_mgr_res **res) +{ + int rc = 0; + struct cam_ife_hw_mgr_res *res_ptr = NULL; + + if (!list_empty(src_list)) { + res_ptr = list_first_entry(src_list, + struct cam_ife_hw_mgr_res, list); + list_del_init(&res_ptr->list); + } else { + CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx"); + rc = -1; + } + *res = res_ptr; + + return rc; +} + +static int cam_ife_hw_mgr_free_hw_res( + struct cam_ife_hw_mgr_res *isp_hw_res) +{ + int rc = 0; + int i; + struct cam_hw_intf *hw_intf; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!isp_hw_res->hw_res[i]) + continue; + hw_intf = isp_hw_res->hw_res[i]->hw_intf; + if (hw_intf->hw_ops.release) { + rc = hw_intf->hw_ops.release(hw_intf->hw_priv, + isp_hw_res->hw_res[i], + sizeof(struct cam_isp_resource_node)); + if (rc) + CAM_ERR(CAM_ISP, + "Release hw resrouce id %d failed", + isp_hw_res->res_id); + isp_hw_res->hw_res[i] = NULL; + } else + CAM_ERR(CAM_ISP, "Release null"); + } + /* caller should make sure the resource is in a list */ + list_del_init(&isp_hw_res->list); + memset(isp_hw_res, 0, sizeof(*isp_hw_res)); + INIT_LIST_HEAD(&isp_hw_res->list); + + return 0; +} + +static int cam_ife_mgr_csid_stop_hw( + struct cam_ife_hw_mgr_ctx *ctx, struct list_head *stop_list, + uint32_t base_idx, uint32_t stop_cmd) +{ + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_isp_resource_node *isp_res; + struct cam_isp_resource_node *stop_res[CAM_IFE_PIX_PATH_RES_MAX - 1]; + struct cam_csid_hw_stop_args stop; + struct cam_hw_intf *hw_intf; + uint32_t i, cnt; + + cnt = 0; + list_for_each_entry(hw_mgr_res, stop_list, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + isp_res = hw_mgr_res->hw_res[i]; + if (isp_res->hw_intf->hw_idx != base_idx) + continue; + + stop_res[cnt] = isp_res; + cnt++; + } + } + + if (cnt) { + hw_intf = stop_res[0]->hw_intf; + stop.num_res = cnt; + stop.node_res = stop_res; + stop.stop_cmd = stop_cmd; + hw_intf->hw_ops.stop(hw_intf->hw_priv, &stop, sizeof(stop)); + } + + return 0; +} + +static int cam_ife_hw_mgr_release_hw_for_ctx( + struct cam_ife_hw_mgr_ctx *ife_ctx) +{ + uint32_t i; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_ife_hw_mgr_res *hw_mgr_res_temp; + + /* ife leaf resource */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) + cam_ife_hw_mgr_free_hw_res(&ife_ctx->res_list_ife_out[i]); + + /* ife source resource */ + list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp, + &ife_ctx->res_list_ife_src, list) { + cam_ife_hw_mgr_free_hw_res(hw_mgr_res); + cam_ife_hw_mgr_put_res(&ife_ctx->free_res_list, &hw_mgr_res); + } + + /* ife csid resource */ + list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp, + &ife_ctx->res_list_ife_csid, list) { + cam_ife_hw_mgr_free_hw_res(hw_mgr_res); + cam_ife_hw_mgr_put_res(&ife_ctx->free_res_list, &hw_mgr_res); + } + + /* ife cid resource */ + list_for_each_entry_safe(hw_mgr_res, hw_mgr_res_temp, + &ife_ctx->res_list_ife_cid, list) { + cam_ife_hw_mgr_free_hw_res(hw_mgr_res); + cam_ife_hw_mgr_put_res(&ife_ctx->free_res_list, &hw_mgr_res); + } + + /* ife root node */ + if (ife_ctx->res_list_ife_in.res_type != CAM_IFE_HW_MGR_RES_UNINIT) + cam_ife_hw_mgr_free_hw_res(&ife_ctx->res_list_ife_in); + + /* clean up the callback function */ + ife_ctx->common.cb_priv = NULL; + memset(ife_ctx->common.event_cb, 0, sizeof(ife_ctx->common.event_cb)); + + CAM_DBG(CAM_ISP, "release context completed ctx id:%d", + ife_ctx->ctx_index); + + return 0; +} + + +static int cam_ife_hw_mgr_put_ctx( + struct list_head *src_list, + struct cam_ife_hw_mgr_ctx **ife_ctx) +{ + int rc = 0; + struct cam_ife_hw_mgr_ctx *ctx_ptr = NULL; + + mutex_lock(&g_ife_hw_mgr.ctx_mutex); + ctx_ptr = *ife_ctx; + if (ctx_ptr) + list_add_tail(&ctx_ptr->list, src_list); + *ife_ctx = NULL; + mutex_unlock(&g_ife_hw_mgr.ctx_mutex); + return rc; +} + +static int cam_ife_hw_mgr_get_ctx( + struct list_head *src_list, + struct cam_ife_hw_mgr_ctx **ife_ctx) +{ + int rc = 0; + struct cam_ife_hw_mgr_ctx *ctx_ptr = NULL; + + mutex_lock(&g_ife_hw_mgr.ctx_mutex); + if (!list_empty(src_list)) { + ctx_ptr = list_first_entry(src_list, + struct cam_ife_hw_mgr_ctx, list); + list_del_init(&ctx_ptr->list); + } else { + CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx"); + rc = -1; + } + *ife_ctx = ctx_ptr; + mutex_unlock(&g_ife_hw_mgr.ctx_mutex); + + return rc; +} + +static void cam_ife_mgr_add_base_info( + struct cam_ife_hw_mgr_ctx *ctx, + enum cam_isp_hw_split_id split_id, + uint32_t base_idx) +{ + uint32_t i; + + if (!ctx->num_base) { + ctx->base[0].split_id = split_id; + ctx->base[0].idx = base_idx; + ctx->num_base++; + CAM_DBG(CAM_ISP, + "Add split id = %d for base idx = %d num_base=%d", + split_id, base_idx, ctx->num_base); + } else { + /*Check if base index is alreay exist in the list */ + for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + if (ctx->base[i].idx == base_idx) { + if (split_id != CAM_ISP_HW_SPLIT_MAX && + ctx->base[i].split_id == + CAM_ISP_HW_SPLIT_MAX) + ctx->base[i].split_id = split_id; + + break; + } + } + + if (i == CAM_IFE_HW_NUM_MAX) { + ctx->base[ctx->num_base].split_id = split_id; + ctx->base[ctx->num_base].idx = base_idx; + ctx->num_base++; + CAM_DBG(CAM_ISP, + "Add split_id=%d for base idx=%d num_base=%d", + split_id, base_idx, ctx->num_base); + } + } +} + +static int cam_ife_mgr_process_base_info( + struct cam_ife_hw_mgr_ctx *ctx) +{ + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_isp_resource_node *res = NULL; + uint32_t i; + + if (list_empty(&ctx->res_list_ife_src)) { + CAM_ERR(CAM_ISP, "Mux List empty"); + return -ENODEV; + } + + /* IFE mux in resources */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + res = hw_mgr_res->hw_res[i]; + cam_ife_mgr_add_base_info(ctx, i, + res->hw_intf->hw_idx); + CAM_DBG(CAM_ISP, "add base info for hw %d", + res->hw_intf->hw_idx); + } + } + CAM_DBG(CAM_ISP, "ctx base num = %d", ctx->num_base); + + return 0; +} + +static int cam_ife_hw_mgr_acquire_res_ife_out_rdi( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_ife_hw_mgr_res *ife_src_res, + struct cam_isp_in_port_info *in_port) +{ + int rc = -EINVAL; + struct cam_vfe_acquire_args vfe_acquire; + struct cam_isp_out_port_info *out_port = NULL; + struct cam_ife_hw_mgr_res *ife_out_res; + struct cam_hw_intf *hw_intf; + uint32_t i, vfe_out_res_id, vfe_in_res_id; + + /* take left resource */ + vfe_in_res_id = ife_src_res->hw_res[0]->res_id; + + switch (vfe_in_res_id) { + case CAM_ISP_HW_VFE_IN_RDI0: + vfe_out_res_id = CAM_ISP_IFE_OUT_RES_RDI_0; + break; + case CAM_ISP_HW_VFE_IN_RDI1: + vfe_out_res_id = CAM_ISP_IFE_OUT_RES_RDI_1; + break; + case CAM_ISP_HW_VFE_IN_RDI2: + vfe_out_res_id = CAM_ISP_IFE_OUT_RES_RDI_2; + break; + case CAM_ISP_HW_VFE_IN_RDI3: + vfe_out_res_id = CAM_ISP_IFE_OUT_RES_RDI_3; + break; + default: + CAM_ERR(CAM_ISP, "invalid resource type"); + goto err; + } + CAM_DBG(CAM_ISP, "vfe_in_res_id = %d, vfe_out_red_id = %d", + vfe_in_res_id, vfe_out_res_id); + + vfe_acquire.rsrc_type = CAM_ISP_RESOURCE_VFE_OUT; + vfe_acquire.tasklet = ife_ctx->common.tasklet_info; + + ife_out_res = &ife_ctx->res_list_ife_out[vfe_out_res_id & 0xFF]; + for (i = 0; i < in_port->num_out_res; i++) { + out_port = &in_port->data[i]; + + CAM_DBG(CAM_ISP, "i = %d, vfe_out_res_id = %d, out_port: %d", + i, vfe_out_res_id, out_port->res_type); + + if (vfe_out_res_id != out_port->res_type) + continue; + + vfe_acquire.vfe_out.cdm_ops = ife_ctx->cdm_ops; + vfe_acquire.vfe_out.ctx = ife_ctx; + vfe_acquire.vfe_out.out_port_info = out_port; + vfe_acquire.vfe_out.split_id = CAM_ISP_HW_SPLIT_LEFT; + vfe_acquire.vfe_out.unique_id = ife_ctx->ctx_index; + vfe_acquire.vfe_out.is_dual = 0; + hw_intf = ife_src_res->hw_res[0]->hw_intf; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &vfe_acquire, + sizeof(struct cam_vfe_acquire_args)); + if (rc) { + CAM_ERR(CAM_ISP, "Can not acquire out resource 0x%x", + out_port->res_type); + goto err; + } + break; + } + + if (i == in_port->num_out_res) { + CAM_ERR(CAM_ISP, + "Cannot acquire out resource, i=%d, num_out_res=%d", + i, in_port->num_out_res); + goto err; + } + + ife_out_res->hw_res[0] = vfe_acquire.vfe_out.rsrc_node; + ife_out_res->is_dual_vfe = 0; + ife_out_res->res_id = vfe_out_res_id; + ife_out_res->res_type = (enum cam_ife_hw_mgr_res_type) + CAM_ISP_RESOURCE_VFE_OUT; + ife_src_res->child[ife_src_res->num_children++] = ife_out_res; + + return 0; +err: + return rc; +} + +static int cam_ife_hw_mgr_acquire_res_ife_out_pixel( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_ife_hw_mgr_res *ife_src_res, + struct cam_isp_in_port_info *in_port) +{ + int rc = -1; + uint32_t i, j, k; + struct cam_vfe_acquire_args vfe_acquire; + struct cam_isp_out_port_info *out_port; + struct cam_ife_hw_mgr_res *ife_out_res; + struct cam_hw_intf *hw_intf; + + for (i = 0; i < in_port->num_out_res; i++) { + out_port = &in_port->data[i]; + k = out_port->res_type & 0xFF; + if (k >= CAM_IFE_HW_OUT_RES_MAX) { + CAM_ERR(CAM_ISP, "invalid output resource type 0x%x", + out_port->res_type); + continue; + } + + if (cam_ife_hw_mgr_is_rdi_res(out_port->res_type)) + continue; + + CAM_DBG(CAM_ISP, "res_type 0x%x", + out_port->res_type); + + ife_out_res = &ife_ctx->res_list_ife_out[k]; + ife_out_res->is_dual_vfe = in_port->usage_type; + + vfe_acquire.rsrc_type = CAM_ISP_RESOURCE_VFE_OUT; + vfe_acquire.tasklet = ife_ctx->common.tasklet_info; + vfe_acquire.vfe_out.cdm_ops = ife_ctx->cdm_ops; + vfe_acquire.vfe_out.ctx = ife_ctx; + vfe_acquire.vfe_out.out_port_info = out_port; + vfe_acquire.vfe_out.is_dual = ife_src_res->is_dual_vfe; + vfe_acquire.vfe_out.unique_id = ife_ctx->ctx_index; + + for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) { + if (!ife_src_res->hw_res[j]) + continue; + + if (j == CAM_ISP_HW_SPLIT_LEFT) { + vfe_acquire.vfe_out.split_id = + CAM_ISP_HW_SPLIT_LEFT; + if (ife_src_res->is_dual_vfe) { + /*TBD */ + vfe_acquire.vfe_out.is_master = 1; + vfe_acquire.vfe_out.dual_slave_core = + 1; + } else { + vfe_acquire.vfe_out.is_master = 0; + vfe_acquire.vfe_out.dual_slave_core = + 0; + } + } else { + vfe_acquire.vfe_out.split_id = + CAM_ISP_HW_SPLIT_RIGHT; + vfe_acquire.vfe_out.is_master = 0; + vfe_acquire.vfe_out.dual_slave_core = 0; + } + + hw_intf = ife_src_res->hw_res[j]->hw_intf; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &vfe_acquire, + sizeof(struct cam_vfe_acquire_args)); + if (rc) { + CAM_ERR(CAM_ISP, + "Can not acquire out resource 0x%x", + out_port->res_type); + goto err; + } + + ife_out_res->hw_res[j] = + vfe_acquire.vfe_out.rsrc_node; + CAM_DBG(CAM_ISP, "resource type :0x%x res id:0x%x", + ife_out_res->hw_res[j]->res_type, + ife_out_res->hw_res[j]->res_id); + + } + ife_out_res->res_type = (enum cam_ife_hw_mgr_res_type) + CAM_ISP_RESOURCE_VFE_OUT; + ife_out_res->res_id = out_port->res_type; + ife_out_res->parent = ife_src_res; + ife_src_res->child[ife_src_res->num_children++] = ife_out_res; + } + + return 0; +err: + /* release resource at the entry function */ + return rc; +} + +static int cam_ife_hw_mgr_acquire_res_ife_out( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port) +{ + int rc = -EINVAL; + struct cam_ife_hw_mgr_res *ife_src_res; + + list_for_each_entry(ife_src_res, &ife_ctx->res_list_ife_src, list) { + if (ife_src_res->num_children) + continue; + + switch (ife_src_res->res_id) { + case CAM_ISP_HW_VFE_IN_CAMIF: + rc = cam_ife_hw_mgr_acquire_res_ife_out_pixel(ife_ctx, + ife_src_res, in_port); + break; + case CAM_ISP_HW_VFE_IN_RDI0: + case CAM_ISP_HW_VFE_IN_RDI1: + case CAM_ISP_HW_VFE_IN_RDI2: + case CAM_ISP_HW_VFE_IN_RDI3: + rc = cam_ife_hw_mgr_acquire_res_ife_out_rdi(ife_ctx, + ife_src_res, in_port); + break; + default: + CAM_ERR(CAM_ISP, "Unknown IFE SRC resource: %d", + ife_src_res->res_id); + break; + } + if (rc) + goto err; + } + + return 0; +err: + /* release resource on entry function */ + return rc; +} + +static int cam_ife_hw_mgr_acquire_res_ife_src( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port) +{ + int rc = -1; + int i; + struct cam_ife_hw_mgr_res *csid_res; + struct cam_ife_hw_mgr_res *ife_src_res; + struct cam_vfe_acquire_args vfe_acquire; + struct cam_hw_intf *hw_intf; + struct cam_ife_hw_mgr *ife_hw_mgr; + + ife_hw_mgr = ife_ctx->hw_mgr; + + list_for_each_entry(csid_res, &ife_ctx->res_list_ife_csid, list) { + if (csid_res->num_children) + continue; + + rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, + &ife_src_res); + if (rc) { + CAM_ERR(CAM_ISP, "No more free hw mgr resource"); + goto err; + } + cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_src, + &ife_src_res); + + vfe_acquire.rsrc_type = CAM_ISP_RESOURCE_VFE_IN; + vfe_acquire.tasklet = ife_ctx->common.tasklet_info; + vfe_acquire.vfe_in.cdm_ops = ife_ctx->cdm_ops; + + switch (csid_res->res_id) { + case CAM_IFE_PIX_PATH_RES_IPP: + vfe_acquire.vfe_in.res_id = CAM_ISP_HW_VFE_IN_CAMIF; + vfe_acquire.vfe_in.in_port = in_port; + if (csid_res->is_dual_vfe) + vfe_acquire.vfe_in.sync_mode = + CAM_ISP_HW_SYNC_MASTER; + else + vfe_acquire.vfe_in.sync_mode = + CAM_ISP_HW_SYNC_NONE; + + break; + case CAM_IFE_PIX_PATH_RES_RDI_0: + vfe_acquire.vfe_in.res_id = CAM_ISP_HW_VFE_IN_RDI0; + vfe_acquire.vfe_in.sync_mode = CAM_ISP_HW_SYNC_NONE; + break; + case CAM_IFE_PIX_PATH_RES_RDI_1: + vfe_acquire.vfe_in.res_id = CAM_ISP_HW_VFE_IN_RDI1; + vfe_acquire.vfe_in.sync_mode = CAM_ISP_HW_SYNC_NONE; + break; + case CAM_IFE_PIX_PATH_RES_RDI_2: + vfe_acquire.vfe_in.res_id = CAM_ISP_HW_VFE_IN_RDI2; + vfe_acquire.vfe_in.sync_mode = CAM_ISP_HW_SYNC_NONE; + break; + case CAM_IFE_PIX_PATH_RES_RDI_3: + vfe_acquire.vfe_in.res_id = CAM_ISP_HW_VFE_IN_RDI3; + vfe_acquire.vfe_in.sync_mode = CAM_ISP_HW_SYNC_NONE; + break; + default: + CAM_ERR(CAM_ISP, "Wrong IFE CSID Resource Node"); + goto err; + } + ife_src_res->res_type = (enum cam_ife_hw_mgr_res_type) + vfe_acquire.rsrc_type; + ife_src_res->res_id = vfe_acquire.vfe_in.res_id; + ife_src_res->is_dual_vfe = csid_res->is_dual_vfe; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!csid_res->hw_res[i]) + continue; + + hw_intf = ife_hw_mgr->ife_devices[ + csid_res->hw_res[i]->hw_intf->hw_idx]; + + /* fill in more acquire information as needed */ + /* slave Camif resource, */ + if (i == CAM_ISP_HW_SPLIT_RIGHT && + ife_src_res->is_dual_vfe) + vfe_acquire.vfe_in.sync_mode = + CAM_ISP_HW_SYNC_SLAVE; + + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &vfe_acquire, + sizeof(struct cam_vfe_acquire_args)); + if (rc) { + CAM_ERR(CAM_ISP, + "Can not acquire IFE HW res %d", + csid_res->res_id); + goto err; + } + ife_src_res->hw_res[i] = vfe_acquire.vfe_in.rsrc_node; + CAM_DBG(CAM_ISP, + "acquire success res type :0x%x res id:0x%x", + ife_src_res->hw_res[i]->res_type, + ife_src_res->hw_res[i]->res_id); + + } + + /* It should be one to one mapping between + * csid resource and ife source resource + */ + csid_res->child[0] = ife_src_res; + ife_src_res->parent = csid_res; + csid_res->child[csid_res->num_children++] = ife_src_res; + CAM_DBG(CAM_ISP, "csid_res=%d num_children=%d ife_src_res=%d", + csid_res->res_id, csid_res->num_children, + ife_src_res->res_id); + } + + return 0; +err: + /* release resource at the entry function */ + return rc; +} + +static int cam_ife_mgr_acquire_cid_res( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port, + uint32_t *cid_res_id, + enum cam_ife_pix_path_res_id csid_path) +{ + int rc = -1; + int i, j; + struct cam_ife_hw_mgr *ife_hw_mgr; + struct cam_ife_hw_mgr_res *cid_res; + struct cam_hw_intf *hw_intf; + struct cam_csid_hw_reserve_resource_args csid_acquire; + + ife_hw_mgr = ife_ctx->hw_mgr; + + rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &cid_res); + if (rc) { + CAM_ERR(CAM_ISP, "No more free hw mgr resource"); + goto err; + } + cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_cid, &cid_res); + + csid_acquire.res_type = CAM_ISP_RESOURCE_CID; + csid_acquire.in_port = in_port; + csid_acquire.res_id = csid_path; + + for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) { + if (!ife_hw_mgr->csid_devices[i]) + continue; + + hw_intf = ife_hw_mgr->csid_devices[i]; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, &csid_acquire, + sizeof(csid_acquire)); + if (rc) + continue; + else + break; + } + + if (i == CAM_IFE_CSID_HW_NUM_MAX || !csid_acquire.node_res) { + CAM_ERR(CAM_ISP, "Can not acquire ife csid rdi resource"); + goto err; + } + + cid_res->res_type = CAM_IFE_HW_MGR_RES_CID; + cid_res->res_id = csid_acquire.node_res->res_id; + cid_res->is_dual_vfe = in_port->usage_type; + cid_res->hw_res[0] = csid_acquire.node_res; + cid_res->hw_res[1] = NULL; + /* CID(DT_ID) value of acquire device, require for path */ + *cid_res_id = csid_acquire.node_res->res_id; + + if (cid_res->is_dual_vfe) { + csid_acquire.node_res = NULL; + csid_acquire.res_type = CAM_ISP_RESOURCE_CID; + csid_acquire.in_port = in_port; + for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) { + if (!ife_hw_mgr->csid_devices[j]) + continue; + + hw_intf = ife_hw_mgr->csid_devices[j]; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &csid_acquire, sizeof(csid_acquire)); + if (rc) + continue; + else + break; + } + + if (j == CAM_IFE_CSID_HW_NUM_MAX) { + CAM_ERR(CAM_ISP, + "Can not acquire ife csid rdi resource"); + goto err; + } + cid_res->hw_res[1] = csid_acquire.node_res; + } + cid_res->parent = &ife_ctx->res_list_ife_in; + ife_ctx->res_list_ife_in.child[ + ife_ctx->res_list_ife_in.num_children++] = cid_res; + + return 0; +err: + return rc; + +} + +static int cam_ife_hw_mgr_acquire_res_ife_csid_ipp( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port) +{ + int rc = -1; + int i; + + struct cam_ife_hw_mgr *ife_hw_mgr; + struct cam_ife_hw_mgr_res *csid_res; + struct cam_ife_hw_mgr_res *cid_res; + struct cam_hw_intf *hw_intf; + uint32_t cid_res_id; + struct cam_csid_hw_reserve_resource_args csid_acquire; + + /* get cid resource */ + rc = cam_ife_mgr_acquire_cid_res(ife_ctx, in_port, &cid_res_id, + CAM_IFE_PIX_PATH_RES_IPP); + if (rc) { + CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed"); + goto err; + } + + ife_hw_mgr = ife_ctx->hw_mgr; + + rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &csid_res); + if (rc) { + CAM_ERR(CAM_ISP, "No more free hw mgr resource"); + goto err; + } + cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_csid, &csid_res); + + csid_acquire.res_type = (enum cam_isp_resource_type)CAM_ISP_RESOURCE_PIX_PATH; + csid_acquire.res_id = CAM_IFE_PIX_PATH_RES_IPP; + csid_acquire.cid = cid_res_id; + csid_acquire.in_port = in_port; + csid_acquire.out_port = in_port->data; + + csid_res->res_type = (enum cam_ife_hw_mgr_res_type)CAM_ISP_RESOURCE_PIX_PATH; + csid_res->res_id = CAM_IFE_PIX_PATH_RES_IPP; + csid_res->is_dual_vfe = in_port->usage_type; + + if (in_port->usage_type) + csid_res->is_dual_vfe = 1; + else { + csid_res->is_dual_vfe = 0; + csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE; + } + + list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid, + list) { + if (cid_res->res_id != cid_res_id) + continue; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!cid_res->hw_res[i]) + continue; + + csid_acquire.node_res = NULL; + if (csid_res->is_dual_vfe) { + if (i == CAM_ISP_HW_SPLIT_LEFT) + csid_acquire.sync_mode = + CAM_ISP_HW_SYNC_MASTER; + else + csid_acquire.sync_mode = + CAM_ISP_HW_SYNC_SLAVE; + } + + hw_intf = ife_hw_mgr->csid_devices[ + cid_res->hw_res[i]->hw_intf->hw_idx]; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &csid_acquire, sizeof(csid_acquire)); + if (rc) { + CAM_ERR(CAM_ISP, + "Cannot acquire ife csid ipp resource"); + goto err; + } + + csid_res->hw_res[i] = csid_acquire.node_res; + CAM_DBG(CAM_ISP, + "acquired csid(%s)=%d ipp rsrc successfully", + (i == 0) ? "left" : "right", + hw_intf->hw_idx); + + } + + if (i == CAM_IFE_CSID_HW_NUM_MAX) { + CAM_ERR(CAM_ISP, + "Can not acquire ife csid ipp resource"); + goto err; + } + + csid_res->parent = cid_res; + cid_res->child[cid_res->num_children++] = csid_res; + } + + CAM_DBG(CAM_ISP, "acquire res %d", csid_acquire.res_id); + + return 0; +err: + return rc; +} + +static enum cam_ife_pix_path_res_id + cam_ife_hw_mgr_get_ife_csid_rdi_res_type( + uint32_t out_port_type) +{ + enum cam_ife_pix_path_res_id path_id; + switch (out_port_type) { + case CAM_ISP_IFE_OUT_RES_RDI_0: + path_id = CAM_IFE_PIX_PATH_RES_RDI_0; + break; + case CAM_ISP_IFE_OUT_RES_RDI_1: + path_id = CAM_IFE_PIX_PATH_RES_RDI_1; + break; + case CAM_ISP_IFE_OUT_RES_RDI_2: + path_id = CAM_IFE_PIX_PATH_RES_RDI_2; + break; + case CAM_ISP_IFE_OUT_RES_RDI_3: + path_id = CAM_IFE_PIX_PATH_RES_RDI_3; + break; + default: + path_id = CAM_IFE_PIX_PATH_RES_MAX; + CAM_DBG(CAM_ISP, "maximum rdi output type exceeded"); + break; + } + + CAM_DBG(CAM_ISP, "out_port %d path_id %d", out_port_type, path_id); + + return path_id; +} + +static int cam_ife_hw_mgr_acquire_res_ife_csid_rdi( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port) +{ + int rc = -1; + int i, j; + + struct cam_ife_hw_mgr *ife_hw_mgr; + struct cam_ife_hw_mgr_res *csid_res; + struct cam_ife_hw_mgr_res *cid_res; + struct cam_hw_intf *hw_intf; + struct cam_isp_out_port_info *out_port; + uint32_t cid_res_id; + struct cam_csid_hw_reserve_resource_args csid_acquire; + + ife_hw_mgr = ife_ctx->hw_mgr; + + for (i = 0; i < in_port->num_out_res; i++) { + out_port = &in_port->data[i]; + if (!cam_ife_hw_mgr_is_rdi_res(out_port->res_type)) + continue; + + /* get cid resource */ + rc = cam_ife_mgr_acquire_cid_res(ife_ctx, + in_port, &cid_res_id, + cam_ife_hw_mgr_get_ife_csid_rdi_res_type( + out_port->res_type)); + if (rc) { + CAM_ERR(CAM_ISP, + "Acquire IFE CID resource Failed"); + goto err; + } + + rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, + &csid_res); + if (rc) { + CAM_ERR(CAM_ISP, "No more free hw mgr resource"); + goto err; + } + cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_csid, &csid_res); + + /* + * no need to check since we are doing one to one mapping + * between the csid rdi type and out port rdi type + */ + + memset(&csid_acquire, 0, sizeof(csid_acquire)); + csid_acquire.res_id = + cam_ife_hw_mgr_get_ife_csid_rdi_res_type( + out_port->res_type); + + csid_acquire.res_type = (enum cam_isp_resource_type)CAM_ISP_RESOURCE_PIX_PATH; + csid_acquire.cid = cid_res_id; + csid_acquire.in_port = in_port; + csid_acquire.out_port = out_port; + csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE; + + list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid, + list) { + if (cid_res->res_id != cid_res_id) + continue; + + for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) { + if (!cid_res->hw_res[j]) + continue; + + csid_acquire.node_res = NULL; + + hw_intf = ife_hw_mgr->csid_devices[ + cid_res->hw_res[j]->hw_intf->hw_idx]; + rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, + &csid_acquire, sizeof(csid_acquire)); + if (rc) { + CAM_DBG(CAM_ISP, + "CSID Path reserve failed hw=%d rc=%d", + hw_intf->hw_idx, rc); + continue; + } + + /* RDI does not need Dual ISP. Break */ + break; + } + + if (j == CAM_ISP_HW_SPLIT_MAX && + csid_acquire.node_res == NULL) { + CAM_ERR(CAM_ISP, + "acquire csid rdi rsrc failed, cid %d", + cid_res_id); + goto err; + } + + csid_res->res_type = (enum cam_ife_hw_mgr_res_type)CAM_ISP_RESOURCE_PIX_PATH; + csid_res->res_id = csid_acquire.res_id; + csid_res->is_dual_vfe = 0; + csid_res->hw_res[0] = csid_acquire.node_res; + csid_res->hw_res[1] = NULL; + CAM_DBG(CAM_ISP, "acquire res %d", + csid_acquire.res_id); + csid_res->parent = cid_res; + cid_res->child[cid_res->num_children++] = + csid_res; + + /* Done with cid_res_id. Break */ + break; + } + } + + return 0; +err: + return rc; +} + +static int cam_ife_hw_mgr_acquire_res_root( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port) +{ + int rc = -1; + + if (ife_ctx->res_list_ife_in.res_type == CAM_IFE_HW_MGR_RES_UNINIT) { + /* first acquire */ + ife_ctx->res_list_ife_in.res_type = CAM_IFE_HW_MGR_RES_ROOT; + ife_ctx->res_list_ife_in.res_id = in_port->res_type; + ife_ctx->res_list_ife_in.is_dual_vfe = in_port->usage_type; + } else if (ife_ctx->res_list_ife_in.res_id != in_port->res_type) { + CAM_ERR(CAM_ISP, "No Free resource for this context"); + goto err; + } else { + /* else do nothing */ + } + return 0; +err: + /* release resource in entry function */ + return rc; +} + +static int cam_ife_hw_mgr_preprocess_out_port( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port, + int *pixel_count, + int *rdi_count) +{ + int pixel_num = 0; + int rdi_num = 0; + uint32_t i; + struct cam_isp_out_port_info *out_port; + struct cam_ife_hw_mgr *ife_hw_mgr; + + ife_hw_mgr = ife_ctx->hw_mgr; + + for (i = 0; i < in_port->num_out_res; i++) { + out_port = &in_port->data[i]; + if (cam_ife_hw_mgr_is_rdi_res(out_port->res_type)) + rdi_num++; + else + pixel_num++; + } + + *pixel_count = pixel_num; + *rdi_count = rdi_num; + + return 0; +} + +static int cam_ife_mgr_acquire_hw_for_ctx( + struct cam_ife_hw_mgr_ctx *ife_ctx, + struct cam_isp_in_port_info *in_port, + uint32_t *num_pix_port, uint32_t *num_rdi_port) +{ + int rc = -1; + int is_dual_vfe = 0; + int pixel_count = 0; + int rdi_count = 0; + + is_dual_vfe = in_port->usage_type; + + /* get root node resource */ + rc = cam_ife_hw_mgr_acquire_res_root(ife_ctx, in_port); + if (rc) { + CAM_ERR(CAM_ISP, "Can not acquire csid rx resource"); + goto err; + } + + cam_ife_hw_mgr_preprocess_out_port(ife_ctx, in_port, + &pixel_count, &rdi_count); + + if (!pixel_count && !rdi_count) { + CAM_ERR(CAM_ISP, "No PIX or RDI resource"); + return -EINVAL; + } + + if (pixel_count) { + /* get ife csid IPP resrouce */ + rc = cam_ife_hw_mgr_acquire_res_ife_csid_ipp(ife_ctx, in_port); + if (rc) { + CAM_ERR(CAM_ISP, + "Acquire IFE CSID IPP resource Failed"); + goto err; + } + } + + if (rdi_count) { + /* get ife csid rdi resource */ + rc = cam_ife_hw_mgr_acquire_res_ife_csid_rdi(ife_ctx, in_port); + if (rc) { + CAM_ERR(CAM_ISP, + "Acquire IFE CSID RDI resource Failed"); + goto err; + } + } + + /* get ife src resource */ + rc = cam_ife_hw_mgr_acquire_res_ife_src(ife_ctx, in_port); + if (rc) { + CAM_ERR(CAM_ISP, "Acquire IFE SRC resource Failed"); + goto err; + } + + rc = cam_ife_hw_mgr_acquire_res_ife_out(ife_ctx, in_port); + if (rc) { + CAM_ERR(CAM_ISP, "Acquire IFE OUT resource Failed"); + goto err; + } + + *num_pix_port += pixel_count; + *num_rdi_port += rdi_count; + + return 0; +err: + /* release resource at the acquire entry funciton */ + return rc; +} + +void cam_ife_cam_cdm_callback(uint32_t handle, void *userdata, + enum cam_cdm_cb_status status, uint64_t cookie) +{ + struct cam_ife_hw_mgr_ctx *ctx = NULL; + + if (!userdata) { + CAM_ERR(CAM_ISP, "Invalid args"); + return; + } + + ctx = userdata; + + if (status == CAM_CDM_CB_STATUS_BL_SUCCESS) { + complete(&ctx->config_done_complete); + CAM_DBG(CAM_ISP, + "Called by CDM hdl=%x, udata=%pK, status=%d, cookie=%llu", + handle, userdata, status, cookie); + } else { + CAM_WARN(CAM_ISP, + "Called by CDM hdl=%x, udata=%pK, status=%d, cookie=%llu", + handle, userdata, status, cookie); + } +} + +/* entry function: acquire_hw */ +static int cam_ife_mgr_acquire_hw(void *hw_mgr_priv, + void *acquire_hw_args) +{ + struct cam_ife_hw_mgr *ife_hw_mgr = hw_mgr_priv; + struct cam_hw_acquire_args *acquire_args = acquire_hw_args; + int rc = -1; + int i, j; + struct cam_ife_hw_mgr_ctx *ife_ctx; + struct cam_isp_in_port_info *in_port = NULL; + struct cam_isp_resource *isp_resource = NULL; + struct cam_cdm_acquire_data cdm_acquire; + uint32_t num_pix_port_per_in = 0; + uint32_t num_rdi_port_per_in = 0; + uint32_t total_pix_port = 0; + uint32_t total_rdi_port = 0; + + CAM_DBG(CAM_ISP, "Enter..."); + + if (!acquire_args || acquire_args->num_acq <= 0) { + CAM_ERR(CAM_ISP, "Nothing to acquire. Seems like error"); + return -EINVAL; + } + + /* get the ife ctx */ + rc = cam_ife_hw_mgr_get_ctx(&ife_hw_mgr->free_ctx_list, &ife_ctx); + if (rc || !ife_ctx) { + CAM_ERR(CAM_ISP, "Get ife hw context failed"); + goto err; + } + + ife_ctx->common.cb_priv = acquire_args->context_data; + for (i = 0; i < CAM_ISP_HW_EVENT_MAX; i++) + ife_ctx->common.event_cb[i] = acquire_args->event_cb; + + ife_ctx->hw_mgr = ife_hw_mgr; + + + memcpy(cdm_acquire.identifier, "ife", sizeof("ife")); + cdm_acquire.cell_index = 0; + cdm_acquire.handle = 0; + cdm_acquire.userdata = ife_ctx; + cdm_acquire.base_array_cnt = CAM_IFE_HW_NUM_MAX; + for (i = 0, j = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + if (ife_hw_mgr->cdm_reg_map[i]) + cdm_acquire.base_array[j++] = + ife_hw_mgr->cdm_reg_map[i]; + } + cdm_acquire.base_array_cnt = j; + + + cdm_acquire.id = CAM_CDM_VIRTUAL; + cdm_acquire.cam_cdm_callback = cam_ife_cam_cdm_callback; + rc = cam_cdm_acquire(&cdm_acquire); + if (rc) { + CAM_ERR(CAM_ISP, "Failed to acquire the CDM HW"); + goto free_ctx; + } + + CAM_DBG(CAM_ISP, "Successfully acquired the CDM HW hdl=%x", + cdm_acquire.handle); + ife_ctx->cdm_handle = cdm_acquire.handle; + ife_ctx->cdm_ops = cdm_acquire.ops; + + isp_resource = (struct cam_isp_resource *)acquire_args->acquire_info; + + /* acquire HW resources */ + for (i = 0; i < acquire_args->num_acq; i++) { + if (isp_resource[i].resource_id != CAM_ISP_RES_ID_PORT) + continue; + + CAM_DBG(CAM_ISP, + "start copy from user handle %lld with len = %d", + isp_resource[i].res_hdl, + isp_resource[i].length); + + in_port = memdup_user( + u64_to_user_ptr(isp_resource[i].res_hdl), + isp_resource[i].length); + if (in_port > 0) { + rc = cam_ife_mgr_acquire_hw_for_ctx(ife_ctx, in_port, + &num_pix_port_per_in, &num_rdi_port_per_in); + total_pix_port += num_pix_port_per_in; + total_rdi_port += num_rdi_port_per_in; + + kfree(in_port); + if (rc) { + CAM_ERR(CAM_ISP, "can not acquire resource"); + goto free_res; + } + } else { + CAM_ERR(CAM_ISP, + "Copy from user failed with in_port = %pK", + in_port); + rc = -EFAULT; + goto free_res; + } + } + + /* Check whether context has only RDI resource */ + if (!total_pix_port) { + ife_ctx->is_rdi_only_context = 1; + CAM_DBG(CAM_ISP, "RDI only context"); + } + + /* Process base info */ + rc = cam_ife_mgr_process_base_info(ife_ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Process base info failed"); + goto free_res; + } + + acquire_args->ctxt_to_hw_map = ife_ctx; + ife_ctx->ctx_in_use = 1; + + cam_ife_hw_mgr_put_ctx(&ife_hw_mgr->used_ctx_list, &ife_ctx); + + CAM_DBG(CAM_ISP, "Exit...(success)"); + + return 0; +free_res: + cam_ife_hw_mgr_release_hw_for_ctx(ife_ctx); + cam_cdm_release(ife_ctx->cdm_handle); +free_ctx: + cam_ife_hw_mgr_put_ctx(&ife_hw_mgr->free_ctx_list, &ife_ctx); +err: + CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc); + return rc; +} + +static int cam_isp_blob_bw_update( + struct cam_isp_bw_config *bw_config, + struct cam_ife_hw_mgr_ctx *ctx) +{ + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_vfe_bw_update_args bw_upd_args; + uint64_t cam_bw_bps = 0; + uint64_t ext_bw_bps = 0; + int rc = -EINVAL; + uint32_t i; + + CAM_DBG(CAM_ISP, + "usage=%u left cam_bw_bps=%llu ext_bw_bps=%llu\n" + "right cam_bw_bps=%llu ext_bw_bps=%llu", + bw_config->usage_type, + bw_config->left_pix_vote.cam_bw_bps, + bw_config->left_pix_vote.ext_bw_bps, + bw_config->right_pix_vote.cam_bw_bps, + bw_config->right_pix_vote.ext_bw_bps); + + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) + if (i == CAM_ISP_HW_SPLIT_LEFT) { + cam_bw_bps = + bw_config->left_pix_vote.cam_bw_bps; + ext_bw_bps = + bw_config->left_pix_vote.ext_bw_bps; + } else { + cam_bw_bps = + bw_config->right_pix_vote.cam_bw_bps; + ext_bw_bps = + bw_config->right_pix_vote.ext_bw_bps; + } + else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0) + && (hw_mgr_res->res_id <= + CAM_ISP_HW_VFE_IN_RDI3)) { + uint32_t idx = hw_mgr_res->res_id - + CAM_ISP_HW_VFE_IN_RDI0; + if (idx >= bw_config->num_rdi) + continue; + + cam_bw_bps = + bw_config->rdi_vote[idx].cam_bw_bps; + ext_bw_bps = + bw_config->rdi_vote[idx].ext_bw_bps; + } else + if (hw_mgr_res->hw_res[i]) { + CAM_ERR(CAM_ISP, "Invalid res_id %u", + hw_mgr_res->res_id); + rc = -EINVAL; + return rc; + } + + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf && hw_intf->hw_ops.process_cmd) { + bw_upd_args.node_res = + hw_mgr_res->hw_res[i]; + + bw_upd_args.camnoc_bw_bytes = cam_bw_bps; + bw_upd_args.external_bw_bytes = ext_bw_bps; + + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_ISP_HW_CMD_BW_UPDATE, + &bw_upd_args, + sizeof(struct cam_vfe_bw_update_args)); + if (rc) + CAM_ERR(CAM_ISP, "BW Update failed"); + } else + CAM_WARN(CAM_ISP, "NULL hw_intf!"); + } + } + + return rc; +} + +/* entry function: config_hw */ +static int cam_ife_mgr_config_hw(void *hw_mgr_priv, + void *config_hw_args) +{ + int rc = -1, i; + struct cam_hw_config_args *cfg; + struct cam_hw_update_entry *cmd; + struct cam_cdm_bl_request *cdm_cmd; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_isp_prepare_hw_update_data *hw_update_data; + + CAM_DBG(CAM_ISP, "Enter"); + if (!hw_mgr_priv || !config_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + cfg = config_hw_args; + ctx = (struct cam_ife_hw_mgr_ctx *)cfg->ctxt_to_hw_map; + if (!ctx) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + if (!ctx->ctx_in_use || !ctx->cdm_cmd) { + CAM_ERR(CAM_ISP, "Invalid context parameters"); + return -EPERM; + } + if (atomic_read(&ctx->overflow_pending)) + return -EINVAL; + + hw_update_data = (struct cam_isp_prepare_hw_update_data *) cfg->priv; + + for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + if (hw_update_data->bw_config_valid[i] == true) { + rc = cam_isp_blob_bw_update( + (struct cam_isp_bw_config *) + &hw_update_data->bw_config[i], ctx); + if (rc) + CAM_ERR(CAM_ISP, "Bandwidth Update Failed"); + } + } + + CAM_DBG(CAM_ISP, "Enter ctx id:%d num_hw_upd_entries %d", + ctx->ctx_index, cfg->num_hw_update_entries); + + if (cfg->num_hw_update_entries > 0) { + cdm_cmd = ctx->cdm_cmd; + cdm_cmd->cmd_arrary_count = cfg->num_hw_update_entries; + cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE; + cdm_cmd->flag = true; + cdm_cmd->userdata = ctx; + cdm_cmd->cookie = cfg->request_id; + + for (i = 0 ; i <= cfg->num_hw_update_entries; i++) { + cmd = (cfg->hw_update_entries + i); + cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle; + cdm_cmd->cmd[i].offset = cmd->offset; + cdm_cmd->cmd[i].len = cmd->len; + } + + if (cfg->init_packet) + init_completion(&ctx->config_done_complete); + CAM_DBG(CAM_ISP, "Submit to CDM"); + rc = cam_cdm_submit_bls(ctx->cdm_handle, cdm_cmd); + if (rc) { + CAM_ERR(CAM_ISP, "Failed to apply the configs"); + return rc; + } + + if (cfg->init_packet) { + rc = wait_for_completion_timeout( + &ctx->config_done_complete, + msecs_to_jiffies(30)); + if (rc <= 0) { + CAM_ERR(CAM_ISP, + "config done completion timeout for req_id=%llu rc = %d", + cfg->request_id, rc); + if (rc == 0) + rc = -ETIMEDOUT; + } else { + rc = 0; + CAM_DBG(CAM_ISP, + "config done Success for req_id=%llu", + cfg->request_id); + } + + rc = 0; + } + } else { + CAM_ERR(CAM_ISP, "No commands to config"); + } + CAM_DBG(CAM_ISP, "Exit"); + + return rc; +} + +static int cam_ife_mgr_stop_hw_in_overflow(void *stop_hw_args) +{ + int rc = 0; + struct cam_hw_stop_args *stop_args = stop_hw_args; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_ife_hw_mgr_ctx *ctx; + uint32_t i, master_base_idx = 0; + + if (!stop_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + ctx = (struct cam_ife_hw_mgr_ctx *)stop_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, "Enter...ctx id:%d", + ctx->ctx_index); + + if (!ctx->num_base) { + CAM_ERR(CAM_ISP, "Number of bases are zero"); + return -EINVAL; + } + + /* get master base index first */ + for (i = 0; i < ctx->num_base; i++) { + if (ctx->base[i].split_id == CAM_ISP_HW_SPLIT_LEFT) { + master_base_idx = ctx->base[i].idx; + break; + } + } + + if (i == ctx->num_base) + master_base_idx = ctx->base[0].idx; + + + /* stop the master CIDs first */ + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid, + master_base_idx, CAM_CSID_HALT_IMMEDIATELY); + + /* stop rest of the CIDs */ + for (i = 0; i < ctx->num_base; i++) { + if (i == master_base_idx) + continue; + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid, + ctx->base[i].idx, CAM_CSID_HALT_IMMEDIATELY); + } + + /* stop the master CSID path first */ + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid, + master_base_idx, CAM_CSID_HALT_IMMEDIATELY); + + /* Stop rest of the CSID paths */ + for (i = 0; i < ctx->num_base; i++) { + if (i == master_base_idx) + continue; + + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid, + ctx->base[i].idx, CAM_CSID_HALT_IMMEDIATELY); + } + + /* IFE mux in resources */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + cam_ife_hw_mgr_stop_hw_res(hw_mgr_res); + } + + /* IFE out resources */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) + cam_ife_hw_mgr_stop_hw_res(&ctx->res_list_ife_out[i]); + + + /* Stop tasklet for context */ + cam_tasklet_stop(ctx->common.tasklet_info); + CAM_DBG(CAM_ISP, "Exit...ctx id:%d rc :%d", + ctx->ctx_index, rc); + + return rc; +} + +static int cam_ife_mgr_bw_control(struct cam_ife_hw_mgr_ctx *ctx, + enum cam_vfe_bw_control_action action) +{ + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_vfe_bw_control_args bw_ctrl_args; + int rc = -EINVAL; + uint32_t i; + + CAM_DBG(CAM_ISP, "Enter...ctx id:%d", ctx->ctx_index); + + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf && hw_intf->hw_ops.process_cmd) { + bw_ctrl_args.node_res = + hw_mgr_res->hw_res[i]; + bw_ctrl_args.action = action; + + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_ISP_HW_CMD_BW_CONTROL, + &bw_ctrl_args, + sizeof(struct cam_vfe_bw_control_args)); + if (rc) + CAM_ERR(CAM_ISP, "BW Update failed"); + } else + CAM_WARN(CAM_ISP, "NULL hw_intf!"); + } + } + + return rc; +} + +static int cam_ife_mgr_pause_hw(struct cam_ife_hw_mgr_ctx *ctx) +{ + return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_EXCLUDE); +} + +/* entry function: stop_hw */ +static int cam_ife_mgr_stop_hw(void *hw_mgr_priv, void *stop_hw_args) +{ + int rc = 0; + struct cam_hw_stop_args *stop_args = stop_hw_args; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_ife_hw_mgr_ctx *ctx; + enum cam_ife_csid_halt_cmd csid_halt_type; + uint32_t i, master_base_idx = 0; + + if (!hw_mgr_priv || !stop_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + ctx = (struct cam_ife_hw_mgr_ctx *)stop_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, " Enter...ctx id:%d", ctx->ctx_index); + + /* Set the csid halt command */ + if (!stop_args->args) + csid_halt_type = CAM_CSID_HALT_IMMEDIATELY; + else + csid_halt_type = CAM_CSID_HALT_AT_FRAME_BOUNDARY; + + /* Note:stop resource will remove the irq mask from the hardware */ + + if (!ctx->num_base) { + CAM_ERR(CAM_ISP, "number of bases are zero"); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "Halting CSIDs"); + + if (cam_cdm_stream_off(ctx->cdm_handle)) + CAM_ERR(CAM_ISP, "CDM stream off failed %d", + ctx->cdm_handle); + + CAM_DBG(CAM_ISP, "Going to stop IFE Mux"); + + /* IFE mux in resources */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + cam_ife_hw_mgr_stop_hw_res(hw_mgr_res); + } + + CAM_DBG(CAM_ISP, "Going to stop IFE Out"); + + /* IFE out resources */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) + cam_ife_hw_mgr_stop_hw_res(&ctx->res_list_ife_out[i]); + /* get master base index first */ + for (i = 0; i < ctx->num_base; i++) { + if (ctx->base[i].split_id == CAM_ISP_HW_SPLIT_LEFT) { + master_base_idx = ctx->base[i].idx; + break; + } + } + + cam_tasklet_stop(ctx->common.tasklet_info); + /* + * If Context does not have PIX resources and has only RDI resource + * then take the first base index. + */ + if (i == ctx->num_base) + master_base_idx = ctx->base[0].idx; + + /* Stop the master CIDs first */ + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid, + master_base_idx, csid_halt_type); + + /* stop rest of the CIDs */ + for (i = 0; i < ctx->num_base; i++) { + if (i == master_base_idx) + continue; + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid, + ctx->base[i].idx, csid_halt_type); + } + + /* Stop the master CSID path first */ + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid, + master_base_idx, csid_halt_type); + + /* stop rest of the CSID paths */ + for (i = 0; i < ctx->num_base; i++) { + if (i == master_base_idx) + continue; + + cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid, + ctx->base[i].idx, csid_halt_type); + } + + + /* Deinit IFE CID */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) { + CAM_DBG(CAM_ISP, "%s: Going to DeInit IFE CID\n", __func__); + cam_ife_hw_mgr_deinit_hw_res(hw_mgr_res); + } + + /* Deinit IFE CSID */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) { + CAM_DBG(CAM_ISP, "%s: Going to DeInit IFE CSID\n", __func__); + cam_ife_hw_mgr_deinit_hw_res(hw_mgr_res); + } + + /* Deint IFE MUX(SRC) */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + cam_ife_hw_mgr_deinit_hw_res(hw_mgr_res); + } + + /* Deinit IFE OUT */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) + cam_ife_hw_mgr_deinit_hw_res(&ctx->res_list_ife_out[i]); + + CAM_DBG(CAM_ISP, "Exit...ctx id:%d rc :%d", ctx->ctx_index, rc); + + mutex_lock(&g_ife_hw_mgr.ctx_mutex); + if (!atomic_dec_return(&g_ife_hw_mgr.active_ctx_cnt)) { + rc = cam_ife_notify_safe_lut_scm(CAM_IFE_SAFE_DISABLE); + if (rc) { + CAM_ERR(CAM_ISP, + "SAFE SCM call failed:Check TZ/HYP dependency"); + rc = 0; + } + } + mutex_unlock(&g_ife_hw_mgr.ctx_mutex); + + return rc; +} + +static int cam_ife_mgr_reset_vfe_hw(struct cam_ife_hw_mgr *hw_mgr, + uint32_t hw_idx) +{ + uint32_t i = 0; + struct cam_hw_intf *vfe_hw_intf; + uint32_t vfe_reset_type; + + if (!hw_mgr) { + CAM_DBG(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + /* Reset VFE HW*/ + vfe_reset_type = CAM_VFE_HW_RESET_HW; + + for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) { + if (hw_idx != hw_mgr->ife_devices[i]->hw_idx) + continue; + CAM_DBG(CAM_ISP, "VFE (id = %d) reset", hw_idx); + vfe_hw_intf = hw_mgr->ife_devices[i]; + vfe_hw_intf->hw_ops.reset(vfe_hw_intf->hw_priv, + &vfe_reset_type, sizeof(vfe_reset_type)); + break; + } + + CAM_DBG(CAM_ISP, "Exit Successfully"); + return 0; +} + +static int cam_ife_mgr_restart_hw(void *start_hw_args) +{ + int rc = -1; + struct cam_hw_start_args *start_args = start_hw_args; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_ife_hw_mgr_res *hw_mgr_res; + uint32_t i; + + if (!start_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + ctx = (struct cam_ife_hw_mgr_ctx *)start_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, "START IFE OUT ... in ctx id:%d", ctx->ctx_index); + + cam_tasklet_start(ctx->common.tasklet_info); + + /* start the IFE out devices */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) { + rc = cam_ife_hw_mgr_start_hw_res( + &ctx->res_list_ife_out[i], ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)", i); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START IFE SRC ... in ctx id:%d", ctx->ctx_index); + /* Start the IFE mux in devices */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START CSID HW ... in ctx id:%d", ctx->ctx_index); + /* Start the IFE CSID HW devices */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) { + rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START CID SRC ... in ctx id:%d", ctx->ctx_index); + /* Start IFE root node: do nothing */ + CAM_DBG(CAM_ISP, "Exit...(success)"); + return 0; + +err: + cam_ife_mgr_stop_hw_in_overflow(start_hw_args); + CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc); + return rc; +} + +static int cam_ife_mgr_start_hw(void *hw_mgr_priv, void *start_hw_args) +{ + int rc = -1; + struct cam_hw_config_args *start_args = start_hw_args; + struct cam_hw_stop_args stop_args; + struct cam_isp_stop_hw_method stop_hw_method; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_ife_hw_mgr_res *hw_mgr_res; + uint32_t i; + + if (!hw_mgr_priv || !start_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + ctx = (struct cam_ife_hw_mgr_ctx *)start_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, "Enter... ctx id:%d", + ctx->ctx_index); + + /* update Bandwidth should be done at the hw layer */ + + cam_tasklet_start(ctx->common.tasklet_info); + + /* set current csid debug information to CSID HW */ + for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) { + if (g_ife_hw_mgr.csid_devices[i]) + rc = g_ife_hw_mgr.csid_devices[i]->hw_ops.process_cmd( + g_ife_hw_mgr.csid_devices[i]->hw_priv, + CAM_IFE_CSID_SET_CSID_DEBUG, + &g_ife_hw_mgr.debug_cfg.csid_debug, + sizeof(g_ife_hw_mgr.debug_cfg.csid_debug)); + } + + /* INIT IFE Root: do nothing */ + + CAM_DBG(CAM_ISP, "INIT IFE CID ... in ctx id:%d", + ctx->ctx_index); + /* INIT IFE CID */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) { + rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res); + if (rc) { + CAM_ERR(CAM_ISP, "Can not INIT IFE CID(id :%d)", + hw_mgr_res->res_id); + goto err; + } + } + + + CAM_DBG(CAM_ISP, "INIT IFE csid ... in ctx id:%d", + ctx->ctx_index); + + /* INIT IFE csid */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) { + rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res); + if (rc) { + CAM_ERR(CAM_ISP, "Can not INIT IFE CSID(id :%d)", + hw_mgr_res->res_id); + goto err; + } + } + + /* INIT IFE SRC */ + CAM_DBG(CAM_ISP, "INIT IFE SRC in ctx id:%d", + ctx->ctx_index); + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res); + if (rc) { + CAM_ERR(CAM_ISP, "Can not INIT IFE SRC (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + /* INIT IFE OUT */ + CAM_DBG(CAM_ISP, "INIT IFE OUT RESOURCES in ctx id:%d", + ctx->ctx_index); + + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) { + rc = cam_ife_hw_mgr_init_hw_res(&ctx->res_list_ife_out[i]); + if (rc) { + CAM_ERR(CAM_ISP, "Can not INIT IFE OUT (%d)", + ctx->res_list_ife_out[i].res_id); + goto err; + } + } + + mutex_lock(&g_ife_hw_mgr.ctx_mutex); + if (!atomic_fetch_inc(&g_ife_hw_mgr.active_ctx_cnt)) { + rc = cam_ife_notify_safe_lut_scm(CAM_IFE_SAFE_ENABLE); + if (rc) { + CAM_ERR(CAM_ISP, + "SAFE SCM call failed:Check TZ/HYP dependency"); + rc = -1; + } + } + mutex_unlock(&g_ife_hw_mgr.ctx_mutex); + + CAM_DBG(CAM_ISP, "start cdm interface"); + rc = cam_cdm_stream_on(ctx->cdm_handle); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start cdm (%d)", + ctx->cdm_handle); + goto err; + } + + /* Apply initial configuration */ + CAM_DBG(CAM_ISP, "Config HW"); + rc = cam_ife_mgr_config_hw(hw_mgr_priv, start_hw_args); + if (rc) { + CAM_ERR(CAM_ISP, "Config HW failed"); + goto err; + } + + CAM_DBG(CAM_ISP, "START IFE OUT ... in ctx id:%d", + ctx->ctx_index); + /* start the IFE out devices */ + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) { + rc = cam_ife_hw_mgr_start_hw_res( + &ctx->res_list_ife_out[i], ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)", + i); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START IFE SRC ... in ctx id:%d", + ctx->ctx_index); + /* Start the IFE mux in devices */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START CSID HW ... in ctx id:%d", + ctx->ctx_index); + /* Start the IFE CSID HW devices */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) { + rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + CAM_DBG(CAM_ISP, "START CID SRC ... in ctx id:%d", + ctx->ctx_index); + /* Start the IFE CID HW devices */ + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) { + rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx); + if (rc) { + CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)", + hw_mgr_res->res_id); + goto err; + } + } + + /* Start IFE root node: do nothing */ + CAM_DBG(CAM_ISP, "Exit...(success)"); + return 0; +err: + stop_hw_method.hw_stop_cmd = (enum cam_isp_hw_stop_cmd)CAM_CSID_HALT_IMMEDIATELY; + stop_args.ctxt_to_hw_map = start_args->ctxt_to_hw_map; + stop_args.args = (void *)(&stop_hw_method); + cam_ife_mgr_stop_hw(hw_mgr_priv, &stop_args); + CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc); + return rc; +} + +static int cam_ife_mgr_read(void *hw_mgr_priv, void *read_args) +{ + return -EPERM; +} + +static int cam_ife_mgr_write(void *hw_mgr_priv, void *write_args) +{ + return -EPERM; +} + +static int cam_ife_mgr_release_hw(void *hw_mgr_priv, + void *release_hw_args) +{ + int rc = 0; + struct cam_hw_release_args *release_args = release_hw_args; + struct cam_ife_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_ife_hw_mgr_ctx *ctx; + uint32_t i; + + if (!hw_mgr_priv || !release_hw_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + ctx = (struct cam_ife_hw_mgr_ctx *)release_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, "Enter...ctx id:%d", + ctx->ctx_index); + + /* we should called the stop hw before this already */ + cam_ife_hw_mgr_release_hw_for_ctx(ctx); + + /* reset base info */ + ctx->num_base = 0; + memset(ctx->base, 0, sizeof(ctx->base)); + + /* release cdm handle */ + cam_cdm_release(ctx->cdm_handle); + + /* clean context */ + list_del_init(&ctx->list); + ctx->ctx_in_use = 0; + ctx->is_rdi_only_context = 0; + ctx->cdm_handle = 0; + ctx->cdm_ops = NULL; + atomic_set(&ctx->overflow_pending, 0); + for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + ctx->sof_cnt[i] = 0; + ctx->eof_cnt[i] = 0; + ctx->epoch_cnt[i] = 0; + } + CAM_DBG(CAM_ISP, "Exit...ctx id:%d", + ctx->ctx_index); + cam_ife_hw_mgr_put_ctx(&hw_mgr->free_ctx_list, &ctx); + return rc; +} + +static int cam_isp_blob_hfr_update( + uint32_t blob_type, + struct cam_isp_generic_blob_info *blob_info, + struct cam_isp_resource_hfr_config *hfr_config, + struct cam_hw_prepare_update_args *prepare) +{ + struct cam_isp_port_hfr_config *port_hfr_config; + struct cam_kmd_buf_info *kmd_buf_info; + struct cam_ife_hw_mgr_ctx *ctx = NULL; + struct cam_ife_hw_mgr_res *hw_mgr_res; + uint32_t res_id_out, i; + uint32_t total_used_bytes = 0; + uint32_t kmd_buf_remain_size; + uint32_t *cmd_buf_addr; + uint32_t bytes_used = 0; + int num_ent, rc = 0; + + ctx = prepare->ctxt_to_hw_map; + CAM_DBG(CAM_ISP, "num_ports= %d", + hfr_config->num_ports); + + /* Max one hw entries required for hfr config update */ + if (prepare->num_hw_update_entries + 1 >= + prepare->max_hw_update_entries) { + CAM_ERR(CAM_ISP, "Insufficient HW entries :%d %d", + prepare->num_hw_update_entries, + prepare->max_hw_update_entries); + return -EINVAL; + } + + kmd_buf_info = blob_info->kmd_buf_info; + for (i = 0; i < hfr_config->num_ports; i++) { + port_hfr_config = &hfr_config->port_hfr_config[i]; + res_id_out = port_hfr_config->resource_type & 0xFF; + + CAM_DBG(CAM_ISP, "hfr config idx %d, type=%d", i, + res_id_out); + + if (res_id_out >= CAM_IFE_HW_OUT_RES_MAX) { + CAM_ERR(CAM_ISP, "invalid out restype:%x", + port_hfr_config->resource_type); + return -EINVAL; + } + + if ((kmd_buf_info->used_bytes + + total_used_bytes) < kmd_buf_info->size) { + kmd_buf_remain_size = kmd_buf_info->size - + (kmd_buf_info->used_bytes + + total_used_bytes); + } else { + CAM_ERR(CAM_ISP, + "no free kmd memory for base %d", + blob_info->base_info->idx); + rc = -ENOMEM; + return rc; + } + + cmd_buf_addr = kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes/4 + + total_used_bytes/4; + hw_mgr_res = &ctx->res_list_ife_out[res_id_out]; + + rc = cam_isp_add_cmd_buf_update( + hw_mgr_res, blob_type, + blob_type_hw_cmd_map[blob_type], + blob_info->base_info->idx, + (void *)cmd_buf_addr, + kmd_buf_remain_size, + (void *)port_hfr_config, + &bytes_used); + if (rc < 0) { + CAM_ERR(CAM_ISP, + "Failed cmd_update, base_idx=%d, rc=%d", + blob_info->base_info->idx, bytes_used); + return rc; + } + + total_used_bytes += bytes_used; + } + + if (total_used_bytes) { + /* Update the HW entries */ + num_ent = prepare->num_hw_update_entries; + prepare->hw_update_entries[num_ent].handle = + kmd_buf_info->handle; + prepare->hw_update_entries[num_ent].len = total_used_bytes; + prepare->hw_update_entries[num_ent].offset = + kmd_buf_info->offset; + num_ent++; + + kmd_buf_info->used_bytes += total_used_bytes; + kmd_buf_info->offset += total_used_bytes; + prepare->num_hw_update_entries = num_ent; + } + + return rc; +} + +static int cam_isp_blob_clock_update( + uint32_t blob_type, + struct cam_isp_generic_blob_info *blob_info, + struct cam_isp_clock_config *clock_config, + struct cam_hw_prepare_update_args *prepare) +{ + struct cam_ife_hw_mgr_ctx *ctx = NULL; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_vfe_clock_update_args clock_upd_args; + uint64_t clk_rate = 0; + int rc = -EINVAL; + uint32_t i; + uint32_t j; + + ctx = prepare->ctxt_to_hw_map; + + CAM_DBG(CAM_ISP, + "usage=%u left_clk= %lu right_clk=%lu", + clock_config->usage_type, + clock_config->left_pix_hz, + clock_config->right_pix_hz); + + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + clk_rate = 0; + if (!hw_mgr_res->hw_res[i]) + continue; + + if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) + if (i == CAM_ISP_HW_SPLIT_LEFT) + clk_rate = + clock_config->left_pix_hz; + else + clk_rate = + clock_config->right_pix_hz; + else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0) + && (hw_mgr_res->res_id <= + CAM_ISP_HW_VFE_IN_RDI3)) + for (j = 0; j < clock_config->num_rdi; j++) + clk_rate = max(clock_config->rdi_hz[j], + clk_rate); + else + if (hw_mgr_res->hw_res[i]) { + CAM_ERR(CAM_ISP, "Invalid res_id %u", + hw_mgr_res->res_id); + rc = -EINVAL; + return rc; + } + + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf && hw_intf->hw_ops.process_cmd) { + clock_upd_args.node_res = + hw_mgr_res->hw_res[i]; + CAM_DBG(CAM_ISP, + "res_id=%u i= %d clk=%llu\n", + hw_mgr_res->res_id, i, clk_rate); + + clock_upd_args.clk_rate = clk_rate; + + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_ISP_HW_CMD_CLOCK_UPDATE, + &clock_upd_args, + sizeof( + struct cam_vfe_clock_update_args)); + if (rc) + CAM_ERR(CAM_ISP, "Clock Update failed"); + } else + CAM_WARN(CAM_ISP, "NULL hw_intf!"); + } + } + + return rc; +} + +static int cam_isp_packet_generic_blob_handler(void *user_data, + uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data) +{ + int rc = 0; + struct cam_isp_generic_blob_info *blob_info = user_data; + struct cam_hw_prepare_update_args *prepare = NULL; + + if (!blob_data || (blob_size == 0) || !blob_info) { + CAM_ERR(CAM_ISP, "Invalid info blob %pK %d prepare %pK", + blob_data, blob_size, prepare); + return -EINVAL; + } + + if (blob_type >= CAM_ISP_GENERIC_BLOB_TYPE_MAX) { + CAM_ERR(CAM_ISP, "Invalid Blob Type %d Max %d", blob_type, + CAM_ISP_GENERIC_BLOB_TYPE_MAX); + return -EINVAL; + } + + prepare = blob_info->prepare; + if (!prepare) { + CAM_ERR(CAM_ISP, "Failed. prepare is NULL, blob_type %d", + blob_type); + return -EINVAL; + } + + switch (blob_type) { + case CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG: { + struct cam_isp_resource_hfr_config *hfr_config = + (struct cam_isp_resource_hfr_config *)blob_data; + + rc = cam_isp_blob_hfr_update(blob_type, blob_info, + hfr_config, prepare); + if (rc) + CAM_ERR(CAM_ISP, "HFR Update Failed"); + } + break; + case CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG: { + struct cam_isp_clock_config *clock_config = + (struct cam_isp_clock_config *)blob_data; + + rc = cam_isp_blob_clock_update(blob_type, blob_info, + clock_config, prepare); + if (rc) + CAM_ERR(CAM_ISP, "Clock Update Failed"); + } + break; + case CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG: { + struct cam_isp_bw_config *bw_config = + (struct cam_isp_bw_config *)blob_data; + struct cam_isp_prepare_hw_update_data *prepare_hw_data; + + if (!prepare || !prepare->priv || + (bw_config->usage_type >= CAM_IFE_HW_NUM_MAX)) { + CAM_ERR(CAM_ISP, "Invalid inputs"); + rc = -EINVAL; + break; + } + + prepare_hw_data = (struct cam_isp_prepare_hw_update_data *) + prepare->priv; + + memcpy(&prepare_hw_data->bw_config[bw_config->usage_type], + bw_config, sizeof(prepare_hw_data->bw_config[0])); + prepare_hw_data->bw_config_valid[bw_config->usage_type] = true; + + } + break; + default: + CAM_WARN(CAM_ISP, "Invalid blob type %d", blob_type); + break; + } + + return rc; +} + +static int cam_ife_mgr_prepare_hw_update(void *hw_mgr_priv, + void *prepare_hw_update_args) +{ + int rc = 0; + struct cam_hw_prepare_update_args *prepare = + (struct cam_hw_prepare_update_args *) prepare_hw_update_args; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_ife_hw_mgr *hw_mgr; + struct cam_kmd_buf_info kmd_buf; + uint32_t i; + bool fill_fence = true; + struct cam_isp_prepare_hw_update_data *prepare_hw_data; + + if (!hw_mgr_priv || !prepare_hw_update_args) { + CAM_ERR(CAM_ISP, "Invalid args"); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "enter"); + + prepare_hw_data = (struct cam_isp_prepare_hw_update_data *) + prepare->priv; + + ctx = (struct cam_ife_hw_mgr_ctx *) prepare->ctxt_to_hw_map; + hw_mgr = (struct cam_ife_hw_mgr *)hw_mgr_priv; + + rc = cam_packet_util_validate_packet(prepare->packet); + if (rc) + return rc; + + /* Pre parse the packet*/ + rc = cam_packet_util_get_kmd_buffer(prepare->packet, &kmd_buf); + if (rc) + return rc; + + rc = cam_packet_util_process_patches(prepare->packet, + hw_mgr->mgr_common.cmd_iommu_hdl, + hw_mgr->mgr_common.cmd_iommu_hdl_secure); + if (rc) { + CAM_ERR(CAM_ISP, "Patch ISP packet failed."); + return rc; + } + + prepare->num_hw_update_entries = 0; + prepare->num_in_map_entries = 0; + prepare->num_out_map_entries = 0; + + memset(&prepare_hw_data->bw_config[0], 0x0, + sizeof(prepare_hw_data->bw_config[0]) * + CAM_IFE_HW_NUM_MAX); + memset(&prepare_hw_data->bw_config_valid[0], 0x0, + sizeof(prepare_hw_data->bw_config_valid[0]) * + CAM_IFE_HW_NUM_MAX); + + for (i = 0; i < ctx->num_base; i++) { + CAM_DBG(CAM_ISP, "process cmd buffer for device %d", i); + + /* Add change base */ + rc = cam_isp_add_change_base(prepare, &ctx->res_list_ife_src, + ctx->base[i].idx, &kmd_buf); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in change base i=%d, idx=%d, rc=%d", + i, ctx->base[i].idx, rc); + goto end; + } + + + /* get command buffers */ + if (ctx->base[i].split_id != CAM_ISP_HW_SPLIT_MAX) { + rc = cam_isp_add_command_buffers(prepare, &kmd_buf, + &ctx->base[i], + cam_isp_packet_generic_blob_handler, + ctx->res_list_ife_out, CAM_IFE_HW_OUT_RES_MAX); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in add cmdbuf, i=%d, split_id=%d, rc=%d", + i, ctx->base[i].split_id, rc); + goto end; + } + } + + /* get IO buffers */ + rc = cam_isp_add_io_buffers(hw_mgr->mgr_common.img_iommu_hdl, + hw_mgr->mgr_common.img_iommu_hdl_secure, + prepare, ctx->base[i].idx, + &kmd_buf, ctx->res_list_ife_out, + CAM_IFE_HW_OUT_RES_MAX, fill_fence); + + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in io buffers, i=%d, rc=%d", + i, rc); + goto end; + } + + /* fence map table entries need to fill only once in the loop */ + if (fill_fence) + fill_fence = false; + } + + /* + * reg update will be done later for the initial configure. + * need to plus one to the op_code and only take the lower + * bits to get the type of operation since UMD definition + * of op_code has some difference from KMD. + */ + if (((prepare->packet->header.op_code + 1) & 0xF) == + CAM_ISP_PACKET_INIT_DEV) { + prepare_hw_data->packet_opcode_type = CAM_ISP_PACKET_INIT_DEV; + goto end; + } else + prepare_hw_data->packet_opcode_type = CAM_ISP_PACKET_UPDATE_DEV; + + /* add reg update commands */ + for (i = 0; i < ctx->num_base; i++) { + /* Add change base */ + rc = cam_isp_add_change_base(prepare, &ctx->res_list_ife_src, + ctx->base[i].idx, &kmd_buf); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in change base adding reg_update cmd i=%d, idx=%d, rc=%d", + i, ctx->base[i].idx, rc); + goto end; + } + + /*Add reg update */ + rc = cam_isp_add_reg_update(prepare, &ctx->res_list_ife_src, + ctx->base[i].idx, &kmd_buf); + if (rc) { + CAM_ERR(CAM_ISP, + "Add Reg_update cmd Failed i=%d, idx=%d, rc=%d", + i, ctx->base[i].idx, rc); + goto end; + } + } + +end: + return rc; +} + +static int cam_ife_mgr_resume_hw(struct cam_ife_hw_mgr_ctx *ctx) +{ + return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_INCLUDE); +} + +static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args) +{ + int rc = 0; + struct cam_isp_hw_cmd_args *hw_cmd_args = cmd_args; + struct cam_ife_hw_mgr_ctx *ctx; + + if (!hw_mgr_priv || !cmd_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + ctx = (struct cam_ife_hw_mgr_ctx *)hw_cmd_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Fatal: Invalid context is used"); + return -EPERM; + } + + switch (hw_cmd_args->cmd_type) { + case CAM_ISP_HW_MGR_CMD_IS_RDI_ONLY_CONTEXT: + if (ctx->is_rdi_only_context) + hw_cmd_args->u.is_rdi_only_context = 1; + else + hw_cmd_args->u.is_rdi_only_context = 0; + + break; + case CAM_ISP_HW_MGR_CMD_PAUSE_HW: + cam_ife_mgr_pause_hw(ctx); + break; + case CAM_ISP_HW_MGR_CMD_RESUME_HW: + cam_ife_mgr_resume_hw(ctx); + break; + default: + CAM_ERR(CAM_ISP, "Invalid HW mgr command:0x%x", + hw_cmd_args->cmd_type); + rc = -EINVAL; + break; + } + + return rc; +} + +static int cam_ife_mgr_cmd_get_sof_timestamp( + struct cam_ife_hw_mgr_ctx *ife_ctx, + uint64_t *time_stamp) +{ + int rc = -EINVAL; + uint32_t i; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_csid_get_time_stamp_args csid_get_time; + + list_for_each_entry(hw_mgr_res, &ife_ctx->res_list_ife_csid, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i] || + (i == CAM_ISP_HW_SPLIT_RIGHT)) + continue; + /* + * Get the SOF time stamp from left resource only. + * Left resource is master for dual vfe case and + * Rdi only context case left resource only hold + * the RDI resource + */ + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf->hw_ops.process_cmd) { + csid_get_time.node_res = + hw_mgr_res->hw_res[i]; + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_IFE_CSID_CMD_GET_TIME_STAMP, + &csid_get_time, + sizeof( + struct cam_csid_get_time_stamp_args)); + if (!rc) + *time_stamp = + csid_get_time.time_stamp_val; + /* + * Single VFE case, Get the time stamp from available + * one csid hw in the context + * Dual VFE case, get the time stamp from master(left) + * would be sufficient + */ + goto end; + } + } + } +end: + if (rc) + CAM_ERR(CAM_ISP, "Getting sof time stamp failed"); + + return rc; +} + +static int cam_ife_mgr_process_recovery_cb(void *priv, void *data) +{ + int32_t rc = 0; + struct cam_hw_event_recovery_data *recovery_data = data; + struct cam_hw_start_args start_args; + struct cam_hw_stop_args stop_args; + struct cam_ife_hw_mgr *ife_hw_mgr = priv; + struct cam_ife_hw_mgr_res *hw_mgr_res; + uint32_t i = 0; + + uint32_t error_type = recovery_data->error_type; + struct cam_ife_hw_mgr_ctx *ctx = NULL; + + /* Here recovery is performed */ + CAM_DBG(CAM_ISP, "ErrorType = %d", error_type); + + switch (error_type) { + case CAM_ISP_HW_ERROR_OVERFLOW: + case CAM_ISP_HW_ERROR_BUSIF_OVERFLOW: + if (!recovery_data->affected_ctx[0]) { + CAM_ERR(CAM_ISP, + "No context is affected but recovery called"); + kfree(recovery_data); + return 0; + } + /* stop resources here */ + CAM_DBG(CAM_ISP, "STOP: Number of affected context: %d", + recovery_data->no_of_context); + for (i = 0; i < recovery_data->no_of_context; i++) { + stop_args.ctxt_to_hw_map = + recovery_data->affected_ctx[i]; + rc = cam_ife_mgr_stop_hw_in_overflow(&stop_args); + if (rc) { + CAM_ERR(CAM_ISP, "CTX stop failed(%d)", rc); + return rc; + } + } + + CAM_DBG(CAM_ISP, "RESET: CSID PATH"); + for (i = 0; i < recovery_data->no_of_context; i++) { + ctx = recovery_data->affected_ctx[i]; + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, + list) { + rc = cam_ife_hw_mgr_reset_csid_res(hw_mgr_res); + if (rc) { + CAM_ERR(CAM_ISP, "Failed RESET (%d)", + hw_mgr_res->res_id); + return rc; + } + } + } + + CAM_DBG(CAM_ISP, "RESET: Calling VFE reset"); + + for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) { + if (recovery_data->affected_core[i]) + cam_ife_mgr_reset_vfe_hw(ife_hw_mgr, i); + } + + CAM_DBG(CAM_ISP, "START: Number of affected context: %d", + recovery_data->no_of_context); + + for (i = 0; i < recovery_data->no_of_context; i++) { + ctx = recovery_data->affected_ctx[i]; + start_args.ctxt_to_hw_map = ctx; + + atomic_set(&ctx->overflow_pending, 0); + + rc = cam_ife_mgr_restart_hw(&start_args); + if (rc) { + CAM_ERR(CAM_ISP, "CTX start failed(%d)", rc); + return rc; + } + CAM_DBG(CAM_ISP, "Started resources rc (%d)", rc); + } + CAM_DBG(CAM_ISP, "Recovery Done rc (%d)", rc); + + break; + + case CAM_ISP_HW_ERROR_P2I_ERROR: + break; + + case CAM_ISP_HW_ERROR_VIOLATION: + break; + + default: + CAM_ERR(CAM_ISP, "Invalid Error"); + } + CAM_DBG(CAM_ISP, "Exit: ErrorType = %d", error_type); + + kfree(recovery_data); + return rc; +} + +static int cam_ife_hw_mgr_do_error_recovery( + struct cam_hw_event_recovery_data *ife_mgr_recovery_data) +{ + int32_t rc = 0; + struct crm_workq_task *task = NULL; + struct cam_hw_event_recovery_data *recovery_data = NULL; + + recovery_data = kzalloc(sizeof(struct cam_hw_event_recovery_data), + GFP_ATOMIC); + if (!recovery_data) + return -ENOMEM; + + memcpy(recovery_data, ife_mgr_recovery_data, + sizeof(struct cam_hw_event_recovery_data)); + + CAM_DBG(CAM_ISP, "Enter: error_type (%d)", recovery_data->error_type); + + task = cam_req_mgr_workq_get_task(g_ife_hw_mgr.workq); + if (!task) { + CAM_ERR(CAM_ISP, "No empty task frame"); + kfree(recovery_data); + return -ENOMEM; + } + + task->process_cb = &cam_ife_mgr_process_recovery_cb; + task->payload = recovery_data; + rc = cam_req_mgr_workq_enqueue_task(task, + recovery_data->affected_ctx[0]->hw_mgr, + CRM_TASK_PRIORITY_0); + + return rc; +} + +/* + * This function checks if any of the valid entry in affected_core[] + * is associated with this context. if YES + * a. It fills the other cores associated with this context.in + * affected_core[] + * b. Return 0 i.e.SUCCESS + */ +static int cam_ife_hw_mgr_is_ctx_affected( + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx, + uint32_t *affected_core, uint32_t size) +{ + + int32_t rc = -EPERM; + uint32_t i = 0, j = 0; + uint32_t max_idx = ife_hwr_mgr_ctx->num_base; + uint32_t ctx_affected_core_idx[CAM_IFE_HW_NUM_MAX] = {0}; + + CAM_DBG(CAM_ISP, "Enter:max_idx = %d", max_idx); + + if ((max_idx >= CAM_IFE_HW_NUM_MAX) || + (size > CAM_IFE_HW_NUM_MAX)) { + CAM_ERR(CAM_ISP, "invalid parameter = %d", max_idx); + return rc; + } + + for (i = 0; i < max_idx; i++) { + if (affected_core[ife_hwr_mgr_ctx->base[i].idx]) + rc = 0; + else { + ctx_affected_core_idx[j] = ife_hwr_mgr_ctx->base[i].idx; + j = j + 1; + } + } + + if (rc == 0) { + while (j) { + if (affected_core[ctx_affected_core_idx[j-1]] != 1) + affected_core[ctx_affected_core_idx[j-1]] = 1; + j = j - 1; + } + } + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +/* + * Loop through each context + * a. match core_idx + * b. For each context from ctx_list Stop the acquired resources + * c. Notify CRM with fatal error for the affected isp context + * d. For any dual VFE context, if copanion VFE is also serving + * other context it should also notify the CRM with fatal error + */ +static int cam_ife_hw_mgr_process_overflow( + struct cam_ife_hw_mgr_ctx *curr_ife_hwr_mgr_ctx, + struct cam_isp_hw_error_event_data *error_event_data, + uint32_t curr_core_idx, + struct cam_hw_event_recovery_data *recovery_data) +{ + uint32_t affected_core[CAM_IFE_HW_NUM_MAX] = {0}; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx = NULL; + cam_hw_event_cb_func ife_hwr_irq_err_cb; + struct cam_ife_hw_mgr *ife_hwr_mgr = NULL; + struct cam_hw_stop_args stop_args; + uint32_t i = 0; + + CAM_DBG(CAM_ISP, "Enter"); + + if (!recovery_data) { + CAM_ERR(CAM_ISP, "recovery_data parameter is NULL"); + return -EINVAL; + } + recovery_data->no_of_context = 0; + /* affected_core is indexed by core_idx*/ + affected_core[curr_core_idx] = 1; + + ife_hwr_mgr = curr_ife_hwr_mgr_ctx->hw_mgr; + + list_for_each_entry(ife_hwr_mgr_ctx, + &ife_hwr_mgr->used_ctx_list, list) { + + /* + * Check if current core_idx matches the HW associated + * with this context + */ + CAM_DBG(CAM_ISP, "Calling match Hw idx"); + if (cam_ife_hw_mgr_is_ctx_affected(ife_hwr_mgr_ctx, + affected_core, CAM_IFE_HW_NUM_MAX)) + continue; + + atomic_set(&ife_hwr_mgr_ctx->overflow_pending, 1); + + ife_hwr_irq_err_cb = + ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_ERROR]; + + stop_args.ctxt_to_hw_map = ife_hwr_mgr_ctx; + + /* Add affected_context in list of recovery data*/ + CAM_DBG(CAM_ISP, "Add new entry in affected_ctx_list"); + if (recovery_data->no_of_context < CAM_CTX_MAX) + recovery_data->affected_ctx[ + recovery_data->no_of_context++] = + ife_hwr_mgr_ctx; + + /* + * In the call back function corresponding ISP context + * will update CRM about fatal Error + */ + + ife_hwr_irq_err_cb(ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_ERROR, error_event_data); + + } + /* fill the affected_core in recovery data */ + for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + recovery_data->affected_core[i] = affected_core[i]; + CAM_DBG(CAM_ISP, "Vfe core %d is affected (%d)", + i, recovery_data->affected_core[i]); + } + CAM_DBG(CAM_ISP, "Exit"); + return 0; +} + +static int cam_ife_hw_mgr_get_err_type( + void *handler_priv, + void *payload) +{ + struct cam_isp_resource_node *hw_res_l = NULL; + struct cam_isp_resource_node *hw_res_r = NULL; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_ife_hw_mgr_res *isp_ife_camif_res = NULL; + uint32_t status = 0; + uint32_t core_idx; + + ife_hwr_mgr_ctx = handler_priv; + evt_payload = payload; + + if (!evt_payload) { + CAM_ERR(CAM_ISP, "No payload"); + return IRQ_HANDLED; + } + + core_idx = evt_payload->core_index; + evt_payload->evt_id = CAM_ISP_HW_EVENT_ERROR; + + list_for_each_entry(isp_ife_camif_res, + &ife_hwr_mgr_ctx->res_list_ife_src, list) { + + if ((isp_ife_camif_res->res_type == + CAM_IFE_HW_MGR_RES_UNINIT) || + (isp_ife_camif_res->res_id != CAM_ISP_HW_VFE_IN_CAMIF)) + continue; + + hw_res_l = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_LEFT]; + hw_res_r = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_RIGHT]; + + CAM_DBG(CAM_ISP, "is_dual_vfe ? = %d\n", + isp_ife_camif_res->is_dual_vfe); + + /* ERROR check for Left VFE */ + if (!hw_res_l) { + CAM_DBG(CAM_ISP, "VFE(L) Device is NULL"); + break; + } + + CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx, + hw_res_l->hw_intf->hw_idx); + + if (core_idx == hw_res_l->hw_intf->hw_idx) { + status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + } + + if (status) + break; + + /* ERROR check for Right VFE */ + if (!hw_res_r) { + CAM_DBG(CAM_ISP, "VFE(R) Device is NULL"); + continue; + } + CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx, + hw_res_r->hw_intf->hw_idx); + + if (core_idx == hw_res_r->hw_intf->hw_idx) { + status = hw_res_r->bottom_half_handler( + hw_res_r, evt_payload); + } + + if (status) + break; + } + CAM_DBG(CAM_ISP, "Exit (status = %d)!", status); + return status; +} + +static int cam_ife_hw_mgr_handle_camif_error( + void *handler_priv, + void *payload) +{ + int32_t error_status; + uint32_t core_idx; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_isp_hw_error_event_data error_event_data = {0}; + struct cam_hw_event_recovery_data recovery_data = {0}; + + ife_hwr_mgr_ctx = handler_priv; + evt_payload = payload; + core_idx = evt_payload->core_index; + + error_status = cam_ife_hw_mgr_get_err_type(ife_hwr_mgr_ctx, + evt_payload); + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + return error_status; + + switch (error_status) { + case CAM_ISP_HW_ERROR_OVERFLOW: + case CAM_ISP_HW_ERROR_P2I_ERROR: + case CAM_ISP_HW_ERROR_VIOLATION: + CAM_ERR(CAM_ISP, "Enter: error_type (%d)", error_status); + + error_event_data.error_type = + CAM_ISP_HW_ERROR_OVERFLOW; + + cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx, + &error_event_data, + core_idx, + &recovery_data); + + /* Trigger for recovery */ + recovery_data.error_type = CAM_ISP_HW_ERROR_OVERFLOW; + cam_ife_hw_mgr_do_error_recovery(&recovery_data); + break; + default: + CAM_DBG(CAM_ISP, "None error (%d)", error_status); + } + + return 0; +} + +/* + * DUAL VFE is valid for PIX processing path + * This function assumes hw_res[0] is master in case + * of dual VFE. + * RDI path does not support DUAl VFE + */ +static int cam_ife_hw_mgr_handle_reg_update( + void *handler_priv, + void *payload) +{ + struct cam_isp_resource_node *hw_res; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_ife_hw_mgr_res *ife_src_res = NULL; + cam_hw_event_cb_func ife_hwr_irq_rup_cb; + struct cam_isp_hw_reg_update_event_data rup_event_data; + uint32_t core_idx; + uint32_t rup_status = -EINVAL; + + CAM_DBG(CAM_ISP, "Enter"); + + ife_hwr_mgr_ctx = handler_priv; + evt_payload = payload; + + if (!handler_priv || !payload) { + CAM_ERR(CAM_ISP, "Invalid Parameter"); + return -EPERM; + } + + core_idx = evt_payload->core_index; + ife_hwr_irq_rup_cb = + ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_REG_UPDATE]; + + evt_payload->evt_id = CAM_ISP_HW_EVENT_REG_UPDATE; + list_for_each_entry(ife_src_res, + &ife_hwr_mgr_ctx->res_list_ife_src, list) { + + if (ife_src_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + CAM_DBG(CAM_ISP, "resource id = %d, curr_core_idx = %d", + ife_src_res->res_id, core_idx); + switch (ife_src_res->res_id) { + case CAM_ISP_HW_VFE_IN_CAMIF: + if (ife_src_res->is_dual_vfe) + /* It checks for slave core RUP ACK*/ + hw_res = ife_src_res->hw_res[1]; + else + hw_res = ife_src_res->hw_res[0]; + + if (!hw_res) { + CAM_ERR(CAM_ISP, "CAMIF device is NULL"); + break; + } + CAM_DBG(CAM_ISP, + "current_core_id = %d , core_idx res = %d", + core_idx, hw_res->hw_intf->hw_idx); + + if (core_idx == hw_res->hw_intf->hw_idx) { + rup_status = hw_res->bottom_half_handler( + hw_res, evt_payload); + } + + if (ife_src_res->is_dual_vfe) { + hw_res = ife_src_res->hw_res[0]; + if (core_idx == hw_res->hw_intf->hw_idx) { + hw_res->bottom_half_handler( + hw_res, evt_payload); + } + } + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + + if (!rup_status) { + ife_hwr_irq_rup_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_REG_UPDATE, + &rup_event_data); + } + break; + + case CAM_ISP_HW_VFE_IN_RDI0: + case CAM_ISP_HW_VFE_IN_RDI1: + case CAM_ISP_HW_VFE_IN_RDI2: + case CAM_ISP_HW_VFE_IN_RDI3: + hw_res = ife_src_res->hw_res[0]; + + if (!hw_res) { + CAM_ERR(CAM_ISP, "RDI Device is NULL"); + break; + } + + if (core_idx == hw_res->hw_intf->hw_idx) + rup_status = hw_res->bottom_half_handler( + hw_res, evt_payload); + + if (!ife_hwr_mgr_ctx->is_rdi_only_context) + continue; + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + if (!rup_status) { + /* Send the Reg update hw event */ + ife_hwr_irq_rup_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_REG_UPDATE, + &rup_event_data); + } + break; + default: + CAM_ERR(CAM_ISP, "Invalid resource id (%d)", + ife_src_res->res_id); + } + + } + + if (!rup_status) + CAM_DBG(CAM_ISP, "Exit rup_status = %d", rup_status); + + return 0; +} + +static int cam_ife_hw_mgr_check_irq_for_dual_vfe( + struct cam_ife_hw_mgr_ctx *ife_hw_mgr_ctx, + uint32_t core_idx0, + uint32_t core_idx1, + uint32_t hw_event_type) +{ + int32_t rc = -1; + uint32_t *event_cnt = NULL; + + switch (hw_event_type) { + case CAM_ISP_HW_EVENT_SOF: + event_cnt = ife_hw_mgr_ctx->sof_cnt; + break; + case CAM_ISP_HW_EVENT_EPOCH: + event_cnt = ife_hw_mgr_ctx->epoch_cnt; + break; + case CAM_ISP_HW_EVENT_EOF: + event_cnt = ife_hw_mgr_ctx->eof_cnt; + break; + default: + return 0; + } + + if (event_cnt[core_idx0] == + event_cnt[core_idx1]) { + + event_cnt[core_idx0] = 0; + event_cnt[core_idx1] = 0; + + rc = 0; + return rc; + } + + if ((event_cnt[core_idx0] && + (event_cnt[core_idx0] - event_cnt[core_idx1] > 1)) || + (event_cnt[core_idx1] && + (event_cnt[core_idx1] - event_cnt[core_idx0] > 1))) { + + CAM_ERR_RATE_LIMIT(CAM_ISP, + "One of the VFE cound not generate hw event %d", + hw_event_type); + rc = -1; + return rc; + } + + CAM_DBG(CAM_ISP, "Only one core_index has given hw event %d", + hw_event_type); + + return rc; +} + +static int cam_ife_hw_mgr_handle_epoch_for_camif_hw_res( + void *handler_priv, + void *payload) +{ + int32_t rc = -EINVAL; + struct cam_isp_resource_node *hw_res_l; + struct cam_isp_resource_node *hw_res_r; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_ife_hw_mgr_res *isp_ife_camif_res = NULL; + cam_hw_event_cb_func ife_hwr_irq_epoch_cb; + struct cam_isp_hw_epoch_event_data epoch_done_event_data; + uint32_t core_idx; + uint32_t epoch_status = -EINVAL; + uint32_t core_index0; + uint32_t core_index1; + + CAM_DBG(CAM_ISP, "Enter"); + + ife_hwr_mgr_ctx = handler_priv; + evt_payload = payload; + ife_hwr_irq_epoch_cb = + ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_EPOCH]; + core_idx = evt_payload->core_index; + + evt_payload->evt_id = CAM_ISP_HW_EVENT_EPOCH; + + list_for_each_entry(isp_ife_camif_res, + &ife_hwr_mgr_ctx->res_list_ife_src, list) { + if ((isp_ife_camif_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + || (isp_ife_camif_res->res_id != + CAM_ISP_HW_VFE_IN_CAMIF)) + continue; + + hw_res_l = isp_ife_camif_res->hw_res[0]; + hw_res_r = isp_ife_camif_res->hw_res[1]; + + switch (isp_ife_camif_res->is_dual_vfe) { + /* Handling Single VFE Scenario */ + case 0: + /* EPOCH check for Left side VFE */ + if (!hw_res_l) { + CAM_ERR(CAM_ISP, "Left Device is NULL"); + break; + } + + if (core_idx == hw_res_l->hw_intf->hw_idx) { + epoch_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + if (atomic_read( + &ife_hwr_mgr_ctx->overflow_pending)) + break; + if (!epoch_status) + ife_hwr_irq_epoch_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_EPOCH, + &epoch_done_event_data); + } + + break; + + /* Handling Dual VFE Scenario */ + case 1: + /* SOF check for Left side VFE (Master)*/ + + if ((!hw_res_l) || (!hw_res_r)) { + CAM_ERR(CAM_ISP, "Dual VFE Device is NULL"); + break; + } + if (core_idx == hw_res_l->hw_intf->hw_idx) { + epoch_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + + if (!epoch_status) + ife_hwr_mgr_ctx->epoch_cnt[core_idx]++; + else + break; + } + + /* SOF check for Right side VFE */ + if (core_idx == hw_res_r->hw_intf->hw_idx) { + epoch_status = hw_res_r->bottom_half_handler( + hw_res_r, evt_payload); + + if (!epoch_status) + ife_hwr_mgr_ctx->epoch_cnt[core_idx]++; + else + break; + } + + core_index0 = hw_res_l->hw_intf->hw_idx; + core_index1 = hw_res_r->hw_intf->hw_idx; + + rc = cam_ife_hw_mgr_check_irq_for_dual_vfe( + ife_hwr_mgr_ctx, + core_index0, + core_index1, + evt_payload->evt_id); + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + if (!rc) + ife_hwr_irq_epoch_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_EPOCH, + &epoch_done_event_data); + + break; + + /* Error */ + default: + CAM_ERR(CAM_ISP, "error with hw_res"); + + } + } + + if (!epoch_status) + CAM_DBG(CAM_ISP, "Exit epoch_status = %d", epoch_status); + + return 0; +} + +static int cam_ife_hw_mgr_process_camif_sof( + struct cam_ife_hw_mgr_res *isp_ife_camif_res, + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx, + struct cam_vfe_top_irq_evt_payload *evt_payload) +{ + struct cam_isp_resource_node *hw_res_l = NULL; + struct cam_isp_resource_node *hw_res_r = NULL; + int32_t rc = -EINVAL; + uint32_t core_idx; + uint32_t sof_status = 0; + uint32_t core_index0; + uint32_t core_index1; + + CAM_DBG(CAM_ISP, "Enter"); + core_idx = evt_payload->core_index; + hw_res_l = isp_ife_camif_res->hw_res[0]; + hw_res_r = isp_ife_camif_res->hw_res[1]; + CAM_DBG(CAM_ISP, "is_dual_vfe ? = %d", + isp_ife_camif_res->is_dual_vfe); + + switch (isp_ife_camif_res->is_dual_vfe) { + /* Handling Single VFE Scenario */ + case 0: + /* SOF check for Left side VFE */ + if (!hw_res_l) { + CAM_ERR(CAM_ISP, "VFE Device is NULL"); + break; + } + CAM_DBG(CAM_ISP, "curr_core_idx = %d,core idx hw = %d", + core_idx, hw_res_l->hw_intf->hw_idx); + + if (core_idx == hw_res_l->hw_intf->hw_idx) { + sof_status = hw_res_l->bottom_half_handler(hw_res_l, + evt_payload); + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + if (!sof_status) + rc = 0; + } + + break; + + /* Handling Dual VFE Scenario */ + case 1: + /* SOF check for Left side VFE */ + + if (!hw_res_l) { + CAM_ERR(CAM_ISP, "VFE Device is NULL"); + break; + } + CAM_DBG(CAM_ISP, "curr_core_idx = %d, res hw idx= %d", + core_idx, + hw_res_l->hw_intf->hw_idx); + + if (core_idx == hw_res_l->hw_intf->hw_idx) { + sof_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + if (!sof_status) + ife_hwr_mgr_ctx->sof_cnt[core_idx]++; + else + break; + } + + /* SOF check for Right side VFE */ + if (!hw_res_r) { + CAM_ERR(CAM_ISP, "VFE Device is NULL"); + break; + } + CAM_DBG(CAM_ISP, "curr_core_idx = %d, ews hw idx= %d", + core_idx, + hw_res_r->hw_intf->hw_idx); + if (core_idx == hw_res_r->hw_intf->hw_idx) { + sof_status = hw_res_r->bottom_half_handler(hw_res_r, + evt_payload); + if (!sof_status) + ife_hwr_mgr_ctx->sof_cnt[core_idx]++; + else + break; + } + + core_index0 = hw_res_l->hw_intf->hw_idx; + core_index1 = hw_res_r->hw_intf->hw_idx; + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + + rc = cam_ife_hw_mgr_check_irq_for_dual_vfe(ife_hwr_mgr_ctx, + core_index0, core_index1, evt_payload->evt_id); + + break; + + default: + CAM_ERR(CAM_ISP, "error with hw_res"); + break; + } + + CAM_DBG(CAM_ISP, "Exit (sof_status = %d)", sof_status); + + return rc; +} + +static int cam_ife_hw_mgr_handle_sof( + void *handler_priv, + void *payload) +{ + struct cam_isp_resource_node *hw_res = NULL; + struct cam_ife_hw_mgr_ctx *ife_hw_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_ife_hw_mgr_res *ife_src_res = NULL; + cam_hw_event_cb_func ife_hw_irq_sof_cb; + struct cam_isp_hw_sof_event_data sof_done_event_data; + uint32_t sof_status = 0; + bool sof_sent = false; + + CAM_DBG(CAM_ISP, "Enter"); + + ife_hw_mgr_ctx = handler_priv; + evt_payload = payload; + if (!evt_payload) { + CAM_ERR(CAM_ISP, "no payload"); + return IRQ_HANDLED; + } + ife_hw_irq_sof_cb = + ife_hw_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_SOF]; + + evt_payload->evt_id = CAM_ISP_HW_EVENT_SOF; + + list_for_each_entry(ife_src_res, + &ife_hw_mgr_ctx->res_list_ife_src, list) { + + if (ife_src_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + switch (ife_src_res->res_id) { + case CAM_ISP_HW_VFE_IN_RDI0: + case CAM_ISP_HW_VFE_IN_RDI1: + case CAM_ISP_HW_VFE_IN_RDI2: + case CAM_ISP_HW_VFE_IN_RDI3: + hw_res = ife_src_res->hw_res[0]; + sof_status = hw_res->bottom_half_handler( + hw_res, evt_payload); + + /* check if it is rdi only context */ + if (ife_hw_mgr_ctx->is_rdi_only_context) { + if (!sof_status && !sof_sent) { + cam_ife_mgr_cmd_get_sof_timestamp( + ife_hw_mgr_ctx, + &sof_done_event_data.timestamp); + + ife_hw_irq_sof_cb( + ife_hw_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_SOF, + &sof_done_event_data); + CAM_DBG(CAM_ISP, "sof_status = %d", + sof_status); + + sof_sent = true; + } + + } + break; + + case CAM_ISP_HW_VFE_IN_CAMIF: + sof_status = cam_ife_hw_mgr_process_camif_sof( + ife_src_res, ife_hw_mgr_ctx, evt_payload); + if (!sof_status && !sof_sent) { + cam_ife_mgr_cmd_get_sof_timestamp( + ife_hw_mgr_ctx, + &sof_done_event_data.timestamp); + + ife_hw_irq_sof_cb( + ife_hw_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_SOF, + &sof_done_event_data); + CAM_DBG(CAM_ISP, "sof_status = %d", + sof_status); + + sof_sent = true; + } + break; + default: + CAM_ERR(CAM_ISP, "Invalid resource id :%d", + ife_src_res->res_id); + break; + } + } + + return 0; +} + +static int cam_ife_hw_mgr_handle_eof_for_camif_hw_res( + void *handler_priv, + void *payload) +{ + int32_t rc = -EINVAL; + struct cam_isp_resource_node *hw_res_l = NULL; + struct cam_isp_resource_node *hw_res_r = NULL; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_ife_hw_mgr_res *isp_ife_camif_res = NULL; + cam_hw_event_cb_func ife_hwr_irq_eof_cb; + struct cam_isp_hw_eof_event_data eof_done_event_data; + uint32_t core_idx; + uint32_t eof_status = 0; + uint32_t core_index0; + uint32_t core_index1; + + CAM_DBG(CAM_ISP, "Enter"); + + ife_hwr_mgr_ctx = handler_priv; + evt_payload = payload; + if (!evt_payload) { + pr_err("%s: no payload\n", __func__); + return IRQ_HANDLED; + } + core_idx = evt_payload->core_index; + ife_hwr_irq_eof_cb = + ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_EOF]; + + evt_payload->evt_id = CAM_ISP_HW_EVENT_EOF; + + list_for_each_entry(isp_ife_camif_res, + &ife_hwr_mgr_ctx->res_list_ife_src, list) { + + if ((isp_ife_camif_res->res_type == + CAM_IFE_HW_MGR_RES_UNINIT) || + (isp_ife_camif_res->res_id != CAM_ISP_HW_VFE_IN_CAMIF)) + continue; + + hw_res_l = isp_ife_camif_res->hw_res[0]; + hw_res_r = isp_ife_camif_res->hw_res[1]; + + CAM_DBG(CAM_ISP, "is_dual_vfe ? = %d", + isp_ife_camif_res->is_dual_vfe); + switch (isp_ife_camif_res->is_dual_vfe) { + /* Handling Single VFE Scenario */ + case 0: + /* EOF check for Left side VFE */ + if (!hw_res_l) { + pr_err("%s: VFE Device is NULL\n", + __func__); + break; + } + CAM_DBG(CAM_ISP, "curr_core_idx = %d, core idx hw = %d", + core_idx, hw_res_l->hw_intf->hw_idx); + + if (core_idx == hw_res_l->hw_intf->hw_idx) { + eof_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + if (atomic_read( + &ife_hwr_mgr_ctx->overflow_pending)) + break; + if (!eof_status) + ife_hwr_irq_eof_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_EOF, + &eof_done_event_data); + } + + break; + /* Handling dual VFE Scenario */ + case 1: + if ((!hw_res_l) || (!hw_res_r)) { + CAM_ERR(CAM_ISP, "Dual VFE Device is NULL"); + break; + } + if (core_idx == hw_res_l->hw_intf->hw_idx) { + eof_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + + if (!eof_status) + ife_hwr_mgr_ctx->eof_cnt[core_idx]++; + else + break; + } + + /* EOF check for Right side VFE */ + if (core_idx == hw_res_r->hw_intf->hw_idx) { + eof_status = hw_res_r->bottom_half_handler( + hw_res_r, evt_payload); + + if (!eof_status) + ife_hwr_mgr_ctx->eof_cnt[core_idx]++; + else + break; + } + + core_index0 = hw_res_l->hw_intf->hw_idx; + core_index1 = hw_res_r->hw_intf->hw_idx; + + rc = cam_ife_hw_mgr_check_irq_for_dual_vfe( + ife_hwr_mgr_ctx, + core_index0, + core_index1, + evt_payload->evt_id); + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + + if (!rc) + ife_hwr_irq_eof_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_EOF, + &eof_done_event_data); + + break; + + default: + CAM_ERR(CAM_ISP, "error with hw_res"); + } + } + + CAM_DBG(CAM_ISP, "Exit (eof_status = %d)", eof_status); + + return 0; +} + + +static int cam_ife_hw_mgr_handle_buf_done_for_hw_res( + void *handler_priv, + void *payload) + +{ + int32_t buf_done_status = 0; + int32_t i; + int32_t rc = 0; + cam_hw_event_cb_func ife_hwr_irq_wm_done_cb; + struct cam_isp_resource_node *hw_res_l = NULL; + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx = NULL; + struct cam_vfe_bus_irq_evt_payload *evt_payload = payload; + struct cam_ife_hw_mgr_res *isp_ife_out_res = NULL; + struct cam_hw_event_recovery_data recovery_data; + struct cam_isp_hw_done_event_data buf_done_event_data = {0}; + struct cam_isp_hw_error_event_data error_event_data = {0}; + uint32_t error_resc_handle[CAM_IFE_HW_OUT_RES_MAX]; + uint32_t num_of_error_handles = 0; + + CAM_DBG(CAM_ISP, "Enter"); + + ife_hwr_mgr_ctx = evt_payload->ctx; + ife_hwr_irq_wm_done_cb = + ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_DONE]; + + evt_payload->evt_id = CAM_ISP_HW_EVENT_DONE; + + for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) { + isp_ife_out_res = &ife_hwr_mgr_ctx->res_list_ife_out[i]; + + if (isp_ife_out_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + hw_res_l = isp_ife_out_res->hw_res[0]; + + /* + * DUAL VFE: Index 0 is always a master. In case of composite + * Error, if the error is not in master, it needs to be checked + * in slave (for debuging purpose only) For other cases: + * Index zero is valid + */ + + if (hw_res_l && (evt_payload->core_index == + hw_res_l->hw_intf->hw_idx)) + buf_done_status = hw_res_l->bottom_half_handler( + hw_res_l, evt_payload); + else + continue; + + switch (buf_done_status) { + case CAM_VFE_IRQ_STATUS_ERR_COMP: + /* + * Write interface can pipeline upto 2 buffer done + * strobes from each write client. If any of the client + * triggers a third buffer done strobe before a + * composite interrupt based on the first buffer doneis + * triggered an error irq is set. This scenario can + * only happen if a client is 3 frames ahead of the + * other clients enabled in the same composite mask. + */ + case CAM_VFE_IRQ_STATUS_COMP_OWRT: + /* + * It is an indication that bandwidth is not sufficient + * to generate composite done irq within the VBI time. + */ + + error_resc_handle[num_of_error_handles++] = + isp_ife_out_res->res_id; + + if (num_of_error_handles > 0) { + error_event_data.error_type = + CAM_ISP_HW_ERROR_BUSIF_OVERFLOW; + goto err; + } + + break; + case CAM_VFE_IRQ_STATUS_ERR: + break; + case CAM_VFE_IRQ_STATUS_SUCCESS: + buf_done_event_data.num_handles = 1; + buf_done_event_data.resource_handle[0] = + isp_ife_out_res->res_id; + + if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) + break; + /* Report for Successful buf_done event if any */ + if (buf_done_event_data.num_handles > 0 && + ife_hwr_irq_wm_done_cb) { + CAM_DBG(CAM_ISP, "notify isp context"); + ife_hwr_irq_wm_done_cb( + ife_hwr_mgr_ctx->common.cb_priv, + CAM_ISP_HW_EVENT_DONE, + &buf_done_event_data); + } + + break; + default: + /* Do NOTHING */ + error_resc_handle[num_of_error_handles++] = + isp_ife_out_res->res_id; + if (num_of_error_handles > 0) { + error_event_data.error_type = + CAM_ISP_HW_ERROR_BUSIF_OVERFLOW; + goto err; + } + break; + } + if (!buf_done_status) + CAM_DBG(CAM_ISP, + "buf_done status:(%d),out_res->res_id: 0x%x", + buf_done_status, isp_ife_out_res->res_id); + } + + return rc; + +err: + /* + * Report for error if any. + * For the first phase, Error is reported as overflow, for all + * the affected context and any successful buf_done event is not + * reported. + */ + rc = cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx, + &error_event_data, evt_payload->core_index, + &recovery_data); + + /* + * We can temporarily return from here as + * for the first phase, we are going to reset entire HW. + */ + + CAM_DBG(CAM_ISP, "Exit buf_done_status Error = %d", + buf_done_status); + return rc; +} + +int cam_ife_mgr_do_tasklet_buf_done(void *handler_priv, + void *evt_payload_priv) +{ + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx = handler_priv; + struct cam_vfe_bus_irq_evt_payload *evt_payload; + int rc = -EINVAL; + + if (!handler_priv) + return rc; + + evt_payload = evt_payload_priv; + ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)evt_payload->ctx; + + CAM_DBG(CAM_ISP, "addr of evt_payload = %llx core index:0x%x", + (uint64_t)evt_payload, evt_payload->core_index); + CAM_DBG(CAM_ISP, "bus_irq_status_0: = %x", evt_payload->irq_reg_val[0]); + CAM_DBG(CAM_ISP, "bus_irq_status_1: = %x", evt_payload->irq_reg_val[1]); + CAM_DBG(CAM_ISP, "bus_irq_status_2: = %x", evt_payload->irq_reg_val[2]); + CAM_DBG(CAM_ISP, "bus_irq_comp_err: = %x", evt_payload->irq_reg_val[3]); + CAM_DBG(CAM_ISP, "bus_irq_comp_owrt: = %x", + evt_payload->irq_reg_val[4]); + CAM_DBG(CAM_ISP, "bus_irq_dual_comp_err: = %x", + evt_payload->irq_reg_val[5]); + CAM_DBG(CAM_ISP, "bus_irq_dual_comp_owrt: = %x", + evt_payload->irq_reg_val[6]); + /* WM Done */ + return cam_ife_hw_mgr_handle_buf_done_for_hw_res(ife_hwr_mgr_ctx, + evt_payload_priv); +} + +int cam_ife_mgr_do_tasklet(void *handler_priv, void *evt_payload_priv) +{ + struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx = handler_priv; + struct cam_vfe_top_irq_evt_payload *evt_payload; + int rc = -EINVAL; + + if (!evt_payload_priv) + return rc; + + evt_payload = evt_payload_priv; + if (!handler_priv) + return rc; + + ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)handler_priv; + + CAM_DBG(CAM_ISP, "addr of evt_payload = %pK core_index:%d", + (void *)evt_payload, + evt_payload->core_index); + CAM_DBG(CAM_ISP, "irq_status_0: = %x", evt_payload->irq_reg_val[0]); + CAM_DBG(CAM_ISP, "irq_status_1: = %x", evt_payload->irq_reg_val[1]); + CAM_DBG(CAM_ISP, "Violation register: = %x", + evt_payload->irq_reg_val[2]); + + /* + * If overflow/overwrite/error/violation are pending + * for this context it needs to be handled remaining + * interrupts are ignored. + */ + if (g_ife_hw_mgr.debug_cfg.enable_recovery) { + CAM_DBG(CAM_ISP, "IFE Mgr recovery is enabled"); + rc = cam_ife_hw_mgr_handle_camif_error(ife_hwr_mgr_ctx, + evt_payload_priv); + } else { + CAM_DBG(CAM_ISP, "recovery is not enabled"); + rc = 0; + } + + if (rc) { + CAM_ERR(CAM_ISP, "Encountered Error (%d), ignoring other irqs", + rc); + goto put_payload; + } + + CAM_DBG(CAM_ISP, "Calling EOF"); + cam_ife_hw_mgr_handle_eof_for_camif_hw_res(ife_hwr_mgr_ctx, + evt_payload_priv); + + CAM_DBG(CAM_ISP, "Calling SOF"); + /* SOF IRQ */ + cam_ife_hw_mgr_handle_sof(ife_hwr_mgr_ctx, + evt_payload_priv); + + CAM_DBG(CAM_ISP, "Calling RUP"); + /* REG UPDATE */ + cam_ife_hw_mgr_handle_reg_update(ife_hwr_mgr_ctx, + evt_payload_priv); + + CAM_DBG(CAM_ISP, "Calling EPOCH"); + /* EPOCH IRQ */ + cam_ife_hw_mgr_handle_epoch_for_camif_hw_res(ife_hwr_mgr_ctx, + evt_payload_priv); + +put_payload: + cam_vfe_put_evt_payload(evt_payload->core_info, &evt_payload); + return IRQ_HANDLED; +} + +static int cam_ife_hw_mgr_sort_dev_with_caps( + struct cam_ife_hw_mgr *ife_hw_mgr) +{ + int i; + + /* get caps for csid devices */ + for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) { + if (!ife_hw_mgr->csid_devices[i]) + continue; + if (ife_hw_mgr->csid_devices[i]->hw_ops.get_hw_caps) { + ife_hw_mgr->csid_devices[i]->hw_ops.get_hw_caps( + ife_hw_mgr->csid_devices[i]->hw_priv, + &ife_hw_mgr->ife_csid_dev_caps[i], + sizeof(ife_hw_mgr->ife_csid_dev_caps[i])); + } + } + + /* get caps for ife devices */ + for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + if (!ife_hw_mgr->ife_devices[i]) + continue; + if (ife_hw_mgr->ife_devices[i]->hw_ops.get_hw_caps) { + ife_hw_mgr->ife_devices[i]->hw_ops.get_hw_caps( + ife_hw_mgr->ife_devices[i]->hw_priv, + &ife_hw_mgr->ife_dev_caps[i], + sizeof(ife_hw_mgr->ife_dev_caps[i])); + } + } + + return 0; +} + +static int cam_ife_set_csid_debug(void *data, u64 val) +{ + g_ife_hw_mgr.debug_cfg.csid_debug = val; + CAM_DBG(CAM_ISP, "Set CSID Debug value :%lld", val); + return 0; +} + +static int cam_ife_get_csid_debug(void *data, u64 *val) +{ + *val = g_ife_hw_mgr.debug_cfg.csid_debug; + CAM_DBG(CAM_ISP, "Get CSID Debug value :%lld", + g_ife_hw_mgr.debug_cfg.csid_debug); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(cam_ife_csid_debug, + cam_ife_get_csid_debug, + cam_ife_set_csid_debug, "%16llu"); + +static int cam_ife_hw_mgr_debug_register(void) +{ + g_ife_hw_mgr.debug_cfg.dentry = debugfs_create_dir("camera_ife", + NULL); + + if (!g_ife_hw_mgr.debug_cfg.dentry) { + CAM_ERR(CAM_ISP, "failed to create dentry"); + return -ENOMEM; + } + + if (!debugfs_create_file("ife_csid_debug", + 0644, + g_ife_hw_mgr.debug_cfg.dentry, NULL, + &cam_ife_csid_debug)) { + CAM_ERR(CAM_ISP, "failed to create cam_ife_csid_debug"); + goto err; + } + + if (!debugfs_create_u32("enable_recovery", + 0644, + g_ife_hw_mgr.debug_cfg.dentry, + &g_ife_hw_mgr.debug_cfg.enable_recovery)) { + CAM_ERR(CAM_ISP, "failed to create enable_recovery"); + goto err; + } + g_ife_hw_mgr.debug_cfg.enable_recovery = 0; + + return 0; + +err: + debugfs_remove_recursive(g_ife_hw_mgr.debug_cfg.dentry); + return -ENOMEM; +} + +int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl) +{ + int rc = -EFAULT; + int i, j; + struct cam_iommu_handle cdm_handles; + + CAM_DBG(CAM_ISP, "Enter"); + + memset(&g_ife_hw_mgr, 0, sizeof(g_ife_hw_mgr)); + + mutex_init(&g_ife_hw_mgr.ctx_mutex); + + if (CAM_IFE_HW_NUM_MAX != CAM_IFE_CSID_HW_NUM_MAX) { + CAM_ERR(CAM_ISP, "CSID num is different then IFE num"); + return -EINVAL; + } + + /* fill ife hw intf information */ + for (i = 0, j = 0; i < CAM_IFE_HW_NUM_MAX; i++) { + rc = cam_vfe_hw_init(&g_ife_hw_mgr.ife_devices[i], i); + if (!rc) { + struct cam_hw_info *vfe_hw = + (struct cam_hw_info *) + g_ife_hw_mgr.ife_devices[i]->hw_priv; + struct cam_hw_soc_info *soc_info = &vfe_hw->soc_info; + + j++; + + g_ife_hw_mgr.cdm_reg_map[i] = &soc_info->reg_map[0]; + CAM_DBG(CAM_ISP, + "reg_map: mem base = %pK cam_base = 0x%llx", + (void __iomem *)soc_info->reg_map[0].mem_base, + (uint64_t) soc_info->reg_map[0].mem_cam_base); + } else { + g_ife_hw_mgr.cdm_reg_map[i] = NULL; + } + } + if (j == 0) { + CAM_ERR(CAM_ISP, "no valid IFE HW"); + return -EINVAL; + } + + /* fill csid hw intf information */ + for (i = 0, j = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) { + rc = cam_ife_csid_hw_init(&g_ife_hw_mgr.csid_devices[i], i); + if (!rc) + j++; + } + if (!j) { + CAM_ERR(CAM_ISP, "no valid IFE CSID HW"); + return -EINVAL; + } + + cam_ife_hw_mgr_sort_dev_with_caps(&g_ife_hw_mgr); + + /* setup ife context list */ + INIT_LIST_HEAD(&g_ife_hw_mgr.free_ctx_list); + INIT_LIST_HEAD(&g_ife_hw_mgr.used_ctx_list); + + /* + * for now, we only support one iommu handle. later + * we will need to setup more iommu handle for other + * use cases. + * Also, we have to release them once we have the + * deinit support + */ + if (cam_smmu_get_handle("ife", + &g_ife_hw_mgr.mgr_common.img_iommu_hdl)) { + CAM_ERR(CAM_ISP, "Can not get iommu handle"); + return -EINVAL; + } + + if (cam_smmu_get_handle("cam-secure", + &g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure)) { + CAM_ERR(CAM_ISP, "Failed to get secure iommu handle"); + goto secure_fail; + } + + CAM_DBG(CAM_ISP, "iommu_handles: non-secure[0x%x], secure[0x%x]", + g_ife_hw_mgr.mgr_common.img_iommu_hdl, + g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure); + + if (!cam_cdm_get_iommu_handle("ife", &cdm_handles)) { + CAM_DBG(CAM_ISP, "Successfully acquired the CDM iommu handles"); + g_ife_hw_mgr.mgr_common.cmd_iommu_hdl = cdm_handles.non_secure; + g_ife_hw_mgr.mgr_common.cmd_iommu_hdl_secure = + cdm_handles.secure; + } else { + CAM_DBG(CAM_ISP, "Failed to acquire the CDM iommu handles"); + g_ife_hw_mgr.mgr_common.cmd_iommu_hdl = -1; + g_ife_hw_mgr.mgr_common.cmd_iommu_hdl_secure = -1; + } + + atomic_set(&g_ife_hw_mgr.active_ctx_cnt, 0); + for (i = 0; i < CAM_CTX_MAX; i++) { + memset(&g_ife_hw_mgr.ctx_pool[i], 0, + sizeof(g_ife_hw_mgr.ctx_pool[i])); + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].list); + + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].res_list_ife_in.list); + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].res_list_ife_cid); + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].res_list_ife_csid); + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].res_list_ife_src); + for (j = 0; j < CAM_IFE_HW_OUT_RES_MAX; j++) { + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i]. + res_list_ife_out[j].list); + } + + /* init context pool */ + INIT_LIST_HEAD(&g_ife_hw_mgr.ctx_pool[i].free_res_list); + for (j = 0; j < CAM_IFE_HW_RES_POOL_MAX; j++) { + INIT_LIST_HEAD( + &g_ife_hw_mgr.ctx_pool[i].res_pool[j].list); + list_add_tail( + &g_ife_hw_mgr.ctx_pool[i].res_pool[j].list, + &g_ife_hw_mgr.ctx_pool[i].free_res_list); + } + + g_ife_hw_mgr.ctx_pool[i].cdm_cmd = + kzalloc(((sizeof(struct cam_cdm_bl_request)) + + ((CAM_IFE_HW_ENTRIES_MAX - 1) * + sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL); + if (!g_ife_hw_mgr.ctx_pool[i].cdm_cmd) { + rc = -ENOMEM; + CAM_ERR(CAM_ISP, "Allocation Failed for cdm command"); + goto end; + } + + g_ife_hw_mgr.ctx_pool[i].ctx_index = i; + g_ife_hw_mgr.ctx_pool[i].hw_mgr = &g_ife_hw_mgr; + + cam_tasklet_init(&g_ife_hw_mgr.mgr_common.tasklet_pool[i], + &g_ife_hw_mgr.ctx_pool[i], i); + g_ife_hw_mgr.ctx_pool[i].common.tasklet_info = + g_ife_hw_mgr.mgr_common.tasklet_pool[i]; + + init_completion(&g_ife_hw_mgr.ctx_pool[i].config_done_complete); + list_add_tail(&g_ife_hw_mgr.ctx_pool[i].list, + &g_ife_hw_mgr.free_ctx_list); + } + + /* Create Worker for ife_hw_mgr with 10 tasks */ + rc = cam_req_mgr_workq_create("cam_ife_worker", 10, + &g_ife_hw_mgr.workq, CRM_WORKQ_USAGE_NON_IRQ); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Unable to create worker"); + goto end; + } + + /* fill return structure */ + hw_mgr_intf->hw_mgr_priv = &g_ife_hw_mgr; + hw_mgr_intf->hw_get_caps = cam_ife_mgr_get_hw_caps; + hw_mgr_intf->hw_acquire = cam_ife_mgr_acquire_hw; + hw_mgr_intf->hw_start = cam_ife_mgr_start_hw; + hw_mgr_intf->hw_stop = cam_ife_mgr_stop_hw; + hw_mgr_intf->hw_read = cam_ife_mgr_read; + hw_mgr_intf->hw_write = cam_ife_mgr_write; + hw_mgr_intf->hw_release = cam_ife_mgr_release_hw; + hw_mgr_intf->hw_prepare_update = cam_ife_mgr_prepare_hw_update; + hw_mgr_intf->hw_config = cam_ife_mgr_config_hw; + hw_mgr_intf->hw_cmd = cam_ife_mgr_cmd; + + if (iommu_hdl) + *iommu_hdl = g_ife_hw_mgr.mgr_common.img_iommu_hdl; + + cam_ife_hw_mgr_debug_register(); + CAM_DBG(CAM_ISP, "Exit"); + + return 0; +end: + if (rc) { + for (i = 0; i < CAM_CTX_MAX; i++) { + cam_tasklet_deinit( + &g_ife_hw_mgr.mgr_common.tasklet_pool[i]); + kfree(g_ife_hw_mgr.ctx_pool[i].cdm_cmd); + g_ife_hw_mgr.ctx_pool[i].cdm_cmd = NULL; + g_ife_hw_mgr.ctx_pool[i].common.tasklet_info = NULL; + } + } + cam_smmu_destroy_handle( + g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure); + g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure = -1; +secure_fail: + cam_smmu_destroy_handle(g_ife_hw_mgr.mgr_common.img_iommu_hdl); + g_ife_hw_mgr.mgr_common.img_iommu_hdl = -1; + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h new file mode 100644 index 000000000000..914a9dead43b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h @@ -0,0 +1,231 @@ +/* Copyright (c) 2017-2018, 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 _CAM_IFE_HW_MGR_H_ +#define _CAM_IFE_HW_MGR_H_ + +#include +#include "cam_isp_hw_mgr.h" +#include "cam_vfe_hw_intf.h" +#include "cam_ife_csid_hw_intf.h" +#include "cam_tasklet_util.h" + +/* enum cam_ife_hw_mgr_res_type - manager resource node type */ +enum cam_ife_hw_mgr_res_type { + CAM_IFE_HW_MGR_RES_UNINIT, + CAM_IFE_HW_MGR_RES_ROOT, + CAM_IFE_HW_MGR_RES_CID, + CAM_IFE_HW_MGR_RES_CSID, + CAM_IFE_HW_MGR_RES_IFE_SRC, + CAM_IFE_HW_MGR_RES_IFE_OUT, +}; + +/* IFE resource constants */ +#define CAM_IFE_HW_IN_RES_MAX (CAM_ISP_IFE_IN_RES_MAX & 0xFF) +#define CAM_IFE_HW_OUT_RES_MAX (CAM_ISP_IFE_OUT_RES_MAX & 0xFF) +#define CAM_IFE_HW_RES_POOL_MAX 64 + +/** + * struct cam_vfe_hw_mgr_res- HW resources for the VFE manager + * + * @list: used by the resource list + * @res_type: IFE manager resource type + * @res_id: resource id based on the resource type for root or + * leaf resource, it matches the KMD interface port id. + * For branch resrouce, it is defined by the ISP HW + * layer + * @hw_res: hw layer resource array. For single VFE, only one VFE + * hw resrouce will be acquired. For dual VFE, two hw + * resources from different VFE HW device will be + * acquired + * @parent: point to the parent resource node. + * @children: point to the children resource nodes + * @child_num: numbe of the child resource node. + * + */ +struct cam_ife_hw_mgr_res { + struct list_head list; + enum cam_ife_hw_mgr_res_type res_type; + uint32_t res_id; + uint32_t is_dual_vfe; + struct cam_isp_resource_node *hw_res[CAM_ISP_HW_SPLIT_MAX]; + + /* graph */ + struct cam_ife_hw_mgr_res *parent; + struct cam_ife_hw_mgr_res *child[CAM_IFE_HW_OUT_RES_MAX]; + uint32_t num_children; +}; + + +/** + * struct ctx_base_info - Base hardware information for the context + * + * @idx: Base resource index + * @split_id: Split info for the base resource + * + */ +struct ctx_base_info { + uint32_t idx; + enum cam_isp_hw_split_id split_id; +}; + +/** + * struct cam_ife_hw_mgr_debug - contain the debug information + * + * @dentry: Debugfs entry + * @csid_debug: csid debug information + * @enable_recovery enable recovery + * + */ +struct cam_ife_hw_mgr_debug { + struct dentry *dentry; + uint64_t csid_debug; + uint32_t enable_recovery; +}; + +/** + * struct cam_vfe_hw_mgr_ctx - IFE HW manager Context object + * + * @list: used by the ctx list. + * @common: common acquired context data + * @ctx_index: acquired context id. + * @hw_mgr: IFE hw mgr which owns this context + * @ctx_in_use: flag to tell whether context is active + * @res_list_ife_in: Starting resource(TPG,PHY0, PHY1...) Can only be + * one. + * @res_list_csid: CSID resource list + * @res_list_ife_src: IFE input resource list + * @res_list_ife_out: IFE output resoruces array + * @free_res_list: Free resources list for the branch node + * @res_pool: memory storage for the free resource list + * @irq_status0_mask: irq_status0_mask for the context + * @irq_status1_mask: irq_status1_mask for the context + * @base device base index array contain the all IFE HW + * instance associated with this context. + * @num_base number of valid base data in the base array + * @cdm_handle cdm hw acquire handle + * @cdm_ops cdm util operation pointer for building + * cdm commands + * @cdm_cmd cdm base and length request pointer + * @sof_cnt sof count value per core, used for dual VFE + * @epoch_cnt epoch count value per core, used for dual VFE + * @eof_cnt eof count value per core, used for dual VFE + * @overflow_pending flat to specify the overflow is pending for the + * context + * @is_rdi_only_context flag to specify the context has only rdi resource + * @config_done_complete indicator for configuration complete + */ +struct cam_ife_hw_mgr_ctx { + struct list_head list; + struct cam_isp_hw_mgr_ctx common; + + uint32_t ctx_index; + struct cam_ife_hw_mgr *hw_mgr; + uint32_t ctx_in_use; + + struct cam_ife_hw_mgr_res res_list_ife_in; + struct list_head res_list_ife_cid; + struct list_head res_list_ife_csid; + struct list_head res_list_ife_src; + struct cam_ife_hw_mgr_res res_list_ife_out[ + CAM_IFE_HW_OUT_RES_MAX]; + + struct list_head free_res_list; + struct cam_ife_hw_mgr_res res_pool[CAM_IFE_HW_RES_POOL_MAX]; + + uint32_t irq_status0_mask[CAM_IFE_HW_NUM_MAX]; + uint32_t irq_status1_mask[CAM_IFE_HW_NUM_MAX]; + struct ctx_base_info base[CAM_IFE_HW_NUM_MAX]; + uint32_t num_base; + uint32_t cdm_handle; + struct cam_cdm_utils_ops *cdm_ops; + struct cam_cdm_bl_request *cdm_cmd; + + uint32_t sof_cnt[CAM_IFE_HW_NUM_MAX]; + uint32_t epoch_cnt[CAM_IFE_HW_NUM_MAX]; + uint32_t eof_cnt[CAM_IFE_HW_NUM_MAX]; + atomic_t overflow_pending; + uint32_t is_rdi_only_context; + struct completion config_done_complete; +}; + +/** + * struct cam_ife_hw_mgr - IFE HW Manager + * + * @mgr_common: common data for all HW managers + * @csid_devices; csid device instances array. This will be filled by + * HW manager during the initialization. + * @ife_devices: IFE device instances array. This will be filled by + * HW layer during initialization + * @ctx_mutex: mutex for the hw context pool + * @free_ctx_list: free hw context list + * @used_ctx_list: used hw context list + * @ctx_pool: context storage + * @ife_csid_dev_caps csid device capability stored per core + * @ife_dev_caps ife device capability per core + * @work q work queue for IFE hw manager + * @debug_cfg debug configuration + */ +struct cam_ife_hw_mgr { + struct cam_isp_hw_mgr mgr_common; + struct cam_hw_intf *csid_devices[CAM_IFE_CSID_HW_NUM_MAX]; + struct cam_hw_intf *ife_devices[CAM_IFE_HW_NUM_MAX]; + struct cam_soc_reg_map *cdm_reg_map[CAM_IFE_HW_NUM_MAX]; + + struct mutex ctx_mutex; + atomic_t active_ctx_cnt; + struct list_head free_ctx_list; + struct list_head used_ctx_list; + struct cam_ife_hw_mgr_ctx ctx_pool[CAM_CTX_MAX]; + + struct cam_ife_csid_hw_caps ife_csid_dev_caps[ + CAM_IFE_CSID_HW_NUM_MAX]; + struct cam_vfe_hw_get_hw_cap ife_dev_caps[CAM_IFE_HW_NUM_MAX]; + struct cam_req_mgr_core_workq *workq; + struct cam_ife_hw_mgr_debug debug_cfg; +}; + +/** + * cam_ife_hw_mgr_init() + * + * @brief: Initialize the IFE hardware manger. This is the + * etnry functinon for the IFE HW manager. + * + * @hw_mgr_intf: IFE hardware manager object returned + * @iommu_hdl: Iommu handle to be returned + * + */ +int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl); + +/** + * cam_ife_mgr_do_tasklet_buf_done() + * + * @brief: Main tasklet handle function for the buf done event + * + * @handler_priv: Tasklet information handle + * @evt_payload_priv: Event payload for the handler funciton + * + */ +int cam_ife_mgr_do_tasklet_buf_done(void *handler_priv, void *evt_payload_priv); + +/** + * cam_ife_mgr_do_tasklet() + * + * @brief: Main tasklet handle function for mux resource events + * + * @handler_priv: Tasklet information handle + * @evt_payload_priv: Event payload for the handler funciton + * + */ +int cam_ife_mgr_do_tasklet(void *handler_priv, void *evt_payload_priv); + +#endif /* _CAM_IFE_HW_MGR_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.c new file mode 100644 index 000000000000..398b9323417a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.c @@ -0,0 +1,36 @@ +/* Copyright (c) 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 "cam_isp_hw_mgr_intf.h" +#include "cam_ife_hw_mgr.h" +#include "cam_debug_util.h" + + +int cam_isp_hw_mgr_init(struct device_node *of_node, + struct cam_hw_mgr_intf *hw_mgr, int *iommu_hdl) +{ + int rc = 0; + const char *compat_str = NULL; + + rc = of_property_read_string_index(of_node, "arch-compat", 0, + (const char **)&compat_str); + + if (strnstr(compat_str, "ife", strlen(compat_str))) + rc = cam_ife_hw_mgr_init(hw_mgr, iommu_hdl); + else { + CAM_ERR(CAM_ISP, "Invalid ISP hw type"); + rc = -EINVAL; + } + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.h new file mode 100644 index 000000000000..2810dbd54035 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/cam_isp_hw_mgr.h @@ -0,0 +1,74 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ISP_HW_MGR_H_ +#define _CAM_ISP_HW_MGR_H_ + +#include "cam_isp_hw_mgr_intf.h" +#include "cam_tasklet_util.h" + +#define CAM_ISP_HW_NUM_MAX 4 + +/** + * struct cam_isp_hw_mgr_ctx - common acquired context for managers + * + * @takslet_info: assciated tasklet + * @event_cb: call back interface to ISP context. Set during + * acquire device + * @cb_priv: first argument for the call back function + * set during acquire device + * + */ +struct cam_isp_hw_mgr_ctx { + void *tasklet_info; + cam_hw_event_cb_func event_cb[CAM_ISP_HW_EVENT_MAX]; + void *cb_priv; +}; + +/** + * struct cam_isp_hw_mgr - ISP HW Manager common object + * + * @tasklet_pool: Tasklet pool + * @img_iommu_hdl: iommu memory handle for regular image buffer + * @img_iommu_hdl_secure: iommu memory handle for secure image buffer + * @cmd_iommu_hdl: iommu memory handle for regular command buffer + * @cmd_iommu_hdl: iommu memory handle for secure command buffer + * @scratch_buf_range: scratch buffer range (not for IFE) + * @scratch_buf_addr: scratch buffer address (not for IFE) + * + */ +struct cam_isp_hw_mgr { + void *tasklet_pool[CAM_CTX_MAX]; + int img_iommu_hdl; + int img_iommu_hdl_secure; + int cmd_iommu_hdl; + int cmd_iommu_hdl_secure; + uint32_t scratch_buf_range; + dma_addr_t scratch_buf_addr; +}; + +/** + * struct cam_hw_event_recovery_data - Payload for the recovery procedure + * + * @error_type: Error type that causes the recovery + * @affected_core: Array of the hardware cores that are affected + * @affected_ctx: Array of the hardware contexts that are affected + * @no_of_context: Actual number of the affected context + * + */ +struct cam_hw_event_recovery_data { + uint32_t error_type; + uint32_t affected_core[CAM_ISP_HW_NUM_MAX]; + struct cam_ife_hw_mgr_ctx *affected_ctx[CAM_CTX_MAX]; + uint32_t no_of_context; +}; +#endif /* _CAM_ISP_HW_MGR_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/Makefile new file mode 100644 index 000000000000..b88f793e0613 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/Makefile @@ -0,0 +1,12 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_tasklet_util.o cam_isp_packet_parser.o +obj-$(CONFIG_SPECTRA_CAMERA) += irq_controller/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c new file mode 100644 index 000000000000..3606af9af117 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c @@ -0,0 +1,745 @@ +/* Copyright (c) 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 +#include +#include "cam_mem_mgr.h" +#include "cam_isp_hw.h" +#include "cam_vfe_hw_intf.h" +#include "cam_isp_packet_parser.h" +#include "cam_debug_util.h" + +int cam_isp_add_change_base( + struct cam_hw_prepare_update_args *prepare, + struct list_head *res_list_isp_src, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info) +{ + int rc = -EINVAL; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_isp_resource_node *res; + struct cam_isp_hw_get_cmd_update get_base; + struct cam_hw_update_entry *hw_entry; + uint32_t num_ent, i; + + hw_entry = prepare->hw_update_entries; + num_ent = prepare->num_hw_update_entries; + + /* Max one hw entries required for each base */ + if (num_ent + 1 >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_ISP, "Insufficient HW entries :%d %d", + num_ent, prepare->max_hw_update_entries); + return -EINVAL; + } + + list_for_each_entry(hw_mgr_res, res_list_isp_src, list) { + if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + res = hw_mgr_res->hw_res[i]; + if (res->hw_intf->hw_idx != base_idx) + continue; + + get_base.res = res; + get_base.cmd_type = CAM_ISP_HW_CMD_GET_CHANGE_BASE; + get_base.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes/4; + get_base.cmd.size = kmd_buf_info->size - + kmd_buf_info->used_bytes; + + rc = res->hw_intf->hw_ops.process_cmd( + res->hw_intf->hw_priv, + CAM_ISP_HW_CMD_GET_CHANGE_BASE, &get_base, + sizeof(struct cam_isp_hw_get_cmd_update)); + if (rc) + return rc; + + hw_entry[num_ent].handle = kmd_buf_info->handle; + hw_entry[num_ent].len = get_base.cmd.used_bytes; + hw_entry[num_ent].offset = kmd_buf_info->offset; + + kmd_buf_info->used_bytes += get_base.cmd.used_bytes; + kmd_buf_info->offset += get_base.cmd.used_bytes; + num_ent++; + prepare->num_hw_update_entries = num_ent; + + /* return success */ + return 0; + } + } + + return rc; +} + +static int cam_isp_update_dual_config( + struct cam_hw_prepare_update_args *prepare, + struct cam_cmd_buf_desc *cmd_desc, + uint32_t split_id, + uint32_t base_idx, + struct cam_ife_hw_mgr_res *res_list_isp_out, + uint32_t size_isp_out) +{ + int rc = -EINVAL; + struct cam_isp_dual_config *dual_config; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_isp_resource_node *res; + struct cam_isp_hw_dual_isp_update_args dual_isp_update_args; + uint32_t outport_id; + uint32_t ports_plane_idx; + size_t len = 0; + uint32_t *cpu_addr; + uint32_t i, j; + + CAM_DBG(CAM_UTIL, "cmd des size %d, length: %d", + cmd_desc->size, cmd_desc->length); + + rc = cam_packet_util_get_cmd_mem_addr( + cmd_desc->mem_handle, &cpu_addr, &len); + if (rc) + return rc; + + cpu_addr += (cmd_desc->offset / 4); + dual_config = (struct cam_isp_dual_config *)cpu_addr; + + for (i = 0; i < dual_config->num_ports; i++) { + + if (i >= CAM_ISP_IFE_OUT_RES_MAX) { + CAM_ERR(CAM_UTIL, + "failed update for i:%d > size_isp_out:%d", + i, size_isp_out); + return -EINVAL; + } + + hw_mgr_res = &res_list_isp_out[i]; + for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) { + if (!hw_mgr_res->hw_res[j]) + continue; + + if (hw_mgr_res->hw_res[j]->hw_intf->hw_idx != base_idx) + continue; + + res = hw_mgr_res->hw_res[j]; + + if (res->res_id < CAM_ISP_IFE_OUT_RES_BASE || + res->res_id >= CAM_ISP_IFE_OUT_RES_MAX) + continue; + + outport_id = res->res_id & 0xFF; + + ports_plane_idx = (j * (dual_config->num_ports * + CAM_PACKET_MAX_PLANES)) + + (outport_id * CAM_PACKET_MAX_PLANES); + + if (dual_config->stripes[ports_plane_idx].port_id == 0) + continue; + + dual_isp_update_args.split_id = j; + dual_isp_update_args.res = res; + dual_isp_update_args.dual_cfg = dual_config; + rc = res->hw_intf->hw_ops.process_cmd( + res->hw_intf->hw_priv, + CAM_ISP_HW_CMD_STRIPE_UPDATE, + &dual_isp_update_args, + sizeof(struct cam_isp_hw_dual_isp_update_args)); + if (rc) + return rc; + } + } + + return rc; +} + +int cam_isp_add_cmd_buf_update( + struct cam_ife_hw_mgr_res *hw_mgr_res, + uint32_t cmd_type, + uint32_t hw_cmd_type, + uint32_t base_idx, + uint32_t *cmd_buf_addr, + uint32_t kmd_buf_remain_size, + void *cmd_update_data, + uint32_t *bytes_used) +{ + int rc = 0; + struct cam_isp_resource_node *res; + struct cam_isp_hw_get_cmd_update cmd_update; + uint32_t i; + uint32_t total_used_bytes = 0; + + if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) { + CAM_ERR(CAM_ISP, "io res id:%d not valid", + hw_mgr_res->res_type); + return -EINVAL; + } + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + if (hw_mgr_res->hw_res[i]->hw_intf->hw_idx != base_idx) + continue; + + res = hw_mgr_res->hw_res[i]; + cmd_update.res = res; + cmd_update.cmd_type = hw_cmd_type; + cmd_update.cmd.cmd_buf_addr = cmd_buf_addr; + cmd_update.cmd.size = kmd_buf_remain_size; + cmd_update.data = cmd_update_data; + + CAM_DBG(CAM_ISP, "cmd buffer 0x%pK, size %d", + cmd_update.cmd.cmd_buf_addr, + cmd_update.cmd.size); + rc = res->hw_intf->hw_ops.process_cmd( + res->hw_intf->hw_priv, + cmd_update.cmd_type, &cmd_update, + sizeof(struct cam_isp_hw_get_cmd_update)); + + if (rc) { + CAM_ERR(CAM_ISP, "get buf cmd error:%d", + res->res_id); + rc = -ENOMEM; + return rc; + } + + total_used_bytes += cmd_update.cmd.used_bytes; + } + *bytes_used = total_used_bytes; + CAM_DBG(CAM_ISP, "total_used_bytes %u", total_used_bytes); + return rc; +} + +int cam_isp_add_command_buffers( + struct cam_hw_prepare_update_args *prepare, + struct cam_kmd_buf_info *kmd_buf_info, + struct ctx_base_info *base_info, + cam_packet_generic_blob_handler blob_handler_cb, + struct cam_ife_hw_mgr_res *res_list_isp_out, + uint32_t size_isp_out) +{ + int rc = 0; + uint32_t cmd_meta_data, num_ent, i; + uint32_t base_idx; + enum cam_isp_hw_split_id split_id; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct cam_hw_update_entry *hw_entry; + + hw_entry = prepare->hw_update_entries; + split_id = base_info->split_id; + base_idx = base_info->idx; + + /* + * set the cmd_desc to point the first command descriptor in the + * packet + */ + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint8_t *)&prepare->packet->payload + + prepare->packet->cmd_buf_offset); + + CAM_DBG(CAM_ISP, "split id = %d, number of command buffers:%d", + split_id, prepare->packet->num_cmd_buf); + + for (i = 0; i < prepare->packet->num_cmd_buf; i++) { + num_ent = prepare->num_hw_update_entries; + if (!cmd_desc[i].length) + continue; + + /* One hw entry space required for left or right or common */ + if (num_ent + 1 >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_ISP, "Insufficient HW entries :%d %d", + num_ent, prepare->max_hw_update_entries); + return -EINVAL; + } + + rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]); + if (rc) + return rc; + + cmd_meta_data = cmd_desc[i].meta_data; + + CAM_DBG(CAM_ISP, "meta type: %d, split_id: %d", + cmd_meta_data, split_id); + + switch (cmd_meta_data) { + case CAM_ISP_PACKET_META_BASE: + case CAM_ISP_PACKET_META_LEFT: + case CAM_ISP_PACKET_META_DMI_LEFT: + if (split_id == CAM_ISP_HW_SPLIT_LEFT) { + hw_entry[num_ent].len = cmd_desc[i].length; + hw_entry[num_ent].handle = + cmd_desc[i].mem_handle; + hw_entry[num_ent].offset = cmd_desc[i].offset; + + if (cmd_meta_data == + CAM_ISP_PACKET_META_DMI_LEFT) + hw_entry[num_ent].flags = 0x1; + + num_ent++; + } + break; + case CAM_ISP_PACKET_META_RIGHT: + case CAM_ISP_PACKET_META_DMI_RIGHT: + if (split_id == CAM_ISP_HW_SPLIT_RIGHT) { + hw_entry[num_ent].len = cmd_desc[i].length; + hw_entry[num_ent].handle = + cmd_desc[i].mem_handle; + hw_entry[num_ent].offset = cmd_desc[i].offset; + + if (cmd_meta_data == + CAM_ISP_PACKET_META_DMI_RIGHT) + hw_entry[num_ent].flags = 0x1; + num_ent++; + } + break; + case CAM_ISP_PACKET_META_COMMON: + case CAM_ISP_PACKET_META_DMI_COMMON: + hw_entry[num_ent].len = cmd_desc[i].length; + hw_entry[num_ent].handle = + cmd_desc[i].mem_handle; + hw_entry[num_ent].offset = cmd_desc[i].offset; + + if (cmd_meta_data == CAM_ISP_PACKET_META_DMI_COMMON) + hw_entry[num_ent].flags = 0x1; + + num_ent++; + break; + case CAM_ISP_PACKET_META_DUAL_CONFIG: + rc = cam_isp_update_dual_config(prepare, + &cmd_desc[i], split_id, base_idx, + res_list_isp_out, size_isp_out); + + if (rc) + return rc; + break; + case CAM_ISP_PACKET_META_GENERIC_BLOB_LEFT: + if (split_id == CAM_ISP_HW_SPLIT_LEFT) { + struct cam_isp_generic_blob_info blob_info; + + prepare->num_hw_update_entries = num_ent; + blob_info.prepare = prepare; + blob_info.base_info = base_info; + blob_info.kmd_buf_info = kmd_buf_info; + + rc = cam_packet_util_process_generic_cmd_buffer( + &cmd_desc[i], + blob_handler_cb, + &blob_info); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in processing blobs %d", + rc); + return rc; + } + num_ent = prepare->num_hw_update_entries; + } + break; + case CAM_ISP_PACKET_META_GENERIC_BLOB_RIGHT: + if (split_id == CAM_ISP_HW_SPLIT_RIGHT) { + struct cam_isp_generic_blob_info blob_info; + + prepare->num_hw_update_entries = num_ent; + blob_info.prepare = prepare; + blob_info.base_info = base_info; + blob_info.kmd_buf_info = kmd_buf_info; + + rc = cam_packet_util_process_generic_cmd_buffer( + &cmd_desc[i], + blob_handler_cb, + &blob_info); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in processing blobs %d", + rc); + return rc; + } + num_ent = prepare->num_hw_update_entries; + } + break; + case CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON: { + struct cam_isp_generic_blob_info blob_info; + + prepare->num_hw_update_entries = num_ent; + blob_info.prepare = prepare; + blob_info.base_info = base_info; + blob_info.kmd_buf_info = kmd_buf_info; + + rc = cam_packet_util_process_generic_cmd_buffer( + &cmd_desc[i], + blob_handler_cb, + &blob_info); + if (rc) { + CAM_ERR(CAM_ISP, + "Failed in processing blobs %d", rc); + return rc; + } + num_ent = prepare->num_hw_update_entries; + } + break; + default: + CAM_ERR(CAM_ISP, "invalid cdm command meta data %d", + cmd_meta_data); + return -EINVAL; + } + prepare->num_hw_update_entries = num_ent; + } + + return rc; +} + +int cam_isp_add_io_buffers( + int iommu_hdl, + int sec_iommu_hdl, + struct cam_hw_prepare_update_args *prepare, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info, + struct cam_ife_hw_mgr_res *res_list_isp_out, + uint32_t size_isp_out, + bool fill_fence) +{ + int rc = 0; + uint64_t io_addr[CAM_PACKET_MAX_PLANES]; + struct cam_buf_io_cfg *io_cfg; + struct cam_isp_resource_node *res; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_isp_hw_get_cmd_update update_buf; + struct cam_isp_hw_get_wm_update wm_update; + uint32_t kmd_buf_remain_size; + uint32_t i, j, num_out_buf, num_in_buf; + uint32_t res_id_out, res_id_in, plane_id; + uint32_t io_cfg_used_bytes, num_ent; + size_t size; + int32_t hdl; + int mmu_hdl; + bool mode, is_buf_secure; + + io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *) + &prepare->packet->payload + + prepare->packet->io_configs_offset); + num_out_buf = 0; + num_in_buf = 0; + io_cfg_used_bytes = 0; + + /* Max one hw entries required for each base */ + if (prepare->num_hw_update_entries + 1 >= + prepare->max_hw_update_entries) { + CAM_ERR(CAM_ISP, "Insufficient HW entries :%d %d", + prepare->num_hw_update_entries, + prepare->max_hw_update_entries); + return -EINVAL; + } + + for (i = 0; i < prepare->packet->num_io_configs; i++) { + CAM_DBG(CAM_ISP, "======= io config idx %d ============", i); + CAM_DBG(CAM_ISP, "i %d resource_type:%d fence:%d", + i, io_cfg[i].resource_type, io_cfg[i].fence); + CAM_DBG(CAM_ISP, "format: %d", io_cfg[i].format); + CAM_DBG(CAM_ISP, "direction %d", + io_cfg[i].direction); + + if (io_cfg[i].direction == CAM_BUF_OUTPUT) { + res_id_out = io_cfg[i].resource_type & 0xFF; + if (res_id_out >= size_isp_out) { + CAM_ERR(CAM_ISP, "invalid out restype:%x", + io_cfg[i].resource_type); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, + "configure output io with fill fence %d", + fill_fence); + if (fill_fence) { + if (num_out_buf < + prepare->max_out_map_entries) { + prepare->out_map_entries[num_out_buf]. + resource_handle = + io_cfg[i].resource_type; + prepare->out_map_entries[num_out_buf]. + sync_id = io_cfg[i].fence; + num_out_buf++; + } else { + CAM_ERR(CAM_ISP, "ln_out:%d max_ln:%d", + num_out_buf, + prepare->max_out_map_entries); + return -EINVAL; + } + } + + hw_mgr_res = &res_list_isp_out[res_id_out]; + if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) { + CAM_ERR(CAM_ISP, "io res id:%d not valid", + io_cfg[i].resource_type); + return -EINVAL; + } + } else if (io_cfg[i].direction == CAM_BUF_INPUT) { + res_id_in = io_cfg[i].resource_type & 0xFF; + CAM_DBG(CAM_ISP, + "configure input io with fill fence %d", + fill_fence); + if (fill_fence) { + if (num_in_buf < prepare->max_in_map_entries) { + prepare->in_map_entries[num_in_buf]. + resource_handle = + io_cfg[i].resource_type; + prepare->in_map_entries[num_in_buf]. + sync_id = + io_cfg[i].fence; + num_in_buf++; + } else { + CAM_ERR(CAM_ISP, "ln_in:%d imax_ln:%d", + num_in_buf, + prepare->max_in_map_entries); + return -EINVAL; + } + } + continue; + } else { + CAM_ERR(CAM_ISP, "Invalid io config direction :%d", + io_cfg[i].direction); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "setup mem io"); + for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) { + if (!hw_mgr_res->hw_res[j]) + continue; + + if (hw_mgr_res->hw_res[j]->hw_intf->hw_idx != base_idx) + continue; + + res = hw_mgr_res->hw_res[j]; + if (res->res_id != io_cfg[i].resource_type) { + CAM_ERR(CAM_ISP, + "wm err res id:%d io res id:%d", + res->res_id, io_cfg[i].resource_type); + return -EINVAL; + } + + memset(io_addr, 0, sizeof(io_addr)); + + for (plane_id = 0; plane_id < CAM_PACKET_MAX_PLANES; + plane_id++) { + if (!io_cfg[i].mem_handle[plane_id]) + break; + + hdl = io_cfg[i].mem_handle[plane_id]; + if (res->process_cmd(res, + CAM_ISP_HW_CMD_GET_SECURE_MODE, + &mode, + sizeof(bool))) + return -EINVAL; + + is_buf_secure = cam_mem_is_secure_buf(hdl); + if ((mode == CAM_SECURE_MODE_SECURE) && + is_buf_secure) { + mmu_hdl = sec_iommu_hdl; + } else if ( + (mode == CAM_SECURE_MODE_NON_SECURE) && + (!is_buf_secure)) { + mmu_hdl = iommu_hdl; + } else { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Invalid hdl: port mode[%u], buf mode[%u]", + mode, is_buf_secure); + return -EINVAL; + } + + rc = cam_mem_get_io_buf( + io_cfg[i].mem_handle[plane_id], + mmu_hdl, &io_addr[plane_id], &size); + if (rc) { + CAM_ERR(CAM_ISP, + "no io addr for plane%d", + plane_id); + rc = -ENOMEM; + return rc; + } + + if (io_addr[plane_id] >> 32) { + CAM_ERR(CAM_ISP, + "Invalid mapped address"); + rc = -EINVAL; + return rc; + } + + /* need to update with offset */ + io_addr[plane_id] += + io_cfg[i].offsets[plane_id]; + CAM_DBG(CAM_ISP, + "get io_addr for plane %d: 0x%llx", + plane_id, io_addr[plane_id]); + } + if (!plane_id) { + CAM_ERR(CAM_ISP, "No valid planes for res%d", + res->res_id); + rc = -ENOMEM; + return rc; + } + + if ((kmd_buf_info->used_bytes + io_cfg_used_bytes) < + kmd_buf_info->size) { + kmd_buf_remain_size = kmd_buf_info->size - + (kmd_buf_info->used_bytes + + io_cfg_used_bytes); + } else { + CAM_ERR(CAM_ISP, + "no free kmd memory for base %d", + base_idx); + rc = -ENOMEM; + return rc; + } + update_buf.res = res; + update_buf.cmd_type = CAM_ISP_HW_CMD_GET_BUF_UPDATE; + update_buf.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes/4 + + io_cfg_used_bytes/4; + wm_update.image_buf = io_addr; + wm_update.num_buf = plane_id; + wm_update.io_cfg = &io_cfg[i]; + update_buf.cmd.size = kmd_buf_remain_size; + update_buf.wm_update = &wm_update; + + CAM_DBG(CAM_ISP, "cmd buffer 0x%pK, size %d", + update_buf.cmd.cmd_buf_addr, + update_buf.cmd.size); + rc = res->hw_intf->hw_ops.process_cmd( + res->hw_intf->hw_priv, + CAM_ISP_HW_CMD_GET_BUF_UPDATE, &update_buf, + sizeof(struct cam_isp_hw_get_cmd_update)); + + if (rc) { + CAM_ERR(CAM_ISP, "get buf cmd error:%d", + res->res_id); + rc = -ENOMEM; + return rc; + } + io_cfg_used_bytes += update_buf.cmd.used_bytes; + } + } + + CAM_DBG(CAM_ISP, "io_cfg_used_bytes %d, fill_fence %d", + io_cfg_used_bytes, fill_fence); + if (io_cfg_used_bytes) { + /* Update the HW entries */ + num_ent = prepare->num_hw_update_entries; + prepare->hw_update_entries[num_ent].handle = + kmd_buf_info->handle; + prepare->hw_update_entries[num_ent].len = io_cfg_used_bytes; + prepare->hw_update_entries[num_ent].offset = + kmd_buf_info->offset; + num_ent++; + + kmd_buf_info->used_bytes += io_cfg_used_bytes; + kmd_buf_info->offset += io_cfg_used_bytes; + prepare->num_hw_update_entries = num_ent; + } + + if (fill_fence) { + prepare->num_out_map_entries = num_out_buf; + prepare->num_in_map_entries = num_in_buf; + } + + return rc; +} + + +int cam_isp_add_reg_update( + struct cam_hw_prepare_update_args *prepare, + struct list_head *res_list_isp_src, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info) +{ + int rc = -EINVAL; + struct cam_isp_resource_node *res; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_update_entry *hw_entry; + struct cam_isp_hw_get_cmd_update get_regup; + uint32_t kmd_buf_remain_size, num_ent, i, reg_update_size; + + hw_entry = prepare->hw_update_entries; + /* Max one hw entries required for each base */ + if (prepare->num_hw_update_entries + 1 >= + prepare->max_hw_update_entries) { + CAM_ERR(CAM_ISP, "Insufficient HW entries :%d %d", + prepare->num_hw_update_entries, + prepare->max_hw_update_entries); + return -EINVAL; + } + + reg_update_size = 0; + list_for_each_entry(hw_mgr_res, res_list_isp_src, list) { + if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) + continue; + + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + res = hw_mgr_res->hw_res[i]; + if (res->hw_intf->hw_idx != base_idx) + continue; + + if (kmd_buf_info->size > (kmd_buf_info->used_bytes + + reg_update_size)) { + kmd_buf_remain_size = kmd_buf_info->size - + (kmd_buf_info->used_bytes + + reg_update_size); + } else { + CAM_ERR(CAM_ISP, "no free mem %d %d %d", + base_idx, kmd_buf_info->size, + kmd_buf_info->used_bytes + + reg_update_size); + rc = -EINVAL; + return rc; + } + + get_regup.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes/4 + + reg_update_size/4; + get_regup.cmd.size = kmd_buf_remain_size; + get_regup.cmd_type = CAM_ISP_HW_CMD_GET_REG_UPDATE; + get_regup.res = res; + + rc = res->hw_intf->hw_ops.process_cmd( + res->hw_intf->hw_priv, + CAM_ISP_HW_CMD_GET_REG_UPDATE, &get_regup, + sizeof(struct cam_isp_hw_get_cmd_update)); + if (rc) + return rc; + + CAM_DBG(CAM_ISP, "Reg update added for res %d hw_id %d", + res->res_id, res->hw_intf->hw_idx); + reg_update_size += get_regup.cmd.used_bytes; + } + } + + if (reg_update_size) { + /* Update the HW entries */ + num_ent = prepare->num_hw_update_entries; + prepare->hw_update_entries[num_ent].handle = + kmd_buf_info->handle; + prepare->hw_update_entries[num_ent].len = reg_update_size; + prepare->hw_update_entries[num_ent].offset = + kmd_buf_info->offset; + num_ent++; + + kmd_buf_info->used_bytes += reg_update_size; + kmd_buf_info->offset += reg_update_size; + prepare->num_hw_update_entries = num_ent; + /* reg update is success return status 0 */ + rc = 0; + } + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c new file mode 100644 index 000000000000..8c71ed904ad9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c @@ -0,0 +1,329 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include "cam_tasklet_util.h" +#include "cam_irq_controller.h" +#include "cam_debug_util.h" + +#define CAM_TASKLETQ_SIZE 256 + +static void cam_tasklet_action(unsigned long data); + +/** + * struct cam_tasklet_queue_cmd: + * @Brief: Structure associated with each slot in the + * tasklet queue + * + * @list: list_head member for each entry in queue + * @payload: Payload structure for the event. This will be + * passed to the handler function + * @bottom_half_handler: Function pointer for event handler in bottom + * half context + * + */ +struct cam_tasklet_queue_cmd { + struct list_head list; + void *payload; + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler; +}; + +/** + * struct cam_tasklet_info: + * @Brief: Tasklet private structure + * + * @list: list_head member for each tasklet + * @index: Instance id for the tasklet + * @tasklet_lock: Spin lock + * @tasklet_active: Atomic variable to control tasklet state + * @tasklet: Tasklet structure used to schedule bottom half + * @free_cmd_list: List of free tasklet queue cmd for use + * @used_cmd_list: List of used tasklet queue cmd + * @cmd_queue: Array of tasklet cmd for storage + * @ctx_priv: Private data passed to the handling function + * + */ +struct cam_tasklet_info { + struct list_head list; + uint32_t index; + spinlock_t tasklet_lock; + atomic_t tasklet_active; + struct tasklet_struct tasklet; + + struct list_head free_cmd_list; + struct list_head used_cmd_list; + struct cam_tasklet_queue_cmd cmd_queue[CAM_TASKLETQ_SIZE]; + + void *ctx_priv; +}; + +/** + * cam_tasklet_get_cmd() + * + * @brief: Get free cmd from tasklet + * + * @tasklet: Tasklet Info structure to get cmd from + * @tasklet_cmd: Return tasklet_cmd pointer if successful + * + * @return: 0: Success + * Negative: Failure + */ +int cam_tasklet_get_cmd( + void *bottom_half, + void **bh_cmd) +{ + int rc = 0; + unsigned long flags; + + struct cam_tasklet_info *tasklet = bottom_half; + struct cam_tasklet_queue_cmd *tasklet_cmd = NULL; + + if (!atomic_read(&tasklet->tasklet_active)) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Tasklet is not active!\n"); + rc = -EPIPE; + return rc; + } + + spin_lock_irqsave(&tasklet->tasklet_lock, flags); + if (list_empty(&tasklet->free_cmd_list)) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "No more free tasklet cmd!\n"); + rc = -ENODEV; + goto spin_unlock; + } else { + tasklet_cmd = list_first_entry(&tasklet->free_cmd_list, + struct cam_tasklet_queue_cmd, list); + list_del_init(&(tasklet_cmd)->list); + *bh_cmd = tasklet_cmd; + } + +spin_unlock: + spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); + return rc; +} + +/** + * cam_tasklet_put_cmd() + * + * @brief: Put back cmd to free list + * + * @tasklet: Tasklet Info structure to put cmd into + * @tasklet_cmd: tasklet_cmd pointer that needs to be put back + * + * @return: Void + */ +int cam_tasklet_put_cmd( + void *bottom_half, + void **bh_cmd) +{ + unsigned long flags; + struct cam_tasklet_info *tasklet = bottom_half; + struct cam_tasklet_queue_cmd *tasklet_cmd = *bh_cmd; + + spin_lock_irqsave(&tasklet->tasklet_lock, flags); + list_add_tail(&(tasklet_cmd)->list, + &tasklet->free_cmd_list); + spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); + + return 0; +} + +/** + * cam_tasklet_dequeue_cmd() + * + * @brief: Initialize the tasklet info structure + * + * @hw_mgr_ctx: Private Ctx data that will be passed to the handler + * function + * @idx: Index of tasklet used as identity + * @tasklet_action: Tasklet callback function that will be called + * when tasklet runs on CPU + * + * @return: 0: Success + * Negative: Failure + */ +static int cam_tasklet_dequeue_cmd( + struct cam_tasklet_info *tasklet, + struct cam_tasklet_queue_cmd **tasklet_cmd) +{ + int rc = 0; + unsigned long flags; + + *tasklet_cmd = NULL; + + CAM_DBG(CAM_ISP, "Dequeue before lock."); + spin_lock_irqsave(&tasklet->tasklet_lock, flags); + if (list_empty(&tasklet->used_cmd_list)) { + CAM_DBG(CAM_ISP, "End of list reached. Exit"); + rc = -ENODEV; + goto spin_unlock; + } else { + *tasklet_cmd = list_first_entry(&tasklet->used_cmd_list, + struct cam_tasklet_queue_cmd, list); + list_del_init(&(*tasklet_cmd)->list); + CAM_DBG(CAM_ISP, "Dequeue Successful"); + } + +spin_unlock: + spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); + return rc; +} + +int cam_tasklet_enqueue_cmd( + void *bottom_half, + void *bh_cmd, + void *evt_payload_priv, + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler) +{ + unsigned long flags; + int rc = 0; + struct cam_tasklet_queue_cmd *tasklet_cmd = bh_cmd; + struct cam_tasklet_info *tasklet = bottom_half; + + if (!bottom_half) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "NULL bottom half"); + return -EINVAL; + } + + if (!bh_cmd) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "NULL bh cmd"); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "Enqueue tasklet cmd"); + tasklet_cmd->bottom_half_handler = bottom_half_handler; + tasklet_cmd->payload = evt_payload_priv; + spin_lock_irqsave(&tasklet->tasklet_lock, flags); + list_add_tail(&tasklet_cmd->list, + &tasklet->used_cmd_list); + spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); + tasklet_schedule(&tasklet->tasklet); + + return rc; +} + +int cam_tasklet_init( + void **tasklet_info, + void *hw_mgr_ctx, + uint32_t idx) +{ + int i; + struct cam_tasklet_info *tasklet = NULL; + + tasklet = kzalloc(sizeof(struct cam_tasklet_info), GFP_KERNEL); + if (!tasklet) { + CAM_DBG(CAM_ISP, + "Error! Unable to allocate memory for tasklet"); + *tasklet_info = NULL; + return -ENOMEM; + } + + tasklet->ctx_priv = hw_mgr_ctx; + tasklet->index = idx; + spin_lock_init(&tasklet->tasklet_lock); + memset(tasklet->cmd_queue, 0, sizeof(tasklet->cmd_queue)); + INIT_LIST_HEAD(&tasklet->free_cmd_list); + INIT_LIST_HEAD(&tasklet->used_cmd_list); + for (i = 0; i < CAM_TASKLETQ_SIZE; i++) { + INIT_LIST_HEAD(&tasklet->cmd_queue[i].list); + list_add_tail(&tasklet->cmd_queue[i].list, + &tasklet->free_cmd_list); + } + tasklet_init(&tasklet->tasklet, cam_tasklet_action, + (unsigned long)tasklet); + tasklet_disable(&tasklet->tasklet); + + *tasklet_info = tasklet; + + return 0; +} + +void cam_tasklet_deinit(void **tasklet_info) +{ + struct cam_tasklet_info *tasklet = *tasklet_info; + + if (atomic_read(&tasklet->tasklet_active)) { + atomic_set(&tasklet->tasklet_active, 0); + tasklet_kill(&tasklet->tasklet); + tasklet_disable(&tasklet->tasklet); + } + kfree(tasklet); + *tasklet_info = NULL; +} + +static inline void cam_tasklet_flush(struct cam_tasklet_info *tasklet_info) +{ + cam_tasklet_action((unsigned long) tasklet_info); +} + +int cam_tasklet_start(void *tasklet_info) +{ + struct cam_tasklet_info *tasklet = tasklet_info; + int i = 0; + + if (atomic_read(&tasklet->tasklet_active)) { + CAM_ERR(CAM_ISP, "Tasklet already active. idx = %d", + tasklet->index); + return -EBUSY; + } + + /* clean up the command queue first */ + for (i = 0; i < CAM_TASKLETQ_SIZE; i++) { + list_del_init(&tasklet->cmd_queue[i].list); + list_add_tail(&tasklet->cmd_queue[i].list, + &tasklet->free_cmd_list); + } + + atomic_set(&tasklet->tasklet_active, 1); + + tasklet_enable(&tasklet->tasklet); + + return 0; +} + +void cam_tasklet_stop(void *tasklet_info) +{ + struct cam_tasklet_info *tasklet = tasklet_info; + + atomic_set(&tasklet->tasklet_active, 0); + tasklet_kill(&tasklet->tasklet); + tasklet_disable(&tasklet->tasklet); + cam_tasklet_flush(tasklet); +} + +/* + * cam_tasklet_action() + * + * @brief: Process function that will be called when tasklet runs + * on CPU + * + * @data: Tasklet Info structure that is passed in tasklet_init + * + * @return: Void + */ +static void cam_tasklet_action(unsigned long data) +{ + struct cam_tasklet_info *tasklet_info = NULL; + struct cam_tasklet_queue_cmd *tasklet_cmd = NULL; + + tasklet_info = (struct cam_tasklet_info *)data; + + while (!cam_tasklet_dequeue_cmd(tasklet_info, &tasklet_cmd)) { + tasklet_cmd->bottom_half_handler(tasklet_info->ctx_priv, + tasklet_cmd->payload); + cam_tasklet_put_cmd(tasklet_info, (void **)(&tasklet_cmd)); + } +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h new file mode 100644 index 000000000000..e3f2ce26e1c5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h @@ -0,0 +1,162 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ISP_HW_PARSER_H_ +#define _CAM_ISP_HW_PARSER_H_ + +#include +#include +#include "cam_isp_hw_mgr_intf.h" +#include "cam_ife_hw_mgr.h" +#include "cam_hw_intf.h" +#include "cam_packet_util.h" + +/* + * struct cam_isp_generic_blob_info + * + * @prepare: Payload for prepare command + * @ctx_base_info: Base hardware information for the context + * @kmd_buf_info: Kmd buffer to store the custom cmd data + */ +struct cam_isp_generic_blob_info { + struct cam_hw_prepare_update_args *prepare; + struct ctx_base_info *base_info; + struct cam_kmd_buf_info *kmd_buf_info; +}; + +/* + * cam_isp_add_change_base() + * + * @brief Add change base in the hw entries list + * processe the isp source list get the change base from + * ISP HW instance + * + * @prepare: Contain the packet and HW update variables + * @res_list_isp_src: Resource list for IFE/VFE source + * @base_idx: Base or dev index of the IFE/VFE HW instance for + * which change change base need to be added + * @kmd_buf_info: Kmd buffer to store the change base command + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_isp_add_change_base( + struct cam_hw_prepare_update_args *prepare, + struct list_head *res_list_isp_src, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info); + +/* + * cam_isp_add_cmd_buf_update() + * + * @brief Add command buffer in the HW entries list for given + * Blob Data. + * + * @hw_mgr_res: HW resource to get the update from + * @cmd_type: Cmd type to get update for + * @hw_cmd_type: HW Cmd type corresponding to cmd_type + * @base_idx: Base hardware index + * @cmd_buf_addr: Cpu buf addr of kmd scratch buffer + * @kmd_buf_remain_size: Remaining size left for cmd buffer update + * @cmd_update_data: Data needed by HW to process the cmd and provide + * cmd buffer + * @bytes_used: Address of the field to be populated with + * total bytes used as output to caller + * + * @return: Negative for Failure + * otherwise returns bytes used + */ +int cam_isp_add_cmd_buf_update( + struct cam_ife_hw_mgr_res *hw_mgr_res, + uint32_t cmd_type, + uint32_t hw_cmd_type, + uint32_t base_idx, + uint32_t *cmd_buf_addr, + uint32_t kmd_buf_remain_size, + void *cmd_update_data, + uint32_t *bytes_used); + +/* + * cam_isp_add_command_buffers() + * + * @brief Add command buffer in the HW entries list for given + * left or right VFE/IFE instance. + * + * @prepare: Contain the packet and HW update variables + * @kmd_buf_info: KMD buffer to store the custom cmd data + * @base_info: base hardware information + * @blob_handler_cb: Call_back_function for Meta handling + * @res_list_isp_out: IFE /VFE out resource list + * @size_isp_out: Size of the res_list_isp_out array + * + * @return: 0 for success + * Negative for Failure + */ +int cam_isp_add_command_buffers( + struct cam_hw_prepare_update_args *prepare, + struct cam_kmd_buf_info *kmd_buf_info, + struct ctx_base_info *base_info, + cam_packet_generic_blob_handler blob_handler_cb, + struct cam_ife_hw_mgr_res *res_list_isp_out, + uint32_t size_isp_out); + +/* + * cam_isp_add_io_buffers() + * + * @brief Add io buffer configurations in the HW entries list + * processe the io configurations based on the base + * index and update the HW entries list + * + * @iommu_hdl: Iommu handle to get the IO buf from memory manager + * @sec_iommu_hdl: Secure iommu handle to get the IO buf from + * memory manager + * @prepare: Contain the packet and HW update variables + * @base_idx: Base or dev index of the IFE/VFE HW instance + * @kmd_buf_info: Kmd buffer to store the change base command + * @res_list_isp_out: IFE /VFE out resource list + * @size_isp_out: Size of the res_list_isp_out array + * @fill_fence: If true, Fence map table will be filled + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_isp_add_io_buffers( + int iommu_hdl, + int sec_iommu_hdl, + struct cam_hw_prepare_update_args *prepare, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info, + struct cam_ife_hw_mgr_res *res_list_isp_out, + uint32_t size_isp_out, + bool fill_fence); + +/* + * cam_isp_add_reg_update() + * + * @brief Add reg update in the hw entries list + * processe the isp source list get the reg update from + * ISP HW instance + * + * @prepare: Contain the packet and HW update variables + * @res_list_isp_src: Resource list for IFE/VFE source + * @base_idx: Base or dev index of the IFE/VFE HW instance + * @kmd_buf_info: Kmd buffer to store the change base command + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_isp_add_reg_update( + struct cam_hw_prepare_update_args *prepare, + struct list_head *res_list_isp_src, + uint32_t base_idx, + struct cam_kmd_buf_info *kmd_buf_info); + +#endif /*_CAM_ISP_HW_PARSER_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h new file mode 100644 index 000000000000..76ec4e613b65 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h @@ -0,0 +1,99 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_TASKLET_UTIL_H_ +#define _CAM_TASKLET_UTIL_H_ + +#include "cam_irq_controller.h" + +/* + * cam_tasklet_init() + * + * @brief: Initialize the tasklet info structure + * + * @tasklet: Tasklet to initialize + * @hw_mgr_ctx: Private Ctx data that will be passed to the handler + * function + * @idx: Index of tasklet used as identity + * + * @return: 0: Success + * Negative: Failure + */ +int cam_tasklet_init( + void **tasklet, + void *hw_mgr_ctx, + uint32_t idx); + +/* + * cam_tasklet_deinit() + * + * @brief: Deinitialize the tasklet info structure + * + * @tasklet: Tasklet to deinitialize + * + * @return: Void + */ +void cam_tasklet_deinit(void **tasklet); + +/* + * cam_tasklet_start() + * + * @brief: Enable the tasklet to be scheduled and run. + * Caller should make sure this function is called + * before trying to enqueue. + * + * @tasklet: Tasklet to start + * + * @return: 0: Success + * Negative: Failure + */ +int cam_tasklet_start(void *tasklet); + +/* + * cam_tasklet_stop() + * + * @brief: Disable the tasklet so it can no longer be scheduled. + * Need to enable again to run. + * + * @tasklet: Tasklet to stop + * + * @return: Void + */ +void cam_tasklet_stop(void *tasklet); + +/* + * cam_tasklet_enqueue_cmd() + * + * @brief: Enqueue the tasklet_cmd to used list + * + * @bottom_half: Tasklet info to enqueue onto + * @handler_priv: Private Handler data that will be passed to the + * handler function + * @evt_payload_priv: Event payload that will be passed to the handler + * function + * @bottom_half_handler: Callback function that will be called by tasklet + * for handling event + * + * @return: 0: Success + * Negative: Failure + */ +int cam_tasklet_enqueue_cmd( + void *bottom_half, + void *bh_cmd, + void *evt_payload_priv, + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler); + +int cam_tasklet_get_cmd(void *bottom_half, void **bh_cmd); + +int cam_tasklet_put_cmd(void *bottom_half, void **bh_cmd); + +#endif /* _CAM_TASKLET_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile new file mode 100644 index 000000000000..1c2f3d0efacd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_irq_controller.o \ No newline at end of file diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c new file mode 100644 index 000000000000..f8b8d0b09b12 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c @@ -0,0 +1,728 @@ +/* Copyright (c) 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 +#include +#include +#include +#include "cam_io_util.h" +#include "cam_irq_controller.h" +#include "cam_debug_util.h" + +/** + * struct cam_irq_evt_handler: + * @Brief: Event handler information + * + * @priority: Priority level of this event + * @evt_bit_mask_arr: evt_bit_mask that has the bits set for IRQs to + * subscribe for + * @handler_priv: Private data that will be passed to the Top/Bottom + * Half handler function + * @top_half_handler: Top half Handler callback function + * @bottom_half_handler: Bottom half Handler callback function + * @bottom_half: Pointer to bottom_half implementation on which to + * enqueue the event for further handling + * @bottom_half_enqueue_func: + * Function used to enqueue the bottom_half event + * @list_node: list_head struct used for overall handler List + * @th_list_node: list_head struct used for top half handler List + */ +struct cam_irq_evt_handler { + enum cam_irq_priority_level priority; + uint32_t *evt_bit_mask_arr; + void *handler_priv; + CAM_IRQ_HANDLER_TOP_HALF top_half_handler; + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler; + void *bottom_half; + struct cam_irq_bh_api irq_bh_api; + struct list_head list_node; + struct list_head th_list_node; + int index; +}; + +/** + * struct cam_irq_register_obj: + * @Brief: Structure containing information related to + * a particular register Set + * + * @index: Index of set in Array + * @mask_reg_offset: Offset of IRQ MASK register + * @clear_reg_offset: Offset of IRQ CLEAR register + * @status_reg_offset: Offset of IRQ STATUS register + * @top_half_enable_mask: Array of enabled bit_mask sorted by priority + */ +struct cam_irq_register_obj { + uint32_t index; + uint32_t mask_reg_offset; + uint32_t clear_reg_offset; + uint32_t status_reg_offset; + uint32_t top_half_enable_mask[CAM_IRQ_PRIORITY_MAX]; +}; + +/** + * struct cam_irq_controller: + * + * @brief: IRQ Controller structure. + * + * @name: Name of IRQ Controller block + * @mem_base: Mapped base address of register space to which + * register offsets are added to access registers + * @num_registers: Number of sets(mask/clear/status) of IRQ registers + * @irq_register_arr: Array of Register object associated with this + * Controller + * @irq_status_arr: Array of IRQ Status values + * @global_clear_offset: Offset of Global IRQ Clear register. This register + * contains the BIT that needs to be set for the CLEAR + * to take effect + * @global_clear_bitmask: Bitmask needed to be used in Global Clear register + * for Clear IRQ cmd to take effect + * @evt_handler_list_head: List of all event handlers + * @th_list_head: List of handlers sorted by priority + * @hdl_idx: Unique identity of handler assigned on Subscribe. + * Used to Unsubscribe. + * @lock: Lock for use by controller + */ +struct cam_irq_controller { + const char *name; + void __iomem *mem_base; + uint32_t num_registers; + struct cam_irq_register_obj *irq_register_arr; + uint32_t *irq_status_arr; + uint32_t global_clear_offset; + uint32_t global_clear_bitmask; + struct list_head evt_handler_list_head; + struct list_head th_list_head[CAM_IRQ_PRIORITY_MAX]; + uint32_t hdl_idx; + spinlock_t lock; + struct cam_irq_th_payload th_payload; +}; + +int cam_irq_controller_deinit(void **irq_controller) +{ + struct cam_irq_controller *controller = *irq_controller; + struct cam_irq_evt_handler *evt_handler = NULL; + + while (!list_empty(&controller->evt_handler_list_head)) { + evt_handler = list_first_entry( + &controller->evt_handler_list_head, + struct cam_irq_evt_handler, list_node); + list_del_init(&evt_handler->list_node); + kfree(evt_handler->evt_bit_mask_arr); + kfree(evt_handler); + } + + kfree(controller->th_payload.evt_status_arr); + kfree(controller->irq_status_arr); + kfree(controller->irq_register_arr); + kfree(controller); + *irq_controller = NULL; + return 0; +} + +int cam_irq_controller_init(const char *name, + void __iomem *mem_base, + struct cam_irq_controller_reg_info *register_info, + void **irq_controller) +{ + struct cam_irq_controller *controller = NULL; + int i, rc = 0; + + *irq_controller = NULL; + + if (!register_info->num_registers || !register_info->irq_reg_set || + !name || !mem_base) { + CAM_ERR(CAM_ISP, "Invalid parameters"); + rc = -EINVAL; + return rc; + } + + controller = kzalloc(sizeof(struct cam_irq_controller), GFP_KERNEL); + if (!controller) { + CAM_DBG(CAM_ISP, "Failed to allocate IRQ Controller"); + return -ENOMEM; + } + + controller->irq_register_arr = kzalloc(register_info->num_registers * + sizeof(struct cam_irq_register_obj), GFP_KERNEL); + if (!controller->irq_register_arr) { + CAM_DBG(CAM_ISP, "Failed to allocate IRQ register Arr"); + rc = -ENOMEM; + goto reg_alloc_error; + } + + controller->irq_status_arr = kzalloc(register_info->num_registers * + sizeof(uint32_t), GFP_KERNEL); + if (!controller->irq_status_arr) { + CAM_DBG(CAM_ISP, "Failed to allocate IRQ status Arr"); + rc = -ENOMEM; + goto status_alloc_error; + } + + controller->th_payload.evt_status_arr = + kzalloc(register_info->num_registers * sizeof(uint32_t), + GFP_KERNEL); + if (!controller->th_payload.evt_status_arr) { + CAM_DBG(CAM_ISP, "Failed to allocate BH payload bit mask Arr"); + rc = -ENOMEM; + goto evt_mask_alloc_error; + } + + controller->name = name; + + CAM_DBG(CAM_ISP, "num_registers: %d", register_info->num_registers); + for (i = 0; i < register_info->num_registers; i++) { + controller->irq_register_arr[i].index = i; + controller->irq_register_arr[i].mask_reg_offset = + register_info->irq_reg_set[i].mask_reg_offset; + controller->irq_register_arr[i].clear_reg_offset = + register_info->irq_reg_set[i].clear_reg_offset; + controller->irq_register_arr[i].status_reg_offset = + register_info->irq_reg_set[i].status_reg_offset; + CAM_DBG(CAM_ISP, "i %d mask_reg_offset: 0x%x", i, + controller->irq_register_arr[i].mask_reg_offset); + CAM_DBG(CAM_ISP, "i %d clear_reg_offset: 0x%x", i, + controller->irq_register_arr[i].clear_reg_offset); + CAM_DBG(CAM_ISP, "i %d status_reg_offset: 0x%x", i, + controller->irq_register_arr[i].status_reg_offset); + } + controller->num_registers = register_info->num_registers; + controller->global_clear_bitmask = register_info->global_clear_bitmask; + controller->global_clear_offset = register_info->global_clear_offset; + controller->mem_base = mem_base; + + CAM_DBG(CAM_ISP, "global_clear_bitmask: 0x%x", + controller->global_clear_bitmask); + CAM_DBG(CAM_ISP, "global_clear_offset: 0x%x", + controller->global_clear_offset); + CAM_DBG(CAM_ISP, "mem_base: %pK", (void __iomem *)controller->mem_base); + + INIT_LIST_HEAD(&controller->evt_handler_list_head); + for (i = 0; i < CAM_IRQ_PRIORITY_MAX; i++) + INIT_LIST_HEAD(&controller->th_list_head[i]); + + spin_lock_init(&controller->lock); + + controller->hdl_idx = 1; + *irq_controller = controller; + + return rc; + +evt_mask_alloc_error: + kfree(controller->irq_status_arr); +status_alloc_error: + kfree(controller->irq_register_arr); +reg_alloc_error: + kfree(controller); + + return rc; +} + +int cam_irq_controller_subscribe_irq(void *irq_controller, + enum cam_irq_priority_level priority, + uint32_t *evt_bit_mask_arr, + void *handler_priv, + CAM_IRQ_HANDLER_TOP_HALF top_half_handler, + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler, + void *bottom_half, + struct cam_irq_bh_api *irq_bh_api) +{ + struct cam_irq_controller *controller = irq_controller; + struct cam_irq_evt_handler *evt_handler = NULL; + int i; + int rc = 0; + uint32_t irq_mask; + unsigned long flags = 0; + bool need_lock; + + if (!controller || !handler_priv || !evt_bit_mask_arr) { + CAM_ERR(CAM_ISP, + "Inval params: ctlr=%pK hdl_priv=%pK bit_mask_arr=%pK", + controller, handler_priv, evt_bit_mask_arr); + return -EINVAL; + } + + if (!top_half_handler) { + CAM_ERR(CAM_ISP, "Missing top half handler"); + return -EINVAL; + } + + if (bottom_half_handler && + (!bottom_half || !irq_bh_api)) { + CAM_ERR(CAM_ISP, + "Invalid params: bh_handler=%pK bh=%pK bh_enq_f=%pK", + bottom_half_handler, + bottom_half, + irq_bh_api); + return -EINVAL; + } + + if (priority >= CAM_IRQ_PRIORITY_MAX) { + CAM_ERR(CAM_ISP, "Invalid priority=%u, max=%u", priority, + CAM_IRQ_PRIORITY_MAX); + return -EINVAL; + } + + evt_handler = kzalloc(sizeof(struct cam_irq_evt_handler), GFP_KERNEL); + if (!evt_handler) { + CAM_DBG(CAM_ISP, "Error allocating hlist_node"); + return -ENOMEM; + } + + evt_handler->evt_bit_mask_arr = kzalloc(sizeof(uint32_t) * + controller->num_registers, GFP_KERNEL); + if (!evt_handler->evt_bit_mask_arr) { + CAM_DBG(CAM_ISP, "Error allocating hlist_node"); + rc = -ENOMEM; + goto free_evt_handler; + } + + INIT_LIST_HEAD(&evt_handler->list_node); + INIT_LIST_HEAD(&evt_handler->th_list_node); + + for (i = 0; i < controller->num_registers; i++) + evt_handler->evt_bit_mask_arr[i] = evt_bit_mask_arr[i]; + + evt_handler->priority = priority; + evt_handler->handler_priv = handler_priv; + evt_handler->top_half_handler = top_half_handler; + evt_handler->bottom_half_handler = bottom_half_handler; + evt_handler->bottom_half = bottom_half; + evt_handler->index = controller->hdl_idx++; + + if (irq_bh_api) + evt_handler->irq_bh_api = *irq_bh_api; + + /* Avoid rollover to negative values */ + if (controller->hdl_idx > 0x3FFFFFFF) + controller->hdl_idx = 1; + + need_lock = !in_irq(); + if (need_lock) + spin_lock_irqsave(&controller->lock, flags); + for (i = 0; i < controller->num_registers; i++) { + controller->irq_register_arr[i].top_half_enable_mask[priority] + |= evt_bit_mask_arr[i]; + + irq_mask = cam_io_r_mb(controller->mem_base + + controller->irq_register_arr[i].mask_reg_offset); + irq_mask |= evt_bit_mask_arr[i]; + + cam_io_w_mb(irq_mask, controller->mem_base + + controller->irq_register_arr[i].mask_reg_offset); + } + + list_add_tail(&evt_handler->list_node, + &controller->evt_handler_list_head); + list_add_tail(&evt_handler->th_list_node, + &controller->th_list_head[priority]); + + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + + return evt_handler->index; + +free_evt_handler: + kfree(evt_handler); + evt_handler = NULL; + + return rc; +} + +int cam_irq_controller_enable_irq(void *irq_controller, uint32_t handle) +{ + struct cam_irq_controller *controller = irq_controller; + struct cam_irq_evt_handler *evt_handler = NULL; + struct cam_irq_evt_handler *evt_handler_temp; + unsigned long flags = 0; + unsigned int i; + uint32_t irq_mask; + uint32_t found = 0; + int rc = -EINVAL; + bool need_lock; + + if (!controller) + return rc; + + need_lock = !in_irq(); + if (need_lock) + spin_lock_irqsave(&controller->lock, flags); + + list_for_each_entry_safe(evt_handler, evt_handler_temp, + &controller->evt_handler_list_head, list_node) { + if (evt_handler->index == handle) { + CAM_DBG(CAM_ISP, "enable item %d", handle); + found = 1; + rc = 0; + break; + } + } + + if (!found) { + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + return rc; + } + + for (i = 0; i < controller->num_registers; i++) { + controller->irq_register_arr[i]. + top_half_enable_mask[evt_handler->priority] |= + evt_handler->evt_bit_mask_arr[i]; + + irq_mask = cam_io_r_mb(controller->mem_base + + controller->irq_register_arr[i]. + mask_reg_offset); + irq_mask |= evt_handler->evt_bit_mask_arr[i]; + + cam_io_w_mb(irq_mask, controller->mem_base + + controller->irq_register_arr[i].mask_reg_offset); + } + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + + return rc; +} + +int cam_irq_controller_disable_irq(void *irq_controller, uint32_t handle) +{ + struct cam_irq_controller *controller = irq_controller; + struct cam_irq_evt_handler *evt_handler = NULL; + struct cam_irq_evt_handler *evt_handler_temp; + unsigned long flags = 0; + unsigned int i; + uint32_t irq_mask; + uint32_t found = 0; + int rc = -EINVAL; + bool need_lock; + + if (!controller) + return rc; + + need_lock = !in_irq(); + if (need_lock) + spin_lock_irqsave(&controller->lock, flags); + + list_for_each_entry_safe(evt_handler, evt_handler_temp, + &controller->evt_handler_list_head, list_node) { + if (evt_handler->index == handle) { + CAM_DBG(CAM_ISP, "disable item %d", handle); + found = 1; + rc = 0; + break; + } + } + + if (!found) { + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + return rc; + } + + for (i = 0; i < controller->num_registers; i++) { + controller->irq_register_arr[i]. + top_half_enable_mask[evt_handler->priority] &= + ~(evt_handler->evt_bit_mask_arr[i]); + + irq_mask = cam_io_r_mb(controller->mem_base + + controller->irq_register_arr[i]. + mask_reg_offset); + CAM_INFO(CAM_ISP, "irq_mask 0x%x before disable 0x%x", + controller->irq_register_arr[i].mask_reg_offset, + irq_mask); + irq_mask &= ~(evt_handler->evt_bit_mask_arr[i]); + + cam_io_w_mb(irq_mask, controller->mem_base + + controller->irq_register_arr[i]. + mask_reg_offset); + CAM_INFO(CAM_ISP, "irq_mask 0x%x after disable 0x%x", + controller->irq_register_arr[i].mask_reg_offset, + irq_mask); + + /* Clear the IRQ bits of this handler */ + cam_io_w_mb(evt_handler->evt_bit_mask_arr[i], + controller->mem_base + + controller->irq_register_arr[i]. + clear_reg_offset); + + if (controller->global_clear_offset) + cam_io_w_mb( + controller->global_clear_bitmask, + controller->mem_base + + controller->global_clear_offset); + } + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + + return rc; +} + +int cam_irq_controller_unsubscribe_irq(void *irq_controller, + uint32_t handle) +{ + struct cam_irq_controller *controller = irq_controller; + struct cam_irq_evt_handler *evt_handler = NULL; + struct cam_irq_evt_handler *evt_handler_temp; + uint32_t i; + uint32_t found = 0; + uint32_t irq_mask; + unsigned long flags = 0; + int rc = -EINVAL; + bool need_lock; + + need_lock = !in_irq(); + if (need_lock) + spin_lock_irqsave(&controller->lock, flags); + + list_for_each_entry_safe(evt_handler, evt_handler_temp, + &controller->evt_handler_list_head, list_node) { + if (evt_handler->index == handle) { + CAM_DBG(CAM_ISP, "unsubscribe item %d", handle); + list_del_init(&evt_handler->list_node); + list_del_init(&evt_handler->th_list_node); + found = 1; + rc = 0; + break; + } + } + + if (found) { + for (i = 0; i < controller->num_registers; i++) { + controller->irq_register_arr[i]. + top_half_enable_mask[evt_handler->priority] &= + ~(evt_handler->evt_bit_mask_arr[i]); + + irq_mask = cam_io_r_mb(controller->mem_base + + controller->irq_register_arr[i]. + mask_reg_offset); + irq_mask &= ~(evt_handler->evt_bit_mask_arr[i]); + + cam_io_w_mb(irq_mask, controller->mem_base + + controller->irq_register_arr[i]. + mask_reg_offset); + + /* Clear the IRQ bits of this handler */ + cam_io_w_mb(evt_handler->evt_bit_mask_arr[i], + controller->mem_base + + controller->irq_register_arr[i]. + clear_reg_offset); + if (controller->global_clear_offset) + cam_io_w_mb( + controller->global_clear_bitmask, + controller->mem_base + + controller->global_clear_offset); + } + + kfree(evt_handler->evt_bit_mask_arr); + kfree(evt_handler); + } + + if (need_lock) + spin_unlock_irqrestore(&controller->lock, flags); + + return rc; +} + +/** + * cam_irq_controller_match_bit_mask() + * + * @Brief: This function checks if any of the enabled IRQ bits + * for a certain handler is Set in the Status values + * of the controller. + * + * @controller: IRQ Controller structure + * @evt_handler: Event handler structure + * + * @Return: True: If any interested IRQ Bit is Set + * False: Otherwise + */ +static bool cam_irq_controller_match_bit_mask( + struct cam_irq_controller *controller, + struct cam_irq_evt_handler *evt_handler) +{ + int i; + + for (i = 0; i < controller->num_registers; i++) { + if (evt_handler->evt_bit_mask_arr[i] & + controller->irq_status_arr[i]) + return true; + } + + return false; +} + +static void cam_irq_controller_th_processing( + struct cam_irq_controller *controller, + struct list_head *th_list_head) +{ + struct cam_irq_evt_handler *evt_handler = NULL; + struct cam_irq_th_payload *th_payload = &controller->th_payload; + bool is_irq_match; + int rc = -EINVAL; + int i; + void *bh_cmd = NULL; + struct cam_irq_bh_api *irq_bh_api = NULL; + + CAM_DBG(CAM_ISP, "Enter"); + + if (list_empty(th_list_head)) + return; + + list_for_each_entry(evt_handler, th_list_head, th_list_node) { + is_irq_match = cam_irq_controller_match_bit_mask(controller, + evt_handler); + + if (!is_irq_match) + continue; + + CAM_DBG(CAM_ISP, "match found"); + + cam_irq_th_payload_init(th_payload); + th_payload->handler_priv = evt_handler->handler_priv; + th_payload->num_registers = controller->num_registers; + for (i = 0; i < controller->num_registers; i++) { + th_payload->evt_status_arr[i] = + controller->irq_status_arr[i] & + evt_handler->evt_bit_mask_arr[i]; + } + + irq_bh_api = &evt_handler->irq_bh_api; + bh_cmd = NULL; + + if (irq_bh_api->get_bh_payload_func) { + if (irq_bh_api->get_bh_payload_func( + evt_handler->bottom_half, &bh_cmd)) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Can't get bh payload"); + continue; + } + } + + /* + * irq_status_arr[0] is dummy argument passed. the entire + * status array is passed in th_payload. + */ + if (evt_handler->top_half_handler) { + rc = evt_handler->top_half_handler( + controller->irq_status_arr[0], + (void *)th_payload); + if (rc) { + CAM_ERR(CAM_ISP, + "Top half handler failed with %d", rc); + if (irq_bh_api->put_bh_payload_func && bh_cmd) { + if (irq_bh_api->put_bh_payload_func( + evt_handler->bottom_half, + &bh_cmd)) { + CAM_ERR(CAM_ISP, + "Can't put bh payload"); + } + } + continue; + } + } + + if (!rc && evt_handler->bottom_half_handler) { + CAM_DBG(CAM_ISP, "Enqueuing bottom half for %s", + controller->name); + if (irq_bh_api->bottom_half_enqueue_func) { + irq_bh_api->bottom_half_enqueue_func( + evt_handler->bottom_half, + bh_cmd, + th_payload->evt_payload_priv, + evt_handler->bottom_half_handler); + } + } + } + + CAM_DBG(CAM_ISP, "Exit"); +} + +irqreturn_t cam_irq_controller_clear_and_mask(int irq_num, void *priv) +{ + struct cam_irq_controller *controller = priv; + uint32_t i = 0; + + if (!controller) + return IRQ_NONE; + + for (i = 0; i < controller->num_registers; i++) { + + cam_io_w_mb(0x0, controller->mem_base + + controller->irq_register_arr[i].clear_reg_offset); + } + + if (controller->global_clear_offset) + cam_io_w_mb(controller->global_clear_bitmask, + controller->mem_base + + controller->global_clear_offset); + + for (i = 0; i < controller->num_registers; i++) { + cam_io_w_mb(0x0, controller->mem_base + + controller->irq_register_arr[i].mask_reg_offset); + } + + return IRQ_HANDLED; +} + +irqreturn_t cam_irq_controller_handle_irq(int irq_num, void *priv) +{ + struct cam_irq_controller *controller = priv; + bool need_th_processing[CAM_IRQ_PRIORITY_MAX] = {false}; + int i; + int j; + + if (!controller) + return IRQ_NONE; + + CAM_DBG(CAM_ISP, "locking controller %pK name %s lock %pK", + controller, controller->name, &controller->lock); + spin_lock(&controller->lock); + for (i = 0; i < controller->num_registers; i++) { + controller->irq_status_arr[i] = cam_io_r_mb( + controller->mem_base + + controller->irq_register_arr[i].status_reg_offset); + cam_io_w_mb(controller->irq_status_arr[i], + controller->mem_base + + controller->irq_register_arr[i].clear_reg_offset); + CAM_DBG(CAM_ISP, "Read irq status%d (0x%x) = 0x%x", i, + controller->irq_register_arr[i].status_reg_offset, + controller->irq_status_arr[i]); + for (j = 0; j < CAM_IRQ_PRIORITY_MAX; j++) { + if (controller->irq_register_arr[i]. + top_half_enable_mask[j] & + controller->irq_status_arr[i]) + need_th_processing[j] = true; + CAM_DBG(CAM_ISP, + "i %d j %d need_th_processing = %d", + i, j, need_th_processing[j]); + } + } + + CAM_DBG(CAM_ISP, "Status Registers read Successful"); + + if (controller->global_clear_offset) + cam_io_w_mb(controller->global_clear_bitmask, + controller->mem_base + controller->global_clear_offset); + + CAM_DBG(CAM_ISP, "Status Clear done"); + + for (i = 0; i < CAM_IRQ_PRIORITY_MAX; i++) { + if (need_th_processing[i]) { + CAM_DBG(CAM_ISP, "Invoke TH processing"); + cam_irq_controller_th_processing(controller, + &controller->th_list_head[i]); + } + } + spin_unlock(&controller->lock); + CAM_DBG(CAM_ISP, "unlocked controller %pK name %s lock %pK", + controller, controller->name, &controller->lock); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h new file mode 100644 index 000000000000..93fe07ad4593 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h @@ -0,0 +1,278 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_IRQ_CONTROLLER_H_ +#define _CAM_IRQ_CONTROLLER_H_ + +#include + +#define CAM_IRQ_BITS_PER_REGISTER 32 + +/* + * enum cam_irq_priority_level: + * @Brief: Priority levels for IRQ events. + * Priority_0 events will be serviced before + * Priority_1 if they these bits are set in the same + * Status Read. And so on upto Priority_4. + * + * Default Priority is Priority_4. + */ +enum cam_irq_priority_level { + CAM_IRQ_PRIORITY_0, + CAM_IRQ_PRIORITY_1, + CAM_IRQ_PRIORITY_2, + CAM_IRQ_PRIORITY_3, + CAM_IRQ_PRIORITY_4, + CAM_IRQ_PRIORITY_MAX, +}; + +/* + * struct cam_irq_register_set: + * @Brief: Structure containing offsets of IRQ related + * registers belonging to a Set + * + * @mask_reg_offset: Offset of IRQ MASK register + * @clear_reg_offset: Offset of IRQ CLEAR register + * @status_reg_offset: Offset of IRQ STATUS register + */ +struct cam_irq_register_set { + uint32_t mask_reg_offset; + uint32_t clear_reg_offset; + uint32_t status_reg_offset; +}; + +/* + * struct cam_irq_controller_reg_info: + * @Brief: Structure describing the IRQ registers + * + * @num_registers: Number of sets(mask/clear/status) of IRQ registers + * @irq_reg_set: Array of Register Set Offsets. + * Length of array = num_registers + * @global_clear_offset: Offset of Global IRQ Clear register. This register + * contains the BIT that needs to be set for the CLEAR + * to take effect + * @global_clear_bitmask: Bitmask needed to be used in Global Clear register + * for Clear IRQ cmd to take effect + */ +struct cam_irq_controller_reg_info { + uint32_t num_registers; + struct cam_irq_register_set *irq_reg_set; + uint32_t global_clear_offset; + uint32_t global_clear_bitmask; +}; + +/* + * struct cam_irq_th_payload: + * @Brief: Event payload structure. This structure will be + * passed to the Top Half handler functions. + * + * @handler_priv: Private Data of handling object set when + * subscribing to IRQ event. + * @num_registers: Length of evt_bit_mask Array below + * @evt_status_arr: Array of Status bitmask read from registers. + * Length of array = num_registers + * @evt_payload_priv: Private payload pointer which can be set by Top + * Half handler for use in Bottom Half. + */ +struct cam_irq_th_payload { + void *handler_priv; + uint32_t num_registers; + uint32_t *evt_status_arr; + void *evt_payload_priv; +}; + +/* + * cam_irq_th_payload_init() + * + * @brief: Initialize the top half payload structure + * + * @th_payload: Top Half payload structure to Initialize + * + * @return: Void + */ +static inline void cam_irq_th_payload_init( + struct cam_irq_th_payload *th_payload) { + th_payload->handler_priv = NULL; + th_payload->evt_payload_priv = NULL; +} + +typedef int (*CAM_IRQ_HANDLER_TOP_HALF)(uint32_t evt_id, + struct cam_irq_th_payload *th_payload); + +typedef int (*CAM_IRQ_HANDLER_BOTTOM_HALF)(void *handler_priv, + void *evt_payload_priv); + +typedef int (*CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC)(void *bh_cmd, + void *handler_priv, void *evt_payload_priv, + CAM_IRQ_HANDLER_BOTTOM_HALF); +typedef int (*CAM_IRQ_GET_TASKLET_PAYLOAD_FUNC)(void *bh, + void **bh_cmd); + +typedef int (*CAM_IRQ_PUT_TASKLET_PAYLOAD_FUNC)(void *bh, + void **bh_cmd); + +struct cam_irq_bh_api { + CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC bottom_half_enqueue_func; + CAM_IRQ_GET_TASKLET_PAYLOAD_FUNC get_bh_payload_func; + CAM_IRQ_PUT_TASKLET_PAYLOAD_FUNC put_bh_payload_func; +}; + +/* + * cam_irq_controller_init() + * + * @brief: Create and Initialize IRQ Controller. + * + * @name: Name of IRQ Controller block + * @mem_base: Mapped base address of register space to which + * register offsets are added to access registers + * @register_info: Register Info structure associated with this Controller + * @irq_controller: Pointer to IRQ Controller that will be filled if + * initialization is successful + * + * @return: 0: Success + * Negative: Failure + */ +int cam_irq_controller_init(const char *name, + void __iomem *mem_base, + struct cam_irq_controller_reg_info *register_info, + void **irq_controller); + +/* + * cam_irq_controller_subscribe_irq() + * + * @brief: Subscribe to certain IRQ events. + * + * @irq_controller: Pointer to IRQ Controller that controls this event IRQ + * @priority: Priority level of these events used if multiple events + * are SET in the Status Register + * @evt_bit_mask_arr: evt_bit_mask that has the bits set for IRQs to + * subscribe for + * @handler_priv: Private data that will be passed to the Top/Bottom Half + * handler function + * @top_half_handler: Top half Handler callback function + * @bottom_half_handler: Bottom half Handler callback function + * @bottom_half: Pointer to bottom_half implementation on which to + * enqueue the event for further handling + * @bottom_half_enqueue_func: + * Function used to enqueue the bottom_half event + * + * @return: Positive: Success. Value represents handle which is + * to be used to unsubscribe + * Negative: Failure + */ +int cam_irq_controller_subscribe_irq(void *irq_controller, + enum cam_irq_priority_level priority, + uint32_t *evt_bit_mask_arr, + void *handler_priv, + CAM_IRQ_HANDLER_TOP_HALF top_half_handler, + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler, + void *bottom_half, + struct cam_irq_bh_api *irq_bh_api); + +/* + * cam_irq_controller_unsubscribe_irq() + * + * @brief: Unsubscribe to IRQ events previously subscribed to. + * + * @irq_controller: Pointer to IRQ Controller that controls this event IRQ + * @handle: Handle returned on successful subscribe used to + * identify the handler object + * + * @return: 0: Success + * Negative: Failure + */ +int cam_irq_controller_unsubscribe_irq(void *irq_controller, + uint32_t handle); + +/* + * cam_irq_controller_deinit() + * + * @brief: Deinitialize IRQ Controller. + * + * @irq_controller: Pointer to IRQ Controller that needs to be + * deinitialized + * + * @return: 0: Success + * Negative: Failure + */ +int cam_irq_controller_deinit(void **irq_controller); + +/* + * cam_irq_controller_handle_irq() + * + * @brief: Function that should be registered with the IRQ line. + * This is the first function to be called when the IRQ + * is fired. It will read the Status register and Clear + * the IRQ bits. It will then call the top_half handlers + * and enqueue the result to bottom_half. + * + * @irq_num: Number of IRQ line that was set that lead to this + * function being called + * @priv: Private data registered with request_irq is passed back + * here. This private data should be the irq_controller + * structure. + * + * @return: IRQ_HANDLED/IRQ_NONE + */ +irqreturn_t cam_irq_controller_handle_irq(int irq_num, void *priv); + +/* + * cam_irq_controller_disable_irq() + * + * @brief: Disable the interrupts on given controller. + * Unsubscribe will disable the IRQ by default, so this is + * only needed if between subscribe/unsubscribe there is + * need to disable IRQ again + * + * @irq_controller: Pointer to IRQ Controller that controls the registered + * events to it. + * @handle: Handle returned on successful subscribe, used to + * identify the handler object + * + * @return: 0: events found and disabled + * Negative: events not registered on this controller + */ +int cam_irq_controller_disable_irq(void *irq_controller, uint32_t handle); + +/* + * cam_irq_controller_enable_irq() + * + * @brief: Enable the interrupts on given controller. + * Subscribe will enable the IRQ by default, so this is + * only needed if between subscribe/unsubscribe there is + * need to enable IRQ again + * + * @irq_controller: Pointer to IRQ Controller that controls the registered + * events to it. + * @handle: Handle returned on successful subscribe, used to + * identify the handler object + * + * @return: 0: events found and enabled + * Negative: events not registered on this controller + */ +int cam_irq_controller_enable_irq(void *irq_controller, uint32_t handle); + +/* + * cam_irq_controller_clear_and_mask() + * + * @brief: This function clears and masks all the irq bits + * + * @irq_num: Number of IRQ line that was set that lead to this + * function being called + * @priv: Private data registered with request_irq is passed back + * here. This private data should be the irq_controller + * structure. + * + * @return: IRQ_HANDLED/IRQ_NONE + */ +irqreturn_t cam_irq_controller_clear_and_mask(int irq_num, void *priv); +#endif /* _CAM_IRQ_CONTROLLER_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h new file mode 100644 index 000000000000..406888a125f8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h @@ -0,0 +1,213 @@ +/* Copyright (c) 2016-2018, 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 _CAM_ISP_HW_MGR_INTF_H_ +#define _CAM_ISP_HW_MGR_INTF_H_ + +#include +#include +#include +#include +#include "cam_hw_mgr_intf.h" + +/* MAX IFE instance */ +#define CAM_IFE_HW_NUM_MAX 4 +#define CAM_IFE_RDI_NUM_MAX 4 + +/** + * enum cam_isp_hw_event_type - Collection of the ISP hardware events + */ +enum cam_isp_hw_event_type { + CAM_ISP_HW_EVENT_ERROR, + CAM_ISP_HW_EVENT_SOF, + CAM_ISP_HW_EVENT_REG_UPDATE, + CAM_ISP_HW_EVENT_EPOCH, + CAM_ISP_HW_EVENT_EOF, + CAM_ISP_HW_EVENT_DONE, + CAM_ISP_HW_EVENT_MAX +}; + + +/** + * enum cam_isp_hw_err_type - Collection of the ISP error types for + * ISP hardware event CAM_ISP_HW_EVENT_ERROR + */ +enum cam_isp_hw_err_type { + CAM_ISP_HW_ERROR_NONE, + CAM_ISP_HW_ERROR_OVERFLOW, + CAM_ISP_HW_ERROR_P2I_ERROR, + CAM_ISP_HW_ERROR_VIOLATION, + CAM_ISP_HW_ERROR_BUSIF_OVERFLOW, + CAM_ISP_HW_ERROR_MAX, +}; + +/** + * enum cam_isp_hw_stop_cmd - Specify the stop command type + */ +enum cam_isp_hw_stop_cmd { + CAM_ISP_HW_STOP_AT_FRAME_BOUNDARY, + CAM_ISP_HW_STOP_IMMEDIATELY, + CAM_ISP_HW_STOP_MAX, +}; + +/** + * struct cam_isp_stop_hw_method - hardware stop method + * + * @hw_stop_cmd: Hardware stop command type information + * + */ +struct cam_isp_stop_hw_method { + enum cam_isp_hw_stop_cmd hw_stop_cmd; +}; + +/** + * struct cam_isp_bw_config_internal - Internal Bandwidth configuration + * + * @usage_type: Usage type (Single/Dual) + * @num_rdi: Number of RDI votes + * @left_pix_vote: Bandwidth vote for left ISP + * @right_pix_vote: Bandwidth vote for right ISP + * @rdi_vote: RDI bandwidth requirements + */ + +struct cam_isp_bw_config_internal { + uint32_t usage_type; + uint32_t num_rdi; + struct cam_isp_bw_vote left_pix_vote; + struct cam_isp_bw_vote right_pix_vote; + struct cam_isp_bw_vote rdi_vote[CAM_IFE_RDI_NUM_MAX]; +}; + +/** + * struct cam_isp_prepare_hw_update_data - hw prepare data + * + * @packet_opcode_type: Packet header opcode in the packet header + * this opcode defines, packet is init packet or + * update packet + * @bw_config: BW config information + * @bw_config_valid: Flag indicating whether the bw_config at the index + * is valid or not + * + */ +struct cam_isp_prepare_hw_update_data { + uint32_t packet_opcode_type; + struct cam_isp_bw_config_internal bw_config[CAM_IFE_HW_NUM_MAX]; + bool bw_config_valid[CAM_IFE_HW_NUM_MAX]; +}; + + +/** + * struct cam_isp_hw_sof_event_data - Event payload for CAM_HW_EVENT_SOF + * + * @timestamp: Time stamp for the sof event + * + */ +struct cam_isp_hw_sof_event_data { + uint64_t timestamp; +}; + +/** + * struct cam_isp_hw_reg_update_event_data - Event payload for + * CAM_HW_EVENT_REG_UPDATE + * + * @timestamp: Time stamp for the reg update event + * + */ +struct cam_isp_hw_reg_update_event_data { + uint64_t timestamp; +}; + +/** + * struct cam_isp_hw_epoch_event_data - Event payload for CAM_HW_EVENT_EPOCH + * + * @timestamp: Time stamp for the epoch event + * + */ +struct cam_isp_hw_epoch_event_data { + uint64_t timestamp; +}; + +/** + * struct cam_isp_hw_done_event_data - Event payload for CAM_HW_EVENT_DONE + * + * @num_handles: Number of resource handeles + * @resource_handle: Resource handle array + * @timestamp: Timestamp for the buf done event + * + */ +struct cam_isp_hw_done_event_data { + uint32_t num_handles; + uint32_t resource_handle[ + CAM_NUM_OUT_PER_COMP_IRQ_MAX]; + uint64_t timestamp; +}; + +/** + * struct cam_isp_hw_eof_event_data - Event payload for CAM_HW_EVENT_EOF + * + * @timestamp: Timestamp for the eof event + * + */ +struct cam_isp_hw_eof_event_data { + uint64_t timestamp; +}; + +/** + * struct cam_isp_hw_error_event_data - Event payload for CAM_HW_EVENT_ERROR + * + * @error_type: Error type for the error event + * @timestamp: Timestamp for the error event + * + */ +struct cam_isp_hw_error_event_data { + uint32_t error_type; + uint64_t timestamp; +}; + +/* enum cam_isp_hw_mgr_command - Hardware manager command type */ +enum cam_isp_hw_mgr_command { + CAM_ISP_HW_MGR_CMD_IS_RDI_ONLY_CONTEXT, + CAM_ISP_HW_MGR_CMD_PAUSE_HW, + CAM_ISP_HW_MGR_CMD_RESUME_HW, + CAM_ISP_HW_MGR_CMD_MAX, +}; + +/** + * struct cam_isp_hw_cmd_args - Payload for hw manager command + * + * @ctxt_to_hw_map: HW context from the acquire + * @cmd_type HW command type + * @get_context Get context type information + */ +struct cam_isp_hw_cmd_args { + void *ctxt_to_hw_map; + uint32_t cmd_type; + union { + uint32_t is_rdi_only_context; + } u; +}; + + +/** + * cam_isp_hw_mgr_init() + * + * @brief: Initialization function for the ISP hardware manager + * + * @of_node: Device node input + * @hw_mgr: Input/output structure for the ISP hardware manager + * initialization + * @iommu_hdl: Iommu handle to be returned + */ +int cam_isp_hw_mgr_init(struct device_node *of_node, + struct cam_hw_mgr_intf *hw_mgr, int *iommu_hdl); + +#endif /* __CAM_ISP_HW_MGR_INTF_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/Makefile new file mode 100644 index 000000000000..4bf4a0e976e1 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SPECTRA_CAMERA) += ife_csid_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += vfe_hw/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile new file mode 100644 index 000000000000..9f20c3cbeb4b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/Makefile @@ -0,0 +1,12 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_ife_csid_dev.o cam_ife_csid_soc.o cam_ife_csid_core.o +obj-$(CONFIG_SPECTRA_CAMERA) += cam_ife_csid170.o cam_ife_csid_lite170.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.c new file mode 100644 index 000000000000..bdd59d232f70 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.c @@ -0,0 +1,60 @@ +/* Copyright (c) 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 +#include "cam_ife_csid_core.h" +#include "cam_ife_csid170.h" +#include "cam_ife_csid_dev.h" + +#define CAM_CSID_DRV_NAME "csid_170" +#define CAM_CSID_VERSION_V170 0x10070000 + +static struct cam_ife_csid_hw_info cam_ife_csid170_hw_info = { + .csid_reg = &cam_ife_csid_170_reg_offset, + .hw_dts_version = CAM_CSID_VERSION_V170, +}; + +static const struct of_device_id cam_ife_csid170_dt_match[] = { + { + .compatible = "qcom,csid170", + .data = &cam_ife_csid170_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_ife_csid170_dt_match); + +static struct platform_driver cam_ife_csid170_driver = { + .probe = cam_ife_csid_probe, + .remove = cam_ife_csid_remove, + .driver = { + .name = CAM_CSID_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_ife_csid170_dt_match, + }, +}; + +static int __init cam_ife_csid170_init_module(void) +{ + return platform_driver_register(&cam_ife_csid170_driver); +} + +static void __exit cam_ife_csid170_exit_module(void) +{ + platform_driver_unregister(&cam_ife_csid170_driver); +} + +module_init(cam_ife_csid170_init_module); +module_exit(cam_ife_csid170_exit_module); +MODULE_DESCRIPTION("CAM IFE_CSID170 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h new file mode 100644 index 000000000000..c68ddf7343fc --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h @@ -0,0 +1,304 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_IFE_CSID_170_H_ +#define _CAM_IFE_CSID_170_H_ + +#include "cam_ife_csid_core.h" + +static struct cam_ife_csid_ipp_reg_offset cam_ife_csid_170_ipp_reg_offset = { + .csid_ipp_irq_status_addr = 0x30, + .csid_ipp_irq_mask_addr = 0x34, + .csid_ipp_irq_clear_addr = 0x38, + .csid_ipp_irq_set_addr = 0x3c, + + .csid_ipp_cfg0_addr = 0x200, + .csid_ipp_cfg1_addr = 0x204, + .csid_ipp_ctrl_addr = 0x208, + .csid_ipp_frm_drop_pattern_addr = 0x20c, + .csid_ipp_frm_drop_period_addr = 0x210, + .csid_ipp_irq_subsample_pattern_addr = 0x214, + .csid_ipp_irq_subsample_period_addr = 0x218, + .csid_ipp_hcrop_addr = 0x21c, + .csid_ipp_vcrop_addr = 0x220, + .csid_ipp_pix_drop_pattern_addr = 0x224, + .csid_ipp_pix_drop_period_addr = 0x228, + .csid_ipp_line_drop_pattern_addr = 0x22c, + .csid_ipp_line_drop_period_addr = 0x230, + .csid_ipp_rst_strobes_addr = 0x240, + .csid_ipp_status_addr = 0x254, + .csid_ipp_misr_val_addr = 0x258, + .csid_ipp_format_measure_cfg0_addr = 0x270, + .csid_ipp_format_measure_cfg1_addr = 0x274, + .csid_ipp_format_measure0_addr = 0x278, + .csid_ipp_format_measure1_addr = 0x27c, + .csid_ipp_format_measure2_addr = 0x280, + .csid_ipp_timestamp_curr0_sof_addr = 0x290, + .csid_ipp_timestamp_curr1_sof_addr = 0x294, + .csid_ipp_timestamp_perv0_sof_addr = 0x298, + .csid_ipp_timestamp_perv1_sof_addr = 0x29c, + .csid_ipp_timestamp_curr0_eof_addr = 0x2a0, + .csid_ipp_timestamp_curr1_eof_addr = 0x2a4, + .csid_ipp_timestamp_perv0_eof_addr = 0x2a8, + .csid_ipp_timestamp_perv1_eof_addr = 0x2ac, + /* configurations */ + .pix_store_en_shift_val = 7, +}; + +static struct cam_ife_csid_rdi_reg_offset cam_ife_csid_170_rdi_0_reg_offset = { + .csid_rdi_irq_status_addr = 0x40, + .csid_rdi_irq_mask_addr = 0x44, + .csid_rdi_irq_clear_addr = 0x48, + .csid_rdi_irq_set_addr = 0x4c, + .csid_rdi_cfg0_addr = 0x300, + .csid_rdi_cfg1_addr = 0x304, + .csid_rdi_ctrl_addr = 0x308, + .csid_rdi_frm_drop_pattern_addr = 0x30c, + .csid_rdi_frm_drop_period_addr = 0x310, + .csid_rdi_irq_subsample_pattern_addr = 0x314, + .csid_rdi_irq_subsample_period_addr = 0x318, + .csid_rdi_rpp_hcrop_addr = 0x31c, + .csid_rdi_rpp_vcrop_addr = 0x320, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x324, + .csid_rdi_rpp_pix_drop_period_addr = 0x328, + .csid_rdi_rpp_line_drop_pattern_addr = 0x32c, + .csid_rdi_rpp_line_drop_period_addr = 0x330, + .csid_rdi_rst_strobes_addr = 0x340, + .csid_rdi_status_addr = 0x350, + .csid_rdi_misr_val0_addr = 0x354, + .csid_rdi_misr_val1_addr = 0x358, + .csid_rdi_misr_val2_addr = 0x35c, + .csid_rdi_misr_val3_addr = 0x360, + .csid_rdi_format_measure_cfg0_addr = 0x370, + .csid_rdi_format_measure_cfg1_addr = 0x374, + .csid_rdi_format_measure0_addr = 0x378, + .csid_rdi_format_measure1_addr = 0x37c, + .csid_rdi_format_measure2_addr = 0x380, + .csid_rdi_timestamp_curr0_sof_addr = 0x390, + .csid_rdi_timestamp_curr1_sof_addr = 0x394, + .csid_rdi_timestamp_prev0_sof_addr = 0x398, + .csid_rdi_timestamp_prev1_sof_addr = 0x39c, + .csid_rdi_timestamp_curr0_eof_addr = 0x3a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x3a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x3a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x3ac, + .csid_rdi_byte_cntr_ping_addr = 0x3e0, + .csid_rdi_byte_cntr_pong_addr = 0x3e4, +}; + +static struct cam_ife_csid_rdi_reg_offset cam_ife_csid_170_rdi_1_reg_offset = { + .csid_rdi_irq_status_addr = 0x50, + .csid_rdi_irq_mask_addr = 0x54, + .csid_rdi_irq_clear_addr = 0x58, + .csid_rdi_irq_set_addr = 0x5c, + .csid_rdi_cfg0_addr = 0x400, + .csid_rdi_cfg1_addr = 0x404, + .csid_rdi_ctrl_addr = 0x408, + .csid_rdi_frm_drop_pattern_addr = 0x40c, + .csid_rdi_frm_drop_period_addr = 0x410, + .csid_rdi_irq_subsample_pattern_addr = 0x414, + .csid_rdi_irq_subsample_period_addr = 0x418, + .csid_rdi_rpp_hcrop_addr = 0x41c, + .csid_rdi_rpp_vcrop_addr = 0x420, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x424, + .csid_rdi_rpp_pix_drop_period_addr = 0x428, + .csid_rdi_rpp_line_drop_pattern_addr = 0x42c, + .csid_rdi_rpp_line_drop_period_addr = 0x430, + .csid_rdi_rst_strobes_addr = 0x440, + .csid_rdi_status_addr = 0x450, + .csid_rdi_misr_val0_addr = 0x454, + .csid_rdi_misr_val1_addr = 0x458, + .csid_rdi_misr_val2_addr = 0x45c, + .csid_rdi_misr_val3_addr = 0x460, + .csid_rdi_format_measure_cfg0_addr = 0x470, + .csid_rdi_format_measure_cfg1_addr = 0x474, + .csid_rdi_format_measure0_addr = 0x478, + .csid_rdi_format_measure1_addr = 0x47c, + .csid_rdi_format_measure2_addr = 0x480, + .csid_rdi_timestamp_curr0_sof_addr = 0x490, + .csid_rdi_timestamp_curr1_sof_addr = 0x494, + .csid_rdi_timestamp_prev0_sof_addr = 0x498, + .csid_rdi_timestamp_prev1_sof_addr = 0x49c, + .csid_rdi_timestamp_curr0_eof_addr = 0x4a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x4a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x4a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x4ac, + .csid_rdi_byte_cntr_ping_addr = 0x4e0, + .csid_rdi_byte_cntr_pong_addr = 0x4e4, +}; + +static struct cam_ife_csid_rdi_reg_offset cam_ife_csid_170_rdi_2_reg_offset = { + .csid_rdi_irq_status_addr = 0x60, + .csid_rdi_irq_mask_addr = 0x64, + .csid_rdi_irq_clear_addr = 0x68, + .csid_rdi_irq_set_addr = 0x6c, + .csid_rdi_cfg0_addr = 0x500, + .csid_rdi_cfg1_addr = 0x504, + .csid_rdi_ctrl_addr = 0x508, + .csid_rdi_frm_drop_pattern_addr = 0x50c, + .csid_rdi_frm_drop_period_addr = 0x510, + .csid_rdi_irq_subsample_pattern_addr = 0x514, + .csid_rdi_irq_subsample_period_addr = 0x518, + .csid_rdi_rpp_hcrop_addr = 0x51c, + .csid_rdi_rpp_vcrop_addr = 0x520, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x524, + .csid_rdi_rpp_pix_drop_period_addr = 0x528, + .csid_rdi_rpp_line_drop_pattern_addr = 0x52c, + .csid_rdi_rpp_line_drop_period_addr = 0x530, + .csid_rdi_yuv_chroma_conversion_addr = 0x534, + .csid_rdi_rst_strobes_addr = 0x540, + .csid_rdi_status_addr = 0x550, + .csid_rdi_misr_val0_addr = 0x554, + .csid_rdi_misr_val1_addr = 0x558, + .csid_rdi_misr_val2_addr = 0x55c, + .csid_rdi_misr_val3_addr = 0x560, + .csid_rdi_format_measure_cfg0_addr = 0x570, + .csid_rdi_format_measure_cfg1_addr = 0x574, + .csid_rdi_format_measure0_addr = 0x578, + .csid_rdi_format_measure1_addr = 0x57c, + .csid_rdi_format_measure2_addr = 0x580, + .csid_rdi_timestamp_curr0_sof_addr = 0x590, + .csid_rdi_timestamp_curr1_sof_addr = 0x594, + .csid_rdi_timestamp_prev0_sof_addr = 0x598, + .csid_rdi_timestamp_prev1_sof_addr = 0x59c, + .csid_rdi_timestamp_curr0_eof_addr = 0x5a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x5a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x5a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x5ac, + .csid_rdi_byte_cntr_ping_addr = 0x5e0, + .csid_rdi_byte_cntr_pong_addr = 0x5e4, +}; + +static struct cam_ife_csid_csi2_rx_reg_offset + cam_ife_csid_170_csi2_reg_offset = { + .csid_csi2_rx_irq_status_addr = 0x20, + .csid_csi2_rx_irq_mask_addr = 0x24, + .csid_csi2_rx_irq_clear_addr = 0x28, + .csid_csi2_rx_irq_set_addr = 0x2c, + + /*CSI2 rx control */ + .csid_csi2_rx_cfg0_addr = 0x100, + .csid_csi2_rx_cfg1_addr = 0x104, + .csid_csi2_rx_capture_ctrl_addr = 0x108, + .csid_csi2_rx_rst_strobes_addr = 0x110, + .csid_csi2_rx_de_scramble_cfg0_addr = 0x114, + .csid_csi2_rx_de_scramble_cfg1_addr = 0x118, + .csid_csi2_rx_cap_unmap_long_pkt_hdr_0_addr = 0x120, + .csid_csi2_rx_cap_unmap_long_pkt_hdr_1_addr = 0x124, + .csid_csi2_rx_captured_short_pkt_0_addr = 0x128, + .csid_csi2_rx_captured_short_pkt_1_addr = 0x12c, + .csid_csi2_rx_captured_long_pkt_0_addr = 0x130, + .csid_csi2_rx_captured_long_pkt_1_addr = 0x134, + .csid_csi2_rx_captured_long_pkt_ftr_addr = 0x138, + .csid_csi2_rx_captured_cphy_pkt_hdr_addr = 0x13c, + .csid_csi2_rx_lane0_misr_addr = 0x150, + .csid_csi2_rx_lane1_misr_addr = 0x154, + .csid_csi2_rx_lane2_misr_addr = 0x158, + .csid_csi2_rx_lane3_misr_addr = 0x15c, + .csid_csi2_rx_total_pkts_rcvd_addr = 0x160, + .csid_csi2_rx_stats_ecc_addr = 0x164, + .csid_csi2_rx_total_crc_err_addr = 0x168, + + .csi2_rst_srb_all = 0x3FFF, + .csi2_rst_done_shift_val = 27, + .csi2_irq_mask_all = 0xFFFFFFF, + .csi2_misr_enable_shift_val = 6, + .csi2_vc_mode_shift_val = 2, + .csi2_capture_long_pkt_en_shift = 0, + .csi2_capture_short_pkt_en_shift = 1, + .csi2_capture_cphy_pkt_en_shift = 2, + .csi2_capture_long_pkt_dt_shift = 4, + .csi2_capture_long_pkt_vc_shift = 10, + .csi2_capture_short_pkt_vc_shift = 15, + .csi2_capture_cphy_pkt_dt_shift = 20, + .csi2_capture_cphy_pkt_vc_shift = 26, +}; + +static struct cam_ife_csid_csi2_tpg_reg_offset + cam_ife_csid_170_tpg_reg_offset = { + /*CSID TPG control */ + .csid_tpg_ctrl_addr = 0x600, + .csid_tpg_vc_cfg0_addr = 0x604, + .csid_tpg_vc_cfg1_addr = 0x608, + .csid_tpg_lfsr_seed_addr = 0x60c, + .csid_tpg_dt_n_cfg_0_addr = 0x610, + .csid_tpg_dt_n_cfg_1_addr = 0x614, + .csid_tpg_dt_n_cfg_2_addr = 0x618, + .csid_tpg_color_bars_cfg_addr = 0x640, + .csid_tpg_color_box_cfg_addr = 0x644, + .csid_tpg_common_gen_cfg_addr = 0x648, + .csid_tpg_cgen_n_cfg_addr = 0x650, + .csid_tpg_cgen_n_x0_addr = 0x654, + .csid_tpg_cgen_n_x1_addr = 0x658, + .csid_tpg_cgen_n_x2_addr = 0x65c, + .csid_tpg_cgen_n_xy_addr = 0x660, + .csid_tpg_cgen_n_y1_addr = 0x664, + .csid_tpg_cgen_n_y2_addr = 0x668, + + /* configurations */ + .tpg_dtn_cfg_offset = 0xc, + .tpg_cgen_cfg_offset = 0x20, + .tpg_cpas_ife_reg_offset = 0x28, +}; + +static struct cam_ife_csid_common_reg_offset + cam_ife_csid_170_cmn_reg_offset = { + .csid_hw_version_addr = 0x0, + .csid_cfg0_addr = 0x4, + .csid_ctrl_addr = 0x8, + .csid_reset_addr = 0xc, + .csid_rst_strobes_addr = 0x10, + + .csid_test_bus_ctrl_addr = 0x14, + .csid_top_irq_status_addr = 0x70, + .csid_top_irq_mask_addr = 0x74, + .csid_top_irq_clear_addr = 0x78, + .csid_top_irq_set_addr = 0x7c, + .csid_irq_cmd_addr = 0x80, + + /*configurations */ + .major_version = 1, + .minor_version = 7, + .version_incr = 0, + .no_rdis = 3, + .no_pix = 1, + .csid_rst_stb = 0x1e, + .csid_rst_stb_sw_all = 0x1f, + .path_rst_stb_all = 0x7f, + .path_rst_done_shift_val = 1, + .path_en_shift_val = 31, + .dt_id_shift_val = 27, + .vc_shift_val = 22, + .dt_shift_val = 16, + .fmt_shift_val = 12, + .plain_fmt_shit_val = 10, + .crop_v_en_shift_val = 6, + .crop_h_en_shift_val = 5, + .crop_shift = 16, + .ipp_irq_mask_all = 0x7FFF, + .rdi_irq_mask_all = 0x7FFF, +}; + +struct cam_ife_csid_reg_offset cam_ife_csid_170_reg_offset = { + .cmn_reg = &cam_ife_csid_170_cmn_reg_offset, + .csi2_reg = &cam_ife_csid_170_csi2_reg_offset, + .ipp_reg = &cam_ife_csid_170_ipp_reg_offset, + .rdi_reg = { + &cam_ife_csid_170_rdi_0_reg_offset, + &cam_ife_csid_170_rdi_1_reg_offset, + &cam_ife_csid_170_rdi_2_reg_offset, + NULL, + }, + .tpg_reg = &cam_ife_csid_170_tpg_reg_offset, +}; + +#endif /*_CAM_IFE_CSID_170_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c new file mode 100644 index 000000000000..aba250dffa88 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c @@ -0,0 +1,2988 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include + +#include "cam_ife_csid_core.h" +#include "cam_isp_hw.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_debug_util.h" + +/* Timeout value in msec */ +#define IFE_CSID_TIMEOUT 1000 + +/* TPG VC/DT values */ +#define CAM_IFE_CSID_TPG_VC_VAL 0xA +#define CAM_IFE_CSID_TPG_DT_VAL 0x2B + +/* Timeout values in usec */ +#define CAM_IFE_CSID_TIMEOUT_SLEEP_US 1000 +#define CAM_IFE_CSID_TIMEOUT_ALL_US 100000 + +/* + * Constant Factors needed to change QTimer ticks to nanoseconds + * QTimer Freq = 19.2 MHz + * Time(us) = ticks/19.2 + * Time(ns) = ticks/19.2 * 1000 + */ +#define CAM_IFE_CSID_QTIMER_MUL_FACTOR 10000 +#define CAM_IFE_CSID_QTIMER_DIV_FACTOR 192 + +static int cam_ife_csid_is_ipp_format_supported( + uint32_t in_format) +{ + int rc = -EINVAL; + + switch (in_format) { + case CAM_FORMAT_MIPI_RAW_6: + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_MIPI_RAW_10: + case CAM_FORMAT_MIPI_RAW_12: + case CAM_FORMAT_MIPI_RAW_14: + case CAM_FORMAT_MIPI_RAW_16: + case CAM_FORMAT_MIPI_RAW_20: + case CAM_FORMAT_DPCM_10_6_10: + case CAM_FORMAT_DPCM_10_8_10: + case CAM_FORMAT_DPCM_12_6_12: + case CAM_FORMAT_DPCM_12_8_12: + case CAM_FORMAT_DPCM_14_8_14: + case CAM_FORMAT_DPCM_14_10_14: + rc = 0; + break; + default: + break; + } + return rc; +} + +static int cam_ife_csid_get_format_rdi( + uint32_t in_format, uint32_t out_format, + uint32_t *decode_fmt, uint32_t *plain_fmt) +{ + int rc = 0; + + switch (in_format) { + case CAM_FORMAT_MIPI_RAW_6: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_6: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN8: + *decode_fmt = 0x0; + *plain_fmt = 0x0; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_8: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_PLAIN128: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN8: + *decode_fmt = 0x1; + *plain_fmt = 0x0; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_10: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_10: + case CAM_FORMAT_PLAIN128: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN16_10: + *decode_fmt = 0x2; + *plain_fmt = 0x1; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_12: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_12: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN16_12: + *decode_fmt = 0x3; + *plain_fmt = 0x1; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_14: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_14: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN16_14: + *decode_fmt = 0x4; + *plain_fmt = 0x1; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_16: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_16: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN16_16: + *decode_fmt = 0x5; + *plain_fmt = 0x1; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_MIPI_RAW_20: + switch (out_format) { + case CAM_FORMAT_MIPI_RAW_20: + *decode_fmt = 0xf; + break; + case CAM_FORMAT_PLAIN32_20: + *decode_fmt = 0x6; + *plain_fmt = 0x2; + break; + default: + rc = -EINVAL; + break; + } + break; + case CAM_FORMAT_DPCM_10_6_10: + *decode_fmt = 0x7; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_10_8_10: + *decode_fmt = 0x8; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_12_6_12: + *decode_fmt = 0x9; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_12_8_12: + *decode_fmt = 0xA; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_14_8_14: + *decode_fmt = 0xB; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_14_10_14: + *decode_fmt = 0xC; + *plain_fmt = 0x1; + break; + default: + rc = -EINVAL; + break; + } + + if (rc) + CAM_ERR(CAM_ISP, "Unsupported format pair in %d out %d", + in_format, out_format); + + return rc; +} + +static int cam_ife_csid_get_format_ipp( + uint32_t in_format, + uint32_t *decode_fmt, uint32_t *plain_fmt) +{ + int rc = 0; + + CAM_DBG(CAM_ISP, "input format:%d", + in_format); + + switch (in_format) { + case CAM_FORMAT_MIPI_RAW_6: + *decode_fmt = 0; + *plain_fmt = 0; + break; + case CAM_FORMAT_MIPI_RAW_8: + *decode_fmt = 0x1; + *plain_fmt = 0; + break; + case CAM_FORMAT_MIPI_RAW_10: + *decode_fmt = 0x2; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_MIPI_RAW_12: + *decode_fmt = 0x3; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_MIPI_RAW_14: + *decode_fmt = 0x4; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_MIPI_RAW_16: + *decode_fmt = 0x5; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_MIPI_RAW_20: + *decode_fmt = 0x6; + *plain_fmt = 0x2; + break; + case CAM_FORMAT_DPCM_10_6_10: + *decode_fmt = 0x7; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_10_8_10: + *decode_fmt = 0x8; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_12_6_12: + *decode_fmt = 0x9; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_12_8_12: + *decode_fmt = 0xA; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_14_8_14: + *decode_fmt = 0xB; + *plain_fmt = 0x1; + break; + case CAM_FORMAT_DPCM_14_10_14: + *decode_fmt = 0xC; + *plain_fmt = 0x1; + break; + default: + CAM_ERR(CAM_ISP, "Unsupported format %d", + in_format); + rc = -EINVAL; + } + + CAM_DBG(CAM_ISP, "decode_fmt:%d plain_fmt:%d", + *decode_fmt, *plain_fmt); + + return rc; +} + +static int cam_ife_csid_cid_get(struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node **res, int32_t vc, uint32_t dt, + uint32_t res_type) +{ + int rc = 0; + struct cam_ife_csid_cid_data *cid_data; + uint32_t i = 0, j = 0; + + for (i = 0; i < CAM_IFE_CSID_CID_RES_MAX; i++) { + if (csid_hw->cid_res[i].res_state >= + CAM_ISP_RESOURCE_STATE_RESERVED) { + cid_data = (struct cam_ife_csid_cid_data *) + csid_hw->cid_res[i].res_priv; + if (res_type == CAM_ISP_IFE_IN_RES_TPG) { + if (cid_data->tpg_set) { + cid_data->cnt++; + *res = &csid_hw->cid_res[i]; + break; + } + } else { + if (cid_data->vc == vc && cid_data->dt == dt) { + cid_data->cnt++; + *res = &csid_hw->cid_res[i]; + break; + } + } + } + } + + if (i == CAM_IFE_CSID_CID_RES_MAX) { + if (res_type == CAM_ISP_IFE_IN_RES_TPG) { + CAM_ERR(CAM_ISP, "CSID:%d TPG CID not available", + csid_hw->hw_intf->hw_idx); + rc = -EINVAL; + } + + for (j = 0; j < CAM_IFE_CSID_CID_RES_MAX; j++) { + if (csid_hw->cid_res[j].res_state == + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + cid_data = (struct cam_ife_csid_cid_data *) + csid_hw->cid_res[j].res_priv; + cid_data->vc = vc; + cid_data->dt = dt; + cid_data->cnt = 1; + csid_hw->cid_res[j].res_state = + CAM_ISP_RESOURCE_STATE_RESERVED; + *res = &csid_hw->cid_res[j]; + CAM_DBG(CAM_ISP, "CSID:%d CID %d allocated", + csid_hw->hw_intf->hw_idx, + csid_hw->cid_res[j].res_id); + break; + } + } + + if (j == CAM_IFE_CSID_CID_RES_MAX) { + CAM_ERR(CAM_ISP, "CSID:%d Free cid is not available", + csid_hw->hw_intf->hw_idx); + rc = -EINVAL; + } + } + + return rc; +} + + +static int cam_ife_csid_global_reset(struct cam_ife_csid_hw *csid_hw) +{ + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_reg_offset *csid_reg; + int rc = 0; + uint32_t val = 0, i; + + soc_info = &csid_hw->hw_info->soc_info; + csid_reg = csid_hw->csid_info->csid_reg; + + if (csid_hw->hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid HW State:%d", + csid_hw->hw_intf->hw_idx, + csid_hw->hw_info->hw_state); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "CSID:%d Csid reset", + csid_hw->hw_intf->hw_idx); + + init_completion(&csid_hw->csid_top_complete); + + /* Mask all interrupts */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr); + + if (csid_reg->cmn_reg->no_pix) + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_mask_addr); + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_irq_mask_addr); + + /* clear all interrupts */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_clear_addr); + + cam_io_w_mb(csid_reg->csi2_reg->csi2_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_clear_addr); + + if (csid_reg->cmn_reg->no_pix) + cam_io_w_mb(csid_reg->cmn_reg->ipp_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_clear_addr); + + for (i = 0 ; i < csid_reg->cmn_reg->no_rdis; i++) + cam_io_w_mb(csid_reg->cmn_reg->rdi_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_irq_clear_addr); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_irq_cmd_addr); + + cam_io_w_mb(0x80, soc_info->reg_map[0].mem_base + + csid_hw->csid_info->csid_reg->csi2_reg->csid_csi2_rx_cfg1_addr); + + /* enable the IPP and RDI format measure */ + if (csid_reg->cmn_reg->no_pix) + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_cfg0_addr); + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) + cam_io_w_mb(0x2, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_cfg0_addr); + + /* perform the top CSID HW reset */ + cam_io_w_mb(csid_reg->cmn_reg->csid_rst_stb, + soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_rst_strobes_addr); + + CAM_DBG(CAM_ISP, " Waiting for reset complete from irq handler"); + rc = wait_for_completion_timeout(&csid_hw->csid_top_complete, + msecs_to_jiffies(IFE_CSID_TIMEOUT)); + if (rc <= 0) { + CAM_ERR(CAM_ISP, "CSID:%d reset completion in fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + if (rc == 0) + rc = -ETIMEDOUT; + } else { + rc = 0; + } + + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr); + if (val != 0) + CAM_ERR(CAM_ISP, "CSID:%d IRQ value after reset rc = %d", + csid_hw->hw_intf->hw_idx, val); + + return rc; +} + +static int cam_ife_csid_path_reset(struct cam_ife_csid_hw *csid_hw, + struct cam_csid_reset_cfg_args *reset) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info; + struct cam_isp_resource_node *res; + struct cam_ife_csid_reg_offset *csid_reg; + uint32_t reset_strb_addr, reset_strb_val, val, id; + struct completion *complete; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + res = reset->node_res; + + if (csid_hw->hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid hw state :%d", + csid_hw->hw_intf->hw_idx, + csid_hw->hw_info->hw_state); + return -EINVAL; + } + + if (res->res_id >= CAM_IFE_PIX_PATH_RES_MAX) { + CAM_DBG(CAM_ISP, "CSID:%d Invalid res id%d", + csid_hw->hw_intf->hw_idx, res->res_id); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_ISP, "CSID:%d resource:%d", + csid_hw->hw_intf->hw_idx, res->res_id); + + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) { + if (!csid_reg->ipp_reg) { + CAM_ERR(CAM_ISP, "CSID:%d IPP not supported :%d", + csid_hw->hw_intf->hw_idx, + res->res_id); + return -EINVAL; + } + + reset_strb_addr = csid_reg->ipp_reg->csid_ipp_rst_strobes_addr; + complete = &csid_hw->csid_ipp_complete; + + /* Enable path reset done interrupt */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_mask_addr); + val |= CSID_PATH_INFO_RST_DONE; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_mask_addr); + + } else { + id = res->res_id; + if (!csid_reg->rdi_reg[id]) { + CAM_ERR(CAM_ISP, "CSID:%d RDI res not supported :%d", + csid_hw->hw_intf->hw_idx, + res->res_id); + return -EINVAL; + } + + reset_strb_addr = + csid_reg->rdi_reg[id]->csid_rdi_rst_strobes_addr; + complete = + &csid_hw->csid_rdin_complete[id]; + + /* Enable path reset done interrupt */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr); + val |= CSID_PATH_INFO_RST_DONE; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr); + } + + init_completion(complete); + reset_strb_val = csid_reg->cmn_reg->path_rst_stb_all; + + /* Enable the Test gen before reset */ + cam_io_w_mb(1, csid_hw->hw_info->soc_info.reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_ctrl_addr); + + /* Reset the corresponding ife csid path */ + cam_io_w_mb(reset_strb_val, soc_info->reg_map[0].mem_base + + reset_strb_addr); + + rc = wait_for_completion_timeout(complete, + msecs_to_jiffies(IFE_CSID_TIMEOUT)); + if (rc <= 0) { + CAM_ERR(CAM_ISP, "CSID:%d Res id %d fail rc = %d", + csid_hw->hw_intf->hw_idx, + res->res_id, rc); + if (rc == 0) + rc = -ETIMEDOUT; + } + + /* Disable Test Gen after reset*/ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_ctrl_addr); + +end: + return rc; + +} + +static int cam_ife_csid_cid_reserve(struct cam_ife_csid_hw *csid_hw, + struct cam_csid_hw_reserve_resource_args *cid_reserv) +{ + int rc = 0; + struct cam_ife_csid_cid_data *cid_data; + + CAM_DBG(CAM_ISP, + "CSID:%d res_sel:0x%x Lane type:%d lane_num:%d dt:%d vc:%d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->res_type, + cid_reserv->in_port->lane_type, + cid_reserv->in_port->lane_num, + cid_reserv->in_port->dt, + cid_reserv->in_port->vc); + + if (cid_reserv->in_port->res_type >= CAM_ISP_IFE_IN_RES_MAX) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid phy sel %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->res_type); + rc = -EINVAL; + goto end; + } + + if (cid_reserv->in_port->lane_type >= CAM_ISP_LANE_TYPE_MAX && + cid_reserv->in_port->res_type != CAM_ISP_IFE_IN_RES_TPG) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid lane type %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->lane_type); + rc = -EINVAL; + goto end; + } + + if ((cid_reserv->in_port->lane_type == CAM_ISP_LANE_TYPE_DPHY && + cid_reserv->in_port->lane_num > 4) && + cid_reserv->in_port->res_type != CAM_ISP_IFE_IN_RES_TPG) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid lane num %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->lane_num); + rc = -EINVAL; + goto end; + } + if ((cid_reserv->in_port->lane_type == CAM_ISP_LANE_TYPE_CPHY && + cid_reserv->in_port->lane_num > 3) && + cid_reserv->in_port->res_type != CAM_ISP_IFE_IN_RES_TPG) { + CAM_ERR(CAM_ISP, " CSID:%d Invalid lane type %d & num %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->lane_type, + cid_reserv->in_port->lane_num); + rc = -EINVAL; + goto end; + } + + /* CSID CSI2 v2.0 supports 31 vc */ + if (cid_reserv->in_port->dt > 0x3f || + cid_reserv->in_port->vc > 0x1f) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid vc:%d dt %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->vc, cid_reserv->in_port->dt); + rc = -EINVAL; + goto end; + } + + if (cid_reserv->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG && ( + (cid_reserv->in_port->format < CAM_FORMAT_MIPI_RAW_8 && + cid_reserv->in_port->format > CAM_FORMAT_MIPI_RAW_16))) { + CAM_ERR(CAM_ISP, " CSID:%d Invalid tpg decode fmt %d", + csid_hw->hw_intf->hw_idx, + cid_reserv->in_port->format); + rc = -EINVAL; + goto end; + } + + if (cid_reserv->in_port->res_type == CAM_ISP_IFE_IN_RES_PHY_3 && + csid_hw->hw_intf->hw_idx != 2) { + rc = -EINVAL; + goto end; + } + + if (csid_hw->csi2_reserve_cnt) { + /* current configure res type should match requested res type */ + if (csid_hw->res_type != cid_reserv->in_port->res_type) { + rc = -EINVAL; + goto end; + } + + if (cid_reserv->in_port->res_type != CAM_ISP_IFE_IN_RES_TPG) { + if (csid_hw->csi2_rx_cfg.lane_cfg != + cid_reserv->in_port->lane_cfg || + csid_hw->csi2_rx_cfg.lane_type != + cid_reserv->in_port->lane_type || + csid_hw->csi2_rx_cfg.lane_num != + cid_reserv->in_port->lane_num) { + rc = -EINVAL; + goto end; + } + } else { + if (csid_hw->tpg_cfg.in_format != + cid_reserv->in_port->format || + csid_hw->tpg_cfg.width != + cid_reserv->in_port->left_width || + csid_hw->tpg_cfg.height != + cid_reserv->in_port->height || + csid_hw->tpg_cfg.test_pattern != + cid_reserv->in_port->test_pattern) { + rc = -EINVAL; + goto end; + } + } + } + + if (!csid_hw->csi2_reserve_cnt) { + csid_hw->res_type = cid_reserv->in_port->res_type; + /* Take the first CID resource*/ + csid_hw->cid_res[0].res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + cid_data = (struct cam_ife_csid_cid_data *) + csid_hw->cid_res[0].res_priv; + + csid_hw->csi2_rx_cfg.lane_cfg = + cid_reserv->in_port->lane_cfg; + csid_hw->csi2_rx_cfg.lane_type = + cid_reserv->in_port->lane_type; + csid_hw->csi2_rx_cfg.lane_num = + cid_reserv->in_port->lane_num; + + if (cid_reserv->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) { + csid_hw->csi2_rx_cfg.phy_sel = 0; + if (cid_reserv->in_port->format > + CAM_FORMAT_MIPI_RAW_16) { + CAM_ERR(CAM_ISP, " Wrong TPG format"); + rc = -EINVAL; + goto end; + } + csid_hw->tpg_cfg.in_format = + cid_reserv->in_port->format; + csid_hw->tpg_cfg.usage_type = + cid_reserv->in_port->usage_type; + if (cid_reserv->in_port->usage_type) + csid_hw->tpg_cfg.width = + (cid_reserv->in_port->right_stop + 1); + else + csid_hw->tpg_cfg.width = + cid_reserv->in_port->left_width; + + csid_hw->tpg_cfg.height = cid_reserv->in_port->height; + csid_hw->tpg_cfg.test_pattern = + cid_reserv->in_port->test_pattern; + + CAM_DBG(CAM_ISP, "CSID:%d TPG width:%d height:%d", + csid_hw->hw_intf->hw_idx, + csid_hw->tpg_cfg.width, + csid_hw->tpg_cfg.height); + + cid_data->tpg_set = 1; + } else { + csid_hw->csi2_rx_cfg.phy_sel = + (cid_reserv->in_port->res_type & 0xFF) - 1; + } + + cid_data->vc = cid_reserv->in_port->vc; + cid_data->dt = cid_reserv->in_port->dt; + cid_data->cnt = 1; + cid_reserv->node_res = &csid_hw->cid_res[0]; + csid_hw->csi2_reserve_cnt++; + + CAM_DBG(CAM_ISP, + "CSID:%d CID :%d resource acquired successfully", + csid_hw->hw_intf->hw_idx, + cid_reserv->node_res->res_id); + } else { + switch (cid_reserv->res_id) { + case CAM_IFE_PIX_PATH_RES_IPP: + if (csid_hw->ipp_res.res_state != + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_DBG(CAM_ISP, + "CSID:%d IPP resource not available", + csid_hw->hw_intf->hw_idx); + rc = -EINVAL; + goto end; + } + break; + case CAM_IFE_PIX_PATH_RES_RDI_0: + case CAM_IFE_PIX_PATH_RES_RDI_1: + case CAM_IFE_PIX_PATH_RES_RDI_2: + case CAM_IFE_PIX_PATH_RES_RDI_3: + if (csid_hw->rdi_res[cid_reserv->res_id].res_state != + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_DBG(CAM_ISP, + "CSID:%d RDI:%d resource not available", + csid_hw->hw_intf->hw_idx, + cid_reserv->res_id); + rc = -EINVAL; + goto end; + } + break; + default: + CAM_ERR(CAM_ISP, "CSID%d: Invalid csid path", + csid_hw->hw_intf->hw_idx); + rc = -EINVAL; + goto end; + } + + rc = cam_ife_csid_cid_get(csid_hw, + &cid_reserv->node_res, + cid_reserv->in_port->vc, + cid_reserv->in_port->dt, + cid_reserv->in_port->res_type); + /* if success then increment the reserve count */ + if (!rc) { + if (csid_hw->csi2_reserve_cnt == UINT_MAX) { + CAM_ERR(CAM_ISP, + "CSID%d reserve cnt reached max", + csid_hw->hw_intf->hw_idx); + rc = -EINVAL; + } else { + csid_hw->csi2_reserve_cnt++; + CAM_DBG(CAM_ISP, "CSID:%d CID:%d acquired", + csid_hw->hw_intf->hw_idx, + cid_reserv->node_res->res_id); + } + } + } + +end: + return rc; +} + + +static int cam_ife_csid_path_reserve(struct cam_ife_csid_hw *csid_hw, + struct cam_csid_hw_reserve_resource_args *reserve) +{ + int rc = 0; + struct cam_ife_csid_path_cfg *path_data; + struct cam_isp_resource_node *res; + + /* CSID CSI2 v2.0 supports 31 vc */ + if (reserve->in_port->dt > 0x3f || reserve->in_port->vc > 0x1f || + (reserve->sync_mode >= CAM_ISP_HW_SYNC_MAX)) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid vc:%d dt %d mode:%d", + csid_hw->hw_intf->hw_idx, + reserve->in_port->vc, reserve->in_port->dt, + reserve->sync_mode); + rc = -EINVAL; + goto end; + } + + switch (reserve->res_id) { + case CAM_IFE_PIX_PATH_RES_IPP: + if (csid_hw->ipp_res.res_state != + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_DBG(CAM_ISP, + "CSID:%d IPP resource not available %d", + csid_hw->hw_intf->hw_idx, + csid_hw->ipp_res.res_state); + rc = -EINVAL; + goto end; + } + + if (cam_ife_csid_is_ipp_format_supported( + reserve->in_port->format)) { + CAM_ERR(CAM_ISP, + "CSID:%d res id:%d un support format %d", + csid_hw->hw_intf->hw_idx, reserve->res_id, + reserve->in_port->format); + rc = -EINVAL; + goto end; + } + + /* assign the IPP resource */ + res = &csid_hw->ipp_res; + CAM_DBG(CAM_ISP, + "CSID:%d IPP resource:%d acquired successfully", + csid_hw->hw_intf->hw_idx, res->res_id); + + break; + case CAM_IFE_PIX_PATH_RES_RDI_0: + case CAM_IFE_PIX_PATH_RES_RDI_1: + case CAM_IFE_PIX_PATH_RES_RDI_2: + case CAM_IFE_PIX_PATH_RES_RDI_3: + if (csid_hw->rdi_res[reserve->res_id].res_state != + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_DBG(CAM_ISP, + "CSID:%d RDI:%d resource not available %d", + csid_hw->hw_intf->hw_idx, + reserve->res_id, + csid_hw->rdi_res[reserve->res_id].res_state); + rc = -EINVAL; + goto end; + } else { + res = &csid_hw->rdi_res[reserve->res_id]; + CAM_DBG(CAM_ISP, + "CSID:%d RDI resource:%d acquire success", + csid_hw->hw_intf->hw_idx, + res->res_id); + } + + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res id:%d", + csid_hw->hw_intf->hw_idx, reserve->res_id); + rc = -EINVAL; + goto end; + } + + res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + path_data = (struct cam_ife_csid_path_cfg *)res->res_priv; + + path_data->cid = reserve->cid; + path_data->in_format = reserve->in_port->format; + path_data->out_format = reserve->out_port->format; + path_data->master_idx = reserve->master_idx; + path_data->sync_mode = reserve->sync_mode; + path_data->height = reserve->in_port->height; + path_data->start_line = reserve->in_port->line_start; + path_data->end_line = reserve->in_port->line_stop; + if (reserve->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) { + path_data->dt = CAM_IFE_CSID_TPG_DT_VAL; + path_data->vc = CAM_IFE_CSID_TPG_VC_VAL; + } else { + path_data->dt = reserve->in_port->dt; + path_data->vc = reserve->in_port->vc; + } + + if (reserve->sync_mode == CAM_ISP_HW_SYNC_MASTER) { + path_data->crop_enable = 1; + path_data->start_pixel = reserve->in_port->left_start; + path_data->end_pixel = reserve->in_port->left_stop; + path_data->width = reserve->in_port->left_width; + CAM_DBG(CAM_ISP, "CSID:%dmaster:startpixel 0x%x endpixel:0x%x", + csid_hw->hw_intf->hw_idx, path_data->start_pixel, + path_data->end_pixel); + CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x", + csid_hw->hw_intf->hw_idx, path_data->start_line, + path_data->end_line); + } else if (reserve->sync_mode == CAM_ISP_HW_SYNC_SLAVE) { + path_data->crop_enable = 1; + path_data->start_pixel = reserve->in_port->right_start; + path_data->end_pixel = reserve->in_port->right_stop; + path_data->width = reserve->in_port->right_width; + CAM_DBG(CAM_ISP, "CSID:%d slave:start:0x%x end:0x%x width 0x%x", + csid_hw->hw_intf->hw_idx, path_data->start_pixel, + path_data->end_pixel, path_data->width); + CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x", + csid_hw->hw_intf->hw_idx, path_data->start_line, + path_data->end_line); + } else { + path_data->crop_enable = 0; + path_data->width = reserve->in_port->left_width; + path_data->start_pixel = reserve->in_port->left_start; + } + + CAM_DBG(CAM_ISP, "Res %d width %d height %d", reserve->res_id, + path_data->width, path_data->height); + reserve->node_res = res; + +end: + return rc; +} + +static int cam_ife_csid_enable_hw(struct cam_ife_csid_hw *csid_hw) +{ + int rc = 0; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t i, status, val; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + /* overflow check before increment */ + if (csid_hw->hw_info->open_count == UINT_MAX) { + CAM_ERR(CAM_ISP, "CSID:%d Open count reached max", + csid_hw->hw_intf->hw_idx); + return -EINVAL; + } + + /* Increment ref Count */ + csid_hw->hw_info->open_count++; + if (csid_hw->hw_info->open_count > 1) { + CAM_DBG(CAM_ISP, "CSID hw has already been enabled"); + return rc; + } + + CAM_DBG(CAM_ISP, "CSID:%d init CSID HW", + csid_hw->hw_intf->hw_idx); + + rc = cam_ife_csid_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ISP, "CSID:%d Enable SOC failed", + csid_hw->hw_intf->hw_idx); + goto err; + } + + + CAM_DBG(CAM_ISP, "CSID:%d enable top irq interrupt", + csid_hw->hw_intf->hw_idx); + + csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_UP; + /* Enable the top IRQ interrupt */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_mask_addr); + + rc = cam_ife_csid_global_reset(csid_hw); + if (rc) { + CAM_ERR(CAM_ISP, "CSID:%d csid_reset fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + rc = -ETIMEDOUT; + goto disable_soc; + } + + /* + * Reset the SW registers + * SW register reset also reset the mask irq, so poll the irq status + * to check the reset complete. + */ + CAM_DBG(CAM_ISP, "CSID:%d Reset Software registers", + csid_hw->hw_intf->hw_idx); + + cam_io_w_mb(csid_reg->cmn_reg->csid_rst_stb_sw_all, + soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_rst_strobes_addr); + + rc = readl_poll_timeout(soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_status_addr, + status, (status & 0x1) == 0x1, + CAM_IFE_CSID_TIMEOUT_SLEEP_US, CAM_IFE_CSID_TIMEOUT_ALL_US); + if (rc < 0) { + CAM_ERR(CAM_ISP, "software register reset timeout....."); + rc = -ETIMEDOUT; + goto disable_soc; + } + + /* clear all interrupts */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_clear_addr); + + cam_io_w_mb(csid_reg->csi2_reg->csi2_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_clear_addr); + + if (csid_reg->cmn_reg->no_pix) + cam_io_w_mb(csid_reg->cmn_reg->ipp_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_clear_addr); + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) + cam_io_w_mb(csid_reg->cmn_reg->rdi_irq_mask_all, + soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_irq_clear_addr); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_irq_cmd_addr); + + /* Enable the top IRQ interrupt */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_mask_addr); + + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_hw_version_addr); + CAM_DBG(CAM_ISP, "CSID:%d CSID HW version: 0x%x", + csid_hw->hw_intf->hw_idx, val); + + return 0; + +disable_soc: + cam_ife_csid_disable_soc_resources(soc_info); + csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN; +err: + csid_hw->hw_info->open_count--; + return rc; +} + +static int cam_ife_csid_disable_hw(struct cam_ife_csid_hw *csid_hw) +{ + int rc = -EINVAL; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_reg_offset *csid_reg; + + /* Check for refcount */ + if (!csid_hw->hw_info->open_count) { + CAM_WARN(CAM_ISP, "Unbalanced disable_hw"); + return rc; + } + + /* Decrement ref Count */ + csid_hw->hw_info->open_count--; + + if (csid_hw->hw_info->open_count) { + rc = 0; + return rc; + } + + soc_info = &csid_hw->hw_info->soc_info; + csid_reg = csid_hw->csid_info->csid_reg; + + CAM_DBG(CAM_ISP, "%s:Calling Global Reset\n", __func__); + cam_ife_csid_global_reset(csid_hw); + CAM_DBG(CAM_ISP, "%s:Global Reset Done\n", __func__); + + CAM_DBG(CAM_ISP, "CSID:%d De-init CSID HW", + csid_hw->hw_intf->hw_idx); + + /*disable the top IRQ interrupt */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_mask_addr); + + rc = cam_ife_csid_disable_soc_resources(soc_info); + if (rc) + CAM_ERR(CAM_ISP, "CSID:%d Disable CSID SOC failed", + csid_hw->hw_intf->hw_idx); + + csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN; + return rc; +} + + +static int cam_ife_csid_tpg_start(struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + uint32_t val = 0; + struct cam_hw_soc_info *soc_info; + + csid_hw->tpg_start_cnt++; + if (csid_hw->tpg_start_cnt == 1) { + /*Enable the TPG */ + CAM_DBG(CAM_ISP, "CSID:%d start CSID TPG", + csid_hw->hw_intf->hw_idx); + + soc_info = &csid_hw->hw_info->soc_info; + { + uint32_t val; + uint32_t i; + uint32_t base = 0x600; + + CAM_DBG(CAM_ISP, "================ TPG ============"); + for (i = 0; i < 16; i++) { + val = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + base + i * 4); + CAM_DBG(CAM_ISP, "reg 0x%x = 0x%x", + (base + i*4), val); + } + + CAM_DBG(CAM_ISP, "================ IPP ============="); + base = 0x200; + for (i = 0; i < 10; i++) { + val = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + base + i * 4); + CAM_DBG(CAM_ISP, "reg 0x%x = 0x%x", + (base + i*4), val); + } + + CAM_DBG(CAM_ISP, "================ RX ============="); + base = 0x100; + for (i = 0; i < 5; i++) { + val = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + base + i * 4); + CAM_DBG(CAM_ISP, "reg 0x%x = 0x%x", + (base + i*4), val); + } + } + + /* Enable the IFE force clock on for dual isp case */ + if (csid_hw->tpg_cfg.usage_type) { + rc = cam_ife_csid_enable_ife_force_clock_on(soc_info, + csid_hw->csid_info->csid_reg->tpg_reg-> + tpg_cpas_ife_reg_offset); + if (rc) + return rc; + } + + CAM_DBG(CAM_ISP, "============ TPG control ============"); + val = (4 << 20); + val |= (0x80 << 8); + val |= (((csid_hw->csi2_rx_cfg.lane_num - 1) & 0x3) << 4); + val |= 7; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_hw->csid_info->csid_reg->tpg_reg-> + csid_tpg_ctrl_addr); + + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + 0x600); + CAM_DBG(CAM_ISP, "reg 0x%x = 0x%x", 0x600, val); + } + + return 0; +} + +static int cam_ife_csid_tpg_stop(struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info; + + if (csid_hw->tpg_start_cnt) + csid_hw->tpg_start_cnt--; + + if (csid_hw->tpg_start_cnt) + return 0; + + soc_info = &csid_hw->hw_info->soc_info; + + /* disable the TPG */ + if (!csid_hw->tpg_start_cnt) { + CAM_DBG(CAM_ISP, "CSID:%d stop CSID TPG", + csid_hw->hw_intf->hw_idx); + + /* Disable the IFE force clock on for dual isp case */ + if (csid_hw->tpg_cfg.usage_type) + rc = cam_ife_csid_disable_ife_force_clock_on(soc_info, + csid_hw->csid_info->csid_reg->tpg_reg-> + tpg_cpas_ife_reg_offset); + + /*stop the TPG */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_hw->csid_info->csid_reg->tpg_reg->csid_tpg_ctrl_addr); + } + + return 0; +} + + +static int cam_ife_csid_config_tpg(struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t val = 0; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + CAM_DBG(CAM_ISP, "CSID:%d TPG config", + csid_hw->hw_intf->hw_idx); + + /* configure one DT, infinite frames */ + val = (0 << 16) | (1 << 10) | CAM_IFE_CSID_TPG_VC_VAL; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_vc_cfg0_addr); + + /* vertical blanking count = 0x3FF, horzontal blanking count = 0x740*/ + val = (0x3FF << 12) | 0x740; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_vc_cfg1_addr); + + cam_io_w_mb(0x12345678, soc_info->reg_map[0].mem_base + + csid_hw->csid_info->csid_reg->tpg_reg->csid_tpg_lfsr_seed_addr); + + val = csid_hw->tpg_cfg.width << 16 | + csid_hw->tpg_cfg.height; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_dt_n_cfg_0_addr); + + cam_io_w_mb(CAM_IFE_CSID_TPG_DT_VAL, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_dt_n_cfg_1_addr); + + /* + * in_format is the same as the input resource format. + * it is one larger than the register spec format. + */ + val = ((csid_hw->tpg_cfg.in_format - 1) << 16) | 0x8; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_dt_n_cfg_2_addr); + + /* static frame with split color bar */ + val = 1 << 5; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_color_bars_cfg_addr); + /* config pix pattern */ + cam_io_w_mb(csid_hw->tpg_cfg.test_pattern, + soc_info->reg_map[0].mem_base + + csid_reg->tpg_reg->csid_tpg_common_gen_cfg_addr); + + return 0; +} + +static int cam_ife_csid_enable_csi2( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_cid_data *cid_data; + uint32_t val = 0; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + CAM_DBG(CAM_ISP, "CSID:%d count:%d config csi2 rx", + csid_hw->hw_intf->hw_idx, csid_hw->csi2_cfg_cnt); + + /* overflow check before increment */ + if (csid_hw->csi2_cfg_cnt == UINT_MAX) { + CAM_ERR(CAM_ISP, "CSID:%d Open count reached max", + csid_hw->hw_intf->hw_idx); + return -EINVAL; + } + + cid_data = (struct cam_ife_csid_cid_data *)res->res_priv; + + res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + csid_hw->csi2_cfg_cnt++; + if (csid_hw->csi2_cfg_cnt > 1) + return rc; + + /* rx cfg0 */ + val = (csid_hw->csi2_rx_cfg.lane_num - 1) | + (csid_hw->csi2_rx_cfg.lane_cfg << 4) | + (csid_hw->csi2_rx_cfg.lane_type << 24); + val |= (csid_hw->csi2_rx_cfg.phy_sel & 0x3) << 20; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg0_addr); + + /* rx cfg1*/ + val = (1 << csid_reg->csi2_reg->csi2_misr_enable_shift_val); + /* if VC value is more than 3 than set full width of VC */ + if (cid_data->vc > 3) + val |= (1 << csid_reg->csi2_reg->csi2_vc_mode_shift_val); + + /* enable packet ecc correction */ + val |= 1; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg1_addr); + + if (csid_hw->res_type == CAM_ISP_IFE_IN_RES_TPG) { + /* Config the TPG */ + rc = cam_ife_csid_config_tpg(csid_hw, res); + if (rc) { + res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + return rc; + } + } + + /*Enable the CSI2 rx inerrupts */ + val = CSID_CSI2_RX_INFO_RST_DONE | + CSID_CSI2_RX_ERROR_TG_FIFO_OVERFLOW | + CSID_CSI2_RX_ERROR_LANE0_FIFO_OVERFLOW | + CSID_CSI2_RX_ERROR_LANE1_FIFO_OVERFLOW | + CSID_CSI2_RX_ERROR_LANE2_FIFO_OVERFLOW | + CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW | + CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION | + CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION | + CSID_CSI2_RX_ERROR_CRC | + CSID_CSI2_RX_ERROR_ECC | + CSID_CSI2_RX_ERROR_MMAPPED_VC_DT | + CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW | + CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME | + CSID_CSI2_RX_ERROR_CPHY_PH_CRC; + + /* Enable the interrupt based on csid debug info set */ + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ) + val |= CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED; + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ) + val |= CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED | + CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED; + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) + val |= CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED; + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) + val |= CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED; + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) + val |= CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr); + + return 0; +} + +static int cam_ife_csid_disable_csi2( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + + if (res->res_id >= CAM_IFE_CSID_CID_MAX) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid res id :%d", + csid_hw->hw_intf->hw_idx, res->res_id); + return -EINVAL; + } + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + CAM_DBG(CAM_ISP, "CSID:%d cnt : %d Disable csi2 rx", + csid_hw->hw_intf->hw_idx, csid_hw->csi2_cfg_cnt); + + if (csid_hw->csi2_cfg_cnt) + csid_hw->csi2_cfg_cnt--; + + if (csid_hw->csi2_cfg_cnt) + return 0; + + /*Disable the CSI2 rx inerrupts */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg0_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg1_addr); + + res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + return 0; +} + +static int cam_ife_csid_init_config_ipp_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + struct cam_ife_csid_path_cfg *path_data; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t decode_format = 0, plain_format = 0, val = 0; + + path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + if (!csid_reg->ipp_reg) { + CAM_ERR(CAM_ISP, "CSID:%d IPP:%d is not supported on HW", + csid_hw->hw_intf->hw_idx, + res->res_id); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "Config IPP Path"); + rc = cam_ife_csid_get_format_ipp(path_data->in_format, + &decode_format, &plain_format); + if (rc) + return rc; + + /* + * configure the IPP and enable the time stamp capture. + * enable the HW measrurement blocks + */ + val = (path_data->vc << csid_reg->cmn_reg->vc_shift_val) | + (path_data->dt << csid_reg->cmn_reg->dt_shift_val) | + (path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) | + (decode_format << csid_reg->cmn_reg->fmt_shift_val) | + (path_data->crop_enable << + csid_reg->cmn_reg->crop_h_en_shift_val) | + (path_data->crop_enable << + csid_reg->cmn_reg->crop_v_en_shift_val) | + (1 << 1) | 1; + val |= (1 << csid_reg->ipp_reg->pix_store_en_shift_val); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_cfg0_addr); + + /* select the post irq sub sample strobe for time stamp capture */ + cam_io_w_mb(CSID_TIMESTAMP_STB_POST_IRQ, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_cfg1_addr); + + if (path_data->crop_enable) { + val = (((path_data->end_pixel & 0xFFFF) << + csid_reg->cmn_reg->crop_shift) | + (path_data->start_pixel & 0xFFFF)); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_hcrop_addr); + CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x", + csid_hw->hw_intf->hw_idx, val); + + val = (((path_data->end_line & 0xFFFF) << + csid_reg->cmn_reg->crop_shift) | + (path_data->start_line & 0xFFFF)); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_vcrop_addr); + CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x", + csid_hw->hw_intf->hw_idx, val); + } + + /* set frame drop pattern to 0 and period to 1 */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_frm_drop_period_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_frm_drop_pattern_addr); + /* set irq sub sample pattern to 0 and period to 1 */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_subsample_period_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_subsample_pattern_addr); + /* set pixel drop pattern to 0 and period to 1 */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_pix_drop_pattern_addr); + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_pix_drop_period_addr); + /* set line drop pattern to 0 and period to 1 */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_line_drop_pattern_addr); + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_line_drop_period_addr); + + /*Set master or slave IPP */ + if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER) + /*Set halt mode as master */ + val = CSID_HALT_MODE_MASTER << 2; + else if (path_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE) + /*Set halt mode as slave and set master idx */ + val = path_data->master_idx << 4 | CSID_HALT_MODE_SLAVE << 2; + else + /* Default is internal halt mode */ + val = 0; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_ctrl_addr); + + /* Enable the IPP path */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_cfg0_addr); + val |= (1 << csid_reg->cmn_reg->path_en_shift_val); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_cfg0_addr); + + /* configure the rx packet capture based on csid debug set */ + val = 0; + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) + val = ((1 << + csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift)); + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) + val |= ((1 << + csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) | + (path_data->dt << + csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift)); + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) + val |= ((1 << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) | + (path_data->dt << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift)); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr); + CAM_DBG(CAM_ISP, "rx capture control value 0x%x", val); + + res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW; + + return rc; +} + +static int cam_ife_csid_deinit_ipp_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + if (res->res_state != CAM_ISP_RESOURCE_STATE_INIT_HW) { + CAM_ERR(CAM_ISP, + "CSID:%d Res type %d res_id:%d in wrong state %d", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id, res->res_state); + rc = -EINVAL; + } + + if (!csid_reg->ipp_reg) { + CAM_ERR(CAM_ISP, "CSID:%d IPP %d is not supported on HW", + csid_hw->hw_intf->hw_idx, + res->res_id); + rc = -EINVAL; + } + + res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + return rc; +} + +static int cam_ife_csid_enable_ipp_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_path_cfg *path_data; + uint32_t val = 0; + + path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + if (res->res_state != CAM_ISP_RESOURCE_STATE_INIT_HW) { + CAM_ERR(CAM_ISP, + "CSID:%d res type:%d res_id:%d Invalid state%d", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id, res->res_state); + return -EINVAL; + } + + if (!csid_reg->ipp_reg) { + CAM_ERR(CAM_ISP, "CSID:%d IPP %d not supported on HW", + csid_hw->hw_intf->hw_idx, + res->res_id); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "Enable IPP path"); + + /* Resume at frame boundary */ + if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER) { + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_ctrl_addr); + val |= CAM_CSID_RESUME_AT_FRAME_BOUNDARY; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_ctrl_addr); + } else if (path_data->sync_mode == CAM_ISP_HW_SYNC_NONE) { + cam_io_w_mb(CAM_CSID_RESUME_AT_FRAME_BOUNDARY, + soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_ctrl_addr); + } + /* for slave mode, not need to resume for slave device */ + + /* Enable the required ipp interrupts */ + val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW; + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ) + val |= CSID_PATH_INFO_INPUT_SOF; + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ) + val |= CSID_PATH_INFO_INPUT_EOF; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_mask_addr); + + res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + return 0; +} + +static int cam_ife_csid_disable_ipp_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res, + enum cam_ife_csid_halt_cmd stop_cmd) +{ + int rc = 0; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_path_cfg *path_data; + + path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + if (res->res_id >= CAM_IFE_PIX_PATH_RES_MAX) { + CAM_DBG(CAM_ISP, "CSID:%d Invalid res id%d", + csid_hw->hw_intf->hw_idx, res->res_id); + return -EINVAL; + } + + if (res->res_state == CAM_ISP_RESOURCE_STATE_INIT_HW || + res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_DBG(CAM_ISP, "CSID:%d Res:%d already in stopped state:%d", + csid_hw->hw_intf->hw_idx, + res->res_id, res->res_state); + return rc; + } + + if (res->res_state != CAM_ISP_RESOURCE_STATE_STREAMING) { + CAM_DBG(CAM_ISP, "CSID:%d Res:%d Invalid state%d", + csid_hw->hw_intf->hw_idx, res->res_id, + res->res_state); + return -EINVAL; + } + + if (!csid_reg->ipp_reg) { + CAM_ERR(CAM_ISP, "CSID:%d IPP%d is not supported on HW", + csid_hw->hw_intf->hw_idx, res->res_id); + return -EINVAL; + } + + if (stop_cmd != CAM_CSID_HALT_AT_FRAME_BOUNDARY && + stop_cmd != CAM_CSID_HALT_IMMEDIATELY) { + CAM_ERR(CAM_ISP, "CSID:%d un supported stop command:%d", + csid_hw->hw_intf->hw_idx, stop_cmd); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "CSID:%d res_id:%d", + csid_hw->hw_intf->hw_idx, res->res_id); + + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_mask_addr); + + return rc; +} + + +static int cam_ife_csid_init_config_rdi_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + struct cam_ife_csid_path_cfg *path_data; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t path_format = 0, plain_fmt = 0, val = 0, id; + + path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + id = res->res_id; + if (!csid_reg->rdi_reg[id]) { + CAM_ERR(CAM_ISP, "CSID:%d RDI:%d is not supported on HW", + csid_hw->hw_intf->hw_idx, id); + return -EINVAL; + } + + rc = cam_ife_csid_get_format_rdi(path_data->in_format, + path_data->out_format, &path_format, &plain_fmt); + if (rc) + return rc; + + /* + * RDI path config and enable the time stamp capture + * Enable the measurement blocks + */ + val = (path_data->vc << csid_reg->cmn_reg->vc_shift_val) | + (path_data->dt << csid_reg->cmn_reg->dt_shift_val) | + (path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) | + (path_format << csid_reg->cmn_reg->fmt_shift_val) | + (plain_fmt << csid_reg->cmn_reg->plain_fmt_shit_val) | + (path_data->crop_enable << + csid_reg->cmn_reg->crop_h_en_shift_val) | + (path_data->crop_enable << + csid_reg->cmn_reg->crop_v_en_shift_val) | + (1 << 2) | 3; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr); + + /* select the post irq sub sample strobe for time stamp capture */ + cam_io_w_mb(CSID_TIMESTAMP_STB_POST_IRQ, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_cfg1_addr); + + if (path_data->crop_enable) { + val = (((path_data->end_pixel & 0xFFFF) << + csid_reg->cmn_reg->crop_shift) | + (path_data->start_pixel & 0xFFFF)); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_hcrop_addr); + CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x", + csid_hw->hw_intf->hw_idx, val); + + val = (((path_data->end_line & 0xFFFF) << + csid_reg->cmn_reg->crop_shift) | + (path_data->start_line & 0xFFFF)); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_vcrop_addr); + CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x", + csid_hw->hw_intf->hw_idx, val); + } + /* set frame drop pattern to 0 and period to 1 */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_frm_drop_period_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_frm_drop_pattern_addr); + /* set IRQ sum sabmple */ + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_subsample_period_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_subsample_pattern_addr); + + /* set pixel drop pattern to 0 and period to 1 */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_pix_drop_pattern_addr); + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_pix_drop_period_addr); + /* set line drop pattern to 0 and period to 1 */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_line_drop_pattern_addr); + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_rpp_line_drop_period_addr); + + /* Configure the halt mode */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_ctrl_addr); + + /* Enable the RPP path */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr); + val |= (1 << csid_reg->cmn_reg->path_en_shift_val); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr); + + /* configure the rx packet capture based on csid debug set */ + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) + val = ((1 << + csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift)); + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) + val |= ((1 << + csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) | + (path_data->dt << + csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift)); + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) + val |= ((1 << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) | + (path_data->dt << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) | + (path_data->vc << + csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift)); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr); + + res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW; + + return rc; +} + +static int cam_ife_csid_deinit_rdi_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + int rc = 0; + uint32_t id; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + id = res->res_id; + + if (res->res_id > CAM_IFE_PIX_PATH_RES_RDI_3 || + res->res_state != CAM_ISP_RESOURCE_STATE_INIT_HW || + !csid_reg->rdi_reg[id]) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid res id%d state:%d", + csid_hw->hw_intf->hw_idx, res->res_id, + res->res_state); + return -EINVAL; + } + + res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + return rc; +} + +static int cam_ife_csid_enable_rdi_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res) +{ + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t id, val; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + id = res->res_id; + + if (res->res_state != CAM_ISP_RESOURCE_STATE_INIT_HW || + res->res_id > CAM_IFE_PIX_PATH_RES_RDI_3 || + !csid_reg->rdi_reg[id]) { + CAM_ERR(CAM_ISP, + "CSID:%d invalid res type:%d res_id:%d state%d", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id, res->res_state); + return -EINVAL; + } + + /*resume at frame boundary */ + cam_io_w_mb(CAM_CSID_RESUME_AT_FRAME_BOUNDARY, + soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_ctrl_addr); + + /* Enable the required RDI interrupts */ + val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW; + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ) + val |= CSID_PATH_INFO_INPUT_SOF; + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ) + val |= CSID_PATH_INFO_INPUT_EOF; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr); + + res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + return 0; +} + + +static int cam_ife_csid_disable_rdi_path( + struct cam_ife_csid_hw *csid_hw, + struct cam_isp_resource_node *res, + enum cam_ife_csid_halt_cmd stop_cmd) +{ + int rc = 0; + uint32_t id; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + id = res->res_id; + + if (res->res_id >= CAM_IFE_PIX_PATH_RES_MAX || + !csid_reg->rdi_reg[res->res_id]) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d Invalid res id%d", + csid_hw->hw_intf->hw_idx, res->res_id); + return -EINVAL; + } + + if (res->res_state == CAM_ISP_RESOURCE_STATE_INIT_HW || + res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d Res:%d already in stopped state:%d", + csid_hw->hw_intf->hw_idx, + res->res_id, res->res_state); + return rc; + } + + if (res->res_state != CAM_ISP_RESOURCE_STATE_STREAMING) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d Res:%d Invalid res_state%d", + csid_hw->hw_intf->hw_idx, res->res_id, + res->res_state); + return -EINVAL; + } + + if (stop_cmd != CAM_CSID_HALT_AT_FRAME_BOUNDARY && + stop_cmd != CAM_CSID_HALT_IMMEDIATELY) { + CAM_ERR(CAM_ISP, "CSID:%d un supported stop command:%d", + csid_hw->hw_intf->hw_idx, stop_cmd); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "CSID:%d res_id:%d", + csid_hw->hw_intf->hw_idx, res->res_id); + + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr); + + return rc; +} + +static int cam_ife_csid_get_time_stamp( + struct cam_ife_csid_hw *csid_hw, void *cmd_args) +{ + struct cam_csid_get_time_stamp_args *time_stamp; + struct cam_isp_resource_node *res; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + uint32_t time_32, id; + + time_stamp = (struct cam_csid_get_time_stamp_args *)cmd_args; + res = time_stamp->node_res; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + if (res->res_type != CAM_ISP_RESOURCE_PIX_PATH || + res->res_id >= CAM_IFE_PIX_PATH_RES_MAX) { + CAM_DBG(CAM_ISP, "CSID:%d Invalid res_type:%d res id%d", + csid_hw->hw_intf->hw_idx, res->res_type, + res->res_id); + return -EINVAL; + } + + if (csid_hw->hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid dev state :%d", + csid_hw->hw_intf->hw_idx, + csid_hw->hw_info->hw_state); + return -EINVAL; + } + + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) { + time_32 = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_timestamp_curr1_sof_addr); + time_stamp->time_stamp_val = time_32; + time_stamp->time_stamp_val = time_stamp->time_stamp_val << 32; + time_32 = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_timestamp_curr0_sof_addr); + time_stamp->time_stamp_val |= time_32; + } else { + id = res->res_id; + time_32 = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]-> + csid_rdi_timestamp_curr1_sof_addr); + time_stamp->time_stamp_val = time_32; + time_stamp->time_stamp_val = time_stamp->time_stamp_val << 32; + + time_32 = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]-> + csid_rdi_timestamp_curr0_sof_addr); + time_stamp->time_stamp_val |= time_32; + } + + time_stamp->time_stamp_val = mul_u64_u32_div( + time_stamp->time_stamp_val, + CAM_IFE_CSID_QTIMER_MUL_FACTOR, + CAM_IFE_CSID_QTIMER_DIV_FACTOR); + + return 0; +} + +static int cam_ife_csid_set_csid_debug(struct cam_ife_csid_hw *csid_hw, + void *cmd_args) +{ + uint32_t *csid_debug; + + csid_debug = (uint32_t *) cmd_args; + csid_hw->csid_debug = *csid_debug; + CAM_DBG(CAM_ISP, "CSID:%d set csid debug value:%d", + csid_hw->hw_intf->hw_idx, csid_hw->csid_debug); + + return 0; +} + +static int cam_ife_csid_get_hw_caps(void *hw_priv, + void *get_hw_cap_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw_caps *hw_caps; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_ife_csid_reg_offset *csid_reg; + + if (!hw_priv || !get_hw_cap_args) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + csid_reg = csid_hw->csid_info->csid_reg; + hw_caps = (struct cam_ife_csid_hw_caps *) get_hw_cap_args; + + hw_caps->no_rdis = csid_reg->cmn_reg->no_rdis; + hw_caps->no_pix = csid_reg->cmn_reg->no_pix; + hw_caps->major_version = csid_reg->cmn_reg->major_version; + hw_caps->minor_version = csid_reg->cmn_reg->minor_version; + hw_caps->version_incr = csid_reg->cmn_reg->version_incr; + + CAM_DBG(CAM_ISP, + "CSID:%d No rdis:%d, no pix:%d, major:%d minor:%d ver :%d", + csid_hw->hw_intf->hw_idx, hw_caps->no_rdis, + hw_caps->no_pix, hw_caps->major_version, hw_caps->minor_version, + hw_caps->version_incr); + + return rc; +} + +static int cam_ife_csid_reset(void *hw_priv, + void *reset_args, uint32_t arg_size) +{ + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_csid_reset_cfg_args *reset; + int rc = 0; + + if (!hw_priv || !reset_args || (arg_size != + sizeof(struct cam_csid_reset_cfg_args))) { + CAM_ERR(CAM_ISP, "CSID:Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + reset = (struct cam_csid_reset_cfg_args *)reset_args; + + switch (reset->reset_type) { + case CAM_IFE_CSID_RESET_GLOBAL: + rc = cam_ife_csid_global_reset(csid_hw); + break; + case CAM_IFE_CSID_RESET_PATH: + rc = cam_ife_csid_path_reset(csid_hw, reset); + break; + default: + CAM_ERR(CAM_ISP, "CSID:Invalid reset type :%d", + reset->reset_type); + rc = -EINVAL; + break; + } + + return rc; +} + +static int cam_ife_csid_reserve(void *hw_priv, + void *reserve_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_csid_hw_reserve_resource_args *reserv; + + if (!hw_priv || !reserve_args || (arg_size != + sizeof(struct cam_csid_hw_reserve_resource_args))) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + reserv = (struct cam_csid_hw_reserve_resource_args *)reserve_args; + + mutex_lock(&csid_hw->hw_info->hw_mutex); + switch (reserv->res_type) { + case CAM_ISP_RESOURCE_CID: + rc = cam_ife_csid_cid_reserve(csid_hw, reserv); + break; + case CAM_ISP_RESOURCE_PIX_PATH: + rc = cam_ife_csid_path_reserve(csid_hw, reserv); + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type :%d", + csid_hw->hw_intf->hw_idx, reserv->res_type); + rc = -EINVAL; + break; + } + mutex_unlock(&csid_hw->hw_info->hw_mutex); + return rc; +} + +static int cam_ife_csid_release(void *hw_priv, + void *release_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_isp_resource_node *res; + struct cam_ife_csid_cid_data *cid_data; + + if (!hw_priv || !release_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + res = (struct cam_isp_resource_node *)release_args; + + mutex_lock(&csid_hw->hw_info->hw_mutex); + if ((res->res_type == CAM_ISP_RESOURCE_CID && + res->res_id >= CAM_IFE_CSID_CID_MAX) || + (res->res_type == CAM_ISP_RESOURCE_PIX_PATH && + res->res_id >= CAM_IFE_PIX_PATH_RES_MAX)) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type:%d res id%d", + csid_hw->hw_intf->hw_idx, res->res_type, + res->res_id); + rc = -EINVAL; + goto end; + } + + if (res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_DBG(CAM_ISP, + "CSID:%d res type:%d Res %d in released state", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id); + goto end; + } + + if (res->res_type == CAM_ISP_RESOURCE_PIX_PATH && + res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_DBG(CAM_ISP, + "CSID:%d res type:%d Res id:%d invalid state:%d", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id, res->res_state); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_ISP, "CSID:%d res type :%d Resource id:%d", + csid_hw->hw_intf->hw_idx, res->res_type, res->res_id); + + switch (res->res_type) { + case CAM_ISP_RESOURCE_CID: + cid_data = (struct cam_ife_csid_cid_data *) res->res_priv; + if (cid_data->cnt) + cid_data->cnt--; + + if (!cid_data->cnt) + res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + + if (csid_hw->csi2_reserve_cnt) + csid_hw->csi2_reserve_cnt--; + + if (!csid_hw->csi2_reserve_cnt) + memset(&csid_hw->csi2_rx_cfg, 0, + sizeof(struct cam_ife_csid_csi2_rx_cfg)); + + CAM_DBG(CAM_ISP, "CSID:%d res id :%d cnt:%d reserv cnt:%d", + csid_hw->hw_intf->hw_idx, + res->res_id, cid_data->cnt, csid_hw->csi2_reserve_cnt); + + break; + case CAM_ISP_RESOURCE_PIX_PATH: + res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type:%d res id%d", + csid_hw->hw_intf->hw_idx, res->res_type, + res->res_id); + rc = -EINVAL; + break; + } + +end: + mutex_unlock(&csid_hw->hw_info->hw_mutex); + return rc; +} + +static int cam_ife_csid_reset_retain_sw_reg( + struct cam_ife_csid_hw *csid_hw) +{ + int rc = 0; + struct cam_ife_csid_reg_offset *csid_reg = + csid_hw->csid_info->csid_reg; + + cam_io_w_mb(csid_reg->cmn_reg->csid_rst_stb, + csid_hw->hw_info->soc_info.reg_map[0].mem_base + + csid_reg->cmn_reg->csid_rst_strobes_addr); + + CAM_DBG(CAM_ISP, " Waiting for SW reset complete from irq handler"); + rc = wait_for_completion_timeout(&csid_hw->csid_top_complete, + msecs_to_jiffies(IFE_CSID_TIMEOUT)); + if (rc <= 0) { + CAM_ERR(CAM_ISP, "CSID:%d reset completion in fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + if (rc == 0) + rc = -ETIMEDOUT; + } else { + rc = 0; + } + + return rc; +} + +static int cam_ife_csid_init_hw(void *hw_priv, + void *init_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_isp_resource_node *res; + struct cam_ife_csid_reg_offset *csid_reg; + + if (!hw_priv || !init_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + res = (struct cam_isp_resource_node *)init_args; + csid_reg = csid_hw->csid_info->csid_reg; + + mutex_lock(&csid_hw->hw_info->hw_mutex); + if ((res->res_type == CAM_ISP_RESOURCE_CID && + res->res_id >= CAM_IFE_CSID_CID_MAX) || + (res->res_type == CAM_ISP_RESOURCE_PIX_PATH && + res->res_id >= CAM_IFE_PIX_PATH_RES_MAX)) { + CAM_ERR(CAM_ISP, "CSID:%d Invalid res tpe:%d res id%d", + csid_hw->hw_intf->hw_idx, res->res_type, + res->res_id); + rc = -EINVAL; + goto end; + } + + if ((res->res_type == CAM_ISP_RESOURCE_PIX_PATH) && + (res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED)) { + CAM_ERR(CAM_ISP, + "CSID:%d res type:%d res_id:%dInvalid state %d", + csid_hw->hw_intf->hw_idx, + res->res_type, res->res_id, res->res_state); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_ISP, "CSID:%d res type :%d res_id:%d", + csid_hw->hw_intf->hw_idx, res->res_type, res->res_id); + + /* Initialize the csid hardware */ + rc = cam_ife_csid_enable_hw(csid_hw); + if (rc) + goto end; + + switch (res->res_type) { + case CAM_ISP_RESOURCE_CID: + rc = cam_ife_csid_enable_csi2(csid_hw, res); + break; + case CAM_ISP_RESOURCE_PIX_PATH: + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) + rc = cam_ife_csid_init_config_ipp_path(csid_hw, res); + else + rc = cam_ife_csid_init_config_rdi_path(csid_hw, res); + + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type state %d", + csid_hw->hw_intf->hw_idx, + res->res_type); + break; + } + + rc = cam_ife_csid_reset_retain_sw_reg(csid_hw); + if (rc < 0) { + CAM_ERR(CAM_ISP, "CSID: Failed in SW reset"); + } + + if (rc) + cam_ife_csid_disable_hw(csid_hw); +end: + mutex_unlock(&csid_hw->hw_info->hw_mutex); + return rc; +} + +static int cam_ife_csid_deinit_hw(void *hw_priv, + void *deinit_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_isp_resource_node *res; + + if (!hw_priv || !deinit_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "CSID:Invalid arguments"); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "Enter"); + res = (struct cam_isp_resource_node *)deinit_args; + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + + mutex_lock(&csid_hw->hw_info->hw_mutex); + if (res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_DBG(CAM_ISP, "CSID:%d Res:%d already in De-init state", + csid_hw->hw_intf->hw_idx, + res->res_id); + goto end; + } + + switch (res->res_type) { + case CAM_ISP_RESOURCE_CID: + CAM_DBG(CAM_ISP, "De-Init ife_csid"); + rc = cam_ife_csid_disable_csi2(csid_hw, res); + break; + case CAM_ISP_RESOURCE_PIX_PATH: + CAM_DBG(CAM_ISP, "De-Init Pix Path: %d\n", res->res_id); + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) + rc = cam_ife_csid_deinit_ipp_path(csid_hw, res); + else + rc = cam_ife_csid_deinit_rdi_path(csid_hw, res); + + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid Res type %d", + csid_hw->hw_intf->hw_idx, + res->res_type); + goto end; + } + + /* Disable CSID HW */ + CAM_DBG(CAM_ISP, "Disabling CSID Hw\n"); + cam_ife_csid_disable_hw(csid_hw); + CAM_DBG(CAM_ISP, "%s: Exit\n", __func__); + +end: + mutex_unlock(&csid_hw->hw_info->hw_mutex); + return rc; +} + +static int cam_ife_csid_start(void *hw_priv, void *start_args, + uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_isp_resource_node *res; + struct cam_ife_csid_reg_offset *csid_reg; + + if (!hw_priv || !start_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + res = (struct cam_isp_resource_node *)start_args; + csid_reg = csid_hw->csid_info->csid_reg; + + if ((res->res_type == CAM_ISP_RESOURCE_CID && + res->res_id >= CAM_IFE_CSID_CID_MAX) || + (res->res_type == CAM_ISP_RESOURCE_PIX_PATH && + res->res_id >= CAM_IFE_PIX_PATH_RES_MAX)) { + CAM_DBG(CAM_ISP, "CSID:%d Invalid res tpe:%d res id:%d", + csid_hw->hw_intf->hw_idx, res->res_type, + res->res_id); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_ISP, "CSID:%d res_type :%d res_id:%d", + csid_hw->hw_intf->hw_idx, res->res_type, res->res_id); + + switch (res->res_type) { + case CAM_ISP_RESOURCE_CID: + if (csid_hw->res_type == CAM_ISP_IFE_IN_RES_TPG) + rc = cam_ife_csid_tpg_start(csid_hw, res); + break; + case CAM_ISP_RESOURCE_PIX_PATH: + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) + rc = cam_ife_csid_enable_ipp_path(csid_hw, res); + else + rc = cam_ife_csid_enable_rdi_path(csid_hw, res); + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type%d", + csid_hw->hw_intf->hw_idx, + res->res_type); + break; + } +end: + return rc; +} + +static int cam_ife_csid_stop(void *hw_priv, + void *stop_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + struct cam_isp_resource_node *res; + struct cam_csid_hw_stop_args *csid_stop; + uint32_t i; + + if (!hw_priv || !stop_args || + (arg_size != sizeof(struct cam_csid_hw_stop_args))) { + CAM_ERR(CAM_ISP, "CSID: Invalid args"); + return -EINVAL; + } + csid_stop = (struct cam_csid_hw_stop_args *) stop_args; + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + + /* Stop the resource first */ + for (i = 0; i < csid_stop->num_res; i++) { + res = csid_stop->node_res[i]; + switch (res->res_type) { + case CAM_ISP_RESOURCE_CID: + if (csid_hw->res_type == CAM_ISP_IFE_IN_RES_TPG) + rc = cam_ife_csid_tpg_stop(csid_hw, res); + break; + case CAM_ISP_RESOURCE_PIX_PATH: + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) + rc = cam_ife_csid_disable_ipp_path(csid_hw, + res, csid_stop->stop_cmd); + else + rc = cam_ife_csid_disable_rdi_path(csid_hw, + res, csid_stop->stop_cmd); + + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d Invalid res type%d", + csid_hw->hw_intf->hw_idx, + res->res_type); + break; + } + } + + for (i = 0; i < csid_stop->num_res; i++) { + res = csid_stop->node_res[i]; + res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW; + } + + CAM_DBG(CAM_ISP, "%s: Exit\n", __func__); + + return rc; + +} + +static int cam_ife_csid_read(void *hw_priv, + void *read_args, uint32_t arg_size) +{ + CAM_ERR(CAM_ISP, "CSID: un supported"); + + return -EINVAL; +} + +static int cam_ife_csid_write(void *hw_priv, + void *write_args, uint32_t arg_size) +{ + CAM_ERR(CAM_ISP, "CSID: un supported"); + return -EINVAL; +} + +static int cam_ife_csid_process_cmd(void *hw_priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_info *csid_hw_info; + + if (!hw_priv || !cmd_args) { + CAM_ERR(CAM_ISP, "CSID: Invalid arguments"); + return -EINVAL; + } + + csid_hw_info = (struct cam_hw_info *)hw_priv; + csid_hw = (struct cam_ife_csid_hw *)csid_hw_info->core_info; + + switch (cmd_type) { + case CAM_IFE_CSID_CMD_GET_TIME_STAMP: + rc = cam_ife_csid_get_time_stamp(csid_hw, cmd_args); + break; + case CAM_IFE_CSID_SET_CSID_DEBUG: + rc = cam_ife_csid_set_csid_debug(csid_hw, cmd_args); + break; + default: + CAM_ERR(CAM_ISP, "CSID:%d un supported cmd:%d", + csid_hw->hw_intf->hw_idx, cmd_type); + rc = -EINVAL; + break; + } + + return rc; + +} + +static int cam_ife_csid_halt_device( + struct cam_ife_csid_hw *csid_hw) +{ + uint32_t i; + int rc = 0; + struct cam_isp_resource_node *res_node; + struct cam_ife_csid_reg_offset *csid_reg; + struct cam_hw_soc_info *soc_info; + + res_node = &csid_hw->ipp_res; + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + if (res_node->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) { + rc = cam_ife_csid_disable_ipp_path(csid_hw, + res_node, CAM_CSID_HALT_IMMEDIATELY); + res_node->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW; + } + + for (i = 0; i < CAM_IFE_CSID_RDI_MAX; i++) { + res_node = &csid_hw->rdi_res[i]; + if (res_node->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) { + rc = cam_ife_csid_disable_rdi_path(csid_hw, + res_node, CAM_CSID_HALT_IMMEDIATELY); + res_node->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW; + } + } + + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg0_addr); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_cfg1_addr); + return rc; +} + +irqreturn_t cam_ife_csid_irq(int irq_num, void *data) +{ + struct cam_ife_csid_hw *csid_hw; + struct cam_hw_soc_info *soc_info; + struct cam_ife_csid_reg_offset *csid_reg; + uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0; + uint32_t irq_status_rdi[4] = {0, 0, 0, 0}; + uint32_t val; + int rc; + + csid_hw = (struct cam_ife_csid_hw *)data; + + CAM_DBG(CAM_ISP, "CSID %d IRQ Handling", csid_hw->hw_intf->hw_idx); + + if (!data) { + CAM_ERR(CAM_ISP, "CSID: Invalid arguments"); + return IRQ_HANDLED; + } + + csid_reg = csid_hw->csid_info->csid_reg; + soc_info = &csid_hw->hw_info->soc_info; + + /* read */ + irq_status_top = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_status_addr); + + irq_status_rx = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_status_addr); + + if (csid_reg->cmn_reg->no_pix) + irq_status_ipp = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_status_addr); + + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) + irq_status_rdi[i] = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_irq_status_addr); + + /* clear */ + cam_io_w_mb(irq_status_top, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_top_irq_clear_addr); + cam_io_w_mb(irq_status_rx, soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg->csid_csi2_rx_irq_clear_addr); + if (csid_reg->cmn_reg->no_pix) + cam_io_w_mb(irq_status_ipp, soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_irq_clear_addr); + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) { + cam_io_w_mb(irq_status_rdi[i], soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_irq_clear_addr); + } + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + csid_reg->cmn_reg->csid_irq_cmd_addr); + + CAM_DBG(CAM_ISP, "irq_status_top = 0x%x", irq_status_top); + CAM_INFO(CAM_ISP, "irq_status_rx = 0x%x", irq_status_rx); + CAM_DBG(CAM_ISP, "irq_status_ipp = 0x%x", irq_status_ipp); + CAM_DBG(CAM_ISP, "irq_status_rdi0= 0x%x", irq_status_rdi[0]); + CAM_DBG(CAM_ISP, "irq_status_rdi1= 0x%x", irq_status_rdi[1]); + CAM_DBG(CAM_ISP, "irq_status_rdi2= 0x%x", irq_status_rdi[2]); + + if (irq_status_top) { + CAM_DBG(CAM_ISP, "CSID global reset complete......Exit"); + complete(&csid_hw->csid_top_complete); + return IRQ_HANDLED; + } + + + if (irq_status_rx & BIT(csid_reg->csi2_reg->csi2_rst_done_shift_val)) { + CAM_DBG(CAM_ISP, "csi rx reset complete"); + complete(&csid_hw->csid_csi2_complete); + } + + if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE0_FIFO_OVERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 0 over flow", + csid_hw->hw_intf->hw_idx); + if (!(irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION)) { + rc = cam_ife_csid_halt_device(csid_hw); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d csid halt device fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + } + } + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE1_FIFO_OVERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 1 over flow", + csid_hw->hw_intf->hw_idx); + if (!(irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION)) { + rc = cam_ife_csid_halt_device(csid_hw); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d csid halt device fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + } + } + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE2_FIFO_OVERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 2 over flow", + csid_hw->hw_intf->hw_idx); + if (!(irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION)) { + rc = cam_ife_csid_halt_device(csid_hw); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d csid halt device fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + } + } + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 3 over flow", + csid_hw->hw_intf->hw_idx); + if (!(irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION)) { + rc = cam_ife_csid_halt_device(csid_hw); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d csid halt device fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + } + } + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_TG_FIFO_OVERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d TG OVER FLOW", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_EOT_RECEPTION", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_SOT_RECEPTION", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_PH_CRC) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_PH_CRC", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_CRC) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_CRC", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_ECC) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_ECC", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_MMAPPED_VC_DT) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d MMAPPED_VC_DT", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_STREAM_UNDERFLOW", + csid_hw->hw_intf->hw_idx); + rc = cam_ife_csid_halt_device(csid_hw); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "CSID:%d csid halt device fail rc = %d", + csid_hw->hw_intf->hw_idx, rc); + } + } + if (irq_status_rx & CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d UNBOUNDED_FRAME", + csid_hw->hw_intf->hw_idx); + } + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ) { + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_EOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_EOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_EOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_EOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + } + + if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ) { + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_SOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_SOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_SOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED) { + CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_SOT_CAPTURED", + csid_hw->hw_intf->hw_idx); + } + } + + if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) && + (irq_status_rx & CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED)) { + CAM_ERR(CAM_ISP, "CSID:%d LONG_PKT_CAPTURED", + csid_hw->hw_intf->hw_idx); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_long_pkt_0_addr); + CAM_ERR(CAM_ISP, "CSID:%d long packet VC :%d DT:%d WC:%d", + csid_hw->hw_intf->hw_idx, + (val >> 22), ((val >> 16) & 0x3F), (val & 0xFFFF)); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_long_pkt_1_addr); + CAM_ERR(CAM_ISP, "CSID:%d long packet ECC :%d", + csid_hw->hw_intf->hw_idx, val); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_long_pkt_ftr_addr); + CAM_ERR(CAM_ISP, "CSID:%d long pkt cal CRC:%d expected CRC:%d", + csid_hw->hw_intf->hw_idx, (val >> 16), (val & 0xFFFF)); + } + if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) && + (irq_status_rx & CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED)) { + CAM_ERR(CAM_ISP, "CSID:%d SHORT_PKT_CAPTURED", + csid_hw->hw_intf->hw_idx); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_short_pkt_0_addr); + CAM_ERR(CAM_ISP, "CSID:%d short pkt VC :%d DT:%d LC:%d", + csid_hw->hw_intf->hw_idx, + (val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF)); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_short_pkt_1_addr); + CAM_ERR(CAM_ISP, "CSID:%d short packet ECC :%d", + csid_hw->hw_intf->hw_idx, val); + } + + if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) && + (irq_status_rx & CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED)) { + CAM_ERR(CAM_ISP, "CSID:%d CPHY_PKT_HDR_CAPTURED", + csid_hw->hw_intf->hw_idx); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->csi2_reg-> + csid_csi2_rx_captured_cphy_pkt_hdr_addr); + CAM_ERR(CAM_ISP, "CSID:%d cphy packet VC :%d DT:%d WC:%d", + csid_hw->hw_intf->hw_idx, + (val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF)); + } + + /*read the IPP errors */ + if (csid_reg->cmn_reg->no_pix) { + /* IPP reset done bit */ + if (irq_status_ipp & + BIT(csid_reg->cmn_reg->path_rst_done_shift_val)) { + CAM_DBG(CAM_ISP, "CSID IPP reset complete"); + complete(&csid_hw->csid_ipp_complete); + } + + if ((irq_status_ipp & CSID_PATH_INFO_INPUT_SOF) && + (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)) + CAM_ERR(CAM_ISP, "CSID:%d IPP SOF received", + csid_hw->hw_intf->hw_idx); + + if ((irq_status_ipp & CSID_PATH_INFO_INPUT_EOF) && + (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)) + CAM_ERR(CAM_ISP, "CSID:%d IPP EOF received", + csid_hw->hw_intf->hw_idx); + + if (irq_status_ipp & CSID_PATH_ERROR_FIFO_OVERFLOW) { + CAM_ERR(CAM_ISP, "CSID:%d IPP fifo over flow", + csid_hw->hw_intf->hw_idx); + /*Stop IPP path immediately */ + cam_io_w_mb(CAM_CSID_HALT_IMMEDIATELY, + soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_ipp_ctrl_addr); + } + } + + for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) { + if (irq_status_rdi[i] & + BIT(csid_reg->cmn_reg->path_rst_done_shift_val)) { + CAM_DBG(CAM_ISP, "CSID RDI%d reset complete", i); + complete(&csid_hw->csid_rdin_complete[i]); + } + + if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_SOF) && + (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)) + CAM_ERR(CAM_ISP, "CSID RDI:%d SOF received", i); + + if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_EOF) && + (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)) + CAM_ERR(CAM_ISP, "CSID RDI:%d EOF received", i); + + if (irq_status_rdi[i] & CSID_PATH_ERROR_FIFO_OVERFLOW) { + CAM_ERR(CAM_ISP, "CSID:%d RDI fifo over flow", + csid_hw->hw_intf->hw_idx); + /*Stop RDI path immediately */ + cam_io_w_mb(CAM_CSID_HALT_IMMEDIATELY, + soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_ctrl_addr); + } + } + + CAM_DBG(CAM_ISP, "IRQ Handling exit"); + return IRQ_HANDLED; +} + +int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, + uint32_t csid_idx) +{ + int rc = -EINVAL; + uint32_t i; + struct cam_ife_csid_path_cfg *path_data; + struct cam_ife_csid_cid_data *cid_data; + struct cam_hw_info *csid_hw_info; + struct cam_ife_csid_hw *ife_csid_hw = NULL; + + if (csid_idx >= CAM_IFE_CSID_HW_RES_MAX) { + CAM_ERR(CAM_ISP, "Invalid csid index:%d", csid_idx); + return rc; + } + + csid_hw_info = (struct cam_hw_info *) csid_hw_intf->hw_priv; + ife_csid_hw = (struct cam_ife_csid_hw *) csid_hw_info->core_info; + + ife_csid_hw->hw_intf = csid_hw_intf; + ife_csid_hw->hw_info = csid_hw_info; + + CAM_DBG(CAM_ISP, "type %d index %d", + ife_csid_hw->hw_intf->hw_type, csid_idx); + + + ife_csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&ife_csid_hw->hw_info->hw_mutex); + spin_lock_init(&ife_csid_hw->hw_info->hw_lock); + init_completion(&ife_csid_hw->hw_info->hw_complete); + + init_completion(&ife_csid_hw->csid_top_complete); + init_completion(&ife_csid_hw->csid_csi2_complete); + init_completion(&ife_csid_hw->csid_ipp_complete); + for (i = 0; i < CAM_IFE_CSID_RDI_MAX; i++) + init_completion(&ife_csid_hw->csid_rdin_complete[i]); + + + rc = cam_ife_csid_init_soc_resources(&ife_csid_hw->hw_info->soc_info, + cam_ife_csid_irq, ife_csid_hw); + if (rc < 0) { + CAM_ERR(CAM_ISP, "CSID:%d Failed to init_soc", csid_idx); + goto err; + } + + ife_csid_hw->hw_intf->hw_ops.get_hw_caps = cam_ife_csid_get_hw_caps; + ife_csid_hw->hw_intf->hw_ops.init = cam_ife_csid_init_hw; + ife_csid_hw->hw_intf->hw_ops.deinit = cam_ife_csid_deinit_hw; + ife_csid_hw->hw_intf->hw_ops.reset = cam_ife_csid_reset; + ife_csid_hw->hw_intf->hw_ops.reserve = cam_ife_csid_reserve; + ife_csid_hw->hw_intf->hw_ops.release = cam_ife_csid_release; + ife_csid_hw->hw_intf->hw_ops.start = cam_ife_csid_start; + ife_csid_hw->hw_intf->hw_ops.stop = cam_ife_csid_stop; + ife_csid_hw->hw_intf->hw_ops.read = cam_ife_csid_read; + ife_csid_hw->hw_intf->hw_ops.write = cam_ife_csid_write; + ife_csid_hw->hw_intf->hw_ops.process_cmd = cam_ife_csid_process_cmd; + + /*Initialize the CID resoure */ + for (i = 0; i < CAM_IFE_CSID_CID_RES_MAX; i++) { + ife_csid_hw->cid_res[i].res_type = CAM_ISP_RESOURCE_CID; + ife_csid_hw->cid_res[i].res_id = i; + ife_csid_hw->cid_res[i].res_state = + CAM_ISP_RESOURCE_STATE_AVAILABLE; + ife_csid_hw->cid_res[i].hw_intf = ife_csid_hw->hw_intf; + + cid_data = kzalloc(sizeof(struct cam_ife_csid_cid_data), + GFP_KERNEL); + if (!cid_data) { + rc = -ENOMEM; + goto err; + } + ife_csid_hw->cid_res[i].res_priv = cid_data; + } + + /* Initialize the IPP resources */ + if (ife_csid_hw->csid_info->csid_reg->cmn_reg->no_pix) { + ife_csid_hw->ipp_res.res_type = CAM_ISP_RESOURCE_PIX_PATH; + ife_csid_hw->ipp_res.res_id = CAM_IFE_PIX_PATH_RES_IPP; + ife_csid_hw->ipp_res.res_state = + CAM_ISP_RESOURCE_STATE_AVAILABLE; + ife_csid_hw->ipp_res.hw_intf = ife_csid_hw->hw_intf; + path_data = kzalloc(sizeof(struct cam_ife_csid_path_cfg), + GFP_KERNEL); + if (!path_data) { + rc = -ENOMEM; + goto err; + } + ife_csid_hw->ipp_res.res_priv = path_data; + } + + /* Initialize the RDI resource */ + for (i = 0; i < ife_csid_hw->csid_info->csid_reg->cmn_reg->no_rdis; + i++) { + /* res type is from RDI 0 to RDI3 */ + ife_csid_hw->rdi_res[i].res_type = + CAM_ISP_RESOURCE_PIX_PATH; + ife_csid_hw->rdi_res[i].res_id = i; + ife_csid_hw->rdi_res[i].res_state = + CAM_ISP_RESOURCE_STATE_AVAILABLE; + ife_csid_hw->rdi_res[i].hw_intf = ife_csid_hw->hw_intf; + + path_data = kzalloc(sizeof(struct cam_ife_csid_path_cfg), + GFP_KERNEL); + if (!path_data) { + rc = -ENOMEM; + goto err; + } + ife_csid_hw->rdi_res[i].res_priv = path_data; + } + + ife_csid_hw->csid_debug = 0; + return 0; +err: + if (rc) { + kfree(ife_csid_hw->ipp_res.res_priv); + for (i = 0; i < + ife_csid_hw->csid_info->csid_reg->cmn_reg->no_rdis; i++) + kfree(ife_csid_hw->rdi_res[i].res_priv); + + for (i = 0; i < CAM_IFE_CSID_CID_RES_MAX; i++) + kfree(ife_csid_hw->cid_res[i].res_priv); + + } + + return rc; +} + + +int cam_ife_csid_hw_deinit(struct cam_ife_csid_hw *ife_csid_hw) +{ + int rc = -EINVAL; + uint32_t i; + + if (!ife_csid_hw) { + CAM_ERR(CAM_ISP, "Invalid param"); + return rc; + } + + /* release the privdate data memory from resources */ + kfree(ife_csid_hw->ipp_res.res_priv); + for (i = 0; i < + ife_csid_hw->csid_info->csid_reg->cmn_reg->no_rdis; + i++) { + kfree(ife_csid_hw->rdi_res[i].res_priv); + } + for (i = 0; i < CAM_IFE_CSID_CID_RES_MAX; i++) + kfree(ife_csid_hw->cid_res[i].res_priv); + + cam_ife_csid_deinit_soc_resources(&ife_csid_hw->hw_info->soc_info); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h new file mode 100644 index 000000000000..681a47f031ba --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h @@ -0,0 +1,466 @@ +/* Copyright (c) 2017-2018, 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 _CAM_IFE_CSID_HW_H_ +#define _CAM_IFE_CSID_HW_H_ + +#include "cam_hw.h" +#include "cam_ife_csid_hw_intf.h" +#include "cam_ife_csid_soc.h" + +#define CAM_IFE_CSID_HW_RES_MAX 4 +#define CAM_IFE_CSID_CID_RES_MAX 4 +#define CAM_IFE_CSID_RDI_MAX 4 + +#define CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED BIT(0) +#define CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED BIT(1) +#define CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED BIT(2) +#define CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED BIT(3) +#define CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED BIT(4) +#define CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED BIT(5) +#define CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED BIT(6) +#define CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED BIT(7) +#define CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED BIT(8) +#define CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED BIT(9) +#define CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED BIT(10) +#define CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION BIT(11) +#define CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION BIT(12) +#define CSID_CSI2_RX_ERROR_CPHY_PH_CRC BIT(13) +#define CSID_CSI2_RX_WARNING_ECC BIT(14) +#define CSID_CSI2_RX_ERROR_LANE0_FIFO_OVERFLOW BIT(15) +#define CSID_CSI2_RX_ERROR_LANE1_FIFO_OVERFLOW BIT(16) +#define CSID_CSI2_RX_ERROR_LANE2_FIFO_OVERFLOW BIT(17) +#define CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW BIT(18) +#define CSID_CSI2_RX_ERROR_CRC BIT(19) +#define CSID_CSI2_RX_ERROR_ECC BIT(20) +#define CSID_CSI2_RX_ERROR_MMAPPED_VC_DT BIT(21) +#define CSID_CSI2_RX_ERROR_UNMAPPED_VC_DT BIT(22) +#define CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW BIT(23) +#define CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME BIT(24) +#define CSID_CSI2_RX_INFO_TG_DONE BIT(25) +#define CSID_CSI2_RX_ERROR_TG_FIFO_OVERFLOW BIT(26) +#define CSID_CSI2_RX_INFO_RST_DONE BIT(27) + +#define CSID_PATH_INFO_RST_DONE BIT(1) +#define CSID_PATH_ERROR_FIFO_OVERFLOW BIT(2) +#define CSID_PATH_INFO_SUBSAMPLED_EOF BIT(3) +#define CSID_PATH_INFO_SUBSAMPLED_SOF BIT(4) +#define CSID_PATH_INFO_FRAME_DROP_EOF BIT(5) +#define CSID_PATH_INFO_FRAME_DROP_EOL BIT(6) +#define CSID_PATH_INFO_FRAME_DROP_SOL BIT(7) +#define CSID_PATH_INFO_FRAME_DROP_SOF BIT(8) +#define CSID_PATH_INFO_INPUT_EOF BIT(9) +#define CSID_PATH_INFO_INPUT_EOL BIT(10) +#define CSID_PATH_INFO_INPUT_SOL BIT(11) +#define CSID_PATH_INFO_INPUT_SOF BIT(12) +#define CSID_PATH_ERROR_PIX_COUNT BIT(13) +#define CSID_PATH_ERROR_LINE_COUNT BIT(14) + +/* + * Debug values enable the corresponding interrupts and debug logs provide + * necessary information + */ +#define CSID_DEBUG_ENABLE_SOF_IRQ BIT(0) +#define CSID_DEBUG_ENABLE_EOF_IRQ BIT(1) +#define CSID_DEBUG_ENABLE_SOT_IRQ BIT(2) +#define CSID_DEBUG_ENABLE_EOT_IRQ BIT(3) +#define CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE BIT(4) +#define CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE BIT(5) +#define CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE BIT(6) + +/* enum cam_csid_path_halt_mode select the path halt mode control */ +enum cam_csid_path_halt_mode { + CSID_HALT_MODE_INTERNAL, + CSID_HALT_MODE_GLOBAL, + CSID_HALT_MODE_MASTER, + CSID_HALT_MODE_SLAVE, +}; + +/** + *enum cam_csid_path_timestamp_stb_sel - select the sof/eof strobes used to + * capture the timestamp + */ +enum cam_csid_path_timestamp_stb_sel { + CSID_TIMESTAMP_STB_PRE_HALT, + CSID_TIMESTAMP_STB_POST_HALT, + CSID_TIMESTAMP_STB_POST_IRQ, + CSID_TIMESTAMP_STB_MAX, +}; + +struct cam_ife_csid_ipp_reg_offset { + /*Image pixel path register offsets*/ + uint32_t csid_ipp_irq_status_addr; + uint32_t csid_ipp_irq_mask_addr; + uint32_t csid_ipp_irq_clear_addr; + uint32_t csid_ipp_irq_set_addr; + + uint32_t csid_ipp_cfg0_addr; + uint32_t csid_ipp_cfg1_addr; + uint32_t csid_ipp_ctrl_addr; + uint32_t csid_ipp_frm_drop_pattern_addr; + uint32_t csid_ipp_frm_drop_period_addr; + uint32_t csid_ipp_irq_subsample_pattern_addr; + uint32_t csid_ipp_irq_subsample_period_addr; + uint32_t csid_ipp_hcrop_addr; + uint32_t csid_ipp_vcrop_addr; + uint32_t csid_ipp_pix_drop_pattern_addr; + uint32_t csid_ipp_pix_drop_period_addr; + uint32_t csid_ipp_line_drop_pattern_addr; + uint32_t csid_ipp_line_drop_period_addr; + uint32_t csid_ipp_rst_strobes_addr; + uint32_t csid_ipp_status_addr; + uint32_t csid_ipp_misr_val_addr; + uint32_t csid_ipp_format_measure_cfg0_addr; + uint32_t csid_ipp_format_measure_cfg1_addr; + uint32_t csid_ipp_format_measure0_addr; + uint32_t csid_ipp_format_measure1_addr; + uint32_t csid_ipp_format_measure2_addr; + uint32_t csid_ipp_timestamp_curr0_sof_addr; + uint32_t csid_ipp_timestamp_curr1_sof_addr; + uint32_t csid_ipp_timestamp_perv0_sof_addr; + uint32_t csid_ipp_timestamp_perv1_sof_addr; + uint32_t csid_ipp_timestamp_curr0_eof_addr; + uint32_t csid_ipp_timestamp_curr1_eof_addr; + uint32_t csid_ipp_timestamp_perv0_eof_addr; + uint32_t csid_ipp_timestamp_perv1_eof_addr; + + /* configuration */ + uint32_t pix_store_en_shift_val; +}; + +struct cam_ife_csid_rdi_reg_offset { + uint32_t csid_rdi_irq_status_addr; + uint32_t csid_rdi_irq_mask_addr; + uint32_t csid_rdi_irq_clear_addr; + uint32_t csid_rdi_irq_set_addr; + + /*RDI N register address */ + uint32_t csid_rdi_cfg0_addr; + uint32_t csid_rdi_cfg1_addr; + uint32_t csid_rdi_ctrl_addr; + uint32_t csid_rdi_frm_drop_pattern_addr; + uint32_t csid_rdi_frm_drop_period_addr; + uint32_t csid_rdi_irq_subsample_pattern_addr; + uint32_t csid_rdi_irq_subsample_period_addr; + uint32_t csid_rdi_rpp_hcrop_addr; + uint32_t csid_rdi_rpp_vcrop_addr; + uint32_t csid_rdi_rpp_pix_drop_pattern_addr; + uint32_t csid_rdi_rpp_pix_drop_period_addr; + uint32_t csid_rdi_rpp_line_drop_pattern_addr; + uint32_t csid_rdi_rpp_line_drop_period_addr; + uint32_t csid_rdi_yuv_chroma_conversion_addr; + uint32_t csid_rdi_rst_strobes_addr; + uint32_t csid_rdi_status_addr; + uint32_t csid_rdi_misr_val0_addr; + uint32_t csid_rdi_misr_val1_addr; + uint32_t csid_rdi_misr_val2_addr; + uint32_t csid_rdi_misr_val3_addr; + uint32_t csid_rdi_format_measure_cfg0_addr; + uint32_t csid_rdi_format_measure_cfg1_addr; + uint32_t csid_rdi_format_measure0_addr; + uint32_t csid_rdi_format_measure1_addr; + uint32_t csid_rdi_format_measure2_addr; + uint32_t csid_rdi_timestamp_curr0_sof_addr; + uint32_t csid_rdi_timestamp_curr1_sof_addr; + uint32_t csid_rdi_timestamp_prev0_sof_addr; + uint32_t csid_rdi_timestamp_prev1_sof_addr; + uint32_t csid_rdi_timestamp_curr0_eof_addr; + uint32_t csid_rdi_timestamp_curr1_eof_addr; + uint32_t csid_rdi_timestamp_prev0_eof_addr; + uint32_t csid_rdi_timestamp_prev1_eof_addr; + uint32_t csid_rdi_byte_cntr_ping_addr; + uint32_t csid_rdi_byte_cntr_pong_addr; +}; + +struct cam_ife_csid_csi2_rx_reg_offset { + uint32_t csid_csi2_rx_irq_status_addr; + uint32_t csid_csi2_rx_irq_mask_addr; + uint32_t csid_csi2_rx_irq_clear_addr; + uint32_t csid_csi2_rx_irq_set_addr; + uint32_t csid_csi2_rx_cfg0_addr; + uint32_t csid_csi2_rx_cfg1_addr; + uint32_t csid_csi2_rx_capture_ctrl_addr; + uint32_t csid_csi2_rx_rst_strobes_addr; + uint32_t csid_csi2_rx_de_scramble_cfg0_addr; + uint32_t csid_csi2_rx_de_scramble_cfg1_addr; /* */ + uint32_t csid_csi2_rx_cap_unmap_long_pkt_hdr_0_addr; + uint32_t csid_csi2_rx_cap_unmap_long_pkt_hdr_1_addr; + uint32_t csid_csi2_rx_captured_short_pkt_0_addr; + uint32_t csid_csi2_rx_captured_short_pkt_1_addr; + uint32_t csid_csi2_rx_captured_long_pkt_0_addr; + uint32_t csid_csi2_rx_captured_long_pkt_1_addr; + uint32_t csid_csi2_rx_captured_long_pkt_ftr_addr; + uint32_t csid_csi2_rx_captured_cphy_pkt_hdr_addr; + uint32_t csid_csi2_rx_lane0_misr_addr; + uint32_t csid_csi2_rx_lane1_misr_addr; + uint32_t csid_csi2_rx_lane2_misr_addr; + uint32_t csid_csi2_rx_lane3_misr_addr; + uint32_t csid_csi2_rx_total_pkts_rcvd_addr; + uint32_t csid_csi2_rx_stats_ecc_addr; + uint32_t csid_csi2_rx_total_crc_err_addr; + + /*configurations */ + uint32_t csi2_rst_srb_all; + uint32_t csi2_rst_done_shift_val; + uint32_t csi2_irq_mask_all; + uint32_t csi2_misr_enable_shift_val; + uint32_t csi2_vc_mode_shift_val; + uint32_t csi2_capture_long_pkt_en_shift; + uint32_t csi2_capture_short_pkt_en_shift; + uint32_t csi2_capture_cphy_pkt_en_shift; + uint32_t csi2_capture_long_pkt_dt_shift; + uint32_t csi2_capture_long_pkt_vc_shift; + uint32_t csi2_capture_short_pkt_vc_shift; + uint32_t csi2_capture_cphy_pkt_dt_shift; + uint32_t csi2_capture_cphy_pkt_vc_shift; +}; + +struct cam_ife_csid_csi2_tpg_reg_offset { + uint32_t csid_tpg_ctrl_addr; + uint32_t csid_tpg_vc_cfg0_addr; + uint32_t csid_tpg_vc_cfg1_addr; + uint32_t csid_tpg_lfsr_seed_addr; + uint32_t csid_tpg_dt_n_cfg_0_addr; + uint32_t csid_tpg_dt_n_cfg_1_addr; + uint32_t csid_tpg_dt_n_cfg_2_addr; + uint32_t csid_tpg_color_bars_cfg_addr; + uint32_t csid_tpg_color_box_cfg_addr; + uint32_t csid_tpg_common_gen_cfg_addr; + uint32_t csid_tpg_cgen_n_cfg_addr; + uint32_t csid_tpg_cgen_n_x0_addr; + uint32_t csid_tpg_cgen_n_x1_addr; + uint32_t csid_tpg_cgen_n_x2_addr; + uint32_t csid_tpg_cgen_n_xy_addr; + uint32_t csid_tpg_cgen_n_y1_addr; + uint32_t csid_tpg_cgen_n_y2_addr; + + /*configurations */ + uint32_t tpg_dtn_cfg_offset; + uint32_t tpg_cgen_cfg_offset; + uint32_t tpg_cpas_ife_reg_offset; + +}; +struct cam_ife_csid_common_reg_offset { + /* MIPI CSID registers */ + uint32_t csid_hw_version_addr; + uint32_t csid_cfg0_addr; + uint32_t csid_ctrl_addr; + uint32_t csid_reset_addr; + uint32_t csid_rst_strobes_addr; + + uint32_t csid_test_bus_ctrl_addr; + uint32_t csid_top_irq_status_addr; + uint32_t csid_top_irq_mask_addr; + uint32_t csid_top_irq_clear_addr; + uint32_t csid_top_irq_set_addr; + uint32_t csid_irq_cmd_addr; + + /*configurations */ + uint32_t major_version; + uint32_t minor_version; + uint32_t version_incr; + uint32_t no_rdis; + uint32_t no_pix; + uint32_t csid_rst_stb; + uint32_t csid_rst_stb_sw_all; + uint32_t path_rst_stb_all; + uint32_t path_rst_done_shift_val; + uint32_t path_en_shift_val; + uint32_t dt_id_shift_val; + uint32_t vc_shift_val; + uint32_t dt_shift_val; + uint32_t fmt_shift_val; + uint32_t plain_fmt_shit_val; + uint32_t crop_v_en_shift_val; + uint32_t crop_h_en_shift_val; + uint32_t crop_shift; + uint32_t ipp_irq_mask_all; + uint32_t rdi_irq_mask_all; +}; + +/** + * struct cam_ife_csid_reg_offset- CSID instance register info + * + * @cmn_reg: csid common registers info + * @ipp_reg: ipp register offset information + * @rdi_reg: rdi register offser information + * + */ +struct cam_ife_csid_reg_offset { + struct cam_ife_csid_common_reg_offset *cmn_reg; + struct cam_ife_csid_csi2_rx_reg_offset *csi2_reg; + struct cam_ife_csid_ipp_reg_offset *ipp_reg; + struct cam_ife_csid_rdi_reg_offset *rdi_reg[CAM_IFE_CSID_RDI_MAX]; + struct cam_ife_csid_csi2_tpg_reg_offset *tpg_reg; +}; + + +/** + * struct cam_ife_csid_hw_info- CSID HW info + * + * @csid_reg: csid register offsets + * @hw_dts_version: HW DTS version + * @csid_max_clk: maximim csid clock + * + */ +struct cam_ife_csid_hw_info { + struct cam_ife_csid_reg_offset *csid_reg; + uint32_t hw_dts_version; + uint32_t csid_max_clk; + +}; + + + +/** + * struct cam_ife_csid_csi2_rx_cfg- csid csi2 rx configuration data + * @phy_sel: input resource type for sensor only + * @lane_type: lane type: c-phy or d-phy + * @lane_num : active lane number + * @lane_cfg: lane configurations: 4 bits per lane + * + */ +struct cam_ife_csid_csi2_rx_cfg { + uint32_t phy_sel; + uint32_t lane_type; + uint32_t lane_num; + uint32_t lane_cfg; +}; + +/** + * struct cam_ife_csid_tpg_cfg- csid tpg configuration data + * @width: width + * @height: height + * @test_pattern : pattern + * @in_format: decode format + * @usage_type: whether dual isp is required + * + */ +struct cam_ife_csid_tpg_cfg { + uint32_t width; + uint32_t height; + uint32_t test_pattern; + uint32_t in_format; + uint32_t usage_type; +}; + +/** + * struct cam_ife_csid_cid_data- cid configuration private data + * + * @vc: Virtual channel + * @dt: Data type + * @cnt: Cid resource reference count. + * @tpg_set: Tpg used for this cid resource + * + */ +struct cam_ife_csid_cid_data { + uint32_t vc; + uint32_t dt; + uint32_t cnt; + uint32_t tpg_set; +}; + + +/** + * struct cam_ife_csid_path_cfg- csid path configuration details. It is stored + * as private data for IPP/ RDI paths + * @vc : Virtual channel number + * @dt : Data type number + * @cid cid number, it is same as DT_ID number in HW + * @in_format: input decode format + * @out_format: output format + * @crop_enable: crop is enable or disabled, if enabled + * then remaining parameters are valid. + * @start_pixel: start pixel + * @end_pixel: end_pixel + * @width: width + * @start_line: start line + * @end_line: end_line + * @height: heigth + * @sync_mode: Applicable for IPP/RDI path reservation + * Reserving the path for master IPP or slave IPP + * master (set value 1), Slave ( set value 2) + * for RDI, set mode to none + * @master_idx: For Slave reservation, Give master IFE instance Index. + * Slave will synchronize with master Start and stop operations + * @clk_rate Clock rate + * + */ +struct cam_ife_csid_path_cfg { + uint32_t vc; + uint32_t dt; + uint32_t cid; + uint32_t in_format; + uint32_t out_format; + bool crop_enable; + uint32_t start_pixel; + uint32_t end_pixel; + uint32_t width; + uint32_t start_line; + uint32_t end_line; + uint32_t height; + enum cam_isp_hw_sync_mode sync_mode; + uint32_t master_idx; + uint64_t clk_rate; +}; + +/** + * struct cam_ife_csid_hw- csid hw device resources data + * + * @hw_intf: contain the csid hw interface information + * @hw_info: csid hw device information + * @csid_info: csid hw specific information + * @res_type: CSID in resource type + * @csi2_rx_cfg: Csi2 rx decoder configuration for csid + * @tpg_cfg: TPG configuration + * @csi2_rx_reserve_cnt: CSI2 reservations count value + * @csi2_cfg_cnt: csi2 configuration count + * @tpg_start_cnt: tpg start count + * @ipp_res: image pixel path resource + * @rdi_res: raw dump image path resources + * @cid_res: cid resources state + * @csid_top_reset_complete: csid top reset completion + * @csid_csi2_reset_complete: csi2 reset completion + * @csid_ipp_reset_complete: ipp reset completion + * @csid_rdin_reset_complete: rdi n completion + * @csid_debug: csid debug information to enable the SOT, EOT, + * SOF, EOF, measure etc in the csid hw + * @clk_rate Clock rate + * + */ +struct cam_ife_csid_hw { + struct cam_hw_intf *hw_intf; + struct cam_hw_info *hw_info; + struct cam_ife_csid_hw_info *csid_info; + uint32_t res_type; + struct cam_ife_csid_csi2_rx_cfg csi2_rx_cfg; + struct cam_ife_csid_tpg_cfg tpg_cfg; + uint32_t csi2_reserve_cnt; + uint32_t csi2_cfg_cnt; + uint32_t tpg_start_cnt; + struct cam_isp_resource_node ipp_res; + struct cam_isp_resource_node rdi_res[CAM_IFE_CSID_RDI_MAX]; + struct cam_isp_resource_node cid_res[CAM_IFE_CSID_CID_RES_MAX]; + struct completion csid_top_complete; + struct completion csid_csi2_complete; + struct completion csid_ipp_complete; + struct completion csid_rdin_complete[CAM_IFE_CSID_RDI_MAX]; + uint64_t csid_debug; + uint64_t clk_rate; +}; + +int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, + uint32_t csid_idx); + +int cam_ife_csid_hw_deinit(struct cam_ife_csid_hw *ife_csid_hw); + +#endif /* _CAM_IFE_CSID_HW_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.c new file mode 100644 index 000000000000..128c050284d4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.c @@ -0,0 +1,141 @@ +/* Copyright (c) 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 +#include +#include +#include "cam_ife_csid_core.h" +#include "cam_ife_csid_dev.h" +#include "cam_ife_csid_hw_intf.h" +#include "cam_debug_util.h" + +static struct cam_hw_intf *cam_ife_csid_hw_list[CAM_IFE_CSID_HW_RES_MAX] = { + 0, 0, 0, 0}; + +int cam_ife_csid_probe(struct platform_device *pdev) +{ + + struct cam_hw_intf *csid_hw_intf; + struct cam_hw_info *csid_hw_info; + struct cam_ife_csid_hw *csid_dev = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_ife_csid_hw_info *csid_hw_data = NULL; + uint32_t csid_dev_idx; + int rc = 0; + + CAM_DBG(CAM_ISP, "probe called"); + + csid_hw_intf = kzalloc(sizeof(*csid_hw_intf), GFP_KERNEL); + if (!csid_hw_intf) { + rc = -ENOMEM; + goto err; + } + + csid_hw_info = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!csid_hw_info) { + rc = -ENOMEM; + goto free_hw_intf; + } + + csid_dev = kzalloc(sizeof(struct cam_ife_csid_hw), GFP_KERNEL); + if (!csid_dev) { + rc = -ENOMEM; + goto free_hw_info; + } + + /* get ife csid hw index */ + of_property_read_u32(pdev->dev.of_node, "cell-index", &csid_dev_idx); + /* get ife csid hw information */ + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_ISP, "No matching table for the IFE CSID HW!"); + rc = -EINVAL; + goto free_dev; + } + + csid_hw_intf->hw_idx = csid_dev_idx; + csid_hw_intf->hw_type = CAM_ISP_HW_TYPE_IFE_CSID; + csid_hw_intf->hw_priv = csid_hw_info; + + csid_hw_info->core_info = csid_dev; + csid_hw_info->soc_info.pdev = pdev; + csid_hw_info->soc_info.dev = &pdev->dev; + csid_hw_info->soc_info.dev_name = pdev->name; + csid_hw_info->soc_info.index = csid_dev_idx; + + csid_hw_data = (struct cam_ife_csid_hw_info *)match_dev->data; + /* need to setup the pdev before call the ife hw probe init */ + csid_dev->csid_info = csid_hw_data; + + rc = cam_ife_csid_hw_probe_init(csid_hw_intf, csid_dev_idx); + if (rc) + goto free_dev; + + platform_set_drvdata(pdev, csid_dev); + CAM_DBG(CAM_ISP, "CSID:%d probe successful", + csid_hw_intf->hw_idx); + + + if (csid_hw_intf->hw_idx < CAM_IFE_CSID_HW_RES_MAX) + cam_ife_csid_hw_list[csid_hw_intf->hw_idx] = csid_hw_intf; + else + goto free_dev; + + return 0; + +free_dev: + kfree(csid_dev); +free_hw_info: + kfree(csid_hw_info); +free_hw_intf: + kfree(csid_hw_intf); +err: + return rc; +} + +int cam_ife_csid_remove(struct platform_device *pdev) +{ + struct cam_ife_csid_hw *csid_dev = NULL; + struct cam_hw_intf *csid_hw_intf; + struct cam_hw_info *csid_hw_info; + + csid_dev = (struct cam_ife_csid_hw *)platform_get_drvdata(pdev); + csid_hw_intf = csid_dev->hw_intf; + csid_hw_info = csid_dev->hw_info; + + CAM_DBG(CAM_ISP, "CSID:%d remove", + csid_dev->hw_intf->hw_idx); + + cam_ife_csid_hw_deinit(csid_dev); + + /*release the csid device memory */ + kfree(csid_dev); + kfree(csid_hw_info); + kfree(csid_hw_intf); + return 0; +} + +int cam_ife_csid_hw_init(struct cam_hw_intf **ife_csid_hw, + uint32_t hw_idx) +{ + int rc = 0; + + if (cam_ife_csid_hw_list[hw_idx]) { + *ife_csid_hw = cam_ife_csid_hw_list[hw_idx]; + } else { + *ife_csid_hw = NULL; + rc = -1; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.h new file mode 100644 index 000000000000..3b213e2394b5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_dev.h @@ -0,0 +1,23 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_IFE_CSID_DEV_H_ +#define _CAM_IFE_CSID_DEV_H_ + +#include "cam_isp_hw.h" + +irqreturn_t cam_ife_csid_irq(int irq_num, void *data); + +int cam_ife_csid_probe(struct platform_device *pdev); +int cam_ife_csid_remove(struct platform_device *pdev); + +#endif /*_CAM_IFE_CSID_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.c new file mode 100644 index 000000000000..36c6df0ae194 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.c @@ -0,0 +1,58 @@ +/* Copyright (c) 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 +#include "cam_ife_csid_lite170.h" +#include "cam_ife_csid_core.h" +#include "cam_ife_csid_dev.h" + +#define CAM_CSID_LITE_DRV_NAME "csid_lite_170" +#define CAM_CSID_LITE_VERSION_V170 0x10070000 + +static struct cam_ife_csid_hw_info cam_ife_csid_lite170_hw_info = { + .csid_reg = &cam_ife_csid_lite_170_reg_offset, + .hw_dts_version = CAM_CSID_LITE_VERSION_V170, +}; + +static const struct of_device_id cam_ife_csid_lite170_dt_match[] = { + { + .compatible = "qcom,csid-lite170", + .data = &cam_ife_csid_lite170_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_ife_csid_lite170_dt_match); + +static struct platform_driver cam_ife_csid_lite170_driver = { + .probe = cam_ife_csid_probe, + .remove = cam_ife_csid_remove, + .driver = { + .name = CAM_CSID_LITE_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_ife_csid_lite170_dt_match, + }, +}; + +static int __init cam_ife_csid_lite170_init_module(void) +{ + return platform_driver_register(&cam_ife_csid_lite170_driver); +} + +static void __exit cam_ife_csid_lite170_exit_module(void) +{ + platform_driver_unregister(&cam_ife_csid_lite170_driver); +} + +module_init(cam_ife_csid_lite170_init_module); +module_exit(cam_ife_csid_lite170_exit_module); +MODULE_DESCRIPTION("CAM IFE_CSID_LITE170 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h new file mode 100644 index 000000000000..952426da8f99 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h @@ -0,0 +1,319 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_IFE_CSID_LITE170_H_ +#define _CAM_IFE_CSID_LITE170_H_ +#include "cam_ife_csid_core.h" + +static struct cam_ife_csid_rdi_reg_offset + cam_ife_csid_lite_170_rdi_0_reg_offset = { + + .csid_rdi_irq_status_addr = 0x30, + .csid_rdi_irq_mask_addr = 0x34, + .csid_rdi_irq_clear_addr = 0x38, + .csid_rdi_irq_set_addr = 0x3c, + .csid_rdi_cfg0_addr = 0x200, + .csid_rdi_cfg1_addr = 0x204, + .csid_rdi_ctrl_addr = 0x208, + .csid_rdi_frm_drop_pattern_addr = 0x20c, + .csid_rdi_frm_drop_period_addr = 0x210, + .csid_rdi_irq_subsample_pattern_addr = 0x214, + .csid_rdi_irq_subsample_period_addr = 0x218, + .csid_rdi_rpp_hcrop_addr = 0x21c, + .csid_rdi_rpp_vcrop_addr = 0x220, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x224, + .csid_rdi_rpp_pix_drop_period_addr = 0x228, + .csid_rdi_rpp_line_drop_pattern_addr = 0x22c, + .csid_rdi_rpp_line_drop_period_addr = 0x230, + .csid_rdi_rst_strobes_addr = 0x240, + .csid_rdi_status_addr = 0x250, + .csid_rdi_misr_val0_addr = 0x254, + .csid_rdi_misr_val1_addr = 0x258, + .csid_rdi_misr_val2_addr = 0x25c, + .csid_rdi_misr_val3_addr = 0x260, + .csid_rdi_format_measure_cfg0_addr = 0x270, + .csid_rdi_format_measure_cfg1_addr = 0x274, + .csid_rdi_format_measure0_addr = 0x278, + .csid_rdi_format_measure1_addr = 0x27c, + .csid_rdi_format_measure2_addr = 0x280, + .csid_rdi_timestamp_curr0_sof_addr = 0x290, + .csid_rdi_timestamp_curr1_sof_addr = 0x294, + .csid_rdi_timestamp_prev0_sof_addr = 0x298, + .csid_rdi_timestamp_prev1_sof_addr = 0x29c, + .csid_rdi_timestamp_curr0_eof_addr = 0x2a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x2a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x2a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x2ac, + .csid_rdi_byte_cntr_ping_addr = 0x2e0, + .csid_rdi_byte_cntr_pong_addr = 0x2e4, +}; + +static struct cam_ife_csid_rdi_reg_offset + cam_ife_csid_lite_170_rdi_1_reg_offset = { + + .csid_rdi_irq_status_addr = 0x40, + .csid_rdi_irq_mask_addr = 0x44, + .csid_rdi_irq_clear_addr = 0x48, + .csid_rdi_irq_set_addr = 0x4c, + .csid_rdi_cfg0_addr = 0x300, + .csid_rdi_cfg1_addr = 0x304, + .csid_rdi_ctrl_addr = 0x308, + .csid_rdi_frm_drop_pattern_addr = 0x30c, + .csid_rdi_frm_drop_period_addr = 0x310, + .csid_rdi_irq_subsample_pattern_addr = 0x314, + .csid_rdi_irq_subsample_period_addr = 0x318, + .csid_rdi_rpp_hcrop_addr = 0x31c, + .csid_rdi_rpp_vcrop_addr = 0x320, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x324, + .csid_rdi_rpp_pix_drop_period_addr = 0x328, + .csid_rdi_rpp_line_drop_pattern_addr = 0x32c, + .csid_rdi_rpp_line_drop_period_addr = 0x330, + .csid_rdi_rst_strobes_addr = 0x340, + .csid_rdi_status_addr = 0x350, + .csid_rdi_misr_val0_addr = 0x354, + .csid_rdi_misr_val1_addr = 0x358, + .csid_rdi_misr_val2_addr = 0x35c, + .csid_rdi_misr_val3_addr = 0x360, + .csid_rdi_format_measure_cfg0_addr = 0x370, + .csid_rdi_format_measure_cfg1_addr = 0x374, + .csid_rdi_format_measure0_addr = 0x378, + .csid_rdi_format_measure1_addr = 0x37c, + .csid_rdi_format_measure2_addr = 0x380, + .csid_rdi_timestamp_curr0_sof_addr = 0x390, + .csid_rdi_timestamp_curr1_sof_addr = 0x394, + .csid_rdi_timestamp_prev0_sof_addr = 0x398, + .csid_rdi_timestamp_prev1_sof_addr = 0x39c, + .csid_rdi_timestamp_curr0_eof_addr = 0x3a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x3a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x3a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x3ac, + .csid_rdi_byte_cntr_ping_addr = 0x3e0, + .csid_rdi_byte_cntr_pong_addr = 0x3e4, +}; + +static struct cam_ife_csid_rdi_reg_offset + cam_ife_csid_lite_170_rdi_2_reg_offset = { + + .csid_rdi_irq_status_addr = 0x50, + .csid_rdi_irq_mask_addr = 0x54, + .csid_rdi_irq_clear_addr = 0x58, + .csid_rdi_irq_set_addr = 0x5c, + .csid_rdi_cfg0_addr = 0x400, + .csid_rdi_cfg1_addr = 0x404, + .csid_rdi_ctrl_addr = 0x408, + .csid_rdi_frm_drop_pattern_addr = 0x40c, + .csid_rdi_frm_drop_period_addr = 0x410, + .csid_rdi_irq_subsample_pattern_addr = 0x414, + .csid_rdi_irq_subsample_period_addr = 0x418, + .csid_rdi_rpp_hcrop_addr = 0x41c, + .csid_rdi_rpp_vcrop_addr = 0x420, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x424, + .csid_rdi_rpp_pix_drop_period_addr = 0x428, + .csid_rdi_rpp_line_drop_pattern_addr = 0x42c, + .csid_rdi_rpp_line_drop_period_addr = 0x430, + .csid_rdi_yuv_chroma_conversion_addr = 0x434, + .csid_rdi_rst_strobes_addr = 0x440, + .csid_rdi_status_addr = 0x450, + .csid_rdi_misr_val0_addr = 0x454, + .csid_rdi_misr_val1_addr = 0x458, + .csid_rdi_misr_val2_addr = 0x45c, + .csid_rdi_misr_val3_addr = 0x460, + .csid_rdi_format_measure_cfg0_addr = 0x470, + .csid_rdi_format_measure_cfg1_addr = 0x474, + .csid_rdi_format_measure0_addr = 0x478, + .csid_rdi_format_measure1_addr = 0x47c, + .csid_rdi_format_measure2_addr = 0x480, + .csid_rdi_timestamp_curr0_sof_addr = 0x490, + .csid_rdi_timestamp_curr1_sof_addr = 0x494, + .csid_rdi_timestamp_prev0_sof_addr = 0x498, + .csid_rdi_timestamp_prev1_sof_addr = 0x49c, + .csid_rdi_timestamp_curr0_eof_addr = 0x4a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x4a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x4a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x4ac, + .csid_rdi_byte_cntr_ping_addr = 0x4e0, + .csid_rdi_byte_cntr_pong_addr = 0x4e4, +}; + +static struct cam_ife_csid_rdi_reg_offset + cam_ife_csid_lite_170_rdi_3_reg_offset = { + + .csid_rdi_irq_status_addr = 0x60, + .csid_rdi_irq_mask_addr = 0x64, + .csid_rdi_irq_clear_addr = 0x68, + .csid_rdi_irq_set_addr = 0x6c, + .csid_rdi_cfg0_addr = 0x500, + .csid_rdi_cfg1_addr = 0x504, + .csid_rdi_ctrl_addr = 0x508, + .csid_rdi_frm_drop_pattern_addr = 0x50c, + .csid_rdi_frm_drop_period_addr = 0x510, + .csid_rdi_irq_subsample_pattern_addr = 0x514, + .csid_rdi_irq_subsample_period_addr = 0x518, + .csid_rdi_rpp_hcrop_addr = 0x51c, + .csid_rdi_rpp_vcrop_addr = 0x520, + .csid_rdi_rpp_pix_drop_pattern_addr = 0x524, + .csid_rdi_rpp_pix_drop_period_addr = 0x528, + .csid_rdi_rpp_line_drop_pattern_addr = 0x52c, + .csid_rdi_rpp_line_drop_period_addr = 0x530, + .csid_rdi_yuv_chroma_conversion_addr = 0x534, + .csid_rdi_rst_strobes_addr = 0x540, + .csid_rdi_status_addr = 0x550, + .csid_rdi_misr_val0_addr = 0x554, + .csid_rdi_misr_val1_addr = 0x558, + .csid_rdi_misr_val2_addr = 0x55c, + .csid_rdi_misr_val3_addr = 0x560, + .csid_rdi_format_measure_cfg0_addr = 0x570, + .csid_rdi_format_measure_cfg1_addr = 0x574, + .csid_rdi_format_measure0_addr = 0x578, + .csid_rdi_format_measure1_addr = 0x57c, + .csid_rdi_format_measure2_addr = 0x580, + .csid_rdi_timestamp_curr0_sof_addr = 0x590, + .csid_rdi_timestamp_curr1_sof_addr = 0x594, + .csid_rdi_timestamp_prev0_sof_addr = 0x598, + .csid_rdi_timestamp_prev1_sof_addr = 0x59c, + .csid_rdi_timestamp_curr0_eof_addr = 0x5a0, + .csid_rdi_timestamp_curr1_eof_addr = 0x5a4, + .csid_rdi_timestamp_prev0_eof_addr = 0x5a8, + .csid_rdi_timestamp_prev1_eof_addr = 0x5ac, + .csid_rdi_byte_cntr_ping_addr = 0x5e0, + .csid_rdi_byte_cntr_pong_addr = 0x5e4, +}; + +static struct cam_ife_csid_csi2_rx_reg_offset + cam_ife_csid_lite_170_csi2_reg_offset = { + + .csid_csi2_rx_irq_status_addr = 0x20, + .csid_csi2_rx_irq_mask_addr = 0x24, + .csid_csi2_rx_irq_clear_addr = 0x28, + .csid_csi2_rx_irq_set_addr = 0x2c, + + /*CSI2 rx control */ + .csid_csi2_rx_cfg0_addr = 0x100, + .csid_csi2_rx_cfg1_addr = 0x104, + .csid_csi2_rx_capture_ctrl_addr = 0x108, + .csid_csi2_rx_rst_strobes_addr = 0x110, + .csid_csi2_rx_de_scramble_cfg0_addr = 0x114, + .csid_csi2_rx_de_scramble_cfg1_addr = 0x118, + .csid_csi2_rx_cap_unmap_long_pkt_hdr_0_addr = 0x120, + .csid_csi2_rx_cap_unmap_long_pkt_hdr_1_addr = 0x124, + .csid_csi2_rx_captured_short_pkt_0_addr = 0x128, + .csid_csi2_rx_captured_short_pkt_1_addr = 0x12c, + .csid_csi2_rx_captured_long_pkt_0_addr = 0x130, + .csid_csi2_rx_captured_long_pkt_1_addr = 0x134, + .csid_csi2_rx_captured_long_pkt_ftr_addr = 0x138, + .csid_csi2_rx_captured_cphy_pkt_hdr_addr = 0x13c, + .csid_csi2_rx_lane0_misr_addr = 0x150, + .csid_csi2_rx_lane1_misr_addr = 0x154, + .csid_csi2_rx_lane2_misr_addr = 0x158, + .csid_csi2_rx_lane3_misr_addr = 0x15c, + .csid_csi2_rx_total_pkts_rcvd_addr = 0x160, + .csid_csi2_rx_stats_ecc_addr = 0x164, + .csid_csi2_rx_total_crc_err_addr = 0x168, + + .csi2_rst_srb_all = 0x3FFF, + .csi2_rst_done_shift_val = 27, + .csi2_irq_mask_all = 0xFFFFFFF, + .csi2_misr_enable_shift_val = 6, + .csi2_vc_mode_shift_val = 2, + .csi2_capture_long_pkt_en_shift = 0, + .csi2_capture_short_pkt_en_shift = 1, + .csi2_capture_cphy_pkt_en_shift = 2, + .csi2_capture_long_pkt_dt_shift = 4, + .csi2_capture_long_pkt_vc_shift = 10, + .csi2_capture_short_pkt_vc_shift = 15, + .csi2_capture_cphy_pkt_dt_shift = 20, + .csi2_capture_cphy_pkt_vc_shift = 26, +}; + + +static struct cam_ife_csid_csi2_tpg_reg_offset + cam_ife_csid_lite_170_tpg_reg_offset = { + + /*CSID TPG control */ + .csid_tpg_ctrl_addr = 0x600, + .csid_tpg_vc_cfg0_addr = 0x604, + .csid_tpg_vc_cfg1_addr = 0x608, + .csid_tpg_lfsr_seed_addr = 0x60c, + .csid_tpg_dt_n_cfg_0_addr = 0x610, + .csid_tpg_dt_n_cfg_1_addr = 0x614, + .csid_tpg_dt_n_cfg_2_addr = 0x618, + .csid_tpg_color_bars_cfg_addr = 0x640, + .csid_tpg_color_box_cfg_addr = 0x644, + .csid_tpg_common_gen_cfg_addr = 0x648, + .csid_tpg_cgen_n_cfg_addr = 0x650, + .csid_tpg_cgen_n_x0_addr = 0x654, + .csid_tpg_cgen_n_x1_addr = 0x658, + .csid_tpg_cgen_n_x2_addr = 0x65c, + .csid_tpg_cgen_n_xy_addr = 0x660, + .csid_tpg_cgen_n_y1_addr = 0x664, + .csid_tpg_cgen_n_y2_addr = 0x668, + + /*configurations */ + .tpg_dtn_cfg_offset = 0xc, + .tpg_cgen_cfg_offset = 0x20, + .tpg_cpas_ife_reg_offset = 0x28, +}; + + +static struct cam_ife_csid_common_reg_offset + cam_csid_lite_170_cmn_reg_offset = { + + .csid_hw_version_addr = 0x0, + .csid_cfg0_addr = 0x4, + .csid_ctrl_addr = 0x8, + .csid_reset_addr = 0xc, + .csid_rst_strobes_addr = 0x10, + + .csid_test_bus_ctrl_addr = 0x14, + .csid_top_irq_status_addr = 0x70, + .csid_top_irq_mask_addr = 0x74, + .csid_top_irq_clear_addr = 0x78, + .csid_top_irq_set_addr = 0x7c, + .csid_irq_cmd_addr = 0x80, + + /*configurations */ + .major_version = 1, + .minor_version = 7, + .version_incr = 0, + .no_rdis = 4, + .no_pix = 0, + .csid_rst_stb = 0x1e, + .csid_rst_stb_sw_all = 0x1f, + .path_rst_stb_all = 0x7f, + .path_rst_done_shift_val = 1, + .path_en_shift_val = 31, + .dt_id_shift_val = 27, + .vc_shift_val = 22, + .dt_shift_val = 16, + .fmt_shift_val = 12, + .plain_fmt_shit_val = 10, + .crop_v_en_shift_val = 6, + .crop_h_en_shift_val = 5, + .crop_shift = 16, + .ipp_irq_mask_all = 0x7FFF, + .rdi_irq_mask_all = 0x7FFF, +}; + +struct cam_ife_csid_reg_offset cam_ife_csid_lite_170_reg_offset = { + .cmn_reg = &cam_csid_lite_170_cmn_reg_offset, + .csi2_reg = &cam_ife_csid_lite_170_csi2_reg_offset, + .ipp_reg = NULL, + .rdi_reg = { + &cam_ife_csid_lite_170_rdi_0_reg_offset, + &cam_ife_csid_lite_170_rdi_1_reg_offset, + &cam_ife_csid_lite_170_rdi_2_reg_offset, + &cam_ife_csid_lite_170_rdi_3_reg_offset, + }, + .tpg_reg = &cam_ife_csid_lite_170_tpg_reg_offset, +}; + +#endif /*_CAM_IFE_CSID_LITE170_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c new file mode 100644 index 000000000000..e11ff6372ae0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c @@ -0,0 +1,239 @@ +/* Copyright (c) 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 +#include "cam_ife_csid_soc.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static int cam_ife_csid_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + struct device_node *of_node = NULL; + struct csid_device_soc_info *csid_soc_info = NULL; + int rc = 0; + + of_node = soc_info->pdev->dev.of_node; + csid_soc_info = (struct csid_device_soc_info *)soc_info->soc_private; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) + return rc; + + return rc; +} + +static int cam_ife_csid_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t csid_irq_handler, + void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, csid_irq_handler, + irq_data); + if (rc) + return rc; + + return rc; +} + +int cam_ife_csid_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t csid_irq_handler, void *irq_data) +{ + int rc = 0; + struct cam_cpas_register_params cpas_register_param; + struct cam_csid_soc_private *soc_private; + + soc_private = kzalloc(sizeof(struct cam_csid_soc_private), GFP_KERNEL); + if (!soc_private) + return -ENOMEM; + + soc_info->soc_private = soc_private; + + rc = cam_ife_csid_get_dt_properties(soc_info); + if (rc < 0) + return rc; + + /* Need to see if we want post process the clock list */ + + rc = cam_ife_csid_request_platform_resource(soc_info, csid_irq_handler, + irq_data); + if (rc < 0) { + CAM_ERR(CAM_ISP, + "Error Request platform resources failed rc=%d", rc); + goto free_soc_private; + } + + memset(&cpas_register_param, 0, sizeof(cpas_register_param)); + strlcpy(cpas_register_param.identifier, "csid", + CAM_HW_IDENTIFIER_LENGTH); + cpas_register_param.cell_index = soc_info->index; + cpas_register_param.dev = soc_info->dev; + rc = cam_cpas_register_client(&cpas_register_param); + if (rc) { + CAM_ERR(CAM_ISP, "CPAS registration failed rc=%d", rc); + goto release_soc; + } else { + soc_private->cpas_handle = cpas_register_param.client_handle; + } + + return rc; + +release_soc: + cam_soc_util_release_platform_resource(soc_info); +free_soc_private: + kfree(soc_private); + + return rc; +} + +int cam_ife_csid_deinit_soc_resources( + struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_csid_soc_private *soc_private; + + soc_private = soc_info->soc_private; + if (!soc_private) { + CAM_ERR(CAM_ISP, "Error soc_private NULL"); + return -ENODEV; + } + + rc = cam_cpas_unregister_client(soc_private->cpas_handle); + if (rc) + CAM_ERR(CAM_ISP, "CPAS unregistration failed rc=%d", rc); + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc < 0) + return rc; + + return rc; +} + +int cam_ife_csid_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_csid_soc_private *soc_private; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + + soc_private = soc_info->soc_private; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + CAM_DBG(CAM_ISP, "csid vote compressed_bw:%lld uncompressed_bw:%lld", + axi_vote.compressed_bw, axi_vote.uncompressed_bw); + + rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_ISP, "Error CPAS start failed"); + rc = -EFAULT; + goto end; + } + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) { + CAM_ERR(CAM_ISP, "enable platform failed"); + goto stop_cpas; + } + + return rc; + +stop_cpas: + cam_cpas_stop(soc_private->cpas_handle); +end: + return rc; +} + +int cam_ife_csid_disable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_csid_soc_private *soc_private; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error Invalid params"); + return -EINVAL; + } + soc_private = soc_info->soc_private; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) + CAM_ERR(CAM_ISP, "Disable platform failed"); + + rc = cam_cpas_stop(soc_private->cpas_handle); + if (rc) { + CAM_ERR(CAM_ISP, "Error CPAS stop failed rc=%d", rc); + return rc; + } + + return rc; +} + +int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info *soc_info, + uint32_t cpas_ife_base_offset) +{ + int rc = 0; + struct cam_csid_soc_private *soc_private; + uint32_t cpass_ife_force_clk_offset; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error Invalid params"); + return -EINVAL; + } + + soc_private = soc_info->soc_private; + cpass_ife_force_clk_offset = + cpas_ife_base_offset + (0x4 * soc_info->index); + rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP, + cpass_ife_force_clk_offset, 1, 1); + + if (rc) + CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock On failed", + soc_info->index); + else + CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock On", + soc_info->index); + + return rc; +} + +int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info, + uint32_t cpas_ife_base_offset) +{ + int rc = 0; + struct cam_csid_soc_private *soc_private; + uint32_t cpass_ife_force_clk_offset; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error Invalid params"); + return -EINVAL; + } + + soc_private = soc_info->soc_private; + cpass_ife_force_clk_offset = + cpas_ife_base_offset + (0x4 * soc_info->index); + rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP, + cpass_ife_force_clk_offset, 1, 0); + + if (rc) + CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock Off failed", + soc_info->index); + else + CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock off", + soc_info->index); + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h new file mode 100644 index 000000000000..8e963de18aec --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h @@ -0,0 +1,114 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_IFE_CSID_SOC_H_ +#define _CAM_IFE_CSID_SOC_H_ + +#include "cam_isp_hw.h" + +/* + * struct cam_csid_soc_private: + * + * @Brief: Private SOC data specific to CSID HW Driver + * + * @cpas_handle: Handle returned on registering with CPAS driver. + * This handle is used for all further interface + * with CPAS. + */ +struct cam_csid_soc_private { + uint32_t cpas_handle; +}; + +/** + * struct csid_device_soc_info - CSID SOC info object + * + * @csi_vdd_voltage: csi vdd voltage value + * + */ +struct csid_device_soc_info { + int csi_vdd_voltage; +}; + +/** + * cam_ife_csid_init_soc_resources() + * + * @brief: csid initialization function for the soc info + * + * @soc_info: soc info structure pointer + * @csid_irq_handler: irq handler function to be registered + * @irq_data: irq data for the callback function + * + */ +int cam_ife_csid_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t csid_irq_handler, void *irq_data); + + +/** + * cam_ife_csid_deinit_soc_resources() + * + * @brief: csid de initialization function for the soc info + * + * @soc_info: soc info structure pointer + * + */ +int cam_ife_csid_deinit_soc_resources(struct cam_hw_soc_info *soc_info); + +/** + * cam_ife_csid_enable_soc_resources() + * + * @brief: csid soc resource enable function + * + * @soc_info: soc info structure pointer + * + */ +int cam_ife_csid_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +/** + * cam_ife_csid_disable_soc_resources() + * + * @brief: csid soc resource disable function + * + * @soc_info: soc info structure pointer + * + */ +int cam_ife_csid_disable_soc_resources(struct cam_hw_soc_info *soc_info); + +/** + * cam_ife_csid_enable_ife_force_clock() + * + * @brief: if csid testgen used for dual isp case, before + * starting csid test gen, enable ife force clock on + * through cpas + * + * @soc_info: soc info structure pointer + * @cpas_ife_base_offset: cpas ife force clock base reg offset value + * + */ +int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info *soc_info, + uint32_t cpas_ife_base_offset); + +/** + * cam_ife_csid_disable_ife_force_clock_on() + * + * @brief: disable the IFE force clock on after dual ISP + * CSID test gen stop + * + * @soc_info: soc info structure pointer + * @cpas_ife_base_offset: cpas ife force clock base reg offset value + * + */ +int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info, + uint32_t cpas_ife_base_offset); + + + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h new file mode 100644 index 000000000000..ceeacbee247d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h @@ -0,0 +1,169 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CSID_HW_INTF_H_ +#define _CAM_CSID_HW_INTF_H_ + +#include "cam_isp_hw.h" +#include "cam_hw_intf.h" + +/* MAX IFE CSID instance */ +#define CAM_IFE_CSID_HW_NUM_MAX 4 + +/** + * enum cam_ife_pix_path_res_id - Specify the csid patch + */ +enum cam_ife_pix_path_res_id { + CAM_IFE_PIX_PATH_RES_RDI_0, + CAM_IFE_PIX_PATH_RES_RDI_1, + CAM_IFE_PIX_PATH_RES_RDI_2, + CAM_IFE_PIX_PATH_RES_RDI_3, + CAM_IFE_PIX_PATH_RES_IPP, + CAM_IFE_PIX_PATH_RES_MAX, +}; + +/** + * enum cam_ife_cid_res_id - Specify the csid cid + */ +enum cam_ife_cid_res_id { + CAM_IFE_CSID_CID_0, + CAM_IFE_CSID_CID_1, + CAM_IFE_CSID_CID_2, + CAM_IFE_CSID_CID_3, + CAM_IFE_CSID_CID_MAX, +}; + +/** + * struct cam_ife_csid_hw_caps- get the CSID hw capability + * @no_rdis : number of rdis supported by CSID HW device + * @no_pix: number of pixel path supported by CSID HW device + * @major_version : major version + * @minor_version: minor version + * @version_incr: version increment + * + */ +struct cam_ife_csid_hw_caps { + uint32_t no_rdis; + uint32_t no_pix; + uint32_t major_version; + uint32_t minor_version; + uint32_t version_incr; +}; + +/** + * struct cam_csid_hw_reserve_resource- hw reserve + * @res_type : Reource type CID or PATH + * if type is CID, then res_id is not required, + * if type is path then res id need to be filled + * @res_id : Resource id to be reserved + * @in_port : Input port resource info + * @out_port: Output port resource info, used for RDI path only + * @sync_mode: Sync mode + * Sync mode could be master, slave or none + * @master_idx: Master device index to be configured in the slave path + * for master path, this value is not required. + * only slave need to configure the master index value + * @cid: cid (DT_ID) value for path, this is applicable for CSID path + * reserve + * @node_res : Reserved resource structure pointer + * + */ +struct cam_csid_hw_reserve_resource_args { + enum cam_isp_resource_type res_type; + uint32_t res_id; + struct cam_isp_in_port_info *in_port; + struct cam_isp_out_port_info *out_port; + enum cam_isp_hw_sync_mode sync_mode; + uint32_t master_idx; + uint32_t cid; + struct cam_isp_resource_node *node_res; +}; + +/** + * enum cam_ife_csid_halt_cmd - Specify the halt command type + */ +enum cam_ife_csid_halt_cmd { + CAM_CSID_HALT_AT_FRAME_BOUNDARY, + CAM_CSID_RESUME_AT_FRAME_BOUNDARY, + CAM_CSID_HALT_IMMEDIATELY, + CAM_CSID_HALT_MAX, +}; + +/** + * struct cam_csid_hw_stop- stop all resources + * @stop_cmd : Applicable only for PATH resources + * if stop command set to Halt immediately,driver will stop + * path immediately, manager need to reset the path after HI + * if stop command set to halt at frame boundary, driver will set + * halt at frame boundary and wait for frame boundary + * @node_res : reource pointer array( ie cid or CSID) + * @num_res : number of resources to be stopped + * + */ +struct cam_csid_hw_stop_args { + enum cam_ife_csid_halt_cmd stop_cmd; + struct cam_isp_resource_node **node_res; + uint32_t num_res; +}; + +/** + * enum cam_ife_csid_reset_type - Specify the reset type + */ +enum cam_ife_csid_reset_type { + CAM_IFE_CSID_RESET_GLOBAL, + CAM_IFE_CSID_RESET_PATH, + CAM_IFE_CSID_RESET_MAX, +}; + +/** + * struct cam_ife_csid_reset_cfg- csid reset configuration + * @ reset_type : Global reset or path reset + * @res_node : resource need to be reset + * + */ +struct cam_csid_reset_cfg_args { + enum cam_ife_csid_reset_type reset_type; + struct cam_isp_resource_node *node_res; +}; + +/** + * struct cam_csid_get_time_stamp_args- time stamp capture arguments + * @res_node : resource to get the time stamp + * @ time_stamp_val : captured time stamp + * + */ +struct cam_csid_get_time_stamp_args { + struct cam_isp_resource_node *node_res; + uint64_t time_stamp_val; +}; + +/** + * enum cam_ife_csid_cmd_type - Specify the csid command + */ +enum cam_ife_csid_cmd_type { + CAM_IFE_CSID_CMD_GET_TIME_STAMP, + CAM_IFE_CSID_SET_CSID_DEBUG, + CAM_IFE_CSID_CMD_MAX, +}; + +/** + * cam_ife_csid_hw_init() + * + * @brief: Initialize function for the CSID hardware + * + * @ife_csid_hw: CSID hardware instance returned + * @hw_idex: CSID hardware instance id + */ +int cam_ife_csid_hw_init(struct cam_hw_intf **ife_csid_hw, + uint32_t hw_idx); + +#endif /* _CAM_CSID_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h new file mode 100644 index 000000000000..c56c49fac5af --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h @@ -0,0 +1,222 @@ +/* Copyright (c) 2017-2018, 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 _CAM_ISP_HW_H_ +#define _CAM_ISP_HW_H_ + +#include +#include "cam_hw.h" +#include +#include "cam_soc_util.h" +#include "cam_irq_controller.h" +#include + +/* + * struct cam_isp_timestamp: + * + * @mono_time: Monotonic boot time + * @vt_time: AV Timer time + * @ticks: Qtimer ticks + */ +struct cam_isp_timestamp { + struct timeval mono_time; + struct timeval vt_time; + uint64_t ticks; +}; + +/* + * cam_isp_hw_get_timestamp() + * + * @Brief: Get timestamp values + * + * @time_stamp: Structure that holds different time values + * + * @Return: Void + */ +void cam_isp_hw_get_timestamp(struct cam_isp_timestamp *time_stamp); + +enum cam_isp_hw_type { + CAM_ISP_HW_TYPE_CSID = 0, + CAM_ISP_HW_TYPE_ISPIF = 1, + CAM_ISP_HW_TYPE_VFE = 2, + CAM_ISP_HW_TYPE_IFE_CSID = 3, + CAM_ISP_HW_TYPE_MAX = 4, +}; + +enum cam_isp_hw_split_id { + CAM_ISP_HW_SPLIT_LEFT = 0, + CAM_ISP_HW_SPLIT_RIGHT, + CAM_ISP_HW_SPLIT_MAX, +}; + +enum cam_isp_hw_sync_mode { + CAM_ISP_HW_SYNC_NONE, + CAM_ISP_HW_SYNC_MASTER, + CAM_ISP_HW_SYNC_SLAVE, + CAM_ISP_HW_SYNC_MAX, +}; + +enum cam_isp_resource_state { + CAM_ISP_RESOURCE_STATE_UNAVAILABLE = 0, + CAM_ISP_RESOURCE_STATE_AVAILABLE = 1, + CAM_ISP_RESOURCE_STATE_RESERVED = 2, + CAM_ISP_RESOURCE_STATE_INIT_HW = 3, + CAM_ISP_RESOURCE_STATE_STREAMING = 4, +}; + +enum cam_isp_resource_type { + CAM_ISP_RESOURCE_UNINT, + CAM_ISP_RESOURCE_SRC, + CAM_ISP_RESOURCE_CID, + CAM_ISP_RESOURCE_PIX_PATH, + CAM_ISP_RESOURCE_VFE_IN, + CAM_ISP_RESOURCE_VFE_OUT, + CAM_ISP_RESOURCE_MAX, +}; + +enum cam_isp_hw_cmd_type { + CAM_ISP_HW_CMD_GET_CHANGE_BASE, + CAM_ISP_HW_CMD_GET_BUF_UPDATE, + CAM_ISP_HW_CMD_GET_REG_UPDATE, + CAM_ISP_HW_CMD_GET_HFR_UPDATE, + CAM_ISP_HW_CMD_GET_SECURE_MODE, + CAM_ISP_HW_CMD_STRIPE_UPDATE, + CAM_ISP_HW_CMD_CLOCK_UPDATE, + CAM_ISP_HW_CMD_BW_UPDATE, + CAM_ISP_HW_CMD_BW_CONTROL, + CAM_ISP_HW_CMD_STOP_BUS_ERR_IRQ, + CAM_ISP_HW_CMD_GET_REG_DUMP, + CAM_ISP_HW_CMD_MAX, +}; + +/* + * struct cam_isp_resource_node: + * + * @Brief: Structure representing HW resource object + * + * @res_type: Resource Type + * @res_id: Unique resource ID within res_type objects + * for a particular HW + * @res_state: State of the resource + * @hw_intf: HW Interface of HW to which this resource + * belongs + * @res_priv: Private data of the resource + * @list: list_head node for this resource + * @cdm_ops: CDM operation functions + * @tasklet_info: Tasklet structure that will be used to + * schedule IRQ events related to this resource + * @irq_handle: handle returned on subscribing for IRQ event + * @rdi_only_ctx: resouce belong to rdi only context or not + * @init: function pointer to init the HW resource + * @deinit: function pointer to deinit the HW resource + * @start: function pointer to start the HW resource + * @stop: function pointer to stop the HW resource + * @process_cmd: function pointer for processing commands + * specific to the resource + * @top_half_handler: Top Half handler function + * @bottom_half_handler: Bottom Half handler function + */ +struct cam_isp_resource_node { + enum cam_isp_resource_type res_type; + uint32_t res_id; + enum cam_isp_resource_state res_state; + struct cam_hw_intf *hw_intf; + void *res_priv; + struct list_head list; + void *cdm_ops; + void *tasklet_info; + int irq_handle; + int rdi_only_ctx; + + int (*init)(struct cam_isp_resource_node *rsrc_node, + void *init_args, uint32_t arg_size); + int (*deinit)(struct cam_isp_resource_node *rsrc_node, + void *deinit_args, uint32_t arg_size); + int (*start)(struct cam_isp_resource_node *rsrc_node); + int (*stop)(struct cam_isp_resource_node *rsrc_node); + int (*process_cmd)(struct cam_isp_resource_node *rsrc_node, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size); + CAM_IRQ_HANDLER_TOP_HALF top_half_handler; + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler; +}; + +/* + * struct cam_isp_hw_cmd_buf_update: + * + * @Brief: Contain the new created command buffer information + * + * @cmd_buf_addr: Command buffer to store the change base command + * @size: Size of the buffer in bytes + * @used_bytes: Consumed bytes in the command buffer + * + */ +struct cam_isp_hw_cmd_buf_update { + uint32_t *cmd_buf_addr; + uint32_t size; + uint32_t used_bytes; +}; + +/* + * struct cam_isp_hw_get_wm_update: + * + * @Brief: Get cmd buffer for WM updates. + * + * @ image_buf: image buffer address array + * @ num_buf: Number of buffers in the image_buf array + * @ io_cfg: IO buffer config information sent from UMD + * + */ +struct cam_isp_hw_get_wm_update { + uint64_t *image_buf; + uint32_t num_buf; + struct cam_buf_io_cfg *io_cfg; +}; + +/* + * struct cam_isp_hw_get_cmd_update: + * + * @Brief: Get cmd buffer update for different CMD types + * + * @res: Resource node + * @cmd_type: Command type for which to get update + * @cmd: Command buffer information + * + */ +struct cam_isp_hw_get_cmd_update { + struct cam_isp_resource_node *res; + enum cam_isp_hw_cmd_type cmd_type; + struct cam_isp_hw_cmd_buf_update cmd; + union { + void *data; + struct cam_isp_hw_get_wm_update *wm_update; + struct cam_isp_port_hfr_config *hfr_update; + struct cam_isp_clock_config *clock_update; + struct cam_isp_bw_config *bw_update; + }; +}; + +/* + * struct cam_isp_hw_dual_isp_update_args: + * + * @Brief: update the dual isp striping configuration. + * + * @ split_id: spilt id to inform left or rifht + * @ res: resource node + * @ dual_cfg: dual isp configuration + * + */ +struct cam_isp_hw_dual_isp_update_args { + enum cam_isp_hw_split_id split_id; + struct cam_isp_resource_node *res; + struct cam_isp_dual_config *dual_cfg; +}; +#endif /* _CAM_ISP_HW_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h new file mode 100644 index 000000000000..05c3c6120f4e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h @@ -0,0 +1,299 @@ +/* Copyright (c) 2017-2018, 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 _CAM_VFE_HW_INTF_H_ +#define _CAM_VFE_HW_INTF_H_ + +#include "cam_isp_hw.h" + +#define CAM_VFE_HW_NUM_MAX 4 + +#define VFE_CORE_BASE_IDX 0 +/* + * VBIF and BUS do not exist on same HW. + * Hence both can be 1 below. + */ +#define VFE_VBIF_BASE_IDX 1 +#define VFE_BUS_BASE_IDX 1 + +enum cam_isp_hw_vfe_in_mux { + CAM_ISP_HW_VFE_IN_CAMIF = 0, + CAM_ISP_HW_VFE_IN_TESTGEN = 1, + CAM_ISP_HW_VFE_IN_BUS_RD = 2, + CAM_ISP_HW_VFE_IN_RDI0 = 3, + CAM_ISP_HW_VFE_IN_RDI1 = 4, + CAM_ISP_HW_VFE_IN_RDI2 = 5, + CAM_ISP_HW_VFE_IN_RDI3 = 6, + CAM_ISP_HW_VFE_IN_MAX, +}; + +enum cam_isp_hw_vfe_core { + CAM_ISP_HW_VFE_CORE_0, + CAM_ISP_HW_VFE_CORE_1, + CAM_ISP_HW_VFE_CORE_2, + CAM_ISP_HW_VFE_CORE_3, + CAM_ISP_HW_VFE_CORE_MAX, +}; + +enum cam_vfe_hw_irq_status { + CAM_VFE_IRQ_STATUS_ERR_COMP = -3, + CAM_VFE_IRQ_STATUS_COMP_OWRT = -2, + CAM_VFE_IRQ_STATUS_ERR = -1, + CAM_VFE_IRQ_STATUS_SUCCESS = 0, + CAM_VFE_IRQ_STATUS_OVERFLOW = 1, + CAM_VFE_IRQ_STATUS_P2I_ERROR = 2, + CAM_VFE_IRQ_STATUS_VIOLATION = 3, + CAM_VFE_IRQ_STATUS_MAX, +}; + +enum cam_vfe_hw_irq_regs { + CAM_IFE_IRQ_CAMIF_REG_STATUS0 = 0, + CAM_IFE_IRQ_CAMIF_REG_STATUS1 = 1, + CAM_IFE_IRQ_VIOLATION_STATUS = 2, + CAM_IFE_IRQ_REGISTERS_MAX, +}; + +enum cam_vfe_bus_irq_regs { + CAM_IFE_IRQ_BUS_REG_STATUS0 = 0, + CAM_IFE_IRQ_BUS_REG_STATUS1 = 1, + CAM_IFE_IRQ_BUS_REG_STATUS2 = 2, + CAM_IFE_IRQ_BUS_REG_COMP_ERR = 3, + CAM_IFE_IRQ_BUS_REG_COMP_OWRT = 4, + CAM_IFE_IRQ_BUS_DUAL_COMP_ERR = 5, + CAM_IFE_IRQ_BUS_DUAL_COMP_OWRT = 6, + CAM_IFE_BUS_IRQ_REGISTERS_MAX, +}; + +enum cam_vfe_reset_type { + CAM_VFE_HW_RESET_HW_AND_REG, + CAM_VFE_HW_RESET_HW, + CAM_VFE_HW_RESET_MAX, +}; + +/* + * struct cam_vfe_hw_get_hw_cap: + * + * @max_width: Max width supported by HW + * @max_height: Max height supported by HW + * @max_pixel_num: Max Pixel channels available + * @max_rdi_num: Max Raw channels available + */ +struct cam_vfe_hw_get_hw_cap { + uint32_t max_width; + uint32_t max_height; + uint32_t max_pixel_num; + uint32_t max_rdi_num; +}; + +/* + * struct cam_vfe_hw_vfe_out_acquire_args: + * + * @rsrc_node: Pointer to Resource Node object, filled if acquire + * is successful + * @out_port_info: Output Port details to acquire + * @unique_id: Unique Identity of Context to associate with this + * resource. Used for composite grouping of multiple + * resources in the same context + * @is_dual: Dual VFE or not + * @split_id: In case of Dual VFE, this is Left or Right. + * (Default is Left if Single VFE) + * @is_master: In case of Dual VFE, this is Master or Slave. + * (Default is Master in case of Single VFE) + * @dual_slave_core: If Master and Slave exists, HW Index of Slave + * @cdm_ops: CDM operations + * @ctx: Context data + */ +struct cam_vfe_hw_vfe_out_acquire_args { + struct cam_isp_resource_node *rsrc_node; + struct cam_isp_out_port_info *out_port_info; + uint32_t unique_id; + uint32_t is_dual; + enum cam_isp_hw_split_id split_id; + uint32_t is_master; + uint32_t dual_slave_core; + struct cam_cdm_utils_ops *cdm_ops; + void *ctx; +}; + +/* + * struct cam_vfe_hw_vfe_in_acquire_args: + * + * @rsrc_node: Pointer to Resource Node object, filled if acquire + * is successful + * @res_id: Resource ID of resource to acquire if specific, + * else CAM_ISP_HW_VFE_IN_MAX + * @cdm_ops: CDM operations + * @sync_mode: In case of Dual VFE, this is Master or Slave. + * (Default is Master in case of Single VFE) + * @in_port: Input port details to acquire + */ +struct cam_vfe_hw_vfe_in_acquire_args { + struct cam_isp_resource_node *rsrc_node; + uint32_t res_id; + void *cdm_ops; + enum cam_isp_hw_sync_mode sync_mode; + struct cam_isp_in_port_info *in_port; +}; + +/* + * struct cam_vfe_acquire_args: + * + * @rsrc_type: Type of Resource (OUT/IN) to acquire + * @tasklet: Tasklet to associate with this resource. This is + * used to schedule bottom of IRQ events associated + * with this resource. + * @vfe_out: Acquire args for VFE_OUT + * @vfe_in: Acquire args for VFE_IN + */ +struct cam_vfe_acquire_args { + enum cam_isp_resource_type rsrc_type; + void *tasklet; + union { + struct cam_vfe_hw_vfe_out_acquire_args vfe_out; + struct cam_vfe_hw_vfe_in_acquire_args vfe_in; + }; +}; + +/* + * struct cam_vfe_clock_update_args: + * + * @node_res: Resource to get the time stamp + * @clk_rate: Clock rate requested + */ +struct cam_vfe_clock_update_args { + struct cam_isp_resource_node *node_res; + uint64_t clk_rate; +}; + +/* + * struct cam_vfe_bw_update_args: + * + * @node_res: Resource to get the time stamp + * @camnoc_bw_bytes: Bandwidth vote request for CAMNOC + * @external_bw_bytes: Bandwidth vote request from CAMNOC + * out to the rest of the path-to-DDR + */ +struct cam_vfe_bw_update_args { + struct cam_isp_resource_node *node_res; + uint64_t camnoc_bw_bytes; + uint64_t external_bw_bytes; +}; + +enum cam_vfe_bw_control_action { + CAM_VFE_BW_CONTROL_EXCLUDE = 0, + CAM_VFE_BW_CONTROL_INCLUDE = 1 +}; + +/* + * struct cam_vfe_bw_control_args: + * + * @node_res: Resource to get the time stamp + * @action: Bandwidth control action + */ +struct cam_vfe_bw_control_args { + struct cam_isp_resource_node *node_res; + enum cam_vfe_bw_control_action action; +}; + +/* + * struct cam_vfe_top_irq_evt_payload: + * + * @Brief: This structure is used to save payload for IRQ + * related to VFE_TOP resources + * + * @list: list_head node for the payload + * @core_index: Index of VFE HW that generated this IRQ event + * @core_info: Private data of handler in bottom half context + * @evt_id: IRQ event + * @irq_reg_val: IRQ and Error register values, read when IRQ was + * handled + * @error_type: Identify different errors + * @ts: Timestamp + */ +struct cam_vfe_top_irq_evt_payload { + struct list_head list; + uint32_t core_index; + void *core_info; + uint32_t evt_id; + uint32_t irq_reg_val[CAM_IFE_IRQ_REGISTERS_MAX]; + uint32_t error_type; + struct cam_isp_timestamp ts; +}; + +/* + * struct cam_vfe_bus_irq_evt_payload: + * + * @Brief: This structure is used to save payload for IRQ + * related to VFE_BUS resources + * + * @list: list_head node for the payload + * @core_index: Index of VFE HW that generated this IRQ event + * @evt_id: IRQ event + * @irq_reg_val: IRQ and Error register values, read when IRQ was + * handled + * @error_type: Identify different errors + * @ts: Timestamp + * @ctx: Context data received during acquire + */ +struct cam_vfe_bus_irq_evt_payload { + struct list_head list; + uint32_t core_index; + uint32_t evt_id; + uint32_t irq_reg_val[CAM_IFE_BUS_IRQ_REGISTERS_MAX]; + uint32_t error_type; + struct cam_isp_timestamp ts; + void *ctx; +}; + +/* + * struct cam_vfe_irq_handler_priv: + * + * @Brief: This structure is used as private data to + * register with IRQ controller. It has information + * needed by top half and bottom half. + * + * @core_index: Index of VFE HW that generated this IRQ event + * @core_info: Private data of handler in bottom half context + * @mem_base: Mapped base address of the register space + * @reset_complete: Completion structure to be signaled if Reset IRQ + * is Set + */ +struct cam_vfe_irq_handler_priv { + uint32_t core_index; + void *core_info; + void __iomem *mem_base; + struct completion *reset_complete; +}; + +/* + * cam_vfe_hw_init() + * + * @Brief: Initialize VFE HW device + * + * @vfe_hw: vfe_hw interface to fill in and return on + * successful initialization + * @hw_idx: Index of VFE HW + */ +int cam_vfe_hw_init(struct cam_hw_intf **vfe_hw, uint32_t hw_idx); + +/* + * cam_vfe_put_evt_payload() + * + * @Brief: Put the evt payload back to free list + * + * @core_info: VFE HW core_info + * @evt_payload: Event payload data + */ +int cam_vfe_put_evt_payload(void *core_info, + struct cam_vfe_top_irq_evt_payload **evt_payload); + +#endif /* _CAM_VFE_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile new file mode 100644 index 000000000000..0bdee6a7877c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile @@ -0,0 +1,15 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include + + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_soc.o cam_vfe_dev.o cam_vfe_core.o +obj-$(CONFIG_SPECTRA_CAMERA) += vfe_bus/ vfe_top/ vfe170/ \ No newline at end of file diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c new file mode 100644 index 000000000000..66b9b3bdbac9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c @@ -0,0 +1,825 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include "cam_tasklet_util.h" +#include "cam_isp_hw_mgr_intf.h" +#include "cam_vfe_soc.h" +#include "cam_vfe_core.h" +#include "cam_vfe_bus.h" +#include "cam_vfe_top.h" +#include "cam_ife_hw_mgr.h" +#include "cam_debug_util.h" + +static const char drv_name[] = "vfe"; +static uint32_t irq_reg_offset[CAM_IFE_IRQ_REGISTERS_MAX] = { + 0x0000006C, + 0x00000070, + 0x0000007C, +}; + +static uint32_t camif_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = { + 0x00000017, + 0x00000000, +}; + +static uint32_t camif_irq_err_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = { + 0x0003FC00, + 0x0FFF7EBC, +}; + +static uint32_t rdi_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = { + 0x780001e0, + 0x00000000, +}; + +static uint32_t top_reset_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = { + 0x80000000, + 0x00000000, +}; + +static int cam_vfe_get_evt_payload(struct cam_vfe_hw_core_info *core_info, + struct cam_vfe_top_irq_evt_payload **evt_payload) +{ + spin_lock(&core_info->spin_lock); + if (list_empty(&core_info->free_payload_list)) { + *evt_payload = NULL; + spin_unlock(&core_info->spin_lock); + CAM_ERR_RATE_LIMIT(CAM_ISP, "No free payload, core info 0x%x\n", + core_info->cpas_handle); + return -ENODEV; + } + + *evt_payload = list_first_entry(&core_info->free_payload_list, + struct cam_vfe_top_irq_evt_payload, list); + list_del_init(&(*evt_payload)->list); + spin_unlock(&core_info->spin_lock); + + return 0; +} + +int cam_vfe_put_evt_payload(void *core_info, + struct cam_vfe_top_irq_evt_payload **evt_payload) +{ + struct cam_vfe_hw_core_info *vfe_core_info = core_info; + unsigned long flags; + + if (!core_info) { + CAM_ERR(CAM_ISP, "Invalid param core_info NULL"); + return -EINVAL; + } + if (*evt_payload == NULL) { + CAM_ERR(CAM_ISP, "No payload to put"); + return -EINVAL; + } + + spin_lock_irqsave(&vfe_core_info->spin_lock, flags); + (*evt_payload)->error_type = 0; + list_add_tail(&(*evt_payload)->list, &vfe_core_info->free_payload_list); + *evt_payload = NULL; + spin_unlock_irqrestore(&vfe_core_info->spin_lock, flags); + + + return 0; +} + +int cam_vfe_get_hw_caps(void *hw_priv, void *get_hw_cap_args, uint32_t arg_size) +{ + struct cam_hw_info *vfe_dev = hw_priv; + struct cam_vfe_hw_core_info *core_info = NULL; + int rc = 0; + + CAM_DBG(CAM_ISP, "Enter"); + if (!hw_priv) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + core_info = (struct cam_vfe_hw_core_info *)vfe_dev->core_info; + + if (core_info->vfe_top->hw_ops.get_hw_caps) + core_info->vfe_top->hw_ops.get_hw_caps( + core_info->vfe_top->top_priv, + get_hw_cap_args, arg_size); + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +int cam_vfe_reset_irq_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int32_t rc = -EINVAL; + struct cam_vfe_irq_handler_priv *handler_priv; + + handler_priv = th_payload->handler_priv; + + CAM_DBG(CAM_ISP, "Enter"); + CAM_DBG(CAM_ISP, "IRQ status_0 = 0x%x", th_payload->evt_status_arr[0]); + + if (th_payload->evt_status_arr[0] & (1<<31)) { + /* + * Clear All IRQs to avoid spurious IRQs immediately + * after Reset Done. + */ + cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x64); + cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x68); + cam_io_w(0x1, handler_priv->mem_base + 0x58); + CAM_DBG(CAM_ISP, "Calling Complete for RESET CMD"); + complete(handler_priv->reset_complete); + + + rc = 0; + } + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +static int cam_vfe_irq_err_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int32_t rc; + int i; + struct cam_vfe_irq_handler_priv *handler_priv; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_vfe_hw_core_info *core_info; + bool error_flag = false; + + CAM_DBG(CAM_ISP, "IRQ status_0 = %x, IRQ status_1 = %x", + th_payload->evt_status_arr[0], th_payload->evt_status_arr[1]); + + handler_priv = th_payload->handler_priv; + core_info = handler_priv->core_info; + /* + * need to handle overflow condition here, otherwise irq storm + * will block everything + */ + if (th_payload->evt_status_arr[1] || + (th_payload->evt_status_arr[0] & camif_irq_err_reg_mask[0])) { + CAM_ERR(CAM_ISP, + "Encountered Error: vfe:%d: Irq_status0=0x%x Status1=0x%x", + handler_priv->core_index, th_payload->evt_status_arr[0], + th_payload->evt_status_arr[1]); + CAM_ERR(CAM_ISP, + "Stopping further IRQ processing from this HW index=%d", + handler_priv->core_index); + cam_irq_controller_disable_irq(core_info->vfe_irq_controller, + core_info->irq_err_handle); + cam_irq_controller_clear_and_mask(evt_id, + core_info->vfe_irq_controller); + error_flag = true; + } + + rc = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "No tasklet_cmd is free in queue"); + CAM_ERR_RATE_LIMIT(CAM_ISP, "IRQ status0=0x%x status1=0x%x", + th_payload->evt_status_arr[0], + th_payload->evt_status_arr[1]); + return rc; + } + + cam_isp_hw_get_timestamp(&evt_payload->ts); + + evt_payload->core_index = handler_priv->core_index; + evt_payload->core_info = handler_priv->core_info; + evt_payload->evt_id = evt_id; + + for (i = 0; i < th_payload->num_registers; i++) + evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i]; + + for (; i < CAM_IFE_IRQ_REGISTERS_MAX; i++) { + evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base + + irq_reg_offset[i]); + } + + if (error_flag) + CAM_INFO(CAM_ISP, "Violation status = %x", + evt_payload->irq_reg_val[2]); + + th_payload->evt_payload_priv = evt_payload; + + return rc; +} + +int cam_vfe_init_hw(void *hw_priv, void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_isp_resource_node *isp_res = NULL; + int rc = 0; + uint32_t reset_core_args = + CAM_VFE_HW_RESET_HW_AND_REG; + + CAM_DBG(CAM_ISP, "Enter"); + if (!hw_priv) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + mutex_lock(&vfe_hw->hw_mutex); + vfe_hw->open_count++; + if (vfe_hw->open_count > 1) { + mutex_unlock(&vfe_hw->hw_mutex); + CAM_DBG(CAM_ISP, "VFE has already been initialized cnt %d", + vfe_hw->open_count); + return 0; + } + mutex_unlock(&vfe_hw->hw_mutex); + + soc_info = &vfe_hw->soc_info; + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + + /* Turn ON Regulators, Clocks and other SOC resources */ + rc = cam_vfe_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_ISP, "Enable SOC failed"); + rc = -EFAULT; + goto decrement_open_cnt; + } + + isp_res = (struct cam_isp_resource_node *)init_hw_args; + if (isp_res && isp_res->init) { + rc = isp_res->init(isp_res, NULL, 0); + if (rc) { + CAM_ERR(CAM_ISP, "init Failed rc=%d", rc); + goto disable_soc; + } + } + + CAM_DBG(CAM_ISP, "Enable soc done"); + + /* Do HW Reset */ + rc = cam_vfe_reset(hw_priv, &reset_core_args, sizeof(uint32_t)); + if (rc) { + CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc); + goto deinint_vfe_res; + } + + rc = core_info->vfe_bus->hw_ops.init(core_info->vfe_bus->bus_priv, + NULL, 0); + if (rc) { + CAM_ERR(CAM_ISP, "Bus HW init Failed rc=%d", rc); + goto deinint_vfe_res; + } + + vfe_hw->hw_state = CAM_HW_STATE_POWER_UP; + return rc; + +deinint_vfe_res: + if (isp_res && isp_res->deinit) + isp_res->deinit(isp_res, NULL, 0); +disable_soc: + cam_vfe_disable_soc_resources(soc_info); +decrement_open_cnt: + mutex_lock(&vfe_hw->hw_mutex); + vfe_hw->open_count--; + mutex_unlock(&vfe_hw->hw_mutex); + return rc; +} + +int cam_vfe_deinit_hw(void *hw_priv, void *deinit_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_isp_resource_node *isp_res = NULL; + int rc = 0; + uint32_t reset_core_args = + CAM_VFE_HW_RESET_HW_AND_REG; + + CAM_DBG(CAM_ISP, "Enter"); + if (!hw_priv) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + mutex_lock(&vfe_hw->hw_mutex); + if (!vfe_hw->open_count) { + mutex_unlock(&vfe_hw->hw_mutex); + CAM_ERR(CAM_ISP, "Error! Unbalanced deinit"); + return -EFAULT; + } + vfe_hw->open_count--; + if (vfe_hw->open_count) { + mutex_unlock(&vfe_hw->hw_mutex); + CAM_DBG(CAM_ISP, "open_cnt non-zero =%d", vfe_hw->open_count); + return 0; + } + mutex_unlock(&vfe_hw->hw_mutex); + + soc_info = &vfe_hw->soc_info; + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + + rc = core_info->vfe_bus->hw_ops.deinit(core_info->vfe_bus->bus_priv, + NULL, 0); + if (rc) + CAM_ERR(CAM_ISP, "Bus HW deinit Failed rc=%d", rc); + + isp_res = (struct cam_isp_resource_node *)deinit_hw_args; + if (isp_res && isp_res->deinit) { + rc = isp_res->deinit(isp_res, NULL, 0); + if (rc) + CAM_ERR(CAM_ISP, "deinit failed"); + } + + rc = cam_vfe_reset(hw_priv, &reset_core_args, sizeof(uint32_t)); + + /* Turn OFF Regulators, Clocks and other SOC resources */ + CAM_DBG(CAM_ISP, "Disable SOC resource"); + rc = cam_vfe_disable_soc_resources(soc_info); + if (rc) + CAM_ERR(CAM_ISP, "Disable SOC failed"); + + vfe_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +int cam_vfe_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size) +{ + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + int rc; + + CAM_DBG(CAM_ISP, "Enter"); + + if (!hw_priv) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + soc_info = &vfe_hw->soc_info; + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + + core_info->irq_payload.core_index = soc_info->index; + core_info->irq_payload.mem_base = + vfe_hw->soc_info.reg_map[VFE_CORE_BASE_IDX].mem_base; + core_info->irq_payload.core_info = core_info; + core_info->irq_payload.reset_complete = &vfe_hw->hw_complete; + + core_info->irq_handle = cam_irq_controller_subscribe_irq( + core_info->vfe_irq_controller, CAM_IRQ_PRIORITY_0, + top_reset_irq_reg_mask, &core_info->irq_payload, + cam_vfe_reset_irq_top_half, NULL, NULL, NULL); + if (core_info->irq_handle < 0) { + CAM_ERR(CAM_ISP, "subscribe irq controller failed"); + return -EFAULT; + } + + reinit_completion(&vfe_hw->hw_complete); + + CAM_INFO(CAM_ISP, "calling RESET on vfe %d", soc_info->index); + core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv, + reset_core_args, arg_size); + CAM_DBG(CAM_ISP, "waiting for vfe reset complete"); + /* Wait for Completion or Timeout of 500ms */ + rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500); + if (!rc) + CAM_ERR(CAM_ISP, "Error! Reset Timeout"); + + CAM_DBG(CAM_ISP, "reset complete done (%d)", rc); + + rc = cam_irq_controller_unsubscribe_irq( + core_info->vfe_irq_controller, core_info->irq_handle); + if (rc) + CAM_ERR(CAM_ISP, "Error! Unsubscribe failed"); + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +void cam_isp_hw_get_timestamp(struct cam_isp_timestamp *time_stamp) +{ + struct timespec ts; + + get_monotonic_boottime(&ts); + time_stamp->mono_time.tv_sec = ts.tv_sec; + time_stamp->mono_time.tv_usec = ts.tv_nsec/1000; +} + +static int cam_vfe_irq_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int32_t rc; + int i; + struct cam_vfe_irq_handler_priv *handler_priv; + struct cam_vfe_top_irq_evt_payload *evt_payload; + struct cam_vfe_hw_core_info *core_info; + + handler_priv = th_payload->handler_priv; + + CAM_DBG(CAM_ISP, "IRQ status_0 = %x", th_payload->evt_status_arr[0]); + CAM_DBG(CAM_ISP, "IRQ status_1 = %x", th_payload->evt_status_arr[1]); + + rc = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "No tasklet_cmd is free in queue"); + CAM_ERR_RATE_LIMIT(CAM_ISP, "IRQ status0=0x%x status1=0x%x", + th_payload->evt_status_arr[0], + th_payload->evt_status_arr[1]); + return rc; + } + + core_info = handler_priv->core_info; + cam_isp_hw_get_timestamp(&evt_payload->ts); + + evt_payload->core_index = handler_priv->core_index; + evt_payload->core_info = handler_priv->core_info; + evt_payload->evt_id = evt_id; + + for (i = 0; i < th_payload->num_registers; i++) + evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i]; + + for (; i < CAM_IFE_IRQ_REGISTERS_MAX; i++) { + evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base + + irq_reg_offset[i]); + } + CAM_DBG(CAM_ISP, "Violation status = %x", evt_payload->irq_reg_val[2]); + + th_payload->evt_payload_priv = evt_payload; + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +int cam_vfe_reserve(void *hw_priv, void *reserve_args, uint32_t arg_size) +{ + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_vfe_acquire_args *acquire; + int rc = -ENODEV; + + + if (!hw_priv || !reserve_args || (arg_size != + sizeof(struct cam_vfe_acquire_args))) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + acquire = (struct cam_vfe_acquire_args *)reserve_args; + + mutex_lock(&vfe_hw->hw_mutex); + if (acquire->rsrc_type == CAM_ISP_RESOURCE_VFE_IN) + rc = core_info->vfe_top->hw_ops.reserve( + core_info->vfe_top->top_priv, + acquire, + sizeof(*acquire)); + else if (acquire->rsrc_type == CAM_ISP_RESOURCE_VFE_OUT) + rc = core_info->vfe_bus->hw_ops.reserve( + core_info->vfe_bus->bus_priv, acquire, + sizeof(*acquire)); + else + CAM_ERR(CAM_ISP, "Invalid res type:%d", acquire->rsrc_type); + + mutex_unlock(&vfe_hw->hw_mutex); + + return rc; +} + + +int cam_vfe_release(void *hw_priv, void *release_args, uint32_t arg_size) +{ + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_isp_resource_node *isp_res; + int rc = -ENODEV; + + if (!hw_priv || !release_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + isp_res = (struct cam_isp_resource_node *) release_args; + + mutex_lock(&vfe_hw->hw_mutex); + if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) + rc = core_info->vfe_top->hw_ops.release( + core_info->vfe_top->top_priv, isp_res, + sizeof(*isp_res)); + else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) + rc = core_info->vfe_bus->hw_ops.release( + core_info->vfe_bus->bus_priv, isp_res, + sizeof(*isp_res)); + else + CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type); + + mutex_unlock(&vfe_hw->hw_mutex); + + return rc; +} + + +int cam_vfe_start(void *hw_priv, void *start_args, uint32_t arg_size) +{ + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_isp_resource_node *isp_res; + int rc = 0; + struct cam_irq_bh_api irq_bh_api; + + if (!hw_priv || !start_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + isp_res = (struct cam_isp_resource_node *)start_args; + core_info->tasklet_info = isp_res->tasklet_info; + irq_bh_api.bottom_half_enqueue_func = cam_tasklet_enqueue_cmd; + irq_bh_api.get_bh_payload_func = cam_tasklet_get_cmd; + irq_bh_api.put_bh_payload_func = cam_tasklet_put_cmd; + + mutex_lock(&vfe_hw->hw_mutex); + if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) { + if (isp_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) { + isp_res->irq_handle = + cam_irq_controller_subscribe_irq( + core_info->vfe_irq_controller, + CAM_IRQ_PRIORITY_1, + camif_irq_reg_mask, + &core_info->irq_payload, + cam_vfe_irq_top_half, + cam_ife_mgr_do_tasklet, + isp_res->tasklet_info, + &irq_bh_api); + if (isp_res->irq_handle < 1) + rc = -ENOMEM; + } else if (isp_res->rdi_only_ctx) { + isp_res->irq_handle = + cam_irq_controller_subscribe_irq( + core_info->vfe_irq_controller, + CAM_IRQ_PRIORITY_1, + rdi_irq_reg_mask, + &core_info->irq_payload, + cam_vfe_irq_top_half, + cam_ife_mgr_do_tasklet, + isp_res->tasklet_info, + &irq_bh_api); + if (isp_res->irq_handle < 1) + rc = -ENOMEM; + } + + if (rc == 0) { + rc = core_info->vfe_top->hw_ops.start( + core_info->vfe_top->top_priv, isp_res, + sizeof(struct cam_isp_resource_node)); + if (rc) + CAM_ERR(CAM_ISP, "Start failed. type:%d", + isp_res->res_type); + } else { + CAM_ERR(CAM_ISP, + "Error! subscribe irq controller failed"); + } + } else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) { + rc = core_info->vfe_bus->hw_ops.start(isp_res, NULL, 0); + } else { + CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type); + rc = -EFAULT; + } + + if (!core_info->irq_err_handle) { + core_info->irq_err_handle = + cam_irq_controller_subscribe_irq( + core_info->vfe_irq_controller, + CAM_IRQ_PRIORITY_0, + camif_irq_err_reg_mask, + &core_info->irq_payload, + cam_vfe_irq_err_top_half, + cam_ife_mgr_do_tasklet, + core_info->tasklet_info, + &irq_bh_api); + if (core_info->irq_err_handle < 1) { + CAM_ERR(CAM_ISP, "Error handle subscribe failure"); + rc = -ENOMEM; + core_info->irq_err_handle = 0; + } + } + + mutex_unlock(&vfe_hw->hw_mutex); + + return rc; +} + +int cam_vfe_stop(void *hw_priv, void *stop_args, uint32_t arg_size) +{ + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_isp_resource_node *isp_res; + int rc = -EINVAL; + + if (!hw_priv || !stop_args || + (arg_size != sizeof(struct cam_isp_resource_node))) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + isp_res = (struct cam_isp_resource_node *)stop_args; + + mutex_lock(&vfe_hw->hw_mutex); + if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) { + cam_irq_controller_unsubscribe_irq( + core_info->vfe_irq_controller, isp_res->irq_handle); + isp_res->irq_handle = 0; + + rc = core_info->vfe_top->hw_ops.stop( + core_info->vfe_top->top_priv, isp_res, + sizeof(struct cam_isp_resource_node)); + } else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) { + rc = core_info->vfe_bus->hw_ops.stop(isp_res, NULL, 0); + } else { + CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type); + } + + if (core_info->irq_err_handle) { + cam_irq_controller_unsubscribe_irq( + core_info->vfe_irq_controller, + core_info->irq_err_handle); + core_info->irq_err_handle = 0; + } + + mutex_unlock(&vfe_hw->hw_mutex); + + return rc; +} + +int cam_vfe_read(void *hw_priv, void *read_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_write(void *hw_priv, void *write_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *vfe_hw = hw_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_vfe_hw_info *hw_info = NULL; + int rc = 0; + + if (!hw_priv) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + soc_info = &vfe_hw->soc_info; + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + hw_info = core_info->vfe_hw_info; + + switch (cmd_type) { + case CAM_ISP_HW_CMD_GET_CHANGE_BASE: + case CAM_ISP_HW_CMD_GET_REG_UPDATE: + case CAM_ISP_HW_CMD_CLOCK_UPDATE: + case CAM_ISP_HW_CMD_BW_UPDATE: + case CAM_ISP_HW_CMD_BW_CONTROL: + rc = core_info->vfe_top->hw_ops.process_cmd( + core_info->vfe_top->top_priv, cmd_type, cmd_args, + arg_size); + break; + case CAM_ISP_HW_CMD_GET_BUF_UPDATE: + case CAM_ISP_HW_CMD_GET_HFR_UPDATE: + case CAM_ISP_HW_CMD_STRIPE_UPDATE: + case CAM_ISP_HW_CMD_STOP_BUS_ERR_IRQ: + rc = core_info->vfe_bus->hw_ops.process_cmd( + core_info->vfe_bus->bus_priv, cmd_type, cmd_args, + arg_size); + break; + + default: + CAM_ERR(CAM_ISP, "Invalid cmd type:%d", cmd_type); + rc = -EINVAL; + break; + } + return rc; +} + +irqreturn_t cam_vfe_irq(int irq_num, void *data) +{ + struct cam_hw_info *vfe_hw; + struct cam_vfe_hw_core_info *core_info; + + if (!data) + return IRQ_NONE; + + vfe_hw = (struct cam_hw_info *)data; + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + + return cam_irq_controller_handle_irq(irq_num, + core_info->vfe_irq_controller); +} + +int cam_vfe_core_init(struct cam_vfe_hw_core_info *core_info, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + struct cam_vfe_hw_info *vfe_hw_info) +{ + int rc = -EINVAL; + int i; + + CAM_DBG(CAM_ISP, "Enter"); + + rc = cam_irq_controller_init(drv_name, + CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX), + vfe_hw_info->irq_reg_info, &core_info->vfe_irq_controller); + if (rc) { + CAM_ERR(CAM_ISP, "Error! cam_irq_controller_init failed"); + return rc; + } + + rc = cam_vfe_top_init(vfe_hw_info->top_version, + soc_info, hw_intf, vfe_hw_info->top_hw_info, + &core_info->vfe_top); + if (rc) { + CAM_ERR(CAM_ISP, "Error! cam_vfe_top_init failed"); + goto deinit_controller; + } + + rc = cam_vfe_bus_init(vfe_hw_info->bus_version, soc_info, hw_intf, + vfe_hw_info->bus_hw_info, core_info->vfe_irq_controller, + &core_info->vfe_bus); + if (rc) { + CAM_ERR(CAM_ISP, "Error! cam_vfe_bus_init failed"); + goto deinit_top; + } + + INIT_LIST_HEAD(&core_info->free_payload_list); + for (i = 0; i < CAM_VFE_EVT_MAX; i++) { + INIT_LIST_HEAD(&core_info->evt_payload[i].list); + list_add_tail(&core_info->evt_payload[i].list, + &core_info->free_payload_list); + } + + spin_lock_init(&core_info->spin_lock); + + return rc; + +deinit_top: + cam_vfe_top_deinit(vfe_hw_info->top_version, + &core_info->vfe_top); + +deinit_controller: + cam_irq_controller_deinit(&core_info->vfe_irq_controller); + + return rc; +} + +int cam_vfe_core_deinit(struct cam_vfe_hw_core_info *core_info, + struct cam_vfe_hw_info *vfe_hw_info) +{ + int rc = -EINVAL; + int i; + unsigned long flags; + + spin_lock_irqsave(&core_info->spin_lock, flags); + + INIT_LIST_HEAD(&core_info->free_payload_list); + for (i = 0; i < CAM_VFE_EVT_MAX; i++) + INIT_LIST_HEAD(&core_info->evt_payload[i].list); + + rc = cam_vfe_bus_deinit(vfe_hw_info->bus_version, + &core_info->vfe_bus); + if (rc) + CAM_ERR(CAM_ISP, "Error cam_vfe_bus_deinit failed rc=%d", rc); + + rc = cam_vfe_top_deinit(vfe_hw_info->top_version, + &core_info->vfe_top); + if (rc) + CAM_ERR(CAM_ISP, "Error cam_vfe_top_deinit failed rc=%d", rc); + + rc = cam_irq_controller_deinit(&core_info->vfe_irq_controller); + if (rc) + CAM_ERR(CAM_ISP, + "Error cam_irq_controller_deinit failed rc=%d", rc); + + spin_unlock_irqrestore(&core_info->spin_lock, flags); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h new file mode 100644 index 000000000000..0674a6addc7f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h @@ -0,0 +1,96 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_CORE_H_ +#define _CAM_VFE_CORE_H_ + +#include +#include "cam_hw_intf.h" +#include "cam_vfe_top.h" +#include "cam_vfe_bus.h" +#include "cam_vfe_hw_intf.h" + +struct cam_vfe_hw_info { + struct cam_irq_controller_reg_info *irq_reg_info; + + uint32_t bus_version; + void *bus_hw_info; + + uint32_t top_version; + void *top_hw_info; + uint32_t camif_version; + void *camif_reg; + + uint32_t testgen_version; + void *testgen_reg; + + uint32_t num_qos_settings; + struct cam_isp_reg_val_pair *qos_settings; + + uint32_t num_ds_settings; + struct cam_isp_reg_val_pair *ds_settings; + + uint32_t num_vbif_settings; + struct cam_isp_reg_val_pair *vbif_settings; +}; + +#define CAM_VFE_EVT_MAX 256 + +struct cam_vfe_hw_core_info { + struct cam_vfe_hw_info *vfe_hw_info; + void *vfe_irq_controller; + struct cam_vfe_top *vfe_top; + struct cam_vfe_bus *vfe_bus; + void *tasklet_info; + struct cam_vfe_top_irq_evt_payload evt_payload[CAM_VFE_EVT_MAX]; + struct list_head free_payload_list; + struct cam_vfe_irq_handler_priv irq_payload; + uint32_t cpas_handle; + int irq_handle; + int irq_err_handle; + spinlock_t spin_lock; +}; + +int cam_vfe_get_hw_caps(void *device_priv, + void *get_hw_cap_args, uint32_t arg_size); +int cam_vfe_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_vfe_deinit_hw(void *hw_priv, + void *deinit_hw_args, uint32_t arg_size); +int cam_vfe_reset(void *device_priv, + void *reset_core_args, uint32_t arg_size); +int cam_vfe_reserve(void *device_priv, + void *reserve_args, uint32_t arg_size); +int cam_vfe_release(void *device_priv, + void *reserve_args, uint32_t arg_size); +int cam_vfe_start(void *device_priv, + void *start_args, uint32_t arg_size); +int cam_vfe_stop(void *device_priv, + void *stop_args, uint32_t arg_size); +int cam_vfe_read(void *device_priv, + void *read_args, uint32_t arg_size); +int cam_vfe_write(void *device_priv, + void *write_args, uint32_t arg_size); +int cam_vfe_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); + +irqreturn_t cam_vfe_irq(int irq_num, void *data); + +int cam_vfe_core_init(struct cam_vfe_hw_core_info *core_info, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + struct cam_vfe_hw_info *vfe_hw_info); + +int cam_vfe_core_deinit(struct cam_vfe_hw_core_info *core_info, + struct cam_vfe_hw_info *vfe_hw_info); + +#endif /* _CAM_VFE_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c new file mode 100644 index 000000000000..74627b8d32ef --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c @@ -0,0 +1,197 @@ +/* Copyright (c) 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 +#include +#include +#include "cam_vfe_dev.h" +#include "cam_vfe_core.h" +#include "cam_vfe_soc.h" +#include "cam_debug_util.h" + +static struct cam_hw_intf *cam_vfe_hw_list[CAM_VFE_HW_NUM_MAX] = {0, 0, 0, 0}; + +int cam_vfe_probe(struct platform_device *pdev) +{ + struct cam_hw_info *vfe_hw = NULL; + struct cam_hw_intf *vfe_hw_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + struct cam_vfe_hw_info *hw_info = NULL; + int rc = 0; + + vfe_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!vfe_hw_intf) { + rc = -ENOMEM; + goto end; + } + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &vfe_hw_intf->hw_idx); + + vfe_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!vfe_hw) { + rc = -ENOMEM; + goto free_vfe_hw_intf; + } + vfe_hw->soc_info.pdev = pdev; + vfe_hw->soc_info.dev = &pdev->dev; + vfe_hw->soc_info.dev_name = pdev->name; + vfe_hw_intf->hw_priv = vfe_hw; + vfe_hw_intf->hw_ops.get_hw_caps = cam_vfe_get_hw_caps; + vfe_hw_intf->hw_ops.init = cam_vfe_init_hw; + vfe_hw_intf->hw_ops.deinit = cam_vfe_deinit_hw; + vfe_hw_intf->hw_ops.reset = cam_vfe_reset; + vfe_hw_intf->hw_ops.reserve = cam_vfe_reserve; + vfe_hw_intf->hw_ops.release = cam_vfe_release; + vfe_hw_intf->hw_ops.start = cam_vfe_start; + vfe_hw_intf->hw_ops.stop = cam_vfe_stop; + vfe_hw_intf->hw_ops.read = cam_vfe_read; + vfe_hw_intf->hw_ops.write = cam_vfe_write; + vfe_hw_intf->hw_ops.process_cmd = cam_vfe_process_cmd; + vfe_hw_intf->hw_type = CAM_ISP_HW_TYPE_VFE; + + CAM_DBG(CAM_ISP, "type %d index %d", + vfe_hw_intf->hw_type, vfe_hw_intf->hw_idx); + + platform_set_drvdata(pdev, vfe_hw_intf); + + vfe_hw->core_info = kzalloc(sizeof(struct cam_vfe_hw_core_info), + GFP_KERNEL); + if (!vfe_hw->core_info) { + CAM_DBG(CAM_ISP, "Failed to alloc for core"); + rc = -ENOMEM; + goto free_vfe_hw; + } + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_ISP, "Of_match Failed"); + rc = -EINVAL; + goto free_core_info; + } + hw_info = (struct cam_vfe_hw_info *)match_dev->data; + core_info->vfe_hw_info = hw_info; + + rc = cam_vfe_init_soc_resources(&vfe_hw->soc_info, cam_vfe_irq, + vfe_hw); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Failed to init soc rc=%d", rc); + goto free_core_info; + } + + rc = cam_vfe_core_init(core_info, &vfe_hw->soc_info, + vfe_hw_intf, hw_info); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Failed to init core rc=%d", rc); + goto deinit_soc; + } + + vfe_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&vfe_hw->hw_mutex); + spin_lock_init(&vfe_hw->hw_lock); + init_completion(&vfe_hw->hw_complete); + + if (vfe_hw_intf->hw_idx < CAM_VFE_HW_NUM_MAX) + cam_vfe_hw_list[vfe_hw_intf->hw_idx] = vfe_hw_intf; + + cam_vfe_init_hw(vfe_hw, NULL, 0); + cam_vfe_deinit_hw(vfe_hw, NULL, 0); + + CAM_DBG(CAM_ISP, "VFE%d probe successful", vfe_hw_intf->hw_idx); + + return rc; + +deinit_soc: + if (cam_vfe_deinit_soc_resources(&vfe_hw->soc_info)) + CAM_ERR(CAM_ISP, "Failed to deinit soc"); +free_core_info: + kfree(vfe_hw->core_info); +free_vfe_hw: + kfree(vfe_hw); +free_vfe_hw_intf: + kfree(vfe_hw_intf); +end: + return rc; +} + +int cam_vfe_remove(struct platform_device *pdev) +{ + struct cam_hw_info *vfe_hw = NULL; + struct cam_hw_intf *vfe_hw_intf = NULL; + struct cam_vfe_hw_core_info *core_info = NULL; + int rc = 0; + + vfe_hw_intf = platform_get_drvdata(pdev); + if (!vfe_hw_intf) { + CAM_ERR(CAM_ISP, "Error! No data in pdev"); + return -EINVAL; + } + + CAM_DBG(CAM_ISP, "type %d index %d", + vfe_hw_intf->hw_type, vfe_hw_intf->hw_idx); + + if (vfe_hw_intf->hw_idx < CAM_VFE_HW_NUM_MAX) + cam_vfe_hw_list[vfe_hw_intf->hw_idx] = NULL; + + vfe_hw = vfe_hw_intf->hw_priv; + if (!vfe_hw) { + CAM_ERR(CAM_ISP, "Error! HW data is NULL"); + rc = -ENODEV; + goto free_vfe_hw_intf; + } + + core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info; + if (!core_info) { + CAM_ERR(CAM_ISP, "Error! core data NULL"); + rc = -EINVAL; + goto deinit_soc; + } + + rc = cam_vfe_core_deinit(core_info, core_info->vfe_hw_info); + if (rc < 0) + CAM_ERR(CAM_ISP, "Failed to deinit core rc=%d", rc); + + kfree(vfe_hw->core_info); + +deinit_soc: + rc = cam_vfe_deinit_soc_resources(&vfe_hw->soc_info); + if (rc < 0) + CAM_ERR(CAM_ISP, "Failed to deinit soc rc=%d", rc); + + mutex_destroy(&vfe_hw->hw_mutex); + kfree(vfe_hw); + + CAM_DBG(CAM_ISP, "VFE%d remove successful", vfe_hw_intf->hw_idx); + +free_vfe_hw_intf: + kfree(vfe_hw_intf); + + return rc; +} + +int cam_vfe_hw_init(struct cam_hw_intf **vfe_hw, uint32_t hw_idx) +{ + int rc = 0; + + if (cam_vfe_hw_list[hw_idx]) { + *vfe_hw = cam_vfe_hw_list[hw_idx]; + rc = 0; + } else { + *vfe_hw = NULL; + rc = -ENODEV; + } + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h new file mode 100644 index 000000000000..9e7352834ad6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h @@ -0,0 +1,42 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_DEV_H_ +#define _CAM_VFE_DEV_H_ + +#include + +/* + * cam_vfe_probe() + * + * @brief: Driver probe function called on Boot + * + * @pdev: Platform Device pointer + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_probe(struct platform_device *pdev); + +/* + * cam_vfe_remove() + * + * @brief: Driver remove function + * + * @pdev: Platform Device pointer + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_remove(struct platform_device *pdev); + +#endif /* _CAM_VFE_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c new file mode 100644 index 000000000000..0f9366454568 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c @@ -0,0 +1,293 @@ +/* Copyright (c) 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 +#include "cam_cpas_api.h" +#include "cam_vfe_soc.h" +#include "cam_debug_util.h" + +static bool cam_vfe_cpas_cb(uint32_t client_handle, void *userdata, + struct cam_cpas_irq_data *irq_data) +{ + bool error_handled = false; + + if (!irq_data) + return error_handled; + + switch (irq_data->irq_type) { + case CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR: + case CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR: + CAM_ERR_RATE_LIMIT(CAM_ISP, + "IFE UBWC Encode error type=%d status=%x", + irq_data->irq_type, + irq_data->u.enc_err.encerr_status.value); + error_handled = true; + break; + default: + break; + } + + return error_handled; +} + +static int cam_vfe_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) { + CAM_ERR(CAM_ISP, "Error! get DT properties failed rc=%d", rc); + return rc; + } + + return rc; +} + +static int cam_vfe_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t vfe_irq_handler, void *irq_data) +{ + int rc = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, vfe_irq_handler, + irq_data); + if (rc) + CAM_ERR(CAM_ISP, + "Error! Request platform resource failed rc=%d", rc); + + return rc; +} + +static int cam_vfe_release_platform_resource(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_ERR(CAM_ISP, + "Error! Release platform resource failed rc=%d", rc); + + return rc; +} + +int cam_vfe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t vfe_irq_handler, void *irq_data) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + struct cam_cpas_register_params cpas_register_param; + + soc_private = kzalloc(sizeof(struct cam_vfe_soc_private), + GFP_KERNEL); + if (!soc_private) { + CAM_DBG(CAM_ISP, "Error! soc_private Alloc Failed"); + return -ENOMEM; + } + soc_info->soc_private = soc_private; + + rc = cam_vfe_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Error! Get DT properties failed rc=%d", rc); + goto free_soc_private; + } + + rc = cam_soc_util_get_option_clk_by_name(soc_info, + CAM_VFE_DSP_CLK_NAME, &soc_private->dsp_clk, + &soc_private->dsp_clk_index, &soc_private->dsp_clk_rate); + if (rc) + CAM_WARN(CAM_ISP, "option clk get failed"); + + rc = cam_vfe_request_platform_resource(soc_info, vfe_irq_handler, + irq_data); + if (rc < 0) { + CAM_ERR(CAM_ISP, + "Error! Request platform resources failed rc=%d", rc); + goto free_soc_private; + } + + memset(&cpas_register_param, 0, sizeof(cpas_register_param)); + strlcpy(cpas_register_param.identifier, "ife", + CAM_HW_IDENTIFIER_LENGTH); + cpas_register_param.cell_index = soc_info->index; + cpas_register_param.dev = soc_info->dev; + cpas_register_param.cam_cpas_client_cb = cam_vfe_cpas_cb; + cpas_register_param.userdata = soc_info; + rc = cam_cpas_register_client(&cpas_register_param); + if (rc) { + CAM_ERR(CAM_ISP, "CPAS registration failed rc=%d", rc); + goto release_soc; + } else { + soc_private->cpas_handle = cpas_register_param.client_handle; + } + + return rc; + +release_soc: + cam_soc_util_release_platform_resource(soc_info); +free_soc_private: + kfree(soc_private); + + return rc; +} + +int cam_vfe_deinit_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error! soc_info NULL"); + return -ENODEV; + } + + soc_private = soc_info->soc_private; + if (!soc_private) { + CAM_ERR(CAM_ISP, "Error! soc_private NULL"); + return -ENODEV; + } + + rc = cam_cpas_unregister_client(soc_private->cpas_handle); + if (rc) + CAM_ERR(CAM_ISP, "CPAS unregistration failed rc=%d", rc); + + rc = cam_vfe_release_platform_resource(soc_info); + if (rc < 0) + CAM_ERR(CAM_ISP, + "Error! Release platform resources failed rc=%d", rc); + + rc = cam_soc_util_clk_put(&soc_private->dsp_clk); + if (rc < 0) + CAM_ERR(CAM_ISP, + "Error Put dsp clk failed rc=%d", rc); + + kfree(soc_private); + + return rc; +} + +int cam_vfe_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error! Invalid params"); + rc = -EINVAL; + goto end; + } + soc_private = soc_info->soc_private; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + + axi_vote.compressed_bw = 10640000000L; + axi_vote.uncompressed_bw = 10640000000L; + + rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_ISP, "Error! CPAS start failed rc=%d", rc); + rc = -EFAULT; + goto end; + } + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_TURBO_VOTE, true); + if (rc) { + CAM_ERR(CAM_ISP, "Error! enable platform failed rc=%d", rc); + goto stop_cpas; + } + + return rc; + +stop_cpas: + cam_cpas_stop(soc_private->cpas_handle); +end: + return rc; +} + +int cam_vfe_soc_enable_clk(struct cam_hw_soc_info *soc_info, + const char *clk_name) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error Invalid params"); + rc = -EINVAL; + return rc; + } + soc_private = soc_info->soc_private; + + if (strcmp(clk_name, CAM_VFE_DSP_CLK_NAME) == 0) { + rc = cam_soc_util_clk_enable(soc_private->dsp_clk, + CAM_VFE_DSP_CLK_NAME, soc_private->dsp_clk_rate); + if (rc) + CAM_ERR(CAM_ISP, + "Error enable dsp clk failed rc=%d", rc); + } + + return rc; +} + +int cam_vfe_soc_disable_clk(struct cam_hw_soc_info *soc_info, + const char *clk_name) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error Invalid params"); + rc = -EINVAL; + return rc; + } + soc_private = soc_info->soc_private; + + if (strcmp(clk_name, CAM_VFE_DSP_CLK_NAME) == 0) { + rc = cam_soc_util_clk_disable(soc_private->dsp_clk, + CAM_VFE_DSP_CLK_NAME); + if (rc) + CAM_ERR(CAM_ISP, + "Error enable dsp clk failed rc=%d", rc); + } + + return rc; +} + + +int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc = 0; + struct cam_vfe_soc_private *soc_private; + + if (!soc_info) { + CAM_ERR(CAM_ISP, "Error! Invalid params"); + rc = -EINVAL; + return rc; + } + soc_private = soc_info->soc_private; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) { + CAM_ERR(CAM_ISP, "Disable platform failed rc=%d", rc); + return rc; + } + + rc = cam_cpas_stop(soc_private->cpas_handle); + if (rc) { + CAM_ERR(CAM_ISP, "Error! CPAS stop failed rc=%d", rc); + return rc; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h new file mode 100644 index 000000000000..7a4dbeadb182 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h @@ -0,0 +1,116 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_SOC_H_ +#define _CAM_VFE_SOC_H_ + +#include "cam_soc_util.h" +#include "cam_isp_hw.h" + +#define CAM_VFE_DSP_CLK_NAME "ife_dsp_clk" + +/* + * struct cam_vfe_soc_private: + * + * @Brief: Private SOC data specific to VFE HW Driver + * + * @cpas_handle: Handle returned on registering with CPAS driver. + * This handle is used for all further interface + * with CPAS. + */ +struct cam_vfe_soc_private { + uint32_t cpas_handle; + struct clk *dsp_clk; + int32_t dsp_clk_index; + int32_t dsp_clk_rate; +}; + +/* + * cam_vfe_init_soc_resources() + * + * @Brief: Initialize SOC resources including private data + * + * @soc_info: Device soc information + * @handler: Irq handler function pointer + * @irq_data: Irq handler function Callback data + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t vfe_irq_handler, void *irq_data); + +/* + * cam_vfe_deinit_soc_resources() + * + * @Brief: Deinitialize SOC resources including private data + * + * @soc_info: Device soc information + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_deinit_soc_resources(struct cam_hw_soc_info *soc_info); + +/* + * cam_vfe_enable_soc_resources() + * + * @brief: Enable regulator, irq resources, start CPAS + * + * @soc_info: Device soc information + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +/* + * cam_vfe_disable_soc_resources() + * + * @brief: Disable regulator, irq resources, stop CPAS + * + * @soc_info: Device soc information + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info); + +/* + * cam_vfe_soc_enable_clk() + * + * @brief: Enable clock with given name + * + * @soc_info: Device soc information + * @clk_name: Name of clock to enable + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_soc_enable_clk(struct cam_hw_soc_info *soc_info, + const char *clk_name); + +/* + * cam_vfe_soc_disable_dsp_clk() + * + * @brief: Disable clock with given name + * + * @soc_info: Device soc information + * @clk_name: Name of clock to enable + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_soc_disable_clk(struct cam_hw_soc_info *soc_info, + const char *clk_name); + +#endif /* _CAM_VFE_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile new file mode 100644 index 000000000000..e6509d621403 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o cam_vfe_lite170.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c new file mode 100644 index 000000000000..0af32ad5d546 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c @@ -0,0 +1,51 @@ +/* Copyright (c) 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 +#include "cam_vfe170.h" +#include "cam_vfe_hw_intf.h" +#include "cam_vfe_core.h" +#include "cam_vfe_dev.h" + +static const struct of_device_id cam_vfe170_dt_match[] = { + { + .compatible = "qcom,vfe170", + .data = &cam_vfe170_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_vfe170_dt_match); + +static struct platform_driver cam_vfe170_driver = { + .probe = cam_vfe_probe, + .remove = cam_vfe_remove, + .driver = { + .name = "cam_vfe170", + .owner = THIS_MODULE, + .of_match_table = cam_vfe170_dt_match, + }, +}; + +static int __init cam_vfe170_init_module(void) +{ + return platform_driver_register(&cam_vfe170_driver); +} + +static void __exit cam_vfe170_exit_module(void) +{ + platform_driver_unregister(&cam_vfe170_driver); +} + +module_init(cam_vfe170_init_module); +module_exit(cam_vfe170_exit_module); +MODULE_DESCRIPTION("CAM VFE170 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h new file mode 100644 index 000000000000..a4ba2e12bca3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h @@ -0,0 +1,837 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE170_H_ +#define _CAM_VFE170_H_ + +#include "cam_vfe_camif_ver2.h" +#include "cam_vfe_bus_ver2.h" +#include "cam_irq_controller.h" +#include "cam_vfe_top_ver2.h" +#include "cam_vfe_core.h" + +static struct cam_irq_register_set vfe170_top_irq_reg_set[2] = { + { + .mask_reg_offset = 0x0000005C, + .clear_reg_offset = 0x00000064, + .status_reg_offset = 0x0000006C, + }, + { + .mask_reg_offset = 0x00000060, + .clear_reg_offset = 0x00000068, + .status_reg_offset = 0x00000070, + }, +}; + +static struct cam_irq_controller_reg_info vfe170_top_irq_reg_info = { + .num_registers = 2, + .irq_reg_set = vfe170_top_irq_reg_set, + .global_clear_offset = 0x00000058, + .global_clear_bitmask = 0x00000001, +}; + +static struct cam_vfe_camif_ver2_reg vfe170_camif_reg = { + .camif_cmd = 0x00000478, + .camif_config = 0x0000047C, + .line_skip_pattern = 0x00000488, + .pixel_skip_pattern = 0x0000048C, + .skip_period = 0x00000490, + .irq_subsample_pattern = 0x0000049C, + .epoch_irq = 0x000004A0, + .raw_crop_width_cfg = 0x00000CE4, + .raw_crop_height_cfg = 0x00000CE8, + .reg_update_cmd = 0x000004AC, +}; + +static struct cam_vfe_camif_reg_data vfe_170_camif_reg_data = { + .raw_crop_first_pixel_shift = 16, + .raw_crop_first_pixel_mask = 0xFFFF, + .raw_crop_last_pixel_shift = 0x0, + .raw_crop_last_pixel_mask = 0x3FFF, + .raw_crop_first_line_shift = 16, + .raw_crop_first_line_mask = 0xFFFF, + .raw_crop_last_line_shift = 0, + .raw_crop_last_line_mask = 0x3FFF, + .input_mux_sel_shift = 5, + .input_mux_sel_mask = 0x3, + .extern_reg_update_shift = 4, + .extern_reg_update_mask = 1, + .pixel_pattern_shift = 0, + .pixel_pattern_mask = 0x7, + .dsp_mode_shift = 23, + .dsp_mode_mask = 0x1, + .dsp_en_shift = 3, + .dsp_en_mask = 0x1, + .reg_update_cmd_data = 0x1, + .epoch_line_cfg = 0x00140014, + .sof_irq_mask = 0x00000001, + .epoch0_irq_mask = 0x00000004, + .reg_update_irq_mask = 0x00000010, + .eof_irq_mask = 0x00000002, + .error_irq_mask0 = 0x0003FC00, + .error_irq_mask1 = 0x0FFF7E80, +}; + +struct cam_vfe_top_ver2_reg_offset_module_ctrl lens_170_reg = { + .reset = 0x0000001C, + .cgc_ovd = 0x0000002C, + .enable = 0x00000040, +}; + +struct cam_vfe_top_ver2_reg_offset_module_ctrl stats_170_reg = { + .reset = 0x00000020, + .cgc_ovd = 0x00000030, + .enable = 0x00000044, +}; + +struct cam_vfe_top_ver2_reg_offset_module_ctrl color_170_reg = { + .reset = 0x00000024, + .cgc_ovd = 0x00000034, + .enable = 0x00000048, +}; + +struct cam_vfe_top_ver2_reg_offset_module_ctrl zoom_170_reg = { + .reset = 0x00000028, + .cgc_ovd = 0x00000038, + .enable = 0x0000004C, +}; + +static struct cam_vfe_top_ver2_reg_offset_common vfe170_top_common_reg = { + .hw_version = 0x00000000, + .hw_capability = 0x00000004, + .lens_feature = 0x00000008, + .stats_feature = 0x0000000C, + .color_feature = 0x00000010, + .zoom_feature = 0x00000014, + .global_reset_cmd = 0x00000018, + .module_ctrl = { + &lens_170_reg, + &stats_170_reg, + &color_170_reg, + &zoom_170_reg, + }, + .bus_cgc_ovd = 0x0000003C, + .core_cfg = 0x00000050, + .three_D_cfg = 0x00000054, + .violation_status = 0x0000007C, + .reg_update_cmd = 0x000004AC, +}; + +static struct cam_vfe_rdi_ver2_reg vfe170_rdi_reg = { + .reg_update_cmd = 0x000004AC, +}; + +static struct cam_vfe_rdi_reg_data vfe_170_rdi_0_data = { + .reg_update_cmd_data = 0x2, + .sof_irq_mask = 0x8000000, + .reg_update_irq_mask = 0x20, +}; + +static struct cam_vfe_rdi_reg_data vfe_170_rdi_1_data = { + .reg_update_cmd_data = 0x4, + .sof_irq_mask = 0x10000000, + .reg_update_irq_mask = 0x40, +}; + +static struct cam_vfe_rdi_reg_data vfe_170_rdi_2_data = { + .reg_update_cmd_data = 0x8, + .sof_irq_mask = 0x20000000, + .reg_update_irq_mask = 0x80, +}; + +static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = { + .common_reg = &vfe170_top_common_reg, + .camif_hw_info = { + .common_reg = &vfe170_top_common_reg, + .camif_reg = &vfe170_camif_reg, + .reg_data = &vfe_170_camif_reg_data, + }, + .rdi_hw_info = { + .common_reg = &vfe170_top_common_reg, + .rdi_reg = &vfe170_rdi_reg, + .reg_data = { + &vfe_170_rdi_0_data, + &vfe_170_rdi_1_data, + &vfe_170_rdi_2_data, + NULL, + }, + }, + .mux_type = { + CAM_VFE_CAMIF_VER_2_0, + CAM_VFE_RDI_VER_1_0, + CAM_VFE_RDI_VER_1_0, + CAM_VFE_RDI_VER_1_0, + }, +}; + +static struct cam_irq_register_set vfe170_bus_irq_reg[3] = { + { + .mask_reg_offset = 0x00002044, + .clear_reg_offset = 0x00002050, + .status_reg_offset = 0x0000205C, + }, + { + .mask_reg_offset = 0x00002048, + .clear_reg_offset = 0x00002054, + .status_reg_offset = 0x00002060, + }, + { + .mask_reg_offset = 0x0000204C, + .clear_reg_offset = 0x00002058, + .status_reg_offset = 0x00002064, + }, +}; + +static struct cam_vfe_bus_ver2_reg_offset_ubwc_client ubwc_regs_client_3 = { + .tile_cfg = 0x0000252C, + .h_init = 0x00002530, + .v_init = 0x00002534, + .meta_addr = 0x00002538, + .meta_offset = 0x0000253C, + .meta_stride = 0x00002540, + .mode_cfg = 0x00002544, + .bw_limit = 0x000025A0, +}; + +static struct cam_vfe_bus_ver2_reg_offset_ubwc_client ubwc_regs_client_4 = { + .tile_cfg = 0x0000262C, + .h_init = 0x00002630, + .v_init = 0x00002634, + .meta_addr = 0x00002638, + .meta_offset = 0x0000263C, + .meta_stride = 0x00002640, + .mode_cfg = 0x00002644, + .bw_limit = 0x000026A0, +}; + +static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = { + .common_reg = { + .hw_version = 0x00002000, + .hw_capability = 0x00002004, + .sw_reset = 0x00002008, + .cgc_ovd = 0x0000200C, + .pwr_iso_cfg = 0x000020CC, + .dual_master_comp_cfg = 0x00002028, + .irq_reg_info = { + .num_registers = 3, + .irq_reg_set = vfe170_bus_irq_reg, + .global_clear_offset = 0x00002068, + .global_clear_bitmask = 0x00000001, + }, + .comp_error_status = 0x0000206C, + .comp_ovrwr_status = 0x00002070, + .dual_comp_error_status = 0x00002074, + .dual_comp_ovrwr_status = 0x00002078, + .addr_sync_cfg = 0x0000207C, + .addr_sync_frame_hdr = 0x00002080, + .addr_sync_no_sync = 0x00002084, + }, + .num_client = 20, + .bus_client_reg = { + /* BUS Client 0 */ + { + .status0 = 0x00002200, + .status1 = 0x00002204, + .cfg = 0x00002208, + .header_addr = 0x0000220C, + .header_cfg = 0x00002210, + .image_addr = 0x00002214, + .image_addr_offset = 0x00002218, + .buffer_width_cfg = 0x0000221C, + .buffer_height_cfg = 0x00002220, + .packer_cfg = 0x00002224, + .stride = 0x00002228, + .irq_subsample_period = 0x00002248, + .irq_subsample_pattern = 0x0000224C, + .framedrop_period = 0x00002250, + .framedrop_pattern = 0x00002254, + .frame_inc = 0x00002258, + .burst_limit = 0x0000225C, + .ubwc_regs = NULL, + }, + /* BUS Client 1 */ + { + .status0 = 0x00002300, + .status1 = 0x00002304, + .cfg = 0x00002308, + .header_addr = 0x0000230C, + .header_cfg = 0x00002310, + .image_addr = 0x00002314, + .image_addr_offset = 0x00002318, + .buffer_width_cfg = 0x0000231C, + .buffer_height_cfg = 0x00002320, + .packer_cfg = 0x00002324, + .stride = 0x00002328, + .irq_subsample_period = 0x00002348, + .irq_subsample_pattern = 0x0000234C, + .framedrop_period = 0x00002350, + .framedrop_pattern = 0x00002354, + .frame_inc = 0x00002358, + .burst_limit = 0x0000235C, + .ubwc_regs = NULL, + }, + /* BUS Client 2 */ + { + .status0 = 0x00002400, + .status1 = 0x00002404, + .cfg = 0x00002408, + .header_addr = 0x0000240C, + .header_cfg = 0x00002410, + .image_addr = 0x00002414, + .image_addr_offset = 0x00002418, + .buffer_width_cfg = 0x0000241C, + .buffer_height_cfg = 0x00002420, + .packer_cfg = 0x00002424, + .stride = 0x00002428, + .irq_subsample_period = 0x00002448, + .irq_subsample_pattern = 0x0000244C, + .framedrop_period = 0x00002450, + .framedrop_pattern = 0x00002454, + .frame_inc = 0x00002458, + .burst_limit = 0x0000245C, + .ubwc_regs = NULL, + }, + /* BUS Client 3 */ + { + .status0 = 0x00002500, + .status1 = 0x00002504, + .cfg = 0x00002508, + .header_addr = 0x0000250C, + .header_cfg = 0x00002510, + .image_addr = 0x00002514, + .image_addr_offset = 0x00002518, + .buffer_width_cfg = 0x0000251C, + .buffer_height_cfg = 0x00002520, + .packer_cfg = 0x00002524, + .stride = 0x00002528, + .irq_subsample_period = 0x00002548, + .irq_subsample_pattern = 0x0000254C, + .framedrop_period = 0x00002550, + .framedrop_pattern = 0x00002554, + .frame_inc = 0x00002558, + .burst_limit = 0x0000255C, + .ubwc_regs = &ubwc_regs_client_3, + }, + /* BUS Client 4 */ + { + .status0 = 0x00002600, + .status1 = 0x00002604, + .cfg = 0x00002608, + .header_addr = 0x0000260C, + .header_cfg = 0x00002610, + .image_addr = 0x00002614, + .image_addr_offset = 0x00002618, + .buffer_width_cfg = 0x0000261C, + .buffer_height_cfg = 0x00002620, + .packer_cfg = 0x00002624, + .stride = 0x00002628, + .irq_subsample_period = 0x00002648, + .irq_subsample_pattern = 0x0000264C, + .framedrop_period = 0x00002650, + .framedrop_pattern = 0x00002654, + .frame_inc = 0x00002658, + .burst_limit = 0x0000265C, + .ubwc_regs = &ubwc_regs_client_4, + }, + /* BUS Client 5 */ + { + .status0 = 0x00002700, + .status1 = 0x00002704, + .cfg = 0x00002708, + .header_addr = 0x0000270C, + .header_cfg = 0x00002710, + .image_addr = 0x00002714, + .image_addr_offset = 0x00002718, + .buffer_width_cfg = 0x0000271C, + .buffer_height_cfg = 0x00002720, + .packer_cfg = 0x00002724, + .stride = 0x00002728, + .irq_subsample_period = 0x00002748, + .irq_subsample_pattern = 0x0000274C, + .framedrop_period = 0x00002750, + .framedrop_pattern = 0x00002754, + .frame_inc = 0x00002758, + .burst_limit = 0x0000275C, + .ubwc_regs = NULL, + }, + /* BUS Client 6 */ + { + .status0 = 0x00002800, + .status1 = 0x00002804, + .cfg = 0x00002808, + .header_addr = 0x0000280C, + .header_cfg = 0x00002810, + .image_addr = 0x00002814, + .image_addr_offset = 0x00002818, + .buffer_width_cfg = 0x0000281C, + .buffer_height_cfg = 0x00002820, + .packer_cfg = 0x00002824, + .stride = 0x00002828, + .irq_subsample_period = 0x00002848, + .irq_subsample_pattern = 0x0000284C, + .framedrop_period = 0x00002850, + .framedrop_pattern = 0x00002854, + .frame_inc = 0x00002858, + .burst_limit = 0x0000285C, + .ubwc_regs = NULL, + }, + /* BUS Client 7 */ + { + .status0 = 0x00002900, + .status1 = 0x00002904, + .cfg = 0x00002908, + .header_addr = 0x0000290C, + .header_cfg = 0x00002910, + .image_addr = 0x00002914, + .image_addr_offset = 0x00002918, + .buffer_width_cfg = 0x0000291C, + .buffer_height_cfg = 0x00002920, + .packer_cfg = 0x00002924, + .stride = 0x00002928, + .irq_subsample_period = 0x00002948, + .irq_subsample_pattern = 0x0000294C, + .framedrop_period = 0x00002950, + .framedrop_pattern = 0x00002954, + .frame_inc = 0x00002958, + .burst_limit = 0x0000295C, + .ubwc_regs = NULL, + }, + /* BUS Client 8 */ + { + .status0 = 0x00002A00, + .status1 = 0x00002A04, + .cfg = 0x00002A08, + .header_addr = 0x00002A0C, + .header_cfg = 0x00002A10, + .image_addr = 0x00002A14, + .image_addr_offset = 0x00002A18, + .buffer_width_cfg = 0x00002A1C, + .buffer_height_cfg = 0x00002A20, + .packer_cfg = 0x00002A24, + .stride = 0x00002A28, + .irq_subsample_period = 0x00002A48, + .irq_subsample_pattern = 0x00002A4C, + .framedrop_period = 0x00002A50, + .framedrop_pattern = 0x00002A54, + .frame_inc = 0x00002A58, + .burst_limit = 0x00002A5C, + .ubwc_regs = NULL, + }, + /* BUS Client 9 */ + { + .status0 = 0x00002B00, + .status1 = 0x00002B04, + .cfg = 0x00002B08, + .header_addr = 0x00002B0C, + .header_cfg = 0x00002B10, + .image_addr = 0x00002B14, + .image_addr_offset = 0x00002B18, + .buffer_width_cfg = 0x00002B1C, + .buffer_height_cfg = 0x00002B20, + .packer_cfg = 0x00002B24, + .stride = 0x00002B28, + .irq_subsample_period = 0x00002B48, + .irq_subsample_pattern = 0x00002B4C, + .framedrop_period = 0x00002B50, + .framedrop_pattern = 0x00002B54, + .frame_inc = 0x00002B58, + .burst_limit = 0x00002B5C, + .ubwc_regs = NULL, + }, + /* BUS Client 10 */ + { + .status0 = 0x00002C00, + .status1 = 0x00002C04, + .cfg = 0x00002C08, + .header_addr = 0x00002C0C, + .header_cfg = 0x00002C10, + .image_addr = 0x00002C14, + .image_addr_offset = 0x00002C18, + .buffer_width_cfg = 0x00002C1C, + .buffer_height_cfg = 0x00002C20, + .packer_cfg = 0x00002C24, + .stride = 0x00002C28, + .irq_subsample_period = 0x00002C48, + .irq_subsample_pattern = 0x00002C4C, + .framedrop_period = 0x00002C50, + .framedrop_pattern = 0x00002C54, + .frame_inc = 0x00002C58, + .burst_limit = 0x00002C5C, + .ubwc_regs = NULL, + }, + /* BUS Client 11 */ + { + .status0 = 0x00002D00, + .status1 = 0x00002D04, + .cfg = 0x00002D08, + .header_addr = 0x00002D0C, + .header_cfg = 0x00002D10, + .image_addr = 0x00002D14, + .image_addr_offset = 0x00002D18, + .buffer_width_cfg = 0x00002D1C, + .buffer_height_cfg = 0x00002D20, + .packer_cfg = 0x00002D24, + .stride = 0x00002D28, + .irq_subsample_period = 0x00002D48, + .irq_subsample_pattern = 0x00002D4C, + .framedrop_period = 0x00002D50, + .framedrop_pattern = 0x00002D54, + .frame_inc = 0x00002D58, + .burst_limit = 0x00002D5C, + .ubwc_regs = NULL, + }, + /* BUS Client 12 */ + { + .status0 = 0x00002E00, + .status1 = 0x00002E04, + .cfg = 0x00002E08, + .header_addr = 0x00002E0C, + .header_cfg = 0x00002E10, + .image_addr = 0x00002E14, + .image_addr_offset = 0x00002E18, + .buffer_width_cfg = 0x00002E1C, + .buffer_height_cfg = 0x00002E20, + .packer_cfg = 0x00002E24, + .stride = 0x00002E28, + .irq_subsample_period = 0x00002E48, + .irq_subsample_pattern = 0x00002E4C, + .framedrop_period = 0x00002E50, + .framedrop_pattern = 0x00002E54, + .frame_inc = 0x00002E58, + .burst_limit = 0x00002E5C, + .ubwc_regs = NULL, + }, + /* BUS Client 13 */ + { + .status0 = 0x00002F00, + .status1 = 0x00002F04, + .cfg = 0x00002F08, + .header_addr = 0x00002F0C, + .header_cfg = 0x00002F10, + .image_addr = 0x00002F14, + .image_addr_offset = 0x00002F18, + .buffer_width_cfg = 0x00002F1C, + .buffer_height_cfg = 0x00002F20, + .packer_cfg = 0x00002F24, + .stride = 0x00002F28, + .irq_subsample_period = 0x00002F48, + .irq_subsample_pattern = 0x00002F4C, + .framedrop_period = 0x00002F50, + .framedrop_pattern = 0x00002F54, + .frame_inc = 0x00002F58, + .burst_limit = 0x00002F5C, + .ubwc_regs = NULL, + }, + /* BUS Client 14 */ + { + .status0 = 0x00003000, + .status1 = 0x00003004, + .cfg = 0x00003008, + .header_addr = 0x0000300C, + .header_cfg = 0x00003010, + .image_addr = 0x00003014, + .image_addr_offset = 0x00003018, + .buffer_width_cfg = 0x0000301C, + .buffer_height_cfg = 0x00003020, + .packer_cfg = 0x00003024, + .stride = 0x00003028, + .irq_subsample_period = 0x00003048, + .irq_subsample_pattern = 0x0000304C, + .framedrop_period = 0x00003050, + .framedrop_pattern = 0x00003054, + .frame_inc = 0x00003058, + .burst_limit = 0x0000305C, + .ubwc_regs = NULL, + }, + /* BUS Client 15 */ + { + .status0 = 0x00003100, + .status1 = 0x00003104, + .cfg = 0x00003108, + .header_addr = 0x0000310C, + .header_cfg = 0x00003110, + .image_addr = 0x00003114, + .image_addr_offset = 0x00003118, + .buffer_width_cfg = 0x0000311C, + .buffer_height_cfg = 0x00003120, + .packer_cfg = 0x00003124, + .stride = 0x00003128, + .irq_subsample_period = 0x00003148, + .irq_subsample_pattern = 0x0000314C, + .framedrop_period = 0x00003150, + .framedrop_pattern = 0x00003154, + .frame_inc = 0x00003158, + .burst_limit = 0x0000315C, + .ubwc_regs = NULL, + }, + /* BUS Client 16 */ + { + .status0 = 0x00003200, + .status1 = 0x00003204, + .cfg = 0x00003208, + .header_addr = 0x0000320C, + .header_cfg = 0x00003210, + .image_addr = 0x00003214, + .image_addr_offset = 0x00003218, + .buffer_width_cfg = 0x0000321C, + .buffer_height_cfg = 0x00003220, + .packer_cfg = 0x00003224, + .stride = 0x00003228, + .irq_subsample_period = 0x00003248, + .irq_subsample_pattern = 0x0000324C, + .framedrop_period = 0x00003250, + .framedrop_pattern = 0x00003254, + .frame_inc = 0x00003258, + .burst_limit = 0x0000325C, + .ubwc_regs = NULL, + }, + /* BUS Client 17 */ + { + .status0 = 0x00003300, + .status1 = 0x00003304, + .cfg = 0x00003308, + .header_addr = 0x0000330C, + .header_cfg = 0x00003310, + .image_addr = 0x00003314, + .image_addr_offset = 0x00003318, + .buffer_width_cfg = 0x0000331C, + .buffer_height_cfg = 0x00003320, + .packer_cfg = 0x00003324, + .stride = 0x00003328, + .irq_subsample_period = 0x00003348, + .irq_subsample_pattern = 0x0000334C, + .framedrop_period = 0x00003350, + .framedrop_pattern = 0x00003354, + .frame_inc = 0x00003358, + .burst_limit = 0x0000335C, + .ubwc_regs = NULL, + }, + /* BUS Client 18 */ + { + .status0 = 0x00003400, + .status1 = 0x00003404, + .cfg = 0x00003408, + .header_addr = 0x0000340C, + .header_cfg = 0x00003410, + .image_addr = 0x00003414, + .image_addr_offset = 0x00003418, + .buffer_width_cfg = 0x0000341C, + .buffer_height_cfg = 0x00003420, + .packer_cfg = 0x00003424, + .stride = 0x00003428, + .irq_subsample_period = 0x00003448, + .irq_subsample_pattern = 0x0000344C, + .framedrop_period = 0x00003450, + .framedrop_pattern = 0x00003454, + .frame_inc = 0x00003458, + .burst_limit = 0x0000345C, + .ubwc_regs = NULL, + }, + /* BUS Client 19 */ + { + .status0 = 0x00003500, + .status1 = 0x00003504, + .cfg = 0x00003508, + .header_addr = 0x0000350C, + .header_cfg = 0x00003510, + .image_addr = 0x00003514, + .image_addr_offset = 0x00003518, + .buffer_width_cfg = 0x0000351C, + .buffer_height_cfg = 0x00003520, + .packer_cfg = 0x00003524, + .stride = 0x00003528, + .irq_subsample_period = 0x00003548, + .irq_subsample_pattern = 0x0000354C, + .framedrop_period = 0x00003550, + .framedrop_pattern = 0x00003554, + .frame_inc = 0x00003558, + .burst_limit = 0x0000355C, + .ubwc_regs = NULL, + }, + }, + .comp_grp_reg = { + /* CAM_VFE_BUS_VER2_COMP_GRP_0 */ + { + .comp_mask = 0x00002010, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_1 */ + { + .comp_mask = 0x00002014, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_2 */ + { + .comp_mask = 0x00002018, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_3 */ + { + .comp_mask = 0x0000201C, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_4 */ + { + .comp_mask = 0x00002020, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_5 */ + { + .comp_mask = 0x00002024, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */ + { + .comp_mask = 0x0000202C, + .addr_sync_mask = 0x00002088, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */ + { + .comp_mask = 0x00002030, + .addr_sync_mask = 0x0000208C, + + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */ + { + .comp_mask = 0x00002034, + .addr_sync_mask = 0x00002090, + + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */ + { + .comp_mask = 0x00002038, + .addr_sync_mask = 0x00002094, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */ + { + .comp_mask = 0x0000203C, + .addr_sync_mask = 0x00002098, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */ + { + .comp_mask = 0x00002040, + .addr_sync_mask = 0x0000209C, + }, + }, + .num_out = 18, + .vfe_out_hw_info = { + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_FULL, + .max_width = 4096, + .max_height = 4096, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_DS4, + .max_width = 1920, + .max_height = 1080, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_DS16, + .max_width = 1920, + .max_height = 1080, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_FD, + .max_width = 1920, + .max_height = 1080, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_PDAF, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST, + .max_width = 1920, + .max_height = 1080, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = + CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST, + .max_width = -1, + .max_height = -1, + }, + }, +}; + +struct cam_vfe_hw_info cam_vfe170_hw_info = { + .irq_reg_info = &vfe170_top_irq_reg_info, + + .bus_version = CAM_VFE_BUS_VER_2_0, + .bus_hw_info = &vfe170_bus_hw_info, + + .top_version = CAM_VFE_TOP_VER_2_0, + .top_hw_info = &vfe170_top_hw_info, + + .camif_version = CAM_VFE_CAMIF_VER_2_0, + .camif_reg = &vfe170_camif_reg, + +}; + +#endif /* _CAM_VFE170_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c new file mode 100644 index 000000000000..3c8abbf065ab --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c @@ -0,0 +1,51 @@ +/* Copyright (c) 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 +#include "cam_vfe_lite170.h" +#include "cam_vfe_hw_intf.h" +#include "cam_vfe_core.h" +#include "cam_vfe_dev.h" + +static const struct of_device_id cam_vfe170_dt_match[] = { + { + .compatible = "qcom,vfe-lite170", + .data = &cam_vfe_lite170_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_vfe170_dt_match); + +static struct platform_driver cam_vfe170_driver = { + .probe = cam_vfe_probe, + .remove = cam_vfe_remove, + .driver = { + .name = "cam_vfe_lite170", + .owner = THIS_MODULE, + .of_match_table = cam_vfe170_dt_match, + }, +}; + +static int __init cam_vfe170_init_module(void) +{ + return platform_driver_register(&cam_vfe170_driver); +} + +static void __exit cam_vfe170_exit_module(void) +{ + platform_driver_unregister(&cam_vfe170_driver); +} + +module_init(cam_vfe170_init_module); +module_exit(cam_vfe170_exit_module); +MODULE_DESCRIPTION("CAM VFE170 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h new file mode 100644 index 000000000000..2f95febee5f7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h @@ -0,0 +1,336 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_LITE170_H_ +#define _CAM_VFE_LITE170_H_ + +#include "cam_vfe_bus_ver2.h" +#include "cam_irq_controller.h" +#include "cam_vfe_top_ver2.h" +#include "cam_vfe_core.h" + +static struct cam_irq_register_set vfe170_top_irq_reg_set[2] = { + { + .mask_reg_offset = 0x0000005C, + .clear_reg_offset = 0x00000064, + .status_reg_offset = 0x0000006C, + }, + { + .mask_reg_offset = 0x00000060, + .clear_reg_offset = 0x00000068, + .status_reg_offset = 0x00000070, + }, +}; + +static struct cam_irq_controller_reg_info vfe170_top_irq_reg_info = { + .num_registers = 2, + .irq_reg_set = vfe170_top_irq_reg_set, + .global_clear_offset = 0x00000058, + .global_clear_bitmask = 0x00000001, +}; + +static struct cam_vfe_top_ver2_reg_offset_common vfe170_top_common_reg = { + .hw_version = 0x00000000, + .hw_capability = 0x00000004, + .lens_feature = 0x00000008, + .stats_feature = 0x0000000C, + .color_feature = 0x00000010, + .zoom_feature = 0x00000014, + .global_reset_cmd = 0x00000018, + .module_ctrl = { + NULL, + NULL, + NULL, + NULL, + }, + .bus_cgc_ovd = 0x0000003C, + .core_cfg = 0x00000000, + .three_D_cfg = 0x00000000, + .violation_status = 0x0000007C, + .reg_update_cmd = 0x000004AC, +}; + +static struct cam_vfe_rdi_ver2_reg vfe170_rdi_reg = { + .reg_update_cmd = 0x000004AC, +}; + +static struct cam_vfe_rdi_reg_data vfe170_rdi_0_data = { + .reg_update_cmd_data = 0x2, + .sof_irq_mask = 0x8000000, + .reg_update_irq_mask = 0x20, +}; + +static struct cam_vfe_rdi_reg_data vfe170_rdi_1_data = { + .reg_update_cmd_data = 0x4, + .sof_irq_mask = 0x10000000, + .reg_update_irq_mask = 0x40, +}; + +static struct cam_vfe_rdi_reg_data vfe170_rdi_2_data = { + .reg_update_cmd_data = 0x8, + .sof_irq_mask = 0x20000000, + .reg_update_irq_mask = 0x80, +}; + +static struct cam_vfe_rdi_reg_data vfe170_rdi_3_data = { + .reg_update_cmd_data = 0x10, + .sof_irq_mask = 0x40000000, + .reg_update_irq_mask = 0x100, +}; + +static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = { + .common_reg = &vfe170_top_common_reg, + .camif_hw_info = { + .common_reg = NULL, + .camif_reg = NULL, + .reg_data = NULL, + }, + .rdi_hw_info = { + .common_reg = &vfe170_top_common_reg, + .rdi_reg = &vfe170_rdi_reg, + .reg_data = { + &vfe170_rdi_0_data, + &vfe170_rdi_1_data, + &vfe170_rdi_2_data, + &vfe170_rdi_3_data, + }, + }, + .mux_type = { + CAM_VFE_RDI_VER_1_0, + CAM_VFE_RDI_VER_1_0, + CAM_VFE_RDI_VER_1_0, + CAM_VFE_RDI_VER_1_0, + }, +}; + +static struct cam_irq_register_set vfe170_bus_irq_reg[3] = { + { + .mask_reg_offset = 0x00002044, + .clear_reg_offset = 0x00002050, + .status_reg_offset = 0x0000205C, + }, + { + .mask_reg_offset = 0x00002048, + .clear_reg_offset = 0x00002054, + .status_reg_offset = 0x00002060, + }, + { + .mask_reg_offset = 0x0000204C, + .clear_reg_offset = 0x00002058, + .status_reg_offset = 0x00002064, + }, +}; + +static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = { + .common_reg = { + .hw_version = 0x00002000, + .hw_capability = 0x00002004, + .sw_reset = 0x00002008, + .cgc_ovd = 0x0000200C, + .pwr_iso_cfg = 0x000020CC, + .dual_master_comp_cfg = 0x00002028, + .irq_reg_info = { + .num_registers = 3, + .irq_reg_set = vfe170_bus_irq_reg, + .global_clear_offset = 0x00002068, + .global_clear_bitmask = 0x00000001, + }, + .comp_error_status = 0x0000206C, + .comp_ovrwr_status = 0x00002070, + .dual_comp_error_status = 0x00002074, + .dual_comp_ovrwr_status = 0x00002078, + .addr_sync_cfg = 0x0000207C, + .addr_sync_frame_hdr = 0x00002080, + .addr_sync_no_sync = 0x00002084, + }, + .num_client = 4, + .bus_client_reg = { + /* BUS Client 0 */ + { + .status0 = 0x00002200, + .status1 = 0x00002204, + .cfg = 0x00002208, + .header_addr = 0x0000220C, + .header_cfg = 0x00002210, + .image_addr = 0x00002214, + .image_addr_offset = 0x00002218, + .buffer_width_cfg = 0x0000221C, + .buffer_height_cfg = 0x00002220, + .packer_cfg = 0x00002224, + .stride = 0x00002228, + .irq_subsample_period = 0x00002248, + .irq_subsample_pattern = 0x0000224C, + .framedrop_period = 0x00002250, + .framedrop_pattern = 0x00002254, + .frame_inc = 0x00002258, + .burst_limit = 0x0000225C, + .ubwc_regs = NULL, + }, + /* BUS Client 1 */ + { + .status0 = 0x00002300, + .status1 = 0x00002304, + .cfg = 0x00002308, + .header_addr = 0x0000230C, + .header_cfg = 0x00002310, + .image_addr = 0x00002314, + .image_addr_offset = 0x00002318, + .buffer_width_cfg = 0x0000231C, + .buffer_height_cfg = 0x00002320, + .packer_cfg = 0x00002324, + .stride = 0x00002328, + .irq_subsample_period = 0x00002348, + .irq_subsample_pattern = 0x0000234C, + .framedrop_period = 0x00002350, + .framedrop_pattern = 0x00002354, + .frame_inc = 0x00002358, + .burst_limit = 0x0000235C, + .ubwc_regs = NULL, + }, + /* BUS Client 2 */ + { + .status0 = 0x00002400, + .status1 = 0x00002404, + .cfg = 0x00002408, + .header_addr = 0x0000240C, + .header_cfg = 0x00002410, + .image_addr = 0x00002414, + .image_addr_offset = 0x00002418, + .buffer_width_cfg = 0x0000241C, + .buffer_height_cfg = 0x00002420, + .packer_cfg = 0x00002424, + .stride = 0x00002428, + .irq_subsample_period = 0x00002448, + .irq_subsample_pattern = 0x0000244C, + .framedrop_period = 0x00002450, + .framedrop_pattern = 0x00002454, + .frame_inc = 0x00002458, + .burst_limit = 0x0000245C, + .ubwc_regs = NULL, + }, + /* BUS Client 3 */ + { + .status0 = 0x00002500, + .status1 = 0x00002504, + .cfg = 0x00002508, + .header_addr = 0x0000250C, + .header_cfg = 0x00002510, + .image_addr = 0x00002514, + .image_addr_offset = 0x00002518, + .buffer_width_cfg = 0x0000251C, + .buffer_height_cfg = 0x00002520, + .packer_cfg = 0x00002524, + .stride = 0x00002528, + .irq_subsample_period = 0x00002548, + .irq_subsample_pattern = 0x0000254C, + .framedrop_period = 0x00002550, + .framedrop_pattern = 0x00002554, + .frame_inc = 0x00002558, + .burst_limit = 0x0000255C, + .ubwc_regs = NULL, + }, + }, + .comp_grp_reg = { + /* CAM_VFE_BUS_VER2_COMP_GRP_0 */ + { + .comp_mask = 0x00002010, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_1 */ + { + .comp_mask = 0x00002014, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_2 */ + { + .comp_mask = 0x00002018, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_3 */ + { + .comp_mask = 0x0000201C, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_4 */ + { + .comp_mask = 0x00002020, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_5 */ + { + .comp_mask = 0x00002024, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */ + { + .comp_mask = 0x0000202C, + .addr_sync_mask = 0x00002088, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */ + { + .comp_mask = 0x00002030, + .addr_sync_mask = 0x0000208C, + + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */ + { + .comp_mask = 0x00002034, + .addr_sync_mask = 0x00002090, + + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */ + { + .comp_mask = 0x00002038, + .addr_sync_mask = 0x00002094, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */ + { + .comp_mask = 0x0000203C, + .addr_sync_mask = 0x00002098, + }, + /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */ + { + .comp_mask = 0x00002040, + .addr_sync_mask = 0x0000209C, + }, + }, + .num_out = 4, + .vfe_out_hw_info = { + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2, + .max_width = -1, + .max_height = -1, + }, + { + .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI3, + .max_width = -1, + .max_height = -1, + }, + }, +}; + +static struct cam_vfe_hw_info cam_vfe_lite170_hw_info = { + .irq_reg_info = &vfe170_top_irq_reg_info, + + .bus_version = CAM_VFE_BUS_VER_2_0, + .bus_hw_info = &vfe170_bus_hw_info, + + .top_version = CAM_VFE_TOP_VER_2_0, + .top_hw_info = &vfe170_top_hw_info, + +}; + +#endif /* _CAM_VFE_LITE170_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile new file mode 100644 index 000000000000..bd776d948c6d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_bus.o cam_vfe_bus_ver2.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c new file mode 100644 index 000000000000..c6c32723eb2f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c @@ -0,0 +1,56 @@ +/* Copyright (c) 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 "cam_vfe_bus.h" +#include "cam_vfe_bus_ver1.h" +#include "cam_vfe_bus_ver2.h" +#include "cam_debug_util.h" + +int cam_vfe_bus_init(uint32_t bus_version, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *bus_hw_info, + void *vfe_irq_controller, + struct cam_vfe_bus **vfe_bus) +{ + int rc = -ENODEV; + + switch (bus_version) { + case CAM_VFE_BUS_VER_2_0: + rc = cam_vfe_bus_ver2_init(soc_info, hw_intf, bus_hw_info, + vfe_irq_controller, vfe_bus); + break; + default: + CAM_ERR(CAM_ISP, "Unsupported Bus Version %x", bus_version); + break; + } + + return rc; +} + +int cam_vfe_bus_deinit(uint32_t bus_version, + struct cam_vfe_bus **vfe_bus) +{ + int rc = -ENODEV; + + switch (bus_version) { + case CAM_VFE_BUS_VER_2_0: + rc = cam_vfe_bus_ver2_deinit(vfe_bus); + break; + default: + CAM_ERR(CAM_ISP, "Unsupported Bus Version %x", bus_version); + break; + } + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h new file mode 100644 index 000000000000..357245196e0b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h @@ -0,0 +1,120 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_BUS_VER1_H_ +#define _CAM_VFE_BUS_VER1_H_ + +enum cam_vfe_bus_ver1_pingpong_id { + CAM_VFE_BUS_VER1_PING, + CAM_VFE_BUS_VER1_PONG, + CAM_VFE_BUS_VER1_PINGPONG_MAX, +}; + +enum cam_vfe_bus_ver1_wm_type { + CAM_VFE_BUS_WM_TYPE_IMAGE, + CAM_VFE_BUS_WM_TYPE_STATS, + CAM_VFE_BUS_WM_TYPE_MAX, +}; + +enum cam_vfe_bus_ver1_comp_grp_type { + CAM_VFE_BUS_VER1_COMP_GRP_IMG0, + CAM_VFE_BUS_VER1_COMP_GRP_IMG1, + CAM_VFE_BUS_VER1_COMP_GRP_IMG2, + CAM_VFE_BUS_VER1_COMP_GRP_IMG3, + CAM_VFE_BUS_VER1_COMP_GRP_STATS0, + CAM_VFE_BUS_VER1_COMP_GRP_STATS1, + CAM_VFE_BUS_VER1_COMP_GRP_MAX, +}; + +struct cam_vfe_bus_ver1_common_reg { + uint32_t cmd_offset; + uint32_t cfg_offset; + uint32_t io_fmt_offset; + uint32_t argb_cfg_offset; + uint32_t xbar_cfg0_offset; + uint32_t xbar_cfg1_offset; + uint32_t xbar_cfg2_offset; + uint32_t xbar_cfg3_offset; + uint32_t ping_pong_status_reg; +}; + +struct cam_vfe_bus_ver1_wm_reg { + uint32_t wm_cfg_offset; + uint32_t ping_addr_offset; + uint32_t ping_max_addr_offset; + uint32_t pong_addr_offset; + uint32_t pong_max_addr_offset; + uint32_t addr_cfg_offset; + uint32_t ub_cfg_offset; + uint32_t image_size_offset; + uint32_t buffer_cfg_offset; + uint32_t framedrop_pattern_offset; + uint32_t irq_subsample_pattern_offset; + uint32_t ping_pong_status_bit; /* 0 - 31 */ + uint32_t composite_bit; /* 0 -31 */ +}; + +struct cam_vfe_bus_ver1_wm_resource_data { + uint32_t index; + uint32_t wm_type; + uint32_t res_type; + + uint32_t offset; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t scanline; + + uint32_t burst_len; + + uint32_t framedrop_period; + uint32_t framedrop_pattern; + + uint32_t buf_valid[CAM_VFE_BUS_VER1_PINGPONG_MAX]; + uint32_t ub_size; + uint32_t ub_offset; + + struct cam_vfe_bus_ver1_wm_reg hw_regs; +}; + +struct cam_vfe_bus_ver1_comp_grp_reg { + enum cam_vfe_bus_ver1_comp_grp_type comp_grp_type; + uint32_t comp_grp_offset; +}; + +struct cam_vfe_bus_ver1_comp_grp { + struct cam_vfe_bus_ver1_comp_grp_reg reg_info; + struct list_head wm_list; + uint32_t cur_bit_mask; +}; + +/* + * cam_vfe_bus_ver1_init() + * + * @Brief: Initialize Bus layer + * + * @mem_base: Mapped base address of register space + * @hw_intf: HW Interface of HW to which this resource belongs + * @bus_hw_info: BUS HW info that contains details of BUS registers + * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top + * level IRQs + * @vfe_bus: Pointer to vfe_bus structure which will be filled + * and returned on successful initialize + */ +int cam_vfe_bus_ver1_init( + void __iomem *mem_base, + struct cam_hw_intf *hw_intf, + void *bus_hw_info, + void *vfe_irq_controller, + struct cam_vfe_bus **vfe_bus); + +#endif /* _CAM_VFE_BUS_VER1_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c new file mode 100644 index 000000000000..c86230986810 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c @@ -0,0 +1,3091 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include "cam_io_util.h" +#include "cam_debug_util.h" +#include "cam_cdm_util.h" +#include "cam_hw_intf.h" +#include "cam_ife_hw_mgr.h" +#include "cam_vfe_hw_intf.h" +#include "cam_irq_controller.h" +#include "cam_tasklet_util.h" +#include "cam_vfe_bus.h" +#include "cam_vfe_bus_ver2.h" +#include "cam_vfe_core.h" +#include "cam_debug_util.h" +#include "cam_cpas_api.h" + +static const char drv_name[] = "vfe_bus"; + +#define CAM_VFE_BUS_IRQ_REG0 0 +#define CAM_VFE_BUS_IRQ_REG1 1 +#define CAM_VFE_BUS_IRQ_REG2 2 +#define CAM_VFE_BUS_IRQ_MAX 3 + +#define CAM_VFE_BUS_VER2_PAYLOAD_MAX 256 + +#define CAM_VFE_RDI_BUS_DEFAULT_WIDTH 0xFF01 +#define CAM_VFE_RDI_BUS_DEFAULT_STRIDE 0xFF01 +#define CAM_VFE_BUS_INTRA_CLIENT_MASK 0x3 +#define CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT 8 +#define CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL 0xFFFFF + +#define ALIGNUP(value, alignment) \ + ((value + alignment - 1) / alignment * alignment) + +#define MAX_BUF_UPDATE_REG_NUM \ + ((sizeof(struct cam_vfe_bus_ver2_reg_offset_bus_client) + \ + sizeof(struct cam_vfe_bus_ver2_reg_offset_ubwc_client))/4) +#define MAX_REG_VAL_PAIR_SIZE \ + (MAX_BUF_UPDATE_REG_NUM * 2 * CAM_PACKET_MAX_PLANES) + +#define CAM_VFE_ADD_REG_VAL_PAIR(buf_array, index, offset, val) \ + do { \ + buf_array[index++] = offset; \ + buf_array[index++] = val; \ + } while (0) + +static uint32_t bus_error_irq_mask[3] = { + 0x7800, + 0x0000, + 0x0040, +}; + +enum cam_vfe_bus_packer_format { + PACKER_FMT_PLAIN_128 = 0x0, + PACKER_FMT_PLAIN_8 = 0x1, + PACKER_FMT_PLAIN_16_10BPP = 0x2, + PACKER_FMT_PLAIN_16_12BPP = 0x3, + PACKER_FMT_PLAIN_16_14BPP = 0x4, + PACKER_FMT_PLAIN_16_16BPP = 0x5, + PACKER_FMT_ARGB_10 = 0x6, + PACKER_FMT_ARGB_12 = 0x7, + PACKER_FMT_ARGB_14 = 0x8, + PACKER_FMT_PLAIN_32_20BPP = 0x9, + PACKER_FMT_PLAIN_64 = 0xA, + PACKER_FMT_TP_10 = 0xB, + PACKER_FMT_PLAIN_32_32BPP = 0xC, + PACKER_FMT_PLAIN_8_ODD_EVEN = 0xD, + PACKER_FMT_PLAIN_8_LSB_MSB_10 = 0xE, + PACKER_FMT_PLAIN_8_LSB_MSB_10_ODD_EVEN = 0xF, + PACKER_FMT_MAX = 0xF, +}; + +enum cam_vfe_bus_comp_grp_id { + CAM_VFE_BUS_COMP_GROUP_NONE = -EINVAL, + CAM_VFE_BUS_COMP_GROUP_ID_0 = 0x0, + CAM_VFE_BUS_COMP_GROUP_ID_1 = 0x1, + CAM_VFE_BUS_COMP_GROUP_ID_2 = 0x2, + CAM_VFE_BUS_COMP_GROUP_ID_3 = 0x3, + CAM_VFE_BUS_COMP_GROUP_ID_4 = 0x4, + CAM_VFE_BUS_COMP_GROUP_ID_5 = 0x5, +}; + +struct cam_vfe_bus_ver2_common_data { + uint32_t core_index; + void __iomem *mem_base; + struct cam_hw_intf *hw_intf; + void *bus_irq_controller; + void *vfe_irq_controller; + struct cam_vfe_bus_ver2_reg_offset_common *common_reg; + uint32_t io_buf_update[ + MAX_REG_VAL_PAIR_SIZE]; + + struct cam_vfe_bus_irq_evt_payload evt_payload[ + CAM_VFE_BUS_VER2_PAYLOAD_MAX]; + struct list_head free_payload_list; + struct mutex bus_mutex; + uint32_t secure_mode; + uint32_t num_sec_out; + uint32_t addr_no_sync; +}; + +struct cam_vfe_bus_ver2_wm_resource_data { + uint32_t index; + struct cam_vfe_bus_ver2_common_data *common_data; + struct cam_vfe_bus_ver2_reg_offset_bus_client *hw_regs; + void *ctx; + + uint32_t irq_enabled; + bool init_cfg_done; + bool hfr_cfg_done; + + uint32_t offset; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; + enum cam_vfe_bus_packer_format pack_fmt; + + uint32_t burst_len; + + uint32_t en_ubwc; + uint32_t packer_cfg; + uint32_t tile_cfg; + uint32_t h_init; + uint32_t v_init; + uint32_t ubwc_meta_stride; + uint32_t ubwc_mode_cfg; + uint32_t ubwc_meta_offset; + + uint32_t irq_subsample_period; + uint32_t irq_subsample_pattern; + uint32_t framedrop_period; + uint32_t framedrop_pattern; + + uint32_t en_cfg; + uint32_t is_dual; +}; + +struct cam_vfe_bus_ver2_comp_grp_data { + enum cam_vfe_bus_ver2_comp_grp_type comp_grp_type; + struct cam_vfe_bus_ver2_common_data *common_data; + struct cam_vfe_bus_ver2_reg_offset_comp_grp *hw_regs; + + uint32_t irq_enabled; + uint32_t comp_grp_local_idx; + uint32_t unique_id; + + uint32_t is_master; + uint32_t dual_slave_core; + uint32_t intra_client_mask; + uint32_t composite_mask; + uint32_t addr_sync_mode; + + uint32_t acquire_dev_cnt; + uint32_t irq_trigger_cnt; + + void *ctx; +}; + +struct cam_vfe_bus_ver2_vfe_out_data { + uint32_t out_type; + struct cam_vfe_bus_ver2_common_data *common_data; + + uint32_t num_wm; + struct cam_isp_resource_node *wm_res[PLANE_MAX]; + + struct cam_isp_resource_node *comp_grp; + enum cam_isp_hw_sync_mode dual_comp_sync_mode; + uint32_t dual_hw_alternate_vfe_id; + struct list_head vfe_out_list; + + uint32_t format; + uint32_t max_width; + uint32_t max_height; + struct cam_cdm_utils_ops *cdm_util_ops; + uint32_t secure_mode; +}; + +struct cam_vfe_bus_ver2_priv { + struct cam_vfe_bus_ver2_common_data common_data; + uint32_t num_client; + uint32_t num_out; + + struct cam_isp_resource_node bus_client[CAM_VFE_BUS_VER2_MAX_CLIENTS]; + struct cam_isp_resource_node comp_grp[CAM_VFE_BUS_VER2_COMP_GRP_MAX]; + struct cam_isp_resource_node vfe_out[CAM_VFE_BUS_VER2_VFE_OUT_MAX]; + + struct list_head free_comp_grp; + struct list_head free_dual_comp_grp; + struct list_head used_comp_grp; + + uint32_t irq_handle; + uint32_t error_irq_handle; +}; + +static int cam_vfe_bus_process_cmd( + struct cam_isp_resource_node *priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size); + +static int cam_vfe_bus_get_evt_payload( + struct cam_vfe_bus_ver2_common_data *common_data, + struct cam_vfe_bus_irq_evt_payload **evt_payload) +{ + if (list_empty(&common_data->free_payload_list)) { + *evt_payload = NULL; + CAM_ERR_RATE_LIMIT(CAM_ISP, "No free payload"); + return -ENODEV; + } + + *evt_payload = list_first_entry(&common_data->free_payload_list, + struct cam_vfe_bus_irq_evt_payload, list); + list_del_init(&(*evt_payload)->list); + return 0; +} + +static enum cam_vfe_bus_comp_grp_id + cam_vfe_bus_comp_grp_id_convert(uint32_t comp_grp) +{ + switch (comp_grp) { + case CAM_ISP_RES_COMP_GROUP_ID_0: + return CAM_VFE_BUS_COMP_GROUP_ID_0; + case CAM_ISP_RES_COMP_GROUP_ID_1: + return CAM_VFE_BUS_COMP_GROUP_ID_1; + case CAM_ISP_RES_COMP_GROUP_ID_2: + return CAM_VFE_BUS_COMP_GROUP_ID_2; + case CAM_ISP_RES_COMP_GROUP_ID_3: + return CAM_VFE_BUS_COMP_GROUP_ID_3; + case CAM_ISP_RES_COMP_GROUP_ID_4: + return CAM_VFE_BUS_COMP_GROUP_ID_4; + case CAM_ISP_RES_COMP_GROUP_ID_5: + return CAM_VFE_BUS_COMP_GROUP_ID_5; + case CAM_ISP_RES_COMP_GROUP_NONE: + default: + return CAM_VFE_BUS_COMP_GROUP_NONE; + } +} + +static int cam_vfe_bus_put_evt_payload(void *core_info, + struct cam_vfe_bus_irq_evt_payload **evt_payload) +{ + struct cam_vfe_bus_ver2_common_data *common_data = NULL; + uint32_t *ife_irq_regs = NULL; + uint32_t status_reg0, status_reg1, status_reg2; + + if (!core_info) { + CAM_ERR(CAM_ISP, "Invalid param core_info NULL"); + return -EINVAL; + } + if (*evt_payload == NULL) { + CAM_ERR(CAM_ISP, "No payload to put"); + return -EINVAL; + } + (*evt_payload)->error_type = 0; + ife_irq_regs = (*evt_payload)->irq_reg_val; + status_reg0 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0]; + status_reg1 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS1]; + status_reg2 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2]; + + if (status_reg0 || status_reg1 || status_reg2) { + CAM_DBG(CAM_ISP, "status0 0x%x status1 0x%x status2 0x%x", + status_reg0, status_reg1, status_reg2); + return 0; + } + + common_data = core_info; + list_add_tail(&(*evt_payload)->list, + &common_data->free_payload_list); + *evt_payload = NULL; + + CAM_DBG(CAM_ISP, "Done"); + return 0; +} + +static int cam_vfe_bus_ver2_get_intra_client_mask( + enum cam_vfe_bus_ver2_vfe_core_id dual_slave_core, + enum cam_vfe_bus_ver2_vfe_core_id current_core, + uint32_t *intra_client_mask) +{ + int rc = 0; + uint32_t camera_hw_version = 0; + uint32_t version_based_intra_client_mask = 0x1; + + *intra_client_mask = 0; + + + if (dual_slave_core == current_core) { + CAM_ERR(CAM_ISP, + "Invalid params. Same core as Master and Slave"); + return -EINVAL; + } + + rc = cam_cpas_get_cpas_hw_version(&camera_hw_version); + + CAM_DBG(CAM_ISP, "CPAS VERSION %d", camera_hw_version); + + switch (camera_hw_version) { + case CAM_CPAS_TITAN_170_V100: + version_based_intra_client_mask = 0x3; + break; + default: + version_based_intra_client_mask = 0x1; + break; + } + + + switch (current_core) { + case CAM_VFE_BUS_VER2_VFE_CORE_0: + switch (dual_slave_core) { + case CAM_VFE_BUS_VER2_VFE_CORE_1: + *intra_client_mask = version_based_intra_client_mask; + break; + default: + CAM_ERR(CAM_ISP, "Invalid value for slave core %u", + dual_slave_core); + rc = -EINVAL; + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_CORE_1: + switch (dual_slave_core) { + case CAM_VFE_BUS_VER2_VFE_CORE_0: + *intra_client_mask = version_based_intra_client_mask; + break; + default: + CAM_ERR(CAM_ISP, "Invalid value for slave core %u", + dual_slave_core); + rc = -EINVAL; + break; + } + break; + default: + CAM_ERR(CAM_ISP, + "Invalid value for master core %u", current_core); + rc = -EINVAL; + break; + } + + return rc; +} + +static bool cam_vfe_bus_can_be_secure(uint32_t out_type) +{ + switch (out_type) { + case CAM_VFE_BUS_VER2_VFE_OUT_FULL: + case CAM_VFE_BUS_VER2_VFE_OUT_DS4: + case CAM_VFE_BUS_VER2_VFE_OUT_DS16: + case CAM_VFE_BUS_VER2_VFE_OUT_FD: + case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI0: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI1: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI2: + return true; + + case CAM_VFE_BUS_VER2_VFE_OUT_PDAF: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST: + default: + return false; + } +} + +static enum cam_vfe_bus_ver2_vfe_out_type + cam_vfe_bus_get_out_res_id(uint32_t res_type) +{ + switch (res_type) { + case CAM_ISP_IFE_OUT_RES_FULL: + return CAM_VFE_BUS_VER2_VFE_OUT_FULL; + case CAM_ISP_IFE_OUT_RES_DS4: + return CAM_VFE_BUS_VER2_VFE_OUT_DS4; + case CAM_ISP_IFE_OUT_RES_DS16: + return CAM_VFE_BUS_VER2_VFE_OUT_DS16; + case CAM_ISP_IFE_OUT_RES_FD: + return CAM_VFE_BUS_VER2_VFE_OUT_FD; + case CAM_ISP_IFE_OUT_RES_RAW_DUMP: + return CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP; + case CAM_ISP_IFE_OUT_RES_PDAF: + return CAM_VFE_BUS_VER2_VFE_OUT_PDAF; + case CAM_ISP_IFE_OUT_RES_RDI_0: + return CAM_VFE_BUS_VER2_VFE_OUT_RDI0; + case CAM_ISP_IFE_OUT_RES_RDI_1: + return CAM_VFE_BUS_VER2_VFE_OUT_RDI1; + case CAM_ISP_IFE_OUT_RES_RDI_2: + return CAM_VFE_BUS_VER2_VFE_OUT_RDI2; + case CAM_ISP_IFE_OUT_RES_RDI_3: + return CAM_VFE_BUS_VER2_VFE_OUT_RDI3; + case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE; + case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST; + case CAM_ISP_IFE_OUT_RES_STATS_TL_BG: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG; + case CAM_ISP_IFE_OUT_RES_STATS_BF: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF; + case CAM_ISP_IFE_OUT_RES_STATS_AWB_BG: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG; + case CAM_ISP_IFE_OUT_RES_STATS_BHIST: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST; + case CAM_ISP_IFE_OUT_RES_STATS_RS: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS; + case CAM_ISP_IFE_OUT_RES_STATS_CS: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS; + case CAM_ISP_IFE_OUT_RES_STATS_IHIST: + return CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST; + default: + return CAM_VFE_BUS_VER2_VFE_OUT_MAX; + } +} + +static int cam_vfe_bus_get_num_wm( + enum cam_vfe_bus_ver2_vfe_out_type res_type, + uint32_t format) +{ + switch (res_type) { + case CAM_VFE_BUS_VER2_VFE_OUT_RDI0: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI1: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI2: + case CAM_VFE_BUS_VER2_VFE_OUT_RDI3: + switch (format) { + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_MIPI_RAW_10: + case CAM_FORMAT_MIPI_RAW_12: + case CAM_FORMAT_MIPI_RAW_14: + case CAM_FORMAT_MIPI_RAW_16: + case CAM_FORMAT_MIPI_RAW_20: + case CAM_FORMAT_DPCM_10_6_10: + case CAM_FORMAT_DPCM_10_8_10: + case CAM_FORMAT_DPCM_12_6_12: + case CAM_FORMAT_DPCM_12_8_12: + case CAM_FORMAT_DPCM_14_8_14: + case CAM_FORMAT_DPCM_14_10_14: + case CAM_FORMAT_PLAIN8: + case CAM_FORMAT_PLAIN16_10: + case CAM_FORMAT_PLAIN16_12: + case CAM_FORMAT_PLAIN16_14: + case CAM_FORMAT_PLAIN16_16: + case CAM_FORMAT_PLAIN32_20: + case CAM_FORMAT_PLAIN128: + return 1; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_FULL: + switch (format) { + case CAM_FORMAT_NV21: + case CAM_FORMAT_NV12: + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_PLAIN8: + case CAM_FORMAT_TP10: + case CAM_FORMAT_UBWC_NV12: + case CAM_FORMAT_UBWC_NV12_4R: + case CAM_FORMAT_UBWC_TP10: + case CAM_FORMAT_UBWC_P010: + case CAM_FORMAT_PLAIN16_10: + return 2; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_FD: + switch (format) { + case CAM_FORMAT_NV21: + case CAM_FORMAT_NV12: + case CAM_FORMAT_PLAIN8: + case CAM_FORMAT_TP10: + case CAM_FORMAT_PLAIN16_10: + return 2; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_DS4: + case CAM_VFE_BUS_VER2_VFE_OUT_DS16: + switch (format) { + case CAM_FORMAT_PD8: + case CAM_FORMAT_PD10: + return 1; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP: + switch (format) { + case CAM_FORMAT_ARGB_14: + case CAM_FORMAT_PLAIN8: + case CAM_FORMAT_PLAIN16_10: + case CAM_FORMAT_PLAIN16_12: + case CAM_FORMAT_PLAIN16_14: + return 1; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_PDAF: + switch (format) { + case CAM_FORMAT_PLAIN8: + case CAM_FORMAT_PLAIN16_10: + case CAM_FORMAT_PLAIN16_12: + case CAM_FORMAT_PLAIN16_14: + return 1; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS: + switch (format) { + case CAM_FORMAT_PLAIN64: + return 1; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS: + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST: + switch (format) { + case CAM_FORMAT_PLAIN16_16: + return 1; + default: + break; + } + break; + default: + break; + } + + CAM_ERR(CAM_ISP, "Unsupported format %u for resource_type %u", + format, res_type); + + return -EINVAL; +} + +static int cam_vfe_bus_get_wm_idx( + enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id, + enum cam_vfe_bus_plane_type plane) +{ + int wm_idx = -1; + + switch (vfe_out_res_id) { + case CAM_VFE_BUS_VER2_VFE_OUT_RDI0: + switch (plane) { + case PLANE_Y: + wm_idx = 0; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_RDI1: + switch (plane) { + case PLANE_Y: + wm_idx = 1; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_RDI2: + switch (plane) { + case PLANE_Y: + wm_idx = 2; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_RDI3: + switch (plane) { + case PLANE_Y: + wm_idx = 3; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_FULL: + switch (plane) { + case PLANE_Y: + wm_idx = 3; + break; + case PLANE_C: + wm_idx = 4; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_DS4: + switch (plane) { + case PLANE_Y: + wm_idx = 5; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_DS16: + switch (plane) { + case PLANE_Y: + wm_idx = 6; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_FD: + switch (plane) { + case PLANE_Y: + wm_idx = 7; + break; + case PLANE_C: + wm_idx = 8; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP: + switch (plane) { + case PLANE_Y: + wm_idx = 9; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_PDAF: + switch (plane) { + case PLANE_Y: + wm_idx = 10; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE: + switch (plane) { + case PLANE_Y: + wm_idx = 11; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST: + switch (plane) { + case PLANE_Y: + wm_idx = 12; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG: + switch (plane) { + case PLANE_Y: + wm_idx = 13; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF: + switch (plane) { + case PLANE_Y: + wm_idx = 14; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG: + switch (plane) { + case PLANE_Y: + wm_idx = 15; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST: + switch (plane) { + case PLANE_Y: + wm_idx = 16; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS: + switch (plane) { + case PLANE_Y: + wm_idx = 17; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS: + switch (plane) { + case PLANE_Y: + wm_idx = 18; + break; + default: + break; + } + break; + case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST: + switch (plane) { + case PLANE_Y: + wm_idx = 19; + break; + default: + break; + } + break; + default: + break; + } + + return wm_idx; +} + +static enum cam_vfe_bus_packer_format + cam_vfe_bus_get_packer_fmt(uint32_t out_fmt, int wm_index) +{ + switch (out_fmt) { + case CAM_FORMAT_NV21: + if (wm_index == 4 || wm_index == 6) + return PACKER_FMT_PLAIN_8_LSB_MSB_10_ODD_EVEN; + case CAM_FORMAT_NV12: + case CAM_FORMAT_UBWC_NV12: + case CAM_FORMAT_UBWC_NV12_4R: + return PACKER_FMT_PLAIN_8_LSB_MSB_10; + case CAM_FORMAT_PLAIN16_16: + return PACKER_FMT_PLAIN_16_16BPP; + case CAM_FORMAT_PLAIN64: + return PACKER_FMT_PLAIN_64; + case CAM_FORMAT_PLAIN8: + return PACKER_FMT_PLAIN_8; + case CAM_FORMAT_PLAIN16_10: + return PACKER_FMT_PLAIN_16_10BPP; + case CAM_FORMAT_PLAIN16_12: + return PACKER_FMT_PLAIN_16_12BPP; + case CAM_FORMAT_PLAIN16_14: + return PACKER_FMT_PLAIN_16_14BPP; + case CAM_FORMAT_PLAIN32_20: + return PACKER_FMT_PLAIN_32_20BPP; + case CAM_FORMAT_MIPI_RAW_6: + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_MIPI_RAW_10: + case CAM_FORMAT_MIPI_RAW_12: + case CAM_FORMAT_MIPI_RAW_14: + case CAM_FORMAT_MIPI_RAW_16: + case CAM_FORMAT_MIPI_RAW_20: + case CAM_FORMAT_PLAIN16_8: + case CAM_FORMAT_PLAIN128: + case CAM_FORMAT_PD8: + case CAM_FORMAT_PD10: + return PACKER_FMT_PLAIN_128; + case CAM_FORMAT_UBWC_TP10: + case CAM_FORMAT_TP10: + return PACKER_FMT_TP_10; + case CAM_FORMAT_ARGB_14: + return PACKER_FMT_ARGB_14; + default: + return PACKER_FMT_MAX; + } +} + +static int cam_vfe_bus_acquire_wm( + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_isp_out_port_info *out_port_info, + void *tasklet, + void *ctx, + enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id, + enum cam_vfe_bus_plane_type plane, + uint32_t subscribe_irq, + struct cam_isp_resource_node **wm_res, + uint32_t *client_done_mask, + uint32_t is_dual) +{ + uint32_t wm_idx = 0; + struct cam_isp_resource_node *wm_res_local = NULL; + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = NULL; + + *wm_res = NULL; + *client_done_mask = 0; + + /* No need to allocate for BUS VER2. VFE OUT to WM is fixed. */ + wm_idx = cam_vfe_bus_get_wm_idx(vfe_out_res_id, plane); + if (wm_idx < 0 || wm_idx >= ver2_bus_priv->num_client) { + CAM_ERR(CAM_ISP, "Unsupported VFE out %d plane %d", + vfe_out_res_id, plane); + return -EINVAL; + } + + wm_res_local = &ver2_bus_priv->bus_client[wm_idx]; + wm_res_local->tasklet_info = tasklet; + wm_res_local->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + rsrc_data = wm_res_local->res_priv; + rsrc_data->irq_enabled = subscribe_irq; + rsrc_data->ctx = ctx; + rsrc_data->format = out_port_info->format; + rsrc_data->pack_fmt = cam_vfe_bus_get_packer_fmt(rsrc_data->format, + wm_idx); + + rsrc_data->width = out_port_info->width; + rsrc_data->height = out_port_info->height; + rsrc_data->is_dual = is_dual; + /* Set WM offset value to default */ + rsrc_data->offset = 0; + CAM_DBG(CAM_ISP, "WM %d width %d height %d", rsrc_data->index, + rsrc_data->width, rsrc_data->height); + + if (rsrc_data->index < 3) { + /* Write master 0-2 refers to RDI 0/ RDI 1/RDI 2 */ + switch (rsrc_data->format) { + case CAM_FORMAT_MIPI_RAW_6: + case CAM_FORMAT_MIPI_RAW_8: + case CAM_FORMAT_MIPI_RAW_10: + case CAM_FORMAT_MIPI_RAW_12: + case CAM_FORMAT_MIPI_RAW_14: + case CAM_FORMAT_MIPI_RAW_16: + case CAM_FORMAT_MIPI_RAW_20: + case CAM_FORMAT_PLAIN128: + rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH; + rsrc_data->height = 0; + rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE; + rsrc_data->pack_fmt = 0x0; + rsrc_data->en_cfg = 0x3; + break; + case CAM_FORMAT_PLAIN8: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0x1; + rsrc_data->width = rsrc_data->width * 2; + rsrc_data->stride = rsrc_data->width; + break; + case CAM_FORMAT_PLAIN16_10: + rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH; + rsrc_data->height = 0; + rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE; + rsrc_data->pack_fmt = 0x0; + rsrc_data->en_cfg = 0x3; + break; + case CAM_FORMAT_PLAIN16_12: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0x3; + rsrc_data->width = rsrc_data->width * 2; + rsrc_data->stride = rsrc_data->width; + break; + case CAM_FORMAT_PLAIN16_14: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0x4; + rsrc_data->width = rsrc_data->width * 2; + rsrc_data->stride = rsrc_data->width; + break; + case CAM_FORMAT_PLAIN16_16: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0x5; + rsrc_data->width = rsrc_data->width * 2; + rsrc_data->stride = rsrc_data->width; + break; + case CAM_FORMAT_PLAIN32_20: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0x9; + break; + case CAM_FORMAT_PLAIN64: + rsrc_data->en_cfg = 0x1; + rsrc_data->pack_fmt = 0xA; + break; + default: + CAM_ERR(CAM_ISP, "Unsupported RDI format %d", + rsrc_data->format); + return -EINVAL; + } + } else if (rsrc_data->index < 5 || + rsrc_data->index == 7 || rsrc_data->index == 8) { + /* Write master 3, 4 - for Full OUT , 7-8 FD OUT */ + switch (rsrc_data->format) { + case CAM_FORMAT_UBWC_NV12_4R: + rsrc_data->en_ubwc = 1; + rsrc_data->width = ALIGNUP(rsrc_data->width, 64); + switch (plane) { + case PLANE_C: + rsrc_data->height /= 2; + break; + case PLANE_Y: + break; + default: + CAM_ERR(CAM_ISP, "Invalid plane %d", plane); + return -EINVAL; + } + break; + case CAM_FORMAT_UBWC_NV12: + rsrc_data->en_ubwc = 1; + /* Fall through for NV12 */ + case CAM_FORMAT_NV21: + case CAM_FORMAT_NV12: + switch (plane) { + case PLANE_C: + rsrc_data->height /= 2; + break; + case PLANE_Y: + break; + default: + CAM_ERR(CAM_ISP, "Invalid plane %d", plane); + return -EINVAL; + } + break; + case CAM_FORMAT_UBWC_TP10: + rsrc_data->en_ubwc = 1; + rsrc_data->width = + ALIGNUP(rsrc_data->width, 48) * 4 / 3; + switch (plane) { + case PLANE_C: + rsrc_data->height /= 2; + break; + case PLANE_Y: + break; + default: + CAM_ERR(CAM_ISP, "Invalid plane %d", plane); + return -EINVAL; + } + break; + case CAM_FORMAT_TP10: + rsrc_data->width = + ALIGNUP(rsrc_data->width, 3) * 4 / 3; + switch (plane) { + case PLANE_C: + rsrc_data->height /= 2; + break; + case PLANE_Y: + break; + default: + CAM_ERR(CAM_ISP, "Invalid plane %d", plane); + return -EINVAL; + } + break; + case CAM_FORMAT_PLAIN16_10: + switch (plane) { + case PLANE_C: + rsrc_data->height /= 2; + break; + case PLANE_Y: + break; + default: + CAM_ERR(CAM_ISP, "Invalid plane %d", plane); + return -EINVAL; + } + rsrc_data->width *= 2; + break; + default: + CAM_ERR(CAM_ISP, "Invalid format %d", + rsrc_data->format); + return -EINVAL; + } + rsrc_data->en_cfg = 0x1; + } else if (rsrc_data->index >= 11) { + /* Write master 11-19 stats */ + rsrc_data->width = 0; + rsrc_data->height = 0; + rsrc_data->stride = 1; + rsrc_data->en_cfg = 0x3; + } else if (rsrc_data->index == 9 || rsrc_data->index == 10) { + /* Write master 9 - Raw dump */ + rsrc_data->width = rsrc_data->width * 2; + rsrc_data->stride = rsrc_data->width; + rsrc_data->en_cfg = 0x1; + /* LSB aligned */ + rsrc_data->pack_fmt |= 0x10; + } else { + /* Write master 5-6 DS ports, 10 PDAF */ + uint32_t align_width; + rsrc_data->width = rsrc_data->width * 4; + rsrc_data->height = rsrc_data->height / 2; + rsrc_data->en_cfg = 0x1; + CAM_DBG(CAM_ISP, "before width %d", rsrc_data->width); + align_width = ALIGNUP(rsrc_data->width, 16); + if (align_width != rsrc_data->width) { + CAM_WARN(CAM_ISP, + "Override width %u with expected %u", + rsrc_data->width, align_width); + rsrc_data->width = align_width; + } + } + + *client_done_mask = (1 << wm_idx); + *wm_res = wm_res_local; + + CAM_DBG(CAM_ISP, "WM %d: processed width %d, processed height %d", + rsrc_data->index, rsrc_data->width, rsrc_data->height); + return 0; +} + +static int cam_vfe_bus_release_wm(void *bus_priv, + struct cam_isp_resource_node *wm_res) +{ + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = + wm_res->res_priv; + + rsrc_data->irq_enabled = 0; + rsrc_data->offset = 0; + rsrc_data->width = 0; + rsrc_data->height = 0; + rsrc_data->stride = 0; + rsrc_data->format = 0; + rsrc_data->pack_fmt = 0; + rsrc_data->burst_len = 0; + rsrc_data->irq_subsample_period = 0; + rsrc_data->irq_subsample_pattern = 0; + rsrc_data->framedrop_period = 0; + rsrc_data->framedrop_pattern = 0; + rsrc_data->packer_cfg = 0; + rsrc_data->en_ubwc = 0; + rsrc_data->tile_cfg = 0; + rsrc_data->h_init = 0; + rsrc_data->v_init = 0; + rsrc_data->ubwc_meta_stride = 0; + rsrc_data->ubwc_mode_cfg = 0; + rsrc_data->ubwc_meta_offset = 0; + rsrc_data->init_cfg_done = false; + rsrc_data->hfr_cfg_done = false; + rsrc_data->en_cfg = 0; + rsrc_data->is_dual = 0; + + wm_res->tasklet_info = NULL; + wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + + return 0; +} + +static int cam_vfe_bus_start_wm(struct cam_isp_resource_node *wm_res) +{ + int rc = 0; + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = + wm_res->res_priv; + struct cam_vfe_bus_ver2_common_data *common_data = + rsrc_data->common_data; + uint32_t bus_irq_reg_mask[CAM_VFE_BUS_IRQ_MAX] = {0}; + struct cam_irq_bh_api irq_bh_api; + + cam_io_w(0xf, common_data->mem_base + rsrc_data->hw_regs->burst_limit); + + cam_io_w_mb(rsrc_data->width, + common_data->mem_base + rsrc_data->hw_regs->buffer_width_cfg); + cam_io_w(rsrc_data->height, + common_data->mem_base + rsrc_data->hw_regs->buffer_height_cfg); + cam_io_w(rsrc_data->pack_fmt, + common_data->mem_base + rsrc_data->hw_regs->packer_cfg); + + /* Configure stride for RDIs */ + if (rsrc_data->index < 3) + cam_io_w_mb(rsrc_data->stride, (common_data->mem_base + + rsrc_data->hw_regs->stride)); + + /* Subscribe IRQ */ + if (rsrc_data->irq_enabled) { + CAM_DBG(CAM_ISP, "Subscribe WM%d IRQ", rsrc_data->index); + bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG1] = + (1 << rsrc_data->index); + irq_bh_api.bottom_half_enqueue_func = + cam_tasklet_enqueue_cmd; + irq_bh_api.get_bh_payload_func = cam_tasklet_get_cmd; + irq_bh_api.put_bh_payload_func = cam_tasklet_put_cmd; + wm_res->irq_handle = cam_irq_controller_subscribe_irq( + common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1, + bus_irq_reg_mask, wm_res, + wm_res->top_half_handler, + cam_ife_mgr_do_tasklet_buf_done, + wm_res->tasklet_info, &irq_bh_api); + if (wm_res->irq_handle < 0) { + CAM_ERR(CAM_ISP, "Subscribe IRQ failed for WM %d", + rsrc_data->index); + return -EFAULT; + } + } + + /* enable ubwc if needed*/ + if (rsrc_data->en_ubwc) { + int val = cam_io_r_mb(common_data->mem_base + + rsrc_data->hw_regs->ubwc_regs->mode_cfg); + CAM_DBG(CAM_ISP, "ubwc reg %d, res id %d", + val, rsrc_data->index); + val |= 0x1; + cam_io_w_mb(val, common_data->mem_base + + rsrc_data->hw_regs->ubwc_regs->mode_cfg); + } + + /* Enable WM */ + cam_io_w_mb(rsrc_data->en_cfg, common_data->mem_base + + rsrc_data->hw_regs->cfg); + + CAM_DBG(CAM_ISP, "WM res %d width = %d, height = %d", rsrc_data->index, + rsrc_data->width, rsrc_data->height); + CAM_DBG(CAM_ISP, "WM res %d pk_fmt = %d", rsrc_data->index, + rsrc_data->pack_fmt & PACKER_FMT_MAX); + CAM_DBG(CAM_ISP, "WM res %d stride = %d, burst len = %d", + rsrc_data->index, rsrc_data->stride, 0xf); + CAM_DBG(CAM_ISP, "enable WM res %d offset 0x%x val 0x%x", + rsrc_data->index, (uint32_t) rsrc_data->hw_regs->cfg, + rsrc_data->en_cfg); + + wm_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + return rc; +} + +static int cam_vfe_bus_stop_wm(struct cam_isp_resource_node *wm_res) +{ + int rc = 0; + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = + wm_res->res_priv; + struct cam_vfe_bus_ver2_common_data *common_data = + rsrc_data->common_data; + + /* Disble WM */ + /* Disable all register access, reply on global reset */ + CAM_DBG(CAM_ISP, "irq_enabled %d", rsrc_data->irq_enabled); + /* Unsubscribe IRQ */ + if (rsrc_data->irq_enabled) + rc = cam_irq_controller_unsubscribe_irq( + common_data->bus_irq_controller, + wm_res->irq_handle); + + wm_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + rsrc_data->init_cfg_done = false; + rsrc_data->hfr_cfg_done = false; + + return rc; +} + +static int cam_vfe_bus_handle_wm_done_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int32_t rc; + int i; + struct cam_isp_resource_node *wm_res = NULL; + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = NULL; + struct cam_vfe_bus_irq_evt_payload *evt_payload; + + wm_res = th_payload->handler_priv; + if (!wm_res) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Error: No resource"); + return -ENODEV; + } + + rsrc_data = wm_res->res_priv; + + CAM_DBG(CAM_ISP, "IRQ status_0 = 0x%x", th_payload->evt_status_arr[0]); + CAM_DBG(CAM_ISP, "IRQ status_1 = 0x%x", th_payload->evt_status_arr[1]); + + rc = cam_vfe_bus_get_evt_payload(rsrc_data->common_data, &evt_payload); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "IRQ status_0 = 0x%x status_1 = 0x%x status_2 = 0x%x", + th_payload->evt_status_arr[0], + th_payload->evt_status_arr[1], + th_payload->evt_status_arr[2]); + + return rc; + } + + cam_isp_hw_get_timestamp(&evt_payload->ts); + + evt_payload->ctx = rsrc_data->ctx; + evt_payload->core_index = rsrc_data->common_data->core_index; + evt_payload->evt_id = evt_id; + + for (i = 0; i < th_payload->num_registers; i++) + evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i]; + + th_payload->evt_payload_priv = evt_payload; + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +static int cam_vfe_bus_handle_wm_done_bottom_half(void *wm_node, + void *evt_payload_priv) +{ + int rc = CAM_VFE_IRQ_STATUS_ERR; + struct cam_isp_resource_node *wm_res = wm_node; + struct cam_vfe_bus_irq_evt_payload *evt_payload = evt_payload_priv; + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = + (wm_res == NULL) ? NULL : wm_res->res_priv; + uint32_t *cam_ife_irq_regs; + uint32_t status_reg; + + if (!evt_payload || !rsrc_data) + return rc; + + cam_ife_irq_regs = evt_payload->irq_reg_val; + status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS1]; + + if (status_reg & BIT(rsrc_data->index)) { + cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS1] &= + ~BIT(rsrc_data->index); + rc = CAM_VFE_IRQ_STATUS_SUCCESS; + } + CAM_DBG(CAM_ISP, "status_reg %x rc %d", status_reg, rc); + + if (rc == CAM_VFE_IRQ_STATUS_SUCCESS) + cam_vfe_bus_put_evt_payload(rsrc_data->common_data, + &evt_payload); + + return rc; +} + +static int cam_vfe_bus_init_wm_resource(uint32_t index, + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_vfe_bus_ver2_hw_info *ver2_hw_info, + struct cam_isp_resource_node *wm_res) +{ + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data; + + rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_wm_resource_data), + GFP_KERNEL); + if (!rsrc_data) { + CAM_DBG(CAM_ISP, "Failed to alloc for WM res priv"); + return -ENOMEM; + } + wm_res->res_priv = rsrc_data; + + rsrc_data->index = index; + rsrc_data->hw_regs = &ver2_hw_info->bus_client_reg[index]; + rsrc_data->common_data = &ver2_bus_priv->common_data; + + wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + INIT_LIST_HEAD(&wm_res->list); + + wm_res->start = cam_vfe_bus_start_wm; + wm_res->stop = cam_vfe_bus_stop_wm; + wm_res->top_half_handler = cam_vfe_bus_handle_wm_done_top_half; + wm_res->bottom_half_handler = cam_vfe_bus_handle_wm_done_bottom_half; + wm_res->hw_intf = ver2_bus_priv->common_data.hw_intf; + + return 0; +} + +static int cam_vfe_bus_deinit_wm_resource( + struct cam_isp_resource_node *wm_res) +{ + struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data; + + wm_res->res_state = CAM_ISP_RESOURCE_STATE_UNAVAILABLE; + INIT_LIST_HEAD(&wm_res->list); + + wm_res->start = NULL; + wm_res->stop = NULL; + wm_res->top_half_handler = NULL; + wm_res->bottom_half_handler = NULL; + wm_res->hw_intf = NULL; + + rsrc_data = wm_res->res_priv; + wm_res->res_priv = NULL; + if (!rsrc_data) + return -ENOMEM; + kfree(rsrc_data); + + return 0; +} + +static void cam_vfe_bus_add_wm_to_comp_grp( + struct cam_isp_resource_node *comp_grp, + uint32_t composite_mask) +{ + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = comp_grp->res_priv; + + rsrc_data->composite_mask |= composite_mask; +} + +static void cam_vfe_bus_match_comp_grp( + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_isp_resource_node **comp_grp, + uint32_t comp_grp_local_idx, + uint32_t unique_id) +{ + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL; + struct cam_isp_resource_node *comp_grp_local = NULL; + + list_for_each_entry(comp_grp_local, + &ver2_bus_priv->used_comp_grp, list) { + rsrc_data = comp_grp_local->res_priv; + if (rsrc_data->comp_grp_local_idx == comp_grp_local_idx && + rsrc_data->unique_id == unique_id) { + /* Match found */ + *comp_grp = comp_grp_local; + return; + } + } + + *comp_grp = NULL; +} + +static int cam_vfe_bus_acquire_comp_grp( + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_isp_out_port_info *out_port_info, + void *tasklet, + void *ctx, + uint32_t unique_id, + uint32_t is_dual, + uint32_t is_master, + enum cam_vfe_bus_ver2_vfe_core_id dual_slave_core, + struct cam_isp_resource_node **comp_grp) +{ + int rc = 0; + uint32_t bus_comp_grp_id; + struct cam_isp_resource_node *comp_grp_local = NULL; + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL; + + bus_comp_grp_id = cam_vfe_bus_comp_grp_id_convert( + out_port_info->comp_grp_id); + /* Perform match only if there is valid comp grp request */ + if (out_port_info->comp_grp_id != CAM_ISP_RES_COMP_GROUP_NONE) { + /* Check if matching comp_grp already acquired */ + cam_vfe_bus_match_comp_grp(ver2_bus_priv, &comp_grp_local, + bus_comp_grp_id, unique_id); + } + + if (!comp_grp_local) { + /* First find a free group */ + if (is_dual) { + CAM_DBG(CAM_ISP, "Acquire dual comp group"); + if (list_empty(&ver2_bus_priv->free_dual_comp_grp)) { + CAM_ERR(CAM_ISP, "No Free Composite Group"); + return -ENODEV; + } + comp_grp_local = list_first_entry( + &ver2_bus_priv->free_dual_comp_grp, + struct cam_isp_resource_node, list); + rsrc_data = comp_grp_local->res_priv; + rc = cam_vfe_bus_ver2_get_intra_client_mask( + dual_slave_core, + comp_grp_local->hw_intf->hw_idx, + &rsrc_data->intra_client_mask); + if (rc) + return rc; + } else { + CAM_DBG(CAM_ISP, "Acquire comp group"); + if (list_empty(&ver2_bus_priv->free_comp_grp)) { + CAM_ERR(CAM_ISP, "No Free Composite Group"); + return -ENODEV; + } + comp_grp_local = list_first_entry( + &ver2_bus_priv->free_comp_grp, + struct cam_isp_resource_node, list); + rsrc_data = comp_grp_local->res_priv; + } + + list_del(&comp_grp_local->list); + comp_grp_local->tasklet_info = tasklet; + comp_grp_local->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + rsrc_data->is_master = is_master; + rsrc_data->composite_mask = 0; + rsrc_data->unique_id = unique_id; + rsrc_data->comp_grp_local_idx = bus_comp_grp_id; + + if (is_master) + rsrc_data->addr_sync_mode = 0; + else + rsrc_data->addr_sync_mode = 1; + + list_add_tail(&comp_grp_local->list, + &ver2_bus_priv->used_comp_grp); + + } else { + rsrc_data = comp_grp_local->res_priv; + /* Do not support runtime change in composite mask */ + if (comp_grp_local->res_state == + CAM_ISP_RESOURCE_STATE_STREAMING) { + CAM_ERR(CAM_ISP, "Invalid State %d Comp Grp %u", + comp_grp_local->res_state, + rsrc_data->comp_grp_type); + return -EBUSY; + } + } + + CAM_DBG(CAM_ISP, "Comp Grp type %u", rsrc_data->comp_grp_type); + + rsrc_data->ctx = ctx; + rsrc_data->acquire_dev_cnt++; + *comp_grp = comp_grp_local; + + return rc; +} + +static int cam_vfe_bus_release_comp_grp( + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_isp_resource_node *in_comp_grp) +{ + struct cam_isp_resource_node *comp_grp = NULL; + struct cam_vfe_bus_ver2_comp_grp_data *in_rsrc_data = NULL; + int match_found = 0; + + if (!in_comp_grp) { + CAM_ERR(CAM_ISP, "Invalid Params Comp Grp %pK", in_comp_grp); + return -EINVAL; + } + + if (in_comp_grp->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_ERR(CAM_ISP, "Already released Comp Grp"); + return 0; + } + + if (in_comp_grp->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) { + CAM_ERR(CAM_ISP, "Invalid State %d", + in_comp_grp->res_state); + return -EBUSY; + } + + in_rsrc_data = in_comp_grp->res_priv; + CAM_DBG(CAM_ISP, "Comp Grp type %u", in_rsrc_data->comp_grp_type); + + list_for_each_entry(comp_grp, &ver2_bus_priv->used_comp_grp, list) { + if (comp_grp == in_comp_grp) { + match_found = 1; + break; + } + } + + if (!match_found) { + CAM_ERR(CAM_ISP, "Could not find matching Comp Grp type %u", + in_rsrc_data->comp_grp_type); + return -ENODEV; + } + + in_rsrc_data->acquire_dev_cnt--; + if (in_rsrc_data->acquire_dev_cnt == 0) { + list_del(&comp_grp->list); + + in_rsrc_data->unique_id = 0; + in_rsrc_data->comp_grp_local_idx = CAM_VFE_BUS_COMP_GROUP_NONE; + in_rsrc_data->composite_mask = 0; + in_rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX; + in_rsrc_data->addr_sync_mode = 0; + + comp_grp->tasklet_info = NULL; + comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + + if (in_rsrc_data->comp_grp_type >= + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 && + in_rsrc_data->comp_grp_type <= + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) + list_add_tail(&comp_grp->list, + &ver2_bus_priv->free_dual_comp_grp); + else if (in_rsrc_data->comp_grp_type >= + CAM_VFE_BUS_VER2_COMP_GRP_0 && + in_rsrc_data->comp_grp_type <= + CAM_VFE_BUS_VER2_COMP_GRP_5) + list_add_tail(&comp_grp->list, + &ver2_bus_priv->free_comp_grp); + } + + return 0; +} + +static int cam_vfe_bus_start_comp_grp(struct cam_isp_resource_node *comp_grp) +{ + int rc = 0; + uint32_t addr_sync_cfg; + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = + comp_grp->res_priv; + struct cam_vfe_bus_ver2_common_data *common_data = + rsrc_data->common_data; + uint32_t bus_irq_reg_mask[CAM_VFE_BUS_IRQ_MAX] = {0}; + struct cam_irq_bh_api irq_bh_api; + + CAM_DBG(CAM_ISP, "comp group id:%d streaming state:%d", + rsrc_data->comp_grp_type, comp_grp->res_state); + + cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base + + rsrc_data->hw_regs->comp_mask); + if (comp_grp->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) + return 0; + + CAM_DBG(CAM_ISP, "composite_mask is 0x%x", rsrc_data->composite_mask); + CAM_DBG(CAM_ISP, "composite_mask addr 0x%x", + rsrc_data->hw_regs->comp_mask); + + if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) { + int dual_comp_grp = (rsrc_data->comp_grp_type - + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0); + + if (rsrc_data->is_master) { + int intra_client_en = cam_io_r_mb( + common_data->mem_base + + common_data->common_reg->dual_master_comp_cfg); + + /* + * 2 Bits per comp_grp. Hence left shift by + * comp_grp * 2 + */ + intra_client_en |= + (rsrc_data->intra_client_mask << + (dual_comp_grp * 2)); + + cam_io_w_mb(intra_client_en, common_data->mem_base + + common_data->common_reg->dual_master_comp_cfg); + + bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG2] = + (1 << dual_comp_grp); + } + + CAM_DBG(CAM_ISP, "addr_sync_mask addr 0x%x", + rsrc_data->hw_regs->addr_sync_mask); + cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base + + rsrc_data->hw_regs->addr_sync_mask); + + addr_sync_cfg = cam_io_r_mb(common_data->mem_base + + common_data->common_reg->addr_sync_cfg); + addr_sync_cfg |= (rsrc_data->addr_sync_mode << dual_comp_grp); + /* + * 2 Bits per dual_comp_grp. dual_comp_grp stats at bit number + * 8. Hence left shift cdual_comp_grp dual comp_grp * 2 and + * add 8 + */ + addr_sync_cfg |= + (rsrc_data->intra_client_mask << + ((dual_comp_grp * 2) + + CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT)); + cam_io_w_mb(addr_sync_cfg, common_data->mem_base + + common_data->common_reg->addr_sync_cfg); + + common_data->addr_no_sync &= ~(rsrc_data->composite_mask); + cam_io_w_mb(common_data->addr_no_sync, common_data->mem_base + + common_data->common_reg->addr_sync_no_sync); + CAM_DBG(CAM_ISP, "addr_sync_cfg: 0x%x addr_no_sync_cfg: 0x%x", + addr_sync_cfg, common_data->addr_no_sync); + } else { + /* IRQ bits for COMP GRP start at 5. So add 5 to the shift */ + bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG0] = + (1 << (rsrc_data->comp_grp_type + 5)); + } + + /* + * For Dual composite subscribe IRQ only for master + * For regular composite, subscribe IRQ always + */ + CAM_DBG(CAM_ISP, "Subscribe COMP_GRP%d IRQ", rsrc_data->comp_grp_type); + if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) && + (rsrc_data->is_master)) || + (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) { + irq_bh_api.bottom_half_enqueue_func = cam_tasklet_enqueue_cmd; + irq_bh_api.get_bh_payload_func = cam_tasklet_get_cmd; + irq_bh_api.put_bh_payload_func = cam_tasklet_put_cmd; + comp_grp->irq_handle = cam_irq_controller_subscribe_irq( + common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1, + bus_irq_reg_mask, comp_grp, + comp_grp->top_half_handler, + cam_ife_mgr_do_tasklet_buf_done, + comp_grp->tasklet_info, &irq_bh_api); + if (comp_grp->irq_handle < 0) { + CAM_ERR(CAM_ISP, "Subscribe IRQ failed for comp_grp %d", + rsrc_data->comp_grp_type); + return -EFAULT; + } + } + comp_grp->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + return rc; +} + +static int cam_vfe_bus_stop_comp_grp(struct cam_isp_resource_node *comp_grp) +{ + int rc = 0; + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = + comp_grp->res_priv; + struct cam_vfe_bus_ver2_common_data *common_data = + rsrc_data->common_data; + + /* Unsubscribe IRQ */ + if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) && + (rsrc_data->is_master)) || + (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) { + rc = cam_irq_controller_unsubscribe_irq( + common_data->bus_irq_controller, + comp_grp->irq_handle); + } + comp_grp->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + return rc; +} + +static int cam_vfe_bus_handle_comp_done_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int32_t rc; + int i; + struct cam_isp_resource_node *comp_grp = NULL; + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL; + struct cam_vfe_bus_irq_evt_payload *evt_payload; + + comp_grp = th_payload->handler_priv; + if (!comp_grp) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "No resource"); + return -ENODEV; + } + + rsrc_data = comp_grp->res_priv; + + CAM_DBG(CAM_ISP, "IRQ status_0 = 0x%x", th_payload->evt_status_arr[0]); + CAM_DBG(CAM_ISP, "IRQ status_1 = 0x%x", th_payload->evt_status_arr[1]); + CAM_DBG(CAM_ISP, "IRQ status_2 = 0x%x", th_payload->evt_status_arr[2]); + + rc = cam_vfe_bus_get_evt_payload(rsrc_data->common_data, &evt_payload); + if (rc) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "No tasklet_cmd is free in queue"); + CAM_ERR_RATE_LIMIT(CAM_ISP, + "IRQ status_0 = 0x%x status_1 = 0x%x status_2 = 0x%x", + th_payload->evt_status_arr[0], + th_payload->evt_status_arr[1], + th_payload->evt_status_arr[2]); + + return rc; + } + + cam_isp_hw_get_timestamp(&evt_payload->ts); + + evt_payload->ctx = rsrc_data->ctx; + evt_payload->core_index = rsrc_data->common_data->core_index; + evt_payload->evt_id = evt_id; + + for (i = 0; i < th_payload->num_registers; i++) + evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i]; + + th_payload->evt_payload_priv = evt_payload; + + CAM_DBG(CAM_ISP, "Exit"); + return rc; +} + +static int cam_vfe_bus_handle_comp_done_bottom_half( + void *handler_priv, + void *evt_payload_priv) +{ + int rc = CAM_VFE_IRQ_STATUS_ERR; + struct cam_isp_resource_node *comp_grp = handler_priv; + struct cam_vfe_bus_irq_evt_payload *evt_payload = evt_payload_priv; + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = comp_grp->res_priv; + uint32_t *cam_ife_irq_regs; + uint32_t status_reg; + uint32_t comp_err_reg; + uint32_t comp_grp_id; + + CAM_DBG(CAM_ISP, "comp grp type %d", rsrc_data->comp_grp_type); + + if (!evt_payload) + return rc; + + cam_ife_irq_regs = evt_payload->irq_reg_val; + + switch (rsrc_data->comp_grp_type) { + case CAM_VFE_BUS_VER2_COMP_GRP_0: + case CAM_VFE_BUS_VER2_COMP_GRP_1: + case CAM_VFE_BUS_VER2_COMP_GRP_2: + case CAM_VFE_BUS_VER2_COMP_GRP_3: + case CAM_VFE_BUS_VER2_COMP_GRP_4: + case CAM_VFE_BUS_VER2_COMP_GRP_5: + comp_grp_id = (rsrc_data->comp_grp_type - + CAM_VFE_BUS_VER2_COMP_GRP_0); + + /* Check for Regular composite error */ + status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0]; + + comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_COMP_ERR]; + if ((status_reg & BIT(11)) && + (comp_err_reg & rsrc_data->composite_mask)) { + /* Check for Regular composite error */ + rc = CAM_VFE_IRQ_STATUS_ERR_COMP; + break; + } + + comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_COMP_OWRT]; + /* Check for Regular composite Overwrite */ + if ((status_reg & BIT(12)) && + (comp_err_reg & rsrc_data->composite_mask)) { + rc = CAM_VFE_IRQ_STATUS_COMP_OWRT; + break; + } + + /* Regular Composite SUCCESS */ + if (status_reg & BIT(comp_grp_id + 5)) { + rsrc_data->irq_trigger_cnt++; + if (rsrc_data->irq_trigger_cnt == + rsrc_data->acquire_dev_cnt) { + cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0] &= + ~BIT(comp_grp_id + 5); + rsrc_data->irq_trigger_cnt = 0; + } + rc = CAM_VFE_IRQ_STATUS_SUCCESS; + } + + CAM_DBG(CAM_ISP, "status reg = 0x%x, bit index = %d rc %d", + status_reg, (comp_grp_id + 5), rc); + break; + + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0: + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1: + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2: + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3: + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4: + case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5: + comp_grp_id = (rsrc_data->comp_grp_type - + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0); + + /* Check for DUAL composite error */ + status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2]; + + comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_DUAL_COMP_ERR]; + if ((status_reg & BIT(6)) && + (comp_err_reg & rsrc_data->composite_mask)) { + /* Check for DUAL composite error */ + rc = CAM_VFE_IRQ_STATUS_ERR_COMP; + break; + } + + /* Check for Dual composite Overwrite */ + comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_DUAL_COMP_OWRT]; + if ((status_reg & BIT(7)) && + (comp_err_reg & rsrc_data->composite_mask)) { + rc = CAM_VFE_IRQ_STATUS_COMP_OWRT; + break; + } + + /* DUAL Composite SUCCESS */ + if (status_reg & BIT(comp_grp_id)) { + rsrc_data->irq_trigger_cnt++; + if (rsrc_data->irq_trigger_cnt == + rsrc_data->acquire_dev_cnt) { + cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2] &= + ~BIT(comp_grp_id); + rsrc_data->irq_trigger_cnt = 0; + } + rc = CAM_VFE_IRQ_STATUS_SUCCESS; + } + + break; + default: + rc = CAM_VFE_IRQ_STATUS_ERR; + CAM_ERR(CAM_ISP, "Invalid comp_grp_type %u", + rsrc_data->comp_grp_type); + break; + } + + if (rc == CAM_VFE_IRQ_STATUS_SUCCESS) + cam_vfe_bus_put_evt_payload(rsrc_data->common_data, + &evt_payload); + + return rc; +} + +static int cam_vfe_bus_init_comp_grp(uint32_t index, + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_vfe_bus_ver2_hw_info *ver2_hw_info, + struct cam_isp_resource_node *comp_grp) +{ + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL; + + rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_comp_grp_data), + GFP_KERNEL); + if (!rsrc_data) + return -ENOMEM; + + comp_grp->res_priv = rsrc_data; + + comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + INIT_LIST_HEAD(&comp_grp->list); + + rsrc_data->comp_grp_type = index; + rsrc_data->common_data = &ver2_bus_priv->common_data; + rsrc_data->hw_regs = &ver2_hw_info->comp_grp_reg[index]; + rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX; + + if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 && + rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) + list_add_tail(&comp_grp->list, + &ver2_bus_priv->free_dual_comp_grp); + else if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 + && rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5) + list_add_tail(&comp_grp->list, &ver2_bus_priv->free_comp_grp); + + comp_grp->start = cam_vfe_bus_start_comp_grp; + comp_grp->stop = cam_vfe_bus_stop_comp_grp; + comp_grp->top_half_handler = cam_vfe_bus_handle_comp_done_top_half; + comp_grp->bottom_half_handler = + cam_vfe_bus_handle_comp_done_bottom_half; + comp_grp->hw_intf = ver2_bus_priv->common_data.hw_intf; + + return 0; +} + +static int cam_vfe_bus_deinit_comp_grp( + struct cam_isp_resource_node *comp_grp) +{ + struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = + comp_grp->res_priv; + + comp_grp->start = NULL; + comp_grp->stop = NULL; + comp_grp->top_half_handler = NULL; + comp_grp->bottom_half_handler = NULL; + comp_grp->hw_intf = NULL; + + list_del_init(&comp_grp->list); + comp_grp->res_state = CAM_ISP_RESOURCE_STATE_UNAVAILABLE; + + comp_grp->res_priv = NULL; + + if (!rsrc_data) { + CAM_ERR(CAM_ISP, "comp_grp_priv is NULL"); + return -ENODEV; + } + kfree(rsrc_data); + + return 0; +} + +static int cam_vfe_bus_get_secure_mode(void *priv, void *cmd_args, + uint32_t arg_size) +{ + bool *mode = cmd_args; + struct cam_isp_resource_node *res = + (struct cam_isp_resource_node *) priv; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = + (struct cam_vfe_bus_ver2_vfe_out_data *)res->res_priv; + + *mode = + (rsrc_data->secure_mode == CAM_SECURE_MODE_SECURE) ? + true : false; + + return 0; +} + +static int cam_vfe_bus_acquire_vfe_out(void *bus_priv, void *acquire_args, + uint32_t args_size) +{ + int rc = -ENODEV; + int i; + enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id; + uint32_t format; + int num_wm; + uint32_t subscribe_irq; + uint32_t client_done_mask; + struct cam_vfe_bus_ver2_priv *ver2_bus_priv = bus_priv; + struct cam_vfe_acquire_args *acq_args = acquire_args; + struct cam_vfe_hw_vfe_out_acquire_args *out_acquire_args; + struct cam_isp_resource_node *rsrc_node = NULL; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL; + uint32_t secure_caps = 0, mode; + + if (!bus_priv || !acquire_args) { + CAM_ERR(CAM_ISP, "Invalid Param"); + return -EINVAL; + } + + out_acquire_args = &acq_args->vfe_out; + format = out_acquire_args->out_port_info->format; + + CAM_DBG(CAM_ISP, "Acquiring resource type 0x%x", + out_acquire_args->out_port_info->res_type); + + vfe_out_res_id = cam_vfe_bus_get_out_res_id( + out_acquire_args->out_port_info->res_type); + if (vfe_out_res_id == CAM_VFE_BUS_VER2_VFE_OUT_MAX) + return -ENODEV; + + num_wm = cam_vfe_bus_get_num_wm(vfe_out_res_id, format); + if (num_wm < 1) + return -EINVAL; + + rsrc_node = &ver2_bus_priv->vfe_out[vfe_out_res_id]; + if (rsrc_node->res_state != CAM_ISP_RESOURCE_STATE_AVAILABLE) { + CAM_ERR(CAM_ISP, "Resource not available: Res_id %d state:%d", + vfe_out_res_id, rsrc_node->res_state); + return -EBUSY; + } + + rsrc_data = rsrc_node->res_priv; + secure_caps = cam_vfe_bus_can_be_secure( + rsrc_data->out_type); + mode = out_acquire_args->out_port_info->secure_mode; + mutex_lock(&rsrc_data->common_data->bus_mutex); + if (secure_caps) { + if (!rsrc_data->common_data->num_sec_out) { + rsrc_data->secure_mode = mode; + rsrc_data->common_data->secure_mode = mode; + } else { + if (mode == rsrc_data->common_data->secure_mode) { + rsrc_data->secure_mode = + rsrc_data->common_data->secure_mode; + } else { + rc = -EINVAL; + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Mismatch: Acquire mode[%d], drvr mode[%d]", + rsrc_data->common_data->secure_mode, + mode); + mutex_unlock( + &rsrc_data->common_data->bus_mutex); + return -EINVAL; + } + } + rsrc_data->common_data->num_sec_out++; + } + mutex_unlock(&rsrc_data->common_data->bus_mutex); + + rsrc_data->num_wm = num_wm; + rsrc_node->res_id = out_acquire_args->out_port_info->res_type; + rsrc_node->tasklet_info = acq_args->tasklet; + rsrc_node->cdm_ops = out_acquire_args->cdm_ops; + rsrc_data->cdm_util_ops = out_acquire_args->cdm_ops; + + /* Reserve Composite Group */ + if (num_wm > 1 || (out_acquire_args->is_dual) || + (out_acquire_args->out_port_info->comp_grp_id > + CAM_ISP_RES_COMP_GROUP_NONE && + out_acquire_args->out_port_info->comp_grp_id < + CAM_ISP_RES_COMP_GROUP_ID_MAX)) { + + rc = cam_vfe_bus_acquire_comp_grp(ver2_bus_priv, + out_acquire_args->out_port_info, + acq_args->tasklet, + out_acquire_args->ctx, + out_acquire_args->unique_id, + out_acquire_args->is_dual, + out_acquire_args->is_master, + out_acquire_args->dual_slave_core, + &rsrc_data->comp_grp); + if (rc) { + CAM_ERR(CAM_ISP, + "VFE%d Comp_Grp acquire fail for Out %d rc=%d", + rsrc_data->common_data->core_index, + vfe_out_res_id, rc); + return rc; + } + + subscribe_irq = 0; + } else { + subscribe_irq = 1; + } + + /* Reserve WM */ + for (i = 0; i < num_wm; i++) { + rc = cam_vfe_bus_acquire_wm(ver2_bus_priv, + out_acquire_args->out_port_info, + acq_args->tasklet, + out_acquire_args->ctx, + vfe_out_res_id, + i, + subscribe_irq, + &rsrc_data->wm_res[i], + &client_done_mask, + out_acquire_args->is_dual); + if (rc) { + CAM_ERR(CAM_ISP, + "VFE%d WM acquire failed for Out %d rc=%d", + rsrc_data->common_data->core_index, + vfe_out_res_id, rc); + goto release_wm; + } + + if (rsrc_data->comp_grp) + cam_vfe_bus_add_wm_to_comp_grp(rsrc_data->comp_grp, + client_done_mask); + } + + rsrc_node->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + out_acquire_args->rsrc_node = rsrc_node; + + CAM_DBG(CAM_ISP, "Acquire successful"); + return rc; + +release_wm: + for (i--; i >= 0; i--) + cam_vfe_bus_release_wm(ver2_bus_priv, rsrc_data->wm_res[i]); + + cam_vfe_bus_release_comp_grp(ver2_bus_priv, + rsrc_data->comp_grp); + + return rc; +} + +static int cam_vfe_bus_release_vfe_out(void *bus_priv, void *release_args, + uint32_t args_size) +{ + uint32_t i; + struct cam_isp_resource_node *vfe_out = NULL; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL; + uint32_t secure_caps = 0; + + if (!bus_priv || !release_args) { + CAM_ERR(CAM_ISP, "Invalid input bus_priv %pK release_args %pK", + bus_priv, release_args); + return -EINVAL; + } + + vfe_out = release_args; + rsrc_data = vfe_out->res_priv; + + if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR(CAM_ISP, "Invalid resource state:%d", + vfe_out->res_state); + } + + for (i = 0; i < rsrc_data->num_wm; i++) + cam_vfe_bus_release_wm(bus_priv, rsrc_data->wm_res[i]); + rsrc_data->num_wm = 0; + + if (rsrc_data->comp_grp) + cam_vfe_bus_release_comp_grp(bus_priv, rsrc_data->comp_grp); + rsrc_data->comp_grp = NULL; + + vfe_out->tasklet_info = NULL; + vfe_out->cdm_ops = NULL; + rsrc_data->cdm_util_ops = NULL; + + secure_caps = cam_vfe_bus_can_be_secure(rsrc_data->out_type); + mutex_lock(&rsrc_data->common_data->bus_mutex); + if (secure_caps) { + if (rsrc_data->secure_mode == + rsrc_data->common_data->secure_mode) { + rsrc_data->common_data->num_sec_out--; + rsrc_data->secure_mode = + CAM_SECURE_MODE_NON_SECURE; + } else { + /* + * The validity of the mode is properly + * checked while acquiring the output port. + * not expected to reach here, unless there is + * some corruption. + */ + CAM_ERR(CAM_ISP, "driver[%d],resource[%d] mismatch", + rsrc_data->common_data->secure_mode, + rsrc_data->secure_mode); + } + + if (!rsrc_data->common_data->num_sec_out) + rsrc_data->common_data->secure_mode = + CAM_SECURE_MODE_NON_SECURE; + } + mutex_unlock(&rsrc_data->common_data->bus_mutex); + + if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) + vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + + return 0; +} + +static int cam_vfe_bus_start_vfe_out( + struct cam_isp_resource_node *vfe_out) +{ + int rc = 0, i; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL; + struct cam_vfe_bus_ver2_common_data *common_data = NULL; + + if (!vfe_out) { + CAM_ERR(CAM_ISP, "Invalid input"); + return -EINVAL; + } + + rsrc_data = vfe_out->res_priv; + common_data = rsrc_data->common_data; + + CAM_DBG(CAM_ISP, "Start resource index %d", rsrc_data->out_type); + + if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR(CAM_ISP, "Invalid resource state:%d", + vfe_out->res_state); + return -EACCES; + } + + for (i = 0; i < rsrc_data->num_wm; i++) + rc = cam_vfe_bus_start_wm(rsrc_data->wm_res[i]); + + if (rsrc_data->comp_grp) + rc = cam_vfe_bus_start_comp_grp(rsrc_data->comp_grp); + + vfe_out->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + return rc; +} + +static int cam_vfe_bus_stop_vfe_out( + struct cam_isp_resource_node *vfe_out) +{ + int rc = 0, i; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL; + + if (!vfe_out) { + CAM_ERR(CAM_ISP, "Invalid input"); + return -EINVAL; + } + + rsrc_data = vfe_out->res_priv; + + if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE || + vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_DBG(CAM_ISP, "vfe_out res_state is %d", vfe_out->res_state); + return rc; + } + + if (rsrc_data->comp_grp) + rc = cam_vfe_bus_stop_comp_grp(rsrc_data->comp_grp); + + for (i = 0; i < rsrc_data->num_wm; i++) + rc = cam_vfe_bus_stop_wm(rsrc_data->wm_res[i]); + + vfe_out->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + return rc; +} + +static int cam_vfe_bus_handle_vfe_out_done_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + return -EPERM; +} + +static int cam_vfe_bus_handle_vfe_out_done_bottom_half( + void *handler_priv, + void *evt_payload_priv) +{ + int rc = -EINVAL; + struct cam_isp_resource_node *vfe_out = handler_priv; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv; + + CAM_DBG(CAM_ISP, "vfe_out %d", rsrc_data->out_type); + /* + * If this resource has Composite Group then we only handle + * Composite done. We acquire Composite if number of WM > 1. + * So Else case is only one individual buf_done = WM[0]. + */ + if (rsrc_data->comp_grp) { + rc = rsrc_data->comp_grp->bottom_half_handler( + rsrc_data->comp_grp, evt_payload_priv); + } else { + rc = rsrc_data->wm_res[0]->bottom_half_handler( + rsrc_data->wm_res[0], evt_payload_priv); + } + + return rc; +} + +static int cam_vfe_bus_init_vfe_out_resource(uint32_t index, + struct cam_vfe_bus_ver2_priv *ver2_bus_priv, + struct cam_vfe_bus_ver2_hw_info *ver2_hw_info) +{ + struct cam_isp_resource_node *vfe_out = NULL; + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL; + int rc = 0; + int32_t vfe_out_type = + ver2_hw_info->vfe_out_hw_info[index].vfe_out_type; + + if (vfe_out_type < 0 || + vfe_out_type >= CAM_VFE_BUS_VER2_VFE_OUT_MAX) { + CAM_ERR(CAM_ISP, "Init VFE Out failed, Invalid type=%d", + vfe_out_type); + return -EINVAL; + } + + vfe_out = &ver2_bus_priv->vfe_out[vfe_out_type]; + if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_UNAVAILABLE || + vfe_out->res_priv) { + CAM_ERR(CAM_ISP, + "Error. Looks like same resource is init again"); + return -EFAULT; + } + + rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_vfe_out_data), + GFP_KERNEL); + if (!rsrc_data) { + rc = -ENOMEM; + return rc; + } + + vfe_out->res_priv = rsrc_data; + + vfe_out->res_type = CAM_ISP_RESOURCE_VFE_OUT; + vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + INIT_LIST_HEAD(&vfe_out->list); + + rsrc_data->out_type = + ver2_hw_info->vfe_out_hw_info[index].vfe_out_type; + rsrc_data->common_data = &ver2_bus_priv->common_data; + rsrc_data->max_width = + ver2_hw_info->vfe_out_hw_info[index].max_width; + rsrc_data->max_height = + ver2_hw_info->vfe_out_hw_info[index].max_height; + rsrc_data->secure_mode = CAM_SECURE_MODE_NON_SECURE; + + vfe_out->start = cam_vfe_bus_start_vfe_out; + vfe_out->stop = cam_vfe_bus_stop_vfe_out; + vfe_out->top_half_handler = cam_vfe_bus_handle_vfe_out_done_top_half; + vfe_out->bottom_half_handler = + cam_vfe_bus_handle_vfe_out_done_bottom_half; + vfe_out->process_cmd = cam_vfe_bus_process_cmd; + vfe_out->hw_intf = ver2_bus_priv->common_data.hw_intf; + + return 0; +} + +static int cam_vfe_bus_deinit_vfe_out_resource( + struct cam_isp_resource_node *vfe_out) +{ + struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv; + + if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_UNAVAILABLE) { + /* + * This is not error. It can happen if the resource is + * never supported in the HW. + */ + CAM_DBG(CAM_ISP, "HW%d Res %d already deinitialized"); + return 0; + } + + vfe_out->start = NULL; + vfe_out->stop = NULL; + vfe_out->top_half_handler = NULL; + vfe_out->bottom_half_handler = NULL; + vfe_out->hw_intf = NULL; + + vfe_out->res_state = CAM_ISP_RESOURCE_STATE_UNAVAILABLE; + INIT_LIST_HEAD(&vfe_out->list); + vfe_out->res_priv = NULL; + + if (!rsrc_data) + return -ENOMEM; + kfree(rsrc_data); + + return 0; +} + +static int cam_vfe_bus_ver2_handle_irq(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + struct cam_vfe_bus_ver2_priv *bus_priv; + int rc = 0; + + bus_priv = th_payload->handler_priv; + CAM_DBG(CAM_ISP, "Enter"); + rc = cam_irq_controller_handle_irq(evt_id, + bus_priv->common_data.bus_irq_controller); + return (IRQ_HANDLED == rc) ? 0 : -EINVAL; +} + +static int cam_vfe_bus_error_irq_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + int i = 0; + struct cam_vfe_bus_ver2_priv *bus_priv = + th_payload->handler_priv; + + CAM_ERR_RATE_LIMIT(CAM_ISP, "Bus Err IRQ"); + for (i = 0; i < th_payload->num_registers; i++) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "vfe:%d: IRQ_Status%d: 0x%x", + bus_priv->common_data.core_index, i, + th_payload->evt_status_arr[i]); + } + cam_irq_controller_disable_irq(bus_priv->common_data.bus_irq_controller, + bus_priv->error_irq_handle); + + /* Returning error stops from enqueuing bottom half */ + return -EFAULT; +} + +static int cam_vfe_bus_update_wm(void *priv, void *cmd_args, + uint32_t arg_size) +{ + struct cam_vfe_bus_ver2_priv *bus_priv; + struct cam_isp_hw_get_cmd_update *update_buf; + struct cam_buf_io_cfg *io_cfg; + struct cam_vfe_bus_ver2_vfe_out_data *vfe_out_data = NULL; + struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL; + uint32_t *reg_val_pair; + uint32_t i, j, size = 0; + uint32_t frame_inc = 0, ubwc_bw_limit = 0, camera_hw_version, val; + int rc = 0; + + bus_priv = (struct cam_vfe_bus_ver2_priv *) priv; + update_buf = (struct cam_isp_hw_get_cmd_update *) cmd_args; + + vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *) + update_buf->res->res_priv; + + if (!vfe_out_data || !vfe_out_data->cdm_util_ops) { + CAM_ERR(CAM_ISP, "Failed! Invalid data"); + return -EINVAL; + } + + if (update_buf->wm_update->num_buf != vfe_out_data->num_wm) { + CAM_ERR(CAM_ISP, + "Failed! Invalid number buffers:%d required:%d", + update_buf->wm_update->num_buf, vfe_out_data->num_wm); + return -EINVAL; + } + + reg_val_pair = &vfe_out_data->common_data->io_buf_update[0]; + io_cfg = update_buf->wm_update->io_cfg; + + for (i = 0, j = 0; i < vfe_out_data->num_wm; i++) { + if (j >= (MAX_REG_VAL_PAIR_SIZE - MAX_BUF_UPDATE_REG_NUM * 2)) { + CAM_ERR(CAM_ISP, + "reg_val_pair %d exceeds the array limit %lu", + j, MAX_REG_VAL_PAIR_SIZE); + return -ENOMEM; + } + + wm_data = vfe_out_data->wm_res[i]->res_priv; + + /* update width register */ + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->buffer_width_cfg, + wm_data->width); + CAM_DBG(CAM_ISP, "WM %d image width 0x%x", + wm_data->index, reg_val_pair[j-1]); + + /* For initial configuration program all bus registers */ + val = io_cfg->planes[i].plane_stride; + CAM_DBG(CAM_ISP, "before stride %d", val); + val = ALIGNUP(val, 16); + if (val != io_cfg->planes[i].plane_stride && + val != wm_data->stride) + CAM_WARN(CAM_ISP, + "Warning stride %u expected %u", + io_cfg->planes[i].plane_stride, + val); + + if ((wm_data->stride != val || + !wm_data->init_cfg_done) && (wm_data->index >= 3)) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->stride, + io_cfg->planes[i].plane_stride); + wm_data->stride = val; + CAM_DBG(CAM_ISP, "WM %d image stride 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->en_ubwc) { + if (!wm_data->hw_regs->ubwc_regs) { + CAM_ERR(CAM_ISP, + "No UBWC register to configure."); + return -EINVAL; + } + if (wm_data->packer_cfg != + io_cfg->planes[i].packer_config || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->packer_cfg, + io_cfg->planes[i].packer_config); + wm_data->packer_cfg = + io_cfg->planes[i].packer_config; + CAM_DBG(CAM_ISP, "WM %d packer cfg 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->is_dual) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->tile_cfg, + wm_data->tile_cfg); + } else if ((wm_data->tile_cfg != + io_cfg->planes[i].tile_config) + || !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->tile_cfg, + io_cfg->planes[i].tile_config); + wm_data->tile_cfg = + io_cfg->planes[i].tile_config; + CAM_DBG(CAM_ISP, "WM %d tile cfg 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->is_dual) { + if ((wm_data->h_init != wm_data->offset) || + !wm_data->init_cfg_done) { + /* + * For dual ife h init value need to + * take from offset. Striping config + * update offset value. + */ + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, + j, + wm_data->hw_regs->ubwc_regs-> + h_init, wm_data->offset); + wm_data->h_init = wm_data->offset; + } + } else if (wm_data->h_init != + io_cfg->planes[i].h_init || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->h_init, + io_cfg->planes[i].h_init); + wm_data->h_init = io_cfg->planes[i].h_init; + CAM_DBG(CAM_ISP, "WM %d h_init 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->v_init != io_cfg->planes[i].v_init || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->v_init, + io_cfg->planes[i].v_init); + wm_data->v_init = io_cfg->planes[i].v_init; + CAM_DBG(CAM_ISP, "WM %d v_init 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->ubwc_meta_stride != + io_cfg->planes[i].meta_stride || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs-> + meta_stride, + io_cfg->planes[i].meta_stride); + wm_data->ubwc_meta_stride = + io_cfg->planes[i].meta_stride; + CAM_DBG(CAM_ISP, "WM %d meta stride 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->ubwc_mode_cfg != + io_cfg->planes[i].mode_config || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->mode_cfg, + io_cfg->planes[i].mode_config); + wm_data->ubwc_mode_cfg = + io_cfg->planes[i].mode_config; + CAM_DBG(CAM_ISP, "WM %d ubwc mode cfg 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + if (wm_data->ubwc_meta_offset != + io_cfg->planes[i].meta_offset || + !wm_data->init_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs-> + meta_offset, + io_cfg->planes[i].meta_offset); + wm_data->ubwc_meta_offset = + io_cfg->planes[i].meta_offset; + CAM_DBG(CAM_ISP, "WM %d ubwc meta offset 0x%x", + wm_data->index, reg_val_pair[j-1]); + } + + /* UBWC meta address */ + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->meta_addr, + update_buf->wm_update->image_buf[i]); + CAM_DBG(CAM_ISP, "WM %d ubwc meta addr 0x%llx", + wm_data->index, + update_buf->wm_update->image_buf[i]); + + /* Enable UBWC bandwidth limit if required */ + rc = cam_cpas_get_cpas_hw_version(&camera_hw_version); + if (camera_hw_version == CAM_CPAS_TITAN_170_V110 + && !rc) { + switch (wm_data->format) { + case CAM_FORMAT_UBWC_TP10: + ubwc_bw_limit = 0x8 | BIT(0); + break; + case CAM_FORMAT_UBWC_NV12_4R: + ubwc_bw_limit = 0xB | BIT(0); + break; + default: + ubwc_bw_limit = 0; + break; + } + } + + if (ubwc_bw_limit) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->ubwc_regs->bw_limit, + ubwc_bw_limit); + CAM_DBG(CAM_ISP, "WM %d ubwc bw limit 0x%x", + wm_data->index, ubwc_bw_limit); + } + } + + /* WM Image address */ + if (wm_data->en_ubwc) + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->image_addr, + (update_buf->wm_update->image_buf[i] + + io_cfg->planes[i].meta_size)); + else + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->image_addr, + update_buf->wm_update->image_buf[i] + + wm_data->offset); + CAM_DBG(CAM_ISP, "WM %d image address 0x%x", + wm_data->index, reg_val_pair[j-1]); + + if (wm_data->en_ubwc) { + frame_inc = ALIGNUP(io_cfg->planes[i].plane_stride * + io_cfg->planes[i].slice_height, 4096); + frame_inc += io_cfg->planes[i].meta_size; + CAM_DBG(CAM_ISP, + "WM %d frm %d: ht: %d stride %d meta: %d", + wm_data->index, frame_inc, + io_cfg->planes[i].slice_height, + io_cfg->planes[i].plane_stride, + io_cfg->planes[i].meta_size); + } else { + frame_inc = io_cfg->planes[i].plane_stride * + io_cfg->planes[i].slice_height; + } + + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->frame_inc, frame_inc); + CAM_DBG(CAM_ISP, "WM %d frame_inc %d", + wm_data->index, reg_val_pair[j-1]); + + + /* enable the WM */ + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->cfg, + wm_data->en_cfg); + + /* set initial configuration done */ + if (!wm_data->init_cfg_done) + wm_data->init_cfg_done = true; + } + + size = vfe_out_data->cdm_util_ops->cdm_required_size_reg_random(j/2); + + /* cdm util returns dwords, need to convert to bytes */ + if ((size * 4) > update_buf->cmd.size) { + CAM_ERR(CAM_ISP, + "Failed! Buf size:%d insufficient, expected size:%d", + update_buf->cmd.size, size); + return -ENOMEM; + } + + vfe_out_data->cdm_util_ops->cdm_write_regrandom( + update_buf->cmd.cmd_buf_addr, j/2, reg_val_pair); + + /* cdm util returns dwords, need to convert to bytes */ + update_buf->cmd.used_bytes = size * 4; + + return 0; +} + +static int cam_vfe_bus_update_hfr(void *priv, void *cmd_args, + uint32_t arg_size) +{ + struct cam_vfe_bus_ver2_priv *bus_priv; + struct cam_isp_hw_get_cmd_update *update_hfr; + struct cam_vfe_bus_ver2_vfe_out_data *vfe_out_data = NULL; + struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL; + struct cam_isp_port_hfr_config *hfr_cfg = NULL; + uint32_t *reg_val_pair; + uint32_t i, j, size = 0; + + bus_priv = (struct cam_vfe_bus_ver2_priv *) priv; + update_hfr = (struct cam_isp_hw_get_cmd_update *) cmd_args; + + vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *) + update_hfr->res->res_priv; + + if (!vfe_out_data || !vfe_out_data->cdm_util_ops) { + CAM_ERR(CAM_ISP, "Failed! Invalid data"); + return -EINVAL; + } + + reg_val_pair = &vfe_out_data->common_data->io_buf_update[0]; + hfr_cfg = update_hfr->hfr_update; + + for (i = 0, j = 0; i < vfe_out_data->num_wm; i++) { + if (j >= (MAX_REG_VAL_PAIR_SIZE - MAX_BUF_UPDATE_REG_NUM * 2)) { + CAM_ERR(CAM_ISP, + "reg_val_pair %d exceeds the array limit %lu", + j, MAX_REG_VAL_PAIR_SIZE); + return -ENOMEM; + } + + wm_data = vfe_out_data->wm_res[i]->res_priv; + + if ((wm_data->framedrop_pattern != + hfr_cfg->framedrop_pattern) || + !wm_data->hfr_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->framedrop_pattern, + hfr_cfg->framedrop_pattern); + wm_data->framedrop_pattern = hfr_cfg->framedrop_pattern; + CAM_DBG(CAM_ISP, "WM %d framedrop pattern 0x%x", + wm_data->index, wm_data->framedrop_pattern); + } + + if (wm_data->framedrop_period != hfr_cfg->framedrop_period || + !wm_data->hfr_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->framedrop_period, + hfr_cfg->framedrop_period); + wm_data->framedrop_period = hfr_cfg->framedrop_period; + CAM_DBG(CAM_ISP, "WM %d framedrop period 0x%x", + wm_data->index, wm_data->framedrop_period); + } + + if (wm_data->irq_subsample_period != hfr_cfg->subsample_period + || !wm_data->hfr_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->irq_subsample_period, + hfr_cfg->subsample_period); + wm_data->irq_subsample_period = + hfr_cfg->subsample_period; + CAM_DBG(CAM_ISP, "WM %d irq subsample period 0x%x", + wm_data->index, wm_data->irq_subsample_period); + } + + if (wm_data->irq_subsample_pattern != hfr_cfg->subsample_pattern + || !wm_data->hfr_cfg_done) { + CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j, + wm_data->hw_regs->irq_subsample_pattern, + hfr_cfg->subsample_pattern); + wm_data->irq_subsample_pattern = + hfr_cfg->subsample_pattern; + CAM_DBG(CAM_ISP, "WM %d irq subsample pattern 0x%x", + wm_data->index, wm_data->irq_subsample_pattern); + } + + /* set initial configuration done */ + if (!wm_data->hfr_cfg_done) + wm_data->hfr_cfg_done = true; + } + + size = vfe_out_data->cdm_util_ops->cdm_required_size_reg_random(j/2); + + /* cdm util returns dwords, need to convert to bytes */ + if ((size * 4) > update_hfr->cmd.size) { + CAM_ERR(CAM_ISP, + "Failed! Buf size:%d insufficient, expected size:%d", + update_hfr->cmd.size, size); + return -ENOMEM; + } + + vfe_out_data->cdm_util_ops->cdm_write_regrandom( + update_hfr->cmd.cmd_buf_addr, j/2, reg_val_pair); + + /* cdm util returns dwords, need to convert to bytes */ + update_hfr->cmd.used_bytes = size * 4; + + return 0; +} + +static int cam_vfe_bus_update_stripe_cfg(void *priv, void *cmd_args, + uint32_t arg_size) +{ + struct cam_vfe_bus_ver2_priv *bus_priv; + struct cam_isp_hw_dual_isp_update_args *stripe_args; + struct cam_vfe_bus_ver2_vfe_out_data *vfe_out_data = NULL; + struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL; + struct cam_isp_dual_stripe_config *stripe_config; + uint32_t outport_id, ports_plane_idx, i; + + bus_priv = (struct cam_vfe_bus_ver2_priv *) priv; + stripe_args = (struct cam_isp_hw_dual_isp_update_args *)cmd_args; + + vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *) + stripe_args->res->res_priv; + + if (!vfe_out_data) { + CAM_ERR(CAM_ISP, "Failed! Invalid data"); + return -EINVAL; + } + + outport_id = stripe_args->res->res_id & 0xFF; + if (stripe_args->res->res_id < CAM_ISP_IFE_OUT_RES_BASE || + stripe_args->res->res_id >= CAM_ISP_IFE_OUT_RES_MAX) + return 0; + + ports_plane_idx = (stripe_args->split_id * + (stripe_args->dual_cfg->num_ports * CAM_PACKET_MAX_PLANES)) + + (outport_id * CAM_PACKET_MAX_PLANES); + for (i = 0; i < vfe_out_data->num_wm; i++) { + wm_data = vfe_out_data->wm_res[i]->res_priv; + stripe_config = (struct cam_isp_dual_stripe_config *) + &stripe_args->dual_cfg->stripes[ports_plane_idx + i]; + wm_data->width = stripe_config->width; + wm_data->offset = stripe_config->offset; + wm_data->tile_cfg = stripe_config->tileconfig; + CAM_DBG(CAM_ISP, "id:%x wm:%d width:0x%x offset:%x tilecfg:%x", + stripe_args->res->res_id, i, wm_data->width, + wm_data->offset, wm_data->tile_cfg); + } + + return 0; +} + +static int cam_vfe_bus_start_hw(void *hw_priv, + void *start_hw_args, uint32_t arg_size) +{ + return cam_vfe_bus_start_vfe_out(hw_priv); +} + +static int cam_vfe_bus_stop_hw(void *hw_priv, + void *stop_hw_args, uint32_t arg_size) +{ + return cam_vfe_bus_stop_vfe_out(hw_priv); +} + +static int cam_vfe_bus_init_hw(void *hw_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_vfe_bus_ver2_priv *bus_priv = hw_priv; + uint32_t top_irq_reg_mask[2] = {0}; + + if (!bus_priv) { + CAM_ERR(CAM_ISP, "Invalid args"); + return -EINVAL; + } + + top_irq_reg_mask[0] = (1 << 9); + + bus_priv->irq_handle = cam_irq_controller_subscribe_irq( + bus_priv->common_data.vfe_irq_controller, + CAM_IRQ_PRIORITY_2, + top_irq_reg_mask, + bus_priv, + cam_vfe_bus_ver2_handle_irq, + NULL, + NULL, + NULL); + + if (bus_priv->irq_handle <= 0) { + CAM_ERR(CAM_ISP, "Failed to subscribe BUS IRQ"); + return -EFAULT; + } + + bus_priv->error_irq_handle = cam_irq_controller_subscribe_irq( + bus_priv->common_data.bus_irq_controller, + CAM_IRQ_PRIORITY_0, + bus_error_irq_mask, + bus_priv, + cam_vfe_bus_error_irq_top_half, + NULL, + NULL, + NULL); + + if (bus_priv->irq_handle <= 0) { + CAM_ERR(CAM_ISP, "Failed to subscribe BUS IRQ"); + return -EFAULT; + } + + /* BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */ + cam_io_w_mb(0x0, bus_priv->common_data.mem_base + + bus_priv->common_data.common_reg->addr_sync_frame_hdr); + + /* no clock gating at bus input */ + cam_io_w_mb(0xFFFFF, bus_priv->common_data.mem_base + 0x0000200C); + + /* BUS_WR_TEST_BUS_CTRL */ + cam_io_w_mb(0x0, bus_priv->common_data.mem_base + 0x0000211C); + + /* if addr_no_sync has default value then config the addr no sync reg */ + cam_io_w_mb(CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL, + bus_priv->common_data.mem_base + + bus_priv->common_data.common_reg->addr_sync_no_sync); + + return 0; +} + +static int cam_vfe_bus_deinit_hw(void *hw_priv, + void *deinit_hw_args, uint32_t arg_size) +{ + struct cam_vfe_bus_ver2_priv *bus_priv = hw_priv; + int rc = 0; + + if (!bus_priv) { + CAM_ERR(CAM_ISP, "Error: Invalid args"); + return -EINVAL; + } + + if (bus_priv->error_irq_handle) { + rc = cam_irq_controller_unsubscribe_irq( + bus_priv->common_data.bus_irq_controller, + bus_priv->error_irq_handle); + if (rc) + CAM_ERR(CAM_ISP, + "Failed to unsubscribe error irq rc=%d", rc); + + bus_priv->error_irq_handle = 0; + } + + if (bus_priv->irq_handle) { + rc = cam_irq_controller_unsubscribe_irq( + bus_priv->common_data.vfe_irq_controller, + bus_priv->irq_handle); + if (rc) + CAM_ERR(CAM_ISP, + "Failed to unsubscribe irq rc=%d", rc); + + bus_priv->irq_handle = 0; + } + + return rc; +} + +static int __cam_vfe_bus_process_cmd(void *priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + return cam_vfe_bus_process_cmd(priv, cmd_type, cmd_args, arg_size); +} + +static int cam_vfe_bus_process_cmd( + struct cam_isp_resource_node *priv, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + int rc = -EINVAL; + struct cam_vfe_bus_ver2_priv *bus_priv; + + if (!priv || !cmd_args) { + CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_ISP_HW_CMD_GET_BUF_UPDATE: + rc = cam_vfe_bus_update_wm(priv, cmd_args, arg_size); + break; + case CAM_ISP_HW_CMD_GET_HFR_UPDATE: + rc = cam_vfe_bus_update_hfr(priv, cmd_args, arg_size); + break; + case CAM_ISP_HW_CMD_GET_SECURE_MODE: + rc = cam_vfe_bus_get_secure_mode(priv, cmd_args, arg_size); + break; + case CAM_ISP_HW_CMD_STRIPE_UPDATE: + rc = cam_vfe_bus_update_stripe_cfg(priv, cmd_args, arg_size); + break; + case CAM_ISP_HW_CMD_STOP_BUS_ERR_IRQ: + bus_priv = (struct cam_vfe_bus_ver2_priv *) priv; + if (bus_priv->error_irq_handle) { + CAM_INFO(CAM_ISP, "Mask off bus error irq handler"); + rc = cam_irq_controller_unsubscribe_irq( + bus_priv->common_data.bus_irq_controller, + bus_priv->error_irq_handle); + if (rc) + CAM_ERR(CAM_ISP, + "Failed to unsubscribe error irq rc=%d", + rc); + + bus_priv->error_irq_handle = 0; + } + break; + default: + CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid camif process command:%d", + cmd_type); + break; + } + + return rc; +} + +int cam_vfe_bus_ver2_init( + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *bus_hw_info, + void *vfe_irq_controller, + struct cam_vfe_bus **vfe_bus) +{ + int i, rc = 0; + struct cam_vfe_bus_ver2_priv *bus_priv = NULL; + struct cam_vfe_bus *vfe_bus_local; + struct cam_vfe_bus_ver2_hw_info *ver2_hw_info = bus_hw_info; + + CAM_DBG(CAM_ISP, "Enter"); + + if (!soc_info || !hw_intf || !bus_hw_info || !vfe_irq_controller) { + CAM_ERR(CAM_ISP, + "Inval_prms soc_info:%pK hw_intf:%pK hw_info%pK", + soc_info, hw_intf, bus_hw_info); + CAM_ERR(CAM_ISP, "controller: %pK", vfe_irq_controller); + rc = -EINVAL; + goto end; + } + + vfe_bus_local = kzalloc(sizeof(struct cam_vfe_bus), GFP_KERNEL); + if (!vfe_bus_local) { + CAM_DBG(CAM_ISP, "Failed to alloc for vfe_bus"); + rc = -ENOMEM; + goto end; + } + + bus_priv = kzalloc(sizeof(struct cam_vfe_bus_ver2_priv), + GFP_KERNEL); + if (!bus_priv) { + CAM_DBG(CAM_ISP, "Failed to alloc for vfe_bus_priv"); + rc = -ENOMEM; + goto free_bus_local; + } + vfe_bus_local->bus_priv = bus_priv; + + bus_priv->num_client = ver2_hw_info->num_client; + bus_priv->num_out = ver2_hw_info->num_out; + bus_priv->common_data.num_sec_out = 0; + bus_priv->common_data.secure_mode = CAM_SECURE_MODE_NON_SECURE; + bus_priv->common_data.core_index = soc_info->index; + bus_priv->common_data.mem_base = + CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX); + bus_priv->common_data.hw_intf = hw_intf; + bus_priv->common_data.vfe_irq_controller = vfe_irq_controller; + bus_priv->common_data.common_reg = &ver2_hw_info->common_reg; + bus_priv->common_data.addr_no_sync = + CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL; + + mutex_init(&bus_priv->common_data.bus_mutex); + + rc = cam_irq_controller_init(drv_name, bus_priv->common_data.mem_base, + &ver2_hw_info->common_reg.irq_reg_info, + &bus_priv->common_data.bus_irq_controller); + if (rc) { + CAM_ERR(CAM_ISP, "cam_irq_controller_init failed"); + goto free_bus_priv; + } + + INIT_LIST_HEAD(&bus_priv->free_comp_grp); + INIT_LIST_HEAD(&bus_priv->free_dual_comp_grp); + INIT_LIST_HEAD(&bus_priv->used_comp_grp); + + for (i = 0; i < bus_priv->num_client; i++) { + rc = cam_vfe_bus_init_wm_resource(i, bus_priv, bus_hw_info, + &bus_priv->bus_client[i]); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Init WM failed rc=%d", rc); + goto deinit_wm; + } + } + + for (i = 0; i < CAM_VFE_BUS_VER2_COMP_GRP_MAX; i++) { + rc = cam_vfe_bus_init_comp_grp(i, bus_priv, bus_hw_info, + &bus_priv->comp_grp[i]); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Init Comp Grp failed rc=%d", rc); + goto deinit_comp_grp; + } + } + + for (i = 0; i < bus_priv->num_out; i++) { + rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv, + bus_hw_info); + if (rc < 0) { + CAM_ERR(CAM_ISP, "Init VFE Out failed rc=%d", rc); + goto deinit_vfe_out; + } + } + + INIT_LIST_HEAD(&bus_priv->common_data.free_payload_list); + for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++) { + INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list); + list_add_tail(&bus_priv->common_data.evt_payload[i].list, + &bus_priv->common_data.free_payload_list); + } + + vfe_bus_local->hw_ops.reserve = cam_vfe_bus_acquire_vfe_out; + vfe_bus_local->hw_ops.release = cam_vfe_bus_release_vfe_out; + vfe_bus_local->hw_ops.start = cam_vfe_bus_start_hw; + vfe_bus_local->hw_ops.stop = cam_vfe_bus_stop_hw; + vfe_bus_local->hw_ops.init = cam_vfe_bus_init_hw; + vfe_bus_local->hw_ops.deinit = cam_vfe_bus_deinit_hw; + vfe_bus_local->top_half_handler = cam_vfe_bus_ver2_handle_irq; + vfe_bus_local->bottom_half_handler = NULL; + vfe_bus_local->hw_ops.process_cmd = __cam_vfe_bus_process_cmd; + + *vfe_bus = vfe_bus_local; + + CAM_DBG(CAM_ISP, "Exit"); + return rc; + +deinit_vfe_out: + if (i < 0) + i = CAM_VFE_BUS_VER2_VFE_OUT_MAX; + for (--i; i >= 0; i--) + cam_vfe_bus_deinit_vfe_out_resource(&bus_priv->vfe_out[i]); + +deinit_comp_grp: + if (i < 0) + i = CAM_VFE_BUS_VER2_COMP_GRP_MAX; + for (--i; i >= 0; i--) + cam_vfe_bus_deinit_comp_grp(&bus_priv->comp_grp[i]); + +deinit_wm: + if (i < 0) + i = bus_priv->num_client; + for (--i; i >= 0; i--) + cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]); + +free_bus_priv: + kfree(vfe_bus_local->bus_priv); + +free_bus_local: + kfree(vfe_bus_local); + +end: + return rc; +} + +int cam_vfe_bus_ver2_deinit( + struct cam_vfe_bus **vfe_bus) +{ + int i, rc = 0; + struct cam_vfe_bus_ver2_priv *bus_priv = NULL; + struct cam_vfe_bus *vfe_bus_local; + + if (!vfe_bus || !*vfe_bus) { + CAM_ERR(CAM_ISP, "Invalid input"); + return -EINVAL; + } + vfe_bus_local = *vfe_bus; + + bus_priv = vfe_bus_local->bus_priv; + if (!bus_priv) { + CAM_ERR(CAM_ISP, "bus_priv is NULL"); + rc = -ENODEV; + goto free_bus_local; + } + + INIT_LIST_HEAD(&bus_priv->common_data.free_payload_list); + for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++) + INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list); + + for (i = 0; i < bus_priv->num_client; i++) { + rc = cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]); + if (rc < 0) + CAM_ERR(CAM_ISP, + "Deinit WM failed rc=%d", rc); + } + + for (i = 0; i < CAM_VFE_BUS_VER2_COMP_GRP_MAX; i++) { + rc = cam_vfe_bus_deinit_comp_grp(&bus_priv->comp_grp[i]); + if (rc < 0) + CAM_ERR(CAM_ISP, + "Deinit Comp Grp failed rc=%d", rc); + } + + for (i = 0; i < CAM_VFE_BUS_VER2_VFE_OUT_MAX; i++) { + rc = cam_vfe_bus_deinit_vfe_out_resource(&bus_priv->vfe_out[i]); + if (rc < 0) + CAM_ERR(CAM_ISP, + "Deinit VFE Out failed rc=%d", rc); + } + + INIT_LIST_HEAD(&bus_priv->free_comp_grp); + INIT_LIST_HEAD(&bus_priv->free_dual_comp_grp); + INIT_LIST_HEAD(&bus_priv->used_comp_grp); + + rc = cam_irq_controller_deinit( + &bus_priv->common_data.bus_irq_controller); + if (rc) + CAM_ERR(CAM_ISP, + "Deinit IRQ Controller failed rc=%d", rc); + + mutex_destroy(&bus_priv->common_data.bus_mutex); + kfree(vfe_bus_local->bus_priv); + +free_bus_local: + kfree(vfe_bus_local); + + *vfe_bus = NULL; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h new file mode 100644 index 000000000000..5a12f745e328 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h @@ -0,0 +1,210 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_BUS_VER2_H_ +#define _CAM_VFE_BUS_VER2_H_ + +#include "cam_irq_controller.h" +#include "cam_vfe_bus.h" + +#define CAM_VFE_BUS_VER2_MAX_CLIENTS 20 + +enum cam_vfe_bus_ver2_vfe_core_id { + CAM_VFE_BUS_VER2_VFE_CORE_0, + CAM_VFE_BUS_VER2_VFE_CORE_1, + CAM_VFE_BUS_VER2_VFE_CORE_MAX, +}; + +enum cam_vfe_bus_ver2_comp_grp_type { + CAM_VFE_BUS_VER2_COMP_GRP_0, + CAM_VFE_BUS_VER2_COMP_GRP_1, + CAM_VFE_BUS_VER2_COMP_GRP_2, + CAM_VFE_BUS_VER2_COMP_GRP_3, + CAM_VFE_BUS_VER2_COMP_GRP_4, + CAM_VFE_BUS_VER2_COMP_GRP_5, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4, + CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5, + CAM_VFE_BUS_VER2_COMP_GRP_MAX, +}; + +enum cam_vfe_bus_ver2_vfe_out_type { + CAM_VFE_BUS_VER2_VFE_OUT_RDI0, + CAM_VFE_BUS_VER2_VFE_OUT_RDI1, + CAM_VFE_BUS_VER2_VFE_OUT_RDI2, + CAM_VFE_BUS_VER2_VFE_OUT_RDI3, + CAM_VFE_BUS_VER2_VFE_OUT_FULL, + CAM_VFE_BUS_VER2_VFE_OUT_DS4, + CAM_VFE_BUS_VER2_VFE_OUT_DS16, + CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP, + CAM_VFE_BUS_VER2_VFE_OUT_FD, + CAM_VFE_BUS_VER2_VFE_OUT_PDAF, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS, + CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST, + CAM_VFE_BUS_VER2_VFE_OUT_MAX, +}; + +/* + * struct cam_vfe_bus_ver2_reg_offset_common: + * + * @Brief: Common registers across all BUS Clients + */ +struct cam_vfe_bus_ver2_reg_offset_common { + uint32_t hw_version; + uint32_t hw_capability; + uint32_t sw_reset; + uint32_t cgc_ovd; + uint32_t pwr_iso_cfg; + uint32_t dual_master_comp_cfg; + struct cam_irq_controller_reg_info irq_reg_info; + uint32_t comp_error_status; + uint32_t comp_ovrwr_status; + uint32_t dual_comp_error_status; + uint32_t dual_comp_ovrwr_status; + uint32_t addr_sync_cfg; + uint32_t addr_sync_frame_hdr; + uint32_t addr_sync_no_sync; +}; + +/* + * struct cam_vfe_bus_ver2_reg_offset_ubwc_client: + * + * @Brief: UBWC register offsets for BUS Clients + */ +struct cam_vfe_bus_ver2_reg_offset_ubwc_client { + uint32_t tile_cfg; + uint32_t h_init; + uint32_t v_init; + uint32_t meta_addr; + uint32_t meta_offset; + uint32_t meta_stride; + uint32_t mode_cfg; + uint32_t bw_limit; +}; + +/* + * struct cam_vfe_bus_ver2_reg_offset_bus_client: + * + * @Brief: Register offsets for BUS Clients + */ +struct cam_vfe_bus_ver2_reg_offset_bus_client { + uint32_t status0; + uint32_t status1; + uint32_t cfg; + uint32_t header_addr; + uint32_t header_cfg; + uint32_t image_addr; + uint32_t image_addr_offset; + uint32_t buffer_width_cfg; + uint32_t buffer_height_cfg; + uint32_t packer_cfg; + uint32_t stride; + uint32_t irq_subsample_period; + uint32_t irq_subsample_pattern; + uint32_t framedrop_period; + uint32_t framedrop_pattern; + uint32_t frame_inc; + uint32_t burst_limit; + struct cam_vfe_bus_ver2_reg_offset_ubwc_client *ubwc_regs; +}; + +/* + * struct cam_vfe_bus_ver2_reg_offset_comp_grp: + * + * @Brief: Register offsets for Composite Group registers + * comp_mask: Comp group register address + * addr_sync_mask:Address sync group register address + */ +struct cam_vfe_bus_ver2_reg_offset_comp_grp { + uint32_t comp_mask; + uint32_t addr_sync_mask; +}; + +/* + * struct cam_vfe_bus_ver2_vfe_out_hw_info: + * + * @Brief: HW capability of VFE Bus Client + */ +struct cam_vfe_bus_ver2_vfe_out_hw_info { + enum cam_vfe_bus_ver2_vfe_out_type vfe_out_type; + uint32_t max_width; + uint32_t max_height; +}; + +/* + * struct cam_vfe_bus_ver2_hw_info: + * + * @Brief: HW register info for entire Bus + * + * @common_reg: Common register details + * @bus_client_reg: Bus client register info + * @comp_reg_grp: Composite group register info + * @vfe_out_hw_info: VFE output capability + */ +struct cam_vfe_bus_ver2_hw_info { + struct cam_vfe_bus_ver2_reg_offset_common common_reg; + uint32_t num_client; + struct cam_vfe_bus_ver2_reg_offset_bus_client + bus_client_reg[CAM_VFE_BUS_VER2_MAX_CLIENTS]; + struct cam_vfe_bus_ver2_reg_offset_comp_grp + comp_grp_reg[CAM_VFE_BUS_VER2_COMP_GRP_MAX]; + uint32_t num_out; + struct cam_vfe_bus_ver2_vfe_out_hw_info + vfe_out_hw_info[CAM_VFE_BUS_VER2_VFE_OUT_MAX]; +}; + +/* + * cam_vfe_bus_ver2_init() + * + * @Brief: Initialize Bus layer + * + * @soc_info: Soc Information for the associated HW + * @hw_intf: HW Interface of HW to which this resource belongs + * @bus_hw_info: BUS HW info that contains details of BUS registers + * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top + * level IRQs + * @vfe_bus: Pointer to vfe_bus structure which will be filled + * and returned on successful initialize + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_bus_ver2_init( + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *bus_hw_info, + void *vfe_irq_controller, + struct cam_vfe_bus **vfe_bus); + +/* + * cam_vfe_bus_ver2_deinit() + * + * @Brief: Deinitialize Bus layer + * + * @vfe_bus: Pointer to vfe_bus structure to deinitialize + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_bus_ver2_deinit(struct cam_vfe_bus **vfe_bus); + +#endif /* _CAM_VFE_BUS_VER2_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h new file mode 100644 index 000000000000..0763bca8bd0d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h @@ -0,0 +1,85 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_BUS_H_ +#define _CAM_VFE_BUS_H_ + +#include +#include "cam_hw_intf.h" +#include "cam_isp_hw.h" + +#define CAM_VFE_BUS_VER_1_0 0x1000 +#define CAM_VFE_BUS_VER_2_0 0x2000 + +enum cam_vfe_bus_plane_type { + PLANE_Y, + PLANE_C, + PLANE_MAX, +}; + +/* + * struct cam_vfe_bus: + * + * @Brief: Bus interface structure + * + * @bus_priv: Private data of BUS + * @hw_ops: Hardware interface functions + * @top_half_handler: Top Half handler function + * @bottom_half_handler: Bottom Half handler function + */ +struct cam_vfe_bus { + void *bus_priv; + + struct cam_hw_ops hw_ops; + CAM_IRQ_HANDLER_TOP_HALF top_half_handler; + CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler; +}; + +/* + * cam_vfe_bus_init() + * + * @Brief: Initialize Bus layer + * + * @bus_version: Version of BUS to initialize + * @soc_info: Soc Information for the associated HW + * @hw_intf: HW Interface of HW to which this resource belongs + * @bus_hw_info: BUS HW info that contains details of BUS registers + * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top + * level IRQs + * @vfe_bus: Pointer to vfe_bus structure which will be filled + * and returned on successful initialize + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_bus_init(uint32_t bus_version, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *bus_hw_info, + void *vfe_irq_controller, + struct cam_vfe_bus **vfe_bus); + +/* + * cam_vfe_bus_deinit() + * + * @Brief: Deinitialize Bus layer + * + * @bus_version: Version of BUS to deinitialize + * @vfe_bus: Pointer to vfe_bus structure to deinitialize + * + * @Return: 0: Success + * Non-zero: Failure + */ +int cam_vfe_bus_deinit(uint32_t bus_version, + struct cam_vfe_bus **vfe_bus); + +#endif /* _CAM_VFE_BUS_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile new file mode 100644 index 000000000000..7bb43577a594 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile @@ -0,0 +1,13 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/hw_utils/irq_controller +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_top.o cam_vfe_top_ver2.o cam_vfe_camif_ver2.o cam_vfe_rdi.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c new file mode 100644 index 000000000000..734cbdb3d4e5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c @@ -0,0 +1,503 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include "cam_io_util.h" +#include "cam_isp_hw_mgr_intf.h" +#include "cam_isp_hw.h" +#include "cam_vfe_hw_intf.h" +#include "cam_vfe_soc.h" +#include "cam_vfe_top.h" +#include "cam_vfe_top_ver2.h" +#include "cam_vfe_camif_ver2.h" +#include "cam_debug_util.h" +#include "cam_cdm_util.h" +#include "cam_cpas_api.h" + +struct cam_vfe_mux_camif_data { + void __iomem *mem_base; + struct cam_hw_intf *hw_intf; + struct cam_vfe_camif_ver2_reg *camif_reg; + struct cam_vfe_top_ver2_reg_offset_common *common_reg; + struct cam_vfe_camif_reg_data *reg_data; + struct cam_hw_soc_info *soc_info; + + enum cam_isp_hw_sync_mode sync_mode; + uint32_t dsp_mode; + uint32_t pix_pattern; + uint32_t first_pixel; + uint32_t first_line; + uint32_t last_pixel; + uint32_t last_line; +}; + +static int cam_vfe_camif_validate_pix_pattern(uint32_t pattern) +{ + int rc; + + switch (pattern) { + case CAM_ISP_PATTERN_BAYER_RGRGRG: + case CAM_ISP_PATTERN_BAYER_GRGRGR: + case CAM_ISP_PATTERN_BAYER_BGBGBG: + case CAM_ISP_PATTERN_BAYER_GBGBGB: + case CAM_ISP_PATTERN_YUV_YCBYCR: + case CAM_ISP_PATTERN_YUV_YCRYCB: + case CAM_ISP_PATTERN_YUV_CBYCRY: + case CAM_ISP_PATTERN_YUV_CRYCBY: + rc = 0; + break; + default: + CAM_ERR(CAM_ISP, "Error! Invalid pix pattern:%d", pattern); + rc = -EINVAL; + break; + } + return rc; +} + +static int cam_vfe_camif_get_reg_update( + struct cam_isp_resource_node *camif_res, + void *cmd_args, uint32_t arg_size) +{ + uint32_t size = 0; + uint32_t reg_val_pair[2]; + struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args; + struct cam_cdm_utils_ops *cdm_util_ops = NULL; + struct cam_vfe_mux_camif_data *rsrc_data = NULL; + + if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) { + CAM_ERR(CAM_ISP, "Invalid cmd size"); + return -EINVAL; + } + + if (!cdm_args || !cdm_args->res) { + CAM_ERR(CAM_ISP, "Invalid args"); + return -EINVAL; + } + + cdm_util_ops = (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops; + + if (!cdm_util_ops) { + CAM_ERR(CAM_ISP, "Invalid CDM ops"); + return -EINVAL; + } + + size = cdm_util_ops->cdm_required_size_reg_random(1); + /* since cdm returns dwords, we need to convert it into bytes */ + if ((size * 4) > cdm_args->cmd.size) { + CAM_ERR(CAM_ISP, "buf size:%d is not sufficient, expected: %d", + cdm_args->cmd.size, size); + return -EINVAL; + } + + rsrc_data = camif_res->res_priv; + reg_val_pair[0] = rsrc_data->camif_reg->reg_update_cmd; + reg_val_pair[1] = rsrc_data->reg_data->reg_update_cmd_data; + CAM_DBG(CAM_ISP, "CAMIF reg_update_cmd %x offset %x", + reg_val_pair[1], reg_val_pair[0]); + + cdm_util_ops->cdm_write_regrandom(cdm_args->cmd.cmd_buf_addr, + 1, reg_val_pair); + + cdm_args->cmd.used_bytes = size * 4; + + return 0; +} + +int cam_vfe_camif_ver2_acquire_resource( + struct cam_isp_resource_node *camif_res, + void *acquire_param) +{ + struct cam_vfe_mux_camif_data *camif_data; + struct cam_vfe_acquire_args *acquire_data; + + int rc = 0; + + camif_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + acquire_data = (struct cam_vfe_acquire_args *)acquire_param; + + rc = cam_vfe_camif_validate_pix_pattern( + acquire_data->vfe_in.in_port->test_pattern); + if (rc) + return rc; + + camif_data->sync_mode = acquire_data->vfe_in.sync_mode; + camif_data->pix_pattern = acquire_data->vfe_in.in_port->test_pattern; + camif_data->dsp_mode = acquire_data->vfe_in.in_port->dsp_mode; + camif_data->first_pixel = acquire_data->vfe_in.in_port->left_start; + camif_data->last_pixel = acquire_data->vfe_in.in_port->left_stop; + camif_data->first_line = acquire_data->vfe_in.in_port->line_start; + camif_data->last_line = acquire_data->vfe_in.in_port->line_stop; + + return rc; +} + +static int cam_vfe_camif_resource_init( + struct cam_isp_resource_node *camif_res, + void *init_args, uint32_t arg_size) +{ + struct cam_vfe_mux_camif_data *camif_data; + struct cam_hw_soc_info *soc_info; + int rc = 0; + + if (!camif_res) { + CAM_ERR(CAM_ISP, "Error Invalid input arguments"); + return -EINVAL; + } + + camif_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + + soc_info = camif_data->soc_info; + + if ((camif_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) && + (camif_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) { + rc = cam_vfe_soc_enable_clk(soc_info, CAM_VFE_DSP_CLK_NAME); + if (rc) + CAM_ERR(CAM_ISP, "failed to enable dsp clk"); + } + + return rc; +} + +static int cam_vfe_camif_resource_deinit( + struct cam_isp_resource_node *camif_res, + void *init_args, uint32_t arg_size) +{ + struct cam_vfe_mux_camif_data *camif_data; + struct cam_hw_soc_info *soc_info; + int rc = 0; + + if (!camif_res) { + CAM_ERR(CAM_ISP, "Error Invalid input arguments"); + return -EINVAL; + } + + camif_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + + soc_info = camif_data->soc_info; + + if ((camif_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) && + (camif_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) { + rc = cam_vfe_soc_disable_clk(soc_info, CAM_VFE_DSP_CLK_NAME); + if (rc) + CAM_ERR(CAM_ISP, "failed to disable dsp clk"); + } + + return rc; + +} + +static int cam_vfe_camif_resource_start( + struct cam_isp_resource_node *camif_res) +{ + struct cam_vfe_mux_camif_data *rsrc_data; + uint32_t val = 0; + + if (!camif_res) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + if (camif_res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR(CAM_ISP, "Error! Invalid camif res res_state:%d", + camif_res->res_state); + return -EINVAL; + } + + rsrc_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + + /*config vfe core*/ + val = (rsrc_data->pix_pattern << + rsrc_data->reg_data->pixel_pattern_shift); + if (rsrc_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE) + val |= (1 << rsrc_data->reg_data->extern_reg_update_shift); + + if ((rsrc_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) && + (rsrc_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) { + /* DSP mode reg val is CAM_ISP_DSP_MODE - 1 */ + val |= (((rsrc_data->dsp_mode - 1) & + rsrc_data->reg_data->dsp_mode_mask) << + rsrc_data->reg_data->dsp_mode_shift); + val |= (0x1 << rsrc_data->reg_data->dsp_en_shift); + } + + cam_io_w_mb(val, rsrc_data->mem_base + rsrc_data->common_reg->core_cfg); + + CAM_DBG(CAM_ISP, "hw id:%d core_cfg val:%d", camif_res->hw_intf->hw_idx, + val); + + /* disable the CGC for stats */ + cam_io_w_mb(0xFFFFFFFF, rsrc_data->mem_base + + rsrc_data->common_reg->module_ctrl[ + CAM_VFE_TOP_VER2_MODULE_STATS]->cgc_ovd); + + /* epoch config with 20 line */ + cam_io_w_mb(rsrc_data->reg_data->epoch_line_cfg, + rsrc_data->mem_base + rsrc_data->camif_reg->epoch_irq); + + camif_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + /* Reg Update */ + cam_io_w_mb(rsrc_data->reg_data->reg_update_cmd_data, + rsrc_data->mem_base + rsrc_data->camif_reg->reg_update_cmd); + + CAM_DBG(CAM_ISP, "Start Camif IFE %d Done", camif_res->hw_intf->hw_idx); + return 0; +} + +static int cam_vfe_camif_reg_dump( + struct cam_isp_resource_node *camif_res) +{ + struct cam_vfe_mux_camif_data *camif_priv; + struct cam_vfe_soc_private *soc_private; + int rc = 0, i; + uint32_t val = 0; + + if (!camif_res) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + if ((camif_res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) || + (camif_res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE)) + return 0; + + camif_priv = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + soc_private = camif_priv->soc_info->soc_private; + for (i = 0xA3C; i <= 0xA90; i += 4) { + val = cam_io_r_mb(camif_priv->mem_base + i); + CAM_INFO(CAM_ISP, "offset 0x%x val 0x%x", i, val); + } + + for (i = 0xE0C; i <= 0xE3C; i += 4) { + val = cam_io_r_mb(camif_priv->mem_base + i); + CAM_INFO(CAM_ISP, "offset 0x%x val 0x%x", i, val); + } + + for (i = 0x2000; i <= 0x20B8; i += 4) { + val = cam_io_r_mb(camif_priv->mem_base + i); + CAM_INFO(CAM_ISP, "offset 0x%x val 0x%x", i, val); + } + + for (i = 0x2500; i <= 0x255C; i += 4) { + val = cam_io_r_mb(camif_priv->mem_base + i); + CAM_INFO(CAM_ISP, "offset 0x%x val 0x%x", i, val); + } + + for (i = 0x2600; i <= 0x265C; i += 4) { + val = cam_io_r_mb(camif_priv->mem_base + i); + CAM_INFO(CAM_ISP, "offset 0x%x val 0x%x", i, val); + } + + cam_cpas_reg_read(soc_private->cpas_handle, + CAM_CPAS_REG_CAMNOC, 0x420, true, &val); + CAM_INFO(CAM_ISP, "IFE02_MAXWR_LOW offset 0x420 val 0x%x", val); + + cam_cpas_reg_read(soc_private->cpas_handle, + CAM_CPAS_REG_CAMNOC, 0x820, true, &val); + CAM_INFO(CAM_ISP, "IFE13_MAXWR_LOW offset 0x820 val 0x%x", val); + + return rc; +} + +static int cam_vfe_camif_resource_stop( + struct cam_isp_resource_node *camif_res) +{ + struct cam_vfe_mux_camif_data *camif_priv; + struct cam_vfe_camif_ver2_reg *camif_reg; + int rc = 0; + uint32_t val = 0; + + if (!camif_res) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + if (camif_res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED || + camif_res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) + return 0; + + camif_priv = (struct cam_vfe_mux_camif_data *)camif_res->res_priv; + camif_reg = camif_priv->camif_reg; + + if ((camif_priv->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) && + (camif_priv->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) { + val = cam_io_r_mb(camif_priv->mem_base + + camif_priv->common_reg->core_cfg); + val &= (~(1 << camif_priv->reg_data->dsp_en_shift)); + cam_io_w_mb(val, camif_priv->mem_base + + camif_priv->common_reg->core_cfg); + } + + if (camif_res->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) + camif_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + return rc; +} + +static int cam_vfe_camif_process_cmd(struct cam_isp_resource_node *rsrc_node, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + int rc = -EINVAL; + + if (!rsrc_node || !cmd_args) { + CAM_ERR(CAM_ISP, "Invalid input arguments"); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_ISP_HW_CMD_GET_REG_UPDATE: + rc = cam_vfe_camif_get_reg_update(rsrc_node, cmd_args, + arg_size); + break; + case CAM_ISP_HW_CMD_GET_REG_DUMP: + rc = cam_vfe_camif_reg_dump(rsrc_node); + break; + default: + CAM_ERR(CAM_ISP, + "unsupported process command:%d", cmd_type); + break; + } + + return rc; +} + +static int cam_vfe_camif_handle_irq_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + return -EPERM; +} + +static int cam_vfe_camif_handle_irq_bottom_half(void *handler_priv, + void *evt_payload_priv) +{ + int ret = CAM_VFE_IRQ_STATUS_ERR; + struct cam_isp_resource_node *camif_node; + struct cam_vfe_mux_camif_data *camif_priv; + struct cam_vfe_top_irq_evt_payload *payload; + uint32_t irq_status0; + uint32_t irq_status1; + + if (!handler_priv || !evt_payload_priv) { + CAM_ERR(CAM_ISP, "Invalid params"); + return ret; + } + + camif_node = handler_priv; + camif_priv = camif_node->res_priv; + payload = evt_payload_priv; + irq_status0 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS0]; + irq_status1 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS1]; + + CAM_DBG(CAM_ISP, "event ID:%d", payload->evt_id); + CAM_DBG(CAM_ISP, "irq_status_0 = %x", irq_status0); + + switch (payload->evt_id) { + case CAM_ISP_HW_EVENT_SOF: + if (irq_status0 & camif_priv->reg_data->sof_irq_mask) { + CAM_DBG(CAM_ISP, "Received SOF"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + case CAM_ISP_HW_EVENT_EPOCH: + if (irq_status0 & camif_priv->reg_data->epoch0_irq_mask) { + CAM_DBG(CAM_ISP, "Received EPOCH"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + case CAM_ISP_HW_EVENT_REG_UPDATE: + if (irq_status0 & camif_priv->reg_data->reg_update_irq_mask) { + CAM_DBG(CAM_ISP, "Received REG_UPDATE_ACK"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + case CAM_ISP_HW_EVENT_EOF: + if (irq_status0 & camif_priv->reg_data->eof_irq_mask) { + CAM_DBG(CAM_ISP, "Received EOF\n"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + case CAM_ISP_HW_EVENT_ERROR: + if (irq_status1 & camif_priv->reg_data->error_irq_mask1) { + CAM_DBG(CAM_ISP, "Received ERROR\n"); + ret = CAM_ISP_HW_ERROR_OVERFLOW; + cam_vfe_camif_reg_dump(camif_node); + } else { + ret = CAM_ISP_HW_ERROR_NONE; + } + break; + default: + break; + } + + CAM_DBG(CAM_ISP, "returing status = %d", ret); + return ret; +} + +int cam_vfe_camif_ver2_init( + struct cam_hw_intf *hw_intf, + struct cam_hw_soc_info *soc_info, + void *camif_hw_info, + struct cam_isp_resource_node *camif_node) +{ + struct cam_vfe_mux_camif_data *camif_priv = NULL; + struct cam_vfe_camif_ver2_hw_info *camif_info = camif_hw_info; + + camif_priv = kzalloc(sizeof(struct cam_vfe_mux_camif_data), + GFP_KERNEL); + if (!camif_priv) { + CAM_DBG(CAM_ISP, "Error! Failed to alloc for camif_priv"); + return -ENOMEM; + } + + camif_node->res_priv = camif_priv; + + camif_priv->mem_base = soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base; + camif_priv->camif_reg = camif_info->camif_reg; + camif_priv->common_reg = camif_info->common_reg; + camif_priv->reg_data = camif_info->reg_data; + camif_priv->hw_intf = hw_intf; + camif_priv->soc_info = soc_info; + + camif_node->init = cam_vfe_camif_resource_init; + camif_node->deinit = cam_vfe_camif_resource_deinit; + camif_node->start = cam_vfe_camif_resource_start; + camif_node->stop = cam_vfe_camif_resource_stop; + camif_node->process_cmd = cam_vfe_camif_process_cmd; + camif_node->top_half_handler = cam_vfe_camif_handle_irq_top_half; + camif_node->bottom_half_handler = cam_vfe_camif_handle_irq_bottom_half; + + return 0; +} + +int cam_vfe_camif_ver2_deinit( + struct cam_isp_resource_node *camif_node) +{ + struct cam_vfe_mux_camif_data *camif_priv = camif_node->res_priv; + + camif_node->start = NULL; + camif_node->stop = NULL; + camif_node->process_cmd = NULL; + camif_node->top_half_handler = NULL; + camif_node->bottom_half_handler = NULL; + + camif_node->res_priv = NULL; + + if (!camif_priv) { + CAM_ERR(CAM_ISP, "Error! camif_priv is NULL"); + return -ENODEV; + } + + kfree(camif_priv); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h new file mode 100644 index 000000000000..4a73bd74c097 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_CAMIF_VER2_H_ +#define _CAM_VFE_CAMIF_VER2_H_ + +#include "cam_isp_hw.h" +#include "cam_vfe_top.h" + +struct cam_vfe_camif_ver2_reg { + uint32_t camif_cmd; + uint32_t camif_config; + uint32_t line_skip_pattern; + uint32_t pixel_skip_pattern; + uint32_t skip_period; + uint32_t irq_subsample_pattern; + uint32_t epoch_irq; + uint32_t raw_crop_width_cfg; + uint32_t raw_crop_height_cfg; + uint32_t reg_update_cmd; +}; + +struct cam_vfe_camif_reg_data { + uint32_t raw_crop_first_pixel_shift; + uint32_t raw_crop_first_pixel_mask; + + uint32_t raw_crop_last_pixel_shift; + uint32_t raw_crop_last_pixel_mask; + + uint32_t raw_crop_first_line_shift; + uint32_t raw_crop_first_line_mask; + + uint32_t raw_crop_last_line_shift; + uint32_t raw_crop_last_line_mask; + + uint32_t input_mux_sel_shift; + uint32_t input_mux_sel_mask; + uint32_t extern_reg_update_shift; + uint32_t extern_reg_update_mask; + + uint32_t pixel_pattern_shift; + uint32_t pixel_pattern_mask; + + uint32_t dsp_mode_shift; + uint32_t dsp_mode_mask; + uint32_t dsp_en_shift; + uint32_t dsp_en_mask; + + uint32_t reg_update_cmd_data; + uint32_t epoch_line_cfg; + uint32_t sof_irq_mask; + uint32_t epoch0_irq_mask; + uint32_t reg_update_irq_mask; + uint32_t eof_irq_mask; + uint32_t error_irq_mask0; + uint32_t error_irq_mask1; +}; + +struct cam_vfe_camif_ver2_hw_info { + struct cam_vfe_top_ver2_reg_offset_common *common_reg; + struct cam_vfe_camif_ver2_reg *camif_reg; + struct cam_vfe_camif_reg_data *reg_data; +}; + +int cam_vfe_camif_ver2_acquire_resource( + struct cam_isp_resource_node *camif_res, + void *acquire_param); + +int cam_vfe_camif_ver2_init( + struct cam_hw_intf *hw_intf, + struct cam_hw_soc_info *soc_info, + void *camif_hw_info, + struct cam_isp_resource_node *camif_node); + +int cam_vfe_camif_ver2_deinit( + struct cam_isp_resource_node *camif_node); + +#endif /* _CAM_VFE_CAMIF_VER2_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c new file mode 100644 index 000000000000..0423e22cda4a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c @@ -0,0 +1,301 @@ +/* Copyright (c) 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 +#include "cam_vfe_rdi.h" +#include "cam_isp_hw_mgr_intf.h" +#include "cam_isp_hw.h" +#include "cam_vfe_hw_intf.h" +#include "cam_io_util.h" +#include "cam_debug_util.h" +#include "cam_cdm_util.h" + +struct cam_vfe_mux_rdi_data { + void __iomem *mem_base; + struct cam_hw_intf *hw_intf; + struct cam_vfe_top_ver2_reg_offset_common *common_reg; + struct cam_vfe_rdi_ver2_reg *rdi_reg; + struct cam_vfe_rdi_reg_data *reg_data; + + enum cam_isp_hw_sync_mode sync_mode; +}; + +static int cam_vfe_rdi_get_reg_update( + struct cam_isp_resource_node *rdi_res, + void *cmd_args, uint32_t arg_size) +{ + uint32_t size = 0; + uint32_t reg_val_pair[2]; + struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args; + struct cam_cdm_utils_ops *cdm_util_ops = NULL; + struct cam_vfe_mux_rdi_data *rsrc_data = NULL; + + if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) { + CAM_ERR(CAM_ISP, "Error - Invalid cmd size"); + return -EINVAL; + } + + if (!cdm_args || !cdm_args->res) { + CAM_ERR(CAM_ISP, "Error - Invalid args"); + return -EINVAL; + } + + cdm_util_ops = (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops; + if (!cdm_util_ops) { + CAM_ERR(CAM_ISP, "Error - Invalid CDM ops"); + return -EINVAL; + } + + size = cdm_util_ops->cdm_required_size_reg_random(1); + /* since cdm returns dwords, we need to convert it into bytes */ + if ((size * 4) > cdm_args->cmd.size) { + CAM_ERR(CAM_ISP, + "Error - buf size:%d is not sufficient, expected: %d", + cdm_args->cmd.size, size * 4); + return -EINVAL; + } + + rsrc_data = rdi_res->res_priv; + reg_val_pair[0] = rsrc_data->rdi_reg->reg_update_cmd; + reg_val_pair[1] = rsrc_data->reg_data->reg_update_cmd_data; + CAM_DBG(CAM_ISP, "RDI%d reg_update_cmd %x", + rdi_res->res_id - CAM_ISP_HW_VFE_IN_RDI0, reg_val_pair[1]); + + cdm_util_ops->cdm_write_regrandom(cdm_args->cmd.cmd_buf_addr, + 1, reg_val_pair); + cdm_args->cmd.used_bytes = size * 4; + + return 0; +} + +int cam_vfe_rdi_ver2_acquire_resource( + struct cam_isp_resource_node *rdi_res, + void *acquire_param) +{ + struct cam_vfe_mux_rdi_data *rdi_data; + struct cam_vfe_acquire_args *acquire_data; + + rdi_data = (struct cam_vfe_mux_rdi_data *)rdi_res->res_priv; + acquire_data = (struct cam_vfe_acquire_args *)acquire_param; + + rdi_data->sync_mode = acquire_data->vfe_in.sync_mode; + + return 0; +} + +static int cam_vfe_rdi_resource_start( + struct cam_isp_resource_node *rdi_res) +{ + struct cam_vfe_mux_rdi_data *rsrc_data; + int rc = 0; + + if (!rdi_res) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + if (rdi_res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR(CAM_ISP, "Error! Invalid rdi res res_state:%d", + rdi_res->res_state); + return -EINVAL; + } + + rsrc_data = (struct cam_vfe_mux_rdi_data *)rdi_res->res_priv; + rdi_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING; + + /* Reg Update */ + cam_io_w_mb(rsrc_data->reg_data->reg_update_cmd_data, + rsrc_data->mem_base + rsrc_data->rdi_reg->reg_update_cmd); + + CAM_DBG(CAM_ISP, "Start RDI %d", + rdi_res->res_id - CAM_ISP_HW_VFE_IN_RDI0); + + return rc; +} + + +static int cam_vfe_rdi_resource_stop( + struct cam_isp_resource_node *rdi_res) +{ + struct cam_vfe_mux_rdi_data *rdi_priv; + int rc = 0; + + if (!rdi_res) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + if (rdi_res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED || + rdi_res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) + return 0; + + rdi_priv = (struct cam_vfe_mux_rdi_data *)rdi_res->res_priv; + + if (rdi_res->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) + rdi_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED; + + + return rc; +} + +static int cam_vfe_rdi_process_cmd(struct cam_isp_resource_node *rsrc_node, + uint32_t cmd_type, void *cmd_args, uint32_t arg_size) +{ + int rc = -EINVAL; + + if (!rsrc_node || !cmd_args) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + switch (cmd_type) { + case CAM_ISP_HW_CMD_GET_REG_UPDATE: + rc = cam_vfe_rdi_get_reg_update(rsrc_node, cmd_args, + arg_size); + break; + default: + CAM_ERR(CAM_ISP, + "unsupported RDI process command:%d", cmd_type); + break; + } + + return rc; +} + +static int cam_vfe_rdi_handle_irq_top_half(uint32_t evt_id, + struct cam_irq_th_payload *th_payload) +{ + return -EPERM; +} + +static int cam_vfe_rdi_handle_irq_bottom_half(void *handler_priv, + void *evt_payload_priv) +{ + int ret = CAM_VFE_IRQ_STATUS_ERR; + struct cam_isp_resource_node *rdi_node; + struct cam_vfe_mux_rdi_data *rdi_priv; + struct cam_vfe_top_irq_evt_payload *payload; + uint32_t irq_status0; + + if (!handler_priv || !evt_payload_priv) { + CAM_ERR(CAM_ISP, "Invalid params"); + return ret; + } + + rdi_node = handler_priv; + rdi_priv = rdi_node->res_priv; + payload = evt_payload_priv; + irq_status0 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS0]; + + CAM_DBG(CAM_ISP, "event ID:%d", payload->evt_id); + CAM_DBG(CAM_ISP, "irq_status_0 = %x", irq_status0); + + switch (payload->evt_id) { + case CAM_ISP_HW_EVENT_SOF: + if (irq_status0 & rdi_priv->reg_data->sof_irq_mask) { + CAM_DBG(CAM_ISP, "Received SOF"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + case CAM_ISP_HW_EVENT_REG_UPDATE: + if (irq_status0 & rdi_priv->reg_data->reg_update_irq_mask) { + CAM_DBG(CAM_ISP, "Received REG UPDATE"); + ret = CAM_VFE_IRQ_STATUS_SUCCESS; + } + break; + default: + break; + } + + CAM_DBG(CAM_ISP, "returing status = %d", ret); + return ret; +} + +int cam_vfe_rdi_ver2_init( + struct cam_hw_intf *hw_intf, + struct cam_hw_soc_info *soc_info, + void *rdi_hw_info, + struct cam_isp_resource_node *rdi_node) +{ + struct cam_vfe_mux_rdi_data *rdi_priv = NULL; + struct cam_vfe_rdi_ver2_hw_info *rdi_info = rdi_hw_info; + + rdi_priv = kzalloc(sizeof(struct cam_vfe_mux_rdi_data), + GFP_KERNEL); + if (!rdi_priv) { + CAM_DBG(CAM_ISP, "Error! Failed to alloc for rdi_priv"); + return -ENOMEM; + } + + rdi_node->res_priv = rdi_priv; + + rdi_priv->mem_base = soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base; + rdi_priv->hw_intf = hw_intf; + rdi_priv->common_reg = rdi_info->common_reg; + rdi_priv->rdi_reg = rdi_info->rdi_reg; + + switch (rdi_node->res_id) { + case CAM_ISP_HW_VFE_IN_RDI0: + rdi_priv->reg_data = rdi_info->reg_data[0]; + break; + case CAM_ISP_HW_VFE_IN_RDI1: + rdi_priv->reg_data = rdi_info->reg_data[1]; + break; + case CAM_ISP_HW_VFE_IN_RDI2: + rdi_priv->reg_data = rdi_info->reg_data[2]; + break; + case CAM_ISP_HW_VFE_IN_RDI3: + if (rdi_info->reg_data[3]) { + rdi_priv->reg_data = rdi_info->reg_data[3]; + } else { + CAM_ERR(CAM_ISP, "Error! RDI3 is not supported"); + goto err_init; + } + break; + default: + CAM_DBG(CAM_ISP, "invalid Resource id:%d", rdi_node->res_id); + goto err_init; + } + + rdi_node->start = cam_vfe_rdi_resource_start; + rdi_node->stop = cam_vfe_rdi_resource_stop; + rdi_node->process_cmd = cam_vfe_rdi_process_cmd; + rdi_node->top_half_handler = cam_vfe_rdi_handle_irq_top_half; + rdi_node->bottom_half_handler = cam_vfe_rdi_handle_irq_bottom_half; + + return 0; +err_init: + kfree(rdi_priv); + return -EINVAL; +} + +int cam_vfe_rdi_ver2_deinit( + struct cam_isp_resource_node *rdi_node) +{ + struct cam_vfe_mux_rdi_data *rdi_priv = rdi_node->res_priv; + + rdi_node->start = NULL; + rdi_node->stop = NULL; + rdi_node->process_cmd = NULL; + rdi_node->top_half_handler = NULL; + rdi_node->bottom_half_handler = NULL; + + rdi_node->res_priv = NULL; + + if (!rdi_priv) { + CAM_ERR(CAM_ISP, "Error! rdi_priv NULL"); + return -ENODEV; + } + kfree(rdi_priv); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.h new file mode 100644 index 000000000000..32a67c45ee28 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.h @@ -0,0 +1,50 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_RDI_H_ +#define _CAM_VFE_RDI_H_ + +#include "cam_isp_hw.h" +#include "cam_vfe_top.h" + +#define CAM_VFE_RDI_VER2_MAX 4 + +struct cam_vfe_rdi_ver2_reg { + uint32_t reg_update_cmd; +}; + +struct cam_vfe_rdi_reg_data { + uint32_t reg_update_cmd_data; + uint32_t sof_irq_mask; + uint32_t reg_update_irq_mask; +}; + +struct cam_vfe_rdi_ver2_hw_info { + struct cam_vfe_top_ver2_reg_offset_common *common_reg; + struct cam_vfe_rdi_ver2_reg *rdi_reg; + struct cam_vfe_rdi_reg_data *reg_data[CAM_VFE_RDI_VER2_MAX]; +}; + +int cam_vfe_rdi_ver2_acquire_resource( + struct cam_isp_resource_node *rdi_res, + void *acquire_param); + +int cam_vfe_rdi_ver2_init( + struct cam_hw_intf *hw_intf, + struct cam_hw_soc_info *soc_info, + void *rdi_hw_info, + struct cam_isp_resource_node *rdi_node); + +int cam_vfe_rdi_ver2_deinit( + struct cam_isp_resource_node *rdi_node); + +#endif /* _CAM_VFE_RDI_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c new file mode 100644 index 000000000000..8eb183568adb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c @@ -0,0 +1,54 @@ +/* Copyright (c) 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 "cam_vfe_top.h" +#include "cam_vfe_top_ver2.h" +#include "cam_debug_util.h" + +int cam_vfe_top_init(uint32_t top_version, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *top_hw_info, + struct cam_vfe_top **vfe_top) +{ + int rc = -EINVAL; + + switch (top_version) { + case CAM_VFE_TOP_VER_2_0: + rc = cam_vfe_top_ver2_init(soc_info, hw_intf, top_hw_info, + vfe_top); + break; + default: + CAM_ERR(CAM_ISP, "Error! Unsupported Version %x", top_version); + break; + } + + return rc; +} + +int cam_vfe_top_deinit(uint32_t top_version, + struct cam_vfe_top **vfe_top) +{ + int rc = -EINVAL; + + switch (top_version) { + case CAM_VFE_TOP_VER_2_0: + rc = cam_vfe_top_ver2_deinit(vfe_top); + break; + default: + CAM_ERR(CAM_ISP, "Error! Unsupported Version %x", top_version); + break; + } + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c new file mode 100644 index 000000000000..976e6d2dcbb6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c @@ -0,0 +1,834 @@ +/* Copyright (c) 2017-2018, 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 +#include "cam_io_util.h" +#include "cam_cdm_util.h" +#include "cam_vfe_hw_intf.h" +#include "cam_vfe_top.h" +#include "cam_vfe_top_ver2.h" +#include "cam_debug_util.h" +#include "cam_cpas_api.h" +#include "cam_vfe_soc.h" + +#define CAM_VFE_HW_RESET_HW_AND_REG_VAL 0x00003F9F +#define CAM_VFE_HW_RESET_HW_VAL 0x00003F87 +#define CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES 3 + +struct cam_vfe_top_ver2_common_data { + struct cam_hw_soc_info *soc_info; + struct cam_hw_intf *hw_intf; + struct cam_vfe_top_ver2_reg_offset_common *common_reg; +}; + +struct cam_vfe_top_ver2_priv { + struct cam_vfe_top_ver2_common_data common_data; + struct cam_isp_resource_node mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX]; + unsigned long hw_clk_rate; + struct cam_axi_vote to_be_applied_axi_vote; + struct cam_axi_vote applied_axi_vote; + uint32_t counter_to_update_axi_vote; + struct cam_axi_vote req_axi_vote[CAM_VFE_TOP_VER2_MUX_MAX]; + unsigned long req_clk_rate[CAM_VFE_TOP_VER2_MUX_MAX]; + enum cam_vfe_bw_control_action + axi_vote_control[CAM_VFE_TOP_VER2_MUX_MAX]; +}; + +static int cam_vfe_top_mux_get_base(struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + uint32_t size = 0; + uint32_t mem_base = 0; + struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args; + struct cam_cdm_utils_ops *cdm_util_ops = NULL; + + if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) { + CAM_ERR(CAM_ISP, "Error! Invalid cmd size"); + return -EINVAL; + } + + if (!cdm_args || !cdm_args->res || !top_priv || + !top_priv->common_data.soc_info) { + CAM_ERR(CAM_ISP, "Error! Invalid args"); + return -EINVAL; + } + + cdm_util_ops = + (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops; + + if (!cdm_util_ops) { + CAM_ERR(CAM_ISP, "Invalid CDM ops"); + return -EINVAL; + } + + size = cdm_util_ops->cdm_required_size_changebase(); + /* since cdm returns dwords, we need to convert it into bytes */ + if ((size * 4) > cdm_args->cmd.size) { + CAM_ERR(CAM_ISP, "buf size:%d is not sufficient, expected: %d", + cdm_args->cmd.size, size); + return -EINVAL; + } + + mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE( + top_priv->common_data.soc_info, VFE_CORE_BASE_IDX); + CAM_DBG(CAM_ISP, "core %d mem_base 0x%x", + top_priv->common_data.soc_info->index, mem_base); + + cdm_util_ops->cdm_write_changebase( + cdm_args->cmd.cmd_buf_addr, mem_base); + cdm_args->cmd.used_bytes = (size * 4); + + return 0; +} + +static int cam_vfe_top_set_hw_clk_rate( + struct cam_vfe_top_ver2_priv *top_priv) +{ + struct cam_hw_soc_info *soc_info = NULL; + int i, rc = 0; + unsigned long max_clk_rate = 0; + + soc_info = top_priv->common_data.soc_info; + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->req_clk_rate[i] > max_clk_rate) + max_clk_rate = top_priv->req_clk_rate[i]; + } + if (max_clk_rate == top_priv->hw_clk_rate) + return 0; + + CAM_DBG(CAM_ISP, "VFE: Clock name=%s idx=%d clk=%lld", + soc_info->clk_name[soc_info->src_clk_idx], + soc_info->src_clk_idx, max_clk_rate); + + rc = cam_soc_util_set_clk_rate( + soc_info->clk[soc_info->src_clk_idx], + soc_info->clk_name[soc_info->src_clk_idx], + max_clk_rate); + + if (!rc) + top_priv->hw_clk_rate = max_clk_rate; + else + CAM_ERR(CAM_ISP, "Set Clock rate failed, rc=%d", rc); + + return rc; +} + +static int cam_vfe_top_set_axi_bw_vote( + struct cam_vfe_top_ver2_priv *top_priv, + bool start_stop) +{ + struct cam_axi_vote sum = {0, 0}; + int i, rc = 0; + struct cam_hw_soc_info *soc_info = + top_priv->common_data.soc_info; + struct cam_vfe_soc_private *soc_private = + soc_info->soc_private; + bool apply_bw_update = false; + + if (!soc_private) { + CAM_ERR(CAM_ISP, "Error soc_private NULL"); + return -EINVAL; + } + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->axi_vote_control[i] == + CAM_VFE_BW_CONTROL_INCLUDE) { + sum.uncompressed_bw += + top_priv->req_axi_vote[i].uncompressed_bw; + sum.compressed_bw += + top_priv->req_axi_vote[i].compressed_bw; + } + } + + CAM_DBG(CAM_ISP, "Updating BW from (%llu %llu) to (%llu %llu)", + top_priv->applied_axi_vote.uncompressed_bw, + top_priv->applied_axi_vote.compressed_bw, + sum.uncompressed_bw, + sum.compressed_bw); + + if ((top_priv->applied_axi_vote.uncompressed_bw == + sum.uncompressed_bw) && + (top_priv->applied_axi_vote.compressed_bw == + sum.compressed_bw)) { + CAM_DBG(CAM_ISP, "BW config unchanged %llu %llu", + top_priv->applied_axi_vote.uncompressed_bw, + top_priv->applied_axi_vote.compressed_bw); + top_priv->counter_to_update_axi_vote = 0; + return 0; + } + + if ((top_priv->to_be_applied_axi_vote.uncompressed_bw != + sum.uncompressed_bw) || + (top_priv->to_be_applied_axi_vote.compressed_bw != + sum.compressed_bw)) { + // we got a new bw value to apply + top_priv->counter_to_update_axi_vote = 0; + + top_priv->to_be_applied_axi_vote.uncompressed_bw = + sum.uncompressed_bw; + top_priv->to_be_applied_axi_vote.compressed_bw = + sum.compressed_bw; + } + + if (start_stop == true) { + CAM_DBG(CAM_ISP, + "New bw in start/stop, applying bw now, counter=%d", + top_priv->counter_to_update_axi_vote); + top_priv->counter_to_update_axi_vote = 0; + apply_bw_update = true; + } else if ((top_priv->to_be_applied_axi_vote.uncompressed_bw < + top_priv->applied_axi_vote.uncompressed_bw) || + (top_priv->to_be_applied_axi_vote.compressed_bw < + top_priv->applied_axi_vote.compressed_bw)) { + if (top_priv->counter_to_update_axi_vote >= + (CAM_VFE_TOP_VER2_MUX_MAX * + CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES)) { + CAM_DBG(CAM_ISP, + "New bw is less, applying bw now, counter=%d", + top_priv->counter_to_update_axi_vote); + top_priv->counter_to_update_axi_vote = 0; + apply_bw_update = true; + } else { + CAM_DBG(CAM_ISP, + "New bw is less, Defer applying bw, counter=%d", + top_priv->counter_to_update_axi_vote); + + top_priv->counter_to_update_axi_vote++; + apply_bw_update = false; + } + } else { + CAM_DBG(CAM_ISP, + "New bw is more, applying bw now, counter=%d", + top_priv->counter_to_update_axi_vote); + top_priv->counter_to_update_axi_vote = 0; + apply_bw_update = true; + } + + CAM_DBG(CAM_ISP, + "counter=%d, apply_bw_update=%d", + top_priv->counter_to_update_axi_vote, + apply_bw_update); + + if (apply_bw_update == true) { + rc = cam_cpas_update_axi_vote( + soc_private->cpas_handle, + &top_priv->to_be_applied_axi_vote); + if (!rc) { + top_priv->applied_axi_vote.uncompressed_bw = + top_priv-> + to_be_applied_axi_vote.uncompressed_bw; + top_priv->applied_axi_vote.compressed_bw = + top_priv-> + to_be_applied_axi_vote.compressed_bw; + } else { + CAM_ERR(CAM_ISP, "BW request failed, rc=%d", rc); + } + top_priv->counter_to_update_axi_vote = 0; + } + + return rc; +} + +static int cam_vfe_top_clock_update( + struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + struct cam_vfe_clock_update_args *clk_update = NULL; + struct cam_isp_resource_node *res = NULL; + struct cam_hw_info *hw_info = NULL; + int i, rc = 0; + + clk_update = + (struct cam_vfe_clock_update_args *)cmd_args; + res = clk_update->node_res; + + if (!res || !res->hw_intf->hw_priv) { + CAM_ERR(CAM_ISP, "Invalid input res %pK", res); + return -EINVAL; + } + + hw_info = res->hw_intf->hw_priv; + + if (res->res_type != CAM_ISP_RESOURCE_VFE_IN || + res->res_id >= CAM_ISP_HW_VFE_IN_MAX) { + CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d", + res->hw_intf->hw_idx, res->res_type, + res->res_id); + return -EINVAL; + } + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->mux_rsrc[i].res_id == res->res_id) { + top_priv->req_clk_rate[i] = clk_update->clk_rate; + break; + } + } + + if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "VFE:%d Not ready to set clocks yet :%d", + res->hw_intf->hw_idx, + hw_info->hw_state); + } else + rc = cam_vfe_top_set_hw_clk_rate(top_priv); + + return rc; +} + +static int cam_vfe_top_bw_update( + struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + struct cam_vfe_bw_update_args *bw_update = NULL; + struct cam_isp_resource_node *res = NULL; + struct cam_hw_info *hw_info = NULL; + int rc = 0; + int i; + + bw_update = (struct cam_vfe_bw_update_args *)cmd_args; + res = bw_update->node_res; + + if (!res || !res->hw_intf->hw_priv) + return -EINVAL; + + hw_info = res->hw_intf->hw_priv; + + if (res->res_type != CAM_ISP_RESOURCE_VFE_IN || + res->res_id >= CAM_ISP_HW_VFE_IN_MAX) { + CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d", + res->hw_intf->hw_idx, res->res_type, + res->res_id); + return -EINVAL; + } + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->mux_rsrc[i].res_id == res->res_id) { + top_priv->req_axi_vote[i].uncompressed_bw = + bw_update->camnoc_bw_bytes; + top_priv->req_axi_vote[i].compressed_bw = + bw_update->external_bw_bytes; + top_priv->axi_vote_control[i] = + CAM_VFE_BW_CONTROL_INCLUDE; + break; + } + } + + if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "VFE:%d Not ready to set BW yet :%d", + res->hw_intf->hw_idx, + hw_info->hw_state); + } else + rc = cam_vfe_top_set_axi_bw_vote(top_priv, false); + + return rc; +} + +static int cam_vfe_top_bw_control( + struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + struct cam_vfe_bw_control_args *bw_ctrl = NULL; + struct cam_isp_resource_node *res = NULL; + struct cam_hw_info *hw_info = NULL; + int rc = 0; + int i; + + bw_ctrl = (struct cam_vfe_bw_control_args *)cmd_args; + res = bw_ctrl->node_res; + + if (!res || !res->hw_intf->hw_priv) + return -EINVAL; + + hw_info = res->hw_intf->hw_priv; + + if (res->res_type != CAM_ISP_RESOURCE_VFE_IN || + res->res_id >= CAM_ISP_HW_VFE_IN_MAX) { + CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d", + res->hw_intf->hw_idx, res->res_type, + res->res_id); + return -EINVAL; + } + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->mux_rsrc[i].res_id == res->res_id) { + top_priv->axi_vote_control[i] = bw_ctrl->action; + break; + } + } + + if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "VFE:%d Not ready to set BW yet :%d", + res->hw_intf->hw_idx, + hw_info->hw_state); + } else { + rc = cam_vfe_top_set_axi_bw_vote(top_priv, true); + } + + return rc; +} + +static int cam_vfe_top_mux_get_reg_update( + struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + struct cam_isp_hw_get_cmd_update *cmd_update = cmd_args; + + if (cmd_update->res->process_cmd) + return cmd_update->res->process_cmd(cmd_update->res, + CAM_ISP_HW_CMD_GET_REG_UPDATE, cmd_args, arg_size); + + return -EINVAL; +} + +int cam_vfe_top_get_hw_caps(void *device_priv, + void *get_hw_cap_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_top_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_top_reset(void *device_priv, + void *reset_core_args, uint32_t arg_size) +{ + struct cam_vfe_top_ver2_priv *top_priv = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_vfe_top_ver2_reg_offset_common *reg_common = NULL; + uint32_t *reset_reg_args = reset_core_args; + uint32_t reset_reg_val; + + if (!top_priv || !reset_reg_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + switch (*reset_reg_args) { + case CAM_VFE_HW_RESET_HW_AND_REG: + reset_reg_val = CAM_VFE_HW_RESET_HW_AND_REG_VAL; + break; + default: + reset_reg_val = CAM_VFE_HW_RESET_HW_VAL; + break; + } + + CAM_DBG(CAM_ISP, "reset reg value: %x", reset_reg_val); + soc_info = top_priv->common_data.soc_info; + reg_common = top_priv->common_data.common_reg; + + /* Mask All the IRQs except RESET */ + cam_io_w_mb((1 << 31), + CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + 0x5C); + + /* Reset HW */ + cam_io_w_mb(reset_reg_val, + CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + + reg_common->global_reset_cmd); + + CAM_DBG(CAM_ISP, "Reset HW exit"); + return 0; +} + +int cam_vfe_top_reserve(void *device_priv, + void *reserve_args, uint32_t arg_size) +{ + struct cam_vfe_top_ver2_priv *top_priv; + struct cam_vfe_acquire_args *args; + struct cam_vfe_hw_vfe_in_acquire_args *acquire_args; + uint32_t i; + int rc = -EINVAL; + + if (!device_priv || !reserve_args) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; + args = (struct cam_vfe_acquire_args *)reserve_args; + acquire_args = &args->vfe_in; + + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->mux_rsrc[i].res_id == acquire_args->res_id && + top_priv->mux_rsrc[i].res_state == + CAM_ISP_RESOURCE_STATE_AVAILABLE) { + + if (acquire_args->res_id == CAM_ISP_HW_VFE_IN_CAMIF) { + rc = cam_vfe_camif_ver2_acquire_resource( + &top_priv->mux_rsrc[i], + args); + if (rc) + break; + } + + top_priv->mux_rsrc[i].cdm_ops = acquire_args->cdm_ops; + top_priv->mux_rsrc[i].tasklet_info = args->tasklet; + top_priv->mux_rsrc[i].res_state = + CAM_ISP_RESOURCE_STATE_RESERVED; + acquire_args->rsrc_node = + &top_priv->mux_rsrc[i]; + + rc = 0; + break; + } + } + + return rc; + +} + +int cam_vfe_top_release(void *device_priv, + void *release_args, uint32_t arg_size) +{ + struct cam_vfe_top_ver2_priv *top_priv; + struct cam_isp_resource_node *mux_res; + + if (!device_priv || !release_args) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; + mux_res = (struct cam_isp_resource_node *)release_args; + + CAM_DBG(CAM_ISP, "Resource in state %d", mux_res->res_state); + if (mux_res->res_state < CAM_ISP_RESOURCE_STATE_RESERVED) { + CAM_ERR(CAM_ISP, "Error! Resource in Invalid res_state :%d", + mux_res->res_state); + return -EINVAL; + } + mux_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; + + return 0; +} + +int cam_vfe_top_start(void *device_priv, + void *start_args, uint32_t arg_size) +{ + struct cam_vfe_top_ver2_priv *top_priv; + struct cam_isp_resource_node *mux_res; + struct cam_hw_info *hw_info = NULL; + int rc = 0; + + if (!device_priv || !start_args) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; + mux_res = (struct cam_isp_resource_node *)start_args; + hw_info = (struct cam_hw_info *)mux_res->hw_intf->hw_priv; + + if (hw_info->hw_state == CAM_HW_STATE_POWER_UP) { + rc = cam_vfe_top_set_hw_clk_rate(top_priv); + if (rc) { + CAM_ERR(CAM_ISP, + "set_hw_clk_rate failed, rc=%d", rc); + return rc; + } + + rc = cam_vfe_top_set_axi_bw_vote(top_priv, true); + if (rc) { + CAM_ERR(CAM_ISP, + "set_axi_bw_vote failed, rc=%d", rc); + return rc; + } + + if (mux_res->start) { + rc = mux_res->start(mux_res); + } else { + CAM_ERR(CAM_ISP, + "Invalid res id:%d", mux_res->res_id); + rc = -EINVAL; + } + } else { + CAM_ERR(CAM_ISP, "VFE HW not powered up"); + rc = -EPERM; + } + + return rc; +} + +int cam_vfe_top_stop(void *device_priv, + void *stop_args, uint32_t arg_size) +{ + struct cam_vfe_top_ver2_priv *top_priv; + struct cam_isp_resource_node *mux_res; + struct cam_hw_info *hw_info = NULL; + int i, rc = 0; + + if (!device_priv || !stop_args) { + CAM_ERR(CAM_ISP, "Error! Invalid input arguments"); + return -EINVAL; + } + + top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; + mux_res = (struct cam_isp_resource_node *)stop_args; + hw_info = (struct cam_hw_info *)mux_res->hw_intf->hw_priv; + + if (mux_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF || + (mux_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0 && + mux_res->res_id <= CAM_ISP_HW_VFE_IN_RDI3)) { + rc = mux_res->stop(mux_res); + } else { + CAM_ERR(CAM_ISP, "Invalid res id:%d", mux_res->res_id); + return -EINVAL; + } + + if (!rc) { + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + if (top_priv->mux_rsrc[i].res_id == mux_res->res_id) { + top_priv->req_clk_rate[i] = 0; + top_priv->req_axi_vote[i].compressed_bw = 0; + top_priv->req_axi_vote[i].uncompressed_bw = 0; + top_priv->axi_vote_control[i] = + CAM_VFE_BW_CONTROL_EXCLUDE; + break; + } + } + + if (hw_info->hw_state == CAM_HW_STATE_POWER_UP) { + rc = cam_vfe_top_set_hw_clk_rate(top_priv); + if (rc) { + CAM_ERR(CAM_ISP, + "set_hw_clk_rate failed, rc=%d", rc); + return rc; + } + + top_priv->hw_clk_rate = 0; + + rc = cam_vfe_top_set_axi_bw_vote(top_priv, true); + if (rc) { + CAM_ERR(CAM_ISP, + "set_axi_bw_vote failed, rc=%d", rc); + return rc; + } + } else { + CAM_ERR(CAM_ISP, "VFE HW not powered up"); + rc = -EPERM; + } + } + + return rc; +} + +int cam_vfe_top_read(void *device_priv, + void *read_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_top_write(void *device_priv, + void *write_args, uint32_t arg_size) +{ + return -EPERM; +} + +int cam_vfe_top_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + int rc = 0; + struct cam_vfe_top_ver2_priv *top_priv; + + if (!device_priv || !cmd_args) { + CAM_ERR(CAM_ISP, "Error! Invalid arguments"); + return -EINVAL; + } + top_priv = (struct cam_vfe_top_ver2_priv *)device_priv; + + switch (cmd_type) { + case CAM_ISP_HW_CMD_GET_CHANGE_BASE: + rc = cam_vfe_top_mux_get_base(top_priv, cmd_args, arg_size); + break; + case CAM_ISP_HW_CMD_GET_REG_UPDATE: + rc = cam_vfe_top_mux_get_reg_update(top_priv, cmd_args, + arg_size); + break; + case CAM_ISP_HW_CMD_CLOCK_UPDATE: + rc = cam_vfe_top_clock_update(top_priv, cmd_args, + arg_size); + break; + case CAM_ISP_HW_CMD_BW_UPDATE: + rc = cam_vfe_top_bw_update(top_priv, cmd_args, + arg_size); + break; + case CAM_ISP_HW_CMD_BW_CONTROL: + rc = cam_vfe_top_bw_control(top_priv, cmd_args, arg_size); + break; + default: + rc = -EINVAL; + CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type); + break; + } + + return rc; +} + +int cam_vfe_top_ver2_init( + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *top_hw_info, + struct cam_vfe_top **vfe_top_ptr) +{ + int i, j, rc = 0; + struct cam_vfe_top_ver2_priv *top_priv = NULL; + struct cam_vfe_top_ver2_hw_info *ver2_hw_info = top_hw_info; + struct cam_vfe_top *vfe_top; + + vfe_top = kzalloc(sizeof(struct cam_vfe_top), GFP_KERNEL); + if (!vfe_top) { + CAM_DBG(CAM_ISP, "Error! Failed to alloc for vfe_top"); + rc = -ENOMEM; + goto end; + } + + top_priv = kzalloc(sizeof(struct cam_vfe_top_ver2_priv), + GFP_KERNEL); + if (!top_priv) { + CAM_DBG(CAM_ISP, "Error! Failed to alloc for vfe_top_priv"); + rc = -ENOMEM; + goto free_vfe_top; + } + vfe_top->top_priv = top_priv; + top_priv->hw_clk_rate = 0; + top_priv->to_be_applied_axi_vote.compressed_bw = 0; + top_priv->to_be_applied_axi_vote.uncompressed_bw = 0; + top_priv->applied_axi_vote.compressed_bw = 0; + top_priv->applied_axi_vote.uncompressed_bw = 0; + top_priv->counter_to_update_axi_vote = 0; + + for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN; + top_priv->mux_rsrc[i].hw_intf = hw_intf; + top_priv->mux_rsrc[i].res_state = + CAM_ISP_RESOURCE_STATE_AVAILABLE; + top_priv->req_clk_rate[i] = 0; + top_priv->req_axi_vote[i].compressed_bw = 0; + top_priv->req_axi_vote[i].uncompressed_bw = 0; + top_priv->axi_vote_control[i] = CAM_VFE_BW_CONTROL_EXCLUDE; + + + if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) { + top_priv->mux_rsrc[i].res_id = + CAM_ISP_HW_VFE_IN_CAMIF; + + rc = cam_vfe_camif_ver2_init(hw_intf, soc_info, + &ver2_hw_info->camif_hw_info, + &top_priv->mux_rsrc[i]); + if (rc) + goto deinit_resources; + } else { + /* set the RDI resource id */ + top_priv->mux_rsrc[i].res_id = + CAM_ISP_HW_VFE_IN_RDI0 + j++; + + rc = cam_vfe_rdi_ver2_init(hw_intf, soc_info, + &ver2_hw_info->rdi_hw_info, + &top_priv->mux_rsrc[i]); + if (rc) + goto deinit_resources; + } + } + + vfe_top->hw_ops.get_hw_caps = cam_vfe_top_get_hw_caps; + vfe_top->hw_ops.init = cam_vfe_top_init_hw; + vfe_top->hw_ops.reset = cam_vfe_top_reset; + vfe_top->hw_ops.reserve = cam_vfe_top_reserve; + vfe_top->hw_ops.release = cam_vfe_top_release; + vfe_top->hw_ops.start = cam_vfe_top_start; + vfe_top->hw_ops.stop = cam_vfe_top_stop; + vfe_top->hw_ops.read = cam_vfe_top_read; + vfe_top->hw_ops.write = cam_vfe_top_write; + vfe_top->hw_ops.process_cmd = cam_vfe_top_process_cmd; + *vfe_top_ptr = vfe_top; + + top_priv->common_data.soc_info = soc_info; + top_priv->common_data.hw_intf = hw_intf; + top_priv->common_data.common_reg = ver2_hw_info->common_reg; + + return rc; + +deinit_resources: + for (--i; i >= 0; i--) { + if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) { + if (cam_vfe_camif_ver2_deinit(&top_priv->mux_rsrc[i])) + CAM_ERR(CAM_ISP, "Camif Deinit failed"); + } else { + if (cam_vfe_rdi_ver2_deinit(&top_priv->mux_rsrc[i])) + CAM_ERR(CAM_ISP, "RDI Deinit failed"); + } + top_priv->mux_rsrc[i].res_state = + CAM_ISP_RESOURCE_STATE_UNAVAILABLE; + } + + kfree(vfe_top->top_priv); +free_vfe_top: + kfree(vfe_top); +end: + return rc; +} + +int cam_vfe_top_ver2_deinit(struct cam_vfe_top **vfe_top_ptr) +{ + int i, rc = 0; + struct cam_vfe_top_ver2_priv *top_priv = NULL; + struct cam_vfe_top *vfe_top; + + if (!vfe_top_ptr) { + CAM_ERR(CAM_ISP, "Error! Invalid input"); + return -EINVAL; + } + + vfe_top = *vfe_top_ptr; + if (!vfe_top) { + CAM_ERR(CAM_ISP, "Error! vfe_top NULL"); + return -ENODEV; + } + + top_priv = vfe_top->top_priv; + if (!top_priv) { + CAM_ERR(CAM_ISP, "Error! vfe_top_priv NULL"); + rc = -ENODEV; + goto free_vfe_top; + } + + for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) { + top_priv->mux_rsrc[i].res_state = + CAM_ISP_RESOURCE_STATE_UNAVAILABLE; + if (top_priv->mux_rsrc[i].res_id == + CAM_ISP_HW_VFE_IN_CAMIF) { + rc = cam_vfe_camif_ver2_deinit(&top_priv->mux_rsrc[i]); + if (rc) + CAM_ERR(CAM_ISP, "Camif deinit failed rc=%d", + rc); + } else { + rc = cam_vfe_rdi_ver2_deinit(&top_priv->mux_rsrc[i]); + if (rc) + CAM_ERR(CAM_ISP, "RDI deinit failed rc=%d", rc); + } + } + + kfree(vfe_top->top_priv); + +free_vfe_top: + kfree(vfe_top); + *vfe_top_ptr = NULL; + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h new file mode 100644 index 000000000000..bafd7f2badac --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h @@ -0,0 +1,66 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_TOP_VER2_H_ +#define _CAM_VFE_TOP_VER2_H_ + +#include "cam_vfe_camif_ver2.h" +#include "cam_vfe_rdi.h" + +#define CAM_VFE_TOP_VER2_MUX_MAX 4 + +enum cam_vfe_top_ver2_module_type { + CAM_VFE_TOP_VER2_MODULE_LENS, + CAM_VFE_TOP_VER2_MODULE_STATS, + CAM_VFE_TOP_VER2_MODULE_COLOR, + CAM_VFE_TOP_VER2_MODULE_ZOOM, + CAM_VFE_TOP_VER2_MODULE_MAX, +}; + +struct cam_vfe_top_ver2_reg_offset_module_ctrl { + uint32_t reset; + uint32_t cgc_ovd; + uint32_t enable; +}; + +struct cam_vfe_top_ver2_reg_offset_common { + uint32_t hw_version; + uint32_t hw_capability; + uint32_t lens_feature; + uint32_t stats_feature; + uint32_t color_feature; + uint32_t zoom_feature; + uint32_t global_reset_cmd; + struct cam_vfe_top_ver2_reg_offset_module_ctrl + *module_ctrl[CAM_VFE_TOP_VER2_MODULE_MAX]; + uint32_t bus_cgc_ovd; + uint32_t core_cfg; + uint32_t three_D_cfg; + uint32_t violation_status; + uint32_t reg_update_cmd; +}; + +struct cam_vfe_top_ver2_hw_info { + struct cam_vfe_top_ver2_reg_offset_common *common_reg; + struct cam_vfe_camif_ver2_hw_info camif_hw_info; + struct cam_vfe_rdi_ver2_hw_info rdi_hw_info; + uint32_t mux_type[CAM_VFE_TOP_VER2_MUX_MAX]; +}; + +int cam_vfe_top_ver2_init(struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *top_hw_info, + struct cam_vfe_top **vfe_top); + +int cam_vfe_top_ver2_deinit(struct cam_vfe_top **vfe_top); + +#endif /* _CAM_VFE_TOP_VER2_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h new file mode 100644 index 000000000000..81e3b481ecfa --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h @@ -0,0 +1,41 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_VFE_TOP_H_ +#define _CAM_VFE_TOP_H_ + +#include "cam_hw_intf.h" +#include "cam_isp_hw.h" + +#define CAM_VFE_TOP_VER_1_0 0x100000 +#define CAM_VFE_TOP_VER_2_0 0x200000 + +#define CAM_VFE_CAMIF_VER_1_0 0x10 +#define CAM_VFE_CAMIF_VER_2_0 0x20 + +#define CAM_VFE_RDI_VER_1_0 0x1000 + +struct cam_vfe_top { + void *top_priv; + struct cam_hw_ops hw_ops; +}; + +int cam_vfe_top_init(uint32_t top_version, + struct cam_hw_soc_info *soc_info, + struct cam_hw_intf *hw_intf, + void *top_hw_info, + struct cam_vfe_top **vfe_top); + +int cam_vfe_top_deinit(uint32_t top_version, + struct cam_vfe_top **vfe_top); + +#endif /* _CAM_VFE_TOP_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/Makefile new file mode 100644 index 000000000000..0d6ed7d3fdfe --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include/ + +obj-$(CONFIG_SPECTRA_CAMERA) += jpeg_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_jpeg_dev.o cam_jpeg_context.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.c new file mode 100644 index 000000000000..02334a4e8195 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include + +#include "cam_mem_mgr.h" +#include "cam_sync_api.h" +#include "cam_jpeg_context.h" +#include "cam_context_utils.h" +#include "cam_debug_util.h" + +static const char jpeg_dev_name[] = "jpeg"; + +static int __cam_jpeg_ctx_acquire_dev_in_available(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_acquire_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_JPEG, "Unable to Acquire device %d", rc); + else + ctx->state = CAM_CTX_ACQUIRED; + + return rc; +} + +static int __cam_jpeg_ctx_release_dev_in_acquired(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_release_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_JPEG, "Unable to release device %d", rc); + + ctx->state = CAM_CTX_AVAILABLE; + + return rc; +} + +static int __cam_jpeg_ctx_flush_dev_in_acquired(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_flush_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_ICP, "Failed to flush device"); + + return rc; +} + +static int __cam_jpeg_ctx_config_dev_in_acquired(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + return cam_context_prepare_dev_to_hw(ctx, cmd); +} + +static int __cam_jpeg_ctx_handle_buf_done_in_acquired(void *ctx, + uint32_t evt_id, void *done) +{ + return cam_context_buf_done_from_hw(ctx, done, evt_id); +} + +static int __cam_jpeg_ctx_stop_dev_in_acquired(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_stop_dev_to_hw(ctx); + if (rc) { + CAM_ERR(CAM_JPEG, "Failed in Stop dev, rc=%d", rc); + return rc; + } + + return rc; +} + +/* top state machine */ +static struct cam_ctx_ops + cam_jpeg_ctx_state_machine[CAM_CTX_STATE_MAX] = { + /* Uninit */ + { + .ioctl_ops = { }, + .crm_ops = { }, + .irq_ops = NULL, + }, + /* Available */ + { + .ioctl_ops = { + .acquire_dev = __cam_jpeg_ctx_acquire_dev_in_available, + }, + .crm_ops = { }, + .irq_ops = NULL, + }, + /* Acquired */ + { + .ioctl_ops = { + .release_dev = __cam_jpeg_ctx_release_dev_in_acquired, + .config_dev = __cam_jpeg_ctx_config_dev_in_acquired, + .stop_dev = __cam_jpeg_ctx_stop_dev_in_acquired, + .flush_dev = __cam_jpeg_ctx_flush_dev_in_acquired, + }, + .crm_ops = { }, + .irq_ops = __cam_jpeg_ctx_handle_buf_done_in_acquired, + }, +}; + +int cam_jpeg_context_init(struct cam_jpeg_context *ctx, + struct cam_context *ctx_base, + struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id) +{ + int rc; + int i; + + if (!ctx || !ctx_base) { + CAM_ERR(CAM_JPEG, "Invalid Context"); + rc = -EFAULT; + goto err; + } + + memset(ctx, 0, sizeof(*ctx)); + + ctx->base = ctx_base; + + for (i = 0; i < CAM_CTX_REQ_MAX; i++) + ctx->req_base[i].req_priv = ctx; + + rc = cam_context_init(ctx_base, jpeg_dev_name, CAM_JPEG, ctx_id, + NULL, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX); + if (rc) { + CAM_ERR(CAM_JPEG, "Camera Context Base init failed"); + goto err; + } + + ctx_base->state_machine = cam_jpeg_ctx_state_machine; + ctx_base->ctx_priv = ctx; + +err: + return rc; +} + +int cam_jpeg_context_deinit(struct cam_jpeg_context *ctx) +{ + if (!ctx || !ctx->base) { + CAM_ERR(CAM_JPEG, "Invalid params: %pK", ctx); + return -EINVAL; + } + + cam_context_deinit(ctx->base); + + memset(ctx, 0, sizeof(*ctx)); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.h new file mode 100644 index 000000000000..1a406793181b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_context.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2017-2018, 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 _CAM_JPEG_CONTEXT_H_ +#define _CAM_JPEG_CONTEXT_H_ + +#include + +#include "cam_context.h" +#include "cam_jpeg_hw_mgr_intf.h" + +#define CAM_JPEG_HW_EVENT_MAX 20 + +/** + * struct cam_jpeg_context - Jpeg context + * @base: Base jpeg cam context object + * @req_base: Common request structure + */ +struct cam_jpeg_context { + struct cam_context *base; + struct cam_ctx_request req_base[CAM_CTX_REQ_MAX]; +}; + +/* cam jpeg context irq handling function type */ +typedef int (*cam_jpeg_hw_event_cb_func)( + struct cam_jpeg_context *ctx_jpeg, + void *evt_data); + +/** + * struct cam_jpeg_ctx_irq_ops - Function table for handling IRQ callbacks + * + * @irq_ops: Array of handle function pointers. + * + */ +struct cam_jpeg_ctx_irq_ops { + cam_jpeg_hw_event_cb_func irq_ops[CAM_JPEG_HW_EVENT_MAX]; +}; + +/** + * cam_jpeg_context_init() + * + * @brief: Initialization function for the JPEG context + * + * @ctx: JPEG context obj to be initialized + * @ctx_base: Context base from cam_context + * @hw_intf: JPEG hw manager interface + * @ctx_id: ID for this context + * + */ +int cam_jpeg_context_init(struct cam_jpeg_context *ctx, + struct cam_context *ctx_base, + struct cam_hw_mgr_intf *hw_intf, + uint32_t ctx_id); + +/** + * cam_jpeg_context_deinit() + * + * @brief: Deinitialize function for the JPEG context + * + * @ctx: JPEG context obj to be deinitialized + * + */ +int cam_jpeg_context_deinit(struct cam_jpeg_context *ctx); + +#endif /* __CAM_JPEG_CONTEXT_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.c new file mode 100644 index 000000000000..0a4135979b78 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include + +#include "cam_node.h" +#include "cam_hw_mgr_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_jpeg_dev.h" +#include "cam_debug_util.h" +#include "cam_smmu_api.h" + +#define CAM_JPEG_DEV_NAME "cam-jpeg" + +static struct cam_jpeg_dev g_jpeg_dev; + +static void cam_jpeg_dev_iommu_fault_handler( + struct iommu_domain *domain, struct device *dev, unsigned long iova, + int flags, void *token, uint32_t buf_info) +{ + int i = 0; + struct cam_node *node = NULL; + + if (!token) { + CAM_ERR(CAM_JPEG, "invalid token in page handler cb"); + return; + } + + node = (struct cam_node *)token; + + for (i = 0; i < node->ctx_size; i++) + cam_context_dump_pf_info(&(node->ctx_list[i]), iova, + buf_info); +} + +static const struct of_device_id cam_jpeg_dt_match[] = { + { + .compatible = "qcom,cam-jpeg" + }, + { } +}; + +static int cam_jpeg_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_node *node = v4l2_get_subdevdata(sd); + + if (!node) { + CAM_ERR(CAM_JPEG, "Node ptr is NULL"); + return -EINVAL; + } + + cam_node_shutdown(node); + + return 0; +} + +static const struct v4l2_subdev_internal_ops cam_jpeg_subdev_internal_ops = { + .close = cam_jpeg_subdev_close, +}; + +static int cam_jpeg_dev_remove(struct platform_device *pdev) +{ + int rc; + int i; + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_jpeg_context_deinit(&g_jpeg_dev.ctx_jpeg[i]); + if (rc) + CAM_ERR(CAM_JPEG, "JPEG context %d deinit failed %d", + i, rc); + } + + rc = cam_subdev_remove(&g_jpeg_dev.sd); + if (rc) + CAM_ERR(CAM_JPEG, "Unregister failed %d", rc); + + return rc; +} + +static int cam_jpeg_dev_probe(struct platform_device *pdev) +{ + int rc; + int i; + struct cam_hw_mgr_intf hw_mgr_intf; + struct cam_node *node; + int iommu_hdl = -1; + + g_jpeg_dev.sd.internal_ops = &cam_jpeg_subdev_internal_ops; + rc = cam_subdev_probe(&g_jpeg_dev.sd, pdev, CAM_JPEG_DEV_NAME, + CAM_JPEG_DEVICE_TYPE); + if (rc) { + CAM_ERR(CAM_JPEG, "JPEG cam_subdev_probe failed %d", rc); + goto err; + } + node = (struct cam_node *)g_jpeg_dev.sd.token; + + rc = cam_jpeg_hw_mgr_init(pdev->dev.of_node, + (uint64_t *)&hw_mgr_intf, &iommu_hdl); + if (rc) { + CAM_ERR(CAM_JPEG, "Can not initialize JPEG HWmanager %d", rc); + goto unregister; + } + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_jpeg_context_init(&g_jpeg_dev.ctx_jpeg[i], + &g_jpeg_dev.ctx[i], + &node->hw_mgr_intf, + i); + if (rc) { + CAM_ERR(CAM_JPEG, "JPEG context init failed %d %d", + i, rc); + goto ctx_init_fail; + } + } + + rc = cam_node_init(node, &hw_mgr_intf, g_jpeg_dev.ctx, CAM_CTX_MAX, + CAM_JPEG_DEV_NAME); + if (rc) { + CAM_ERR(CAM_JPEG, "JPEG node init failed %d", rc); + goto ctx_init_fail; + } + + cam_smmu_set_client_page_fault_handler(iommu_hdl, + cam_jpeg_dev_iommu_fault_handler, node); + + mutex_init(&g_jpeg_dev.jpeg_mutex); + + CAM_INFO(CAM_JPEG, "Camera JPEG probe complete"); + + return rc; + +ctx_init_fail: + for (--i; i >= 0; i--) + if (cam_jpeg_context_deinit(&g_jpeg_dev.ctx_jpeg[i])) + CAM_ERR(CAM_JPEG, "deinit fail %d %d", i, rc); +unregister: + if (cam_subdev_remove(&g_jpeg_dev.sd)) + CAM_ERR(CAM_JPEG, "remove fail %d", rc); +err: + return rc; +} + +static struct platform_driver jpeg_driver = { + .probe = cam_jpeg_dev_probe, + .remove = cam_jpeg_dev_remove, + .driver = { + .name = "cam_jpeg", + .owner = THIS_MODULE, + .of_match_table = cam_jpeg_dt_match, + }, +}; + +static int __init cam_jpeg_dev_init_module(void) +{ + return platform_driver_register(&jpeg_driver); +} + +static void __exit cam_jpeg_dev_exit_module(void) +{ + platform_driver_unregister(&jpeg_driver); +} + +module_init(cam_jpeg_dev_init_module); +module_exit(cam_jpeg_dev_exit_module); +MODULE_DESCRIPTION("MSM JPEG driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.h new file mode 100644 index 000000000000..deab2d5c0d02 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/cam_jpeg_dev.h @@ -0,0 +1,37 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_JPEG_DEV_H_ +#define _CAM_JPEG_DEV_H_ + +#include "cam_subdev.h" +#include "cam_hw_mgr_intf.h" +#include "cam_context.h" +#include "cam_jpeg_context.h" + +/** + * struct cam_jpeg_dev - Camera JPEG V4l2 device node + * + * @sd: Commone camera subdevice node + * @node: Pointer to jpeg subdevice + * @ctx: JPEG base context storage + * @ctx_jpeg: JPEG private context storage + * @jpeg_mutex: Jpeg dev mutex + */ +struct cam_jpeg_dev { + struct cam_subdev sd; + struct cam_node *node; + struct cam_context ctx[CAM_CTX_MAX]; + struct cam_jpeg_context ctx_jpeg[CAM_CTX_MAX]; + struct mutex jpeg_mutex; +}; +#endif /* __CAM_JPEG_DEV_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/Makefile new file mode 100644 index 000000000000..ee6adb8c853c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/Makefile @@ -0,0 +1,13 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/ + +obj-$(CONFIG_SPECTRA_CAMERA) += jpeg_enc_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += jpeg_dma_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_jpeg_hw_mgr.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c new file mode 100644 index 000000000000..1542890b731b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c @@ -0,0 +1,1488 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam_sync_api.h" +#include "cam_packet_util.h" +#include "cam_hw.h" +#include "cam_hw_mgr_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_jpeg_hw_mgr.h" +#include "cam_smmu_api.h" +#include "cam_mem_mgr.h" +#include "cam_req_mgr_workq.h" +#include "cam_mem_mgr.h" +#include "cam_cdm_intf_api.h" +#include "cam_debug_util.h" + +#define CAM_JPEG_HW_ENTRIES_MAX 20 +#define CAM_JPEG_CHBASE 0 +#define CAM_JPEG_CFG 1 +#define CAM_JPEG_PARAM 2 + +static struct cam_jpeg_hw_mgr g_jpeg_hw_mgr; + +static int32_t cam_jpeg_hw_mgr_cb(uint32_t irq_status, + int32_t result_size, void *data); +static int cam_jpeg_mgr_process_cmd(void *priv, void *data); + +static int cam_jpeg_mgr_process_irq(void *priv, void *data) +{ + int rc = 0; + int mem_hdl = 0; + struct cam_jpeg_process_irq_work_data_t *task_data; + struct cam_jpeg_hw_mgr *hw_mgr; + int32_t i; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + struct cam_hw_done_event_data buf_data; + struct cam_jpeg_set_irq_cb irq_cb; + uintptr_t dev_type = 0; + uintptr_t kaddr; + uint32_t *cmd_buf_kaddr; + size_t cmd_buf_len; + struct cam_jpeg_config_inout_param_info *p_params; + struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL; + struct crm_workq_task *task; + struct cam_jpeg_process_frame_work_data_t *wq_task_data; + + if (!data || !priv) { + CAM_ERR(CAM_JPEG, "Invalid data"); + return -EINVAL; + } + + task_data = data; + hw_mgr = &g_jpeg_hw_mgr; + + ctx_data = (struct cam_jpeg_hw_ctx_data *)task_data->data; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + return -EINVAL; + } + + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + + mutex_lock(&g_jpeg_hw_mgr.hw_mgr_mutex); + + p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0]; + + if (hw_mgr->device_in_use[dev_type][0] == false || + p_cfg_req == NULL) { + CAM_ERR(CAM_JPEG, "irq for old request %d", rc); + mutex_unlock(&g_jpeg_hw_mgr.hw_mgr_mutex); + return -EINVAL; + } + + irq_cb.jpeg_hw_mgr_cb = cam_jpeg_hw_mgr_cb; + irq_cb.data = NULL; + irq_cb.b_set_cb = false; + if (!hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) { + CAM_ERR(CAM_JPEG, "process_cmd null "); + mutex_unlock(&g_jpeg_hw_mgr.hw_mgr_mutex); + return -EINVAL; + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd( + hw_mgr->devices[dev_type][0]->hw_priv, + CAM_JPEG_CMD_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) { + CAM_ERR(CAM_JPEG, "CMD_SET_IRQ_CB failed %d", rc); + mutex_unlock(&g_jpeg_hw_mgr.hw_mgr_mutex); + return rc; + } + + if (hw_mgr->devices[dev_type][0]->hw_ops.deinit) { + rc = hw_mgr->devices[dev_type][0]->hw_ops.deinit( + hw_mgr->devices[dev_type][0]->hw_priv, NULL, 0); + if (rc) + CAM_ERR(CAM_JPEG, "Failed to Deinit %d HW", dev_type); + } + + hw_mgr->device_in_use[dev_type][0] = false; + hw_mgr->dev_hw_cfg_args[dev_type][0] = NULL; + mutex_unlock(&g_jpeg_hw_mgr.hw_mgr_mutex); + + task = cam_req_mgr_workq_get_task( + g_jpeg_hw_mgr.work_process_frame); + if (!task) { + CAM_ERR(CAM_JPEG, "no empty task"); + return -EINVAL; + } + + wq_task_data = (struct cam_jpeg_process_frame_work_data_t *) + task->payload; + if (!task_data) { + CAM_ERR(CAM_JPEG, "task_data is NULL"); + return -EINVAL; + } + wq_task_data->data = (void *)(uint64_t)dev_type; + wq_task_data->request_id = 0; + wq_task_data->type = CAM_JPEG_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_jpeg_mgr_process_cmd; + rc = cam_req_mgr_workq_enqueue_task(task, &g_jpeg_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) { + CAM_ERR(CAM_JPEG, "could not enque task %d", rc); + return rc; + } + + mem_hdl = + p_cfg_req->hw_cfg_args.hw_update_entries[CAM_JPEG_PARAM].handle; + rc = cam_mem_get_cpu_buf(mem_hdl, &kaddr, &cmd_buf_len); + if (rc) { + CAM_ERR(CAM_JPEG, "unable to get info for cmd buf: %x %d", + hw_mgr->iommu_hdl, rc); + return rc; + } + + cmd_buf_kaddr = (uint32_t *)kaddr; + + if ((p_cfg_req->hw_cfg_args.hw_update_entries[CAM_JPEG_PARAM].offset / + sizeof(uint32_t)) >= cmd_buf_len) { + CAM_ERR(CAM_JPEG, "Invalid offset: %u cmd buf len: %zu", + p_cfg_req->hw_cfg_args.hw_update_entries[ + CAM_JPEG_PARAM].offset, cmd_buf_len); + return -EINVAL; + } + + cmd_buf_kaddr = + (cmd_buf_kaddr + (p_cfg_req->hw_cfg_args. + hw_update_entries[CAM_JPEG_PARAM].offset / sizeof(uint32_t))); + + p_params = (struct cam_jpeg_config_inout_param_info *)cmd_buf_kaddr; + + p_params->output_size = task_data->result_size; + CAM_DBG(CAM_JPEG, "Encoded Size %d", task_data->result_size); + + buf_data.num_handles = p_cfg_req-> + hw_cfg_args.num_out_map_entries; + for (i = 0; i < buf_data.num_handles; i++) { + buf_data.resource_handle[i] = + p_cfg_req->hw_cfg_args. + out_map_entries[i].resource_handle; + } + buf_data.request_id = + (uint64_t)p_cfg_req->hw_cfg_args.priv; + ctx_data->ctxt_event_cb(ctx_data->context_priv, 0, &buf_data); + + list_add_tail(&p_cfg_req->list, &hw_mgr->free_req_list); + + return rc; +} + +static int cam_jpeg_hw_mgr_cb( + uint32_t irq_status, int32_t result_size, void *data) +{ + int32_t rc; + unsigned long flags; + struct cam_jpeg_hw_mgr *hw_mgr = &g_jpeg_hw_mgr; + struct crm_workq_task *task; + struct cam_jpeg_process_irq_work_data_t *task_data; + + spin_lock_irqsave(&hw_mgr->hw_mgr_lock, flags); + task = cam_req_mgr_workq_get_task( + g_jpeg_hw_mgr.work_process_irq_cb); + if (!task) { + CAM_ERR(CAM_JPEG, "no empty task"); + spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags); + return -ENOMEM; + } + + task_data = (struct cam_jpeg_process_irq_work_data_t *)task->payload; + task_data->data = data; + task_data->irq_status = irq_status; + task_data->result_size = result_size; + task_data->type = CAM_JPEG_WORKQ_TASK_MSG_TYPE; + task->process_cb = cam_jpeg_mgr_process_irq; + + rc = cam_req_mgr_workq_enqueue_task(task, &g_jpeg_hw_mgr, + CRM_TASK_PRIORITY_0); + spin_unlock_irqrestore(&hw_mgr->hw_mgr_lock, flags); + + return rc; +} + +static int cam_jpeg_mgr_get_free_ctx(struct cam_jpeg_hw_mgr *hw_mgr) +{ + int i = 0; + int num_ctx = CAM_JPEG_CTX_MAX; + + for (i = 0; i < num_ctx; i++) { + mutex_lock(&hw_mgr->ctx_data[i].ctx_mutex); + if (hw_mgr->ctx_data[i].in_use == false) { + hw_mgr->ctx_data[i].in_use = true; + mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex); + break; + } + mutex_unlock(&hw_mgr->ctx_data[i].ctx_mutex); + } + + return i; +} + + +static int cam_jpeg_mgr_release_ctx( + struct cam_jpeg_hw_mgr *hw_mgr, struct cam_jpeg_hw_ctx_data *ctx_data) +{ + if (!ctx_data) { + CAM_ERR(CAM_JPEG, "invalid ctx_data %pK", ctx_data); + return -EINVAL; + } + + mutex_lock(&ctx_data->ctx_mutex); + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is already un-used: %pK", ctx_data); + mutex_unlock(&ctx_data->ctx_mutex); + return -EINVAL; + } + + ctx_data->in_use = false; + mutex_unlock(&ctx_data->ctx_mutex); + + return 0; +} + +static int cam_jpeg_insert_cdm_change_base( + struct cam_hw_config_args *config_args, + struct cam_jpeg_hw_ctx_data *ctx_data, + struct cam_jpeg_hw_mgr *hw_mgr) +{ + int rc; + uint32_t dev_type; + struct cam_cdm_bl_request *cdm_cmd; + uint32_t size; + uint32_t mem_cam_base; + uintptr_t iova_addr; + uint32_t *ch_base_iova_addr; + size_t ch_base_len; + + rc = cam_mem_get_cpu_buf(config_args-> + hw_update_entries[CAM_JPEG_CHBASE].handle, + &iova_addr, &ch_base_len); + if (rc) { + CAM_ERR(CAM_JPEG, + "unable to get src buf info for cmd buf: %d", rc); + return rc; + } + + if (config_args->hw_update_entries[CAM_JPEG_CHBASE].offset >= + ch_base_len) { + CAM_ERR(CAM_JPEG, "Not enough buf"); + return -EINVAL; + } + CAM_DBG(CAM_JPEG, "iova %pK len %zu offset %d", + (void *)iova_addr, ch_base_len, + config_args->hw_update_entries[CAM_JPEG_CHBASE].offset); + ch_base_iova_addr = (uint32_t *)iova_addr; + ch_base_iova_addr = (ch_base_iova_addr + + (config_args->hw_update_entries[CAM_JPEG_CHBASE].offset / + sizeof(uint32_t))); + + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + mem_cam_base = hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base; + size = hw_mgr->cdm_info[dev_type][0].cdm_ops-> + cdm_required_size_changebase(); + hw_mgr->cdm_info[dev_type][0].cdm_ops-> + cdm_write_changebase(ch_base_iova_addr, mem_cam_base); + + cdm_cmd = ctx_data->cdm_cmd; + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].bl_addr.mem_handle = + config_args->hw_update_entries[CAM_JPEG_CHBASE].handle; + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset = + config_args->hw_update_entries[CAM_JPEG_CHBASE].offset; + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len = size * sizeof(uint32_t); + cdm_cmd->cmd_arrary_count++; + + ch_base_iova_addr += size; + *ch_base_iova_addr = 0; + ch_base_iova_addr += size; + *ch_base_iova_addr = 0; + + return rc; +} + +static int cam_jpeg_mgr_process_cmd(void *priv, void *data) +{ + int rc; + int i = 0; + struct cam_jpeg_hw_mgr *hw_mgr = priv; + struct cam_hw_update_entry *cmd; + struct cam_cdm_bl_request *cdm_cmd; + struct cam_hw_config_args *config_args = NULL; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + uint64_t request_id = 0; + struct cam_jpeg_process_frame_work_data_t *task_data = + (struct cam_jpeg_process_frame_work_data_t *)data; + uint32_t dev_type; + struct cam_jpeg_set_irq_cb irq_cb; + struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL; + struct cam_hw_done_event_data buf_data; + + if (!hw_mgr || !task_data) { + CAM_ERR(CAM_JPEG, "Invalid arguments %pK %pK", + hw_mgr, task_data); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + + if (list_empty(&hw_mgr->hw_config_req_list)) { + CAM_DBG(CAM_JPEG, "no available request"); + rc = -EFAULT; + goto end; + } + + p_cfg_req = list_first_entry(&hw_mgr->hw_config_req_list, + struct cam_jpeg_hw_cfg_req, list); + if (!p_cfg_req) { + CAM_ERR(CAM_JPEG, "no request"); + rc = -EFAULT; + goto end; + } + + if (false == hw_mgr->device_in_use[p_cfg_req->dev_type][0]) { + hw_mgr->device_in_use[p_cfg_req->dev_type][0] = true; + hw_mgr->dev_hw_cfg_args[p_cfg_req->dev_type][0] = p_cfg_req; + list_del_init(&p_cfg_req->list); + } else { + CAM_DBG(CAM_JPEG, "Not dequeing, just return"); + rc = -EFAULT; + goto end; + } + + config_args = (struct cam_hw_config_args *)&p_cfg_req->hw_cfg_args; + request_id = task_data->request_id; + if (request_id != (uint64_t)config_args->priv) { + CAM_DBG(CAM_JPEG, "not a recent req %lld %lld", + request_id, (uint64_t)config_args->priv); + } + + if (!config_args->num_hw_update_entries) { + CAM_ERR(CAM_JPEG, "No hw update enteries are available"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + rc = -EINVAL; + goto end_unusedev; + } + + ctx_data = (struct cam_jpeg_hw_ctx_data *)config_args->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + rc = -EINVAL; + goto end_unusedev; + } + + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + + if (dev_type != p_cfg_req->dev_type) + CAM_WARN(CAM_JPEG, "dev types not same something wrong"); + + if (!hw_mgr->devices[dev_type][0]->hw_ops.init) { + CAM_ERR(CAM_JPEG, "hw op init null "); + rc = -EFAULT; + goto end; + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.init( + hw_mgr->devices[dev_type][0]->hw_priv, + ctx_data, + sizeof(ctx_data)); + if (rc) { + CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type); + goto end; + } + + irq_cb.jpeg_hw_mgr_cb = cam_jpeg_hw_mgr_cb; + irq_cb.data = (void *)ctx_data; + irq_cb.b_set_cb = true; + if (!hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) { + CAM_ERR(CAM_JPEG, "op process_cmd null "); + rc = -EFAULT; + goto end_callcb; + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd( + hw_mgr->devices[dev_type][0]->hw_priv, + CAM_JPEG_CMD_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) { + CAM_ERR(CAM_JPEG, "SET_IRQ_CB failed %d", rc); + goto end_callcb; + } + + if (!hw_mgr->devices[dev_type][0]->hw_ops.reset) { + CAM_ERR(CAM_JPEG, "op reset null "); + rc = -EFAULT; + goto end_callcb; + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.reset( + hw_mgr->devices[dev_type][0]->hw_priv, + NULL, 0); + if (rc) { + CAM_ERR(CAM_JPEG, "jpeg hw reset failed %d", rc); + goto end_callcb; + } + + cdm_cmd = ctx_data->cdm_cmd; + cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE; + cdm_cmd->flag = false; + cdm_cmd->userdata = NULL; + cdm_cmd->cookie = 0; + cdm_cmd->cmd_arrary_count = 0; + + rc = cam_jpeg_insert_cdm_change_base(config_args, + ctx_data, hw_mgr); + if (rc) { + CAM_ERR(CAM_JPEG, "insert change base failed %d", rc); + goto end_callcb; + } + + CAM_DBG(CAM_JPEG, "num hw up %d", config_args->num_hw_update_entries); + for (i = CAM_JPEG_CFG; i < (config_args->num_hw_update_entries - 1); + i++) { + cmd = (config_args->hw_update_entries + i); + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count]. + bl_addr.mem_handle = cmd->handle; + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset = + cmd->offset; + cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len = + cmd->len; + CAM_DBG(CAM_JPEG, "i %d entry h %d o %d l %d", + i, cmd->handle, cmd->offset, cmd->len); + cdm_cmd->cmd_arrary_count++; + } + + rc = cam_cdm_submit_bls( + hw_mgr->cdm_info[dev_type][0].cdm_handle, + cdm_cmd); + if (rc) { + CAM_ERR(CAM_JPEG, "Failed to apply the configs %d", rc); + goto end_callcb; + } + + if (!hw_mgr->devices[dev_type][0]->hw_ops.start) { + CAM_ERR(CAM_JPEG, "op start null "); + rc = -EINVAL; + goto end_callcb; + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.start( + hw_mgr->devices[dev_type][0]->hw_priv, + NULL, 0); + if (rc) { + CAM_ERR(CAM_JPEG, "Failed to start hw %d", + rc); + goto end_callcb; + } + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; + +end_callcb: + mutex_unlock(&hw_mgr->hw_mgr_mutex); + if (p_cfg_req) { + buf_data.num_handles = p_cfg_req-> + hw_cfg_args.num_out_map_entries; + for (i = 0; i < buf_data.num_handles; i++) { + buf_data.resource_handle[i] = + p_cfg_req->hw_cfg_args. + out_map_entries[i].resource_handle; + } + buf_data.request_id = + (uint64_t)p_cfg_req->hw_cfg_args.priv; + ctx_data->ctxt_event_cb(ctx_data->context_priv, 0, &buf_data); + } + +end_unusedev: + mutex_lock(&hw_mgr->hw_mgr_mutex); + hw_mgr->device_in_use[p_cfg_req->dev_type][0] = false; + hw_mgr->dev_hw_cfg_args[p_cfg_req->dev_type][0] = NULL; + +end: + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int cam_jpeg_mgr_config_hw(void *hw_mgr_priv, void *config_hw_args) +{ + int rc; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_config_args *config_args = config_hw_args; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + uint64_t request_id = 0; + struct cam_hw_update_entry *hw_update_entries; + struct crm_workq_task *task; + struct cam_jpeg_process_frame_work_data_t *task_data; + struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL; + + if (!hw_mgr || !config_args) { + CAM_ERR(CAM_JPEG, "Invalid arguments %pK %pK", + hw_mgr, config_args); + return -EINVAL; + } + + if (!config_args->num_hw_update_entries) { + CAM_ERR(CAM_JPEG, "No hw update enteries are available"); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + + ctx_data = (struct cam_jpeg_hw_ctx_data *)config_args->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + + if (list_empty(&hw_mgr->free_req_list)) { + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_ERR(CAM_JPEG, "list empty"); + return -ENOMEM; + } + + p_cfg_req = list_first_entry(&hw_mgr->free_req_list, + struct cam_jpeg_hw_cfg_req, list); + list_del_init(&p_cfg_req->list); + + /* Update Currently Processing Config Request */ + p_cfg_req->hw_cfg_args = *config_args; + p_cfg_req->dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + + request_id = (uint64_t)config_args->priv; + p_cfg_req->req_id = request_id; + hw_update_entries = config_args->hw_update_entries; + CAM_DBG(CAM_JPEG, "ctx_data = %pK req_id = %lld %lld", + ctx_data, request_id, (uint64_t)config_args->priv); + task = cam_req_mgr_workq_get_task(g_jpeg_hw_mgr.work_process_frame); + if (!task) { + CAM_ERR(CAM_JPEG, "no empty task"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + rc = -ENOMEM; + goto err_after_dq_free_list; + } + + + task_data = (struct cam_jpeg_process_frame_work_data_t *) + task->payload; + if (!task_data) { + CAM_ERR(CAM_JPEG, "task_data is NULL"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + rc = -EINVAL; + goto err_after_dq_free_list; + } + CAM_DBG(CAM_JPEG, "cfge %pK num %d", + p_cfg_req->hw_cfg_args.hw_update_entries, + p_cfg_req->hw_cfg_args.num_hw_update_entries); + + list_add_tail(&p_cfg_req->list, &hw_mgr->hw_config_req_list); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + task_data->data = (void *)(int64_t)p_cfg_req->dev_type; + task_data->request_id = request_id; + task_data->type = CAM_JPEG_WORKQ_TASK_CMD_TYPE; + task->process_cb = cam_jpeg_mgr_process_cmd; + + rc = cam_req_mgr_workq_enqueue_task(task, &g_jpeg_hw_mgr, + CRM_TASK_PRIORITY_0); + if (rc) { + CAM_ERR(CAM_JPEG, "failed to enqueue task %d", rc); + goto err_after_get_task; + } + + return rc; + +err_after_get_task: + list_del_init(&p_cfg_req->list); +err_after_dq_free_list: + list_add_tail(&p_cfg_req->list, &hw_mgr->free_req_list); + + return rc; +} + + +static int cam_jpeg_mgr_prepare_hw_update(void *hw_mgr_priv, + void *prepare_hw_update_args) +{ + int rc, i, j, k; + struct cam_hw_prepare_update_args *prepare_args = + prepare_hw_update_args; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + struct cam_packet *packet = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct cam_buf_io_cfg *io_cfg_ptr = NULL; + struct cam_kmd_buf_info kmd_buf; + + if (!prepare_args || !hw_mgr) { + CAM_ERR(CAM_JPEG, "Invalid args %pK %pK", + prepare_args, hw_mgr); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + ctx_data = (struct cam_jpeg_hw_ctx_data *)prepare_args->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + packet = prepare_args->packet; + if (!packet) { + CAM_ERR(CAM_JPEG, "received packet is NULL"); + return -EINVAL; + } + + if (((packet->header.op_code & 0xff) != CAM_JPEG_OPCODE_ENC_UPDATE) && + ((packet->header.op_code + & 0xff) != CAM_JPEG_OPCODE_DMA_UPDATE)) { + CAM_ERR(CAM_JPEG, "Invalid Opcode in pkt: %d", + packet->header.op_code & 0xff); + return -EINVAL; + } + + rc = cam_packet_util_validate_packet(packet); + if (rc) { + CAM_ERR(CAM_JPEG, "invalid packet %d", rc); + return rc; + } + + if ((packet->num_cmd_buf > 5) || !packet->num_patches || + !packet->num_io_configs) { + CAM_ERR(CAM_JPEG, "wrong number of cmd/patch info: %u %u", + packet->num_cmd_buf, + packet->num_patches); + return -EINVAL; + } + + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *)&packet->payload + + (packet->cmd_buf_offset / 4)); + CAM_DBG(CAM_JPEG, "packet = %pK cmd_desc = %pK size = %lu", + (void *)packet, (void *)cmd_desc, + sizeof(struct cam_cmd_buf_desc)); + + rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl, -1); + if (rc) { + CAM_ERR(CAM_JPEG, "Patch processing failed %d", rc); + return rc; + } + + io_cfg_ptr = (struct cam_buf_io_cfg *)((uint32_t *)&packet->payload + + packet->io_configs_offset / 4); + CAM_DBG(CAM_JPEG, "packet = %pK io_cfg_ptr = %pK size = %lu", + (void *)packet, (void *)io_cfg_ptr, + sizeof(struct cam_buf_io_cfg)); + + prepare_args->num_out_map_entries = 0; + + for (i = 0, j = 0, k = 0; i < packet->num_io_configs; i++) { + if (io_cfg_ptr[i].direction == CAM_BUF_INPUT) { + prepare_args->in_map_entries[j].resource_handle = + io_cfg_ptr[i].resource_type; + prepare_args->in_map_entries[j++].sync_id = + io_cfg_ptr[i].fence; + prepare_args->num_in_map_entries++; + } else { + prepare_args->in_map_entries[k].resource_handle = + io_cfg_ptr[i].resource_type; + prepare_args->out_map_entries[k++].sync_id = + io_cfg_ptr[i].fence; + prepare_args->num_out_map_entries++; + } + CAM_DBG(CAM_JPEG, "dir[%d]: %u, fence: %u", + i, io_cfg_ptr[i].direction, io_cfg_ptr[i].fence); + } + + + j = prepare_args->num_hw_update_entries; + rc = cam_packet_util_get_kmd_buffer(packet, &kmd_buf); + if (rc) { + CAM_ERR(CAM_JPEG, "get kmd buf failed %d", rc); + return rc; + } + /* fill kmd buf info into 1st hw update entry */ + prepare_args->hw_update_entries[j].len = + (uint32_t)kmd_buf.used_bytes; + prepare_args->hw_update_entries[j].handle = + (uint32_t)kmd_buf.handle; + prepare_args->hw_update_entries[j].offset = + (uint32_t)kmd_buf.offset; + j++; + + for (i = 0; i < packet->num_cmd_buf; i++, j++) { + prepare_args->hw_update_entries[j].len = + (uint32_t)cmd_desc[i].length; + prepare_args->hw_update_entries[j].handle = + (uint32_t)cmd_desc[i].mem_handle; + prepare_args->hw_update_entries[j].offset = + (uint32_t)cmd_desc[i].offset; + } + prepare_args->num_hw_update_entries = j; + prepare_args->priv = (void *)packet->header.request_id; + + CAM_DBG(CAM_JPEG, "will wait on input sync sync_id %d", + prepare_args->in_map_entries[0].sync_id); + + return rc; +} + +static int cam_jpeg_mgr_flush(void *hw_mgr_priv, + struct cam_jpeg_hw_ctx_data *ctx_data) +{ + int rc = 0; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + uint32_t dev_type; + struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL; + struct cam_jpeg_hw_cfg_req *cfg_req = NULL, *req_temp = NULL; + struct cam_jpeg_set_irq_cb irq_cb; + + CAM_DBG(CAM_JPEG, "E: JPEG flush ctx"); + + if (!hw_mgr || !ctx_data) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + + p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0]; + if (hw_mgr->device_in_use[dev_type][0] == true && + p_cfg_req != NULL) { + if ((struct cam_jpeg_hw_ctx_data *)p_cfg_req-> + hw_cfg_args.ctxt_to_hw_map == ctx_data) { + /* stop reset Unregister CB and deinit */ + irq_cb.jpeg_hw_mgr_cb = cam_jpeg_hw_mgr_cb; + irq_cb.data = NULL; + irq_cb.b_set_cb = false; + if (hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) { + rc = hw_mgr->devices[dev_type][0]-> + hw_ops.process_cmd( + hw_mgr->devices[dev_type][0]->hw_priv, + CAM_JPEG_CMD_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) + CAM_ERR(CAM_JPEG, + "CMD_SET_IRQ_CB failed %d", rc); + + } else { + CAM_ERR(CAM_JPEG, "process_cmd null "); + } + rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd( + hw_mgr->devices[dev_type][0]->hw_priv, + CAM_JPEG_CMD_SET_IRQ_CB, + &irq_cb, sizeof(irq_cb)); + if (rc) + CAM_ERR(CAM_JPEG, + "CMD_SET_IRQ_CB failed %d", rc); + + if (hw_mgr->devices[dev_type][0]->hw_ops.stop) { + rc = hw_mgr->devices[dev_type][0]->hw_ops.stop( + hw_mgr->devices[dev_type][0]->hw_priv, + NULL, 0); + if (rc) + CAM_ERR(CAM_JPEG, "stop fail %d", rc); + } else { + CAM_ERR(CAM_JPEG, "op stop null "); + } + + if (hw_mgr->devices[dev_type][0]->hw_ops.deinit) { + rc = hw_mgr->devices[dev_type][0] + ->hw_ops.deinit( + hw_mgr->devices[dev_type][0]->hw_priv, + NULL, 0); + if (rc) + CAM_ERR(CAM_JPEG, + "Failed to Deinit %d HW", + dev_type); + } else { + CAM_ERR(CAM_JPEG, "op deinit null"); + } + } + + hw_mgr->device_in_use[dev_type][0] = false; + p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0]; + hw_mgr->dev_hw_cfg_args[dev_type][0] = NULL; + } + + list_for_each_entry_safe(cfg_req, req_temp, + &hw_mgr->hw_config_req_list, list) { + if ((cfg_req) && ((struct cam_jpeg_hw_ctx_data *)cfg_req-> + hw_cfg_args.ctxt_to_hw_map != ctx_data)) + continue; + + list_del_init(&cfg_req->list); + } + + CAM_DBG(CAM_JPEG, "X: JPEG flush ctx with rc: %d", rc); + + return rc; +} + + +static int cam_jpeg_mgr_flush_req(void *hw_mgr_priv, + struct cam_jpeg_hw_ctx_data *ctx_data, + struct cam_hw_flush_args *flush_args) +{ + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_cfg_req *cfg_req, *req_temp; + int64_t request_id; + + CAM_DBG(CAM_JPEG, "E: JPEG flush req"); + + if (!hw_mgr || !ctx_data || !flush_args) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + + if (flush_args->num_req_pending) + return 0; + + request_id = *(int64_t *)flush_args->flush_req_active[0]; + list_for_each_entry_safe(cfg_req, req_temp, + &hw_mgr->hw_config_req_list, list) { + if ((cfg_req) && (cfg_req->hw_cfg_args.ctxt_to_hw_map + != ctx_data)) + continue; + + if (cfg_req->req_id != request_id) + continue; + + list_del_init(&cfg_req->list); + } + + CAM_DBG(CAM_JPEG, "X: JPEG flush req"); + + return 0; +} + +static int cam_jpeg_mgr_hw_flush(void *hw_mgr_priv, void *flush_hw_args) +{ + int rc = 0; + struct cam_hw_flush_args *flush_args = flush_hw_args; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + + if (!hw_mgr || !flush_args || !flush_args->ctxt_to_hw_map) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + mutex_lock(&hw_mgr->hw_mgr_mutex); + + ctx_data = (struct cam_jpeg_hw_ctx_data *)flush_args->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + + if ((flush_args->flush_type >= CAM_FLUSH_TYPE_MAX) || + (flush_args->flush_type < CAM_FLUSH_TYPE_REQ)) { + CAM_ERR(CAM_JPEG, "Invalid flush type: %d", + flush_args->flush_type); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + + switch (flush_args->flush_type) { + case CAM_FLUSH_TYPE_ALL: + rc = cam_jpeg_mgr_flush(hw_mgr_priv, ctx_data); + if ((rc)) + CAM_ERR(CAM_JPEG, "Flush failed %d", rc); + break; + case CAM_FLUSH_TYPE_REQ: + rc = cam_jpeg_mgr_flush_req(hw_mgr_priv, ctx_data, flush_args); + CAM_ERR(CAM_JPEG, "Flush per request is not supported"); + break; + default: + CAM_ERR(CAM_JPEG, "Invalid flush type: %d", + flush_args->flush_type); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_jpeg_mgr_hw_stop(void *hw_mgr_priv, void *stop_hw_args) +{ + int rc; + struct cam_hw_stop_args *stop_args = + (struct cam_hw_stop_args *)stop_hw_args; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + + if (!hw_mgr || !stop_args || !stop_args->ctxt_to_hw_map) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + mutex_lock(&hw_mgr->hw_mgr_mutex); + + ctx_data = (struct cam_jpeg_hw_ctx_data *)stop_args->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + + rc = cam_jpeg_mgr_flush(hw_mgr_priv, ctx_data); + if ((rc)) + CAM_ERR(CAM_JPEG, "flush failed %d", rc); + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_jpeg_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args) +{ + int rc; + struct cam_hw_release_args *release_hw = release_hw_args; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + uint32_t dev_type; + + if (!hw_mgr || !release_hw || !release_hw->ctxt_to_hw_map) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + + ctx_data = (struct cam_jpeg_hw_ctx_data *)release_hw->ctxt_to_hw_map; + if (!ctx_data->in_use) { + CAM_ERR(CAM_JPEG, "ctx is not in use"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EINVAL; + } + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + + mutex_lock(&hw_mgr->hw_mgr_mutex); + if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 0) { + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_ERR(CAM_JPEG, "Error Unbalanced deinit"); + kfree(ctx_data->cdm_cmd); + return -EFAULT; + } + + hw_mgr->cdm_info[dev_type][0].ref_cnt--; + if (!(hw_mgr->cdm_info[dev_type][0].ref_cnt)) { + if (cam_cdm_stream_off( + hw_mgr->cdm_info[dev_type][0].cdm_handle)) { + CAM_ERR(CAM_JPEG, "CDM stream off failed %d", + hw_mgr->cdm_info[dev_type][0].cdm_handle); + } + /* release cdm handle */ + cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle); + } + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + rc = cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data); + if (rc) { + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_ERR(CAM_JPEG, "JPEG release ctx failed"); + kfree(ctx_data->cdm_cmd); + return -EINVAL; + } + + kfree(ctx_data->cdm_cmd); + CAM_DBG(CAM_JPEG, "handle %llu", ctx_data); + + return rc; +} + +static int cam_jpeg_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args) +{ + int rc; + int32_t ctx_id = 0; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_jpeg_hw_ctx_data *ctx_data = NULL; + struct cam_hw_acquire_args *args = acquire_hw_args; + struct cam_jpeg_acquire_dev_info jpeg_dev_acquire_info; + struct cam_cdm_acquire_data cdm_acquire; + uint32_t dev_type; + uint32_t size = 0; + + if ((!hw_mgr_priv) || (!acquire_hw_args)) { + CAM_ERR(CAM_JPEG, "Invalid params: %pK %pK", hw_mgr_priv, + acquire_hw_args); + return -EINVAL; + } + + if (args->num_acq > 1) { + CAM_ERR(CAM_JPEG, + "number of resources are wrong: %u", + args->num_acq); + return -EINVAL; + } + + if (copy_from_user(&jpeg_dev_acquire_info, + (void __user *)args->acquire_info, + sizeof(jpeg_dev_acquire_info))) { + CAM_ERR(CAM_JPEG, "copy failed"); + return -EFAULT; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + ctx_id = cam_jpeg_mgr_get_free_ctx(hw_mgr); + if (ctx_id >= CAM_JPEG_CTX_MAX) { + CAM_ERR(CAM_JPEG, "No free ctx space in hw_mgr"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return -EFAULT; + } + + ctx_data = &hw_mgr->ctx_data[ctx_id]; + + ctx_data->cdm_cmd = + kzalloc(((sizeof(struct cam_cdm_bl_request)) + + ((CAM_JPEG_HW_ENTRIES_MAX - 1) * + sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL); + if (!ctx_data->cdm_cmd) { + rc = -ENOMEM; + goto jpeg_release_ctx; + } + + mutex_lock(&ctx_data->ctx_mutex); + ctx_data->jpeg_dev_acquire_info = jpeg_dev_acquire_info; + mutex_unlock(&ctx_data->ctx_mutex); + + if (ctx_data->jpeg_dev_acquire_info.dev_type >= + CAM_JPEG_RES_TYPE_MAX) { + rc = -EINVAL; + goto acq_cdm_hdl_failed; + } + dev_type = ctx_data->jpeg_dev_acquire_info.dev_type; + if (!hw_mgr->cdm_info[dev_type][0].ref_cnt) { + + if (dev_type == CAM_JPEG_RES_TYPE_ENC) { + memcpy(cdm_acquire.identifier, + "jpegenc", sizeof("jpegenc")); + } else { + memcpy(cdm_acquire.identifier, + "jpegdma", sizeof("jpegdma")); + } + cdm_acquire.cell_index = 0; + cdm_acquire.handle = 0; + cdm_acquire.userdata = ctx_data; + if (hw_mgr->cdm_reg_map[dev_type][0]) { + cdm_acquire.base_array[0] = + hw_mgr->cdm_reg_map[dev_type][0]; + } + cdm_acquire.base_array_cnt = 1; + cdm_acquire.id = CAM_CDM_VIRTUAL; + cdm_acquire.cam_cdm_callback = NULL; + + rc = cam_cdm_acquire(&cdm_acquire); + if (rc) { + CAM_ERR(CAM_JPEG, "Failed to acquire the CDM HW %d", + rc); + rc = -EFAULT; + goto acq_cdm_hdl_failed; + } + hw_mgr->cdm_info[dev_type][0].cdm_handle = cdm_acquire.handle; + hw_mgr->cdm_info[dev_type][0].cdm_ops = cdm_acquire.ops; + hw_mgr->cdm_info[dev_type][0].ref_cnt++; + } else { + hw_mgr->cdm_info[dev_type][0].ref_cnt++; + } + + size = hw_mgr->cdm_info[dev_type][0]. + cdm_ops->cdm_required_size_changebase(); + + if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1) + if (cam_cdm_stream_on( + hw_mgr->cdm_info[dev_type][0].cdm_handle)) { + CAM_ERR(CAM_JPEG, "Can not start cdm (%d)!", + hw_mgr->cdm_info[dev_type][0].cdm_handle); + rc = -EFAULT; + goto start_cdm_hdl_failed; + } + + mutex_lock(&ctx_data->ctx_mutex); + ctx_data->context_priv = args->context_data; + + args->ctxt_to_hw_map = (void *)&(hw_mgr->ctx_data[ctx_id]); + + mutex_unlock(&ctx_data->ctx_mutex); + + hw_mgr->ctx_data[ctx_id].ctxt_event_cb = args->event_cb; + + + if (copy_to_user((void __user *)args->acquire_info, + &jpeg_dev_acquire_info, + sizeof(jpeg_dev_acquire_info))) { + rc = -EFAULT; + goto copy_to_user_failed; + } + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + CAM_DBG(CAM_JPEG, "success ctx_data= %pK", ctx_data); + + return rc; + +copy_to_user_failed: + if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1) + cam_cdm_stream_off(hw_mgr->cdm_info[dev_type][0].cdm_handle); +start_cdm_hdl_failed: + if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1) + cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle); + hw_mgr->cdm_info[dev_type][0].ref_cnt--; +acq_cdm_hdl_failed: + kfree(ctx_data->cdm_cmd); +jpeg_release_ctx: + cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_jpeg_mgr_get_hw_caps(void *hw_mgr_priv, void *hw_caps_args) +{ + int rc; + struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_query_cap_cmd *query_cap = hw_caps_args; + + if (!hw_mgr_priv || !hw_caps_args) { + CAM_ERR(CAM_JPEG, "Invalid params: %pK %pK", + hw_mgr_priv, hw_caps_args); + return -EINVAL; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + + if (copy_to_user(u64_to_user_ptr(query_cap->caps_handle), + &g_jpeg_hw_mgr.jpeg_caps, + sizeof(struct cam_jpeg_query_cap_cmd))) { + CAM_ERR(CAM_JPEG, "copy_to_user failed"); + rc = -EFAULT; + goto copy_error; + } + CAM_DBG(CAM_JPEG, "cam_jpeg_mgr_get_hw_caps success"); + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return 0; + +copy_error: + mutex_unlock(&hw_mgr->hw_mgr_mutex); + return rc; +} + +static int cam_jpeg_setup_workqs(void) +{ + int rc, i; + + rc = cam_req_mgr_workq_create( + "jpeg_command_queue", + CAM_JPEG_WORKQ_NUM_TASK, + &g_jpeg_hw_mgr.work_process_frame, + CRM_WORKQ_USAGE_NON_IRQ); + if (rc) { + CAM_ERR(CAM_JPEG, "unable to create a worker %d", rc); + goto work_process_frame_failed; + } + + rc = cam_req_mgr_workq_create( + "jpeg_message_queue", + CAM_JPEG_WORKQ_NUM_TASK, + &g_jpeg_hw_mgr.work_process_irq_cb, + CRM_WORKQ_USAGE_IRQ); + if (rc) { + CAM_ERR(CAM_JPEG, "unable to create a worker %d", rc); + goto work_process_irq_cb_failed; + } + + g_jpeg_hw_mgr.process_frame_work_data = + (struct cam_jpeg_process_frame_work_data_t *) + kzalloc(sizeof(struct cam_jpeg_process_frame_work_data_t) * + CAM_JPEG_WORKQ_NUM_TASK, GFP_KERNEL); + if (!g_jpeg_hw_mgr.process_frame_work_data) { + rc = -ENOMEM; + goto work_process_frame_data_failed; + } + + g_jpeg_hw_mgr.process_irq_cb_work_data = + (struct cam_jpeg_process_irq_work_data_t *) + kzalloc(sizeof(struct cam_jpeg_process_irq_work_data_t) * + CAM_JPEG_WORKQ_NUM_TASK, GFP_KERNEL); + if (!g_jpeg_hw_mgr.process_irq_cb_work_data) { + rc = -ENOMEM; + goto work_process_irq_cb_data_failed; + } + + for (i = 0; i < CAM_JPEG_WORKQ_NUM_TASK; i++) + g_jpeg_hw_mgr.work_process_irq_cb->task.pool[i].payload = + &g_jpeg_hw_mgr.process_irq_cb_work_data[i]; + + for (i = 0; i < CAM_JPEG_WORKQ_NUM_TASK; i++) + g_jpeg_hw_mgr.work_process_frame->task.pool[i].payload = + &g_jpeg_hw_mgr.process_frame_work_data[i]; + + INIT_LIST_HEAD(&g_jpeg_hw_mgr.hw_config_req_list); + INIT_LIST_HEAD(&g_jpeg_hw_mgr.free_req_list); + for (i = 0; i < CAM_JPEG_HW_CFG_Q_MAX; i++) { + INIT_LIST_HEAD(&(g_jpeg_hw_mgr.req_list[i].list)); + list_add_tail(&(g_jpeg_hw_mgr.req_list[i].list), + &(g_jpeg_hw_mgr.free_req_list)); + } + + return rc; + +work_process_irq_cb_data_failed: + kfree(g_jpeg_hw_mgr.process_frame_work_data); +work_process_frame_data_failed: + cam_req_mgr_workq_destroy(&g_jpeg_hw_mgr.work_process_irq_cb); +work_process_irq_cb_failed: + cam_req_mgr_workq_destroy(&g_jpeg_hw_mgr.work_process_frame); +work_process_frame_failed: + + return rc; +} + +static int cam_jpeg_init_devices(struct device_node *of_node, + uint32_t *p_num_enc_dev, + uint32_t *p_num_dma_dev) +{ + int count, i, rc; + uint32_t num_dev; + uint32_t num_dma_dev; + const char *name = NULL; + struct device_node *child_node = NULL; + struct platform_device *child_pdev = NULL; + struct cam_hw_intf *child_dev_intf = NULL; + struct cam_hw_info *enc_hw = NULL; + struct cam_hw_info *dma_hw = NULL; + struct cam_hw_soc_info *enc_soc_info = NULL; + struct cam_hw_soc_info *dma_soc_info = NULL; + + if (!p_num_enc_dev || !p_num_dma_dev) { + rc = -EINVAL; + goto num_dev_failed; + } + count = of_property_count_strings(of_node, "compat-hw-name"); + if (!count) { + CAM_ERR(CAM_JPEG, + "no compat hw found in dev tree, count = %d", + count); + rc = -EINVAL; + goto num_dev_failed; + } + + rc = of_property_read_u32(of_node, "num-jpeg-enc", &num_dev); + if (rc) { + CAM_ERR(CAM_JPEG, "read num enc devices failed %d", rc); + goto num_enc_failed; + } + g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_ENC] = kzalloc( + sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL); + if (!g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_ENC]) { + rc = -ENOMEM; + CAM_ERR(CAM_JPEG, "getting number of dma dev nodes failed"); + goto num_enc_failed; + } + + rc = of_property_read_u32(of_node, "num-jpeg-dma", &num_dma_dev); + if (rc) { + CAM_ERR(CAM_JPEG, "get num dma dev nodes failed %d", rc); + goto num_dma_failed; + } + + g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_DMA] = kzalloc( + sizeof(struct cam_hw_intf *) * num_dma_dev, GFP_KERNEL); + if (!g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_DMA]) { + rc = -ENOMEM; + goto num_dma_failed; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, "compat-hw-name", + i, &name); + if (rc) { + CAM_ERR(CAM_JPEG, "getting dev object name failed"); + goto compat_hw_name_failed; + } + + child_node = of_find_node_by_name(NULL, name); + if (!child_node) { + CAM_ERR(CAM_JPEG, + "error! Cannot find node in dtsi %s", name); + rc = -ENODEV; + goto compat_hw_name_failed; + } + + child_pdev = of_find_device_by_node(child_node); + if (!child_pdev) { + CAM_ERR(CAM_JPEG, "failed to find device on bus %s", + child_node->name); + rc = -ENODEV; + of_node_put(child_node); + goto compat_hw_name_failed; + } + + child_dev_intf = (struct cam_hw_intf *)platform_get_drvdata( + child_pdev); + if (!child_dev_intf) { + CAM_ERR(CAM_JPEG, "no child device"); + of_node_put(child_node); + rc = -ENODEV; + goto compat_hw_name_failed; + } + CAM_DBG(CAM_JPEG, "child_intf %pK type %d id %d", + child_dev_intf, + child_dev_intf->hw_type, + child_dev_intf->hw_idx); + + if ((child_dev_intf->hw_type == CAM_JPEG_DEV_ENC && + child_dev_intf->hw_idx >= num_dev) || + (child_dev_intf->hw_type == CAM_JPEG_DEV_DMA && + child_dev_intf->hw_idx >= num_dma_dev)) { + CAM_ERR(CAM_JPEG, "index out of range"); + rc = -ENODEV; + goto compat_hw_name_failed; + } + g_jpeg_hw_mgr.devices[child_dev_intf->hw_type] + [child_dev_intf->hw_idx] = child_dev_intf; + + of_node_put(child_node); + } + + enc_hw = (struct cam_hw_info *) + g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_ENC][0]->hw_priv; + enc_soc_info = &enc_hw->soc_info; + g_jpeg_hw_mgr.cdm_reg_map[CAM_JPEG_DEV_ENC][0] = + &enc_soc_info->reg_map[0]; + dma_hw = (struct cam_hw_info *) + g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_DMA][0]->hw_priv; + dma_soc_info = &dma_hw->soc_info; + g_jpeg_hw_mgr.cdm_reg_map[CAM_JPEG_DEV_DMA][0] = + &dma_soc_info->reg_map[0]; + + *p_num_enc_dev = num_dev; + *p_num_dma_dev = num_dma_dev; + + return rc; + +compat_hw_name_failed: + kfree(g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_DMA]); +num_dma_failed: + kfree(g_jpeg_hw_mgr.devices[CAM_JPEG_DEV_ENC]); +num_enc_failed: +num_dev_failed: + + return rc; +} + +int cam_jpeg_hw_mgr_init(struct device_node *of_node, uint64_t *hw_mgr_hdl, + int *iommu_hdl) +{ + int i, rc; + uint32_t num_dev; + uint32_t num_dma_dev; + struct cam_hw_mgr_intf *hw_mgr_intf; + struct cam_iommu_handle cdm_handles; + + hw_mgr_intf = (struct cam_hw_mgr_intf *)hw_mgr_hdl; + if (!of_node || !hw_mgr_intf) { + CAM_ERR(CAM_JPEG, "Invalid args of_node %pK hw_mgr %pK", + of_node, hw_mgr_intf); + return -EINVAL; + } + + memset(hw_mgr_hdl, 0x0, sizeof(struct cam_hw_mgr_intf)); + hw_mgr_intf->hw_mgr_priv = &g_jpeg_hw_mgr; + hw_mgr_intf->hw_get_caps = cam_jpeg_mgr_get_hw_caps; + hw_mgr_intf->hw_acquire = cam_jpeg_mgr_acquire_hw; + hw_mgr_intf->hw_release = cam_jpeg_mgr_release_hw; + hw_mgr_intf->hw_prepare_update = cam_jpeg_mgr_prepare_hw_update; + hw_mgr_intf->hw_config = cam_jpeg_mgr_config_hw; + hw_mgr_intf->hw_flush = cam_jpeg_mgr_hw_flush; + hw_mgr_intf->hw_stop = cam_jpeg_mgr_hw_stop; + + mutex_init(&g_jpeg_hw_mgr.hw_mgr_mutex); + spin_lock_init(&g_jpeg_hw_mgr.hw_mgr_lock); + + for (i = 0; i < CAM_JPEG_CTX_MAX; i++) + mutex_init(&g_jpeg_hw_mgr.ctx_data[i].ctx_mutex); + + rc = cam_jpeg_init_devices(of_node, &num_dev, &num_dma_dev); + if (rc) { + CAM_ERR(CAM_JPEG, "jpeg init devices %d", rc); + goto smmu_get_failed; + } + + rc = cam_smmu_get_handle("jpeg", &g_jpeg_hw_mgr.iommu_hdl); + if (rc) { + CAM_ERR(CAM_JPEG, "jpeg get iommu handle failed %d", rc); + goto smmu_get_failed; + } + + CAM_DBG(CAM_JPEG, "mmu handle :%d", g_jpeg_hw_mgr.iommu_hdl); + + rc = cam_cdm_get_iommu_handle("jpegenc", &cdm_handles); + if (rc) { + CAM_ERR(CAM_JPEG, "acquire cdm iommu handle Fail %d", rc); + g_jpeg_hw_mgr.cdm_iommu_hdl = -1; + g_jpeg_hw_mgr.cdm_iommu_hdl_secure = -1; + goto cdm_iommu_failed; + } + g_jpeg_hw_mgr.cdm_iommu_hdl = cdm_handles.non_secure; + g_jpeg_hw_mgr.cdm_iommu_hdl_secure = cdm_handles.secure; + + g_jpeg_hw_mgr.jpeg_caps.dev_iommu_handle.non_secure = + g_jpeg_hw_mgr.iommu_hdl; + g_jpeg_hw_mgr.jpeg_caps.dev_iommu_handle.secure = + g_jpeg_hw_mgr.iommu_sec_hdl; + g_jpeg_hw_mgr.jpeg_caps.cdm_iommu_handle.non_secure = + g_jpeg_hw_mgr.cdm_iommu_hdl; + g_jpeg_hw_mgr.jpeg_caps.cdm_iommu_handle.secure = + g_jpeg_hw_mgr.cdm_iommu_hdl_secure; + g_jpeg_hw_mgr.jpeg_caps.num_enc = num_dev; + g_jpeg_hw_mgr.jpeg_caps.num_dma = num_dma_dev; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_ENC].hw_ver.major = 4; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_ENC].hw_ver.minor = 2; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_ENC].hw_ver.incr = 0; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_ENC].hw_ver.reserved = 0; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_DMA].hw_ver.major = 4; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_DMA].hw_ver.minor = 2; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_DMA].hw_ver.incr = 0; + g_jpeg_hw_mgr.jpeg_caps.dev_ver[CAM_JPEG_DEV_DMA].hw_ver.reserved = 0; + + rc = cam_jpeg_setup_workqs(); + if (rc) { + CAM_ERR(CAM_JPEG, "setup work qs failed %d", rc); + goto cdm_iommu_failed; + } + + if (iommu_hdl) + *iommu_hdl = g_jpeg_hw_mgr.iommu_hdl; + + return rc; + +cdm_iommu_failed: + cam_smmu_destroy_handle(g_jpeg_hw_mgr.iommu_hdl); + g_jpeg_hw_mgr.iommu_hdl = 0; +smmu_get_failed: + mutex_destroy(&g_jpeg_hw_mgr.hw_mgr_mutex); + for (i = 0; i < CAM_JPEG_CTX_MAX; i++) + mutex_destroy(&g_jpeg_hw_mgr.ctx_data[i].ctx_mutex); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.h new file mode 100644 index 000000000000..35e2fc1421ae --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.h @@ -0,0 +1,162 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_HW_MGR_H +#define CAM_JPEG_HW_MGR_H + +#include +#include +#include + +#include "cam_jpeg_hw_intf.h" +#include "cam_hw_mgr_intf.h" +#include "cam_hw_intf.h" +#include "cam_req_mgr_workq.h" +#include "cam_mem_mgr.h" + +#define CAM_JPEG_WORKQ_NUM_TASK 30 +#define CAM_JPEG_WORKQ_TASK_CMD_TYPE 1 +#define CAM_JPEG_WORKQ_TASK_MSG_TYPE 2 +#define CAM_JPEG_HW_CFG_Q_MAX 50 + +/** + * struct cam_jpeg_process_frame_work_data_t + * + * @type: Task type + * @data: Pointer to command data + * @request_id: Request id + */ +struct cam_jpeg_process_frame_work_data_t { + uint32_t type; + void *data; + uint64_t request_id; +}; + +/** + * struct cam_jpeg_process_irq_work_data_t + * + * @type: Task type + * @data: Pointer to message data + * @result_size: Result size of enc/dma + * @irq_status: IRQ status + */ +struct cam_jpeg_process_irq_work_data_t { + uint32_t type; + void *data; + int32_t result_size; + uint32_t irq_status; +}; + +/** + * struct cam_jpeg_hw_cdm_info_t + * + * @ref_cnt: Ref count of how many times device type is acquired + * @cdm_handle: Cdm handle + * @cdm_ops: Cdm ops struct + */ +struct cam_jpeg_hw_cdm_info_t { + int ref_cnt; + uint32_t cdm_handle; + struct cam_cdm_utils_ops *cdm_ops; +}; + +/** + * struct cam_jpeg_hw_cfg_req_t + * + * @list_head: List head + * @hw_cfg_args: Hw config args + * @dev_type: Dev type for cfg request + * @req_id: Request Id + */ +struct cam_jpeg_hw_cfg_req { + struct list_head list; + struct cam_hw_config_args hw_cfg_args; + uint32_t dev_type; + int64_t req_id; +}; + +/** + * struct cam_jpeg_hw_ctx_data + * + * @context_priv: Context private data, cam_context from + * acquire. + * @ctx_mutex: Mutex for context + * @jpeg_dev_acquire_info: Acquire device info + * @ctxt_event_cb: Context callback function + * @in_use: Flag for context usage + * @wait_complete: Completion info + * @cdm_cmd: Cdm cmd submitted for that context. + */ +struct cam_jpeg_hw_ctx_data { + void *context_priv; + struct mutex ctx_mutex; + struct cam_jpeg_acquire_dev_info jpeg_dev_acquire_info; + cam_hw_event_cb_func ctxt_event_cb; + bool in_use; + struct completion wait_complete; + struct cam_cdm_bl_request *cdm_cmd; +}; + +/** + * struct cam_jpeg_hw_mgr + * @hw_mgr_mutex: Mutex for JPEG hardware manager + * @hw_mgr_lock: Spinlock for JPEG hardware manager + * @ctx_data: Context data + * @jpeg_caps: JPEG capabilities + * @iommu_hdl: Non secure IOMMU handle + * @iommu_sec_hdl: Secure IOMMU handle + * @work_process_frame: Work queue for hw config requests + * @work_process_irq_cb: Work queue for processing IRQs. + * @process_frame_work_data: Work data pool for hw config + * requests + * @process_irq_cb_work_data: Work data pool for irq requests + * @cdm_iommu_hdl: Iommu handle received from cdm + * @cdm_iommu_hdl_secure: Secure iommu handle received from cdm + * @devices: Core hw Devices of JPEG hardware manager + * @cdm_info: Cdm info for each core device. + * @cdm_reg_map: Regmap of each device for cdm. + * @device_in_use: Flag device being used for an active request + * @dev_hw_cfg_args: Current cfg request per core dev + * @hw_config_req_list: Pending hw update requests list + * @free_req_list: Free nodes for above list + * @req_list: Nodes of hw update list + */ +struct cam_jpeg_hw_mgr { + struct mutex hw_mgr_mutex; + spinlock_t hw_mgr_lock; + struct cam_jpeg_hw_ctx_data ctx_data[CAM_JPEG_CTX_MAX]; + struct cam_jpeg_query_cap_cmd jpeg_caps; + int32_t iommu_hdl; + int32_t iommu_sec_hdl; + struct cam_req_mgr_core_workq *work_process_frame; + struct cam_req_mgr_core_workq *work_process_irq_cb; + struct cam_jpeg_process_frame_work_data_t *process_frame_work_data; + struct cam_jpeg_process_irq_work_data_t *process_irq_cb_work_data; + int cdm_iommu_hdl; + int cdm_iommu_hdl_secure; + + struct cam_hw_intf **devices[CAM_JPEG_DEV_TYPE_MAX]; + struct cam_jpeg_hw_cdm_info_t cdm_info[CAM_JPEG_DEV_TYPE_MAX] + [CAM_JPEG_NUM_DEV_PER_RES_MAX]; + struct cam_soc_reg_map *cdm_reg_map[CAM_JPEG_DEV_TYPE_MAX] + [CAM_JPEG_NUM_DEV_PER_RES_MAX]; + uint32_t device_in_use[CAM_JPEG_DEV_TYPE_MAX] + [CAM_JPEG_NUM_DEV_PER_RES_MAX]; + struct cam_jpeg_hw_cfg_req *dev_hw_cfg_args[CAM_JPEG_DEV_TYPE_MAX] + [CAM_JPEG_NUM_DEV_PER_RES_MAX]; + + struct list_head hw_config_req_list; + struct list_head free_req_list; + struct cam_jpeg_hw_cfg_req req_list[CAM_JPEG_HW_CFG_Q_MAX]; +}; + +#endif /* CAM_JPEG_HW_MGR_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h new file mode 100644 index 000000000000..44b134ae0340 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h @@ -0,0 +1,44 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_HW_INTF_H +#define CAM_JPEG_HW_INTF_H + +#include "cam_cpas_api.h" + +#define CAM_JPEG_CTX_MAX 8 +#define CAM_JPEG_DEV_PER_TYPE_MAX 1 + +#define CAM_JPEG_CMD_BUF_MAX_SIZE 128 +#define CAM_JPEG_MSG_BUF_MAX_SIZE CAM_JPEG_CMD_BUF_MAX_SIZE + +#define JPEG_VOTE 640000000 + +enum cam_jpeg_hw_type { + CAM_JPEG_DEV_ENC, + CAM_JPEG_DEV_DMA, +}; + +struct cam_jpeg_set_irq_cb { + int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status, + int32_t result_size, void *data); + void *data; + uint32_t b_set_cb; +}; + +enum cam_jpeg_cmd_type { + CAM_JPEG_CMD_CDM_CFG, + CAM_JPEG_CMD_SET_IRQ_CB, + CAM_JPEG_CMD_MAX, +}; + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h new file mode 100644 index 000000000000..64778b7b0014 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h @@ -0,0 +1,24 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_HW_MGR_INTF_H +#define CAM_JPEG_HW_MGR_INTF_H + +#include +#include +#include + + +int cam_jpeg_hw_mgr_init(struct device_node *of_node, + uint64_t *hw_mgr_hdl, int *iommu_hdl); + +#endif /* CAM_JPEG_HW_MGR_INTF_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/Makefile new file mode 100644 index 000000000000..8d26d6ada557 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw + +obj-$(CONFIG_SPECTRA_CAMERA) += jpeg_dma_dev.o jpeg_dma_core.o jpeg_dma_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c new file mode 100644 index 000000000000..2d343dd521c5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c @@ -0,0 +1,198 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "jpeg_dma_core.h" +#include "jpeg_dma_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_jpeg_hw_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +int cam_jpeg_dma_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_dma_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_dma_device_core_info *core_info = NULL; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &jpeg_dma_dev->soc_info; + core_info = + (struct cam_jpeg_dma_device_core_info *)jpeg_dma_dev-> + core_info; + + if (!soc_info || !core_info) { + CAM_ERR(CAM_JPEG, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + mutex_lock(&core_info->core_mutex); + if (++core_info->ref_count > 1) { + mutex_unlock(&core_info->core_mutex); + return 0; + } + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = JPEG_VOTE; + axi_vote.uncompressed_bw = JPEG_VOTE; + + rc = cam_cpas_start(core_info->cpas_handle, + &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc); + goto cpas_failed; + } + + rc = cam_jpeg_dma_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_JPEG, "soc enable is failed %d", rc); + goto soc_failed; + } + + mutex_unlock(&core_info->core_mutex); + + return 0; + +soc_failed: + cam_cpas_stop(core_info->cpas_handle); +cpas_failed: + --core_info->ref_count; + mutex_unlock(&core_info->core_mutex); + + return rc; +} + +int cam_jpeg_dma_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_dma_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_dma_device_core_info *core_info = NULL; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &jpeg_dma_dev->soc_info; + core_info = (struct cam_jpeg_dma_device_core_info *) + jpeg_dma_dev->core_info; + if (!soc_info || !core_info) { + CAM_ERR(CAM_JPEG, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + mutex_lock(&core_info->core_mutex); + if (--core_info->ref_count > 0) { + mutex_unlock(&core_info->core_mutex); + return 0; + } + + if (core_info->ref_count < 0) { + CAM_ERR(CAM_JPEG, "ref cnt %d", core_info->ref_count); + core_info->ref_count = 0; + mutex_unlock(&core_info->core_mutex); + return -EFAULT; + } + + rc = cam_jpeg_dma_disable_soc_resources(soc_info); + if (rc) + CAM_ERR(CAM_JPEG, "soc enable failed %d", rc); + + rc = cam_cpas_stop(core_info->cpas_handle); + if (rc) + CAM_ERR(CAM_JPEG, "cpas stop failed: %d", rc); + + mutex_unlock(&core_info->core_mutex); + + return 0; +} + +int cam_jpeg_dma_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_dma_dev = device_priv; + struct cam_jpeg_dma_device_core_info *core_info = NULL; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_JPEG_CMD_MAX) { + CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + core_info = + (struct cam_jpeg_dma_device_core_info *)jpeg_dma_dev-> + core_info; + + switch (cmd_type) { + case CAM_JPEG_CMD_SET_IRQ_CB: + { + struct cam_jpeg_set_irq_cb *irq_cb = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_JPEG, "cmd args NULL"); + return -EINVAL; + } + if (irq_cb->b_set_cb) { + core_info->irq_cb.jpeg_hw_mgr_cb = + irq_cb->jpeg_hw_mgr_cb; + core_info->irq_cb.data = irq_cb->data; + } else { + core_info->irq_cb.jpeg_hw_mgr_cb = NULL; + core_info->irq_cb.data = NULL; + } + rc = 0; + break; + } + default: + rc = -EINVAL; + break; + } + + return rc; +} + +irqreturn_t cam_jpeg_dma_irq(int irq_num, void *data) +{ + return IRQ_HANDLED; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h new file mode 100644 index 000000000000..0ef4572d34f0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h @@ -0,0 +1,51 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_DMA_CORE_H +#define CAM_JPEG_DMA_CORE_H + +#include +#include +#include +#include + +#include "cam_jpeg_hw_intf.h" + +struct cam_jpeg_dma_device_hw_info { + uint32_t reserved; +}; + +enum cam_jpeg_dma_core_state { + CAM_JPEG_DMA_CORE_NOT_READY, + CAM_JPEG_DMA_CORE_READY, + CAM_JPEG_DMA_CORE_RESETTING, + CAM_JPEG_DMA_CORE_STATE_MAX, +}; + +struct cam_jpeg_dma_device_core_info { + enum cam_jpeg_dma_core_state core_state; + struct cam_jpeg_dma_device_hw_info *jpeg_dma_hw_info; + uint32_t cpas_handle; + struct cam_jpeg_set_irq_cb irq_cb; + int32_t ref_count; + struct mutex core_mutex; +}; + +int cam_jpeg_dma_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_jpeg_dma_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_jpeg_dma_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); +irqreturn_t cam_jpeg_dma_irq(int irq_num, void *data); + +#endif /* CAM_JPEG_DMA_CORE_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c new file mode 100644 index 000000000000..05ae7cc95ddf --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c @@ -0,0 +1,239 @@ +/* Copyright (c) 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 +#include +#include +#include +#include + +#include "jpeg_dma_core.h" +#include "jpeg_dma_soc.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_io_util.h" +#include "cam_jpeg_hw_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +static struct cam_jpeg_dma_device_hw_info cam_jpeg_dma_hw_info = { + .reserved = 0, +}; +EXPORT_SYMBOL(cam_jpeg_dma_hw_info); + +static int cam_jpeg_dma_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_jpeg_dma_device_core_info *core_info, + uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = soc_info->dev; + memcpy(cpas_register_params.identifier, "jpeg-dma", + sizeof("jpeg-dma")); + cpas_register_params.cam_cpas_client_cb = NULL; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc) { + CAM_ERR(CAM_JPEG, "cpas_register failed: %d", rc); + return rc; + } + core_info->cpas_handle = cpas_register_params.client_handle; + + return rc; +} + +static int cam_jpeg_dma_unregister_cpas( + struct cam_jpeg_dma_device_core_info *core_info) +{ + int rc; + + rc = cam_cpas_unregister_client(core_info->cpas_handle); + if (rc) + CAM_ERR(CAM_JPEG, "cpas unregister failed: %d", rc); + core_info->cpas_handle = 0; + + return rc; +} + +static int cam_jpeg_dma_remove(struct platform_device *pdev) +{ + struct cam_hw_info *jpeg_dma_dev = NULL; + struct cam_hw_intf *jpeg_dma_dev_intf = NULL; + struct cam_jpeg_dma_device_core_info *core_info = NULL; + int rc; + + jpeg_dma_dev_intf = platform_get_drvdata(pdev); + if (!jpeg_dma_dev_intf) { + CAM_ERR(CAM_JPEG, "error No data in pdev"); + return -EINVAL; + } + + jpeg_dma_dev = jpeg_dma_dev_intf->hw_priv; + if (!jpeg_dma_dev) { + CAM_ERR(CAM_JPEG, "error HW data is NULL"); + rc = -ENODEV; + goto free_jpeg_hw_intf; + } + + core_info = (struct cam_jpeg_dma_device_core_info *) + jpeg_dma_dev->core_info; + if (!core_info) { + CAM_ERR(CAM_JPEG, "error core data NULL"); + goto deinit_soc; + } + + rc = cam_jpeg_dma_unregister_cpas(core_info); + if (rc) + CAM_ERR(CAM_JPEG, " unreg failed to reg cpas %d", rc); + + mutex_destroy(&core_info->core_mutex); + kfree(core_info); + +deinit_soc: + rc = cam_soc_util_release_platform_resource(&jpeg_dma_dev->soc_info); + if (rc) + CAM_ERR(CAM_JPEG, "Failed to deinit soc rc=%d", rc); + + mutex_destroy(&jpeg_dma_dev->hw_mutex); + kfree(jpeg_dma_dev); + +free_jpeg_hw_intf: + kfree(jpeg_dma_dev_intf); + return rc; +} + +static int cam_jpeg_dma_probe(struct platform_device *pdev) +{ + struct cam_hw_info *jpeg_dma_dev = NULL; + struct cam_hw_intf *jpeg_dma_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_jpeg_dma_device_core_info *core_info = NULL; + struct cam_jpeg_dma_device_hw_info *hw_info = NULL; + int rc; + + jpeg_dma_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!jpeg_dma_dev_intf) + return -ENOMEM; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &jpeg_dma_dev_intf->hw_idx); + + jpeg_dma_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!jpeg_dma_dev) { + rc = -ENOMEM; + goto error_alloc_dev; + } + jpeg_dma_dev->soc_info.pdev = pdev; + jpeg_dma_dev->soc_info.dev = &pdev->dev; + jpeg_dma_dev->soc_info.dev_name = pdev->name; + jpeg_dma_dev_intf->hw_priv = jpeg_dma_dev; + jpeg_dma_dev_intf->hw_ops.init = cam_jpeg_dma_init_hw; + jpeg_dma_dev_intf->hw_ops.deinit = cam_jpeg_dma_deinit_hw; + jpeg_dma_dev_intf->hw_ops.process_cmd = cam_jpeg_dma_process_cmd; + jpeg_dma_dev_intf->hw_type = CAM_JPEG_DEV_DMA; + + platform_set_drvdata(pdev, jpeg_dma_dev_intf); + jpeg_dma_dev->core_info = + kzalloc(sizeof(struct cam_jpeg_dma_device_core_info), + GFP_KERNEL); + if (!jpeg_dma_dev->core_info) { + rc = -ENOMEM; + goto error_alloc_core; + } + core_info = (struct cam_jpeg_dma_device_core_info *)jpeg_dma_dev-> + core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_JPEG, " No jpeg_dma hardware info"); + rc = -EINVAL; + goto error_match_dev; + } + hw_info = (struct cam_jpeg_dma_device_hw_info *)match_dev->data; + core_info->jpeg_dma_hw_info = hw_info; + core_info->core_state = CAM_JPEG_DMA_CORE_NOT_READY; + mutex_init(&core_info->core_mutex); + + rc = cam_jpeg_dma_init_soc_resources(&jpeg_dma_dev->soc_info, + cam_jpeg_dma_irq, + jpeg_dma_dev); + if (rc) { + CAM_ERR(CAM_JPEG, "failed to init_soc %d", rc); + goto error_init_soc; + } + + rc = cam_jpeg_dma_register_cpas(&jpeg_dma_dev->soc_info, + core_info, jpeg_dma_dev_intf->hw_idx); + if (rc) { + CAM_ERR(CAM_JPEG, " failed to reg cpas %d", rc); + goto error_reg_cpas; + } + jpeg_dma_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&jpeg_dma_dev->hw_mutex); + spin_lock_init(&jpeg_dma_dev->hw_lock); + init_completion(&jpeg_dma_dev->hw_complete); + + CAM_DBG(CAM_JPEG, " hwidx %d", jpeg_dma_dev_intf->hw_idx); + + return rc; + +error_reg_cpas: + rc = cam_soc_util_release_platform_resource(&jpeg_dma_dev->soc_info); +error_init_soc: + mutex_destroy(&core_info->core_mutex); +error_match_dev: + kfree(jpeg_dma_dev->core_info); +error_alloc_core: + kfree(jpeg_dma_dev); +error_alloc_dev: + kfree(jpeg_dma_dev_intf); + return rc; +} + +static const struct of_device_id cam_jpeg_dma_dt_match[] = { + { + .compatible = "qcom,cam_jpeg_dma", + .data = &cam_jpeg_dma_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_jpeg_dma_dt_match); + +static struct platform_driver cam_jpeg_dma_driver = { + .probe = cam_jpeg_dma_probe, + .remove = cam_jpeg_dma_remove, + .driver = { + .name = "cam-jpeg-dma", + .owner = THIS_MODULE, + .of_match_table = cam_jpeg_dma_dt_match, + }, +}; + +static int __init cam_jpeg_dma_init_module(void) +{ + return platform_driver_register(&cam_jpeg_dma_driver); +} + +static void __exit cam_jpeg_dma_exit_module(void) +{ + platform_driver_unregister(&cam_jpeg_dma_driver); +} + +module_init(cam_jpeg_dma_init_module); +module_exit(cam_jpeg_dma_exit_module); +MODULE_DESCRIPTION("CAM JPEG_DMA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c new file mode 100644 index 000000000000..2feac6b05834 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c @@ -0,0 +1,62 @@ +/* Copyright (c) 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 +#include +#include +#include +#include + +#include "jpeg_dma_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +int cam_jpeg_dma_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t jpeg_dma_irq_handler, void *irq_data) +{ + int rc; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) + return rc; + + rc = cam_soc_util_request_platform_resource(soc_info, + jpeg_dma_irq_handler, + irq_data); + if (rc) + CAM_ERR(CAM_JPEG, "init soc failed %d", rc); + + return rc; +} + +int cam_jpeg_dma_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) + CAM_ERR(CAM_JPEG, "enable platform failed %d", rc); + + return rc; +} + +int cam_jpeg_dma_disable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) + CAM_ERR(CAM_JPEG, "disable platform failed %d", rc); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.h new file mode 100644 index 000000000000..bc9bed868d59 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_JPEG_DMA_SOC_H_ +#define _CAM_JPEG_DMA_SOC_H_ + +#include "cam_soc_util.h" + +int cam_jpeg_dma_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t jpeg_dma_irq_handler, void *irq_data); + +int cam_jpeg_dma_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_jpeg_dma_disable_soc_resources(struct cam_hw_soc_info *soc_info); + +#endif /* _CAM_JPEG_DMA_SOC_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/Makefile new file mode 100644 index 000000000000..5c6619a4ee99 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_hw_mgr/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw + +obj-$(CONFIG_SPECTRA_CAMERA) += jpeg_enc_dev.o jpeg_enc_core.o jpeg_enc_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h new file mode 100644 index 000000000000..2ac4db6c0a46 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h @@ -0,0 +1,79 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_ENC_HW_INFO_TITAN170_H +#define CAM_JPEG_ENC_HW_INFO_TITAN170_H + +#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001 +#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000 + +#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000 +#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a + +#define CAM_JPEG_HW_IRQ_STATUS_STOP_DONE_MASK 0x8000000 +#define CAM_JPEG_HW_IRQ_STATUS_STOP_DONE_SHIFT 0x0000001b + +#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800 +#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b + +#define CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF (0x1<<19) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR (0x1<<20) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR (0x1<<21) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW (0x1<<23) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM (0x1<<24) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ (0x1<<25) +#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM (0x1<<26) +#define CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK (0x1<<29) + +#define CAM_JPEG_HW_MASK_COMP_FRAMEDONE \ + CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK +#define CAM_JPEG_HW_MASK_COMP_RESET_ACK \ + CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK +#define CAM_JPEG_HW_MASK_COMP_ERR \ + (CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \ + CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \ + CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK) + +static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = { + .reg_offset = { + .hw_version = 0x0, + .int_clr = 0x1c, + .int_status = 0x20, + .int_mask = 0x18, + .hw_cmd = 0x10, + .reset_cmd = 0x8, + .encode_size = 0x180, + }, + .reg_val = { + .int_clr_clearall = 0xFFFFFFFF, + .int_mask_disable_all = 0x00000000, + .int_mask_enable_all = 0xFFFFFFFF, + .hw_cmd_start = 0x00000001, + .reset_cmd = 0x00032093, + .hw_cmd_stop = 0x00000002, + }, + .int_status = { + .framedone = CAM_JPEG_HW_MASK_COMP_FRAMEDONE, + .resetdone = CAM_JPEG_HW_MASK_COMP_RESET_ACK, + .iserror = CAM_JPEG_HW_MASK_COMP_ERR, + .stopdone = CAM_JPEG_HW_IRQ_STATUS_STOP_DONE_MASK, + } +}; + +#endif /* CAM_JPEG_ENC_HW_INFO_TITAN170_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c new file mode 100644 index 000000000000..934b911ad7c1 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c @@ -0,0 +1,432 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "jpeg_enc_core.h" +#include "jpeg_enc_soc.h" +#include "cam_soc_util.h" +#include "cam_io_util.h" +#include "cam_jpeg_hw_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" + +#define CAM_JPEG_HW_IRQ_IS_FRAME_DONE(jpeg_irq_status, hi) \ + ((jpeg_irq_status) & (hi)->int_status.framedone) +#define CAM_JPEG_HW_IRQ_IS_RESET_ACK(jpeg_irq_status, hi) \ + ((jpeg_irq_status) & (hi)->int_status.resetdone) +#define CAM_JPEG_HW_IRQ_IS_ERR(jpeg_irq_status, hi) \ + ((jpeg_irq_status) & (hi)->int_status.iserror) +#define CAM_JPEG_HW_IRQ_IS_STOP_DONE(jpeg_irq_status, hi) \ + ((jpeg_irq_status) & (hi)->int_status.stopdone) + +#define CAM_JPEG_ENC_RESET_TIMEOUT msecs_to_jiffies(500) + +int cam_jpeg_enc_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &jpeg_enc_dev->soc_info; + core_info = + (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + + if (!soc_info || !core_info) { + CAM_ERR(CAM_JPEG, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + mutex_lock(&core_info->core_mutex); + if (++core_info->ref_count > 1) { + mutex_unlock(&core_info->core_mutex); + return 0; + } + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = JPEG_VOTE; + axi_vote.uncompressed_bw = JPEG_VOTE; + + rc = cam_cpas_start(core_info->cpas_handle, + &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc); + goto cpas_failed; + } + + rc = cam_jpeg_enc_enable_soc_resources(soc_info); + if (rc) { + CAM_ERR(CAM_JPEG, "soc enable is failed %d", rc); + goto soc_failed; + } + + mutex_unlock(&core_info->core_mutex); + + return 0; + +soc_failed: + cam_cpas_stop(core_info->cpas_handle); +cpas_failed: + --core_info->ref_count; + mutex_unlock(&core_info->core_mutex); + + return rc; +} + +int cam_jpeg_enc_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = device_priv; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid cam_dev_info"); + return -EINVAL; + } + + soc_info = &jpeg_enc_dev->soc_info; + core_info = (struct cam_jpeg_enc_device_core_info *) + jpeg_enc_dev->core_info; + if (!soc_info || !core_info) { + CAM_ERR(CAM_JPEG, "soc_info = %pK core_info = %pK", + soc_info, core_info); + return -EINVAL; + } + + mutex_lock(&core_info->core_mutex); + if (--core_info->ref_count > 0) { + mutex_unlock(&core_info->core_mutex); + return 0; + } + + if (core_info->ref_count < 0) { + CAM_ERR(CAM_JPEG, "ref cnt %d", core_info->ref_count); + core_info->ref_count = 0; + mutex_unlock(&core_info->core_mutex); + return -EFAULT; + } + + rc = cam_jpeg_enc_disable_soc_resources(soc_info); + if (rc) + CAM_ERR(CAM_JPEG, "soc disable failed %d", rc); + + rc = cam_cpas_stop(core_info->cpas_handle); + if (rc) + CAM_ERR(CAM_JPEG, "cpas stop failed: %d", rc); + + mutex_unlock(&core_info->core_mutex); + + return 0; +} + +irqreturn_t cam_jpeg_enc_irq(int irq_num, void *data) +{ + struct cam_hw_info *jpeg_enc_dev = data; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + uint32_t irq_status = 0; + uint32_t encoded_size = 0; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_hw_info *hw_info = NULL; + void __iomem *mem_base; + + if (!jpeg_enc_dev) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return IRQ_HANDLED; + } + soc_info = &jpeg_enc_dev->soc_info; + core_info = + (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + hw_info = core_info->jpeg_enc_hw_info; + mem_base = soc_info->reg_map[0].mem_base; + + irq_status = cam_io_r_mb(mem_base + + core_info->jpeg_enc_hw_info->reg_offset.int_status); + + cam_io_w_mb(irq_status, + soc_info->reg_map[0].mem_base + + core_info->jpeg_enc_hw_info->reg_offset.int_clr); + + CAM_DBG(CAM_JPEG, "irq_num %d irq_status = %x , core_state %d", + irq_num, irq_status, core_info->core_state); + + if (CAM_JPEG_HW_IRQ_IS_FRAME_DONE(irq_status, hw_info)) { + spin_lock(&jpeg_enc_dev->hw_lock); + if (core_info->core_state == CAM_JPEG_ENC_CORE_READY) { + encoded_size = cam_io_r_mb(mem_base + + core_info->jpeg_enc_hw_info->reg_offset. + encode_size); + if (core_info->irq_cb.jpeg_hw_mgr_cb) { + core_info->irq_cb.jpeg_hw_mgr_cb(irq_status, + encoded_size, + core_info->irq_cb.data); + } else { + CAM_ERR(CAM_JPEG, "unexpected done, no cb"); + } + } else { + CAM_ERR(CAM_JPEG, "unexpected done irq"); + } + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + spin_unlock(&jpeg_enc_dev->hw_lock); + } + if (CAM_JPEG_HW_IRQ_IS_RESET_ACK(irq_status, hw_info)) { + spin_lock(&jpeg_enc_dev->hw_lock); + if (core_info->core_state == CAM_JPEG_ENC_CORE_RESETTING) { + core_info->core_state = CAM_JPEG_ENC_CORE_READY; + complete(&jpeg_enc_dev->hw_complete); + } else { + CAM_ERR(CAM_JPEG, "unexpected reset irq"); + } + spin_unlock(&jpeg_enc_dev->hw_lock); + } + if (CAM_JPEG_HW_IRQ_IS_STOP_DONE(irq_status, hw_info)) { + spin_lock(&jpeg_enc_dev->hw_lock); + if (core_info->core_state == CAM_JPEG_ENC_CORE_ABORTING) { + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + complete(&jpeg_enc_dev->hw_complete); + if (core_info->irq_cb.jpeg_hw_mgr_cb) { + core_info->irq_cb.jpeg_hw_mgr_cb(irq_status, + -1, + core_info->irq_cb.data); + } + } else { + CAM_ERR(CAM_JPEG, "unexpected abort irq"); + } + spin_unlock(&jpeg_enc_dev->hw_lock); + } + /* Unexpected/unintended HW interrupt */ + if (CAM_JPEG_HW_IRQ_IS_ERR(irq_status, hw_info)) { + spin_lock(&jpeg_enc_dev->hw_lock); + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + CAM_ERR_RATE_LIMIT(CAM_JPEG, + "error irq_num %d irq_status = %x , core_state %d", + irq_num, irq_status, core_info->core_state); + + if (core_info->irq_cb.jpeg_hw_mgr_cb) { + core_info->irq_cb.jpeg_hw_mgr_cb(irq_status, + -1, + core_info->irq_cb.data); + } + spin_unlock(&jpeg_enc_dev->hw_lock); + } + + return IRQ_HANDLED; +} + +int cam_jpeg_enc_reset_hw(void *data, + void *start_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = data; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_hw_info *hw_info = NULL; + void __iomem *mem_base; + unsigned long rem_jiffies; + + if (!jpeg_enc_dev) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + /* maskdisable.clrirq.maskenable.resetcmd */ + soc_info = &jpeg_enc_dev->soc_info; + core_info = + (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + hw_info = core_info->jpeg_enc_hw_info; + mem_base = soc_info->reg_map[0].mem_base; + + mutex_lock(&core_info->core_mutex); + spin_lock(&jpeg_enc_dev->hw_lock); + if (core_info->core_state == CAM_JPEG_ENC_CORE_RESETTING) { + CAM_ERR(CAM_JPEG, "alrady resetting"); + spin_unlock(&jpeg_enc_dev->hw_lock); + mutex_unlock(&core_info->core_mutex); + return 0; + } + + reinit_completion(&jpeg_enc_dev->hw_complete); + core_info->core_state = CAM_JPEG_ENC_CORE_RESETTING; + spin_unlock(&jpeg_enc_dev->hw_lock); + + cam_io_w_mb(hw_info->reg_val.int_mask_disable_all, + mem_base + hw_info->reg_offset.int_mask); + cam_io_w_mb(hw_info->reg_val.int_clr_clearall, + mem_base + hw_info->reg_offset.int_clr); + cam_io_w_mb(hw_info->reg_val.int_mask_enable_all, + mem_base + hw_info->reg_offset.int_mask); + cam_io_w_mb(hw_info->reg_val.reset_cmd, + mem_base + hw_info->reg_offset.reset_cmd); + + rem_jiffies = wait_for_completion_timeout(&jpeg_enc_dev->hw_complete, + CAM_JPEG_ENC_RESET_TIMEOUT); + if (!rem_jiffies) { + CAM_ERR(CAM_JPEG, "error Reset Timeout"); + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + } + + mutex_unlock(&core_info->core_mutex); + return 0; +} + +int cam_jpeg_enc_start_hw(void *data, + void *start_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = data; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_hw_info *hw_info = NULL; + void __iomem *mem_base; + + if (!jpeg_enc_dev) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + + soc_info = &jpeg_enc_dev->soc_info; + core_info = (struct cam_jpeg_enc_device_core_info *) + jpeg_enc_dev->core_info; + hw_info = core_info->jpeg_enc_hw_info; + mem_base = soc_info->reg_map[0].mem_base; + + if (core_info->core_state != CAM_JPEG_ENC_CORE_READY) { + CAM_ERR(CAM_JPEG, "Error not ready"); + return -EINVAL; + } + + cam_io_w_mb(hw_info->reg_val.hw_cmd_start, + mem_base + hw_info->reg_offset.hw_cmd); + + return 0; +} + +int cam_jpeg_enc_stop_hw(void *data, + void *stop_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = data; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_jpeg_enc_device_hw_info *hw_info = NULL; + void __iomem *mem_base; + unsigned long rem_jiffies; + + if (!jpeg_enc_dev) { + CAM_ERR(CAM_JPEG, "Invalid args"); + return -EINVAL; + } + soc_info = &jpeg_enc_dev->soc_info; + core_info = + (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + hw_info = core_info->jpeg_enc_hw_info; + mem_base = soc_info->reg_map[0].mem_base; + + mutex_unlock(&core_info->core_mutex); + spin_lock(&jpeg_enc_dev->hw_lock); + if (core_info->core_state == CAM_JPEG_ENC_CORE_ABORTING) { + CAM_ERR(CAM_JPEG, "alrady stopping"); + spin_unlock(&jpeg_enc_dev->hw_lock); + mutex_unlock(&core_info->core_mutex); + return 0; + } + + reinit_completion(&jpeg_enc_dev->hw_complete); + core_info->core_state = CAM_JPEG_ENC_CORE_ABORTING; + spin_unlock(&jpeg_enc_dev->hw_lock); + + cam_io_w_mb(hw_info->reg_val.hw_cmd_stop, + mem_base + hw_info->reg_offset.hw_cmd); + + rem_jiffies = wait_for_completion_timeout(&jpeg_enc_dev->hw_complete, + CAM_JPEG_ENC_RESET_TIMEOUT); + if (!rem_jiffies) { + CAM_ERR(CAM_JPEG, "error Reset Timeout"); + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + } + + mutex_unlock(&core_info->core_mutex); + return 0; +} + +int cam_jpeg_enc_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *jpeg_enc_dev = device_priv; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + int rc; + + if (!device_priv) { + CAM_ERR(CAM_JPEG, "Invalid arguments"); + return -EINVAL; + } + + if (cmd_type >= CAM_JPEG_CMD_MAX) { + CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type); + return -EINVAL; + } + + core_info = + (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + + switch (cmd_type) { + case CAM_JPEG_CMD_SET_IRQ_CB: + { + struct cam_jpeg_set_irq_cb *irq_cb = cmd_args; + + if (!cmd_args) { + CAM_ERR(CAM_JPEG, "cmd args NULL"); + return -EINVAL; + } + if (irq_cb->b_set_cb) { + core_info->irq_cb.jpeg_hw_mgr_cb = + irq_cb->jpeg_hw_mgr_cb; + core_info->irq_cb.data = irq_cb->data; + } else { + core_info->irq_cb.jpeg_hw_mgr_cb = NULL; + core_info->irq_cb.data = NULL; + } + rc = 0; + break; + } + default: + rc = -EINVAL; + break; + } + if (rc) + CAM_ERR(CAM_JPEG, "error cmdtype %d rc = %d", cmd_type, rc); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h new file mode 100644 index 000000000000..ca4d2692a455 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h @@ -0,0 +1,86 @@ +/* Copyright (c) 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. + */ + +#ifndef CAM_JPEG_ENC_CORE_H +#define CAM_JPEG_ENC_CORE_H + +#include +#include +#include +#include + +#include "cam_jpeg_hw_intf.h" + +struct cam_jpeg_enc_reg_offsets { + uint32_t hw_version; + uint32_t int_status; + uint32_t int_clr; + uint32_t int_mask; + uint32_t hw_cmd; + uint32_t reset_cmd; + uint32_t encode_size; +}; + +struct cam_jpeg_enc_regval { + uint32_t int_clr_clearall; + uint32_t int_mask_disable_all; + uint32_t int_mask_enable_all; + uint32_t hw_cmd_start; + uint32_t reset_cmd; + uint32_t hw_cmd_stop; +}; + +struct cam_jpeg_enc_int_status { + uint32_t framedone; + uint32_t resetdone; + uint32_t iserror; + uint32_t stopdone; +}; + +struct cam_jpeg_enc_device_hw_info { + struct cam_jpeg_enc_reg_offsets reg_offset; + struct cam_jpeg_enc_regval reg_val; + struct cam_jpeg_enc_int_status int_status; +}; + +enum cam_jpeg_enc_core_state { + CAM_JPEG_ENC_CORE_NOT_READY, + CAM_JPEG_ENC_CORE_READY, + CAM_JPEG_ENC_CORE_RESETTING, + CAM_JPEG_ENC_CORE_ABORTING, + CAM_JPEG_ENC_CORE_STATE_MAX, +}; + +struct cam_jpeg_enc_device_core_info { + enum cam_jpeg_enc_core_state core_state; + struct cam_jpeg_enc_device_hw_info *jpeg_enc_hw_info; + uint32_t cpas_handle; + struct cam_jpeg_set_irq_cb irq_cb; + int32_t ref_count; + struct mutex core_mutex; +}; + +int cam_jpeg_enc_init_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_jpeg_enc_deinit_hw(void *device_priv, + void *init_hw_args, uint32_t arg_size); +int cam_jpeg_enc_start_hw(void *device_priv, + void *start_hw_args, uint32_t arg_size); +int cam_jpeg_enc_stop_hw(void *device_priv, + void *stop_hw_args, uint32_t arg_size); +int cam_jpeg_enc_reset_hw(void *device_priv, + void *reset_hw_args, uint32_t arg_size); +int cam_jpeg_enc_process_cmd(void *device_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); +irqreturn_t cam_jpeg_enc_irq(int irq_num, void *data); + +#endif /* CAM_JPEG_ENC_CORE_H */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c new file mode 100644 index 000000000000..21804486a338 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c @@ -0,0 +1,237 @@ +/* Copyright (c) 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 +#include +#include +#include +#include + +#include "jpeg_enc_core.h" +#include "jpeg_enc_soc.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_io_util.h" +#include "cam_jpeg_hw_intf.h" +#include "cam_jpeg_hw_mgr_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "cam_jpeg_enc_hw_info_ver_4_2_0.h" + +static int cam_jpeg_enc_register_cpas(struct cam_hw_soc_info *soc_info, + struct cam_jpeg_enc_device_core_info *core_info, + uint32_t hw_idx) +{ + struct cam_cpas_register_params cpas_register_params; + int rc; + + cpas_register_params.dev = soc_info->dev; + memcpy(cpas_register_params.identifier, "jpeg-enc", + sizeof("jpeg-enc")); + cpas_register_params.cam_cpas_client_cb = NULL; + cpas_register_params.cell_index = hw_idx; + cpas_register_params.userdata = NULL; + + rc = cam_cpas_register_client(&cpas_register_params); + if (rc) { + CAM_ERR(CAM_JPEG, "cpas_register failed: %d", rc); + return rc; + } + core_info->cpas_handle = cpas_register_params.client_handle; + + return rc; +} + +static int cam_jpeg_enc_unregister_cpas( + struct cam_jpeg_enc_device_core_info *core_info) +{ + int rc; + + rc = cam_cpas_unregister_client(core_info->cpas_handle); + if (rc) + CAM_ERR(CAM_JPEG, "cpas unregister failed: %d", rc); + core_info->cpas_handle = 0; + + return rc; +} + +static int cam_jpeg_enc_remove(struct platform_device *pdev) +{ + struct cam_hw_info *jpeg_enc_dev = NULL; + struct cam_hw_intf *jpeg_enc_dev_intf = NULL; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + int rc; + + jpeg_enc_dev_intf = platform_get_drvdata(pdev); + if (!jpeg_enc_dev_intf) { + CAM_ERR(CAM_JPEG, "error No data in pdev"); + return -EINVAL; + } + + jpeg_enc_dev = jpeg_enc_dev_intf->hw_priv; + if (!jpeg_enc_dev) { + CAM_ERR(CAM_JPEG, "error HW data is NULL"); + rc = -ENODEV; + goto free_jpeg_hw_intf; + } + + core_info = (struct cam_jpeg_enc_device_core_info *) + jpeg_enc_dev->core_info; + if (!core_info) { + CAM_ERR(CAM_JPEG, "error core data NULL"); + goto deinit_soc; + } + + rc = cam_jpeg_enc_unregister_cpas(core_info); + if (rc) + CAM_ERR(CAM_JPEG, " unreg failed to reg cpas %d", rc); + + mutex_destroy(&core_info->core_mutex); + kfree(core_info); + +deinit_soc: + rc = cam_soc_util_release_platform_resource(&jpeg_enc_dev->soc_info); + if (rc) + CAM_ERR(CAM_JPEG, "Failed to deinit soc rc=%d", rc); + + mutex_destroy(&jpeg_enc_dev->hw_mutex); + kfree(jpeg_enc_dev); + +free_jpeg_hw_intf: + kfree(jpeg_enc_dev_intf); + return rc; +} + +static int cam_jpeg_enc_probe(struct platform_device *pdev) +{ + struct cam_hw_info *jpeg_enc_dev = NULL; + struct cam_hw_intf *jpeg_enc_dev_intf = NULL; + const struct of_device_id *match_dev = NULL; + struct cam_jpeg_enc_device_core_info *core_info = NULL; + struct cam_jpeg_enc_device_hw_info *hw_info = NULL; + int rc; + + jpeg_enc_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL); + if (!jpeg_enc_dev_intf) + return -ENOMEM; + + of_property_read_u32(pdev->dev.of_node, + "cell-index", &jpeg_enc_dev_intf->hw_idx); + + jpeg_enc_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!jpeg_enc_dev) { + rc = -ENOMEM; + goto error_alloc_dev; + } + jpeg_enc_dev->soc_info.pdev = pdev; + jpeg_enc_dev->soc_info.dev = &pdev->dev; + jpeg_enc_dev->soc_info.dev_name = pdev->name; + jpeg_enc_dev_intf->hw_priv = jpeg_enc_dev; + jpeg_enc_dev_intf->hw_ops.init = cam_jpeg_enc_init_hw; + jpeg_enc_dev_intf->hw_ops.deinit = cam_jpeg_enc_deinit_hw; + jpeg_enc_dev_intf->hw_ops.start = cam_jpeg_enc_start_hw; + jpeg_enc_dev_intf->hw_ops.stop = cam_jpeg_enc_stop_hw; + jpeg_enc_dev_intf->hw_ops.reset = cam_jpeg_enc_reset_hw; + jpeg_enc_dev_intf->hw_ops.process_cmd = cam_jpeg_enc_process_cmd; + jpeg_enc_dev_intf->hw_type = CAM_JPEG_DEV_ENC; + + platform_set_drvdata(pdev, jpeg_enc_dev_intf); + jpeg_enc_dev->core_info = + kzalloc(sizeof(struct cam_jpeg_enc_device_core_info), + GFP_KERNEL); + if (!jpeg_enc_dev->core_info) { + rc = -ENOMEM; + goto error_alloc_core; + } + core_info = (struct cam_jpeg_enc_device_core_info *)jpeg_enc_dev-> + core_info; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_JPEG, " No jpeg_enc hardware info"); + rc = -EINVAL; + goto error_match_dev; + } + hw_info = (struct cam_jpeg_enc_device_hw_info *)match_dev->data; + core_info->jpeg_enc_hw_info = hw_info; + core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY; + mutex_init(&core_info->core_mutex); + + rc = cam_jpeg_enc_init_soc_resources(&jpeg_enc_dev->soc_info, + cam_jpeg_enc_irq, + jpeg_enc_dev); + if (rc) { + CAM_ERR(CAM_JPEG, " failed to init_soc %d", rc); + goto error_init_soc; + } + + rc = cam_jpeg_enc_register_cpas(&jpeg_enc_dev->soc_info, + core_info, jpeg_enc_dev_intf->hw_idx); + if (rc) { + CAM_ERR(CAM_JPEG, " failed to reg cpas %d", rc); + goto error_reg_cpas; + } + jpeg_enc_dev->hw_state = CAM_HW_STATE_POWER_DOWN; + mutex_init(&jpeg_enc_dev->hw_mutex); + spin_lock_init(&jpeg_enc_dev->hw_lock); + init_completion(&jpeg_enc_dev->hw_complete); + + return rc; + +error_reg_cpas: + cam_soc_util_release_platform_resource(&jpeg_enc_dev->soc_info); +error_init_soc: + mutex_destroy(&core_info->core_mutex); +error_match_dev: + kfree(jpeg_enc_dev->core_info); +error_alloc_core: + kfree(jpeg_enc_dev); +error_alloc_dev: + kfree(jpeg_enc_dev_intf); + + return rc; +} + +static const struct of_device_id cam_jpeg_enc_dt_match[] = { + { + .compatible = "qcom,cam_jpeg_enc", + .data = &cam_jpeg_enc_hw_info, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cam_jpeg_enc_dt_match); + +static struct platform_driver cam_jpeg_enc_driver = { + .probe = cam_jpeg_enc_probe, + .remove = cam_jpeg_enc_remove, + .driver = { + .name = "cam-jpeg-enc", + .owner = THIS_MODULE, + .of_match_table = cam_jpeg_enc_dt_match, + }, +}; + +static int __init cam_jpeg_enc_init_module(void) +{ + return platform_driver_register(&cam_jpeg_enc_driver); +} + +static void __exit cam_jpeg_enc_exit_module(void) +{ + platform_driver_unregister(&cam_jpeg_enc_driver); +} + +module_init(cam_jpeg_enc_init_module); +module_exit(cam_jpeg_enc_exit_module); +MODULE_DESCRIPTION("CAM JPEG_ENC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c new file mode 100644 index 000000000000..fd34bee08b22 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c @@ -0,0 +1,62 @@ +/* Copyright (c) 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 +#include +#include +#include +#include + +#include "jpeg_enc_soc.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +int cam_jpeg_enc_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t jpeg_enc_irq_handler, void *irq_data) +{ + int rc; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) + return rc; + + rc = cam_soc_util_request_platform_resource(soc_info, + jpeg_enc_irq_handler, + irq_data); + if (rc) + CAM_ERR(CAM_JPEG, "init soc failed %d", rc); + + return rc; +} + +int cam_jpeg_enc_enable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, true); + if (rc) + CAM_ERR(CAM_JPEG, "enable platform failed %d", rc); + + return rc; +} + +int cam_jpeg_enc_disable_soc_resources(struct cam_hw_soc_info *soc_info) +{ + int rc; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) + CAM_ERR(CAM_JPEG, "disable platform failed %d", rc); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.h new file mode 100644 index 000000000000..a0485a2b9f0c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_JPEG_ENC_SOC_H_ +#define _CAM_JPEG_ENC_SOC_H_ + +#include "cam_soc_util.h" + +int cam_jpeg_enc_init_soc_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t jpeg_enc_irq_handler, void *irq_data); + +int cam_jpeg_enc_enable_soc_resources(struct cam_hw_soc_info *soc_info); + +int cam_jpeg_enc_disable_soc_resources(struct cam_hw_soc_info *soc_info); + +#endif /* _CAM_JPEG_ENC_SOC_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_lrme/Makefile new file mode 100644 index 000000000000..0caffc466834 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include/ + +obj-$(CONFIG_SPECTRA_CAMERA) += lrme_hw_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_dev.o cam_lrme_context.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.c new file mode 100644 index 000000000000..3d0266d6fb13 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2017-2018, 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 +#include + +#include "cam_debug_util.h" +#include "cam_lrme_context.h" + +static const char lrme_dev_name[] = "lrme"; + +static int __cam_lrme_ctx_acquire_dev_in_available(struct cam_context *ctx, + struct cam_acquire_dev_cmd *cmd) +{ + int rc = 0; + uint64_t ctxt_to_hw_map = (uint64_t)ctx->ctxt_to_hw_map; + struct cam_lrme_context *lrme_ctx = ctx->ctx_priv; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_acquire_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to acquire"); + return rc; + } + + ctxt_to_hw_map |= (lrme_ctx->index << CAM_LRME_CTX_INDEX_SHIFT); + ctx->ctxt_to_hw_map = (void *)ctxt_to_hw_map; + + ctx->state = CAM_CTX_ACQUIRED; + + return rc; +} + +static int __cam_lrme_ctx_release_dev_in_acquired(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_release_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to release"); + return rc; + } + + ctx->state = CAM_CTX_AVAILABLE; + + return rc; +} + +static int __cam_lrme_ctx_start_dev_in_acquired(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_start_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to start"); + return rc; + } + + ctx->state = CAM_CTX_ACTIVATED; + + return rc; +} + +static int __cam_lrme_ctx_config_dev_in_activated(struct cam_context *ctx, + struct cam_config_dev_cmd *cmd) +{ + int rc; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_prepare_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to config"); + return rc; + } + + return rc; +} + +static int __cam_lrme_ctx_flush_dev_in_activated(struct cam_context *ctx, + struct cam_flush_dev_cmd *cmd) +{ + int rc; + + rc = cam_context_flush_dev_to_hw(ctx, cmd); + if (rc) + CAM_ERR(CAM_LRME, "Failed to flush device"); + + return rc; +} +static int __cam_lrme_ctx_stop_dev_in_activated(struct cam_context *ctx, + struct cam_start_stop_dev_cmd *cmd) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_stop_dev_to_hw(ctx); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to stop dev"); + return rc; + } + + ctx->state = CAM_CTX_ACQUIRED; + + return rc; +} + +static int __cam_lrme_ctx_release_dev_in_activated(struct cam_context *ctx, + struct cam_release_dev_cmd *cmd) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = __cam_lrme_ctx_stop_dev_in_activated(ctx, NULL); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to stop"); + return rc; + } + + rc = cam_context_release_dev_to_hw(ctx, cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to release"); + return rc; + } + + ctx->state = CAM_CTX_AVAILABLE; + + return rc; +} + +static int __cam_lrme_ctx_handle_irq_in_activated(void *context, + uint32_t evt_id, void *evt_data) +{ + int rc; + + CAM_DBG(CAM_LRME, "Enter"); + + rc = cam_context_buf_done_from_hw(context, evt_data, evt_id); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in buf done, rc=%d", rc); + return rc; + } + + return rc; +} + +/* top state machine */ +static struct cam_ctx_ops + cam_lrme_ctx_state_machine[CAM_CTX_STATE_MAX] = { + /* Uninit */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Available */ + { + .ioctl_ops = { + .acquire_dev = __cam_lrme_ctx_acquire_dev_in_available, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Acquired */ + { + .ioctl_ops = { + .release_dev = __cam_lrme_ctx_release_dev_in_acquired, + .start_dev = __cam_lrme_ctx_start_dev_in_acquired, + }, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Ready */ + { + .ioctl_ops = {}, + .crm_ops = {}, + .irq_ops = NULL, + }, + /* Activate */ + { + .ioctl_ops = { + .config_dev = __cam_lrme_ctx_config_dev_in_activated, + .release_dev = __cam_lrme_ctx_release_dev_in_activated, + .stop_dev = __cam_lrme_ctx_stop_dev_in_activated, + .flush_dev = __cam_lrme_ctx_flush_dev_in_activated, + }, + .crm_ops = {}, + .irq_ops = __cam_lrme_ctx_handle_irq_in_activated, + }, +}; + +int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx, + struct cam_context *base_ctx, + struct cam_hw_mgr_intf *hw_intf, + uint32_t index) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + if (!base_ctx || !lrme_ctx) { + CAM_ERR(CAM_LRME, "Invalid input"); + return -EINVAL; + } + + memset(lrme_ctx, 0, sizeof(*lrme_ctx)); + + rc = cam_context_init(base_ctx, lrme_dev_name, CAM_LRME, index, + NULL, hw_intf, lrme_ctx->req_base, CAM_CTX_REQ_MAX); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to init context"); + return rc; + } + lrme_ctx->base = base_ctx; + lrme_ctx->index = index; + base_ctx->ctx_priv = lrme_ctx; + base_ctx->state_machine = cam_lrme_ctx_state_machine; + + return rc; +} + +int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx) +{ + int rc = 0; + + CAM_DBG(CAM_LRME, "Enter"); + + if (!lrme_ctx) { + CAM_ERR(CAM_LRME, "No ctx to deinit"); + return -EINVAL; + } + + rc = cam_context_deinit(lrme_ctx->base); + + memset(lrme_ctx, 0, sizeof(*lrme_ctx)); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.h new file mode 100644 index 000000000000..4c705c139405 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_context.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2017-2018, 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 _CAM_LRME_CONTEXT_H_ +#define _CAM_LRME_CONTEXT_H_ + +#include "cam_context.h" +#include "cam_context_utils.h" +#include "cam_hw_mgr_intf.h" +#include "cam_req_mgr_interface.h" +#include "cam_sync_api.h" + +#define CAM_LRME_CTX_INDEX_SHIFT 32 + +/** + * struct cam_lrme_context + * + * @base : Base context pointer for this LRME context + * @req_base : List of base request for this LRME context + */ +struct cam_lrme_context { + struct cam_context *base; + struct cam_ctx_request req_base[CAM_CTX_REQ_MAX]; + uint64_t index; +}; + +int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx, + struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf, + uint32_t index); +int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx); + +#endif /* _CAM_LRME_CONTEXT_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_dev.c new file mode 100644 index 000000000000..5be16ef6a174 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/cam_lrme_dev.c @@ -0,0 +1,233 @@ +/* Copyright (c) 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 +#include +#include +#include +#include + +#include "cam_subdev.h" +#include "cam_node.h" +#include "cam_lrme_context.h" +#include "cam_lrme_hw_mgr.h" +#include "cam_lrme_hw_mgr_intf.h" + +#define CAM_LRME_DEV_NAME "cam-lrme" + +/** + * struct cam_lrme_dev + * + * @sd : Subdev information + * @ctx : List of base contexts + * @lrme_ctx : List of LRME contexts + * @lock : Mutex for LRME subdev + * @open_cnt : Open count of LRME subdev + */ +struct cam_lrme_dev { + struct cam_subdev sd; + struct cam_context ctx[CAM_CTX_MAX]; + struct cam_lrme_context lrme_ctx[CAM_CTX_MAX]; + struct mutex lock; + uint32_t open_cnt; +}; + +static struct cam_lrme_dev *g_lrme_dev; + +static int cam_lrme_dev_buf_done_cb(void *ctxt_to_hw_map, uint32_t evt_id, + void *evt_data) +{ + uint64_t index; + struct cam_context *ctx; + int rc; + + index = CAM_LRME_DECODE_CTX_INDEX(ctxt_to_hw_map); + CAM_DBG(CAM_LRME, "ctx index %llu, evt_id %u\n", index, evt_id); + ctx = &g_lrme_dev->ctx[index]; + rc = ctx->irq_cb_intf(ctx, evt_id, evt_data); + if (rc) + CAM_ERR(CAM_LRME, "irq callback failed"); + + return rc; +} + +static int cam_lrme_dev_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_lrme_dev *lrme_dev = g_lrme_dev; + + if (!lrme_dev) { + CAM_ERR(CAM_LRME, + "LRME Dev not initialized, dev=%pK", lrme_dev); + return -ENODEV; + } + + mutex_lock(&lrme_dev->lock); + lrme_dev->open_cnt++; + mutex_unlock(&lrme_dev->lock); + + return 0; +} + +static int cam_lrme_dev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_lrme_dev *lrme_dev = g_lrme_dev; + struct cam_node *node = v4l2_get_subdevdata(sd); + + if (!lrme_dev) { + CAM_ERR(CAM_LRME, "Invalid args"); + return -ENODEV; + } + + mutex_lock(&lrme_dev->lock); + lrme_dev->open_cnt--; + mutex_unlock(&lrme_dev->lock); + + if (!node) { + CAM_ERR(CAM_LRME, "Node is NULL"); + return -EINVAL; + } + + if (lrme_dev->open_cnt == 0) + cam_node_shutdown(node); + + return 0; +} + +static const struct v4l2_subdev_internal_ops cam_lrme_subdev_internal_ops = { + .open = cam_lrme_dev_open, + .close = cam_lrme_dev_close, +}; + +static int cam_lrme_dev_probe(struct platform_device *pdev) +{ + int rc; + int i; + struct cam_hw_mgr_intf hw_mgr_intf; + struct cam_node *node; + + g_lrme_dev = kzalloc(sizeof(struct cam_lrme_dev), GFP_KERNEL); + if (!g_lrme_dev) { + CAM_ERR(CAM_LRME, "No memory"); + return -ENOMEM; + } + g_lrme_dev->sd.internal_ops = &cam_lrme_subdev_internal_ops; + + mutex_init(&g_lrme_dev->lock); + + rc = cam_subdev_probe(&g_lrme_dev->sd, pdev, CAM_LRME_DEV_NAME, + CAM_LRME_DEVICE_TYPE); + if (rc) { + CAM_ERR(CAM_LRME, "LRME cam_subdev_probe failed"); + goto free_mem; + } + node = (struct cam_node *)g_lrme_dev->sd.token; + + rc = cam_lrme_hw_mgr_init(&hw_mgr_intf, cam_lrme_dev_buf_done_cb); + if (rc) { + CAM_ERR(CAM_LRME, "Can not initialized LRME HW manager"); + goto unregister; + } + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_lrme_context_init(&g_lrme_dev->lrme_ctx[i], + &g_lrme_dev->ctx[i], + &node->hw_mgr_intf, i); + if (rc) { + CAM_ERR(CAM_LRME, "LRME context init failed"); + goto deinit_ctx; + } + } + + rc = cam_node_init(node, &hw_mgr_intf, g_lrme_dev->ctx, CAM_CTX_MAX, + CAM_LRME_DEV_NAME); + if (rc) { + CAM_ERR(CAM_LRME, "LRME node init failed"); + goto deinit_ctx; + } + + CAM_DBG(CAM_LRME, "%s probe complete", g_lrme_dev->sd.name); + + return 0; + +deinit_ctx: + for (--i; i >= 0; i--) { + if (cam_lrme_context_deinit(&g_lrme_dev->lrme_ctx[i])) + CAM_ERR(CAM_LRME, "LRME context %d deinit failed", i); + } +unregister: + if (cam_subdev_remove(&g_lrme_dev->sd)) + CAM_ERR(CAM_LRME, "Failed in subdev remove"); +free_mem: + kfree(g_lrme_dev); + + return rc; +} + +static int cam_lrme_dev_remove(struct platform_device *pdev) +{ + int i; + int rc = 0; + + for (i = 0; i < CAM_CTX_MAX; i++) { + rc = cam_lrme_context_deinit(&g_lrme_dev->lrme_ctx[i]); + if (rc) + CAM_ERR(CAM_LRME, "LRME context %d deinit failed", i); + } + + rc = cam_lrme_hw_mgr_deinit(); + if (rc) + CAM_ERR(CAM_LRME, "Failed in hw mgr deinit, rc=%d", rc); + + rc = cam_subdev_remove(&g_lrme_dev->sd); + if (rc) + CAM_ERR(CAM_LRME, "Unregister failed"); + + mutex_destroy(&g_lrme_dev->lock); + kfree(g_lrme_dev); + g_lrme_dev = NULL; + + return rc; +} + +static const struct of_device_id cam_lrme_dt_match[] = { + { + .compatible = "qcom,cam-lrme" + }, + {} +}; + +static struct platform_driver cam_lrme_driver = { + .probe = cam_lrme_dev_probe, + .remove = cam_lrme_dev_remove, + .driver = { + .name = "cam_lrme", + .owner = THIS_MODULE, + .of_match_table = cam_lrme_dt_match, + }, +}; + +static int __init cam_lrme_dev_init_module(void) +{ + return platform_driver_register(&cam_lrme_driver); +} + +static void __exit cam_lrme_dev_exit_module(void) +{ + platform_driver_unregister(&cam_lrme_driver); +} + +module_init(cam_lrme_dev_init_module); +module_exit(cam_lrme_dev_exit_module); +MODULE_DESCRIPTION("MSM LRME driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/Makefile new file mode 100644 index 000000000000..d11e2036d43a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/Makefile @@ -0,0 +1,14 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += lrme_hw/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_hw_mgr.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c new file mode 100644 index 000000000000..1ed7fb10a485 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c @@ -0,0 +1,1122 @@ +/* Copyright (c) 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 +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_soc_util.h" +#include "cam_mem_mgr_api.h" +#include "cam_smmu_api.h" +#include "cam_packet_util.h" +#include "cam_lrme_context.h" +#include "cam_lrme_hw_intf.h" +#include "cam_lrme_hw_core.h" +#include "cam_lrme_hw_soc.h" +#include "cam_lrme_hw_mgr_intf.h" +#include "cam_lrme_hw_mgr.h" + +static struct cam_lrme_hw_mgr g_lrme_hw_mgr; + +static int cam_lrme_mgr_util_reserve_device(struct cam_lrme_hw_mgr *hw_mgr, + struct cam_lrme_acquire_args *lrme_acquire_args) +{ + int i, index = 0; + uint32_t min_ctx = UINT_MAX; + struct cam_lrme_device *hw_device = NULL; + + mutex_lock(&hw_mgr->hw_mgr_mutex); + if (!hw_mgr->device_count) { + mutex_unlock(&hw_mgr->hw_mgr_mutex); + CAM_ERR(CAM_LRME, "No device is registered"); + return -EINVAL; + } + + for (i = 0; i < hw_mgr->device_count && i < CAM_LRME_HW_MAX; i++) { + hw_device = &hw_mgr->hw_device[i]; + if (!hw_device->num_context) { + index = i; + break; + } + if (hw_device->num_context < min_ctx) { + min_ctx = hw_device->num_context; + index = i; + } + } + + hw_device = &hw_mgr->hw_device[index]; + hw_device->num_context++; + + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + CAM_DBG(CAM_LRME, "reserve device index %d", index); + + return index; +} + +static int cam_lrme_mgr_util_get_device(struct cam_lrme_hw_mgr *hw_mgr, + uint32_t device_index, struct cam_lrme_device **hw_device) +{ + if (!hw_mgr) { + CAM_ERR(CAM_LRME, "invalid params hw_mgr %pK", hw_mgr); + return -EINVAL; + } + + if (device_index >= CAM_LRME_HW_MAX) { + CAM_ERR(CAM_LRME, "Wrong device index %d", device_index); + return -EINVAL; + } + + *hw_device = &hw_mgr->hw_device[device_index]; + + return 0; +} + +static int cam_lrme_mgr_util_packet_validate(struct cam_packet *packet) +{ + struct cam_cmd_buf_desc *cmd_desc = NULL; + int i, rc; + + if (!packet) { + CAM_ERR(CAM_LRME, "Invalid args"); + return -EINVAL; + } + + CAM_DBG(CAM_LRME, "Packet request=%d, op_code=0x%x, size=%d, flags=%d", + packet->header.request_id, packet->header.op_code, + packet->header.size, packet->header.flags); + CAM_DBG(CAM_LRME, + "Packet cmdbuf(offset=%d, num=%d) io(offset=%d, num=%d)", + packet->cmd_buf_offset, packet->num_cmd_buf, + packet->io_configs_offset, packet->num_io_configs); + CAM_DBG(CAM_LRME, + "Packet Patch(offset=%d, num=%d) kmd(offset=%d, num=%d)", + packet->patch_offset, packet->num_patches, + packet->kmd_cmd_buf_offset, packet->kmd_cmd_buf_index); + + if (cam_packet_util_validate_packet(packet)) { + CAM_ERR(CAM_LRME, "invalid packet:%d %d %d %d %d", + packet->kmd_cmd_buf_index, + packet->num_cmd_buf, packet->cmd_buf_offset, + packet->io_configs_offset, packet->header.size); + return -EINVAL; + } + + if (!packet->num_io_configs) { + CAM_ERR(CAM_LRME, "no io configs"); + return -EINVAL; + } + + cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *)&packet->payload + + packet->cmd_buf_offset); + + for (i = 0; i < packet->num_cmd_buf; i++) { + if (!cmd_desc[i].length) + continue; + + CAM_DBG(CAM_LRME, + "CmdBuf[%d] hdl=%d, offset=%d, size=%d, len=%d, type=%d, meta_data=%d", + i, + cmd_desc[i].mem_handle, cmd_desc[i].offset, + cmd_desc[i].size, cmd_desc[i].length, cmd_desc[i].type, + cmd_desc[i].meta_data); + + rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]); + if (rc) { + CAM_ERR(CAM_LRME, "Invalid cmd buffer %d", i); + return rc; + } + } + + return 0; +} + +static int cam_lrme_mgr_util_prepare_io_buffer(int32_t iommu_hdl, + struct cam_hw_prepare_update_args *prepare, + struct cam_lrme_hw_io_buffer *input_buf, + struct cam_lrme_hw_io_buffer *output_buf, uint32_t io_buf_size) +{ + int rc = -EINVAL; + uint32_t num_in_buf, num_out_buf, i, j, plane; + struct cam_buf_io_cfg *io_cfg; + dma_addr_t io_addr[CAM_PACKET_MAX_PLANES]; + size_t size; + + num_in_buf = 0; + num_out_buf = 0; + io_cfg = (struct cam_buf_io_cfg *)((uint8_t *) + &prepare->packet->payload + + prepare->packet->io_configs_offset); + + for (i = 0; i < prepare->packet->num_io_configs; i++) { + CAM_DBG(CAM_LRME, + "IOConfig[%d] : handle[%d] Dir[%d] Res[%d] Fence[%d], Format[%d]", + i, io_cfg[i].mem_handle[0], io_cfg[i].direction, + io_cfg[i].resource_type, + io_cfg[i].fence, io_cfg[i].format); + + memset(io_addr, 0, sizeof(io_addr)); + for (plane = 0; plane < CAM_PACKET_MAX_PLANES; plane++) { + if (!io_cfg[i].mem_handle[plane]) + break; + + rc = cam_mem_get_io_buf(io_cfg[i].mem_handle[plane], + iommu_hdl, &io_addr[plane], &size); + if (rc) { + CAM_ERR(CAM_LRME, "Cannot get io buf for %d %d", + plane, rc); + return -ENOMEM; + } + + if ((size_t)io_cfg[i].offsets[plane] >= size) { + CAM_ERR(CAM_LRME, "Invalid plane offset: %zu", + (size_t)io_cfg[i].offsets[plane]); + return -EINVAL; + } + + io_addr[plane] += io_cfg[i].offsets[plane]; + + CAM_DBG(CAM_LRME, "IO Address[%d][%d] : %llu", + io_cfg[i].direction, plane, io_addr[plane]); + } + + switch (io_cfg[i].direction) { + case CAM_BUF_INPUT: { + if (num_in_buf >= io_buf_size) { + CAM_ERR(CAM_LRME, + "Invalid number of buffers %d %d %d", + num_in_buf, num_out_buf, io_buf_size); + return -EINVAL; + } + prepare->in_map_entries[num_in_buf].resource_handle = + io_cfg[i].resource_type; + prepare->in_map_entries[num_in_buf].sync_id = + io_cfg[i].fence; + + input_buf[num_in_buf].valid = true; + for (j = 0; j < plane; j++) + input_buf[num_in_buf].io_addr[j] = io_addr[j]; + input_buf[num_in_buf].num_plane = plane; + input_buf[num_in_buf].io_cfg = &io_cfg[i]; + + num_in_buf++; + break; + } + case CAM_BUF_OUTPUT: { + if (num_out_buf >= io_buf_size) { + CAM_ERR(CAM_LRME, + "Invalid number of buffers %d %d %d", + num_in_buf, num_out_buf, io_buf_size); + return -EINVAL; + } + prepare->out_map_entries[num_out_buf].resource_handle = + io_cfg[i].resource_type; + prepare->out_map_entries[num_out_buf].sync_id = + io_cfg[i].fence; + + output_buf[num_out_buf].valid = true; + for (j = 0; j < plane; j++) + output_buf[num_out_buf].io_addr[j] = io_addr[j]; + output_buf[num_out_buf].num_plane = plane; + output_buf[num_out_buf].io_cfg = &io_cfg[i]; + + num_out_buf++; + break; + } + default: + CAM_ERR(CAM_LRME, "Unsupported io direction %d", + io_cfg[i].direction); + return -EINVAL; + } + } + prepare->num_in_map_entries = num_in_buf; + prepare->num_out_map_entries = num_out_buf; + + return 0; +} + +static int cam_lrme_mgr_util_prepare_hw_update_entries( + struct cam_lrme_hw_mgr *hw_mgr, + struct cam_hw_prepare_update_args *prepare, + struct cam_lrme_hw_cmd_config_args *config_args, + struct cam_kmd_buf_info *kmd_buf_info) +{ + int i, rc = 0; + struct cam_lrme_device *hw_device = NULL; + uint32_t *kmd_buf_addr; + uint32_t num_entry; + uint32_t kmd_buf_max_size; + uint32_t kmd_buf_used_bytes = 0; + struct cam_hw_update_entry *hw_entry; + struct cam_cmd_buf_desc *cmd_desc = NULL; + + hw_device = config_args->hw_device; + if (!hw_device) { + CAM_ERR(CAM_LRME, "Invalid hw_device"); + return -EINVAL; + } + + kmd_buf_addr = (uint32_t *)((uint8_t *)kmd_buf_info->cpu_addr + + kmd_buf_info->used_bytes); + kmd_buf_max_size = kmd_buf_info->size - kmd_buf_info->used_bytes; + + config_args->cmd_buf_addr = kmd_buf_addr; + config_args->size = kmd_buf_max_size; + config_args->config_buf_size = 0; + + if (hw_device->hw_intf.hw_ops.process_cmd) { + rc = hw_device->hw_intf.hw_ops.process_cmd( + hw_device->hw_intf.hw_priv, + CAM_LRME_HW_CMD_PREPARE_HW_UPDATE, + config_args, + sizeof(struct cam_lrme_hw_cmd_config_args)); + if (rc) { + CAM_ERR(CAM_LRME, + "Failed in CMD_PREPARE_HW_UPDATE %d", rc); + return rc; + } + } else { + CAM_ERR(CAM_LRME, "Can't find handle function"); + return -EINVAL; + } + + kmd_buf_used_bytes += config_args->config_buf_size; + + if (!kmd_buf_used_bytes || (kmd_buf_used_bytes > kmd_buf_max_size)) { + CAM_ERR(CAM_LRME, "Invalid kmd used bytes %d (%d)", + kmd_buf_used_bytes, kmd_buf_max_size); + return -ENOMEM; + } + + hw_entry = prepare->hw_update_entries; + num_entry = 0; + + if (config_args->config_buf_size) { + if ((num_entry + 1) >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_LRME, "Insufficient HW entries :%d %d", + num_entry, prepare->max_hw_update_entries); + return -EINVAL; + } + + hw_entry[num_entry].handle = kmd_buf_info->handle; + hw_entry[num_entry].len = config_args->config_buf_size; + hw_entry[num_entry].offset = kmd_buf_info->offset; + + kmd_buf_info->used_bytes += config_args->config_buf_size; + kmd_buf_info->offset += config_args->config_buf_size; + num_entry++; + } + + cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *) + &prepare->packet->payload + prepare->packet->cmd_buf_offset); + + for (i = 0; i < prepare->packet->num_cmd_buf; i++) { + if (!cmd_desc[i].length) + continue; + + if ((num_entry + 1) >= prepare->max_hw_update_entries) { + CAM_ERR(CAM_LRME, "Exceed max num of entry"); + return -EINVAL; + } + + hw_entry[num_entry].handle = cmd_desc[i].mem_handle; + hw_entry[num_entry].len = cmd_desc[i].length; + hw_entry[num_entry].offset = cmd_desc[i].offset; + num_entry++; + } + prepare->num_hw_update_entries = num_entry; + + CAM_DBG(CAM_LRME, "FinalConfig : hw_entries=%d, Sync(in=%d, out=%d)", + prepare->num_hw_update_entries, prepare->num_in_map_entries, + prepare->num_out_map_entries); + + return rc; +} + +static void cam_lrme_mgr_util_put_frame_req( + struct list_head *src_list, + struct list_head *list, + spinlock_t *lock) +{ + spin_lock(lock); + list_add_tail(list, src_list); + spin_unlock(lock); +} + +static int cam_lrme_mgr_util_get_frame_req( + struct list_head *src_list, + struct cam_lrme_frame_request **frame_req, + spinlock_t *lock) +{ + int rc = 0; + struct cam_lrme_frame_request *req_ptr = NULL; + + spin_lock(lock); + if (!list_empty(src_list)) { + req_ptr = list_first_entry(src_list, + struct cam_lrme_frame_request, frame_list); + list_del_init(&req_ptr->frame_list); + } else { + rc = -ENOENT; + } + *frame_req = req_ptr; + spin_unlock(lock); + + return rc; +} + + +static int cam_lrme_mgr_util_submit_req(void *priv, void *data) +{ + struct cam_lrme_device *hw_device; + struct cam_lrme_hw_mgr *hw_mgr; + struct cam_lrme_frame_request *frame_req = NULL; + struct cam_lrme_hw_submit_args submit_args; + struct cam_lrme_mgr_work_data *work_data; + int rc; + int req_prio = 0; + + if (!priv) { + CAM_ERR(CAM_LRME, "worker doesn't have private data"); + return -EINVAL; + } + + hw_mgr = (struct cam_lrme_hw_mgr *)priv; + work_data = (struct cam_lrme_mgr_work_data *)data; + hw_device = work_data->hw_device; + + rc = cam_lrme_mgr_util_get_frame_req(&hw_device-> + frame_pending_list_high, &frame_req, &hw_device->high_req_lock); + + if (!frame_req) { + rc = cam_lrme_mgr_util_get_frame_req(&hw_device-> + frame_pending_list_normal, &frame_req, + &hw_device->normal_req_lock); + if (frame_req) + req_prio = 1; + } + + if (!frame_req) { + CAM_DBG(CAM_LRME, "No pending request"); + return 0; + } + + if (hw_device->hw_intf.hw_ops.process_cmd) { + submit_args.hw_update_entries = frame_req->hw_update_entries; + submit_args.num_hw_update_entries = + frame_req->num_hw_update_entries; + submit_args.frame_req = frame_req; + + rc = hw_device->hw_intf.hw_ops.process_cmd( + hw_device->hw_intf.hw_priv, + CAM_LRME_HW_CMD_SUBMIT, + &submit_args, sizeof(struct cam_lrme_hw_submit_args)); + + if (rc == -EBUSY) + CAM_DBG(CAM_LRME, "device busy"); + else if (rc) + CAM_ERR(CAM_LRME, "submit request failed rc %d", rc); + if (rc) { + req_prio == 0 ? spin_lock(&hw_device->high_req_lock) : + spin_lock(&hw_device->normal_req_lock); + list_add(&frame_req->frame_list, + (req_prio == 0 ? + &hw_device->frame_pending_list_high : + &hw_device->frame_pending_list_normal)); + req_prio == 0 ? spin_unlock(&hw_device->high_req_lock) : + spin_unlock(&hw_device->normal_req_lock); + } + if (rc == -EBUSY) + rc = 0; + } else { + req_prio == 0 ? spin_lock(&hw_device->high_req_lock) : + spin_lock(&hw_device->normal_req_lock); + list_add(&frame_req->frame_list, + (req_prio == 0 ? + &hw_device->frame_pending_list_high : + &hw_device->frame_pending_list_normal)); + req_prio == 0 ? spin_unlock(&hw_device->high_req_lock) : + spin_unlock(&hw_device->normal_req_lock); + rc = -EINVAL; + } + + CAM_DBG(CAM_LRME, "End of submit, rc %d", rc); + + return rc; +} + +static int cam_lrme_mgr_util_schedule_frame_req( + struct cam_lrme_hw_mgr *hw_mgr, struct cam_lrme_device *hw_device) +{ + int rc = 0; + struct crm_workq_task *task; + struct cam_lrme_mgr_work_data *work_data; + + task = cam_req_mgr_workq_get_task(hw_device->work); + if (!task) { + CAM_ERR(CAM_LRME, "Can not get task for worker"); + return -ENOMEM; + } + + work_data = (struct cam_lrme_mgr_work_data *)task->payload; + work_data->hw_device = hw_device; + + task->process_cb = cam_lrme_mgr_util_submit_req; + CAM_DBG(CAM_LRME, "enqueue submit task"); + rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0); + + return rc; +} + +static int cam_lrme_mgr_util_release(struct cam_lrme_hw_mgr *hw_mgr, + uint32_t device_index) +{ + int rc = 0; + struct cam_lrme_device *hw_device; + + rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device); + if (rc) { + CAM_ERR(CAM_LRME, "Error in getting device %d", rc); + return rc; + } + + mutex_lock(&hw_mgr->hw_mgr_mutex); + hw_device->num_context--; + mutex_unlock(&hw_mgr->hw_mgr_mutex); + + return rc; +} + +static int cam_lrme_mgr_cb(void *data, + struct cam_lrme_hw_cb_args *cb_args) +{ + struct cam_lrme_hw_mgr *hw_mgr = &g_lrme_hw_mgr; + int rc = 0; + bool frame_abort = true; + struct cam_lrme_frame_request *frame_req; + struct cam_lrme_device *hw_device; + + if (!data || !cb_args) { + CAM_ERR(CAM_LRME, "Invalid input args"); + return -EINVAL; + } + + hw_device = (struct cam_lrme_device *)data; + frame_req = cb_args->frame_req; + + if (cb_args->cb_type & CAM_LRME_CB_PUT_FRAME) { + memset(frame_req, 0x0, sizeof(*frame_req)); + INIT_LIST_HEAD(&frame_req->frame_list); + cam_lrme_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &frame_req->frame_list, + &hw_mgr->free_req_lock); + cb_args->cb_type &= ~CAM_LRME_CB_PUT_FRAME; + frame_req = NULL; + } + + if (cb_args->cb_type & CAM_LRME_CB_COMP_REG_UPDATE) { + cb_args->cb_type &= ~CAM_LRME_CB_COMP_REG_UPDATE; + CAM_DBG(CAM_LRME, "Reg update"); + } + + if (!frame_req) + return rc; + + if (cb_args->cb_type & CAM_LRME_CB_BUF_DONE) { + cb_args->cb_type &= ~CAM_LRME_CB_BUF_DONE; + frame_abort = false; + } else if (cb_args->cb_type & CAM_LRME_CB_ERROR) { + cb_args->cb_type &= ~CAM_LRME_CB_ERROR; + frame_abort = true; + } else { + CAM_ERR(CAM_LRME, "Wrong cb type %d, req %lld", + cb_args->cb_type, frame_req->req_id); + return -EINVAL; + } + + if (hw_mgr->event_cb) { + struct cam_hw_done_event_data buf_data; + + buf_data.request_id = frame_req->req_id; + CAM_DBG(CAM_LRME, "frame req %llu, frame_abort %d", + frame_req->req_id, frame_abort); + rc = hw_mgr->event_cb(frame_req->ctxt_to_hw_map, + frame_abort, &buf_data); + } else { + CAM_ERR(CAM_LRME, "No cb function"); + } + memset(frame_req, 0x0, sizeof(*frame_req)); + INIT_LIST_HEAD(&frame_req->frame_list); + cam_lrme_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &frame_req->frame_list, + &hw_mgr->free_req_lock); + + rc = cam_lrme_mgr_util_schedule_frame_req(hw_mgr, hw_device); + + return rc; +} + +static int cam_lrme_mgr_get_caps(void *hw_mgr_priv, void *hw_get_caps_args) +{ + int rc = 0; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_query_cap_cmd *args = hw_get_caps_args; + + if (sizeof(struct cam_lrme_query_cap_cmd) != args->size) { + CAM_ERR(CAM_LRME, + "sizeof(struct cam_query_cap_cmd) = %lu, args->size = %d", + sizeof(struct cam_query_cap_cmd), args->size); + return -EFAULT; + } + + if (copy_to_user(u64_to_user_ptr(args->caps_handle), + &(hw_mgr->lrme_caps), + sizeof(struct cam_lrme_query_cap_cmd))) { + CAM_ERR(CAM_LRME, "copy to user failed"); + return -EFAULT; + } + + return rc; +} + +static int cam_lrme_mgr_hw_acquire(void *hw_mgr_priv, void *hw_acquire_args) +{ + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_acquire_args *args = + (struct cam_hw_acquire_args *)hw_acquire_args; + struct cam_lrme_acquire_args lrme_acquire_args; + uint64_t device_index; + + if (!hw_mgr_priv || !args) { + CAM_ERR(CAM_LRME, + "Invalid input params hw_mgr_priv %pK, acquire_args %pK", + hw_mgr_priv, args); + return -EINVAL; + } + + if (copy_from_user(&lrme_acquire_args, + (void __user *)args->acquire_info, + sizeof(struct cam_lrme_acquire_args))) { + CAM_ERR(CAM_LRME, "Failed to copy acquire args from user"); + return -EFAULT; + } + + device_index = cam_lrme_mgr_util_reserve_device(hw_mgr, + &lrme_acquire_args); + CAM_DBG(CAM_LRME, "Get device id %llu", device_index); + + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Get wrong device id %llu", device_index); + return -EINVAL; + } + + /* device_index is the right 4 bit in ctxt_to_hw_map */ + args->ctxt_to_hw_map = (void *)device_index; + + return 0; +} + +static int cam_lrme_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args) +{ + int rc = 0; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_release_args *args = + (struct cam_hw_release_args *)hw_release_args; + uint64_t device_index; + + if (!hw_mgr_priv || !hw_release_args) { + CAM_ERR(CAM_LRME, "Invalid arguments %pK, %pK", + hw_mgr_priv, hw_release_args); + return -EINVAL; + } + + device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map); + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Invalid device index %llu", device_index); + return -EPERM; + } + + rc = cam_lrme_mgr_util_release(hw_mgr, device_index); + if (rc) + CAM_ERR(CAM_LRME, "Failed in release device, rc=%d", rc); + + return rc; +} + +static int cam_lrme_mgr_hw_flush(void *hw_mgr_priv, void *hw_flush_args) +{ int rc = 0, i; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_flush_args *args; + struct cam_lrme_device *hw_device; + struct cam_lrme_frame_request *frame_req = NULL, *req_to_flush = NULL; + struct cam_lrme_frame_request **req_list = NULL; + uint32_t device_index; + struct cam_lrme_hw_flush_args lrme_flush_args; + uint32_t priority; + + if (!hw_mgr_priv || !hw_flush_args) { + CAM_ERR(CAM_LRME, "Invalid args %pK %pK", + hw_mgr_priv, hw_flush_args); + return -EINVAL; + } + + args = (struct cam_hw_flush_args *)hw_flush_args; + device_index = ((uint64_t)args->ctxt_to_hw_map & 0xF); + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Invalid device index %d", device_index); + return -EPERM; + } + + rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device); + if (rc) { + CAM_ERR(CAM_LRME, "Error in getting device %d", rc); + goto end; + } + + req_list = (struct cam_lrme_frame_request **)args->flush_req_pending; + for (i = 0; i < args->num_req_pending; i++) { + frame_req = req_list[i]; + memset(frame_req, 0x0, sizeof(*frame_req)); + cam_lrme_mgr_util_put_frame_req(&hw_mgr->frame_free_list, + &frame_req->frame_list, &hw_mgr->free_req_lock); + } + + req_list = (struct cam_lrme_frame_request **)args->flush_req_active; + for (i = 0; i < args->num_req_active; i++) { + frame_req = req_list[i]; + priority = CAM_LRME_DECODE_PRIORITY(args->ctxt_to_hw_map); + spin_lock((priority == CAM_LRME_PRIORITY_HIGH) ? + &hw_device->high_req_lock : + &hw_device->normal_req_lock); + if (!list_empty(&frame_req->frame_list)) { + list_del_init(&frame_req->frame_list); + cam_lrme_mgr_util_put_frame_req( + &hw_mgr->frame_free_list, + &frame_req->frame_list, + &hw_mgr->free_req_lock); + } else + req_to_flush = frame_req; + spin_unlock((priority == CAM_LRME_PRIORITY_HIGH) ? + &hw_device->high_req_lock : + &hw_device->normal_req_lock); + } + if (!req_to_flush) + goto end; + if (hw_device->hw_intf.hw_ops.flush) { + lrme_flush_args.ctxt_to_hw_map = req_to_flush->ctxt_to_hw_map; + lrme_flush_args.flush_type = args->flush_type; + lrme_flush_args.req_to_flush = req_to_flush; + rc = hw_device->hw_intf.hw_ops.flush(hw_device->hw_intf.hw_priv, + &lrme_flush_args, + sizeof(lrme_flush_args)); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in HW Stop %d", rc); + goto end; + } + } else { + CAM_ERR(CAM_LRME, "No stop ops"); + goto end; + } + +end: + return rc; +} + + +static int cam_lrme_mgr_hw_start(void *hw_mgr_priv, void *hw_start_args) +{ + int rc = 0; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_start_args *args = + (struct cam_hw_start_args *)hw_start_args; + struct cam_lrme_device *hw_device; + uint32_t device_index; + + if (!hw_mgr || !args) { + CAM_ERR(CAM_LRME, "Invald input params"); + return -EINVAL; + } + + device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map); + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Invalid device index %d", device_index); + return -EPERM; + } + + CAM_DBG(CAM_LRME, "Start device index %d", device_index); + + rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to get hw device"); + return rc; + } + + if (hw_device->hw_intf.hw_ops.start) { + rc = hw_device->hw_intf.hw_ops.start( + hw_device->hw_intf.hw_priv, NULL, 0); + } else { + CAM_ERR(CAM_LRME, "Invald start function"); + return -EINVAL; + } + + return rc; +} + +static int cam_lrme_mgr_hw_stop(void *hw_mgr_priv, void *stop_args) +{ + int rc = 0; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_stop_args *args = + (struct cam_hw_stop_args *)stop_args; + struct cam_lrme_device *hw_device; + uint32_t device_index; + + if (!hw_mgr_priv || !stop_args) { + CAM_ERR(CAM_LRME, "Invalid arguments"); + return -EINVAL; + } + + device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map); + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Invalid device index %d", device_index); + return -EPERM; + } + + CAM_DBG(CAM_LRME, "Stop device index %d", device_index); + + rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to get hw device"); + return rc; + } + + if (hw_device->hw_intf.hw_ops.stop) { + rc = hw_device->hw_intf.hw_ops.stop( + hw_device->hw_intf.hw_priv, NULL, 0); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in HW stop %d", rc); + goto end; + } + } + +end: + return rc; +} + +static int cam_lrme_mgr_hw_prepare_update(void *hw_mgr_priv, + void *hw_prepare_update_args) +{ + int rc = 0, i; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_prepare_update_args *args = + (struct cam_hw_prepare_update_args *)hw_prepare_update_args; + struct cam_lrme_device *hw_device; + struct cam_kmd_buf_info kmd_buf; + struct cam_lrme_hw_cmd_config_args config_args; + struct cam_lrme_frame_request *frame_req = NULL; + uint32_t device_index; + + if (!hw_mgr_priv || !hw_prepare_update_args) { + CAM_ERR(CAM_LRME, "Invalid args %pK %pK", + hw_mgr_priv, hw_prepare_update_args); + return -EINVAL; + } + + device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map); + if (device_index >= hw_mgr->device_count) { + CAM_ERR(CAM_LRME, "Invalid device index %d", device_index); + return -EPERM; + } + + rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device); + if (rc) { + CAM_ERR(CAM_LRME, "Error in getting device %d", rc); + goto error; + } + + rc = cam_lrme_mgr_util_packet_validate(args->packet); + if (rc) { + CAM_ERR(CAM_LRME, "Error in packet validation %d", rc); + goto error; + } + + rc = cam_packet_util_get_kmd_buffer(args->packet, &kmd_buf); + if (rc) { + CAM_ERR(CAM_LRME, "Error in get kmd buf buffer %d", rc); + goto error; + } + + CAM_DBG(CAM_LRME, + "KMD Buf : hdl=%d, cpu_addr=%pK, offset=%d, size=%d, used=%d", + kmd_buf.handle, kmd_buf.cpu_addr, kmd_buf.offset, + kmd_buf.size, kmd_buf.used_bytes); + + rc = cam_packet_util_process_patches(args->packet, + hw_mgr->device_iommu.non_secure, hw_mgr->device_iommu.secure); + if (rc) { + CAM_ERR(CAM_LRME, "Patch packet failed, rc=%d", rc); + return rc; + } + + memset(&config_args, 0, sizeof(config_args)); + config_args.hw_device = hw_device; + + rc = cam_lrme_mgr_util_prepare_io_buffer( + hw_mgr->device_iommu.non_secure, args, + config_args.input_buf, config_args.output_buf, + CAM_LRME_MAX_IO_BUFFER); + if (rc) { + CAM_ERR(CAM_LRME, "Error in prepare IO Buf %d", rc); + goto error; + } + /* Check port number */ + if (args->num_in_map_entries == 0 || args->num_out_map_entries == 0) { + CAM_ERR(CAM_LRME, "Error in port number in %d, out %d", + args->num_in_map_entries, args->num_out_map_entries); + rc = -EINVAL; + goto error; + } + + rc = cam_lrme_mgr_util_prepare_hw_update_entries(hw_mgr, args, + &config_args, &kmd_buf); + if (rc) { + CAM_ERR(CAM_LRME, "Error in hw update entries %d", rc); + goto error; + } + + rc = cam_lrme_mgr_util_get_frame_req(&hw_mgr->frame_free_list, + &frame_req, &hw_mgr->free_req_lock); + if (rc || !frame_req) { + CAM_ERR(CAM_LRME, "Can not get free frame request"); + goto error; + } + + frame_req->ctxt_to_hw_map = args->ctxt_to_hw_map; + frame_req->req_id = args->packet->header.request_id; + frame_req->hw_device = hw_device; + frame_req->num_hw_update_entries = args->num_hw_update_entries; + for (i = 0; i < args->num_hw_update_entries; i++) + frame_req->hw_update_entries[i] = args->hw_update_entries[i]; + + args->priv = frame_req; + + CAM_DBG(CAM_LRME, "FramePrepare : Frame[%lld]", frame_req->req_id); + + return 0; + +error: + return rc; +} + +static int cam_lrme_mgr_hw_config(void *hw_mgr_priv, + void *hw_config_args) +{ + int rc = 0; + struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_config_args *args = + (struct cam_hw_config_args *)hw_config_args; + struct cam_lrme_frame_request *frame_req; + struct cam_lrme_device *hw_device = NULL; + enum cam_lrme_hw_mgr_ctx_priority priority; + + if (!hw_mgr_priv || !hw_config_args) { + CAM_ERR(CAM_LRME, "Invalid arguments, hw_mgr %pK, config %pK", + hw_mgr_priv, hw_config_args); + return -EINVAL; + } + + if (!args->num_hw_update_entries) { + CAM_ERR(CAM_LRME, "No hw update entries"); + return -EINVAL; + } + + frame_req = (struct cam_lrme_frame_request *)args->priv; + if (!frame_req) { + CAM_ERR(CAM_LRME, "No frame request"); + return -EINVAL; + } + + hw_device = frame_req->hw_device; + if (!hw_device) + return -EINVAL; + + priority = CAM_LRME_DECODE_PRIORITY(args->ctxt_to_hw_map); + if (priority == CAM_LRME_PRIORITY_HIGH) { + cam_lrme_mgr_util_put_frame_req( + &hw_device->frame_pending_list_high, + &frame_req->frame_list, &hw_device->high_req_lock); + } else { + cam_lrme_mgr_util_put_frame_req( + &hw_device->frame_pending_list_normal, + &frame_req->frame_list, &hw_device->normal_req_lock); + } + + CAM_DBG(CAM_LRME, "schedule req %llu", frame_req->req_id); + rc = cam_lrme_mgr_util_schedule_frame_req(hw_mgr, hw_device); + + return rc; +} + +int cam_lrme_mgr_register_device( + struct cam_hw_intf *lrme_hw_intf, + struct cam_iommu_handle *device_iommu, + struct cam_iommu_handle *cdm_iommu) +{ + struct cam_lrme_device *hw_device; + char buf[128]; + int i, rc; + + hw_device = &g_lrme_hw_mgr.hw_device[lrme_hw_intf->hw_idx]; + + g_lrme_hw_mgr.device_iommu = *device_iommu; + g_lrme_hw_mgr.cdm_iommu = *cdm_iommu; + + memcpy(&hw_device->hw_intf, lrme_hw_intf, sizeof(struct cam_hw_intf)); + + spin_lock_init(&hw_device->high_req_lock); + spin_lock_init(&hw_device->normal_req_lock); + INIT_LIST_HEAD(&hw_device->frame_pending_list_high); + INIT_LIST_HEAD(&hw_device->frame_pending_list_normal); + + rc = snprintf(buf, sizeof(buf), "cam_lrme_device_submit_worker%d", + lrme_hw_intf->hw_idx); + CAM_DBG(CAM_LRME, "Create submit workq for %s", buf); + rc = cam_req_mgr_workq_create(buf, + CAM_LRME_WORKQ_NUM_TASK, + &hw_device->work, CRM_WORKQ_USAGE_NON_IRQ); + if (rc) { + CAM_ERR(CAM_LRME, + "Unable to create a worker, rc=%d", rc); + return rc; + } + + for (i = 0; i < CAM_LRME_WORKQ_NUM_TASK; i++) + hw_device->work->task.pool[i].payload = + &hw_device->work_data[i]; + + if (hw_device->hw_intf.hw_ops.process_cmd) { + struct cam_lrme_hw_cmd_set_cb cb_args; + + cb_args.cam_lrme_hw_mgr_cb = cam_lrme_mgr_cb; + cb_args.data = hw_device; + + rc = hw_device->hw_intf.hw_ops.process_cmd( + hw_device->hw_intf.hw_priv, + CAM_LRME_HW_CMD_REGISTER_CB, + &cb_args, sizeof(cb_args)); + if (rc) { + CAM_ERR(CAM_LRME, "Register cb failed"); + goto destroy_workqueue; + } + CAM_DBG(CAM_LRME, "cb registered"); + } + + if (hw_device->hw_intf.hw_ops.get_hw_caps) { + rc = hw_device->hw_intf.hw_ops.get_hw_caps( + hw_device->hw_intf.hw_priv, &hw_device->hw_caps, + sizeof(hw_device->hw_caps)); + if (rc) + CAM_ERR(CAM_LRME, "Get caps failed"); + } else { + CAM_ERR(CAM_LRME, "No get_hw_caps function"); + goto destroy_workqueue; + } + g_lrme_hw_mgr.lrme_caps.dev_caps[lrme_hw_intf->hw_idx] = + hw_device->hw_caps; + g_lrme_hw_mgr.device_count++; + g_lrme_hw_mgr.lrme_caps.device_iommu = g_lrme_hw_mgr.device_iommu; + g_lrme_hw_mgr.lrme_caps.cdm_iommu = g_lrme_hw_mgr.cdm_iommu; + g_lrme_hw_mgr.lrme_caps.num_devices = g_lrme_hw_mgr.device_count; + + hw_device->valid = true; + + CAM_DBG(CAM_LRME, "device registration done"); + return 0; + +destroy_workqueue: + cam_req_mgr_workq_destroy(&hw_device->work); + + return rc; +} + +int cam_lrme_mgr_deregister_device(int device_index) +{ + struct cam_lrme_device *hw_device; + + hw_device = &g_lrme_hw_mgr.hw_device[device_index]; + cam_req_mgr_workq_destroy(&hw_device->work); + memset(hw_device, 0x0, sizeof(struct cam_lrme_device)); + g_lrme_hw_mgr.device_count--; + + return 0; +} + +int cam_lrme_hw_mgr_deinit(void) +{ + mutex_destroy(&g_lrme_hw_mgr.hw_mgr_mutex); + memset(&g_lrme_hw_mgr, 0x0, sizeof(g_lrme_hw_mgr)); + + return 0; +} + +int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, + cam_hw_event_cb_func cam_lrme_dev_buf_done_cb) +{ + int i, rc = 0; + struct cam_lrme_frame_request *frame_req; + + if (!hw_mgr_intf) + return -EINVAL; + + CAM_DBG(CAM_LRME, "device count %d", g_lrme_hw_mgr.device_count); + if (g_lrme_hw_mgr.device_count > CAM_LRME_HW_MAX) { + CAM_ERR(CAM_LRME, "Invalid count of devices"); + return -EINVAL; + } + + memset(hw_mgr_intf, 0, sizeof(*hw_mgr_intf)); + + mutex_init(&g_lrme_hw_mgr.hw_mgr_mutex); + spin_lock_init(&g_lrme_hw_mgr.free_req_lock); + INIT_LIST_HEAD(&g_lrme_hw_mgr.frame_free_list); + + /* Init hw mgr frame requests and add to free list */ + for (i = 0; i < CAM_CTX_REQ_MAX * CAM_CTX_MAX; i++) { + frame_req = &g_lrme_hw_mgr.frame_req[i]; + + memset(frame_req, 0x0, sizeof(*frame_req)); + INIT_LIST_HEAD(&frame_req->frame_list); + + list_add_tail(&frame_req->frame_list, + &g_lrme_hw_mgr.frame_free_list); + } + + hw_mgr_intf->hw_mgr_priv = &g_lrme_hw_mgr; + hw_mgr_intf->hw_get_caps = cam_lrme_mgr_get_caps; + hw_mgr_intf->hw_acquire = cam_lrme_mgr_hw_acquire; + hw_mgr_intf->hw_release = cam_lrme_mgr_hw_release; + hw_mgr_intf->hw_start = cam_lrme_mgr_hw_start; + hw_mgr_intf->hw_stop = cam_lrme_mgr_hw_stop; + hw_mgr_intf->hw_prepare_update = cam_lrme_mgr_hw_prepare_update; + hw_mgr_intf->hw_config = cam_lrme_mgr_hw_config; + hw_mgr_intf->hw_read = NULL; + hw_mgr_intf->hw_write = NULL; + hw_mgr_intf->hw_close = NULL; + hw_mgr_intf->hw_flush = cam_lrme_mgr_hw_flush; + + g_lrme_hw_mgr.event_cb = cam_lrme_dev_buf_done_cb; + + CAM_DBG(CAM_LRME, "Hw mgr init done"); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h new file mode 100644 index 000000000000..f7ce4d23cb63 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h @@ -0,0 +1,120 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_MGR_H_ +#define _CAM_LRME_HW_MGR_H_ + +#include +#include + +#include +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_cpas_api.h" +#include "cam_debug_util.h" +#include "cam_hw_mgr_intf.h" +#include "cam_req_mgr_workq.h" +#include "cam_lrme_hw_intf.h" +#include "cam_context.h" + +#define CAM_LRME_HW_MAX 1 +#define CAM_LRME_WORKQ_NUM_TASK 10 + +#define CAM_LRME_DECODE_DEVICE_INDEX(ctxt_to_hw_map) \ + ((uint64_t)ctxt_to_hw_map & 0xF) + +#define CAM_LRME_DECODE_PRIORITY(ctxt_to_hw_map) \ + (((uint64_t)ctxt_to_hw_map & 0xF0) >> 4) + +#define CAM_LRME_DECODE_CTX_INDEX(ctxt_to_hw_map) \ + ((uint64_t)ctxt_to_hw_map >> CAM_LRME_CTX_INDEX_SHIFT) + +/** + * enum cam_lrme_hw_mgr_ctx_priority + * + * CAM_LRME_PRIORITY_HIGH : High priority client + * CAM_LRME_PRIORITY_NORMAL : Normal priority client + */ +enum cam_lrme_hw_mgr_ctx_priority { + CAM_LRME_PRIORITY_HIGH, + CAM_LRME_PRIORITY_NORMAL, +}; + +/** + * struct cam_lrme_mgr_work_data : HW Mgr work data + * + * hw_device : Pointer to the hw device + */ +struct cam_lrme_mgr_work_data { + struct cam_lrme_device *hw_device; +}; + +/** + * struct cam_lrme_device : LRME HW device + * + * @hw_caps : HW device's capabilities + * @hw_intf : HW device's interface information + * @num_context : Number of contexts using this device + * @valid : Whether this device is valid + * @work : HW device's work queue + * @work_data : HW device's work data + * @frame_pending_list_high : High priority request queue + * @frame_pending_list_normal : Normal priority request queue + * @high_req_lock : Spinlock of high priority queue + * @normal_req_lock : Spinlock of normal priority queue + */ +struct cam_lrme_device { + struct cam_lrme_dev_cap hw_caps; + struct cam_hw_intf hw_intf; + uint32_t num_context; + bool valid; + struct cam_req_mgr_core_workq *work; + struct cam_lrme_mgr_work_data work_data[CAM_LRME_WORKQ_NUM_TASK]; + struct list_head frame_pending_list_high; + struct list_head frame_pending_list_normal; + spinlock_t high_req_lock; + spinlock_t normal_req_lock; +}; + +/** + * struct cam_lrme_hw_mgr : LRME HW manager + * + * @device_count : Number of HW devices + * @frame_free_list : List of free frame request + * @hw_mgr_mutex : Mutex to protect HW manager data + * @free_req_lock :Spinlock to protect frame_free_list + * @hw_device : List of HW devices + * @device_iommu : Device iommu + * @cdm_iommu : cdm iommu + * @frame_req : List of frame request to use + * @lrme_caps : LRME capabilities + * @event_cb : IRQ callback function + */ +struct cam_lrme_hw_mgr { + uint32_t device_count; + struct list_head frame_free_list; + struct mutex hw_mgr_mutex; + spinlock_t free_req_lock; + struct cam_lrme_device hw_device[CAM_LRME_HW_MAX]; + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + struct cam_lrme_frame_request frame_req[CAM_CTX_REQ_MAX * CAM_CTX_MAX]; + struct cam_lrme_query_cap_cmd lrme_caps; + cam_hw_event_cb_func event_cb; +}; + +int cam_lrme_mgr_register_device(struct cam_hw_intf *lrme_hw_intf, + struct cam_iommu_handle *device_iommu, + struct cam_iommu_handle *cdm_iommu); +int cam_lrme_mgr_deregister_device(int device_index); + +#endif /* _CAM_LRME_HW_MGR_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h new file mode 100644 index 000000000000..8bb609cdb01b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_MGR_INTF_H_ +#define _CAM_LRME_HW_MGR_INTF_H_ + +#include + +#include "cam_debug_util.h" +#include "cam_hw_mgr_intf.h" + +int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, + cam_hw_event_cb_func cam_lrme_dev_buf_done_cb); +int cam_lrme_hw_mgr_deinit(void); + +#endif /* _CAM_LRME_HW_MGR_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile new file mode 100644 index 000000000000..a40eb5d005d8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile @@ -0,0 +1,13 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cdm +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus0 +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_hw_dev.o cam_lrme_hw_core.o cam_lrme_hw_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c new file mode 100644 index 000000000000..21e66a2dbd39 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c @@ -0,0 +1,1243 @@ +/* Copyright (c) 2017-2018, 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 "cam_lrme_hw_core.h" +#include "cam_lrme_hw_soc.h" +#include "cam_smmu_api.h" + +static void cam_lrme_cdm_write_reg_val_pair(uint32_t *buffer, + uint32_t *index, uint32_t reg_offset, uint32_t reg_value) +{ + buffer[(*index)++] = reg_offset; + buffer[(*index)++] = reg_value; +} + +static void cam_lrme_hw_util_fill_fe_reg(struct cam_lrme_hw_io_buffer *io_buf, + uint32_t index, uint32_t *reg_val_pair, uint32_t *num_cmd, + struct cam_lrme_hw_info *hw_info) +{ + uint32_t reg_val; + + /* 1. config buffer size */ + reg_val = io_buf->io_cfg->planes[0].width; + reg_val |= (io_buf->io_cfg->planes[0].height << 16); + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].rd_buffer_size, + reg_val); + + CAM_DBG(CAM_LRME, + "width %d", io_buf->io_cfg->planes[0].width); + CAM_DBG(CAM_LRME, + "height %d", io_buf->io_cfg->planes[0].height); + + /* 2. config image address */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].addr_image, + io_buf->io_addr[0]); + + CAM_DBG(CAM_LRME, "io addr %llu", io_buf->io_addr[0]); + + /* 3. config stride */ + reg_val = io_buf->io_cfg->planes[0].plane_stride; + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].rd_stride, + reg_val); + + CAM_DBG(CAM_LRME, "plane_stride %d", + io_buf->io_cfg->planes[0].plane_stride); + + /* 4. enable client */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].core_cfg, 0x1); + + /* 5. unpack_cfg */ + if (io_buf->io_cfg->format == CAM_FORMAT_PD10) + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0, + 0x0); + else if (io_buf->io_cfg->format == CAM_FORMAT_Y_ONLY) + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0, + 0x1); + else if (io_buf->io_cfg->format == CAM_FORMAT_PLAIN16_10) + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0, + 0x22); + else + CAM_ERR(CAM_LRME, "Unsupported format %d", + io_buf->io_cfg->format); +} + +static void cam_lrme_hw_util_fill_we_reg(struct cam_lrme_hw_io_buffer *io_buf, + uint32_t index, uint32_t *reg_val_pair, uint32_t *num_cmd, + struct cam_lrme_hw_info *hw_info) +{ + /* config client mode */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].cfg, + 0x1); + + /* image address */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].addr_image, + io_buf->io_addr[0]); + CAM_DBG(CAM_LRME, "io addr %llu", io_buf->io_addr[0]); + + /* buffer width and height */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].buffer_width_cfg, + io_buf->io_cfg->planes[0].width); + CAM_DBG(CAM_LRME, "width %d", io_buf->io_cfg->planes[0].width); + + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].buffer_height_cfg, + io_buf->io_cfg->planes[0].height); + CAM_DBG(CAM_LRME, "height %d", io_buf->io_cfg->planes[0].height); + + /* packer cfg */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].packer_cfg, + (index == 0) ? 0x1 : 0x5); + + /* client stride */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd, + hw_info->bus_wr_reg.bus_client_reg[index].wr_stride, + io_buf->io_cfg->planes[0].meta_stride); + CAM_DBG(CAM_LRME, "plane_stride %d", + io_buf->io_cfg->planes[0].plane_stride); +} + + +static int cam_lrme_hw_util_process_config_hw(struct cam_hw_info *lrme_hw, + struct cam_lrme_hw_cmd_config_args *config_args) +{ + int i; + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_cdm_info *hw_cdm_info; + uint32_t *cmd_buf_addr = config_args->cmd_buf_addr; + uint32_t reg_val_pair[CAM_LRME_MAX_REG_PAIR_NUM]; + struct cam_lrme_hw_io_buffer *io_buf; + struct cam_lrme_hw_info *hw_info = + ((struct cam_lrme_core *)lrme_hw->core_info)->hw_info; + uint32_t num_cmd = 0; + uint32_t size; + uint32_t mem_base, available_size = config_args->size; + uint32_t output_res_mask = 0, input_res_mask = 0; + + + if (!cmd_buf_addr) { + CAM_ERR(CAM_LRME, "Invalid input args"); + return -EINVAL; + } + + hw_cdm_info = + ((struct cam_lrme_core *)lrme_hw->core_info)->hw_cdm_info; + + for (i = 0; i < CAM_LRME_MAX_IO_BUFFER; i++) { + io_buf = &config_args->input_buf[i]; + + if (io_buf->valid == false) + break; + + if (io_buf->io_cfg->direction != CAM_BUF_INPUT) { + CAM_ERR(CAM_LRME, "Incorrect direction %d %d", + io_buf->io_cfg->direction, CAM_BUF_INPUT); + return -EINVAL; + } + CAM_DBG(CAM_LRME, + "resource_type %d", io_buf->io_cfg->resource_type); + + switch (io_buf->io_cfg->resource_type) { + case CAM_LRME_IO_TYPE_TAR: + cam_lrme_hw_util_fill_fe_reg(io_buf, 0, reg_val_pair, + &num_cmd, hw_info); + + input_res_mask |= CAM_LRME_INPUT_PORT_TYPE_TAR; + break; + case CAM_LRME_IO_TYPE_REF: + cam_lrme_hw_util_fill_fe_reg(io_buf, 1, reg_val_pair, + &num_cmd, hw_info); + + input_res_mask |= CAM_LRME_INPUT_PORT_TYPE_REF; + break; + default: + CAM_ERR(CAM_LRME, "wrong resource_type %d", + io_buf->io_cfg->resource_type); + return -EINVAL; + } + } + + for (i = 0; i < CAM_LRME_BUS_RD_MAX_CLIENTS; i++) + if (!((input_res_mask >> i) & 0x1)) + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd, + hw_info->bus_rd_reg.bus_client_reg[i].core_cfg, + 0x0); + + for (i = 0; i < CAM_LRME_MAX_IO_BUFFER; i++) { + io_buf = &config_args->output_buf[i]; + + if (io_buf->valid == false) + break; + + if (io_buf->io_cfg->direction != CAM_BUF_OUTPUT) { + CAM_ERR(CAM_LRME, "Incorrect direction %d %d", + io_buf->io_cfg->direction, CAM_BUF_INPUT); + return -EINVAL; + } + + CAM_DBG(CAM_LRME, "resource_type %d", + io_buf->io_cfg->resource_type); + switch (io_buf->io_cfg->resource_type) { + case CAM_LRME_IO_TYPE_DS2: + cam_lrme_hw_util_fill_we_reg(io_buf, 0, reg_val_pair, + &num_cmd, hw_info); + + output_res_mask |= CAM_LRME_OUTPUT_PORT_TYPE_DS2; + break; + case CAM_LRME_IO_TYPE_RES: + cam_lrme_hw_util_fill_we_reg(io_buf, 1, reg_val_pair, + &num_cmd, hw_info); + + output_res_mask |= CAM_LRME_OUTPUT_PORT_TYPE_RES; + break; + + default: + CAM_ERR(CAM_LRME, "wrong resource_type %d", + io_buf->io_cfg->resource_type); + return -EINVAL; + } + } + + for (i = 0; i < CAM_LRME_BUS_RD_MAX_CLIENTS; i++) + if (!((output_res_mask >> i) & 0x1)) + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd, + hw_info->bus_wr_reg.bus_client_reg[i].cfg, 0x0); + + if (output_res_mask) { + /* write composite mask */ + cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd, + hw_info->bus_wr_reg.common_reg.composite_mask_0, + output_res_mask); + } + + size = hw_cdm_info->cdm_ops->cdm_required_size_changebase(); + if ((size * 4) > available_size) { + CAM_ERR(CAM_LRME, "buf size:%d is not sufficient, expected: %d", + available_size, size); + return -EINVAL; + } + + mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(soc_info, CAM_LRME_BASE_IDX); + + hw_cdm_info->cdm_ops->cdm_write_changebase(cmd_buf_addr, mem_base); + cmd_buf_addr += size; + available_size -= (size * 4); + + size = hw_cdm_info->cdm_ops->cdm_required_size_reg_random( + num_cmd / 2); + + if ((size * 4) > available_size) { + CAM_ERR(CAM_LRME, "buf size:%d is not sufficient, expected: %d", + available_size, size); + return -ENOMEM; + } + + hw_cdm_info->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmd / 2, + reg_val_pair); + cmd_buf_addr += size; + available_size -= (size * 4); + + config_args->config_buf_size = + config_args->size - available_size; + + return 0; +} + +static int cam_lrme_hw_util_submit_go(struct cam_hw_info *lrme_hw) +{ + struct cam_lrme_core *lrme_core; + struct cam_hw_soc_info *soc_info; + struct cam_lrme_hw_info *hw_info; + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + hw_info = lrme_core->hw_info; + soc_info = &lrme_hw->soc_info; + + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.cmd); + + return 0; +} + +static int cam_lrme_hw_util_reset(struct cam_hw_info *lrme_hw, + uint32_t reset_type) +{ + struct cam_lrme_core *lrme_core; + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_hw_info *hw_info; + long time_left; + + lrme_core = lrme_hw->core_info; + hw_info = lrme_core->hw_info; + + switch (reset_type) { + case CAM_LRME_HW_RESET_TYPE_HW_RESET: + reinit_completion(&lrme_core->reset_complete); + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_rst_cmd); + time_left = wait_for_completion_timeout( + &lrme_core->reset_complete, + msecs_to_jiffies(CAM_LRME_HW_RESET_TIMEOUT)); + if (time_left <= 0) { + CAM_ERR(CAM_LRME, + "HW reset wait failed time_left=%ld", + time_left); + return -ETIMEDOUT; + } + break; + case CAM_LRME_HW_RESET_TYPE_SW_RESET: + cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.sw_reset); + cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.sw_reset); + reinit_completion(&lrme_core->reset_complete); + cam_io_w_mb(0x2, soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_rst_cmd); + time_left = wait_for_completion_timeout( + &lrme_core->reset_complete, + msecs_to_jiffies(CAM_LRME_HW_RESET_TIMEOUT)); + if (time_left <= 0) { + CAM_ERR(CAM_LRME, + "SW reset wait failed time_left=%ld", + time_left); + return -ETIMEDOUT; + } + break; + } + + return 0; +} + +int cam_lrme_hw_util_get_caps(struct cam_hw_info *lrme_hw, + struct cam_lrme_dev_cap *hw_caps) +{ + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_hw_info *hw_info = + ((struct cam_lrme_core *)lrme_hw->core_info)->hw_info; + uint32_t reg_value; + + if (!hw_info) { + CAM_ERR(CAM_LRME, "Invalid hw info data"); + return -EINVAL; + } + + reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base + + hw_info->clc_reg.clc_hw_version); + hw_caps->clc_hw_version.gen = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C); + hw_caps->clc_hw_version.rev = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->clc_hw_version.step = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.hw_version); + hw_caps->bus_rd_hw_version.gen = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C); + hw_caps->bus_rd_hw_version.rev = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->bus_rd_hw_version.step = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.hw_version); + hw_caps->bus_wr_hw_version.gen = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C); + hw_caps->bus_wr_hw_version.rev = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->bus_wr_hw_version.step = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_hw_version); + hw_caps->top_hw_version.gen = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C); + hw_caps->top_hw_version.rev = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->top_hw_version.step = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_titan_version); + hw_caps->top_titan_version.gen = + CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C); + hw_caps->top_titan_version.rev = + CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10); + hw_caps->top_titan_version.step = + CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0); + + return 0; +} + +static int cam_lrme_hw_util_submit_req(struct cam_lrme_core *lrme_core, + struct cam_lrme_frame_request *frame_req) +{ + struct cam_lrme_cdm_info *hw_cdm_info = + lrme_core->hw_cdm_info; + struct cam_cdm_bl_request *cdm_cmd = hw_cdm_info->cdm_cmd; + struct cam_hw_update_entry *cmd; + int i, rc = 0; + + if (frame_req->num_hw_update_entries > 0) { + cdm_cmd->cmd_arrary_count = frame_req->num_hw_update_entries; + cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE; + cdm_cmd->flag = false; + cdm_cmd->userdata = NULL; + cdm_cmd->cookie = 0; + + for (i = 0; i <= frame_req->num_hw_update_entries; i++) { + cmd = (frame_req->hw_update_entries + i); + cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle; + cdm_cmd->cmd[i].offset = cmd->offset; + cdm_cmd->cmd[i].len = cmd->len; + } + + rc = cam_cdm_submit_bls(hw_cdm_info->cdm_handle, cdm_cmd); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to submit cdm commands"); + return -EINVAL; + } + } else { + CAM_ERR(CAM_LRME, "No hw update entry"); + rc = -EINVAL; + } + + return rc; +} + +static int cam_lrme_hw_util_flush_ctx(struct cam_hw_info *lrme_hw, + void *ctxt_to_hw_map) +{ + int rc = -ENODEV; + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + struct cam_lrme_hw_cb_args cb_args; + struct cam_lrme_frame_request *req_proc, *req_submit; + struct cam_lrme_hw_submit_args submit_args; + + rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET); + if (rc) { + CAM_ERR(CAM_LRME, "reset failed"); + return rc; + } + + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + req_proc = lrme_core->req_proc; + req_submit = lrme_core->req_submit; + lrme_core->req_proc = NULL; + lrme_core->req_submit = NULL; + + if (req_submit && req_submit->ctxt_to_hw_map == ctxt_to_hw_map) { + cb_args.cb_type = CAM_LRME_CB_PUT_FRAME; + cb_args.frame_req = req_submit; + if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core-> + hw_mgr_cb.data, &cb_args); + } else if (req_submit) { + submit_args.frame_req = req_submit; + submit_args.hw_update_entries = req_submit->hw_update_entries; + submit_args.num_hw_update_entries = + req_submit->num_hw_update_entries; + rc = cam_lrme_hw_util_submit_req(lrme_core, req_submit); + if (rc) + CAM_ERR(CAM_LRME, "Submit failed"); + lrme_core->req_submit = req_submit; + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + } + + if (req_proc && req_proc->ctxt_to_hw_map == ctxt_to_hw_map) { + cb_args.cb_type = CAM_LRME_CB_PUT_FRAME; + cb_args.frame_req = req_proc; + if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core-> + hw_mgr_cb.data, &cb_args); + } else if (req_proc) { + submit_args.frame_req = req_proc; + submit_args.hw_update_entries = req_proc->hw_update_entries; + submit_args.num_hw_update_entries = + req_proc->num_hw_update_entries; + rc = cam_lrme_hw_util_submit_req(lrme_core, req_proc); + if (rc) + CAM_ERR(CAM_LRME, "Submit failed"); + lrme_core->req_submit = req_proc; + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + } + + return rc; +} + +static int cam_lrme_hw_util_flush_req(struct cam_hw_info *lrme_hw, + struct cam_lrme_frame_request *req_to_flush) +{ + int rc = -ENODEV; + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + struct cam_lrme_hw_cb_args cb_args; + struct cam_lrme_frame_request *req_proc, *req_submit; + struct cam_lrme_hw_submit_args submit_args; + + rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET); + if (rc) { + CAM_ERR(CAM_LRME, "reset failed"); + return rc; + } + + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + req_proc = lrme_core->req_proc; + req_submit = lrme_core->req_submit; + lrme_core->req_proc = NULL; + lrme_core->req_submit = NULL; + + if (req_submit && req_submit == req_to_flush) { + cb_args.cb_type = CAM_LRME_CB_PUT_FRAME; + cb_args.frame_req = req_submit; + if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core-> + hw_mgr_cb.data, &cb_args); + } else if (req_submit) { + submit_args.frame_req = req_submit; + submit_args.hw_update_entries = req_submit->hw_update_entries; + submit_args.num_hw_update_entries = + req_submit->num_hw_update_entries; + rc = cam_lrme_hw_util_submit_req(lrme_core, req_submit); + if (rc) + CAM_ERR(CAM_LRME, "Submit failed"); + lrme_core->req_submit = req_submit; + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + } + + if (req_proc && req_proc == req_to_flush) { + cb_args.cb_type = CAM_LRME_CB_PUT_FRAME; + cb_args.frame_req = req_proc; + if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core-> + hw_mgr_cb.data, &cb_args); + } else if (req_proc) { + submit_args.frame_req = req_proc; + submit_args.hw_update_entries = req_proc->hw_update_entries; + submit_args.num_hw_update_entries = + req_proc->num_hw_update_entries; + rc = cam_lrme_hw_util_submit_req(lrme_core, req_proc); + if (rc) + CAM_ERR(CAM_LRME, "Submit failed"); + lrme_core->req_submit = req_proc; + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + } + + return rc; +} + + +static int cam_lrme_hw_util_process_err(struct cam_hw_info *lrme_hw) +{ + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + struct cam_lrme_frame_request *req_proc, *req_submit; + struct cam_lrme_hw_cb_args cb_args; + int rc; + + req_proc = lrme_core->req_proc; + req_submit = lrme_core->req_submit; + cb_args.cb_type = CAM_LRME_CB_ERROR; + + if ((lrme_core->state != CAM_LRME_CORE_STATE_PROCESSING) && + (lrme_core->state != CAM_LRME_CORE_STATE_REQ_PENDING) && + (lrme_core->state != CAM_LRME_CORE_STATE_REQ_PROC_PEND)) { + CAM_ERR(CAM_LRME, "Get error irq in wrong state %d", + lrme_core->state); + } + + CAM_ERR_RATE_LIMIT(CAM_LRME, "Start recovery"); + lrme_core->state = CAM_LRME_CORE_STATE_RECOVERY; + rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET); + if (rc) + CAM_ERR(CAM_LRME, "Failed to reset"); + + lrme_core->req_proc = NULL; + lrme_core->req_submit = NULL; + if (!rc) + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + + cb_args.frame_req = req_proc; + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core->hw_mgr_cb.data, + &cb_args); + + cb_args.frame_req = req_submit; + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core->hw_mgr_cb.data, + &cb_args); + + return rc; +} + +static int cam_lrme_hw_util_process_reg_update( + struct cam_hw_info *lrme_hw, struct cam_lrme_hw_cb_args *cb_args) +{ + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + int rc = 0; + + cb_args->cb_type |= CAM_LRME_CB_COMP_REG_UPDATE; + if (lrme_core->state == CAM_LRME_CORE_STATE_REQ_PENDING) { + lrme_core->state = CAM_LRME_CORE_STATE_PROCESSING; + } else { + CAM_ERR(CAM_LRME, "Reg update in wrong state %d", + lrme_core->state); + rc = cam_lrme_hw_util_process_err(lrme_hw); + if (rc) + CAM_ERR(CAM_LRME, "Failed to reset"); + return -EINVAL; + } + + lrme_core->req_proc = lrme_core->req_submit; + lrme_core->req_submit = NULL; + + return 0; +} + +static int cam_lrme_hw_util_process_idle( + struct cam_hw_info *lrme_hw, struct cam_lrme_hw_cb_args *cb_args) +{ + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + int rc = 0; + + cb_args->cb_type |= CAM_LRME_CB_BUF_DONE; + switch (lrme_core->state) { + case CAM_LRME_CORE_STATE_REQ_PROC_PEND: + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + break; + + case CAM_LRME_CORE_STATE_PROCESSING: + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + break; + + default: + CAM_ERR(CAM_LRME, "Idle in wrong state %d", + lrme_core->state); + rc = cam_lrme_hw_util_process_err(lrme_hw); + return rc; + } + cb_args->frame_req = lrme_core->req_proc; + lrme_core->req_proc = NULL; + + return 0; +} + +void cam_lrme_set_irq(struct cam_hw_info *lrme_hw, + enum cam_lrme_irq_set set) +{ + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_core *lrme_core = lrme_hw->core_info; + struct cam_lrme_hw_info *hw_info = lrme_core->hw_info; + + switch (set) { + case CAM_LRME_IRQ_ENABLE: + cam_io_w_mb(0xFFFF, + soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_irq_mask); + cam_io_w_mb(0xFFFF, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_mask_0); + cam_io_w_mb(0xFFFF, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_mask_1); + cam_io_w_mb(0xFFFF, + soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.irq_mask); + break; + + case CAM_LRME_IRQ_DISABLE: + cam_io_w_mb(0x0, + soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_irq_mask); + cam_io_w_mb(0x0, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_mask_0); + cam_io_w_mb(0x0, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_mask_1); + cam_io_w_mb(0x0, + soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.irq_mask); + break; + } +} + + +int cam_lrme_hw_process_irq(void *priv, void *data) +{ + struct cam_lrme_hw_work_data *work_data; + struct cam_hw_info *lrme_hw; + struct cam_lrme_core *lrme_core; + int rc = 0; + uint32_t top_irq_status, fe_irq_status; + uint32_t *we_irq_status; + struct cam_lrme_hw_cb_args cb_args; + + if (!data || !priv) { + CAM_ERR(CAM_LRME, "Invalid data %pK %pK", data, priv); + return -EINVAL; + } + + memset(&cb_args, 0, sizeof(struct cam_lrme_hw_cb_args)); + lrme_hw = (struct cam_hw_info *)priv; + work_data = (struct cam_lrme_hw_work_data *)data; + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + top_irq_status = work_data->top_irq_status; + fe_irq_status = work_data->fe_irq_status; + we_irq_status = work_data->we_irq_status; + + CAM_DBG(CAM_LRME, + "top status %x, fe status %x, we status0 %x, we status1 %x", + top_irq_status, fe_irq_status, we_irq_status[0], + we_irq_status[1]); + CAM_DBG(CAM_LRME, "Current state %d", lrme_core->state); + + mutex_lock(&lrme_hw->hw_mutex); + + if (top_irq_status & (1 << 3)) { + CAM_DBG(CAM_LRME, "Error"); + rc = cam_lrme_hw_util_process_err(lrme_hw); + if (rc) + CAM_ERR(CAM_LRME, "Process error failed"); + goto end; + } + + if (we_irq_status[0] & (1 << 1)) { + CAM_DBG(CAM_LRME, "reg update"); + rc = cam_lrme_hw_util_process_reg_update(lrme_hw, &cb_args); + if (rc) { + CAM_ERR(CAM_LRME, "Process reg_update failed"); + goto end; + } + } + + if (top_irq_status & (1 << 4)) { + CAM_DBG(CAM_LRME, "IDLE"); + if (!lrme_core->req_proc) { + CAM_DBG(CAM_LRME, "No frame request to process idle"); + goto end; + } + rc = cam_lrme_hw_util_process_idle(lrme_hw, &cb_args); + if (rc) { + CAM_ERR(CAM_LRME, "Process idle failed"); + goto end; + } + } + + if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) { + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core-> + hw_mgr_cb.data, &cb_args); + } else { + CAM_ERR(CAM_LRME, "No hw mgr cb"); + rc = -EINVAL; + } + +end: + mutex_unlock(&lrme_hw->hw_mutex); + return rc; +} + +int cam_lrme_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv; + int rc = 0; + struct cam_lrme_core *lrme_core; + + if (!lrme_hw) { + CAM_ERR(CAM_LRME, + "Invalid input params, lrme_hw %pK", + lrme_hw); + return -EINVAL; + } + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + + mutex_lock(&lrme_hw->hw_mutex); + + if (lrme_hw->open_count > 0) { + lrme_hw->open_count++; + CAM_DBG(CAM_LRME, "This device is activated before"); + goto unlock; + } + + rc = cam_lrme_soc_enable_resources(lrme_hw); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to enable soc resources"); + goto unlock; + } + + rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to reset hw"); + goto disable_soc; + } + + if (lrme_core->hw_cdm_info) { + struct cam_lrme_cdm_info *hw_cdm_info = + lrme_core->hw_cdm_info; + + rc = cam_cdm_stream_on(hw_cdm_info->cdm_handle); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to stream on cdm"); + goto disable_soc; + } + } + + lrme_hw->hw_state = CAM_HW_STATE_POWER_UP; + lrme_hw->open_count++; + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + + CAM_DBG(CAM_LRME, "open count %d", lrme_hw->open_count); + mutex_unlock(&lrme_hw->hw_mutex); + return rc; + +disable_soc: + if (cam_lrme_soc_disable_resources(lrme_hw)) + CAM_ERR(CAM_LRME, "Error in disable soc resources"); +unlock: + CAM_DBG(CAM_LRME, "open count %d", lrme_hw->open_count); + mutex_unlock(&lrme_hw->hw_mutex); + return rc; +} + +int cam_lrme_hw_stop(void *hw_priv, void *hw_stop_args, uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv; + int rc = 0; + struct cam_lrme_core *lrme_core; + + if (!lrme_hw) { + CAM_ERR(CAM_LRME, "Invalid argument"); + return -EINVAL; + } + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + + mutex_lock(&lrme_hw->hw_mutex); + + if (lrme_hw->open_count == 0) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_ERR(CAM_LRME, "Error Unbalanced stop"); + return -EINVAL; + } + lrme_hw->open_count--; + + CAM_DBG(CAM_LRME, "open count %d", lrme_hw->open_count); + + if (lrme_hw->open_count) + goto unlock; + + lrme_core->req_proc = NULL; + lrme_core->req_submit = NULL; + + if (lrme_core->hw_cdm_info) { + struct cam_lrme_cdm_info *hw_cdm_info = + lrme_core->hw_cdm_info; + + rc = cam_cdm_stream_off(hw_cdm_info->cdm_handle); + if (rc) { + CAM_ERR(CAM_LRME, + "Failed in CDM StreamOff, handle=0x%x, rc=%d", + hw_cdm_info->cdm_handle, rc); + goto unlock; + } + } + + rc = cam_lrme_soc_disable_resources(lrme_hw); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in Disable SOC, rc=%d", rc); + goto unlock; + } + + lrme_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + if (lrme_core->state == CAM_LRME_CORE_STATE_IDLE) { + lrme_core->state = CAM_LRME_CORE_STATE_INIT; + } else { + CAM_ERR(CAM_LRME, "HW in wrong state %d", lrme_core->state); + return -EINVAL; + } + +unlock: + mutex_unlock(&lrme_hw->hw_mutex); + return rc; +} + +int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args, + uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv; + struct cam_lrme_core *lrme_core; + struct cam_lrme_hw_submit_args *args = + (struct cam_lrme_hw_submit_args *)hw_submit_args; + int rc = 0; + struct cam_lrme_frame_request *frame_req; + + + if (!hw_priv || !hw_submit_args) { + CAM_ERR(CAM_LRME, "Invalid input"); + return -EINVAL; + } + + if (sizeof(struct cam_lrme_hw_submit_args) != arg_size) { + CAM_ERR(CAM_LRME, + "size of args %lu, arg_size %d", + sizeof(struct cam_lrme_hw_submit_args), arg_size); + return -EINVAL; + } + + frame_req = args->frame_req; + + mutex_lock(&lrme_hw->hw_mutex); + + if (lrme_hw->open_count == 0) { + CAM_ERR(CAM_LRME, "HW is not open"); + mutex_unlock(&lrme_hw->hw_mutex); + return -EINVAL; + } + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + if (lrme_core->state != CAM_LRME_CORE_STATE_IDLE && + lrme_core->state != CAM_LRME_CORE_STATE_PROCESSING) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "device busy, can not submit, state %d", + lrme_core->state); + return -EBUSY; + } + + if (lrme_core->req_submit != NULL) { + CAM_ERR(CAM_LRME, "req_submit is not NULL"); + return -EBUSY; + } + + rc = cam_lrme_hw_util_submit_req(lrme_core, frame_req); + if (rc) { + CAM_ERR(CAM_LRME, "Submit req failed"); + goto error; + } + + switch (lrme_core->state) { + case CAM_LRME_CORE_STATE_PROCESSING: + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PROC_PEND; + break; + + case CAM_LRME_CORE_STATE_IDLE: + cam_lrme_hw_util_submit_go(lrme_hw); + lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING; + break; + + default: + CAM_ERR(CAM_LRME, "Wrong hw state"); + rc = -EINVAL; + goto error; + } + + lrme_core->req_submit = frame_req; + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "Release lock, submit done for req %llu", + frame_req->req_id); + + return 0; + +error: + mutex_unlock(&lrme_hw->hw_mutex); + + return rc; + +} + +int cam_lrme_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw = hw_priv; + struct cam_lrme_core *lrme_core; + struct cam_lrme_hw_reset_args *lrme_reset_args = reset_core_args; + int rc; + + if (!hw_priv) { + CAM_ERR(CAM_LRME, "Invalid input args"); + return -EINVAL; + } + + if (!reset_core_args || + sizeof(struct cam_lrme_hw_reset_args) != arg_size) { + CAM_ERR(CAM_LRME, "Invalid reset args"); + return -EINVAL; + } + + lrme_core = lrme_hw->core_info; + + mutex_lock(&lrme_hw->hw_mutex); + if (lrme_core->state == CAM_LRME_CORE_STATE_RECOVERY) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_ERR(CAM_LRME, "Reset not allowed in %d state", + lrme_core->state); + return -EINVAL; + } + + lrme_core->state = CAM_LRME_CORE_STATE_RECOVERY; + + rc = cam_lrme_hw_util_reset(lrme_hw, lrme_reset_args->reset_type); + if (rc) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_ERR(CAM_FD, "Failed to reset"); + return rc; + } + + lrme_core->state = CAM_LRME_CORE_STATE_IDLE; + + mutex_unlock(&lrme_hw->hw_mutex); + + return 0; +} + +int cam_lrme_hw_flush(void *hw_priv, void *hw_flush_args, uint32_t arg_size) +{ + struct cam_lrme_core *lrme_core = NULL; + struct cam_hw_info *lrme_hw = hw_priv; + struct cam_lrme_hw_flush_args *flush_args = + (struct cam_lrme_hw_flush_args *)hw_flush_args; + int rc = -ENODEV; + + if (!hw_priv) { + CAM_ERR(CAM_LRME, "Invalid arguments %pK", hw_priv); + return -EINVAL; + } + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + + mutex_lock(&lrme_hw->hw_mutex); + + if (lrme_core->state != CAM_LRME_CORE_STATE_PROCESSING && + lrme_core->state != CAM_LRME_CORE_STATE_REQ_PENDING && + lrme_core->state == CAM_LRME_CORE_STATE_REQ_PROC_PEND) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "Stop not needed in %d state", + lrme_core->state); + return 0; + } + + if (!lrme_core->req_proc && !lrme_core->req_submit) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "no req in device"); + return 0; + } + + switch (flush_args->flush_type) { + case CAM_FLUSH_TYPE_ALL: + if ((!lrme_core->req_submit || + lrme_core->req_submit->ctxt_to_hw_map != + flush_args->ctxt_to_hw_map) && + (!lrme_core->req_proc || + lrme_core->req_proc->ctxt_to_hw_map != + flush_args->ctxt_to_hw_map)) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "hw running on different ctx"); + return 0; + } + rc = cam_lrme_hw_util_flush_ctx(lrme_hw, + flush_args->ctxt_to_hw_map); + if (rc) + CAM_ERR(CAM_LRME, "Flush all failed"); + break; + + case CAM_FLUSH_TYPE_REQ: + if ((!lrme_core->req_submit || + lrme_core->req_submit != flush_args->req_to_flush) && + (!lrme_core->req_proc || + lrme_core->req_proc != flush_args->req_to_flush)) { + mutex_unlock(&lrme_hw->hw_mutex); + CAM_DBG(CAM_LRME, "hw running on different ctx"); + return 0; + } + rc = cam_lrme_hw_util_flush_req(lrme_hw, + flush_args->req_to_flush); + if (rc) + CAM_ERR(CAM_LRME, "Flush req failed"); + break; + + default: + CAM_ERR(CAM_LRME, "Unsupported flush type"); + break; + } + + mutex_unlock(&lrme_hw->hw_mutex); + + return rc; +} + +int cam_lrme_hw_get_caps(void *hw_priv, void *get_hw_cap_args, + uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw; + struct cam_lrme_core *lrme_core; + struct cam_lrme_dev_cap *lrme_hw_caps = + (struct cam_lrme_dev_cap *)get_hw_cap_args; + + if (!hw_priv || !get_hw_cap_args) { + CAM_ERR(CAM_LRME, "Invalid input pointers %pK %pK", + hw_priv, get_hw_cap_args); + return -EINVAL; + } + + lrme_hw = (struct cam_hw_info *)hw_priv; + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + *lrme_hw_caps = lrme_core->hw_caps; + + return 0; +} + +irqreturn_t cam_lrme_hw_irq(int irq_num, void *data) +{ + struct cam_hw_info *lrme_hw; + struct cam_lrme_core *lrme_core; + struct cam_hw_soc_info *soc_info; + struct cam_lrme_hw_info *hw_info; + struct crm_workq_task *task; + struct cam_lrme_hw_work_data *work_data; + uint32_t top_irq_status, fe_irq_status, we_irq_status0, we_irq_status1; + int rc; + + if (!data) { + CAM_ERR(CAM_LRME, "Invalid data in IRQ callback"); + return -EINVAL; + } + + lrme_hw = (struct cam_hw_info *)data; + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + soc_info = &lrme_hw->soc_info; + hw_info = lrme_core->hw_info; + + top_irq_status = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_irq_status); + CAM_DBG(CAM_LRME, "top_irq_status %x", top_irq_status); + cam_io_w_mb(top_irq_status, + soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_irq_clear); + top_irq_status &= CAM_LRME_TOP_IRQ_MASK; + + fe_irq_status = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.irq_status); + CAM_DBG(CAM_LRME, "fe_irq_status %x", fe_irq_status); + cam_io_w_mb(fe_irq_status, + soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.irq_clear); + fe_irq_status &= CAM_LRME_FE_IRQ_MASK; + + we_irq_status0 = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_status_0); + CAM_DBG(CAM_LRME, "we_irq_status[0] %x", we_irq_status0); + cam_io_w_mb(we_irq_status0, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_clear_0); + we_irq_status0 &= CAM_LRME_WE_IRQ_MASK_0; + + we_irq_status1 = cam_io_r_mb( + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_status_1); + CAM_DBG(CAM_LRME, "we_irq_status[1] %x", we_irq_status1); + cam_io_w_mb(we_irq_status1, + soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_clear_1); + we_irq_status1 &= CAM_LRME_WE_IRQ_MASK_1; + + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + hw_info->titan_reg.top_irq_cmd); + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + hw_info->bus_wr_reg.common_reg.irq_cmd); + cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base + + hw_info->bus_rd_reg.common_reg.irq_cmd); + + if (top_irq_status & 0x1) { + complete(&lrme_core->reset_complete); + top_irq_status &= (~0x1); + } + + if (top_irq_status || fe_irq_status || + we_irq_status0 || we_irq_status1) { + task = cam_req_mgr_workq_get_task(lrme_core->work); + if (!task) { + CAM_ERR(CAM_LRME, "no empty task available"); + return -ENOMEM; + } + work_data = (struct cam_lrme_hw_work_data *)task->payload; + work_data->top_irq_status = top_irq_status; + work_data->fe_irq_status = fe_irq_status; + work_data->we_irq_status[0] = we_irq_status0; + work_data->we_irq_status[1] = we_irq_status1; + task->process_cb = cam_lrme_hw_process_irq; + rc = cam_req_mgr_workq_enqueue_task(task, data, + CRM_TASK_PRIORITY_0); + if (rc) + CAM_ERR(CAM_LRME, + "Failed in enqueue work task, rc=%d", rc); + } + + return IRQ_HANDLED; +} + +int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size) +{ + struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv; + int rc = 0; + + switch (cmd_type) { + case CAM_LRME_HW_CMD_PREPARE_HW_UPDATE: { + struct cam_lrme_hw_cmd_config_args *config_args; + + config_args = (struct cam_lrme_hw_cmd_config_args *)cmd_args; + rc = cam_lrme_hw_util_process_config_hw(lrme_hw, config_args); + break; + } + + case CAM_LRME_HW_CMD_REGISTER_CB: { + struct cam_lrme_hw_cmd_set_cb *cb_args; + struct cam_lrme_device *hw_device; + struct cam_lrme_core *lrme_core = + (struct cam_lrme_core *)lrme_hw->core_info; + cb_args = (struct cam_lrme_hw_cmd_set_cb *)cmd_args; + lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb = + cb_args->cam_lrme_hw_mgr_cb; + lrme_core->hw_mgr_cb.data = cb_args->data; + hw_device = cb_args->data; + rc = 0; + break; + } + + case CAM_LRME_HW_CMD_SUBMIT: { + struct cam_lrme_hw_submit_args *submit_args; + + submit_args = (struct cam_lrme_hw_submit_args *)cmd_args; + rc = cam_lrme_hw_submit_req(hw_priv, + submit_args, arg_size); + break; + } + + default: + break; + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h new file mode 100644 index 000000000000..bf2f37084cd1 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h @@ -0,0 +1,457 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_CORE_H_ +#define _CAM_LRME_HW_CORE_H_ + +#include +#include +#include +#include +#include + +#include "cam_common_util.h" +#include "cam_debug_util.h" +#include "cam_io_util.h" +#include "cam_cpas_api.h" +#include "cam_cdm_intf_api.h" +#include "cam_lrme_hw_intf.h" +#include "cam_lrme_hw_soc.h" +#include "cam_req_mgr_workq.h" + +#define CAM_LRME_HW_RESET_TIMEOUT 3000 + +#define CAM_LRME_BUS_RD_MAX_CLIENTS 2 +#define CAM_LRME_BUS_WR_MAX_CLIENTS 2 + +#define CAM_LRME_HW_WORKQ_NUM_TASK 30 + +#define CAM_LRME_TOP_IRQ_MASK 0x19 +#define CAM_LRME_WE_IRQ_MASK_0 0x2 +#define CAM_LRME_WE_IRQ_MASK_1 0x0 +#define CAM_LRME_FE_IRQ_MASK 0x0 + +#define CAM_LRME_MAX_REG_PAIR_NUM 60 + +/** + * enum cam_lrme_irq_set + * + * @CAM_LRME_IRQ_ENABLE : Enable irqs + * @CAM_LRME_IRQ_DISABLE : Disable irqs + */ +enum cam_lrme_irq_set { + CAM_LRME_IRQ_ENABLE, + CAM_LRME_IRQ_DISABLE, +}; + +/** + * struct cam_lrme_cdm_info : information used to submit cdm command + * + * @cdm_handle : CDM handle for this device + * @cdm_ops : CDM ops + * @cdm_cmd : CDM command pointer + */ +struct cam_lrme_cdm_info { + uint32_t cdm_handle; + struct cam_cdm_utils_ops *cdm_ops; + struct cam_cdm_bl_request *cdm_cmd; +}; + +/** + * struct cam_lrme_hw_work_data : Work data for HW work queue + * + * @top_irq_status : Top registers irq status + * @fe_irq_status : FE engine irq status + * @we_irq_status : WE engine irq status + */ +struct cam_lrme_hw_work_data { + uint32_t top_irq_status; + uint32_t fe_irq_status; + uint32_t we_irq_status[2]; +}; + +/** + * enum cam_lrme_core_state : LRME core states + * + * @CAM_LRME_CORE_STATE_UNINIT : LRME is in uninit state + * @CAM_LRME_CORE_STATE_INIT : LRME is in init state after probe + * @ CAM_LRME_CORE_STATE_IDLE : LRME is in idle state. Hardware is in + * this state when no frame is processing + * or waiting for this core. + * @CAM_LRME_CORE_STATE_REQ_PENDING : LRME is in pending state. One frame is + * waiting for processing + * @CAM_LRME_CORE_STATE_PROCESSING : LRME is in processing state. HW manager + * can submit one more frame to HW + * @CAM_LRME_CORE_STATE_REQ_PROC_PEND : Indicate two frames are inside HW. + * @CAM_LRME_CORE_STATE_RECOVERY : Indicate core is in the process of reset + * @CAM_LRME_CORE_STATE_MAX : upper limit of states + */ +enum cam_lrme_core_state { + CAM_LRME_CORE_STATE_UNINIT, + CAM_LRME_CORE_STATE_INIT, + CAM_LRME_CORE_STATE_IDLE, + CAM_LRME_CORE_STATE_REQ_PENDING, + CAM_LRME_CORE_STATE_PROCESSING, + CAM_LRME_CORE_STATE_REQ_PROC_PEND, + CAM_LRME_CORE_STATE_RECOVERY, + CAM_LRME_CORE_STATE_MAX, +}; + +/** + * struct cam_lrme_core : LRME HW core information + * + * @hw_info : Pointer to base HW information structure + * @device_iommu : Device iommu handle + * @cdm_iommu : CDM iommu handle + * @hw_caps : Hardware capabilities + * @state : Hardware state + * @reset_complete : Reset completion + * @work : Hardware workqueue to handle irq events + * @work_data : Work data used by hardware workqueue + * @hw_mgr_cb : Hw manager callback + * @req_proc : Pointer to the processing frame request + * @req_submit : Pointer to the frame request waiting for processing + * @hw_cdm_info : CDM information used by this device + * @hw_idx : Hardware index + */ +struct cam_lrme_core { + struct cam_lrme_hw_info *hw_info; + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + struct cam_lrme_dev_cap hw_caps; + enum cam_lrme_core_state state; + struct completion reset_complete; + struct cam_req_mgr_core_workq *work; + struct cam_lrme_hw_work_data work_data[CAM_LRME_HW_WORKQ_NUM_TASK]; + struct cam_lrme_hw_cmd_set_cb hw_mgr_cb; + struct cam_lrme_frame_request *req_proc; + struct cam_lrme_frame_request *req_submit; + struct cam_lrme_cdm_info *hw_cdm_info; + uint32_t hw_idx; +}; + +/** + * struct cam_lrme_bus_rd_reg_common : Offsets of FE common registers + * + * @hw_version : Offset of hw_version register + * @hw_capability : Offset of hw_capability register + * @sw_reset : Offset of sw_reset register + * @cgc_override : Offset of cgc_override register + * @irq_mask : Offset of irq_mask register + * @irq_clear : Offset of irq_clear register + * @irq_cmd : Offset of irq_cmd register + * @irq_status : Offset of irq_status register + * @cmd : Offset of cmd register + * @irq_set : Offset of irq_set register + * @misr_reset : Offset of misr_reset register + * @security_cfg : Offset of security_cfg register + * @pwr_iso_cfg : Offset of pwr_iso_cfg register + * @pwr_iso_seed : Offset of pwr_iso_seed register + * @test_bus_ctrl : Offset of test_bus_ctrl register + * @spare : Offset of spare register + */ +struct cam_lrme_bus_rd_reg_common { + uint32_t hw_version; + uint32_t hw_capability; + uint32_t sw_reset; + uint32_t cgc_override; + uint32_t irq_mask; + uint32_t irq_clear; + uint32_t irq_cmd; + uint32_t irq_status; + uint32_t cmd; + uint32_t irq_set; + uint32_t misr_reset; + uint32_t security_cfg; + uint32_t pwr_iso_cfg; + uint32_t pwr_iso_seed; + uint32_t test_bus_ctrl; + uint32_t spare; +}; + +/** + * struct cam_lrme_bus_wr_reg_common : Offset of WE common registers + * @hw_version : Offset of hw_version register + * @hw_capability : Offset of hw_capability register + * @sw_reset : Offset of sw_reset register + * @cgc_override : Offset of cgc_override register + * @misr_reset : Offset of misr_reset register + * @pwr_iso_cfg : Offset of pwr_iso_cfg register + * @test_bus_ctrl : Offset of test_bus_ctrl register + * @composite_mask_0 : Offset of composite_mask_0 register + * @irq_mask_0 : Offset of irq_mask_0 register + * @irq_mask_1 : Offset of irq_mask_1 register + * @irq_clear_0 : Offset of irq_clear_0 register + * @irq_clear_1 : Offset of irq_clear_1 register + * @irq_status_0 : Offset of irq_status_0 register + * @irq_status_1 : Offset of irq_status_1 register + * @irq_cmd : Offset of irq_cmd register + * @irq_set_0 : Offset of irq_set_0 register + * @irq_set_1 : Offset of irq_set_1 register + * @addr_fifo_status : Offset of addr_fifo_status register + * @frame_header_cfg0 : Offset of frame_header_cfg0 register + * @frame_header_cfg1 : Offset of frame_header_cfg1 register + * @spare : Offset of spare register + */ +struct cam_lrme_bus_wr_reg_common { + uint32_t hw_version; + uint32_t hw_capability; + uint32_t sw_reset; + uint32_t cgc_override; + uint32_t misr_reset; + uint32_t pwr_iso_cfg; + uint32_t test_bus_ctrl; + uint32_t composite_mask_0; + uint32_t irq_mask_0; + uint32_t irq_mask_1; + uint32_t irq_clear_0; + uint32_t irq_clear_1; + uint32_t irq_status_0; + uint32_t irq_status_1; + uint32_t irq_cmd; + uint32_t irq_set_0; + uint32_t irq_set_1; + uint32_t addr_fifo_status; + uint32_t frame_header_cfg0; + uint32_t frame_header_cfg1; + uint32_t spare; +}; + +/** + * struct cam_lrme_bus_rd_bus_client : Offset of FE registers + * + * @core_cfg : Offset of core_cfg register + * @ccif_meta_data : Offset of ccif_meta_data register + * @addr_image : Offset of addr_image register + * @rd_buffer_size : Offset of rd_buffer_size register + * @rd_stride : Offset of rd_stride register + * @unpack_cfg_0 : Offset of unpack_cfg_0 register + * @latency_buff_allocation : Offset of latency_buff_allocation register + * @burst_limit_cfg : Offset of burst_limit_cfg register + * @misr_cfg_0 : Offset of misr_cfg_0 register + * @misr_cfg_1 : Offset of misr_cfg_1 register + * @misr_rd_val : Offset of misr_rd_val register + * @debug_status_cfg : Offset of debug_status_cfg register + * @debug_status_0 : Offset of debug_status_0 register + * @debug_status_1 : Offset of debug_status_1 register + */ +struct cam_lrme_bus_rd_bus_client { + uint32_t core_cfg; + uint32_t ccif_meta_data; + uint32_t addr_image; + uint32_t rd_buffer_size; + uint32_t rd_stride; + uint32_t unpack_cfg_0; + uint32_t latency_buff_allocation; + uint32_t burst_limit_cfg; + uint32_t misr_cfg_0; + uint32_t misr_cfg_1; + uint32_t misr_rd_val; + uint32_t debug_status_cfg; + uint32_t debug_status_0; + uint32_t debug_status_1; +}; + +/** + * struct cam_lrme_bus_wr_bus_client : Offset of WE registers + * + * @status_0 : Offset of status_0 register + * @status_1 : Offset of status_1 register + * @cfg : Offset of cfg register + * @addr_frame_header : Offset of addr_frame_header register + * @frame_header_cfg : Offset of frame_header_cfg register + * @addr_image : Offset of addr_image register + * @addr_image_offset : Offset of addr_image_offset register + * @buffer_width_cfg : Offset of buffer_width_cfg register + * @buffer_height_cfg : Offset of buffer_height_cfg register + * @packer_cfg : Offset of packer_cfg register + * @wr_stride : Offset of wr_stride register + * @irq_subsample_cfg_period : Offset of irq_subsample_cfg_period register + * @irq_subsample_cfg_pattern : Offset of irq_subsample_cfg_pattern register + * @burst_limit_cfg : Offset of burst_limit_cfg register + * @misr_cfg : Offset of misr_cfg register + * @misr_rd_word_sel : Offset of misr_rd_word_sel register + * @misr_val : Offset of misr_val register + * @debug_status_cfg : Offset of debug_status_cfg register + * @debug_status_0 : Offset of debug_status_0 register + * @debug_status_1 : Offset of debug_status_1 register + */ +struct cam_lrme_bus_wr_bus_client { + uint32_t status_0; + uint32_t status_1; + uint32_t cfg; + uint32_t addr_frame_header; + uint32_t frame_header_cfg; + uint32_t addr_image; + uint32_t addr_image_offset; + uint32_t buffer_width_cfg; + uint32_t buffer_height_cfg; + uint32_t packer_cfg; + uint32_t wr_stride; + uint32_t irq_subsample_cfg_period; + uint32_t irq_subsample_cfg_pattern; + uint32_t burst_limit_cfg; + uint32_t misr_cfg; + uint32_t misr_rd_word_sel; + uint32_t misr_val; + uint32_t debug_status_cfg; + uint32_t debug_status_0; + uint32_t debug_status_1; +}; + +/** + * struct cam_lrme_bus_rd_hw_info : FE registers information + * + * @common_reg : FE common register + * @bus_client_reg : List of FE bus registers information + */ +struct cam_lrme_bus_rd_hw_info { + struct cam_lrme_bus_rd_reg_common common_reg; + struct cam_lrme_bus_rd_bus_client + bus_client_reg[CAM_LRME_BUS_RD_MAX_CLIENTS]; +}; + +/** + * struct cam_lrme_bus_wr_hw_info : WE engine registers information + * + * @common_reg : WE common register + * @bus_client_reg : List of WE bus registers information + */ +struct cam_lrme_bus_wr_hw_info { + struct cam_lrme_bus_wr_reg_common common_reg; + struct cam_lrme_bus_wr_bus_client + bus_client_reg[CAM_LRME_BUS_WR_MAX_CLIENTS]; +}; + +/** + * struct cam_lrme_clc_reg : Offset of clc registers + * + * @clc_hw_version : Offset of clc_hw_version register + * @clc_hw_status : Offset of clc_hw_status register + * @clc_hw_status_dbg : Offset of clc_hw_status_dbg register + * @clc_module_cfg : Offset of clc_module_cfg register + * @clc_moduleformat : Offset of clc_moduleformat register + * @clc_rangestep : Offset of clc_rangestep register + * @clc_offset : Offset of clc_offset register + * @clc_maxallowedsad : Offset of clc_maxallowedsad register + * @clc_minallowedtarmad : Offset of clc_minallowedtarmad register + * @clc_meaningfulsaddiff : Offset of clc_meaningfulsaddiff register + * @clc_minsaddiffdenom : Offset of clc_minsaddiffdenom register + * @clc_robustnessmeasuredistmap_0 : Offset of measuredistmap_0 register + * @clc_robustnessmeasuredistmap_1 : Offset of measuredistmap_1 register + * @clc_robustnessmeasuredistmap_2 : Offset of measuredistmap_2 register + * @clc_robustnessmeasuredistmap_3 : Offset of measuredistmap_3 register + * @clc_robustnessmeasuredistmap_4 : Offset of measuredistmap_4 register + * @clc_robustnessmeasuredistmap_5 : Offset of measuredistmap_5 register + * @clc_robustnessmeasuredistmap_6 : Offset of measuredistmap_6 register + * @clc_robustnessmeasuredistmap_7 : Offset of measuredistmap_7 register + * @clc_ds_crop_horizontal : Offset of clc_ds_crop_horizontal register + * @clc_ds_crop_vertical : Offset of clc_ds_crop_vertical register + * @clc_tar_pd_unpacker : Offset of clc_tar_pd_unpacker register + * @clc_ref_pd_unpacker : Offset of clc_ref_pd_unpacker register + * @clc_sw_override : Offset of clc_sw_override register + * @clc_tar_height : Offset of clc_tar_height register + * @clc_test_bus_ctrl : Offset of clc_test_bus_ctrl register + * @clc_spare : Offset of clc_spare register + */ +struct cam_lrme_clc_reg { + uint32_t clc_hw_version; + uint32_t clc_hw_status; + uint32_t clc_hw_status_dbg; + uint32_t clc_module_cfg; + uint32_t clc_moduleformat; + uint32_t clc_rangestep; + uint32_t clc_offset; + uint32_t clc_maxallowedsad; + uint32_t clc_minallowedtarmad; + uint32_t clc_meaningfulsaddiff; + uint32_t clc_minsaddiffdenom; + uint32_t clc_robustnessmeasuredistmap_0; + uint32_t clc_robustnessmeasuredistmap_1; + uint32_t clc_robustnessmeasuredistmap_2; + uint32_t clc_robustnessmeasuredistmap_3; + uint32_t clc_robustnessmeasuredistmap_4; + uint32_t clc_robustnessmeasuredistmap_5; + uint32_t clc_robustnessmeasuredistmap_6; + uint32_t clc_robustnessmeasuredistmap_7; + uint32_t clc_ds_crop_horizontal; + uint32_t clc_ds_crop_vertical; + uint32_t clc_tar_pd_unpacker; + uint32_t clc_ref_pd_unpacker; + uint32_t clc_sw_override; + uint32_t clc_tar_height; + uint32_t clc_ref_height; + uint32_t clc_test_bus_ctrl; + uint32_t clc_spare; +}; + +/** + * struct cam_lrme_titan_reg : Offset of LRME top registers + * + * @top_hw_version : Offset of top_hw_version register + * @top_titan_version : Offset of top_titan_version register + * @top_rst_cmd : Offset of top_rst_cmd register + * @top_core_clk_cfg : Offset of top_core_clk_cfg register + * @top_irq_status : Offset of top_irq_status register + * @top_irq_mask : Offset of top_irq_mask register + * @top_irq_clear : Offset of top_irq_clear register + * @top_irq_set : Offset of top_irq_set register + * @top_irq_cmd : Offset of top_irq_cmd register + * @top_violation_status : Offset of top_violation_status register + * @top_spare : Offset of top_spare register + */ +struct cam_lrme_titan_reg { + uint32_t top_hw_version; + uint32_t top_titan_version; + uint32_t top_rst_cmd; + uint32_t top_core_clk_cfg; + uint32_t top_irq_status; + uint32_t top_irq_mask; + uint32_t top_irq_clear; + uint32_t top_irq_set; + uint32_t top_irq_cmd; + uint32_t top_violation_status; + uint32_t top_spare; +}; + +/** + * struct cam_lrme_hw_info : LRME registers information + * + * @clc_reg : LRME CLC registers + * @bus_rd_reg : LRME FE registers + * @bus_wr_reg : LRME WE registers + * @titan_reg : LRME top reisters + */ +struct cam_lrme_hw_info { + struct cam_lrme_clc_reg clc_reg; + struct cam_lrme_bus_rd_hw_info bus_rd_reg; + struct cam_lrme_bus_wr_hw_info bus_wr_reg; + struct cam_lrme_titan_reg titan_reg; +}; + +int cam_lrme_hw_process_irq(void *priv, void *data); +int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args, + uint32_t arg_size); +int cam_lrme_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size); +int cam_lrme_hw_stop(void *hw_priv, void *stop_args, uint32_t arg_size); +int cam_lrme_hw_get_caps(void *hw_priv, void *get_hw_cap_args, + uint32_t arg_size); +irqreturn_t cam_lrme_hw_irq(int irq_num, void *data); +int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type, + void *cmd_args, uint32_t arg_size); +int cam_lrme_hw_util_get_caps(struct cam_hw_info *lrme_hw, + struct cam_lrme_dev_cap *hw_caps); +int cam_lrme_hw_start(void *hw_priv, void *hw_init_args, uint32_t arg_size); +int cam_lrme_hw_flush(void *hw_priv, void *hw_flush_args, uint32_t arg_size); +void cam_lrme_set_irq(struct cam_hw_info *lrme_hw, enum cam_lrme_irq_set set); + +#endif /* _CAM_LRME_HW_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c new file mode 100644 index 000000000000..6825ef6bedf3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c @@ -0,0 +1,312 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include + +#include "cam_subdev.h" +#include "cam_lrme_hw_intf.h" +#include "cam_lrme_hw_core.h" +#include "cam_lrme_hw_soc.h" +#include "cam_lrme_hw_reg.h" +#include "cam_req_mgr_workq.h" +#include "cam_lrme_hw_mgr.h" +#include "cam_mem_mgr_api.h" +#include "cam_smmu_api.h" + +#define CAM_LRME_HW_WORKQ_NUM_TASK 30 + +static int cam_lrme_hw_dev_util_cdm_acquire(struct cam_lrme_core *lrme_core, + struct cam_hw_info *lrme_hw) +{ + int rc, i; + struct cam_cdm_bl_request *cdm_cmd; + struct cam_cdm_acquire_data cdm_acquire; + struct cam_lrme_cdm_info *hw_cdm_info; + + hw_cdm_info = kzalloc(sizeof(struct cam_lrme_cdm_info), + GFP_KERNEL); + if (!hw_cdm_info) { + CAM_ERR(CAM_LRME, "No memory for hw_cdm_info"); + return -ENOMEM; + } + + cdm_cmd = kzalloc((sizeof(struct cam_cdm_bl_request) + + ((CAM_LRME_MAX_HW_ENTRIES - 1) * + sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL); + if (!cdm_cmd) { + CAM_ERR(CAM_LRME, "No memory for cdm_cmd"); + kfree(hw_cdm_info); + return -ENOMEM; + } + + memset(&cdm_acquire, 0, sizeof(cdm_acquire)); + strlcpy(cdm_acquire.identifier, "lrmecdm", sizeof("lrmecdm")); + cdm_acquire.cell_index = lrme_hw->soc_info.index; + cdm_acquire.handle = 0; + cdm_acquire.userdata = hw_cdm_info; + cdm_acquire.cam_cdm_callback = NULL; + cdm_acquire.id = CAM_CDM_VIRTUAL; + cdm_acquire.base_array_cnt = lrme_hw->soc_info.num_reg_map; + for (i = 0; i < lrme_hw->soc_info.num_reg_map; i++) + cdm_acquire.base_array[i] = &lrme_hw->soc_info.reg_map[i]; + + rc = cam_cdm_acquire(&cdm_acquire); + if (rc) { + CAM_ERR(CAM_LRME, "Can't acquire cdm"); + goto error; + } + + hw_cdm_info->cdm_cmd = cdm_cmd; + hw_cdm_info->cdm_ops = cdm_acquire.ops; + hw_cdm_info->cdm_handle = cdm_acquire.handle; + + lrme_core->hw_cdm_info = hw_cdm_info; + CAM_DBG(CAM_LRME, "cdm acquire done"); + + return 0; +error: + kfree(cdm_cmd); + kfree(hw_cdm_info); + return rc; +} + +static int cam_lrme_hw_dev_probe(struct platform_device *pdev) +{ + struct cam_hw_info *lrme_hw; + struct cam_hw_intf lrme_hw_intf; + struct cam_lrme_core *lrme_core; + const struct of_device_id *match_dev = NULL; + struct cam_lrme_hw_info *hw_info; + int rc, i; + + lrme_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); + if (!lrme_hw) { + CAM_ERR(CAM_LRME, "No memory to create lrme_hw"); + return -ENOMEM; + } + + lrme_core = kzalloc(sizeof(struct cam_lrme_core), GFP_KERNEL); + if (!lrme_core) { + CAM_ERR(CAM_LRME, "No memory to create lrme_core"); + kfree(lrme_hw); + return -ENOMEM; + } + + lrme_hw->core_info = lrme_core; + lrme_hw->hw_state = CAM_HW_STATE_POWER_DOWN; + lrme_hw->soc_info.pdev = pdev; + lrme_hw->soc_info.dev = &pdev->dev; + lrme_hw->soc_info.dev_name = pdev->name; + lrme_hw->open_count = 0; + lrme_core->state = CAM_LRME_CORE_STATE_INIT; + + mutex_init(&lrme_hw->hw_mutex); + spin_lock_init(&lrme_hw->hw_lock); + init_completion(&lrme_hw->hw_complete); + init_completion(&lrme_core->reset_complete); + + rc = cam_req_mgr_workq_create("cam_lrme_hw_worker", + CAM_LRME_HW_WORKQ_NUM_TASK, + &lrme_core->work, CRM_WORKQ_USAGE_IRQ); + if (rc) { + CAM_ERR(CAM_LRME, "Unable to create a workq, rc=%d", rc); + goto free_memory; + } + + for (i = 0; i < CAM_LRME_HW_WORKQ_NUM_TASK; i++) + lrme_core->work->task.pool[i].payload = + &lrme_core->work_data[i]; + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev || !match_dev->data) { + CAM_ERR(CAM_LRME, "No Of_match data, %pK", match_dev); + rc = -EINVAL; + goto destroy_workqueue; + } + hw_info = (struct cam_lrme_hw_info *)match_dev->data; + lrme_core->hw_info = hw_info; + + rc = cam_lrme_soc_init_resources(&lrme_hw->soc_info, + cam_lrme_hw_irq, lrme_hw); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to init soc, rc=%d", rc); + goto destroy_workqueue; + } + + rc = cam_lrme_hw_dev_util_cdm_acquire(lrme_core, lrme_hw); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to acquire cdm"); + goto deinit_platform_res; + } + + rc = cam_smmu_get_handle("lrme", &lrme_core->device_iommu.non_secure); + if (rc) { + CAM_ERR(CAM_LRME, "Get iommu handle failed"); + goto release_cdm; + } + + rc = cam_lrme_hw_start(lrme_hw, NULL, 0); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to hw init, rc=%d", rc); + goto detach_smmu; + } + + rc = cam_lrme_hw_util_get_caps(lrme_hw, &lrme_core->hw_caps); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to get hw caps, rc=%d", rc); + if (cam_lrme_hw_stop(lrme_hw, NULL, 0)) + CAM_ERR(CAM_LRME, "Failed in hw deinit"); + goto detach_smmu; + } + + rc = cam_lrme_hw_stop(lrme_hw, NULL, 0); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to deinit hw, rc=%d", rc); + goto detach_smmu; + } + + lrme_core->hw_idx = lrme_hw->soc_info.index; + lrme_hw_intf.hw_priv = lrme_hw; + lrme_hw_intf.hw_idx = lrme_hw->soc_info.index; + lrme_hw_intf.hw_ops.get_hw_caps = cam_lrme_hw_get_caps; + lrme_hw_intf.hw_ops.init = NULL; + lrme_hw_intf.hw_ops.deinit = NULL; + lrme_hw_intf.hw_ops.reset = cam_lrme_hw_reset; + lrme_hw_intf.hw_ops.reserve = NULL; + lrme_hw_intf.hw_ops.release = NULL; + lrme_hw_intf.hw_ops.start = cam_lrme_hw_start; + lrme_hw_intf.hw_ops.stop = cam_lrme_hw_stop; + lrme_hw_intf.hw_ops.read = NULL; + lrme_hw_intf.hw_ops.write = NULL; + lrme_hw_intf.hw_ops.process_cmd = cam_lrme_hw_process_cmd; + lrme_hw_intf.hw_ops.flush = cam_lrme_hw_flush; + lrme_hw_intf.hw_type = CAM_HW_LRME; + + rc = cam_cdm_get_iommu_handle("lrmecdm", &lrme_core->cdm_iommu); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to acquire the CDM iommu handles"); + goto detach_smmu; + } + + rc = cam_lrme_mgr_register_device(&lrme_hw_intf, + &lrme_core->device_iommu, + &lrme_core->cdm_iommu); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to register device"); + goto detach_smmu; + } + + platform_set_drvdata(pdev, lrme_hw); + CAM_DBG(CAM_LRME, "LRME-%d probe successful", lrme_hw_intf.hw_idx); + + return rc; + +detach_smmu: + cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure); +release_cdm: + cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle); + kfree(lrme_core->hw_cdm_info->cdm_cmd); + kfree(lrme_core->hw_cdm_info); +deinit_platform_res: + if (cam_lrme_soc_deinit_resources(&lrme_hw->soc_info)) + CAM_ERR(CAM_LRME, "Failed in soc deinit"); + mutex_destroy(&lrme_hw->hw_mutex); +destroy_workqueue: + cam_req_mgr_workq_destroy(&lrme_core->work); +free_memory: + mutex_destroy(&lrme_hw->hw_mutex); + kfree(lrme_hw); + kfree(lrme_core); + + return rc; +} + +static int cam_lrme_hw_dev_remove(struct platform_device *pdev) +{ + int rc = 0; + struct cam_hw_info *lrme_hw; + struct cam_lrme_core *lrme_core; + + lrme_hw = platform_get_drvdata(pdev); + if (!lrme_hw) { + CAM_ERR(CAM_LRME, "Invalid lrme_hw from fd_hw_intf"); + rc = -ENODEV; + goto deinit_platform_res; + } + + lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; + if (!lrme_core) { + CAM_ERR(CAM_LRME, "Invalid lrme_core from fd_hw"); + rc = -EINVAL; + goto deinit_platform_res; + } + + cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure); + cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle); + cam_lrme_mgr_deregister_device(lrme_core->hw_idx); + + kfree(lrme_core->hw_cdm_info->cdm_cmd); + kfree(lrme_core->hw_cdm_info); + kfree(lrme_core); + +deinit_platform_res: + rc = cam_lrme_soc_deinit_resources(&lrme_hw->soc_info); + if (rc) + CAM_ERR(CAM_LRME, "Error in LRME soc deinit, rc=%d", rc); + + mutex_destroy(&lrme_hw->hw_mutex); + kfree(lrme_hw); + + return rc; +} + +static const struct of_device_id cam_lrme_hw_dt_match[] = { + { + .compatible = "qcom,lrme", + .data = &cam_lrme10_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_lrme_hw_dt_match); + +static struct platform_driver cam_lrme_hw_driver = { + .probe = cam_lrme_hw_dev_probe, + .remove = cam_lrme_hw_dev_remove, + .driver = { + .name = "cam_lrme_hw", + .owner = THIS_MODULE, + .of_match_table = cam_lrme_hw_dt_match, + }, +}; + +static int __init cam_lrme_hw_init_module(void) +{ + return platform_driver_register(&cam_lrme_hw_driver); +} + +static void __exit cam_lrme_hw_exit_module(void) +{ + platform_driver_unregister(&cam_lrme_hw_driver); +} + +module_init(cam_lrme_hw_init_module); +module_exit(cam_lrme_hw_exit_module); +MODULE_DESCRIPTION("CAM LRME HW driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h new file mode 100644 index 000000000000..d16b174767cc --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h @@ -0,0 +1,200 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_INTF_H_ +#define _CAM_LRME_HW_INTF_H_ + +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" +#include "cam_soc_util.h" +#include "cam_hw.h" +#include "cam_hw_intf.h" +#include "cam_subdev.h" +#include "cam_cpas_api.h" +#include "cam_hw_mgr_intf.h" +#include "cam_debug_util.h" + + +#define CAM_LRME_MAX_IO_BUFFER 2 +#define CAM_LRME_MAX_HW_ENTRIES 5 + +#define CAM_LRME_BASE_IDX 0 + +/** + * enum cam_lrme_hw_type : Enum for LRME HW type + * + * @CAM_HW_LRME : LRME HW type + */ +enum cam_lrme_hw_type { + CAM_HW_LRME, +}; + +/** + * enum cam_lrme_cb_type : HW manager call back type + * + * @CAM_LRME_CB_BUF_DONE : Indicate buf done has been generated + * @CAM_LRME_CB_COMP_REG_UPDATE : Indicate receiving WE comp reg update + * @CAM_LRME_CB_PUT_FRAME : Request HW manager to put back the frame + * @CAM_LRME_CB_ERROR : Indicate error irq has been generated + */ +enum cam_lrme_cb_type { + CAM_LRME_CB_BUF_DONE = 1, + CAM_LRME_CB_COMP_REG_UPDATE = 1 << 1, + CAM_LRME_CB_PUT_FRAME = 1 << 2, + CAM_LRME_CB_ERROR = 1 << 3, +}; + +/** + * enum cam_lrme_hw_cmd_type : HW CMD type + * + * @CAM_LRME_HW_CMD_prepare_hw_update : Prepare HW update + * @CAM_LRME_HW_CMD_REGISTER_CB : register HW manager callback + * @CAM_LRME_HW_CMD_SUBMIT : Submit frame to HW + */ +enum cam_lrme_hw_cmd_type { + CAM_LRME_HW_CMD_PREPARE_HW_UPDATE, + CAM_LRME_HW_CMD_REGISTER_CB, + CAM_LRME_HW_CMD_SUBMIT, +}; + +/** + * enum cam_lrme_hw_reset_type : Type of reset + * + * @CAM_LRME_HW_RESET_TYPE_HW_RESET : HW reset + * @CAM_LRME_HW_RESET_TYPE_SW_RESET : SW reset + */ +enum cam_lrme_hw_reset_type { + CAM_LRME_HW_RESET_TYPE_HW_RESET, + CAM_LRME_HW_RESET_TYPE_SW_RESET, +}; + +/** + *struct cam_lrme_frame_request : LRME frame request + * + * @frame_list : List head + * @req_id : Request ID + * @ctxt_to_hw_map : Information about context id, priority and device id + * @hw_device : Pointer to HW device + * @hw_update_entries : List of hw_update_entries + * @num_hw_update_entries : number of hw_update_entries + */ +struct cam_lrme_frame_request { + struct list_head frame_list; + uint64_t req_id; + void *ctxt_to_hw_map; + struct cam_lrme_device *hw_device; + struct cam_hw_update_entry hw_update_entries[CAM_LRME_MAX_HW_ENTRIES]; + uint32_t num_hw_update_entries; +}; + +/** + * struct cam_lrme_hw_io_buffer : IO buffer information + * + * @valid : Indicate whether this IO config is valid + * @io_cfg : Pointer to IO configuration + * @num_buf : Number of buffers + * @num_plane : Number of planes + * @io_addr : List of IO address + */ +struct cam_lrme_hw_io_buffer { + bool valid; + struct cam_buf_io_cfg *io_cfg; + uint32_t num_buf; + uint32_t num_plane; + uint64_t io_addr[CAM_PACKET_MAX_PLANES]; +}; + +/** + * struct cam_lrme_hw_cmd_config_args : Args for prepare HW update + * + * @hw_device : Pointer to HW device + * @input_buf : List of input buffers + * @output_buf : List of output buffers + * @cmd_buf_addr : Pointer to available KMD buffer + * @size : Available KMD buffer size + * @config_buf_size : Size used to prepare update + */ +struct cam_lrme_hw_cmd_config_args { + struct cam_lrme_device *hw_device; + struct cam_lrme_hw_io_buffer input_buf[CAM_LRME_MAX_IO_BUFFER]; + struct cam_lrme_hw_io_buffer output_buf[CAM_LRME_MAX_IO_BUFFER]; + uint32_t *cmd_buf_addr; + uint32_t size; + uint32_t config_buf_size; +}; + +/** + * struct cam_lrme_hw_flush_args : Args for flush HW + * + * @ctxt_to_hw_map : Identity of context + * @req_to_flush : Pointer to the frame need to flush in + * case of single frame flush + * @flush_type : Flush type + */ +struct cam_lrme_hw_flush_args { + void *ctxt_to_hw_map; + struct cam_lrme_frame_request *req_to_flush; + uint32_t flush_type; +}; + +/** + * struct cam_lrme_hw_reset_args : Args for reset HW + * + * @reset_type : Enum cam_lrme_hw_reset_type + */ +struct cam_lrme_hw_reset_args { + uint32_t reset_type; +}; + +/** + * struct cam_lrme_hw_cb_args : HW manager callback args + * + * @cb_type : Callback event type + * @frame_req : Pointer to the frame associated with the cb + */ +struct cam_lrme_hw_cb_args { + uint32_t cb_type; + struct cam_lrme_frame_request *frame_req; +}; + +/** + * struct cam_lrme_hw_cmd_set_cb : Args for set callback function + * + * @cam_lrme_hw_mgr_cb : Callback function pointer + * @data : Data sent along with callback function + */ +struct cam_lrme_hw_cmd_set_cb { + int (*cam_lrme_hw_mgr_cb)(void *data, + struct cam_lrme_hw_cb_args *args); + void *data; +}; + +/** + * struct cam_lrme_hw_submit_args : Args for submit request + * + * @hw_update_entries : List of hw update entries used to program registers + * @num_hw_update_entries : Number of hw update entries + * @frame_req : Pointer to the frame request + */ +struct cam_lrme_hw_submit_args { + struct cam_hw_update_entry *hw_update_entries; + uint32_t num_hw_update_entries; + struct cam_lrme_frame_request *frame_req; +}; + +#endif /* _CAM_LRME_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h new file mode 100644 index 000000000000..39cfde724953 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h @@ -0,0 +1,193 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_REG_H_ +#define _CAM_LRME_HW_REG_H_ + +#include "cam_lrme_hw_core.h" + +static struct cam_lrme_hw_info cam_lrme10_hw_info = { + .clc_reg = { + .clc_hw_version = 0x00000000, + .clc_hw_status = 0x00000004, + .clc_hw_status_dbg = 0x00000008, + .clc_module_cfg = 0x00000060, + .clc_moduleformat = 0x000000A8, + .clc_rangestep = 0x00000068, + .clc_offset = 0x0000006C, + .clc_maxallowedsad = 0x00000070, + .clc_minallowedtarmad = 0x00000074, + .clc_meaningfulsaddiff = 0x00000078, + .clc_minsaddiffdenom = 0x0000007C, + .clc_robustnessmeasuredistmap_0 = 0x00000080, + .clc_robustnessmeasuredistmap_1 = 0x00000084, + .clc_robustnessmeasuredistmap_2 = 0x00000088, + .clc_robustnessmeasuredistmap_3 = 0x0000008C, + .clc_robustnessmeasuredistmap_4 = 0x00000090, + .clc_robustnessmeasuredistmap_5 = 0x00000094, + .clc_robustnessmeasuredistmap_6 = 0x00000098, + .clc_robustnessmeasuredistmap_7 = 0x0000009C, + .clc_ds_crop_horizontal = 0x000000A0, + .clc_ds_crop_vertical = 0x000000A4, + .clc_tar_pd_unpacker = 0x000000AC, + .clc_ref_pd_unpacker = 0x000000B0, + .clc_sw_override = 0x000000B4, + .clc_tar_height = 0x000000B8, + .clc_ref_height = 0x000000BC, + .clc_test_bus_ctrl = 0x000001F8, + .clc_spare = 0x000001FC, + }, + .bus_rd_reg = { + .common_reg = { + .hw_version = 0x00000200, + .hw_capability = 0x00000204, + .sw_reset = 0x00000208, + .cgc_override = 0x0000020C, + .irq_mask = 0x00000210, + .irq_clear = 0x00000214, + .irq_cmd = 0x00000218, + .irq_status = 0x0000021C, + .cmd = 0x00000220, + .irq_set = 0x00000224, + .misr_reset = 0x0000022C, + .security_cfg = 0x00000230, + .pwr_iso_cfg = 0x00000234, + .pwr_iso_seed = 0x00000238, + .test_bus_ctrl = 0x00000248, + .spare = 0x0000024C, + }, + .bus_client_reg = { + /* bus client 0 */ + { + .core_cfg = 0x00000250, + .ccif_meta_data = 0x00000254, + .addr_image = 0x00000258, + .rd_buffer_size = 0x0000025C, + .rd_stride = 0x00000260, + .unpack_cfg_0 = 0x00000264, + .latency_buff_allocation = 0x00000278, + .burst_limit_cfg = 0x00000280, + .misr_cfg_0 = 0x00000284, + .misr_cfg_1 = 0x00000288, + .misr_rd_val = 0x0000028C, + .debug_status_cfg = 0x00000290, + .debug_status_0 = 0x00000294, + .debug_status_1 = 0x00000298, + }, + /* bus client 1 */ + { + .core_cfg = 0x000002F0, + .ccif_meta_data = 0x000002F4, + .addr_image = 0x000002F8, + .rd_buffer_size = 0x000002FC, + .rd_stride = 0x00000300, + .unpack_cfg_0 = 0x00000304, + .latency_buff_allocation = 0x00000318, + .burst_limit_cfg = 0x00000320, + .misr_cfg_0 = 0x00000324, + .misr_cfg_1 = 0x00000328, + .misr_rd_val = 0x0000032C, + .debug_status_cfg = 0x00000330, + .debug_status_0 = 0x00000334, + .debug_status_1 = 0x00000338, + }, + }, + }, + .bus_wr_reg = { + .common_reg = { + .hw_version = 0x00000500, + .hw_capability = 0x00000504, + .sw_reset = 0x00000508, + .cgc_override = 0x0000050C, + .misr_reset = 0x000005C8, + .pwr_iso_cfg = 0x000005CC, + .test_bus_ctrl = 0x0000061C, + .composite_mask_0 = 0x00000510, + .irq_mask_0 = 0x00000544, + .irq_mask_1 = 0x00000548, + .irq_clear_0 = 0x00000550, + .irq_clear_1 = 0x00000554, + .irq_status_0 = 0x0000055C, + .irq_status_1 = 0x00000560, + .irq_cmd = 0x00000568, + .irq_set_0 = 0x000005BC, + .irq_set_1 = 0x000005C0, + .addr_fifo_status = 0x000005A8, + .frame_header_cfg0 = 0x000005AC, + .frame_header_cfg1 = 0x000005B0, + .spare = 0x00000620, + }, + .bus_client_reg = { + /* bus client 0 */ + { + .status_0 = 0x00000700, + .status_1 = 0x00000704, + .cfg = 0x00000708, + .addr_frame_header = 0x0000070C, + .frame_header_cfg = 0x00000710, + .addr_image = 0x00000714, + .addr_image_offset = 0x00000718, + .buffer_width_cfg = 0x0000071C, + .buffer_height_cfg = 0x00000720, + .packer_cfg = 0x00000724, + .wr_stride = 0x00000728, + .irq_subsample_cfg_period = 0x00000748, + .irq_subsample_cfg_pattern = 0x0000074C, + .burst_limit_cfg = 0x0000075C, + .misr_cfg = 0x00000760, + .misr_rd_word_sel = 0x00000764, + .misr_val = 0x00000768, + .debug_status_cfg = 0x0000076C, + .debug_status_0 = 0x00000770, + .debug_status_1 = 0x00000774, + }, + /* bus client 1 */ + { + .status_0 = 0x00000800, + .status_1 = 0x00000804, + .cfg = 0x00000808, + .addr_frame_header = 0x0000080C, + .frame_header_cfg = 0x00000810, + .addr_image = 0x00000814, + .addr_image_offset = 0x00000818, + .buffer_width_cfg = 0x0000081C, + .buffer_height_cfg = 0x00000820, + .packer_cfg = 0x00000824, + .wr_stride = 0x00000828, + .irq_subsample_cfg_period = 0x00000848, + .irq_subsample_cfg_pattern = 0x0000084C, + .burst_limit_cfg = 0x0000085C, + .misr_cfg = 0x00000860, + .misr_rd_word_sel = 0x00000864, + .misr_val = 0x00000868, + .debug_status_cfg = 0x0000086C, + .debug_status_0 = 0x00000870, + .debug_status_1 = 0x00000874, + }, + }, + }, + .titan_reg = { + .top_hw_version = 0x00000900, + .top_titan_version = 0x00000904, + .top_rst_cmd = 0x00000908, + .top_core_clk_cfg = 0x00000920, + .top_irq_status = 0x0000090C, + .top_irq_mask = 0x00000910, + .top_irq_clear = 0x00000914, + .top_irq_set = 0x00000918, + .top_irq_cmd = 0x0000091C, + .top_violation_status = 0x00000924, + .top_spare = 0x000009FC, + }, +}; + +#endif /* _CAM_LRME_HW_REG_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c new file mode 100644 index 000000000000..75de0ddbfd79 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c @@ -0,0 +1,158 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_lrme_hw_core.h" +#include "cam_lrme_hw_soc.h" + + +int cam_lrme_soc_enable_resources(struct cam_hw_info *lrme_hw) +{ + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_soc_private *soc_private = + (struct cam_lrme_soc_private *)soc_info->soc_private; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + int rc = 0; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = 7200000; + axi_vote.uncompressed_bw = 7200000; + rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to start cpas, rc %d", rc); + return -EFAULT; + } + + rc = cam_soc_util_enable_platform_resource(soc_info, true, CAM_SVS_VOTE, + true); + if (rc) { + CAM_ERR(CAM_LRME, + "Failed to enable platform resource, rc %d", rc); + goto stop_cpas; + } + + cam_lrme_set_irq(lrme_hw, CAM_LRME_IRQ_ENABLE); + + return rc; + +stop_cpas: + if (cam_cpas_stop(soc_private->cpas_handle)) + CAM_ERR(CAM_LRME, "Failed to stop cpas"); + + return rc; +} + +int cam_lrme_soc_disable_resources(struct cam_hw_info *lrme_hw) +{ + struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info; + struct cam_lrme_soc_private *soc_private; + int rc = 0; + + soc_private = soc_info->soc_private; + + cam_lrme_set_irq(lrme_hw, CAM_LRME_IRQ_DISABLE); + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) { + CAM_ERR(CAM_LRME, "Failed to disable platform resource"); + return rc; + } + rc = cam_cpas_stop(soc_private->cpas_handle); + if (rc) + CAM_ERR(CAM_LRME, "Failed to stop cpas"); + + return rc; +} + +int cam_lrme_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, void *private_data) +{ + struct cam_lrme_soc_private *soc_private; + struct cam_cpas_register_params cpas_register_param; + int rc; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in get_dt_properties, rc=%d", rc); + return rc; + } + + rc = cam_soc_util_request_platform_resource(soc_info, irq_handler, + private_data); + if (rc) { + CAM_ERR(CAM_LRME, "Failed in request_platform_resource rc=%d", + rc); + return rc; + } + + soc_private = kzalloc(sizeof(struct cam_lrme_soc_private), GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto release_res; + } + soc_info->soc_private = soc_private; + + memset(&cpas_register_param, 0, sizeof(cpas_register_param)); + strlcpy(cpas_register_param.identifier, + "lrmecpas", CAM_HW_IDENTIFIER_LENGTH); + cpas_register_param.cell_index = soc_info->index; + cpas_register_param.dev = &soc_info->pdev->dev; + cpas_register_param.userdata = private_data; + cpas_register_param.cam_cpas_client_cb = NULL; + + rc = cam_cpas_register_client(&cpas_register_param); + if (rc) { + CAM_ERR(CAM_LRME, "CPAS registration failed"); + goto free_soc_private; + } + soc_private->cpas_handle = cpas_register_param.client_handle; + CAM_DBG(CAM_LRME, "CPAS handle=%d", soc_private->cpas_handle); + + return rc; + +free_soc_private: + kfree(soc_info->soc_private); + soc_info->soc_private = NULL; +release_res: + cam_soc_util_release_platform_resource(soc_info); + + return rc; +} + +int cam_lrme_soc_deinit_resources(struct cam_hw_soc_info *soc_info) +{ + struct cam_lrme_soc_private *soc_private = + (struct cam_lrme_soc_private *)soc_info->soc_private; + int rc; + + rc = cam_cpas_unregister_client(soc_private->cpas_handle); + if (rc) + CAM_ERR(CAM_LRME, "Unregister cpas failed, handle=%d, rc=%d", + soc_private->cpas_handle, rc); + + rc = cam_soc_util_release_platform_resource(soc_info); + if (rc) + CAM_ERR(CAM_LRME, "release platform failed, rc=%d", rc); + + kfree(soc_info->soc_private); + soc_info->soc_private = NULL; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h new file mode 100644 index 000000000000..44e8486ecbe4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h @@ -0,0 +1,28 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_LRME_HW_SOC_H_ +#define _CAM_LRME_HW_SOC_H_ + +#include "cam_soc_util.h" + +struct cam_lrme_soc_private { + uint32_t cpas_handle; +}; + +int cam_lrme_soc_enable_resources(struct cam_hw_info *lrme_hw); +int cam_lrme_soc_disable_resources(struct cam_hw_info *lrme_hw); +int cam_lrme_soc_init_resources(struct cam_hw_soc_info *soc_info, + irq_handler_t irq_handler, void *private_data); +int cam_lrme_soc_deinit_resources(struct cam_hw_soc_info *soc_info); + +#endif /* _CAM_LRME_HW_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/Makefile new file mode 100644 index 000000000000..1220a9b427c0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_core.o\ + cam_req_mgr_dev.o \ + cam_req_mgr_util.o \ + cam_req_mgr_workq.o \ + cam_mem_mgr.o \ + cam_req_mgr_timer.o \ + cam_req_mgr_debug.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.c new file mode 100644 index 000000000000..54bdb5cc5d2f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.c @@ -0,0 +1,1446 @@ +/* Copyright (c) 2016-2018, 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 +#include +#include +#include +#include +#include +#include +#include + +#include "cam_req_mgr_util.h" +#include "cam_mem_mgr.h" +#include "cam_smmu_api.h" +#include "cam_debug_util.h" +#include "cam_trace.h" +#include "cam_common_util.h" + +static struct cam_mem_table tbl; +static atomic_t cam_mem_mgr_state = ATOMIC_INIT(CAM_MEM_MGR_UNINITIALIZED); + +static int cam_mem_util_get_dma_dir(uint32_t flags) +{ + int rc = -EINVAL; + + if (flags & CAM_MEM_FLAG_HW_READ_ONLY) + rc = DMA_TO_DEVICE; + else if (flags & CAM_MEM_FLAG_HW_WRITE_ONLY) + rc = DMA_FROM_DEVICE; + else if (flags & CAM_MEM_FLAG_HW_READ_WRITE) + rc = DMA_BIDIRECTIONAL; + else if (flags & CAM_MEM_FLAG_PROTECTED_MODE) + rc = DMA_BIDIRECTIONAL; + + return rc; +} + +static int cam_mem_util_map_cpu_va(struct dma_buf *dmabuf, + uintptr_t *vaddr, + size_t *len) +{ + int i, j, rc; + void *addr; + + /* + * dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() + * need to be called in pair to avoid stability issue. + */ + rc = dma_buf_begin_cpu_access(dmabuf, DMA_BIDIRECTIONAL); + if (rc) { + CAM_ERR(CAM_MEM, "dma begin access failed rc=%d", rc); + return rc; + } + + /* + * Code could be simplified if ION support of dma_buf_vmap is + * available. This workaround takes the avandaage that ion_alloc + * returns a virtually contiguous memory region, so we just need + * to _kmap each individual page and then only use the virtual + * address returned from the first call to _kmap. + */ + for (i = 0; i < PAGE_ALIGN(dmabuf->size) / PAGE_SIZE; i++) { + addr = dma_buf_kmap(dmabuf, i); + if (IS_ERR_OR_NULL(addr)) { + CAM_ERR(CAM_MEM, "kernel map fail"); + for (j = 0; j < i; j++) + dma_buf_kunmap(dmabuf, + j, + (void *)(*vaddr + (j * PAGE_SIZE))); + *vaddr = 0; + *len = 0; + rc = -ENOSPC; + goto fail; + } + if (i == 0) + *vaddr = (uint64_t)addr; + } + + *len = dmabuf->size; + + return 0; + +fail: + dma_buf_end_cpu_access(dmabuf, DMA_BIDIRECTIONAL); + return rc; +} +static int cam_mem_util_unmap_cpu_va(struct dma_buf *dmabuf, + uint64_t vaddr) +{ + int i, rc = 0, page_num; + + if (!dmabuf || !vaddr) { + CAM_ERR(CAM_MEM, "Invalid input args %pK %llX", dmabuf, vaddr); + return -EINVAL; + } + + page_num = PAGE_ALIGN(dmabuf->size) / PAGE_SIZE; + + for (i = 0; i < page_num; i++) { + dma_buf_kunmap(dmabuf, i, + (void *)(vaddr + (i * PAGE_SIZE))); + } + + /* + * dma_buf_begin_cpu_access() and + * dma_buf_end_cpu_access() need to be called in pair + * to avoid stability issue. + */ + rc = dma_buf_end_cpu_access(dmabuf, DMA_BIDIRECTIONAL); + if (rc) { + CAM_ERR(CAM_MEM, "Failed in end cpu access, dmabuf=%pK", + dmabuf); + return rc; + } + + return rc; +} + +static int cam_mem_mgr_create_debug_fs(void) +{ + tbl.dentry = debugfs_create_dir("camera_memmgr", NULL); + if (!tbl.dentry) { + CAM_ERR(CAM_MEM, "failed to create dentry"); + return -ENOMEM; + } + + if (!debugfs_create_bool("alloc_profile_enable", + 0644, + tbl.dentry, + &tbl.alloc_profile_enable)) { + CAM_ERR(CAM_MEM, + "failed to create alloc_profile_enable"); + goto err; + } + + return 0; +err: + debugfs_remove_recursive(tbl.dentry); + return -ENOMEM; +} + +int cam_mem_mgr_init(void) +{ + int i; + int bitmap_size; + + memset(tbl.bufq, 0, sizeof(tbl.bufq)); + + bitmap_size = BITS_TO_LONGS(CAM_MEM_BUFQ_MAX) * sizeof(long); + tbl.bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!tbl.bitmap) + return -ENOMEM; + + tbl.bits = bitmap_size * BITS_PER_BYTE; + bitmap_zero(tbl.bitmap, tbl.bits); + /* We need to reserve slot 0 because 0 is invalid */ + set_bit(0, tbl.bitmap); + + for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) { + tbl.bufq[i].fd = -1; + tbl.bufq[i].buf_handle = -1; + } + mutex_init(&tbl.m_lock); + + atomic_set(&cam_mem_mgr_state, CAM_MEM_MGR_INITIALIZED); + + cam_mem_mgr_create_debug_fs(); + + return 0; +} + +static int32_t cam_mem_get_slot(void) +{ + int32_t idx; + + mutex_lock(&tbl.m_lock); + if (tbl.bitmap) { + idx = find_first_zero_bit(tbl.bitmap, tbl.bits); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + mutex_unlock(&tbl.m_lock); + return -ENOMEM; + } + + set_bit(idx, tbl.bitmap); + tbl.bufq[idx].active = true; + mutex_init(&tbl.bufq[idx].q_lock); + mutex_unlock(&tbl.m_lock); + return idx; + } + + mutex_unlock(&tbl.m_lock); + return -EINVAL; +} + +static void cam_mem_put_slot(int32_t idx) +{ + mutex_lock(&tbl.m_lock); + mutex_lock(&tbl.bufq[idx].q_lock); + tbl.bufq[idx].active = false; + mutex_unlock(&tbl.bufq[idx].q_lock); + mutex_destroy(&tbl.bufq[idx].q_lock); + clear_bit(idx, tbl.bitmap); + mutex_unlock(&tbl.m_lock); +} + +int cam_mem_get_io_buf(int32_t buf_handle, int32_t mmu_handle, + dma_addr_t *iova_ptr, size_t *len_ptr) +{ + int rc = 0, idx; + + *len_ptr = 0; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) + return -ENOENT; + + if (!tbl.bufq[idx].active) + return -EAGAIN; + + mutex_lock(&tbl.bufq[idx].q_lock); + if (buf_handle != tbl.bufq[idx].buf_handle) { + rc = -EINVAL; + goto handle_mismatch; + } + + if (CAM_MEM_MGR_IS_SECURE_HDL(buf_handle)) + rc = cam_smmu_get_stage2_iova(mmu_handle, + tbl.bufq[idx].fd, + iova_ptr, + len_ptr); + else + rc = cam_smmu_get_iova(mmu_handle, + tbl.bufq[idx].fd, + iova_ptr, + len_ptr); + if (rc) { + CAM_ERR(CAM_MEM, + "fail to map buf_hdl:0x%x, mmu_hdl: 0x%x for fd:%d", + buf_handle, mmu_handle, tbl.bufq[idx].fd); + goto handle_mismatch; + } + + CAM_DBG(CAM_MEM, + "handle:0x%x fd:%d iova_ptr:%pK len_ptr:%llu", + mmu_handle, tbl.bufq[idx].fd, iova_ptr, *len_ptr); +handle_mismatch: + mutex_unlock(&tbl.bufq[idx].q_lock); + return rc; +} +EXPORT_SYMBOL(cam_mem_get_io_buf); + +int cam_mem_get_cpu_buf(int32_t buf_handle, uintptr_t *vaddr_ptr, size_t *len) +{ + int idx; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!buf_handle || !vaddr_ptr || !len) + return -EINVAL; + + idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) + return -EINVAL; + + if (!tbl.bufq[idx].active) + return -EPERM; + + if (buf_handle != tbl.bufq[idx].buf_handle) + return -EINVAL; + + if (!(tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS)) + return -EINVAL; + + if (tbl.bufq[idx].kmdvaddr) { + *vaddr_ptr = tbl.bufq[idx].kmdvaddr; + *len = tbl.bufq[idx].len; + } else { + CAM_ERR(CAM_MEM, "No KMD access was requested for 0x%x handle", + buf_handle); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(cam_mem_get_cpu_buf); + +int cam_mem_mgr_cache_ops(struct cam_mem_cache_ops_cmd *cmd) +{ + int rc = 0, idx; + uint32_t cache_dir; + unsigned long dmabuf_flag = 0; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!cmd) + return -EINVAL; + + idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) + return -EINVAL; + + mutex_lock(&tbl.bufq[idx].q_lock); + + if (!tbl.bufq[idx].active) { + rc = -EINVAL; + goto end; + } + + if (cmd->buf_handle != tbl.bufq[idx].buf_handle) { + rc = -EINVAL; + goto end; + } + + rc = dma_buf_get_flags(tbl.bufq[idx].dma_buf, &dmabuf_flag); + if (rc) { + CAM_ERR(CAM_MEM, "cache get flags failed %d", rc); + goto end; + } + + if (dmabuf_flag & ION_FLAG_CACHED) { + switch (cmd->mem_cache_ops) { + case CAM_MEM_CLEAN_CACHE: + cache_dir = DMA_TO_DEVICE; + break; + case CAM_MEM_INV_CACHE: + cache_dir = DMA_FROM_DEVICE; + break; + case CAM_MEM_CLEAN_INV_CACHE: + cache_dir = DMA_BIDIRECTIONAL; + break; + default: + CAM_ERR(CAM_MEM, + "invalid cache ops :%d", cmd->mem_cache_ops); + rc = -EINVAL; + goto end; + } + } else { + CAM_DBG(CAM_MEM, "BUF is not cached"); + goto end; + } + + rc = dma_buf_begin_cpu_access(tbl.bufq[idx].dma_buf, + (cmd->mem_cache_ops == CAM_MEM_CLEAN_INV_CACHE) ? + DMA_BIDIRECTIONAL : DMA_TO_DEVICE); + if (rc) { + CAM_ERR(CAM_MEM, "dma begin access failed rc=%d", rc); + goto end; + } + + rc = dma_buf_end_cpu_access(tbl.bufq[idx].dma_buf, + cache_dir); + if (rc) { + CAM_ERR(CAM_MEM, "dma end access failed rc=%d", rc); + goto end; + } + +end: + mutex_unlock(&tbl.bufq[idx].q_lock); + return rc; +} +EXPORT_SYMBOL(cam_mem_mgr_cache_ops); + +static int cam_mem_util_get_dma_buf(size_t len, + unsigned int heap_id_mask, + unsigned int flags, + struct dma_buf **buf) +{ + int rc = 0; + + if (!buf) { + CAM_ERR(CAM_MEM, "Invalid params"); + return -EINVAL; + } + + *buf = ion_alloc(len, heap_id_mask, flags); + if (IS_ERR_OR_NULL(*buf)) + return -ENOMEM; + + return rc; +} + +static int cam_mem_util_get_dma_buf_fd(size_t len, + size_t align, + unsigned int heap_id_mask, + unsigned int flags, + struct dma_buf **buf, + int *fd) +{ + struct dma_buf *dmabuf = NULL; + int rc = 0; + + if (!buf || !fd) { + CAM_ERR(CAM_MEM, "Invalid params, buf=%pK, fd=%pK", buf, fd); + return -EINVAL; + } + + *buf = ion_alloc(len, heap_id_mask, flags); + if (IS_ERR_OR_NULL(*buf)) + return -ENOMEM; + + *fd = dma_buf_fd(*buf, O_CLOEXEC); + if (*fd < 0) { + CAM_ERR(CAM_MEM, "get fd fail, *fd=%d", *fd); + rc = -EINVAL; + goto get_fd_fail; + } + + /* + * increment the ref count so that ref count becomes 2 here + * when we close fd, refcount becomes 1 and when we do + * dmap_put_buf, ref count becomes 0 and memory will be freed. + */ + dmabuf = dma_buf_get(*fd); + if (IS_ERR_OR_NULL(dmabuf)) { + CAM_ERR(CAM_MEM, "dma_buf_get failed, *fd=%d", *fd); + rc = -EINVAL; + } + + return rc; + +get_fd_fail: + dma_buf_put(*buf); + return rc; +} + +static int cam_mem_util_ion_alloc(struct cam_mem_mgr_alloc_cmd *cmd, + struct dma_buf **dmabuf, + int *fd) +{ + uint32_t heap_id; + uint32_t ion_flag = 0; + int rc; + + if ((cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) && + (cmd->flags & CAM_MEM_FLAG_CDSP_OUTPUT)) { + heap_id = ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID); + ion_flag |= + ION_FLAG_SECURE | ION_FLAG_CP_CAMERA | ION_FLAG_CP_CDSP; + } else if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) { + heap_id = ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID); + ion_flag |= ION_FLAG_SECURE | ION_FLAG_CP_CAMERA; + } else { + heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) | + ION_HEAP(ION_CAMERA_HEAP_ID); + } + + if (cmd->flags & CAM_MEM_FLAG_CACHE) + ion_flag |= ION_FLAG_CACHED; + else + ion_flag &= ~ION_FLAG_CACHED; + + rc = cam_mem_util_get_dma_buf_fd(cmd->len, + cmd->align, + heap_id, + ion_flag, + dmabuf, + fd); + + return rc; +} + + +static int cam_mem_util_check_alloc_flags(struct cam_mem_mgr_alloc_cmd *cmd) +{ + if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE) { + CAM_ERR(CAM_MEM, "Num of mmu hdl exceeded maximum(%d)", + CAM_MEM_MMU_MAX_HANDLE); + return -EINVAL; + } + + if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE && + cmd->flags & CAM_MEM_FLAG_KMD_ACCESS) { + CAM_ERR(CAM_MEM, "Kernel mapping in secure mode not allowed"); + return -EINVAL; + } + + return 0; +} + +static int cam_mem_util_check_map_flags(struct cam_mem_mgr_map_cmd *cmd) +{ + if (!cmd->flags) { + CAM_ERR(CAM_MEM, "Invalid flags"); + return -EINVAL; + } + + if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE) { + CAM_ERR(CAM_MEM, "Num of mmu hdl %d exceeded maximum(%d)", + cmd->num_hdl, CAM_MEM_MMU_MAX_HANDLE); + return -EINVAL; + } + + if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE && + cmd->flags & CAM_MEM_FLAG_KMD_ACCESS) { + CAM_ERR(CAM_MEM, + "Kernel mapping in secure mode not allowed, flags=0x%x", + cmd->flags); + return -EINVAL; + } + + if (cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) { + CAM_ERR(CAM_MEM, + "Shared memory buffers are not allowed to be mapped"); + return -EINVAL; + } + + return 0; +} + +static int cam_mem_util_map_hw_va(uint32_t flags, + int32_t *mmu_hdls, + int32_t num_hdls, + int fd, + dma_addr_t *hw_vaddr, + size_t *len, + enum cam_smmu_region_id region) +{ + int i; + int rc = -1; + int dir = cam_mem_util_get_dma_dir(flags); + bool dis_delayed_unmap = false; + + if (dir < 0) { + CAM_ERR(CAM_MEM, "fail to map DMA direction, dir=%d", dir); + return dir; + } + + if (flags & CAM_MEM_FLAG_DISABLE_DELAYED_UNMAP) + dis_delayed_unmap = true; + + CAM_DBG(CAM_MEM, + "map_hw_va : fd = %d, flags = 0x%x, dir=%d, num_hdls=%d", + fd, flags, dir, num_hdls); + + if (flags & CAM_MEM_FLAG_PROTECTED_MODE) { + for (i = 0; i < num_hdls; i++) { + rc = cam_smmu_map_stage2_iova(mmu_hdls[i], + fd, + dir, + hw_vaddr, + len); + + if (rc < 0) { + CAM_ERR(CAM_MEM, + "Failed to securely map to smmu, i=%d, fd=%d, dir=%d, mmu_hdl=%d, rc=%d", + i, fd, dir, mmu_hdls[i], rc); + goto multi_map_fail; + } + } + } else { + for (i = 0; i < num_hdls; i++) { + rc = cam_smmu_map_user_iova(mmu_hdls[i], + fd, + dis_delayed_unmap, + dir, + (dma_addr_t *)hw_vaddr, + len, + region); + + if (rc < 0) { + CAM_ERR(CAM_MEM, + "Failed to map to smmu, i=%d, fd=%d, dir=%d, mmu_hdl=%d, region=%d, rc=%d", + i, fd, dir, mmu_hdls[i], region, rc); + goto multi_map_fail; + } + } + } + + return rc; +multi_map_fail: + if (flags & CAM_MEM_FLAG_PROTECTED_MODE) + for (--i; i > 0; i--) + cam_smmu_unmap_stage2_iova(mmu_hdls[i], fd); + else + for (--i; i > 0; i--) + cam_smmu_unmap_user_iova(mmu_hdls[i], + fd, + CAM_SMMU_REGION_IO); + return rc; + +} + +int cam_mem_mgr_alloc_and_map(struct cam_mem_mgr_alloc_cmd *cmd) +{ + int rc; + int32_t idx; + struct dma_buf *dmabuf = NULL; + int fd = -1; + dma_addr_t hw_vaddr = 0; + size_t len; + uintptr_t kvaddr = 0; + size_t klen; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_MEM, " Invalid argument"); + return -EINVAL; + } + len = cmd->len; + + rc = cam_mem_util_check_alloc_flags(cmd); + if (rc) { + CAM_ERR(CAM_MEM, "Invalid flags: flags = 0x%X, rc=%d", + cmd->flags, rc); + return rc; + } + + rc = cam_mem_util_ion_alloc(cmd, + &dmabuf, + &fd); + if (rc) { + CAM_ERR(CAM_MEM, + "Ion Alloc failed, len=%llu, align=%llu, flags=0x%x, num_hdl=%d", + cmd->len, cmd->align, cmd->flags, cmd->num_hdl); + return rc; + } + + idx = cam_mem_get_slot(); + if (idx < 0) { + CAM_ERR(CAM_MEM, "Failed in getting mem slot, idx=%d", idx); + rc = -ENOMEM; + goto slot_fail; + } + + mutex_lock(&tbl.m_lock); + if ((cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) || + (cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) || + (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)) { + + enum cam_smmu_region_id region; + + if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) + region = CAM_SMMU_REGION_IO; + + + if (cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) + region = CAM_SMMU_REGION_SHARED; + + if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) + region = CAM_SMMU_REGION_SECHEAP; + + rc = cam_mem_util_map_hw_va(cmd->flags, + cmd->mmu_hdls, + cmd->num_hdl, + fd, + &hw_vaddr, + &len, + region); + + if (rc) { + CAM_ERR(CAM_MEM, + "Failed in map_hw_va, len=%llu, flags=0x%x, fd=%d, region=%d, num_hdl=%d, rc=%d", + len, cmd->flags, fd, region, + cmd->num_hdl, rc); + mutex_unlock(&tbl.m_lock); + goto map_hw_fail; + } + } + mutex_unlock(&tbl.m_lock); + mutex_lock(&tbl.bufq[idx].q_lock); + tbl.bufq[idx].fd = fd; + tbl.bufq[idx].dma_buf = NULL; + tbl.bufq[idx].flags = cmd->flags; + tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, fd); + if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) + CAM_MEM_MGR_SET_SECURE_HDL(tbl.bufq[idx].buf_handle, true); + + if (cmd->flags & CAM_MEM_FLAG_KMD_ACCESS) { + rc = cam_mem_util_map_cpu_va(dmabuf, &kvaddr, &klen); + if (rc) { + CAM_ERR(CAM_MEM, "dmabuf: %pK mapping failed: %d", + dmabuf, rc); + goto map_kernel_fail; + } + } + + tbl.bufq[idx].kmdvaddr = kvaddr; + tbl.bufq[idx].vaddr = hw_vaddr; + tbl.bufq[idx].dma_buf = dmabuf; + tbl.bufq[idx].len = len; + tbl.bufq[idx].num_hdl = cmd->num_hdl; + memcpy(tbl.bufq[idx].hdls, cmd->mmu_hdls, + sizeof(int32_t) * cmd->num_hdl); + tbl.bufq[idx].is_imported = false; + mutex_unlock(&tbl.bufq[idx].q_lock); + + cmd->out.buf_handle = tbl.bufq[idx].buf_handle; + cmd->out.fd = tbl.bufq[idx].fd; + cmd->out.vaddr = 0; + + CAM_DBG(CAM_MEM, + "fd=%d, flags=0x%x, num_hdl=%d, idx=%d, buf handle=%x, len=%zu", + cmd->out.fd, cmd->flags, cmd->num_hdl, idx, cmd->out.buf_handle, + tbl.bufq[idx].len); + + return rc; + +map_kernel_fail: + mutex_unlock(&tbl.bufq[idx].q_lock); +map_hw_fail: + cam_mem_put_slot(idx); +slot_fail: + dma_buf_put(dmabuf); + return rc; +} + +int cam_mem_mgr_map(struct cam_mem_mgr_map_cmd *cmd) +{ + int32_t idx; + int rc; + struct dma_buf *dmabuf; + dma_addr_t hw_vaddr = 0; + size_t len = 0; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!cmd || (cmd->fd < 0)) { + CAM_ERR(CAM_MEM, "Invalid argument"); + return -EINVAL; + } + + if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE) { + CAM_ERR(CAM_MEM, "Num of mmu hdl %d exceeded maximum(%d)", + cmd->num_hdl, CAM_MEM_MMU_MAX_HANDLE); + return -EINVAL; + } + + rc = cam_mem_util_check_map_flags(cmd); + if (rc) { + CAM_ERR(CAM_MEM, "Invalid flags: flags = %X", cmd->flags); + return rc; + } + + dmabuf = dma_buf_get(cmd->fd); + if (IS_ERR_OR_NULL((void *)(dmabuf))) { + CAM_ERR(CAM_MEM, "Failed to import dma_buf fd"); + return -EINVAL; + } + + mutex_lock(&tbl.m_lock); + if ((cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) || + (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)) { + rc = cam_mem_util_map_hw_va(cmd->flags, + cmd->mmu_hdls, + cmd->num_hdl, + cmd->fd, + &hw_vaddr, + &len, + CAM_SMMU_REGION_IO); + if (rc) { + CAM_ERR(CAM_MEM, + "Failed in map_hw_va, flags=0x%x, fd=%d, region=%d, num_hdl=%d, rc=%d", + cmd->flags, cmd->fd, CAM_SMMU_REGION_IO, + cmd->num_hdl, rc); + mutex_unlock(&tbl.m_lock); + goto map_fail; + } + } + mutex_unlock(&tbl.m_lock); + + idx = cam_mem_get_slot(); + if (idx < 0) { + rc = -ENOMEM; + goto map_fail; + } + + mutex_lock(&tbl.bufq[idx].q_lock); + tbl.bufq[idx].fd = cmd->fd; + tbl.bufq[idx].dma_buf = NULL; + tbl.bufq[idx].flags = cmd->flags; + tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, cmd->fd); + if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) + CAM_MEM_MGR_SET_SECURE_HDL(tbl.bufq[idx].buf_handle, true); + tbl.bufq[idx].kmdvaddr = 0; + + if (cmd->num_hdl > 0) + tbl.bufq[idx].vaddr = hw_vaddr; + else + tbl.bufq[idx].vaddr = 0; + + tbl.bufq[idx].dma_buf = dmabuf; + tbl.bufq[idx].len = len; + tbl.bufq[idx].num_hdl = cmd->num_hdl; + memcpy(tbl.bufq[idx].hdls, cmd->mmu_hdls, + sizeof(int32_t) * cmd->num_hdl); + tbl.bufq[idx].is_imported = true; + mutex_unlock(&tbl.bufq[idx].q_lock); + + cmd->out.buf_handle = tbl.bufq[idx].buf_handle; + cmd->out.vaddr = 0; + + CAM_DBG(CAM_MEM, + "fd=%d, flags=0x%x, num_hdl=%d, idx=%d, buf handle=%x, len=%zu", + cmd->fd, cmd->flags, cmd->num_hdl, idx, cmd->out.buf_handle, + tbl.bufq[idx].len); + + return rc; + +map_fail: + dma_buf_put(dmabuf); + return rc; +} + +static int cam_mem_util_unmap_hw_va(int32_t idx, + enum cam_smmu_region_id region, + enum cam_smmu_mapping_client client) +{ + int i; + uint32_t flags; + int32_t *mmu_hdls; + int num_hdls; + int fd; + int rc = 0; + + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + CAM_ERR(CAM_MEM, "Incorrect index"); + return -EINVAL; + } + + flags = tbl.bufq[idx].flags; + mmu_hdls = tbl.bufq[idx].hdls; + num_hdls = tbl.bufq[idx].num_hdl; + fd = tbl.bufq[idx].fd; + + CAM_DBG(CAM_MEM, + "unmap_hw_va : idx=%d, fd=%x, flags=0x%x, num_hdls=%d, client=%d", + idx, fd, flags, num_hdls, client); + + if (flags & CAM_MEM_FLAG_PROTECTED_MODE) { + for (i = 0; i < num_hdls; i++) { + rc = cam_smmu_unmap_stage2_iova(mmu_hdls[i], fd); + if (rc < 0) { + CAM_ERR(CAM_MEM, + "Failed in secure unmap, i=%d, fd=%d, mmu_hdl=%d, rc=%d", + i, fd, mmu_hdls[i], rc); + goto unmap_end; + } + } + } else { + for (i = 0; i < num_hdls; i++) { + if (client == CAM_SMMU_MAPPING_USER) { + rc = cam_smmu_unmap_user_iova(mmu_hdls[i], + fd, region); + } else if (client == CAM_SMMU_MAPPING_KERNEL) { + rc = cam_smmu_unmap_kernel_iova(mmu_hdls[i], + tbl.bufq[idx].dma_buf, region); + } else { + CAM_ERR(CAM_MEM, + "invalid caller for unmapping : %d", + client); + rc = -EINVAL; + } + if (rc < 0) { + CAM_ERR(CAM_MEM, + "Failed in unmap, i=%d, fd=%d, mmu_hdl=%d, region=%d, rc=%d", + i, fd, mmu_hdls[i], region, rc); + goto unmap_end; + } + } + } + + return rc; + +unmap_end: + CAM_ERR(CAM_MEM, "unmapping failed"); + return rc; +} + +static void cam_mem_mgr_unmap_active_buf(int idx) +{ + enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED; + + if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) + region = CAM_SMMU_REGION_SHARED; + else if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE) + region = CAM_SMMU_REGION_IO; + + cam_mem_util_unmap_hw_va(idx, region, CAM_SMMU_MAPPING_USER); +} + +static int cam_mem_mgr_cleanup_table(void) +{ + int i; + + mutex_lock(&tbl.m_lock); + for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) { + if (!tbl.bufq[i].active) { + CAM_DBG(CAM_MEM, + "Buffer inactive at idx=%d, continuing", i); + continue; + } else { + CAM_DBG(CAM_MEM, + "Active buffer at idx=%d, possible leak needs unmapping", + i); + cam_mem_mgr_unmap_active_buf(i); + } + + mutex_lock(&tbl.bufq[i].q_lock); + if (tbl.bufq[i].dma_buf) { + dma_buf_put(tbl.bufq[i].dma_buf); + tbl.bufq[i].dma_buf = NULL; + } + tbl.bufq[i].fd = -1; + tbl.bufq[i].flags = 0; + tbl.bufq[i].buf_handle = -1; + tbl.bufq[i].vaddr = 0; + tbl.bufq[i].len = 0; + memset(tbl.bufq[i].hdls, 0, + sizeof(int32_t) * tbl.bufq[i].num_hdl); + tbl.bufq[i].num_hdl = 0; + tbl.bufq[i].dma_buf = NULL; + tbl.bufq[i].active = false; + mutex_unlock(&tbl.bufq[i].q_lock); + mutex_destroy(&tbl.bufq[i].q_lock); + } + + bitmap_zero(tbl.bitmap, tbl.bits); + /* We need to reserve slot 0 because 0 is invalid */ + set_bit(0, tbl.bitmap); + mutex_unlock(&tbl.m_lock); + + return 0; +} + +void cam_mem_mgr_deinit(void) +{ + atomic_set(&cam_mem_mgr_state, CAM_MEM_MGR_UNINITIALIZED); + cam_mem_mgr_cleanup_table(); + mutex_lock(&tbl.m_lock); + bitmap_zero(tbl.bitmap, tbl.bits); + kfree(tbl.bitmap); + tbl.bitmap = NULL; + mutex_unlock(&tbl.m_lock); + mutex_destroy(&tbl.m_lock); +} + +static int cam_mem_util_unmap(int32_t idx, + enum cam_smmu_mapping_client client) +{ + int rc = 0; + enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED; + + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + CAM_ERR(CAM_MEM, "Incorrect index"); + return -EINVAL; + } + + CAM_DBG(CAM_MEM, "Flags = %X idx %d", tbl.bufq[idx].flags, idx); + + mutex_lock(&tbl.m_lock); + if ((!tbl.bufq[idx].active) && + (tbl.bufq[idx].vaddr) == 0) { + CAM_WARN(CAM_MEM, "Buffer at idx=%d is already unmapped,", + idx); + mutex_unlock(&tbl.m_lock); + return 0; + } + + if (tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS) { + if (tbl.bufq[idx].dma_buf && tbl.bufq[idx].kmdvaddr) { + rc = cam_mem_util_unmap_cpu_va(tbl.bufq[idx].dma_buf, + tbl.bufq[idx].kmdvaddr); + if (rc) + CAM_ERR(CAM_MEM, + "Failed, dmabuf=%pK, kmdvaddr=%pK", + tbl.bufq[idx].dma_buf, + (void *) tbl.bufq[idx].kmdvaddr); + } + } + + /* SHARED flag gets precedence, all other flags after it */ + if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) { + region = CAM_SMMU_REGION_SHARED; + } else { + if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE) + region = CAM_SMMU_REGION_IO; + } + + if ((tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE) || + (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) || + (tbl.bufq[idx].flags & CAM_MEM_FLAG_PROTECTED_MODE)) { + if (cam_mem_util_unmap_hw_va(idx, region, client)) + CAM_ERR(CAM_MEM, "Failed, dmabuf=%pK", + tbl.bufq[idx].dma_buf); + if (client == CAM_SMMU_MAPPING_KERNEL) + tbl.bufq[idx].dma_buf = NULL; + } + + mutex_lock(&tbl.bufq[idx].q_lock); + tbl.bufq[idx].flags = 0; + tbl.bufq[idx].buf_handle = -1; + tbl.bufq[idx].vaddr = 0; + memset(tbl.bufq[idx].hdls, 0, + sizeof(int32_t) * CAM_MEM_MMU_MAX_HANDLE); + + CAM_DBG(CAM_MEM, + "Ion buf at idx = %d freeing fd = %d, imported %d, dma_buf %pK", + idx, tbl.bufq[idx].fd, + tbl.bufq[idx].is_imported, + tbl.bufq[idx].dma_buf); + + if (tbl.bufq[idx].dma_buf) + dma_buf_put(tbl.bufq[idx].dma_buf); + + tbl.bufq[idx].fd = -1; + tbl.bufq[idx].dma_buf = NULL; + tbl.bufq[idx].is_imported = false; + tbl.bufq[idx].len = 0; + tbl.bufq[idx].num_hdl = 0; + tbl.bufq[idx].active = false; + mutex_unlock(&tbl.bufq[idx].q_lock); + mutex_destroy(&tbl.bufq[idx].q_lock); + clear_bit(idx, tbl.bitmap); + mutex_unlock(&tbl.m_lock); + + return rc; +} + +int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd) +{ + int idx; + int rc; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!cmd) { + CAM_ERR(CAM_MEM, "Invalid argument"); + return -EINVAL; + } + + idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + CAM_ERR(CAM_MEM, "Incorrect index %d extracted from mem handle", + idx); + return -EINVAL; + } + + if (!tbl.bufq[idx].active) { + CAM_ERR(CAM_MEM, "Released buffer state should be active"); + return -EINVAL; + } + + if (tbl.bufq[idx].buf_handle != cmd->buf_handle) { + CAM_ERR(CAM_MEM, + "Released buf handle %d not matching within table %d, idx=%d", + cmd->buf_handle, tbl.bufq[idx].buf_handle, idx); + return -EINVAL; + } + + CAM_DBG(CAM_MEM, "Releasing hdl = %x, idx = %d", cmd->buf_handle, idx); + rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_USER); + + return rc; +} + +int cam_mem_mgr_request_mem(struct cam_mem_mgr_request_desc *inp, + struct cam_mem_mgr_memory_desc *out) +{ + struct dma_buf *buf = NULL; + int ion_fd = -1; + int rc = 0; + uint32_t heap_id; + int32_t ion_flag = 0; + uintptr_t kvaddr; + dma_addr_t iova = 0; + size_t request_len = 0; + uint32_t mem_handle; + int32_t idx; + int32_t smmu_hdl = 0; + int32_t num_hdl = 0; + + enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!inp || !out) { + CAM_ERR(CAM_MEM, "Invalid params"); + return -EINVAL; + } + + if (!(inp->flags & CAM_MEM_FLAG_HW_READ_WRITE || + inp->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS || + inp->flags & CAM_MEM_FLAG_CACHE)) { + CAM_ERR(CAM_MEM, "Invalid flags for request mem"); + return -EINVAL; + } + + if (inp->flags & CAM_MEM_FLAG_CACHE) + ion_flag |= ION_FLAG_CACHED; + else + ion_flag &= ~ION_FLAG_CACHED; + + heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) | + ION_HEAP(ION_CAMERA_HEAP_ID); + + rc = cam_mem_util_get_dma_buf(inp->size, + heap_id, + ion_flag, + &buf); + + if (rc) { + CAM_ERR(CAM_MEM, "ION alloc failed for shared buffer"); + goto ion_fail; + } else { + CAM_DBG(CAM_MEM, "Got dma_buf = %pK", buf); + } + + /* + * we are mapping kva always here, + * update flags so that we do unmap properly + */ + inp->flags |= CAM_MEM_FLAG_KMD_ACCESS; + rc = cam_mem_util_map_cpu_va(buf, &kvaddr, &request_len); + if (rc) { + CAM_ERR(CAM_MEM, "Failed to get kernel vaddr"); + goto map_fail; + } + + if (!inp->smmu_hdl) { + CAM_ERR(CAM_MEM, "Invalid SMMU handle"); + rc = -EINVAL; + goto smmu_fail; + } + + /* SHARED flag gets precedence, all other flags after it */ + if (inp->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) { + region = CAM_SMMU_REGION_SHARED; + } else { + if (inp->flags & CAM_MEM_FLAG_HW_READ_WRITE) + region = CAM_SMMU_REGION_IO; + } + + rc = cam_smmu_map_kernel_iova(inp->smmu_hdl, + buf, + CAM_SMMU_MAP_RW, + &iova, + &request_len, + region); + + if (rc < 0) { + CAM_ERR(CAM_MEM, "SMMU mapping failed"); + goto smmu_fail; + } + + smmu_hdl = inp->smmu_hdl; + num_hdl = 1; + + idx = cam_mem_get_slot(); + if (idx < 0) { + rc = -ENOMEM; + goto slot_fail; + } + + mutex_lock(&tbl.bufq[idx].q_lock); + mem_handle = GET_MEM_HANDLE(idx, ion_fd); + tbl.bufq[idx].dma_buf = buf; + tbl.bufq[idx].fd = -1; + tbl.bufq[idx].flags = inp->flags; + tbl.bufq[idx].buf_handle = mem_handle; + tbl.bufq[idx].kmdvaddr = kvaddr; + + tbl.bufq[idx].vaddr = iova; + + tbl.bufq[idx].len = inp->size; + tbl.bufq[idx].num_hdl = num_hdl; + memcpy(tbl.bufq[idx].hdls, &smmu_hdl, + sizeof(int32_t)); + tbl.bufq[idx].is_imported = false; + mutex_unlock(&tbl.bufq[idx].q_lock); + + out->kva = kvaddr; + out->iova = (uint32_t)iova; + out->smmu_hdl = smmu_hdl; + out->mem_handle = mem_handle; + out->len = inp->size; + out->region = region; + + return rc; +slot_fail: + cam_smmu_unmap_kernel_iova(inp->smmu_hdl, + buf, region); +smmu_fail: + cam_mem_util_unmap_cpu_va(buf, kvaddr); +map_fail: + dma_buf_put(buf); +ion_fail: + return rc; +} +EXPORT_SYMBOL(cam_mem_mgr_request_mem); + +int cam_mem_mgr_release_mem(struct cam_mem_mgr_memory_desc *inp) +{ + int32_t idx; + int rc; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!inp) { + CAM_ERR(CAM_MEM, "Invalid argument"); + return -EINVAL; + } + + idx = CAM_MEM_MGR_GET_HDL_IDX(inp->mem_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + CAM_ERR(CAM_MEM, "Incorrect index extracted from mem handle"); + return -EINVAL; + } + + if (!tbl.bufq[idx].active) { + if (tbl.bufq[idx].vaddr == 0) { + CAM_ERR(CAM_MEM, "buffer is released already"); + return 0; + } + CAM_ERR(CAM_MEM, "Released buffer state should be active"); + return -EINVAL; + } + + if (tbl.bufq[idx].buf_handle != inp->mem_handle) { + CAM_ERR(CAM_MEM, + "Released buf handle not matching within table"); + return -EINVAL; + } + + CAM_DBG(CAM_MEM, "Releasing hdl = %X", inp->mem_handle); + rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_KERNEL); + + return rc; +} +EXPORT_SYMBOL(cam_mem_mgr_release_mem); + +int cam_mem_mgr_reserve_memory_region(struct cam_mem_mgr_request_desc *inp, + enum cam_smmu_region_id region, + struct cam_mem_mgr_memory_desc *out) +{ + struct dma_buf *buf = NULL; + int rc = 0; + int ion_fd = -1; + uint32_t heap_id; + dma_addr_t iova = 0; + size_t request_len = 0; + uint32_t mem_handle; + int32_t idx; + int32_t smmu_hdl = 0; + int32_t num_hdl = 0; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!inp || !out) { + CAM_ERR(CAM_MEM, "Invalid param(s)"); + return -EINVAL; + } + + if (!inp->smmu_hdl) { + CAM_ERR(CAM_MEM, "Invalid SMMU handle"); + return -EINVAL; + } + + if (region != CAM_SMMU_REGION_SECHEAP) { + CAM_ERR(CAM_MEM, "Only secondary heap supported"); + return -EINVAL; + } + + heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) | + ION_HEAP(ION_CAMERA_HEAP_ID); + rc = cam_mem_util_get_dma_buf(inp->size, + heap_id, + 0, + &buf); + + if (rc) { + CAM_ERR(CAM_MEM, "ION alloc failed for sec heap buffer"); + goto ion_fail; + } else { + CAM_DBG(CAM_MEM, "Got dma_buf = %pK", buf); + } + + rc = cam_smmu_reserve_sec_heap(inp->smmu_hdl, + buf, + &iova, + &request_len); + + if (rc) { + CAM_ERR(CAM_MEM, "Reserving secondary heap failed"); + goto smmu_fail; + } + + smmu_hdl = inp->smmu_hdl; + num_hdl = 1; + + idx = cam_mem_get_slot(); + if (idx < 0) { + rc = -ENOMEM; + goto slot_fail; + } + + mutex_lock(&tbl.bufq[idx].q_lock); + mem_handle = GET_MEM_HANDLE(idx, ion_fd); + tbl.bufq[idx].fd = -1; + tbl.bufq[idx].dma_buf = buf; + tbl.bufq[idx].flags = inp->flags; + tbl.bufq[idx].buf_handle = mem_handle; + tbl.bufq[idx].kmdvaddr = 0; + + tbl.bufq[idx].vaddr = iova; + + tbl.bufq[idx].len = request_len; + tbl.bufq[idx].num_hdl = num_hdl; + memcpy(tbl.bufq[idx].hdls, &smmu_hdl, + sizeof(int32_t)); + tbl.bufq[idx].is_imported = false; + mutex_unlock(&tbl.bufq[idx].q_lock); + + out->kva = 0; + out->iova = (uint32_t)iova; + out->smmu_hdl = smmu_hdl; + out->mem_handle = mem_handle; + out->len = request_len; + out->region = region; + + return rc; + +slot_fail: + cam_smmu_release_sec_heap(smmu_hdl); +smmu_fail: + dma_buf_put(buf); +ion_fail: + return rc; +} +EXPORT_SYMBOL(cam_mem_mgr_reserve_memory_region); + +int cam_mem_mgr_free_memory_region(struct cam_mem_mgr_memory_desc *inp) +{ + int32_t idx; + int rc; + int32_t smmu_hdl; + + if (!atomic_read(&cam_mem_mgr_state)) { + CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized"); + return -EINVAL; + } + + if (!inp) { + CAM_ERR(CAM_MEM, "Invalid argument"); + return -EINVAL; + } + + if (inp->region != CAM_SMMU_REGION_SECHEAP) { + CAM_ERR(CAM_MEM, "Only secondary heap supported"); + return -EINVAL; + } + + idx = CAM_MEM_MGR_GET_HDL_IDX(inp->mem_handle); + if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { + CAM_ERR(CAM_MEM, "Incorrect index extracted from mem handle"); + return -EINVAL; + } + + if (!tbl.bufq[idx].active) { + if (tbl.bufq[idx].vaddr == 0) { + CAM_ERR(CAM_MEM, "buffer is released already"); + return 0; + } + CAM_ERR(CAM_MEM, "Released buffer state should be active"); + return -EINVAL; + } + + if (tbl.bufq[idx].buf_handle != inp->mem_handle) { + CAM_ERR(CAM_MEM, + "Released buf handle not matching within table"); + return -EINVAL; + } + + if (tbl.bufq[idx].num_hdl != 1) { + CAM_ERR(CAM_MEM, + "Sec heap region should have only one smmu hdl"); + return -ENODEV; + } + + memcpy(&smmu_hdl, tbl.bufq[idx].hdls, + sizeof(int32_t)); + if (inp->smmu_hdl != smmu_hdl) { + CAM_ERR(CAM_MEM, + "Passed SMMU handle doesn't match with internal hdl"); + return -ENODEV; + } + + rc = cam_smmu_release_sec_heap(inp->smmu_hdl); + if (rc) { + CAM_ERR(CAM_MEM, + "Sec heap region release failed"); + return -ENODEV; + } + + CAM_DBG(CAM_MEM, "Releasing hdl = %X", inp->mem_handle); + rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_KERNEL); + if (rc) + CAM_ERR(CAM_MEM, "unmapping secondary heap failed"); + + return rc; +} +EXPORT_SYMBOL(cam_mem_mgr_free_memory_region); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.h new file mode 100644 index 000000000000..3b91933f673a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr.h @@ -0,0 +1,137 @@ +/* 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. + */ + +#ifndef _CAM_MEM_MGR_H_ +#define _CAM_MEM_MGR_H_ + +#include +#include +#include +#include "cam_mem_mgr_api.h" + +#define CAM_MEM_BUFQ_MAX 1024 + +/* Enum for possible mem mgr states */ +enum cam_mem_mgr_state { + CAM_MEM_MGR_UNINITIALIZED, + CAM_MEM_MGR_INITIALIZED, +}; + +/*Enum for possible SMMU operations */ +enum cam_smmu_mapping_client { + CAM_SMMU_MAPPING_USER, + CAM_SMMU_MAPPING_KERNEL, +}; + +/** + * struct cam_mem_buf_queue + * + * @dma_buf: pointer to the allocated dma_buf in the table + * @q_lock: mutex lock for buffer + * @hdls: list of mapped handles + * @num_hdl: number of handles + * @fd: file descriptor of buffer + * @buf_handle: unique handle for buffer + * @align: alignment for allocation + * @len: size of buffer + * @flags: attributes of buffer + * @vaddr: IOVA of buffer + * @kmdvaddr: Kernel virtual address + * @active: state of the buffer + * @is_imported: Flag indicating if buffer is imported from an FD in user space + */ +struct cam_mem_buf_queue { + struct dma_buf *dma_buf; + struct mutex q_lock; + int32_t hdls[CAM_MEM_MMU_MAX_HANDLE]; + int32_t num_hdl; + int32_t fd; + int32_t buf_handle; + int32_t align; + size_t len; + uint32_t flags; + uint64_t vaddr; + uintptr_t kmdvaddr; + bool active; + bool is_imported; +}; + +/** + * struct cam_mem_table + * + * @m_lock: mutex lock for table + * @bitmap: bitmap of the mem mgr utility + * @bits: max bits of the utility + * @bufq: array of buffers + * @dentry: Debugfs entry + * @alloc_profile_enable: Whether to enable alloc profiling + */ +struct cam_mem_table { + struct mutex m_lock; + void *bitmap; + size_t bits; + struct cam_mem_buf_queue bufq[CAM_MEM_BUFQ_MAX]; + struct dentry *dentry; + bool alloc_profile_enable; +}; + +/** + * @brief: Allocates and maps buffer + * + * @cmd: Allocation information + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_alloc_and_map(struct cam_mem_mgr_alloc_cmd *cmd); + +/** + * @brief: Releases a buffer reference + * + * @cmd: Buffer release information + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd); + +/** + * @brief Maps a buffer + * + * @cmd: Buffer mapping information + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_map(struct cam_mem_mgr_map_cmd *cmd); + +/** + * @brief: Perform cache ops on the buffer + * + * @cmd: Cache ops information + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_cache_ops(struct cam_mem_cache_ops_cmd *cmd); + +/** + * @brief: Initializes the memory manager + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_init(void); + +/** + * @brief: Tears down the memory manager + * + * @return None + */ +void cam_mem_mgr_deinit(void); + +#endif /* _CAM_MEM_MGR_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr_api.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr_api.h new file mode 100644 index 000000000000..fc6a628a20bb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_mem_mgr_api.h @@ -0,0 +1,127 @@ +/* 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. + */ + +#ifndef _CAM_MEM_MGR_API_H_ +#define _CAM_MEM_MGR_API_H_ + +#include +#include "cam_smmu_api.h" + +/** + * struct cam_mem_mgr_request_desc + * + * @size : Size of memory requested for allocation + * @align : Alignment of requested memory + * @smmu_hdl: SMMU handle to identify context bank where memory will be mapped + * @flags : Flags to indicate cached/uncached property + * @region : Region where memory should be allocated + */ +struct cam_mem_mgr_request_desc { + uint64_t size; + uint64_t align; + int32_t smmu_hdl; + uint32_t flags; +}; + +/** + * struct cam_mem_mgr_memory_desc + * + * @kva : Kernel virtual address of allocated memory + * @iova : IOVA of allocated memory + * @smmu_hdl : SMMU handle of allocated memory + * @mem_handle : Mem handle identifying allocated memory + * @len : Length of allocated memory + * @region : Region to which allocated memory belongs + */ +struct cam_mem_mgr_memory_desc { + uintptr_t kva; + uint32_t iova; + int32_t smmu_hdl; + uint32_t mem_handle; + uint64_t len; + enum cam_smmu_region_id region; +}; + +/** + * @brief: Requests a memory buffer + * + * @inp: Information specifying requested buffer properties + * @out: Information about allocated buffer + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_request_mem(struct cam_mem_mgr_request_desc *inp, + struct cam_mem_mgr_memory_desc *out); + +/** + * @brief: Releases a memory buffer + * + * @inp: Information specifying buffer to be released + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_release_mem(struct cam_mem_mgr_memory_desc *inp); + +/** + * @brief: Returns IOVA information about buffer + * + * @buf_handle: Handle of the buffer + * @mmu_handle: SMMU handle where buffer is mapped + * @iova_ptr : Pointer to mmu's iova + * @len_ptr : Length of the buffer + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_get_io_buf(int32_t buf_handle, int32_t mmu_handle, + dma_addr_t *iova_ptr, size_t *len_ptr); + +/** + * @brief: This indicates begin of CPU access. + * Also returns CPU address information about DMA buffer + * + * @buf_handle: Handle for the buffer + * @vaddr_ptr : pointer to kernel virtual address + * @len_ptr : Length of the buffer + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_get_cpu_buf(int32_t buf_handle, uintptr_t *vaddr_ptr, + size_t *len); + +static inline bool cam_mem_is_secure_buf(int32_t buf_handle) +{ + return CAM_MEM_MGR_IS_SECURE_HDL(buf_handle); +} + +/** + * @brief: Reserves a memory region + * + * @inp: Information specifying requested region properties + * @region : Region which is to be reserved + * @out : Information about reserved region + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_reserve_memory_region(struct cam_mem_mgr_request_desc *inp, + enum cam_smmu_region_id region, + struct cam_mem_mgr_memory_desc *out); + +/** + * @brief: Frees a memory region + * + * @inp : Information about region which is to be freed + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_mem_mgr_free_memory_region(struct cam_mem_mgr_memory_desc *inp); + +#endif /* _CAM_MEM_MGR_API_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.c new file mode 100644 index 000000000000..d94663b3bb13 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.c @@ -0,0 +1,2732 @@ +/* Copyright (c) 2016-2018, 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 +#include +#include +#include +#include "cam_req_mgr_interface.h" +#include "cam_req_mgr_util.h" +#include "cam_req_mgr_core.h" +#include "cam_req_mgr_workq.h" +#include "cam_req_mgr_debug.h" +#include "cam_trace.h" +#include "cam_debug_util.h" +#include "cam_req_mgr_dev.h" + +static struct cam_req_mgr_core_device *g_crm_core_dev; + +void cam_req_mgr_handle_core_shutdown(void) +{ + struct cam_req_mgr_core_session *session; + struct cam_req_mgr_core_session *tsession; + struct cam_req_mgr_session_info ses_info; + + if (!list_empty(&g_crm_core_dev->session_head)) { + list_for_each_entry_safe(session, tsession, + &g_crm_core_dev->session_head, entry) { + ses_info.session_hdl = + session->session_hdl; + cam_req_mgr_destroy_session(&ses_info); + } + } +} + +static int __cam_req_mgr_setup_payload(struct cam_req_mgr_core_workq *workq) +{ + int32_t i = 0; + int rc = 0; + struct crm_task_payload *task_data = NULL; + + task_data = kcalloc( + workq->task.num_task, sizeof(*task_data), + GFP_KERNEL); + if (!task_data) { + rc = -ENOMEM; + } else { + for (i = 0; i < workq->task.num_task; i++) + workq->task.pool[i].payload = &task_data[i]; + } + + return rc; +} + +/** + * __cam_req_mgr_reset_req_tbl() + * + * @brief : Initialize req table data + * @in_q : request queue pointer + * + * @return: 0 for success, negative for failure + * + */ +static int __cam_req_mgr_print_req_tbl(struct cam_req_mgr_req_data *req) +{ + int rc = 0; + int32_t i = 0; + struct cam_req_mgr_req_queue *in_q = req->in_q; + struct cam_req_mgr_req_tbl *req_tbl = req->l_tbl; + + if (!in_q || !req_tbl) { + CAM_WARN(CAM_CRM, "NULL pointer %pK %pK", in_q, req_tbl); + return -EINVAL; + } + CAM_DBG(CAM_CRM, "in_q %pK %pK %d", in_q, req_tbl, req_tbl->num_slots); + mutex_lock(&req->lock); + for (i = 0; i < in_q->num_slots; i++) { + CAM_DBG(CAM_CRM, "IN_Q %d: idx %d, red_id %lld", i, + in_q->slot[i].idx, CRM_GET_REQ_ID(in_q, i)); + } + + while (req_tbl != NULL) { + for (i = 0; i < req_tbl->num_slots; i++) { + CAM_DBG(CAM_CRM, "idx= %d, map= %x, state= %d", + req_tbl->slot[i].idx, + req_tbl->slot[i].req_ready_map, + req_tbl->slot[i].state); + } + CAM_DBG(CAM_CRM, + "TBL:id= %d, pd=%d cnt=%d mask=%x skip=%d num_slt= %d", + req_tbl->id, req_tbl->pd, req_tbl->dev_count, + req_tbl->dev_mask, req_tbl->skip_traverse, + req_tbl->num_slots); + req_tbl = req_tbl->next; + } + mutex_unlock(&req->lock); + + return rc; +} + +/** + * __cam_req_mgr_find_pd_tbl() + * + * @brief : Find pipeline delay based table pointer which matches delay + * @tbl : Pointer to list of request table + * @delay : Pipeline delay value to be searched for comparison + * + * @return : pointer to request table for matching pipeline delay table. + * + */ +static struct cam_req_mgr_req_tbl *__cam_req_mgr_find_pd_tbl( + struct cam_req_mgr_req_tbl *tbl, int32_t delay) +{ + if (!tbl) + return NULL; + + do { + if (delay != tbl->pd) + tbl = tbl->next; + else + return tbl; + } while (tbl != NULL); + + return NULL; +} + +/** + * __cam_req_mgr_inc_idx() + * + * @brief : Increment val passed by step size and rollover after max_val + * @val : value to be incremented + * @step : amount/step by which val is incremented + * @max_val : max val after which idx will roll over + * + */ +static void __cam_req_mgr_inc_idx(int32_t *val, int32_t step, int32_t max_val) +{ + *val = (*val + step) % max_val; +} + +/** + * __cam_req_mgr_dec_idx() + * + * @brief : Decrement val passed by step size and rollover after max_val + * @val : value to be decremented + * @step : amount/step by which val is decremented + * @max_val : after zero value will roll over to max val + * + */ +static void __cam_req_mgr_dec_idx(int32_t *val, int32_t step, int32_t max_val) +{ + *val = *val - step; + if (*val < 0) + *val = max_val + (*val); +} + +/** + * __cam_req_mgr_traverse() + * + * @brief : Traverse through pd tables, it will internally cover all linked + * pd tables. Each pd table visited will check if idx passed to its + * in ready state. If ready means all devices linked to the pd table + * have this request id packet ready. Then it calls subsequent pd + * tbl with new idx. New idx value takes into account the delta + * between current pd table and next one. + * @traverse_data: contains all the info to traverse through pd tables + * + * @return: 0 for success, negative for failure + * + */ +static int __cam_req_mgr_traverse(struct cam_req_mgr_traverse *traverse_data) +{ + int rc = 0; + int32_t next_idx = traverse_data->idx; + int32_t curr_idx = traverse_data->idx; + struct cam_req_mgr_req_tbl *tbl; + struct cam_req_mgr_apply *apply_data; + struct cam_req_mgr_tbl_slot *slot = NULL; + + if (!traverse_data->tbl || !traverse_data->apply_data) { + CAM_ERR(CAM_CRM, "NULL pointer %pK %pK", + traverse_data->tbl, traverse_data->apply_data); + traverse_data->result = 0; + return -EINVAL; + } + + tbl = traverse_data->tbl; + apply_data = traverse_data->apply_data; + slot = &tbl->slot[curr_idx]; + CAM_DBG(CAM_CRM, + "Enter pd %d idx %d state %d skip %d status %d skip_idx %d", + tbl->pd, curr_idx, tbl->slot[curr_idx].state, + tbl->skip_traverse, traverse_data->in_q->slot[curr_idx].status, + traverse_data->in_q->slot[curr_idx].skip_idx); + + if ((slot->inject_delay > 0) && + (traverse_data->self_link == true)) { + CAM_DBG(CAM_CRM, "Injecting Delay of one frame"); + apply_data[tbl->pd].req_id = -1; + slot->inject_delay--; + /* This pd table is not ready to proceed with asked idx */ + SET_FAILURE_BIT(traverse_data->result, tbl->pd); + return -EAGAIN; + } + + /* Check if req is ready or in skip mode or pd tbl is in skip mode */ + if (tbl->slot[curr_idx].state == CRM_REQ_STATE_READY || + traverse_data->in_q->slot[curr_idx].skip_idx == 1 || + tbl->skip_traverse > 0) { + if (tbl->next) { + __cam_req_mgr_dec_idx(&next_idx, tbl->pd_delta, + tbl->num_slots); + traverse_data->idx = next_idx; + traverse_data->tbl = tbl->next; + rc = __cam_req_mgr_traverse(traverse_data); + } + if (rc >= 0) { + SET_SUCCESS_BIT(traverse_data->result, tbl->pd); + + if (traverse_data->validate_only == false) { + apply_data[tbl->pd].pd = tbl->pd; + apply_data[tbl->pd].req_id = + CRM_GET_REQ_ID( + traverse_data->in_q, curr_idx); + apply_data[tbl->pd].idx = curr_idx; + + CAM_DBG(CAM_CRM, "req_id: %d with pd of %d", + apply_data[tbl->pd].req_id, + apply_data[tbl->pd].pd); + /* + * If traverse is successful decrement + * traverse skip + */ + if (tbl->skip_traverse > 0) { + apply_data[tbl->pd].req_id = -1; + tbl->skip_traverse--; + } + } + } else { + /* linked pd table is not ready for this traverse yet */ + return rc; + } + } else { + /* This pd table is not ready to proceed with asked idx */ + SET_FAILURE_BIT(traverse_data->result, tbl->pd); + return -EAGAIN; + } + return 0; +} + +/** + * __cam_req_mgr_in_q_skip_idx() + * + * @brief : Decrement val passed by step size and rollover after max_val + * @in_q : input queue pointer + * @idx : Sets skip_idx bit of the particular slot to true so when traverse + * happens for this idx, no req will be submitted for devices + * handling this idx. + * + */ +static void __cam_req_mgr_in_q_skip_idx(struct cam_req_mgr_req_queue *in_q, + int32_t idx) +{ + in_q->slot[idx].req_id = -1; + in_q->slot[idx].skip_idx = 1; + in_q->slot[idx].status = CRM_SLOT_STATUS_REQ_ADDED; + CAM_DBG(CAM_CRM, "SET IDX SKIP on slot= %d", idx); +} + +/** + * __cam_req_mgr_tbl_set_id() + * + * @brief : Set unique id to table + * @tbl : pipeline based table which requires new id + * @req : pointer to request data wihch contains num_tables counter + * + */ +static void __cam_req_mgr_tbl_set_id(struct cam_req_mgr_req_tbl *tbl, + struct cam_req_mgr_req_data *req) +{ + if (!tbl) + return; + do { + tbl->id = req->num_tbl++; + CAM_DBG(CAM_CRM, "%d: pd %d skip_traverse %d delta %d", + tbl->id, tbl->pd, tbl->skip_traverse, + tbl->pd_delta); + tbl = tbl->next; + } while (tbl != NULL); +} + +/** + * __cam_req_mgr_tbl_set_all_skip_cnt() + * + * @brief : Each pd table sets skip value based on delta between itself and + * max pd value. During initial streamon or bubble case this is + * used. That way each pd table skips required num of traverse and + * align themselve with req mgr connected devs. + * @l_tbl : iterates through list of pd tables and sets skip traverse + * + */ +static void __cam_req_mgr_tbl_set_all_skip_cnt( + struct cam_req_mgr_req_tbl **l_tbl) +{ + struct cam_req_mgr_req_tbl *tbl = *l_tbl; + int32_t max_pd; + + if (!tbl) + return; + + max_pd = tbl->pd; + do { + tbl->skip_traverse = max_pd - tbl->pd; + CAM_DBG(CAM_CRM, "%d: pd %d skip_traverse %d delta %d", + tbl->id, tbl->pd, tbl->skip_traverse, + tbl->pd_delta); + tbl = tbl->next; + } while (tbl != NULL); +} + +/** + * __cam_req_mgr_reset_req_slot() + * + * @brief : reset specified idx/slot in input queue as well as all pd tables + * @link : link pointer + * @idx : slot index which will be reset + * + */ +static void __cam_req_mgr_reset_req_slot(struct cam_req_mgr_core_link *link, + int32_t idx) +{ + struct cam_req_mgr_slot *slot; + struct cam_req_mgr_req_tbl *tbl = link->req.l_tbl; + struct cam_req_mgr_req_queue *in_q = link->req.in_q; + + slot = &in_q->slot[idx]; + CAM_DBG(CAM_CRM, "RESET: idx: %d: slot->status %d", idx, slot->status); + + /* Check if CSL has already pushed new request*/ + if (slot->status == CRM_SLOT_STATUS_REQ_ADDED) + return; + + /* Reset input queue slot */ + slot->req_id = -1; + slot->skip_idx = 0; + slot->recover = 0; + slot->sync_mode = CAM_REQ_MGR_SYNC_MODE_NO_SYNC; + slot->status = CRM_SLOT_STATUS_NO_REQ; + + /* Reset all pd table slot */ + while (tbl != NULL) { + CAM_DBG(CAM_CRM, "pd: %d: idx %d state %d", + tbl->pd, idx, tbl->slot[idx].state); + tbl->slot[idx].req_ready_map = 0; + tbl->slot[idx].state = CRM_REQ_STATE_EMPTY; + tbl = tbl->next; + } +} + +/** + * __cam_req_mgr_check_next_req_slot() + * + * @brief : While streaming if input queue does not contain any pending + * request, req mgr still needs to submit pending request ids to + * devices with lower pipeline delay value. + * @in_q : Pointer to input queue where req mgr wil peep into + * + */ +static void __cam_req_mgr_check_next_req_slot( + struct cam_req_mgr_req_queue *in_q) +{ + int32_t idx = in_q->rd_idx; + struct cam_req_mgr_slot *slot; + + __cam_req_mgr_inc_idx(&idx, 1, in_q->num_slots); + slot = &in_q->slot[idx]; + + CAM_DBG(CAM_CRM, "idx: %d: slot->status %d", idx, slot->status); + + /* Check if there is new req from CSL, if not complete req */ + if (slot->status == CRM_SLOT_STATUS_NO_REQ) { + __cam_req_mgr_in_q_skip_idx(in_q, idx); + if (in_q->wr_idx != idx) + CAM_WARN(CAM_CRM, + "CHECK here wr %d, rd %d", in_q->wr_idx, idx); + __cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots); + } +} + +/** + * __cam_req_mgr_send_req() + * + * @brief : send request id to be applied to each device connected on link + * @link : pointer to link whose input queue and req tbl are + * traversed through + * @in_q : pointer to input request queue + * + * @return : 0 for success, negative for failure + * + */ +static int __cam_req_mgr_send_req(struct cam_req_mgr_core_link *link, + struct cam_req_mgr_req_queue *in_q, uint32_t trigger) +{ + int rc = 0, pd, i, idx; + struct cam_req_mgr_connected_device *dev = NULL; + struct cam_req_mgr_apply_request apply_req; + struct cam_req_mgr_link_evt_data evt_data; + + apply_req.link_hdl = link->link_hdl; + apply_req.report_if_bubble = 0; + + for (i = 0; i < link->num_devs; i++) { + dev = &link->l_dev[i]; + if (dev) { + pd = dev->dev_info.p_delay; + if (pd >= CAM_PIPELINE_DELAY_MAX) { + CAM_WARN(CAM_CRM, "pd %d greater than max", + pd); + continue; + } + if (link->req.apply_data[pd].skip_idx || + link->req.apply_data[pd].req_id < 0) { + CAM_DBG(CAM_CRM, "skip %d req_id %lld", + link->req.apply_data[pd].skip_idx, + link->req.apply_data[pd].req_id); + continue; + } + if (!(dev->dev_info.trigger & trigger)) + continue; + + apply_req.dev_hdl = dev->dev_hdl; + apply_req.request_id = + link->req.apply_data[pd].req_id; + idx = link->req.apply_data[pd].idx; + apply_req.report_if_bubble = + in_q->slot[idx].recover; + + trace_cam_req_mgr_apply_request(link, &apply_req, dev); + + apply_req.trigger_point = trigger; + CAM_DBG(CAM_CRM, "SEND: link_hdl: %x pd %d req_id %lld", + link->link_hdl, pd, apply_req.request_id); + if (dev->ops && dev->ops->apply_req) { + rc = dev->ops->apply_req(&apply_req); + if (rc < 0) + break; + } + } + } + if (rc < 0) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "APPLY FAILED pd %d req_id %lld", + dev->dev_info.p_delay, apply_req.request_id); + /* Apply req failed notify already applied devs */ + for (; i >= 0; i--) { + dev = &link->l_dev[i]; + evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_ERR; + evt_data.link_hdl = link->link_hdl; + evt_data.req_id = apply_req.request_id; + evt_data.u.error = CRM_KMD_ERR_BUBBLE; + if (dev->ops && dev->ops->process_evt) + dev->ops->process_evt(&evt_data); + } + } + return rc; +} + +/** + * __cam_req_mgr_check_link_is_ready() + * + * @brief : traverse through all request tables and see if all devices are + * ready to apply request settings. + * @link : pointer to link whose input queue and req tbl are + * traversed through + * @idx : index within input request queue + * @validate_only : Whether to validate only and/or update settings + * @self_link : To indicate whether the validation is for the given link or + * other sync link + * + * @return : 0 for success, negative for failure + * + */ +static int __cam_req_mgr_check_link_is_ready(struct cam_req_mgr_core_link *link, + int32_t idx, bool validate_only, bool self_link) +{ + int rc; + struct cam_req_mgr_traverse traverse_data; + struct cam_req_mgr_req_queue *in_q; + struct cam_req_mgr_apply *apply_data; + + in_q = link->req.in_q; + + apply_data = link->req.apply_data; + + if (validate_only == false) { + memset(apply_data, 0, + sizeof(struct cam_req_mgr_apply) * CAM_PIPELINE_DELAY_MAX); + } + + traverse_data.apply_data = apply_data; + traverse_data.idx = idx; + traverse_data.tbl = link->req.l_tbl; + traverse_data.in_q = in_q; + traverse_data.result = 0; + traverse_data.validate_only = validate_only; + traverse_data.self_link = self_link; + /* + * Traverse through all pd tables, if result is success, + * apply the settings + */ + + rc = __cam_req_mgr_traverse(&traverse_data); + CAM_DBG(CAM_CRM, + "SOF: idx %d self_link %d validate %d result %x pd_mask %x rc %d", + idx, traverse_data.self_link, traverse_data.validate_only, + traverse_data.result, link->pd_mask, rc); + + if (!rc && traverse_data.result == link->pd_mask) { + CAM_DBG(CAM_CRM, + "APPLY: link_hdl= %x idx= %d, req_id= %lld :%lld :%lld", + link->link_hdl, idx, + apply_data[2].req_id, + apply_data[1].req_id, + apply_data[0].req_id); + } else + rc = -EAGAIN; + + return rc; +} + +/** + * __cam_req_mgr_find_slot_for_req() + * + * @brief : Find idx from input queue at which req id is enqueued + * @in_q : input request queue pointer + * @req_id : request id which needs to be searched in input queue + * + * @return : slot index where passed request id is stored, -1 for failure + * + */ +static int32_t __cam_req_mgr_find_slot_for_req( + struct cam_req_mgr_req_queue *in_q, int64_t req_id) +{ + int32_t idx, i; + struct cam_req_mgr_slot *slot; + + idx = in_q->rd_idx; + for (i = 0; i < in_q->num_slots; i++) { + slot = &in_q->slot[idx]; + if (slot->req_id == req_id) { + CAM_DBG(CAM_CRM, + "req: %lld found at idx: %d status: %d sync_mode: %d", + req_id, idx, slot->status, slot->sync_mode); + break; + } + __cam_req_mgr_dec_idx(&idx, 1, in_q->num_slots); + } + if (i >= in_q->num_slots) + idx = -1; + + return idx; +} + +/** + * __cam_req_mgr_reset_sof_cnt() + * + * @brief : the sof_count for both the links are reset + * @link : pointer to link whose input queue and req tbl are + * traversed through + * + */ +static void __cam_req_mgr_reset_sof_cnt( + struct cam_req_mgr_core_link *link) +{ + link->sof_counter = -1; + link->sync_link->sof_counter = -1; + link->frame_skip_flag = false; + + CAM_DBG(CAM_CRM, + "link_hdl %x self_counter %lld other_counter %lld frame_skip_lag %d", + link->link_hdl, link->sof_counter, + link->sync_link->sof_counter, link->frame_skip_flag); +} + +/** + * __cam_req_mgr_sof_cnt_initialize() + * + * @brief : when the sof count is intially -1 it increments count + * and computes the sync_self_ref for this link + * the count needs to be wrapped back starting from 0 + * @link : pointer to link whose input queue and req tbl are + * traversed through + * + */ +static void __cam_req_mgr_sof_cnt_initialize( + struct cam_req_mgr_core_link *link) +{ + link->sof_counter++; + link->sync_self_ref = link->sof_counter - + link->sync_link->sof_counter; + + CAM_DBG(CAM_CRM, + "link_hdl %x self_counter %lld other_counter %lld", + link->link_hdl, link->sof_counter, + link->sync_link->sof_counter); +} + +/** + * __cam_req_mgr_wrap_sof_cnt() + * + * @brief : once the sof count reaches a predefined maximum + * the count needs to be wrapped back starting from 0 + * @link : pointer to link whose input queue and req tbl are + * traversed through + * + */ +static void __cam_req_mgr_wrap_sof_cnt( + struct cam_req_mgr_core_link *link) +{ + link->sof_counter = (MAX_SYNC_COUNT - + (link->sync_link->sof_counter)); + link->sync_link->sof_counter = 0; + + CAM_DBG(CAM_CRM, + "link_hdl %x self_counter %lld sync_link_hdl %x other_counter %lld", + link->link_hdl, link->sof_counter, + link->sync_link->link_hdl, link->sync_link->sof_counter); +} + +/** + * __cam_req_mgr_validate_sof_cnt() + * + * @brief : validates sof count difference for a given link + * @link : pointer to link whose input queue and req tbl are + * traversed through + * @sync_link : pointer to the sync link + * @return : 0 for success, negative for failure + * + */ +static int __cam_req_mgr_validate_sof_cnt( + struct cam_req_mgr_core_link *link, + struct cam_req_mgr_core_link *sync_link) +{ + int64_t sync_diff = 0; + int rc = 0; + + if (link->sof_counter == MAX_SYNC_COUNT) + __cam_req_mgr_wrap_sof_cnt(link); + + sync_diff = link->sof_counter - sync_link->sof_counter; + + CAM_DBG(CAM_CRM, + "link[%x] self_counter=%lld other_counter=%lld diff=%lld sync_self_ref=%lld", + link->link_hdl, link->sof_counter, + sync_link->sof_counter, sync_diff, link->sync_self_ref); + + if (sync_diff > SYNC_LINK_SOF_CNT_MAX_LMT) { + link->sync_link->frame_skip_flag = true; + CAM_WARN(CAM_CRM, + "Detected anomaly, skip link_hdl %x self_counter=%lld other_counter=%lld sync_self_ref=%lld", + link->link_hdl, link->sof_counter, + sync_link->sof_counter, link->sync_self_ref); + rc = -EPERM; + } + + return rc; +} + + +/** + * __cam_req_mgr_process_sync_req() + * + * @brief : processes requests during sync mode + * @link : pointer to link whose input queue and req tbl are + * traversed through + * @slot : pointer to the current slot being processed + * @return : 0 for success, negative for failure + * + */ +static int __cam_req_mgr_process_sync_req( + struct cam_req_mgr_core_link *link, + struct cam_req_mgr_slot *slot) +{ + struct cam_req_mgr_core_link *sync_link = NULL; + int64_t req_id = 0; + int sync_slot_idx = 0, rc = 0; + + if (!link->sync_link) { + CAM_ERR(CAM_CRM, "Sync link null"); + return -EINVAL; + } + + sync_link = link->sync_link; + req_id = slot->req_id; + + CAM_DBG(CAM_CRM, + "link_hdl %x req %lld sync_self_ref %lld sof_counter %lld frame_skip_flag %d sync_link_self_ref %lld", + link->link_hdl, req_id, link->sync_self_ref, link->sof_counter, + link->frame_skip_flag, link->sync_link->sync_self_ref); + + if (sync_link->sync_link_sof_skip) { + CAM_DBG(CAM_CRM, + "No req applied on corresponding SOF on sync link: %x", + sync_link->link_hdl); + sync_link->sync_link_sof_skip = false; + return -EINVAL; + } + + if (link->sof_counter == -1) { + __cam_req_mgr_sof_cnt_initialize(link); + } else if ((link->frame_skip_flag) && + (sync_link->sync_self_ref != -1)) { + CAM_DBG(CAM_CRM, "Link[%x] Req[%lld] Resetting values ", + link->link_hdl, req_id); + __cam_req_mgr_reset_sof_cnt(link); + __cam_req_mgr_sof_cnt_initialize(link); + } else { + link->sof_counter++; + } + + rc = __cam_req_mgr_check_link_is_ready(link, slot->idx, true, true); + if (rc) { + CAM_DBG(CAM_CRM, + "Req: %lld [My link]not available link: %x, rc=%d", + req_id, link->link_hdl, rc); + link->sync_link_sof_skip = true; + goto failure; + } + + sync_slot_idx = __cam_req_mgr_find_slot_for_req( + sync_link->req.in_q, req_id); + + if (sync_slot_idx != -1) { + rc = __cam_req_mgr_check_link_is_ready( + sync_link, sync_slot_idx, true, false); + CAM_DBG(CAM_CRM, "sync_slot_idx=%d, status=%d, rc=%d", + sync_slot_idx, + sync_link->req.in_q->slot[sync_slot_idx].status, + rc); + } else { + CAM_DBG(CAM_CRM, "sync_slot_idx=%d, rc=%d", + sync_slot_idx, rc); + } + + if ((sync_slot_idx != -1) && + ((sync_link->req.in_q->slot[sync_slot_idx].status == + CRM_SLOT_STATUS_REQ_APPLIED) || (rc == 0))) { + rc = __cam_req_mgr_validate_sof_cnt(link, sync_link); + if (rc) { + CAM_DBG(CAM_CRM, + "Req: %lld validate failed: %x", + req_id, sync_link->link_hdl); + goto failure; + } + + CAM_DBG(CAM_CRM, + "Req: %lld ready to apply on link: %x [validation successful]", + req_id, link->link_hdl); + /* + * At this point all validation is successfully done + * and we can proceed to apply the given request. + * Ideally the next call should return success. + */ + rc = __cam_req_mgr_check_link_is_ready(link, + slot->idx, false, true); + + if (rc) { + CAM_WARN(CAM_CRM, "Unexpected return value rc: %d", rc); + } + } else { + CAM_DBG(CAM_CRM, + "Req: %lld [Other link] not ready to apply on link: %x", + req_id, sync_link->link_hdl); + rc = -EPERM; + link->sync_link_sof_skip = true; + goto failure; + } + + return rc; + +failure: + link->sof_counter--; + return rc; +} + +/** + * __cam_req_mgr_process_req() + * + * @brief : processes read index in request queue and traverse through table + * @link : pointer to link whose input queue and req tbl are + * traversed through + * + * @return : 0 for success, negative for failure + * + */ +static int __cam_req_mgr_process_req(struct cam_req_mgr_core_link *link, + uint32_t trigger) +{ + int rc = 0, idx; + struct cam_req_mgr_slot *slot = NULL; + struct cam_req_mgr_req_queue *in_q; + struct cam_req_mgr_core_session *session; + + in_q = link->req.in_q; + session = (struct cam_req_mgr_core_session *)link->parent; + mutex_lock(&session->lock); + /* + * Check if new read index, + * - if in pending state, traverse again to complete + * transaction of this read index. + * - if in applied_state, somthign wrong. + * - if in no_req state, no new req + */ + CAM_DBG(CAM_CRM, "SOF Req[%lld] idx %d req_status %d link_hdl %x", + in_q->slot[in_q->rd_idx].req_id, in_q->rd_idx, + in_q->slot[in_q->rd_idx].status, link->link_hdl); + + slot = &in_q->slot[in_q->rd_idx]; + if (slot->status == CRM_SLOT_STATUS_NO_REQ) { + CAM_DBG(CAM_CRM, "No Pending req"); + rc = 0; + goto error; + } + + if ((trigger != CAM_TRIGGER_POINT_SOF) && + (trigger != CAM_TRIGGER_POINT_EOF)) + goto error; + + if ((trigger == CAM_TRIGGER_POINT_EOF) && + (!(link->trigger_mask & CAM_TRIGGER_POINT_SOF))) { + CAM_DBG(CAM_CRM, "Applying for last SOF fails"); + rc = -EINVAL; + goto error; + } + + if (trigger == CAM_TRIGGER_POINT_SOF) { + if (link->trigger_mask) { + CAM_ERR_RATE_LIMIT(CAM_CRM, + "Applying for last EOF fails"); + rc = -EINVAL; + goto error; + } + + if (slot->sync_mode == CAM_REQ_MGR_SYNC_MODE_SYNC) + rc = __cam_req_mgr_process_sync_req(link, slot); + else + rc = __cam_req_mgr_check_link_is_ready(link, + slot->idx, false, true); + + if (rc < 0) { + /* + * If traverse result is not success, then some devices + * are not ready with packet for the asked request id, + * hence try again in next sof + */ + slot->status = CRM_SLOT_STATUS_REQ_PENDING; + spin_lock_bh(&link->link_state_spin_lock); + if (link->state == CAM_CRM_LINK_STATE_ERR) { + /* + * During error recovery all tables should be + * ready, don't expect to enter here. + * @TODO: gracefully handle if recovery fails. + */ + CAM_ERR_RATE_LIMIT(CAM_CRM, + "FATAL recovery cant finish idx %d status %d", + in_q->rd_idx, + in_q->slot[in_q->rd_idx].status); + rc = -EPERM; + } + spin_unlock_bh(&link->link_state_spin_lock); + goto error; + } + } + + rc = __cam_req_mgr_send_req(link, link->req.in_q, trigger); + if (rc < 0) { + /* Apply req failed retry at next sof */ + slot->status = CRM_SLOT_STATUS_REQ_PENDING; + } else { + link->trigger_mask |= trigger; + + CAM_DBG(CAM_CRM, "Applied req[%lld] on link[%x] success", + slot->req_id, link->link_hdl); + spin_lock_bh(&link->link_state_spin_lock); + if (link->state == CAM_CRM_LINK_STATE_ERR) { + CAM_WARN(CAM_CRM, "Err recovery done idx %d", + in_q->rd_idx); + link->state = CAM_CRM_LINK_STATE_READY; + } + spin_unlock_bh(&link->link_state_spin_lock); + + if (link->sync_link_sof_skip) + link->sync_link_sof_skip = false; + + if (link->trigger_mask == link->subscribe_event) { + slot->status = CRM_SLOT_STATUS_REQ_APPLIED; + link->trigger_mask = 0; + CAM_DBG(CAM_CRM, "req %d is applied on link %x", + slot->req_id, + link->link_hdl); + idx = in_q->rd_idx; + __cam_req_mgr_dec_idx( + &idx, link->max_delay + 1, + in_q->num_slots); + __cam_req_mgr_reset_req_slot(link, idx); + } + } + + mutex_unlock(&session->lock); + return rc; +error: + mutex_unlock(&session->lock); + return rc; +} + +/** + * __cam_req_mgr_add_tbl_to_link() + * + * @brief : Add table to list under link sorted by pd decremeting order + * @l_tbl : list of pipeline delay tables. + * @new_tbl : new tbl which will be appended to above list as per its pd value + * + */ +static void __cam_req_mgr_add_tbl_to_link(struct cam_req_mgr_req_tbl **l_tbl, + struct cam_req_mgr_req_tbl *new_tbl) +{ + struct cam_req_mgr_req_tbl *tbl; + + if (!(*l_tbl) || (*l_tbl)->pd < new_tbl->pd) { + new_tbl->next = *l_tbl; + if (*l_tbl) { + new_tbl->pd_delta = + new_tbl->pd - (*l_tbl)->pd; + } + *l_tbl = new_tbl; + } else { + tbl = *l_tbl; + + /* Reach existing tbl which has less pd value */ + while (tbl->next != NULL && + new_tbl->pd < tbl->next->pd) { + tbl = tbl->next; + } + if (tbl->next != NULL) { + new_tbl->pd_delta = + new_tbl->pd - tbl->next->pd; + } else { + /* This is last table in linked list*/ + new_tbl->pd_delta = 0; + } + new_tbl->next = tbl->next; + tbl->next = new_tbl; + tbl->pd_delta = tbl->pd - new_tbl->pd; + } + CAM_DBG(CAM_CRM, "added pd %d tbl to link delta %d", new_tbl->pd, + new_tbl->pd_delta); +} + +/** + * __cam_req_mgr_create_pd_tbl() + * + * @brief : Creates new request table for new delay value + * @delay : New pd table allocated will have this delay value + * + * @return : pointer to newly allocated table, NULL for failure + * + */ +static struct cam_req_mgr_req_tbl *__cam_req_mgr_create_pd_tbl(int32_t delay) +{ + struct cam_req_mgr_req_tbl *tbl = + kzalloc(sizeof(struct cam_req_mgr_req_tbl), GFP_KERNEL); + if (tbl != NULL) { + tbl->num_slots = MAX_REQ_SLOTS; + CAM_DBG(CAM_CRM, "pd= %d slots= %d", delay, tbl->num_slots); + } + + return tbl; +} + +/** + * __cam_req_mgr_destroy_all_tbl() + * + * @brief : This func will destroy all pipeline delay based req table structs + * @l_tbl : pointer to first table in list and it has max pd . + * + */ +static void __cam_req_mgr_destroy_all_tbl(struct cam_req_mgr_req_tbl **l_tbl) +{ + struct cam_req_mgr_req_tbl *tbl = *l_tbl, *temp; + + CAM_DBG(CAM_CRM, "*l_tbl %pK", tbl); + while (tbl != NULL) { + temp = tbl->next; + kfree(tbl); + tbl = temp; + } + *l_tbl = NULL; +} + +/** + * __cam_req_mgr_setup_in_q() + * + * @brief : Initialize req table data + * @req : request data pointer + * + * @return: 0 for success, negative for failure + * + */ +static int __cam_req_mgr_setup_in_q(struct cam_req_mgr_req_data *req) +{ + int i; + struct cam_req_mgr_req_queue *in_q = req->in_q; + + if (!in_q) { + CAM_ERR(CAM_CRM, "NULL in_q"); + return -EINVAL; + } + + mutex_lock(&req->lock); + in_q->num_slots = MAX_REQ_SLOTS; + + for (i = 0; i < in_q->num_slots; i++) { + in_q->slot[i].idx = i; + in_q->slot[i].req_id = -1; + in_q->slot[i].skip_idx = 0; + in_q->slot[i].status = CRM_SLOT_STATUS_NO_REQ; + } + + in_q->wr_idx = 0; + in_q->rd_idx = 0; + mutex_unlock(&req->lock); + + return 0; +} + +/** + * __cam_req_mgr_reset_req_tbl() + * + * @brief : Initialize req table data + * @req : request queue pointer + * + * @return: 0 for success, negative for failure + * + */ +static int __cam_req_mgr_reset_in_q(struct cam_req_mgr_req_data *req) +{ + struct cam_req_mgr_req_queue *in_q = req->in_q; + + if (!in_q) { + CAM_ERR(CAM_CRM, "NULL in_q"); + return -EINVAL; + } + + mutex_lock(&req->lock); + memset(in_q->slot, 0, + sizeof(struct cam_req_mgr_slot) * in_q->num_slots); + in_q->num_slots = 0; + + in_q->wr_idx = 0; + in_q->rd_idx = 0; + mutex_unlock(&req->lock); + + return 0; +} + +/** + * __cam_req_mgr_sof_freeze() + * + * @brief : Apoptosis - Handles case when connected devices are not responding + * @data : timer pointer + * + */ +static void __cam_req_mgr_sof_freeze(struct timer_list *timer_data) +{ + struct cam_req_mgr_timer *timer = + container_of(timer_data, struct cam_req_mgr_timer, sys_timer); + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_core_session *session = NULL; + struct cam_req_mgr_message msg; + + if (!timer) { + CAM_ERR(CAM_CRM, "NULL timer"); + return; + } + link = (struct cam_req_mgr_core_link *)timer->parent; + session = (struct cam_req_mgr_core_session *)link->parent; + + CAM_ERR(CAM_CRM, "SOF freeze for session %d link 0x%x", + session->session_hdl, link->link_hdl); + + memset(&msg, 0, sizeof(msg)); + + msg.session_hdl = session->session_hdl; + msg.u.err_msg.error_type = CAM_REQ_MGR_ERROR_TYPE_DEVICE; + msg.u.err_msg.request_id = 0; + msg.u.err_msg.link_hdl = link->link_hdl; + + + if (cam_req_mgr_notify_message(&msg, + V4L_EVENT_CAM_REQ_MGR_ERROR, V4L_EVENT_CAM_REQ_MGR_EVENT)) + CAM_ERR(CAM_CRM, + "Error notifying SOF freeze for session %d link 0x%x", + session->session_hdl, link->link_hdl); +} + +/** + * __cam_req_mgr_create_subdevs() + * + * @brief : Create new crm subdev to link with realtime devices + * @l_dev : list of subdevs internal to crm + * @num_dev : num of subdevs to be created for link + * + * @return : pointer to allocated list of devices + */ +static int __cam_req_mgr_create_subdevs( + struct cam_req_mgr_connected_device **l_dev, int32_t num_dev) +{ + int rc = 0; + *l_dev = (struct cam_req_mgr_connected_device *) + kzalloc(sizeof(struct cam_req_mgr_connected_device) * num_dev, + GFP_KERNEL); + if (!*l_dev) + rc = -ENOMEM; + + return rc; +} + +/** + * __cam_req_mgr_destroy_subdev() + * + * @brief : Cleans up the subdevs allocated by crm for link + * @l_device : pointer to list of subdevs crm created + * + */ +static void __cam_req_mgr_destroy_subdev( + struct cam_req_mgr_connected_device **l_device) +{ + kfree(*l_device); + *l_device = NULL; +} + +/** + * __cam_req_mgr_destroy_link_info() + * + * @brief : Cleans up the mem allocated while linking + * @link : pointer to link, mem associated with this link is freed + * + * @return : returns if unlink for any device was success or failure + */ +static int __cam_req_mgr_destroy_link_info(struct cam_req_mgr_core_link *link) +{ + int32_t i = 0; + struct cam_req_mgr_connected_device *dev; + struct cam_req_mgr_core_dev_link_setup link_data; + int rc = 0; + + link_data.link_enable = 0; + link_data.link_hdl = link->link_hdl; + link_data.crm_cb = NULL; + link_data.subscribe_event = 0; + + /* Using device ops unlink devices */ + for (i = 0; i < link->num_devs; i++) { + dev = &link->l_dev[i]; + if (dev != NULL) { + link_data.dev_hdl = dev->dev_hdl; + if (dev->ops && dev->ops->link_setup) { + rc = dev->ops->link_setup(&link_data); + if (rc) + CAM_ERR(CAM_CRM, + "Unlink failed dev_hdl %d", + dev->dev_hdl); + } + dev->dev_hdl = 0; + dev->parent = NULL; + dev->ops = NULL; + } + } + __cam_req_mgr_destroy_all_tbl(&link->req.l_tbl); + __cam_req_mgr_reset_in_q(&link->req); + link->req.num_tbl = 0; + mutex_destroy(&link->req.lock); + + link->pd_mask = 0; + link->num_devs = 0; + link->max_delay = 0; + + return rc; +} + +/** + * __cam_req_mgr_reserve_link() + * + * @brief: Reserves one link data struct within session + * @session: session identifier + * + * @return: pointer to link reserved + * + */ +static struct cam_req_mgr_core_link *__cam_req_mgr_reserve_link( + struct cam_req_mgr_core_session *session) +{ + struct cam_req_mgr_core_link *link; + struct cam_req_mgr_req_queue *in_q; + int i; + + if (!session || !g_crm_core_dev) { + CAM_ERR(CAM_CRM, "NULL session/core_dev ptr"); + return NULL; + } + + if (session->num_links >= MAX_LINKS_PER_SESSION) { + CAM_ERR(CAM_CRM, "Reached max links %d per session limit %d", + session->num_links, MAX_LINKS_PER_SESSION); + return NULL; + } + + link = (struct cam_req_mgr_core_link *) + kzalloc(sizeof(struct cam_req_mgr_core_link), GFP_KERNEL); + if (!link) { + CAM_ERR(CAM_CRM, "failed to create link, no mem"); + return NULL; + } + in_q = (struct cam_req_mgr_req_queue *) + kzalloc(sizeof(struct cam_req_mgr_req_queue), GFP_KERNEL); + if (!in_q) { + CAM_ERR(CAM_CRM, "failed to create input queue, no mem"); + kfree(link); + return NULL; + } + mutex_init(&link->lock); + spin_lock_init(&link->link_state_spin_lock); + + mutex_lock(&link->lock); + link->state = CAM_CRM_LINK_STATE_AVAILABLE; + link->num_devs = 0; + link->max_delay = 0; + memset(in_q->slot, 0, + sizeof(struct cam_req_mgr_slot) * MAX_REQ_SLOTS); + link->req.in_q = in_q; + in_q->num_slots = 0; + link->state = CAM_CRM_LINK_STATE_IDLE; + link->parent = (void *)session; + link->sync_link = NULL; + mutex_unlock(&link->lock); + + mutex_lock(&session->lock); + /* Loop through and find a free index */ + for (i = 0; i < MAX_LINKS_PER_SESSION; i++) { + if (!session->links[i]) { + CAM_DBG(CAM_CRM, + "Free link index %d found, num_links=%d", + i, session->num_links); + session->links[i] = link; + break; + } + } + + if (i == MAX_LINKS_PER_SESSION) { + CAM_ERR(CAM_CRM, "Free link index not found"); + goto error; + } + + session->num_links++; + CAM_DBG(CAM_CRM, "Active session links (%d)", + session->num_links); + mutex_unlock(&session->lock); + + return link; +error: + mutex_unlock(&session->lock); + kfree(link); + kfree(in_q); + return NULL; +} + +/* + * __cam_req_mgr_free_link() + * + * @brief: Frees the link and its request queue + * + * @link: link identifier + * + */ +static void __cam_req_mgr_free_link(struct cam_req_mgr_core_link *link) +{ + kfree(link->req.in_q); + link->req.in_q = NULL; + kfree(link); +} + +/** + * __cam_req_mgr_unreserve_link() + * + * @brief : Removes the link data struct from the session and frees it + * @session: session identifier + * @link : link identifier + * + */ +static void __cam_req_mgr_unreserve_link( + struct cam_req_mgr_core_session *session, + struct cam_req_mgr_core_link *link) +{ + int i; + + if (!session || !link) { + CAM_ERR(CAM_CRM, "NULL session/link ptr %pK %pK", + session, link); + return; + } + + mutex_lock(&session->lock); + if (!session->num_links) { + CAM_WARN(CAM_CRM, "No active link or invalid state: hdl %x", + link->link_hdl); + mutex_unlock(&session->lock); + return; + } + + for (i = 0; i < MAX_LINKS_PER_SESSION; i++) { + if (session->links[i] == link) + session->links[i] = NULL; + } + + if ((session->sync_mode != CAM_REQ_MGR_SYNC_MODE_NO_SYNC) && + (link->sync_link)) { + /* + * make sure to unlink sync setup under the assumption + * of only having 2 links in a given session + */ + session->sync_mode = CAM_REQ_MGR_SYNC_MODE_NO_SYNC; + for (i = 0; i < MAX_LINKS_PER_SESSION; i++) { + if (session->links[i]) + session->links[i]->sync_link = NULL; + } + } + + session->num_links--; + CAM_DBG(CAM_CRM, "Active session links (%d)", session->num_links); + mutex_unlock(&session->lock); + __cam_req_mgr_free_link(link); +} + +/* Workqueue context processing section */ + +/** + * cam_req_mgr_process_send_req() + * + * @brief: This runs in workque thread context. Call core funcs to send + * apply request id to drivers. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +int cam_req_mgr_process_send_req(void *priv, void *data) +{ + int rc = 0; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_send_request *send_req = NULL; + struct cam_req_mgr_req_queue *in_q = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + link = (struct cam_req_mgr_core_link *)priv; + send_req = (struct cam_req_mgr_send_request *)data; + in_q = send_req->in_q; + + rc = __cam_req_mgr_send_req(link, in_q, CAM_TRIGGER_POINT_SOF); +end: + return rc; +} + +/** + * cam_req_mgr_process_flush_req() + * + * @brief: This runs in workque thread context. Call core funcs to check + * which requests need to be removed/cancelled. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +int cam_req_mgr_process_flush_req(void *priv, void *data) +{ + int rc = 0, i = 0, idx = -1; + struct cam_req_mgr_flush_info *flush_info = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_req_queue *in_q = NULL; + struct cam_req_mgr_slot *slot = NULL; + struct cam_req_mgr_connected_device *device = NULL; + struct cam_req_mgr_flush_request flush_req; + struct crm_task_payload *task_data = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + link = (struct cam_req_mgr_core_link *)priv; + task_data = (struct crm_task_payload *)data; + flush_info = (struct cam_req_mgr_flush_info *)&task_data->u; + CAM_DBG(CAM_CRM, "link_hdl %x req_id %lld type %d", + flush_info->link_hdl, + flush_info->req_id, + flush_info->flush_type); + + in_q = link->req.in_q; + + trace_cam_flush_req(link, flush_info); + + mutex_lock(&link->req.lock); + if (flush_info->flush_type == CAM_REQ_MGR_FLUSH_TYPE_ALL) { + for (i = 0; i < in_q->num_slots; i++) { + slot = &in_q->slot[i]; + slot->req_id = -1; + slot->sync_mode = CAM_REQ_MGR_SYNC_MODE_NO_SYNC; + slot->skip_idx = 1; + slot->status = CRM_SLOT_STATUS_NO_REQ; + } + in_q->wr_idx = 0; + in_q->rd_idx = 0; + } else if (flush_info->flush_type == + CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) { + idx = __cam_req_mgr_find_slot_for_req(in_q, flush_info->req_id); + if (idx < 0) { + CAM_ERR(CAM_CRM, "req_id %lld not found in input queue", + flush_info->req_id); + } else { + CAM_DBG(CAM_CRM, "req_id %lld found at idx %d", + flush_info->req_id, idx); + slot = &in_q->slot[idx]; + if (slot->status == CRM_SLOT_STATUS_REQ_PENDING || + slot->status == CRM_SLOT_STATUS_REQ_APPLIED) { + CAM_WARN(CAM_CRM, + "req_id %lld can not be cancelled", + flush_info->req_id); + mutex_unlock(&link->req.lock); + return -EINVAL; + } + __cam_req_mgr_in_q_skip_idx(in_q, idx); + } + } + + for (i = 0; i < link->num_devs; i++) { + device = &link->l_dev[i]; + flush_req.link_hdl = flush_info->link_hdl; + flush_req.dev_hdl = device->dev_hdl; + flush_req.req_id = flush_info->req_id; + flush_req.type = flush_info->flush_type; + /* @TODO: error return handling from drivers */ + if (device->ops && device->ops->flush_req) + rc = device->ops->flush_req(&flush_req); + } + complete(&link->workq_comp); + mutex_unlock(&link->req.lock); + +end: + return rc; +} + +/** + * cam_req_mgr_process_sched_req() + * + * @brief: This runs in workque thread context. Call core funcs to check + * which peding requests can be processed. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +int cam_req_mgr_process_sched_req(void *priv, void *data) +{ + int rc = 0; + struct cam_req_mgr_sched_request *sched_req = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_req_queue *in_q = NULL; + struct cam_req_mgr_slot *slot = NULL; + struct crm_task_payload *task_data = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + link = (struct cam_req_mgr_core_link *)priv; + task_data = (struct crm_task_payload *)data; + sched_req = (struct cam_req_mgr_sched_request *)&task_data->u; + in_q = link->req.in_q; + + CAM_DBG(CAM_CRM, "link_hdl %x req_id %lld at slot %d sync_mode %d", + sched_req->link_hdl, sched_req->req_id, + in_q->wr_idx, sched_req->sync_mode); + + mutex_lock(&link->req.lock); + slot = &in_q->slot[in_q->wr_idx]; + + if (slot->status != CRM_SLOT_STATUS_NO_REQ && + slot->status != CRM_SLOT_STATUS_REQ_APPLIED) + CAM_WARN(CAM_CRM, "in_q overwrite %d", slot->status); + + slot->status = CRM_SLOT_STATUS_REQ_ADDED; + slot->req_id = sched_req->req_id; + slot->sync_mode = sched_req->sync_mode; + slot->skip_idx = 0; + slot->recover = sched_req->bubble_enable; + __cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots); + mutex_unlock(&link->req.lock); + +end: + return rc; +} + +/** + * cam_req_mgr_process_add_req() + * + * @brief: This runs in workque thread context. Call core funcs to check + * which peding requests can be processed. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +int cam_req_mgr_process_add_req(void *priv, void *data) +{ + int rc = 0, i = 0, idx; + struct cam_req_mgr_add_request *add_req = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_connected_device *device = NULL; + struct cam_req_mgr_req_tbl *tbl = NULL; + struct cam_req_mgr_tbl_slot *slot = NULL; + struct crm_task_payload *task_data = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + + link = (struct cam_req_mgr_core_link *)priv; + task_data = (struct crm_task_payload *)data; + add_req = (struct cam_req_mgr_add_request *)&task_data->u; + + for (i = 0; i < link->num_devs; i++) { + device = &link->l_dev[i]; + if (device->dev_hdl == add_req->dev_hdl) { + tbl = device->pd_tbl; + break; + } + } + if (!tbl) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "dev_hdl not found %x, %x %x", + add_req->dev_hdl, + link->l_dev[0].dev_hdl, + link->l_dev[1].dev_hdl); + rc = -EINVAL; + goto end; + } + /* + * Go through request table and add + * request id to proper table + * 1. find req slot in in_q matching req_id.sent by dev + * 2. goto table of this device based on p_delay + * 3. mark req_ready_map with this dev_bit. + */ + + mutex_lock(&link->req.lock); + idx = __cam_req_mgr_find_slot_for_req(link->req.in_q, add_req->req_id); + if (idx < 0) { + CAM_ERR(CAM_CRM, "req %lld not found in in_q", add_req->req_id); + rc = -EBADSLT; + mutex_unlock(&link->req.lock); + goto end; + } + + slot = &tbl->slot[idx]; + if (add_req->skip_before_applying > slot->inject_delay) { + slot->inject_delay = add_req->skip_before_applying; + CAM_DBG(CAM_CRM, "Req_id %llu injecting delay %u", + add_req->req_id, add_req->skip_before_applying); + } + + if (slot->state != CRM_REQ_STATE_PENDING && + slot->state != CRM_REQ_STATE_EMPTY) { + CAM_WARN(CAM_CRM, "Unexpected state %d for slot %d map %x", + slot->state, idx, slot->req_ready_map); + } + + slot->state = CRM_REQ_STATE_PENDING; + slot->req_ready_map |= (1 << device->dev_bit); + + CAM_DBG(CAM_CRM, "idx %d dev_hdl %x req_id %lld pd %d ready_map %x", + idx, add_req->dev_hdl, add_req->req_id, tbl->pd, + slot->req_ready_map); + + trace_cam_req_mgr_add_req(link, idx, add_req, tbl, device); + + if (slot->req_ready_map == tbl->dev_mask) { + CAM_DBG(CAM_CRM, "idx %d req_id %lld pd %d SLOT READY", + idx, add_req->req_id, tbl->pd); + slot->state = CRM_REQ_STATE_READY; + } + mutex_unlock(&link->req.lock); + +end: + return rc; +} + +/** + * cam_req_mgr_process_error() + * + * @brief: This runs in workque thread context. bubble /err recovery. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +int cam_req_mgr_process_error(void *priv, void *data) +{ + int rc = 0, idx = -1, i; + struct cam_req_mgr_error_notify *err_info = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_req_queue *in_q = NULL; + struct cam_req_mgr_slot *slot = NULL; + struct cam_req_mgr_connected_device *device = NULL; + struct cam_req_mgr_link_evt_data evt_data; + struct crm_task_payload *task_data = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + link = (struct cam_req_mgr_core_link *)priv; + task_data = (struct crm_task_payload *)data; + err_info = (struct cam_req_mgr_error_notify *)&task_data->u; + CAM_DBG(CAM_CRM, "link_hdl %x req_id %lld error %d", + err_info->link_hdl, + err_info->req_id, + err_info->error); + + in_q = link->req.in_q; + + mutex_lock(&link->req.lock); + if (err_info->error == CRM_KMD_ERR_BUBBLE) { + idx = __cam_req_mgr_find_slot_for_req(in_q, err_info->req_id); + if (idx < 0) { + CAM_ERR_RATE_LIMIT(CAM_CRM, + "req_id %lld not found in input queue", + err_info->req_id); + } else { + CAM_DBG(CAM_CRM, "req_id %lld found at idx %d", + err_info->req_id, idx); + slot = &in_q->slot[idx]; + if (!slot->recover) { + CAM_WARN(CAM_CRM, + "err recovery disabled req_id %lld", + err_info->req_id); + mutex_unlock(&link->req.lock); + return 0; + } else if (slot->status != CRM_SLOT_STATUS_REQ_PENDING + && slot->status != CRM_SLOT_STATUS_REQ_APPLIED) { + CAM_WARN(CAM_CRM, + "req_id %lld can not be recovered %d", + err_info->req_id, slot->status); + mutex_unlock(&link->req.lock); + return -EINVAL; + } + /* Notify all devices in the link about error */ + for (i = 0; i < link->num_devs; i++) { + device = &link->l_dev[i]; + if (device != NULL) { + evt_data.dev_hdl = device->dev_hdl; + evt_data.evt_type = + CAM_REQ_MGR_LINK_EVT_ERR; + evt_data.link_hdl = link->link_hdl; + evt_data.req_id = err_info->req_id; + evt_data.u.error = err_info->error; + if (device->ops && + device->ops->process_evt) + rc = device->ops-> + process_evt(&evt_data); + } + } + /* Bring processing pointer to bubbled req id */ + __cam_req_mgr_tbl_set_all_skip_cnt(&link->req.l_tbl); + in_q->rd_idx = idx; + in_q->slot[idx].status = CRM_SLOT_STATUS_REQ_ADDED; + spin_lock_bh(&link->link_state_spin_lock); + link->state = CAM_CRM_LINK_STATE_ERR; + spin_unlock_bh(&link->link_state_spin_lock); + } + } + mutex_unlock(&link->req.lock); + +end: + return rc; +} + +/** + * cam_req_mgr_process_trigger() + * + * @brief: This runs in workque thread context. Call core funcs to check + * which peding requests can be processed. + * @priv : link information. + * @data : contains information about frame_id, link etc. + * + * @return: 0 on success. + */ +static int cam_req_mgr_process_trigger(void *priv, void *data) +{ + int rc = 0; + struct cam_req_mgr_trigger_notify *trigger_data = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_req_queue *in_q = NULL; + struct crm_task_payload *task_data = NULL; + + if (!data || !priv) { + CAM_ERR(CAM_CRM, "input args NULL %pK %pK", data, priv); + rc = -EINVAL; + goto end; + } + link = (struct cam_req_mgr_core_link *)priv; + task_data = (struct crm_task_payload *)data; + trigger_data = (struct cam_req_mgr_trigger_notify *)&task_data->u; + + CAM_DBG(CAM_CRM, "link_hdl %x frame_id %lld, trigger %x\n", + trigger_data->link_hdl, + trigger_data->frame_id, + trigger_data->trigger); + + in_q = link->req.in_q; + + mutex_lock(&link->req.lock); + /* + * Check if current read index is in applied state, if yes make it free + * and increment read index to next slot. + */ + CAM_DBG(CAM_CRM, "link_hdl %x curent idx %d req_status %d", + link->link_hdl, in_q->rd_idx, in_q->slot[in_q->rd_idx].status); + + spin_lock_bh(&link->link_state_spin_lock); + if (link->state == CAM_CRM_LINK_STATE_ERR) + CAM_WARN(CAM_CRM, "Error recovery idx %d status %d", + in_q->rd_idx, + in_q->slot[in_q->rd_idx].status); + + spin_unlock_bh(&link->link_state_spin_lock); + + if (in_q->slot[in_q->rd_idx].status == CRM_SLOT_STATUS_REQ_APPLIED) { + /* + * Do NOT reset req q slot data here, it can not be done + * here because we need to preserve the data to handle bubble. + * + * Check if any new req is pending in slot, if not finish the + * lower pipeline delay device with available req ids. + */ + CAM_DBG(CAM_CRM, "link[%x] Req[%lld] invalidating slot", + link->link_hdl, in_q->slot[in_q->rd_idx].req_id); + __cam_req_mgr_check_next_req_slot(in_q); + __cam_req_mgr_inc_idx(&in_q->rd_idx, 1, in_q->num_slots); + } + rc = __cam_req_mgr_process_req(link, trigger_data->trigger); + mutex_unlock(&link->req.lock); + +end: + return rc; +} + + +/* Linked devices' Callback section */ + +/** + * cam_req_mgr_cb_add_req() + * + * @brief : Drivers call this function to notify new packet is available. + * @add_req : Information about new request available at a device. + * + * @return : 0 on success, negative in case of failure + * + */ +static int cam_req_mgr_cb_add_req(struct cam_req_mgr_add_request *add_req) +{ + int rc = 0, idx; + struct crm_workq_task *task = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_add_request *dev_req; + struct crm_task_payload *task_data; + + if (!add_req) { + CAM_ERR(CAM_CRM, "sof_data is NULL"); + return -EINVAL; + } + + CAM_DBG(CAM_CRM, "E: dev %x dev req %lld", + add_req->dev_hdl, add_req->req_id); + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(add_req->link_hdl); + + if (!link) { + CAM_DBG(CAM_CRM, "link ptr NULL %x", add_req->link_hdl); + return -EINVAL; + } + + mutex_lock(&link->lock); + spin_lock_bh(&link->link_state_spin_lock); + if (link->state != CAM_CRM_LINK_STATE_READY) { + CAM_WARN(CAM_CRM, "invalid link state:%d", link->state); + rc = -EPERM; + spin_unlock_bh(&link->link_state_spin_lock); + goto end; + } + spin_unlock_bh(&link->link_state_spin_lock); + + /* Validate if req id is present in input queue */ + idx = __cam_req_mgr_find_slot_for_req(link->req.in_q, add_req->req_id); + if (idx < 0) { + CAM_ERR(CAM_CRM, "req %lld not found in in_q", add_req->req_id); + rc = -ENOENT; + goto end; + } + + task = cam_req_mgr_workq_get_task(link->workq); + if (!task) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "no empty task dev %x req %lld", + add_req->dev_hdl, add_req->req_id); + rc = -EBUSY; + goto end; + } + + task_data = (struct crm_task_payload *)task->payload; + task_data->type = CRM_WORKQ_TASK_DEV_ADD_REQ; + dev_req = (struct cam_req_mgr_add_request *)&task_data->u; + dev_req->req_id = add_req->req_id; + dev_req->link_hdl = add_req->link_hdl; + dev_req->dev_hdl = add_req->dev_hdl; + dev_req->skip_before_applying = add_req->skip_before_applying; + task->process_cb = &cam_req_mgr_process_add_req; + rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0); + CAM_DBG(CAM_CRM, "X: dev %x dev req %lld", + add_req->dev_hdl, add_req->req_id); + +end: + mutex_unlock(&link->lock); + return rc; +} + +/** + * cam_req_mgr_cb_notify_err() + * + * @brief : Error received from device, sends bubble recovery + * @err_info : contains information about error occurred like bubble/overflow + * + * @return : 0 on success, negative in case of failure + * + */ +static int cam_req_mgr_cb_notify_err( + struct cam_req_mgr_error_notify *err_info) +{ + int rc = 0; + struct crm_workq_task *task = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_error_notify *notify_err; + struct crm_task_payload *task_data; + + if (!err_info) { + CAM_ERR(CAM_CRM, "err_info is NULL"); + rc = -EINVAL; + goto end; + } + + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(err_info->link_hdl); + if (!link) { + CAM_DBG(CAM_CRM, "link ptr NULL %x", err_info->link_hdl); + rc = -EINVAL; + goto end; + } + + spin_lock_bh(&link->link_state_spin_lock); + if (link->state != CAM_CRM_LINK_STATE_READY) { + CAM_WARN(CAM_CRM, "invalid link state:%d", link->state); + spin_unlock_bh(&link->link_state_spin_lock); + rc = -EPERM; + goto end; + } + crm_timer_reset(link->watchdog); + spin_unlock_bh(&link->link_state_spin_lock); + + task = cam_req_mgr_workq_get_task(link->workq); + if (!task) { + CAM_ERR(CAM_CRM, "no empty task req_id %lld", err_info->req_id); + rc = -EBUSY; + goto end; + } + + task_data = (struct crm_task_payload *)task->payload; + task_data->type = CRM_WORKQ_TASK_NOTIFY_ERR; + notify_err = (struct cam_req_mgr_error_notify *)&task_data->u; + notify_err->req_id = err_info->req_id; + notify_err->link_hdl = err_info->link_hdl; + notify_err->dev_hdl = err_info->dev_hdl; + notify_err->error = err_info->error; + task->process_cb = &cam_req_mgr_process_error; + rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0); + +end: + return rc; +} + +/** + * cam_req_mgr_cb_notify_trigger() + * + * @brief : SOF received from device, sends trigger through workqueue + * @sof_data: contains information about frame_id, link etc. + * + * @return : 0 on success + * + */ +static int cam_req_mgr_cb_notify_trigger( + struct cam_req_mgr_trigger_notify *trigger_data) +{ + int rc = 0; + struct crm_workq_task *task = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_trigger_notify *notify_trigger; + struct crm_task_payload *task_data; + + if (!trigger_data) { + CAM_ERR(CAM_CRM, "sof_data is NULL"); + rc = -EINVAL; + goto end; + } + + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(trigger_data->link_hdl); + if (!link) { + CAM_DBG(CAM_CRM, "link ptr NULL %x", trigger_data->link_hdl); + rc = -EINVAL; + goto end; + } + + spin_lock_bh(&link->link_state_spin_lock); + if (link->state != CAM_CRM_LINK_STATE_READY) { + CAM_WARN(CAM_CRM, "invalid link state:%d", link->state); + spin_unlock_bh(&link->link_state_spin_lock); + rc = -EPERM; + goto end; + } + crm_timer_reset(link->watchdog); + spin_unlock_bh(&link->link_state_spin_lock); + + task = cam_req_mgr_workq_get_task(link->workq); + if (!task) { + CAM_ERR(CAM_CRM, "no empty task frame %lld", + trigger_data->frame_id); + rc = -EBUSY; + goto end; + } + task_data = (struct crm_task_payload *)task->payload; + task_data->type = CRM_WORKQ_TASK_NOTIFY_SOF; + notify_trigger = (struct cam_req_mgr_trigger_notify *)&task_data->u; + notify_trigger->frame_id = trigger_data->frame_id; + notify_trigger->link_hdl = trigger_data->link_hdl; + notify_trigger->dev_hdl = trigger_data->dev_hdl; + notify_trigger->trigger = trigger_data->trigger; + task->process_cb = &cam_req_mgr_process_trigger; + rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0); + +end: + return rc; +} + +static struct cam_req_mgr_crm_cb cam_req_mgr_ops = { + .notify_trigger = cam_req_mgr_cb_notify_trigger, + .notify_err = cam_req_mgr_cb_notify_err, + .add_req = cam_req_mgr_cb_add_req, +}; + +/** + * __cam_req_mgr_setup_link_info() + * + * @brief : Sets up input queue, create pd based tables, communicate with + * devs connected on this link and setup communication. + * @link : pointer to link to setup + * @link_info : link_info coming from CSL to prepare link + * + * @return : 0 on success, negative in case of failure + * + */ +static int __cam_req_mgr_setup_link_info(struct cam_req_mgr_core_link *link, + struct cam_req_mgr_link_info *link_info) +{ + int rc = 0, i = 0; + struct cam_req_mgr_core_dev_link_setup link_data; + struct cam_req_mgr_connected_device *dev; + struct cam_req_mgr_req_tbl *pd_tbl; + enum cam_pipeline_delay max_delay; + uint32_t subscribe_event = 0; + + if (link_info->num_devices > CAM_REQ_MGR_MAX_HANDLES) + return -EPERM; + + mutex_init(&link->req.lock); + CAM_DBG(CAM_CRM, "LOCK_DBG in_q lock %pK", &link->req.lock); + link->req.num_tbl = 0; + + rc = __cam_req_mgr_setup_in_q(&link->req); + if (rc < 0) + return rc; + + max_delay = CAM_PIPELINE_DELAY_0; + for (i = 0; i < link_info->num_devices; i++) { + dev = &link->l_dev[i]; + /* Using dev hdl, get ops ptr to communicate with device */ + dev->ops = (struct cam_req_mgr_kmd_ops *) + cam_get_device_ops(link_info->dev_hdls[i]); + if (!dev->ops || + !dev->ops->get_dev_info || + !dev->ops->link_setup) { + CAM_ERR(CAM_CRM, "FATAL: device ops NULL"); + rc = -ENXIO; + goto error; + } + dev->dev_hdl = link_info->dev_hdls[i]; + dev->parent = (void *)link; + dev->dev_info.dev_hdl = dev->dev_hdl; + rc = dev->ops->get_dev_info(&dev->dev_info); + + trace_cam_req_mgr_connect_device(link, &dev->dev_info); + + CAM_DBG(CAM_CRM, + "%x: connected: %s, id %d, delay %d, trigger %x", + link_info->session_hdl, dev->dev_info.name, + dev->dev_info.dev_id, dev->dev_info.p_delay, + dev->dev_info.trigger); + if (rc < 0 || + dev->dev_info.p_delay >= + CAM_PIPELINE_DELAY_MAX || + dev->dev_info.p_delay < + CAM_PIPELINE_DELAY_0) { + CAM_ERR(CAM_CRM, "get device info failed"); + goto error; + } else { + CAM_DBG(CAM_CRM, "%x: connected: %s, delay %d", + link_info->session_hdl, + dev->dev_info.name, + dev->dev_info.p_delay); + if (dev->dev_info.p_delay > max_delay) + max_delay = dev->dev_info.p_delay; + + subscribe_event |= (uint32_t)dev->dev_info.trigger; + } + } + + link->subscribe_event = subscribe_event; + link_data.link_enable = 1; + link_data.link_hdl = link->link_hdl; + link_data.crm_cb = &cam_req_mgr_ops; + link_data.max_delay = max_delay; + link_data.subscribe_event = subscribe_event; + + for (i = 0; i < link_info->num_devices; i++) { + dev = &link->l_dev[i]; + + link_data.dev_hdl = dev->dev_hdl; + /* + * For unique pipeline delay table create request + * tracking table + */ + if (link->pd_mask & (1 << dev->dev_info.p_delay)) { + pd_tbl = __cam_req_mgr_find_pd_tbl(link->req.l_tbl, + dev->dev_info.p_delay); + if (!pd_tbl) { + CAM_ERR(CAM_CRM, "pd %d tbl not found", + dev->dev_info.p_delay); + rc = -ENXIO; + goto error; + } + } else { + pd_tbl = __cam_req_mgr_create_pd_tbl( + dev->dev_info.p_delay); + if (pd_tbl == NULL) { + CAM_ERR(CAM_CRM, "create new pd tbl failed"); + rc = -ENXIO; + goto error; + } + pd_tbl->pd = dev->dev_info.p_delay; + link->pd_mask |= (1 << pd_tbl->pd); + /* + * Add table to list and also sort list + * from max pd to lowest + */ + __cam_req_mgr_add_tbl_to_link(&link->req.l_tbl, pd_tbl); + } + dev->dev_bit = pd_tbl->dev_count++; + dev->pd_tbl = pd_tbl; + pd_tbl->dev_mask |= (1 << dev->dev_bit); + + /* Communicate with dev to establish the link */ + dev->ops->link_setup(&link_data); + + if (link->max_delay < dev->dev_info.p_delay) + link->max_delay = dev->dev_info.p_delay; + } + link->num_devs = link_info->num_devices; + + /* Assign id for pd tables */ + __cam_req_mgr_tbl_set_id(link->req.l_tbl, &link->req); + + /* At start, expect max pd devices, all are in skip state */ + __cam_req_mgr_tbl_set_all_skip_cnt(&link->req.l_tbl); + + return 0; + +error: + __cam_req_mgr_destroy_link_info(link); + return rc; +} + +/* IOCTLs handling section */ +int cam_req_mgr_create_session( + struct cam_req_mgr_session_info *ses_info) +{ + int rc = 0; + int32_t session_hdl; + struct cam_req_mgr_core_session *cam_session = NULL; + + if (!ses_info) { + CAM_DBG(CAM_CRM, "NULL session info pointer"); + return -EINVAL; + } + mutex_lock(&g_crm_core_dev->crm_lock); + cam_session = (struct cam_req_mgr_core_session *) + kzalloc(sizeof(*cam_session), GFP_KERNEL); + if (!cam_session) { + rc = -ENOMEM; + goto end; + } + + session_hdl = cam_create_session_hdl((void *)cam_session); + if (session_hdl < 0) { + CAM_ERR(CAM_CRM, "unable to create session_hdl = %x", + session_hdl); + rc = session_hdl; + kfree(cam_session); + goto end; + } + ses_info->session_hdl = session_hdl; + + mutex_init(&cam_session->lock); + CAM_DBG(CAM_CRM, "LOCK_DBG session lock %pK", &cam_session->lock); + + mutex_lock(&cam_session->lock); + cam_session->session_hdl = session_hdl; + cam_session->num_links = 0; + cam_session->sync_mode = CAM_REQ_MGR_SYNC_MODE_NO_SYNC; + list_add(&cam_session->entry, &g_crm_core_dev->session_head); + mutex_unlock(&cam_session->lock); +end: + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +/** + * __cam_req_mgr_unlink() + * + * @brief : Unlink devices on a link structure from the session + * @link : Pointer to the link structure + * + * @return: 0 for success, negative for failure + * + */ +static int __cam_req_mgr_unlink(struct cam_req_mgr_core_link *link) +{ + int rc; + + mutex_lock(&link->lock); + spin_lock_bh(&link->link_state_spin_lock); + link->state = CAM_CRM_LINK_STATE_IDLE; + + /* Destroy timer of link */ + crm_timer_exit(&link->watchdog); + spin_unlock_bh(&link->link_state_spin_lock); + __cam_req_mgr_print_req_tbl(&link->req); + + /* Destroy workq payload data */ + kfree(link->workq->task.pool[0].payload); + link->workq->task.pool[0].payload = NULL; + + /* Destroy workq of link */ + cam_req_mgr_workq_destroy(&link->workq); + + /* Cleanup request tables and unlink devices */ + rc = __cam_req_mgr_destroy_link_info(link); + if (rc) + CAM_ERR(CAM_CORE, + "Unlink for all devices was not successful"); + + /* Free memory holding data of linked devs */ + __cam_req_mgr_destroy_subdev(&(link->l_dev)); + /* Destroy the link handle */ + rc = cam_destroy_device_hdl(link->link_hdl); + if (rc < 0) { + CAM_ERR(CAM_CRM, "error while destroying dev handle %d %x", + rc, link->link_hdl); + } + + mutex_unlock(&link->lock); + return rc; +} + +int cam_req_mgr_destroy_session( + struct cam_req_mgr_session_info *ses_info) +{ + int rc; + int i; + struct cam_req_mgr_core_session *cam_session = NULL; + struct cam_req_mgr_core_link *link; + + if (!ses_info) { + CAM_DBG(CAM_CRM, "NULL session info pointer"); + return -EINVAL; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + cam_session = (struct cam_req_mgr_core_session *) + cam_get_device_priv(ses_info->session_hdl); + if (!cam_session) { + CAM_ERR(CAM_CRM, "failed to get session priv"); + rc = -ENOENT; + goto end; + + } + mutex_lock(&cam_session->lock); + if (cam_session->num_links) { + CAM_DBG(CAM_CRM, "destroy session %x num_active_links %d", + ses_info->session_hdl, + cam_session->num_links); + + for (i = 0; i < MAX_LINKS_PER_SESSION; i++) { + link = cam_session->links[i]; + + if (!link) + continue; + + /* Ignore return value since session is going away */ + __cam_req_mgr_unlink(link); + __cam_req_mgr_free_link(link); + } + } + list_del(&cam_session->entry); + mutex_unlock(&cam_session->lock); + mutex_destroy(&cam_session->lock); + kfree(cam_session); + + rc = cam_destroy_session_hdl(ses_info->session_hdl); + if (rc < 0) + CAM_ERR(CAM_CRM, "unable to destroy session_hdl = %x rc %d", + ses_info->session_hdl, rc); + +end: + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_link(struct cam_req_mgr_link_info *link_info) +{ + int rc = 0; + char buf[128]; + struct cam_create_dev_hdl root_dev; + struct cam_req_mgr_core_session *cam_session; + struct cam_req_mgr_core_link *link; + + if (!link_info) { + CAM_DBG(CAM_CRM, "NULL pointer"); + return -EINVAL; + } + if (link_info->num_devices > CAM_REQ_MGR_MAX_HANDLES) { + CAM_ERR(CAM_CRM, "Invalid num devices %d", + link_info->num_devices); + return -EINVAL; + } + + /* session hdl's priv data is cam session struct */ + cam_session = (struct cam_req_mgr_core_session *) + cam_get_device_priv(link_info->session_hdl); + if (!cam_session) { + CAM_DBG(CAM_CRM, "NULL pointer"); + return -EINVAL; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + + /* Allocate link struct and map it with session's request queue */ + link = __cam_req_mgr_reserve_link(cam_session); + if (!link) { + CAM_ERR(CAM_CRM, "failed to reserve new link"); + mutex_unlock(&g_crm_core_dev->crm_lock); + return -EINVAL; + } + CAM_DBG(CAM_CRM, "link reserved %pK %x", link, link->link_hdl); + + memset(&root_dev, 0, sizeof(struct cam_create_dev_hdl)); + root_dev.session_hdl = link_info->session_hdl; + root_dev.priv = (void *)link; + + mutex_lock(&link->lock); + /* Create unique dev handle for link */ + link->link_hdl = cam_create_device_hdl(&root_dev); + if (link->link_hdl < 0) { + CAM_ERR(CAM_CRM, + "Insufficient memory to create new device handle"); + rc = link->link_hdl; + goto link_hdl_fail; + } + link_info->link_hdl = link->link_hdl; + + /* Allocate memory to hold data of all linked devs */ + rc = __cam_req_mgr_create_subdevs(&link->l_dev, + link_info->num_devices); + if (rc < 0) { + CAM_ERR(CAM_CRM, + "Insufficient memory to create new crm subdevs"); + goto create_subdev_failed; + } + + /* Using device ops query connected devs, prepare request tables */ + rc = __cam_req_mgr_setup_link_info(link, link_info); + if (rc < 0) + goto setup_failed; + + spin_lock_bh(&link->link_state_spin_lock); + link->state = CAM_CRM_LINK_STATE_READY; + spin_unlock_bh(&link->link_state_spin_lock); + + /* Create worker for current link */ + snprintf(buf, sizeof(buf), "%x-%x", + link_info->session_hdl, link->link_hdl); + rc = cam_req_mgr_workq_create(buf, CRM_WORKQ_NUM_TASKS, + &link->workq, CRM_WORKQ_USAGE_NON_IRQ); + if (rc < 0) { + CAM_ERR(CAM_CRM, "FATAL: unable to create worker"); + __cam_req_mgr_destroy_link_info(link); + goto setup_failed; + } + + /* Assign payload to workqueue tasks */ + rc = __cam_req_mgr_setup_payload(link->workq); + if (rc < 0) { + __cam_req_mgr_destroy_link_info(link); + cam_req_mgr_workq_destroy(&link->workq); + goto setup_failed; + } + + mutex_unlock(&link->lock); + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +setup_failed: + __cam_req_mgr_destroy_subdev(&(link->l_dev)); +create_subdev_failed: + cam_destroy_device_hdl(link->link_hdl); + link_info->link_hdl = 0; +link_hdl_fail: + mutex_unlock(&link->lock); + __cam_req_mgr_unreserve_link(cam_session, link); + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_unlink(struct cam_req_mgr_unlink_info *unlink_info) +{ + int rc = 0; + struct cam_req_mgr_core_session *cam_session; + struct cam_req_mgr_core_link *link; + + if (!unlink_info) { + CAM_ERR(CAM_CRM, "NULL pointer"); + return -EINVAL; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + CAM_DBG(CAM_CRM, "link_hdl %x", unlink_info->link_hdl); + + /* session hdl's priv data is cam session struct */ + cam_session = (struct cam_req_mgr_core_session *) + cam_get_device_priv(unlink_info->session_hdl); + if (!cam_session) { + CAM_ERR(CAM_CRM, "NULL pointer"); + mutex_unlock(&g_crm_core_dev->crm_lock); + return -EINVAL; + } + + /* link hdl's priv data is core_link struct */ + link = cam_get_device_priv(unlink_info->link_hdl); + if (!link) { + CAM_ERR(CAM_CRM, "NULL pointer"); + rc = -EINVAL; + goto done; + } + + rc = __cam_req_mgr_unlink(link); + + /* Free curent link and put back into session's free pool of links */ + if (!rc) + __cam_req_mgr_unreserve_link(cam_session, link); + +done: + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_schedule_request( + struct cam_req_mgr_sched_request *sched_req) +{ + int rc = 0; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_core_session *session = NULL; + struct cam_req_mgr_sched_request *sched; + struct crm_task_payload task_data; + + if (!sched_req) { + CAM_ERR(CAM_CRM, "csl_req is NULL"); + return -EINVAL; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(sched_req->link_hdl); + if (!link) { + CAM_DBG(CAM_CRM, "link ptr NULL %x", sched_req->link_hdl); + rc = -EINVAL; + goto end; + } + + session = (struct cam_req_mgr_core_session *)link->parent; + if (!session) { + CAM_WARN(CAM_CRM, "session ptr NULL %x", sched_req->link_hdl); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_CRM, "link %x req %lld, sync_mode %d", + sched_req->link_hdl, sched_req->req_id, sched_req->sync_mode); + + task_data.type = CRM_WORKQ_TASK_SCHED_REQ; + sched = (struct cam_req_mgr_sched_request *)&task_data.u; + sched->req_id = sched_req->req_id; + sched->sync_mode = sched_req->sync_mode; + sched->link_hdl = sched_req->link_hdl; + if (session->force_err_recovery == AUTO_RECOVERY) { + sched->bubble_enable = sched_req->bubble_enable; + } else { + sched->bubble_enable = + (session->force_err_recovery == FORCE_ENABLE_RECOVERY) ? 1 : 0; + } + + rc = cam_req_mgr_process_sched_req(link, &task_data); + + CAM_DBG(CAM_CRM, "DONE dev %x req %lld sync_mode %d", + sched_req->link_hdl, sched_req->req_id, sched_req->sync_mode); +end: + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_sync_config( + struct cam_req_mgr_sync_mode *sync_info) +{ + int rc = 0; + struct cam_req_mgr_core_session *cam_session; + struct cam_req_mgr_core_link *link1 = NULL; + struct cam_req_mgr_core_link *link2 = NULL; + + if (!sync_info) { + CAM_ERR(CAM_CRM, "NULL pointer"); + return -EINVAL; + } + + if ((sync_info->num_links < 0) || + (sync_info->num_links > MAX_LINKS_PER_SESSION)) { + CAM_ERR(CAM_CRM, "Invalid num links %d", sync_info->num_links); + return -EINVAL; + } + + if ((!sync_info->link_hdls[0]) || (!sync_info->link_hdls[1])) { + CAM_WARN(CAM_CRM, "Invalid link handles 0x%x 0x%x", + sync_info->link_hdls[0], sync_info->link_hdls[1]); + return -EINVAL; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + /* session hdl's priv data is cam session struct */ + cam_session = (struct cam_req_mgr_core_session *) + cam_get_device_priv(sync_info->session_hdl); + if (!cam_session) { + CAM_ERR(CAM_CRM, "NULL pointer"); + mutex_unlock(&g_crm_core_dev->crm_lock); + return -EINVAL; + } + + mutex_lock(&cam_session->lock); + + CAM_DBG(CAM_CRM, "link handles %x %x", + sync_info->link_hdls[0], sync_info->link_hdls[1]); + + /* only two links existing per session in dual cam use case*/ + link1 = cam_get_device_priv(sync_info->link_hdls[0]); + if (!link1) { + CAM_ERR(CAM_CRM, "link1 NULL pointer"); + rc = -EINVAL; + goto done; + } + + link2 = cam_get_device_priv(sync_info->link_hdls[1]); + if (!link2) { + CAM_ERR(CAM_CRM, "link2 NULL pointer"); + rc = -EINVAL; + goto done; + } + + link1->sof_counter = -1; + link1->sync_self_ref = -1; + link1->frame_skip_flag = false; + link1->sync_link_sof_skip = false; + link1->sync_link = link2; + + link2->sof_counter = -1; + link2->sync_self_ref = -1; + link2->frame_skip_flag = false; + link2->sync_link_sof_skip = false; + link2->sync_link = link1; + + cam_session->sync_mode = sync_info->sync_mode; + +done: + mutex_unlock(&cam_session->lock); + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_flush_requests( + struct cam_req_mgr_flush_info *flush_info) +{ + int rc = 0; + struct crm_workq_task *task = NULL; + struct cam_req_mgr_core_link *link = NULL; + struct cam_req_mgr_flush_info *flush; + struct crm_task_payload *task_data; + struct cam_req_mgr_core_session *session = NULL; + + if (!flush_info) { + CAM_ERR(CAM_CRM, "flush req is NULL"); + rc = -EFAULT; + goto end; + } + if (flush_info->flush_type >= CAM_REQ_MGR_FLUSH_TYPE_MAX) { + CAM_ERR(CAM_CRM, "incorrect flush type %x", + flush_info->flush_type); + rc = -EINVAL; + goto end; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + /* session hdl's priv data is cam session struct */ + session = (struct cam_req_mgr_core_session *) + cam_get_device_priv(flush_info->session_hdl); + if (!session) { + CAM_ERR(CAM_CRM, "Invalid session %x", flush_info->session_hdl); + rc = -EINVAL; + goto end; + } + if (session->num_links <= 0) { + CAM_WARN(CAM_CRM, "No active links in session %x", + flush_info->session_hdl); + goto end; + } + + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(flush_info->link_hdl); + if (!link) { + CAM_DBG(CAM_CRM, "link ptr NULL %x", flush_info->link_hdl); + rc = -EINVAL; + goto end; + } + + task = cam_req_mgr_workq_get_task(link->workq); + if (!task) { + rc = -ENOMEM; + goto end; + } + + task_data = (struct crm_task_payload *)task->payload; + task_data->type = CRM_WORKQ_TASK_FLUSH_REQ; + flush = (struct cam_req_mgr_flush_info *)&task_data->u; + flush->req_id = flush_info->req_id; + flush->link_hdl = flush_info->link_hdl; + flush->flush_type = flush_info->flush_type; + task->process_cb = &cam_req_mgr_process_flush_req; + init_completion(&link->workq_comp); + rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0); + + /* Blocking call */ + rc = wait_for_completion_timeout( + &link->workq_comp, + msecs_to_jiffies(CAM_REQ_MGR_SCHED_REQ_TIMEOUT)); +end: + mutex_unlock(&g_crm_core_dev->crm_lock); + return rc; +} + +int cam_req_mgr_link_control(struct cam_req_mgr_link_control *control) +{ + int rc = 0; + int i, j; + struct cam_req_mgr_core_link *link = NULL; + + struct cam_req_mgr_connected_device *dev = NULL; + struct cam_req_mgr_link_evt_data evt_data; + + if (!control) { + CAM_ERR(CAM_CRM, "Control command is NULL"); + rc = -EINVAL; + goto end; + } + + mutex_lock(&g_crm_core_dev->crm_lock); + for (i = 0; i < control->num_links; i++) { + link = (struct cam_req_mgr_core_link *) + cam_get_device_priv(control->link_hdls[i]); + if (!link) { + CAM_ERR_RATE_LIMIT(CAM_CRM, + "Link(%d) is NULL on session 0x%x", + i, control->session_hdl); + rc = -EINVAL; + break; + } + + mutex_lock(&link->lock); + if (control->ops == CAM_REQ_MGR_LINK_ACTIVATE) { + /* Start SOF watchdog timer */ + rc = crm_timer_init(&link->watchdog, + CAM_REQ_MGR_WATCHDOG_TIMEOUT, link, + &__cam_req_mgr_sof_freeze); + if (rc < 0) { + CAM_ERR(CAM_CRM, + "SOF timer start fails: link=0x%x", + link->link_hdl); + rc = -EFAULT; + } + /* notify nodes */ + for (j = 0; j < link->num_devs; j++) { + dev = &link->l_dev[j]; + evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_RESUME; + evt_data.link_hdl = link->link_hdl; + evt_data.dev_hdl = dev->dev_hdl; + evt_data.req_id = 0; + if (dev->ops && dev->ops->process_evt) + dev->ops->process_evt(&evt_data); + } + } else if (control->ops == CAM_REQ_MGR_LINK_DEACTIVATE) { + /* Destroy SOF watchdog timer */ + spin_lock_bh(&link->link_state_spin_lock); + crm_timer_exit(&link->watchdog); + spin_unlock_bh(&link->link_state_spin_lock); + /* notify nodes */ + for (j = 0; j < link->num_devs; j++) { + dev = &link->l_dev[j]; + evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_PAUSE; + evt_data.link_hdl = link->link_hdl; + evt_data.dev_hdl = dev->dev_hdl; + evt_data.req_id = 0; + if (dev->ops && dev->ops->process_evt) + dev->ops->process_evt(&evt_data); + } + } else { + CAM_ERR(CAM_CRM, "Invalid link control command"); + rc = -EINVAL; + } + mutex_unlock(&link->lock); + } + mutex_unlock(&g_crm_core_dev->crm_lock); +end: + return rc; +} + + +int cam_req_mgr_core_device_init(void) +{ + CAM_DBG(CAM_CRM, "Enter g_crm_core_dev %pK", g_crm_core_dev); + + if (g_crm_core_dev) { + CAM_WARN(CAM_CRM, "core device is already initialized"); + return 0; + } + g_crm_core_dev = (struct cam_req_mgr_core_device *) + kzalloc(sizeof(*g_crm_core_dev), GFP_KERNEL); + if (!g_crm_core_dev) + return -ENOMEM; + + CAM_DBG(CAM_CRM, "g_crm_core_dev %pK", g_crm_core_dev); + INIT_LIST_HEAD(&g_crm_core_dev->session_head); + mutex_init(&g_crm_core_dev->crm_lock); + cam_req_mgr_debug_register(g_crm_core_dev); + + return 0; +} + +int cam_req_mgr_core_device_deinit(void) +{ + if (!g_crm_core_dev) { + CAM_ERR(CAM_CRM, "NULL pointer"); + return -EINVAL; + } + + CAM_DBG(CAM_CRM, "g_crm_core_dev %pK", g_crm_core_dev); + mutex_destroy(&g_crm_core_dev->crm_lock); + kfree(g_crm_core_dev); + g_crm_core_dev = NULL; + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.h new file mode 100644 index 000000000000..0083d1fd6310 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core.h @@ -0,0 +1,454 @@ +/* Copyright (c) 2016-2018, 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 _CAM_REQ_MGR_CORE_H_ +#define _CAM_REQ_MGR_CORE_H_ + +#include +#include "cam_req_mgr_interface.h" +#include "cam_req_mgr_core_defs.h" +#include "cam_req_mgr_timer.h" + +#define CAM_REQ_MGR_MAX_LINKED_DEV 16 +#define MAX_REQ_SLOTS 48 + +#define CAM_REQ_MGR_WATCHDOG_TIMEOUT 5000 +#define CAM_REQ_MGR_SCHED_REQ_TIMEOUT 1000 +#define CAM_REQ_MGR_SIMULATE_SCHED_REQ 30 + +#define FORCE_DISABLE_RECOVERY 2 +#define FORCE_ENABLE_RECOVERY 1 +#define AUTO_RECOVERY 0 + +#define CRM_WORKQ_NUM_TASKS 60 + +#define MAX_SYNC_COUNT 65535 + +#define SYNC_LINK_SOF_CNT_MAX_LMT 1 + +/** + * enum crm_workq_task_type + * @codes: to identify which type of task is present + */ +enum crm_workq_task_type { + CRM_WORKQ_TASK_GET_DEV_INFO, + CRM_WORKQ_TASK_SETUP_LINK, + CRM_WORKQ_TASK_DEV_ADD_REQ, + CRM_WORKQ_TASK_APPLY_REQ, + CRM_WORKQ_TASK_NOTIFY_SOF, + CRM_WORKQ_TASK_NOTIFY_ERR, + CRM_WORKQ_TASK_SCHED_REQ, + CRM_WORKQ_TASK_FLUSH_REQ, + CRM_WORKQ_TASK_INVALID, +}; + +/** + * struct crm_task_payload + * @type : to identify which type of task is present + * @u : union of payload of all types of tasks supported + * @sched_req : contains info of incoming reqest from CSL to CRM + * @flush_info : contains info of cancelled reqest + * @dev_req : contains tracking info of available req id at device + * @send_req : contains info of apply settings to be sent to devs in link + * @apply_req : contains info of which request is applied at device + * @notify_trigger : contains notification from IFE to CRM about trigger + * @notify_err : contains error info happened while processing request + * - + */ +struct crm_task_payload { + enum crm_workq_task_type type; + union { + struct cam_req_mgr_sched_request sched_req; + struct cam_req_mgr_flush_info flush_info; + struct cam_req_mgr_add_request dev_req; + struct cam_req_mgr_send_request send_req; + struct cam_req_mgr_trigger_notify notify_trigger; + struct cam_req_mgr_error_notify notify_err; + } u; +}; + +/** + * enum crm_req_state + * State machine for life cycle of request in pd table + * EMPTY : indicates req slot is empty + * PENDING : indicates req slot is waiting for reqs from all devs + * READY : indicates req slot is ready to be sent to devs + * INVALID : indicates req slot is not in valid state + */ +enum crm_req_state { + CRM_REQ_STATE_EMPTY, + CRM_REQ_STATE_PENDING, + CRM_REQ_STATE_READY, + CRM_REQ_STATE_INVALID, +}; + +/** + * enum crm_slot_status + * State machine for life cycle of request in input queue + * NO_REQ : empty slot + * REQ_ADDED : new entry in slot + * PENDING : waiting for next trigger to apply + * APPLIED : req is sent to all devices + * INVALID : invalid state + */ +enum crm_slot_status { + CRM_SLOT_STATUS_NO_REQ, + CRM_SLOT_STATUS_REQ_ADDED, + CRM_SLOT_STATUS_REQ_PENDING, + CRM_SLOT_STATUS_REQ_APPLIED, + CRM_SLOT_STATUS_INVALID, +}; + +/** + * enum cam_req_mgr_link_state + * State machine for life cycle of link in crm + * AVAILABLE : link available + * IDLE : link initialized but not ready yet + * READY : link is ready for use + * ERR : link has encountered error + * MAX : invalid state + */ +enum cam_req_mgr_link_state { + CAM_CRM_LINK_STATE_AVAILABLE, + CAM_CRM_LINK_STATE_IDLE, + CAM_CRM_LINK_STATE_READY, + CAM_CRM_LINK_STATE_ERR, + CAM_CRM_LINK_STATE_MAX, +}; + +/** + * struct cam_req_mgr_traverse + * @idx : slot index + * @result : contains which all tables were able to apply successfully + * @tbl : pointer of pipeline delay based request table + * @apply_data : pointer which various tables will update during traverse + * @in_q : input request queue pointer + * @validate_only : Whether to validate only and/or update settings + * @self_link : To indicate whether the check is for the given link or the + * other sync link + */ +struct cam_req_mgr_traverse { + int32_t idx; + uint32_t result; + struct cam_req_mgr_req_tbl *tbl; + struct cam_req_mgr_apply *apply_data; + struct cam_req_mgr_req_queue *in_q; + bool validate_only; + bool self_link; +}; + +/** + * struct cam_req_mgr_apply + * @idx : corresponding input queue slot index + * @pd : pipeline delay of device + * @req_id : req id for dev with above pd to process + * @skip_idx: skip applying settings when this is set. + */ +struct cam_req_mgr_apply { + int32_t idx; + int32_t pd; + int64_t req_id; + int32_t skip_idx; +}; + +/** + * struct cam_req_mgr_tbl_slot + * @idx : slot index + * @req_ready_map : mask tracking which all devices have request ready + * @state : state machine for life cycle of a slot + * @inject_delay : insert extra bubbling for flash type of use cases + */ +struct cam_req_mgr_tbl_slot { + int32_t idx; + uint32_t req_ready_map; + enum crm_req_state state; + uint32_t inject_delay; +}; + +/** + * struct cam_req_mgr_req_tbl + * @id : table indetifier + * @pd : pipeline delay of table + * @dev_count : num of devices having same pipeline delay + * @dev_mask : mask to track which devices are linked + * @skip_traverse : to indicate how many traverses need to be dropped + * by this table especially in the beginning or bubble recovery + * @next : pointer to next pipeline delay request table + * @pd_delta : differnce between this table's pipeline delay and next + * @num_slots : number of request slots present in the table + * @slot : array of slots tracking requests availability at devices + */ +struct cam_req_mgr_req_tbl { + int32_t id; + int32_t pd; + int32_t dev_count; + int32_t dev_mask; + int32_t skip_traverse; + struct cam_req_mgr_req_tbl *next; + int32_t pd_delta; + int32_t num_slots; + struct cam_req_mgr_tbl_slot slot[MAX_REQ_SLOTS]; +}; + +/** + * struct cam_req_mgr_slot + * - Internal Book keeping + * @idx : slot index + * @skip_idx : if req id in this slot needs to be skipped/not applied + * @status : state machine for life cycle of a slot + * - members updated due to external events + * @recover : if user enabled recovery for this request. + * @req_id : mask tracking which all devices have request ready + * @sync_mode : Sync mode in which req id in this slot has to applied + */ +struct cam_req_mgr_slot { + int32_t idx; + int32_t skip_idx; + enum crm_slot_status status; + int32_t recover; + int64_t req_id; + int32_t sync_mode; +}; + +/** + * struct cam_req_mgr_req_queue + * @num_slots : max num of input queue slots + * @slot : request slot holding incoming request id and bubble info. + * @rd_idx : indicates slot index currently in process. + * @wr_idx : indicates slot index to hold new upcoming req. + */ +struct cam_req_mgr_req_queue { + int32_t num_slots; + struct cam_req_mgr_slot slot[MAX_REQ_SLOTS]; + int32_t rd_idx; + int32_t wr_idx; +}; + +/** + * struct cam_req_mgr_req_data + * @in_q : Poiner to Input request queue + * @l_tbl : unique pd request tables. + * @num_tbl : how many unique pd value devices are present + * @apply_data : Holds information about request id for a request + * @lock : mutex lock protecting request data ops. + */ +struct cam_req_mgr_req_data { + struct cam_req_mgr_req_queue *in_q; + struct cam_req_mgr_req_tbl *l_tbl; + int32_t num_tbl; + struct cam_req_mgr_apply apply_data[CAM_PIPELINE_DELAY_MAX]; + struct mutex lock; +}; + +/** + * struct cam_req_mgr_connected_device + * - Device Properties + * @dev_hdl : device handle + * @dev_bit : unique bit assigned to device in link + * - Device characteristics + * @pd_tbl : tracks latest available req id at this device + * @dev_info : holds dev characteristics such as pipeline delay, dev name + * @ops : holds func pointer to call methods on this device + * @parent : pvt data - like link which this dev hdl belongs to + */ +struct cam_req_mgr_connected_device { + int32_t dev_hdl; + int64_t dev_bit; + struct cam_req_mgr_req_tbl *pd_tbl; + struct cam_req_mgr_device_info dev_info; + struct cam_req_mgr_kmd_ops *ops; + void *parent; +}; + +/** + * struct cam_req_mgr_core_link + * - Link Properties + * @link_hdl : Link identifier + * @num_devs : num of connected devices to this link + * @max_delay : Max of pipeline delay of all connected devs + * @workq : Pointer to handle workq related jobs + * @pd_mask : each set bit indicates the device with pd equal to + * bit position is available. + * - List of connected devices + * @l_dev : List of connected devices to this link + * - Request handling data struct + * @req : req data holder. + * - Timer + * @watchdog : watchdog timer to recover from sof freeze + * - Link private data + * @workq_comp : conditional variable to block user thread for workq + * to finish schedule request processing + * @state : link state machine + * @parent : pvt data - link's parent is session + * @lock : mutex lock to guard link data operations + * @link_state_spin_lock : spin lock to protect link state variable + * @subscribe_event : irqs that link subscribes, IFE should send + * notification to CRM at those hw events. + * @trigger_mask : mask on which irq the req is already applied + * @sync_link : pointer to the sync link for synchronization + * @sof_counter : sof counter during sync_mode + * @sync_self_ref : reference sync count against which the difference + * between sync_counts for a given link is checked + * @frame_skip_flag : flag that determines if a frame needs to be skipped + * @sync_link_sof_skip : flag determines if a pkt is not available for a given + * frame in a particular link skip corresponding + * frame in sync link as well. + * + */ +struct cam_req_mgr_core_link { + int32_t link_hdl; + int32_t num_devs; + enum cam_pipeline_delay max_delay; + struct cam_req_mgr_core_workq *workq; + int32_t pd_mask; + struct cam_req_mgr_connected_device *l_dev; + struct cam_req_mgr_req_data req; + struct cam_req_mgr_timer *watchdog; + struct completion workq_comp; + enum cam_req_mgr_link_state state; + void *parent; + struct mutex lock; + spinlock_t link_state_spin_lock; + uint32_t subscribe_event; + uint32_t trigger_mask; + struct cam_req_mgr_core_link *sync_link; + int64_t sof_counter; + int64_t sync_self_ref; + bool frame_skip_flag; + bool sync_link_sof_skip; +}; + +/** + * struct cam_req_mgr_core_session + * - Session Properties + * @session_hdl : session identifier + * @num_links : num of active links for current session + * - Links of this session + * @links : pointer to array of links within session + * @in_q : Input request queue one per session + * - Session private data + * @entry : pvt data - entry in the list of sessions + * @lock : pvt data - spin lock to guard session data + * - Debug data + * @force_err_recovery : For debugging, we can force bubble recovery + * to be always ON or always OFF using debugfs. + * @sync_mode : Sync mode for this session links + */ +struct cam_req_mgr_core_session { + int32_t session_hdl; + uint32_t num_links; + struct cam_req_mgr_core_link *links[MAX_LINKS_PER_SESSION]; + struct list_head entry; + struct mutex lock; + int32_t force_err_recovery; + int32_t sync_mode; +}; + +/** + * struct cam_req_mgr_core_device + * - Core camera request manager data struct + * @session_head : list head holding sessions + * @crm_lock : mutex lock to protect session creation & destruction + */ +struct cam_req_mgr_core_device { + struct list_head session_head; + struct mutex crm_lock; +}; + +/** + * cam_req_mgr_create_session() + * @brief : creates session + * @ses_info : output param for session handle + * + * called as part of session creation. + */ +int cam_req_mgr_create_session(struct cam_req_mgr_session_info *ses_info); + +/** + * cam_req_mgr_destroy_session() + * @brief : destroy session + * @ses_info : session handle info, input param + * + * Called as part of session destroy + * return success/failure + */ +int cam_req_mgr_destroy_session(struct cam_req_mgr_session_info *ses_info); + +/** + * cam_req_mgr_link() + * @brief : creates a link for a session + * @link_info : handle and session info to create a link + * + * link is formed in a session for multiple devices. it creates + * a unique link handle for the link and is specific to a + * session. Returns link handle + */ +int cam_req_mgr_link(struct cam_req_mgr_link_info *link_info); + +/** + * cam_req_mgr_unlink() + * @brief : destroy a link in a session + * @unlink_info : session and link handle info + * + * link is destroyed in a session + */ +int cam_req_mgr_unlink(struct cam_req_mgr_unlink_info *unlink_info); + +/** + * cam_req_mgr_schedule_request() + * @brief: Request is scheduled + * @sched_req: request id, session and link id info, bubble recovery info + */ +int cam_req_mgr_schedule_request( + struct cam_req_mgr_sched_request *sched_req); + +/** + * cam_req_mgr_sync_mode_setup() + * @brief: sync for links in a session + * @sync_info: session, links info and master link info + */ +int cam_req_mgr_sync_config(struct cam_req_mgr_sync_mode *sync_info); + +/** + * cam_req_mgr_flush_requests() + * @brief: flush all requests + * @flush_info: requests related to link and session + */ +int cam_req_mgr_flush_requests( + struct cam_req_mgr_flush_info *flush_info); + +/** + * cam_req_mgr_core_device_init() + * @brief: initialize crm core + */ +int cam_req_mgr_core_device_init(void); + +/** + * cam_req_mgr_core_device_deinit() + * @brief: cleanp crm core + */ +int cam_req_mgr_core_device_deinit(void); + +/** + * cam_req_mgr_handle_core_shutdown() + * @brief: Handles camera close + */ +void cam_req_mgr_handle_core_shutdown(void); + +/** + * cam_req_mgr_link_control() + * @brief: Handles link control operations + * @control: Link control command + */ +int cam_req_mgr_link_control(struct cam_req_mgr_link_control *control); + +#endif + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core_defs.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core_defs.h new file mode 100644 index 000000000000..475b64099d52 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_core_defs.h @@ -0,0 +1,25 @@ +/* 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. + */ +#ifndef _CAM_REQ_MGR_CORE_DEFS_H_ +#define _CAM_REQ_MGR_CORE_DEFS_H_ + +#define CRM_TRACE_ENABLE 0 +#define CRM_DEBUG_MUTEX 0 + +#define SET_SUCCESS_BIT(ret, pd) (ret |= (1 << (pd))) + +#define SET_FAILURE_BIT(ret, pd) (ret &= (~(1 << (pd)))) + +#define CRM_GET_REQ_ID(in_q, idx) in_q->slot[idx].req_id + +#endif + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.c new file mode 100644 index 000000000000..19833d85a23a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.c @@ -0,0 +1,139 @@ +/* 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 "cam_req_mgr_debug.h" + +#define MAX_SESS_INFO_LINE_BUFF_LEN 256 + +static char sess_info_buffer[MAX_SESS_INFO_LINE_BUFF_LEN]; + +static int cam_req_mgr_debug_set_bubble_recovery(void *data, u64 val) +{ + struct cam_req_mgr_core_device *core_dev = data; + struct cam_req_mgr_core_session *session; + int rc = 0; + + mutex_lock(&core_dev->crm_lock); + + if (!list_empty(&core_dev->session_head)) { + list_for_each_entry(session, + &core_dev->session_head, entry) { + session->force_err_recovery = val; + } + } + + mutex_unlock(&core_dev->crm_lock); + + return rc; +} + +static int cam_req_mgr_debug_get_bubble_recovery(void *data, u64 *val) +{ + struct cam_req_mgr_core_device *core_dev = data; + struct cam_req_mgr_core_session *session; + + mutex_lock(&core_dev->crm_lock); + + if (!list_empty(&core_dev->session_head)) { + session = list_first_entry(&core_dev->session_head, + struct cam_req_mgr_core_session, + entry); + *val = session->force_err_recovery; + } + mutex_unlock(&core_dev->crm_lock); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(bubble_recovery, cam_req_mgr_debug_get_bubble_recovery, + cam_req_mgr_debug_set_bubble_recovery, "%lld\n"); + +static int session_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t session_info_read(struct file *t_file, char *t_char, + size_t t_size_t, loff_t *t_loff_t) +{ + int i; + char *out_buffer = sess_info_buffer; + char line_buffer[MAX_SESS_INFO_LINE_BUFF_LEN] = {0}; + struct cam_req_mgr_core_device *core_dev = + (struct cam_req_mgr_core_device *) t_file->private_data; + struct cam_req_mgr_core_session *session; + + memset(out_buffer, 0, MAX_SESS_INFO_LINE_BUFF_LEN); + + mutex_lock(&core_dev->crm_lock); + + if (!list_empty(&core_dev->session_head)) { + list_for_each_entry(session, + &core_dev->session_head, entry) { + snprintf(line_buffer, sizeof(line_buffer), + "session_hdl = %x \t" + "num_links = %d\n", + session->session_hdl, session->num_links); + strlcat(out_buffer, line_buffer, + sizeof(sess_info_buffer)); + for (i = 0; i < session->num_links; i++) { + snprintf(line_buffer, sizeof(line_buffer), + "link_hdl[%d] = 0x%x, num_devs connected = %d\n", + i, session->links[i]->link_hdl, + session->links[i]->num_devs); + strlcat(out_buffer, line_buffer, + sizeof(sess_info_buffer)); + } + } + } + + mutex_unlock(&core_dev->crm_lock); + + return simple_read_from_buffer(t_char, t_size_t, + t_loff_t, out_buffer, strlen(out_buffer)); +} + +static ssize_t session_info_write(struct file *t_file, + const char *t_char, size_t t_size_t, loff_t *t_loff_t) +{ + memset(sess_info_buffer, 0, MAX_SESS_INFO_LINE_BUFF_LEN); + + return 0; +} + +static const struct file_operations session_info = { + .open = session_info_open, + .read = session_info_read, + .write = session_info_write, +}; + +int cam_req_mgr_debug_register(struct cam_req_mgr_core_device *core_dev) +{ + struct dentry *debugfs_root; + char dirname[32] = {0}; + + snprintf(dirname, sizeof(dirname), "cam_req_mgr"); + debugfs_root = debugfs_create_dir(dirname, NULL); + if (!debugfs_root) + return -ENOMEM; + + if (!debugfs_create_file("sessions_info", 0644, + debugfs_root, core_dev, &session_info)) + return -ENOMEM; + + if (!debugfs_create_file("bubble_recovery", 0644, + debugfs_root, core_dev, &bubble_recovery)) + return -ENOMEM; + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.h new file mode 100644 index 000000000000..82ac7644ff8e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_debug.h @@ -0,0 +1,21 @@ +/* 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. + */ + +#ifndef _CAM_REQ_MGR_DEBUG_H_ +#define _CAM_REQ_MGR_DEBUG_H_ + +#include +#include "cam_req_mgr_core.h" + +int cam_req_mgr_debug_register(struct cam_req_mgr_core_device *core_dev); + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.c new file mode 100644 index 000000000000..223cfc6f6ca4 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.c @@ -0,0 +1,759 @@ +/* Copyright (c) 2016-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_req_mgr_dev.h" +#include "cam_req_mgr_util.h" +#include "cam_req_mgr_core.h" +#include "cam_subdev.h" +#include "cam_mem_mgr.h" +#include "cam_debug_util.h" +#include + +#define CAM_REQ_MGR_EVENT_MAX 30 + +static struct cam_req_mgr_device g_dev; +struct kmem_cache *g_cam_req_mgr_timer_cachep; + +static int cam_media_device_setup(struct device *dev) +{ + int rc; + + g_dev.v4l2_dev->mdev = kzalloc(sizeof(*g_dev.v4l2_dev->mdev), + GFP_KERNEL); + if (!g_dev.v4l2_dev->mdev) { + rc = -ENOMEM; + goto mdev_fail; + } + + media_device_init(g_dev.v4l2_dev->mdev); + g_dev.v4l2_dev->mdev->dev = dev; + strlcpy(g_dev.v4l2_dev->mdev->model, CAM_REQ_MGR_VNODE_NAME, + sizeof(g_dev.v4l2_dev->mdev->model)); + + rc = media_device_register(g_dev.v4l2_dev->mdev); + if (rc) + goto media_fail; + + return rc; + +media_fail: + kfree(g_dev.v4l2_dev->mdev); + g_dev.v4l2_dev->mdev = NULL; +mdev_fail: + return rc; +} + +static void cam_media_device_cleanup(void) +{ + media_entity_cleanup(&g_dev.video->entity); + media_device_unregister(g_dev.v4l2_dev->mdev); + kfree(g_dev.v4l2_dev->mdev); + g_dev.v4l2_dev->mdev = NULL; +} + +static int cam_v4l2_device_setup(struct device *dev) +{ + int rc; + + g_dev.v4l2_dev = kzalloc(sizeof(*g_dev.v4l2_dev), + GFP_KERNEL); + if (!g_dev.v4l2_dev) + return -ENOMEM; + + rc = v4l2_device_register(dev, g_dev.v4l2_dev); + if (rc) + goto reg_fail; + + return rc; + +reg_fail: + kfree(g_dev.v4l2_dev); + g_dev.v4l2_dev = NULL; + return rc; +} + +static void cam_v4l2_device_cleanup(void) +{ + v4l2_device_unregister(g_dev.v4l2_dev); + kfree(g_dev.v4l2_dev); + g_dev.v4l2_dev = NULL; +} + +static int cam_req_mgr_open(struct file *filep) +{ + int rc; + + mutex_lock(&g_dev.cam_lock); + if (g_dev.open_cnt >= 1) { + rc = -EALREADY; + goto end; + } + + rc = v4l2_fh_open(filep); + if (rc) { + CAM_ERR(CAM_CRM, "v4l2_fh_open failed: %d", rc); + goto end; + } + + spin_lock_bh(&g_dev.cam_eventq_lock); + g_dev.cam_eventq = filep->private_data; + spin_unlock_bh(&g_dev.cam_eventq_lock); + + g_dev.open_cnt++; + rc = cam_mem_mgr_init(); + if (rc) { + g_dev.open_cnt--; + CAM_ERR(CAM_CRM, "mem mgr init failed"); + goto mem_mgr_init_fail; + } + + mutex_unlock(&g_dev.cam_lock); + return rc; + +mem_mgr_init_fail: + v4l2_fh_release(filep); +end: + mutex_unlock(&g_dev.cam_lock); + return rc; +} + +static unsigned int cam_req_mgr_poll(struct file *f, + struct poll_table_struct *pll_table) +{ + int rc = 0; + struct v4l2_fh *eventq = f->private_data; + + if (!eventq) + return -EINVAL; + + poll_wait(f, &eventq->wait, pll_table); + if (v4l2_event_pending(eventq)) + rc = POLLPRI; + + return rc; +} + +static int cam_req_mgr_close(struct file *filep) +{ + struct v4l2_subdev *sd; + struct v4l2_fh *vfh = filep->private_data; + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); + + CAM_WARN(CAM_CRM, + "release invoked associated userspace process has died"); + mutex_lock(&g_dev.cam_lock); + + if (g_dev.open_cnt <= 0) { + mutex_unlock(&g_dev.cam_lock); + return -EINVAL; + } + + cam_req_mgr_handle_core_shutdown(); + + list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) + continue; + if (sd->internal_ops && sd->internal_ops->close) { + CAM_DBG(CAM_CRM, "Invoke subdev close for device %s", + sd->name); + sd->internal_ops->close(sd, subdev_fh); + } + } + + g_dev.open_cnt--; + v4l2_fh_release(filep); + + spin_lock_bh(&g_dev.cam_eventq_lock); + g_dev.cam_eventq = NULL; + spin_unlock_bh(&g_dev.cam_eventq_lock); + + cam_req_mgr_util_free_hdls(); + cam_mem_mgr_deinit(); + mutex_unlock(&g_dev.cam_lock); + + return 0; +} + +static struct v4l2_file_operations g_cam_fops = { + .owner = THIS_MODULE, + .open = cam_req_mgr_open, + .poll = cam_req_mgr_poll, + .release = cam_req_mgr_close, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static int cam_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_subscribe(fh, sub, CAM_REQ_MGR_EVENT_MAX, NULL); +} + +static int cam_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} + +static long cam_private_ioctl(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + int rc; + struct cam_control *k_ioctl; + + if ((!arg) || (cmd != VIDIOC_CAM_CONTROL)) + return -EINVAL; + + k_ioctl = (struct cam_control *)arg; + + if (!k_ioctl->handle) + return -EINVAL; + + switch (k_ioctl->op_code) { + case CAM_REQ_MGR_CREATE_SESSION: { + struct cam_req_mgr_session_info ses_info; + + if (k_ioctl->size != sizeof(ses_info)) + return -EINVAL; + + if (copy_from_user(&ses_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_create_session(&ses_info); + if (!rc) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->handle), + &ses_info, + k_ioctl->size)) + rc = -EFAULT; + } + break; + + case CAM_REQ_MGR_DESTROY_SESSION: { + struct cam_req_mgr_session_info ses_info; + + if (k_ioctl->size != sizeof(ses_info)) + return -EINVAL; + + if (copy_from_user(&ses_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_destroy_session(&ses_info); + } + break; + + case CAM_REQ_MGR_LINK: { + struct cam_req_mgr_link_info link_info; + + if (k_ioctl->size != sizeof(link_info)) + return -EINVAL; + + if (copy_from_user(&link_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_link(&link_info); + if (!rc) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->handle), + &link_info, + k_ioctl->size)) + rc = -EFAULT; + } + break; + + case CAM_REQ_MGR_UNLINK: { + struct cam_req_mgr_unlink_info unlink_info; + + if (k_ioctl->size != sizeof(unlink_info)) + return -EINVAL; + + if (copy_from_user(&unlink_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_unlink(&unlink_info); + } + break; + + case CAM_REQ_MGR_SCHED_REQ: { + struct cam_req_mgr_sched_request sched_req; + + if (k_ioctl->size != sizeof(sched_req)) + return -EINVAL; + + if (copy_from_user(&sched_req, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_schedule_request(&sched_req); + } + break; + + case CAM_REQ_MGR_FLUSH_REQ: { + struct cam_req_mgr_flush_info flush_info; + + if (k_ioctl->size != sizeof(flush_info)) + return -EINVAL; + + if (copy_from_user(&flush_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_flush_requests(&flush_info); + } + break; + + case CAM_REQ_MGR_SYNC_MODE: { + struct cam_req_mgr_sync_mode sync_info; + + if (k_ioctl->size != sizeof(sync_info)) + return -EINVAL; + + if (copy_from_user(&sync_info, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + return -EFAULT; + } + + rc = cam_req_mgr_sync_config(&sync_info); + } + break; + case CAM_REQ_MGR_ALLOC_BUF: { + struct cam_mem_mgr_alloc_cmd cmd; + + if (k_ioctl->size != sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(&cmd, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + rc = -EFAULT; + break; + } + + rc = cam_mem_mgr_alloc_and_map(&cmd); + if (!rc) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->handle), + &cmd, k_ioctl->size)) { + rc = -EFAULT; + break; + } + } + break; + case CAM_REQ_MGR_MAP_BUF: { + struct cam_mem_mgr_map_cmd cmd; + + if (k_ioctl->size != sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(&cmd, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + rc = -EFAULT; + break; + } + + rc = cam_mem_mgr_map(&cmd); + if (!rc) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->handle), + &cmd, k_ioctl->size)) { + rc = -EFAULT; + break; + } + } + break; + case CAM_REQ_MGR_RELEASE_BUF: { + struct cam_mem_mgr_release_cmd cmd; + + if (k_ioctl->size != sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(&cmd, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + rc = -EFAULT; + break; + } + + rc = cam_mem_mgr_release(&cmd); + } + break; + case CAM_REQ_MGR_CACHE_OPS: { + struct cam_mem_cache_ops_cmd cmd; + + if (k_ioctl->size != sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(&cmd, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + rc = -EFAULT; + break; + } + + rc = cam_mem_mgr_cache_ops(&cmd); + if (rc) + rc = -EINVAL; + } + break; + case CAM_REQ_MGR_LINK_CONTROL: { + struct cam_req_mgr_link_control cmd; + + if (k_ioctl->size != sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(&cmd, + u64_to_user_ptr(k_ioctl->handle), + k_ioctl->size)) { + rc = -EFAULT; + break; + } + + rc = cam_req_mgr_link_control(&cmd); + if (rc) + rc = -EINVAL; + } + break; + default: + return -ENOIOCTLCMD; + } + + return rc; +} + +static const struct v4l2_ioctl_ops g_cam_ioctl_ops = { + .vidioc_subscribe_event = cam_subscribe_event, + .vidioc_unsubscribe_event = cam_unsubscribe_event, + .vidioc_default = cam_private_ioctl, +}; + +static int cam_video_device_setup(void) +{ + int rc; + + g_dev.video = video_device_alloc(); + if (!g_dev.video) { + rc = -ENOMEM; + goto video_fail; + } + + g_dev.video->v4l2_dev = g_dev.v4l2_dev; + + strlcpy(g_dev.video->name, "cam-req-mgr", + sizeof(g_dev.video->name)); + g_dev.video->release = video_device_release; + g_dev.video->fops = &g_cam_fops; + g_dev.video->ioctl_ops = &g_cam_ioctl_ops; + g_dev.video->minor = -1; + g_dev.video->vfl_type = VFL_TYPE_GRABBER; + rc = video_register_device(g_dev.video, VFL_TYPE_GRABBER, -1); + if (rc) + goto v4l2_fail; + + rc = media_entity_pads_init(&g_dev.video->entity, 0, NULL); + if (rc) + goto entity_fail; + + g_dev.video->entity.function = CAM_VNODE_DEVICE_TYPE; + g_dev.video->entity.name = video_device_node_name(g_dev.video); + + return rc; + +entity_fail: + video_unregister_device(g_dev.video); +v4l2_fail: + video_device_release(g_dev.video); + g_dev.video = NULL; +video_fail: + return rc; +} + +int cam_req_mgr_notify_message(struct cam_req_mgr_message *msg, + uint32_t id, + uint32_t type) +{ + struct v4l2_event event; + struct cam_req_mgr_message *ev_header; + + if (!msg) + return -EINVAL; + + event.id = id; + event.type = type; + ev_header = CAM_REQ_MGR_GET_PAYLOAD_PTR(event, + struct cam_req_mgr_message); + memcpy(ev_header, msg, sizeof(struct cam_req_mgr_message)); + v4l2_event_queue(g_dev.video, &event); + + return 0; +} +EXPORT_SYMBOL(cam_req_mgr_notify_message); + +void cam_video_device_cleanup(void) +{ + video_unregister_device(g_dev.video); + video_device_release(g_dev.video); + g_dev.video = NULL; +} + +void cam_register_subdev_fops(struct v4l2_file_operations *fops) +{ + *fops = v4l2_subdev_fops; +} +EXPORT_SYMBOL(cam_register_subdev_fops); + +int cam_register_subdev(struct cam_subdev *csd) +{ + struct v4l2_subdev *sd; + int rc; + + if (g_dev.state != true) { + CAM_ERR(CAM_CRM, "camera root device not ready yet"); + return -ENODEV; + } + + if (!csd || !csd->name) { + CAM_ERR(CAM_CRM, "invalid arguments"); + return -EINVAL; + } + + mutex_lock(&g_dev.dev_lock); + if ((g_dev.subdev_nodes_created) && + (csd->sd_flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) { + CAM_ERR(CAM_CRM, + "dynamic node is not allowed, name: %s, type :%d", + csd->name, csd->ent_function); + rc = -EINVAL; + goto reg_fail; + } + + sd = &csd->sd; + v4l2_subdev_init(sd, csd->ops); + sd->internal_ops = csd->internal_ops; + snprintf(sd->name, ARRAY_SIZE(sd->name), csd->name); + v4l2_set_subdevdata(sd, csd->token); + + sd->flags = csd->sd_flags; + sd->entity.num_pads = 0; + sd->entity.pads = NULL; + sd->entity.function = csd->ent_function; + + rc = v4l2_device_register_subdev(g_dev.v4l2_dev, sd); + if (rc) { + CAM_ERR(CAM_CRM, "register subdev failed"); + goto reg_fail; + } + g_dev.count++; + +reg_fail: + mutex_unlock(&g_dev.dev_lock); + return rc; +} +EXPORT_SYMBOL(cam_register_subdev); + +int cam_unregister_subdev(struct cam_subdev *csd) +{ + if (g_dev.state != true) { + CAM_ERR(CAM_CRM, "camera root device not ready yet"); + return -ENODEV; + } + + mutex_lock(&g_dev.dev_lock); + v4l2_device_unregister_subdev(&csd->sd); + g_dev.count--; + mutex_unlock(&g_dev.dev_lock); + + return 0; +} +EXPORT_SYMBOL(cam_unregister_subdev); + +static int cam_req_mgr_remove(struct platform_device *pdev) +{ + cam_req_mgr_core_device_deinit(); + cam_req_mgr_util_deinit(); + cam_media_device_cleanup(); + cam_video_device_cleanup(); + cam_v4l2_device_cleanup(); + mutex_destroy(&g_dev.dev_lock); + g_dev.state = false; + g_dev.subdev_nodes_created = false; + + return 0; +} + +static int cam_req_mgr_probe(struct platform_device *pdev) +{ + int rc; + + rc = cam_v4l2_device_setup(&pdev->dev); + if (rc) + return rc; + + rc = cam_media_device_setup(&pdev->dev); + if (rc) + goto media_setup_fail; + + rc = cam_video_device_setup(); + if (rc) + goto video_setup_fail; + + g_dev.open_cnt = 0; + mutex_init(&g_dev.cam_lock); + spin_lock_init(&g_dev.cam_eventq_lock); + g_dev.subdev_nodes_created = false; + mutex_init(&g_dev.dev_lock); + + rc = cam_req_mgr_util_init(); + if (rc) { + CAM_ERR(CAM_CRM, "cam req mgr util init is failed"); + goto req_mgr_util_fail; + } + + rc = cam_req_mgr_core_device_init(); + if (rc) { + CAM_ERR(CAM_CRM, "core device setup failed"); + goto req_mgr_core_fail; + } + + g_dev.state = true; + + if (g_cam_req_mgr_timer_cachep == NULL) { + g_cam_req_mgr_timer_cachep = kmem_cache_create("crm_timer", + sizeof(struct cam_req_mgr_timer), 64, + SLAB_CONSISTENCY_CHECKS | SLAB_RED_ZONE | + SLAB_POISON | SLAB_STORE_USER, NULL); + if (!g_cam_req_mgr_timer_cachep) + CAM_ERR(CAM_CRM, + "Failed to create kmem_cache for crm_timer"); + else + CAM_DBG(CAM_CRM, "Name : %s", + g_cam_req_mgr_timer_cachep->name); + } + + return rc; + +req_mgr_core_fail: + cam_req_mgr_util_deinit(); +req_mgr_util_fail: + mutex_destroy(&g_dev.dev_lock); + mutex_destroy(&g_dev.cam_lock); + cam_video_device_cleanup(); +video_setup_fail: + cam_media_device_cleanup(); +media_setup_fail: + cam_v4l2_device_cleanup(); + return rc; +} + +static const struct of_device_id cam_req_mgr_dt_match[] = { + {.compatible = "qcom,cam-req-mgr"}, + {} +}; +MODULE_DEVICE_TABLE(of, cam_dt_match); + +static struct platform_driver cam_req_mgr_driver = { + .probe = cam_req_mgr_probe, + .remove = cam_req_mgr_remove, + .driver = { + .name = "cam_req_mgr", + .owner = THIS_MODULE, + .of_match_table = cam_req_mgr_dt_match, + }, +}; + +int cam_dev_mgr_create_subdev_nodes(void) +{ + int rc; + struct v4l2_subdev *sd; + + if (!g_dev.v4l2_dev) + return -EINVAL; + + if (g_dev.state != true) { + CAM_ERR(CAM_CRM, "camera root device not ready yet"); + return -ENODEV; + } + + mutex_lock(&g_dev.dev_lock); + if (g_dev.subdev_nodes_created) { + rc = -EEXIST; + goto create_fail; + } + + rc = v4l2_device_register_subdev_nodes(g_dev.v4l2_dev); + if (rc) { + CAM_ERR(CAM_CRM, "failed to register the sub devices"); + goto create_fail; + } + + list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) + continue; + sd->entity.name = video_device_node_name(sd->devnode); + CAM_DBG(CAM_CRM, "created node :%s", sd->entity.name); + } + + g_dev.subdev_nodes_created = true; + +create_fail: + mutex_unlock(&g_dev.dev_lock); + return rc; +} + +static int __init cam_req_mgr_init(void) +{ + return platform_driver_register(&cam_req_mgr_driver); +} + +static int __init cam_req_mgr_late_init(void) +{ + return cam_dev_mgr_create_subdev_nodes(); +} + +static void __exit cam_req_mgr_exit(void) +{ + platform_driver_unregister(&cam_req_mgr_driver); +} + +module_init(cam_req_mgr_init); +late_initcall(cam_req_mgr_late_init); +module_exit(cam_req_mgr_exit); +MODULE_DESCRIPTION("Camera Request Manager"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.h new file mode 100644 index 000000000000..93278b8e8b0c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_dev.h @@ -0,0 +1,50 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_REQ_MGR_DEV_H_ +#define _CAM_REQ_MGR_DEV_H_ + +/** + * struct cam_req_mgr_device - a camera request manager device + * + * @video: pointer to struct video device. + * @v4l2_dev: pointer to struct v4l2 device. + * @subdev_nodes_created: flag to check the created state. + * @count: number of subdevices registered. + * @dev_lock: lock for the subdevice count. + * @state: state of the root device. + * @open_cnt: open count of subdev + * @cam_lock: per file handle lock + * @cam_eventq: event queue + * @cam_eventq_lock: lock for event queue + */ +struct cam_req_mgr_device { + struct video_device *video; + struct v4l2_device *v4l2_dev; + bool subdev_nodes_created; + int count; + struct mutex dev_lock; + bool state; + int32_t open_cnt; + struct mutex cam_lock; + struct v4l2_fh *cam_eventq; + spinlock_t cam_eventq_lock; +}; + +#define CAM_REQ_MGR_GET_PAYLOAD_PTR(ev, type) \ + (type *)((char *)ev.u.data) + +int cam_req_mgr_notify_message(struct cam_req_mgr_message *msg, + uint32_t id, + uint32_t type); + +#endif /* _CAM_REQ_MGR_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_interface.h new file mode 100644 index 000000000000..1ca6cc598ec8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_interface.h @@ -0,0 +1,332 @@ +/* Copyright (c) 2016-2018, 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 _CAM_REQ_MGR_INTERFACE_H +#define _CAM_REQ_MGR_INTERFACE_H + +#include +#include +#include "cam_req_mgr_core_defs.h" +#include "cam_req_mgr_util.h" + +struct cam_req_mgr_trigger_notify; +struct cam_req_mgr_error_notify; +struct cam_req_mgr_add_request; +struct cam_req_mgr_device_info; +struct cam_req_mgr_core_dev_link_setup; +struct cam_req_mgr_apply_request; +struct cam_req_mgr_flush_request; +struct cam_req_mgr_link_evt_data; + +/* Request Manager -- camera device driver interface */ +/** + * @brief: camera kernel drivers to cam req mgr communication + * + * @cam_req_mgr_notify_trigger: for device which generates trigger to inform CRM + * @cam_req_mgr_notify_err : device use this to inform about different errors + * @cam_req_mgr_add_req : to info CRm about new rqeuest received from + * userspace + */ +typedef int (*cam_req_mgr_notify_trigger)( + struct cam_req_mgr_trigger_notify *); +typedef int (*cam_req_mgr_notify_err)(struct cam_req_mgr_error_notify *); +typedef int (*cam_req_mgr_add_req)(struct cam_req_mgr_add_request *); + +/** + * @brief: cam req mgr to camera device drivers + * + * @cam_req_mgr_get_dev_info: to fetch details about device linked + * @cam_req_mgr_link_setup : to establish link with device for a session + * @cam_req_mgr_notify_err : to broadcast error happened on link for request id + * @cam_req_mgr_apply_req : CRM asks device to apply certain request id. + * @cam_req_mgr_flush_req : Flush or cancle request + * cam_req_mgr_process_evt : generic events + */ +typedef int (*cam_req_mgr_get_dev_info) (struct cam_req_mgr_device_info *); +typedef int (*cam_req_mgr_link_setup)( + struct cam_req_mgr_core_dev_link_setup *); +typedef int (*cam_req_mgr_apply_req)(struct cam_req_mgr_apply_request *); +typedef int (*cam_req_mgr_flush_req)(struct cam_req_mgr_flush_request *); +typedef int (*cam_req_mgr_process_evt)(struct cam_req_mgr_link_evt_data *); + +/** + * @brief : cam_req_mgr_crm_cb - func table + * + * @notify_trigger : payload for trigger indication event + * @notify_err : payload for different error occurred at device + * @add_req : payload to inform which device and what request is received + */ +struct cam_req_mgr_crm_cb { + cam_req_mgr_notify_trigger notify_trigger; + cam_req_mgr_notify_err notify_err; + cam_req_mgr_add_req add_req; +}; + +/** + * @brief : cam_req_mgr_kmd_ops - func table + * + * @get_dev_info : payload to fetch device details + * @link_setup : payload to establish link with device + * @apply_req : payload to apply request id on a device linked + * @flush_req : payload to flush request + * @process_evt : payload to generic event + */ +struct cam_req_mgr_kmd_ops { + cam_req_mgr_get_dev_info get_dev_info; + cam_req_mgr_link_setup link_setup; + cam_req_mgr_apply_req apply_req; + cam_req_mgr_flush_req flush_req; + cam_req_mgr_process_evt process_evt; +}; + +/** + * enum cam_pipeline_delay + * @brief : enumerator for different pipeline delays in camera + * + * @DELAY_0 : device processed settings on same frame + * @DELAY_1 : device processed settings after 1 frame + * @DELAY_2 : device processed settings after 2 frames + * @DELAY_MAX : maximum supported pipeline delay + */ +enum cam_pipeline_delay { + CAM_PIPELINE_DELAY_0, + CAM_PIPELINE_DELAY_1, + CAM_PIPELINE_DELAY_2, + CAM_PIPELINE_DELAY_MAX, +}; + +/** + * @CAM_TRIGGER_POINT_SOF : Trigger point for SOF + * @CAM_TRIGGER_POINT_EOF : Trigger point for EOF + */ +#define CAM_TRIGGER_POINT_SOF (1 << 0) +#define CAM_TRIGGER_POINT_EOF (1 << 1) + +/** + * enum cam_req_status + * @brief : enumerator for request status + * + * @SUCCESS : device processed settings successfully + * @FAILED : device processed settings failed + * @MAX : invalid status value + */ +enum cam_req_status { + CAM_REQ_STATUS_SUCCESS, + CAM_REQ_STATUS_FAILED, + CAM_REQ_STATUS_MAX, +}; + +/** + * enum cam_req_mgr_device_error + * @brief : enumerator for different errors occurred at device + * + * @NOT_FOUND : settings asked by request manager is not found + * @BUBBLE : device hit timing issue and is able to recover + * @FATAL : device is in bad shape and can not recover from error + * @PAGE_FAULT : Page fault while accessing memory + * @OVERFLOW : Bus Overflow for IFE/VFE + * @TIMEOUT : Timeout from cci or bus. + * @MAX : Invalid error value + */ +enum cam_req_mgr_device_error { + CRM_KMD_ERR_NOT_FOUND, + CRM_KMD_ERR_BUBBLE, + CRM_KMD_ERR_FATAL, + CRM_KMD_ERR_PAGE_FAULT, + CRM_KMD_ERR_OVERFLOW, + CRM_KMD_ERR_TIMEOUT, + CRM_KMD_ERR_MAX, +}; + +/** + * enum cam_req_mgr_device_id + * @brief : enumerator for different devices in subsystem + * + * @CAM_REQ_MGR : request manager itself + * @SENSOR : sensor device + * @FLASH : LED flash or dual LED device + * @ACTUATOR : lens mover + * @IFE : Image processing device + * @EXTERNAL_1 : third party device + * @EXTERNAL_2 : third party device + * @EXTERNAL_3 : third party device + * @MAX : invalid device id + */ +enum cam_req_mgr_device_id { + CAM_REQ_MGR_DEVICE, + CAM_REQ_MGR_DEVICE_SENSOR, + CAM_REQ_MGR_DEVICE_FLASH, + CAM_REQ_MGR_DEVICE_ACTUATOR, + CAM_REQ_MGR_DEVICE_IFE, + CAM_REQ_MGR_DEVICE_EXTERNAL_1, + CAM_REQ_MGR_DEVICE_EXTERNAL_2, + CAM_REQ_MGR_DEVICE_EXTERNAL_3, + CAM_REQ_MGR_DEVICE_ID_MAX, +}; + +/* Camera device driver to Req Mgr device interface */ + +/** + * enum cam_req_mgr_link_evt_type + * @CAM_REQ_MGR_LINK_EVT_ERR: + * @CAM_REQ_MGR_LINK_EVT_MAX: + */ +enum cam_req_mgr_link_evt_type { + CAM_REQ_MGR_LINK_EVT_ERR, + CAM_REQ_MGR_LINK_EVT_PAUSE, + CAM_REQ_MGR_LINK_EVT_RESUME, + CAM_REQ_MGR_LINK_EVT_MAX, +}; + +/** + * struct cam_req_mgr_trigger_notify + * @link_hdl : link identifier + * @dev_hdl : device handle which has sent this req id + * @frame_id : frame id for internal tracking + * @trigger : trigger point of this notification, CRM will send apply + * only to the devices which subscribe to this point. + */ +struct cam_req_mgr_trigger_notify { + int32_t link_hdl; + int32_t dev_hdl; + int64_t frame_id; + uint32_t trigger; +}; + +/** + * struct cam_req_mgr_error_notify + * @link_hdl : link identifier + * @dev_hdl : device handle which has sent this req id + * @req_id : req id which hit error + * @error : what error device hit while processing this req + */ +struct cam_req_mgr_error_notify { + int32_t link_hdl; + int32_t dev_hdl; + uint64_t req_id; + enum cam_req_mgr_device_error error; +}; + +/** + * struct cam_req_mgr_add_request + * @link_hdl : link identifier + * @dev_hdl : device handle which has sent this req id + * @req_id : req id which device is ready to process + * @skip_before_applying : before applying req mgr introduce bubble + * by not sending request to device/s. + * ex: IFE and Flash + */ +struct cam_req_mgr_add_request { + int32_t link_hdl; + int32_t dev_hdl; + uint64_t req_id; + uint32_t skip_before_applying; +}; + + +/* CRM to KMD devices */ +/** + * struct cam_req_mgr_device_info + * @dev_hdl : Input_param : device handle for reference + * @name : link link or unlink + * @dev_id : device id info + * @p_delay : delay between time settings applied and take effect + * @trigger : Trigger point for the client + * + */ +struct cam_req_mgr_device_info { + int32_t dev_hdl; + char name[256]; + enum cam_req_mgr_device_id dev_id; + enum cam_pipeline_delay p_delay; + uint32_t trigger; +}; + +/** + * struct cam_req_mgr_core_dev_link_setup + * @link_enable : link link or unlink + * @link_hdl : link identifier + * @dev_hdl : device handle for reference + * @max_delay : max pipeline delay on this link + * @crm_cb : callback funcs to communicate with req mgr + * @subscribe_event : the mask of trigger points this link subscribes + * + */ +struct cam_req_mgr_core_dev_link_setup { + int32_t link_enable; + int32_t link_hdl; + int32_t dev_hdl; + enum cam_pipeline_delay max_delay; + struct cam_req_mgr_crm_cb *crm_cb; + uint32_t subscribe_event; +}; + +/** + * struct cam_req_mgr_apply_request + * @link_hdl : link identifier + * @dev_hdl : device handle for cross check + * @request_id : request id settings to apply + * @report_if_bubble : report to crm if failure in applying + * @trigger_point : the trigger point of this apply + * + */ +struct cam_req_mgr_apply_request { + int32_t link_hdl; + int32_t dev_hdl; + uint64_t request_id; + int32_t report_if_bubble; + uint32_t trigger_point; +}; + +/** + * struct cam_req_mgr_flush_request + * @link_hdl : link identifier + * @dev_hdl : device handle for cross check + * @type : cancel request type flush all or a request + * @request_id : request id to cancel + * + */ +struct cam_req_mgr_flush_request { + int32_t link_hdl; + int32_t dev_hdl; + uint32_t type; + uint64_t req_id; +}; + +/** + * struct cam_req_mgr_event_data + * @link_hdl : link handle + * @req_id : request id + * + */ +struct cam_req_mgr_link_evt_data { + int32_t link_hdl; + int32_t dev_hdl; + uint64_t req_id; + + enum cam_req_mgr_link_evt_type evt_type; + union { + enum cam_req_mgr_device_error error; + } u; +}; + +/** + * struct cam_req_mgr_send_request + * @link_hdl : link identifier + * @idx : slot idx + * + */ +struct cam_req_mgr_send_request { + int32_t link_hdl; + struct cam_req_mgr_req_queue *in_q; +}; +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.c new file mode 100644 index 000000000000..4d8a2ec32f9b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2016-2018, 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 "cam_req_mgr_timer.h" +#include "cam_debug_util.h" + +extern struct kmem_cache *g_cam_req_mgr_timer_cachep; + +void crm_timer_reset(struct cam_req_mgr_timer *crm_timer) +{ + if (!crm_timer) + return; + CAM_DBG(CAM_CRM, "Starting timer to fire in %d ms. (jiffies=%lu)\n", + crm_timer->expires, jiffies); + mod_timer(&crm_timer->sys_timer, + (jiffies + msecs_to_jiffies(crm_timer->expires))); +} + +void crm_timer_callback(struct timer_list *timer_data) +{ + struct cam_req_mgr_timer *timer = + container_of(timer_data, struct cam_req_mgr_timer, sys_timer); + if (!timer) { + CAM_ERR(CAM_CRM, "NULL timer"); + return; + } + CAM_DBG(CAM_CRM, "timer %pK parent %pK", timer, timer->parent); + crm_timer_reset(timer); +} + +void crm_timer_modify(struct cam_req_mgr_timer *crm_timer, + int32_t expires) +{ + CAM_DBG(CAM_CRM, "new time %d", expires); + if (crm_timer) { + crm_timer->expires = expires; + crm_timer_reset(crm_timer); + } +} + +int crm_timer_init(struct cam_req_mgr_timer **timer, + int32_t expires, void *parent, void (*timer_cb)(struct timer_list *)) +{ + int ret = 0; + struct cam_req_mgr_timer *crm_timer = NULL; + + CAM_DBG(CAM_CRM, "init timer %d %pK", expires, *timer); + if (*timer == NULL) { + if (g_cam_req_mgr_timer_cachep) { + crm_timer = kmem_cache_alloc(g_cam_req_mgr_timer_cachep, + __GFP_ZERO | GFP_KERNEL); + if (!crm_timer) { + ret = -ENOMEM; + goto end; + } + } + + else { + ret = -ENOMEM; + goto end; + } + + if (timer_cb != NULL) + crm_timer->timer_cb = timer_cb; + else + crm_timer->timer_cb = crm_timer_callback; + + crm_timer->expires = expires; + crm_timer->parent = parent; + timer_setup(&crm_timer->sys_timer, + crm_timer->timer_cb, 0); + crm_timer_reset(crm_timer); + *timer = crm_timer; + } else { + CAM_WARN(CAM_CRM, "Timer already exists!!"); + ret = -EINVAL; + } +end: + return ret; +} +void crm_timer_exit(struct cam_req_mgr_timer **crm_timer) +{ + CAM_DBG(CAM_CRM, "destroy timer %pK @ %pK", *crm_timer, crm_timer); + if (*crm_timer) { + del_timer_sync(&(*crm_timer)->sys_timer); + if (g_cam_req_mgr_timer_cachep) + kmem_cache_free(g_cam_req_mgr_timer_cachep, *crm_timer); + *crm_timer = NULL; + } +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.h new file mode 100644 index 000000000000..587e17f5e0b0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_timer.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2016-2018, 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 _CAM_REQ_MGR_TIMER_H_ +#define _CAM_REQ_MGR_TIMER_H_ + +#include +#include + +#include "cam_req_mgr_core_defs.h" + +/** struct cam_req_mgr_timer + * @expires : timeout value for timer + * @sys_timer : system timer variable + * @parent : priv data - link pointer + * @timer_cb : callback func which will be called when timeout expires + * @pause_timer : flag to pause SOF timer + */ +struct cam_req_mgr_timer { + int32_t expires; + struct timer_list sys_timer; + void *parent; + void (*timer_cb)(struct timer_list *timer_data); + bool pause_timer; +}; + +/** + * crm_timer_modify() + * @brief : allows ser to modify expiry time. + * @timer : timer which will be reset to expires values + */ +void crm_timer_modify(struct cam_req_mgr_timer *crm_timer, + int32_t expires); + +/** + * crm_timer_reset() + * @brief : destroys the timer allocated. + * @timer : timer which will be reset to expires values + */ +void crm_timer_reset(struct cam_req_mgr_timer *timer); + +/** + * crm_timer_init() + * @brief : create a new general purpose timer. + * timer utility takes care of allocating memory and deleting + * @timer : double pointer to new timer allocated + * @expires : Timeout value to fire callback + * @parent : void pointer which caller can use for book keeping + * @timer_cb : caller can chose to use its own callback function when + * timer fires the timeout. If no value is set timer util + * will use default. + */ +int crm_timer_init(struct cam_req_mgr_timer **timer, + int32_t expires, void *parent, void (*timer_cb)(struct timer_list *)); + +/** + * crm_timer_exit() + * @brief : destroys the timer allocated. + * @timer : timer pointer which will be freed + */ +void crm_timer_exit(struct cam_req_mgr_timer **timer); + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.c new file mode 100644 index 000000000000..dda04f8e5164 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.c @@ -0,0 +1,340 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "CAM-REQ-MGR_UTIL %s:%d " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include "cam_req_mgr_util.h" +#include "cam_debug_util.h" + +static struct cam_req_mgr_util_hdl_tbl *hdl_tbl; +static DEFINE_SPINLOCK(hdl_tbl_lock); + +int cam_req_mgr_util_init(void) +{ + int rc = 0; + int bitmap_size; + static struct cam_req_mgr_util_hdl_tbl *hdl_tbl_local; + + if (hdl_tbl) { + rc = -EINVAL; + CAM_ERR(CAM_CRM, "Hdl_tbl is already present"); + goto hdl_tbl_check_failed; + } + + hdl_tbl_local = kzalloc(sizeof(*hdl_tbl), GFP_KERNEL); + if (!hdl_tbl_local) { + rc = -ENOMEM; + goto hdl_tbl_alloc_failed; + } + spin_lock_bh(&hdl_tbl_lock); + if (hdl_tbl) { + spin_unlock_bh(&hdl_tbl_lock); + rc = -EEXIST; + kfree(hdl_tbl_local); + goto hdl_tbl_check_failed; + } + hdl_tbl = hdl_tbl_local; + spin_unlock_bh(&hdl_tbl_lock); + + bitmap_size = BITS_TO_LONGS(CAM_REQ_MGR_MAX_HANDLES) * sizeof(long); + hdl_tbl->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!hdl_tbl->bitmap) { + rc = -ENOMEM; + goto bitmap_alloc_fail; + } + hdl_tbl->bits = bitmap_size * BITS_PER_BYTE; + + return rc; + +bitmap_alloc_fail: + kfree(hdl_tbl); + hdl_tbl = NULL; +hdl_tbl_alloc_failed: +hdl_tbl_check_failed: + return rc; +} + +int cam_req_mgr_util_deinit(void) +{ + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + spin_unlock_bh(&hdl_tbl_lock); + return -EINVAL; + } + + kfree(hdl_tbl->bitmap); + hdl_tbl->bitmap = NULL; + kfree(hdl_tbl); + hdl_tbl = NULL; + spin_unlock_bh(&hdl_tbl_lock); + + return 0; +} + +int cam_req_mgr_util_free_hdls(void) +{ + int i = 0; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + spin_unlock_bh(&hdl_tbl_lock); + return -EINVAL; + } + + for (i = 0; i < CAM_REQ_MGR_MAX_HANDLES; i++) { + if (hdl_tbl->hdl[i].state == HDL_ACTIVE) { + CAM_ERR(CAM_CRM, "Dev handle = %x session_handle = %x", + hdl_tbl->hdl[i].hdl_value, + hdl_tbl->hdl[i].session_hdl); + hdl_tbl->hdl[i].state = HDL_FREE; + clear_bit(i, hdl_tbl->bitmap); + } + } + bitmap_zero(hdl_tbl->bitmap, CAM_REQ_MGR_MAX_HANDLES); + spin_unlock_bh(&hdl_tbl_lock); + + return 0; +} + +static int32_t cam_get_free_handle_index(void) +{ + int idx; + + idx = find_first_zero_bit(hdl_tbl->bitmap, hdl_tbl->bits); + + if (idx >= CAM_REQ_MGR_MAX_HANDLES || idx < 0) + return -ENOSR; + + set_bit(idx, hdl_tbl->bitmap); + + return idx; +} + +int32_t cam_create_session_hdl(void *priv) +{ + int idx; + int rand = 0; + int32_t handle = 0; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + spin_unlock_bh(&hdl_tbl_lock); + return -EINVAL; + } + + idx = cam_get_free_handle_index(); + if (idx < 0) { + CAM_ERR(CAM_CRM, "Unable to create session handle"); + spin_unlock_bh(&hdl_tbl_lock); + return idx; + } + + get_random_bytes(&rand, CAM_REQ_MGR_RND1_BYTES); + handle = GET_DEV_HANDLE(rand, HDL_TYPE_SESSION, idx); + hdl_tbl->hdl[idx].session_hdl = handle; + hdl_tbl->hdl[idx].hdl_value = handle; + hdl_tbl->hdl[idx].type = HDL_TYPE_SESSION; + hdl_tbl->hdl[idx].state = HDL_ACTIVE; + hdl_tbl->hdl[idx].priv = priv; + hdl_tbl->hdl[idx].ops = NULL; + spin_unlock_bh(&hdl_tbl_lock); + + return handle; +} + +int32_t cam_create_device_hdl(struct cam_create_dev_hdl *hdl_data) +{ + int idx; + int rand = 0; + int32_t handle; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + spin_unlock_bh(&hdl_tbl_lock); + return -EINVAL; + } + + idx = cam_get_free_handle_index(); + if (idx < 0) { + CAM_ERR(CAM_CRM, "Unable to create device handle"); + spin_unlock_bh(&hdl_tbl_lock); + return idx; + } + + get_random_bytes(&rand, CAM_REQ_MGR_RND1_BYTES); + handle = GET_DEV_HANDLE(rand, HDL_TYPE_DEV, idx); + hdl_tbl->hdl[idx].session_hdl = hdl_data->session_hdl; + hdl_tbl->hdl[idx].hdl_value = handle; + hdl_tbl->hdl[idx].type = HDL_TYPE_DEV; + hdl_tbl->hdl[idx].state = HDL_ACTIVE; + hdl_tbl->hdl[idx].priv = hdl_data->priv; + hdl_tbl->hdl[idx].ops = hdl_data->ops; + spin_unlock_bh(&hdl_tbl_lock); + + pr_debug("%s: handle = %x", __func__, handle); + return handle; +} + +void *cam_get_device_priv(int32_t dev_hdl) +{ + int idx; + int type; + void *priv; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "Hdl tbl is NULL"); + goto device_priv_fail; + } + + idx = CAM_REQ_MGR_GET_HDL_IDX(dev_hdl); + if (idx >= CAM_REQ_MGR_MAX_HANDLES) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid idx"); + goto device_priv_fail; + } + + if (hdl_tbl->hdl[idx].state != HDL_ACTIVE) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid state"); + goto device_priv_fail; + } + + type = CAM_REQ_MGR_GET_HDL_TYPE(dev_hdl); + if (HDL_TYPE_DEV != type && HDL_TYPE_SESSION != type) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid type"); + goto device_priv_fail; + } + + if (hdl_tbl->hdl[idx].hdl_value != dev_hdl) { + CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid hdl"); + goto device_priv_fail; + } + + priv = hdl_tbl->hdl[idx].priv; + spin_unlock_bh(&hdl_tbl_lock); + + return priv; + +device_priv_fail: + spin_unlock_bh(&hdl_tbl_lock); + return NULL; +} + +void *cam_get_device_ops(int32_t dev_hdl) +{ + int idx; + int type; + void *ops; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + goto device_ops_fail; + } + + idx = CAM_REQ_MGR_GET_HDL_IDX(dev_hdl); + if (idx >= CAM_REQ_MGR_MAX_HANDLES) { + CAM_ERR(CAM_CRM, "Invalid idx"); + goto device_ops_fail; + } + + if (hdl_tbl->hdl[idx].state != HDL_ACTIVE) { + CAM_ERR(CAM_CRM, "Invalid state"); + goto device_ops_fail; + } + + type = CAM_REQ_MGR_GET_HDL_TYPE(dev_hdl); + if (HDL_TYPE_DEV != type && HDL_TYPE_SESSION != type) { + CAM_ERR(CAM_CRM, "Invalid type"); + goto device_ops_fail; + } + + if (hdl_tbl->hdl[idx].hdl_value != dev_hdl) { + CAM_ERR(CAM_CRM, "Invalid hdl"); + goto device_ops_fail; + } + + ops = hdl_tbl->hdl[idx].ops; + spin_unlock_bh(&hdl_tbl_lock); + + return ops; + +device_ops_fail: + spin_unlock_bh(&hdl_tbl_lock); + return NULL; +} + +static int cam_destroy_hdl(int32_t dev_hdl, int dev_hdl_type) +{ + int idx; + int type; + + spin_lock_bh(&hdl_tbl_lock); + if (!hdl_tbl) { + CAM_ERR(CAM_CRM, "Hdl tbl is NULL"); + goto destroy_hdl_fail; + } + + idx = CAM_REQ_MGR_GET_HDL_IDX(dev_hdl); + if (idx >= CAM_REQ_MGR_MAX_HANDLES) { + CAM_ERR(CAM_CRM, "Invalid idx %d", idx); + goto destroy_hdl_fail; + } + + if (hdl_tbl->hdl[idx].state != HDL_ACTIVE) { + CAM_ERR(CAM_CRM, "Invalid state"); + goto destroy_hdl_fail; + } + + type = CAM_REQ_MGR_GET_HDL_TYPE(dev_hdl); + if (type != dev_hdl_type) { + CAM_ERR(CAM_CRM, "Invalid type %d, %d", type, dev_hdl_type); + goto destroy_hdl_fail; + } + + if (hdl_tbl->hdl[idx].hdl_value != dev_hdl) { + CAM_ERR(CAM_CRM, "Invalid hdl"); + goto destroy_hdl_fail; + } + + hdl_tbl->hdl[idx].state = HDL_FREE; + hdl_tbl->hdl[idx].ops = NULL; + hdl_tbl->hdl[idx].priv = NULL; + clear_bit(idx, hdl_tbl->bitmap); + spin_unlock_bh(&hdl_tbl_lock); + + return 0; + +destroy_hdl_fail: + spin_unlock_bh(&hdl_tbl_lock); + return -EINVAL; +} + +int cam_destroy_device_hdl(int32_t dev_hdl) +{ + return cam_destroy_hdl(dev_hdl, HDL_TYPE_DEV); +} + +int cam_destroy_session_hdl(int32_t dev_hdl) +{ + return cam_destroy_hdl(dev_hdl, HDL_TYPE_SESSION); +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.h new file mode 100644 index 000000000000..21cd6dd66117 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util.h @@ -0,0 +1,172 @@ +/* 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. + */ + +#ifndef _CAM_REQ_MGR_UTIL_API_H_ +#define _CAM_REQ_MGR_UTIL_API_H_ + +#include +#include "cam_req_mgr_util_priv.h" + +/** + * state of a handle(session/device) + * @HDL_FREE: free handle + * @HDL_ACTIVE: active handles + */ +enum hdl_state { + HDL_FREE, + HDL_ACTIVE +}; + +/** + * handle type + * @HDL_TYPE_DEV: for device and link + * @HDL_TYPE_SESSION: for session + */ +enum hdl_type { + HDL_TYPE_DEV = 1, + HDL_TYPE_SESSION +}; + +/** + * struct handle + * @session_hdl: session handle + * @hdl_value: Allocated handle + * @type: session/device handle + * @state: free/used + * @ops: ops structure + * @priv: private data of a handle + */ +struct handle { + int32_t session_hdl; + uint32_t hdl_value; + enum hdl_type type; + enum hdl_state state; + void *ops; + void *priv; +}; + +/** + * struct cam_req_mgr_util_hdl_tbl + * @hdl: row of handles + * @bitmap: bit map to get free hdl row idx + * @bits: size of bit map in bits + */ +struct cam_req_mgr_util_hdl_tbl { + struct handle hdl[CAM_REQ_MGR_MAX_HANDLES]; + void *bitmap; + size_t bits; +}; + +/** + * cam_req_mgr_util APIs for KMD drivers and cam_req_mgr + * @session_hdl: session_hdl info + * @v4l2_sub_dev_flag: flag to create v4l2 sub device + * @media_entity_flag: flag for media entity + * @reserved: reserved field + * @ops: ops pointer for a device handle + * @priv: private data for a device handle + */ +struct cam_create_dev_hdl { + int32_t session_hdl; + int32_t v4l2_sub_dev_flag; + int32_t media_entity_flag; + int32_t reserved; + void *ops; + void *priv; +}; + +/** + * cam_create_session_hdl() - create a session handle + * @priv: private data for a session handle + * + * cam_req_mgr core calls this function to get + * a unique session handle. Returns a unique session + * handle + */ +int32_t cam_create_session_hdl(void *priv); + +/** + * cam_create_device_hdl() - create a device handle + * @hdl_data: session hdl, flags, ops and priv dara as input + * + * cam_req_mgr_core calls this function to get + * session and link handles + * KMD drivers calls this function to create + * a device handle. Returns a unique device handle + */ +int32_t cam_create_device_hdl(struct cam_create_dev_hdl *hdl_data); + +/** + * cam_get_device_priv() - get private data of a handle + * @dev_hdl: handle for a session/link/device + * + * cam_req_mgr_core and KMD drivers use this function to + * get private data of a handle. Returns a private data + * structure pointer. + */ +void *cam_get_device_priv(int32_t dev_hdl); + +/** + * cam_get_device_ops() - get ops of a handle + * @dev_hdl: handle for a session/link/device + * + * cam_req_mgr_core and KMD drivers use this function to + * get ops of a handle. Returns a pointer to ops. + */ +void *cam_get_device_ops(int32_t dev_hdl); + +/** + * cam_destroy_device_hdl() - destroy device handle + * @dev_hdl: handle for a link/device. + * + * Returns success/failure + */ +int32_t cam_destroy_device_hdl(int32_t dev_hdl); + +/** + * cam_destroy_session_hdl() - destroy device handle + * @dev_hdl: handle for a session + * + * Returns success/failure + */ +int32_t cam_destroy_session_hdl(int32_t dev_hdl); + + +/* Internal functions */ +/** + * cam_req_mgr_util_init() - init function of cam_req_mgr_util + * + * This is called as part of probe function to initialize + * handle table, bitmap, locks + */ +int cam_req_mgr_util_init(void); + +/** + * cam_req_mgr_util_deinit() - deinit function of cam_req_mgr_util + * + * This function is called in case of probe failure + */ +int32_t cam_req_mgr_util_deinit(void); + +/** + * cam_req_mgr_util_free_hdls() - free handles in case of crash + * + * Called from cam_req_mgr_dev release function to make sure + * all data structures are cleaned to avoid leaks + * + * cam_req_mgr core can call this function at the end of + * camera to make sure all stale entries are printed and + * cleaned + */ +int32_t cam_req_mgr_util_free_hdls(void); + +#endif /* _CAM_REQ_MGR_UTIL_API_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util_priv.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util_priv.h new file mode 100644 index 000000000000..8624442f643b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_util_priv.h @@ -0,0 +1,49 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_REQ_MGR_UTIL_PRIV_H_ +#define _CAM_REQ_MGR_UTIL_PRIV_H_ + +/** + * handle format: + * @bits (0-7): handle index + * @bits (8-11): handle type + * @bits (12-15): reserved + * @bits (16-23): random bits + * @bits (24-31): zeros + */ + +#define CAM_REQ_MGR_HDL_SIZE 32 +#define CAM_REQ_MGR_RND1_SIZE 8 +#define CAM_REQ_MGR_RVD_SIZE 4 +#define CAM_REQ_MGR_HDL_TYPE_SIZE 4 +#define CAM_REQ_MGR_HDL_IDX_SIZE 8 + +#define CAM_REQ_MGR_RND1_POS 24 +#define CAM_REQ_MGR_RVD_POS 16 +#define CAM_REQ_MGR_HDL_TYPE_POS 12 + +#define CAM_REQ_MGR_RND1_BYTES 1 + +#define CAM_REQ_MGR_HDL_TYPE_MASK ((1 << CAM_REQ_MGR_HDL_TYPE_SIZE) - 1) + +#define GET_DEV_HANDLE(rnd1, type, idx) \ + ((rnd1 << (CAM_REQ_MGR_RND1_POS - CAM_REQ_MGR_RND1_SIZE)) | \ + (0x0 << (CAM_REQ_MGR_RVD_POS - CAM_REQ_MGR_RVD_SIZE)) | \ + (type << (CAM_REQ_MGR_HDL_TYPE_POS - CAM_REQ_MGR_HDL_TYPE_SIZE)) | \ + (idx << (CAM_REQ_MGR_HDL_IDX_POS - CAM_REQ_MGR_HDL_IDX_SIZE))) \ + +#define CAM_REQ_MGR_GET_HDL_IDX(hdl) (hdl & CAM_REQ_MGR_HDL_IDX_MASK) +#define CAM_REQ_MGR_GET_HDL_TYPE(hdl) \ + ((hdl >> CAM_REQ_MGR_HDL_IDX_POS) & CAM_REQ_MGR_HDL_TYPE_MASK) + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.c b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.c new file mode 100644 index 000000000000..2e0187ce7075 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.c @@ -0,0 +1,280 @@ +/* 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 "cam_req_mgr_workq.h" +#include "cam_debug_util.h" + +#define WORKQ_ACQUIRE_LOCK(workq, flags) {\ + if ((workq)->in_irq) \ + spin_lock_irqsave(&(workq)->lock_bh, (flags)); \ + else \ + spin_lock_bh(&(workq)->lock_bh); \ +} + +#define WORKQ_RELEASE_LOCK(workq, flags) {\ + if ((workq)->in_irq) \ + spin_unlock_irqrestore(&(workq)->lock_bh, (flags)); \ + else \ + spin_unlock_bh(&(workq)->lock_bh); \ +} + +struct crm_workq_task *cam_req_mgr_workq_get_task( + struct cam_req_mgr_core_workq *workq) +{ + struct crm_workq_task *task = NULL; + unsigned long flags = 0; + + if (!workq) + return NULL; + + WORKQ_ACQUIRE_LOCK(workq, flags); + if (list_empty(&workq->task.empty_head)) + goto end; + + task = list_first_entry(&workq->task.empty_head, + struct crm_workq_task, entry); + if (task) { + atomic_sub(1, &workq->task.free_cnt); + list_del_init(&task->entry); + } + +end: + WORKQ_RELEASE_LOCK(workq, flags); + + return task; +} + +static void cam_req_mgr_workq_put_task(struct crm_workq_task *task) +{ + struct cam_req_mgr_core_workq *workq = + (struct cam_req_mgr_core_workq *)task->parent; + unsigned long flags = 0; + + WORKQ_ACQUIRE_LOCK(workq, flags); + list_del_init(&task->entry); + task->cancel = 0; + task->process_cb = NULL; + task->priv = NULL; + list_add_tail(&task->entry, + &workq->task.empty_head); + atomic_add(1, &workq->task.free_cnt); + WORKQ_RELEASE_LOCK(workq, flags); +} + +/** + * cam_req_mgr_process_task() - Process the enqueued task + * @task: pointer to task workq thread shall process + */ +static int cam_req_mgr_process_task(struct crm_workq_task *task) +{ + struct cam_req_mgr_core_workq *workq = NULL; + + if (!task) + return -EINVAL; + + workq = (struct cam_req_mgr_core_workq *)task->parent; + if (task->process_cb) + task->process_cb(task->priv, task->payload); + else + CAM_WARN(CAM_CRM, "FATAL:no task handler registered for workq"); + cam_req_mgr_workq_put_task(task); + + return 0; +} + +/** + * cam_req_mgr_process_workq() - main loop handling + * @w: workqueue task pointer + */ +static void cam_req_mgr_process_workq(struct work_struct *w) +{ + struct cam_req_mgr_core_workq *workq = NULL; + struct crm_workq_task *task; + int32_t i = CRM_TASK_PRIORITY_0; + unsigned long flags = 0; + + if (!w) { + CAM_ERR(CAM_CRM, "NULL task pointer can not schedule"); + return; + } + workq = (struct cam_req_mgr_core_workq *) + container_of(w, struct cam_req_mgr_core_workq, work); + + while (i < CRM_TASK_PRIORITY_MAX) { + WORKQ_ACQUIRE_LOCK(workq, flags); + while (!list_empty(&workq->task.process_head[i])) { + task = list_first_entry(&workq->task.process_head[i], + struct crm_workq_task, entry); + atomic_sub(1, &workq->task.pending_cnt); + list_del_init(&task->entry); + WORKQ_RELEASE_LOCK(workq, flags); + cam_req_mgr_process_task(task); + CAM_DBG(CAM_CRM, "processed task %pK free_cnt %d", + task, atomic_read(&workq->task.free_cnt)); + WORKQ_ACQUIRE_LOCK(workq, flags); + } + WORKQ_RELEASE_LOCK(workq, flags); + i++; + } +} + +void crm_workq_clear_q(struct cam_req_mgr_core_workq *workq) +{ + int32_t i = CRM_TASK_PRIORITY_0; + struct crm_workq_task *task, *task_save; + + CAM_DBG(CAM_CRM, "pending_cnt %d", + atomic_read(&workq->task.pending_cnt)); + + while (i < CRM_TASK_PRIORITY_MAX) { + if (!list_empty(&workq->task.process_head[i])) { + list_for_each_entry_safe(task, task_save, + &workq->task.process_head[i], entry) { + cam_req_mgr_workq_put_task(task); + CAM_WARN(CAM_CRM, "flush task %pK, %d, cnt %d", + task, i, atomic_read( + &workq->task.free_cnt)); + } + } + i++; + } +} + +int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, + void *priv, int32_t prio) +{ + int rc = 0; + struct cam_req_mgr_core_workq *workq = NULL; + unsigned long flags = 0; + + if (!task) { + CAM_WARN(CAM_CRM, "NULL task pointer can not schedule"); + rc = -EINVAL; + goto end; + } + workq = (struct cam_req_mgr_core_workq *)task->parent; + if (!workq) { + CAM_DBG(CAM_CRM, "NULL workq pointer suspect mem corruption"); + rc = -EINVAL; + goto end; + } + if (!workq->job) { + rc = -EINVAL; + goto end; + } + + if (task->cancel == 1) { + cam_req_mgr_workq_put_task(task); + CAM_WARN(CAM_CRM, "task aborted and queued back to pool"); + rc = 0; + goto end; + } + task->priv = priv; + task->priority = + (prio < CRM_TASK_PRIORITY_MAX && prio >= CRM_TASK_PRIORITY_0) + ? prio : CRM_TASK_PRIORITY_0; + + WORKQ_ACQUIRE_LOCK(workq, flags); + list_add_tail(&task->entry, + &workq->task.process_head[task->priority]); + WORKQ_RELEASE_LOCK(workq, flags); + + atomic_add(1, &workq->task.pending_cnt); + CAM_DBG(CAM_CRM, "enq task %pK pending_cnt %d", + task, atomic_read(&workq->task.pending_cnt)); + + queue_work(workq->job, &workq->work); + +end: + return rc; +} + +int cam_req_mgr_workq_create(char *name, int32_t num_tasks, + struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq) +{ + int32_t i; + struct crm_workq_task *task; + struct cam_req_mgr_core_workq *crm_workq = NULL; + char buf[128] = "crm_workq-"; + + if (!*workq) { + crm_workq = (struct cam_req_mgr_core_workq *) + kzalloc(sizeof(struct cam_req_mgr_core_workq), + GFP_KERNEL); + if (crm_workq == NULL) + return -ENOMEM; + + strlcat(buf, name, sizeof(buf)); + CAM_DBG(CAM_CRM, "create workque crm_workq-%s", name); + crm_workq->job = alloc_workqueue(buf, + WQ_HIGHPRI | WQ_UNBOUND, 0, NULL); + if (!crm_workq->job) { + kfree(crm_workq); + return -ENOMEM; + } + + /* Workq attributes initialization */ + INIT_WORK(&crm_workq->work, cam_req_mgr_process_workq); + spin_lock_init(&crm_workq->lock_bh); + CAM_DBG(CAM_CRM, "LOCK_DBG workq %s lock %pK", + name, &crm_workq->lock_bh); + + /* Task attributes initialization */ + atomic_set(&crm_workq->task.pending_cnt, 0); + atomic_set(&crm_workq->task.free_cnt, 0); + for (i = CRM_TASK_PRIORITY_0; i < CRM_TASK_PRIORITY_MAX; i++) + INIT_LIST_HEAD(&crm_workq->task.process_head[i]); + INIT_LIST_HEAD(&crm_workq->task.empty_head); + crm_workq->in_irq = in_irq; + crm_workq->task.num_task = num_tasks; + crm_workq->task.pool = (struct crm_workq_task *) + kzalloc(sizeof(struct crm_workq_task) * + crm_workq->task.num_task, + GFP_KERNEL); + if (!crm_workq->task.pool) { + CAM_WARN(CAM_CRM, "Insufficient memory %lu", + sizeof(struct crm_workq_task) * + crm_workq->task.num_task); + kfree(crm_workq); + return -ENOMEM; + } + + for (i = 0; i < crm_workq->task.num_task; i++) { + task = &crm_workq->task.pool[i]; + task->parent = (void *)crm_workq; + /* Put all tasks in free pool */ + list_add_tail(&task->entry, + &crm_workq->task.process_head[CRM_TASK_PRIORITY_0]); + cam_req_mgr_workq_put_task(task); + } + *workq = crm_workq; + CAM_DBG(CAM_CRM, "free tasks %d", + atomic_read(&crm_workq->task.free_cnt)); + } + + return 0; +} + +void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **crm_workq) +{ + CAM_DBG(CAM_CRM, "destroy workque %pK", crm_workq); + if (*crm_workq) { + crm_workq_clear_q(*crm_workq); + if ((*crm_workq)->job) { + destroy_workqueue((*crm_workq)->job); + (*crm_workq)->job = NULL; + } + kfree((*crm_workq)->task.pool); + kfree(*crm_workq); + *crm_workq = NULL; + } +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.h new file mode 100644 index 000000000000..eb3b804f97a9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_req_mgr_workq.h @@ -0,0 +1,138 @@ +/* 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. + */ + +#ifndef _CAM_REQ_MGR_WORKQ_H_ +#define _CAM_REQ_MGR_WORKQ_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "cam_req_mgr_core.h" + +/* Task priorities, lower the number higher the priority*/ +enum crm_task_priority { + CRM_TASK_PRIORITY_0, + CRM_TASK_PRIORITY_1, + CRM_TASK_PRIORITY_MAX, +}; + +/* workqueue will be used from irq context or not */ +enum crm_workq_context { + CRM_WORKQ_USAGE_NON_IRQ, + CRM_WORKQ_USAGE_IRQ, + CRM_WORKQ_USAGE_INVALID, +}; + +/** struct crm_workq_task + * @priority : caller can assign priority to task based on type. + * @payload : depending of user of task this payload type will change + * @process_cb : registered callback called by workq when task enqueued is + * ready for processing in workq thread context + * @parent : workq's parent is link which is enqqueing taks to this workq + * @entry : list head of this list entry is worker's empty_head + * @cancel : if caller has got free task from pool but wants to abort + * or put back without using it + * @priv : when task is enqueuer caller can attach priv along which + * it will get in process callback + * @ret : return value in future to use for blocking calls + */ +struct crm_workq_task { + int32_t priority; + void *payload; + int32_t (*process_cb)(void *, void *); + void *parent; + struct list_head entry; + uint8_t cancel; + void *priv; + int32_t ret; +}; + +/** struct cam_req_mgr_core_workq + * @work : work token used by workqueue + * @job : workqueue internal job struct + * task - + * @lock_bh : lock for task structs + * @in_irq : set true if workque can be used in irq context + * @free_cnt : num of free/available tasks + * @empty_head : list head of available taska which can be used + * or acquired in order to enqueue a task to workq + * @pool : pool of tasks used for handling events in workq context + * @num_task : size of tasks pool + * - + */ +struct cam_req_mgr_core_workq { + struct work_struct work; + struct workqueue_struct *job; + spinlock_t lock_bh; + uint32_t in_irq; + + /* tasks */ + struct { + struct mutex lock; + atomic_t pending_cnt; + atomic_t free_cnt; + + struct list_head process_head[CRM_TASK_PRIORITY_MAX]; + struct list_head empty_head; + struct crm_workq_task *pool; + uint32_t num_task; + } task; +}; + +/** + * cam_req_mgr_workq_create() + * @brief : create a workqueue + * @name : Name of the workque to be allocated, it is combination + * of session handle and link handle + * @num_task : Num_tasks to be allocated for workq + * @workq : Double pointer worker + * @in_irq : Set to one if workq might be used in irq context + * This function will allocate and create workqueue and pass + * the workq pointer to caller. + */ +int cam_req_mgr_workq_create(char *name, int32_t num_tasks, + struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq); + +/** + * cam_req_mgr_workq_destroy() + * @brief: destroy workqueue + * @workq: pointer to worker data struct + * this function will destroy workqueue and clean up resources + * associated with worker such as tasks. + */ +void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **workq); + +/** + * cam_req_mgr_workq_enqueue_task() + * @brief: Enqueue task in worker queue + * @task : task to be processed by worker + * @priv : clients private data + * @prio : task priority + * process callback func + */ +int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, + void *priv, int32_t prio); + +/** + * cam_req_mgr_workq_get_task() + * @brief: Returns empty task pointer for use + * @workq: workque used for processing + */ +struct crm_workq_task *cam_req_mgr_workq_get_task( + struct cam_req_mgr_core_workq *workq); + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_subdev.h b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_subdev.h new file mode 100644 index 000000000000..8cd3214674d5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_req_mgr/cam_subdev.h @@ -0,0 +1,115 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SUBDEV_H_ +#define _CAM_SUBDEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define CAM_SUBDEVICE_EVENT_MAX 30 + +/** + * struct cam_subdev - describes a camera sub-device + * + * @pdev: Pointer to the platform device + * @sd: V4l2 subdevice + * @ops: V4l2 subdecie operations + * @internal_ops: V4l2 subdevice internal operations + * @name: Name of the sub-device. Please notice that the name + * must be unique. + * @sd_flags: Subdev flags. Can be: + * %V4L2_SUBDEV_FL_HAS_DEVNODE - Set this flag if + * this subdev needs a device node. + * %V4L2_SUBDEV_FL_HAS_EVENTS - Set this flag if + * this subdev generates events. + * @token: Pointer to cookie of the client driver + * @ent_function: Media entity function type. Can be: + * %CAM_IFE_DEVICE_TYPE - identifies as IFE device. + * %CAM_ICP_DEVICE_TYPE - identifies as ICP device. + * + * Each instance of a subdev driver should create this struct, either + * stand-alone or embedded in a larger struct. This structure should be + * initialized/registered by cam_register_subdev + * + */ +struct cam_subdev { + struct platform_device *pdev; + struct v4l2_subdev sd; + const struct v4l2_subdev_ops *ops; + const struct v4l2_subdev_internal_ops *internal_ops; + char *name; + u32 sd_flags; + void *token; + u32 ent_function; +}; + +/** + * cam_subdev_probe() + * + * @brief: Camera Subdevice node probe function for v4l2 setup + * + * @sd: Camera subdevice object + * @name: Name of the subdevice node + * @dev_type: Subdevice node type + * + */ +int cam_subdev_probe(struct cam_subdev *sd, struct platform_device *pdev, + char *name, uint32_t dev_type); + +/** + * cam_subdev_remove() + * + * @brief: Called when subdevice node is unloaded + * + * @sd: Camera subdevice node object + * + */ +int cam_subdev_remove(struct cam_subdev *sd); + +/** + * cam_register_subdev_fops() + * + * @brief: This common utility function assigns subdev ops + * + * @fops: v4l file operations + */ +void cam_register_subdev_fops(struct v4l2_file_operations *fops); + +/** + * cam_register_subdev() + * + * @brief: This is the common utility function to be called by each camera + * subdevice node when it tries to register itself to the camera + * request manager + * + * @sd: Pointer to struct cam_subdev. + */ +int cam_register_subdev(struct cam_subdev *sd); + +/** + * cam_unregister_subdev() + * + * @brief: This is the common utility function to be called by each camera + * subdevice node when it tries to unregister itself from the + * camera request manger + * + * @sd: Pointer to struct cam_subdev. + */ +int cam_unregister_subdev(struct cam_subdev *sd); + +#endif /* _CAM_SUBDEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/Makefile new file mode 100644 index 000000000000..65c23274e5ae --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_res_mgr/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_utils/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_actuator/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_eeprom/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/Makefile new file mode 100644 index 000000000000..3f9157cf3001 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/Makefile @@ -0,0 +1,11 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_actuator_dev.o cam_actuator_core.o cam_actuator_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.c new file mode 100644 index 000000000000..fcd90fb288ad --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.c @@ -0,0 +1,867 @@ +/* Copyright (c) 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 +#include +#include "cam_actuator_core.h" +#include "cam_sensor_util.h" +#include "cam_trace.h" +#include "cam_res_mgr_api.h" + +int32_t cam_actuator_construct_default_power_setting( + struct cam_sensor_power_ctrl_t *power_info) +{ + int rc = 0; + + power_info->power_setting_size = 1; + power_info->power_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting), + GFP_KERNEL); + if (!power_info->power_setting) + return -ENOMEM; + + power_info->power_setting[0].seq_type = SENSOR_VAF; + power_info->power_setting[0].seq_val = CAM_VAF; + power_info->power_setting[0].config_val = 1; + power_info->power_setting[0].delay = 2; + + power_info->power_down_setting_size = 1; + power_info->power_down_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting), + GFP_KERNEL); + if (!power_info->power_down_setting) { + rc = -ENOMEM; + goto free_power_settings; + } + + power_info->power_down_setting[0].seq_type = SENSOR_VAF; + power_info->power_down_setting[0].seq_val = CAM_VAF; + power_info->power_down_setting[0].config_val = 0; + + return rc; + +free_power_settings: + kfree(power_info->power_setting); + return rc; +} + +static int32_t cam_actuator_power_up(struct cam_actuator_ctrl_t *a_ctrl) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info = + &a_ctrl->soc_info; + struct cam_actuator_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + if ((power_info->power_setting == NULL) && + (power_info->power_down_setting == NULL)) { + CAM_INFO(CAM_ACTUATOR, + "Using default power settings"); + rc = cam_actuator_construct_default_power_setting(power_info); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Construct default actuator power setting failed."); + return rc; + } + } + + /* Parse and fill vreg params for power up settings */ + rc = msm_camera_fill_vreg_params( + &a_ctrl->soc_info, + power_info->power_setting, + power_info->power_setting_size); + if (rc) { + CAM_ERR(CAM_ACTUATOR, + "failed to fill vreg params for power up rc:%d", rc); + return rc; + } + + /* Parse and fill vreg params for power down settings*/ + rc = msm_camera_fill_vreg_params( + &a_ctrl->soc_info, + power_info->power_down_setting, + power_info->power_down_setting_size); + if (rc) { + CAM_ERR(CAM_ACTUATOR, + "failed to fill vreg params power down rc:%d", rc); + return rc; + } + + power_info->dev = soc_info->dev; + + rc = cam_sensor_core_power_up(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_ACTUATOR, + "failed in actuator power up rc %d", rc); + return rc; + } + + rc = camera_io_init(&a_ctrl->io_master_info); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, "cci init failed: rc: %d", rc); + + return rc; +} + +static int32_t cam_actuator_power_down(struct cam_actuator_ctrl_t *a_ctrl) +{ + int32_t rc = 0; + struct cam_sensor_power_ctrl_t *power_info; + struct cam_hw_soc_info *soc_info = &a_ctrl->soc_info; + struct cam_actuator_soc_private *soc_private; + + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "failed: a_ctrl %pK", a_ctrl); + return -EINVAL; + } + + soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + soc_info = &a_ctrl->soc_info; + + if (!power_info) { + CAM_ERR(CAM_ACTUATOR, "failed: power_info %pK", power_info); + return -EINVAL; + } + rc = msm_camera_power_down(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_ACTUATOR, "power down the core is failed:%d", rc); + return rc; + } + + camera_io_release(&a_ctrl->io_master_info); + + return rc; +} + +static int32_t cam_actuator_i2c_modes_util( + struct camera_io_master *io_master_info, + struct i2c_settings_list *i2c_list) +{ + int32_t rc = 0; + uint32_t i, size; + + if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) { + rc = camera_io_dev_write(io_master_info, + &(i2c_list->i2c_settings)); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed to random write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_SEQ) { + rc = camera_io_dev_write_continuous( + io_master_info, + &(i2c_list->i2c_settings), + 0); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed to seq write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_BURST) { + rc = camera_io_dev_write_continuous( + io_master_info, + &(i2c_list->i2c_settings), + 1); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed to burst write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) { + size = i2c_list->i2c_settings.size; + for (i = 0; i < size; i++) { + rc = camera_io_dev_poll( + io_master_info, + i2c_list->i2c_settings. + reg_setting[i].reg_addr, + i2c_list->i2c_settings. + reg_setting[i].reg_data, + i2c_list->i2c_settings. + reg_setting[i].data_mask, + i2c_list->i2c_settings.addr_type, + i2c_list->i2c_settings.data_type, + i2c_list->i2c_settings. + reg_setting[i].delay); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "i2c poll apply setting Fail: %d", rc); + return rc; + } + } + } + + return rc; +} + +int32_t cam_actuator_slaveInfo_pkt_parser(struct cam_actuator_ctrl_t *a_ctrl, + uint32_t *cmd_buf) +{ + int32_t rc = 0; + struct cam_cmd_i2c_info *i2c_info; + + if (!a_ctrl || !cmd_buf) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args"); + return -EINVAL; + } + + i2c_info = (struct cam_cmd_i2c_info *)cmd_buf; + if (a_ctrl->io_master_info.master_type == CCI_MASTER) { + a_ctrl->io_master_info.cci_client->cci_i2c_master = + a_ctrl->cci_i2c_master; + a_ctrl->io_master_info.cci_client->i2c_freq_mode = + i2c_info->i2c_freq_mode; + a_ctrl->io_master_info.cci_client->sid = + i2c_info->slave_addr >> 1; + CAM_DBG(CAM_ACTUATOR, "Slave addr: 0x%x Freq Mode: %d", + i2c_info->slave_addr, i2c_info->i2c_freq_mode); + } else if (a_ctrl->io_master_info.master_type == I2C_MASTER) { + a_ctrl->io_master_info.client->addr = i2c_info->slave_addr; + CAM_DBG(CAM_ACTUATOR, "Slave addr: 0x%x", i2c_info->slave_addr); + } else { + CAM_ERR(CAM_ACTUATOR, "Invalid Master type: %d", + a_ctrl->io_master_info.master_type); + rc = -EINVAL; + } + + return rc; +} + +int32_t cam_actuator_apply_settings(struct cam_actuator_ctrl_t *a_ctrl, + struct i2c_settings_array *i2c_set) +{ + struct i2c_settings_list *i2c_list; + int32_t rc = 0; + + if (a_ctrl == NULL || i2c_set == NULL) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args"); + return -EINVAL; + } + + if (i2c_set->is_settings_valid != 1) { + CAM_ERR(CAM_ACTUATOR, " Invalid settings"); + return -EINVAL; + } + + list_for_each_entry(i2c_list, + &(i2c_set->list_head), list) { + rc = cam_actuator_i2c_modes_util( + &(a_ctrl->io_master_info), + i2c_list); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed to apply settings: %d", + rc); + return rc; + } + } + + return rc; +} + +int32_t cam_actuator_apply_request(struct cam_req_mgr_apply_request *apply) +{ + int32_t rc = 0, request_id, del_req_id; + struct cam_actuator_ctrl_t *a_ctrl = NULL; + + if (!apply) { + CAM_ERR(CAM_ACTUATOR, "Invalid Input Args"); + return -EINVAL; + } + + a_ctrl = (struct cam_actuator_ctrl_t *) + cam_get_device_priv(apply->dev_hdl); + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "Device data is NULL"); + return -EINVAL; + } + request_id = apply->request_id % MAX_PER_FRAME_ARRAY; + + trace_cam_apply_req("Actuator", apply->request_id); + + CAM_DBG(CAM_ACTUATOR, "Request Id: %lld", apply->request_id); + + if ((apply->request_id == + a_ctrl->i2c_data.per_frame[request_id].request_id) && + (a_ctrl->i2c_data.per_frame[request_id].is_settings_valid) + == 1) { + rc = cam_actuator_apply_settings(a_ctrl, + &a_ctrl->i2c_data.per_frame[request_id]); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed in applying the request: %lld\n", + apply->request_id); + return rc; + } + } + del_req_id = (request_id + + MAX_PER_FRAME_ARRAY - MAX_SYSTEM_PIPELINE_DELAY) % + MAX_PER_FRAME_ARRAY; + + if (apply->request_id > + a_ctrl->i2c_data.per_frame[del_req_id].request_id) { + a_ctrl->i2c_data.per_frame[del_req_id].request_id = 0; + rc = delete_request(&a_ctrl->i2c_data.per_frame[del_req_id]); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Fail deleting the req: %d err: %d\n", + del_req_id, rc); + return rc; + } + } else { + CAM_DBG(CAM_ACTUATOR, "No Valid Req to clean Up"); + } + + return rc; +} + +int32_t cam_actuator_establish_link( + struct cam_req_mgr_core_dev_link_setup *link) +{ + struct cam_actuator_ctrl_t *a_ctrl = NULL; + + if (!link) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args"); + return -EINVAL; + } + + a_ctrl = (struct cam_actuator_ctrl_t *) + cam_get_device_priv(link->dev_hdl); + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "Device data is NULL"); + return -EINVAL; + } + if (link->link_enable) { + a_ctrl->bridge_intf.link_hdl = link->link_hdl; + a_ctrl->bridge_intf.crm_cb = link->crm_cb; + } else { + a_ctrl->bridge_intf.link_hdl = -1; + a_ctrl->bridge_intf.crm_cb = NULL; + } + + return 0; +} + +int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info) +{ + if (!info) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args"); + return -EINVAL; + } + + info->dev_id = CAM_REQ_MGR_DEVICE_ACTUATOR; + strlcpy(info->name, CAM_ACTUATOR_NAME, sizeof(info->name)); + info->p_delay = 0; + info->trigger = CAM_TRIGGER_POINT_SOF; + + return 0; +} + +int32_t cam_actuator_i2c_pkt_parse(struct cam_actuator_ctrl_t *a_ctrl, + void *arg) +{ + int32_t rc = 0; + int32_t i = 0; + uint32_t total_cmd_buf_in_bytes = 0; + size_t len_of_buff = 0; + uint32_t *offset = NULL; + uint32_t *cmd_buf = NULL; + uintptr_t generic_ptr; + uintptr_t generic_pkt_ptr; + struct common_header *cmm_hdr = NULL; + struct cam_control *ioctl_ctrl = NULL; + struct cam_packet *csl_packet = NULL; + struct cam_config_dev_cmd config; + struct i2c_data_settings *i2c_data = NULL; + struct i2c_settings_array *i2c_reg_settings = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct cam_req_mgr_add_request add_req; + struct cam_actuator_soc_private *soc_private = NULL; + struct cam_sensor_power_ctrl_t *power_info = NULL; + + if (!a_ctrl || !arg) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args"); + return -EINVAL; + } + + soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + + power_info = &soc_private->power_info; + + ioctl_ctrl = (struct cam_control *)arg; + if (copy_from_user(&config, + u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(config))) + return -EFAULT; + + rc = cam_mem_get_cpu_buf(config.packet_handle, + &generic_pkt_ptr, &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Error in converting command Handle %d", + rc); + return rc; + } + + if (config.offset > len_of_buff) { + CAM_ERR(CAM_ACTUATOR, + "offset is out of bounds: offset: %lld len: %zu", + config.offset, len_of_buff); + return -EINVAL; + } + + csl_packet = (struct cam_packet *) + (generic_pkt_ptr + (uint32_t)config.offset); + CAM_DBG(CAM_ACTUATOR, "Pkt opcode: %d", csl_packet->header.op_code); + + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_ACTUATOR_PACKET_OPCODE_INIT: + offset = (uint32_t *)&csl_packet->payload; + offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + + /* Loop through multiple command buffers */ + for (i = 0; i < csl_packet->num_cmd_buf; i++) { + total_cmd_buf_in_bytes = cmd_desc[i].length; + if (!total_cmd_buf_in_bytes) + continue; + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &generic_ptr, &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Failed to get cpu buf"); + return rc; + } + cmd_buf = (uint32_t *)generic_ptr; + if (!cmd_buf) { + CAM_ERR(CAM_ACTUATOR, "invalid cmd buf"); + return -EINVAL; + } + cmd_buf += cmd_desc[i].offset / sizeof(uint32_t); + cmm_hdr = (struct common_header *)cmd_buf; + + switch (cmm_hdr->cmd_type) { + case CAMERA_SENSOR_CMD_TYPE_I2C_INFO: + CAM_DBG(CAM_ACTUATOR, + "Received slave info buffer"); + rc = cam_actuator_slaveInfo_pkt_parser( + a_ctrl, cmd_buf); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed to parse slave info: %d", rc); + return rc; + } + break; + case CAMERA_SENSOR_CMD_TYPE_PWR_UP: + case CAMERA_SENSOR_CMD_TYPE_PWR_DOWN: + CAM_DBG(CAM_ACTUATOR, + "Received power settings buffer"); + rc = cam_sensor_update_power_settings( + cmd_buf, + total_cmd_buf_in_bytes, + power_info); + if (rc) { + CAM_ERR(CAM_ACTUATOR, + "Failed:parse power settings: %d", + rc); + return rc; + } + break; + default: + CAM_DBG(CAM_ACTUATOR, + "Received initSettings buffer"); + i2c_data = &(a_ctrl->i2c_data); + i2c_reg_settings = + &i2c_data->init_settings; + + i2c_reg_settings->request_id = 0; + i2c_reg_settings->is_settings_valid = 1; + rc = cam_sensor_i2c_command_parser( + i2c_reg_settings, + &cmd_desc[i], 1); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed:parse init settings: %d", + rc); + return rc; + } + break; + } + } + + if (a_ctrl->cam_act_state == CAM_ACTUATOR_ACQUIRE) { + rc = cam_actuator_power_up(a_ctrl); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + " Actuator Power up failed"); + return rc; + } + a_ctrl->cam_act_state = CAM_ACTUATOR_CONFIG; + } + + rc = cam_actuator_apply_settings(a_ctrl, + &a_ctrl->i2c_data.init_settings); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Cannot apply Init settings"); + return rc; + } + + /* Delete the request even if the apply is failed */ + rc = delete_request(&a_ctrl->i2c_data.init_settings); + if (rc < 0) { + CAM_WARN(CAM_ACTUATOR, + "Fail in deleting the Init settings"); + rc = 0; + } + break; + case CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS: + if (a_ctrl->cam_act_state < CAM_ACTUATOR_CONFIG) { + rc = -EINVAL; + CAM_WARN(CAM_ACTUATOR, + "Not in right state to move lens: %d", + a_ctrl->cam_act_state); + return rc; + } + a_ctrl->setting_apply_state = ACT_APPLY_SETTINGS_NOW; + + i2c_data = &(a_ctrl->i2c_data); + i2c_reg_settings = &i2c_data->init_settings; + + i2c_data->init_settings.request_id = + csl_packet->header.request_id; + i2c_reg_settings->is_settings_valid = 1; + offset = (uint32_t *)&csl_packet->payload; + offset += csl_packet->cmd_buf_offset / sizeof(uint32_t); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_sensor_i2c_command_parser(i2c_reg_settings, + cmd_desc, 1); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Auto move lens parsing failed: %d", rc); + return rc; + } + break; + case CAM_ACTUATOR_PACKET_MANUAL_MOVE_LENS: + if (a_ctrl->cam_act_state < CAM_ACTUATOR_CONFIG) { + rc = -EINVAL; + CAM_WARN(CAM_ACTUATOR, + "Not in right state to move lens: %d", + a_ctrl->cam_act_state); + return rc; + } + i2c_data = &(a_ctrl->i2c_data); + i2c_reg_settings = &i2c_data->per_frame[ + csl_packet->header.request_id % MAX_PER_FRAME_ARRAY]; + + i2c_data->init_settings.request_id = + csl_packet->header.request_id; + i2c_reg_settings->is_settings_valid = 1; + offset = (uint32_t *)&csl_packet->payload; + offset += csl_packet->cmd_buf_offset / sizeof(uint32_t); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_sensor_i2c_command_parser(i2c_reg_settings, + cmd_desc, 1); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Manual move lens parsing failed: %d", rc); + return rc; + } + break; + } + + if ((csl_packet->header.op_code & 0xFFFFFF) != + CAM_ACTUATOR_PACKET_OPCODE_INIT) { + add_req.link_hdl = a_ctrl->bridge_intf.link_hdl; + add_req.req_id = csl_packet->header.request_id; + add_req.dev_hdl = a_ctrl->bridge_intf.device_hdl; + add_req.skip_before_applying = 0; + if (a_ctrl->bridge_intf.crm_cb && + a_ctrl->bridge_intf.crm_cb->add_req) + a_ctrl->bridge_intf.crm_cb->add_req(&add_req); + CAM_DBG(CAM_ACTUATOR, "Req Id: %lld added to Bridge", + add_req.req_id); + } + + return rc; +} + +void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl) +{ + int rc; + + if (a_ctrl->cam_act_state == CAM_ACTUATOR_INIT) + return; + + if (a_ctrl->cam_act_state >= CAM_ACTUATOR_CONFIG) { + rc = cam_actuator_power_down(a_ctrl); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed"); + } + + if (a_ctrl->cam_act_state >= CAM_ACTUATOR_ACQUIRE) { + rc = cam_destroy_device_hdl(a_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, "destroying dhdl failed"); + a_ctrl->bridge_intf.device_hdl = -1; + a_ctrl->bridge_intf.link_hdl = -1; + a_ctrl->bridge_intf.session_hdl = -1; + } + a_ctrl->cam_act_state = CAM_ACTUATOR_INIT; +} + +int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, + void *arg) +{ + int rc = 0; + struct cam_control *cmd = (struct cam_control *)arg; + + if (!a_ctrl || !cmd) { + CAM_ERR(CAM_ACTUATOR, " Invalid Args"); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_ACTUATOR, "Invalid handle type: %d", + cmd->handle_type); + return -EINVAL; + } + + CAM_DBG(CAM_ACTUATOR, "Opcode to Actuator: %d", cmd->op_code); + + mutex_lock(&(a_ctrl->actuator_mutex)); + switch (cmd->op_code) { + case CAM_ACQUIRE_DEV: { + struct cam_sensor_acquire_dev actuator_acq_dev; + struct cam_create_dev_hdl bridge_params; + + if (a_ctrl->bridge_intf.device_hdl != -1) { + CAM_ERR(CAM_ACTUATOR, "Device is already acquired"); + rc = -EINVAL; + goto release_mutex; + } + + rc = copy_from_user(&actuator_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(actuator_acq_dev)); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Failed Copying from user\n"); + goto release_mutex; + } + + bridge_params.session_hdl = actuator_acq_dev.session_handle; + bridge_params.ops = &a_ctrl->bridge_intf.ops; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = a_ctrl; + + actuator_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + a_ctrl->bridge_intf.device_hdl = actuator_acq_dev.device_handle; + a_ctrl->bridge_intf.session_hdl = + actuator_acq_dev.session_handle; + + CAM_DBG(CAM_ACTUATOR, "Device Handle: %d", + actuator_acq_dev.device_handle); + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &actuator_acq_dev, + sizeof(struct cam_sensor_acquire_dev))) { + CAM_ERR(CAM_ACTUATOR, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + + a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE; + } + break; + case CAM_RELEASE_DEV: { + if (a_ctrl->cam_act_state == CAM_ACTUATOR_START) { + rc = -EINVAL; + CAM_WARN(CAM_ACTUATOR, + "Cant release actuator: in start state"); + goto release_mutex; + } + + if (a_ctrl->cam_act_state == CAM_ACTUATOR_CONFIG) { + rc = cam_actuator_power_down(a_ctrl); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Actuator Power down failed"); + goto release_mutex; + } + } + + if (a_ctrl->bridge_intf.device_hdl == -1) { + CAM_ERR(CAM_ACTUATOR, "link hdl: %d device hdl: %d", + a_ctrl->bridge_intf.device_hdl, + a_ctrl->bridge_intf.link_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_destroy_device_hdl(a_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, "destroying the device hdl"); + a_ctrl->bridge_intf.device_hdl = -1; + a_ctrl->bridge_intf.link_hdl = -1; + a_ctrl->bridge_intf.session_hdl = -1; + a_ctrl->cam_act_state = CAM_ACTUATOR_INIT; + } + break; + case CAM_QUERY_CAP: { + struct cam_actuator_query_cap actuator_cap = {0}; + + actuator_cap.slot_info = a_ctrl->soc_info.index; + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &actuator_cap, + sizeof(struct cam_actuator_query_cap))) { + CAM_ERR(CAM_ACTUATOR, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + } + break; + case CAM_START_DEV: { + if (a_ctrl->cam_act_state != CAM_ACTUATOR_CONFIG) { + rc = -EINVAL; + CAM_WARN(CAM_ACTUATOR, + "Not in right state to start : %d", + a_ctrl->cam_act_state); + goto release_mutex; + } + a_ctrl->cam_act_state = CAM_ACTUATOR_START; + } + break; + case CAM_STOP_DEV: { + struct i2c_settings_array *i2c_set = NULL; + int i; + + if (a_ctrl->cam_act_state != CAM_ACTUATOR_START) { + rc = -EINVAL; + CAM_WARN(CAM_ACTUATOR, + "Not in right state to stop : %d", + a_ctrl->cam_act_state); + goto release_mutex; + } + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + i2c_set = &(a_ctrl->i2c_data.per_frame[i]); + + if (i2c_set->is_settings_valid == 1) { + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "delete request: %lld rc: %d", + i2c_set->request_id, rc); + } + } + a_ctrl->cam_act_state = CAM_ACTUATOR_CONFIG; + } + break; + case CAM_CONFIG_DEV: { + a_ctrl->setting_apply_state = + ACT_APPLY_SETTINGS_LATER; + rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Failed in actuator Parsing"); + goto release_mutex; + } + + if (a_ctrl->setting_apply_state == + ACT_APPLY_SETTINGS_NOW) { + rc = cam_actuator_apply_settings(a_ctrl, + &a_ctrl->i2c_data.init_settings); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, + "Cannot apply Update settings"); + + /* Delete the request even if the apply is failed */ + rc = delete_request(&a_ctrl->i2c_data.init_settings); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "Failed in Deleting the Init Pkt: %d", + rc); + goto release_mutex; + } + } + } + break; + default: + CAM_ERR(CAM_ACTUATOR, "Invalid Opcode %d", cmd->op_code); + } + +release_mutex: + mutex_unlock(&(a_ctrl->actuator_mutex)); + + return rc; +} + +int32_t cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush_req) +{ + int32_t rc = 0, i; + uint32_t cancel_req_id_found = 0; + struct cam_actuator_ctrl_t *a_ctrl = NULL; + struct i2c_settings_array *i2c_set = NULL; + + if (!flush_req) + return -EINVAL; + + a_ctrl = (struct cam_actuator_ctrl_t *) + cam_get_device_priv(flush_req->dev_hdl); + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "Device data is NULL"); + return -EINVAL; + } + + /* PATCH, add by xcb */ + if (a_ctrl->i2c_data.per_frame == NULL) { + CAM_ERR(CAM_ACTUATOR, "i2c frame data is NULL"); + return -EINVAL; + } + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + i2c_set = &(a_ctrl->i2c_data.per_frame[i]); + + if ((flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) + && (i2c_set->request_id != flush_req->req_id)) + continue; + + if (i2c_set->is_settings_valid == 1) { + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_ACTUATOR, + "delete request: %lld rc: %d", + i2c_set->request_id, rc); + + if (flush_req->type == + CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) { + cancel_req_id_found = 1; + break; + } + } + } + + if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ && + !cancel_req_id_found) + CAM_DBG(CAM_ACTUATOR, + "Flush request id:%lld not found in the pending list", + flush_req->req_id); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.h new file mode 100644 index 000000000000..c28d79d0554d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_core.h @@ -0,0 +1,73 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ACTUATOR_CORE_H_ +#define _CAM_ACTUATOR_CORE_H_ + +#include "cam_actuator_dev.h" + +/** + * @power_info: power setting info to control the power + * + * This API construct the default actuator power setting. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int32_t cam_actuator_construct_default_power_setting( + struct cam_sensor_power_ctrl_t *power_info); + +/** + * @apply: Req mgr structure for applying request + * + * This API applies the request that is mentioned + */ +int32_t cam_actuator_apply_request(struct cam_req_mgr_apply_request *apply); + +/** + * @info: Sub device info to req mgr + * + * This API publish the subdevice info to req mgr + */ +int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info); + +/** + * @flush: Req mgr structure for flushing request + * + * This API flushes the request that is mentioned + */ +int cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush); + + +/** + * @link: Link setup info + * + * This API establishes link actuator subdevice with req mgr + */ +int32_t cam_actuator_establish_link( + struct cam_req_mgr_core_dev_link_setup *link); + +/** + * @a_ctrl: Actuator ctrl structure + * @arg: Camera control command argument + * + * This API handles the camera control argument reached to actuator + */ +int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, void *arg); + +/** + * @a_ctrl: Actuator ctrl structure + * + * This API handles the shutdown ioctl/close + */ +void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl); + +#endif /* _CAM_ACTUATOR_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.c new file mode 100644 index 000000000000..96fdfeb1b4ba --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.c @@ -0,0 +1,440 @@ +/* Copyright (c) 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 "cam_actuator_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_actuator_soc.h" +#include "cam_actuator_core.h" +#include "cam_trace.h" + +static long cam_actuator_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_actuator_ctrl_t *a_ctrl = + v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_actuator_driver_cmd(a_ctrl, arg); + break; + default: + CAM_ERR(CAM_ACTUATOR, "Invalid ioctl cmd"); + rc = -EINVAL; + break; + } + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_actuator_init_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_ACTUATOR, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + cmd = VIDIOC_CAM_CONTROL; + rc = cam_actuator_subdev_ioctl(sd, cmd, &cmd_data); + if (rc) { + CAM_ERR(CAM_ACTUATOR, + "Failed in actuator subdev handling rc: %d", + rc); + return rc; + } + break; + default: + CAM_ERR(CAM_ACTUATOR, "Invalid compat ioctl: %d", cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_ACTUATOR, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + return rc; +} +#endif + +static int cam_actuator_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_actuator_ctrl_t *a_ctrl = + v4l2_get_subdevdata(sd); + + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "a_ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&(a_ctrl->actuator_mutex)); + cam_actuator_shutdown(a_ctrl); + mutex_unlock(&(a_ctrl->actuator_mutex)); + + return 0; +} + +static struct v4l2_subdev_core_ops cam_actuator_subdev_core_ops = { + .ioctl = cam_actuator_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_actuator_init_subdev_do_ioctl, +#endif +}; + +static struct v4l2_subdev_ops cam_actuator_subdev_ops = { + .core = &cam_actuator_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops = { + .close = cam_actuator_subdev_close, +}; + +static int cam_actuator_init_subdev(struct cam_actuator_ctrl_t *a_ctrl) +{ + int rc = 0; + + a_ctrl->v4l2_dev_str.internal_ops = + &cam_actuator_internal_ops; + a_ctrl->v4l2_dev_str.ops = + &cam_actuator_subdev_ops; + strlcpy(a_ctrl->device_name, CAMX_ACTUATOR_DEV_NAME, + sizeof(a_ctrl->device_name)); + a_ctrl->v4l2_dev_str.name = + a_ctrl->device_name; + a_ctrl->v4l2_dev_str.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + a_ctrl->v4l2_dev_str.ent_function = + CAM_ACTUATOR_DEVICE_TYPE; + a_ctrl->v4l2_dev_str.token = a_ctrl; + + rc = cam_register_subdev(&(a_ctrl->v4l2_dev_str)); + if (rc) + CAM_ERR(CAM_SENSOR, "Fail with cam_register_subdev rc: %d", rc); + + return rc; +} + +static int32_t cam_actuator_driver_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t rc = 0; + int32_t i = 0; + struct cam_actuator_ctrl_t *a_ctrl; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_actuator_soc_private *soc_private = NULL; + + if (client == NULL || id == NULL) { + CAM_ERR(CAM_ACTUATOR, "Invalid Args client: %pK id: %pK", + client, id); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CAM_ERR(CAM_ACTUATOR, "%s :: i2c_check_functionality failed", + client->name); + rc = -EFAULT; + return rc; + } + + /* Create sensor control structure */ + a_ctrl = kzalloc(sizeof(*a_ctrl), GFP_KERNEL); + if (!a_ctrl) + return -ENOMEM; + + i2c_set_clientdata(client, a_ctrl); + + soc_private = kzalloc(sizeof(struct cam_actuator_soc_private), + GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto free_ctrl; + } + a_ctrl->soc_info.soc_private = soc_private; + + a_ctrl->io_master_info.client = client; + soc_info = &a_ctrl->soc_info; + soc_info->dev = &client->dev; + soc_info->dev_name = client->name; + a_ctrl->io_master_info.master_type = I2C_MASTER; + + rc = cam_actuator_parse_dt(a_ctrl, &client->dev); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "failed: cam_sensor_parse_dt rc %d", rc); + goto free_soc; + } + + rc = cam_actuator_init_subdev(a_ctrl); + if (rc) + goto free_soc; + + if (soc_private->i2c_info.slave_addr != 0) + a_ctrl->io_master_info.client->addr = + soc_private->i2c_info.slave_addr; + + a_ctrl->i2c_data.per_frame = + (struct i2c_settings_array *) + kzalloc(sizeof(struct i2c_settings_array) * + MAX_PER_FRAME_ARRAY, GFP_KERNEL); + if (a_ctrl->i2c_data.per_frame == NULL) { + rc = -ENOMEM; + goto unreg_subdev; + } + + INIT_LIST_HEAD(&(a_ctrl->i2c_data.init_settings.list_head)); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) + INIT_LIST_HEAD(&(a_ctrl->i2c_data.per_frame[i].list_head)); + + a_ctrl->bridge_intf.device_hdl = -1; + a_ctrl->bridge_intf.ops.get_dev_info = + cam_actuator_publish_dev_info; + a_ctrl->bridge_intf.ops.link_setup = + cam_actuator_establish_link; + a_ctrl->bridge_intf.ops.apply_req = + cam_actuator_apply_request; + + v4l2_set_subdevdata(&(a_ctrl->v4l2_dev_str.sd), a_ctrl); + + a_ctrl->cam_act_state = CAM_ACTUATOR_INIT; + + return rc; + +unreg_subdev: + cam_unregister_subdev(&(a_ctrl->v4l2_dev_str)); +free_soc: + kfree(soc_private); +free_ctrl: + kfree(a_ctrl); + return rc; +} + +static int32_t cam_actuator_platform_remove(struct platform_device *pdev) +{ + int32_t rc = 0; + struct cam_actuator_ctrl_t *a_ctrl; + struct cam_actuator_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + a_ctrl = platform_get_drvdata(pdev); + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "Actuator device is NULL"); + return 0; + } + + soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + kfree(a_ctrl->io_master_info.cci_client); + a_ctrl->io_master_info.cci_client = NULL; + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + kfree(a_ctrl->soc_info.soc_private); + kfree(a_ctrl->i2c_data.per_frame); + a_ctrl->i2c_data.per_frame = NULL; + devm_kfree(&pdev->dev, a_ctrl); + + return rc; +} + +static int32_t cam_actuator_driver_i2c_remove(struct i2c_client *client) +{ + int32_t rc = 0; + struct cam_actuator_ctrl_t *a_ctrl = + i2c_get_clientdata(client); + struct cam_actuator_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + /* Handle I2C Devices */ + if (!a_ctrl) { + CAM_ERR(CAM_ACTUATOR, "Actuator device is NULL"); + return -EINVAL; + } + + soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + /*Free Allocated Mem */ + kfree(a_ctrl->i2c_data.per_frame); + a_ctrl->i2c_data.per_frame = NULL; + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + kfree(a_ctrl->soc_info.soc_private); + a_ctrl->soc_info.soc_private = NULL; + kfree(a_ctrl); + return rc; +} + +static const struct of_device_id cam_actuator_driver_dt_match[] = { + {.compatible = "qcom,actuator"}, + {} +}; + +static int32_t cam_actuator_driver_platform_probe( + struct platform_device *pdev) +{ + int32_t rc = 0; + int32_t i = 0; + struct cam_actuator_ctrl_t *a_ctrl = NULL; + struct cam_actuator_soc_private *soc_private = NULL; + + /* Create actuator control structure */ + a_ctrl = devm_kzalloc(&pdev->dev, + sizeof(struct cam_actuator_ctrl_t), GFP_KERNEL); + if (!a_ctrl) + return -ENOMEM; + + /*fill in platform device*/ + a_ctrl->v4l2_dev_str.pdev = pdev; + a_ctrl->soc_info.pdev = pdev; + a_ctrl->soc_info.dev = &pdev->dev; + a_ctrl->soc_info.dev_name = pdev->name; + a_ctrl->io_master_info.master_type = CCI_MASTER; + + a_ctrl->io_master_info.cci_client = kzalloc(sizeof( + struct cam_sensor_cci_client), GFP_KERNEL); + if (!(a_ctrl->io_master_info.cci_client)) { + rc = -ENOMEM; + goto free_ctrl; + } + + soc_private = kzalloc(sizeof(struct cam_actuator_soc_private), + GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto free_cci_client; + } + a_ctrl->soc_info.soc_private = soc_private; + soc_private->power_info.dev = &pdev->dev; + + a_ctrl->i2c_data.per_frame = + (struct i2c_settings_array *) + kzalloc(sizeof(struct i2c_settings_array) * + MAX_PER_FRAME_ARRAY, GFP_KERNEL); + if (a_ctrl->i2c_data.per_frame == NULL) { + rc = -ENOMEM; + goto free_soc; + } + + INIT_LIST_HEAD(&(a_ctrl->i2c_data.init_settings.list_head)); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) + INIT_LIST_HEAD(&(a_ctrl->i2c_data.per_frame[i].list_head)); + + rc = cam_actuator_parse_dt(a_ctrl, &(pdev->dev)); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "Paring actuator dt failed rc %d", rc); + goto free_mem; + } + + /* Fill platform device id*/ + pdev->id = a_ctrl->soc_info.index; + + rc = cam_actuator_init_subdev(a_ctrl); + if (rc) + goto free_mem; + + a_ctrl->bridge_intf.device_hdl = -1; + a_ctrl->bridge_intf.ops.get_dev_info = + cam_actuator_publish_dev_info; + a_ctrl->bridge_intf.ops.link_setup = + cam_actuator_establish_link; + a_ctrl->bridge_intf.ops.apply_req = + cam_actuator_apply_request; + a_ctrl->bridge_intf.ops.flush_req = + cam_actuator_flush_request; + + platform_set_drvdata(pdev, a_ctrl); + v4l2_set_subdevdata(&a_ctrl->v4l2_dev_str.sd, a_ctrl); + a_ctrl->cam_act_state = CAM_ACTUATOR_INIT; + + return rc; + +free_mem: + kfree(a_ctrl->i2c_data.per_frame); +free_soc: + kfree(soc_private); +free_cci_client: + kfree(a_ctrl->io_master_info.cci_client); +free_ctrl: + devm_kfree(&pdev->dev, a_ctrl); + return rc; +} + +MODULE_DEVICE_TABLE(of, cam_actuator_driver_dt_match); + +static struct platform_driver cam_actuator_platform_driver = { + .probe = cam_actuator_driver_platform_probe, + .driver = { + .name = "qcom,actuator", + .owner = THIS_MODULE, + .of_match_table = cam_actuator_driver_dt_match, + }, + .remove = cam_actuator_platform_remove, +}; + +static const struct i2c_device_id i2c_id[] = { + {ACTUATOR_DRIVER_I2C, (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver cam_actuator_driver_i2c = { + .id_table = i2c_id, + .probe = cam_actuator_driver_i2c_probe, + .remove = cam_actuator_driver_i2c_remove, + .driver = { + .name = ACTUATOR_DRIVER_I2C, + }, +}; + +static int __init cam_actuator_driver_init(void) +{ + int32_t rc = 0; + + rc = platform_driver_register(&cam_actuator_platform_driver); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, + "platform_driver_register failed rc = %d", rc); + return rc; + } + rc = i2c_add_driver(&cam_actuator_driver_i2c); + if (rc) + CAM_ERR(CAM_ACTUATOR, "i2c_add_driver failed rc = %d", rc); + + return rc; +} + +static void __exit cam_actuator_driver_exit(void) +{ + platform_driver_unregister(&cam_actuator_platform_driver); + i2c_del_driver(&cam_actuator_driver_i2c); +} + +module_init(cam_actuator_driver_init); +module_exit(cam_actuator_driver_exit); +MODULE_DESCRIPTION("cam_actuator_driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.h new file mode 100644 index 000000000000..3b54baddc630 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_dev.h @@ -0,0 +1,128 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ACTUATOR_DEV_H_ +#define _CAM_ACTUATOR_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_sensor_util.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + +#define ACTUATOR_DRIVER_I2C "i2c_actuator" +#define CAMX_ACTUATOR_DEV_NAME "cam-actuator-driver" + +#define MSM_ACTUATOR_MAX_VREGS (10) +#define ACTUATOR_MAX_POLL_COUNT 10 + + +enum cam_actuator_apply_state_t { + ACT_APPLY_SETTINGS_NOW, + ACT_APPLY_SETTINGS_LATER, +}; + +enum cam_actuator_state { + CAM_ACTUATOR_INIT, + CAM_ACTUATOR_ACQUIRE, + CAM_ACTUATOR_CONFIG, + CAM_ACTUATOR_START, +}; + +/** + * struct cam_actuator_i2c_info_t - I2C info + * @slave_addr : slave address + * @i2c_freq_mode : i2c frequency mode + */ +struct cam_actuator_i2c_info_t { + uint16_t slave_addr; + uint8_t i2c_freq_mode; +}; + +struct cam_actuator_soc_private { + struct cam_actuator_i2c_info_t i2c_info; + struct cam_sensor_power_ctrl_t power_info; +}; + +/** + * struct intf_params + * @device_hdl: Device Handle + * @session_hdl: Session Handle + * @ops: KMD operations + * @crm_cb: Callback API pointers + */ +struct intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_actuator_ctrl_t + * @i2c_driver: I2C device info + * @pdev: Platform device + * @cci_i2c_master: I2C structure + * @io_master_info: Information about the communication master + * @actuator_mutex: Actuator mutex + * @act_apply_state: Actuator settings aRegulator config + * @id: Cell Index + * @res_apply_state: Actuator settings apply state + * @cam_act_state: Actuator state + * @gconf: GPIO config + * @pinctrl_info: Pinctrl information + * @v4l2_dev_str: V4L2 device structure + * @i2c_data: I2C register settings structure + * @act_info: Sensor query cap structure + * @of_node: Node ptr + * @device_name: Device name + */ +struct cam_actuator_ctrl_t { + struct i2c_driver *i2c_driver; + enum cci_i2c_master_t cci_i2c_master; + struct camera_io_master io_master_info; + struct cam_hw_soc_info soc_info; + struct mutex actuator_mutex; + uint32_t id; + enum cam_actuator_apply_state_t setting_apply_state; + enum cam_actuator_state cam_act_state; + uint8_t cam_pinctrl_status; + struct cam_subdev v4l2_dev_str; + struct i2c_data_settings i2c_data; + struct cam_actuator_query_cap act_info; + struct intf_params bridge_intf; + char device_name[20]; +}; + +#endif /* _CAM_ACTUATOR_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.c new file mode 100644 index 000000000000..96dc2840b0dd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.c @@ -0,0 +1,75 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include "cam_actuator_soc.h" +#include "cam_soc_util.h" + +int32_t cam_actuator_parse_dt(struct cam_actuator_ctrl_t *a_ctrl, + struct device *dev) +{ + int32_t rc = 0; + struct cam_hw_soc_info *soc_info = &a_ctrl->soc_info; + struct cam_actuator_soc_private *soc_private = + (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + struct device_node *of_node = NULL; + + /* Initialize mutex */ + mutex_init(&(a_ctrl->actuator_mutex)); + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_ACTUATOR, "parsing common soc dt(rc %d)", rc); + return rc; + } + + of_node = soc_info->dev->of_node; + + if (a_ctrl->io_master_info.master_type == CCI_MASTER) { + rc = of_property_read_u32(of_node, "cci-master", + &(a_ctrl->cci_i2c_master)); + CAM_DBG(CAM_ACTUATOR, "cci-master %d, rc %d", + a_ctrl->cci_i2c_master, rc); + if ((rc < 0) || (a_ctrl->cci_i2c_master >= MASTER_MAX)) { + CAM_ERR(CAM_ACTUATOR, + "Wrong info: rc: %d, dt CCI master:%d", + rc, a_ctrl->cci_i2c_master); + rc = -EFAULT; + return rc; + } + } + + if (!soc_info->gpio_data) { + CAM_INFO(CAM_ACTUATOR, "No GPIO found"); + rc = 0; + return rc; + } + + if (!soc_info->gpio_data->cam_gpio_common_tbl_size) { + CAM_INFO(CAM_ACTUATOR, "No GPIO found"); + return -EINVAL; + } + + rc = cam_sensor_util_init_gpio_pin_tbl(soc_info, + &power_info->gpio_num_info); + if ((rc < 0) || (!power_info->gpio_num_info)) { + CAM_ERR(CAM_ACTUATOR, "No/Error Actuator GPIOs"); + return -EINVAL; + } + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.h new file mode 100644 index 000000000000..05d51f41a24b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_actuator/cam_actuator_soc.h @@ -0,0 +1,26 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_ACTUATOR_SOC_H_ +#define _CAM_ACTUATOR_SOC_H_ + +#include "cam_actuator_dev.h" + +/** + * @a_ctrl: Actuator ctrl structure + * + * This API parses actuator device tree + */ +int cam_actuator_parse_dt(struct cam_actuator_ctrl_t *a_ctrl, + struct device *dev); + +#endif /* _CAM_ACTUATOR_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/Makefile new file mode 100644 index 000000000000..2ebe8bde8f4a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/Makefile @@ -0,0 +1,8 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci_dev.o cam_cci_core.o cam_cci_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.c new file mode 100644 index 000000000000..b975418df86a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.c @@ -0,0 +1,1383 @@ +/* Copyright (c) 2017-2018, 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 +#include "cam_cci_core.h" +#include "cam_cci_dev.h" + +static int32_t cam_cci_convert_type_to_num_bytes( + enum camera_sensor_i2c_type type) +{ + int32_t num_bytes; + + switch (type) { + case CAMERA_SENSOR_I2C_TYPE_BYTE: + num_bytes = 1; + break; + case CAMERA_SENSOR_I2C_TYPE_WORD: + num_bytes = 2; + break; + case CAMERA_SENSOR_I2C_TYPE_3B: + num_bytes = 3; + break; + case CAMERA_SENSOR_I2C_TYPE_DWORD: + num_bytes = 4; + break; + default: + CAM_ERR(CAM_CCI, "failed: %d", type); + num_bytes = 0; + break; + } + return num_bytes; +} + +static void cam_cci_flush_queue(struct cci_device *cci_dev, + enum cci_i2c_master_t master) +{ + int32_t rc = 0; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + cam_io_w_mb(1 << master, base + CCI_HALT_REQ_ADDR); + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT); + if (rc < 0) { + CAM_ERR(CAM_CCI, "wait failed"); + } else if (rc == 0) { + CAM_ERR(CAM_CCI, "wait timeout"); + + /* Set reset pending flag to TRUE */ + cci_dev->cci_master_info[master].reset_pending = TRUE; + + /* Set proper mask to RESET CMD address based on MASTER */ + if (master == MASTER_0) + cam_io_w_mb(CCI_M0_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + else + cam_io_w_mb(CCI_M1_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + + /* wait for reset done irq */ + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master].reset_complete, + CCI_TIMEOUT); + if (rc <= 0) + CAM_ERR(CAM_CCI, "wait failed %d", rc); + } +} + +static int32_t cam_cci_validate_queue(struct cci_device *cci_dev, + uint32_t len, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + uint32_t read_val = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + unsigned long flags; + + read_val = cam_io_r_mb(base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CAM_DBG(CAM_CCI, "CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d", + read_val, len, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size); + if ((read_val + len + 1) > cci_dev-> + cci_i2c_queue_info[master][queue].max_queue_size) { + uint32_t reg_val = 0; + uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8); + + CAM_DBG(CAM_CCI, "CCI_I2C_REPORT_CMD"); + cam_io_w_mb(report_val, + base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + read_val++; + CAM_DBG(CAM_CCI, + "CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d, queue: %d", + read_val, queue); + cam_io_w_mb(read_val, base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + reg_val = 1 << ((master * 2) + queue); + CAM_DBG(CAM_CCI, "CCI_QUEUE_START_ADDR"); + spin_lock_irqsave( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + cam_io_w_mb(reg_val, base + + CCI_QUEUE_START_ADDR); + CAM_DBG(CAM_CCI, "wait_for_completion_timeout"); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].report_q[queue], CCI_TIMEOUT); + if (rc <= 0) { + CAM_ERR(CAM_CCI, "Wait_for_completion_timeout: rc: %d", + rc); + if (rc == 0) + rc = -ETIMEDOUT; + cam_cci_flush_queue(cci_dev, master); + return rc; + } + rc = cci_dev->cci_master_info[master].status; + if (rc < 0) + CAM_ERR(CAM_CCI, "Failed rc %d", rc); + } + + return rc; +} + +static int32_t cam_cci_write_i2c_queue(struct cci_device *cci_dev, + uint32_t val, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + if (!cci_dev) { + CAM_ERR(CAM_CCI, "Failed"); + return -EINVAL; + } + + rc = cam_cci_validate_queue(cci_dev, 1, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Failed %d", rc); + return rc; + } + CAM_DBG(CAM_CCI, "CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x", + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset, val); + cam_io_w_mb(val, base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + return rc; +} + +static int32_t cam_cci_lock_queue(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue, uint32_t en) +{ + uint32_t val; + + if (queue != PRIORITY_QUEUE) + return 0; + + val = en ? CCI_I2C_LOCK_CMD : CCI_I2C_UNLOCK_CMD; + return cam_cci_write_i2c_queue(cci_dev, val, master, queue); +} + +#ifdef DUMP_CCI_REGISTERS +static void cam_cci_dump_registers(struct cci_device *cci_dev, + enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) +{ + uint32_t read_val = 0; + uint32_t i = 0; + uint32_t reg_offset = 0; + void __iomem *base = cci_dev->soc_info.reg_map[0].mem_base; + + /* CCI Top Registers */ + CAM_INFO(CAM_CCI, "****CCI TOP Registers ****"); + for (i = 0; i < DEBUG_TOP_REG_COUNT; i++) { + reg_offset = DEBUG_TOP_REG_START + i * 4; + read_val = cam_io_r_mb(base + reg_offset); + CAM_INFO(CAM_CCI, "offset = 0x%X value = 0x%X", + reg_offset, read_val); + } + + /* CCI Master registers */ + CAM_INFO(CAM_CCI, "****CCI MASTER %d Registers ****", + master); + for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) { + if (i == 6) + continue; + reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4; + read_val = cam_io_r_mb(base + reg_offset); + CAM_INFO(CAM_CCI, "offset = 0x%X value = 0x%X", + reg_offset, read_val); + } + + /* CCI Master Queue registers */ + CAM_INFO(CAM_CCI, " **** CCI MASTER%d QUEUE%d Registers ****", + master, queue); + for (i = 0; i < DEBUG_MASTER_QUEUE_REG_COUNT; i++) { + reg_offset = DEBUG_MASTER_QUEUE_REG_START + master*0x200 + + queue*0x100 + i * 4; + read_val = cam_io_r_mb(base + reg_offset); + CAM_INFO(CAM_CCI, "offset = 0x%X value = 0x%X", + reg_offset, read_val); + } + + /* CCI Interrupt registers */ + CAM_INFO(CAM_CCI, " ****CCI Interrupt Registers ****"); + for (i = 0; i < DEBUG_INTR_REG_COUNT; i++) { + reg_offset = DEBUG_INTR_REG_START + i * 4; + read_val = cam_io_r_mb(base + reg_offset); + CAM_INFO(CAM_CCI, "offset = 0x%X value = 0x%X", + reg_offset, read_val); + } +} +#endif + +static uint32_t cam_cci_wait(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + + if (!cci_dev) { + CAM_ERR(CAM_CCI, "failed"); + return -EINVAL; + } + + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].report_q[queue], CCI_TIMEOUT); + CAM_DBG(CAM_CCI, "wait DONE_for_completion_timeout"); + + if (rc <= 0) { +#ifdef DUMP_CCI_REGISTERS + cam_cci_dump_registers(cci_dev, master, queue); +#endif + CAM_ERR(CAM_CCI, "wait for queue: %d", queue); + if (rc == 0) + rc = -ETIMEDOUT; + cam_cci_flush_queue(cci_dev, master); + return rc; + } + rc = cci_dev->cci_master_info[master].status; + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + + return 0; +} + +static void cam_cci_load_report_cmd(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + uint32_t reg_offset = master * 0x200 + queue * 0x100; + uint32_t read_val = cam_io_r_mb(base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8); + + CAM_DBG(CAM_CCI, "CCI_I2C_REPORT_CMD curr_w_cnt: %d", read_val); + cam_io_w_mb(report_val, + base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + read_val++; + + CAM_DBG(CAM_CCI, "CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d", read_val); + cam_io_w_mb(read_val, base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); +} + +static int32_t cam_cci_wait_report_cmd(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + unsigned long flags; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + uint32_t reg_val = 1 << ((master * 2) + queue); + + cam_cci_load_report_cmd(cci_dev, master, queue); + spin_lock_irqsave( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1); + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + cam_io_w_mb(reg_val, base + + CCI_QUEUE_START_ADDR); + + return cam_cci_wait(cci_dev, master, queue); +} + +static int32_t cam_cci_transfer_end(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + unsigned long flags; + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); + if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) { + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + rc = cam_cci_lock_queue(cci_dev, master, queue, 0); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc: %d", rc); + return rc; + } + rc = cam_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + } else { + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + rc = cam_cci_wait(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + rc = cam_cci_lock_queue(cci_dev, master, queue, 0); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + rc = cam_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Failed rc %d", rc); + return rc; + } + } + + return rc; +} + +static int32_t cam_cci_get_queue_free_size(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + uint32_t read_val = 0; + uint32_t reg_offset = master * 0x200 + queue * 0x100; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + read_val = cam_io_r_mb(base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CAM_DBG(CAM_CCI, "CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d max %d", read_val, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size); + return (cci_dev-> + cci_i2c_queue_info[master][queue].max_queue_size) - + read_val; +} + +static void cam_cci_process_half_q(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + uint32_t reg_val = 1 << ((master * 2) + queue); + unsigned long flags; + + spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue], + flags); + if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) { + cam_cci_load_report_cmd(cci_dev, master, queue); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + cam_io_w_mb(reg_val, base + + CCI_QUEUE_START_ADDR); + } + spin_unlock_irqrestore(&cci_dev->cci_master_info[master].lock_q[queue], + flags); +} + +static int32_t cam_cci_process_full_q(struct cci_device *cci_dev, + enum cci_i2c_master_t master, + enum cci_i2c_queue_t queue) +{ + int32_t rc = 0; + unsigned long flags; + + + spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue], + flags); + if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 1) { + atomic_set(&cci_dev->cci_master_info[master]. + done_pending[queue], 1); + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + rc = cam_cci_wait(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + } else { + spin_unlock_irqrestore( + &cci_dev->cci_master_info[master].lock_q[queue], flags); + rc = cam_cci_wait_report_cmd(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + } + + return rc; +} + +static int32_t cam_cci_calc_cmd_len(struct cci_device *cci_dev, + struct cam_cci_ctrl *c_ctrl, uint32_t cmd_size, + struct cam_sensor_i2c_reg_array *i2c_cmd, uint32_t *pack) +{ + uint8_t i; + uint32_t len = 0; + uint8_t data_len = 0, addr_len = 0; + uint8_t pack_max_len; + struct cam_sensor_i2c_reg_setting *msg; + struct cam_sensor_i2c_reg_array *cmd = i2c_cmd; + uint32_t size = cmd_size; + + if (!cci_dev || !c_ctrl) { + CAM_ERR(CAM_CCI, "failed"); + return -EINVAL; + } + + msg = &c_ctrl->cfg.cci_i2c_write_cfg; + *pack = 0; + + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) { + addr_len = cam_cci_convert_type_to_num_bytes(msg->addr_type); + len = (size + addr_len) <= (cci_dev->payload_size) ? + (size + addr_len):cci_dev->payload_size; + } else { + addr_len = cam_cci_convert_type_to_num_bytes(msg->addr_type); + data_len = cam_cci_convert_type_to_num_bytes(msg->data_type); + len = data_len + addr_len; + pack_max_len = size < (cci_dev->payload_size-len) ? + size : (cci_dev->payload_size-len); + for (i = 0; i < pack_max_len;) { + if (cmd->delay || ((cmd - i2c_cmd) >= (cmd_size - 1))) + break; + if (cmd->reg_addr + 1 == + (cmd+1)->reg_addr) { + len += data_len; + if (len > cci_dev->payload_size) { + len = len - data_len; + break; + } + (*pack)++; + } else { + break; + } + i += data_len; + cmd++; + } + } + + if (len > cci_dev->payload_size) { + CAM_ERR(CAM_CCI, "Len error: %d", len); + return -EINVAL; + } + + len += 1; /*add i2c WR command*/ + len = len/4 + 1; + + return len; +} + +static uint32_t cam_cci_cycles_per_ms(unsigned long clk) +{ + uint32_t cycles_per_us; + + if (clk) { + cycles_per_us = ((clk/1000)*256)/1000; + } else { + CAM_ERR(CAM_CCI, "failed: Can use default: %d", + CYCLES_PER_MICRO_SEC_DEFAULT); + cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT; + } + + return cycles_per_us; +} + +void cam_cci_get_clk_rates(struct cci_device *cci_dev, + struct cam_cci_ctrl *c_ctrl) + +{ + int32_t src_clk_idx, j; + uint32_t cci_clk_src; + unsigned long clk; + struct cam_cci_clk_params_t *clk_params = NULL; + + enum i2c_freq_mode i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode; + struct cam_hw_soc_info *soc_info = &cci_dev->soc_info; + + if (i2c_freq_mode >= I2C_MAX_MODES || + i2c_freq_mode < I2C_STANDARD_MODE) { + CAM_ERR(CAM_CCI, "Invalid frequency mode: %d", + (int32_t)i2c_freq_mode); + cci_dev->clk_level_index = -1; + return; + } + + clk_params = &cci_dev->cci_clk_params[i2c_freq_mode]; + cci_clk_src = clk_params->cci_clk_src; + + src_clk_idx = soc_info->src_clk_idx; + + if (src_clk_idx < 0) { + cci_dev->cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT; + cci_dev->clk_level_index = 0; + return; + } + + if (cci_clk_src == 0) { + clk = soc_info->clk_rate[0][src_clk_idx]; + cci_dev->cycles_per_us = cam_cci_cycles_per_ms(clk); + cci_dev->clk_level_index = 0; + return; + } + + for (j = 0; j < CAM_MAX_VOTE; j++) { + clk = soc_info->clk_rate[j][src_clk_idx]; + if (clk == cci_clk_src) { + cci_dev->cycles_per_us = cam_cci_cycles_per_ms(clk); + cci_dev->clk_level_index = j; + return; + } + } + return; +} + +static int32_t cam_cci_set_clk_param(struct cci_device *cci_dev, + struct cam_cci_ctrl *c_ctrl) +{ + struct cam_cci_clk_params_t *clk_params = NULL; + enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master; + enum i2c_freq_mode i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + + if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) { + CAM_ERR(CAM_CCI, "invalid i2c_freq_mode = %d", i2c_freq_mode); + return -EINVAL; + } + + clk_params = &cci_dev->cci_clk_params[i2c_freq_mode]; + + if (cci_dev->i2c_freq_mode[master] == i2c_freq_mode) + return 0; + if (master == MASTER_0) { + cam_io_w_mb(clk_params->hw_thigh << 16 | + clk_params->hw_tlow, + base + CCI_I2C_M0_SCL_CTL_ADDR); + cam_io_w_mb(clk_params->hw_tsu_sto << 16 | + clk_params->hw_tsu_sta, + base + CCI_I2C_M0_SDA_CTL_0_ADDR); + cam_io_w_mb(clk_params->hw_thd_dat << 16 | + clk_params->hw_thd_sta, + base + CCI_I2C_M0_SDA_CTL_1_ADDR); + cam_io_w_mb(clk_params->hw_tbuf, + base + CCI_I2C_M0_SDA_CTL_2_ADDR); + cam_io_w_mb(clk_params->hw_scl_stretch_en << 8 | + clk_params->hw_trdhld << 4 | clk_params->hw_tsp, + base + CCI_I2C_M0_MISC_CTL_ADDR); + } else if (master == MASTER_1) { + cam_io_w_mb(clk_params->hw_thigh << 16 | + clk_params->hw_tlow, + base + CCI_I2C_M1_SCL_CTL_ADDR); + cam_io_w_mb(clk_params->hw_tsu_sto << 16 | + clk_params->hw_tsu_sta, + base + CCI_I2C_M1_SDA_CTL_0_ADDR); + cam_io_w_mb(clk_params->hw_thd_dat << 16 | + clk_params->hw_thd_sta, + base + CCI_I2C_M1_SDA_CTL_1_ADDR); + cam_io_w_mb(clk_params->hw_tbuf, + base + CCI_I2C_M1_SDA_CTL_2_ADDR); + cam_io_w_mb(clk_params->hw_scl_stretch_en << 8 | + clk_params->hw_trdhld << 4 | clk_params->hw_tsp, + base + CCI_I2C_M1_MISC_CTL_ADDR); + } + cci_dev->i2c_freq_mode[master] = i2c_freq_mode; + + return 0; +} + +static int32_t cam_cci_data_queue(struct cci_device *cci_dev, + struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + uint16_t i = 0, j = 0, k = 0, h = 0, len = 0; + int32_t rc = 0, free_size = 0, en_seq_write = 0; + uint8_t data[12]; + struct cam_sensor_i2c_reg_setting *i2c_msg = + &c_ctrl->cfg.cci_i2c_write_cfg; + struct cam_sensor_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting; + enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master; + uint16_t reg_addr = 0, cmd_size = i2c_msg->size; + uint32_t read_val = 0, reg_offset, val, delay = 0; + uint32_t max_queue_size, queue_size = 0, cmd = 0; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + unsigned long flags; + + if (i2c_cmd == NULL) { + CAM_ERR(CAM_CCI, "Failed: i2c cmd is NULL"); + return -EINVAL; + } + + if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) { + CAM_ERR(CAM_CCI, "failed: invalid cmd_size %d", + cmd_size); + return -EINVAL; + } + + CAM_DBG(CAM_CCI, "addr type %d data type %d cmd_size %d", + i2c_msg->addr_type, i2c_msg->data_type, cmd_size); + + if (i2c_msg->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_CCI, "failed: invalid addr_type 0x%X", + i2c_msg->addr_type); + return -EINVAL; + } + if (i2c_msg->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_CCI, "failed: invalid data_type 0x%X", + i2c_msg->data_type); + return -EINVAL; + } + reg_offset = master * 0x200 + queue * 0x100; + + cam_io_w_mb(cci_dev->cci_wait_sync_cfg.cid, + base + CCI_SET_CID_SYNC_TIMER_ADDR + + cci_dev->cci_wait_sync_cfg.csid * + CCI_SET_CID_SYNC_TIMER_OFFSET); + + val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 | + c_ctrl->cci_info->retries << 16 | + c_ctrl->cci_info->id_map << 18; + + CAM_DBG(CAM_CCI, "CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x", + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset, val); + cam_io_w_mb(val, base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + + spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue], + flags); + atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master].lock_q[queue], + flags); + + max_queue_size = cci_dev->cci_i2c_queue_info[master][queue]. + max_queue_size; + + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) + queue_size = max_queue_size; + else + queue_size = max_queue_size/2; + reg_addr = i2c_cmd->reg_addr; + + if (sync_en == MSM_SYNC_ENABLE && cci_dev->valid_sync && + cmd_size < max_queue_size) { + val = CCI_I2C_WAIT_SYNC_CMD | + ((cci_dev->cci_wait_sync_cfg.line) << 4); + cam_io_w_mb(val, + base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + reg_offset); + } + + rc = cam_cci_lock_queue(cci_dev, master, queue, 1); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed line %d", rc); + return rc; + } + + while (cmd_size) { + uint32_t pack = 0; + + len = cam_cci_calc_cmd_len(cci_dev, c_ctrl, cmd_size, + i2c_cmd, &pack); + if (len <= 0) { + CAM_ERR(CAM_CCI, "failed"); + return -EINVAL; + } + + read_val = cam_io_r_mb(base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + CAM_DBG(CAM_CCI, "CUR_WORD_CNT_ADDR %d len %d max %d", + read_val, len, max_queue_size); + /* + 1 - space alocation for Report CMD */ + if ((read_val + len + 1) > queue_size) { + if ((read_val + len + 1) > max_queue_size) { + rc = cam_cci_process_full_q(cci_dev, + master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc: %d", rc); + return rc; + } + continue; + } + cam_cci_process_half_q(cci_dev, master, queue); + } + + CAM_DBG(CAM_CCI, "cmd_size %d addr 0x%x data 0x%x", + cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data); + delay = i2c_cmd->delay; + i = 0; + data[i++] = CCI_I2C_WRITE_CMD; + + /* + * in case of multiple command + * MSM_CCI_I2C_WRITE : address is not continuous, so update + * address for a new packet. + * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep + * the incremented address for a + * new packet + */ + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_ASYNC || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC_BLOCK) + reg_addr = i2c_cmd->reg_addr; + + if (en_seq_write == 0) { + /* either byte or word addr */ + if (i2c_msg->addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) + data[i++] = reg_addr; + else { + data[i++] = (reg_addr & 0xFF00) >> 8; + data[i++] = reg_addr & 0x00FF; + } + } + /* max of 10 data bytes */ + do { + if (i2c_msg->data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + data[i++] = i2c_cmd->reg_data; + if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) + reg_addr++; + } else { + if ((i + 1) <= cci_dev->payload_size) { + switch (i2c_msg->data_type) { + case CAMERA_SENSOR_I2C_TYPE_DWORD: + data[i++] = (i2c_cmd->reg_data & + 0xFF000000) >> 24; + /* fallthrough */ + case CAMERA_SENSOR_I2C_TYPE_3B: + data[i++] = (i2c_cmd->reg_data & + 0x00FF0000) >> 16; + /* fallthrough */ + case CAMERA_SENSOR_I2C_TYPE_WORD: + data[i++] = (i2c_cmd->reg_data & + 0x0000FF00) >> 8; + /* fallthrough */ + case CAMERA_SENSOR_I2C_TYPE_BYTE: + data[i++] = i2c_cmd->reg_data & + 0x000000FF; + break; + default: + CAM_ERR(CAM_CCI, + "invalid data type: %d", + i2c_msg->data_type); + return -EINVAL; + } + + if (c_ctrl->cmd == + MSM_CCI_I2C_WRITE_SEQ) + reg_addr++; + } else + break; + } + i2c_cmd++; + --cmd_size; + } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) || pack--) && + (cmd_size > 0) && (i <= cci_dev->payload_size)); + free_size = cam_cci_get_queue_free_size(cci_dev, master, + queue); + if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ || + c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) && + ((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) && + cci_dev->support_seq_write && cmd_size > 0 && + free_size > BURST_MIN_FREE_SIZE) { + data[0] |= 0xF0; + en_seq_write = 1; + } else { + data[0] |= ((i-1) << 4); + en_seq_write = 0; + } + len = ((i-1)/4) + 1; + + read_val = cam_io_r_mb(base + + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset); + for (h = 0, k = 0; h < len; h++) { + cmd = 0; + for (j = 0; (j < 4 && k < i); j++) + cmd |= (data[k++] << (j * 8)); + CAM_DBG(CAM_CCI, + "LOAD_DATA_ADDR 0x%x, q: %d, len:%d, cnt: %d", + cmd, queue, len, read_val); + cam_io_w_mb(cmd, base + + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + master * 0x200 + queue * 0x100); + + read_val += 1; + cam_io_w_mb(read_val, base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + } + + if ((delay > 0) && (delay < CCI_MAX_DELAY) && + en_seq_write == 0) { + cmd = (uint32_t)((delay * cci_dev->cycles_per_us) / + 0x100); + cmd <<= 4; + cmd |= CCI_I2C_WAIT_CMD; + CAM_DBG(CAM_CCI, + "CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x", cmd); + cam_io_w_mb(cmd, base + + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + + master * 0x200 + queue * 0x100); + read_val += 1; + cam_io_w_mb(read_val, base + + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); + } + } + + rc = cam_cci_transfer_end(cci_dev, master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + return rc; + } + + return rc; +} + +static int32_t cam_cci_read(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + uint32_t val = 0; + int32_t read_words = 0, exp_words = 0; + int32_t index = 0, first_byte = 0; + uint32_t i = 0; + enum cci_i2c_master_t master; + enum cci_i2c_queue_t queue = QUEUE_1; + struct cci_device *cci_dev = NULL; + struct cam_cci_read_cfg *read_cfg = NULL; + struct cam_hw_soc_info *soc_info = NULL; + void __iomem *base = NULL; + + cci_dev = v4l2_get_subdevdata(sd); + master = c_ctrl->cci_info->cci_i2c_master; + read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg; + + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX + || c_ctrl->cci_info->cci_i2c_master < 0) { + CAM_ERR(CAM_CCI, "Invalid I2C master addr"); + return -EINVAL; + } + + soc_info = &cci_dev->soc_info; + base = soc_info->reg_map[0].mem_base; + + mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]); + + /* + * Todo: If there is a change in frequency of operation + * Wait for previos transaction to complete + */ + + /* Set the I2C Frequency */ + rc = cam_cci_set_clk_param(cci_dev, c_ctrl); + if (rc < 0) { + CAM_ERR(CAM_CCI, "cam_cci_set_clk_param failed rc = %d", rc); + goto rel_mutex; + } + + /* + * Call validate queue to make sure queue is empty before starting. + * If this call fails, don't proceed with i2c_read call. This is to + * avoid overflow / underflow of queue + */ + rc = cam_cci_validate_queue(cci_dev, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1, + master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Initial validataion failed rc %d", rc); + goto rel_mutex; + } + + if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) { + CAM_ERR(CAM_CCI, "More than max retries"); + goto rel_mutex; + } + + if (read_cfg->data == NULL) { + CAM_ERR(CAM_CCI, "Data ptr is NULL"); + goto rel_mutex; + } + + CAM_DBG(CAM_CCI, "master %d, queue %d", master, queue); + CAM_DBG(CAM_CCI, "set param sid 0x%x retries %d id_map %d", + c_ctrl->cci_info->sid, c_ctrl->cci_info->retries, + c_ctrl->cci_info->id_map); + val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 | + c_ctrl->cci_info->retries << 16 | + c_ctrl->cci_info->id_map << 18; + rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CAM_DBG(CAM_CCI, "failed rc: %d", rc); + goto rel_mutex; + } + + val = CCI_I2C_LOCK_CMD; + rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CAM_DBG(CAM_CCI, "failed rc: %d", rc); + goto rel_mutex; + } + + if (read_cfg->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_CCI, "failed : Invalid addr type: %u", + read_cfg->addr_type); + rc = -EINVAL; + goto rel_mutex; + } + + val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4); + for (i = 0; i < read_cfg->addr_type; i++) { + val |= ((read_cfg->addr >> (i << 3)) & 0xFF) << + ((read_cfg->addr_type - i) << 3); + } + + rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CAM_DBG(CAM_CCI, "failed rc: %d", rc); + goto rel_mutex; + } + + val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4); + rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CAM_DBG(CAM_CCI, "failed rc: %d", rc); + goto rel_mutex; + } + + val = CCI_I2C_UNLOCK_CMD; + rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue); + if (rc < 0) { + CAM_DBG(CAM_CCI, "failed rc: %d", rc); + goto rel_mutex; + } + + val = cam_io_r_mb(base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + + master * 0x200 + queue * 0x100); + CAM_DBG(CAM_CCI, "cur word cnt 0x%x", val); + cam_io_w_mb(val, base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + + master * 0x200 + queue * 0x100); + + val = 1 << ((master * 2) + queue); + cam_io_w_mb(val, base + CCI_QUEUE_START_ADDR); + CAM_DBG(CAM_CCI, "wait_for_completion_timeout"); + + rc = wait_for_completion_timeout(&cci_dev-> + cci_master_info[master].reset_complete, CCI_TIMEOUT); + if (rc <= 0) { +#ifdef DUMP_CCI_REGISTERS + cam_cci_dump_registers(cci_dev, master, queue); +#endif + if (rc == 0) + rc = -ETIMEDOUT; + CAM_ERR(CAM_CCI, "wait_for_completion_timeout rc = %d", rc); + cam_cci_flush_queue(cci_dev, master); + goto rel_mutex; + } else { + rc = 0; + } + + read_words = cam_io_r_mb(base + + CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100); + exp_words = ((read_cfg->num_byte / 4) + 1); + if (read_words != exp_words) { + CAM_ERR(CAM_CCI, "read_words = %d, exp words = %d", + read_words, exp_words); + memset(read_cfg->data, 0, read_cfg->num_byte); + rc = -EINVAL; + goto rel_mutex; + } + index = 0; + CAM_DBG(CAM_CCI, "index %d num_type %d", index, read_cfg->num_byte); + first_byte = 0; + do { + val = cam_io_r_mb(base + + CCI_I2C_M0_READ_DATA_ADDR + master * 0x100); + CAM_DBG(CAM_CCI, "read val 0x%x", val); + for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) { + CAM_DBG(CAM_CCI, "i:%d index:%d", i, index); + if (!first_byte) { + CAM_DBG(CAM_CCI, "sid 0x%x", val & 0xFF); + first_byte++; + } else { + read_cfg->data[index] = + (val >> (i * 8)) & 0xFF; + CAM_DBG(CAM_CCI, "data[%d] 0x%x", index, + read_cfg->data[index]); + index++; + } + } + } while (--read_words > 0); +rel_mutex: + mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]); + + return rc; +} + +static int32_t cam_cci_i2c_write(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master; + + cci_dev = v4l2_get_subdevdata(sd); + + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + CAM_ERR(CAM_CCI, "invalid cci state %d", + cci_dev->cci_state); + return -EINVAL; + } + master = c_ctrl->cci_info->cci_i2c_master; + CAM_DBG(CAM_CCI, "set param sid 0x%x retries %d id_map %d", + c_ctrl->cci_info->sid, c_ctrl->cci_info->retries, + c_ctrl->cci_info->id_map); + + /* Set the I2C Frequency */ + rc = cam_cci_set_clk_param(cci_dev, c_ctrl); + if (rc < 0) { + CAM_ERR(CAM_CCI, "cam_cci_set_clk_param failed rc = %d", rc); + return rc; + } + /* + * Call validate queue to make sure queue is empty before starting. + * If this call fails, don't proceed with i2c_write call. This is to + * avoid overflow / underflow of queue + */ + rc = cam_cci_validate_queue(cci_dev, + cci_dev->cci_i2c_queue_info[master][queue].max_queue_size-1, + master, queue); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Initial validataion failed rc %d", + rc); + return rc; + } + if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) { + CAM_ERR(CAM_CCI, "More than max retries"); + return rc; + } + rc = cam_cci_data_queue(cci_dev, c_ctrl, queue, sync_en); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc: %d", rc); + return rc; + } + + return rc; +} + +static void cam_cci_write_async_helper(struct work_struct *work) +{ + int rc; + struct cci_device *cci_dev; + struct cci_write_async *write_async = + container_of(work, struct cci_write_async, work); + struct cam_sensor_i2c_reg_setting *i2c_msg; + enum cci_i2c_master_t master; + struct cam_cci_master_info *cci_master_info; + + cci_dev = write_async->cci_dev; + i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg; + master = write_async->c_ctrl.cci_info->cci_i2c_master; + cci_master_info = &cci_dev->cci_master_info[master]; + + mutex_lock(&cci_master_info->mutex_q[write_async->queue]); + rc = cam_cci_i2c_write(&(cci_dev->v4l2_dev_str.sd), + &write_async->c_ctrl, write_async->queue, write_async->sync_en); + mutex_unlock(&cci_master_info->mutex_q[write_async->queue]); + if (rc < 0) + CAM_ERR(CAM_CCI, "failed rc: %d", rc); + + kfree(write_async->c_ctrl.cfg.cci_i2c_write_cfg.reg_setting); + kfree(write_async); +} + +static int32_t cam_cci_i2c_write_async(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue, + enum cci_i2c_sync sync_en) +{ + int32_t rc = 0; + struct cci_write_async *write_async; + struct cci_device *cci_dev; + struct cam_sensor_i2c_reg_setting *cci_i2c_write_cfg; + struct cam_sensor_i2c_reg_setting *cci_i2c_write_cfg_w; + + cci_dev = v4l2_get_subdevdata(sd); + + write_async = kzalloc(sizeof(*write_async), GFP_KERNEL); + if (!write_async) + return -ENOMEM; + + + INIT_WORK(&write_async->work, cam_cci_write_async_helper); + write_async->cci_dev = cci_dev; + write_async->c_ctrl = *c_ctrl; + write_async->queue = queue; + write_async->sync_en = sync_en; + + cci_i2c_write_cfg = &c_ctrl->cfg.cci_i2c_write_cfg; + cci_i2c_write_cfg_w = &write_async->c_ctrl.cfg.cci_i2c_write_cfg; + + if (cci_i2c_write_cfg->size == 0) { + kfree(write_async); + return -EINVAL; + } + + cci_i2c_write_cfg_w->reg_setting = + kzalloc(sizeof(struct cam_sensor_i2c_reg_array)* + cci_i2c_write_cfg->size, GFP_KERNEL); + if (!cci_i2c_write_cfg_w->reg_setting) { + CAM_ERR(CAM_CCI, "Couldn't allocate memory"); + kfree(write_async); + return -ENOMEM; + } + memcpy(cci_i2c_write_cfg_w->reg_setting, + cci_i2c_write_cfg->reg_setting, + (sizeof(struct cam_sensor_i2c_reg_array)* + cci_i2c_write_cfg->size)); + + cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type; + cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type; + cci_i2c_write_cfg_w->data_type = cci_i2c_write_cfg->data_type; + cci_i2c_write_cfg_w->size = cci_i2c_write_cfg->size; + cci_i2c_write_cfg_w->delay = cci_i2c_write_cfg->delay; + + queue_work(cci_dev->write_wq[write_async->queue], &write_async->work); + + return rc; +} + +static int32_t cam_cci_read_bytes(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev = NULL; + enum cci_i2c_master_t master; + struct cam_cci_read_cfg *read_cfg = NULL; + uint16_t read_bytes = 0; + + if (!sd || !c_ctrl) { + CAM_ERR(CAM_CCI, "sd %pK c_ctrl %pK", sd, c_ctrl); + return -EINVAL; + } + if (!c_ctrl->cci_info) { + CAM_ERR(CAM_CCI, "cci_info NULL"); + return -EINVAL; + } + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev) { + CAM_ERR(CAM_CCI, "cci_dev NULL"); + return -EINVAL; + } + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + CAM_ERR(CAM_CCI, "invalid cci state %d", cci_dev->cci_state); + return -EINVAL; + } + + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX + || c_ctrl->cci_info->cci_i2c_master < 0) { + CAM_ERR(CAM_CCI, "Invalid I2C master addr"); + return -EINVAL; + } + + master = c_ctrl->cci_info->cci_i2c_master; + read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg; + if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) { + CAM_ERR(CAM_CCI, "read num bytes 0"); + rc = -EINVAL; + goto ERROR; + } + + read_bytes = read_cfg->num_byte; + do { + if (read_bytes > CCI_READ_MAX) + read_cfg->num_byte = CCI_READ_MAX; + else + read_cfg->num_byte = read_bytes; + rc = cam_cci_read(sd, c_ctrl); + if (rc < 0) { + CAM_ERR(CAM_CCI, "failed rc %d", rc); + goto ERROR; + } + if (read_bytes > CCI_READ_MAX) { + read_cfg->addr += CCI_READ_MAX; + read_cfg->data += CCI_READ_MAX; + read_bytes -= CCI_READ_MAX; + } else { + read_bytes = 0; + } + } while (read_bytes); + +ERROR: + return rc; +} + +static int32_t cam_cci_i2c_set_sync_prms(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK", + cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + cci_dev->cci_wait_sync_cfg = c_ctrl->cfg.cci_wait_sync_cfg; + cci_dev->valid_sync = cci_dev->cci_wait_sync_cfg.csid < 0 ? 0 : 1; + + return rc; +} + +static int32_t cam_cci_release(struct v4l2_subdev *sd) +{ + uint8_t rc = 0; + struct cci_device *cci_dev; + + cci_dev = v4l2_get_subdevdata(sd); + + rc = cam_cci_soc_release(cci_dev); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Failed in releasing the cci: %d", rc); + return rc; + } + + return rc; +} + +static int32_t cam_cci_write(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl) +{ + int32_t rc = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master; + struct cam_cci_master_info *cci_master_info; + uint32_t i; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK", + cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + + master = c_ctrl->cci_info->cci_i2c_master; + + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX + || c_ctrl->cci_info->cci_i2c_master < 0) { + CAM_ERR(CAM_CCI, "Invalid I2C master addr"); + return -EINVAL; + } + + cci_master_info = &cci_dev->cci_master_info[master]; + + switch (c_ctrl->cmd) { + case MSM_CCI_I2C_WRITE_SYNC_BLOCK: + mutex_lock(&cci_master_info->mutex_q[SYNC_QUEUE]); + rc = cam_cci_i2c_write(sd, c_ctrl, + SYNC_QUEUE, MSM_SYNC_ENABLE); + mutex_unlock(&cci_master_info->mutex_q[SYNC_QUEUE]); + break; + case MSM_CCI_I2C_WRITE_SYNC: + rc = cam_cci_i2c_write_async(sd, c_ctrl, + SYNC_QUEUE, MSM_SYNC_ENABLE); + break; + case MSM_CCI_I2C_WRITE: + case MSM_CCI_I2C_WRITE_SEQ: + case MSM_CCI_I2C_WRITE_BURST: + for (i = 0; i < NUM_QUEUES; i++) { + if (mutex_trylock(&cci_master_info->mutex_q[i])) { + rc = cam_cci_i2c_write(sd, c_ctrl, i, + MSM_SYNC_DISABLE); + mutex_unlock(&cci_master_info->mutex_q[i]); + return rc; + } + } + mutex_lock(&cci_master_info->mutex_q[PRIORITY_QUEUE]); + rc = cam_cci_i2c_write(sd, c_ctrl, + PRIORITY_QUEUE, MSM_SYNC_DISABLE); + mutex_unlock(&cci_master_info->mutex_q[PRIORITY_QUEUE]); + break; + case MSM_CCI_I2C_WRITE_ASYNC: + rc = cam_cci_i2c_write_async(sd, c_ctrl, + PRIORITY_QUEUE, MSM_SYNC_DISABLE); + break; + default: + rc = -ENOIOCTLCMD; + } + + return rc; +} + +int32_t cam_cci_core_cfg(struct v4l2_subdev *sd, + struct cam_cci_ctrl *cci_ctrl) +{ + int32_t rc = 0; + + CAM_DBG(CAM_CCI, "cmd %d", cci_ctrl->cmd); + switch (cci_ctrl->cmd) { + case MSM_CCI_INIT: + rc = cam_cci_init(sd, cci_ctrl); + break; + case MSM_CCI_RELEASE: + rc = cam_cci_release(sd); + break; + case MSM_CCI_I2C_READ: + rc = cam_cci_read_bytes(sd, cci_ctrl); + break; + case MSM_CCI_I2C_WRITE: + case MSM_CCI_I2C_WRITE_SEQ: + case MSM_CCI_I2C_WRITE_BURST: + case MSM_CCI_I2C_WRITE_SYNC: + case MSM_CCI_I2C_WRITE_ASYNC: + case MSM_CCI_I2C_WRITE_SYNC_BLOCK: + rc = cam_cci_write(sd, cci_ctrl); + break; + case MSM_CCI_GPIO_WRITE: + break; + case MSM_CCI_SET_SYNC_CID: + rc = cam_cci_i2c_set_sync_prms(sd, cci_ctrl); + break; + + default: + rc = -ENOIOCTLCMD; + } + + cci_ctrl->status = rc; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.h new file mode 100644 index 000000000000..a28d5d8ed303 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_core.h @@ -0,0 +1,46 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_CCI_CORE_H_ +#define _CAM_CCI_CORE_H_ + +#include +#include +#include "cam_cci_dev.h" +#include "cam_cci_soc.h" + +/** + * @cci_dev: CCI device structure + * @c_ctrl: CCI control structure + * + * This API gets CCI clk rates + */ +void cam_cci_get_clk_rates(struct cci_device *cci_dev, + struct cam_cci_ctrl *c_ctrl); + +/** + * @sd: V4L2 sub device + * @c_ctrl: CCI control structure + * + * This API handles I2C operations for CCI + */ +int32_t cam_cci_core_cfg(struct v4l2_subdev *sd, + struct cam_cci_ctrl *cci_ctrl); + +/** + * @irq_num: IRQ number + * @data: CCI private structure + * + * This API handles CCI IRQs + */ +irqreturn_t cam_cci_irq(int irq_num, void *data); + +#endif /* _CAM_CCI_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.c new file mode 100644 index 000000000000..1f3e7d46a66a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.c @@ -0,0 +1,384 @@ +/* Copyright (c) 2017-2018, 2020 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 "cam_cci_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_cci_soc.h" +#include "cam_cci_core.h" + +#define CCI_MAX_DELAY 1000000 +#define CCI_TIMEOUT msecs_to_jiffies(500) + +static struct v4l2_subdev *g_cci_subdev; + +struct v4l2_subdev *cam_cci_get_subdev(void) +{ + return g_cci_subdev; +} + +static long cam_cci_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int32_t rc = 0; + + if (arg == NULL) { + CAM_ERR(CAM_CCI, "Invalid Args"); + return rc; + } + + switch (cmd) { + case VIDIOC_MSM_CCI_CFG: + rc = cam_cci_core_cfg(sd, arg); + break; + case VIDIOC_CAM_CONTROL: + break; + default: + CAM_ERR(CAM_CCI, "Invalid ioctl cmd: %d", cmd); + rc = -ENOIOCTLCMD; + } + + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_cci_subdev_compat_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + return cam_cci_subdev_ioctl(sd, cmd, NULL); +} +#endif + +irqreturn_t cam_cci_irq(int irq_num, void *data) +{ + uint32_t irq; + struct cci_device *cci_dev = data; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + void __iomem *base = soc_info->reg_map[0].mem_base; + unsigned long flags; + + irq = cam_io_r_mb(base + CCI_IRQ_STATUS_0_ADDR); + cam_io_w_mb(irq, base + CCI_IRQ_CLEAR_0_ADDR); + cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) { + if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) { + cci_dev->cci_master_info[MASTER_0].reset_pending = + FALSE; + complete(&cci_dev->cci_master_info[MASTER_0]. + reset_complete); + } + if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) { + cci_dev->cci_master_info[MASTER_1].reset_pending = + FALSE; + complete(&cci_dev->cci_master_info[MASTER_1]. + reset_complete); + } + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) { + cci_dev->cci_master_info[MASTER_0].status = 0; + complete(&cci_dev->cci_master_info[MASTER_0].reset_complete); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) { + struct cam_cci_master_info *cci_master_info; + + cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave( + &cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_0], + flags); + atomic_set(&cci_master_info->q_free[QUEUE_0], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { + complete(&cci_master_info->report_q[QUEUE_0]); + atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); + } + spin_unlock_irqrestore( + &cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_0], + flags); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) { + struct cam_cci_master_info *cci_master_info; + + cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave( + &cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_1], + flags); + atomic_set(&cci_master_info->q_free[QUEUE_1], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { + complete(&cci_master_info->report_q[QUEUE_1]); + atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); + } + spin_unlock_irqrestore( + &cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_1], + flags); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) { + cci_dev->cci_master_info[MASTER_1].status = 0; + complete(&cci_dev->cci_master_info[MASTER_1].reset_complete); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) { + struct cam_cci_master_info *cci_master_info; + + cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave( + &cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_0], + flags); + atomic_set(&cci_master_info->q_free[QUEUE_0], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { + complete(&cci_master_info->report_q[QUEUE_0]); + atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); + } + spin_unlock_irqrestore( + &cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_0], + flags); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) { + struct cam_cci_master_info *cci_master_info; + + cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave( + &cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_1], + flags); + atomic_set(&cci_master_info->q_free[QUEUE_1], 0); + cci_master_info->status = 0; + if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { + complete(&cci_master_info->report_q[QUEUE_1]); + atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); + } + spin_unlock_irqrestore( + &cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_1], + flags); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) { + cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; + cam_io_w_mb(CCI_M0_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) { + cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE; + cam_io_w_mb(CCI_M1_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) { + cci_dev->cci_master_info[MASTER_0].status = -EINVAL; + cam_io_w_mb(CCI_M0_HALT_REQ_RMSK, + base + CCI_HALT_REQ_ADDR); + CAM_DBG(CAM_CCI, "MASTER_0 error 0x%x", irq); + } + if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) { + cci_dev->cci_master_info[MASTER_1].status = -EINVAL; + cam_io_w_mb(CCI_M1_HALT_REQ_RMSK, + base + CCI_HALT_REQ_ADDR); + CAM_DBG(CAM_CCI, "MASTER_1 error 0x%x", irq); + } + return IRQ_HANDLED; +} + +static int cam_cci_irq_routine(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cci_device *cci_dev = v4l2_get_subdevdata(sd); + irqreturn_t ret; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + + ret = cam_cci_irq(soc_info->irq_line->start, cci_dev); + *handled = TRUE; + return 0; +} + +static struct v4l2_subdev_core_ops cci_subdev_core_ops = { + .ioctl = cam_cci_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_cci_subdev_compat_ioctl, +#endif + .interrupt_service_routine = cam_cci_irq_routine, +}; + +static const struct v4l2_subdev_ops cci_subdev_ops = { + .core = &cci_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cci_subdev_intern_ops; + +static struct v4l2_file_operations cci_v4l2_subdev_fops; + +static long cam_cci_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return cam_cci_subdev_ioctl(sd, cmd, NULL); +} + +static long cam_cci_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, cam_cci_subdev_do_ioctl); +} + +#ifdef CONFIG_COMPAT +static long cam_cci_subdev_fops_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return v4l2_subdev_call(sd, core, ioctl, cmd, NULL); +} +#endif + +static int cam_cci_platform_probe(struct platform_device *pdev) +{ + struct cam_cpas_register_params cpas_parms; + struct cci_device *new_cci_dev; + struct cam_hw_soc_info *soc_info = NULL; + int rc = 0; + + new_cci_dev = kzalloc(sizeof(struct cci_device), + GFP_KERNEL); + if (!new_cci_dev) + return -ENOMEM; + + soc_info = &new_cci_dev->soc_info; + + new_cci_dev->v4l2_dev_str.pdev = pdev; + + soc_info->pdev = pdev; + soc_info->dev = &pdev->dev; + soc_info->dev_name = pdev->name; + + rc = cam_cci_parse_dt_info(pdev, new_cci_dev); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Resource get Failed: %d", rc); + goto cci_no_resource; + } + + new_cci_dev->v4l2_dev_str.internal_ops = + &cci_subdev_intern_ops; + new_cci_dev->v4l2_dev_str.ops = + &cci_subdev_ops; + strlcpy(new_cci_dev->device_name, CAMX_CCI_DEV_NAME, + sizeof(new_cci_dev->device_name)); + new_cci_dev->v4l2_dev_str.name = + new_cci_dev->device_name; + new_cci_dev->v4l2_dev_str.sd_flags = V4L2_SUBDEV_FL_HAS_EVENTS; + new_cci_dev->v4l2_dev_str.ent_function = + CAM_CCI_DEVICE_TYPE; + new_cci_dev->v4l2_dev_str.token = + new_cci_dev; + + rc = cam_register_subdev(&(new_cci_dev->v4l2_dev_str)); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Fail with cam_register_subdev"); + goto cci_no_resource; + } + + platform_set_drvdata(pdev, &(new_cci_dev->v4l2_dev_str.sd)); + v4l2_set_subdevdata(&new_cci_dev->v4l2_dev_str.sd, new_cci_dev); + g_cci_subdev = &new_cci_dev->v4l2_dev_str.sd; + + cam_register_subdev_fops(&cci_v4l2_subdev_fops); + cci_v4l2_subdev_fops.unlocked_ioctl = cam_cci_subdev_fops_ioctl; +#ifdef CONFIG_COMPAT + cci_v4l2_subdev_fops.compat_ioctl32 = + cam_cci_subdev_fops_compat_ioctl; +#endif + + cpas_parms.cam_cpas_client_cb = NULL; + cpas_parms.cell_index = 0; + cpas_parms.dev = &pdev->dev; + cpas_parms.userdata = new_cci_dev; + strlcpy(cpas_parms.identifier, "cci", CAM_HW_IDENTIFIER_LENGTH); + rc = cam_cpas_register_client(&cpas_parms); + if (rc) { + CAM_ERR(CAM_CCI, "CPAS registration failed"); + goto cci_no_resource; + } + CAM_DBG(CAM_CCI, "CPAS registration successful handle=%d", + cpas_parms.client_handle); + new_cci_dev->cpas_handle = cpas_parms.client_handle; + + mutex_init(&new_cci_dev->ref_count_mutex); + return rc; +cci_no_resource: + kfree(new_cci_dev); + return rc; +} + +static int cam_cci_device_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct cci_device *cci_dev = + v4l2_get_subdevdata(subdev); + + cam_cpas_unregister_client(cci_dev->cpas_handle); + cam_cci_soc_remove(pdev, cci_dev); + devm_kfree(&pdev->dev, cci_dev); + return 0; +} + +static const struct of_device_id cam_cci_dt_match[] = { + {.compatible = "qcom,cci"}, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_cci_dt_match); + +static struct platform_driver cci_driver = { + .probe = cam_cci_platform_probe, + .remove = cam_cci_device_remove, + .driver = { + .name = CAMX_CCI_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_cci_dt_match, + }, +}; + +static int cam_cci_assign_fops(void) +{ + struct v4l2_subdev *sd; + + sd = g_cci_subdev; + if (!sd || !(sd->devnode)) { + CAM_ERR(CAM_CRM, + "Invalid args sd node: %pK", sd); + return -EINVAL; + } + sd->devnode->fops = &cci_v4l2_subdev_fops; + + return 0; +} + +static int __init cam_cci_late_init(void) +{ + return cam_cci_assign_fops(); +} + +static int __init cam_cci_init_module(void) +{ + return platform_driver_register(&cci_driver); +} + +static void __exit cam_cci_exit_module(void) +{ + platform_driver_unregister(&cci_driver); +} + +module_init(cam_cci_init_module); +late_initcall(cam_cci_late_init); +module_exit(cam_cci_exit_module); +MODULE_DESCRIPTION("MSM CCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.h new file mode 100644 index 000000000000..12386fd3508f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_dev.h @@ -0,0 +1,304 @@ +/* Copyright (c) 2017-2020, 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 _CAM_CCI_DEV_H_ +#define _CAM_CCI_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_cci_hwreg.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define V4L2_IDENT_CCI 50005 +#define CCI_I2C_QUEUE_0_SIZE 128 +#define CCI_I2C_QUEUE_1_SIZE 32 +#define CYCLES_PER_MICRO_SEC_DEFAULT 4915 +#define CCI_MAX_DELAY 1000000 + +#define CCI_TIMEOUT msecs_to_jiffies(500) + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + +#define CCI_PINCTRL_STATE_DEFAULT "cci_default" +#define CCI_PINCTRL_STATE_SLEEP "cci_suspend" + +#define CCI_NUM_CLK_MAX 16 +#define CCI_NUM_CLK_CASES 5 +#define CCI_CLK_SRC_NAME "cci_src_clk" +#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10 10 +#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11 11 +#define BURST_MIN_FREE_SIZE 8 +#define MAX_LRME_V4l2_EVENTS 30 + +/* Max bytes that can be read per CCI read transaction */ +#define CCI_READ_MAX 12 +#define CCI_I2C_READ_MAX_RETRIES 3 +#define CCI_I2C_MAX_READ 8192 +#define CCI_I2C_MAX_WRITE 8192 + +#define CAMX_CCI_DEV_NAME "cam-cci-driver" + +/* Max bytes that can be read per CCI read transaction */ +#define CCI_READ_MAX 12 +#define CCI_I2C_READ_MAX_RETRIES 3 +#define CCI_I2C_MAX_READ 8192 +#define CCI_I2C_MAX_WRITE 8192 + +#define PRIORITY_QUEUE (QUEUE_0) +#define SYNC_QUEUE (QUEUE_1) + +enum cci_i2c_sync { + MSM_SYNC_DISABLE, + MSM_SYNC_ENABLE, +}; + +enum cam_cci_cmd_type { + MSM_CCI_INIT, + MSM_CCI_RELEASE, + MSM_CCI_SET_SID, + MSM_CCI_SET_FREQ, + MSM_CCI_SET_SYNC_CID, + MSM_CCI_I2C_READ, + MSM_CCI_I2C_WRITE, + MSM_CCI_I2C_WRITE_SEQ, + MSM_CCI_I2C_WRITE_BURST, + MSM_CCI_I2C_WRITE_ASYNC, + MSM_CCI_GPIO_WRITE, + MSM_CCI_I2C_WRITE_SYNC, + MSM_CCI_I2C_WRITE_SYNC_BLOCK, +}; + +enum cci_i2c_queue_t { + QUEUE_0, + QUEUE_1, + QUEUE_INVALID, +}; + +struct cam_cci_wait_sync_cfg { + uint16_t cid; + int16_t csid; + uint16_t line; + uint16_t delay; +}; + +struct cam_cci_gpio_cfg { + uint16_t gpio_queue; + uint16_t i2c_queue; +}; + +struct cam_cci_read_cfg { + uint32_t addr; + uint16_t addr_type; + uint8_t *data; + uint16_t num_byte; +}; + +struct cam_cci_i2c_queue_info { + uint32_t max_queue_size; + uint32_t report_id; + uint32_t irq_en; + uint32_t capture_rep_data; +}; + +struct cam_cci_master_info { + uint32_t status; + atomic_t q_free[NUM_QUEUES]; + uint8_t q_lock[NUM_QUEUES]; + uint8_t reset_pending; + struct mutex mutex; + struct completion reset_complete; + struct mutex mutex_q[NUM_QUEUES]; + struct completion report_q[NUM_QUEUES]; + atomic_t done_pending[NUM_QUEUES]; + spinlock_t lock_q[NUM_QUEUES]; +}; + +struct cam_cci_clk_params_t { + uint16_t hw_thigh; + uint16_t hw_tlow; + uint16_t hw_tsu_sto; + uint16_t hw_tsu_sta; + uint16_t hw_thd_dat; + uint16_t hw_thd_sta; + uint16_t hw_tbuf; + uint8_t hw_scl_stretch_en; + uint8_t hw_trdhld; + uint8_t hw_tsp; + uint32_t cci_clk_src; +}; + +enum cam_cci_state_t { + CCI_STATE_ENABLED, + CCI_STATE_DISABLED, +}; + +/** + * struct cci_device + * @pdev: Platform device + * @subdev: V4L2 sub device + * @base: Base address of CCI device + * @hw_version: Hardware version + * @ref_count: Reference Count + * @cci_state: CCI state machine + * @num_clk: Number of CCI clock + * @cci_clk: CCI clock structure + * @cci_clk_info: CCI clock information + * @cam_cci_i2c_queue_info: CCI queue information + * @i2c_freq_mode: I2C frequency of operations + * @cci_clk_params: CCI hw clk params + * @cci_gpio_tbl: CCI GPIO table + * @cci_gpio_tbl_size: GPIO table size + * @cci_pinctrl: Pinctrl structure + * @cci_pinctrl_status: CCI pinctrl status + * @cci_clk_src: CCI clk src rate + * @cci_vreg: CCI regulator structure + * @cci_reg_ptr: CCI individual regulator structure + * @regulator_count: Regulator count + * @support_seq_write: + * Set this flag when sequential write is enabled + * @write_wq: Work queue structure + * @valid_sync: Is it a valid sync with CSID + * @v4l2_dev_str: V4L2 device structure + * @cci_wait_sync_cfg: CCI sync config + * @cycles_per_us: Cycles per micro sec + * @payload_size: CCI packet payload size + */ +struct cci_device { + struct v4l2_subdev subdev; + struct cam_hw_soc_info soc_info; + uint32_t hw_version; + uint8_t ref_count; + enum cam_cci_state_t cci_state; + struct cam_cci_i2c_queue_info + cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES]; + struct cam_cci_master_info cci_master_info[NUM_MASTERS]; + enum i2c_freq_mode i2c_freq_mode[NUM_MASTERS]; + struct cam_cci_clk_params_t cci_clk_params[I2C_MAX_MODES]; + struct msm_pinctrl_info cci_pinctrl; + uint8_t cci_pinctrl_status; + uint8_t support_seq_write; + struct workqueue_struct *write_wq[MASTER_MAX]; + struct cam_cci_wait_sync_cfg cci_wait_sync_cfg; + uint8_t valid_sync; + struct cam_subdev v4l2_dev_str; + uint32_t cycles_per_us; + int32_t clk_level_index; + uint8_t payload_size; + char device_name[20]; + uint32_t cpas_handle; + struct mutex ref_count_mutex; +}; + +enum cam_cci_i2c_cmd_type { + CCI_I2C_SET_PARAM_CMD = 1, + CCI_I2C_WAIT_CMD, + CCI_I2C_WAIT_SYNC_CMD, + CCI_I2C_WAIT_GPIO_EVENT_CMD, + CCI_I2C_TRIG_I2C_EVENT_CMD, + CCI_I2C_LOCK_CMD, + CCI_I2C_UNLOCK_CMD, + CCI_I2C_REPORT_CMD, + CCI_I2C_WRITE_CMD, + CCI_I2C_READ_CMD, + CCI_I2C_WRITE_DISABLE_P_CMD, + CCI_I2C_READ_DISABLE_P_CMD, + CCI_I2C_WRITE_CMD2, + CCI_I2C_WRITE_CMD3, + CCI_I2C_REPEAT_CMD, + CCI_I2C_INVALID_CMD, +}; + +enum cam_cci_gpio_cmd_type { + CCI_GPIO_SET_PARAM_CMD = 1, + CCI_GPIO_WAIT_CMD, + CCI_GPIO_WAIT_SYNC_CMD, + CCI_GPIO_WAIT_GPIO_IN_EVENT_CMD, + CCI_GPIO_WAIT_I2C_Q_TRIG_EVENT_CMD, + CCI_GPIO_OUT_CMD, + CCI_GPIO_TRIG_EVENT_CMD, + CCI_GPIO_REPORT_CMD, + CCI_GPIO_REPEAT_CMD, + CCI_GPIO_CONTINUE_CMD, + CCI_GPIO_INVALID_CMD, +}; + +struct cam_sensor_cci_client { + struct v4l2_subdev *cci_subdev; + uint32_t freq; + enum i2c_freq_mode i2c_freq_mode; + enum cci_i2c_master_t cci_i2c_master; + uint16_t sid; + uint16_t cid; + uint32_t timeout; + uint16_t retries; + uint16_t id_map; +}; + +struct cam_cci_ctrl { + int32_t status; + struct cam_sensor_cci_client *cci_info; + enum cam_cci_cmd_type cmd; + union { + struct cam_sensor_i2c_reg_setting cci_i2c_write_cfg; + struct cam_cci_read_cfg cci_i2c_read_cfg; + struct cam_cci_wait_sync_cfg cci_wait_sync_cfg; + struct cam_cci_gpio_cfg gpio_cfg; + } cfg; +}; + +struct cci_write_async { + struct cci_device *cci_dev; + struct cam_cci_ctrl c_ctrl; + enum cci_i2c_queue_t queue; + struct work_struct work; + enum cci_i2c_sync sync_en; +}; + +irqreturn_t cam_cci_irq(int irq_num, void *data); + +#ifdef CONFIG_SPECTRA_CAMERA +extern struct v4l2_subdev *cam_cci_get_subdev(void); +#else +static inline struct v4l2_subdev *cam_cci_get_subdev(void) +{ + return NULL; +} +#endif + +#define VIDIOC_MSM_CCI_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct cam_cci_ctrl) + +#endif /* _CAM_CCI_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_hwreg.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_hwreg.h new file mode 100644 index 000000000000..c18593eb5e78 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_hwreg.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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 _CAM_CCI_HWREG_ +#define _CAM_CCI_HWREG_ + +#define CCI_HW_VERSION_ADDR 0x00000000 +#define CCI_RESET_CMD_ADDR 0x00000004 +#define CCI_RESET_CMD_RMSK 0x0f73f3f7 +#define CCI_M0_RESET_RMSK 0x3F1 +#define CCI_M1_RESET_RMSK 0x3F001 +#define CCI_QUEUE_START_ADDR 0x00000008 +#define CCI_SET_CID_SYNC_TIMER_ADDR 0x00000010 +#define CCI_SET_CID_SYNC_TIMER_OFFSET 0x00000004 +#define CCI_I2C_M0_SCL_CTL_ADDR 0x00000100 +#define CCI_I2C_M0_SDA_CTL_0_ADDR 0x00000104 +#define CCI_I2C_M0_SDA_CTL_1_ADDR 0x00000108 +#define CCI_I2C_M0_SDA_CTL_2_ADDR 0x0000010c +#define CCI_I2C_M0_READ_DATA_ADDR 0x00000118 +#define CCI_I2C_M0_MISC_CTL_ADDR 0x00000110 +#define CCI_I2C_M0_READ_BUF_LEVEL_ADDR 0x0000011C +#define CCI_HALT_REQ_ADDR 0x00000034 +#define CCI_M0_HALT_REQ_RMSK 0x1 +#define CCI_M1_HALT_REQ_RMSK 0x2 +#define CCI_I2C_M1_SCL_CTL_ADDR 0x00000200 +#define CCI_I2C_M1_SDA_CTL_0_ADDR 0x00000204 +#define CCI_I2C_M1_SDA_CTL_1_ADDR 0x00000208 +#define CCI_I2C_M1_SDA_CTL_2_ADDR 0x0000020c +#define CCI_I2C_M1_MISC_CTL_ADDR 0x00000210 +#define CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR 0x00000304 +#define CCI_I2C_M0_Q0_CUR_CMD_ADDR 0x00000308 +#define CCI_I2C_M0_Q0_REPORT_STATUS_ADDR 0x0000030c +#define CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR 0x00000300 +#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310 +#define CCI_IRQ_MASK_0_ADDR 0x00000c04 +#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7 +#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08 +#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c +#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000 +#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000 +#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000 +#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000 +#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000 +#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000 +#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100 +#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10 +#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6 +#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000 +#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1 +#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00 + +#define DEBUG_TOP_REG_START 0x0 +#define DEBUG_TOP_REG_COUNT 14 +#define DEBUG_MASTER_REG_START 0x100 +#define DEBUG_MASTER_REG_COUNT 8 +#define DEBUG_MASTER_QUEUE_REG_START 0x300 +#define DEBUG_MASTER_QUEUE_REG_COUNT 6 +#define DEBUG_INTR_REG_START 0xC00 +#define DEBUG_INTR_REG_COUNT 7 +#endif /* _CAM_CCI_HWREG_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.c new file mode 100644 index 000000000000..d6de56d47fdf --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.c @@ -0,0 +1,409 @@ +/* Copyright (c) 2017-2018, 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 "cam_cci_dev.h" +#include "cam_cci_core.h" + +int cam_cci_init(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl) +{ + uint8_t i = 0, j = 0; + int32_t rc = 0; + struct cci_device *cci_dev; + enum cci_i2c_master_t master = MASTER_0; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + struct cam_hw_soc_info *soc_info = NULL; + void __iomem *base = NULL; + + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !c_ctrl) { + CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK", + cci_dev, c_ctrl); + rc = -EINVAL; + return rc; + } + + soc_info = &cci_dev->soc_info; + base = soc_info->reg_map[0].mem_base; + + if (!soc_info || !base) { + CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK", + soc_info, base); + rc = -EINVAL; + return rc; + } + + CAM_DBG(CAM_CCI, "Base address %pK", base); + + mutex_lock(&cci_dev->ref_count_mutex); + if (cci_dev->ref_count++) { + CAM_DBG(CAM_CCI, "ref_count %d", cci_dev->ref_count); + master = c_ctrl->cci_info->cci_i2c_master; + CAM_DBG(CAM_CCI, "master %d", master); + if (master < MASTER_MAX && master >= 0) { + mutex_lock(&cci_dev->cci_master_info[master].mutex); + flush_workqueue(cci_dev->write_wq[master]); + /* Re-initialize the completion */ + reinit_completion(&cci_dev-> + cci_master_info[master].reset_complete); + for (i = 0; i < NUM_QUEUES; i++) + reinit_completion(&cci_dev-> + cci_master_info[master].report_q[i]); + /* Set reset pending flag to TRUE */ + cci_dev->cci_master_info[master].reset_pending = TRUE; + /* Set proper mask to RESET CMD address */ + if (master == MASTER_0) + cam_io_w_mb(CCI_M0_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + else + cam_io_w_mb(CCI_M1_RESET_RMSK, + base + CCI_RESET_CMD_ADDR); + /* wait for reset done irq */ + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[master]. + reset_complete, + CCI_TIMEOUT); + if (rc <= 0) + CAM_ERR(CAM_CCI, "wait failed %d", rc); + mutex_unlock(&cci_dev->cci_master_info[master].mutex); + } + mutex_unlock(&cci_dev->ref_count_mutex); + return 0; + } + + mutex_unlock(&cci_dev->ref_count_mutex); + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(cci_dev->cpas_handle, + &ahb_vote, &axi_vote); + if (rc != 0) + CAM_ERR(CAM_CCI, "CPAS start failed"); + + cam_cci_get_clk_rates(cci_dev, c_ctrl); + + /* Re-initialize the completion */ + reinit_completion(&cci_dev->cci_master_info[master].reset_complete); + for (i = 0; i < NUM_QUEUES; i++) + reinit_completion(&cci_dev->cci_master_info[master]. + report_q[i]); + + /* Enable Regulators and IRQ*/ + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_LOWSVS_VOTE, true); + if (rc < 0) { + CAM_DBG(CAM_CCI, "request platform resources failed"); + goto platform_enable_failed; + } + + cci_dev->hw_version = cam_io_r_mb(base + + CCI_HW_VERSION_ADDR); + CAM_DBG(CAM_CCI, "hw_version = 0x%x", cci_dev->hw_version); + + cci_dev->payload_size = + MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11; + cci_dev->support_seq_write = 1; + + for (i = 0; i < NUM_MASTERS; i++) { + for (j = 0; j < NUM_QUEUES; j++) { + if (j == QUEUE_0) + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_QUEUE_0_SIZE; + else + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size = + CCI_I2C_QUEUE_1_SIZE; + + CAM_DBG(CAM_CCI, "CCI Master[%d] :: Q0 : %d Q1 : %d", i + , cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size, + cci_dev->cci_i2c_queue_info[i][j]. + max_queue_size); + } + } + + cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; + cam_io_w_mb(CCI_RESET_CMD_RMSK, base + + CCI_RESET_CMD_ADDR); + cam_io_w_mb(0x1, base + CCI_RESET_CMD_ADDR); + rc = wait_for_completion_timeout( + &cci_dev->cci_master_info[MASTER_0].reset_complete, + CCI_TIMEOUT); + if (rc <= 0) { + CAM_ERR(CAM_CCI, "wait_for_completion_timeout"); + if (rc == 0) + rc = -ETIMEDOUT; + goto reset_complete_failed; + } + for (i = 0; i < MASTER_MAX; i++) + cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES; + cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, + base + CCI_IRQ_MASK_0_ADDR); + cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, + base + CCI_IRQ_CLEAR_0_ADDR); + cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + for (i = 0; i < MASTER_MAX; i++) { + if (!cci_dev->write_wq[i]) { + CAM_ERR(CAM_CCI, "Failed to flush write wq"); + rc = -ENOMEM; + goto reset_complete_failed; + } else { + flush_workqueue(cci_dev->write_wq[i]); + } + } + cci_dev->cci_state = CCI_STATE_ENABLED; + + return 0; + +reset_complete_failed: + cam_soc_util_disable_platform_resource(soc_info, 1, 1); + +platform_enable_failed: + mutex_lock(&cci_dev->ref_count_mutex); + cci_dev->ref_count--; + mutex_unlock(&cci_dev->ref_count_mutex); + cam_cpas_stop(cci_dev->cpas_handle); + + return rc; +} + +void cam_cci_soc_remove(struct platform_device *pdev, + struct cci_device *cci_dev) +{ + struct cam_hw_soc_info *soc_info = &cci_dev->soc_info; + + cam_soc_util_release_platform_resource(soc_info); +} + +static void cam_cci_init_cci_params(struct cci_device *new_cci_dev) +{ + uint8_t i = 0, j = 0; + + for (i = 0; i < NUM_MASTERS; i++) { + new_cci_dev->cci_master_info[i].status = 0; + mutex_init(&new_cci_dev->cci_master_info[i].mutex); + init_completion(&new_cci_dev-> + cci_master_info[i].reset_complete); + + for (j = 0; j < NUM_QUEUES; j++) { + mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]); + init_completion(&new_cci_dev-> + cci_master_info[i].report_q[j]); + spin_lock_init( + &new_cci_dev->cci_master_info[i].lock_q[j]); + } + } +} + +static void cam_cci_init_default_clk_params(struct cci_device *cci_dev, + uint8_t index) +{ + /* default clock params are for 100Khz */ + cci_dev->cci_clk_params[index].hw_thigh = 201; + cci_dev->cci_clk_params[index].hw_tlow = 174; + cci_dev->cci_clk_params[index].hw_tsu_sto = 204; + cci_dev->cci_clk_params[index].hw_tsu_sta = 231; + cci_dev->cci_clk_params[index].hw_thd_dat = 22; + cci_dev->cci_clk_params[index].hw_thd_sta = 162; + cci_dev->cci_clk_params[index].hw_tbuf = 227; + cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0; + cci_dev->cci_clk_params[index].hw_trdhld = 6; + cci_dev->cci_clk_params[index].hw_tsp = 3; + cci_dev->cci_clk_params[index].cci_clk_src = 37500000; +} + +static void cam_cci_init_clk_params(struct cci_device *cci_dev) +{ + int32_t rc = 0; + uint32_t val = 0; + uint8_t count = 0; + struct device_node *of_node = cci_dev->v4l2_dev_str.pdev->dev.of_node; + struct device_node *src_node = NULL; + + for (count = 0; count < I2C_MAX_MODES; count++) { + + if (count == I2C_STANDARD_MODE) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_standard_mode"); + else if (count == I2C_FAST_MODE) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_fast_mode"); + else if (count == I2C_FAST_PLUS_MODE) + src_node = of_find_node_by_name(of_node, + "qcom,i2c_fast_plus_mode"); + else + src_node = of_find_node_by_name(of_node, + "qcom,i2c_custom_mode"); + + rc = of_property_read_u32(src_node, "hw-thigh", &val); + CAM_DBG(CAM_CCI, "hw-thigh %d, rc %d", val, rc); + if (!rc) { + cci_dev->cci_clk_params[count].hw_thigh = val; + rc = of_property_read_u32(src_node, "hw-tlow", + &val); + CAM_DBG(CAM_CCI, "hw-tlow %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tlow = val; + rc = of_property_read_u32(src_node, "hw-tsu-sto", + &val); + CAM_DBG(CAM_CCI, "hw-tsu-sto %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsu_sto = val; + rc = of_property_read_u32(src_node, "hw-tsu-sta", + &val); + CAM_DBG(CAM_CCI, "hw-tsu-sta %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsu_sta = val; + rc = of_property_read_u32(src_node, "hw-thd-dat", + &val); + CAM_DBG(CAM_CCI, "hw-thd-dat %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_thd_dat = val; + rc = of_property_read_u32(src_node, "hw-thd-sta", + &val); + CAM_DBG(CAM_CCI, "hw-thd-sta %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_thd_sta = val; + rc = of_property_read_u32(src_node, "hw-tbuf", + &val); + CAM_DBG(CAM_CCI, "hw-tbuf %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tbuf = val; + rc = of_property_read_u32(src_node, + "hw-scl-stretch-en", &val); + CAM_DBG(CAM_CCI, "hw-scl-stretch-en %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_scl_stretch_en = val; + rc = of_property_read_u32(src_node, "hw-trdhld", + &val); + CAM_DBG(CAM_CCI, "hw-trdhld %d, rc %d", + val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_trdhld = val; + rc = of_property_read_u32(src_node, "hw-tsp", + &val); + CAM_DBG(CAM_CCI, "hw-tsp %d, rc %d", val, rc); + } + if (!rc) { + cci_dev->cci_clk_params[count].hw_tsp = val; + val = 0; + rc = of_property_read_u32(src_node, "cci-clk-src", + &val); + CAM_DBG(CAM_CCI, "cci-clk-src %d, rc %d", val, rc); + cci_dev->cci_clk_params[count].cci_clk_src = val; + } else + cam_cci_init_default_clk_params(cci_dev, count); + + of_node_put(src_node); + } +} + +int cam_cci_parse_dt_info(struct platform_device *pdev, + struct cci_device *new_cci_dev) +{ + int rc = 0, i = 0; + struct cam_hw_soc_info *soc_info = + &new_cci_dev->soc_info; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_CCI, "Parsing DT data failed:%d", rc); + return -EINVAL; + } + + new_cci_dev->ref_count = 0; + + rc = cam_soc_util_request_platform_resource(soc_info, + cam_cci_irq, new_cci_dev); + if (rc < 0) { + CAM_ERR(CAM_CCI, "requesting platform resources failed:%d", rc); + return -EINVAL; + } + new_cci_dev->v4l2_dev_str.pdev = pdev; + cam_cci_init_cci_params(new_cci_dev); + cam_cci_init_clk_params(new_cci_dev); + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) + CAM_ERR(CAM_CCI, "failed to add child nodes, rc=%d", rc); + + for (i = 0; i < MASTER_MAX; i++) { + new_cci_dev->write_wq[i] = create_singlethread_workqueue( + "cam_cci_wq"); + if (!new_cci_dev->write_wq[i]) + CAM_ERR(CAM_CCI, "Failed to create write wq"); + } + CAM_DBG(CAM_CCI, "Exit"); + return 0; +} + +int cam_cci_soc_release(struct cci_device *cci_dev) +{ + uint8_t i = 0, rc = 0; + struct cam_hw_soc_info *soc_info = + &cci_dev->soc_info; + + mutex_lock(&cci_dev->ref_count_mutex); + if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) { + CAM_ERR(CAM_CCI, "invalid ref count %d / cci state %d", + cci_dev->ref_count, cci_dev->cci_state); + mutex_unlock(&cci_dev->ref_count_mutex); + return -EINVAL; + } + if (--cci_dev->ref_count) { + CAM_DBG(CAM_CCI, "ref_count Exit %d", cci_dev->ref_count); + mutex_unlock(&cci_dev->ref_count_mutex); + return 0; + } + mutex_unlock(&cci_dev->ref_count_mutex); + + for (i = 0; i < MASTER_MAX; i++) + if (cci_dev->write_wq[i]) + flush_workqueue(cci_dev->write_wq[i]); + + for (i = 0; i < MASTER_MAX; i++) + cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES; + + rc = cam_soc_util_disable_platform_resource(soc_info, true, true); + if (rc) { + CAM_ERR(CAM_CCI, "platform resources disable failed, rc=%d", + rc); + return rc; + } + + cci_dev->cci_state = CCI_STATE_DISABLED; + cci_dev->cycles_per_us = 0; + + cam_cpas_stop(cci_dev->cpas_handle); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.h new file mode 100644 index 000000000000..331227bbee3b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci/cam_cci_soc.h @@ -0,0 +1,52 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CCI_SOC_H_ +#define _CAM_CCI_SOC_H_ + +#include "cam_cci_core.h" +#include "cam_soc_util.h" + +/** + * @sd: V4L2 sub device + * @c_ctrl: CCI control structure + * + * This API initializes the CCI and acquires SOC resources + */ +int cam_cci_init(struct v4l2_subdev *sd, + struct cam_cci_ctrl *c_ctrl); + +/** + * @cci_dev: CCI device structure + * + * This API releases the CCI and its SOC resources + */ +int cam_cci_soc_release(struct cci_device *cci_dev); + +/** + * @pdev: Platform device + * @new_cci_dev: CCI device structure + * + * This API parses CCI device tree + */ +int cam_cci_parse_dt_info(struct platform_device *pdev, + struct cci_device *new_cci_dev); + +/** + * @pdev: Platform device + * @cci_dev: CCI device structure + * + * This API puts all SOC resources + */ +void cam_cci_soc_remove(struct platform_device *pdev, + struct cci_device *cci_dev); +#endif /* _CAM_CCI_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/Makefile new file mode 100644 index 000000000000..110171480119 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy_soc.o cam_csiphy_dev.o cam_csiphy_core.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.c new file mode 100644 index 000000000000..04e52fd660af --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.c @@ -0,0 +1,728 @@ +/* Copyright (c) 2017-2018, 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 +#include "cam_csiphy_core.h" +#include "cam_csiphy_dev.h" +#include "cam_csiphy_soc.h" + +#include +#include + +#define SCM_SVC_CAMERASS 0x18 +#define SECURE_SYSCALL_ID 0x6 + +static int csiphy_dump; +module_param(csiphy_dump, int, 0644); + +static int cam_csiphy_notify_secure_mode(int phy, bool protect) +{ + struct scm_desc desc = {0}; + + desc.arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL); + desc.args[0] = protect; + desc.args[1] = phy; + + CAM_DBG(CAM_CSIPHY, "phy : %d, protect : %d", phy, protect); + if (scm_call2(SCM_SIP_FNID(SCM_SVC_CAMERASS, SECURE_SYSCALL_ID), + &desc)) { + CAM_ERR(CAM_CSIPHY, "scm call to hypervisor failed"); + return -EINVAL; + } + + return 0; +} + +void cam_csiphy_query_cap(struct csiphy_device *csiphy_dev, + struct cam_csiphy_query_cap *csiphy_cap) +{ + struct cam_hw_soc_info *soc_info = &csiphy_dev->soc_info; + + csiphy_cap->slot_info = soc_info->index; + csiphy_cap->version = csiphy_dev->hw_version; + csiphy_cap->clk_lane = csiphy_dev->clk_lane; +} + +void cam_csiphy_reset(struct csiphy_device *csiphy_dev) +{ + int32_t i; + void __iomem *base = NULL; + uint32_t size = + csiphy_dev->ctrl_reg->csiphy_reg.csiphy_reset_array_size; + struct cam_hw_soc_info *soc_info = &csiphy_dev->soc_info; + + base = soc_info->reg_map[0].mem_base; + + for (i = 0; i < size; i++) { + cam_io_w_mb( + csiphy_dev->ctrl_reg-> + csiphy_reset_reg[i].reg_data, + base + + csiphy_dev->ctrl_reg-> + csiphy_reset_reg[i].reg_addr); + + usleep_range(csiphy_dev->ctrl_reg-> + csiphy_reset_reg[i].delay * 1000, + csiphy_dev->ctrl_reg-> + csiphy_reset_reg[i].delay * 1000 + 10); + } +} + +int32_t cam_cmd_buf_parser(struct csiphy_device *csiphy_dev, + struct cam_config_dev_cmd *cfg_dev) +{ + int32_t rc = 0; + uintptr_t generic_ptr; + uintptr_t generic_pkt_ptr; + struct cam_packet *csl_packet = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + uint32_t *cmd_buf = NULL; + struct cam_csiphy_info *cam_cmd_csiphy_info = NULL; + size_t len; + + if (!cfg_dev || !csiphy_dev) { + CAM_ERR(CAM_CSIPHY, "Invalid Args"); + return -EINVAL; + } + + rc = cam_mem_get_cpu_buf((int32_t) cfg_dev->packet_handle, + &generic_pkt_ptr, &len); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "Failed to get packet Mem address: %d", rc); + return rc; + } + + if (cfg_dev->offset > len) { + CAM_ERR(CAM_CSIPHY, + "offset is out of bounds: offset: %lld len: %zu", + cfg_dev->offset, len); + return -EINVAL; + } + + csl_packet = (struct cam_packet *) + (generic_pkt_ptr + (uint32_t)cfg_dev->offset); + + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *)&csl_packet->payload + + csl_packet->cmd_buf_offset / 4); + + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + &generic_ptr, &len); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, + "Failed to get cmd buf Mem address : %d", rc); + return rc; + } + + cmd_buf = (uint32_t *)generic_ptr; + cmd_buf += cmd_desc->offset / 4; + cam_cmd_csiphy_info = (struct cam_csiphy_info *)cmd_buf; + + csiphy_dev->config_count++; + csiphy_dev->csiphy_info.lane_cnt += cam_cmd_csiphy_info->lane_cnt; + csiphy_dev->csiphy_info.lane_mask |= cam_cmd_csiphy_info->lane_mask; + csiphy_dev->csiphy_info.csiphy_3phase = + cam_cmd_csiphy_info->csiphy_3phase; + csiphy_dev->csiphy_info.combo_mode |= cam_cmd_csiphy_info->combo_mode; + if (cam_cmd_csiphy_info->combo_mode == 1) + csiphy_dev->csiphy_info.settle_time_combo_sensor = + cam_cmd_csiphy_info->settle_time; + else + csiphy_dev->csiphy_info.settle_time = + cam_cmd_csiphy_info->settle_time; + csiphy_dev->csiphy_info.data_rate = cam_cmd_csiphy_info->data_rate; + csiphy_dev->csiphy_info.secure_mode = cam_cmd_csiphy_info->secure_mode; + + return rc; +} + +void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev) +{ + int32_t i; + void __iomem *csiphybase = + csiphy_dev->soc_info.reg_map[0].mem_base; + + for (i = 0; i < csiphy_dev->num_irq_registers; i++) + cam_io_w_mb(csiphy_dev->ctrl_reg-> + csiphy_irq_reg[i].reg_data, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_irq_reg[i].reg_addr); +} + +void cam_csiphy_cphy_irq_disable(struct csiphy_device *csiphy_dev) +{ + int32_t i; + void __iomem *csiphybase = + csiphy_dev->soc_info.reg_map[0].mem_base; + + for (i = 0; i < csiphy_dev->num_irq_registers; i++) + cam_io_w_mb(0x0, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_irq_reg[i].reg_addr); +} + +irqreturn_t cam_csiphy_irq(int irq_num, void *data) +{ + uint32_t irq; + uint8_t i; + struct csiphy_device *csiphy_dev = + (struct csiphy_device *)data; + struct cam_hw_soc_info *soc_info = NULL; + void __iomem *base = NULL; + + if (!csiphy_dev) { + CAM_ERR(CAM_CSIPHY, "Invalid Args"); + return -EINVAL; + } + + soc_info = &csiphy_dev->soc_info; + base = csiphy_dev->soc_info.reg_map[0].mem_base; + + for (i = 0; i < csiphy_dev->num_irq_registers; i++) { + irq = cam_io_r( + base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_status0_addr + 0x4*i); + cam_io_w_mb(irq, + base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_clear0_addr + 0x4*i); + CAM_ERR_RATE_LIMIT(CAM_CSIPHY, + "CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x", + soc_info->index, i, irq); + cam_io_w_mb(0x0, + base + + csiphy_dev->ctrl_reg->csiphy_reg. + mipi_csiphy_interrupt_clear0_addr + 0x4*i); + } + cam_io_w_mb(0x1, base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); + cam_io_w_mb(0x0, base + + csiphy_dev->ctrl_reg-> + csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); + + return IRQ_HANDLED; +} + +int32_t cam_csiphy_config_dev(struct csiphy_device *csiphy_dev) +{ + int32_t rc = 0; + uint32_t lane_enable = 0, mask = 1, size = 0; + uint16_t lane_mask = 0, i = 0, cfg_size = 0, temp = 0; + uint8_t lane_cnt, lane_pos = 0; + uint16_t settle_cnt = 0; + void __iomem *csiphybase; + struct csiphy_reg_t (*reg_array)[MAX_SETTINGS_PER_LANE]; + + lane_cnt = csiphy_dev->csiphy_info.lane_cnt; + csiphybase = csiphy_dev->soc_info.reg_map[0].mem_base; + + if (!csiphybase) { + CAM_ERR(CAM_CSIPHY, "csiphybase NULL"); + return -EINVAL; + } + + if (!csiphy_dev->csiphy_info.csiphy_3phase) { + if (csiphy_dev->csiphy_info.combo_mode == 1) + reg_array = + csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg; + else + reg_array = + csiphy_dev->ctrl_reg->csiphy_2ph_reg; + csiphy_dev->num_irq_registers = 11; + cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. + csiphy_2ph_config_array_size; + + lane_mask = csiphy_dev->csiphy_info.lane_mask & 0x1f; + for (i = 0; i < MAX_DPHY_DATA_LN; i++) { + if (mask == 0x2) { + if (lane_mask & mask) + lane_enable |= 0x80; + i--; + } else if (lane_mask & mask) { + lane_enable |= 0x1 << (i<<1); + } + mask <<= 1; + } + } else { + if (csiphy_dev->csiphy_info.combo_mode == 1) + reg_array = + csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg; + else + reg_array = + csiphy_dev->ctrl_reg->csiphy_3ph_reg; + csiphy_dev->num_irq_registers = 11; + cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. + csiphy_3ph_config_array_size; + + lane_mask = csiphy_dev->csiphy_info.lane_mask & 0x7; + mask = lane_mask; + while (mask != 0) { + temp = (i << 1)+1; + lane_enable |= ((mask & 0x1) << temp); + mask >>= 1; + i++; + } + } + + size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_common_array_size; + + for (i = 0; i < size; i++) { + switch (csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].csiphy_param_type) { + case CSIPHY_LANE_ENABLE: + cam_io_w_mb(lane_enable, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].reg_addr); + usleep_range(csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].delay*1000, + csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].delay*1000 + 10); + break; + case CSIPHY_DEFAULT_PARAMS: + cam_io_w_mb(csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].reg_data, + csiphybase + + csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].reg_addr); + usleep_range(csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].delay*1000, + csiphy_dev->ctrl_reg-> + csiphy_common_reg[i].delay*1000 + 10); + break; + default: + break; + } + } + + while (lane_mask) { + if (!(lane_mask & 0x1)) { + lane_pos++; + lane_mask >>= 1; + continue; + } + + settle_cnt = (csiphy_dev->csiphy_info.settle_time / 200000000); + if (csiphy_dev->csiphy_info.combo_mode == 1 && + (lane_pos >= 3)) + settle_cnt = + (csiphy_dev->csiphy_info. + settle_time_combo_sensor / 200000000); + for (i = 0; i < cfg_size; i++) { + switch (reg_array[lane_pos][i].csiphy_param_type) { + case CSIPHY_LANE_ENABLE: + cam_io_w_mb(lane_enable, + csiphybase + + reg_array[lane_pos][i].reg_addr); + break; + case CSIPHY_DEFAULT_PARAMS: + cam_io_w_mb(reg_array[lane_pos][i].reg_data, + csiphybase + + reg_array[lane_pos][i].reg_addr); + break; + case CSIPHY_SETTLE_CNT_LOWER_BYTE: + cam_io_w_mb(settle_cnt & 0xFF, + csiphybase + + reg_array[lane_pos][i].reg_addr); + break; + case CSIPHY_SETTLE_CNT_HIGHER_BYTE: + cam_io_w_mb((settle_cnt >> 8) & 0xFF, + csiphybase + + reg_array[lane_pos][i].reg_addr); + break; + default: + CAM_DBG(CAM_CSIPHY, "Do Nothing"); + break; + } + usleep_range(reg_array[lane_pos][i].delay*1000, + reg_array[lane_pos][i].delay*1000 + 1000); + } + lane_mask >>= 1; + lane_pos++; + } + + cam_csiphy_cphy_irq_config(csiphy_dev); + + return rc; +} + +void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev) +{ + struct cam_hw_soc_info *soc_info; + + if (csiphy_dev->csiphy_state == CAM_CSIPHY_INIT) + return; + + if (csiphy_dev->csiphy_state == CAM_CSIPHY_START) { + soc_info = &csiphy_dev->soc_info; + + if (csiphy_dev->csiphy_info.secure_mode) + cam_csiphy_notify_secure_mode( + csiphy_dev->soc_info.index, + CAM_SECURE_MODE_NON_SECURE); + + csiphy_dev->csiphy_info.secure_mode = + CAM_SECURE_MODE_NON_SECURE; + + cam_csiphy_reset(csiphy_dev); + cam_soc_util_disable_platform_resource(soc_info, true, true); + + cam_cpas_stop(csiphy_dev->cpas_handle); + csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE; + } + + if (csiphy_dev->csiphy_state == CAM_CSIPHY_ACQUIRE) { + if (csiphy_dev->bridge_intf.device_hdl[0] != -1) + cam_destroy_device_hdl( + csiphy_dev->bridge_intf.device_hdl[0]); + if (csiphy_dev->bridge_intf.device_hdl[1] != -1) + cam_destroy_device_hdl( + csiphy_dev->bridge_intf.device_hdl[1]); + csiphy_dev->bridge_intf.device_hdl[0] = -1; + csiphy_dev->bridge_intf.device_hdl[1] = -1; + csiphy_dev->bridge_intf.link_hdl[0] = -1; + csiphy_dev->bridge_intf.link_hdl[1] = -1; + csiphy_dev->bridge_intf.session_hdl[0] = -1; + csiphy_dev->bridge_intf.session_hdl[1] = -1; + } + + csiphy_dev->ref_count = 0; + csiphy_dev->is_acquired_dev_combo_mode = 0; + csiphy_dev->acquire_count = 0; + csiphy_dev->start_dev_count = 0; + csiphy_dev->csiphy_state = CAM_CSIPHY_INIT; +} + +static int32_t cam_csiphy_external_cmd(struct csiphy_device *csiphy_dev, + struct cam_config_dev_cmd *p_submit_cmd) +{ + struct cam_csiphy_info cam_cmd_csiphy_info; + int32_t rc = 0; + + if (copy_from_user(&cam_cmd_csiphy_info, + u64_to_user_ptr(p_submit_cmd->packet_handle), + sizeof(struct cam_csiphy_info))) { + CAM_ERR(CAM_CSIPHY, "failed to copy cam_csiphy_info\n"); + rc = -EFAULT; + } else { + csiphy_dev->csiphy_info.lane_cnt = + cam_cmd_csiphy_info.lane_cnt; + csiphy_dev->csiphy_info.lane_cnt = + cam_cmd_csiphy_info.lane_cnt; + csiphy_dev->csiphy_info.lane_mask = + cam_cmd_csiphy_info.lane_mask; + csiphy_dev->csiphy_info.csiphy_3phase = + cam_cmd_csiphy_info.csiphy_3phase; + csiphy_dev->csiphy_info.combo_mode = + cam_cmd_csiphy_info.combo_mode; + csiphy_dev->csiphy_info.settle_time = + cam_cmd_csiphy_info.settle_time; + csiphy_dev->csiphy_info.data_rate = + cam_cmd_csiphy_info.data_rate; + CAM_DBG(CAM_CSIPHY, + "%s CONFIG_DEV_EXT settle_time= %lld lane_cnt=%d lane_mask=0x%x", + __func__, + csiphy_dev->csiphy_info.settle_time, + csiphy_dev->csiphy_info.lane_cnt, + csiphy_dev->csiphy_info.lane_mask); + } + + return rc; +} + +int32_t cam_csiphy_core_cfg(void *phy_dev, + void *arg) +{ + struct csiphy_device *csiphy_dev = + (struct csiphy_device *)phy_dev; + struct cam_control *cmd = (struct cam_control *)arg; + int32_t rc = 0; + + if (!csiphy_dev || !cmd) { + CAM_ERR(CAM_CSIPHY, "Invalid input args"); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_CSIPHY, "Invalid handle type: %d", + cmd->handle_type); + return -EINVAL; + } + + CAM_DBG(CAM_CSIPHY, "Opcode received: %d", cmd->op_code); + mutex_lock(&csiphy_dev->mutex); + switch (cmd->op_code) { + case CAM_ACQUIRE_DEV: { + struct cam_sensor_acquire_dev csiphy_acq_dev; + struct cam_csiphy_acquire_dev_info csiphy_acq_params; + + struct cam_create_dev_hdl bridge_params; + + rc = copy_from_user(&csiphy_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(csiphy_acq_dev)); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "Failed copying from User"); + goto release_mutex; + } + + csiphy_acq_params.combo_mode = 0; + + if (copy_from_user(&csiphy_acq_params, + u64_to_user_ptr(csiphy_acq_dev.info_handle), + sizeof(csiphy_acq_params))) { + CAM_ERR(CAM_CSIPHY, + "Failed copying from User"); + goto release_mutex; + } + + if (csiphy_dev->acquire_count == 2) { + CAM_ERR(CAM_CSIPHY, + "CSIPHY device do not allow more than 2 acquires"); + rc = -EINVAL; + goto release_mutex; + } + + if ((csiphy_acq_params.combo_mode == 1) && + (csiphy_dev->is_acquired_dev_combo_mode == 1)) { + CAM_ERR(CAM_CSIPHY, + "Multiple Combo Acq are not allowed: cm: %d, acm: %d", + csiphy_acq_params.combo_mode, + csiphy_dev->is_acquired_dev_combo_mode); + rc = -EINVAL; + goto release_mutex; + } + + if ((csiphy_acq_params.combo_mode != 1) && + (csiphy_dev->is_acquired_dev_combo_mode != 1) && + (csiphy_dev->acquire_count == 1)) { + CAM_ERR(CAM_CSIPHY, + "Multiple Acquires are not allowed cm: %d acm: %d", + csiphy_acq_params.combo_mode, + csiphy_dev->is_acquired_dev_combo_mode); + rc = -EINVAL; + goto release_mutex; + } + + bridge_params.ops = NULL; + bridge_params.session_hdl = csiphy_acq_dev.session_handle; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = csiphy_dev; + + if (csiphy_acq_params.combo_mode >= 2) { + CAM_ERR(CAM_CSIPHY, "Invalid combo_mode %d", + csiphy_acq_params.combo_mode); + rc = -EINVAL; + goto release_mutex; + } + + csiphy_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + csiphy_dev->bridge_intf. + device_hdl[csiphy_acq_params.combo_mode] = + csiphy_acq_dev.device_handle; + csiphy_dev->bridge_intf. + session_hdl[csiphy_acq_params.combo_mode] = + csiphy_acq_dev.session_handle; + + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &csiphy_acq_dev, + sizeof(struct cam_sensor_acquire_dev))) { + CAM_ERR(CAM_CSIPHY, "Failed copying from User"); + rc = -EINVAL; + goto release_mutex; + } + if (csiphy_acq_params.combo_mode == 1) + csiphy_dev->is_acquired_dev_combo_mode = 1; + + csiphy_dev->acquire_count++; + csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE; + } + break; + case CAM_QUERY_CAP: { + struct cam_csiphy_query_cap csiphy_cap = {0}; + + cam_csiphy_query_cap(csiphy_dev, &csiphy_cap); + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &csiphy_cap, sizeof(struct cam_csiphy_query_cap))) { + CAM_ERR(CAM_CSIPHY, "Failed copying from User"); + rc = -EINVAL; + goto release_mutex; + } + } + break; + case CAM_STOP_DEV: { + if ((csiphy_dev->csiphy_state != CAM_CSIPHY_START) || + !csiphy_dev->start_dev_count) { + CAM_ERR(CAM_CSIPHY, "Not in right state to stop : %d", + csiphy_dev->csiphy_state); + goto release_mutex; + } + + if (--csiphy_dev->start_dev_count) { + CAM_DBG(CAM_CSIPHY, "Stop Dev ref Cnt: %d", + csiphy_dev->start_dev_count); + goto release_mutex; + } + + if (csiphy_dev->csiphy_info.secure_mode) + cam_csiphy_notify_secure_mode( + csiphy_dev->soc_info.index, + CAM_SECURE_MODE_NON_SECURE); + + csiphy_dev->csiphy_info.secure_mode = + CAM_SECURE_MODE_NON_SECURE; + + rc = cam_csiphy_disable_hw(csiphy_dev); + if (rc < 0) + CAM_ERR(CAM_CSIPHY, "Failed in csiphy release"); + + rc = cam_cpas_stop(csiphy_dev->cpas_handle); + if (rc < 0) + CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc); + + csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE; + } + break; + case CAM_RELEASE_DEV: { + struct cam_release_dev_cmd release; + + if (!csiphy_dev->acquire_count) { + CAM_ERR(CAM_CSIPHY, "No valid devices to release"); + rc = -EINVAL; + goto release_mutex; + } + + if (copy_from_user(&release, + u64_to_user_ptr(cmd->handle), + sizeof(release))) { + rc = -EFAULT; + goto release_mutex; + } + + rc = cam_destroy_device_hdl(release.dev_handle); + if (rc < 0) + CAM_ERR(CAM_CSIPHY, "destroying the device hdl"); + if (release.dev_handle == + csiphy_dev->bridge_intf.device_hdl[0]) { + csiphy_dev->bridge_intf.device_hdl[0] = -1; + csiphy_dev->bridge_intf.link_hdl[0] = -1; + csiphy_dev->bridge_intf.session_hdl[0] = -1; + } else { + csiphy_dev->bridge_intf.device_hdl[1] = -1; + csiphy_dev->bridge_intf.link_hdl[1] = -1; + csiphy_dev->bridge_intf.session_hdl[1] = -1; + csiphy_dev->is_acquired_dev_combo_mode = 0; + } + + csiphy_dev->config_count--; + csiphy_dev->acquire_count--; + + if (csiphy_dev->acquire_count == 0) + csiphy_dev->csiphy_state = CAM_CSIPHY_INIT; + } + break; + case CAM_CONFIG_DEV: { + struct cam_config_dev_cmd config; + + if (copy_from_user(&config, + u64_to_user_ptr(cmd->handle), + sizeof(config))) { + rc = -EFAULT; + } else { + rc = cam_cmd_buf_parser(csiphy_dev, &config); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "Fail in cmd buf parser"); + goto release_mutex; + } + } + break; + } + case CAM_START_DEV: { + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote; + + if (csiphy_dev->csiphy_state == CAM_CSIPHY_START) { + csiphy_dev->start_dev_count++; + goto release_mutex; + } + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; + + rc = cam_cpas_start(csiphy_dev->cpas_handle, + &ahb_vote, &axi_vote); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "voting CPAS: %d", rc); + goto release_mutex; + } + + if (csiphy_dev->csiphy_info.secure_mode) { + rc = cam_csiphy_notify_secure_mode( + csiphy_dev->soc_info.index, + CAM_SECURE_MODE_SECURE); + if (rc < 0) + csiphy_dev->csiphy_info.secure_mode = + CAM_SECURE_MODE_NON_SECURE; + } + + rc = cam_csiphy_enable_hw(csiphy_dev); + if (rc != 0) { + CAM_ERR(CAM_CSIPHY, "cam_csiphy_enable_hw failed"); + cam_cpas_stop(csiphy_dev->cpas_handle); + goto release_mutex; + } + rc = cam_csiphy_config_dev(csiphy_dev); + if (csiphy_dump == 1) + cam_csiphy_mem_dmp(&csiphy_dev->soc_info); + + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "cam_csiphy_config_dev failed"); + cam_csiphy_disable_hw(csiphy_dev); + cam_cpas_stop(csiphy_dev->cpas_handle); + goto release_mutex; + } + csiphy_dev->start_dev_count++; + csiphy_dev->csiphy_state = CAM_CSIPHY_START; + } + break; + case CAM_CONFIG_DEV_EXTERNAL: { + struct cam_config_dev_cmd submit_cmd; + + if (copy_from_user(&submit_cmd, + u64_to_user_ptr(cmd->handle), + sizeof(struct cam_config_dev_cmd))) { + CAM_ERR(CAM_CSIPHY, "failed copy config ext\n"); + rc = -EFAULT; + } else { + rc = cam_csiphy_external_cmd(csiphy_dev, &submit_cmd); + } + break; + } + default: + CAM_ERR(CAM_CSIPHY, "Invalid Opcode: %d", cmd->op_code); + rc = -EINVAL; + goto release_mutex; + } + +release_mutex: + mutex_unlock(&csiphy_dev->mutex); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.h new file mode 100644 index 000000000000..361004beb3cd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_core.h @@ -0,0 +1,59 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CSIPHY_CORE_H_ +#define _CAM_CSIPHY_CORE_H_ + +#include +#include "cam_csiphy_dev.h" +#include +#include +#include + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API programs CSIPhy IRQ registers + */ +void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev); + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API resets CSIPhy hardware + */ +void cam_csiphy_reset(struct csiphy_device *csiphy_dev); + +/** + * @csiphy_dev: CSIPhy device structure + * @arg: Camera control command argument + * + * This API handles the camera control argument reached to CSIPhy + */ +int cam_csiphy_core_cfg(void *csiphy_dev, void *arg); + +/** + * @irq_num: IRQ number + * @data: CSIPhy device structure + * + * This API handles CSIPhy IRQs + */ +irqreturn_t cam_csiphy_irq(int irq_num, void *data); + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API handles the CSIPhy close + */ +void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev); + +#endif /* _CAM_CSIPHY_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c new file mode 100644 index 000000000000..e2f061f49a31 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c @@ -0,0 +1,252 @@ +/* Copyright (c) 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 "cam_csiphy_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_csiphy_soc.h" +#include "cam_csiphy_core.h" +#include + +static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd); + int rc = 0; + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_csiphy_core_cfg(csiphy_dev, arg); + if (rc != 0) { + CAM_ERR(CAM_CSIPHY, "in configuring the device"); + return rc; + } + break; + default: + CAM_ERR(CAM_CSIPHY, "Wrong ioctl : %d", cmd); + break; + } + + return rc; +} + +static int cam_csiphy_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct csiphy_device *csiphy_dev = + v4l2_get_subdevdata(sd); + + if (!csiphy_dev) { + CAM_ERR(CAM_CSIPHY, "csiphy_dev ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&csiphy_dev->mutex); + cam_csiphy_shutdown(csiphy_dev); + mutex_unlock(&csiphy_dev->mutex); + + return 0; +} + +#ifdef CONFIG_COMPAT +static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + int32_t rc = 0; + struct cam_control cmd_data; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_CSIPHY, "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + /* All the arguments converted to 64 bit here + * Passed to the api in core.c + */ + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_csiphy_subdev_ioctl(sd, cmd, &cmd_data); + break; + default: + CAM_ERR(CAM_CSIPHY, "Invalid compat ioctl cmd: %d", cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_CSIPHY, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + +static struct v4l2_subdev_core_ops csiphy_subdev_core_ops = { + .ioctl = cam_csiphy_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_csiphy_subdev_compat_ioctl, +#endif +}; + +static const struct v4l2_subdev_ops csiphy_subdev_ops = { + .core = &csiphy_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops = { + .close = cam_csiphy_subdev_close, +}; + +static int32_t cam_csiphy_platform_probe(struct platform_device *pdev) +{ + struct cam_cpas_register_params cpas_parms; + struct csiphy_device *new_csiphy_dev; + int32_t rc = 0; + + new_csiphy_dev = devm_kzalloc(&pdev->dev, + sizeof(struct csiphy_device), GFP_KERNEL); + if (!new_csiphy_dev) + return -ENOMEM; + + new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t), + GFP_KERNEL); + if (!new_csiphy_dev->ctrl_reg) { + devm_kfree(&pdev->dev, new_csiphy_dev); + return -ENOMEM; + } + + mutex_init(&new_csiphy_dev->mutex); + new_csiphy_dev->v4l2_dev_str.pdev = pdev; + + new_csiphy_dev->soc_info.pdev = pdev; + new_csiphy_dev->soc_info.dev = &pdev->dev; + new_csiphy_dev->soc_info.dev_name = pdev->name; + new_csiphy_dev->ref_count = 0; + + rc = cam_csiphy_parse_dt_info(pdev, new_csiphy_dev); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "DT parsing failed: %d", rc); + goto csiphy_no_resource; + } + + new_csiphy_dev->v4l2_dev_str.internal_ops = + &csiphy_subdev_intern_ops; + new_csiphy_dev->v4l2_dev_str.ops = + &csiphy_subdev_ops; + strlcpy(new_csiphy_dev->device_name, CAMX_CSIPHY_DEV_NAME, + sizeof(new_csiphy_dev->device_name)); + new_csiphy_dev->v4l2_dev_str.name = + new_csiphy_dev->device_name; + new_csiphy_dev->v4l2_dev_str.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + new_csiphy_dev->v4l2_dev_str.ent_function = + CAM_CSIPHY_DEVICE_TYPE; + new_csiphy_dev->v4l2_dev_str.token = + new_csiphy_dev; + + rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str)); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "cam_register_subdev Failed rc: %d", rc); + goto csiphy_no_resource; + } + + platform_set_drvdata(pdev, &(new_csiphy_dev->v4l2_dev_str.sd)); + v4l2_set_subdevdata(&(new_csiphy_dev->v4l2_dev_str.sd), new_csiphy_dev); + + new_csiphy_dev->bridge_intf.device_hdl[0] = -1; + new_csiphy_dev->bridge_intf.device_hdl[1] = -1; + new_csiphy_dev->bridge_intf.ops.get_dev_info = + NULL; + new_csiphy_dev->bridge_intf.ops.link_setup = + NULL; + new_csiphy_dev->bridge_intf.ops.apply_req = + NULL; + + new_csiphy_dev->acquire_count = 0; + new_csiphy_dev->start_dev_count = 0; + new_csiphy_dev->is_acquired_dev_combo_mode = 0; + + cpas_parms.cam_cpas_client_cb = NULL; + cpas_parms.cell_index = new_csiphy_dev->soc_info.index; + cpas_parms.dev = &pdev->dev; + cpas_parms.userdata = new_csiphy_dev; + + strlcpy(cpas_parms.identifier, "csiphy", CAM_HW_IDENTIFIER_LENGTH); + rc = cam_cpas_register_client(&cpas_parms); + if (rc) { + CAM_ERR(CAM_CSIPHY, "CPAS registration failed rc: %d", rc); + goto csiphy_no_resource; + } + CAM_DBG(CAM_CSIPHY, "CPAS registration successful handle=%d", + cpas_parms.client_handle); + new_csiphy_dev->cpas_handle = cpas_parms.client_handle; + + return rc; +csiphy_no_resource: + mutex_destroy(&new_csiphy_dev->mutex); + kfree(new_csiphy_dev->ctrl_reg); + devm_kfree(&pdev->dev, new_csiphy_dev); + return rc; +} + + +static int32_t cam_csiphy_device_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = + platform_get_drvdata(pdev); + struct csiphy_device *csiphy_dev = + v4l2_get_subdevdata(subdev); + + cam_cpas_unregister_client(csiphy_dev->cpas_handle); + cam_csiphy_soc_release(csiphy_dev); + kfree(csiphy_dev->ctrl_reg); + devm_kfree(&pdev->dev, csiphy_dev); + + return 0; +} + +static const struct of_device_id cam_csiphy_dt_match[] = { + {.compatible = "qcom,csiphy"}, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_csiphy_dt_match); + +static struct platform_driver csiphy_driver = { + .probe = cam_csiphy_platform_probe, + .remove = cam_csiphy_device_remove, + .driver = { + .name = CAMX_CSIPHY_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_csiphy_dt_match, + }, +}; + +static int32_t __init cam_csiphy_init_module(void) +{ + return platform_driver_register(&csiphy_driver); +} + +static void __exit cam_csiphy_exit_module(void) +{ + platform_driver_unregister(&csiphy_driver); +} + +module_init(cam_csiphy_init_module); +module_exit(cam_csiphy_exit_module); +MODULE_DESCRIPTION("CAM CSIPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h new file mode 100644 index 000000000000..04a3243d1fcd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h @@ -0,0 +1,237 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CSIPHY_DEV_H_ +#define _CAM_CSIPHY_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define MAX_CSIPHY 3 +#define MAX_DPHY_DATA_LN 4 +#define MAX_LRME_V4l2_EVENTS 30 +#define CSIPHY_NUM_CLK_MAX 16 +#define MAX_CSIPHY_REG_ARRAY 70 +#define MAX_CSIPHY_CMN_REG_ARRAY 5 + +#define MAX_LANES 5 +#define MAX_SETTINGS_PER_LANE 20 + +#define MAX_REGULATOR 5 +#define CAMX_CSIPHY_DEV_NAME "cam-csiphy-driver" + +#define CSIPHY_POWER_UP 0 +#define CSIPHY_POWER_DOWN 1 + +#define CSIPHY_DEFAULT_PARAMS 0 +#define CSIPHY_LANE_ENABLE 1 +#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2 +#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3 +#define CSIPHY_DNP_PARAMS 4 + +#define ENABLE_IRQ false + +#undef CDBG +#ifdef CAM_CSIPHY_CORE_DEBUG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +enum cam_csiphy_state { + CAM_CSIPHY_INIT, + CAM_CSIPHY_ACQUIRE, + CAM_CSIPHY_START, +}; + +/** + * struct csiphy_reg_parms_t + * @mipi_csiphy_glbl_irq_cmd_addr: CSIPhy irq addr + * @mipi_csiphy_interrupt_status0_addr: + * CSIPhy interrupt status addr + * @mipi_csiphy_interrupt_mask0_addr: + * CSIPhy interrupt mask addr + * @mipi_csiphy_interrupt_mask_val: + * CSIPhy interrupt mask val + * @mipi_csiphy_interrupt_clear0_addr: + * CSIPhy interrupt clear addr + * @csiphy_version: CSIPhy Version + * @csiphy_common_array_size: CSIPhy common array size + * @csiphy_reset_array_size: CSIPhy reset array size + */ +struct csiphy_reg_parms_t { +/*MIPI CSI PHY registers*/ + uint32_t mipi_csiphy_glbl_irq_cmd_addr; + uint32_t mipi_csiphy_interrupt_status0_addr; + uint32_t mipi_csiphy_interrupt_mask0_addr; + uint32_t mipi_csiphy_interrupt_mask_val; + uint32_t mipi_csiphy_interrupt_mask_addr; + uint32_t mipi_csiphy_interrupt_clear0_addr; + uint32_t csiphy_version; + uint32_t csiphy_common_array_size; + uint32_t csiphy_reset_array_size; + uint32_t csiphy_2ph_config_array_size; + uint32_t csiphy_3ph_config_array_size; +}; + +/** + * struct intf_params + * @device_hdl: Device Handle + * @session_hdl: Session Handle + * @ops: KMD operations + * @crm_cb: Callback API pointers + */ +struct intf_params { + int32_t device_hdl[2]; + int32_t session_hdl[2]; + int32_t link_hdl[2]; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct csiphy_reg_t + * @reg_addr: Register address + * @reg_data: Register data + * @delay: Delay + * @csiphy_param_type: CSIPhy parameter type + */ +struct csiphy_reg_t { + int32_t reg_addr; + int32_t reg_data; + int32_t delay; + uint32_t csiphy_param_type; +}; + +/** + * struct csiphy_ctrl_t + * @csiphy_reg: Register address + * @csiphy_common_reg: Common register set + * @csiphy_reset_reg: Reset register set + * @csiphy_2ph_reg: 2phase register set + * @csiphy_2ph_combo_mode_reg: + * 2phase combo register set + * @csiphy_3ph_reg: 3phase register set + * @csiphy_2ph_3ph_mode_reg: + * 2 phase 3phase combo register set + */ +struct csiphy_ctrl_t { + struct csiphy_reg_parms_t csiphy_reg; + struct csiphy_reg_t *csiphy_common_reg; + struct csiphy_reg_t *csiphy_irq_reg; + struct csiphy_reg_t *csiphy_reset_reg; + struct csiphy_reg_t (*csiphy_2ph_reg)[MAX_SETTINGS_PER_LANE]; + struct csiphy_reg_t (*csiphy_2ph_combo_mode_reg)[MAX_SETTINGS_PER_LANE]; + struct csiphy_reg_t (*csiphy_3ph_reg)[MAX_SETTINGS_PER_LANE]; + struct csiphy_reg_t (*csiphy_2ph_3ph_mode_reg)[MAX_SETTINGS_PER_LANE]; +}; + +/** + * cam_csiphy_param: Provides cmdbuffer structre + * @lane_mask : Lane mask details + * @lane_assign : Lane sensor will be using + * @csiphy_3phase : Mentions DPHY or CPHY + * @combo_mode : Info regarding combo_mode is enable / disable + * @lane_cnt : Total number of lanes + * @reserved + * @3phase : Details whether 3Phase / 2Phase operation + * @settle_time : Settling time in ms + * @settle_time_combo_sensor : Settling time in ms + * @data_rate : Data rate in mbps + * + */ +struct cam_csiphy_param { + uint16_t lane_mask; + uint16_t lane_assign; + uint8_t csiphy_3phase; + uint8_t combo_mode; + uint8_t lane_cnt; + uint8_t secure_mode; + uint64_t settle_time; + uint64_t settle_time_combo_sensor; + uint64_t data_rate; +}; + +/** + * struct csiphy_device + * @pdev: Platform device + * @irq: Interrupt structure + * @base: Base address + * @hw_version: Hardware Version + * @csiphy_state: CSIPhy state + * @ctrl_reg: CSIPhy control registers + * @num_clk: Number of clocks + * @csiphy_max_clk: Max timer clock rate + * @num_vreg: Number of regulators + * @csiphy_clk: Clock structure + * @csiphy_clk_info: Clock information structure + * @csiphy_vreg: Regulator structure + * @csiphy_reg_ptr: Regulator structure + * @csiphy_3p_clk_info: 3Phase clock information + * @csiphy_3p_clk: 3Phase clocks structure + * @csiphy_clk_index: Timer Src clk index + * @csi_3phase: Is it a 3Phase mode + * @ref_count: Reference count + * @clk_lane: Clock lane + * @acquire_count: Acquire device count + * @start_dev_count: Start count + * @is_acquired_dev_combo_mode: + * Flag that mentions whether already acquired + * device is for combo mode + */ +struct csiphy_device { + struct mutex mutex; + uint32_t hw_version; + enum cam_csiphy_state csiphy_state; + struct csiphy_ctrl_t *ctrl_reg; + uint32_t csiphy_max_clk; + struct msm_cam_clk_info csiphy_3p_clk_info[2]; + struct clk *csiphy_3p_clk[2]; + uint32_t csiphy_clk_index; + unsigned char csi_3phase; + int32_t ref_count; + uint16_t lane_mask[MAX_CSIPHY]; + uint8_t is_csiphy_3phase_hw; + uint8_t num_irq_registers; + struct cam_subdev v4l2_dev_str; + struct cam_csiphy_param csiphy_info; + struct intf_params bridge_intf; + uint32_t clk_lane; + uint32_t acquire_count; + uint32_t start_dev_count; + char device_name[20]; + uint32_t is_acquired_dev_combo_mode; + struct cam_hw_soc_info soc_info; + uint32_t cpas_handle; + uint32_t config_count; +}; + +#endif /* _CAM_CSIPHY_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c new file mode 100644 index 000000000000..6db5a9796606 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c @@ -0,0 +1,237 @@ +/* Copyright (c) 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 "cam_csiphy_soc.h" +#include "cam_csiphy_core.h" +#include "include/cam_csiphy_1_0_hwreg.h" + +#define BYTES_PER_REGISTER 4 +#define NUM_REGISTER_PER_LINE 4 +#define REG_OFFSET(__start, __i) ((__start) + ((__i) * BYTES_PER_REGISTER)) + +static int cam_io_phy_dump(void __iomem *base_addr, + uint32_t start_offset, int size) +{ + char line_str[128]; + char *p_str; + int i; + uint32_t data; + + CAM_INFO(CAM_CSIPHY, "addr=%pK offset=0x%x size=%d", + base_addr, start_offset, size); + + if (!base_addr || (size <= 0)) + return -EINVAL; + + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size; i++) { + if (i % NUM_REGISTER_PER_LINE == 0) { + snprintf(p_str, 12, "0x%08x: ", + REG_OFFSET(start_offset, i)); + p_str += 11; + } + data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i)); + snprintf(p_str, 9, "%08x ", data); + p_str += 8; + if ((i + 1) % NUM_REGISTER_PER_LINE == 0) { + CAM_ERR(CAM_CSIPHY, "%s", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + CAM_ERR(CAM_CSIPHY, "%s", line_str); + + return 0; +} + +int32_t cam_csiphy_mem_dmp(struct cam_hw_soc_info *soc_info) +{ + int32_t rc = 0; + resource_size_t size = 0; + void __iomem *addr = NULL; + + if (!soc_info) { + rc = -EINVAL; + CAM_ERR(CAM_CSIPHY, "invalid input %d", rc); + return rc; + } + addr = soc_info->reg_map[0].mem_base; + size = resource_size(soc_info->mem_block[0]); + rc = cam_io_phy_dump(addr, 0, (size >> 2)); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "generating dump failed %d", rc); + return rc; + } + return rc; +} + +int32_t cam_csiphy_enable_hw(struct csiphy_device *csiphy_dev) +{ + int32_t rc = 0; + struct cam_hw_soc_info *soc_info; + + soc_info = &csiphy_dev->soc_info; + + if (csiphy_dev->ref_count++) { + CAM_ERR(CAM_CSIPHY, "csiphy refcount = %d", + csiphy_dev->ref_count); + return rc; + } + + rc = cam_soc_util_enable_platform_resource(soc_info, true, + CAM_SVS_VOTE, ENABLE_IRQ); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "failed to enable platform resources %d", + rc); + return rc; + } + + rc = cam_soc_util_set_clk_rate( + soc_info->clk[csiphy_dev->csiphy_clk_index], + soc_info->clk_name[csiphy_dev->csiphy_clk_index], + soc_info->clk_rate[0][csiphy_dev->csiphy_clk_index]); + + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "csiphy_clk_set_rate failed rc: %d", rc); + goto csiphy_disable_platform_resource; + } + + cam_csiphy_reset(csiphy_dev); + + return rc; + + +csiphy_disable_platform_resource: + cam_soc_util_disable_platform_resource(soc_info, true, true); + + return rc; +} + +int32_t cam_csiphy_disable_hw(struct csiphy_device *csiphy_dev) +{ + struct cam_hw_soc_info *soc_info; + + if (!csiphy_dev || !csiphy_dev->ref_count) { + CAM_ERR(CAM_CSIPHY, "csiphy dev NULL / ref_count ZERO"); + return 0; + } + soc_info = &csiphy_dev->soc_info; + + if (--csiphy_dev->ref_count) { + CAM_ERR(CAM_CSIPHY, "csiphy refcount = %d", + csiphy_dev->ref_count); + return 0; + } + + cam_csiphy_reset(csiphy_dev); + + cam_soc_util_disable_platform_resource(soc_info, true, true); + + return 0; +} + +int32_t cam_csiphy_parse_dt_info(struct platform_device *pdev, + struct csiphy_device *csiphy_dev) +{ + int32_t rc = 0, i = 0; + uint32_t clk_cnt = 0; + char *csi_3p_clk_name = "csi_phy_3p_clk"; + char *csi_3p_clk_src_name = "csiphy_3p_clk_src"; + struct cam_hw_soc_info *soc_info; + + csiphy_dev->is_csiphy_3phase_hw = 0; + soc_info = &csiphy_dev->soc_info; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "parsing common soc dt(rc %d)", rc); + return rc; + } + + csiphy_dev->is_csiphy_3phase_hw = 0; + if (of_device_is_compatible(csiphy_dev->v4l2_dev_str.pdev->dev.of_node, + "qcom,csiphy-v1.0")) { + csiphy_dev->ctrl_reg->csiphy_2ph_reg = csiphy_2ph_v1_0_reg; + csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg = + csiphy_2ph_v1_0_combo_mode_reg; + csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_3ph_v1_0_reg; + csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg = + csiphy_3ph_v1_0_combo_mode_reg; + csiphy_dev->ctrl_reg->csiphy_irq_reg = csiphy_irq_reg_1_0; + csiphy_dev->ctrl_reg->csiphy_common_reg = csiphy_common_reg_1_0; + csiphy_dev->ctrl_reg->csiphy_reset_reg = csiphy_reset_reg_1_0; + csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v1_0; + csiphy_dev->hw_version = CSIPHY_VERSION_V10; + csiphy_dev->is_csiphy_3phase_hw = CSI_3PHASE_HW; + csiphy_dev->clk_lane = 0; + } else { + CAM_ERR(CAM_CSIPHY, "invalid hw version : 0x%x", + csiphy_dev->hw_version); + rc = -EINVAL; + return rc; + } + + if (soc_info->num_clk > CSIPHY_NUM_CLK_MAX) { + CAM_ERR(CAM_CSIPHY, "invalid clk count=%d, max is %d", + soc_info->num_clk, CSIPHY_NUM_CLK_MAX); + return -EINVAL; + } + for (i = 0; i < soc_info->num_clk; i++) { + if (!strcmp(soc_info->clk_name[i], + csi_3p_clk_src_name)) { + csiphy_dev->csiphy_3p_clk_info[0].clk_name = + soc_info->clk_name[i]; + csiphy_dev->csiphy_3p_clk_info[0].clk_rate = + soc_info->clk_rate[0][i]; + csiphy_dev->csiphy_3p_clk[0] = + soc_info->clk[i]; + continue; + } else if (!strcmp(soc_info->clk_name[i], + csi_3p_clk_name)) { + csiphy_dev->csiphy_3p_clk_info[1].clk_name = + soc_info->clk_name[i]; + csiphy_dev->csiphy_3p_clk_info[1].clk_rate = + soc_info->clk_rate[0][i]; + csiphy_dev->csiphy_3p_clk[1] = + soc_info->clk[i]; + continue; + } + + if (!strcmp(soc_info->clk_name[i], + "csiphy_timer_src_clk")) { + csiphy_dev->csiphy_max_clk = + soc_info->clk_rate[0][clk_cnt]; + csiphy_dev->csiphy_clk_index = clk_cnt; + } + CAM_DBG(CAM_CSIPHY, "clk_rate[%d] = %d", clk_cnt, + soc_info->clk_rate[0][clk_cnt]); + clk_cnt++; + } + rc = cam_soc_util_request_platform_resource(&csiphy_dev->soc_info, + cam_csiphy_irq, csiphy_dev); + + return rc; +} + +int32_t cam_csiphy_soc_release(struct csiphy_device *csiphy_dev) +{ + if (!csiphy_dev) { + CAM_ERR(CAM_CSIPHY, "csiphy dev NULL"); + return 0; + } + + cam_soc_util_release_platform_resource(&csiphy_dev->soc_info); + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h new file mode 100644 index 000000000000..443048979d67 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/cam_csiphy_soc.h @@ -0,0 +1,78 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_CSIPHY_SOC_H_ +#define _CAM_CSIPHY_SOC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_csiphy_dev.h" +#include "cam_csiphy_core.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +#define CSI_3PHASE_HW 1 +#define CSIPHY_VERSION_V35 0x35 +#define CSIPHY_VERSION_V10 0x10 + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API release SOC related parameters + */ +int cam_csiphy_soc_release(struct csiphy_device *csiphy_dev); + +/** + * @pdev: Platform device + * @csiphy_dev: CSIPhy device structure + * + * This API parses csiphy device tree information + */ +int cam_csiphy_parse_dt_info(struct platform_device *pdev, + struct csiphy_device *csiphy_dev); + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API enables SOC related parameters + */ +int cam_csiphy_enable_hw(struct csiphy_device *csiphy_dev); + +/** + * @csiphy_dev: CSIPhy device structure + * + * This API disables SOC related parameters + */ +int cam_csiphy_disable_hw(struct csiphy_device *csiphy_dev); + +/** + * @soc_info: Soc info of cam hw driver module + * + * This API dumps memory for the entire mapped region + * (needs to be macro enabled before use) + */ +int cam_csiphy_mem_dmp(struct cam_hw_soc_info *soc_info); + +#endif /* _CAM_CSIPHY_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h new file mode 100644 index 000000000000..d37f09a30dfe --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h @@ -0,0 +1,355 @@ +/* Copyright (c) 2017-2018, 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 _CAM_CSIPHY_1_0_HWREG_H_ +#define _CAM_CSIPHY_1_0_HWREG_H_ + +#include "../cam_csiphy_dev.h" + +struct csiphy_reg_parms_t csiphy_v1_0 = { + .mipi_csiphy_interrupt_status0_addr = 0x8B0, + .mipi_csiphy_interrupt_clear0_addr = 0x858, + .mipi_csiphy_glbl_irq_cmd_addr = 0x828, + .csiphy_common_array_size = 5, + .csiphy_reset_array_size = 5, + .csiphy_2ph_config_array_size = 14, + .csiphy_3ph_config_array_size = 19, +}; + +struct csiphy_reg_t csiphy_common_reg_1_0[] = { + {0x0814, 0x00, 0x00, CSIPHY_LANE_ENABLE}, + {0x0818, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081C, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x01, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, +}; + +struct csiphy_reg_t csiphy_reset_reg_1_0[] = { + {0x0814, 0x00, 0x05, CSIPHY_LANE_ENABLE}, + {0x0818, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x01, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, +}; + +struct csiphy_reg_t csiphy_irq_reg_1_0[] = { + {0x082c, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0830, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0834, 0xFB, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0838, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x083c, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0840, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0844, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0848, 0xEF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x084c, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0850, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0854, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, +}; + +struct csiphy_reg_t csiphy_2ph_v1_0_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = { + { + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + +struct csiphy_reg_t + csiphy_2ph_v1_0_combo_mode_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = { + { + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x0A, 0x00, CSIPHY_DNP_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x0A, 0x00, CSIPHY_DNP_PARAMS}, + {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x0E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x060C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + +struct csiphy_reg_t csiphy_3ph_v1_0_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = { + { + {0x015C, 0x43, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0168, 0xA0, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x016C, 0x25, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0104, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x010C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0108, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0114, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0150, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0118, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x011C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0120, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0124, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0128, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x012C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0144, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0160, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x01CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0164, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x01DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x035C, 0x43, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0368, 0xA0, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x036C, 0x25, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0304, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x030C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0308, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0314, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0350, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0318, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x031C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0320, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0324, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0328, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x032C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0344, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0360, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x03CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0364, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x03DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x055C, 0x43, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0568, 0xA0, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x056C, 0x25, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0504, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x050C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0508, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0514, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0550, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0518, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x051C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0520, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0524, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0528, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x052C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0544, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0560, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x05CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0564, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x05DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + +struct csiphy_reg_t + csiphy_3ph_v1_0_combo_mode_reg[MAX_LANES][MAX_SETTINGS_PER_LANE] = { + { + {0x015C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0168, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x016C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0104, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x010C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0108, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0114, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0150, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0118, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x011C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0120, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0124, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0128, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x012C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0144, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0160, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x01CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0164, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x01DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x035C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0368, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x036C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0304, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x030C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0308, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0314, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0350, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0318, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x031C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0320, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0324, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0328, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x032C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0344, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0360, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x03CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0364, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x03DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x055C, 0x63, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0568, 0xAC, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x056C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0504, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x050C, 0x12, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0508, 0x00, 0x00, CSIPHY_SETTLE_CNT_HIGHER_BYTE}, + {0x0514, 0x20, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0550, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0518, 0x3e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x051C, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0520, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0524, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0528, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x052C, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0544, 0x12, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0560, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x05CC, 0x41, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0564, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x05DC, 0x51, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + +#endif /* _CAM_CSIPHY_1_0_HWREG_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/Makefile new file mode 100644 index 000000000000..f7e1be6ff233 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/Makefile @@ -0,0 +1,8 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_eeprom_dev.o cam_eeprom_core.o cam_eeprom_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.c new file mode 100644 index 000000000000..bcde06d80306 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.c @@ -0,0 +1,999 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include + +#include "cam_eeprom_core.h" +#include "cam_eeprom_soc.h" +#include "cam_debug_util.h" + +#ifdef CONFIG_PROJECT_INFO +#include + +struct ois_vendor_match_tbl { + uint16_t ois_id; + char ois_name[32]; + char vendor_name[32]; +}; +static struct ois_vendor_match_tbl match_tbl[] = { + {0x24, "BU24218GWL", "Rohm"}, + {0x28, "BU24228GWL", "Rohm"}, +}; +#endif + +/** + * cam_eeprom_read_memory() - read map data into buffer + * @e_ctrl: eeprom control struct + * @block: block to be read + * + * This function iterates through blocks stored in block->map, reads each + * region and concatenate them into the pre-allocated block->mapdata + */ +static int cam_eeprom_read_memory(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_eeprom_memory_block_t *block) +{ + int rc = 0; + int j; + struct cam_sensor_i2c_reg_setting i2c_reg_settings; + struct cam_sensor_i2c_reg_array i2c_reg_array; + struct cam_eeprom_memory_map_t *emap = block->map; + struct cam_eeprom_soc_private *eb_info; + uint8_t *memptr = block->mapdata; + + int ret = 0; + uint32_t reg_data; + uint32_t ois_driver_id; + // 0xA0>>1 is the slave address of main camera Eeprom + uint16_t eeprom_slave_addr = 0xA0>>1; + // 0x0110 and 0x0111 are the registers of OIS driver ID + uint16_t ois_driver_id_reg_addr = 0x0110; + + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "e_ctrl is NULL"); + return -EINVAL; + } + + eb_info = (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + + for (j = 0; j < block->num_map; j++) { + CAM_DBG(CAM_EEPROM, "slave-addr = 0x%X", emap[j].saddr); + if (emap[j].saddr) { + eb_info->i2c_info.slave_addr = emap[j].saddr; + rc = cam_eeprom_update_i2c_info(e_ctrl, + &eb_info->i2c_info); + if (rc) { + CAM_ERR(CAM_EEPROM, + "failed: to update i2c info rc %d", + rc); + return rc; + } + } + + if (emap[j].page.valid_size) { + i2c_reg_settings.addr_type = emap[j].page.addr_type; + i2c_reg_settings.data_type = emap[j].page.data_type; + i2c_reg_settings.size = 1; + i2c_reg_array.reg_addr = emap[j].page.addr; + i2c_reg_array.reg_data = emap[j].page.data; + i2c_reg_array.delay = emap[j].page.delay; + i2c_reg_settings.reg_setting = &i2c_reg_array; + rc = camera_io_dev_write(&e_ctrl->io_master_info, + &i2c_reg_settings); + if (rc) { + CAM_ERR(CAM_EEPROM, "page write failed rc %d", + rc); + return rc; + } + } + + if (emap[j].pageen.valid_size) { + i2c_reg_settings.addr_type = emap[j].pageen.addr_type; + i2c_reg_settings.data_type = emap[j].pageen.data_type; + i2c_reg_settings.size = 1; + i2c_reg_array.reg_addr = emap[j].pageen.addr; + i2c_reg_array.reg_data = emap[j].pageen.data; + i2c_reg_array.delay = emap[j].pageen.delay; + i2c_reg_settings.reg_setting = &i2c_reg_array; + rc = camera_io_dev_write(&e_ctrl->io_master_info, + &i2c_reg_settings); + if (rc) { + CAM_ERR(CAM_EEPROM, "page enable failed rc %d", + rc); + return rc; + } + } + + if (emap[j].poll.valid_size) { + rc = camera_io_dev_poll(&e_ctrl->io_master_info, + emap[j].poll.addr, emap[j].poll.data, + 0, emap[j].poll.addr_type, + emap[j].poll.data_type, + emap[j].poll.delay); + if (rc) { + CAM_ERR(CAM_EEPROM, "poll failed rc %d", + rc); + return rc; + } + } + + if (emap[j].mem.valid_size) { + rc = camera_io_dev_read_seq(&e_ctrl->io_master_info, + emap[j].mem.addr, memptr, + emap[j].mem.addr_type, + emap[j].mem.valid_size); + if (rc) { + CAM_ERR(CAM_EEPROM, "read failed rc %d", + rc); + return rc; + } + memptr += emap[j].mem.valid_size; + } + + if (emap[j].pageen.valid_size) { + i2c_reg_settings.addr_type = emap[j].pageen.addr_type; + i2c_reg_settings.data_type = emap[j].pageen.data_type; + i2c_reg_settings.size = 1; + i2c_reg_array.reg_addr = emap[j].pageen.addr; + i2c_reg_array.reg_data = 0; + i2c_reg_array.delay = emap[j].pageen.delay; + i2c_reg_settings.reg_setting = &i2c_reg_array; + rc = camera_io_dev_write(&e_ctrl->io_master_info, + &i2c_reg_settings); + if (rc) { + CAM_ERR(CAM_EEPROM, + "page disable failed rc %d", + rc); + return rc; + } + } + } + + if (e_ctrl->io_master_info.cci_client->sid == eeprom_slave_addr) { + ret = camera_io_dev_read(&(e_ctrl->io_master_info), + ois_driver_id_reg_addr, ®_data, + CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_BYTE); + if (ret) { + CAM_ERR(CAM_EEPROM, "failed: to read 0x%x rc %d", + ois_driver_id_reg_addr, ret); + } else { + ois_driver_id = reg_data & 0xFF; + ret = camera_io_dev_read(&(e_ctrl->io_master_info), + ois_driver_id_reg_addr+1, ®_data, + CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_BYTE); + if (ret) { + CAM_ERR(CAM_EEPROM, "failed: to read 0x%x rc %d", + ois_driver_id_reg_addr+1, ret); + } else { + ois_driver_id = + ((reg_data & 0xFF) << 8) | ois_driver_id; +#ifdef CONFIG_PROJECT_INFO + if (ois_driver_id == match_tbl[0].ois_id) { + push_component_info(OIS, + match_tbl[0].ois_name, + match_tbl[0].vendor_name); + } else if (ois_driver_id == match_tbl[1].ois_id) { + push_component_info(OIS, match_tbl[1].ois_name, + match_tbl[1].vendor_name); + } +#endif + CAM_ERR(CAM_EEPROM, "OIS module 0x%x", ois_driver_id); + } + } + } + + return rc; +} + +/** + * cam_eeprom_power_up - Power up eeprom hardware + * @e_ctrl: ctrl structure + * @power_info: power up/down info for eeprom + * + * Returns success or failure + */ +static int cam_eeprom_power_up(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_sensor_power_ctrl_t *power_info) +{ + int32_t rc = 0; + struct cam_hw_soc_info *soc_info = + &e_ctrl->soc_info; + + /* Parse and fill vreg params for power up settings */ + rc = msm_camera_fill_vreg_params( + &e_ctrl->soc_info, + power_info->power_setting, + power_info->power_setting_size); + if (rc) { + CAM_ERR(CAM_EEPROM, + "failed to fill power up vreg params rc:%d", rc); + return rc; + } + + /* Parse and fill vreg params for power down settings*/ + rc = msm_camera_fill_vreg_params( + &e_ctrl->soc_info, + power_info->power_down_setting, + power_info->power_down_setting_size); + if (rc) { + CAM_ERR(CAM_EEPROM, + "failed to fill power down vreg params rc:%d", rc); + return rc; + } + + power_info->dev = soc_info->dev; + + rc = cam_sensor_core_power_up(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed in eeprom power up rc %d", rc); + return rc; + } + + if (e_ctrl->io_master_info.master_type == CCI_MASTER) { + rc = camera_io_init(&(e_ctrl->io_master_info)); + if (rc) { + CAM_ERR(CAM_EEPROM, "cci_init failed"); + return -EINVAL; + } + } + return rc; +} + +/** + * cam_eeprom_power_down - Power down eeprom hardware + * @e_ctrl: ctrl structure + * + * Returns success or failure + */ +static int cam_eeprom_power_down(struct cam_eeprom_ctrl_t *e_ctrl) +{ + struct cam_sensor_power_ctrl_t *power_info; + struct cam_hw_soc_info *soc_info; + struct cam_eeprom_soc_private *soc_private; + int rc = 0; + + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "failed: e_ctrl %pK", e_ctrl); + return -EINVAL; + } + + soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + soc_info = &e_ctrl->soc_info; + + if (!power_info) { + CAM_ERR(CAM_EEPROM, "failed: power_info %pK", power_info); + return -EINVAL; + } + rc = msm_camera_power_down(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "power down the core is failed:%d", rc); + return rc; + } + + if (e_ctrl->io_master_info.master_type == CCI_MASTER) + camera_io_release(&(e_ctrl->io_master_info)); + + return rc; +} + +/** + * cam_eeprom_match_id - match eeprom id + * @e_ctrl: ctrl structure + * + * Returns success or failure + */ +static int cam_eeprom_match_id(struct cam_eeprom_ctrl_t *e_ctrl) +{ + int rc; + struct camera_io_master *client = &e_ctrl->io_master_info; + uint8_t id[2]; + + rc = cam_spi_query_id(client, 0, CAMERA_SENSOR_I2C_TYPE_WORD, + &id[0], 2); + if (rc) + return rc; + CAM_DBG(CAM_EEPROM, "read 0x%x 0x%x, check 0x%x 0x%x", + id[0], id[1], client->spi_client->mfr_id0, + client->spi_client->device_id0); + if (id[0] != client->spi_client->mfr_id0 + || id[1] != client->spi_client->device_id0) + return -ENODEV; + return 0; +} + +/** + * cam_eeprom_parse_read_memory_map - Parse memory map + * @of_node: device node + * @e_ctrl: ctrl structure + * + * Returns success or failure + */ +int32_t cam_eeprom_parse_read_memory_map(struct device_node *of_node, + struct cam_eeprom_ctrl_t *e_ctrl) +{ + int32_t rc = 0; + struct cam_eeprom_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "failed: e_ctrl is NULL"); + return -EINVAL; + } + + soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + rc = cam_eeprom_parse_dt_memory_map(of_node, &e_ctrl->cal_data); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: eeprom dt parse rc %d", rc); + return rc; + } + rc = cam_eeprom_power_up(e_ctrl, power_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: eeprom power up rc %d", rc); + goto data_mem_free; + } + + e_ctrl->cam_eeprom_state = CAM_EEPROM_CONFIG; + if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE) { + rc = cam_eeprom_match_id(e_ctrl); + if (rc) { + CAM_DBG(CAM_EEPROM, "eeprom not matching %d", rc); + goto power_down; + } + } + rc = cam_eeprom_read_memory(e_ctrl, &e_ctrl->cal_data); + if (rc) { + CAM_ERR(CAM_EEPROM, "read_eeprom_memory failed"); + goto power_down; + } + + rc = cam_eeprom_power_down(e_ctrl); + if (rc) + CAM_ERR(CAM_EEPROM, "failed: eeprom power down rc %d", rc); + + e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE; + return rc; +power_down: + cam_eeprom_power_down(e_ctrl); +data_mem_free: + vfree(e_ctrl->cal_data.mapdata); + vfree(e_ctrl->cal_data.map); + e_ctrl->cal_data.num_data = 0; + e_ctrl->cal_data.num_map = 0; + e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE; + return rc; +} + +/** + * cam_eeprom_get_dev_handle - get device handle + * @e_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +static int32_t cam_eeprom_get_dev_handle(struct cam_eeprom_ctrl_t *e_ctrl, + void *arg) +{ + struct cam_sensor_acquire_dev eeprom_acq_dev; + struct cam_create_dev_hdl bridge_params; + struct cam_control *cmd = (struct cam_control *)arg; + + if (e_ctrl->bridge_intf.device_hdl != -1) { + CAM_ERR(CAM_EEPROM, "Device is already acquired"); + return -EFAULT; + } + if (copy_from_user(&eeprom_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(eeprom_acq_dev))) { + CAM_ERR(CAM_EEPROM, + "EEPROM:ACQUIRE_DEV: copy from user failed"); + return -EFAULT; + } + + bridge_params.session_hdl = eeprom_acq_dev.session_handle; + bridge_params.ops = &e_ctrl->bridge_intf.ops; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = e_ctrl; + + eeprom_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + e_ctrl->bridge_intf.device_hdl = eeprom_acq_dev.device_handle; + e_ctrl->bridge_intf.session_hdl = eeprom_acq_dev.session_handle; + + CAM_DBG(CAM_EEPROM, "Device Handle: %d", eeprom_acq_dev.device_handle); + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &eeprom_acq_dev, sizeof(struct cam_sensor_acquire_dev))) { + CAM_ERR(CAM_EEPROM, "EEPROM:ACQUIRE_DEV: copy to user failed"); + return -EFAULT; + } + return 0; +} + +/** + * cam_eeprom_update_slaveInfo - Update slave info + * @e_ctrl: ctrl structure + * @cmd_buf: command buffer + * + * Returns success or failure + */ +static int32_t cam_eeprom_update_slaveInfo(struct cam_eeprom_ctrl_t *e_ctrl, + void *cmd_buf) +{ + int32_t rc = 0; + struct cam_eeprom_soc_private *soc_private; + struct cam_cmd_i2c_info *cmd_i2c_info = NULL; + + soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + cmd_i2c_info = (struct cam_cmd_i2c_info *)cmd_buf; + soc_private->i2c_info.slave_addr = cmd_i2c_info->slave_addr; + soc_private->i2c_info.i2c_freq_mode = cmd_i2c_info->i2c_freq_mode; + + rc = cam_eeprom_update_i2c_info(e_ctrl, + &soc_private->i2c_info); + CAM_DBG(CAM_EEPROM, "Slave addr: 0x%x Freq Mode: %d", + soc_private->i2c_info.slave_addr, + soc_private->i2c_info.i2c_freq_mode); + + return rc; +} + +/** + * cam_eeprom_parse_memory_map - Parse memory map info + * @data: memory block data + * @cmd_buf: command buffer + * @cmd_length: command buffer length + * @num_map: memory map size + * @cmd_length_bytes: command length processed in this function + * + * Returns success or failure + */ +static int32_t cam_eeprom_parse_memory_map( + struct cam_eeprom_memory_block_t *data, + void *cmd_buf, int cmd_length, uint16_t *cmd_length_bytes, + int *num_map) +{ + int32_t rc = 0; + int32_t cnt = 0; + int32_t processed_size = 0; + uint8_t generic_op_code; + struct cam_eeprom_memory_map_t *map = data->map; + struct common_header *cmm_hdr = + (struct common_header *)cmd_buf; + uint16_t cmd_length_in_bytes = 0; + struct cam_cmd_i2c_random_wr *i2c_random_wr = NULL; + struct cam_cmd_i2c_continuous_rd *i2c_cont_rd = NULL; + struct cam_cmd_conditional_wait *i2c_poll = NULL; + struct cam_cmd_unconditional_wait *i2c_uncond_wait = NULL; + + generic_op_code = cmm_hdr->third_byte; + switch (cmm_hdr->cmd_type) { + case CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR: + i2c_random_wr = (struct cam_cmd_i2c_random_wr *)cmd_buf; + cmd_length_in_bytes = sizeof(struct cam_cmd_i2c_random_wr) + + ((i2c_random_wr->header.count - 1) * + sizeof(struct i2c_random_wr_payload)); + + for (cnt = 0; cnt < (i2c_random_wr->header.count); + cnt++) { + map[*num_map + cnt].page.addr = + i2c_random_wr->random_wr_payload[cnt].reg_addr; + map[*num_map + cnt].page.addr_type = + i2c_random_wr->header.addr_type; + map[*num_map + cnt].page.data = + i2c_random_wr->random_wr_payload[cnt].reg_data; + map[*num_map + cnt].page.data_type = + i2c_random_wr->header.data_type; + map[*num_map + cnt].page.valid_size = 1; + } + + *num_map += (i2c_random_wr->header.count - 1); + cmd_buf += cmd_length_in_bytes / sizeof(int32_t); + processed_size += + cmd_length_in_bytes; + break; + case CAMERA_SENSOR_CMD_TYPE_I2C_CONT_RD: + i2c_cont_rd = (struct cam_cmd_i2c_continuous_rd *)cmd_buf; + cmd_length_in_bytes = sizeof(struct cam_cmd_i2c_continuous_rd); + + map[*num_map].mem.addr = i2c_cont_rd->reg_addr; + map[*num_map].mem.addr_type = i2c_cont_rd->header.addr_type; + map[*num_map].mem.data_type = i2c_cont_rd->header.data_type; + map[*num_map].mem.valid_size = + i2c_cont_rd->header.count; + cmd_buf += cmd_length_in_bytes / sizeof(int32_t); + processed_size += + cmd_length_in_bytes; + data->num_data += map[*num_map].mem.valid_size; + break; + case CAMERA_SENSOR_CMD_TYPE_WAIT: + if (generic_op_code == + CAMERA_SENSOR_WAIT_OP_HW_UCND || + generic_op_code == + CAMERA_SENSOR_WAIT_OP_SW_UCND) { + i2c_uncond_wait = + (struct cam_cmd_unconditional_wait *)cmd_buf; + cmd_length_in_bytes = + sizeof(struct cam_cmd_unconditional_wait); + + if (*num_map < 1) { + CAM_ERR(CAM_EEPROM, + "invalid map number, num_map=%d", + *num_map); + return -EINVAL; + } + + /* + * Though delay is added all of them, but delay will + * be applicable to only one of them as only one of + * them will have valid_size set to >= 1. + */ + map[*num_map - 1].mem.delay = i2c_uncond_wait->delay; + map[*num_map - 1].page.delay = i2c_uncond_wait->delay; + map[*num_map - 1].pageen.delay = i2c_uncond_wait->delay; + } else if (generic_op_code == + CAMERA_SENSOR_WAIT_OP_COND) { + i2c_poll = (struct cam_cmd_conditional_wait *)cmd_buf; + cmd_length_in_bytes = + sizeof(struct cam_cmd_conditional_wait); + + map[*num_map].poll.addr = i2c_poll->reg_addr; + map[*num_map].poll.addr_type = i2c_poll->addr_type; + map[*num_map].poll.data = i2c_poll->reg_data; + map[*num_map].poll.data_type = i2c_poll->data_type; + map[*num_map].poll.delay = i2c_poll->timeout; + map[*num_map].poll.valid_size = 1; + } + cmd_buf += cmd_length_in_bytes / sizeof(int32_t); + processed_size += + cmd_length_in_bytes; + break; + default: + break; + } + + *cmd_length_bytes = processed_size; + return rc; +} + +/** + * cam_eeprom_init_pkt_parser - Parse eeprom packet + * @e_ctrl: ctrl structure + * @csl_packet: csl packet received + * + * Returns success or failure + */ +static int32_t cam_eeprom_init_pkt_parser(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_packet *csl_packet) +{ + int32_t rc = 0; + int i = 0; + struct cam_cmd_buf_desc *cmd_desc = NULL; + uint32_t *offset = NULL; + uint32_t *cmd_buf = NULL; + uintptr_t generic_pkt_addr; + size_t pkt_len = 0; + uint32_t total_cmd_buf_in_bytes = 0; + uint32_t processed_cmd_buf_in_bytes = 0; + struct common_header *cmm_hdr = NULL; + uint16_t cmd_length_in_bytes = 0; + struct cam_cmd_i2c_info *i2c_info = NULL; + int num_map = -1; + struct cam_eeprom_memory_map_t *map = NULL; + struct cam_eeprom_soc_private *soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + + e_ctrl->cal_data.map = vzalloc((MSM_EEPROM_MEMORY_MAP_MAX_SIZE * + MSM_EEPROM_MAX_MEM_MAP_CNT) * + (sizeof(struct cam_eeprom_memory_map_t))); + if (!e_ctrl->cal_data.map) { + rc = -ENOMEM; + CAM_ERR(CAM_EEPROM, "failed"); + return rc; + } + map = e_ctrl->cal_data.map; + + offset = (uint32_t *)&csl_packet->payload; + offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + + /* Loop through multiple command buffers */ + for (i = 0; i < csl_packet->num_cmd_buf; i++) { + total_cmd_buf_in_bytes = cmd_desc[i].length; + processed_cmd_buf_in_bytes = 0; + if (!total_cmd_buf_in_bytes) + continue; + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &generic_pkt_addr, &pkt_len); + if (rc) { + CAM_ERR(CAM_EEPROM, "Failed to get cpu buf"); + return rc; + } + cmd_buf = (uint32_t *)generic_pkt_addr; + if (!cmd_buf) { + CAM_ERR(CAM_EEPROM, "invalid cmd buf"); + return -EINVAL; + } + cmd_buf += cmd_desc[i].offset / sizeof(uint32_t); + /* Loop through multiple cmd formats in one cmd buffer */ + while (processed_cmd_buf_in_bytes < total_cmd_buf_in_bytes) { + cmm_hdr = (struct common_header *)cmd_buf; + switch (cmm_hdr->cmd_type) { + case CAMERA_SENSOR_CMD_TYPE_I2C_INFO: + i2c_info = (struct cam_cmd_i2c_info *)cmd_buf; + /* Configure the following map slave address */ + map[num_map + 1].saddr = i2c_info->slave_addr; + rc = cam_eeprom_update_slaveInfo(e_ctrl, + cmd_buf); + cmd_length_in_bytes = + sizeof(struct cam_cmd_i2c_info); + processed_cmd_buf_in_bytes += + cmd_length_in_bytes; + cmd_buf += cmd_length_in_bytes/ + sizeof(uint32_t); + break; + case CAMERA_SENSOR_CMD_TYPE_PWR_UP: + case CAMERA_SENSOR_CMD_TYPE_PWR_DOWN: + cmd_length_in_bytes = total_cmd_buf_in_bytes; + rc = cam_sensor_update_power_settings(cmd_buf, + cmd_length_in_bytes, power_info); + processed_cmd_buf_in_bytes += + cmd_length_in_bytes; + cmd_buf += cmd_length_in_bytes/ + sizeof(uint32_t); + if (rc) { + CAM_ERR(CAM_EEPROM, "Failed"); + return rc; + } + break; + case CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR: + case CAMERA_SENSOR_CMD_TYPE_I2C_CONT_RD: + case CAMERA_SENSOR_CMD_TYPE_WAIT: + num_map++; + rc = cam_eeprom_parse_memory_map( + &e_ctrl->cal_data, cmd_buf, + total_cmd_buf_in_bytes, + &cmd_length_in_bytes, &num_map); + processed_cmd_buf_in_bytes += + cmd_length_in_bytes; + cmd_buf += cmd_length_in_bytes/sizeof(uint32_t); + break; + default: + break; + } + } + e_ctrl->cal_data.num_map = num_map + 1; + } + return rc; +} + +/** + * cam_eeprom_get_cal_data - parse the userspace IO config and + * copy read data to share with userspace + * @e_ctrl: ctrl structure + * @csl_packet: csl packet received + * + * Returns success or failure + */ +static int32_t cam_eeprom_get_cal_data(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_packet *csl_packet) +{ + struct cam_buf_io_cfg *io_cfg; + uint32_t i = 0; + int rc = 0; + uintptr_t buf_addr; + size_t buf_size; + uint8_t *read_buffer; + + io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *) + &csl_packet->payload + + csl_packet->io_configs_offset); + + CAM_DBG(CAM_EEPROM, "number of IO configs: %d:", + csl_packet->num_io_configs); + + for (i = 0; i < csl_packet->num_io_configs; i++) { + CAM_DBG(CAM_EEPROM, "Direction: %d:", io_cfg->direction); + if (io_cfg->direction == CAM_BUF_OUTPUT) { + rc = cam_mem_get_cpu_buf(io_cfg->mem_handle[0], + &buf_addr, &buf_size); + CAM_DBG(CAM_EEPROM, "buf_addr : %pK, buf_size : %zu\n", + (void *)buf_addr, buf_size); + + read_buffer = (uint8_t *)buf_addr; + if (!read_buffer) { + CAM_ERR(CAM_EEPROM, + "invalid buffer to copy data"); + return -EINVAL; + } + read_buffer += io_cfg->offsets[0]; + + if (buf_size < e_ctrl->cal_data.num_data) { + CAM_ERR(CAM_EEPROM, + "failed to copy, Invalid size"); + return -EINVAL; + } + + CAM_DBG(CAM_EEPROM, "copy the data, len:%d", + e_ctrl->cal_data.num_data); + memcpy(read_buffer, e_ctrl->cal_data.mapdata, + e_ctrl->cal_data.num_data); + + } else { + CAM_ERR(CAM_EEPROM, "Invalid direction"); + rc = -EINVAL; + } + } + return rc; +} + +/** + * cam_eeprom_pkt_parse - Parse csl packet + * @e_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +static int32_t cam_eeprom_pkt_parse(struct cam_eeprom_ctrl_t *e_ctrl, void *arg) +{ + int32_t rc = 0; + struct cam_control *ioctl_ctrl = NULL; + struct cam_config_dev_cmd dev_config; + uintptr_t generic_pkt_addr; + size_t pkt_len; + struct cam_packet *csl_packet = NULL; + struct cam_eeprom_soc_private *soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + + ioctl_ctrl = (struct cam_control *)arg; + + if (copy_from_user(&dev_config, + u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(dev_config))) + return -EFAULT; + rc = cam_mem_get_cpu_buf(dev_config.packet_handle, + &generic_pkt_addr, &pkt_len); + if (rc) { + CAM_ERR(CAM_EEPROM, + "error in converting command Handle Error: %d", rc); + return rc; + } + + if (dev_config.offset > pkt_len) { + CAM_ERR(CAM_EEPROM, + "Offset is out of bound: off: %lld, %zu", + dev_config.offset, pkt_len); + return -EINVAL; + } + + csl_packet = (struct cam_packet *) + (generic_pkt_addr + dev_config.offset); + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_EEPROM_PACKET_OPCODE_INIT: + if (e_ctrl->userspace_probe == false) { + rc = cam_eeprom_parse_read_memory_map( + e_ctrl->soc_info.dev->of_node, e_ctrl); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "Failed: rc : %d", rc); + return rc; + } + rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet); + vfree(e_ctrl->cal_data.mapdata); + vfree(e_ctrl->cal_data.map); + e_ctrl->cal_data.num_data = 0; + e_ctrl->cal_data.num_map = 0; + CAM_DBG(CAM_EEPROM, + "Returning the data using kernel probe"); + break; + } + rc = cam_eeprom_init_pkt_parser(e_ctrl, csl_packet); + if (rc) { + CAM_ERR(CAM_EEPROM, + "Failed in parsing the pkt"); + return rc; + } + + if ((e_ctrl->cal_data.num_data) < PAGE_SIZE) { + e_ctrl->cal_data.mapdata = + kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL); + if (!e_ctrl->cal_data.mapdata) { + rc = -ENOMEM; + CAM_ERR(CAM_EEPROM, "failed"); + goto error; + } + } else { + e_ctrl->cal_data.mapdata = + vzalloc(e_ctrl->cal_data.num_data); + if (!e_ctrl->cal_data.mapdata) { + rc = -ENOMEM; + CAM_ERR(CAM_EEPROM, "failed"); + goto error; + } + } + + rc = cam_eeprom_power_up(e_ctrl, + &soc_private->power_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed rc %d", rc); + goto memdata_free; + } + + e_ctrl->cam_eeprom_state = CAM_EEPROM_CONFIG; + rc = cam_eeprom_read_memory(e_ctrl, &e_ctrl->cal_data); + if (rc) { + CAM_ERR(CAM_EEPROM, + "read_eeprom_memory failed"); + goto power_down; + } + + rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet); + rc = cam_eeprom_power_down(e_ctrl); + e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE; + vfree(e_ctrl->cal_data.mapdata); + vfree(e_ctrl->cal_data.map); + e_ctrl->cal_data.num_data = 0; + e_ctrl->cal_data.num_map = 0; + break; + default: + break; + } + return rc; +power_down: + cam_eeprom_power_down(e_ctrl); +memdata_free: + vfree(e_ctrl->cal_data.mapdata); +error: + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + vfree(e_ctrl->cal_data.map); + e_ctrl->cal_data.num_data = 0; + e_ctrl->cal_data.num_map = 0; + e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT; + return rc; +} + +void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl) +{ + int rc; + struct cam_eeprom_soc_private *soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + + if (e_ctrl->cam_eeprom_state == CAM_EEPROM_INIT) + return; + + if (e_ctrl->cam_eeprom_state == CAM_EEPROM_CONFIG) { + rc = cam_eeprom_power_down(e_ctrl); + if (rc < 0) + CAM_ERR(CAM_EEPROM, "EEPROM Power down failed"); + e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE; + } + + if (e_ctrl->cam_eeprom_state == CAM_EEPROM_ACQUIRE) { + rc = cam_destroy_device_hdl(e_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_EEPROM, "destroying the device hdl"); + + e_ctrl->bridge_intf.device_hdl = -1; + e_ctrl->bridge_intf.link_hdl = -1; + e_ctrl->bridge_intf.session_hdl = -1; + + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + } + + e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT; +} + +/** + * cam_eeprom_driver_cmd - Handle eeprom cmds + * @e_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg) +{ + int rc = 0; + struct cam_eeprom_query_cap_t eeprom_cap = {0}; + struct cam_control *cmd = (struct cam_control *)arg; + + if (!e_ctrl || !cmd) { + CAM_ERR(CAM_EEPROM, "e_ctrl is NULL"); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_EEPROM, "Invalid Handle Type"); + return -EINVAL; + } + + mutex_lock(&(e_ctrl->eeprom_mutex)); + switch (cmd->op_code) { + case CAM_QUERY_CAP: + eeprom_cap.slot_info = e_ctrl->soc_info.index; + if (e_ctrl->userspace_probe == false) + eeprom_cap.eeprom_kernel_probe = true; + else + eeprom_cap.eeprom_kernel_probe = false; + + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &eeprom_cap, + sizeof(struct cam_eeprom_query_cap_t))) { + CAM_ERR(CAM_EEPROM, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + CAM_DBG(CAM_EEPROM, "eeprom_cap: ID: %d", eeprom_cap.slot_info); + break; + case CAM_ACQUIRE_DEV: + rc = cam_eeprom_get_dev_handle(e_ctrl, arg); + if (rc) { + CAM_ERR(CAM_EEPROM, "Failed to acquire dev"); + goto release_mutex; + } + e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE; + break; + case CAM_RELEASE_DEV: + if (e_ctrl->cam_eeprom_state != CAM_EEPROM_ACQUIRE) { + rc = -EINVAL; + CAM_WARN(CAM_EEPROM, + "Not in right state to release : %d", + e_ctrl->cam_eeprom_state); + goto release_mutex; + } + + if (e_ctrl->bridge_intf.device_hdl == -1) { + CAM_ERR(CAM_EEPROM, + "Invalid Handles: link hdl: %d device hdl: %d", + e_ctrl->bridge_intf.device_hdl, + e_ctrl->bridge_intf.link_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_destroy_device_hdl(e_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_EEPROM, + "failed in destroying the device hdl"); + e_ctrl->bridge_intf.device_hdl = -1; + e_ctrl->bridge_intf.link_hdl = -1; + e_ctrl->bridge_intf.session_hdl = -1; + e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT; + break; + case CAM_CONFIG_DEV: + rc = cam_eeprom_pkt_parse(e_ctrl, arg); + if (rc) { + CAM_ERR(CAM_EEPROM, "Failed in eeprom pkt Parsing"); + goto release_mutex; + } + break; + default: + CAM_DBG(CAM_EEPROM, "invalid opcode"); + break; + } + +release_mutex: + mutex_unlock(&(e_ctrl->eeprom_mutex)); + + return rc; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.h new file mode 100644 index 000000000000..c9fccbb173e7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_core.h @@ -0,0 +1,28 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_EEPROM_CORE_H_ +#define _CAM_EEPROM_CORE_H_ + +#include "cam_eeprom_dev.h" + +int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg); +int32_t cam_eeprom_parse_read_memory_map(struct device_node *of_node, + struct cam_eeprom_ctrl_t *e_ctrl); +/** + * @e_ctrl: EEPROM ctrl structure + * + * This API handles the shutdown ioctl/close + */ +void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl); + +#endif +/* _CAM_EEPROM_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c new file mode 100644 index 000000000000..68c5eea0b175 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c @@ -0,0 +1,573 @@ +/* Copyright (c) 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 "cam_eeprom_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_eeprom_soc.h" +#include "cam_eeprom_core.h" +#include "cam_debug_util.h" + +static long cam_eeprom_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_eeprom_driver_cmd(e_ctrl, arg); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + + return rc; +} + +static int cam_eeprom_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_eeprom_ctrl_t *e_ctrl = + v4l2_get_subdevdata(sd); + + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "e_ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&(e_ctrl->eeprom_mutex)); + cam_eeprom_shutdown(e_ctrl); + mutex_unlock(&(e_ctrl->eeprom_mutex)); + + return 0; +} + +int32_t cam_eeprom_update_i2c_info(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_eeprom_i2c_info_t *i2c_info) +{ + struct cam_sensor_cci_client *cci_client = NULL; + + if (e_ctrl->io_master_info.master_type == CCI_MASTER) { + cci_client = e_ctrl->io_master_info.cci_client; + if (!cci_client) { + CAM_ERR(CAM_EEPROM, "failed: cci_client %pK", + cci_client); + return -EINVAL; + } + cci_client->cci_i2c_master = e_ctrl->cci_i2c_master; + cci_client->sid = (i2c_info->slave_addr) >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = i2c_info->i2c_freq_mode; + } + return 0; +} + +#ifdef CONFIG_COMPAT +static long cam_eeprom_init_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_EEPROM, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_eeprom_subdev_ioctl(sd, cmd, &cmd_data); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, + "Failed in eeprom suddev handling rc %d", + rc); + return rc; + } + break; + default: + CAM_ERR(CAM_EEPROM, "Invalid compat ioctl: %d", cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_EEPROM, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + return rc; +} +#endif + +static const struct v4l2_subdev_internal_ops cam_eeprom_internal_ops = { + .close = cam_eeprom_subdev_close, +}; + +static struct v4l2_subdev_core_ops cam_eeprom_subdev_core_ops = { + .ioctl = cam_eeprom_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_eeprom_init_subdev_do_ioctl, +#endif +}; + +static struct v4l2_subdev_ops cam_eeprom_subdev_ops = { + .core = &cam_eeprom_subdev_core_ops, +}; + +static int cam_eeprom_init_subdev(struct cam_eeprom_ctrl_t *e_ctrl) +{ + int rc = 0; + + e_ctrl->v4l2_dev_str.internal_ops = &cam_eeprom_internal_ops; + e_ctrl->v4l2_dev_str.ops = &cam_eeprom_subdev_ops; + strlcpy(e_ctrl->device_name, CAM_EEPROM_NAME, + sizeof(e_ctrl->device_name)); + e_ctrl->v4l2_dev_str.name = e_ctrl->device_name; + e_ctrl->v4l2_dev_str.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + e_ctrl->v4l2_dev_str.ent_function = CAM_EEPROM_DEVICE_TYPE; + e_ctrl->v4l2_dev_str.token = e_ctrl; + + rc = cam_register_subdev(&(e_ctrl->v4l2_dev_str)); + if (rc) + CAM_ERR(CAM_SENSOR, "Fail with cam_register_subdev"); + + return rc; +} + +static int cam_eeprom_i2c_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct cam_eeprom_ctrl_t *e_ctrl = NULL; + struct cam_eeprom_soc_private *soc_private = NULL; + struct cam_hw_soc_info *soc_info = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CAM_ERR(CAM_EEPROM, "i2c_check_functionality failed"); + goto probe_failure; + } + + e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL); + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "kzalloc failed"); + rc = -ENOMEM; + goto probe_failure; + } + + soc_private = kzalloc(sizeof(*soc_private), GFP_KERNEL); + if (!soc_private) + goto ectrl_free; + + e_ctrl->soc_info.soc_private = soc_private; + + i2c_set_clientdata(client, e_ctrl); + + mutex_init(&(e_ctrl->eeprom_mutex)); + + soc_info = &e_ctrl->soc_info; + soc_info->dev = &client->dev; + soc_info->dev_name = client->name; + e_ctrl->io_master_info.master_type = I2C_MASTER; + e_ctrl->io_master_info.client = client; + e_ctrl->eeprom_device_type = MSM_CAMERA_I2C_DEVICE; + + rc = cam_eeprom_parse_dt(e_ctrl); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: soc init rc %d", rc); + goto free_soc; + } + + rc = cam_eeprom_update_i2c_info(e_ctrl, &soc_private->i2c_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: to update i2c info rc %d", rc); + goto free_soc; + } + + rc = cam_eeprom_init_subdev(e_ctrl); + if (rc) + goto free_soc; + + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + e_ctrl->userspace_probe = false; + + if (soc_private->i2c_info.slave_addr != 0) + e_ctrl->io_master_info.client->addr = + soc_private->i2c_info.slave_addr; + + e_ctrl->bridge_intf.device_hdl = -1; + e_ctrl->bridge_intf.ops.get_dev_info = NULL; + e_ctrl->bridge_intf.ops.link_setup = NULL; + e_ctrl->bridge_intf.ops.apply_req = NULL; + v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl); + e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT; + + return rc; +free_soc: + kfree(soc_private); +ectrl_free: + kfree(e_ctrl); +probe_failure: + return rc; +} + +static int cam_eeprom_i2c_driver_remove(struct i2c_client *client) +{ + int i; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct cam_eeprom_ctrl_t *e_ctrl; + struct cam_eeprom_soc_private *soc_private; + struct cam_hw_soc_info *soc_info; + + if (!sd) { + CAM_ERR(CAM_EEPROM, "Subdevice is NULL"); + return -EINVAL; + } + + e_ctrl = (struct cam_eeprom_ctrl_t *)v4l2_get_subdevdata(sd); + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "eeprom device is NULL"); + return -EINVAL; + } + + soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + if (!soc_private) { + CAM_ERR(CAM_EEPROM, "soc_info.soc_private is NULL"); + return -EINVAL; + } + + soc_info = &e_ctrl->soc_info; + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + if (soc_private) + kfree(soc_private); + + kfree(e_ctrl); + + return 0; +} + +static int cam_eeprom_spi_setup(struct spi_device *spi) +{ + struct cam_eeprom_ctrl_t *e_ctrl = NULL; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_sensor_spi_client *spi_client; + struct cam_eeprom_soc_private *eb_info; + struct cam_sensor_power_ctrl_t *power_info = NULL; + int rc = 0; + + e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL); + if (!e_ctrl) + return -ENOMEM; + + soc_info = &e_ctrl->soc_info; + soc_info->dev = &spi->dev; + soc_info->dev_name = spi->modalias; + + e_ctrl->v4l2_dev_str.ops = &cam_eeprom_subdev_ops; + e_ctrl->userspace_probe = false; + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + + spi_client = kzalloc(sizeof(*spi_client), GFP_KERNEL); + if (!spi_client) { + kfree(e_ctrl); + return -ENOMEM; + } + + eb_info = kzalloc(sizeof(*eb_info), GFP_KERNEL); + if (!eb_info) + goto spi_free; + e_ctrl->soc_info.soc_private = eb_info; + + e_ctrl->eeprom_device_type = MSM_CAMERA_SPI_DEVICE; + e_ctrl->io_master_info.spi_client = spi_client; + e_ctrl->io_master_info.master_type = SPI_MASTER; + spi_client->spi_master = spi; + + power_info = &eb_info->power_info; + power_info->dev = &spi->dev; + + /* set spi instruction info */ + spi_client->retry_delay = 1; + spi_client->retries = 0; + + /* Initialize mutex */ + mutex_init(&(e_ctrl->eeprom_mutex)); + + rc = cam_eeprom_parse_dt(e_ctrl); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: spi soc init rc %d", rc); + goto board_free; + } + + rc = cam_eeprom_spi_parse_of(spi_client); + if (rc) { + CAM_ERR(CAM_EEPROM, "Device tree parsing error"); + goto board_free; + } + + rc = cam_eeprom_init_subdev(e_ctrl); + if (rc) + goto board_free; + + e_ctrl->bridge_intf.device_hdl = -1; + e_ctrl->bridge_intf.ops.get_dev_info = NULL; + e_ctrl->bridge_intf.ops.link_setup = NULL; + e_ctrl->bridge_intf.ops.apply_req = NULL; + + v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl); + return rc; + +board_free: + kfree(e_ctrl->soc_info.soc_private); +spi_free: + kfree(spi_client); + kfree(e_ctrl); + return rc; +} + +static int cam_eeprom_spi_driver_probe(struct spi_device *spi) +{ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + spi_setup(spi); + + CAM_DBG(CAM_EEPROM, "irq[%d] cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x]", + spi->irq, spi->chip_select, (spi->mode & SPI_CPHA) ? 1 : 0, + (spi->mode & SPI_CPOL) ? 1 : 0, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); + CAM_DBG(CAM_EEPROM, "max_speed[%u]", spi->max_speed_hz); + + return cam_eeprom_spi_setup(spi); +} + +static int cam_eeprom_spi_driver_remove(struct spi_device *sdev) +{ + int i; + struct v4l2_subdev *sd = spi_get_drvdata(sdev); + struct cam_eeprom_ctrl_t *e_ctrl; + struct cam_eeprom_soc_private *soc_private; + struct cam_hw_soc_info *soc_info; + + if (!sd) { + CAM_ERR(CAM_EEPROM, "Subdevice is NULL"); + return -EINVAL; + } + + e_ctrl = (struct cam_eeprom_ctrl_t *)v4l2_get_subdevdata(sd); + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "eeprom device is NULL"); + return -EINVAL; + } + + soc_info = &e_ctrl->soc_info; + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + kfree(e_ctrl->io_master_info.spi_client); + soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + if (soc_private) { + kfree(soc_private->power_info.gpio_num_info); + kfree(soc_private); + } + kfree(e_ctrl); + + return 0; +} + +static int32_t cam_eeprom_platform_driver_probe( + struct platform_device *pdev) +{ + int32_t rc = 0; + struct cam_eeprom_ctrl_t *e_ctrl = NULL; + struct cam_eeprom_soc_private *soc_private = NULL; + + e_ctrl = kzalloc(sizeof(struct cam_eeprom_ctrl_t), GFP_KERNEL); + if (!e_ctrl) + return -ENOMEM; + + e_ctrl->soc_info.pdev = pdev; + e_ctrl->soc_info.dev = &pdev->dev; + e_ctrl->soc_info.dev_name = pdev->name; + e_ctrl->eeprom_device_type = MSM_CAMERA_PLATFORM_DEVICE; + e_ctrl->cal_data.mapdata = NULL; + e_ctrl->cal_data.map = NULL; + e_ctrl->userspace_probe = false; + + e_ctrl->io_master_info.master_type = CCI_MASTER; + e_ctrl->io_master_info.cci_client = kzalloc( + sizeof(struct cam_sensor_cci_client), GFP_KERNEL); + if (!e_ctrl->io_master_info.cci_client) { + rc = -ENOMEM; + goto free_e_ctrl; + } + + soc_private = kzalloc(sizeof(struct cam_eeprom_soc_private), + GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto free_cci_client; + } + e_ctrl->soc_info.soc_private = soc_private; + soc_private->power_info.dev = &pdev->dev; + + /* Initialize mutex */ + mutex_init(&(e_ctrl->eeprom_mutex)); + rc = cam_eeprom_parse_dt(e_ctrl); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: soc init rc %d", rc); + goto free_soc; + } + rc = cam_eeprom_update_i2c_info(e_ctrl, &soc_private->i2c_info); + if (rc) { + CAM_ERR(CAM_EEPROM, "failed: to update i2c info rc %d", rc); + goto free_soc; + } + + rc = cam_eeprom_init_subdev(e_ctrl); + if (rc) + goto free_soc; + + e_ctrl->bridge_intf.device_hdl = -1; + e_ctrl->bridge_intf.ops.get_dev_info = NULL; + e_ctrl->bridge_intf.ops.link_setup = NULL; + e_ctrl->bridge_intf.ops.apply_req = NULL; + + platform_set_drvdata(pdev, e_ctrl); + v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl); + + e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT; + + return rc; +free_soc: + kfree(soc_private); +free_cci_client: + kfree(e_ctrl->io_master_info.cci_client); +free_e_ctrl: + kfree(e_ctrl); + return rc; +} + +static int cam_eeprom_platform_driver_remove(struct platform_device *pdev) +{ + int i; + struct cam_eeprom_ctrl_t *e_ctrl; + struct cam_hw_soc_info *soc_info; + + e_ctrl = platform_get_drvdata(pdev); + if (!e_ctrl) { + CAM_ERR(CAM_EEPROM, "eeprom device is NULL"); + return -EINVAL; + } + + soc_info = &e_ctrl->soc_info; + + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + kfree(soc_info->soc_private); + kfree(e_ctrl->io_master_info.cci_client); + kfree(e_ctrl); + return 0; +} + +static const struct of_device_id cam_eeprom_dt_match[] = { + { .compatible = "qcom,eeprom" }, + { } +}; + + +MODULE_DEVICE_TABLE(of, cam_eeprom_dt_match); + +static struct platform_driver cam_eeprom_platform_driver = { + .driver = { + .name = "qcom,eeprom", + .owner = THIS_MODULE, + .of_match_table = cam_eeprom_dt_match, + }, + .probe = cam_eeprom_platform_driver_probe, + .remove = cam_eeprom_platform_driver_remove, +}; + +static const struct i2c_device_id cam_eeprom_i2c_id[] = { + { "msm_eeprom", (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver cam_eeprom_i2c_driver = { + .id_table = cam_eeprom_i2c_id, + .probe = cam_eeprom_i2c_driver_probe, + .remove = cam_eeprom_i2c_driver_remove, + .driver = { + .name = "msm_eeprom", + }, +}; + +static struct spi_driver cam_eeprom_spi_driver = { + .driver = { + .name = "qcom_eeprom", + .owner = THIS_MODULE, + .of_match_table = cam_eeprom_dt_match, + }, + .probe = cam_eeprom_spi_driver_probe, + .remove = cam_eeprom_spi_driver_remove, +}; +static int __init cam_eeprom_driver_init(void) +{ + int rc = 0; + + rc = platform_driver_register(&cam_eeprom_platform_driver); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "platform_driver_register failed rc = %d", + rc); + return rc; + } + + rc = spi_register_driver(&cam_eeprom_spi_driver); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "spi_register_driver failed rc = %d", rc); + return rc; + } + + rc = i2c_add_driver(&cam_eeprom_i2c_driver); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "i2c_add_driver failed rc = %d", rc); + return rc; + } + + return rc; +} + +static void __exit cam_eeprom_driver_exit(void) +{ + platform_driver_unregister(&cam_eeprom_platform_driver); + spi_unregister_driver(&cam_eeprom_spi_driver); + i2c_del_driver(&cam_eeprom_i2c_driver); +} + +module_init(cam_eeprom_driver_init); +module_exit(cam_eeprom_driver_exit); +MODULE_DESCRIPTION("CAM EEPROM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h new file mode 100644 index 000000000000..4a2190da7c70 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h @@ -0,0 +1,189 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_EEPROM_DEV_H_ +#define _CAM_EEPROM_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_soc_util.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +#define PROPERTY_MAXSIZE 32 + +#define MSM_EEPROM_MEMORY_MAP_MAX_SIZE 80 +#define MSM_EEPROM_MAX_MEM_MAP_CNT 8 +#define MSM_EEPROM_MEM_MAP_PROPERTIES_CNT 8 + +enum cam_eeprom_state { + CAM_EEPROM_INIT, + CAM_EEPROM_ACQUIRE, + CAM_EEPROM_CONFIG, +}; + +/** + * struct cam_eeprom_map_t - eeprom map + * @data_type : Data type + * @addr_type : Address type + * @addr : Address + * @data : data + * @delay : Delay + * + */ +struct cam_eeprom_map_t { + uint32_t valid_size; + uint32_t addr; + uint32_t addr_type; + uint32_t data; + uint32_t data_type; + uint32_t delay; +}; + +/** + * struct cam_eeprom_memory_map_t - eeprom memory map types + * @page : page memory + * @pageen : pageen memory + * @poll : poll memory + * @mem : mem + * @saddr : slave addr + * + */ +struct cam_eeprom_memory_map_t { + struct cam_eeprom_map_t page; + struct cam_eeprom_map_t pageen; + struct cam_eeprom_map_t poll; + struct cam_eeprom_map_t mem; + uint32_t saddr; +}; + +/** + * struct cam_eeprom_memory_block_t - eeprom mem block info + * @map : eeprom memory map + * @num_map : number of map blocks + * @mapdata : map data + * @cmd_type : size of total mapdata + * + */ +struct cam_eeprom_memory_block_t { + struct cam_eeprom_memory_map_t *map; + uint32_t num_map; + uint8_t *mapdata; + uint32_t num_data; +}; + +/** + * struct cam_eeprom_cmm_t - camera multimodule + * @cmm_support : cmm support flag + * @cmm_compression : cmm compression flag + * @cmm_offset : cmm data start offset + * @cmm_size : cmm data size + * + */ +struct cam_eeprom_cmm_t { + uint32_t cmm_support; + uint32_t cmm_compression; + uint32_t cmm_offset; + uint32_t cmm_size; +}; + +/** + * struct cam_eeprom_i2c_info_t - I2C info + * @slave_addr : slave address + * @i2c_freq_mode : i2c frequency mode + * + */ +struct cam_eeprom_i2c_info_t { + uint16_t slave_addr; + uint8_t i2c_freq_mode; +}; + +/** + * struct cam_eeprom_soc_private - eeprom soc private data structure + * @eeprom_name : eeprom name + * @i2c_info : i2c info structure + * @power_info : eeprom power info + * @cmm_data : cmm data + * + */ +struct cam_eeprom_soc_private { + const char *eeprom_name; + struct cam_eeprom_i2c_info_t i2c_info; + struct cam_sensor_power_ctrl_t power_info; + struct cam_eeprom_cmm_t cmm_data; +}; + +/** + * struct cam_eeprom_intf_params - bridge interface params + * @device_hdl : Device Handle + * @session_hdl : Session Handle + * @ops : KMD operations + * @crm_cb : Callback API pointers + */ +struct cam_eeprom_intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_cmd_conditional_wait - Conditional wait command + * @pdev : platform device + * @spi : spi device + * @eeprom_mutex : eeprom mutex + * @soc_info : eeprom soc related info + * @io_master_info : Information about the communication master + * @gpio_num_info : gpio info + * @cci_i2c_master : I2C structure + * @v4l2_dev_str : V4L2 device structure + * @bridge_intf : bridge interface params + * @cam_eeprom_state: eeprom_device_state + * @userspace_probe : flag indicates userspace or kernel probe + * @cal_data : Calibration data + * @device_name : Device name + * + */ +struct cam_eeprom_ctrl_t { + struct platform_device *pdev; + struct spi_device *spi; + struct mutex eeprom_mutex; + struct cam_hw_soc_info soc_info; + struct camera_io_master io_master_info; + struct msm_camera_gpio_num_info *gpio_num_info; + enum cci_i2c_master_t cci_i2c_master; + struct cam_subdev v4l2_dev_str; + struct cam_eeprom_intf_params bridge_intf; + enum msm_camera_device_type_t eeprom_device_type; + enum cam_eeprom_state cam_eeprom_state; + bool userspace_probe; + struct cam_eeprom_memory_block_t cal_data; + char device_name[20]; +}; + +int32_t cam_eeprom_update_i2c_info(struct cam_eeprom_ctrl_t *e_ctrl, + struct cam_eeprom_i2c_info_t *i2c_info); + +#endif /*_CAM_EEPROM_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c new file mode 100644 index 000000000000..d93a7ae41304 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c @@ -0,0 +1,374 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_eeprom_soc.h" +#include "cam_debug_util.h" + +#define cam_eeprom_spi_parse_cmd(spi_dev, name, out) \ + { \ + spi_dev->cmd_tbl.name.opcode = out[0]; \ + spi_dev->cmd_tbl.name.addr_len = out[1]; \ + spi_dev->cmd_tbl.name.dummy_len = out[2]; \ + spi_dev->cmd_tbl.name.delay_intv = out[3]; \ + spi_dev->cmd_tbl.name.delay_count = out[4]; \ + } + +int cam_eeprom_spi_parse_of(struct cam_sensor_spi_client *spi_dev) +{ + int rc = -EFAULT; + uint32_t tmp[5]; + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-read", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, read, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get read data"); + return -EFAULT; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-readseq", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, read_seq, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get readseq data"); + return -EFAULT; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-queryid", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, query_id, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get queryid data"); + return -EFAULT; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-pprog", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, page_program, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get page program data"); + return -EFAULT; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-wenable", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, write_enable, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get write enable data"); + return rc; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-readst", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, read_status, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get readdst data"); + return rc; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "spiop-erase", tmp, 5); + if (!rc) { + cam_eeprom_spi_parse_cmd(spi_dev, erase, tmp); + } else { + CAM_ERR(CAM_EEPROM, "Failed to get erase data"); + return rc; + } + + rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node, + "eeprom-id", tmp, 2); + if (rc) { + CAM_ERR(CAM_EEPROM, "Failed to get eeprom id"); + return rc; + } + + spi_dev->mfr_id0 = tmp[0]; + spi_dev->device_id0 = tmp[1]; + + return 0; +} + +/* + * cam_eeprom_parse_memory_map() - parse memory map in device node + * @of: device node + * @data: memory block for output + * + * This functions parses @of to fill @data. It allocates map itself, parses + * the @of node, calculate total data length, and allocates required buffer. + * It only fills the map, but does not perform actual reading. + */ +int cam_eeprom_parse_dt_memory_map(struct device_node *node, + struct cam_eeprom_memory_block_t *data) +{ + int i, rc = 0; + char property[PROPERTY_MAXSIZE]; + uint32_t count = MSM_EEPROM_MEM_MAP_PROPERTIES_CNT; + struct cam_eeprom_memory_map_t *map; + + snprintf(property, PROPERTY_MAXSIZE, "num-blocks"); + rc = of_property_read_u32(node, property, &data->num_map); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed: num-blocks not available rc %d", + rc); + return rc; + } + + map = vzalloc((sizeof(*map) * data->num_map)); + if (!map) { + rc = -ENOMEM; + return rc; + } + data->map = map; + + for (i = 0; i < data->num_map; i++) { + snprintf(property, PROPERTY_MAXSIZE, "page%d", i); + rc = of_property_read_u32_array(node, property, + (uint32_t *) &map[i].page, count); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed: page not available rc %d", + rc); + goto ERROR; + } + + snprintf(property, PROPERTY_MAXSIZE, "pageen%d", i); + rc = of_property_read_u32_array(node, property, + (uint32_t *) &map[i].pageen, count); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "pageen not needed"); + + snprintf(property, PROPERTY_MAXSIZE, "saddr%d", i); + rc = of_property_read_u32_array(node, property, + (uint32_t *) &map[i].saddr, 1); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "saddr not needed - block %d", i); + + snprintf(property, PROPERTY_MAXSIZE, "poll%d", i); + rc = of_property_read_u32_array(node, property, + (uint32_t *) &map[i].poll, count); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed: poll not available rc %d", + rc); + goto ERROR; + } + + snprintf(property, PROPERTY_MAXSIZE, "mem%d", i); + rc = of_property_read_u32_array(node, property, + (uint32_t *) &map[i].mem, count); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed: mem not available rc %d", + rc); + goto ERROR; + } + data->num_data += map[i].mem.valid_size; + } + + data->mapdata = vzalloc(data->num_data); + if (!data->mapdata) { + rc = -ENOMEM; + goto ERROR; + } + return rc; + +ERROR: + vfree(data->map); + memset(data, 0, sizeof(*data)); + return rc; +} + +/** + * @e_ctrl: ctrl structure + * + * Parses eeprom dt + */ +static int cam_eeprom_get_dt_data(struct cam_eeprom_ctrl_t *e_ctrl) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info = &e_ctrl->soc_info; + struct cam_eeprom_soc_private *soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + struct device_node *of_node = NULL; + + of_node = soc_info->dev->of_node; + + if (e_ctrl->userspace_probe == false) { + rc = cam_get_dt_power_setting_data(of_node, + soc_info, power_info); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed in getting power settings"); + return rc; + } + } + + if (!soc_info->gpio_data) { + CAM_INFO(CAM_EEPROM, "No GPIO found"); + return 0; + } + + if (!soc_info->gpio_data->cam_gpio_common_tbl_size) { + CAM_INFO(CAM_EEPROM, "No GPIO found"); + return -EINVAL; + } + + rc = cam_sensor_util_init_gpio_pin_tbl(soc_info, + &power_info->gpio_num_info); + if ((rc < 0) || (!power_info->gpio_num_info)) { + CAM_ERR(CAM_EEPROM, "No/Error EEPROM GPIOs"); + return -EINVAL; + } + + return rc; +} + +/** + * @eb_info: eeprom private data structure + * @of_node: eeprom device node + * + * This function parses the eeprom dt to get the MM data + */ +static int cam_eeprom_cmm_dts(struct cam_eeprom_soc_private *eb_info, + struct device_node *of_node) +{ + int rc = 0; + struct cam_eeprom_cmm_t *cmm_data = &eb_info->cmm_data; + + cmm_data->cmm_support = + of_property_read_bool(of_node, "cmm-data-support"); + if (!cmm_data->cmm_support) { + CAM_DBG(CAM_EEPROM, "No cmm support"); + return 0; + } + + cmm_data->cmm_compression = + of_property_read_bool(of_node, "cmm-data-compressed"); + + rc = of_property_read_u32(of_node, "cmm-data-offset", + &cmm_data->cmm_offset); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "No MM offset data rc %d", rc); + + rc = of_property_read_u32(of_node, "cmm-data-size", + &cmm_data->cmm_size); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "No MM size data rc %d", rc); + + CAM_DBG(CAM_EEPROM, "cmm_compr %d, cmm_offset %d, cmm_size %d", + cmm_data->cmm_compression, cmm_data->cmm_offset, + cmm_data->cmm_size); + return 0; +} + +/** + * @e_ctrl: ctrl structure + * + * This function is called from cam_eeprom_platform/i2c/spi_driver_probe + * it parses the eeprom dt node and decides for userspace or kernel probe. + */ +int cam_eeprom_parse_dt(struct cam_eeprom_ctrl_t *e_ctrl) +{ + int i, rc = 0; + struct cam_hw_soc_info *soc_info = &e_ctrl->soc_info; + struct device_node *of_node = NULL; + struct cam_eeprom_soc_private *soc_private = + (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private; + uint32_t temp; + + if (!soc_info->dev) { + CAM_ERR(CAM_EEPROM, "Dev is NULL"); + return -EINVAL; + } + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "Failed to read DT properties rc : %d", rc); + return rc; + } + + of_node = soc_info->dev->of_node; + + rc = of_property_read_string(of_node, "eeprom-name", + &soc_private->eeprom_name); + if (rc < 0) { + CAM_DBG(CAM_EEPROM, "kernel probe is not enabled"); + e_ctrl->userspace_probe = true; + } + + if (e_ctrl->io_master_info.master_type == CCI_MASTER) { + rc = of_property_read_u32(of_node, "cci-master", + &e_ctrl->cci_i2c_master); + if (rc < 0 || (e_ctrl->cci_i2c_master >= MASTER_MAX)) { + CAM_DBG(CAM_EEPROM, "failed rc %d", rc); + rc = -EFAULT; + return rc; + } + } + + if (e_ctrl->io_master_info.master_type == SPI_MASTER) { + rc = cam_eeprom_cmm_dts(soc_private, soc_info->dev->of_node); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "MM data not available rc %d", rc); + } + + rc = cam_eeprom_get_dt_data(e_ctrl); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "failed: eeprom get dt data rc %d", rc); + + if ((e_ctrl->userspace_probe == false) && + (e_ctrl->io_master_info.master_type != SPI_MASTER)) { + rc = of_property_read_u32(of_node, "slave-addr", &temp); + if (rc < 0) + CAM_DBG(CAM_EEPROM, "failed: no slave-addr rc %d", rc); + + soc_private->i2c_info.slave_addr = temp; + + rc = of_property_read_u32(of_node, "i2c-freq-mode", &temp); + soc_private->i2c_info.i2c_freq_mode = temp; + if (rc < 0) { + CAM_ERR(CAM_EEPROM, + "i2c-freq-mode read fail %d", rc); + soc_private->i2c_info.i2c_freq_mode = 0; + } + if (soc_private->i2c_info.i2c_freq_mode >= I2C_MAX_MODES) { + CAM_ERR(CAM_EEPROM, "invalid i2c_freq_mode = %d", + soc_private->i2c_info.i2c_freq_mode); + soc_private->i2c_info.i2c_freq_mode = 0; + } + CAM_DBG(CAM_EEPROM, "slave-addr = 0x%X", + soc_private->i2c_info.slave_addr); + } + + for (i = 0; i < soc_info->num_clk; i++) { + soc_info->clk[i] = devm_clk_get(soc_info->dev, + soc_info->clk_name[i]); + if (!soc_info->clk[i]) { + CAM_ERR(CAM_EEPROM, "get failed for %s", + soc_info->clk_name[i]); + rc = -ENOENT; + return rc; + } + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h new file mode 100644 index 000000000000..d3115499173c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h @@ -0,0 +1,23 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_EEPROM_SOC_H_ +#define _CAM_EEPROM_SOC_H_ + +#include "cam_eeprom_dev.h" + +int cam_eeprom_spi_parse_of(struct cam_sensor_spi_client *client); + +int cam_eeprom_parse_dt_memory_map(struct device_node *of, + struct cam_eeprom_memory_block_t *data); + +int cam_eeprom_parse_dt(struct cam_eeprom_ctrl_t *e_ctrl); +#endif/* _CAM_EEPROM_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/Makefile new file mode 100644 index 000000000000..aa8032fdca5e --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/Makefile @@ -0,0 +1,10 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash_dev.o cam_flash_core.o cam_flash_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.c new file mode 100644 index 000000000000..be6f6ff78284 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.c @@ -0,0 +1,983 @@ +/* Copyright (c) 2017-2018, 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 + +#include "cam_sensor_cmn_header.h" +#include "cam_flash_core.h" +#include "cam_res_mgr_api.h" + +int cam_flash_prepare(struct cam_flash_ctrl *flash_ctrl, + bool regulator_enable) +{ + int rc = 0; + + if (!(flash_ctrl->switch_trigger)) { + CAM_ERR(CAM_FLASH, "Invalid argument"); + return -EINVAL; + } + + if (regulator_enable && + (flash_ctrl->is_regulator_enabled == false)) { + rc = qpnp_flash_led_prepare(flash_ctrl->switch_trigger, + ENABLE_REGULATOR, NULL); + if (rc) { + CAM_ERR(CAM_FLASH, "regulator enable failed rc = %d", + rc); + return rc; + } + flash_ctrl->is_regulator_enabled = true; + } else if ((!regulator_enable) && + (flash_ctrl->is_regulator_enabled == true)) { + rc = qpnp_flash_led_prepare(flash_ctrl->switch_trigger, + DISABLE_REGULATOR, NULL); + if (rc) { + CAM_ERR(CAM_FLASH, "regulator disable failed rc = %d", + rc); + return rc; + } + flash_ctrl->is_regulator_enabled = false; + } else { + CAM_ERR(CAM_FLASH, "Wrong Flash State : %d", + flash_ctrl->flash_state); + rc = -EINVAL; + } + + return rc; +} + +static int cam_flash_flush_nrt(struct cam_flash_ctrl *fctrl) +{ + int j = 0; + struct cam_flash_frame_setting *nrt_settings; + + if (!fctrl) + return -EINVAL; + + nrt_settings = &fctrl->nrt_info; + + if (nrt_settings->cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_INFO) { + fctrl->flash_init_setting.cmn_attr.is_settings_valid = false; + } else if ((nrt_settings->cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET) || + (nrt_settings->cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_RER) || + (nrt_settings->cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_FIRE)) { + fctrl->nrt_info.cmn_attr.is_settings_valid = false; + fctrl->nrt_info.cmn_attr.count = 0; + fctrl->nrt_info.num_iterations = 0; + fctrl->nrt_info.led_on_delay_ms = 0; + fctrl->nrt_info.led_off_delay_ms = 0; + for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++) + fctrl->nrt_info.led_current_ma[j] = 0; + } + + return 0; +} + +int cam_flash_flush_request(struct cam_req_mgr_flush_request *flush) +{ + int rc = 0; + int i = 0, j = 0; + struct cam_flash_ctrl *fctrl = NULL; + int frame_offset = 0; + + fctrl = (struct cam_flash_ctrl *) cam_get_device_priv(flush->dev_hdl); + if (!fctrl) { + CAM_ERR(CAM_FLASH, "Device data is NULL"); + return -EINVAL; + } + + if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_ALL) { + /* flush all requests*/ + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + fctrl->per_frame[i].cmn_attr.request_id = 0; + fctrl->per_frame[i].cmn_attr.is_settings_valid = false; + fctrl->per_frame[i].cmn_attr.count = 0; + for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++) + fctrl->per_frame[i].led_current_ma[j] = 0; + } + + rc = cam_flash_flush_nrt(fctrl); + if (rc) + CAM_ERR(CAM_FLASH, "NonRealTime flush error"); + } else if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) { + /* flush request with req_id*/ + frame_offset = flush->req_id % MAX_PER_FRAME_ARRAY; + fctrl->per_frame[frame_offset].cmn_attr.request_id = 0; + fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid = + false; + fctrl->per_frame[frame_offset].cmn_attr.count = 0; + for (i = 0; i < CAM_FLASH_MAX_LED_TRIGGERS; i++) + fctrl->per_frame[frame_offset].led_current_ma[i] = 0; + } + return rc; +} + +static int cam_flash_ops(struct cam_flash_ctrl *flash_ctrl, + struct cam_flash_frame_setting *flash_data, enum camera_flash_opcode op) +{ + uint32_t curr = 0, max_current = 0; + struct cam_flash_private_soc *soc_private = NULL; + int i = 0; + + if (!flash_ctrl || !flash_data) { + CAM_ERR(CAM_FLASH, "Fctrl or Data NULL"); + return -EINVAL; + } + + soc_private = (struct cam_flash_private_soc *) + flash_ctrl->soc_info.soc_private; + + if (op == CAMERA_SENSOR_FLASH_OP_FIRELOW) { + for (i = 0; i < flash_ctrl->torch_num_sources; i++) { + if (flash_data->led_current_ma[i]) { + if (i) + flash_data->led_current_ma[i-1] = + flash_data->led_current_ma[i]; + else + flash_data->led_current_ma[i+1] = + flash_data->led_current_ma[i]; + break; + } + } + } else if (op == CAMERA_SENSOR_FLASH_OP_FIREHIGH) { + for (i = 0; i < flash_ctrl->flash_num_sources; i++) { + if (flash_data->led_current_ma[i]) { + if (i) + flash_data->led_current_ma[i-1] = + flash_data->led_current_ma[i]; + else + flash_data->led_current_ma[i+1] = + flash_data->led_current_ma[i]; + break; + } + } + } else { + CAM_ERR(CAM_FLASH, "Wrong Operation: %d", op); + } + + if (op == CAMERA_SENSOR_FLASH_OP_FIRELOW) { + for (i = 0; i < flash_ctrl->torch_num_sources; i++) { + if (flash_ctrl->torch_trigger[i]) { + max_current = soc_private->torch_max_current[i]; + + if (flash_data->led_current_ma[i] <= + max_current) + curr = flash_data->led_current_ma[i]; + else + curr = soc_private->torch_op_current[i]; + + CAM_DBG(CAM_FLASH, + "Led_Current[%d] = %d", i, curr); + cam_res_mgr_led_trigger_event( + flash_ctrl->torch_trigger[i], + curr); + } + } + } else if (op == CAMERA_SENSOR_FLASH_OP_FIREHIGH) { + for (i = 0; i < flash_ctrl->flash_num_sources; i++) { + if (flash_ctrl->flash_trigger[i]) { + max_current = soc_private->flash_max_current[i]; + + if (flash_data->led_current_ma[i] <= + max_current) + curr = flash_data->led_current_ma[i]; + else + curr = soc_private->flash_op_current[i]; + + CAM_DBG(CAM_FLASH, "LED flash_current[%d]: %d", + i, curr); + cam_res_mgr_led_trigger_event( + flash_ctrl->flash_trigger[i], + curr); + } + } + } else { + CAM_ERR(CAM_FLASH, "Wrong Operation: %d", op); + return -EINVAL; + } + + if (flash_ctrl->switch_trigger) + cam_res_mgr_led_trigger_event( + flash_ctrl->switch_trigger, + (enum led_brightness)LED_SWITCH_ON); + + return 0; +} + +int cam_flash_off(struct cam_flash_ctrl *flash_ctrl) +{ + int i = 0; + + if (!flash_ctrl) { + CAM_ERR(CAM_FLASH, "Flash control Null"); + return -EINVAL; + } + + for (i = 0; i < flash_ctrl->flash_num_sources; i++) + if (flash_ctrl->flash_trigger[i]) + cam_res_mgr_led_trigger_event( + flash_ctrl->flash_trigger[i], + LED_OFF); + + for (i = 0; i < flash_ctrl->torch_num_sources; i++) + if (flash_ctrl->torch_trigger[i]) + cam_res_mgr_led_trigger_event( + flash_ctrl->torch_trigger[i], + LED_OFF); + + if (flash_ctrl->switch_trigger) + cam_res_mgr_led_trigger_event(flash_ctrl->switch_trigger, + (enum led_brightness)LED_SWITCH_OFF); + + flash_ctrl->flash_state = CAM_FLASH_STATE_START; + return 0; +} + +static int cam_flash_low( + struct cam_flash_ctrl *flash_ctrl, + struct cam_flash_frame_setting *flash_data) +{ + int i = 0, rc = 0; + + if (!flash_data) { + CAM_ERR(CAM_FLASH, "Flash Data Null"); + return -EINVAL; + } + + for (i = 0; i < flash_ctrl->flash_num_sources; i++) + if (flash_ctrl->flash_trigger[i]) + cam_res_mgr_led_trigger_event( + flash_ctrl->flash_trigger[i], + LED_OFF); + + rc = cam_flash_ops(flash_ctrl, flash_data, + CAMERA_SENSOR_FLASH_OP_FIRELOW); + if (rc) + CAM_ERR(CAM_FLASH, "Fire Torch failed: %d", rc); + + return rc; +} + +static int cam_flash_high( + struct cam_flash_ctrl *flash_ctrl, + struct cam_flash_frame_setting *flash_data) +{ + int i = 0, rc = 0; + + if (!flash_data) { + CAM_ERR(CAM_FLASH, "Flash Data Null"); + return -EINVAL; + } + + for (i = 0; i < flash_ctrl->torch_num_sources; i++) + if (flash_ctrl->torch_trigger[i]) + cam_res_mgr_led_trigger_event( + flash_ctrl->torch_trigger[i], + LED_OFF); + + rc = cam_flash_ops(flash_ctrl, flash_data, + CAMERA_SENSOR_FLASH_OP_FIREHIGH); + if (rc) + CAM_ERR(CAM_FLASH, "Fire Flash Failed: %d", rc); + + return rc; +} + +static int delete_req(struct cam_flash_ctrl *fctrl, uint64_t req_id) +{ + int i = 0; + struct cam_flash_frame_setting *flash_data = NULL; + uint64_t top = 0, del_req_id = 0; + + if (req_id == 0) { + flash_data = &fctrl->nrt_info; + if ((fctrl->nrt_info.cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET) || + (fctrl->nrt_info.cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_RER)) { + flash_data->cmn_attr.is_settings_valid = false; + for (i = 0; i < flash_data->cmn_attr.count; i++) + flash_data->led_current_ma[i] = 0; + } else { + fctrl->flash_init_setting.cmn_attr. + is_settings_valid = false; + } + } else { + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + flash_data = &fctrl->per_frame[i]; + if (req_id >= flash_data->cmn_attr.request_id && + flash_data->cmn_attr.is_settings_valid + == 1) { + if (top < flash_data->cmn_attr.request_id) { + del_req_id = top; + top = flash_data->cmn_attr.request_id; + } else if (top > + flash_data->cmn_attr.request_id && + del_req_id < + flash_data->cmn_attr.request_id) { + del_req_id = + flash_data->cmn_attr.request_id; + } + } + } + + if (top < req_id) { + if ((((top % MAX_PER_FRAME_ARRAY) - (req_id % + MAX_PER_FRAME_ARRAY)) >= BATCH_SIZE_MAX) || + (((top % MAX_PER_FRAME_ARRAY) - (req_id % + MAX_PER_FRAME_ARRAY)) <= -BATCH_SIZE_MAX)) + del_req_id = req_id; + } + + if (!del_req_id) + return 0; + + CAM_DBG(CAM_FLASH, "top: %llu, del_req_id:%llu", + top, del_req_id); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + flash_data = &fctrl->per_frame[i]; + if ((del_req_id == + flash_data->cmn_attr.request_id) && + (flash_data->cmn_attr. + is_settings_valid == 1)) { + CAM_DBG(CAM_FLASH, "Deleting request[%d] %llu", + i, flash_data->cmn_attr.request_id); + flash_data->cmn_attr.request_id = 0; + flash_data->cmn_attr.is_settings_valid = false; + flash_data->opcode = 0; + for (i = 0; i < flash_data->cmn_attr.count; i++) + flash_data->led_current_ma[i] = 0; + } + } + } + + return 0; +} + +int cam_flash_apply_setting(struct cam_flash_ctrl *fctrl, + uint64_t req_id) +{ + int rc = 0, i = 0; + int frame_offset = 0; + uint16_t num_iterations; + struct cam_flash_frame_setting *flash_data = NULL; + + if (req_id == 0) { + if (fctrl->nrt_info.cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_FIRE) { + flash_data = &fctrl->nrt_info; + if (flash_data->opcode == + CAMERA_SENSOR_FLASH_OP_FIREHIGH) { + if (fctrl->flash_state != + CAM_FLASH_STATE_CONFIG) { + CAM_WARN(CAM_FLASH, + "Cannot apply Start Dev:Prev state: %d", + fctrl->flash_state); + return rc; + } + rc = cam_flash_prepare(fctrl, true); + if (rc) { + CAM_ERR(CAM_FLASH, + "Enable Regulator Failed rc = %d", rc); + return rc; + } + rc = cam_flash_high(fctrl, flash_data); + if (rc) + CAM_ERR(CAM_FLASH, + "FLASH ON failed : %d", + rc); + } + if (flash_data->opcode == + CAMERA_SENSOR_FLASH_OP_OFF) { + rc = cam_flash_off(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, + "LED OFF FAILED: %d", + rc); + return rc; + } + if ((fctrl->flash_state == + CAM_FLASH_STATE_START) && + (fctrl->is_regulator_enabled == true)) { + rc = cam_flash_prepare(fctrl, false); + if (rc) + CAM_ERR(CAM_FLASH, + "Disable Regulator failed: %d", + rc); + } + } + } else if (fctrl->nrt_info.cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET) { + flash_data = &fctrl->nrt_info; + if (flash_data->opcode == + CAMERA_SENSOR_FLASH_OP_FIRELOW) { + rc = cam_flash_low(fctrl, flash_data); + if (rc) { + CAM_ERR(CAM_FLASH, + "Torch ON failed : %d", + rc); + goto nrt_del_req; + } + } else if (flash_data->opcode == + CAMERA_SENSOR_FLASH_OP_OFF) { + rc = cam_flash_off(fctrl); + if (rc) + CAM_ERR(CAM_FLASH, + "LED off failed: %d", + rc); + } + } else if (fctrl->nrt_info.cmn_attr.cmd_type == + CAMERA_SENSOR_FLASH_CMD_TYPE_RER) { + flash_data = &fctrl->nrt_info; + + if (fctrl->flash_state != CAM_FLASH_STATE_START) { + rc = cam_flash_off(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, + "Flash off failed: %d", + rc); + goto nrt_del_req; + } + } + num_iterations = flash_data->num_iterations; + for (i = 0; i < num_iterations; i++) { + /* Turn On Torch */ + if (fctrl->flash_state == + CAM_FLASH_STATE_START) { + rc = cam_flash_low(fctrl, flash_data); + if (rc) { + CAM_ERR(CAM_FLASH, + "Fire Torch Failed"); + goto nrt_del_req; + } + + usleep_range( + flash_data->led_on_delay_ms * 1000, + flash_data->led_on_delay_ms * 1000 + + 100); + } + /* Turn Off Torch */ + rc = cam_flash_off(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, + "Flash off failed: %d", + rc); + continue; + } + fctrl->flash_state = CAM_FLASH_STATE_START; + usleep_range( + flash_data->led_off_delay_ms * 1000, + flash_data->led_off_delay_ms * 1000 + 100); + } + } + } else { + frame_offset = req_id % MAX_PER_FRAME_ARRAY; + flash_data = &fctrl->per_frame[frame_offset]; + + if ((flash_data->opcode == CAMERA_SENSOR_FLASH_OP_FIREHIGH) && + (flash_data->cmn_attr.is_settings_valid) && + (flash_data->cmn_attr.request_id == req_id)) { + /* Turn On Flash */ + if (fctrl->flash_state == CAM_FLASH_STATE_START) { + rc = cam_flash_high(fctrl, flash_data); + if (rc) { + CAM_ERR(CAM_FLASH, + "Flash ON failed: rc= %d", + rc); + goto apply_setting_err; + } + } + } else if ((flash_data->opcode == + CAMERA_SENSOR_FLASH_OP_FIRELOW) && + (flash_data->cmn_attr.is_settings_valid) && + (flash_data->cmn_attr.request_id == req_id)) { + /* Turn On Torch */ + if (fctrl->flash_state == CAM_FLASH_STATE_START) { + rc = cam_flash_low(fctrl, flash_data); + if (rc) { + CAM_ERR(CAM_FLASH, + "Torch ON failed: rc= %d", + rc); + goto apply_setting_err; + } + } + } else if ((flash_data->opcode == CAMERA_SENSOR_FLASH_OP_OFF) && + (flash_data->cmn_attr.is_settings_valid) && + (flash_data->cmn_attr.request_id == req_id)) { + rc = cam_flash_off(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, + "Flash off failed %d", rc); + goto apply_setting_err; + } + } else { + CAM_DBG(CAM_FLASH, "NOP opcode: req_id: %u", req_id); + } + } + +nrt_del_req: + delete_req(fctrl, req_id); +apply_setting_err: + return rc; +} + +int cam_flash_parser(struct cam_flash_ctrl *fctrl, void *arg) +{ + int rc = 0, i = 0; + uintptr_t generic_ptr; + uint32_t *cmd_buf = NULL; + uint32_t *offset = NULL; + uint32_t frame_offset = 0; + size_t len_of_buffer; + struct cam_control *ioctl_ctrl = NULL; + struct cam_packet *csl_packet = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct common_header *cmn_hdr; + struct cam_config_dev_cmd config; + struct cam_req_mgr_add_request add_req; + struct cam_flash_init *cam_flash_info = NULL; + struct cam_flash_set_rer *flash_rer_info = NULL; + struct cam_flash_set_on_off *flash_operation_info = NULL; + struct cam_flash_query_curr *flash_query_info = NULL; + + if (!fctrl || !arg) { + CAM_ERR(CAM_FLASH, "fctrl/arg is NULL"); + return -EINVAL; + } + /* getting CSL Packet */ + ioctl_ctrl = (struct cam_control *)arg; + + if (copy_from_user((&config), + u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(config))) { + CAM_ERR(CAM_FLASH, "Copy cmd handle from user failed"); + rc = -EFAULT; + return rc; + } + + rc = cam_mem_get_cpu_buf(config.packet_handle, + &generic_ptr, &len_of_buffer); + if (rc) { + CAM_ERR(CAM_FLASH, "Failed in getting the buffer : %d", rc); + return rc; + } + + if (config.offset > len_of_buffer) { + CAM_ERR(CAM_FLASH, + "offset is out of bounds: offset: %lld len: %zu", + config.offset, len_of_buffer); + return -EINVAL; + } + + /* Add offset to the flash csl header */ + csl_packet = (struct cam_packet *)(generic_ptr + config.offset); + + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_FLASH_PACKET_OPCODE_INIT: { + /* INIT packet*/ + offset = (uint32_t *)((uint8_t *)&csl_packet->payload + + csl_packet->cmd_buf_offset); + fctrl->flash_init_setting.cmn_attr.request_id = 0; + fctrl->flash_init_setting.cmn_attr.is_settings_valid = true; + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + &generic_ptr, &len_of_buffer); + cmd_buf = (uint32_t *)(generic_ptr + + cmd_desc->offset); + cam_flash_info = (struct cam_flash_init *)cmd_buf; + + switch (cam_flash_info->cmd_type) { + case CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_INFO: + fctrl->flash_type = cam_flash_info->flash_type; + fctrl->is_regulator_enabled = false; + fctrl->nrt_info.cmn_attr.cmd_type = + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_INFO; + fctrl->flash_state = + CAM_FLASH_STATE_CONFIG; + break; + case CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_FIRE: + CAM_DBG(CAM_FLASH, "Widget Flash Operation"); + flash_operation_info = + (struct cam_flash_set_on_off *) cmd_buf; + fctrl->nrt_info.cmn_attr.count = + flash_operation_info->count; + fctrl->nrt_info.cmn_attr.request_id = 0; + fctrl->nrt_info.opcode = + flash_operation_info->opcode; + fctrl->nrt_info.cmn_attr.cmd_type = + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_FIRE; + for (i = 0; + i < flash_operation_info->count; i++) + fctrl->nrt_info.led_current_ma[i] = + flash_operation_info->led_current_ma[i]; + + mutex_lock(&fctrl->flash_wq_mutex); + rc = cam_flash_apply_setting(fctrl, 0); + if (rc) + CAM_ERR(CAM_FLASH, + "Apply setting failed: %d", + rc); + mutex_unlock(&fctrl->flash_wq_mutex); + fctrl->flash_state = + CAM_FLASH_STATE_CONFIG; + break; + default: + CAM_ERR(CAM_FLASH, "Wrong cmd_type = %d", + cam_flash_info->cmd_type); + return -EINVAL; + } + break; + } + case CAM_FLASH_PACKET_OPCODE_SET_OPS: { + offset = (uint32_t *)((uint8_t *)&csl_packet->payload + + csl_packet->cmd_buf_offset); + frame_offset = csl_packet->header.request_id % + MAX_PER_FRAME_ARRAY; + if (fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid + == true) { + fctrl->per_frame[frame_offset].cmn_attr.request_id = 0; + fctrl->per_frame[frame_offset]. + cmn_attr.is_settings_valid = false; + for (i = 0; + i < fctrl->per_frame[frame_offset].cmn_attr.count; + i++) { + fctrl->per_frame[frame_offset]. + led_current_ma[i] = 0; + } + } + + fctrl->per_frame[frame_offset].cmn_attr.request_id = + csl_packet->header.request_id; + fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid = + true; + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + &generic_ptr, &len_of_buffer); + cmd_buf = (uint32_t *)(generic_ptr + + cmd_desc->offset); + + if (!cmd_buf) + return -EINVAL; + + cmn_hdr = (struct common_header *)cmd_buf; + + switch (cmn_hdr->cmd_type) { + case CAMERA_SENSOR_FLASH_CMD_TYPE_FIRE: { + CAM_DBG(CAM_FLASH, + "CAMERA_FLASH_CMD_TYPE_OPS case called"); + if ((fctrl->flash_state == CAM_FLASH_STATE_INIT) || + (fctrl->flash_state == + CAM_FLASH_STATE_ACQUIRE)) { + CAM_WARN(CAM_FLASH, + "Rxed Flash fire ops without linking"); + fctrl->per_frame[frame_offset]. + cmn_attr.is_settings_valid = false; + return 0; + } + + flash_operation_info = + (struct cam_flash_set_on_off *) cmd_buf; + if (!flash_operation_info) { + CAM_ERR(CAM_FLASH, + "flash_operation_info Null"); + return -EINVAL; + } + + fctrl->per_frame[frame_offset].opcode = + flash_operation_info->opcode; + fctrl->per_frame[frame_offset].cmn_attr.count = + flash_operation_info->count; + for (i = 0; + i < flash_operation_info->count; i++) + fctrl->per_frame[frame_offset]. + led_current_ma[i] + = flash_operation_info-> + led_current_ma[i]; + } + break; + default: + CAM_ERR(CAM_FLASH, "Wrong cmd_type = %d", + cmn_hdr->cmd_type); + return -EINVAL; + } + break; + } + case CAM_FLASH_PACKET_OPCODE_NON_REALTIME_SET_OPS: { + offset = (uint32_t *)((uint8_t *)&csl_packet->payload + + csl_packet->cmd_buf_offset); + fctrl->nrt_info.cmn_attr.is_settings_valid = true; + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + &generic_ptr, &len_of_buffer); + cmd_buf = (uint32_t *)(generic_ptr + + cmd_desc->offset); + cmn_hdr = (struct common_header *)cmd_buf; + + switch (cmn_hdr->cmd_type) { + case CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET: { + CAM_DBG(CAM_FLASH, "Widget Flash Operation"); + flash_operation_info = + (struct cam_flash_set_on_off *) cmd_buf; + fctrl->nrt_info.cmn_attr.count = + flash_operation_info->count; + fctrl->nrt_info.cmn_attr.request_id = 0; + fctrl->nrt_info.opcode = + flash_operation_info->opcode; + fctrl->nrt_info.cmn_attr.cmd_type = + CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET; + + for (i = 0; i < flash_operation_info->count; i++) + fctrl->nrt_info.led_current_ma[i] = + flash_operation_info->led_current_ma[i]; + + mutex_lock(&fctrl->flash_wq_mutex); + rc = cam_flash_apply_setting(fctrl, 0); + if (rc) + CAM_ERR(CAM_FLASH, "Apply setting failed: %d", + rc); + mutex_unlock(&fctrl->flash_wq_mutex); + return rc; + } + case CAMERA_SENSOR_FLASH_CMD_TYPE_QUERYCURR: { + int query_curr_ma = 0; + + flash_query_info = + (struct cam_flash_query_curr *)cmd_buf; + + rc = qpnp_flash_led_prepare(fctrl->switch_trigger, + QUERY_MAX_CURRENT, &query_curr_ma); + CAM_DBG(CAM_FLASH, "query_curr_ma = %d", + query_curr_ma); + if (rc) { + CAM_ERR(CAM_FLASH, + "Query current failed with rc=%d", rc); + return rc; + } + flash_query_info->query_current_ma = query_curr_ma; + break; + } + case CAMERA_SENSOR_FLASH_CMD_TYPE_RER: { + rc = 0; + flash_rer_info = (struct cam_flash_set_rer *)cmd_buf; + fctrl->nrt_info.cmn_attr.cmd_type = + CAMERA_SENSOR_FLASH_CMD_TYPE_RER; + fctrl->nrt_info.opcode = flash_rer_info->opcode; + fctrl->nrt_info.cmn_attr.count = flash_rer_info->count; + fctrl->nrt_info.cmn_attr.request_id = 0; + fctrl->nrt_info.num_iterations = + flash_rer_info->num_iteration; + fctrl->nrt_info.led_on_delay_ms = + flash_rer_info->led_on_delay_ms; + fctrl->nrt_info.led_off_delay_ms = + flash_rer_info->led_off_delay_ms; + + for (i = 0; i < flash_rer_info->count; i++) + fctrl->nrt_info.led_current_ma[i] = + flash_rer_info->led_current_ma[i]; + + + mutex_lock(&fctrl->flash_wq_mutex); + rc = cam_flash_apply_setting(fctrl, 0); + if (rc) + CAM_ERR(CAM_FLASH, "apply_setting failed: %d", + rc); + mutex_unlock(&fctrl->flash_wq_mutex); + return rc; + } + default: + CAM_ERR(CAM_FLASH, "Wrong cmd_type : %d", + cmn_hdr->cmd_type); + return -EINVAL; + } + + break; + } + case CAM_PKT_NOP_OPCODE: { + if ((fctrl->flash_state == CAM_FLASH_STATE_INIT) || + (fctrl->flash_state == CAM_FLASH_STATE_ACQUIRE)) { + CAM_WARN(CAM_FLASH, + "Rxed NOP packets without linking"); + fctrl->per_frame[frame_offset]. + cmn_attr.is_settings_valid = false; + return 0; + } + + CAM_DBG(CAM_FLASH, "NOP Packet is Received: req_id: %u", + csl_packet->header.request_id); + goto update_req_mgr; + } + default: + CAM_ERR(CAM_FLASH, "Wrong Opcode : %d", + (csl_packet->header.op_code & 0xFFFFFF)); + return -EINVAL; + } +update_req_mgr: + if (((csl_packet->header.op_code & 0xFFFFF) == + CAM_PKT_NOP_OPCODE) || + ((csl_packet->header.op_code & 0xFFFFF) == + CAM_FLASH_PACKET_OPCODE_SET_OPS)) { + add_req.link_hdl = fctrl->bridge_intf.link_hdl; + add_req.req_id = csl_packet->header.request_id; + add_req.dev_hdl = fctrl->bridge_intf.device_hdl; + + if ((csl_packet->header.op_code & 0xFFFFF) == + CAM_FLASH_PACKET_OPCODE_SET_OPS) + add_req.skip_before_applying = 1; + else + add_req.skip_before_applying = 0; + + if (fctrl->bridge_intf.crm_cb && + fctrl->bridge_intf.crm_cb->add_req) + fctrl->bridge_intf.crm_cb->add_req(&add_req); + CAM_DBG(CAM_FLASH, "add req to req_mgr= %lld", add_req.req_id); + } + + return rc; +} + +int cam_flash_publish_dev_info(struct cam_req_mgr_device_info *info) +{ + info->dev_id = CAM_REQ_MGR_DEVICE_FLASH; + strlcpy(info->name, CAM_FLASH_NAME, sizeof(info->name)); + info->p_delay = CAM_FLASH_PIPELINE_DELAY; + info->trigger = CAM_TRIGGER_POINT_SOF; + return 0; +} + +int cam_flash_establish_link(struct cam_req_mgr_core_dev_link_setup *link) +{ + struct cam_flash_ctrl *fctrl = NULL; + + if (!link) + return -EINVAL; + + fctrl = (struct cam_flash_ctrl *)cam_get_device_priv(link->dev_hdl); + if (!fctrl) { + CAM_ERR(CAM_FLASH, " Device data is NULL"); + return -EINVAL; + } + + if (link->link_enable) { + fctrl->bridge_intf.link_hdl = link->link_hdl; + fctrl->bridge_intf.crm_cb = link->crm_cb; + } else { + fctrl->bridge_intf.link_hdl = -1; + fctrl->bridge_intf.crm_cb = NULL; + } + + return 0; +} + + +int cam_flash_stop_dev(struct cam_flash_ctrl *fctrl) +{ + int rc = 0, i, j; + + cam_flash_off(fctrl); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + fctrl->per_frame[i].cmn_attr.request_id = 0; + fctrl->per_frame[i].cmn_attr.is_settings_valid = false; + fctrl->per_frame[i].cmn_attr.count = 0; + for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++) + fctrl->per_frame[i].led_current_ma[j] = 0; + } + + rc = cam_flash_flush_nrt(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, + "NonRealTime Dev flush failed rc: %d", rc); + return rc; + } + + if ((fctrl->flash_state == CAM_FLASH_STATE_START) && + (fctrl->is_regulator_enabled == true)) { + rc = cam_flash_prepare(fctrl, false); + if (rc) + CAM_ERR(CAM_FLASH, "Disable Regulator Failed rc: %d", + rc); + } + + return rc; +} + +int cam_flash_release_dev(struct cam_flash_ctrl *fctrl) +{ + int rc = 0; + + if (fctrl->bridge_intf.device_hdl != 1) { + rc = cam_destroy_device_hdl(fctrl->bridge_intf.device_hdl); + if (rc) + CAM_ERR(CAM_FLASH, + "Failed in destroying device handle rc = %d", + rc); + fctrl->bridge_intf.device_hdl = -1; + fctrl->bridge_intf.link_hdl = -1; + fctrl->bridge_intf.session_hdl = -1; + } + + return rc; +} + +void cam_flash_shutdown(struct cam_flash_ctrl *fctrl) +{ + int rc; + + if (fctrl->flash_state == CAM_FLASH_STATE_INIT) + return; + + if ((fctrl->flash_state == CAM_FLASH_STATE_CONFIG) || + (fctrl->flash_state == CAM_FLASH_STATE_START)) { + rc = cam_flash_stop_dev(fctrl); + if (rc) + CAM_ERR(CAM_FLASH, "Stop Failed rc: %d", rc); + } + + rc = cam_flash_release_dev(fctrl); + if (rc) + CAM_ERR(CAM_FLASH, "Release failed rc: %d", rc); + + fctrl->flash_state = CAM_FLASH_STATE_INIT; +} + +int cam_flash_apply_request(struct cam_req_mgr_apply_request *apply) +{ + int rc = 0; + struct cam_flash_ctrl *fctrl = NULL; + + if (!apply) + return -EINVAL; + + fctrl = (struct cam_flash_ctrl *) cam_get_device_priv(apply->dev_hdl); + if (!fctrl) { + CAM_ERR(CAM_FLASH, "Device data is NULL"); + rc = -EINVAL; + goto free_resource; + } + + if (!(apply->report_if_bubble)) { + mutex_lock(&fctrl->flash_wq_mutex); + rc = cam_flash_apply_setting(fctrl, apply->request_id); + if (rc) + CAM_ERR(CAM_FLASH, "apply_setting failed with rc=%d", + rc); + mutex_unlock(&fctrl->flash_wq_mutex); + } + +free_resource: + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.h new file mode 100644 index 000000000000..f73409a0a935 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_core.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FLASH_CORE_H_ +#define _CAM_FLASH_CORE_H_ + +#include +#include +#include "cam_flash_dev.h" +#include "cam_sync_api.h" +#include "cam_mem_mgr_api.h" + +int cam_flash_parser(struct cam_flash_ctrl *fctrl, void *arg); +int cam_flash_publish_dev_info(struct cam_req_mgr_device_info *info); +int cam_flash_establish_link(struct cam_req_mgr_core_dev_link_setup *link); +int cam_flash_apply_setting(struct cam_flash_ctrl *fctrl, uint64_t req_id); +int cam_flash_apply_request(struct cam_req_mgr_apply_request *apply); +int cam_flash_process_evt(struct cam_req_mgr_link_evt_data *event_data); +int cam_flash_flush_request(struct cam_req_mgr_flush_request *flush); +int cam_flash_off(struct cam_flash_ctrl *fctrl); +int cam_flash_prepare(struct cam_flash_ctrl *flash_ctrl, + bool regulator_enable); +void cam_flash_shutdown(struct cam_flash_ctrl *flash_ctrl); +int cam_flash_stop_dev(struct cam_flash_ctrl *flash_ctrl); +int cam_flash_release_dev(struct cam_flash_ctrl *fctrl); +#endif /*_CAM_FLASH_CORE_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.c new file mode 100644 index 000000000000..5d33054d7af6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.c @@ -0,0 +1,422 @@ +/* Copyright (c) 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 +#include "cam_flash_dev.h" +#include "cam_flash_soc.h" +#include "cam_flash_core.h" + +static int32_t cam_flash_driver_cmd(struct cam_flash_ctrl *fctrl, + void *arg, struct cam_flash_private_soc *soc_private) +{ + int rc = 0; + int i = 0; + struct cam_control *cmd = (struct cam_control *)arg; + + if (!fctrl || !arg) { + CAM_ERR(CAM_FLASH, "fctrl/arg is NULL with arg:%pK fctrl%pK", + fctrl, arg); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_FLASH, "Invalid handle type: %d", + cmd->handle_type); + rc = -EINVAL; + goto release_mutex; + } + + mutex_lock(&(fctrl->flash_mutex)); + switch (cmd->op_code) { + case CAM_ACQUIRE_DEV: { + struct cam_sensor_acquire_dev flash_acq_dev; + struct cam_create_dev_hdl bridge_params; + + CAM_DBG(CAM_FLASH, "CAM_ACQUIRE_DEV"); + + if (fctrl->flash_state != CAM_FLASH_STATE_INIT) { + CAM_ERR(CAM_FLASH, + "Cannot apply Acquire dev: Prev state: %d", + fctrl->flash_state); + rc = -EINVAL; + goto release_mutex; + } + + if (fctrl->bridge_intf.device_hdl != -1) { + CAM_ERR(CAM_FLASH, "Device is already acquired"); + rc = -EINVAL; + goto release_mutex; + } + + rc = copy_from_user(&flash_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(flash_acq_dev)); + if (rc) { + CAM_ERR(CAM_FLASH, "Failed Copying from User"); + goto release_mutex; + } + + bridge_params.session_hdl = flash_acq_dev.session_handle; + bridge_params.ops = &fctrl->bridge_intf.ops; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = fctrl; + + flash_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + fctrl->bridge_intf.device_hdl = + flash_acq_dev.device_handle; + fctrl->bridge_intf.session_hdl = + flash_acq_dev.session_handle; + + rc = copy_to_user(u64_to_user_ptr(cmd->handle), + &flash_acq_dev, + sizeof(struct cam_sensor_acquire_dev)); + if (rc) { + CAM_ERR(CAM_FLASH, "Failed Copy to User with rc = %d", + rc); + rc = -EFAULT; + goto release_mutex; + } + fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE; + break; + } + case CAM_RELEASE_DEV: { + CAM_DBG(CAM_FLASH, "CAM_RELEASE_DEV"); + if ((fctrl->flash_state == CAM_FLASH_STATE_INIT) || + (fctrl->flash_state == CAM_FLASH_STATE_START)) { + CAM_WARN(CAM_FLASH, + "Cannot apply Release dev: Prev state:%d", + fctrl->flash_state); + } + + if (fctrl->bridge_intf.device_hdl == -1 && + fctrl->flash_state == CAM_FLASH_STATE_ACQUIRE) { + CAM_ERR(CAM_FLASH, + "Invalid Handle: Link Hdl: %d device hdl: %d", + fctrl->bridge_intf.device_hdl, + fctrl->bridge_intf.link_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_flash_release_dev(fctrl); + if (rc) + CAM_ERR(CAM_FLASH, + "Failed in destroying the device Handle rc= %d", + rc); + fctrl->flash_state = CAM_FLASH_STATE_INIT; + break; + } + case CAM_QUERY_CAP: { + struct cam_flash_query_cap_info flash_cap = {0}; + + CAM_DBG(CAM_FLASH, "CAM_QUERY_CAP"); + flash_cap.slot_info = fctrl->soc_info.index; + for (i = 0; i < fctrl->flash_num_sources; i++) { + flash_cap.max_current_flash[i] = + soc_private->flash_max_current[i]; + flash_cap.max_duration_flash[i] = + soc_private->flash_max_duration[i]; + } + + for (i = 0; i < fctrl->torch_num_sources; i++) + flash_cap.max_current_torch[i] = + soc_private->torch_max_current[i]; + + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &flash_cap, sizeof(struct cam_flash_query_cap_info))) { + CAM_ERR(CAM_FLASH, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + break; + } + case CAM_START_DEV: { + CAM_DBG(CAM_FLASH, "CAM_START_DEV"); + if ((fctrl->flash_state == CAM_FLASH_STATE_INIT) || + (fctrl->flash_state == CAM_FLASH_STATE_START)) { + CAM_WARN(CAM_FLASH, + "Cannot apply Start Dev: Prev state: %d", + fctrl->flash_state); + rc = -EINVAL; + goto release_mutex; + } + + rc = cam_flash_prepare(fctrl, true); + if (rc) { + CAM_ERR(CAM_FLASH, + "Enable Regulator Failed rc = %d", rc); + goto release_mutex; + } + rc = cam_flash_apply_setting(fctrl, 0); + if (rc) { + CAM_ERR(CAM_FLASH, "cannot apply settings rc = %d", rc); + goto release_mutex; + } + fctrl->flash_state = CAM_FLASH_STATE_START; + break; + } + case CAM_STOP_DEV: { + CAM_DBG(CAM_FLASH, "CAM_STOP_DEV ENTER"); + if (fctrl->flash_state != CAM_FLASH_STATE_START) { + CAM_WARN(CAM_FLASH, + "Cannot apply Stop dev: Prev state is: %d", + fctrl->flash_state); + rc = -EINVAL; + goto release_mutex; + } + + rc = cam_flash_stop_dev(fctrl); + if (rc) { + CAM_ERR(CAM_FLASH, "Stop Dev Failed rc = %d", + rc); + goto release_mutex; + } + fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE; + break; + } + case CAM_CONFIG_DEV: { + CAM_DBG(CAM_FLASH, "CAM_CONFIG_DEV"); + rc = cam_flash_parser(fctrl, arg); + if (rc) { + CAM_ERR(CAM_FLASH, "Failed Flash Config: rc=%d\n", rc); + goto release_mutex; + } + break; + } + default: + CAM_ERR(CAM_FLASH, "Invalid Opcode: %d", cmd->op_code); + rc = -EINVAL; + } + +release_mutex: + mutex_unlock(&(fctrl->flash_mutex)); + return rc; +} + +static const struct of_device_id cam_flash_dt_match[] = { + {.compatible = "qcom,camera-flash", .data = NULL}, + {} +}; + +static long cam_flash_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_flash_ctrl *fctrl = NULL; + struct cam_flash_private_soc *soc_private = NULL; + + CAM_DBG(CAM_FLASH, "Enter"); + + fctrl = v4l2_get_subdevdata(sd); + soc_private = fctrl->soc_info.soc_private; + + switch (cmd) { + case VIDIOC_CAM_CONTROL: { + rc = cam_flash_driver_cmd(fctrl, arg, + soc_private); + break; + } + default: + CAM_ERR(CAM_FLASH, "Invalid ioctl cmd type"); + rc = -EINVAL; + break; + } + + CAM_DBG(CAM_FLASH, "Exit"); + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_flash_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_FLASH, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: { + rc = cam_flash_subdev_ioctl(sd, cmd, &cmd_data); + if (rc) + CAM_ERR(CAM_FLASH, "cam_flash_ioctl failed"); + break; + } + default: + CAM_ERR(CAM_FLASH, "Invalid compat ioctl cmd_type:%d", + cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_FLASH, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + +static int cam_flash_platform_remove(struct platform_device *pdev) +{ + struct cam_flash_ctrl *fctrl; + + fctrl = platform_get_drvdata(pdev); + if (!fctrl) { + CAM_ERR(CAM_FLASH, "Flash device is NULL"); + return 0; + } + + devm_kfree(&pdev->dev, fctrl); + + return 0; +} + +static int cam_flash_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_flash_ctrl *flash_ctrl = + v4l2_get_subdevdata(sd); + + if (!flash_ctrl) { + CAM_ERR(CAM_FLASH, "Flash ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&flash_ctrl->flash_mutex); + cam_flash_shutdown(flash_ctrl); + mutex_unlock(&flash_ctrl->flash_mutex); + + return 0; +} + +static struct v4l2_subdev_core_ops cam_flash_subdev_core_ops = { + .ioctl = cam_flash_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_flash_subdev_do_ioctl +#endif +}; + +static struct v4l2_subdev_ops cam_flash_subdev_ops = { + .core = &cam_flash_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cam_flash_internal_ops = { + .close = cam_flash_subdev_close, +}; + +static int32_t cam_flash_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct cam_flash_ctrl *flash_ctrl = NULL; + + CAM_DBG(CAM_FLASH, "Enter"); + if (!pdev->dev.of_node) { + CAM_ERR(CAM_FLASH, "of_node NULL"); + return -EINVAL; + } + + flash_ctrl = kzalloc(sizeof(struct cam_flash_ctrl), GFP_KERNEL); + if (!flash_ctrl) + return -ENOMEM; + + flash_ctrl->pdev = pdev; + flash_ctrl->soc_info.pdev = pdev; + flash_ctrl->soc_info.dev = &pdev->dev; + flash_ctrl->soc_info.dev_name = pdev->name; + + rc = cam_flash_get_dt_data(flash_ctrl, &flash_ctrl->soc_info); + if (rc) { + CAM_ERR(CAM_FLASH, "cam_flash_get_dt_data failed with %d", rc); + kfree(flash_ctrl); + return -EINVAL; + } + + flash_ctrl->v4l2_dev_str.internal_ops = + &cam_flash_internal_ops; + flash_ctrl->v4l2_dev_str.ops = &cam_flash_subdev_ops; + flash_ctrl->v4l2_dev_str.name = CAMX_FLASH_DEV_NAME; + flash_ctrl->v4l2_dev_str.sd_flags = + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + flash_ctrl->v4l2_dev_str.ent_function = CAM_FLASH_DEVICE_TYPE; + flash_ctrl->v4l2_dev_str.token = flash_ctrl; + + rc = cam_register_subdev(&(flash_ctrl->v4l2_dev_str)); + if (rc) { + CAM_ERR(CAM_FLASH, "Fail to create subdev with %d", rc); + goto free_resource; + } + flash_ctrl->bridge_intf.device_hdl = -1; + flash_ctrl->bridge_intf.ops.get_dev_info = cam_flash_publish_dev_info; + flash_ctrl->bridge_intf.ops.link_setup = cam_flash_establish_link; + flash_ctrl->bridge_intf.ops.apply_req = cam_flash_apply_request; + flash_ctrl->bridge_intf.ops.flush_req = cam_flash_flush_request; + + platform_set_drvdata(pdev, flash_ctrl); + v4l2_set_subdevdata(&flash_ctrl->v4l2_dev_str.sd, flash_ctrl); + + mutex_init(&(flash_ctrl->flash_mutex)); + mutex_init(&(flash_ctrl->flash_wq_mutex)); + + flash_ctrl->flash_state = CAM_FLASH_STATE_INIT; + CAM_DBG(CAM_FLASH, "Probe success"); + return rc; +free_resource: + kfree(flash_ctrl); + return rc; +} + +MODULE_DEVICE_TABLE(of, cam_flash_dt_match); + +static struct platform_driver cam_flash_platform_driver = { + .probe = cam_flash_platform_probe, + .remove = cam_flash_platform_remove, + .driver = { + .name = "CAM-FLASH-DRIVER", + .owner = THIS_MODULE, + .of_match_table = cam_flash_dt_match, + }, +}; + +static int __init cam_flash_init_module(void) +{ + int32_t rc = 0; + + rc = platform_driver_register(&cam_flash_platform_driver); + if (rc) + CAM_ERR(CAM_FLASH, "platform probe for flash failed"); + + return rc; +} + +static void __exit cam_flash_exit_module(void) +{ + platform_driver_unregister(&cam_flash_platform_driver); +} + +module_init(cam_flash_init_module); +module_exit(cam_flash_exit_module); +MODULE_DESCRIPTION("CAM FLASH"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.h new file mode 100644 index 000000000000..92726a9a125c --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_dev.h @@ -0,0 +1,181 @@ +/* Copyright (c) 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. + * + */ + +#ifndef _CAM_FLASH_DEV_H_ +#define _CAM_FLASH_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_req_mgr_util.h" +#include "cam_req_mgr_interface.h" +#include "cam_subdev.h" +#include "cam_mem_mgr.h" +#include "cam_sensor_cmn_header.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define CAMX_FLASH_DEV_NAME "cam-flash-dev" + +#define CAM_FLASH_PIPELINE_DELAY 1 + +#define CAM_FLASH_PACKET_OPCODE_INIT 0 +#define CAM_FLASH_PACKET_OPCODE_SET_OPS 1 +#define CAM_FLASH_PACKET_OPCODE_NON_REALTIME_SET_OPS 2 + +enum cam_flash_switch_trigger_ops { + LED_SWITCH_OFF = 0, + LED_SWITCH_ON, +}; + +enum cam_flash_state { + CAM_FLASH_STATE_INIT, + CAM_FLASH_STATE_ACQUIRE, + CAM_FLASH_STATE_CONFIG, + CAM_FLASH_STATE_START, +}; + +/** + * struct cam_flash_intf_params + * @device_hdl : Device Handle + * @session_hdl : Session Handle + * @link_hdl : Link Handle + * @ops : KMD operations + * @crm_cb : Callback API pointers + */ +struct cam_flash_intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_flash_common_attr + * @is_settings_valid : Notify the valid settings + * @request_id : Request id provided by umd + * @count : Number of led count + * @cmd_type : Command buffer type + */ +struct cam_flash_common_attr { + bool is_settings_valid; + int32_t request_id; + uint16_t count; + uint8_t cmd_type; +}; + +/** + * struct flash_init_packet + * @cmn_attr : Provides common attributes + * @flash_type : Flash type(PMIC/I2C/GPIO) + */ +struct cam_flash_init_packet { + struct cam_flash_common_attr cmn_attr; + uint8_t flash_type; +}; + +/** + * struct flash_frame_setting + * @cmn_attr : Provides common attributes + * @num_iterations : Iterations used to perform RER + * @led_on_delay_ms : LED on time in milisec + * @led_off_delay_ms : LED off time in milisec + * @opcode : Command buffer opcode + * @led_current_ma[] : LED current array in miliamps + * + */ +struct cam_flash_frame_setting { + struct cam_flash_common_attr cmn_attr; + uint16_t num_iterations; + uint16_t led_on_delay_ms; + uint16_t led_off_delay_ms; + int8_t opcode; + uint32_t led_current_ma[CAM_FLASH_MAX_LED_TRIGGERS]; +}; + +/** + * struct cam_flash_private_soc + * @switch_trigger_name : Switch trigger name + * @flash_trigger_name : Flash trigger name array + * @flash_op_current : Flash operational current + * @flash_max_current : Max supported current for LED in flash mode + * @flash_max_duration : Max turn on duration for LED in Flash mode + * @torch_trigger_name : Torch trigger name array + * @torch_op_current : Torch operational current + * @torch_max_current : Max supported current for LED in torch mode + */ + +struct cam_flash_private_soc { + const char *switch_trigger_name; + const char *flash_trigger_name[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t flash_op_current[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t flash_max_current[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t flash_max_duration[CAM_FLASH_MAX_LED_TRIGGERS]; + const char *torch_trigger_name[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t torch_op_current[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t torch_max_current[CAM_FLASH_MAX_LED_TRIGGERS]; +}; + +/** + * struct cam_flash_ctrl + * @soc_info : Soc related information + * @pdev : Platform device + * @per_frame[] : Per_frame setting array + * @nrt_info : NonRealTime settings + * @of_node : Of Node ptr + * @v4l2_dev_str : V4L2 device structure + * @bridge_intf : CRM interface + * @flash_init_setting : Init command buffer structure + * @switch_trigger : Switch trigger ptr + * @flash_num_sources : Number of flash sources + * @torch_num_source : Number of torch sources + * @flash_mutex : Mutex for flash operations + * @flash_wq_mutex : Mutex for flash apply setting + * @flash_state : Current flash state (LOW/OFF/ON/INIT) + * @flash_type : Flash types (PMIC/I2C/GPIO) + * @is_regulator_enable : Regulator disable/enable notifier + * @flash_trigger : Flash trigger ptr + * @torch_trigger : Torch trigger ptr + */ +struct cam_flash_ctrl { + struct cam_hw_soc_info soc_info; + struct platform_device *pdev; + struct cam_flash_frame_setting per_frame[MAX_PER_FRAME_ARRAY]; + struct cam_flash_frame_setting nrt_info; + struct device_node *of_node; + struct cam_subdev v4l2_dev_str; + struct cam_flash_intf_params bridge_intf; + struct cam_flash_init_packet flash_init_setting; + struct led_trigger *switch_trigger; + uint32_t flash_num_sources; + uint32_t torch_num_sources; + struct mutex flash_mutex; + struct mutex flash_wq_mutex; + enum cam_flash_state flash_state; + uint8_t flash_type; + bool is_regulator_enabled; + struct led_trigger *flash_trigger[CAM_FLASH_MAX_LED_TRIGGERS]; + struct led_trigger *torch_trigger[CAM_FLASH_MAX_LED_TRIGGERS]; +}; + +#endif /*_CAM_FLASH_DEV_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.c new file mode 100644 index 000000000000..a195762c249f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.c @@ -0,0 +1,225 @@ +/* Copyright (c) 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 +#include +#include "cam_flash_soc.h" +#include "cam_res_mgr_api.h" + +static int32_t cam_get_source_node_info( + struct device_node *of_node, + struct cam_flash_ctrl *fctrl, + struct cam_flash_private_soc *soc_private) +{ + int32_t rc = 0; + uint32_t count = 0, i = 0; + struct device_node *flash_src_node = NULL; + struct device_node *torch_src_node = NULL; + struct device_node *switch_src_node = NULL; + + switch_src_node = of_parse_phandle(of_node, "switch-source", 0); + if (!switch_src_node) { + CAM_DBG(CAM_FLASH, "switch_src_node NULL"); + } else { + rc = of_property_read_string(switch_src_node, + "qcom,default-led-trigger", + &soc_private->switch_trigger_name); + if (rc) { + CAM_ERR(CAM_FLASH, + "default-led-trigger read failed rc=%d", rc); + } else { + CAM_DBG(CAM_FLASH, "switch trigger %s", + soc_private->switch_trigger_name); + cam_res_mgr_led_trigger_register( + soc_private->switch_trigger_name, + &fctrl->switch_trigger); + } + + of_node_put(switch_src_node); + } + + if (of_get_property(of_node, "flash-source", &count)) { + count /= sizeof(uint32_t); + + if (count > CAM_FLASH_MAX_LED_TRIGGERS) { + CAM_ERR(CAM_FLASH, "Invalid LED count: %d", count); + return -EINVAL; + } + + fctrl->flash_num_sources = count; + + for (i = 0; i < count; i++) { + flash_src_node = of_parse_phandle(of_node, + "flash-source", i); + if (!flash_src_node) { + CAM_WARN(CAM_FLASH, "flash_src_node NULL"); + continue; + } + + rc = of_property_read_string(flash_src_node, + "qcom,default-led-trigger", + &soc_private->flash_trigger_name[i]); + if (rc) { + CAM_WARN(CAM_FLASH, + "defalut-led-trigger read failed rc=%d", rc); + of_node_put(flash_src_node); + continue; + } + + CAM_DBG(CAM_FLASH, "default trigger %s", + soc_private->flash_trigger_name[i]); + + /* Read operational-current */ + rc = of_property_read_u32(flash_src_node, + "qcom,current-ma", + &soc_private->flash_op_current[i]); + if (rc) { + CAM_WARN(CAM_FLASH, "op-current: read failed"); + of_node_put(flash_src_node); + continue; + } + + /* Read max-current */ + rc = of_property_read_u32(flash_src_node, + "qcom,max-current", + &soc_private->flash_max_current[i]); + if (rc) { + CAM_WARN(CAM_FLASH, + "max-current: read failed"); + of_node_put(flash_src_node); + continue; + } + + /* Read max-duration */ + rc = of_property_read_u32(flash_src_node, + "qcom,duration-ms", + &soc_private->flash_max_duration[i]); + if (rc) + CAM_WARN(CAM_FLASH, + "max-duration: read failed"); + + of_node_put(flash_src_node); + + CAM_DBG(CAM_FLASH, "max_current[%d]: %d", + i, soc_private->flash_max_current[i]); + + cam_res_mgr_led_trigger_register( + soc_private->flash_trigger_name[i], + &fctrl->flash_trigger[i]); + } + } + + if (of_get_property(of_node, "torch-source", &count)) { + count /= sizeof(uint32_t); + if (count > CAM_FLASH_MAX_LED_TRIGGERS) { + CAM_ERR(CAM_FLASH, "Invalid LED count : %d", count); + return -EINVAL; + } + + fctrl->torch_num_sources = count; + + CAM_DBG(CAM_FLASH, "torch_num_sources = %d", + fctrl->torch_num_sources); + for (i = 0; i < count; i++) { + torch_src_node = of_parse_phandle(of_node, + "torch-source", i); + if (!torch_src_node) { + CAM_WARN(CAM_FLASH, "torch_src_node NULL"); + continue; + } + + rc = of_property_read_string(torch_src_node, + "qcom,default-led-trigger", + &soc_private->torch_trigger_name[i]); + if (rc < 0) { + CAM_WARN(CAM_FLASH, + "default-trigger read failed"); + of_node_put(torch_src_node); + continue; + } + + /* Read operational-current */ + rc = of_property_read_u32(torch_src_node, + "qcom,current-ma", + &soc_private->torch_op_current[i]); + if (rc < 0) { + CAM_WARN(CAM_FLASH, "current: read failed"); + of_node_put(torch_src_node); + continue; + } + + /* Read max-current */ + rc = of_property_read_u32(torch_src_node, + "qcom,max-current", + &soc_private->torch_max_current[i]); + if (rc < 0) { + CAM_WARN(CAM_FLASH, + "max-current: read failed"); + of_node_put(torch_src_node); + continue; + } + + of_node_put(torch_src_node); + + CAM_DBG(CAM_FLASH, "max_current[%d]: %d", + i, soc_private->torch_max_current[i]); + + cam_res_mgr_led_trigger_register( + soc_private->torch_trigger_name[i], + &fctrl->torch_trigger[i]); + } + } + + return rc; +} + +int cam_flash_get_dt_data(struct cam_flash_ctrl *fctrl, + struct cam_hw_soc_info *soc_info) +{ + int32_t rc = 0; + struct device_node *of_node = NULL; + + if (!fctrl) { + CAM_ERR(CAM_FLASH, "NULL flash control structure"); + return -EINVAL; + } + + of_node = fctrl->pdev->dev.of_node; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_FLASH, "Get_dt_properties failed rc %d", rc); + return rc; + } + + soc_info->soc_private = + kzalloc(sizeof(struct cam_flash_private_soc), GFP_KERNEL); + if (!soc_info->soc_private) { + rc = -ENOMEM; + goto release_soc_res; + } + + rc = cam_get_source_node_info(of_node, fctrl, soc_info->soc_private); + if (rc < 0) { + CAM_ERR(CAM_FLASH, + "cam_flash_get_pmic_source_info failed rc %d", rc); + goto free_soc_private; + } + + return rc; + +free_soc_private: + kfree(soc_info->soc_private); +release_soc_res: + cam_soc_util_release_platform_resource(soc_info); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.h new file mode 100644 index 000000000000..2e1da69dd317 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_flash/cam_flash_soc.h @@ -0,0 +1,21 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_FLASH_SOC_H_ +#define _CAM_FLASH_SOC_H_ + +#include "cam_flash_dev.h" + +int cam_flash_get_dt_data(struct cam_flash_ctrl *fctrl, + struct cam_hw_soc_info *soc_info); + +#endif /*_CAM_FLASH_SOC_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/Makefile new file mode 100644 index 000000000000..f10b36a8efa8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/Makefile @@ -0,0 +1,10 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sync +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +#obj-$(CONFIG_SPECTRA_CAMERA) += cam_ir_led_dev.o cam_ir_led_soc.o cam_ir_led_core.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.c new file mode 100644 index 000000000000..7cac1628107a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2019, 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 "cam_ir_led_core.h" + +static int cam_ir_cut_on(struct cam_ir_led_ctrl *ictrl) +{ + if (!ictrl) { + CAM_ERR(CAM_IR_LED, "Ir_led control Null"); + return -EINVAL; + } + + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[4].gpio, 0); + gpio_direction_input( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[3].gpio); + + return 0; +} + +static int cam_ir_cut_off(struct cam_ir_led_ctrl *ictrl) +{ + if (!ictrl) { + CAM_ERR(CAM_IR_LED, "Ir_led control Null"); + return -EINVAL; + } + + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, 0); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[3].gpio, 0); + gpio_direction_input( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[4].gpio); + + return 0; +} + +static int cam_ir_led_set_intensity(struct cam_ir_led_ctrl *ictrl, + uint32_t ir_led_intensity) +{ + CAM_DBG(CAM_IR_LED, "ir_led_intensity=%d", ir_led_intensity); + switch (ir_led_intensity) { + case IRLED_INTENSITY_OFF: + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, + 0); + break; + + case IRLED_INTENSITY_LEVEL1: + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio, + 1); + break; + + case IRLED_INTENSITY_LEVEL2: + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio, + 0); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio, + 1); + break; + case IRLED_INTENSITY_LEVEL3: + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio, + 0); + break; + case IRLED_INTENSITY_LEVEL4: + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, + 1); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio, + 0); + gpio_direction_output( + ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio, + 0); + break; + } + return 0; +} + +int cam_ir_led_parser(struct cam_ir_led_ctrl *ictrl, void *arg) +{ + int rc = 0; + uint32_t *cmd_buf = NULL; + uintptr_t generic_ptr; + uint32_t *offset = NULL; + size_t len_of_buffer; + struct cam_control *ioctl_ctrl = NULL; + struct cam_packet *csl_packet = NULL; + struct cam_config_dev_cmd config; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct cam_ir_led_set_on_off *cam_ir_led_info = NULL; + + if (!ictrl || !arg) { + CAM_ERR(CAM_IR_LED, "ictrl/arg is NULL"); + return -EINVAL; + } + /* getting CSL Packet */ + ioctl_ctrl = (struct cam_control *)arg; + + if (copy_from_user((&config), u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(config))) { + CAM_ERR(CAM_IR_LED, "Copy cmd handle from user failed"); + rc = -EFAULT; + return rc; + } + + rc = cam_mem_get_cpu_buf(config.packet_handle, + (uintptr_t *)&generic_ptr, &len_of_buffer); + if (rc) { + CAM_ERR(CAM_IR_LED, "Failed in getting the buffer : %d", rc); + return rc; + } + + if (config.offset > len_of_buffer) { + CAM_ERR(CAM_IR_LED, + "offset is out of bounds: offset: %lld len: %zu", + config.offset, len_of_buffer); + return -EINVAL; + } + + /* Add offset to the ir_led csl header */ + csl_packet = (struct cam_packet *)(uintptr_t)(generic_ptr + + config.offset); + + offset = (uint32_t *)((uint8_t *)&csl_packet->payload + + csl_packet->cmd_buf_offset); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + (uintptr_t *)&generic_ptr, &len_of_buffer); + if (rc < 0) { + CAM_ERR(CAM_IR_LED, "Failed to get the command Buffer"); + return -EINVAL; + } + + cmd_buf = (uint32_t *)((uint8_t *)generic_ptr + + cmd_desc->offset); + cam_ir_led_info = (struct cam_ir_led_set_on_off *)cmd_buf; + + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_IR_LED_PACKET_OPCODE_ON: + CAM_DBG(CAM_IR_LED, ":CAM_IR_LED_PACKET_OPCODE_ON"); + cam_ir_cut_on(ictrl); + cam_ir_led_set_intensity(ictrl, + cam_ir_led_info->ir_led_intensity); + break; + case CAM_IR_LED_PACKET_OPCODE_OFF: + CAM_DBG(CAM_IR_LED, "CAM_IR_LED_PACKET_OPCODE_OFF"); + cam_ir_cut_off(ictrl); + break; + case CAM_PKT_NOP_OPCODE: + CAM_DBG(CAM_IR_LED, "CAM_IR_LED: CAM_PKT_NOP_OPCODE"); + break; + default: + CAM_ERR(CAM_IR_LED, "Wrong Opcode : %d", + (csl_packet->header.op_code & 0xFFFFFF)); + return -EINVAL; + } + + return 0; +} + +int cam_ir_led_stop_dev(struct cam_ir_led_ctrl *ictrl) +{ + int rc = 0; + + rc = cam_ir_cut_off(ictrl); + + return rc; +} + +int cam_ir_led_release_dev(struct cam_ir_led_ctrl *ictrl) +{ + int rc = 0; + + if (ictrl->device_hdl != -1) { + rc = cam_destroy_device_hdl(ictrl->device_hdl); + if (rc) + CAM_ERR(CAM_IR_LED, + "Failed in destroying device handle rc = %d", + rc); + ictrl->device_hdl = -1; + } + + return rc; +} + +void cam_ir_led_shutdown(struct cam_ir_led_ctrl *ictrl) +{ + int rc; + + if (ictrl->ir_led_state == CAM_IR_LED_STATE_INIT) + return; + + if ((ictrl->ir_led_state == CAM_IR_LED_STATE_CONFIG) || + (ictrl->ir_led_state == CAM_IR_LED_STATE_START)) { + rc = cam_ir_led_stop_dev(ictrl); + if (rc) + CAM_ERR(CAM_IR_LED, "Stop Failed rc: %d", rc); + } + + rc = cam_ir_led_release_dev(ictrl); + if (rc) + CAM_ERR(CAM_IR_LED, "Release failed rc: %d", rc); + + ictrl->ir_led_state = CAM_IR_LED_STATE_INIT; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.h new file mode 100644 index 000000000000..6d50a4068d97 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_core.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2019, 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 _CAM_IR_LED_CORE_H_ +#define _CAM_IR_LED_CORE_H_ +#include "cam_ir_led_dev.h" + +#define IRLED_INTENSITY_OFF 0 +#define IRLED_INTENSITY_LEVEL1 1 +#define IRLED_INTENSITY_LEVEL2 2 +#define IRLED_INTENSITY_LEVEL3 3 +#define IRLED_INTENSITY_LEVEL4 4 +#define IRLED_INTENSITY_MAX 5 + + +int cam_ir_led_parser(struct cam_ir_led_ctrl *fctrl, void *arg); +void cam_ir_led_shutdown(struct cam_ir_led_ctrl *ir_led_ctrl); +int cam_ir_led_stop_dev(struct cam_ir_led_ctrl *ir_led_ctrl); +int cam_ir_led_release_dev(struct cam_ir_led_ctrl *fctrl); +#endif /*_CAM_IR_LED_CORE_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.c new file mode 100644 index 000000000000..d3b28eaf92e9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.c @@ -0,0 +1,383 @@ +/* Copyright (c) 2019, 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 +#include "cam_ir_led_dev.h" +#include "cam_ir_led_soc.h" +#include "cam_ir_led_core.h" + +static int32_t cam_ir_led_driver_cmd(struct cam_ir_led_ctrl *ictrl, + void *arg, struct cam_ir_led_private_soc *soc_private) +{ + int rc = 0; + struct cam_control *cmd = (struct cam_control *)arg; + + if (!ictrl || !arg) { + CAM_ERR(CAM_IR_LED, "ictrl/arg is NULL with arg:%pK ictrl%pK", + ictrl, arg); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_IR_LED, "Invalid handle type: %d", + cmd->handle_type); + return -EINVAL; + } + + mutex_lock(&(ictrl->ir_led_mutex)); + switch (cmd->op_code) { + case CAM_ACQUIRE_DEV: { + struct cam_sensor_acquire_dev ir_led_acq_dev; + struct cam_create_dev_hdl dev_hdl; + + CAM_DBG(CAM_IR_LED, "CAM_ACQUIRE_DEV"); + + if (ictrl->ir_led_state != CAM_IR_LED_STATE_INIT) { + CAM_ERR(CAM_IR_LED, + " Cannot apply Acquire dev: Prev state: %d", + ictrl->ir_led_state); + rc = -EINVAL; + goto release_mutex; + } + + rc = copy_from_user(&ir_led_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(ir_led_acq_dev)); + if (rc) { + CAM_ERR(CAM_IR_LED, "Failed Copy from User rc=%d", rc); + goto release_mutex; + } + + dev_hdl.priv = ictrl; + + ir_led_acq_dev.device_handle = + cam_create_device_hdl(&dev_hdl); + ictrl->device_hdl = + ir_led_acq_dev.device_handle; + + rc = copy_to_user(u64_to_user_ptr(cmd->handle), &ir_led_acq_dev, + sizeof(struct cam_sensor_acquire_dev)); + if (rc) { + CAM_ERR(CAM_IR_LED, "Failed Copy to User rc=%d", rc); + rc = -EFAULT; + goto release_mutex; + } + ictrl->ir_led_state = CAM_IR_LED_STATE_ACQUIRE; + break; + } + case CAM_RELEASE_DEV: { + CAM_DBG(CAM_IR_LED, "CAM_RELEASE_DEV"); + if ((ictrl->ir_led_state == CAM_IR_LED_STATE_INIT) || + (ictrl->ir_led_state == CAM_IR_LED_STATE_START)) { + CAM_WARN(CAM_IR_LED, + " Cannot apply Release dev: Prev state:%d", + ictrl->ir_led_state); + } + + if (ictrl->device_hdl == -1 && + ictrl->ir_led_state == CAM_IR_LED_STATE_ACQUIRE) { + CAM_ERR(CAM_IR_LED, + " Invalid Handle: device hdl: %d", + ictrl->device_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_ir_led_release_dev(ictrl); + if (rc) + CAM_ERR(CAM_IR_LED, + " Failed in destroying the device Handle rc= %d", + rc); + ictrl->ir_led_state = CAM_IR_LED_STATE_INIT; + break; + } + case CAM_QUERY_CAP: { + struct cam_ir_led_query_cap_info ir_led_cap = {0}; + + CAM_DBG(CAM_IR_LED, "CAM_QUERY_CAP"); + ir_led_cap.slot_info = ictrl->soc_info.index; + + if (copy_to_user(u64_to_user_ptr(cmd->handle), &ir_led_cap, + sizeof(struct cam_ir_led_query_cap_info))) { + CAM_ERR(CAM_IR_LED, " Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + break; + } + case CAM_START_DEV: { + CAM_DBG(CAM_IR_LED, "CAM_START_DEV"); + if ((ictrl->ir_led_state == CAM_IR_LED_STATE_INIT) || + (ictrl->ir_led_state == CAM_IR_LED_STATE_START)) { + CAM_ERR(CAM_IR_LED, + "Cannot apply Start Dev: Prev state: %d", + ictrl->ir_led_state); + rc = -EINVAL; + goto release_mutex; + } + ictrl->ir_led_state = CAM_IR_LED_STATE_START; + + break; + } + case CAM_STOP_DEV: { + CAM_DBG(CAM_IR_LED, "CAM_STOP_DEV ENTER"); + if (ictrl->ir_led_state != CAM_IR_LED_STATE_START) { + CAM_WARN(CAM_IR_LED, + " Cannot apply Stop dev: Prev state is: %d", + ictrl->ir_led_state); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_ir_led_stop_dev(ictrl); + if (rc) { + CAM_ERR(CAM_IR_LED, "Failed STOP_DEV: rc=%d\n", rc); + goto release_mutex; + } + ictrl->ir_led_state = CAM_IR_LED_STATE_ACQUIRE; + break; + } + case CAM_CONFIG_DEV: { + CAM_DBG(CAM_IR_LED, "CAM_CONFIG_DEV"); + rc = cam_ir_led_parser(ictrl, arg); + if (rc) { + CAM_ERR(CAM_IR_LED, "Failed CONFIG_DEV: rc=%d\n", rc); + goto release_mutex; + } + break; + } + default: + CAM_ERR(CAM_IR_LED, "Invalid Opcode: %d", cmd->op_code); + rc = -EINVAL; + } + +release_mutex: + mutex_unlock(&(ictrl->ir_led_mutex)); + return rc; +} + +static const struct of_device_id cam_ir_led_dt_match[] = { + {.compatible = "qcom,camera-ir-led", .data = NULL}, + {} +}; + +static long cam_ir_led_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_ir_led_ctrl *ictrl = NULL; + struct cam_ir_led_private_soc *soc_private = NULL; + + CAM_DBG(CAM_IR_LED, "Enter"); + + ictrl = v4l2_get_subdevdata(sd); + soc_private = ictrl->soc_info.soc_private; + + switch (cmd) { + case VIDIOC_CAM_CONTROL: { + rc = cam_ir_led_driver_cmd(ictrl, arg, + soc_private); + break; + } + default: + CAM_ERR(CAM_IR_LED, " Invalid ioctl cmd type"); + rc = -EINVAL; + break; + } + + CAM_DBG(CAM_IR_LED, "Exit"); + return rc; +} + +#ifdef CONFIG_COMPAT +static long cam_ir_led_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_IR_LED, + " Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: { + rc = cam_ir_led_subdev_ioctl(sd, cmd, &cmd_data); + if (rc) + CAM_ERR(CAM_IR_LED, "cam_ir_led_ioctl failed"); + break; + } + default: + CAM_ERR(CAM_IR_LED, " Invalid compat ioctl cmd_type:%d", + cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_IR_LED, + " Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + +static int cam_ir_led_platform_remove(struct platform_device *pdev) +{ + struct cam_ir_led_ctrl *ictrl; + + ictrl = platform_get_drvdata(pdev); + if (!ictrl) { + CAM_ERR(CAM_IR_LED, " Ir_led device is NULL"); + return 0; + } + + devm_kfree(&pdev->dev, ictrl); + + return 0; +} + +static int cam_ir_led_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_ir_led_ctrl *ir_led_ctrl = + v4l2_get_subdevdata(sd); + + if (!ir_led_ctrl) { + CAM_ERR(CAM_IR_LED, " Ir_led ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&ir_led_ctrl->ir_led_mutex); + cam_ir_led_shutdown(ir_led_ctrl); + mutex_unlock(&ir_led_ctrl->ir_led_mutex); + + return 0; +} + +static struct v4l2_subdev_core_ops cam_ir_led_subdev_core_ops = { + .ioctl = cam_ir_led_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_ir_led_subdev_do_ioctl +#endif +}; + +static struct v4l2_subdev_ops cam_ir_led_subdev_ops = { + .core = &cam_ir_led_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cam_ir_led_internal_ops = { + .close = cam_ir_led_subdev_close, +}; + +static int32_t cam_ir_led_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct cam_ir_led_ctrl *ir_led_ctrl = NULL; + + CAM_ERR(CAM_IR_LED, "DBG:Enter"); + if (!pdev->dev.of_node) { + CAM_ERR(CAM_IR_LED, "of_node NULL"); + return -EINVAL; + } + + ir_led_ctrl = kzalloc(sizeof(struct cam_ir_led_ctrl), GFP_KERNEL); + if (!ir_led_ctrl) + return -ENOMEM; + + ir_led_ctrl->pdev = pdev; + ir_led_ctrl->soc_info.pdev = pdev; + ir_led_ctrl->soc_info.dev = &pdev->dev; + ir_led_ctrl->soc_info.dev_name = pdev->name; + + rc = cam_ir_led_get_dt_data(ir_led_ctrl, &ir_led_ctrl->soc_info); + if (rc) { + CAM_ERR(CAM_IR_LED, "cam_ir_led_get_dt_data failed rc=%d", rc); + if (ir_led_ctrl->soc_info.soc_private != NULL) { + kfree(ir_led_ctrl->soc_info.soc_private); + ir_led_ctrl->soc_info.soc_private = NULL; + } + kfree(ir_led_ctrl); + ir_led_ctrl = NULL; + return -EINVAL; + } + + ir_led_ctrl->v4l2_dev_str.internal_ops = + &cam_ir_led_internal_ops; + ir_led_ctrl->v4l2_dev_str.ops = &cam_ir_led_subdev_ops; + ir_led_ctrl->v4l2_dev_str.name = CAMX_IR_LED_DEV_NAME; + ir_led_ctrl->v4l2_dev_str.sd_flags = + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + ir_led_ctrl->v4l2_dev_str.ent_function = CAM_IRLED_DEVICE_TYPE; + ir_led_ctrl->v4l2_dev_str.token = ir_led_ctrl; + + rc = cam_register_subdev(&(ir_led_ctrl->v4l2_dev_str)); + if (rc) { + CAM_ERR(CAM_IR_LED, "Fail to create subdev with %d", rc); + goto free_resource; + } + ir_led_ctrl->device_hdl = -1; + + platform_set_drvdata(pdev, ir_led_ctrl); + v4l2_set_subdevdata(&ir_led_ctrl->v4l2_dev_str.sd, ir_led_ctrl); + + mutex_init(&(ir_led_ctrl->ir_led_mutex)); + + ir_led_ctrl->ir_led_state = CAM_IR_LED_STATE_INIT; + CAM_ERR(CAM_IR_LED, "DBG:Probe success"); + return rc; +free_resource: + kfree(ir_led_ctrl); + return rc; +} + +MODULE_DEVICE_TABLE(of, cam_ir_led_dt_match); + +static struct platform_driver cam_ir_led_platform_driver = { + .probe = cam_ir_led_platform_probe, + .remove = cam_ir_led_platform_remove, + .driver = { + .name = "CAM-IR-LED-DRIVER", + .owner = THIS_MODULE, + .of_match_table = cam_ir_led_dt_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init cam_ir_led_init_module(void) +{ + int32_t rc = 0; + + rc = platform_driver_register(&cam_ir_led_platform_driver); + if (rc) + CAM_ERR(CAM_IR_LED, "platform probe for ir_led failed"); + + return rc; +} + +static void __exit cam_ir_led_exit_module(void) +{ + platform_driver_unregister(&cam_ir_led_platform_driver); +} + +module_init(cam_ir_led_init_module); +module_exit(cam_ir_led_exit_module); +MODULE_DESCRIPTION("CAM IR_LED"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.h new file mode 100644 index 000000000000..b462adbd4d43 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_dev.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2019, 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 _CAM_IR_LED_DEV_H_ +#define _CAM_IR_LED_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_req_mgr_util.h" +#include "cam_req_mgr_interface.h" +#include "cam_subdev.h" +#include "cam_mem_mgr.h" +#include "cam_sensor_cmn_header.h" +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define CAMX_IR_LED_DEV_NAME "cam-ir-led-dev" + +#define CAM_IR_LED_PIPELINE_DELAY 1 + +#define CAM_IR_LED_PACKET_OPCODE_OFF 0 +#define CAM_IR_LED_PACKET_OPCODE_ON 1 + +enum cam_ir_led_switch_trigger_ops { + LED_SWITCH_OFF = 0, + LED_SWITCH_ON, +}; + +enum cam_ir_led_state { + CAM_IR_LED_STATE_INIT = 0, + CAM_IR_LED_STATE_ACQUIRE, + CAM_IR_LED_STATE_CONFIG, + CAM_IR_LED_STATE_START, +}; + +/** + * struct cam_ir_led_intf_params + * @device_hdl : Device Handle + * @session_hdl : Session Handle + * @link_hdl : Link Handle + * @ops : KMD operations + * @crm_cb : Callback API pointers + */ +struct cam_ir_led_intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_ir_led_common_attr + * @is_settings_valid : Notify the valid settings + * @request_id : Request id provided by umd + * @count : Number of led count + * @cmd_type : Command buffer type + */ +struct cam_ir_led_common_attr { + bool is_settings_valid; + uint64_t request_id; + uint16_t count; + uint8_t cmd_type; +}; + +/** + * struct ir_led_init_packet + * @cmn_attr : Provides common attributes + * @ir_led_type : Ir_led type(PMIC/I2C/GPIO) + */ +struct cam_ir_led_init_packet { + struct cam_ir_led_common_attr cmn_attr; + uint8_t ir_led_type; +}; + +/** + * struct cam_ir_led_private_soc + * @switch_trigger_name : Switch trigger name + * @ir_led_trigger_name : Ir_led trigger name array + * @ir_led_op_current : Ir_led operational current + * @ir_led_max_current : Max supported current for LED in ir_led mode + * @ir_led_max_duration : Max turn on duration for LED in Ir_led mode + * @torch_trigger_name : Torch trigger name array + * @torch_op_current : Torch operational current + * @torch_max_current : Max supported current for LED in torch mode + */ + +struct cam_ir_led_private_soc { + const char *switch_trigger_name; + const char *ir_led_trigger_name; + uint32_t ir_led_op_current; + uint32_t ir_led_max_current; + uint32_t ir_led_max_duration; + const char *torch_trigger_name; + uint32_t torch_op_current; + uint32_t torch_max_current; +}; + +/** + * struct cam_ir_led_ctrl + * @soc_info : Soc related information + * @pdev : Platform device + * @of_node : Of Node ptr + * @v4l2_dev_str : V4L2 device structure + * @ir_led_mutex : Mutex for ir_led operations + * @ir_led_state : Current ir_led state (LOW/OFF/ON/INIT) + * @device_hdl : Device Handle + */ +struct cam_ir_led_ctrl { + struct cam_hw_soc_info soc_info; + struct platform_device *pdev; + struct device_node *of_node; + struct cam_subdev v4l2_dev_str; + struct mutex ir_led_mutex; + enum cam_ir_led_state ir_led_state; + int32_t device_hdl; +}; + +#endif /*_CAM_IR_LED_DEV_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.c new file mode 100644 index 000000000000..65b53614cc90 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2019, 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 +#include +#include "cam_ir_led_soc.h" +#include "cam_res_mgr_api.h" + +int cam_ir_led_get_dt_data(struct cam_ir_led_ctrl *ictrl, + struct cam_hw_soc_info *soc_info) +{ + int32_t rc = 0; + + if (!ictrl) { + CAM_ERR(CAM_IR_LED, "NULL ir_led control structure"); + return -EINVAL; + } + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_IR_LED, "get_dt_properties failed rc %d", rc); + return rc; + } + + soc_info->soc_private = + kzalloc(sizeof(struct cam_ir_led_private_soc), GFP_KERNEL); + if (!soc_info->soc_private) { + rc = -ENOMEM; + goto release_soc_res; + } + + return rc; + +release_soc_res: + cam_soc_util_release_platform_resource(soc_info); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.h new file mode 100644 index 000000000000..3a9139ae2cc1 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ir_led/cam_ir_led_soc.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2019, 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 _CAM_IR_LED_SOC_H_ +#define _CAM_IR_LED_SOC_H_ + +#include "cam_ir_led_dev.h" + +int cam_ir_led_get_dt_data(struct cam_ir_led_ctrl *fctrl, + struct cam_hw_soc_info *soc_info); + +#endif /*_CAM_IR_LED_SOC_H_*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/Makefile new file mode 100644 index 000000000000..436836a4c67f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/Makefile @@ -0,0 +1,10 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois_dev.o cam_ois_core.o cam_ois_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/ROHM_BU24218GWL_OIS.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/ROHM_BU24218GWL_OIS.h new file mode 100644 index 000000000000..ff03f48d53c8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/ROHM_BU24218GWL_OIS.h @@ -0,0 +1,2206 @@ +.start_download = { +.reg_settings = { +{.reg_addr = 0xF010, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 1, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 1, +}, +.firmware = { +.reg_settings = { +{.reg_addr = 0x0000, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0001, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0002, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0003, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0004, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0005, .reg_data = 0x85, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0006, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0007, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0008, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0009, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000A, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000B, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000C, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000E, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x000F, .reg_data = 0x67, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0010, .reg_data = 0x4F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0011, .reg_data = 0x67, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0012, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0013, .reg_data = 0x67, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0014, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0015, .reg_data = 0x67, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0016, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0017, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0018, .reg_data = 0x37, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0019, .reg_data = 0xB4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001A, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001B, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001C, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001D, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001E, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x001F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0020, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0021, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0022, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0023, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0024, .reg_data = 0xF9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0025, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0026, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0027, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0028, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0029, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002A, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002B, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002C, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002D, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002E, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x002F, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0030, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0031, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0032, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0033, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0034, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0035, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0036, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0037, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0038, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0039, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003A, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003B, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003C, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003E, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x003F, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0040, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0041, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0042, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0043, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0044, .reg_data = 0xF9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0045, .reg_data = 0x1F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0046, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0047, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0048, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0049, .reg_data = 0x5A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004A, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004B, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004C, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004D, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004E, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x004F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0050, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0051, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0052, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0053, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0054, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0055, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0056, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0057, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0058, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0059, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005C, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005D, .reg_data = 0x56, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005E, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x005F, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0060, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0061, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0062, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0063, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0064, .reg_data = 0xDD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0065, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0066, .reg_data = 0x2E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0067, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0068, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0069, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006A, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006B, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006C, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006D, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x006F, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0070, .reg_data = 0xDA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0071, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0072, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0073, .reg_data = 0x86, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0074, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0075, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0076, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0077, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0078, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0079, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007A, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007B, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007C, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007E, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x007F, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0080, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0081, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0082, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0083, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0084, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0085, .reg_data = 0x8B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0086, .reg_data = 0xFA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0087, .reg_data = 0xAF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0088, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0089, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008A, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008B, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008C, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008E, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x008F, .reg_data = 0x3B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0090, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0091, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0092, .reg_data = 0xD8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0093, .reg_data = 0x17, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0094, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0095, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0096, .reg_data = 0x55, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0097, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0098, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0099, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009A, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009B, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009C, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009D, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009E, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x009F, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A0, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A1, .reg_data = 0x3B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A2, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A3, .reg_data = 0x8C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A4, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A5, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A6, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A7, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A8, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00A9, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AA, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AB, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AC, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AD, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AE, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00AF, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B0, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B1, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B2, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B3, .reg_data = 0x76, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B4, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B5, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B6, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B7, .reg_data = 0xA3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B8, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00B9, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BA, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BB, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BC, .reg_data = 0xFA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BD, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BE, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00BF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C0, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C1, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C2, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C3, .reg_data = 0x6E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C4, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C5, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C6, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C7, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C8, .reg_data = 0xB2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00C9, .reg_data = 0xCD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CA, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CB, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CC, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CE, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00CF, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D0, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D1, .reg_data = 0x3A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D2, .reg_data = 0xFA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D3, .reg_data = 0xBB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D4, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D5, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D6, .reg_data = 0x55, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D7, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D8, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00D9, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DA, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DB, .reg_data = 0x36, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DC, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DD, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DE, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00DF, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E0, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E2, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E3, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E4, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E5, .reg_data = 0xC5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E6, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E7, .reg_data = 0x73, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E8, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00E9, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00EA, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00EB, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00EC, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00ED, .reg_data = 0x57, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00EE, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00EF, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F0, .reg_data = 0xFA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F1, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F2, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F5, .reg_data = 0xA1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F6, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F8, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00F9, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FA, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FB, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FC, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FD, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FE, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x00FF, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0100, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0101, .reg_data = 0x89, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0102, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0103, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0104, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0105, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0106, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0107, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0108, .reg_data = 0xDB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0109, .reg_data = 0xF4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010A, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010B, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010D, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010E, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x010F, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0110, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0111, .reg_data = 0x2B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0112, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0113, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0114, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0115, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0116, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0117, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0118, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0119, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011A, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011B, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011C, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011D, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011E, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x011F, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0120, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0121, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0122, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0123, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0124, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0125, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0126, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0127, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0128, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0129, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012A, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012B, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012C, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012D, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x012F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0130, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0131, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0132, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0133, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0134, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0135, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0136, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0137, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0138, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0139, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013A, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013B, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013C, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013D, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013E, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x013F, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0140, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0141, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0142, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0143, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0144, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0145, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0146, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0147, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0148, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0149, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014B, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014C, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014D, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014E, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x014F, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0150, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0151, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0152, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0153, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0154, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0155, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0156, .reg_data = 0x9A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0157, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0158, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0159, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015B, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015C, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015D, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015E, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x015F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0160, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0161, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0162, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0163, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0164, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0165, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0166, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0167, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0168, .reg_data = 0x4F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0169, .reg_data = 0x16, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016A, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016B, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016C, .reg_data = 0x37, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016D, .reg_data = 0xB4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016E, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x016F, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0170, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0171, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0172, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0173, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0174, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0175, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0176, .reg_data = 0x9A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0177, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0178, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0179, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017B, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017C, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017E, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x017F, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0180, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0181, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0182, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0183, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0184, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0185, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0186, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0187, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0188, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0189, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018A, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018D, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018E, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x018F, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0190, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0191, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0192, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0193, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0194, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0195, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0196, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0197, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0198, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0199, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019B, .reg_data = 0x1F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019C, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019D, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x019F, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A0, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A1, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A2, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A3, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A6, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A7, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01A9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AA, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AB, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AE, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01AF, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B2, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B3, .reg_data = 0xD9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B4, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B7, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01B9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BA, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BB, .reg_data = 0xAA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BC, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BE, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01BF, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C0, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C2, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C6, .reg_data = 0x2B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C7, .reg_data = 0x25, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C8, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01C9, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CA, .reg_data = 0xBA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CB, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CC, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CD, .reg_data = 0x86, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CE, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01CF, .reg_data = 0xF1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D0, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D1, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D2, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D3, .reg_data = 0x5F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D4, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D5, .reg_data = 0x5F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D6, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D7, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D8, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01D9, .reg_data = 0x7D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DA, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DC, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DD, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DE, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01DF, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E0, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E1, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E2, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E3, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E5, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E6, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E7, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E8, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01E9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01EA, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01EB, .reg_data = 0x8C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01EC, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01ED, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01EE, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01EF, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F0, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F2, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F4, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F5, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F6, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F7, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F8, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01F9, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FA, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FB, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FC, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FE, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x01FF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0200, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0201, .reg_data = 0x55, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0202, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0203, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0204, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0205, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0206, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0207, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0208, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0209, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020A, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020B, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020C, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020D, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020E, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020F, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0210, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0211, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0212, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0213, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0214, .reg_data = 0xD5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0215, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0216, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0217, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0218, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0219, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021A, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021B, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021C, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021E, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x021F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0220, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0221, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0222, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0223, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0224, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0225, .reg_data = 0x45, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0226, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0227, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0228, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0229, .reg_data = 0x6D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022A, .reg_data = 0xD5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022C, .reg_data = 0xDC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022E, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x022F, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0230, .reg_data = 0xDD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0231, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0232, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0233, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0234, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0235, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0236, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0237, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0238, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0239, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023A, .reg_data = 0x1E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023B, .reg_data = 0x76, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023E, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x023F, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0240, .reg_data = 0x1E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0241, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0242, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0243, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0244, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0245, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0246, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0247, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0248, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0249, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024A, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024B, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024C, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024E, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x024F, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0250, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0251, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0252, .reg_data = 0x77, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0253, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0254, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0255, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0256, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0257, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0258, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0259, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025A, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025B, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025C, .reg_data = 0xDD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025D, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025E, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x025F, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0260, .reg_data = 0xDD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0261, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0262, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0263, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0264, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0265, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0266, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0267, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0268, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0269, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026C, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x026F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0270, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0271, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0272, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0273, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0274, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0275, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0276, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0277, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0278, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0279, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027A, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027C, .reg_data = 0x76, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027D, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027E, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x027F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0280, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0281, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0282, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0283, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0284, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0285, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0286, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0287, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0288, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0289, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028A, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028B, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028D, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028E, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x028F, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0290, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0291, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0292, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0293, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0294, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0295, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0296, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0297, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0298, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0299, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029A, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029B, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029C, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029D, .reg_data = 0x89, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029E, .reg_data = 0x4E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x029F, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A0, .reg_data = 0x36, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A1, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A2, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A3, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A4, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A6, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A8, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02A9, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AA, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AB, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AC, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AD, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AE, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02AF, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B0, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B1, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B2, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B3, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B4, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B5, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B6, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B7, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B8, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02B9, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BA, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BB, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BC, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BD, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BE, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02BF, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C0, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C1, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C2, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C3, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C4, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C5, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C6, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C7, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C8, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02C9, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CA, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CB, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CC, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CD, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CE, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02CF, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D0, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D1, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D2, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D3, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D4, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D5, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D6, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D7, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D8, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02D9, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DA, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DB, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DC, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DD, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DE, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02DF, .reg_data = 0xEC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E0, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E1, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E2, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E3, .reg_data = 0xF1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E4, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E5, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E6, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E7, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E8, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02E9, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02EA, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02EB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02EC, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02ED, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02EE, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02EF, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F0, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F2, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F3, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F4, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F5, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F6, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F7, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F8, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02F9, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FA, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FB, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FC, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FD, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FE, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x02FF, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0300, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0301, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0302, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0303, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0304, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0305, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0306, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0307, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0308, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0309, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030A, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030B, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030C, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030D, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030E, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0310, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0311, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0312, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0313, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0314, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0315, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0316, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0317, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0318, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0319, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031A, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031B, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031C, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031E, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x031F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0320, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0321, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0322, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0323, .reg_data = 0x6E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0324, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0325, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0326, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0327, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0328, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0329, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032A, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032D, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x032F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0330, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0331, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0332, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0333, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0334, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0335, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0336, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0337, .reg_data = 0x3A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0338, .reg_data = 0xDB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0339, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033A, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033B, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033C, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033E, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x033F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0340, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0341, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0342, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0343, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0344, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0345, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0346, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0347, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0348, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0349, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034A, .reg_data = 0xDB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034B, .reg_data = 0xEE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034C, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034D, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034E, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034F, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0350, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0351, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0352, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0353, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0354, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0355, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0356, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0357, .reg_data = 0xCD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0358, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0359, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035A, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035B, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035C, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035E, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x035F, .reg_data = 0xBC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0360, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0361, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0362, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0363, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0364, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0365, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0366, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0367, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0368, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0369, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036A, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036B, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036E, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x036F, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0370, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0371, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0372, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0373, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0374, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0375, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0376, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0377, .reg_data = 0xE4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0378, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0379, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037A, .reg_data = 0xDC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037B, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037C, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037D, .reg_data = 0x5B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037E, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x037F, .reg_data = 0x9B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0380, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0381, .reg_data = 0x9B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0382, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0383, .reg_data = 0x9B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0384, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0385, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0386, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0387, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0388, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0389, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038A, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038B, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038C, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038D, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038E, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x038F, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0390, .reg_data = 0x4E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0391, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0392, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0393, .reg_data = 0x9D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0394, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0395, .reg_data = 0xAE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0396, .reg_data = 0x59, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0397, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0398, .reg_data = 0x59, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0399, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039A, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039B, .reg_data = 0x37, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039C, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039D, .reg_data = 0x1E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039E, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x039F, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A0, .reg_data = 0x25, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A1, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A2, .reg_data = 0x36, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A3, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A4, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A5, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A6, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A7, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A8, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03A9, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AA, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AB, .reg_data = 0x5B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AC, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AD, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AE, .reg_data = 0xDB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03AF, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B0, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B1, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B2, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B3, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B4, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B5, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B6, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B7, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B8, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03B9, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BA, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BC, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03BF, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C0, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C1, .reg_data = 0x79, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C2, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C3, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C4, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C5, .reg_data = 0xC2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C6, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C7, .reg_data = 0x85, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C8, .reg_data = 0x4B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03C9, .reg_data = 0x77, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CA, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CB, .reg_data = 0x77, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CC, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CD, .reg_data = 0xA4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CE, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03CF, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D0, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D1, .reg_data = 0x75, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D2, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D3, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D4, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D5, .reg_data = 0x75, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D6, .reg_data = 0x94, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D7, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D8, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03D9, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DA, .reg_data = 0x33, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DB, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DC, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DD, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DE, .reg_data = 0x4F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03DF, .reg_data = 0x73, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E0, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E1, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E2, .reg_data = 0x4E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E3, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E4, .reg_data = 0x37, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E5, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E6, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E7, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E8, .reg_data = 0x2B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03E9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03EA, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03EB, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03EC, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03ED, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03EE, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03EF, .reg_data = 0x74, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F0, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F1, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F2, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F4, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F6, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F7, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F8, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03F9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FA, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FB, .reg_data = 0x89, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FC, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FE, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x03FF, .reg_data = 0x6C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0400, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0401, .reg_data = 0x39, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0402, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0403, .reg_data = 0x6C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0404, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0405, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0406, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0407, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0408, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0409, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040A, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040B, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040C, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040D, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040E, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040F, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0410, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0411, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0412, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0413, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0414, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0415, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0416, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0417, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0418, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0419, .reg_data = 0xEA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041B, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041C, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041D, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041E, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x041F, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0420, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0421, .reg_data = 0x59, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0422, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0423, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0424, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0425, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0426, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0427, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0428, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0429, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042A, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042C, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042D, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042E, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x042F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0430, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0431, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0432, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0433, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0434, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0435, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0436, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0437, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0438, .reg_data = 0x1E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0439, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043A, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043B, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043C, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043D, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043E, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x043F, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0440, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0441, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0442, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0443, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0444, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0445, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0446, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0447, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0448, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0449, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044A, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044B, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044C, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044D, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x044F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0450, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0451, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0452, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0453, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0454, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0455, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0456, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0457, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0458, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0459, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045A, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045C, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045E, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x045F, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0460, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0461, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0462, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0463, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0464, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0465, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0466, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0467, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0468, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0469, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046B, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046C, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046D, .reg_data = 0xF2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046E, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x046F, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0470, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0471, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0472, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0473, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0474, .reg_data = 0x1E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0475, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0476, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0477, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0478, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0479, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047A, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047C, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047D, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x047F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0480, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0481, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0482, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0483, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0484, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0485, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0486, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0487, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0488, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0489, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048B, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048C, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048E, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x048F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0490, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0491, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0492, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0493, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0494, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0495, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0496, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0497, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0498, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0499, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049B, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049C, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049E, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x049F, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A0, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A1, .reg_data = 0x89, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A2, .reg_data = 0x4B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A3, .reg_data = 0x45, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A4, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A5, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A6, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A7, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A8, .reg_data = 0x1D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04A9, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AA, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AB, .reg_data = 0x4B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AC, .reg_data = 0x4B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AD, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AE, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04AF, .reg_data = 0x8B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B0, .reg_data = 0x4E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B1, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B2, .reg_data = 0x63, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B3, .reg_data = 0xCB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B4, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B5, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B6, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B7, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B8, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04B9, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BA, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BB, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BC, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BD, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BE, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04BF, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C0, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C1, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C2, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C3, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C4, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C5, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C6, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C7, .reg_data = 0xF2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C8, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04C9, .reg_data = 0xB2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CA, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CB, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CC, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CD, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CE, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04CF, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D0, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D1, .reg_data = 0x1F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D2, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D3, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D4, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D5, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D6, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D7, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D8, .reg_data = 0x7B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04D9, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DA, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DB, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DC, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DD, .reg_data = 0x52, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DE, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04DF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E0, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E1, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E2, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E3, .reg_data = 0x33, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E4, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E5, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E6, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E7, .reg_data = 0xC2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E8, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04E9, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04EA, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04EB, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04EC, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04ED, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04EE, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04EF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F0, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F1, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F2, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F3, .reg_data = 0x94, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F4, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F5, .reg_data = 0x2E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F6, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F7, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F8, .reg_data = 0x35, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04F9, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FA, .reg_data = 0xB2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FB, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FC, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FE, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x04FF, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0500, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0501, .reg_data = 0x5C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0502, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0503, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0504, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0505, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0506, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0507, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0508, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0509, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050A, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050C, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050E, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x050F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0510, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0511, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0512, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0513, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0514, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0515, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0516, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0517, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0518, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0519, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051A, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051B, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051C, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051E, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x051F, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0520, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0521, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0522, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0523, .reg_data = 0x41, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0524, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0525, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0526, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0527, .reg_data = 0x67, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0528, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0529, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052A, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052B, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052C, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052E, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x052F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0530, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0531, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0532, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0533, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0534, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0535, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0536, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0537, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0538, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0539, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053A, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053B, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053C, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053D, .reg_data = 0x6D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053E, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x053F, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0540, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0541, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0542, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0543, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0544, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0545, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0546, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0547, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0548, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0549, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054A, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054B, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054C, .reg_data = 0x9A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054E, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x054F, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0550, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0551, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0552, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0553, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0554, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0555, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0556, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0557, .reg_data = 0xE2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0558, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0559, .reg_data = 0x52, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055A, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055B, .reg_data = 0xCA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055C, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055D, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055E, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x055F, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0560, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0561, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0562, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0563, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0564, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0565, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0566, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0567, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0568, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0569, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056A, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056B, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056C, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056E, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x056F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0570, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0571, .reg_data = 0xA3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0572, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0573, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0574, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0575, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0576, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0577, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0578, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0579, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057A, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057C, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057D, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057E, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x057F, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0580, .reg_data = 0xF7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0581, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0582, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0583, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0584, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0585, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0586, .reg_data = 0xFE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0587, .reg_data = 0xF5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0588, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0589, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058A, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058B, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058C, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058D, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058E, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x058F, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0590, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0591, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0592, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0593, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0594, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0595, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0596, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0597, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0598, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0599, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059A, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059B, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059C, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059D, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059E, .reg_data = 0xB2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x059F, .reg_data = 0x8B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A0, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A1, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A2, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A3, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A4, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A5, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A6, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A7, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A8, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05A9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AA, .reg_data = 0x17, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AB, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AE, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05AF, .reg_data = 0xCD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B0, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B2, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B3, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B4, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B6, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B7, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05B9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BA, .reg_data = 0x61, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BB, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BE, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05BF, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C2, .reg_data = 0x53, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C3, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C4, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C7, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C8, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05C9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CA, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CC, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CE, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05CF, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D0, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D3, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D4, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D5, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D6, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D7, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D8, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05D9, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DA, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DB, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DC, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DE, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05DF, .reg_data = 0xC1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E0, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E2, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E3, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E4, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E5, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E6, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E7, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E8, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05E9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05EA, .reg_data = 0x4B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05EB, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05EC, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05ED, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05EE, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05EF, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F0, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F1, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F2, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F3, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F4, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F5, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F6, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F7, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F8, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05F9, .reg_data = 0x8A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FA, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FB, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FC, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FD, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FE, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x05FF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0600, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0601, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0602, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0603, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0604, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0605, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0606, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0607, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0608, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0609, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060A, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060B, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060C, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060E, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x060F, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0610, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0611, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0612, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0613, .reg_data = 0xFE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0614, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0615, .reg_data = 0x16, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0616, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0617, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0618, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0619, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061A, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061B, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061C, .reg_data = 0xF7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061D, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061E, .reg_data = 0x5A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x061F, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0620, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0621, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0622, .reg_data = 0xFD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0623, .reg_data = 0xD3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0624, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0625, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0626, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0627, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0628, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0629, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062B, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062C, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062E, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x062F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0630, .reg_data = 0xA9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0631, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0632, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0633, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0634, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0635, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0636, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0637, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0638, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0639, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063A, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063B, .reg_data = 0xFE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063E, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x063F, .reg_data = 0xCD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0640, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0641, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0642, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0643, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0644, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0645, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0646, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0647, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0648, .reg_data = 0x49, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0649, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064A, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064B, .reg_data = 0x7C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064C, .reg_data = 0x4E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064D, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064E, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x064F, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0650, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0651, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0652, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0653, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0654, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0655, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0656, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0657, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0658, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0659, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065A, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065B, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065E, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x065F, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0660, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0661, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0662, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0663, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0664, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0665, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0666, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0667, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0668, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0669, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066B, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066C, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066D, .reg_data = 0xFC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066E, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x066F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0670, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0671, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0672, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0673, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0674, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0675, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0676, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0677, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0678, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0679, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067A, .reg_data = 0xBD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067B, .reg_data = 0x7C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067C, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067D, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067E, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x067F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0680, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0681, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0682, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0683, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0684, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0685, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0686, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0687, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0688, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0689, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068A, .reg_data = 0x47, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068B, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068C, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068D, .reg_data = 0xFC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068E, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x068F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0690, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0691, .reg_data = 0xEF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0692, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0693, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0694, .reg_data = 0xE7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0695, .reg_data = 0xEE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0696, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0697, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0698, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0699, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069A, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069B, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069E, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x069F, .reg_data = 0xB5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A2, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A3, .reg_data = 0xF4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A4, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A6, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A7, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06A9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06AF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06B9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06BF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06C9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06CA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x06CB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 1740, //1096//1097//1483 +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 0, +}, +.init_setting = { +.reg_settings = { +{.reg_addr = 0x1C00, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C01, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C02, .reg_data = 0x77, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C03, .reg_data = 0x7E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C04, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C05, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C06, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C07, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C08, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C09, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C0F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C10, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C11, .reg_data = 0x45, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C12, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C13, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C14, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C15, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C16, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C17, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C18, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C19, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1C, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C1F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C20, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C21, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C22, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C23, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C24, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C25, .reg_data = 0xEC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C26, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C27, .reg_data = 0xEC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C28, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C29, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2A, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2C, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2D, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2E, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C2F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C30, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C31, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C32, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C33, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C34, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C35, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C36, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C37, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C38, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C39, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3A, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C3F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C40, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C41, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C42, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C43, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C44, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C45, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C46, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C47, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C48, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C49, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C4F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C50, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C51, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C52, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C53, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C54, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C55, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C56, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C57, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C58, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C59, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5A, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5B, .reg_data = 0x66, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C5F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C60, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C61, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C62, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C63, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C64, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C65, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C66, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C67, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C68, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C69, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6B, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C6F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C70, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C71, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C72, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C73, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C74, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C75, .reg_data = 0xCE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C76, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C77, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C78, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C79, .reg_data = 0xD1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7A, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7B, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7C, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7D, .reg_data = 0xFE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7E, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C7F, .reg_data = 0xFC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C80, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C81, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C82, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C83, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C84, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C85, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C86, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C87, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C88, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C89, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8C, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8D, .reg_data = 0xFC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8E, .reg_data = 0xC1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C8F, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C90, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C91, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C92, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C93, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C94, .reg_data = 0x1A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C95, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C96, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C97, .reg_data = 0xA5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C98, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C99, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9A, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9B, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9D, .reg_data = 0xCE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9E, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1C9F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA1, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA2, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA3, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA4, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA5, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA6, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA8, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CA9, .reg_data = 0xF2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAA, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAB, .reg_data = 0xF2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAC, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAD, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAE, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CAF, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB0, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB1, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB2, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB3, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB4, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB6, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB7, .reg_data = 0x9A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB8, .reg_data = 0x6F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CB9, .reg_data = 0xF9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBA, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBB, .reg_data = 0x6D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBC, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBD, .reg_data = 0xA6, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBE, .reg_data = 0x16, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CBF, .reg_data = 0xC1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC0, .reg_data = 0xDD, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC1, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC2, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC3, .reg_data = 0xD5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC4, .reg_data = 0x7A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC5, .reg_data = 0xF7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC6, .reg_data = 0x89, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC7, .reg_data = 0x99, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC8, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CC9, .reg_data = 0xAE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCA, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCB, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCC, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCD, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCE, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CCF, .reg_data = 0x3A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD0, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD1, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD2, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD4, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD5, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD6, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD7, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD8, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CD9, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDA, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDB, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CDF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE8, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CE9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CEA, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CEB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CEC, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CED, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CEE, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CEF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF1, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF3, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF5, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF7, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF8, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CF9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFE, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1CFF, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D00, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D01, .reg_data = 0x9D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D02, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D03, .reg_data = 0x9D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D04, .reg_data = 0xC6, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D05, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D06, .reg_data = 0xC6, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D07, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D08, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D09, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0A, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0B, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0C, .reg_data = 0x7C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0D, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0E, .reg_data = 0x7C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D0F, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D10, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D11, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D12, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D13, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D14, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D15, .reg_data = 0xAE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D16, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D17, .reg_data = 0xAE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D18, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D19, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1A, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1B, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1C, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1D, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1E, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D1F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D20, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D21, .reg_data = 0x3A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D22, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D23, .reg_data = 0x3A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D24, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D25, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D26, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D27, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D28, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D29, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2B, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2D, .reg_data = 0x1F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D2F, .reg_data = 0x1C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D30, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D31, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D32, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D33, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D34, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D35, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D36, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D37, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D38, .reg_data = 0x75, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D39, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3A, .reg_data = 0x74, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D3F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D40, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D41, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D42, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D43, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D44, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D45, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D46, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D47, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D48, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D49, .reg_data = 0x6D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4A, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4B, .reg_data = 0xF4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4C, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4D, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4E, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D4F, .reg_data = 0xA5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D50, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D51, .reg_data = 0x7D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D52, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D53, .reg_data = 0x5B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D54, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D55, .reg_data = 0x86, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D56, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D57, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D58, .reg_data = 0x3D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D59, .reg_data = 0x7A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5A, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5B, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5C, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5D, .reg_data = 0xFB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5E, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D5F, .reg_data = 0xFB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D60, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D61, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D62, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D63, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D64, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D65, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D66, .reg_data = 0xFF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1D67, .reg_data = 0xD0, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 360, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 0, +}, +.calib_data = { +.reg_settings = { +{.reg_addr = 0x1DC0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC1, .reg_data = 0x24, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC3, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC5, .reg_data = 0xea, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC7, .reg_data = 0xec, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC8, .reg_data = 0xff, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DC9, .reg_data = 0x5f, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCA, .reg_data = 0xff, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCB, .reg_data = 0xc3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCD, .reg_data = 0x35, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DCF, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD0, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD2, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD5, .reg_data = 0x6a, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD6, .reg_data = 0x0d, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD7, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD8, .reg_data = 0x0e, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DD9, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDA, .reg_data = 0xc9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDB, .reg_data = 0x0a, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDC, .reg_data = 0xc9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDD, .reg_data = 0x0a, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDE, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DDF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE0, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x1DE5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xF006, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 39, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 0, +}, +.control_data_0 = { +.reg_settings = { +// servo on +{.reg_addr = 0x6020, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 1, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 100, +}, +.control_data_1 = { +.reg_settings = { +// LC on +{.reg_addr = 0x614F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +// gyro on +{.reg_addr = 0x6023, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +// zero shutter mode +{.reg_addr = 0x6021, .reg_data = 0x7B, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 3, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 10, +}, +.control_data_2 = { +.reg_settings = { +{.reg_addr = 0x6020, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00},// ois on +}, +.size = 1, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 0, +}, + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.c new file mode 100644 index 000000000000..1f7b080a3591 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.c @@ -0,0 +1,891 @@ +/* Copyright (c) 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 +#include +#include +#include +#include "cam_ois_core.h" +#include "cam_ois_soc.h" +#include "cam_sensor_util.h" +#include "cam_debug_util.h" +#include "cam_res_mgr_api.h" + +int32_t cam_ois_construct_default_power_setting( + struct cam_sensor_power_ctrl_t *power_info) +{ + int rc = 0; + + power_info->power_setting_size = 1; + power_info->power_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting), + GFP_KERNEL); + if (!power_info->power_setting) + return -ENOMEM; + + power_info->power_setting[0].seq_type = SENSOR_VAF; + power_info->power_setting[0].seq_val = CAM_VAF; + power_info->power_setting[0].config_val = 1; + power_info->power_setting[0].delay = 2; + + power_info->power_down_setting_size = 1; + power_info->power_down_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting), + GFP_KERNEL); + if (!power_info->power_down_setting) { + rc = -ENOMEM; + goto free_power_settings; + } + + power_info->power_down_setting[0].seq_type = SENSOR_VAF; + power_info->power_down_setting[0].seq_val = CAM_VAF; + power_info->power_down_setting[0].config_val = 0; + + return rc; + +free_power_settings: + kfree(power_info->power_setting); + return rc; +} + + +/** + * cam_ois_get_dev_handle - get device handle + * @o_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +static int cam_ois_get_dev_handle(struct cam_ois_ctrl_t *o_ctrl, + void *arg) +{ + struct cam_sensor_acquire_dev ois_acq_dev; + struct cam_create_dev_hdl bridge_params; + struct cam_control *cmd = (struct cam_control *)arg; + + if (o_ctrl->bridge_intf.device_hdl != -1) { + CAM_ERR(CAM_OIS, "Device is already acquired"); + return -EFAULT; + } + if (copy_from_user(&ois_acq_dev, u64_to_user_ptr(cmd->handle), + sizeof(ois_acq_dev))) + return -EFAULT; + + bridge_params.session_hdl = ois_acq_dev.session_handle; + bridge_params.ops = &o_ctrl->bridge_intf.ops; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = o_ctrl; + + ois_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + o_ctrl->bridge_intf.device_hdl = ois_acq_dev.device_handle; + o_ctrl->bridge_intf.session_hdl = ois_acq_dev.session_handle; + + CAM_DBG(CAM_OIS, "Device Handle: %d", ois_acq_dev.device_handle); + if (copy_to_user(u64_to_user_ptr(cmd->handle), &ois_acq_dev, + sizeof(struct cam_sensor_acquire_dev))) { + CAM_ERR(CAM_OIS, "ACQUIRE_DEV: copy to user failed"); + return -EFAULT; + } + return 0; +} + +static int cam_ois_power_up(struct cam_ois_ctrl_t *o_ctrl) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info = + &o_ctrl->soc_info; + struct cam_ois_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + soc_private = + (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + if ((power_info->power_setting == NULL) && + (power_info->power_down_setting == NULL)) { + CAM_INFO(CAM_OIS, + "Using default power settings"); + rc = cam_ois_construct_default_power_setting(power_info); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "Construct default ois power setting failed."); + return rc; + } + } + + /* Parse and fill vreg params for power up settings */ + rc = msm_camera_fill_vreg_params( + soc_info, + power_info->power_setting, + power_info->power_setting_size); + if (rc) { + CAM_ERR(CAM_OIS, + "failed to fill vreg params for power up rc:%d", rc); + return rc; + } + + /* Parse and fill vreg params for power down settings*/ + rc = msm_camera_fill_vreg_params( + soc_info, + power_info->power_down_setting, + power_info->power_down_setting_size); + if (rc) { + CAM_ERR(CAM_OIS, + "failed to fill vreg params for power down rc:%d", rc); + return rc; + } + + power_info->dev = soc_info->dev; + + rc = cam_sensor_core_power_up(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_OIS, "failed in ois power up rc %d", rc); + return rc; + } + + rc = camera_io_init(&o_ctrl->io_master_info); + if (rc) + CAM_ERR(CAM_OIS, "cci_init failed: rc: %d", rc); + + return rc; +} + +/** + * cam_ois_power_down - power down OIS device + * @o_ctrl: ctrl structure + * + * Returns success or failure + */ +static int cam_ois_power_down(struct cam_ois_ctrl_t *o_ctrl) +{ + int32_t rc = 0; + struct cam_sensor_power_ctrl_t *power_info; + struct cam_hw_soc_info *soc_info = + &o_ctrl->soc_info; + struct cam_ois_soc_private *soc_private; + + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "failed: o_ctrl %pK", o_ctrl); + return -EINVAL; + } + + soc_private = + (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + soc_info = &o_ctrl->soc_info; + + if (!power_info) { + CAM_ERR(CAM_OIS, "failed: power_info %pK", power_info); + return -EINVAL; + } + + rc = msm_camera_power_down(power_info, soc_info); + if (rc) { + CAM_ERR(CAM_OIS, "power down the core is failed:%d", rc); + return rc; + } + + camera_io_release(&o_ctrl->io_master_info); + + return rc; +} + +static int cam_ois_apply_settings(struct cam_ois_ctrl_t *o_ctrl, + struct i2c_settings_array *i2c_set) +{ + struct i2c_settings_list *i2c_list; + int32_t rc = 0; + uint32_t i, size; + + if (o_ctrl == NULL || i2c_set == NULL) { + CAM_ERR(CAM_OIS, "Invalid Args"); + return -EINVAL; + } + + if (i2c_set->is_settings_valid != 1) { + CAM_ERR(CAM_OIS, " Invalid settings"); + return -EINVAL; + } + + list_for_each_entry(i2c_list, + &(i2c_set->list_head), list) { + if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) { + rc = camera_io_dev_write(&(o_ctrl->io_master_info), + &(i2c_list->i2c_settings)); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "Failed in Applying i2c wrt settings"); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) { + size = i2c_list->i2c_settings.size; + for (i = 0; i < size; i++) { + rc = camera_io_dev_poll( + &(o_ctrl->io_master_info), + i2c_list->i2c_settings. + reg_setting[i].reg_addr, + i2c_list->i2c_settings. + reg_setting[i].reg_data, + i2c_list->i2c_settings. + reg_setting[i].data_mask, + i2c_list->i2c_settings.addr_type, + i2c_list->i2c_settings.data_type, + i2c_list->i2c_settings. + reg_setting[i].delay); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "i2c poll apply setting Fail"); + return rc; + } + } + } + } + + return rc; +} + +static int cam_ois_slaveInfo_pkt_parser(struct cam_ois_ctrl_t *o_ctrl, + uint32_t *cmd_buf) +{ + int32_t rc = 0; + struct cam_cmd_ois_info *ois_info; + + if (!o_ctrl || !cmd_buf) { + CAM_ERR(CAM_OIS, "Invalid Args"); + return -EINVAL; + } + + ois_info = (struct cam_cmd_ois_info *)cmd_buf; + if (o_ctrl->io_master_info.master_type == CCI_MASTER) { + o_ctrl->io_master_info.cci_client->i2c_freq_mode = + ois_info->i2c_freq_mode; + o_ctrl->io_master_info.cci_client->sid = + ois_info->slave_addr >> 1; + o_ctrl->ois_fw_flag = ois_info->ois_fw_flag; + o_ctrl->is_ois_calib = ois_info->is_ois_calib; + memcpy(o_ctrl->ois_name, ois_info->ois_name, 32); + o_ctrl->io_master_info.cci_client->retries = 3; + o_ctrl->io_master_info.cci_client->id_map = 0; + memcpy(&(o_ctrl->opcode), &(ois_info->opcode), + sizeof(struct cam_ois_opcode)); + CAM_DBG(CAM_OIS, "Slave addr: 0x%x Freq Mode: %d", + ois_info->slave_addr, ois_info->i2c_freq_mode); + } else if (o_ctrl->io_master_info.master_type == I2C_MASTER) { + o_ctrl->io_master_info.client->addr = ois_info->slave_addr; + CAM_DBG(CAM_OIS, "Slave addr: 0x%x", ois_info->slave_addr); + } else { + CAM_ERR(CAM_OIS, "Invalid Master type : %d", + o_ctrl->io_master_info.master_type); + rc = -EINVAL; + } + + return rc; +} + +static int cam_ois_fw_download(struct cam_ois_ctrl_t *o_ctrl) +{ + uint16_t total_bytes = 0; + uint8_t *ptr = NULL; + int32_t rc = 0, cnt; + uint32_t fw_size; + const struct firmware *fw = NULL; + const char *fw_name_prog = NULL; + const char *fw_name_coeff = NULL; + char name_prog[32] = {0}; + char name_coeff[32] = {0}; + struct device *dev = &(o_ctrl->pdev->dev); + struct cam_sensor_i2c_reg_setting i2c_reg_setting; + struct page *page = NULL; + + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "Invalid Args"); + return -EINVAL; + } + + snprintf(name_coeff, 32, "%s.coeff", o_ctrl->ois_name); + + snprintf(name_prog, 32, "%s.prog", o_ctrl->ois_name); + + /* cast pointer as const pointer*/ + fw_name_prog = name_prog; + fw_name_coeff = name_coeff; + + /* Load FW */ + rc = request_firmware(&fw, fw_name_prog, dev); + if (rc) { + CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_prog); + return rc; + } + + total_bytes = fw->size; + i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + i2c_reg_setting.size = total_bytes; + i2c_reg_setting.delay = 0; + fw_size = PAGE_ALIGN(sizeof(struct cam_sensor_i2c_reg_array) * + total_bytes) >> PAGE_SHIFT; + page = cma_alloc(dev_get_cma_area((o_ctrl->soc_info.dev)), + fw_size, 0, GFP_KERNEL); + if (!page) { + CAM_ERR(CAM_OIS, "Failed in allocating i2c_array"); + release_firmware(fw); + return -ENOMEM; + } + + i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *)( + page_address(page)); + + for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes; + cnt++, ptr++) { + i2c_reg_setting.reg_setting[cnt].reg_addr = + o_ctrl->opcode.prog; + i2c_reg_setting.reg_setting[cnt].reg_data = *ptr; + i2c_reg_setting.reg_setting[cnt].delay = 0; + i2c_reg_setting.reg_setting[cnt].data_mask = 0; + } + + rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info), + &i2c_reg_setting, 1); + if (rc < 0) { + CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc); + goto release_firmware; + } + cma_release(dev_get_cma_area((o_ctrl->soc_info.dev)), + page, fw_size); + page = NULL; + fw_size = 0; + release_firmware(fw); + + rc = request_firmware(&fw, fw_name_coeff, dev); + if (rc) { + CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_coeff); + return rc; + } + + total_bytes = fw->size; + i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + i2c_reg_setting.size = total_bytes; + i2c_reg_setting.delay = 0; + fw_size = PAGE_ALIGN(sizeof(struct cam_sensor_i2c_reg_array) * + total_bytes) >> PAGE_SHIFT; + page = cma_alloc(dev_get_cma_area((o_ctrl->soc_info.dev)), + fw_size, 0, GFP_KERNEL); + if (!page) { + CAM_ERR(CAM_OIS, "Failed in allocating i2c_array"); + release_firmware(fw); + return -ENOMEM; + } + + i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *)( + page_address(page)); + + for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes; + cnt++, ptr++) { + i2c_reg_setting.reg_setting[cnt].reg_addr = + o_ctrl->opcode.coeff; + i2c_reg_setting.reg_setting[cnt].reg_data = *ptr; + i2c_reg_setting.reg_setting[cnt].delay = 0; + i2c_reg_setting.reg_setting[cnt].data_mask = 0; + } + + rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info), + &i2c_reg_setting, 1); + if (rc < 0) + CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc); + +release_firmware: + cma_release(dev_get_cma_area((o_ctrl->soc_info.dev)), + page, fw_size); + release_firmware(fw); + + return rc; +} + +/** + * cam_ois_pkt_parse - Parse csl packet + * @o_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +static int cam_ois_pkt_parse(struct cam_ois_ctrl_t *o_ctrl, void *arg) +{ + int32_t rc = 0; + int32_t i = 0; + uint32_t total_cmd_buf_in_bytes = 0; + struct common_header *cmm_hdr = NULL; + uintptr_t generic_ptr; + struct cam_control *ioctl_ctrl = NULL; + struct cam_config_dev_cmd dev_config; + struct i2c_settings_array *i2c_reg_settings = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + uintptr_t generic_pkt_addr; + size_t pkt_len; + struct cam_packet *csl_packet = NULL; + size_t len_of_buff = 0; + uint32_t *offset = NULL, *cmd_buf; + struct cam_ois_soc_private *soc_private = + (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + struct cam_sensor_i2c_reg_setting hhk_add_setting;//add by hhk + struct cam_sensor_i2c_reg_array hhk_add_reg[3];//add by hhk + int32_t j = 0; + uint32_t red_reg_data = 0; + ioctl_ctrl = (struct cam_control *)arg; + + if (copy_from_user(&dev_config, + u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(dev_config))) + return -EFAULT; + + rc = cam_mem_get_cpu_buf(dev_config.packet_handle, + &generic_pkt_addr, &pkt_len); + if (rc) { + CAM_ERR(CAM_OIS, + "error in converting command Handle Error: %d", rc); + return rc; + } + + if (dev_config.offset > pkt_len) { + CAM_ERR(CAM_OIS, + "offset is out of bound: off: %lld len: %zu", + dev_config.offset, pkt_len); + return -EINVAL; + } + + csl_packet = (struct cam_packet *) + (generic_pkt_addr + (uint32_t)dev_config.offset); + + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_OIS_PACKET_OPCODE_INIT: + offset = (uint32_t *)&csl_packet->payload; + offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + + /* Loop through multiple command buffers */ + for (i = 0; i < csl_packet->num_cmd_buf; i++) { + total_cmd_buf_in_bytes = cmd_desc[i].length; + if (!total_cmd_buf_in_bytes) + continue; + + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &generic_ptr, &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to get cpu buf"); + return rc; + } + cmd_buf = (uint32_t *)generic_ptr; + if (!cmd_buf) { + CAM_ERR(CAM_OIS, "invalid cmd buf"); + return -EINVAL; + } + cmd_buf += cmd_desc[i].offset / sizeof(uint32_t); + cmm_hdr = (struct common_header *)cmd_buf; + + switch (cmm_hdr->cmd_type) { + case CAMERA_SENSOR_CMD_TYPE_I2C_INFO: + rc = cam_ois_slaveInfo_pkt_parser( + o_ctrl, cmd_buf); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "Failed in parsing slave info"); + return rc; + } + break; + case CAMERA_SENSOR_CMD_TYPE_PWR_UP: + case CAMERA_SENSOR_CMD_TYPE_PWR_DOWN: + CAM_DBG(CAM_OIS, + "Received power settings buffer"); + rc = cam_sensor_update_power_settings( + cmd_buf, + total_cmd_buf_in_bytes, + power_info); + if (rc) { + CAM_ERR(CAM_OIS, + "Failed: parse power settings"); + return rc; + } + break; + default: + if (o_ctrl->i2c_init_data.is_settings_valid == 0) { + CAM_DBG(CAM_OIS, + "Received init settings"); + i2c_reg_settings = + &(o_ctrl->i2c_init_data); + i2c_reg_settings->is_settings_valid = 1; + i2c_reg_settings->request_id = 0; + rc = cam_sensor_i2c_command_parser( + i2c_reg_settings, + &cmd_desc[i], 1); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "init parsing failed: %d", rc); + return rc; + } + } else if ((o_ctrl->is_ois_calib != 0) && + (o_ctrl->i2c_calib_data. + is_settings_valid == 0)) { + CAM_DBG(CAM_OIS, + "Received calib settings"); + i2c_reg_settings = &(o_ctrl->i2c_calib_data); + i2c_reg_settings->is_settings_valid = 1; + i2c_reg_settings->request_id = 0; + rc = cam_sensor_i2c_command_parser( + i2c_reg_settings, + &cmd_desc[i], 1); + if (rc < 0) { + CAM_ERR(CAM_OIS, + "Calib parsing failed: %d", rc); + return rc; + } + } + break; + } + } + + if (o_ctrl->cam_ois_state != CAM_OIS_CONFIG) { + rc = cam_ois_power_up(o_ctrl); + if (rc) { + CAM_ERR(CAM_OIS, " OIS Power up failed"); + return rc; + } + o_ctrl->cam_ois_state = CAM_OIS_CONFIG; + } + + if (o_ctrl->ois_fw_flag) { + rc = cam_ois_fw_download(o_ctrl); + if (rc) { + CAM_ERR(CAM_OIS, "Failed OIS FW Download"); + goto pwr_dwn; + } + } + + rc = cam_ois_apply_settings(o_ctrl, &o_ctrl->i2c_init_data); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Cannot apply Init settings"); + goto pwr_dwn; + } + + if (o_ctrl->is_ois_calib) { + //modify by huanghongkun begin + //for debug + rc = camera_io_dev_read(&(o_ctrl->io_master_info), + 0xF008, + &red_reg_data, + CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_DWORD); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to read 0xF008"); + } else + CAM_DBG(CAM_OIS, "read 0xF008 = 0x%x", + red_reg_data); + + if (o_ctrl->i2c_calib_data.is_settings_valid == 1) { + rc = cam_ois_apply_settings(o_ctrl, + &o_ctrl->i2c_calib_data); + if (rc) { + CAM_ERR(CAM_OIS, + "Cannot apply calib data"); + goto pwr_dwn; + } + } else{ + CAM_ERR(CAM_OIS, + "WA ois calib data invalid,ignore it."); + } + } + + rc = delete_request(&o_ctrl->i2c_init_data); + if (rc < 0) { + CAM_WARN(CAM_OIS, + "Fail deleting Init data: rc: %d", rc); + rc = 0; + } + if (o_ctrl->is_ois_calib + && o_ctrl->i2c_calib_data.is_settings_valid == 1){ + o_ctrl->isPollNeeded = true; + CAM_ERR(CAM_OIS, + "WA calib data invalid,ignore poll."); + } + rc = delete_request(&o_ctrl->i2c_calib_data); + if (rc < 0) { + CAM_WARN(CAM_OIS, + "Fail deleting Calibration data: rc: %d", rc); + rc = 0; + } + break; + case CAM_OIS_PACKET_OPCODE_OIS_CONTROL: + if (o_ctrl->cam_ois_state < CAM_OIS_CONFIG) { + rc = -EINVAL; + CAM_WARN(CAM_OIS, + "Not in right state to control OIS: %d", + o_ctrl->cam_ois_state); + return rc; + } + + if (o_ctrl->isPollNeeded == true) { + red_reg_data = 0; + for (j = 0; j < 50; j++) { + rc = camera_io_dev_read(&(o_ctrl->io_master_info), 0x6024, &red_reg_data, + CAMERA_SENSOR_I2C_TYPE_WORD, CAMERA_SENSOR_I2C_TYPE_BYTE); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to read 0x6024"); + } else + CAM_DBG(CAM_OIS, "calib_data end read 0x6024 = 0x%x", red_reg_data); + if (red_reg_data == 1) + break; + else + msleep(10); + } //control data 0 + hhk_add_reg[0].data_mask = 0x00; + hhk_add_reg[0].delay = 0x00; + hhk_add_reg[0].reg_addr = 0x6020; + hhk_add_reg[0].reg_data = 0x01; + hhk_add_setting.reg_setting = &hhk_add_reg[0]; + hhk_add_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD; + hhk_add_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + hhk_add_setting.size = 1; + hhk_add_setting.delay = 10; + rc = camera_io_dev_write(&(o_ctrl->io_master_info), &hhk_add_setting); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to write control_data_0"); + goto pwr_dwn; + } + red_reg_data = 0; + for (j = 0; j < 50; j++) { + rc = camera_io_dev_read(&(o_ctrl->io_master_info), 0x6024, &red_reg_data, + CAMERA_SENSOR_I2C_TYPE_WORD, CAMERA_SENSOR_I2C_TYPE_BYTE); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to read 0x6024"); + } else + CAM_DBG(CAM_OIS, "control_data_0 end read 0x6024 = 0x%x", red_reg_data); + if (red_reg_data == 1) + break; + else + msleep(10); + } //control data 1 + hhk_add_reg[0].data_mask = 0x00; + hhk_add_reg[0].delay = 0x00; + hhk_add_reg[0].reg_addr = 0x614F; + hhk_add_reg[0].reg_data = 0x01; + hhk_add_reg[1].data_mask = 0x00; + hhk_add_reg[1].delay = 0x00; + hhk_add_reg[1].reg_addr = 0x6023; + hhk_add_reg[1].reg_data = 0x00; + hhk_add_reg[2].data_mask = 0x00; + hhk_add_reg[2].delay = 0x00; + hhk_add_reg[2].reg_addr = 0x6021; + hhk_add_reg[2].reg_data = 0x7B; + hhk_add_setting.reg_setting = &hhk_add_reg[0]; + hhk_add_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD; + hhk_add_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + hhk_add_setting.size = 3; + hhk_add_setting.delay = 0; + rc = camera_io_dev_write(&(o_ctrl->io_master_info), &hhk_add_setting); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to write control_data_1"); + goto pwr_dwn; + } + red_reg_data = 0; + for (j = 0; j < 50; j++) { + rc = camera_io_dev_read(&(o_ctrl->io_master_info), 0x6024, &red_reg_data, + CAMERA_SENSOR_I2C_TYPE_WORD, CAMERA_SENSOR_I2C_TYPE_BYTE); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Failed to read 0x6024"); + } else + CAM_DBG(CAM_OIS, "control_data_1 end read 0x6024 = 0x%x", red_reg_data); + if (red_reg_data == 1) + break; + else + msleep(10); + } + //modify by huanghongkun end + o_ctrl->isPollNeeded = false; + } + + offset = (uint32_t *)&csl_packet->payload; + offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t)); + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + i2c_reg_settings = &(o_ctrl->i2c_mode_data); + i2c_reg_settings->is_settings_valid = 1; + i2c_reg_settings->request_id = 0; + rc = cam_sensor_i2c_command_parser(i2c_reg_settings, + cmd_desc, 1); + if (rc < 0) { + CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d", rc); + return rc; + } + + rc = cam_ois_apply_settings(o_ctrl, i2c_reg_settings); + if (rc < 0) { + CAM_ERR(CAM_OIS, "Cannot apply mode settings"); + return rc; + } + + rc = delete_request(i2c_reg_settings); + if (rc < 0) + CAM_ERR(CAM_OIS, + "Fail deleting Mode data: rc: %d", rc); + break; + default: + break; + } + return rc; +pwr_dwn: + CAM_ERR(CAM_OIS, "cam_ois_power_down."); + cam_ois_power_down(o_ctrl); + return rc; +} + +void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl) +{ + int rc; + + if (o_ctrl->cam_ois_state == CAM_OIS_INIT) + return; + + if (o_ctrl->cam_ois_state >= CAM_OIS_CONFIG) { + CAM_ERR(CAM_OIS, "cam_ois_power_down."); + rc = cam_ois_power_down(o_ctrl); + if (rc < 0) + CAM_ERR(CAM_OIS, "OIS Power down failed"); + } + + if (o_ctrl->cam_ois_state >= CAM_OIS_ACQUIRE) { + rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_OIS, "destroying the device hdl"); + o_ctrl->bridge_intf.device_hdl = -1; + o_ctrl->bridge_intf.link_hdl = -1; + o_ctrl->bridge_intf.session_hdl = -1; + } + + o_ctrl->cam_ois_state = CAM_OIS_INIT; +} + +/** + * cam_ois_driver_cmd - Handle ois cmds + * @e_ctrl: ctrl structure + * @arg: Camera control command argument + * + * Returns success or failure + */ +int cam_ois_driver_cmd(struct cam_ois_ctrl_t *o_ctrl, void *arg) +{ + int rc = 0; + struct cam_ois_query_cap_t ois_cap = {0}; + struct cam_control *cmd = (struct cam_control *)arg; + + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "e_ctrl is NULL"); + return -EINVAL; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_OIS, "Invalid handle type: %d", + cmd->handle_type); + return -EINVAL; + } + + mutex_lock(&(o_ctrl->ois_mutex)); + switch (cmd->op_code) { + case CAM_QUERY_CAP: + ois_cap.slot_info = o_ctrl->soc_info.index; + + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &ois_cap, + sizeof(struct cam_ois_query_cap_t))) { + CAM_ERR(CAM_OIS, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + CAM_DBG(CAM_OIS, "ois_cap: ID: %d", ois_cap.slot_info); + break; + case CAM_ACQUIRE_DEV: + rc = cam_ois_get_dev_handle(o_ctrl, arg); + if (rc) { + CAM_ERR(CAM_OIS, "Failed to acquire dev"); + goto release_mutex; + } + + o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE; + break; + case CAM_START_DEV: + if (o_ctrl->cam_ois_state != CAM_OIS_CONFIG) { + rc = -EINVAL; + CAM_WARN(CAM_OIS, + "Not in right state for start : %d", + o_ctrl->cam_ois_state); + goto release_mutex; + } + o_ctrl->cam_ois_state = CAM_OIS_START; + break; + case CAM_CONFIG_DEV: + rc = cam_ois_pkt_parse(o_ctrl, arg); + if (rc) { + CAM_ERR(CAM_OIS, "Failed in ois pkt Parsing"); + goto release_mutex; + } + break; + case CAM_RELEASE_DEV: + if (o_ctrl->cam_ois_state == CAM_OIS_START) { + rc = -EINVAL; + CAM_WARN(CAM_OIS, + "Cant release ois: in start state"); + goto release_mutex; + } + + if (o_ctrl->cam_ois_state == CAM_OIS_CONFIG) { + CAM_ERR(CAM_OIS, "cam_ois_power_down."); + rc = cam_ois_power_down(o_ctrl); + if (rc < 0) { + CAM_ERR(CAM_OIS, "OIS Power down failed"); + goto release_mutex; + } + } + + if (o_ctrl->bridge_intf.device_hdl == -1) { + CAM_ERR(CAM_OIS, "link hdl: %d device hdl: %d", + o_ctrl->bridge_intf.device_hdl, + o_ctrl->bridge_intf.link_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_OIS, "destroying the device hdl"); + o_ctrl->bridge_intf.device_hdl = -1; + o_ctrl->bridge_intf.link_hdl = -1; + o_ctrl->bridge_intf.session_hdl = -1; + o_ctrl->cam_ois_state = CAM_OIS_INIT; + break; + case CAM_STOP_DEV: + if (o_ctrl->cam_ois_state != CAM_OIS_START) { + rc = -EINVAL; + CAM_WARN(CAM_OIS, + "Not in right state for stop : %d", + o_ctrl->cam_ois_state); + } + o_ctrl->cam_ois_state = CAM_OIS_CONFIG; + break; + default: + CAM_ERR(CAM_OIS, "invalid opcode"); + goto release_mutex; + } +release_mutex: + mutex_unlock(&(o_ctrl->ois_mutex)); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.h new file mode 100644 index 000000000000..3f961a8a0042 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_core.h @@ -0,0 +1,39 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_OIS_CORE_H_ +#define _CAM_OIS_CORE_H_ + +#include +#include "cam_ois_dev.h" + +/** + * @power_info: power setting info to control the power + * + * This API construct the default ois power setting. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int32_t cam_ois_construct_default_power_setting( + struct cam_sensor_power_ctrl_t *power_info); + + +int cam_ois_driver_cmd(struct cam_ois_ctrl_t *e_ctrl, void *arg); + +/** + * @o_ctrl: OIS ctrl structure + * + * This API handles the shutdown ioctl/close + */ +void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl); + +#endif +/* _CAM_OIS_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.c new file mode 100644 index 000000000000..d742acf7813d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.c @@ -0,0 +1,419 @@ +/* Copyright (c) 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 "cam_ois_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_ois_soc.h" +#include "cam_ois_core.h" +#include "cam_debug_util.h" + +static long cam_ois_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_ois_driver_cmd(o_ctrl, arg); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + + return rc; +} + +static int cam_ois_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_ois_ctrl_t *o_ctrl = + v4l2_get_subdevdata(sd); + + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "o_ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&(o_ctrl->ois_mutex)); + cam_ois_shutdown(o_ctrl); + mutex_unlock(&(o_ctrl->ois_mutex)); + + return 0; +} + +static int32_t cam_ois_update_i2c_info(struct cam_ois_ctrl_t *o_ctrl, + struct cam_ois_i2c_info_t *i2c_info) +{ + struct cam_sensor_cci_client *cci_client = NULL; + + if (o_ctrl->io_master_info.master_type == CCI_MASTER) { + cci_client = o_ctrl->io_master_info.cci_client; + if (!cci_client) { + CAM_ERR(CAM_OIS, "failed: cci_client %pK", + cci_client); + return -EINVAL; + } + cci_client->cci_i2c_master = o_ctrl->cci_i2c_master; + cci_client->sid = (i2c_info->slave_addr) >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = i2c_info->i2c_freq_mode; + } + + return 0; +} + +#ifdef CONFIG_COMPAT +static long cam_ois_init_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_OIS, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_ois_subdev_ioctl(sd, cmd, &cmd_data); + if (rc) { + CAM_ERR(CAM_OIS, + "Failed in ois suddev handling rc %d", + rc); + return rc; + } + break; + default: + CAM_ERR(CAM_OIS, "Invalid compat ioctl: %d", cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_OIS, + "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + return rc; +} +#endif + +static const struct v4l2_subdev_internal_ops cam_ois_internal_ops = { + .close = cam_ois_subdev_close, +}; + +static struct v4l2_subdev_core_ops cam_ois_subdev_core_ops = { + .ioctl = cam_ois_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_ois_init_subdev_do_ioctl, +#endif +}; + +static struct v4l2_subdev_ops cam_ois_subdev_ops = { + .core = &cam_ois_subdev_core_ops, +}; + +static int cam_ois_init_subdev_param(struct cam_ois_ctrl_t *o_ctrl) +{ + int rc = 0; + + o_ctrl->v4l2_dev_str.internal_ops = &cam_ois_internal_ops; + o_ctrl->v4l2_dev_str.ops = &cam_ois_subdev_ops; + strlcpy(o_ctrl->device_name, CAM_OIS_NAME, + sizeof(o_ctrl->device_name)); + o_ctrl->v4l2_dev_str.name = o_ctrl->device_name; + o_ctrl->v4l2_dev_str.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + o_ctrl->v4l2_dev_str.ent_function = CAM_OIS_DEVICE_TYPE; + o_ctrl->v4l2_dev_str.token = o_ctrl; + + rc = cam_register_subdev(&(o_ctrl->v4l2_dev_str)); + if (rc) + CAM_ERR(CAM_OIS, "fail to create subdev"); + + return rc; +} + +static int cam_ois_i2c_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct cam_ois_ctrl_t *o_ctrl = NULL; + struct cam_ois_soc_private *soc_private = NULL; + + if (client == NULL || id == NULL) { + CAM_ERR(CAM_OIS, "Invalid Args client: %pK id: %pK", + client, id); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CAM_ERR(CAM_OIS, "i2c_check_functionality failed"); + goto probe_failure; + } + + o_ctrl = kzalloc(sizeof(*o_ctrl), GFP_KERNEL); + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "kzalloc failed"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, o_ctrl); + + o_ctrl->soc_info.dev = &client->dev; + o_ctrl->soc_info.dev_name = client->name; + o_ctrl->ois_device_type = MSM_CAMERA_I2C_DEVICE; + o_ctrl->io_master_info.master_type = I2C_MASTER; + o_ctrl->io_master_info.client = client; + + soc_private = kzalloc(sizeof(struct cam_ois_soc_private), + GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto octrl_free; + } + + o_ctrl->soc_info.soc_private = soc_private; + rc = cam_ois_driver_soc_init(o_ctrl); + if (rc) { + CAM_ERR(CAM_OIS, "failed: cam_sensor_parse_dt rc %d", rc); + goto soc_free; + } + + rc = cam_ois_init_subdev_param(o_ctrl); + if (rc) + goto soc_free; + + o_ctrl->cam_ois_state = CAM_OIS_INIT; + + return rc; + +soc_free: + kfree(soc_private); +octrl_free: + kfree(o_ctrl); +probe_failure: + return rc; +} + +static int cam_ois_i2c_driver_remove(struct i2c_client *client) +{ + int i; + struct cam_ois_ctrl_t *o_ctrl = i2c_get_clientdata(client); + struct cam_hw_soc_info *soc_info; + struct cam_ois_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "ois device is NULL"); + return -EINVAL; + } + + soc_info = &o_ctrl->soc_info; + + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + soc_private = + (struct cam_ois_soc_private *)soc_info->soc_private; + power_info = &soc_private->power_info; + + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + kfree(o_ctrl->soc_info.soc_private); + kfree(o_ctrl); + + return 0; +} + +static int32_t cam_ois_platform_driver_probe( + struct platform_device *pdev) +{ + int32_t rc = 0; + struct cam_ois_ctrl_t *o_ctrl = NULL; + struct cam_ois_soc_private *soc_private = NULL; + + o_ctrl = kzalloc(sizeof(struct cam_ois_ctrl_t), GFP_KERNEL); + if (!o_ctrl) + return -ENOMEM; + + o_ctrl->soc_info.pdev = pdev; + o_ctrl->pdev = pdev; + o_ctrl->soc_info.dev = &pdev->dev; + o_ctrl->soc_info.dev_name = pdev->name; + + o_ctrl->ois_device_type = MSM_CAMERA_PLATFORM_DEVICE; + + o_ctrl->io_master_info.master_type = CCI_MASTER; + o_ctrl->io_master_info.cci_client = kzalloc( + sizeof(struct cam_sensor_cci_client), GFP_KERNEL); + if (!o_ctrl->io_master_info.cci_client) + goto free_o_ctrl; + + soc_private = kzalloc(sizeof(struct cam_ois_soc_private), + GFP_KERNEL); + if (!soc_private) { + rc = -ENOMEM; + goto free_cci_client; + } + o_ctrl->soc_info.soc_private = soc_private; + soc_private->power_info.dev = &pdev->dev; + + INIT_LIST_HEAD(&(o_ctrl->i2c_init_data.list_head)); + INIT_LIST_HEAD(&(o_ctrl->i2c_calib_data.list_head)); + INIT_LIST_HEAD(&(o_ctrl->i2c_mode_data.list_head)); + mutex_init(&(o_ctrl->ois_mutex)); + rc = cam_ois_driver_soc_init(o_ctrl); + if (rc) { + CAM_ERR(CAM_OIS, "failed: soc init rc %d", rc); + goto free_soc; + } + + rc = cam_ois_init_subdev_param(o_ctrl); + if (rc) + goto free_soc; + + rc = cam_ois_update_i2c_info(o_ctrl, &soc_private->i2c_info); + if (rc) { + CAM_ERR(CAM_OIS, "failed: to update i2c info rc %d", rc); + goto unreg_subdev; + } + o_ctrl->bridge_intf.device_hdl = -1; + + platform_set_drvdata(pdev, o_ctrl); + v4l2_set_subdevdata(&o_ctrl->v4l2_dev_str.sd, o_ctrl); + + o_ctrl->cam_ois_state = CAM_OIS_INIT; + + return rc; +unreg_subdev: + cam_unregister_subdev(&(o_ctrl->v4l2_dev_str)); +free_soc: + kfree(soc_private); +free_cci_client: + kfree(o_ctrl->io_master_info.cci_client); +free_o_ctrl: + kfree(o_ctrl); + return rc; +} + +static int cam_ois_platform_driver_remove(struct platform_device *pdev) +{ + int i; + struct cam_ois_ctrl_t *o_ctrl; + struct cam_ois_soc_private *soc_private; + struct cam_sensor_power_ctrl_t *power_info; + struct cam_hw_soc_info *soc_info; + + o_ctrl = platform_get_drvdata(pdev); + if (!o_ctrl) { + CAM_ERR(CAM_OIS, "ois device is NULL"); + return -EINVAL; + } + + soc_info = &o_ctrl->soc_info; + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + soc_private = + (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private; + power_info = &soc_private->power_info; + + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + kfree(o_ctrl->soc_info.soc_private); + kfree(o_ctrl->io_master_info.cci_client); + kfree(o_ctrl); + return 0; +} + +static const struct of_device_id cam_ois_dt_match[] = { + { .compatible = "qcom,ois" }, + { } +}; + + +MODULE_DEVICE_TABLE(of, cam_ois_dt_match); + +static struct platform_driver cam_ois_platform_driver = { + .driver = { + .name = "qcom,ois", + .owner = THIS_MODULE, + .of_match_table = cam_ois_dt_match, + }, + .probe = cam_ois_platform_driver_probe, + .remove = cam_ois_platform_driver_remove, +}; +static const struct i2c_device_id cam_ois_i2c_id[] = { + { "msm_ois", (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver cam_ois_i2c_driver = { + .id_table = cam_ois_i2c_id, + .probe = cam_ois_i2c_driver_probe, + .remove = cam_ois_i2c_driver_remove, + .driver = { + .name = "msm_ois", + }, +}; + +static struct cam_ois_registered_driver_t registered_driver = { + 0, 0}; + +static int __init cam_ois_driver_init(void) +{ + int rc = 0; + + rc = platform_driver_register(&cam_ois_platform_driver); + if (rc) { + CAM_ERR(CAM_OIS, "platform_driver_register failed rc = %d", + rc); + return rc; + } + + registered_driver.platform_driver = 1; + + rc = i2c_add_driver(&cam_ois_i2c_driver); + if (rc) { + CAM_ERR(CAM_OIS, "i2c_add_driver failed rc = %d", rc); + return rc; + } + + registered_driver.i2c_driver = 1; + return rc; +} + +static void __exit cam_ois_driver_exit(void) +{ + if (registered_driver.platform_driver) + platform_driver_unregister(&cam_ois_platform_driver); + + if (registered_driver.i2c_driver) + i2c_del_driver(&cam_ois_i2c_driver); +} + +module_init(cam_ois_driver_init); +module_exit(cam_ois_driver_exit); +MODULE_DESCRIPTION("CAM OIS driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.h new file mode 100644 index 000000000000..3a30b250f6bd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_dev.h @@ -0,0 +1,133 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_OIS_DEV_H_ +#define _CAM_OIS_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_soc_util.h" + +#define DEFINE_MSM_MUTEX(mutexname) \ + static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +enum cam_ois_state { + CAM_OIS_INIT, + CAM_OIS_ACQUIRE, + CAM_OIS_CONFIG, + CAM_OIS_START, +}; + +/** + * struct cam_ois_registered_driver_t - registered driver info + * @platform_driver : flag indicates if platform driver is registered + * @i2c_driver : flag indicates if i2c driver is registered + * + */ +struct cam_ois_registered_driver_t { + bool platform_driver; + bool i2c_driver; +}; + +/** + * struct cam_ois_i2c_info_t - I2C info + * @slave_addr : slave address + * @i2c_freq_mode : i2c frequency mode + * + */ +struct cam_ois_i2c_info_t { + uint16_t slave_addr; + uint8_t i2c_freq_mode; +}; + +/** + * struct cam_ois_soc_private - ois soc private data structure + * @ois_name : ois name + * @i2c_info : i2c info structure + * @power_info : ois power info + * + */ +struct cam_ois_soc_private { + const char *ois_name; + struct cam_ois_i2c_info_t i2c_info; + struct cam_sensor_power_ctrl_t power_info; +}; + +/** + * struct cam_ois_intf_params - bridge interface params + * @device_hdl : Device Handle + * @session_hdl : Session Handle + * @ops : KMD operations + * @crm_cb : Callback API pointers + */ +struct cam_ois_intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_ois_ctrl_t - OIS ctrl private data + * @pdev : platform device + * @ois_mutex : ois mutex + * @soc_info : ois soc related info + * @io_master_info : Information about the communication master + * @cci_i2c_master : I2C structure + * @v4l2_dev_str : V4L2 device structure + * @bridge_intf : bridge interface params + * @i2c_init_data : ois i2c init settings + * @i2c_mode_data : ois i2c mode settings + * @i2c_calib_data : ois i2c calib settings + * @ois_device_type : ois device type + * @cam_ois_state : ois_device_state + * @ois_name : ois name + * @ois_fw_flag : flag for firmware download + * @is_ois_calib : flag for Calibration data + * @opcode : ois opcode + * @device_name : Device name + * + */ +struct cam_ois_ctrl_t { + struct platform_device *pdev; + struct mutex ois_mutex; + struct cam_hw_soc_info soc_info; + struct camera_io_master io_master_info; + enum cci_i2c_master_t cci_i2c_master; + struct cam_subdev v4l2_dev_str; + struct cam_ois_intf_params bridge_intf; + struct i2c_settings_array i2c_init_data; + struct i2c_settings_array i2c_calib_data; + struct i2c_settings_array i2c_mode_data; + enum msm_camera_device_type_t ois_device_type; + enum cam_ois_state cam_ois_state; + char device_name[20]; + char ois_name[32]; + uint8_t ois_fw_flag; + uint8_t is_ois_calib; + struct cam_ois_opcode opcode; + bool isPollNeeded; +}; + +#endif /*_CAM_OIS_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.c new file mode 100644 index 000000000000..c8b34487a1fa --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.c @@ -0,0 +1,118 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include + +#include "cam_ois_soc.h" +#include "cam_debug_util.h" + +/** + * @e_ctrl: ctrl structure + * + * Parses ois dt + */ +static int cam_ois_get_dt_data(struct cam_ois_ctrl_t *o_ctrl) +{ + int i, rc = 0; + struct cam_hw_soc_info *soc_info = &o_ctrl->soc_info; + struct cam_ois_soc_private *soc_private = + (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private; + struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info; + struct device_node *of_node = NULL; + + of_node = soc_info->dev->of_node; + + if (!of_node) { + CAM_ERR(CAM_OIS, "of_node is NULL, device type %d", + o_ctrl->ois_device_type); + return -EINVAL; + } + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_OIS, "cam_soc_util_get_dt_properties rc %d", + rc); + return rc; + } + + if (!soc_info->gpio_data) { + CAM_INFO(CAM_OIS, "No GPIO found"); + return 0; + } + + if (!soc_info->gpio_data->cam_gpio_common_tbl_size) { + CAM_INFO(CAM_OIS, "No GPIO found"); + return -EINVAL; + } + + rc = cam_sensor_util_init_gpio_pin_tbl(soc_info, + &power_info->gpio_num_info); + if ((rc < 0) || (!power_info->gpio_num_info)) { + CAM_ERR(CAM_OIS, "No/Error OIS GPIOs"); + return -EINVAL; + } + + for (i = 0; i < soc_info->num_clk; i++) { + soc_info->clk[i] = devm_clk_get(soc_info->dev, + soc_info->clk_name[i]); + if (!soc_info->clk[i]) { + CAM_ERR(CAM_SENSOR, "get failed for %s", + soc_info->clk_name[i]); + rc = -ENOENT; + return rc; + } + } + + return rc; +} +/** + * @o_ctrl: ctrl structure + * + * This function is called from cam_ois_platform/i2c_driver_probe, it parses + * the ois dt node. + */ +int cam_ois_driver_soc_init(struct cam_ois_ctrl_t *o_ctrl) +{ + int rc = 0; + struct cam_hw_soc_info *soc_info = &o_ctrl->soc_info; + struct device_node *of_node = NULL; + + if (!soc_info->dev) { + CAM_ERR(CAM_OIS, "soc_info is not initialized"); + return -EINVAL; + } + + of_node = soc_info->dev->of_node; + if (!of_node) { + CAM_ERR(CAM_OIS, "dev.of_node NULL"); + return -EINVAL; + } + + if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = of_property_read_u32(of_node, "cci-master", + &o_ctrl->cci_i2c_master); + if (rc < 0) { + CAM_DBG(CAM_OIS, "failed rc %d", rc); + return rc; + } + } + + rc = cam_ois_get_dt_data(o_ctrl); + if (rc < 0) + CAM_DBG(CAM_OIS, "failed: ois get dt data rc %d", rc); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.h new file mode 100644 index 000000000000..af0413436a0b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_ois/cam_ois_soc.h @@ -0,0 +1,19 @@ +/* Copyright (c) 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. + */ +#ifndef _CAM_OIS_SOC_H_ +#define _CAM_OIS_SOC_H_ + +#include "cam_ois_dev.h" + +int cam_ois_driver_soc_init(struct cam_ois_ctrl_t *o_ctrl); + +#endif/* _CAM_OIS_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/Makefile new file mode 100644 index 000000000000..ed911ac1e5a0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_res_mgr.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr.c new file mode 100644 index 000000000000..bb3789b12dcc --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr.c @@ -0,0 +1,737 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include "cam_debug_util.h" +#include "cam_res_mgr_api.h" +#include "cam_res_mgr_private.h" + +static struct cam_res_mgr *cam_res; + +static void cam_res_mgr_free_res(void) +{ + struct cam_dev_res *dev_res, *dev_temp; + struct cam_gpio_res *gpio_res, *gpio_temp; + struct cam_flash_res *flash_res, *flash_temp; + + if (!cam_res) + return; + + mutex_lock(&cam_res->gpio_res_lock); + list_for_each_entry_safe(gpio_res, gpio_temp, + &cam_res->gpio_res_list, list) { + list_for_each_entry_safe(dev_res, dev_temp, + &gpio_res->dev_list, list) { + list_del_init(&dev_res->list); + kfree(dev_res); + } + list_del_init(&gpio_res->list); + kfree(gpio_res); + } + mutex_unlock(&cam_res->gpio_res_lock); + + mutex_lock(&cam_res->flash_res_lock); + list_for_each_entry_safe(flash_res, flash_temp, + &cam_res->flash_res_list, list) { + list_del_init(&flash_res->list); + kfree(flash_res); + } + mutex_unlock(&cam_res->flash_res_lock); + + mutex_lock(&cam_res->clk_res_lock); + cam_res->shared_clk_ref_count = 0; + mutex_unlock(&cam_res->clk_res_lock); +} + +void cam_res_mgr_led_trigger_register(const char *name, struct led_trigger **tp) +{ + bool found = false; + struct cam_flash_res *flash_res; + + if (!cam_res) { + /* + * If this driver not probed, then just register the + * led trigger. + */ + led_trigger_register_simple(name, tp); + return; + } + + mutex_lock(&cam_res->flash_res_lock); + list_for_each_entry(flash_res, &cam_res->flash_res_list, list) { + if (!strcmp(flash_res->name, name)) { + found = true; + break; + } + } + mutex_unlock(&cam_res->flash_res_lock); + + if (found) { + *tp = flash_res->trigger; + } else { + flash_res = kzalloc(sizeof(struct cam_flash_res), GFP_KERNEL); + if (!flash_res) { + CAM_ERR(CAM_RES, + "Failed to malloc memory for flash_res:%s", + name); + *tp = NULL; + return; + } + + led_trigger_register_simple(name, tp); + INIT_LIST_HEAD(&flash_res->list); + flash_res->trigger = *tp; + flash_res->name = name; + + mutex_lock(&cam_res->flash_res_lock); + list_add_tail(&flash_res->list, &cam_res->flash_res_list); + mutex_unlock(&cam_res->flash_res_lock); + } +} +EXPORT_SYMBOL(cam_res_mgr_led_trigger_register); + +void cam_res_mgr_led_trigger_unregister(struct led_trigger *tp) +{ + bool found = false; + struct cam_flash_res *flash_res; + + if (!cam_res) { + /* + * If this driver not probed, then just unregister the + * led trigger. + */ + led_trigger_unregister_simple(tp); + return; + } + + mutex_lock(&cam_res->flash_res_lock); + list_for_each_entry(flash_res, &cam_res->flash_res_list, list) { + if (flash_res->trigger == tp) { + found = true; + break; + } + } + + if (found) { + led_trigger_unregister_simple(tp); + list_del_init(&flash_res->list); + kfree(flash_res); + } + mutex_unlock(&cam_res->flash_res_lock); +} +EXPORT_SYMBOL(cam_res_mgr_led_trigger_unregister); + +void cam_res_mgr_led_trigger_event(struct led_trigger *trig, + enum led_brightness brightness) +{ + bool found = false; + struct cam_flash_res *flash_res; + + if (!cam_res) { + /* + * If this driver not probed, then just trigger + * the led event. + */ + led_trigger_event(trig, brightness); + return; + } + + mutex_lock(&cam_res->flash_res_lock); + list_for_each_entry(flash_res, &cam_res->flash_res_list, list) { + if (flash_res->trigger == trig) { + found = true; + break; + } + } + mutex_unlock(&cam_res->flash_res_lock); + + if (found) + led_trigger_event(trig, brightness); +} +EXPORT_SYMBOL(cam_res_mgr_led_trigger_event); + +int cam_res_mgr_shared_pinctrl_init(void) +{ + struct cam_soc_pinctrl_info *pinctrl_info; + + /* + * We allow the cam_res is NULL or shared_gpio_enabled + * is false, it means this driver no probed or doesn't + * have shared gpio in this device. + */ + if (!cam_res || !cam_res->shared_gpio_enabled) { + CAM_DBG(CAM_RES, "Not support shared gpio."); + return 0; + } + + if (cam_res->pstatus != PINCTRL_STATUS_PUT) { + CAM_DBG(CAM_RES, "The shared pinctrl already been got."); + return 0; + } + + pinctrl_info = &cam_res->dt.pinctrl_info; + + pinctrl_info->pinctrl = + devm_pinctrl_get(cam_res->dev); + if (IS_ERR_OR_NULL(pinctrl_info->pinctrl)) { + CAM_ERR(CAM_RES, "Pinctrl not available"); + cam_res->shared_gpio_enabled = false; + return -EINVAL; + } + + pinctrl_info->gpio_state_active = + pinctrl_lookup_state(pinctrl_info->pinctrl, + CAM_RES_MGR_DEFAULT); + if (IS_ERR_OR_NULL(pinctrl_info->gpio_state_active)) { + CAM_ERR(CAM_RES, + "Failed to get the active state pinctrl handle"); + cam_res->shared_gpio_enabled = false; + return -EINVAL; + } + + pinctrl_info->gpio_state_suspend = + pinctrl_lookup_state(pinctrl_info->pinctrl, + CAM_RES_MGR_SLEEP); + if (IS_ERR_OR_NULL(pinctrl_info->gpio_state_suspend)) { + CAM_ERR(CAM_RES, + "Failed to get the active state pinctrl handle"); + cam_res->shared_gpio_enabled = false; + return -EINVAL; + } + + mutex_lock(&cam_res->gpio_res_lock); + cam_res->pstatus = PINCTRL_STATUS_GOT; + mutex_unlock(&cam_res->gpio_res_lock); + + return 0; +} +EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_init); + +static bool cam_res_mgr_shared_pinctrl_check_hold(void) +{ + int index = 0; + int dev_num = 0; + bool hold = false; + struct list_head *list; + struct cam_gpio_res *gpio_res; + struct cam_res_mgr_dt *dt = &cam_res->dt; + + for (; index < dt->num_shared_gpio; index++) { + list_for_each_entry(gpio_res, + &cam_res->gpio_res_list, list) { + + if (gpio_res->gpio == + dt->shared_gpio[index]) { + list_for_each(list, &gpio_res->dev_list) + dev_num++; + + if (dev_num >= 2) { + hold = true; + break; + } + } + } + } + + if (cam_res->shared_clk_ref_count > 1) + hold = true; + + return hold; +} + +void cam_res_mgr_shared_pinctrl_put(void) +{ + struct cam_soc_pinctrl_info *pinctrl_info; + + if (!cam_res || !cam_res->shared_gpio_enabled) { + CAM_DBG(CAM_RES, "Not support shared gpio."); + return; + } + + mutex_lock(&cam_res->gpio_res_lock); + if (cam_res->pstatus == PINCTRL_STATUS_PUT) { + CAM_DBG(CAM_RES, "The shared pinctrl already been put"); + mutex_unlock(&cam_res->gpio_res_lock); + return; + } + + if (cam_res_mgr_shared_pinctrl_check_hold()) { + CAM_INFO(CAM_RES, "Need hold put this pinctrl"); + mutex_unlock(&cam_res->gpio_res_lock); + return; + } + + pinctrl_info = &cam_res->dt.pinctrl_info; + + devm_pinctrl_put(pinctrl_info->pinctrl); + + cam_res->pstatus = PINCTRL_STATUS_PUT; + mutex_unlock(&cam_res->gpio_res_lock); +} +EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_put); + +int cam_res_mgr_shared_pinctrl_select_state(bool active) +{ + int rc = 0; + struct cam_soc_pinctrl_info *pinctrl_info; + + if (!cam_res || !cam_res->shared_gpio_enabled) { + CAM_DBG(CAM_RES, "Not support shared gpio."); + return 0; + } + + mutex_lock(&cam_res->gpio_res_lock); + if (cam_res->pstatus == PINCTRL_STATUS_PUT) { + CAM_DBG(CAM_RES, "The shared pinctrl alerady been put.!"); + mutex_unlock(&cam_res->gpio_res_lock); + return 0; + } + + pinctrl_info = &cam_res->dt.pinctrl_info; + + if (active && (cam_res->pstatus != PINCTRL_STATUS_ACTIVE)) { + rc = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->gpio_state_active); + cam_res->pstatus = PINCTRL_STATUS_ACTIVE; + } else if (!active && + !cam_res_mgr_shared_pinctrl_check_hold()) { + rc = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->gpio_state_suspend); + cam_res->pstatus = PINCTRL_STATUS_SUSPEND; + } + mutex_unlock(&cam_res->gpio_res_lock); + + return rc; +} +EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_select_state); + +int cam_res_mgr_shared_pinctrl_post_init(void) +{ + int ret = 0; + struct cam_soc_pinctrl_info *pinctrl_info; + + if (!cam_res || !cam_res->shared_gpio_enabled) { + CAM_DBG(CAM_RES, "Not support shared gpio."); + return ret; + } + + mutex_lock(&cam_res->gpio_res_lock); + if (cam_res->pstatus == PINCTRL_STATUS_PUT) { + CAM_DBG(CAM_RES, "The shared pinctrl alerady been put.!"); + mutex_unlock(&cam_res->gpio_res_lock); + return ret; + } + + pinctrl_info = &cam_res->dt.pinctrl_info; + + /* + * If no gpio resource in gpio_res_list, and + * no shared clk now, it means this device + * don't have shared gpio. + */ + if (list_empty(&cam_res->gpio_res_list) && + cam_res->shared_clk_ref_count < 1) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->gpio_state_suspend); + devm_pinctrl_put(pinctrl_info->pinctrl); + cam_res->pstatus = PINCTRL_STATUS_PUT; + } + mutex_unlock(&cam_res->gpio_res_lock); + + return ret; +} +EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_post_init); + +static int cam_res_mgr_add_device(struct device *dev, + struct cam_gpio_res *gpio_res) +{ + struct cam_dev_res *dev_res = NULL; + + dev_res = kzalloc(sizeof(struct cam_dev_res), GFP_KERNEL); + if (!dev_res) + return -ENOMEM; + + dev_res->dev = dev; + INIT_LIST_HEAD(&dev_res->list); + + list_add_tail(&dev_res->list, &gpio_res->dev_list); + + return 0; +} + +static bool cam_res_mgr_gpio_is_shared(uint gpio) +{ + int index = 0; + bool found = false; + struct cam_res_mgr_dt *dt = &cam_res->dt; + + for (; index < dt->num_shared_gpio; index++) { + if (gpio == dt->shared_gpio[index]) { + found = true; + break; + } + } + + return found; +} + +int cam_res_mgr_gpio_request(struct device *dev, uint gpio, + unsigned long flags, const char *label) +{ + int rc = 0; + bool found = false; + struct cam_gpio_res *gpio_res = NULL; + + if (cam_res && cam_res->shared_gpio_enabled) { + mutex_lock(&cam_res->gpio_res_lock); + list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) { + if (gpio == gpio_res->gpio) { + found = true; + break; + } + } + mutex_unlock(&cam_res->gpio_res_lock); + } + + /* + * found equal to false has two situation: + * 1. shared gpio not enabled + * 2. shared gpio enabled, but not find this gpio + * from the gpio_res_list + * These two situation both need request gpio. + */ + if (!found) { + rc = gpio_request_one(gpio, flags, label); + if (rc) { + CAM_ERR(CAM_RES, "gpio %d:%s request fails", + gpio, label); + return rc; + } + } + + /* + * If the gpio is in the shared list, and not find + * from gpio_res_list, then insert a cam_gpio_res + * to gpio_res_list. + */ + if (!found && cam_res + && cam_res->shared_gpio_enabled && + cam_res_mgr_gpio_is_shared(gpio)) { + + gpio_res = kzalloc(sizeof(struct cam_gpio_res), GFP_KERNEL); + if (!gpio_res) + return -ENOMEM; + + gpio_res->gpio = gpio; + gpio_res->power_on_count = 0; + INIT_LIST_HEAD(&gpio_res->list); + INIT_LIST_HEAD(&gpio_res->dev_list); + + rc = cam_res_mgr_add_device(dev, gpio_res); + if (rc) { + kfree(gpio_res); + return rc; + } + + mutex_lock(&cam_res->gpio_res_lock); + list_add_tail(&gpio_res->list, &cam_res->gpio_res_list); + mutex_unlock(&cam_res->gpio_res_lock); + } + + if (found && cam_res + && cam_res->shared_gpio_enabled) { + struct cam_dev_res *dev_res = NULL; + + found = 0; + mutex_lock(&cam_res->gpio_res_lock); + list_for_each_entry(dev_res, &gpio_res->dev_list, list) { + if (dev_res->dev == dev) { + found = 1; + break; + } + } + + if (!found) + rc = cam_res_mgr_add_device(dev, gpio_res); + + mutex_unlock(&cam_res->gpio_res_lock); + } + + return rc; +} +EXPORT_SYMBOL(cam_res_mgr_gpio_request); + +static void cam_res_mgr_gpio_free(struct device *dev, uint gpio) +{ + bool found = false; + bool need_free = true; + int dev_num = 0; + struct cam_gpio_res *gpio_res = NULL; + + if (cam_res && cam_res->shared_gpio_enabled) { + mutex_lock(&cam_res->gpio_res_lock); + list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) { + if (gpio == gpio_res->gpio) { + found = true; + break; + } + } + mutex_unlock(&cam_res->gpio_res_lock); + } + + if (found && cam_res + && cam_res->shared_gpio_enabled) { + struct list_head *list; + struct cam_dev_res *dev_res = NULL; + + mutex_lock(&cam_res->gpio_res_lock); + /* Count the dev number in the dev_list */ + list_for_each(list, &gpio_res->dev_list) + dev_num++; + + /* + * Need free the gpio if only has last 1 device + * in the dev_list, otherwise, not free this + * gpio. + */ + if (dev_num == 1) { + dev_res = list_first_entry(&gpio_res->dev_list, + struct cam_dev_res, list); + list_del_init(&dev_res->list); + kfree(dev_res); + + list_del_init(&gpio_res->list); + kfree(gpio_res); + } else { + list_for_each_entry(dev_res, + &gpio_res->dev_list, list) { + if (dev_res->dev == dev) { + list_del_init(&dev_res->list); + kfree(dev_res); + need_free = false; + break; + } + } + } + mutex_unlock(&cam_res->gpio_res_lock); + } + + if (need_free) + gpio_free(gpio); +} + +void cam_res_mgr_gpio_free_arry(struct device *dev, + const struct gpio *array, size_t num) +{ + while (num--) + cam_res_mgr_gpio_free(dev, (array[num]).gpio); +} +EXPORT_SYMBOL(cam_res_mgr_gpio_free_arry); + +int cam_res_mgr_gpio_set_value(unsigned int gpio, int value) +{ + int rc = 0; + bool found = false; + struct cam_gpio_res *gpio_res = NULL; + + if (cam_res && cam_res->shared_gpio_enabled) { + mutex_lock(&cam_res->gpio_res_lock); + list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) { + if (gpio == gpio_res->gpio) { + found = true; + break; + } + } + mutex_unlock(&cam_res->gpio_res_lock); + } + + /* + * Set the value directly if can't find the gpio from + * gpio_res_list, otherwise, need add ref count support + **/ + if (!found) { + gpio_set_value_cansleep(gpio, value); + } else { + if (value) { + gpio_res->power_on_count++; + if (gpio_res->power_on_count < 2) { + gpio_set_value_cansleep(gpio, value); + CAM_DBG(CAM_RES, + "Shared GPIO(%d) : HIGH", gpio); + } + } else { + gpio_res->power_on_count--; + if (gpio_res->power_on_count < 1) { + gpio_set_value_cansleep(gpio, value); + CAM_DBG(CAM_RES, + "Shared GPIO(%d) : LOW", gpio); + } + } + } + + return rc; +} +EXPORT_SYMBOL(cam_res_mgr_gpio_set_value); + +void cam_res_mgr_shared_clk_config(bool value) +{ + if (!cam_res) + return; + + mutex_lock(&cam_res->clk_res_lock); + if (value) + cam_res->shared_clk_ref_count++; + else + cam_res->shared_clk_ref_count--; + mutex_unlock(&cam_res->clk_res_lock); +} +EXPORT_SYMBOL(cam_res_mgr_shared_clk_config); + +static int cam_res_mgr_parse_dt(struct device *dev) +{ + int rc = 0; + struct device_node *of_node = NULL; + struct cam_res_mgr_dt *dt = &cam_res->dt; + + of_node = dev->of_node; + + dt->num_shared_gpio = of_property_count_u32_elems(of_node, + "shared-gpios"); + + if (dt->num_shared_gpio > MAX_SHARED_GPIO_SIZE || + dt->num_shared_gpio <= 0) { + /* + * Not really an error, it means dtsi not configure + * the shared gpio. + */ + CAM_DBG(CAM_RES, "Invalid GPIO number %d. No shared gpio.", + dt->num_shared_gpio); + return -EINVAL; + } + + rc = of_property_read_u32_array(of_node, "shared-gpios", + dt->shared_gpio, dt->num_shared_gpio); + if (rc) { + CAM_ERR(CAM_RES, "Get shared gpio array failed."); + return -EINVAL; + } + + dt->pinctrl_info.pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(dt->pinctrl_info.pinctrl)) { + CAM_ERR(CAM_RES, "Pinctrl not available"); + return -EINVAL; + } + + /* + * Check the pinctrl state to make sure the gpio + * shared enabled. + */ + dt->pinctrl_info.gpio_state_active = + pinctrl_lookup_state(dt->pinctrl_info.pinctrl, + CAM_RES_MGR_DEFAULT); + if (IS_ERR_OR_NULL(dt->pinctrl_info.gpio_state_active)) { + CAM_ERR(CAM_RES, + "Failed to get the active state pinctrl handle"); + return -EINVAL; + } + + dt->pinctrl_info.gpio_state_suspend = + pinctrl_lookup_state(dt->pinctrl_info.pinctrl, + CAM_RES_MGR_SLEEP); + if (IS_ERR_OR_NULL(dt->pinctrl_info.gpio_state_suspend)) { + CAM_ERR(CAM_RES, + "Failed to get the active state pinctrl handle"); + return -EINVAL; + } + + devm_pinctrl_put(dt->pinctrl_info.pinctrl); + + return rc; +} + +static int cam_res_mgr_probe(struct platform_device *pdev) +{ + int rc = 0; + + cam_res = kzalloc(sizeof(*cam_res), GFP_KERNEL); + if (!cam_res) + return -ENOMEM; + + cam_res->dev = &pdev->dev; + mutex_init(&cam_res->flash_res_lock); + mutex_init(&cam_res->gpio_res_lock); + mutex_init(&cam_res->clk_res_lock); + + rc = cam_res_mgr_parse_dt(&pdev->dev); + if (rc) { + CAM_INFO(CAM_RES, "Disable shared gpio support."); + cam_res->shared_gpio_enabled = false; + } else { + CAM_INFO(CAM_RES, "Enable shared gpio support."); + cam_res->shared_gpio_enabled = true; + } + + cam_res->shared_clk_ref_count = 0; + cam_res->pstatus = PINCTRL_STATUS_PUT; + + INIT_LIST_HEAD(&cam_res->gpio_res_list); + INIT_LIST_HEAD(&cam_res->flash_res_list); + + return 0; +} + +static int cam_res_mgr_remove(struct platform_device *pdev) +{ + if (cam_res) { + cam_res_mgr_free_res(); + kfree(cam_res); + cam_res = NULL; + } + + return 0; +} + +static const struct of_device_id cam_res_mgr_dt_match[] = { + {.compatible = "qcom,cam-res-mgr"}, + {} +}; +MODULE_DEVICE_TABLE(of, cam_res_mgr_dt_match); + +static struct platform_driver cam_res_mgr_driver = { + .probe = cam_res_mgr_probe, + .remove = cam_res_mgr_remove, + .driver = { + .name = "cam_res_mgr", + .owner = THIS_MODULE, + .of_match_table = cam_res_mgr_dt_match, + }, +}; + +static int __init cam_res_mgr_init(void) +{ + return platform_driver_register(&cam_res_mgr_driver); +} + +static void __exit cam_res_mgr_exit(void) +{ + platform_driver_unregister(&cam_res_mgr_driver); +} + +module_init(cam_res_mgr_init); +module_exit(cam_res_mgr_exit); +MODULE_DESCRIPTION("Camera resource manager driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h new file mode 100644 index 000000000000..7fb13ba4b9e9 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h @@ -0,0 +1,148 @@ +/* Copyright (c) 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. + */ + +#ifndef __CAM_RES_MGR_API_H__ +#define __CAM_RES_MGR_API_H__ + +#include + +/** + * @brief: Register the led trigger + * + * The newly registered led trigger is assigned to flash_res_list. + * + * @name : Pointer to int led trigger name + * @tp : Save the returned led trigger + * + * @return None + */ +void cam_res_mgr_led_trigger_register(const char *name, + struct led_trigger **tp); + +/** + * @brief: Unregister the led trigger + * + * Free the flash_res if this led trigger isn't used by other device . + * + * @tp : Pointer to the led trigger + * + * @return None + */ +void cam_res_mgr_led_trigger_unregister(struct led_trigger *tp); + +/** + * @brief: Trigger the event to led core + * + * @trig : Pointer to the led trigger + * @brightness : The brightness need to fire + * + * @return None + */ +void cam_res_mgr_led_trigger_event(struct led_trigger *trig, + enum led_brightness brightness); + +/** + * @brief: Get the corresponding pinctrl of dev + * + * Init the shared pinctrl if shared pinctrl enabled. + * + * @return None + */ +int cam_res_mgr_shared_pinctrl_init(void); + +/** + * @brief: Put the pinctrl + * + * Put the shared pinctrl. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +void cam_res_mgr_shared_pinctrl_put(void); + +/** + * @brief: Select the corresponding state + * + * Active state can be selected directly, but need hold to suspend the + * pinctrl if the gpios in this pinctrl also held by other pinctrl. + * + * @active : The flag to indicate whether active or suspend + * the shared pinctrl. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_res_mgr_shared_pinctrl_select_state(bool active); + +/** + * @brief: Post init shared pinctrl + * + * Post init to check if the device really has shared gpio, + * suspend and put the pinctrl if not use shared gpio. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_res_mgr_shared_pinctrl_post_init(void); + +/** + * @brief: Request a gpio + * + * Will alloc a gpio_res for the new gpio, other find the corresponding + * gpio_res. + * + * @dev : Pointer to the device + * @gpio : The GPIO number + * @flags : GPIO configuration as specified by GPIOF_* + * @label : A literal description string of this GPIO + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_res_mgr_gpio_request(struct device *dev, unsigned int gpio, + unsigned long flags, const char *label); + +/** + * @brief: Free a array GPIO + * + * Free the GPIOs and release corresponding gpio_res. + * + * @dev : Pointer to the device + * @gpio : Array of the GPIO number + * @num : The number of gpio + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +void cam_res_mgr_gpio_free_arry(struct device *dev, + const struct gpio *array, size_t num); + +/** + * @brief: Set GPIO power level + * + * Add ref count support for shared GPIOs. + * + * @gpio : The GPIO number + * @value : The power level need to setup + * + * @return Status of operation. Negative in case of error. Zero otherwise. + * -EINVAL will be returned if the gpio can't be found in gpio_res_list. + */ +int cam_res_mgr_gpio_set_value(unsigned int gpio, int value); + +/** + * @brief: Config the shared clk ref count + * + * Config the shared clk ref count.. + * + * @value : get or put the shared clk. + * + * @return None + */ +void cam_res_mgr_shared_clk_config(bool value); + +#endif /* __CAM_RES_MGR_API_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h new file mode 100644 index 000000000000..53a877808d55 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h @@ -0,0 +1,117 @@ +/* Copyright (c) 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. + */ + +#ifndef __CAM_RES_MGR_PRIVATE_H__ +#define __CAM_RES_MGR_PRIVATE_H__ + +#include +#include +#include "cam_soc_util.h" + +#define MAX_SHARED_GPIO_SIZE 16 + +/* pinctrl states name */ +#define CAM_RES_MGR_SLEEP "cam_res_mgr_suspend" +#define CAM_RES_MGR_DEFAULT "cam_res_mgr_default" + +/** + * enum pinctrl_status - Enum for pinctrl status + */ +enum pinctrl_status { + PINCTRL_STATUS_GOT = 0, + PINCTRL_STATUS_ACTIVE, + PINCTRL_STATUS_SUSPEND, + PINCTRL_STATUS_PUT, +}; + +/** + * struct cam_dev_res + * + * @list : List member used to append this node to a dev list + * @dev : Device pointer associated with device + */ +struct cam_dev_res { + struct list_head list; + struct device *dev; +}; + +/** + * struct cam_gpio_res + * + * @list : List member used to append this node to a gpio list + * @dev_list : List the device which request this gpio + * @gpio : Gpio value + * @power_on_count : Record the power on times of this gpio + */ +struct cam_gpio_res { + struct list_head list; + struct list_head dev_list; + unsigned int gpio; + int power_on_count; +}; + +/** + * struct cam_pinctrl_res + * + * @list : List member used to append this node to a linked list + * @name : Pointer to the flash trigger's name. + * @trigger : Pointer to the flash trigger + */ +struct cam_flash_res { + struct list_head list; + const char *name; + struct led_trigger *trigger; +}; + +/** + * struct cam_res_mgr_dt + * + * @shared_gpio : Shared gpios list in the device tree + * @num_shared_gpio : The number of shared gpio + * @pinctrl_info : Pinctrl information + */ +struct cam_res_mgr_dt { + uint shared_gpio[MAX_SHARED_GPIO_SIZE]; + int num_shared_gpio; + struct cam_soc_pinctrl_info pinctrl_info; +}; + +/** + * struct cam_pinctrl_res + * + * @dev : Pointer to the device + * @dt : Device tree resource + * @shared_gpio_enabled : The flag to indicate if support shared gpio + * @pstatus : Shared pinctrl status + * @gpio_res_list : List head of the gpio resource + * @flash_res_list : List head of the flash resource + * @gpio_res_lock : GPIO resource lock + * @flash_res_lock : Flash resource lock + * @clk_res_lock : Clk resource lock + */ +struct cam_res_mgr { + struct device *dev; + struct cam_res_mgr_dt dt; + + bool shared_gpio_enabled; + enum pinctrl_status pstatus; + + uint shared_clk_ref_count; + + struct list_head gpio_res_list; + struct list_head flash_res_list; + struct mutex gpio_res_lock; + struct mutex flash_res_lock; + struct mutex clk_res_lock; +}; + +#endif /* __CAM_RES_MGR_PRIVATE_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/CAM_SENSOR_SETTINGS.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/CAM_SENSOR_SETTINGS.h new file mode 100644 index 000000000000..69ccb965a7ea --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/CAM_SENSOR_SETTINGS.h @@ -0,0 +1,1018 @@ +.imx519_setting = { +.reg_setting = { +{.reg_addr = 0x0136, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0137, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7E, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7F, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3020, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E35, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5609, .reg_data = 0x57, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5613, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x561F, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5623, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5637, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5657, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5659, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5733, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5905, .reg_data = 0x57, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x590F, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x591B, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x591F, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5933, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5953, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5955, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5A2F, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5A85, .reg_data = 0x57, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5A8F, .reg_data = 0x51, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5A9B, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5A9F, .reg_data = 0xD2, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5AB3, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5AD3, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5AD5, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5BAF, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C15, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C17, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C19, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C1B, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C25, .reg_data = 0x25, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C27, .reg_data = 0x7B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C29, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C2B, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C2D, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C2F, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C35, .reg_data = 0x2B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C37, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C39, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C3B, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C45, .reg_data = 0x25, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C47, .reg_data = 0x7B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C49, .reg_data = 0x2A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C4B, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C4D, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C4F, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C55, .reg_data = 0x2D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C57, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C59, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C5B, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C65, .reg_data = 0x29, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C67, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C69, .reg_data = 0x2E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C6B, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C6D, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5C6F, .reg_data = 0x88, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5E69, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5E9D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F18, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F1A, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F20, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F22, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F24, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F28, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F2A, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F30, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F32, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F34, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F38, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F39, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F3C, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F3D, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F3E, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F61, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F64, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F67, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F6A, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F6D, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F70, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F73, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F76, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F79, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F7C, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F7F, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F82, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F85, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F88, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F8B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F8E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F91, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F94, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F97, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5F9D, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FA0, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FA3, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FA6, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FA9, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FAC, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FAF, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FB5, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FB8, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FBB, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FC1, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FC4, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FC7, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5FD1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6302, .reg_data = 0x79, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6305, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6306, .reg_data = 0xA5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6308, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6309, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630B, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630D, .reg_data = 0x48, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630F, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6311, .reg_data = 0xA4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6313, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6314, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6316, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6317, .reg_data = 0x31, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6318, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631A, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631B, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631C, .reg_data = 0xA4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631E, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631F, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6321, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6323, .reg_data = 0x4A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6328, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6329, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632A, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632D, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632E, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6330, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6332, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6333, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6334, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6335, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6336, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6338, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633A, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633C, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633D, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633E, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6340, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6341, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6342, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6343, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6344, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6345, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6346, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6348, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6349, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634A, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634C, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634E, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6350, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6351, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6352, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6353, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6354, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6355, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6356, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6357, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6358, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6359, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x635A, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x635B, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x635C, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x635F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6360, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6361, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6362, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6363, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6364, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6368, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636A, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636C, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637D, .reg_data = 0xE4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637E, .reg_data = 0xB4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638C, .reg_data = 0x8E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638D, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638E, .reg_data = 0xE3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638F, .reg_data = 0x4C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6390, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6391, .reg_data = 0xC3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6392, .reg_data = 0xAE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6393, .reg_data = 0xBA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6394, .reg_data = 0xEB, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6395, .reg_data = 0x6E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6396, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6397, .reg_data = 0xE3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6398, .reg_data = 0xCF, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6399, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639A, .reg_data = 0xF3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639B, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639C, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639D, .reg_data = 0xC1, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x63B9, .reg_data = 0xA3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x63BA, .reg_data = 0xFE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7600, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A0, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A1, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A2, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A3, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A4, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A5, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79A9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79AA, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79AD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x79AF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x8173, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x835C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x8A74, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x8C1F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x8C27, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x8C3B, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9004, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920C, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920D, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920E, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920F, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9214, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9215, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9216, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9217, .reg_data = 0x21, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9385, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9387, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x938D, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x938F, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9391, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9395, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9397, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9399, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939D, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939F, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93A5, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93A7, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93A9, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93AD, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93AF, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93B5, .reg_data = 0x3E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93B7, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93BD, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93BF, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93C1, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93C5, .reg_data = 0x4D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93C7, .reg_data = 0x43, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93C9, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x974B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x995C, .reg_data = 0x8C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x995D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x995E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9963, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9964, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA0A, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAE03, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAE04, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAE05, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC1C, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA06, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA07, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA08, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA12, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA13, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA14, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA1E, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA1F, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA20, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA2A, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA2B, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA2C, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC19, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC1B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC1D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC3C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC3D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC3E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC3F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC40, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC41, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC61, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC63, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC65, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC84, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC85, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC86, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC87, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC88, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAC89, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0111, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0112, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0113, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0114, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0342, .reg_data = 0x17, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0343, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0340, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0341, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0344, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0345, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0346, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0347, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0348, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0349, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034A, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034B, .reg_data = 0xA7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0220, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0221, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0222, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0900, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0901, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0902, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4254, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0401, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0404, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0405, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0408, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0409, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040C, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040D, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040E, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040F, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034C, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034D, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034E, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034F, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0301, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0303, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0305, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0306, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0307, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0309, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030F, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0310, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0820, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0821, .reg_data = 0xE4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0822, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0823, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E20, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E37, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E3B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0106, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B00, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3230, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F14, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F3C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F0D, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3FBC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C06, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C07, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C0A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C0B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F78, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F79, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0202, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0203, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0224, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0225, .reg_data = 0xF4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0204, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0205, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0216, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0217, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0218, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0219, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0100, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 387, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 1, +}, +.imx376k_setting = { +.reg_setting = { +{.reg_addr = 0x0136, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0137, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7D, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7E, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7F, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B06, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F02, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F22, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4421, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4430, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4431, .reg_data = 0xDC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5222, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x56B7, .reg_data = 0x74, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6204, .reg_data = 0xC6, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x620E, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6210, .reg_data = 0x69, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6211, .reg_data = 0xD6, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6213, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6215, .reg_data = 0x5A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6216, .reg_data = 0x75, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6218, .reg_data = 0x5A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6219, .reg_data = 0x75, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6220, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6222, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6225, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6228, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6229, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622B, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622E, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6231, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6234, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6236, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6237, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6239, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x623C, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x623F, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6240, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6242, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6243, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6245, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6246, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6248, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624B, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624D, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x625C, .reg_data = 0xC9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x625F, .reg_data = 0x92, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6262, .reg_data = 0x26, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6264, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6265, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6267, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626A, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626D, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626E, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6270, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6271, .reg_data = 0x68, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6273, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6276, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6279, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x627B, .reg_data = 0x46, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x627C, .reg_data = 0x55, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x627F, .reg_data = 0x95, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6282, .reg_data = 0x84, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6283, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6284, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6285, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6286, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6287, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6288, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6289, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628A, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628B, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628E, .reg_data = 0x35, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6290, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6291, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6292, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6293, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6294, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6296, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6297, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6298, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6299, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629A, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629E, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629F, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62A0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B3, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BB, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BF, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D0, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D2, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D4, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D6, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D7, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D8, .reg_data = 0xD8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DA, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DB, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DC, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DD, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DE, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62EF, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F0, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F1, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F3, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F4, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F5, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F6, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F7, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F8, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62F9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FA, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FB, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FC, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FE, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62FF, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6300, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6301, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6302, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6303, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6304, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6305, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6306, .reg_data = 0x1B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6307, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6308, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630A, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630C, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630E, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x630F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6310, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6312, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6313, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6314, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6315, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6316, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6317, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6318, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6319, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631A, .reg_data = 0x60, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631B, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x631C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632D, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x632F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6331, .reg_data = 0x44, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6332, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6333, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6334, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6335, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6336, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6337, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6338, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6339, .reg_data = 0xF0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x633B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634C, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x634E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6350, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6351, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6352, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6353, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6354, .reg_data = 0xD8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6355, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6356, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6357, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6358, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6359, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x635A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x636F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6370, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6371, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6372, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6373, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6374, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6375, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6376, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6377, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6378, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6379, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637A, .reg_data = 0x13, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637B, .reg_data = 0xD4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6388, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6389, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638A, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639D, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BA0, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BA9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BAA, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BAD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9002, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9003, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9004, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9006, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9200, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9201, .reg_data = 0x85, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9202, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9203, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9204, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9205, .reg_data = 0x8D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9206, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9207, .reg_data = 0x8F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9208, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9209, .reg_data = 0x2C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920A, .reg_data = 0x62, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920B, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920C, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920D, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920E, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920F, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9210, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9211, .reg_data = 0x09, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9212, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9213, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9214, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9215, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9216, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9217, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9218, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9219, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x935D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9389, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x938B, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9391, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9393, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9395, .reg_data = 0x65, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9397, .reg_data = 0x5A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9399, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939B, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939D, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939F, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93A1, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x93A3, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xB3F1, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xB3F2, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC40, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC82, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC83, .reg_data = 0xB0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC84, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC85, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xE0A6, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA3F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA41, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA43, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA5D, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA5F, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA61, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAACF, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAD1, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAD3, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAED, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAEF, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAF1, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xB6D9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0112, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0113, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0114, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0342, .reg_data = 0x15, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0343, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0340, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0341, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F39, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F3A, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F3B, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0344, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0345, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0346, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0347, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0348, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0349, .reg_data = 0x3F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034A, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034B, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0381, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0383, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0385, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0387, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0900, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0901, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0902, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4D, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4C, .reg_data = 0x81, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4254, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0401, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0404, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0405, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0408, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0409, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040C, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040D, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040E, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040F, .reg_data = 0x94, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034C, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034D, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034E, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034F, .reg_data = 0x94, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0301, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0303, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0305, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0306, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0307, .reg_data = 0xFA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030B, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030D, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030F, .reg_data = 0x5E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0310, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0820, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0821, .reg_data = 0xA0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0822, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0823, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC41, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0106, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B00, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B05, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3230, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3602, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3607, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C00, .reg_data = 0x74, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C01, .reg_data = 0x5F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C02, .reg_data = 0x73, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C03, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C04, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C05, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C06, .reg_data = 0xBE, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C07, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C08, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C09, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C0A, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C0B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C0C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E20, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E3D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F14, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F17, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F3C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F78, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F79, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7A, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7B, .reg_data = 0xBC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x562B, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x562D, .reg_data = 0x34, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5617, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7849, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9104, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0202, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0203, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0204, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0205, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0100, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 375, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 1, +}, +.imx371_setting = { +.reg_setting = { +{.reg_addr = 0x0136, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0137, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7D, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C7F, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F02, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7F, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4421, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4430, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4431, .reg_data = 0xDC, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5222, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x562B, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x562D, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x56B7, .reg_data = 0x74, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6200, .reg_data = 0x95, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6201, .reg_data = 0xB9, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6202, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6220, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6222, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6225, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6228, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6229, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622A, .reg_data = 0xC3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622B, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622C, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622E, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x622F, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6231, .reg_data = 0x73, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6233, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6234, .reg_data = 0xEA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6235, .reg_data = 0x8C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6236, .reg_data = 0x35, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6237, .reg_data = 0xC5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6239, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x623A, .reg_data = 0x96, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x623C, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x623F, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6240, .reg_data = 0x59, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6241, .reg_data = 0x83, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6242, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6243, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6245, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6246, .reg_data = 0xA8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6248, .reg_data = 0x53, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624A, .reg_data = 0x42, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624B, .reg_data = 0xA5, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624C, .reg_data = 0x98, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x624D, .reg_data = 0x35, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6265, .reg_data = 0x45, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6267, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626A, .reg_data = 0x19, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626D, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626E, .reg_data = 0x72, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x626F, .reg_data = 0xC3, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6270, .reg_data = 0x64, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6271, .reg_data = 0x58, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6273, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6276, .reg_data = 0x91, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6279, .reg_data = 0x27, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x627B, .reg_data = 0x36, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6282, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6283, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6286, .reg_data = 0x07, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6287, .reg_data = 0xC0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6288, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628A, .reg_data = 0x18, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628B, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628C, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x628E, .reg_data = 0x32, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6290, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6292, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6296, .reg_data = 0x50, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629A, .reg_data = 0xF8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x629F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B1, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B5, .reg_data = 0x3C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62B9, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BC, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62BD, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D0, .reg_data = 0x06, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D4, .reg_data = 0x38, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62D8, .reg_data = 0xB8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DB, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DC, .reg_data = 0x40, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x62DD, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637A, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x637B, .reg_data = 0xD4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6388, .reg_data = 0x22, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x6389, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x638A, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x639D, .reg_data = 0x20, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BA0, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BA9, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BAA, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x7BAD, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9002, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9003, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9004, .reg_data = 0x08, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9006, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9200, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9201, .reg_data = 0x85, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9202, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9203, .reg_data = 0x87, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9204, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9205, .reg_data = 0x8D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9206, .reg_data = 0x93, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9207, .reg_data = 0x8F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9208, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9209, .reg_data = 0x23, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920A, .reg_data = 0x6A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920B, .reg_data = 0x0F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920C, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920D, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920E, .reg_data = 0x71, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x920F, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x935D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9389, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x938B, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9391, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9393, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9395, .reg_data = 0x82, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9397, .reg_data = 0x78, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x9399, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x939B, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA3F, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA41, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA43, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA5D, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA5F, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAA61, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAACF, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAD1, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAD3, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAED, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAEF, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xAAF1, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xB388, .reg_data = 0x28, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC40, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xE0A5, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xE0A6, .reg_data = 0x0B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x5617, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0112, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0113, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0114, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0342, .reg_data = 0x17, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0343, .reg_data = 0x90, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0340, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0341, .reg_data = 0xE0, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0344, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0345, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0346, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0347, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0348, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0349, .reg_data = 0x2F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034A, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034B, .reg_data = 0xA7, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0381, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0383, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0385, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0387, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0900, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0901, .reg_data = 0x11, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0902, .reg_data = 0x0A, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F4D, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x4254, .reg_data = 0x7F, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0401, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0404, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0405, .reg_data = 0x10, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0408, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0409, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040A, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040B, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040C, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040D, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040E, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x040F, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0xBC41, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034C, .reg_data = 0x12, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034D, .reg_data = 0x30, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034E, .reg_data = 0x0D, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x034F, .reg_data = 0x80, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0301, .reg_data = 0x05, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0303, .reg_data = 0x02, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0305, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0306, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0307, .reg_data = 0x0C, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030B, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030D, .reg_data = 0x04, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030E, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x030F, .reg_data = 0xDA, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0310, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0820, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0821, .reg_data = 0x70, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0822, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0823, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E20, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E37, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3E3B, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3614, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3616, .reg_data = 0x0E, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3617, .reg_data = 0x66, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0106, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B00, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B05, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0B06, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3230, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3602, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C00, .reg_data = 0x5B, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C01, .reg_data = 0x54, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C02, .reg_data = 0x77, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C03, .reg_data = 0x66, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C04, .reg_data = 0xC8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C05, .reg_data = 0x14, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C06, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3C07, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F14, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F17, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F3C, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F78, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F79, .reg_data = 0xB4, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7C, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x3F7D, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0202, .reg_data = 0x03, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0203, .reg_data = 0xE8, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0204, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0205, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020E, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x020F, .reg_data = 0x00, .delay = 0x00, .data_mask = 0x00}, +{.reg_addr = 0x0100, .reg_data = 0x01, .delay = 0x00, .data_mask = 0x00}, +}, +.size = 232, +.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, +.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, +.delay = 1, +}, \ No newline at end of file diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/Makefile new file mode 100644 index 000000000000..37cc8068b202 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/Makefile @@ -0,0 +1,10 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_dev.o cam_sensor_core.o cam_sensor_soc.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.c new file mode 100644 index 000000000000..a153f2fa7df8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.c @@ -0,0 +1,1467 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. 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 "cam_sensor_core.h" +#include "cam_sensor_util.h" +#include "cam_soc_util.h" +#include "cam_trace.h" + +#ifdef CONFIG_PROJECT_INFO +#include + +struct camera_vendor_match_tbl { + uint16_t sensor_id; + char sensor_name[32]; + char vendor_name[32]; +}; +static struct camera_vendor_match_tbl match_tbl[] = { + {0x519, "imx519", "Sony"}, + {0x376, "imx376k", "Sony"}, + {0x371, "imx371", "Sony"}, +}; +#endif + +static struct cam_sensor_i2c_reg_array lotid_on_setting[2] = { + { + .reg_addr = 0x0A02, + .reg_data = 0x27, + .delay = 0x01, + .data_mask = 0x00 + }, + { + .reg_addr = 0x0A00, + .reg_data = 0x01, + .delay = 0x01, + .data_mask = 0x00 + }, +}; + +static struct cam_sensor_i2c_reg_array lotid_off_setting = { + .reg_addr = 0x0A00, + .reg_data = 0x00, + .delay = 0x01, + .data_mask = 0x00 +}; + +static struct cam_sensor_i2c_reg_setting lotid_on = { + .reg_setting = lotid_on_setting, + .size = 2, + .addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, + .data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, + .delay = 5, +}; + +static struct cam_sensor_i2c_reg_setting lotid_off = { + .reg_setting = &lotid_off_setting, + .size = 1, + .addr_type = CAMERA_SENSOR_I2C_TYPE_WORD, + .data_type = CAMERA_SENSOR_I2C_TYPE_BYTE, + .delay = 5, +}; + +#define LOTID_START_ADDR 0x0A20 +#define LOTID_LENGTH 8 + +static char fuse_id[64] = {'\0'}; + +static int sensor_get_fuseid(struct cam_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t lotid_addr = LOTID_START_ADDR; + struct cam_camera_slave_info *slave_info; + + int i = 0; + uint32_t check_reg_val = 0; + int retry_cnt = 5; + char str_tmp[6] = {'\0'}; + + slave_info = &(s_ctrl->sensordata->slave_info); + if (!slave_info) { + CAM_ERR(CAM_SENSOR, "slave_info is NULL: %pK", + slave_info); + return -EINVAL; + } + + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + 0x0A01, + &check_reg_val, CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_BYTE); + + //enable read lot id + rc = camera_io_dev_write( + &(s_ctrl->io_master_info), + &lotid_on); + + //verify lot id availability + for (i = 0; i < retry_cnt; i++) { + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + 0x0A01, + &check_reg_val, CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_BYTE); + if (check_reg_val & (0x1)) + break; + } + + if (i == retry_cnt) { + CAM_ERR(CAM_SENSOR, "lot id not available"); + return -EINVAL; + } + + //read lot id + for (i = 0; i < LOTID_LENGTH; i++) { + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + lotid_addr+i, + &check_reg_val, CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_BYTE); + snprintf(str_tmp, sizeof(str_tmp), "%02x", + (check_reg_val&0x00FF)); + strlcat(fuse_id, str_tmp, sizeof(fuse_id)); + } + + //disable read lot id + rc = camera_io_dev_write( + &(s_ctrl->io_master_info), + &lotid_off); + + return 0; +} + +static void cam_sensor_update_req_mgr( + struct cam_sensor_ctrl_t *s_ctrl, + struct cam_packet *csl_packet) +{ + struct cam_req_mgr_add_request add_req; + + add_req.link_hdl = s_ctrl->bridge_intf.link_hdl; + add_req.req_id = csl_packet->header.request_id; + CAM_DBG(CAM_SENSOR, " Rxed Req Id: %lld", + csl_packet->header.request_id); + add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl; + add_req.skip_before_applying = 0; + if (s_ctrl->bridge_intf.crm_cb && + s_ctrl->bridge_intf.crm_cb->add_req) + s_ctrl->bridge_intf.crm_cb->add_req(&add_req); + + CAM_DBG(CAM_SENSOR, " add req to req mgr: %lld", + add_req.req_id); +} + +static void cam_sensor_release_stream_rsc( + struct cam_sensor_ctrl_t *s_ctrl) +{ + struct i2c_settings_array *i2c_set = NULL; + int rc; + + i2c_set = &(s_ctrl->i2c_data.streamoff_settings); + if (i2c_set->is_settings_valid == 1) { + i2c_set->is_settings_valid = -1; + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "failed while deleting Streamoff settings"); + } + + i2c_set = &(s_ctrl->i2c_data.streamon_settings); + if (i2c_set->is_settings_valid == 1) { + i2c_set->is_settings_valid = -1; + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "failed while deleting Streamon settings"); + } +} + +static void cam_sensor_release_resource( + struct cam_sensor_ctrl_t *s_ctrl) +{ + struct i2c_settings_array *i2c_set = NULL; + int i, rc; + + i2c_set = &(s_ctrl->i2c_data.init_settings); + if (i2c_set->is_settings_valid == 1) { + i2c_set->is_settings_valid = -1; + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "failed while deleting Init settings"); + } + + i2c_set = &(s_ctrl->i2c_data.config_settings); + if (i2c_set->is_settings_valid == 1) { + i2c_set->is_settings_valid = -1; + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "failed while deleting Res settings"); + } + + if (s_ctrl->i2c_data.per_frame != NULL) { + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + i2c_set = &(s_ctrl->i2c_data.per_frame[i]); + if (i2c_set->is_settings_valid == 1) { + i2c_set->is_settings_valid = -1; + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "delete request: %lld rc: %d", + i2c_set->request_id, rc); + } + } + } +} + +static int32_t cam_sensor_i2c_pkt_parse(struct cam_sensor_ctrl_t *s_ctrl, + void *arg) +{ + int32_t rc = 0; + uintptr_t generic_ptr; + struct cam_control *ioctl_ctrl = NULL; + struct cam_packet *csl_packet = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + struct i2c_settings_array *i2c_reg_settings = NULL; + size_t len_of_buff = 0; + uint32_t *offset = NULL; + struct cam_config_dev_cmd config; + struct i2c_data_settings *i2c_data = NULL; + + ioctl_ctrl = (struct cam_control *)arg; + + if (ioctl_ctrl->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_SENSOR, "Invalid Handle Type"); + return -EINVAL; + } + + if (copy_from_user(&config, + u64_to_user_ptr(ioctl_ctrl->handle), + sizeof(config))) + return -EFAULT; + + rc = cam_mem_get_cpu_buf( + config.packet_handle, + &generic_ptr, + &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed in getting the buffer: %d", rc); + return rc; + } + + csl_packet = (struct cam_packet *)(generic_ptr + + (uint32_t)config.offset); + + if (config.offset > len_of_buff) { + CAM_ERR(CAM_SENSOR, + "offset is out of bounds: off: %lld len: %zu", + config.offset, len_of_buff); + return -EINVAL; + } + + i2c_data = &(s_ctrl->i2c_data); + CAM_DBG(CAM_SENSOR, "Header OpCode: %d", csl_packet->header.op_code); + switch (csl_packet->header.op_code & 0xFFFFFF) { + case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: { + i2c_reg_settings = &i2c_data->init_settings; + i2c_reg_settings->request_id = 0; + i2c_reg_settings->is_settings_valid = 1; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG: { + i2c_reg_settings = &i2c_data->config_settings; + i2c_reg_settings->request_id = 0; + i2c_reg_settings->is_settings_valid = 1; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: { + if (s_ctrl->streamon_count > 0) + return 0; + + s_ctrl->streamon_count = s_ctrl->streamon_count + 1; + i2c_reg_settings = &i2c_data->streamon_settings; + i2c_reg_settings->request_id = 0; + i2c_reg_settings->is_settings_valid = 1; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: { + if (s_ctrl->streamoff_count > 0) + return 0; + + s_ctrl->streamoff_count = s_ctrl->streamoff_count + 1; + i2c_reg_settings = &i2c_data->streamoff_settings; + i2c_reg_settings->request_id = 0; + i2c_reg_settings->is_settings_valid = 1; + break; + } + + case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE: { + if ((s_ctrl->sensor_state == CAM_SENSOR_INIT) || + (s_ctrl->sensor_state == CAM_SENSOR_ACQUIRE)) { + CAM_WARN(CAM_SENSOR, + "Rxed Update packets without linking"); + return 0; + } + + i2c_reg_settings = + &i2c_data-> + per_frame[csl_packet->header.request_id % + MAX_PER_FRAME_ARRAY]; + CAM_DBG(CAM_SENSOR, "Received Packet: %lld req: %lld", + csl_packet->header.request_id % MAX_PER_FRAME_ARRAY, + csl_packet->header.request_id); + if (i2c_reg_settings->is_settings_valid == 1) { + CAM_ERR(CAM_SENSOR, + "Already some pkt in offset req : %lld", + csl_packet->header.request_id); + /* + * Update req mgr even in case of failure. + * This will help not to wait indefinitely + * and freeze. If this log is triggered then + * fix it. + */ + cam_sensor_update_req_mgr(s_ctrl, csl_packet); + return 0; + } + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP: { + if ((s_ctrl->sensor_state == CAM_SENSOR_INIT) || + (s_ctrl->sensor_state == CAM_SENSOR_ACQUIRE)) { + CAM_WARN(CAM_SENSOR, + "Rxed NOP packets without linking"); + return 0; + } + + cam_sensor_update_req_mgr(s_ctrl, csl_packet); + return 0; + } + default: + CAM_ERR(CAM_SENSOR, "Invalid Packet Header"); + return -EINVAL; + } + + offset = (uint32_t *)&csl_packet->payload; + offset += csl_packet->cmd_buf_offset / 4; + cmd_desc = (struct cam_cmd_buf_desc *)(offset); + + rc = cam_sensor_i2c_command_parser(i2c_reg_settings, cmd_desc, 1); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Fail parsing I2C Pkt: %d", rc); + return rc; + } + + if ((csl_packet->header.op_code & 0xFFFFFF) == + CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) { + i2c_reg_settings->request_id = + csl_packet->header.request_id; + cam_sensor_update_req_mgr(s_ctrl, csl_packet); + } + + return rc; +} + +static int32_t cam_sensor_i2c_modes_util( + struct camera_io_master *io_master_info, + struct i2c_settings_list *i2c_list) +{ + int32_t rc = 0; + uint32_t i, size; + + if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) { + rc = camera_io_dev_write(io_master_info, + &(i2c_list->i2c_settings)); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to random write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_SEQ) { + rc = camera_io_dev_write_continuous( + io_master_info, + &(i2c_list->i2c_settings), + 0); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to seq write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_BURST) { + rc = camera_io_dev_write_continuous( + io_master_info, + &(i2c_list->i2c_settings), + 1); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to burst write I2C settings: %d", + rc); + return rc; + } + } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) { + size = i2c_list->i2c_settings.size; + for (i = 0; i < size; i++) { + rc = camera_io_dev_poll( + io_master_info, + i2c_list->i2c_settings. + reg_setting[i].reg_addr, + i2c_list->i2c_settings. + reg_setting[i].reg_data, + i2c_list->i2c_settings. + reg_setting[i].data_mask, + i2c_list->i2c_settings.addr_type, + i2c_list->i2c_settings.data_type, + i2c_list->i2c_settings. + reg_setting[i].delay); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "i2c poll apply setting Fail: %d", rc); + return rc; + } + } + } + + return rc; +} + +int32_t cam_sensor_update_i2c_info(struct cam_cmd_i2c_info *i2c_info, + struct cam_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct cam_sensor_cci_client *cci_client = NULL; + + if (s_ctrl->io_master_info.master_type == CCI_MASTER) { + cci_client = s_ctrl->io_master_info.cci_client; + if (!cci_client) { + CAM_ERR(CAM_SENSOR, "failed: cci_client %pK", + cci_client); + return -EINVAL; + } + cci_client->cci_i2c_master = s_ctrl->cci_i2c_master; + cci_client->sid = i2c_info->slave_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = i2c_info->i2c_freq_mode; + CAM_DBG(CAM_SENSOR, " Master: %d sid: %d freq_mode: %d", + cci_client->cci_i2c_master, i2c_info->slave_addr, + i2c_info->i2c_freq_mode); + } + + s_ctrl->sensordata->slave_info.sensor_slave_addr = + i2c_info->slave_addr; + return rc; +} + +int32_t cam_sensor_update_slave_info(struct cam_cmd_probe *probe_info, + struct cam_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + + s_ctrl->sensordata->slave_info.sensor_id_reg_addr = + probe_info->reg_addr; + s_ctrl->sensordata->slave_info.sensor_id = + probe_info->expected_data; + s_ctrl->sensordata->slave_info.sensor_id_mask = + probe_info->data_mask; + + s_ctrl->sensor_probe_addr_type = probe_info->addr_type; + s_ctrl->sensor_probe_data_type = probe_info->data_type; + CAM_DBG(CAM_SENSOR, + "Sensor Addr: 0x%x sensor_id: 0x%x sensor_mask: 0x%x", + s_ctrl->sensordata->slave_info.sensor_id_reg_addr, + s_ctrl->sensordata->slave_info.sensor_id, + s_ctrl->sensordata->slave_info.sensor_id_mask); + return rc; +} + +int32_t cam_sensor_update_id_info(struct cam_cmd_probe *probe_info, + struct cam_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + + s_ctrl->sensordata->id_info.sensor_slave_addr = + probe_info->reserved; + s_ctrl->sensordata->id_info.sensor_id_reg_addr = + probe_info->reg_addr; + s_ctrl->sensordata->id_info.sensor_id_mask = + probe_info->data_mask; + s_ctrl->sensordata->id_info.sensor_id = + probe_info->expected_data; + s_ctrl->sensordata->id_info.sensor_addr_type = + probe_info->addr_type; + s_ctrl->sensordata->id_info.sensor_data_type = + probe_info->data_type; + + CAM_ERR(CAM_SENSOR, + "Sensor Addr: 0x%x sensor_id: 0x%x sensor_mask: 0x%x", + s_ctrl->sensordata->id_info.sensor_id_reg_addr, + s_ctrl->sensordata->id_info.sensor_id, + s_ctrl->sensordata->id_info.sensor_id_mask); + return rc; +} + +int32_t cam_handle_cmd_buffers_for_probe(void *cmd_buf, + struct cam_sensor_ctrl_t *s_ctrl, + int32_t cmd_buf_num, int cmd_buf_length) +{ + int32_t rc = 0; + + switch (cmd_buf_num) { + case 0: { + struct cam_cmd_i2c_info *i2c_info = NULL; + struct cam_cmd_probe *probe_info; + + i2c_info = (struct cam_cmd_i2c_info *)cmd_buf; + rc = cam_sensor_update_i2c_info(i2c_info, s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed in Updating the i2c Info"); + return rc; + } + probe_info = (struct cam_cmd_probe *) + (cmd_buf + sizeof(struct cam_cmd_i2c_info)); + rc = cam_sensor_update_slave_info(probe_info, s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Updating the slave Info"); + return rc; + } + probe_info = (struct cam_cmd_probe *) + (cmd_buf + sizeof(struct cam_cmd_i2c_info) + sizeof(struct cam_cmd_probe)); + rc = cam_sensor_update_id_info(probe_info, s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Updating the id Info"); + return rc; + } + cmd_buf = probe_info; + } + break; + case 1: { + rc = cam_sensor_update_power_settings(cmd_buf, + cmd_buf_length, &s_ctrl->sensordata->power_info); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed in updating power settings"); + return rc; + } + } + break; + default: + CAM_ERR(CAM_SENSOR, "Invalid command buffer"); + break; + } + return rc; +} + +int32_t cam_handle_mem_ptr(uint64_t handle, struct cam_sensor_ctrl_t *s_ctrl) +{ + int rc = 0, i; + uint32_t *cmd_buf; + void *ptr; + size_t len; + struct cam_packet *pkt = NULL; + struct cam_cmd_buf_desc *cmd_desc = NULL; + uintptr_t cmd_buf1 = 0; + uintptr_t packet = 0; + + rc = cam_mem_get_cpu_buf(handle, + &packet, &len); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed to get the command Buffer"); + return -EINVAL; + } + pkt = (struct cam_packet *)packet; + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *)&pkt->payload + pkt->cmd_buf_offset/4); + if (cmd_desc == NULL) { + CAM_ERR(CAM_SENSOR, "command descriptor pos is invalid"); + return -EINVAL; + } + if (pkt->num_cmd_buf != 2) { + CAM_ERR(CAM_SENSOR, "Expected More Command Buffers : %d", + pkt->num_cmd_buf); + return -EINVAL; + } + for (i = 0; i < pkt->num_cmd_buf; i++) { + if (!(cmd_desc[i].length)) + continue; + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &cmd_buf1, &len); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to parse the command Buffer Header"); + return -EINVAL; + } + cmd_buf = (uint32_t *)cmd_buf1; + cmd_buf += cmd_desc[i].offset/4; + ptr = (void *) cmd_buf; + + rc = cam_handle_cmd_buffers_for_probe(ptr, s_ctrl, + i, cmd_desc[i].length); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to parse the command Buffer Header"); + return -EINVAL; + } + } + return rc; +} + +void cam_sensor_query_cap(struct cam_sensor_ctrl_t *s_ctrl, + struct cam_sensor_query_cap *query_cap) +{ + query_cap->pos_roll = s_ctrl->sensordata->pos_roll; + query_cap->pos_pitch = s_ctrl->sensordata->pos_pitch; + query_cap->pos_yaw = s_ctrl->sensordata->pos_yaw; + query_cap->secure_camera = 0; + query_cap->actuator_slot_id = + s_ctrl->sensordata->subdev_id[SUB_MODULE_ACTUATOR]; + query_cap->csiphy_slot_id = + s_ctrl->sensordata->subdev_id[SUB_MODULE_CSIPHY]; + query_cap->eeprom_slot_id = + s_ctrl->sensordata->subdev_id[SUB_MODULE_EEPROM]; + query_cap->flash_slot_id = + s_ctrl->sensordata->subdev_id[SUB_MODULE_LED_FLASH]; + query_cap->ois_slot_id = + s_ctrl->sensordata->subdev_id[SUB_MODULE_OIS]; + query_cap->slot_info = + s_ctrl->soc_info.index; +} + +static uint16_t cam_sensor_id_by_mask(struct cam_sensor_ctrl_t *s_ctrl, + uint32_t chipid) +{ + uint16_t sensor_id = (uint16_t)(chipid & 0xFFFF); + int16_t sensor_id_mask = s_ctrl->sensordata->slave_info.sensor_id_mask; + + if (!sensor_id_mask) + sensor_id_mask = ~sensor_id_mask; + + sensor_id &= sensor_id_mask; + sensor_id_mask &= -sensor_id_mask; + sensor_id_mask -= 1; + while (sensor_id_mask) { + sensor_id_mask >>= 1; + sensor_id >>= 1; + } + return sensor_id; +} + +void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl) +{ + struct cam_sensor_power_ctrl_t *power_info = + &s_ctrl->sensordata->power_info; + int rc = 0; + + s_ctrl->is_probe_succeed = 0; + if (s_ctrl->sensor_state == CAM_SENSOR_INIT) + return; + + CAM_INFO(CAM_SENSOR, "streamoff Sensor"); + rc = cam_sensor_apply_settings(s_ctrl, 0, + CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF); + if (rc < 0) { + CAM_INFO(CAM_SENSOR, "non-fatal cannot apply streamoff settings"); + } + + cam_sensor_release_resource(s_ctrl); + cam_sensor_release_stream_rsc(s_ctrl); + if (s_ctrl->sensor_state >= CAM_SENSOR_ACQUIRE) + cam_sensor_power_down(s_ctrl); + + rc = cam_destroy_device_hdl(s_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_SENSOR, " failed destroying dhdl"); + s_ctrl->bridge_intf.device_hdl = -1; + s_ctrl->bridge_intf.link_hdl = -1; + s_ctrl->bridge_intf.session_hdl = -1; + + kfree(power_info->power_setting); + kfree(power_info->power_down_setting); + + s_ctrl->streamon_count = 0; + s_ctrl->streamoff_count = 0; + s_ctrl->sensor_state = CAM_SENSOR_INIT; +} + +int cam_sensor_match_id(struct cam_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint32_t chipid = 0; + uint32_t sensor_version = 0; + uint16_t sensor_version_reg = 0x0018; + uint16_t sensor_slave_addr = 0; + uint32_t id = 0; + uint32_t id_l = 0; + uint32_t id_h = 0; + struct cam_camera_slave_info *slave_info; + + slave_info = &(s_ctrl->sensordata->slave_info); + + if (!slave_info) { + CAM_ERR(CAM_SENSOR, " failed: %pK", + slave_info); + return -EINVAL; + } + + if (((s_ctrl->sensordata->id_info.sensor_id_reg_addr & 0xFFFF) != 0) && ((s_ctrl->sensordata->id_info.sensor_id_reg_addr >> 16) != 0)) { + sensor_slave_addr = s_ctrl->io_master_info.cci_client->sid; + s_ctrl->io_master_info.cci_client->sid = s_ctrl->sensordata->id_info.sensor_slave_addr >> 1; + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + s_ctrl->sensordata->id_info.sensor_id_reg_addr & 0xFFFF, + &id_l, s_ctrl->sensordata->id_info.sensor_addr_type, + s_ctrl->sensordata->id_info.sensor_data_type); + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + s_ctrl->sensordata->id_info.sensor_id_reg_addr >> 16, + &id_h, s_ctrl->sensordata->id_info.sensor_addr_type, + s_ctrl->sensordata->id_info.sensor_data_type); + s_ctrl->io_master_info.cci_client->sid = sensor_slave_addr; + id = (id_h << 8) | id_l; + if (s_ctrl->sensordata->id_info.sensor_id != id) { + CAM_ERR(CAM_SENSOR, "sensor id 0x%x does not match 0x%x", id, s_ctrl->sensordata->id_info.sensor_id); + return -ENODEV; + } else { + CAM_ERR(CAM_SENSOR, "sensor id 0x%x matched", id); + } + } else { + CAM_ERR(CAM_SENSOR, "sensor id register is 0x%x", s_ctrl->sensordata->id_info.sensor_id_reg_addr); + } + + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + slave_info->sensor_id_reg_addr, + &chipid, CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_WORD); + + CAM_DBG(CAM_SENSOR, "read id: 0x%x expected id 0x%x:", + chipid, slave_info->sensor_id); + + if (chipid == 0x519) { + rc = camera_io_dev_read( + &(s_ctrl->io_master_info), + sensor_version_reg, + &sensor_version, CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_WORD); + + CAM_INFO(CAM_SENSOR, "imx519 sensor_version: 0x%x", + sensor_version >> 8); + } + + if (cam_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) { + CAM_ERR(CAM_SENSOR, "chip id %x does not match %x", + chipid, slave_info->sensor_id); + return -ENODEV; + } + if (slave_info->sensor_id == 0x519 && fuse_id[0] == '\0') { + sensor_get_fuseid(s_ctrl); + CAM_ERR(CAM_SENSOR, + "sensor_id: 0x%x, fuse_id:%s", + slave_info->sensor_id, fuse_id); + } + return rc; +} + +int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, + void *arg) +{ + int rc = 0; + struct cam_control *cmd = (struct cam_control *)arg; + struct cam_sensor_power_setting *pu = NULL; + struct cam_sensor_power_setting *pd = NULL; + struct cam_sensor_power_ctrl_t *power_info = NULL; +#ifdef CONFIG_PROJECT_INFO + uint32_t count = 0, i; + enum COMPONENT_TYPE CameraID; +#endif + + if (!s_ctrl || !arg) { + CAM_ERR(CAM_SENSOR, "s_ctrl is NULL"); + return -EINVAL; + } + + power_info = &s_ctrl->sensordata->power_info; + + if (cmd->op_code != CAM_SENSOR_PROBE_CMD) { + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_SENSOR, "Invalid handle type: %d", + cmd->handle_type); + return -EINVAL; + } + } + + mutex_lock(&(s_ctrl->cam_sensor_mutex)); + switch (cmd->op_code) { + case CAM_SENSOR_PROBE_CMD: { + if (s_ctrl->is_probe_succeed == 1) { + CAM_ERR(CAM_SENSOR, + "Already Sensor Probed in the slot"); + break; + } + /* Allocate memory for power up setting */ + pu = kzalloc(sizeof(struct cam_sensor_power_setting) * + MAX_POWER_CONFIG, GFP_KERNEL); + if (!pu) { + rc = -ENOMEM; + goto release_mutex; + } + + pd = kzalloc(sizeof(struct cam_sensor_power_setting) * + MAX_POWER_CONFIG, GFP_KERNEL); + if (!pd) { + kfree(pu); + rc = -ENOMEM; + goto release_mutex; + } + + power_info->power_setting = pu; + power_info->power_down_setting = pd; + + if (cmd->handle_type == + CAM_HANDLE_MEM_HANDLE) { + rc = cam_handle_mem_ptr(cmd->handle, s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Get Buffer Handle Failed"); + kfree(pu); + kfree(pd); + goto release_mutex; + } + } else { + CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d", + cmd->handle_type); + return -EINVAL; + } + + /* Parse and fill vreg params for powerup settings */ + rc = msm_camera_fill_vreg_params( + &s_ctrl->soc_info, + s_ctrl->sensordata->power_info.power_setting, + s_ctrl->sensordata->power_info.power_setting_size); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Fail in filling vreg params for PUP rc %d", + rc); + kfree(pu); + kfree(pd); + goto release_mutex; + } + + /* Parse and fill vreg params for powerdown settings*/ + rc = msm_camera_fill_vreg_params( + &s_ctrl->soc_info, + s_ctrl->sensordata->power_info.power_down_setting, + s_ctrl->sensordata->power_info.power_down_setting_size); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Fail in filling vreg params for PDOWN rc %d", + rc); + kfree(pu); + kfree(pd); + goto release_mutex; + } + + /* Power up and probe sensor */ + rc = cam_sensor_power_up(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "power up failed"); + kfree(pu); + kfree(pd); + goto release_mutex; + } + + /* Match sensor ID */ + rc = cam_sensor_match_id(s_ctrl); + if (rc < 0) { + cam_sensor_power_down(s_ctrl); + msleep(20); + kfree(pu); + kfree(pd); + goto release_mutex; + } + + CAM_INFO(CAM_SENSOR, + "Probe Succees,slot:%d,slave_addr:0x%x,sensor_id:0x%x", + s_ctrl->soc_info.index, + s_ctrl->sensordata->slave_info.sensor_slave_addr, + s_ctrl->sensordata->slave_info.sensor_id); + + rc = cam_sensor_power_down(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "fail in Sensor Power Down"); + kfree(pu); + kfree(pd); + goto release_mutex; + } + /* + * Set probe succeeded flag to 1 so that no other camera shall + * probed on this slot + */ + s_ctrl->is_probe_succeed = 1; + s_ctrl->sensor_state = CAM_SENSOR_INIT; + +#ifdef CONFIG_PROJECT_INFO + if (s_ctrl->id == 0) + CameraID = R_CAMERA; + else if (s_ctrl->id == 1) + CameraID = SECOND_R_CAMERA; + else if (s_ctrl->id == 2) + CameraID = F_CAMERA; + else + CameraID = -1; + + count = ARRAY_SIZE(match_tbl); + for (i = 0; i < count; i++) { + if (s_ctrl->sensordata->slave_info.sensor_id + == match_tbl[i].sensor_id) + break; + } + if (i >= count) + CAM_ERR(CAM_SENSOR, "current sensor name is 0x%x", + s_ctrl->sensordata->slave_info.sensor_id); + else + push_component_info(CameraID, match_tbl[i].sensor_name, + match_tbl[i].vendor_name); +#endif + } + break; + case CAM_ACQUIRE_DEV: { + struct cam_sensor_acquire_dev sensor_acq_dev; + struct cam_create_dev_hdl bridge_params; + + if (s_ctrl->bridge_intf.device_hdl != -1) { + CAM_ERR(CAM_SENSOR, "Device is already acquired"); + rc = -EINVAL; + goto release_mutex; + } + + rc = copy_from_user(&sensor_acq_dev, + u64_to_user_ptr(cmd->handle), + sizeof(sensor_acq_dev)); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed Copying from user"); + goto release_mutex; + } + + bridge_params.session_hdl = sensor_acq_dev.session_handle; + bridge_params.ops = &s_ctrl->bridge_intf.ops; + bridge_params.v4l2_sub_dev_flag = 0; + bridge_params.media_entity_flag = 0; + bridge_params.priv = s_ctrl; + + sensor_acq_dev.device_handle = + cam_create_device_hdl(&bridge_params); + s_ctrl->bridge_intf.device_hdl = sensor_acq_dev.device_handle; + s_ctrl->bridge_intf.session_hdl = sensor_acq_dev.session_handle; + + CAM_DBG(CAM_SENSOR, "Device Handle: %d", + sensor_acq_dev.device_handle); + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &sensor_acq_dev, + sizeof(struct cam_sensor_acquire_dev))) { + CAM_ERR(CAM_SENSOR, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + + rc = cam_sensor_power_up(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Sensor Power up failed"); + goto release_mutex; + } + + s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE; + CAM_INFO(CAM_SENSOR, + "CAM_ACQUIRE_DEV Success, sensor_id:0x%x", + s_ctrl->sensordata->slave_info.sensor_id); + } + break; + case CAM_RELEASE_DEV: { + if ((s_ctrl->sensor_state == CAM_SENSOR_INIT) || + (s_ctrl->sensor_state == CAM_SENSOR_START)) { + rc = -EINVAL; + CAM_WARN(CAM_SENSOR, + "Not in right state to release : %d", + s_ctrl->sensor_state); + goto release_mutex; + } + + rc = cam_sensor_power_down(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Sensor Power Down failed"); + goto release_mutex; + } + + cam_sensor_release_resource(s_ctrl); + cam_sensor_release_stream_rsc(s_ctrl); + if (s_ctrl->bridge_intf.device_hdl == -1) { + CAM_ERR(CAM_SENSOR, + "Invalid Handles: link hdl: %d device hdl: %d", + s_ctrl->bridge_intf.device_hdl, + s_ctrl->bridge_intf.link_hdl); + rc = -EINVAL; + goto release_mutex; + } + rc = cam_destroy_device_hdl(s_ctrl->bridge_intf.device_hdl); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "failed in destroying the device hdl"); + s_ctrl->bridge_intf.device_hdl = -1; + s_ctrl->bridge_intf.link_hdl = -1; + s_ctrl->bridge_intf.session_hdl = -1; + + s_ctrl->sensor_state = CAM_SENSOR_INIT; + CAM_INFO(CAM_SENSOR, + "CAM_RELEASE_DEV Success, sensor_id:0x%x", + s_ctrl->sensordata->slave_info.sensor_id); + s_ctrl->streamon_count = 0; + s_ctrl->streamoff_count = 0; + } + break; + case CAM_QUERY_CAP: { + struct cam_sensor_query_cap sensor_cap; + + cam_sensor_query_cap(s_ctrl, &sensor_cap); + if (copy_to_user(u64_to_user_ptr(cmd->handle), + &sensor_cap, sizeof(struct cam_sensor_query_cap))) { + CAM_ERR(CAM_SENSOR, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + break; + } + case CAM_START_DEV: { + if ((s_ctrl->sensor_state == CAM_SENSOR_INIT) || + (s_ctrl->sensor_state == CAM_SENSOR_START)) { + rc = -EINVAL; + CAM_WARN(CAM_SENSOR, + "Not in right state to start : %d", + s_ctrl->sensor_state); + goto release_mutex; + } + + if (s_ctrl->i2c_data.streamon_settings.is_settings_valid && + (s_ctrl->i2c_data.streamon_settings.request_id == 0)) { + rc = cam_sensor_apply_settings(s_ctrl, 0, + CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "cannot apply streamon settings"); + goto release_mutex; + } + } + s_ctrl->sensor_state = CAM_SENSOR_START; + CAM_INFO(CAM_SENSOR, + "CAM_START_DEV Success, sensor_id:0x%x", + s_ctrl->sensordata->slave_info.sensor_id); + } + break; + case CAM_STOP_DEV: { + if (s_ctrl->sensor_state != CAM_SENSOR_START) { + rc = -EINVAL; + CAM_WARN(CAM_SENSOR, + "Not in right state to stop : %d", + s_ctrl->sensor_state); + goto release_mutex; + } + + if (s_ctrl->i2c_data.streamoff_settings.is_settings_valid && + (s_ctrl->i2c_data.streamoff_settings.request_id == 0)) { + rc = cam_sensor_apply_settings(s_ctrl, 0, + CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "cannot apply streamoff settings"); + } + } + + cam_sensor_release_resource(s_ctrl); + s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE; + CAM_INFO(CAM_SENSOR, + "CAM_STOP_DEV Success, sensor_id:0x%x", + s_ctrl->sensordata->slave_info.sensor_id); + } + break; + case CAM_CONFIG_DEV: { + rc = cam_sensor_i2c_pkt_parse(s_ctrl, arg); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed CCI Config: %d", rc); + goto release_mutex; + } + if (s_ctrl->i2c_data.init_settings.is_settings_valid && + (s_ctrl->i2c_data.init_settings.request_id == 0)) { + + rc = cam_sensor_apply_settings(s_ctrl, 0, + CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "cannot apply init settings"); + goto release_mutex; + } + rc = delete_request(&s_ctrl->i2c_data.init_settings); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Fail in deleting the Init settings"); + goto release_mutex; + } + s_ctrl->i2c_data.init_settings.request_id = -1; + } + + if (s_ctrl->i2c_data.config_settings.is_settings_valid && + (s_ctrl->i2c_data.config_settings.request_id == 0)) { + rc = cam_sensor_apply_settings(s_ctrl, 0, + CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "cannot apply config settings"); + goto release_mutex; + } + rc = delete_request(&s_ctrl->i2c_data.config_settings); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Fail in deleting the config settings"); + goto release_mutex; + } + s_ctrl->sensor_state = CAM_SENSOR_CONFIG; + s_ctrl->i2c_data.config_settings.request_id = -1; + } + } + break; + case CAM_GET_FUSE_ID: { + CAM_ERR(CAM_SENSOR, "fuse_id:%s", fuse_id); + if (fuse_id[0] == '\0') { + CAM_ERR(CAM_SENSOR, "fuse_id is empty"); + rc = -EFAULT; + goto release_mutex; + } else if (copy_to_user(u64_to_user_ptr(cmd->handle), &fuse_id, + sizeof(fuse_id))) { + CAM_ERR(CAM_SENSOR, "Failed Copy to User"); + rc = -EFAULT; + goto release_mutex; + } + break; + } + default: + CAM_ERR(CAM_SENSOR, "Invalid Opcode: %d", cmd->op_code); + rc = -EINVAL; + goto release_mutex; + } + +release_mutex: + mutex_unlock(&(s_ctrl->cam_sensor_mutex)); + return rc; +} + +int cam_sensor_publish_dev_info(struct cam_req_mgr_device_info *info) +{ + int rc = 0; + + if (!info) + return -EINVAL; + + info->dev_id = CAM_REQ_MGR_DEVICE_SENSOR; + strlcpy(info->name, CAM_SENSOR_NAME, sizeof(info->name)); + info->p_delay = 2; + info->trigger = CAM_TRIGGER_POINT_SOF; + + return rc; +} + +int cam_sensor_establish_link(struct cam_req_mgr_core_dev_link_setup *link) +{ + struct cam_sensor_ctrl_t *s_ctrl = NULL; + + if (!link) + return -EINVAL; + + s_ctrl = (struct cam_sensor_ctrl_t *) + cam_get_device_priv(link->dev_hdl); + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "Device data is NULL"); + return -EINVAL; + } + if (link->link_enable) { + s_ctrl->bridge_intf.link_hdl = link->link_hdl; + s_ctrl->bridge_intf.crm_cb = link->crm_cb; + } else { + s_ctrl->bridge_intf.link_hdl = -1; + s_ctrl->bridge_intf.crm_cb = NULL; + } + + return 0; +} + +int cam_sensor_power(struct v4l2_subdev *sd, int on) +{ + struct cam_sensor_ctrl_t *s_ctrl = v4l2_get_subdevdata(sd); + + mutex_lock(&(s_ctrl->cam_sensor_mutex)); + if (!on && s_ctrl->sensor_state == CAM_SENSOR_START) { + cam_sensor_power_down(s_ctrl); + s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE; + } + mutex_unlock(&(s_ctrl->cam_sensor_mutex)); + + return 0; +} + +int cam_sensor_power_up(struct cam_sensor_ctrl_t *s_ctrl) +{ + int rc; + struct cam_sensor_power_ctrl_t *power_info; + struct cam_camera_slave_info *slave_info; + struct cam_hw_soc_info *soc_info = + &s_ctrl->soc_info; + + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "failed: %pK", s_ctrl); + return -EINVAL; + } + + power_info = &s_ctrl->sensordata->power_info; + slave_info = &(s_ctrl->sensordata->slave_info); + + if (!power_info || !slave_info) { + CAM_ERR(CAM_SENSOR, "failed: %pK %pK", power_info, slave_info); + return -EINVAL; + } + + rc = cam_sensor_core_power_up(power_info, soc_info); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "power up the core is failed:%d", rc); + return rc; + } + + rc = camera_io_init(&(s_ctrl->io_master_info)); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "cci_init failed: rc: %d", rc); + + return rc; +} + +int cam_sensor_power_down(struct cam_sensor_ctrl_t *s_ctrl) +{ + struct cam_sensor_power_ctrl_t *power_info; + struct cam_hw_soc_info *soc_info; + int rc = 0; + + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "failed: s_ctrl %pK", s_ctrl); + return -EINVAL; + } + + power_info = &s_ctrl->sensordata->power_info; + soc_info = &s_ctrl->soc_info; + + if (!power_info) { + CAM_ERR(CAM_SENSOR, "failed: power_info %pK", power_info); + return -EINVAL; + } + rc = msm_camera_power_down(power_info, soc_info); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "power down the core is failed:%d", rc); + return rc; + } + + camera_io_release(&(s_ctrl->io_master_info)); + + return rc; +} + +int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, + int64_t req_id, enum cam_sensor_packet_opcodes opcode) +{ + int rc = 0, offset, i; + uint64_t top = 0, del_req_id = 0; + struct i2c_settings_array *i2c_set = NULL; + struct i2c_settings_list *i2c_list; + + if (req_id == 0) { + switch (opcode) { + case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: { + i2c_set = &s_ctrl->i2c_data.streamon_settings; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: { + i2c_set = &s_ctrl->i2c_data.init_settings; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG: { + i2c_set = &s_ctrl->i2c_data.config_settings; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: { + i2c_set = &s_ctrl->i2c_data.streamoff_settings; + break; + } + case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE: + case CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE: + default: + return 0; + } + if (i2c_set->is_settings_valid == 1) { + list_for_each_entry(i2c_list, + &(i2c_set->list_head), list) { + rc = cam_sensor_i2c_modes_util( + &(s_ctrl->io_master_info), + i2c_list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to apply settings: %d", + rc); + return rc; + } + } + } + } else { + offset = req_id % MAX_PER_FRAME_ARRAY; + i2c_set = &(s_ctrl->i2c_data.per_frame[offset]); + if (i2c_set->is_settings_valid == 1 && + i2c_set->request_id == req_id) { + list_for_each_entry(i2c_list, + &(i2c_set->list_head), list) { + rc = cam_sensor_i2c_modes_util( + &(s_ctrl->io_master_info), + i2c_list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed to apply settings: %d", + rc); + return rc; + } + } + } else { + CAM_DBG(CAM_SENSOR, + "Invalid/NOP request to apply: %lld", req_id); + } + + /* Change the logic dynamically */ + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + if ((req_id >= + s_ctrl->i2c_data.per_frame[i].request_id) && + (top < + s_ctrl->i2c_data.per_frame[i].request_id) && + (s_ctrl->i2c_data.per_frame[i]. + is_settings_valid == 1)) { + del_req_id = top; + top = s_ctrl->i2c_data.per_frame[i].request_id; + } + } + + if (top < req_id) { + if ((((top % MAX_PER_FRAME_ARRAY) - (req_id % + MAX_PER_FRAME_ARRAY)) >= BATCH_SIZE_MAX) || + (((top % MAX_PER_FRAME_ARRAY) - (req_id % + MAX_PER_FRAME_ARRAY)) <= -BATCH_SIZE_MAX)) + del_req_id = req_id; + } + + if (!del_req_id) + return rc; + + CAM_DBG(CAM_SENSOR, "top: %llu, del_req_id:%llu", + top, del_req_id); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + if ((del_req_id > + s_ctrl->i2c_data.per_frame[i].request_id) && + (s_ctrl->i2c_data.per_frame[i]. + is_settings_valid == 1)) { + s_ctrl->i2c_data.per_frame[i].request_id = 0; + rc = delete_request( + &(s_ctrl->i2c_data.per_frame[i])); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "Delete request Fail:%lld rc:%d", + del_req_id, rc); + } + } + } + + return rc; +} + +int32_t cam_sensor_apply_request(struct cam_req_mgr_apply_request *apply) +{ + int32_t rc = 0; + struct cam_sensor_ctrl_t *s_ctrl = NULL; + + if (!apply) + return -EINVAL; + + s_ctrl = (struct cam_sensor_ctrl_t *) + cam_get_device_priv(apply->dev_hdl); + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "Device data is NULL"); + return -EINVAL; + } + CAM_DBG(CAM_SENSOR, " Req Id: %lld", apply->request_id); + trace_cam_apply_req("Sensor", apply->request_id); + rc = cam_sensor_apply_settings(s_ctrl, apply->request_id, + CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE); + return rc; +} + +int32_t cam_sensor_flush_request(struct cam_req_mgr_flush_request *flush_req) +{ + int32_t rc = 0, i; + uint32_t cancel_req_id_found = 0; + struct cam_sensor_ctrl_t *s_ctrl = NULL; + struct i2c_settings_array *i2c_set = NULL; + + if (!flush_req) + return -EINVAL; + + s_ctrl = (struct cam_sensor_ctrl_t *) + cam_get_device_priv(flush_req->dev_hdl); + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "Device data is NULL"); + return -EINVAL; + } + + /* PATCH, add by xcb */ + if (s_ctrl->i2c_data.per_frame == NULL) { + CAM_ERR(CAM_SENSOR, "i2c frame data is NULL"); + return -EINVAL; + } + + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) { + i2c_set = &(s_ctrl->i2c_data.per_frame[i]); + + if ((flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) + && (i2c_set->request_id != flush_req->req_id)) + continue; + + if (i2c_set->is_settings_valid == 1) { + rc = delete_request(i2c_set); + if (rc < 0) + CAM_ERR(CAM_SENSOR, + "delete request: %lld rc: %d", + i2c_set->request_id, rc); + + if (flush_req->type == + CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) { + cancel_req_id_found = 1; + break; + } + } + } + + if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ && + !cancel_req_id_found) + CAM_DBG(CAM_SENSOR, + "Flush request id:%lld not found in the pending list", + flush_req->req_id); + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.h new file mode 100644 index 000000000000..45701794be4a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_core.h @@ -0,0 +1,93 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SENSOR_CORE_H_ +#define _CAM_SENSOR_CORE_H_ + +#include "cam_sensor_dev.h" + +/** + * @s_ctrl: Sensor ctrl structure + * + * This API powers up the camera sensor module + */ +int cam_sensor_power_up(struct cam_sensor_ctrl_t *s_ctrl); + +/** + * @s_ctrl: Sensor ctrl structure + * + * This API powers down the camera sensor module + */ +int cam_sensor_power_down(struct cam_sensor_ctrl_t *s_ctrl); + +/** + * @sd: V4L2 subdevice + * @on: Turn off/on flag + * + * This API powers down the sensor module + */ +int cam_sensor_power(struct v4l2_subdev *sd, int on); + +/** + * @s_ctrl: Sensor ctrl structure + * @req_id: Request id + * @opcode: opcode for settings + * + * This API applies the req_id settings to sensor + */ +int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id, + enum cam_sensor_packet_opcodes opcode); + +/** + * @apply: Req mgr structure for applying request + * + * This API applies the request that is mentioned + */ +int cam_sensor_apply_request(struct cam_req_mgr_apply_request *apply); + +/** + * @flush: Req mgr structure for flushing request + * + * This API flushes the request that is mentioned + */ +int cam_sensor_flush_request(struct cam_req_mgr_flush_request *flush); + +/** + * @info: Sub device info to req mgr + * + * Publish the subdevice info + */ +int cam_sensor_publish_dev_info(struct cam_req_mgr_device_info *info); + +/** + * @link: Link setup info + * + * This API establishes link with sensor subdevice with req mgr + */ +int cam_sensor_establish_link(struct cam_req_mgr_core_dev_link_setup *link); + +/** + * @s_ctrl: Sensor ctrl structure + * @arg: Camera control command argument + * + * This API handles the camera control argument reached to sensor + */ +int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, void *arg); + +/** + * @s_ctrl: Sensor ctrl structure + * + * This API handles the camera sensor close/shutdown + */ +void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl); + +#endif /* _CAM_SENSOR_CORE_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.c new file mode 100644 index 000000000000..f6c38885ad29 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.c @@ -0,0 +1,445 @@ +/* Copyright (c) 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 "cam_sensor_dev.h" +#include "cam_req_mgr_dev.h" +#include "cam_sensor_soc.h" +#include "cam_sensor_core.h" + +struct cam_sensor_i2c_reg_setting_array { + struct cam_sensor_i2c_reg_array reg_setting[512]; + unsigned short size; + enum camera_sensor_i2c_type addr_type; + enum camera_sensor_i2c_type data_type; + unsigned short delay; +}; + +struct cam_sensor_settings { + struct cam_sensor_i2c_reg_setting_array imx519_setting; + struct cam_sensor_i2c_reg_setting_array imx376k_setting; + struct cam_sensor_i2c_reg_setting_array imx371_setting; +}; + +struct cam_sensor_settings sensor_settings = { +#include "CAM_SENSOR_SETTINGS.h" +}; + +static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int rc = 0; + struct cam_sensor_ctrl_t *s_ctrl = + v4l2_get_subdevdata(sd); + struct cam_sensor_i2c_reg_setting sensor_setting; + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_sensor_driver_cmd(s_ctrl, arg); + break; + case VIDIOC_CAM_FTM_POWNER_DOWN: + CAM_INFO(CAM_SENSOR, "FTM power down"); + return cam_sensor_power_down(s_ctrl); + break; + case VIDIOC_CAM_FTM_POWNER_UP: + CAM_INFO(CAM_SENSOR, "FTM power up, sensor id 0x%x", s_ctrl->sensordata->slave_info.sensor_id); + rc = cam_sensor_power_up(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "ftm power up failed!"); + break; + } + + if (s_ctrl->sensordata->slave_info.sensor_id == 0x519) { + sensor_setting.reg_setting = sensor_settings.imx519_setting.reg_setting; + sensor_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD; + sensor_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + sensor_setting.size = sensor_settings.imx519_setting.size; + sensor_setting.delay = sensor_settings.imx519_setting.delay; + } else if (s_ctrl->sensordata->slave_info.sensor_id == 0x376) { + sensor_setting.reg_setting = sensor_settings.imx376k_setting.reg_setting; + sensor_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD; + sensor_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + sensor_setting.size = sensor_settings.imx376k_setting.size; + sensor_setting.delay = sensor_settings.imx376k_setting.delay; + } else if (s_ctrl->sensordata->slave_info.sensor_id == 0x371) { + sensor_setting.reg_setting = sensor_settings.imx371_setting.reg_setting; + sensor_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD; + sensor_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + sensor_setting.size = sensor_settings.imx371_setting.size; + sensor_setting.delay = sensor_settings.imx371_setting.delay; + } + rc = camera_io_dev_write(&(s_ctrl->io_master_info), &sensor_setting); + + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "FTM Failed to write sensor setting"); + } else { + CAM_ERR(CAM_SENSOR, "FTM successfully to write sensor setting"); + } + break; + default: + CAM_ERR(CAM_SENSOR, "Invalid ioctl cmd: %d", cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static int cam_sensor_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_sensor_ctrl_t *s_ctrl = + v4l2_get_subdevdata(sd); + + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "s_ctrl ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&(s_ctrl->cam_sensor_mutex)); + cam_sensor_shutdown(s_ctrl); + mutex_unlock(&(s_ctrl->cam_sensor_mutex)); + + return 0; +} + +#ifdef CONFIG_COMPAT +static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + struct cam_control cmd_data; + int32_t rc = 0; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_SENSOR, "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_sensor_subdev_ioctl(sd, cmd, &cmd_data); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "cam_sensor_subdev_ioctl failed"); + break; + default: + CAM_ERR(CAM_SENSOR, "Invalid compat ioctl cmd_type: %d", cmd); + rc = -EINVAL; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_SENSOR, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} + +#endif +static struct v4l2_subdev_core_ops cam_sensor_subdev_core_ops = { + .ioctl = cam_sensor_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_sensor_init_subdev_do_ioctl, +#endif + .s_power = cam_sensor_power, +}; + +static struct v4l2_subdev_ops cam_sensor_subdev_ops = { + .core = &cam_sensor_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops = { + .close = cam_sensor_subdev_close, +}; + +static int cam_sensor_init_subdev_params(struct cam_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + + s_ctrl->v4l2_dev_str.internal_ops = + &cam_sensor_internal_ops; + s_ctrl->v4l2_dev_str.ops = + &cam_sensor_subdev_ops; + strlcpy(s_ctrl->device_name, CAMX_SENSOR_DEV_NAME, + sizeof(s_ctrl->device_name)); + s_ctrl->v4l2_dev_str.name = + s_ctrl->device_name; + s_ctrl->v4l2_dev_str.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + s_ctrl->v4l2_dev_str.ent_function = + CAM_SENSOR_DEVICE_TYPE; + s_ctrl->v4l2_dev_str.token = s_ctrl; + + rc = cam_register_subdev(&(s_ctrl->v4l2_dev_str)); + if (rc) + CAM_ERR(CAM_SENSOR, "Fail with cam_register_subdev rc: %d", rc); + + return rc; +} + +static int32_t cam_sensor_driver_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t rc = 0; + int i = 0; + struct cam_sensor_ctrl_t *s_ctrl = NULL; + struct cam_hw_soc_info *soc_info = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CAM_ERR(CAM_SENSOR, + "%s :i2c_check_functionality failed", client->name); + return -EFAULT; + } + + /* Create sensor control structure */ + s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL); + if (!s_ctrl) + return -ENOMEM; + + i2c_set_clientdata(client, s_ctrl); + + s_ctrl->io_master_info.client = client; + soc_info = &s_ctrl->soc_info; + soc_info->dev = &client->dev; + soc_info->dev_name = client->name; + + /* Initialize sensor device type */ + s_ctrl->of_node = client->dev.of_node; + s_ctrl->io_master_info.master_type = I2C_MASTER; + s_ctrl->is_probe_succeed = 0; + + rc = cam_sensor_parse_dt(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "cam_sensor_parse_dt rc %d", rc); + goto free_s_ctrl; + } + + rc = cam_sensor_init_subdev_params(s_ctrl); + if (rc) + goto free_s_ctrl; + + s_ctrl->i2c_data.per_frame = + (struct i2c_settings_array *) + kzalloc(sizeof(struct i2c_settings_array) * + MAX_PER_FRAME_ARRAY, GFP_KERNEL); + if (s_ctrl->i2c_data.per_frame == NULL) { + rc = -ENOMEM; + goto unreg_subdev; + } + + INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.config_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head)); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) + INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head)); + + s_ctrl->bridge_intf.device_hdl = -1; + s_ctrl->bridge_intf.ops.get_dev_info = cam_sensor_publish_dev_info; + s_ctrl->bridge_intf.ops.link_setup = cam_sensor_establish_link; + s_ctrl->bridge_intf.ops.apply_req = cam_sensor_apply_request; + s_ctrl->bridge_intf.ops.flush_req = cam_sensor_flush_request; + + s_ctrl->sensordata->power_info.dev = soc_info->dev; + v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl); + return rc; +unreg_subdev: + cam_unregister_subdev(&(s_ctrl->v4l2_dev_str)); +free_s_ctrl: + kfree(s_ctrl); + return rc; +} + +static int cam_sensor_platform_remove(struct platform_device *pdev) +{ + int i; + struct cam_sensor_ctrl_t *s_ctrl; + struct cam_hw_soc_info *soc_info; + + s_ctrl = platform_get_drvdata(pdev); + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "sensor device is NULL"); + return 0; + } + + soc_info = &s_ctrl->soc_info; + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + kfree(s_ctrl->i2c_data.per_frame); + devm_kfree(&pdev->dev, s_ctrl); + + return 0; +} + +static int cam_sensor_driver_i2c_remove(struct i2c_client *client) +{ + int i; + struct cam_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client); + struct cam_hw_soc_info *soc_info; + + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "sensor device is NULL"); + return 0; + } + + soc_info = &s_ctrl->soc_info; + for (i = 0; i < soc_info->num_clk; i++) + devm_clk_put(soc_info->dev, soc_info->clk[i]); + + kfree(s_ctrl->i2c_data.per_frame); + kfree(s_ctrl); + + return 0; +} + +static const struct of_device_id cam_sensor_driver_dt_match[] = { + {.compatible = "qcom,cam-sensor"}, + {} +}; + +static int32_t cam_sensor_driver_platform_probe( + struct platform_device *pdev) +{ + int32_t rc = 0, i = 0; + struct cam_sensor_ctrl_t *s_ctrl = NULL; + struct cam_hw_soc_info *soc_info = NULL; + + /* Create sensor control structure */ + s_ctrl = devm_kzalloc(&pdev->dev, + sizeof(struct cam_sensor_ctrl_t), GFP_KERNEL); + if (!s_ctrl) + return -ENOMEM; + + soc_info = &s_ctrl->soc_info; + soc_info->pdev = pdev; + soc_info->dev = &pdev->dev; + soc_info->dev_name = pdev->name; + + /* Initialize sensor device type */ + s_ctrl->of_node = pdev->dev.of_node; + s_ctrl->is_probe_succeed = 0; + + /*fill in platform device*/ + s_ctrl->pdev = pdev; + + s_ctrl->io_master_info.master_type = CCI_MASTER; + + rc = cam_sensor_parse_dt(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed: cam_sensor_parse_dt rc %d", rc); + goto free_s_ctrl; + } + + /* Fill platform device id*/ + pdev->id = soc_info->index; + + rc = cam_sensor_init_subdev_params(s_ctrl); + if (rc) + goto free_s_ctrl; + + s_ctrl->i2c_data.per_frame = + (struct i2c_settings_array *) + kzalloc(sizeof(struct i2c_settings_array) * + MAX_PER_FRAME_ARRAY, GFP_KERNEL); + if (s_ctrl->i2c_data.per_frame == NULL) { + rc = -ENOMEM; + goto unreg_subdev; + } + + INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.config_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head)); + INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head)); + + for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) + INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head)); + + s_ctrl->bridge_intf.device_hdl = -1; + s_ctrl->bridge_intf.ops.get_dev_info = cam_sensor_publish_dev_info; + s_ctrl->bridge_intf.ops.link_setup = cam_sensor_establish_link; + s_ctrl->bridge_intf.ops.apply_req = cam_sensor_apply_request; + s_ctrl->bridge_intf.ops.flush_req = cam_sensor_flush_request; + + s_ctrl->sensordata->power_info.dev = &pdev->dev; + platform_set_drvdata(pdev, s_ctrl); + v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl); + + s_ctrl->sensor_state = CAM_SENSOR_INIT; + + return rc; +unreg_subdev: + cam_unregister_subdev(&(s_ctrl->v4l2_dev_str)); +free_s_ctrl: + devm_kfree(&pdev->dev, s_ctrl); + return rc; +} + +MODULE_DEVICE_TABLE(of, cam_sensor_driver_dt_match); + +static struct platform_driver cam_sensor_platform_driver = { + .probe = cam_sensor_driver_platform_probe, + .driver = { + .name = "qcom,camera", + .owner = THIS_MODULE, + .of_match_table = cam_sensor_driver_dt_match, + }, + .remove = cam_sensor_platform_remove, +}; + +static const struct i2c_device_id i2c_id[] = { + {SENSOR_DRIVER_I2C, (kernel_ulong_t)NULL}, + { } +}; + +static struct i2c_driver cam_sensor_driver_i2c = { + .id_table = i2c_id, + .probe = cam_sensor_driver_i2c_probe, + .remove = cam_sensor_driver_i2c_remove, + .driver = { + .name = SENSOR_DRIVER_I2C, + }, +}; + +static int __init cam_sensor_driver_init(void) +{ + int32_t rc = 0; + + rc = platform_driver_register(&cam_sensor_platform_driver); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "platform_driver_register Failed: rc = %d", + rc); + return rc; + } + + rc = i2c_add_driver(&cam_sensor_driver_i2c); + if (rc) + CAM_ERR(CAM_SENSOR, "i2c_add_driver failed rc = %d", rc); + + return rc; +} + +static void __exit cam_sensor_driver_exit(void) +{ + platform_driver_unregister(&cam_sensor_platform_driver); + i2c_del_driver(&cam_sensor_driver_i2c); +} + +module_init(cam_sensor_driver_init); +module_exit(cam_sensor_driver_exit); +MODULE_DESCRIPTION("cam_sensor_driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.h new file mode 100644 index 000000000000..37b08d918936 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_dev.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2017-2018, 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 _CAM_SENSOR_DEV_H_ +#define _CAM_SENSOR_DEV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_debug_util.h" + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + +#undef CDBG +#ifdef CAM_SENSOR_DEBUG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#define SENSOR_DRIVER_I2C "i2c_camera" +#define CAMX_SENSOR_DEV_NAME "cam-sensor-driver" + +enum cam_sensor_state_t { + CAM_SENSOR_INIT, + CAM_SENSOR_ACQUIRE, + CAM_SENSOR_CONFIG, + CAM_SENSOR_START, +}; + +/** + * struct intf_params + * @device_hdl: Device Handle + * @session_hdl: Session Handle + * @link_hdl: Link Handle + * @ops: KMD operations + * @crm_cb: Callback API pointers + */ +struct intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * struct cam_sensor_ctrl_t: Camera control structure + * @pdev: Platform device + * @cam_sensor_mutex: Sensor mutex + * @sensordata: Sensor board Information + * @cci_i2c_master: I2C structure + * @io_master_info: Information about the communication master + * @sensor_state: Sensor states + * @is_probe_succeed: Probe succeeded or not + * @id: Cell Index + * @of_node: Of node ptr + * @v4l2_dev_str: V4L2 device structure + * @sensor_probe_addr_type: Sensor probe address type + * @sensor_probe_data_type: Sensor probe data type + * @i2c_data: Sensor I2C register settings + * @sensor_info: Sensor query cap structure + * @bridge_intf: Bridge interface structure + * @device_name: Sensor device structure + * @streamon_count: Count to hold the number of times stream on called + * @streamoff_count: Count to hold the number of times stream off called + */ +struct cam_sensor_ctrl_t { + struct platform_device *pdev; + struct cam_hw_soc_info soc_info; + struct mutex cam_sensor_mutex; + struct cam_sensor_board_info *sensordata; + enum cci_i2c_master_t cci_i2c_master; + struct camera_io_master io_master_info; + enum cam_sensor_state_t sensor_state; + uint8_t is_probe_succeed; + uint32_t id; + struct device_node *of_node; + struct cam_subdev v4l2_dev_str; + uint8_t sensor_probe_addr_type; + uint8_t sensor_probe_data_type; + struct i2c_data_settings i2c_data; + struct cam_sensor_query_cap sensor_info; + struct intf_params bridge_intf; + char device_name[20]; + uint32_t streamon_count; + uint32_t streamoff_count; +}; + +#endif /* _CAM_SENSOR_DEV_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.c new file mode 100644 index 000000000000..1c3ead0c25bf --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.c @@ -0,0 +1,252 @@ +/* Copyright (c) 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 +#include +#include +#include +#include +#include +#include "cam_sensor_soc.h" +#include "cam_soc_util.h" + +int32_t cam_sensor_get_sub_module_index(struct device_node *of_node, + struct cam_sensor_board_info *s_info) +{ + int rc = 0, i = 0; + uint32_t val = 0; + struct device_node *src_node = NULL; + struct cam_sensor_board_info *sensor_info; + + sensor_info = s_info; + + for (i = 0; i < SUB_MODULE_MAX; i++) + sensor_info->subdev_id[i] = -1; + + src_node = of_parse_phandle(of_node, "actuator-src", 0); + if (!src_node) { + CAM_DBG(CAM_SENSOR, "src_node NULL"); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CAM_DBG(CAM_SENSOR, "actuator cell index %d, rc %d", val, rc); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + of_node_put(src_node); + return rc; + } + sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val; + of_node_put(src_node); + } + + src_node = of_parse_phandle(of_node, "ois-src", 0); + if (!src_node) { + CAM_DBG(CAM_SENSOR, "src_node NULL"); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CAM_DBG(CAM_SENSOR, " ois cell index %d, rc %d", val, rc); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + of_node_put(src_node); + return rc; + } + sensor_info->subdev_id[SUB_MODULE_OIS] = val; + of_node_put(src_node); + } + + src_node = of_parse_phandle(of_node, "eeprom-src", 0); + if (!src_node) { + CAM_DBG(CAM_SENSOR, "eeprom src_node NULL"); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CAM_DBG(CAM_SENSOR, "eeprom cell index %d, rc %d", val, rc); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + of_node_put(src_node); + return rc; + } + sensor_info->subdev_id[SUB_MODULE_EEPROM] = val; + of_node_put(src_node); + } + + src_node = of_parse_phandle(of_node, "led-flash-src", 0); + if (!src_node) { + CAM_DBG(CAM_SENSOR, " src_node NULL"); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CAM_DBG(CAM_SENSOR, "led flash cell index %d, rc %d", val, rc); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + of_node_put(src_node); + return rc; + } + sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val; + of_node_put(src_node); + } + + rc = of_property_read_u32(of_node, "csiphy-sd-index", &val); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "paring the dt node for csiphy rc %d", rc); + else + sensor_info->subdev_id[SUB_MODULE_CSIPHY] = val; + + return rc; +} + +static int32_t cam_sensor_driver_get_dt_data(struct cam_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct cam_sensor_board_info *sensordata = NULL; + struct device_node *of_node = s_ctrl->of_node; + struct cam_hw_soc_info *soc_info = &s_ctrl->soc_info; + s_ctrl->sensordata = kzalloc(sizeof(*sensordata), GFP_KERNEL); + if (!s_ctrl->sensordata) + return -ENOMEM; + + sensordata = s_ctrl->sensordata; + + rc = cam_soc_util_get_dt_properties(soc_info); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed to read DT properties rc %d", rc); + goto FREE_SENSOR_DATA; + } + + rc = cam_sensor_util_init_gpio_pin_tbl(soc_info, + &sensordata->power_info.gpio_num_info); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed to read gpios %d", rc); + goto FREE_SENSOR_DATA; + } + + s_ctrl->id = soc_info->index; + + /* Validate cell_id */ + if (s_ctrl->id >= MAX_CAMERAS) { + CAM_ERR(CAM_SENSOR, "Failed invalid cell_id %d", s_ctrl->id); + rc = -EINVAL; + goto FREE_SENSOR_DATA; + } + + /* Read subdev info */ + rc = cam_sensor_get_sub_module_index(of_node, sensordata); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed to get sub module index, rc=%d", + rc); + goto FREE_SENSOR_DATA; + } + + if (s_ctrl->io_master_info.master_type == CCI_MASTER) { + /* Get CCI master */ + rc = of_property_read_u32(of_node, "cci-master", + &s_ctrl->cci_i2c_master); + CAM_DBG(CAM_SENSOR, "cci-master %d, rc %d", + s_ctrl->cci_i2c_master, rc); + if (rc < 0) { + /* Set default master 0 */ + s_ctrl->cci_i2c_master = MASTER_0; + rc = 0; + } + } + + if (of_property_read_u32(of_node, "sensor-position-pitch", + &sensordata->pos_pitch) < 0) { + CAM_DBG(CAM_SENSOR, "Invalid sensor position"); + sensordata->pos_pitch = 360; + } + if (of_property_read_u32(of_node, "sensor-position-roll", + &sensordata->pos_roll) < 0) { + CAM_DBG(CAM_SENSOR, "Invalid sensor position"); + sensordata->pos_roll = 360; + } + if (of_property_read_u32(of_node, "sensor-position-yaw", + &sensordata->pos_yaw) < 0) { + CAM_DBG(CAM_SENSOR, "Invalid sensor position"); + sensordata->pos_yaw = 360; + } + + return rc; + +FREE_SENSOR_DATA: + kfree(sensordata); + return rc; +} + +int32_t msm_sensor_init_default_params(struct cam_sensor_ctrl_t *s_ctrl) +{ + /* Validate input parameters */ + if (!s_ctrl) { + CAM_ERR(CAM_SENSOR, "failed: invalid params s_ctrl %pK", + s_ctrl); + return -EINVAL; + } + + CAM_DBG(CAM_SENSOR, + "master_type: %d", s_ctrl->io_master_info.master_type); + /* Initialize cci_client */ + if (s_ctrl->io_master_info.master_type == CCI_MASTER) { + s_ctrl->io_master_info.cci_client = kzalloc(sizeof( + struct cam_sensor_cci_client), GFP_KERNEL); + if (!(s_ctrl->io_master_info.cci_client)) + return -ENOMEM; + + } else if (s_ctrl->io_master_info.master_type == I2C_MASTER) { + if (!(s_ctrl->io_master_info.client)) + return -EINVAL; + } else { + CAM_ERR(CAM_SENSOR, + "Invalid master / Master type Not supported"); + return -EINVAL; + } + + return 0; +} + +int32_t cam_sensor_parse_dt(struct cam_sensor_ctrl_t *s_ctrl) +{ + int32_t i, rc = 0; + struct cam_hw_soc_info *soc_info = &s_ctrl->soc_info; + + /* Parse dt information and store in sensor control structure */ + rc = cam_sensor_driver_get_dt_data(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed to get dt data rc %d", rc); + return rc; + } + + /* Initialize mutex */ + mutex_init(&(s_ctrl->cam_sensor_mutex)); + + /* Initialize default parameters */ + for (i = 0; i < soc_info->num_clk; i++) { + soc_info->clk[i] = devm_clk_get(soc_info->dev, + soc_info->clk_name[i]); + if (!soc_info->clk[i]) { + CAM_ERR(CAM_SENSOR, "get failed for %s", + soc_info->clk_name[i]); + rc = -ENOENT; + return rc; + } + } + rc = msm_sensor_init_default_params(s_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "failed: msm_sensor_init_default_params rc %d", rc); + goto FREE_DT_DATA; + } + + return rc; + +FREE_DT_DATA: + kfree(s_ctrl->sensordata); + s_ctrl->sensordata = NULL; + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.h new file mode 100644 index 000000000000..7f4a551d997f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor/cam_sensor_soc.h @@ -0,0 +1,25 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SENSOR_SOC_H_ +#define _CAM_SENSOR_SOC_H_ + +#include "cam_sensor_dev.h" + +/** + * @s_ctrl: Sensor ctrl structure + * + * Parses sensor dt + */ +int cam_sensor_parse_dt(struct cam_sensor_ctrl_t *s_ctrl); + +#endif /* _CAM_SENSOR_SOC_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/Makefile new file mode 100644 index 000000000000..9726654c4db2 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_cpas/include +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o cam_sensor_qup_i2c.o cam_sensor_spi.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c new file mode 100644 index 000000000000..4e5956f1875d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c @@ -0,0 +1,235 @@ +/* Copyright (c) 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 "cam_sensor_cmn_header.h" +#include "cam_sensor_i2c.h" +#include "cam_cci_dev.h" + +int32_t cam_cci_i2c_read(struct cam_sensor_cci_client *cci_client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + int32_t rc = -EINVAL; + unsigned char buf[CAMERA_SENSOR_I2C_TYPE_DWORD]; + struct cam_cci_ctrl cci_ctrl; + + if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) + return rc; + + cci_ctrl.cmd = MSM_CCI_I2C_READ; + cci_ctrl.cci_info = cci_client; + cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr; + cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = addr_type; + cci_ctrl.cfg.cci_i2c_read_cfg.data = buf; + cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = data_type; + rc = v4l2_subdev_call(cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "rc = %d", rc); + return rc; + } + + rc = cci_ctrl.status; + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) + *data = buf[0]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) + *data = buf[0] << 8 | buf[1]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) + *data = buf[0] << 16 | buf[1] << 8 | buf[2]; + else + *data = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]; + + return rc; +} + +int32_t cam_camera_cci_i2c_read_seq(struct cam_sensor_cci_client *cci_client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte) +{ + int32_t rc = -EFAULT; + unsigned char *buf = NULL; + int i = 0; + struct cam_cci_ctrl cci_ctrl; + + if ((addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) + || (num_byte > I2C_REG_DATA_MAX)) { + CAM_ERR(CAM_SENSOR, "addr_type %d num_byte %d", addr_type, + num_byte); + return rc; + } + + buf = kzalloc(num_byte, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cci_ctrl.cmd = MSM_CCI_I2C_READ; + cci_ctrl.cci_info = cci_client; + cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr; + cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = addr_type; + cci_ctrl.cfg.cci_i2c_read_cfg.data = buf; + cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = num_byte; + cci_ctrl.status = -EFAULT; + rc = v4l2_subdev_call(cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + rc = cci_ctrl.status; + CAM_DBG(CAM_SENSOR, "addr = 0x%x, rc = %d", addr, rc); + for (i = 0; i < num_byte; i++) { + data[i] = buf[i]; + CAM_DBG(CAM_SENSOR, "Byte %d: Data: 0x%x\n", i, data[i]); + } + kfree(buf); + return rc; +} + +static int32_t cam_cci_i2c_write_table_cmd( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting, + enum cam_cci_cmd_type cmd) +{ + int32_t rc = -EINVAL; + struct cam_cci_ctrl cci_ctrl; + + if (!client || !write_setting) + return rc; + + if (write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) + return rc; + + cci_ctrl.cmd = cmd; + cci_ctrl.cci_info = client->cci_client; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = + write_setting->reg_setting; + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = write_setting->addr_type; + cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size; + rc = v4l2_subdev_call(client->cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed rc = %d", rc); + return rc; + } + rc = cci_ctrl.status; + if (write_setting->delay > 20) + msleep(write_setting->delay); + else if (write_setting->delay) + usleep_range(write_setting->delay * 1000, (write_setting->delay + * 1000) + 1000); + + return rc; +} + + +int32_t cam_cci_i2c_write_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + return cam_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE); +} + +int32_t cam_cci_i2c_write_continuous_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting, + uint8_t cam_sensor_i2c_write_flag) +{ + int32_t rc = 0; + + if (cam_sensor_i2c_write_flag == 1) + rc = cam_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE_BURST); + else if (cam_sensor_i2c_write_flag == 0) + rc = cam_cci_i2c_write_table_cmd(client, write_setting, + MSM_CCI_I2C_WRITE_SEQ); + + return rc; +} + +static int32_t cam_cci_i2c_compare(struct cam_sensor_cci_client *client, + uint32_t addr, uint16_t data, uint16_t data_mask, + enum camera_sensor_i2c_type data_type, + enum camera_sensor_i2c_type addr_type) +{ + int32_t rc; + uint32_t reg_data = 0; + + rc = cam_cci_i2c_read(client, addr, ®_data, + addr_type, data_type); + if (rc < 0) + return rc; + + reg_data = reg_data & 0xFFFF; + if (data == (reg_data & ~data_mask)) + return I2C_COMPARE_MATCH; + return I2C_COMPARE_MISMATCH; +} + +int32_t cam_cci_i2c_poll(struct cam_sensor_cci_client *client, + uint32_t addr, uint16_t data, uint16_t data_mask, + enum camera_sensor_i2c_type data_type, + enum camera_sensor_i2c_type addr_type, + uint32_t delay_ms) +{ + int32_t rc = -EINVAL; + int32_t i = 0; + + CAM_DBG(CAM_SENSOR, "addr: 0x%x data: 0x%x dt: %d", + addr, data, data_type); + + if (delay_ms > MAX_POLL_DELAY_MS) { + CAM_ERR(CAM_SENSOR, "invalid delay = %d max_delay = %d", + delay_ms, MAX_POLL_DELAY_MS); + return -EINVAL; + } + for (i = 0; i < delay_ms; i++) { + rc = cam_cci_i2c_compare(client, + addr, data, data_mask, data_type, addr_type); + if (!rc) + return rc; + + usleep_range(1000, 1010); + } + + /* If rc is 1 then read is successful but poll is failure */ + if (rc == 1) + CAM_ERR(CAM_SENSOR, "poll failed rc=%d(non-fatal)", rc); + + if (rc < 0) + CAM_ERR(CAM_SENSOR, "poll failed rc=%d", rc); + + return rc; +} + +int32_t cam_sensor_cci_i2c_util(struct cam_sensor_cci_client *cci_client, + uint16_t cci_cmd) +{ + int32_t rc = 0; + struct cam_cci_ctrl cci_ctrl; + + cci_ctrl.cmd = cci_cmd; + cci_ctrl.cci_info = cci_client; + rc = v4l2_subdev_call(cci_client->cci_subdev, + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "Failed rc = %d", rc); + return rc; + } + return cci_ctrl.status; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h new file mode 100644 index 000000000000..7cddcf9338b0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h @@ -0,0 +1,180 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SENSOR_I2C_H_ +#define _CAM_SENSOR_I2C_H_ + +#include +#include +#include +#include +#include "cam_cci_dev.h" +#include "cam_sensor_io.h" + +#define I2C_POLL_TIME_MS 5 +#define MAX_POLL_DELAY_MS 100 + +#define I2C_COMPARE_MATCH 0 +#define I2C_COMPARE_MISMATCH 1 + +#define I2C_REG_DATA_MAX (8*1024) + +/** + * @client: CCI client structure + * @data: I2C data + * @addr_type: I2c address type + * @data_type: I2C data type + * + * This API handles CCI read + */ +int32_t cam_cci_i2c_read(struct cam_sensor_cci_client *client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type); + +/** + * @client: CCI client structure + * @addr: I2c address + * @data: I2C data + * @addr_type: I2c address type + * @num_byte: number of bytes + * + * This API handles CCI sequential read + */ +int32_t cam_camera_cci_i2c_read_seq(struct cam_sensor_cci_client *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte); + +/** + * @client: CCI client structure + * @write_setting: I2C register setting + * + * This API handles CCI random write + */ +int32_t cam_cci_i2c_write_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting); + +/** + * @client: CCI client structure + * @write_setting: I2C register setting + * @cam_sensor_i2c_write_flag: burst or seq write + * + * This API handles CCI continuous write + */ +int32_t cam_cci_i2c_write_continuous_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting, + uint8_t cam_sensor_i2c_write_flag); + +/** + * @cci_client: CCI client structure + * @cci_cmd: CCI command type + * + * Does I2C call to I2C functionalities + */ +int32_t cam_sensor_cci_i2c_util(struct cam_sensor_cci_client *cci_client, + uint16_t cci_cmd); + +/** + * @client: CCI client structure + * @addr: I2C address + * @data: I2C data + * @data_mask: I2C data mask + * @data_type: I2C data type + * @addr_type: I2C addr type + * @delay_ms: Delay in milli seconds + * + * This API implements CCI based I2C poll + */ +int32_t cam_cci_i2c_poll(struct cam_sensor_cci_client *client, + uint32_t addr, uint16_t data, uint16_t data_mask, + enum camera_sensor_i2c_type data_type, + enum camera_sensor_i2c_type addr_type, + uint32_t delay_ms); + + +/** + * cam_qup_i2c_read : QUP based i2c read + * @client : QUP I2C client structure + * @data : I2C data + * @addr_type : I2c address type + * @data_type : I2C data type + * + * This API handles QUP I2C read + */ + +int32_t cam_qup_i2c_read(struct i2c_client *client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type); + +/** + * cam_qup_i2c_read_seq : QUP based I2C sequential read + * @client : QUP I2C client structure + * @data : I2C data + * @addr_type : I2c address type + * @num_bytes : number of bytes to read + * This API handles QUP I2C Sequential read + */ + +int32_t cam_qup_i2c_read_seq(struct i2c_client *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte); + +/** + * cam_qup_i2c_poll : QUP based I2C poll operation + * @client : QUP I2C client structure + * @addr : I2C address + * @data : I2C data + * @data_mask : I2C data mask + * @data_type : I2C data type + * @addr_type : I2C addr type + * @delay_ms : Delay in milli seconds + * + * This API implements QUP based I2C poll + */ + +int32_t cam_qup_i2c_poll(struct i2c_client *client, + uint32_t addr, uint16_t data, uint16_t data_mask, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type, + uint32_t delay_ms); + +/** + * cam_qup_i2c_write_table : QUP based I2C write random + * @client : QUP I2C client structure + * @write_setting : I2C register settings + * + * This API handles QUP I2C random write + */ + +int32_t cam_qup_i2c_write_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting); + +/** + * cam_qup_i2c_write_continuous_write: QUP based I2C write continuous(Burst/Seq) + * @client: QUP I2C client structure + * @write_setting: I2C register setting + * @cam_sensor_i2c_write_flag: burst or seq write + * + * This API handles QUP continuous write + */ +int32_t cam_qup_i2c_write_continuous_table( + struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting, + uint8_t cam_sensor_i2c_write_flag); + +#endif /*_CAM_SENSOR_I2C_H*/ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.c new file mode 100644 index 000000000000..89aad4e7aa15 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.c @@ -0,0 +1,194 @@ +/* Copyright (c) 2017-2018, 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 "cam_sensor_io.h" +#include "cam_sensor_i2c.h" + +int32_t camera_io_dev_poll(struct camera_io_master *io_master_info, + uint32_t addr, uint16_t data, uint32_t data_mask, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type, + uint32_t delay_ms) +{ + int16_t mask = data_mask & 0xFF; + + if (!io_master_info) { + CAM_ERR(CAM_SENSOR, "Invalid Args"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + return cam_cci_i2c_poll(io_master_info->cci_client, + addr, data, mask, data_type, addr_type, delay_ms); + } else if (io_master_info->master_type == I2C_MASTER) { + return cam_qup_i2c_poll(io_master_info->client, + addr, data, data_mask, addr_type, data_type, + delay_ms); + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } +} + +int32_t camera_io_dev_read(struct camera_io_master *io_master_info, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + if (!io_master_info) { + CAM_ERR(CAM_SENSOR, "Invalid Args"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + return cam_cci_i2c_read(io_master_info->cci_client, + addr, data, addr_type, data_type); + } else if (io_master_info->master_type == I2C_MASTER) { + return cam_qup_i2c_read(io_master_info->client, + addr, data, addr_type, data_type); + } else if (io_master_info->master_type == SPI_MASTER) { + return cam_spi_read(io_master_info, + addr, data, addr_type, data_type); + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } + return 0; +} + +int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, int32_t num_bytes) +{ + if (io_master_info->master_type == CCI_MASTER) { + return cam_camera_cci_i2c_read_seq(io_master_info->cci_client, + addr, data, addr_type, num_bytes); + } else if (io_master_info->master_type == I2C_MASTER) { + return cam_qup_i2c_read_seq(io_master_info->client, + addr, data, addr_type, num_bytes); + } else if (io_master_info->master_type == SPI_MASTER) { + return cam_spi_read_seq(io_master_info, + addr, data, addr_type, num_bytes); + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } + return 0; +} + +int32_t camera_io_dev_write(struct camera_io_master *io_master_info, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + if (!write_setting || !io_master_info) { + CAM_ERR(CAM_SENSOR, + "Input parameters not valid ws: %pK ioinfo: %pK", + write_setting, io_master_info); + return -EINVAL; + } + + if (!write_setting->reg_setting) { + CAM_ERR(CAM_SENSOR, "Invalid Register Settings"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + return cam_cci_i2c_write_table(io_master_info, + write_setting); + } else if (io_master_info->master_type == I2C_MASTER) { + return cam_qup_i2c_write_table(io_master_info, + write_setting); + } else if (io_master_info->master_type == SPI_MASTER) { + return cam_spi_write_table(io_master_info, + write_setting); + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } +} + +int32_t camera_io_dev_write_continuous(struct camera_io_master *io_master_info, + struct cam_sensor_i2c_reg_setting *write_setting, + uint8_t cam_sensor_i2c_write_flag) +{ + if (!write_setting || !io_master_info) { + CAM_ERR(CAM_SENSOR, + "Input parameters not valid ws: %pK ioinfo: %pK", + write_setting, io_master_info); + return -EINVAL; + } + + if (!write_setting->reg_setting) { + CAM_ERR(CAM_SENSOR, "Invalid Register Settings"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + return cam_cci_i2c_write_continuous_table(io_master_info, + write_setting, cam_sensor_i2c_write_flag); + } else if (io_master_info->master_type == I2C_MASTER) { + return cam_qup_i2c_write_continuous_table(io_master_info, + write_setting, cam_sensor_i2c_write_flag); + } else if (io_master_info->master_type == SPI_MASTER) { + return cam_spi_write_table(io_master_info, + write_setting); + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } +} + +int32_t camera_io_init(struct camera_io_master *io_master_info) +{ + if (!io_master_info) { + CAM_ERR(CAM_SENSOR, "Invalid Args"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + io_master_info->cci_client->cci_subdev = + cam_cci_get_subdev(); + return cam_sensor_cci_i2c_util(io_master_info->cci_client, + MSM_CCI_INIT); + } else if ((io_master_info->master_type == I2C_MASTER) || + (io_master_info->master_type == SPI_MASTER)) { + return 0; + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } +} + +int32_t camera_io_release(struct camera_io_master *io_master_info) +{ + if (!io_master_info) { + CAM_ERR(CAM_SENSOR, "Invalid Args"); + return -EINVAL; + } + + if (io_master_info->master_type == CCI_MASTER) { + return cam_sensor_cci_i2c_util(io_master_info->cci_client, + MSM_CCI_RELEASE); + } else if ((io_master_info->master_type == I2C_MASTER) || + (io_master_info->master_type == SPI_MASTER)) { + return 0; + } else { + CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d", + io_master_info->master_type); + return -EINVAL; + } +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.h new file mode 100644 index 000000000000..ec5ed25c0785 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_io.h @@ -0,0 +1,119 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SENSOR_IO_H_ +#define _CAM_SENSOR_IO_H_ + +#include + +#include "cam_sensor_cmn_header.h" + +#define CCI_MASTER 1 +#define I2C_MASTER 2 +#define SPI_MASTER 3 + +/** + * @master_type: CCI master type + * @client: I2C client information structure + * @cci_client: CCI client information structure + * @spi_client: SPI client information structure + */ +struct camera_io_master { + int master_type; + struct i2c_client *client; + struct cam_sensor_cci_client *cci_client; + struct cam_sensor_spi_client *spi_client; +}; + +/** + * @io_master_info: I2C/SPI master information + * @addr: I2C address + * @data: I2C data + * @addr_type: I2C addr_type + * @data_type: I2C data type + * + * This API abstracts read functionality based on master type + */ +int32_t camera_io_dev_read(struct camera_io_master *io_master_info, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type); + +/** + * @io_master_info: I2C/SPI master information + * @addr: I2C address + * @data: I2C data + * @data_type: I2C data type + * @num_bytes: number of bytes + * + * This API abstracts read functionality based on master type + */ +int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + int32_t num_bytes); + +/** + * @io_master_info: I2C/SPI master information + * + * This API initializes the I2C/SPI master based on master type + */ +int32_t camera_io_init(struct camera_io_master *io_master_info); + +/** + * @io_master_info: I2C/SPI master information + * + * This API releases the I2C/SPI master based on master type + */ +int32_t camera_io_release(struct camera_io_master *io_master_info); + +/** + * @io_master_info: I2C/SPI master information + * @write_setting: write settings information + * + * This API abstracts write functionality based on master type + */ +int32_t camera_io_dev_write(struct camera_io_master *io_master_info, + struct cam_sensor_i2c_reg_setting *write_setting); + +/** + * @io_master_info: I2C/SPI master information + * @write_setting: write settings information + * @cam_sensor_i2c_write_flag: differentiate between burst & seq + * + * This API abstracts write functionality based on master type and + * write flag for continuous write + */ +int32_t camera_io_dev_write_continuous(struct camera_io_master *io_master_info, + struct cam_sensor_i2c_reg_setting *write_setting, + uint8_t cam_sensor_i2c_write_flag); + +/** + * @io_master_info: I2C/SPI master information + * @addr: I2C address + * @data: I2C data + * @data_mask: I2C data mask + * @data_type: I2C data type + * @addr_type: I2C address type + * @delay_ms: delay in milli seconds + * + * This API abstracts poll functionality based on master type + */ +int32_t camera_io_dev_poll(struct camera_io_master *io_master_info, + uint32_t addr, uint16_t data, uint32_t data_mask, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type, + uint32_t delay_ms); + +#include "cam_sensor_i2c.h" +#include "cam_sensor_spi.h" +#endif /* _CAM_SENSOR_IO_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c new file mode 100644 index 000000000000..05e3b7a5cdf5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c @@ -0,0 +1,537 @@ +/* Copyright (c) 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 "cam_sensor_cmn_header.h" +#include "cam_sensor_i2c.h" +#include "cam_sensor_io.h" + +#define I2C_REG_DATA_MAX (8*1024) +#define I2C_REG_MAX_BUF_SIZE 8 + +static int32_t cam_qup_i2c_rxdata( + struct i2c_client *dev_client, unsigned char *rxdata, + enum camera_sensor_i2c_type addr_type, + int data_length) +{ + int32_t rc = 0; + uint16_t saddr = dev_client->addr >> 1; + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = addr_type, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = data_length, + .buf = rxdata, + }, + }; + rc = i2c_transfer(dev_client->adapter, msgs, 2); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "failed 0x%x", saddr); + return rc; +} + + +static int32_t cam_qup_i2c_txdata( + struct camera_io_master *dev_client, unsigned char *txdata, + int length) +{ + int32_t rc = 0; + uint16_t saddr = dev_client->client->addr >> 1; + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + rc = i2c_transfer(dev_client->client->adapter, msg, 1); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "failed 0x%x", saddr); + return rc; +} + +int32_t cam_qup_i2c_read(struct i2c_client *client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + int32_t rc = -EINVAL; + unsigned char *buf = NULL; + + if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_SENSOR, "Failed with addr/data_type verfication"); + return rc; + } + + buf = kzalloc(addr_type + data_type, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[0] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[0] = addr >> 8; + buf[1] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[0] = addr >> 16; + buf[1] = addr >> 8; + buf[2] = addr; + } else { + buf[0] = addr >> 24; + buf[1] = addr >> 16; + buf[2] = addr >> 8; + buf[3] = addr; + } + + rc = cam_qup_i2c_rxdata(client, buf, addr_type, data_type); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed rc: %d", rc); + goto read_fail; + } + + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) + *data = buf[0]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) + *data = buf[0] << 8 | buf[1]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) + *data = buf[0] << 16 | buf[1] << 8 | buf[2]; + else + *data = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]; + + CAM_DBG(CAM_SENSOR, "addr = 0x%x data: 0x%x", addr, *data); +read_fail: + kfree(buf); + buf = NULL; + return rc; +} + +int32_t cam_qup_i2c_read_seq(struct i2c_client *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte) +{ + int32_t rc = -EFAULT; + unsigned char *buf = NULL; + int i; + + if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_SENSOR, "Failed with addr_type verification"); + return rc; + } + + if ((num_byte == 0) || (num_byte > I2C_REG_DATA_MAX)) { + CAM_ERR(CAM_SENSOR, "num_byte:0x%x max supported:0x%x", + num_byte, I2C_REG_DATA_MAX); + return rc; + } + + buf = kzalloc(addr_type + num_byte, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[0] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[0] = addr >> BITS_PER_BYTE; + buf[1] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[0] = addr >> 16; + buf[1] = addr >> 8; + buf[2] = addr; + } else { + buf[0] = addr >> 24; + buf[1] = addr >> 16; + buf[2] = addr >> 8; + buf[3] = addr; + } + + rc = cam_qup_i2c_rxdata(client, buf, addr_type, num_byte); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed rc: %d", rc); + goto read_seq_fail; + } + + for (i = 0; i < num_byte; i++) + data[i] = buf[i]; + +read_seq_fail: + kfree(buf); + buf = NULL; + return rc; +} + +static int32_t cam_qup_i2c_compare(struct i2c_client *client, + uint32_t addr, uint32_t data, uint16_t data_mask, + enum camera_sensor_i2c_type data_type, + enum camera_sensor_i2c_type addr_type) +{ + int32_t rc; + uint32_t reg_data = 0; + + rc = cam_qup_i2c_read(client, addr, ®_data, + addr_type, data_type); + if (rc < 0) + return rc; + + reg_data = reg_data & 0xFFFF; + if (data != (reg_data & ~data_mask)) + return I2C_COMPARE_MISMATCH; + + return I2C_COMPARE_MATCH; +} + +int32_t cam_qup_i2c_poll(struct i2c_client *client, + uint32_t addr, uint16_t data, uint16_t data_mask, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type, + uint32_t delay_ms) +{ + int32_t rc = 0; + int i = 0; + + if ((delay_ms > MAX_POLL_DELAY_MS) || (delay_ms == 0)) { + CAM_ERR(CAM_SENSOR, "invalid delay = %d max_delay = %d", + delay_ms, MAX_POLL_DELAY_MS); + return -EINVAL; + } + + if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)) + return -EINVAL; + + for (i = 0; i < delay_ms; i++) { + rc = cam_qup_i2c_compare(client, + addr, data, data_mask, data_type, addr_type); + if (rc == I2C_COMPARE_MATCH) + return rc; + + usleep_range(1000, 1010); + } + /* If rc is MISMATCH then read is successful but poll is failure */ + if (rc == I2C_COMPARE_MISMATCH) + CAM_ERR(CAM_SENSOR, "poll failed rc=%d(non-fatal)", rc); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "poll failed rc=%d", rc); + + return rc; +} + +static int32_t cam_qup_i2c_write(struct camera_io_master *client, + struct cam_sensor_i2c_reg_array *reg_setting, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + int32_t rc = 0; + unsigned char *buf = NULL; + uint8_t len = 0; + + buf = kzalloc(I2C_REG_MAX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + CAM_ERR(CAM_SENSOR, "Buffer memory allocation failed"); + return -ENOMEM; + } + + CAM_DBG(CAM_SENSOR, "reg addr = 0x%x data type: %d", + reg_setting->reg_addr, data_type); + if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[0] = reg_setting->reg_addr; + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len, buf[len]); + len = 1; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[0] = reg_setting->reg_addr >> 8; + buf[1] = reg_setting->reg_addr; + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len+1, buf[len+1]); + len = 2; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[0] = reg_setting->reg_addr >> 16; + buf[1] = reg_setting->reg_addr >> 8; + buf[2] = reg_setting->reg_addr; + len = 3; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_DWORD) { + buf[0] = reg_setting->reg_addr >> 24; + buf[1] = reg_setting->reg_addr >> 16; + buf[2] = reg_setting->reg_addr >> 8; + buf[3] = reg_setting->reg_addr; + len = 4; + } else { + CAM_ERR(CAM_SENSOR, "Invalid I2C addr type"); + rc = -EINVAL; + goto deallocate_buffer; + } + + CAM_DBG(CAM_SENSOR, "Data: 0x%x", reg_setting->reg_data); + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[len] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len, buf[len]); + len += 1; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[len] = reg_setting->reg_data >> 8; + buf[len+1] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+1, buf[len+1]); + len += 2; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[len] = reg_setting->reg_data >> 16; + buf[len + 1] = reg_setting->reg_data >> 8; + buf[len + 2] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+1, buf[len+1]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+2, buf[len+2]); + len += 3; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_DWORD) { + buf[len] = reg_setting->reg_data >> 24; + buf[len + 1] = reg_setting->reg_data >> 16; + buf[len + 2] = reg_setting->reg_data >> 8; + buf[len + 3] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+1, buf[len+1]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+2, buf[len+2]); + CAM_DBG(CAM_SENSOR, "Byte %d: 0x%x", len+3, buf[len+3]); + len += 4; + } else { + CAM_ERR(CAM_SENSOR, "Invalid Data Type"); + rc = -EINVAL; + goto deallocate_buffer; + } + + rc = cam_qup_i2c_txdata(client, buf, len); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "failed rc: %d", rc); + +deallocate_buffer: + kfree(buf); + return rc; +} + +int32_t cam_qup_i2c_write_table(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + int i; + int32_t rc = -EINVAL; + struct cam_sensor_i2c_reg_array *reg_setting; + + if (!client || !write_setting) + return rc; + + if ((write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || (write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))) + return rc; + + reg_setting = write_setting->reg_setting; + + for (i = 0; i < write_setting->size; i++) { + CAM_DBG(CAM_SENSOR, "addr 0x%x data 0x%x", + reg_setting->reg_addr, reg_setting->reg_data); + + rc = cam_qup_i2c_write(client, reg_setting, + write_setting->addr_type, write_setting->data_type); + if (rc < 0) + break; + reg_setting++; + } + + if (write_setting->delay > 20) + msleep(write_setting->delay); + else if (write_setting->delay) + usleep_range(write_setting->delay * 1000, (write_setting->delay + * 1000) + 1000); + + return rc; +} + +static int32_t cam_qup_i2c_write_seq(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + int i; + int32_t rc = 0; + struct cam_sensor_i2c_reg_array *reg_setting; + + reg_setting = write_setting->reg_setting; + + for (i = 0; i < write_setting->size; i++) { + reg_setting->reg_addr += i; + rc = cam_qup_i2c_write(client, reg_setting, + write_setting->addr_type, write_setting->data_type); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Sequential i2c write failed: rc: %d", rc); + break; + } + reg_setting++; + } + + if (write_setting->delay > 20) + msleep(write_setting->delay); + else if (write_setting->delay) + usleep_range(write_setting->delay * 1000, (write_setting->delay + * 1000) + 1000); + + return rc; +} + +static int32_t cam_qup_i2c_write_burst(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + int i; + int32_t rc = 0; + uint32_t len = 0; + unsigned char *buf = NULL; + struct cam_sensor_i2c_reg_array *reg_setting; + enum camera_sensor_i2c_type addr_type; + enum camera_sensor_i2c_type data_type; + + buf = kzalloc((write_setting->addr_type + + (write_setting->size * write_setting->data_type)), + GFP_KERNEL); + + if (!buf) { + CAM_ERR(CAM_SENSOR, "BUF is NULL"); + return -ENOMEM; + } + + reg_setting = write_setting->reg_setting; + addr_type = write_setting->addr_type; + data_type = write_setting->data_type; + + CAM_DBG(CAM_SENSOR, "reg addr = 0x%x data type: %d", + reg_setting->reg_addr, data_type); + if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[0] = reg_setting->reg_addr; + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len, buf[len]); + len = 1; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[0] = reg_setting->reg_addr >> 8; + buf[1] = reg_setting->reg_addr; + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, "byte %d: 0x%x", len+1, buf[len+1]); + len = 2; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[0] = reg_setting->reg_addr >> 16; + buf[1] = reg_setting->reg_addr >> 8; + buf[2] = reg_setting->reg_addr; + len = 3; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_DWORD) { + buf[0] = reg_setting->reg_addr >> 24; + buf[1] = reg_setting->reg_addr >> 16; + buf[2] = reg_setting->reg_addr >> 8; + buf[3] = reg_setting->reg_addr; + len = 4; + } else { + CAM_ERR(CAM_SENSOR, "Invalid I2C addr type"); + rc = -EINVAL; + goto free_res; + } + + for (i = 0; i < write_setting->size; i++) { + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[len] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len, buf[len]); + len += 1; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[len] = reg_setting->reg_data >> 8; + buf[len+1] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+1, buf[len+1]); + len += 2; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[len] = reg_setting->reg_data >> 16; + buf[len + 1] = reg_setting->reg_data >> 8; + buf[len + 2] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+1, buf[len+1]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+2, buf[len+2]); + len += 3; + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_DWORD) { + buf[len] = reg_setting->reg_data >> 24; + buf[len + 1] = reg_setting->reg_data >> 16; + buf[len + 2] = reg_setting->reg_data >> 8; + buf[len + 3] = reg_setting->reg_data; + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len, buf[len]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+1, buf[len+1]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+2, buf[len+2]); + CAM_DBG(CAM_SENSOR, + "Byte %d: 0x%x", len+3, buf[len+3]); + len += 4; + } else { + CAM_ERR(CAM_SENSOR, "Invalid Data Type"); + rc = -EINVAL; + goto free_res; + } + reg_setting++; + } + + if (len > (write_setting->addr_type + + (write_setting->size * write_setting->data_type))) { + CAM_ERR(CAM_SENSOR, "Invalid Length: %u | Expected length: %u", + len, (write_setting->addr_type + + (write_setting->size * write_setting->data_type))); + rc = -EINVAL; + goto free_res; + } + + rc = cam_qup_i2c_txdata(client, buf, len); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "failed rc: %d", rc); + +free_res: + kfree(buf); + return rc; +} + +int32_t cam_qup_i2c_write_continuous_table(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_settings, + uint8_t cam_sensor_i2c_write_flag) +{ + int32_t rc = 0; + + if (!client || !write_settings) + return -EINVAL; + + if ((write_settings->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_settings->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || (write_settings->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || write_settings->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))) + return -EINVAL; + + if (cam_sensor_i2c_write_flag == CAM_SENSOR_I2C_WRITE_BURST) + rc = cam_qup_i2c_write_burst(client, write_settings); + else if (cam_sensor_i2c_write_flag == CAM_SENSOR_I2C_WRITE_SEQ) + rc = cam_qup_i2c_write_seq(client, write_settings); + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c new file mode 100644 index 000000000000..6cc96391abdd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c @@ -0,0 +1,618 @@ +/* Copyright (c) 2017-2018, 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 +#include "cam_sensor_spi.h" +#include "cam_debug_util.h" + +static int cam_spi_txfr(struct spi_device *spi, char *txbuf, + char *rxbuf, int num_byte) +{ + struct spi_transfer txfr; + struct spi_message msg; + + memset(&txfr, 0, sizeof(txfr)); + txfr.tx_buf = txbuf; + txfr.rx_buf = rxbuf; + txfr.len = num_byte; + spi_message_init(&msg); + spi_message_add_tail(&txfr, &msg); + + return spi_sync(spi, &msg); +} + +static int cam_spi_txfr_read(struct spi_device *spi, char *txbuf, + char *rxbuf, int txlen, int rxlen) +{ + struct spi_transfer tx; + struct spi_transfer rx; + struct spi_message m; + + memset(&tx, 0, sizeof(tx)); + memset(&rx, 0, sizeof(rx)); + tx.tx_buf = txbuf; + rx.rx_buf = rxbuf; + tx.len = txlen; + rx.len = rxlen; + spi_message_init(&m); + spi_message_add_tail(&tx, &m); + spi_message_add_tail(&rx, &m); + return spi_sync(spi, &m); +} + +/** + * cam_set_addr() - helper function to set transfer address + * @addr: device address + * @addr_len: the addr field length of an instruction + * @type: type (i.e. byte-length) of @addr + * @str: shifted address output, must be zeroed when passed in + * + * This helper function sets @str based on the addr field length of an + * instruction and the data length. + */ +static void cam_set_addr(uint32_t addr, uint8_t addr_len, + enum camera_sensor_i2c_type addr_type, + char *str) +{ + if (!addr_len) + return; + + if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + str[0] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + str[0] = addr >> 8; + str[1] = addr; + } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) { + str[0] = addr >> 16; + str[1] = addr >> 8; + str[2] = addr; + } else { + str[0] = addr >> 24; + str[1] = addr >> 16; + str[2] = addr >> 8; + str[3] = addr; + } +} + +/** + * cam_spi_tx_helper() - wrapper for SPI transaction + * @client: io client + * @inst: inst of this transaction + * @addr: device addr following the inst + * @data: output byte array (could be NULL) + * @num_byte: size of @data + * @tx, rx: optional transfer buffer. It must be at least header + * + @num_byte long. + * + * This is the core function for SPI transaction, except for writes. It first + * checks address type, then allocates required memory for tx/rx buffers. + * It sends out , and optionally receives @num_byte of response, + * if @data is not NULL. This function does not check for wait conditions, + * and will return immediately once bus transaction finishes. + * + * This function will allocate buffers of header + @num_byte long. For + * large transfers, the allocation could fail. External buffer @tx, @rx + * should be passed in to bypass allocation. The size of buffer should be + * at least header + num_byte long. Since buffer is managed externally, + * @data will be ignored, and read results will be in @rx. + * @tx, @rx also can be used for repeated transfers to improve performance. + */ +static int32_t cam_spi_tx_helper(struct camera_io_master *client, + struct cam_camera_spi_inst *inst, uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte, char *tx, char *rx) +{ + int32_t rc = -EINVAL; + struct spi_device *spi = client->spi_client->spi_master; + struct device *dev = NULL; + char *ctx = NULL, *crx = NULL; + uint32_t len, hlen; + uint8_t retries = client->spi_client->retries; + uint32_t txr = 0, rxr = 0; + struct page *page_tx = NULL, *page_rx = NULL; + + hlen = cam_camera_spi_get_hlen(inst); + len = hlen + num_byte; + dev = &(spi->dev); + + if (!dev) { + CAM_ERR(CAM_SENSOR, "Invalid arguments"); + return -EINVAL; + } + + if (tx) { + ctx = tx; + } else { + txr = PAGE_ALIGN(len) >> PAGE_SHIFT; + page_tx = cma_alloc(dev_get_cma_area(dev), + txr, 0, GFP_KERNEL); + if (!page_tx) + return -ENOMEM; + + ctx = page_address(page_tx); + } + + if (num_byte) { + if (rx) { + crx = rx; + } else { + rxr = PAGE_ALIGN(len) >> PAGE_SHIFT; + page_rx = cma_alloc(dev_get_cma_area(dev), + rxr, 0, GFP_KERNEL); + if (!page_rx) { + if (!tx) + cma_release(dev_get_cma_area(dev), + page_tx, txr); + + return -ENOMEM; + } + crx = page_address(page_rx); + } + } else { + crx = NULL; + } + + ctx[0] = inst->opcode; + cam_set_addr(addr, inst->addr_len, addr_type, ctx + 1); + while ((rc = cam_spi_txfr(spi, ctx, crx, len)) && retries) { + retries--; + msleep(client->spi_client->retry_delay); + } + if (rc < 0) { + CAM_ERR(CAM_EEPROM, "failed: spi txfr rc %d", rc); + goto out; + } + if (data && num_byte && !rx) + memcpy(data, crx + hlen, num_byte); + +out: + if (!tx) + cma_release(dev_get_cma_area(dev), page_tx, txr); + if (!rx) + cma_release(dev_get_cma_area(dev), page_rx, rxr); + return rc; +} + +static int32_t cam_spi_tx_read(struct camera_io_master *client, + struct cam_camera_spi_inst *inst, uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint32_t num_byte, char *tx, char *rx) +{ + int32_t rc = -EINVAL; + struct spi_device *spi = client->spi_client->spi_master; + char *ctx = NULL, *crx = NULL; + uint32_t hlen; + uint8_t retries = client->spi_client->retries; + + hlen = cam_camera_spi_get_hlen(inst); + if (tx) { + ctx = tx; + } else { + ctx = kzalloc(hlen, GFP_KERNEL | GFP_DMA); + if (!ctx) + return -ENOMEM; + } + if (num_byte) { + if (rx) { + crx = rx; + } else { + crx = kzalloc(num_byte, GFP_KERNEL | GFP_DMA); + if (!crx) { + if (!tx) + kfree(ctx); + return -ENOMEM; + } + } + } else { + crx = NULL; + } + + ctx[0] = inst->opcode; + cam_set_addr(addr, inst->addr_len, addr_type, ctx + 1); + + CAM_DBG(CAM_EEPROM, "tx(%u): %02x %02x %02x %02x", hlen, ctx[0], + ctx[1], ctx[2], ctx[3]); + while ((rc = cam_spi_txfr_read(spi, ctx, crx, hlen, num_byte)) + && retries) { + retries--; + msleep(client->spi_client->retry_delay); + } + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + goto out; + } + if (data && num_byte && !rx) + memcpy(data, crx, num_byte); +out: + if (!tx) + kfree(ctx); + if (!rx) + kfree(crx); + return rc; +} + +int cam_spi_read(struct camera_io_master *client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + int rc = -EINVAL; + uint8_t temp[CAMERA_SENSOR_I2C_TYPE_MAX]; + + if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX + || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID + || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) { + CAM_ERR(CAM_SENSOR, "Failed with addr/data_type verification"); + return rc; + } + + rc = cam_spi_tx_read(client, + &client->spi_client->cmd_tbl.read, addr, &temp[0], + addr_type, data_type, NULL, NULL); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + return rc; + } + + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) + *data = temp[0]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) + *data = (temp[0] << BITS_PER_BYTE) | temp[1]; + else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) + *data = (temp[0] << 16 | temp[1] << 8 | temp[2]); + else + *data = (temp[0] << 24 | temp[1] << 16 | temp[2] << 8 | + temp[3]); + + CAM_DBG(CAM_SENSOR, "addr 0x%x, data %u", addr, *data); + return rc; +} + +int32_t cam_spi_read_seq(struct camera_io_master *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, int32_t num_bytes) +{ + if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID) + || (addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)) { + CAM_ERR(CAM_SENSOR, "Failed with addr_type verification"); + return -EINVAL; + } + + if (num_bytes == 0) { + CAM_ERR(CAM_SENSOR, "num_byte: 0x%x", num_bytes); + return -EINVAL; + } + + return cam_spi_tx_helper(client, + &client->spi_client->cmd_tbl.read_seq, addr, data, + addr_type, num_bytes, NULL, NULL); +} + +int cam_spi_query_id(struct camera_io_master *client, + uint32_t addr, enum camera_sensor_i2c_type addr_type, + uint8_t *data, uint32_t num_byte) +{ + return cam_spi_tx_helper(client, + &client->spi_client->cmd_tbl.query_id, + addr, data, addr_type, num_byte, NULL, NULL); +} + +static int32_t cam_spi_read_status_reg( + struct camera_io_master *client, uint8_t *status, + enum camera_sensor_i2c_type addr_type) +{ + struct cam_camera_spi_inst *rs = + &client->spi_client->cmd_tbl.read_status; + + if (rs->addr_len != 0) { + CAM_ERR(CAM_SENSOR, "not implemented yet"); + return -ENXIO; + } + return cam_spi_tx_helper(client, rs, 0, status, + addr_type, 1, NULL, NULL); +} + +static int32_t cam_spi_device_busy(struct camera_io_master *client, + uint8_t *busy, enum camera_sensor_i2c_type addr_type) +{ + int rc; + uint8_t st = 0; + + rc = cam_spi_read_status_reg(client, &st, addr_type); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed to read status reg"); + return rc; + } + *busy = st & client->spi_client->busy_mask; + return 0; +} + +static int32_t cam_spi_wait(struct camera_io_master *client, + struct cam_camera_spi_inst *inst, + enum camera_sensor_i2c_type addr_type) +{ + uint8_t busy; + int i, rc; + + CAM_DBG(CAM_SENSOR, "op 0x%x wait start", inst->opcode); + for (i = 0; i < inst->delay_count; i++) { + rc = cam_spi_device_busy(client, &busy, addr_type); + if (rc < 0) + return rc; + if (!busy) + break; + msleep(inst->delay_intv); + CAM_DBG(CAM_SENSOR, "op 0x%x wait", inst->opcode); + } + if (i > inst->delay_count) { + CAM_ERR(CAM_SENSOR, "op %x timed out", inst->opcode); + return -ETIMEDOUT; + } + CAM_DBG(CAM_SENSOR, "op %x finished", inst->opcode); + return 0; +} + +static int32_t cam_spi_write_enable(struct camera_io_master *client, + enum camera_sensor_i2c_type addr_type) +{ + struct cam_camera_spi_inst *we = + &client->spi_client->cmd_tbl.write_enable; + int rc; + + if (we->opcode == 0) + return 0; + if (we->addr_len != 0) { + CAM_ERR(CAM_SENSOR, "not implemented yet"); + return -EINVAL; + } + rc = cam_spi_tx_helper(client, we, 0, NULL, addr_type, + 0, NULL, NULL); + if (rc < 0) + CAM_ERR(CAM_SENSOR, "write enable failed"); + return rc; +} + +/** + * cam_spi_page_program() - core function to perform write + * @client: need for obtaining SPI device + * @addr: address to program on device + * @data: data to write + * @len: size of data + * @tx: tx buffer, size >= header + len + * + * This function performs SPI write, and has no boundary check. Writing range + * should not cross page boundary, or data will be corrupted. Transaction is + * guaranteed to be finished when it returns. This function should never be + * used outside cam_spi_write_seq(). + */ +static int32_t cam_spi_page_program(struct camera_io_master *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + uint16_t len, uint8_t *tx) +{ + int rc; + struct cam_camera_spi_inst *pg = + &client->spi_client->cmd_tbl.page_program; + struct spi_device *spi = client->spi_client->spi_master; + uint8_t retries = client->spi_client->retries; + uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len; + + CAM_DBG(CAM_SENSOR, "addr 0x%x, size 0x%x", addr, len); + rc = cam_spi_write_enable(client, addr_type); + if (rc < 0) + return rc; + memset(tx, 0, header_len); + tx[0] = pg->opcode; + cam_set_addr(addr, pg->addr_len, addr_type, tx + 1); + memcpy(tx + header_len, data, len); + CAM_DBG(CAM_SENSOR, "tx(%u): %02x %02x %02x %02x", + len, tx[0], tx[1], tx[2], tx[3]); + while ((rc = spi_write(spi, tx, len + header_len)) && retries) { + rc = cam_spi_wait(client, pg, addr_type); + msleep(client->spi_client->retry_delay); + retries--; + } + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed %d", rc); + return rc; + } + rc = cam_spi_wait(client, pg, addr_type); + return rc; +} + +int cam_spi_write(struct camera_io_master *client, + uint32_t addr, uint32_t data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type) +{ + struct cam_camera_spi_inst *pg = + &client->spi_client->cmd_tbl.page_program; + uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len; + uint16_t len = 0; + char buf[CAMERA_SENSOR_I2C_TYPE_MAX]; + char *tx; + int rc = -EINVAL; + + if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID) + || (addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) + || (data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID) + || (data_type != CAMERA_SENSOR_I2C_TYPE_MAX)) + return rc; + + CAM_DBG(CAM_EEPROM, "Data: 0x%x", data); + len = header_len + (uint8_t)data_type; + tx = kmalloc(len, GFP_KERNEL | GFP_DMA); + if (!tx) + goto NOMEM; + + if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) { + buf[0] = data; + CAM_DBG(CAM_EEPROM, "Byte %d: 0x%x", len, buf[0]); + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) { + buf[0] = (data >> BITS_PER_BYTE) & 0x00FF; + buf[1] = (data & 0x00FF); + } else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) { + buf[0] = (data >> 16) & 0x00FF; + buf[1] = (data >> 8) & 0x00FF; + buf[2] = (data & 0x00FF); + } else { + buf[0] = (data >> 24) & 0x00FF; + buf[1] = (data >> 16) & 0x00FF; + buf[2] = (data >> 8) & 0x00FF; + buf[3] = (data & 0x00FF); + } + + rc = cam_spi_page_program(client, addr, buf, + addr_type, data_type, tx); + if (rc < 0) + goto ERROR; + goto OUT; +NOMEM: + CAM_ERR(CAM_SENSOR, "memory allocation failed"); + return -ENOMEM; +ERROR: + CAM_ERR(CAM_SENSOR, "error write"); +OUT: + kfree(tx); + return rc; +} + +int32_t cam_spi_write_seq(struct camera_io_master *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, uint32_t num_byte) +{ + struct cam_camera_spi_inst *pg = + &client->spi_client->cmd_tbl.page_program; + const uint32_t page_size = client->spi_client->page_size; + uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len; + uint16_t len; + uint32_t cur_len, end; + char *tx, *pdata = data; + int rc = -EINVAL; + + if ((addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) || + (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)) + return rc; + /* single page write */ + if ((addr % page_size) + num_byte <= page_size) { + len = header_len + num_byte; + tx = kmalloc(len, GFP_KERNEL | GFP_DMA); + if (!tx) + goto NOMEM; + rc = cam_spi_page_program(client, addr, data, addr_type, + num_byte, tx); + if (rc < 0) + goto ERROR; + goto OUT; + } + /* multi page write */ + len = header_len + page_size; + tx = kmalloc(len, GFP_KERNEL | GFP_DMA); + if (!tx) + goto NOMEM; + while (num_byte) { + end = min(page_size, (addr % page_size) + num_byte); + cur_len = end - (addr % page_size); + CAM_ERR(CAM_SENSOR, "Addr: 0x%x curr_len: 0x%x pgSize: %d", + addr, cur_len, page_size); + rc = cam_spi_page_program(client, addr, pdata, addr_type, + cur_len, tx); + if (rc < 0) + goto ERROR; + addr += cur_len; + pdata += cur_len; + num_byte -= cur_len; + } + goto OUT; +NOMEM: + pr_err("%s: memory allocation failed\n", __func__); + return -ENOMEM; +ERROR: + pr_err("%s: error write\n", __func__); +OUT: + kfree(tx); + return rc; +} + +int cam_spi_write_table(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting) +{ + int i; + int rc = -EFAULT; + struct cam_sensor_i2c_reg_array *reg_setting; + uint16_t client_addr_type; + enum camera_sensor_i2c_type addr_type; + + if (!client || !write_setting) + return rc; + + if ((write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID) + || (write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) + || (write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID) + || (write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)) + return rc; + + reg_setting = write_setting->reg_setting; + client_addr_type = write_setting->addr_type; + addr_type = write_setting->addr_type; + for (i = 0; i < write_setting->size; i++) { + CAM_DBG(CAM_SENSOR, "addr %x data %x", + reg_setting->reg_addr, reg_setting->reg_data); + rc = cam_spi_write(client, + reg_setting->reg_addr, reg_setting->reg_data, + write_setting->addr_type, write_setting->data_type); + if (rc < 0) + break; + reg_setting++; + } + if (write_setting->delay > 20) + msleep(write_setting->delay); + else if (write_setting->delay) + usleep_range(write_setting->delay * 1000, + (write_setting->delay + * 1000) + 1000); + addr_type = client_addr_type; + return rc; +} + +int cam_spi_erase(struct camera_io_master *client, + uint32_t addr, enum camera_sensor_i2c_type addr_type, + uint32_t size) { + struct cam_camera_spi_inst *se = &client->spi_client->cmd_tbl.erase; + int rc = 0; + uint32_t cur; + uint32_t end = addr + size; + uint32_t erase_size = client->spi_client->erase_size; + + end = addr + size; + for (cur = rounddown(addr, erase_size); cur < end; cur += erase_size) { + CAM_ERR(CAM_SENSOR, "%s: erasing 0x%x size: %d\n", + __func__, cur, erase_size); + rc = cam_spi_write_enable(client, addr_type); + if (rc < 0) + return rc; + rc = cam_spi_tx_helper(client, se, cur, NULL, addr_type, 0, + NULL, NULL); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "%s: erase failed\n", __func__); + return rc; + } + rc = cam_spi_wait(client, se, addr_type); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "%s: erase timedout\n", __func__); + return rc; + } + } + + return rc; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h new file mode 100644 index 000000000000..86f775c51791 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2017-2018, 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 _CAM_SENSOR_SPI_H_ +#define _CAM_SENSOR_SPI_H_ + +#include +#include +#include +#include "cam_sensor_i2c.h" + +#define MAX_SPI_SIZE 110 +#define SPI_DYNAMIC_ALLOC + +struct cam_camera_spi_inst { + uint8_t opcode; + uint8_t addr_len; + uint8_t dummy_len; + uint8_t delay_intv; + uint8_t delay_count; +}; + +struct cam_spi_write_burst_data { + u8 data_msb; + u8 data_lsb; +}; + +struct cam_spi_write_burst_packet { + u8 cmd; + u8 addr_msb; + u8 addr_lsb; + struct cam_spi_write_burst_data data_arr[MAX_SPI_SIZE]; +}; + +struct cam_camera_burst_info { + uint32_t burst_addr; + uint32_t burst_start; + uint32_t burst_len; + uint32_t chunk_size; +}; + +struct cam_camera_spi_inst_tbl { + struct cam_camera_spi_inst read; + struct cam_camera_spi_inst read_seq; + struct cam_camera_spi_inst query_id; + struct cam_camera_spi_inst page_program; + struct cam_camera_spi_inst write_enable; + struct cam_camera_spi_inst read_status; + struct cam_camera_spi_inst erase; +}; + +struct cam_sensor_spi_client { + struct spi_device *spi_master; + struct cam_camera_spi_inst_tbl cmd_tbl; + uint8_t device_id0; + uint8_t device_id1; + uint8_t mfr_id0; + uint8_t mfr_id1; + uint8_t retry_delay; + uint8_t retries; + uint8_t busy_mask; + uint16_t page_size; + uint32_t erase_size; +}; +static __always_inline +uint16_t cam_camera_spi_get_hlen(struct cam_camera_spi_inst *inst) +{ + return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len; +} + +int cam_spi_read(struct camera_io_master *client, + uint32_t addr, uint32_t *data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type); + +int cam_spi_read_seq(struct camera_io_master *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, + int32_t num_bytes); + +int cam_spi_query_id(struct camera_io_master *client, + uint32_t addr, + enum camera_sensor_i2c_type addr_type, + uint8_t *data, uint32_t num_byte); + +int cam_spi_write(struct camera_io_master *client, + uint32_t addr, uint32_t data, + enum camera_sensor_i2c_type addr_type, + enum camera_sensor_i2c_type data_type); + +int cam_spi_write_table(struct camera_io_master *client, + struct cam_sensor_i2c_reg_setting *write_setting); + +int cam_spi_erase(struct camera_io_master *client, + uint32_t addr, enum camera_sensor_i2c_type addr_type, + uint32_t size); + +int32_t cam_spi_write_seq(struct camera_io_master *client, + uint32_t addr, uint8_t *data, + enum camera_sensor_i2c_type addr_type, uint32_t num_byte); + +#endif diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/Makefile new file mode 100644 index 000000000000..def8d6944cc5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/Makefile @@ -0,0 +1,8 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_io +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_cci +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_res_mgr +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_util.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h new file mode 100644 index 000000000000..1047a170ab68 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h @@ -0,0 +1,394 @@ +/* Copyright (c) 2017-2018, 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 _CAM_SENSOR_CMN_HEADER_ +#define _CAM_SENSOR_CMN_HEADER_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_REGULATOR 5 +#define MAX_POWER_CONFIG 12 + +#define MAX_PER_FRAME_ARRAY 32 +#define BATCH_SIZE_MAX 16 + +#define CAM_SENSOR_NAME "cam-sensor" +#define CAM_ACTUATOR_NAME "cam-actuator" +#define CAM_CSIPHY_NAME "cam-csiphy" +#define CAM_FLASH_NAME "cam-flash" +#define CAM_EEPROM_NAME "cam-eeprom" +#define CAM_OIS_NAME "cam-ois" + +#define MAX_SYSTEM_PIPELINE_DELAY 2 + +#define CAM_PKT_NOP_OPCODE 127 + +enum camera_sensor_cmd_type { + CAMERA_SENSOR_CMD_TYPE_INVALID, + CAMERA_SENSOR_CMD_TYPE_PROBE, + CAMERA_SENSOR_CMD_TYPE_PWR_UP, + CAMERA_SENSOR_CMD_TYPE_PWR_DOWN, + CAMERA_SENSOR_CMD_TYPE_I2C_INFO, + CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR, + CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_RD, + CAMERA_SENSOR_CMD_TYPE_I2C_CONT_WR, + CAMERA_SENSOR_CMD_TYPE_I2C_CONT_RD, + CAMERA_SENSOR_CMD_TYPE_WAIT, + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_INFO, + CAMERA_SENSOR_FLASH_CMD_TYPE_FIRE, + CAMERA_SENSOR_FLASH_CMD_TYPE_RER, + CAMERA_SENSOR_FLASH_CMD_TYPE_QUERYCURR, + CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET, + CAMERA_SENSOR_CMD_TYPE_RD_DATA, + CAMERA_SENSOR_FLASH_CMD_TYPE_INIT_FIRE, + CAMERA_SENSOR_CMD_TYPE_MAX, +}; + +enum camera_sensor_i2c_op_code { + CAMERA_SENSOR_I2C_OP_INVALID, + CAMERA_SENSOR_I2C_OP_RNDM_WR, + CAMERA_SENSOR_I2C_OP_RNDM_WR_VERF, + CAMERA_SENSOR_I2C_OP_CONT_WR_BRST, + CAMERA_SENSOR_I2C_OP_CONT_WR_BRST_VERF, + CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN, + CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN_VERF, + CAMERA_SENSOR_I2C_OP_MAX, +}; + +enum camera_sensor_wait_op_code { + CAMERA_SENSOR_WAIT_OP_INVALID, + CAMERA_SENSOR_WAIT_OP_COND, + CAMERA_SENSOR_WAIT_OP_HW_UCND, + CAMERA_SENSOR_WAIT_OP_SW_UCND, + CAMERA_SENSOR_WAIT_OP_MAX, +}; + +enum camera_flash_opcode { + CAMERA_SENSOR_FLASH_OP_INVALID, + CAMERA_SENSOR_FLASH_OP_OFF, + CAMERA_SENSOR_FLASH_OP_FIRELOW, + CAMERA_SENSOR_FLASH_OP_FIREHIGH, + CAMERA_SENSOR_FLASH_OP_MAX, +}; + +enum camera_sensor_i2c_type { + CAMERA_SENSOR_I2C_TYPE_INVALID, + CAMERA_SENSOR_I2C_TYPE_BYTE, + CAMERA_SENSOR_I2C_TYPE_WORD, + CAMERA_SENSOR_I2C_TYPE_3B, + CAMERA_SENSOR_I2C_TYPE_DWORD, + CAMERA_SENSOR_I2C_TYPE_MAX, +}; + +enum i2c_freq_mode { + I2C_STANDARD_MODE, + I2C_FAST_MODE, + I2C_CUSTOM_MODE, + I2C_FAST_PLUS_MODE, + I2C_MAX_MODES, +}; + +enum position_roll { + ROLL_0 = 0, + ROLL_90 = 90, + ROLL_180 = 180, + ROLL_270 = 270, + ROLL_INVALID = 360, +}; + +enum position_yaw { + FRONT_CAMERA_YAW = 0, + REAR_CAMERA_YAW = 180, + INVALID_YAW = 360, +}; + +enum position_pitch { + LEVEL_PITCH = 0, + INVALID_PITCH = 360, +}; + +enum sensor_sub_module { + SUB_MODULE_SENSOR, + SUB_MODULE_ACTUATOR, + SUB_MODULE_EEPROM, + SUB_MODULE_LED_FLASH, + SUB_MODULE_CSID, + SUB_MODULE_CSIPHY, + SUB_MODULE_OIS, + SUB_MODULE_EXT, + SUB_MODULE_MAX, +}; + +enum msm_camera_power_seq_type { + SENSOR_MCLK, + SENSOR_VANA, + SENSOR_VDIG, + SENSOR_VIO, + SENSOR_VAF, + SENSOR_VAF_PWDM, + SENSOR_CUSTOM_REG1, + SENSOR_CUSTOM_REG2, + SENSOR_RESET, + SENSOR_STANDBY, + SENSOR_CUSTOM_GPIO1, + SENSOR_CUSTOM_GPIO2, + SENSOR_SEQ_TYPE_MAX, +}; + +enum cam_sensor_packet_opcodes { + CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON, + CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE, + CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG, + CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE, + CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, + CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF, + CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP = 127 +}; + +enum cam_actuator_packet_opcodes { + CAM_ACTUATOR_PACKET_OPCODE_INIT, + CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS, + CAM_ACTUATOR_PACKET_MANUAL_MOVE_LENS +}; + +enum cam_eeprom_packet_opcodes { + CAM_EEPROM_PACKET_OPCODE_INIT +}; + +enum cam_ois_packet_opcodes { + CAM_OIS_PACKET_OPCODE_INIT, + CAM_OIS_PACKET_OPCODE_OIS_CONTROL +}; + +enum msm_bus_perf_setting { + S_INIT, + S_PREVIEW, + S_VIDEO, + S_CAPTURE, + S_ZSL, + S_STEREO_VIDEO, + S_STEREO_CAPTURE, + S_DEFAULT, + S_LIVESHOT, + S_DUAL, + S_EXIT +}; + +enum msm_camera_device_type_t { + MSM_CAMERA_I2C_DEVICE, + MSM_CAMERA_PLATFORM_DEVICE, + MSM_CAMERA_SPI_DEVICE, +}; + +enum cam_flash_device_type { + CAMERA_FLASH_DEVICE_TYPE_PMIC = 0, + CAMERA_FLASH_DEVICE_TYPE_I2C, + CAMERA_FLASH_DEVICE_TYPE_GPIO, +}; + +enum cci_i2c_master_t { + MASTER_0, + MASTER_1, + MASTER_MAX, +}; + +enum camera_vreg_type { + VREG_TYPE_DEFAULT, + VREG_TYPE_CUSTOM, +}; + +enum cam_sensor_i2c_cmd_type { + CAM_SENSOR_I2C_WRITE_RANDOM, + CAM_SENSOR_I2C_WRITE_BURST, + CAM_SENSOR_I2C_WRITE_SEQ, + CAM_SENSOR_I2C_READ, + CAM_SENSOR_I2C_POLL +}; + +struct common_header { + uint16_t first_word; + uint8_t third_byte; + uint8_t cmd_type; +}; + +struct camera_vreg_t { + const char *reg_name; + int min_voltage; + int max_voltage; + int op_mode; + uint32_t delay; + const char *custom_vreg_name; + enum camera_vreg_type type; +}; + +struct msm_camera_gpio_num_info { + uint16_t gpio_num[SENSOR_SEQ_TYPE_MAX]; + uint8_t valid[SENSOR_SEQ_TYPE_MAX]; +}; + +struct msm_cam_clk_info { + const char *clk_name; + long clk_rate; + uint32_t delay; +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + bool use_pinctrl; +}; + +struct cam_sensor_i2c_reg_array { + uint32_t reg_addr; + uint32_t reg_data; + uint32_t delay; + uint32_t data_mask; +}; + +struct cam_sensor_i2c_reg_setting { + struct cam_sensor_i2c_reg_array *reg_setting; + unsigned short size; + enum camera_sensor_i2c_type addr_type; + enum camera_sensor_i2c_type data_type; + unsigned short delay; +}; + +struct i2c_settings_list { + struct cam_sensor_i2c_reg_setting i2c_settings; + enum cam_sensor_i2c_cmd_type op_code; + struct list_head list; +}; + +struct i2c_settings_array { + struct list_head list_head; + int32_t is_settings_valid; + int64_t request_id; +}; + +struct i2c_data_settings { + struct i2c_settings_array init_settings; + struct i2c_settings_array config_settings; + struct i2c_settings_array streamon_settings; + struct i2c_settings_array streamoff_settings; + struct i2c_settings_array *per_frame; +}; + +struct cam_sensor_power_ctrl_t { + struct device *dev; + struct cam_sensor_power_setting *power_setting; + uint16_t power_setting_size; + struct cam_sensor_power_setting *power_down_setting; + uint16_t power_down_setting_size; + struct msm_camera_gpio_num_info *gpio_num_info; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; +}; + +struct cam_camera_slave_info { + uint16_t sensor_slave_addr; + uint16_t sensor_id_reg_addr; + uint16_t sensor_id; + uint16_t sensor_id_mask; +}; + +struct cam_camera_id_info { + uint16_t sensor_slave_addr; + uint16_t sensor_id_mask; + uint32_t sensor_id_reg_addr; + uint32_t sensor_id; + uint8_t sensor_addr_type; + uint8_t sensor_data_type; +}; + +struct msm_sensor_init_params { + int modes_supported; + unsigned int sensor_mount_angle; +}; + +enum msm_sensor_camera_id_t { + CAMERA_0, + CAMERA_1, + CAMERA_2, + CAMERA_3, + MAX_CAMERAS, +}; + +struct msm_sensor_id_info_t { + unsigned short sensor_id_reg_addr; + unsigned short sensor_id; + unsigned short sensor_id_mask; +}; + +enum msm_sensor_output_format_t { + MSM_SENSOR_BAYER, + MSM_SENSOR_YCBCR, + MSM_SENSOR_META, +}; + +struct cam_sensor_power_setting { + enum msm_camera_power_seq_type seq_type; + unsigned short seq_val; + long config_val; + unsigned short delay; + void *data[10]; +}; + +struct cam_sensor_board_info { + struct cam_camera_slave_info slave_info; + struct cam_camera_id_info id_info; + int32_t sensor_mount_angle; + int32_t secure_mode; + int modes_supported; + int32_t pos_roll; + int32_t pos_yaw; + int32_t pos_pitch; + int32_t subdev_id[SUB_MODULE_MAX]; + int32_t subdev_intf[SUB_MODULE_MAX]; + const char *misc_regulator; + struct cam_sensor_power_ctrl_t power_info; +}; + +enum msm_camera_vreg_name_t { + CAM_VDIG, + CAM_VIO, + CAM_VANA, + CAM_VAF, + CAM_V_CUSTOM1, + CAM_V_CUSTOM2, + CAM_VREG_MAX, +}; + +struct msm_camera_gpio_conf { + void *cam_gpiomux_conf_tbl; + uint8_t cam_gpiomux_conf_tbl_size; + struct gpio *cam_gpio_common_tbl; + uint8_t cam_gpio_common_tbl_size; + struct gpio *cam_gpio_req_tbl; + uint8_t cam_gpio_req_tbl_size; + uint32_t gpio_no_mux; + uint32_t *camera_off_table; + uint8_t camera_off_table_size; + uint32_t *camera_on_table; + uint8_t camera_on_table_size; + struct msm_camera_gpio_num_info *gpio_num_info; +}; + +#endif /* _CAM_SENSOR_CMN_HEADER_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c new file mode 100644 index 000000000000..9b8af7056d20 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c @@ -0,0 +1,1754 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include "cam_sensor_util.h" +#include +#include "cam_res_mgr_api.h" + +#define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend" +#define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default" + +#define VALIDATE_VOLTAGE(min, max, config_val) ((config_val) && \ + (config_val >= min) && (config_val <= max)) + +static struct i2c_settings_list* + cam_sensor_get_i2c_ptr(struct i2c_settings_array *i2c_reg_settings, + uint32_t size) +{ + struct i2c_settings_list *tmp; + + tmp = (struct i2c_settings_list *) + kzalloc(sizeof(struct i2c_settings_list), GFP_KERNEL); + + if (tmp != NULL) + list_add_tail(&(tmp->list), + &(i2c_reg_settings->list_head)); + else + return NULL; + + if ((sizeof(struct cam_sensor_i2c_reg_array) * size) < PAGE_SIZE) { + tmp->i2c_settings.reg_setting = + (struct cam_sensor_i2c_reg_array *) + kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * + size, GFP_KERNEL); + if (tmp->i2c_settings.reg_setting == NULL) { + list_del(&(tmp->list)); + kfree(tmp); + return NULL; + } + } else { + tmp->i2c_settings.reg_setting = + (struct cam_sensor_i2c_reg_array *) + vzalloc(sizeof(struct cam_sensor_i2c_reg_array) * + size); + if (tmp->i2c_settings.reg_setting == NULL) { + list_del(&(tmp->list)); + kfree(tmp); + return NULL; + } + } + tmp->i2c_settings.size = size; + + return tmp; +} + +int32_t delete_request(struct i2c_settings_array *i2c_array) +{ + struct i2c_settings_list *i2c_list = NULL, *i2c_next = NULL; + int32_t rc = 0; + + if (i2c_array == NULL) { + CAM_ERR(CAM_SENSOR, "FATAL:: Invalid argument"); + return -EINVAL; + } + + list_for_each_entry_safe(i2c_list, i2c_next, + &(i2c_array->list_head), list) { + + if ((sizeof(struct cam_sensor_i2c_reg_array) * + i2c_list->i2c_settings.size) < PAGE_SIZE) { + kfree(i2c_list->i2c_settings.reg_setting); + } else { + vfree(i2c_list->i2c_settings.reg_setting); + } + + list_del(&(i2c_list->list)); + kfree(i2c_list); + } + INIT_LIST_HEAD(&(i2c_array->list_head)); + i2c_array->is_settings_valid = 0; + + return rc; +} + +int32_t cam_sensor_handle_delay( + uint32_t **cmd_buf, + uint16_t generic_op_code, + struct i2c_settings_array *i2c_reg_settings, + uint32_t offset, uint32_t *byte_cnt, + struct list_head *list_ptr) +{ + int32_t rc = 0; + struct cam_cmd_unconditional_wait *cmd_uncond_wait = + (struct cam_cmd_unconditional_wait *) *cmd_buf; + struct i2c_settings_list *i2c_list = NULL; + + if (list_ptr == NULL) { + CAM_ERR(CAM_SENSOR, "Invalid list ptr"); + return -EINVAL; + } + + if (offset > 0) { + i2c_list = + list_entry(list_ptr, struct i2c_settings_list, list); + if (generic_op_code == + CAMERA_SENSOR_WAIT_OP_HW_UCND) + i2c_list->i2c_settings. + reg_setting[offset - 1].delay = + cmd_uncond_wait->delay; + else + i2c_list->i2c_settings.delay = + cmd_uncond_wait->delay; + (*cmd_buf) += + sizeof( + struct cam_cmd_unconditional_wait) / sizeof(uint32_t); + (*byte_cnt) += + sizeof( + struct cam_cmd_unconditional_wait); + } else { + CAM_ERR(CAM_SENSOR, "Delay Rxed Before any buffer: %d", offset); + return -EINVAL; + } + + return rc; +} + +int32_t cam_sensor_handle_poll( + uint32_t **cmd_buf, + struct i2c_settings_array *i2c_reg_settings, + uint32_t *byte_cnt, int32_t *offset, + struct list_head **list_ptr) +{ + struct i2c_settings_list *i2c_list; + int32_t rc = 0; + struct cam_cmd_conditional_wait *cond_wait + = (struct cam_cmd_conditional_wait *) *cmd_buf; + + i2c_list = + cam_sensor_get_i2c_ptr(i2c_reg_settings, 1); + if (!i2c_list || !i2c_list->i2c_settings.reg_setting) { + CAM_ERR(CAM_SENSOR, "Failed in allocating mem for list"); + return -ENOMEM; + } + *offset = 0;//add by HHK fox fix delayus invalid + i2c_list->op_code = CAM_SENSOR_I2C_POLL; + i2c_list->i2c_settings.data_type = + cond_wait->data_type; + i2c_list->i2c_settings.addr_type = + cond_wait->addr_type; + i2c_list->i2c_settings.reg_setting->reg_addr = + cond_wait->reg_addr; + i2c_list->i2c_settings.reg_setting->reg_data = + cond_wait->reg_data; + i2c_list->i2c_settings.reg_setting->delay = + cond_wait->timeout; + + (*cmd_buf) += sizeof(struct cam_cmd_conditional_wait) / + sizeof(uint32_t); + (*byte_cnt) += sizeof(struct cam_cmd_conditional_wait); + + *offset = 1; + *list_ptr = &(i2c_list->list); + + return rc; +} + +int32_t cam_sensor_handle_random_write( + struct cam_cmd_i2c_random_wr *cam_cmd_i2c_random_wr, + struct i2c_settings_array *i2c_reg_settings, + uint16_t *cmd_length_in_bytes, int32_t *offset, + struct list_head **list) +{ + struct i2c_settings_list *i2c_list; + int32_t rc = 0, cnt; + + i2c_list = cam_sensor_get_i2c_ptr(i2c_reg_settings, + cam_cmd_i2c_random_wr->header.count); + if (i2c_list == NULL || + i2c_list->i2c_settings.reg_setting == NULL) { + CAM_ERR(CAM_SENSOR, "Failed in allocating i2c_list"); + return -ENOMEM; + } + + *cmd_length_in_bytes = (sizeof(struct i2c_rdwr_header) + + sizeof(struct i2c_random_wr_payload) * + (cam_cmd_i2c_random_wr->header.count)); + i2c_list->op_code = CAM_SENSOR_I2C_WRITE_RANDOM; + i2c_list->i2c_settings.addr_type = + cam_cmd_i2c_random_wr->header.addr_type; + i2c_list->i2c_settings.data_type = + cam_cmd_i2c_random_wr->header.data_type; + *offset = 0;//add by HHK fox fix delayus invalid + for (cnt = 0; cnt < (cam_cmd_i2c_random_wr->header.count); + cnt++) { + i2c_list->i2c_settings.reg_setting[cnt].reg_addr = + cam_cmd_i2c_random_wr-> + random_wr_payload[cnt].reg_addr; + i2c_list->i2c_settings. + reg_setting[cnt].reg_data = + cam_cmd_i2c_random_wr-> + random_wr_payload[cnt].reg_data; + i2c_list->i2c_settings. + reg_setting[cnt].data_mask = 0; + } + *offset = cnt; + *list = &(i2c_list->list); + + return rc; +} + +static int32_t cam_sensor_handle_continuous_write( + struct cam_cmd_i2c_continuous_wr *cam_cmd_i2c_continuous_wr, + struct i2c_settings_array *i2c_reg_settings, + uint16_t *cmd_length_in_bytes, int32_t *offset, + struct list_head **list) +{ + struct i2c_settings_list *i2c_list; + int32_t rc = 0, cnt; + + i2c_list = cam_sensor_get_i2c_ptr(i2c_reg_settings, + cam_cmd_i2c_continuous_wr->header.count); + if (i2c_list == NULL || + i2c_list->i2c_settings.reg_setting == NULL) { + CAM_ERR(CAM_SENSOR, "Failed in allocating i2c_list"); + return -ENOMEM; + } + + *cmd_length_in_bytes = (sizeof(struct i2c_rdwr_header) + + sizeof(cam_cmd_i2c_continuous_wr->reg_addr) + + sizeof(struct cam_cmd_read) * + (cam_cmd_i2c_continuous_wr->header.count)); + if (cam_cmd_i2c_continuous_wr->header.op_code == + CAMERA_SENSOR_I2C_OP_CONT_WR_BRST) + i2c_list->op_code = CAM_SENSOR_I2C_WRITE_BURST; + else if (cam_cmd_i2c_continuous_wr->header.op_code == + CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN) + i2c_list->op_code = CAM_SENSOR_I2C_WRITE_SEQ; + else + return -EINVAL; + + i2c_list->i2c_settings.addr_type = + cam_cmd_i2c_continuous_wr->header.addr_type; + i2c_list->i2c_settings.data_type = + cam_cmd_i2c_continuous_wr->header.data_type; + i2c_list->i2c_settings.size = + cam_cmd_i2c_continuous_wr->header.count; + *offset = 0;//add by HHK fox fix delayus invalid + for (cnt = 0; cnt < (cam_cmd_i2c_continuous_wr->header.count); + cnt++) { + i2c_list->i2c_settings.reg_setting[cnt].reg_addr = + cam_cmd_i2c_continuous_wr-> + reg_addr; + i2c_list->i2c_settings. + reg_setting[cnt].reg_data = + cam_cmd_i2c_continuous_wr-> + data_read[cnt].reg_data; + i2c_list->i2c_settings. + reg_setting[cnt].data_mask = 0; + } + *offset = cnt; + *list = &(i2c_list->list); + + return rc; +} + +/** + * Name : cam_sensor_i2c_command_parser + * Description : Parse CSL CCI packet and apply register settings + * Parameters : s_ctrl input/output sub_device + * arg input cam_control + * Description : + * Handle multiple I2C RD/WR and WAIT cmd formats in one command + * buffer, for example, a command buffer of m x RND_WR + 1 x HW_ + * WAIT + n x RND_WR with num_cmd_buf = 1. Do not exepect RD/WR + * with different cmd_type and op_code in one command buffer. + */ +int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings, + struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers) +{ + int16_t rc = 0, i = 0; + size_t len_of_buff = 0; + uintptr_t generic_ptr; + + for (i = 0; i < num_cmd_buffers; i++) { + uint32_t *cmd_buf = NULL; + struct common_header *cmm_hdr; + uint16_t generic_op_code; + uint32_t byte_cnt = 0; + uint32_t j = 0; + struct list_head *list = NULL; + + /* + * It is not expected the same settings to + * be spread across multiple cmd buffers + */ + + CAM_DBG(CAM_SENSOR, "Total cmd Buf in Bytes: %d", + cmd_desc[i].length); + + if (!cmd_desc[i].length) + continue; + + rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, + &generic_ptr, &len_of_buff); + cmd_buf = (uint32_t *)generic_ptr; + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "cmd hdl failed:%d, Err: %d, Buffer_len: %ld", + cmd_desc[i].mem_handle, rc, len_of_buff); + return rc; + } + cmd_buf += cmd_desc[i].offset / sizeof(uint32_t); + + while (byte_cnt < cmd_desc[i].length) { + cmm_hdr = (struct common_header *)cmd_buf; + generic_op_code = cmm_hdr->third_byte; + switch (cmm_hdr->cmd_type) { + case CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR: { + uint16_t cmd_length_in_bytes = 0; + struct cam_cmd_i2c_random_wr + *cam_cmd_i2c_random_wr = + (struct cam_cmd_i2c_random_wr *)cmd_buf; + + rc = cam_sensor_handle_random_write( + cam_cmd_i2c_random_wr, + i2c_reg_settings, + &cmd_length_in_bytes, &j, &list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed in random write %d", rc); + return rc; + } + + cmd_buf += cmd_length_in_bytes / + sizeof(uint32_t); + byte_cnt += cmd_length_in_bytes; + break; + } + case CAMERA_SENSOR_CMD_TYPE_I2C_CONT_WR: { + uint16_t cmd_length_in_bytes = 0; + struct cam_cmd_i2c_continuous_wr + *cam_cmd_i2c_continuous_wr = + (struct cam_cmd_i2c_continuous_wr *) + cmd_buf; + + rc = cam_sensor_handle_continuous_write( + cam_cmd_i2c_continuous_wr, + i2c_reg_settings, + &cmd_length_in_bytes, &j, &list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Failed in continuous write %d", rc); + return rc; + } + + cmd_buf += cmd_length_in_bytes / + sizeof(uint32_t); + byte_cnt += cmd_length_in_bytes; + break; + } + case CAMERA_SENSOR_CMD_TYPE_WAIT: { + if (generic_op_code == + CAMERA_SENSOR_WAIT_OP_HW_UCND || + generic_op_code == + CAMERA_SENSOR_WAIT_OP_SW_UCND) { + + rc = cam_sensor_handle_delay( + &cmd_buf, generic_op_code, + i2c_reg_settings, j, &byte_cnt, + list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "delay hdl failed: %d", + rc); + return rc; + } + + } else if (generic_op_code == + CAMERA_SENSOR_WAIT_OP_COND) { + rc = cam_sensor_handle_poll( + &cmd_buf, i2c_reg_settings, + &byte_cnt, &j, &list); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Random read fail: %d", + rc); + return rc; + } + } else { + CAM_ERR(CAM_SENSOR, + "Wrong Wait Command: %d", + generic_op_code); + return -EINVAL; + } + break; + } + default: + CAM_ERR(CAM_SENSOR, "Invalid Command Type:%d", + cmm_hdr->cmd_type); + return -EINVAL; + } + } + i2c_reg_settings->is_settings_valid = 1; + } + + return rc; +} + +int32_t msm_camera_fill_vreg_params( + struct cam_hw_soc_info *soc_info, + struct cam_sensor_power_setting *power_setting, + uint16_t power_setting_size) +{ + int32_t rc = 0, j = 0, i = 0; + int num_vreg; + + /* Validate input parameters */ + if (!soc_info || !power_setting) { + CAM_ERR(CAM_SENSOR, "failed: soc_info %pK power_setting %pK", + soc_info, power_setting); + return -EINVAL; + } + + num_vreg = soc_info->num_rgltr; + + if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) { + CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg); + return -EINVAL; + } + + for (i = 0; i < power_setting_size; i++) { + + if (power_setting[i].seq_type < SENSOR_MCLK || + power_setting[i].seq_type >= SENSOR_SEQ_TYPE_MAX) { + CAM_ERR(CAM_SENSOR, "failed: Invalid Seq type: %d", + power_setting[i].seq_type); + return -EINVAL; + } + + switch (power_setting[i].seq_type) { + case SENSOR_VDIG: + for (j = 0; j < num_vreg; j++) { + if (!strcmp(soc_info->rgltr_name[j], + "cam_vdig")) { + + CAM_DBG(CAM_SENSOR, + "i: %d j: %d cam_vdig", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case SENSOR_VIO: + for (j = 0; j < num_vreg; j++) { + + if (!strcmp(soc_info->rgltr_name[j], + "cam_vio")) { + CAM_DBG(CAM_SENSOR, + "i: %d j: %d cam_vio", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + break; + } + + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case SENSOR_VANA: + for (j = 0; j < num_vreg; j++) { + + if (!strcmp(soc_info->rgltr_name[j], + "cam_vana")) { + CAM_DBG(CAM_SENSOR, + "i: %d j: %d cam_vana", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + break; + } + + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case SENSOR_VAF: + for (j = 0; j < num_vreg; j++) { + + if (!strcmp(soc_info->rgltr_name[j], + "cam_vaf")) { + CAM_DBG(CAM_SENSOR, + "i: %d j: %d cam_vaf", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + + break; + } + + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + + case SENSOR_CUSTOM_REG1: + for (j = 0; j < num_vreg; j++) { + + if (!strcmp(soc_info->rgltr_name[j], + "cam_v_custom1")) { + CAM_DBG(CAM_SENSOR, + "i:%d j:%d cam_vcustom1", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + break; + } + + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + case SENSOR_CUSTOM_REG2: + for (j = 0; j < num_vreg; j++) { + + if (!strcmp(soc_info->rgltr_name[j], + "cam_v_custom2")) { + CAM_DBG(CAM_SENSOR, + "i:%d j:%d cam_vcustom2", i, j); + power_setting[i].seq_val = j; + + if (VALIDATE_VOLTAGE( + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + power_setting[i].config_val)) { + soc_info->rgltr_min_volt[j] = + soc_info->rgltr_max_volt[j] = + power_setting[i].config_val; + } + break; + } + } + if (j == num_vreg) + power_setting[i].seq_val = INVALID_VREG; + break; + default: + break; + } + } + + return rc; +} + +int cam_sensor_util_request_gpio_table( + struct cam_hw_soc_info *soc_info, int gpio_en) +{ + int rc = 0, i = 0; + uint8_t size = 0; + struct cam_soc_gpio_data *gpio_conf = + soc_info->gpio_data; + struct gpio *gpio_tbl = NULL; + + if (!gpio_conf) { + CAM_INFO(CAM_SENSOR, "No GPIO data"); + return 0; + } + + if (gpio_conf->cam_gpio_common_tbl_size <= 0) { + CAM_INFO(CAM_SENSOR, "No GPIO entry"); + return -EINVAL; + } + + gpio_tbl = gpio_conf->cam_gpio_req_tbl; + size = gpio_conf->cam_gpio_req_tbl_size; + + if (!gpio_tbl || !size) { + CAM_ERR(CAM_SENSOR, "invalid gpio_tbl %pK / size %d", + gpio_tbl, size); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + CAM_DBG(CAM_SENSOR, "i: %d, gpio %d dir %ld", i, + gpio_tbl[i].gpio, gpio_tbl[i].flags); + } + + if (gpio_en) { + for (i = 0; i < size; i++) { + rc = cam_res_mgr_gpio_request(soc_info->dev, + gpio_tbl[i].gpio, + gpio_tbl[i].flags, gpio_tbl[i].label); + if (rc) { + /* + * After GPIO request fails, contine to + * apply new gpios, outout a error message + * for driver bringup debug + */ + CAM_ERR(CAM_SENSOR, "gpio %d:%s request fails", + gpio_tbl[i].gpio, gpio_tbl[i].label); + } + } + } else { + cam_res_mgr_gpio_free_arry(soc_info->dev, gpio_tbl, size); + } + + return rc; +} + +int32_t cam_sensor_update_power_settings(void *cmd_buf, + int cmd_length, struct cam_sensor_power_ctrl_t *power_info) +{ + int32_t rc = 0, tot_size = 0, last_cmd_type = 0; + int32_t i = 0, pwr_up = 0, pwr_down = 0; + void *ptr = cmd_buf, *scr; + struct cam_cmd_power *pwr_cmd = (struct cam_cmd_power *)cmd_buf; + struct common_header *cmm_hdr = (struct common_header *)cmd_buf; + + if (!pwr_cmd || !cmd_length) { + CAM_ERR(CAM_SENSOR, "Invalid Args: pwr_cmd %pK, cmd_length: %d", + pwr_cmd, cmd_length); + return -EINVAL; + } + + power_info->power_setting_size = 0; + power_info->power_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting) * + MAX_POWER_CONFIG, GFP_KERNEL); + if (!power_info->power_setting) + return -ENOMEM; + + power_info->power_down_setting_size = 0; + power_info->power_down_setting = + (struct cam_sensor_power_setting *) + kzalloc(sizeof(struct cam_sensor_power_setting) * + MAX_POWER_CONFIG, GFP_KERNEL); + if (!power_info->power_down_setting) { + rc = -ENOMEM; + goto free_power_settings; + } + + while (tot_size < cmd_length) { + if (cmm_hdr->cmd_type == + CAMERA_SENSOR_CMD_TYPE_PWR_UP) { + struct cam_cmd_power *pwr_cmd = + (struct cam_cmd_power *)ptr; + + power_info->power_setting_size += pwr_cmd->count; + scr = ptr + sizeof(struct cam_cmd_power); + tot_size = tot_size + sizeof(struct cam_cmd_power); + + if (pwr_cmd->count == 0) + CAM_WARN(CAM_SENSOR, "Un expected Command"); + + for (i = 0; i < pwr_cmd->count; i++, pwr_up++) { + power_info-> + power_setting[pwr_up].seq_type = + pwr_cmd->power_settings[i]. + power_seq_type; + power_info-> + power_setting[pwr_up].config_val = + pwr_cmd->power_settings[i]. + config_val_low; + power_info->power_setting[pwr_up].delay = 0; + if (i) { + scr = scr + + sizeof( + struct cam_power_settings); + tot_size = tot_size + + sizeof( + struct cam_power_settings); + } + if (tot_size > cmd_length) { + CAM_ERR(CAM_SENSOR, + "Error: Cmd Buffer is wrong"); + rc = -EINVAL; + goto free_power_down_settings; + } + CAM_DBG(CAM_SENSOR, + "Seq Type[%d]: %d Config_val: %ld", + pwr_up, + power_info-> + power_setting[pwr_up].seq_type, + power_info-> + power_setting[pwr_up]. + config_val); + } + last_cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP; + ptr = (void *) scr; + cmm_hdr = (struct common_header *)ptr; + } else if (cmm_hdr->cmd_type == CAMERA_SENSOR_CMD_TYPE_WAIT) { + struct cam_cmd_unconditional_wait *wait_cmd = + (struct cam_cmd_unconditional_wait *)ptr; + if (wait_cmd->op_code == + CAMERA_SENSOR_WAIT_OP_SW_UCND) { + if (last_cmd_type == + CAMERA_SENSOR_CMD_TYPE_PWR_UP) { + if (pwr_up > 0) + power_info-> + power_setting + [pwr_up - 1].delay += + wait_cmd->delay; + else + CAM_ERR(CAM_SENSOR, + "Delay is expected only after valid power up setting"); + } else if (last_cmd_type == + CAMERA_SENSOR_CMD_TYPE_PWR_DOWN) { + if (pwr_down > 0) + power_info-> + power_down_setting + [pwr_down - 1].delay += + wait_cmd->delay; + else + CAM_ERR(CAM_SENSOR, + "Delay is expected only after valid power up setting"); + } + } else + CAM_DBG(CAM_SENSOR, "Invalid op code: %d", + wait_cmd->op_code); + tot_size = tot_size + + sizeof(struct cam_cmd_unconditional_wait); + if (tot_size > cmd_length) { + CAM_ERR(CAM_SENSOR, "Command Buffer is wrong"); + rc = -EINVAL; + goto free_power_down_settings; + } + scr = (void *) (wait_cmd); + ptr = (void *) + (scr + + sizeof(struct cam_cmd_unconditional_wait)); + CAM_DBG(CAM_SENSOR, "ptr: %pK sizeof: %d Next: %pK", + scr, (int32_t)sizeof( + struct cam_cmd_unconditional_wait), ptr); + + cmm_hdr = (struct common_header *)ptr; + } else if (cmm_hdr->cmd_type == + CAMERA_SENSOR_CMD_TYPE_PWR_DOWN) { + struct cam_cmd_power *pwr_cmd = + (struct cam_cmd_power *)ptr; + + scr = ptr + sizeof(struct cam_cmd_power); + tot_size = tot_size + sizeof(struct cam_cmd_power); + power_info->power_down_setting_size += pwr_cmd->count; + + if (pwr_cmd->count == 0) + CAM_ERR(CAM_SENSOR, "Invalid Command"); + + for (i = 0; i < pwr_cmd->count; i++, pwr_down++) { + power_info-> + power_down_setting[pwr_down]. + seq_type = + pwr_cmd->power_settings[i]. + power_seq_type; + power_info-> + power_down_setting[pwr_down]. + config_val = + pwr_cmd->power_settings[i]. + config_val_low; + power_info-> + power_down_setting[pwr_down].delay = 0; + if (i) { + scr = scr + + sizeof( + struct cam_power_settings); + tot_size = + tot_size + + sizeof( + struct cam_power_settings); + } + if (tot_size > cmd_length) { + CAM_ERR(CAM_SENSOR, + "Command Buffer is wrong"); + rc = -EINVAL; + goto free_power_down_settings; + } + CAM_DBG(CAM_SENSOR, + "Seq Type[%d]: %d Config_val: %ld", + pwr_down, + power_info-> + power_down_setting[pwr_down]. + seq_type, + power_info-> + power_down_setting[pwr_down]. + config_val); + } + last_cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN; + ptr = (void *) scr; + cmm_hdr = (struct common_header *)ptr; + } else { + CAM_ERR(CAM_SENSOR, + "Error: Un expected Header Type: %d", + cmm_hdr->cmd_type); + } + } + + return rc; +free_power_down_settings: + kfree(power_info->power_down_setting); +free_power_settings: + kfree(power_info->power_setting); + return rc; +} + +int cam_get_dt_power_setting_data(struct device_node *of_node, + struct cam_hw_soc_info *soc_info, + struct cam_sensor_power_ctrl_t *power_info) +{ + int rc = 0, i; + int count = 0; + const char *seq_name = NULL; + uint32_t *array = NULL; + struct cam_sensor_power_setting *ps; + int c, end; + + if (!power_info) + return -EINVAL; + + count = of_property_count_strings(of_node, "qcom,cam-power-seq-type"); + power_info->power_setting_size = count; + + CAM_DBG(CAM_SENSOR, "qcom,cam-power-seq-type count %d", count); + + if (count <= 0) + return 0; + + ps = kcalloc(count, sizeof(*ps), GFP_KERNEL); + if (!ps) + return -ENOMEM; + power_info->power_setting = ps; + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,cam-power-seq-type", i, &seq_name); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed"); + goto ERROR1; + } + CAM_DBG(CAM_SENSOR, "seq_name[%d] = %s", i, seq_name); + if (!strcmp(seq_name, "cam_vio")) { + ps[i].seq_type = SENSOR_VIO; + } else if (!strcmp(seq_name, "cam_vana")) { + ps[i].seq_type = SENSOR_VANA; + } else if (!strcmp(seq_name, "cam_clk")) { + ps[i].seq_type = SENSOR_MCLK; + } else { + CAM_ERR(CAM_SENSOR, "unrecognized seq-type %s", + seq_name); + rc = -EILSEQ; + goto ERROR1; + } + CAM_DBG(CAM_SENSOR, "seq_type[%d] %d", i, ps[i].seq_type); + } + + array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL); + if (!array) { + rc = -ENOMEM; + goto ERROR1; + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-cfg-val", + array, count); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed "); + goto ERROR2; + } + + for (i = 0; i < count; i++) { + ps[i].config_val = array[i]; + CAM_DBG(CAM_SENSOR, "power_setting[%d].config_val = %ld", i, + ps[i].config_val); + } + + rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-delay", + array, count); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "failed"); + goto ERROR2; + } + for (i = 0; i < count; i++) { + ps[i].delay = array[i]; + CAM_DBG(CAM_SENSOR, "power_setting[%d].delay = %d", i, + ps[i].delay); + } + kfree(array); + + power_info->power_down_setting = + kzalloc(sizeof(*ps) * count, GFP_KERNEL); + + if (!power_info->power_down_setting) { + CAM_ERR(CAM_SENSOR, "failed"); + rc = -ENOMEM; + goto ERROR1; + } + + power_info->power_down_setting_size = count; + + end = count - 1; + + for (c = 0; c < count; c++) { + power_info->power_down_setting[c] = ps[end]; + end--; + } + return rc; +ERROR2: + kfree(array); +ERROR1: + kfree(ps); + return rc; +} + +int cam_sensor_util_init_gpio_pin_tbl( + struct cam_hw_soc_info *soc_info, + struct msm_camera_gpio_num_info **pgpio_num_info) +{ + int rc = 0, val = 0; + uint32_t gpio_array_size; + struct device_node *of_node = NULL; + struct cam_soc_gpio_data *gconf = NULL; + struct msm_camera_gpio_num_info *gpio_num_info = NULL; + + if (!soc_info->dev) { + CAM_ERR(CAM_SENSOR, "device node NULL"); + return -EINVAL; + } + + of_node = soc_info->dev->of_node; + + gconf = soc_info->gpio_data; + if (!gconf) { + CAM_ERR(CAM_SENSOR, "No gpio_common_table is found"); + return -EINVAL; + } + + if (!gconf->cam_gpio_common_tbl) { + CAM_ERR(CAM_SENSOR, "gpio_common_table is not initialized"); + return -EINVAL; + } + + gpio_array_size = gconf->cam_gpio_common_tbl_size; + + if (!gpio_array_size) { + CAM_ERR(CAM_SENSOR, "invalid size of gpio table"); + return -EINVAL; + } + + *pgpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info), + GFP_KERNEL); + if (!*pgpio_num_info) + return -ENOMEM; + gpio_num_info = *pgpio_num_info; + + rc = of_property_read_u32(of_node, "gpio-vana", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "read gpio-vana failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-vana invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_VANA] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_VANA] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-vana %d", + gpio_num_info->gpio_num[SENSOR_VANA]); + } + + rc = of_property_read_u32(of_node, "gpio-vio", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "read gpio-vio failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-vio invalid %d", val); + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_VIO] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_VIO] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-vio %d", + gpio_num_info->gpio_num[SENSOR_VIO]); + } + + rc = of_property_read_u32(of_node, "gpio-vaf", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "read gpio-vaf failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-vaf invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_VAF] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_VAF] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-vaf %d", + gpio_num_info->gpio_num[SENSOR_VAF]); + } + + rc = of_property_read_u32(of_node, "gpio-vdig", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "read gpio-vdig failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-vdig invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_VDIG] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_VDIG] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-vdig %d", + gpio_num_info->gpio_num[SENSOR_VDIG]); + } + + rc = of_property_read_u32(of_node, "gpio-reset", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "read gpio-reset failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-reset invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_RESET] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_RESET] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-reset %d", + gpio_num_info->gpio_num[SENSOR_RESET]); + } + + rc = of_property_read_u32(of_node, "gpio-standby", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "read gpio-standby failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-standby invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_STANDBY] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_STANDBY] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-standby %d", + gpio_num_info->gpio_num[SENSOR_STANDBY]); + } + + rc = of_property_read_u32(of_node, "gpio-af-pwdm", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "read gpio-af-pwdm failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-af-pwdm invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_VAF_PWDM] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_VAF_PWDM] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-af-pwdm %d", + gpio_num_info->gpio_num[SENSOR_VAF_PWDM]); + } + + rc = of_property_read_u32(of_node, "gpio-custom1", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "read gpio-custom1 failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-custom1 invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO1] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_CUSTOM_GPIO1] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-custom1 %d", + gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO1]); + } + + rc = of_property_read_u32(of_node, "gpio-custom2", &val); + if (rc != -EINVAL) { + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "read gpio-custom2 failed rc %d", rc); + goto free_gpio_info; + } else if (val >= gpio_array_size) { + CAM_ERR(CAM_SENSOR, "gpio-custom2 invalid %d", val); + rc = -EINVAL; + goto free_gpio_info; + } + gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO2] = + gconf->cam_gpio_common_tbl[val].gpio; + gpio_num_info->valid[SENSOR_CUSTOM_GPIO2] = 1; + + CAM_DBG(CAM_SENSOR, "gpio-custom2 %d", + gpio_num_info->gpio_num[SENSOR_CUSTOM_GPIO2]); + } else { + rc = 0; + } + + return rc; + +free_gpio_info: + kfree(gpio_num_info); + gpio_num_info = NULL; + return rc; +} + +int msm_camera_pinctrl_init( + struct msm_pinctrl_info *sensor_pctrl, struct device *dev) { + + sensor_pctrl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) { + CAM_DBG(CAM_SENSOR, "Getting pinctrl handle failed"); + return -EINVAL; + } + sensor_pctrl->gpio_state_active = + pinctrl_lookup_state(sensor_pctrl->pinctrl, + CAM_SENSOR_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) { + CAM_ERR(CAM_SENSOR, + "Failed to get the active state pinctrl handle"); + return -EINVAL; + } + sensor_pctrl->gpio_state_suspend + = pinctrl_lookup_state(sensor_pctrl->pinctrl, + CAM_SENSOR_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) { + CAM_ERR(CAM_SENSOR, + "Failed to get the suspend state pinctrl handle"); + return -EINVAL; + } + + return 0; +} + +int msm_cam_sensor_handle_reg_gpio(int seq_type, + struct msm_camera_gpio_num_info *gpio_num_info, int val) +{ + int gpio_offset = -1; + + if (!gpio_num_info) { + CAM_INFO(CAM_SENSOR, "Input Parameters are not proper"); + return 0; + } + + CAM_DBG(CAM_SENSOR, "Seq type: %d, config: %d", seq_type, val); + + gpio_offset = seq_type; + + if (gpio_num_info->valid[gpio_offset] == 1) { + CAM_DBG(CAM_SENSOR, "VALID GPIO offset: %d, seqtype: %d", + gpio_offset, seq_type); + cam_res_mgr_gpio_set_value( + gpio_num_info->gpio_num + [gpio_offset], val); + } + + return 0; +} + +int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl, + struct cam_hw_soc_info *soc_info) +{ + int rc = 0, index = 0, no_gpio = 0, ret = 0, num_vreg, j = 0; + int32_t vreg_idx = -1; + struct cam_sensor_power_setting *power_setting = NULL; + struct msm_camera_gpio_num_info *gpio_num_info = NULL; + + CAM_DBG(CAM_SENSOR, "Enter"); + if (!ctrl) { + CAM_ERR(CAM_SENSOR, "Invalid ctrl handle"); + return -EINVAL; + } + + gpio_num_info = ctrl->gpio_num_info; + num_vreg = soc_info->num_rgltr; + + if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) { + CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg); + return -EINVAL; + } + + if (soc_info->use_shared_clk) + cam_res_mgr_shared_clk_config(true); + + ret = msm_camera_pinctrl_init(&(ctrl->pinctrl_info), ctrl->dev); + if (ret < 0) { + /* Some sensor subdev no pinctrl. */ + CAM_DBG(CAM_SENSOR, "Initialization of pinctrl failed"); + ctrl->cam_pinctrl_status = 0; + } else { + ctrl->cam_pinctrl_status = 1; + } + + if (cam_res_mgr_shared_pinctrl_init()) { + CAM_ERR(CAM_SENSOR, + "Failed to init shared pinctrl"); + return -EINVAL; + } + + rc = cam_sensor_util_request_gpio_table(soc_info, 1); + if (rc < 0) + no_gpio = rc; + + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state( + ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_active); + if (ret) + CAM_ERR(CAM_SENSOR, "cannot set pin to active state"); + } + + ret = cam_res_mgr_shared_pinctrl_select_state(true); + if (ret) + CAM_ERR(CAM_SENSOR, + "Cannot set shared pin to active state"); + + CAM_DBG(CAM_SENSOR, "power setting size: %d", ctrl->power_setting_size); + + for (index = 0; index < ctrl->power_setting_size; index++) { + CAM_DBG(CAM_SENSOR, "index: %d", index); + power_setting = &ctrl->power_setting[index]; + CAM_DBG(CAM_SENSOR, "seq_type %d", power_setting->seq_type); + + switch (power_setting->seq_type) { + case SENSOR_MCLK: + if (power_setting->seq_val >= soc_info->num_clk) { + CAM_ERR(CAM_SENSOR, "clk index %d >= max %u", + power_setting->seq_val, + soc_info->num_clk); + goto power_up_failed; + } + for (j = 0; j < num_vreg; j++) { + if (!strcmp(soc_info->rgltr_name[j], + "cam_clk")) { + CAM_DBG(CAM_SENSOR, + "Enable cam_clk: %d", j); + + soc_info->rgltr[j] = + regulator_get( + soc_info->dev, + soc_info->rgltr_name[j]); + + if (IS_ERR_OR_NULL( + soc_info->rgltr[j])) { + rc = PTR_ERR( + soc_info->rgltr[j]); + rc = rc ? rc : -EINVAL; + CAM_ERR(CAM_SENSOR, + "vreg %s %d", + soc_info->rgltr_name[j], + rc); + soc_info->rgltr[j] = NULL; + } + + rc = cam_soc_util_regulator_enable( + soc_info->rgltr[j], + soc_info->rgltr_name[j], + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + soc_info->rgltr_op_mode[j], + soc_info->rgltr_delay[j]); + + power_setting->data[0] = + soc_info->rgltr[j]; + } + } + if (power_setting->config_val) + soc_info->clk_rate[0][power_setting->seq_val] = + power_setting->config_val; + + for (j = 0; j < soc_info->num_clk; j++) { + rc = cam_soc_util_clk_enable(soc_info->clk[j], + soc_info->clk_name[j], + soc_info->clk_rate[0][j]); + if (rc) + break; + } + + if (rc < 0) { + CAM_ERR(CAM_SENSOR, "clk enable failed"); + goto power_up_failed; + } + break; + case SENSOR_RESET: + case SENSOR_STANDBY: + case SENSOR_CUSTOM_GPIO1: + case SENSOR_CUSTOM_GPIO2: + if (no_gpio) { + CAM_ERR(CAM_SENSOR, "request gpio failed"); + return no_gpio; + } + if (!gpio_num_info) { + CAM_ERR(CAM_SENSOR, "Invalid gpio_num_info"); + goto power_up_failed; + } + CAM_DBG(CAM_SENSOR, "gpio set val %d", + gpio_num_info->gpio_num + [power_setting->seq_type]); + + rc = msm_cam_sensor_handle_reg_gpio( + power_setting->seq_type, + gpio_num_info, + (int) power_setting->config_val); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Error in handling VREG GPIO"); + goto power_up_failed; + } + break; + case SENSOR_VANA: + case SENSOR_VDIG: + case SENSOR_VIO: + case SENSOR_VAF: + case SENSOR_VAF_PWDM: + case SENSOR_CUSTOM_REG1: + case SENSOR_CUSTOM_REG2: + if (power_setting->seq_val == INVALID_VREG) + break; + + if (power_setting->seq_val >= CAM_VREG_MAX) { + CAM_ERR(CAM_SENSOR, "vreg index %d >= max %d", + power_setting->seq_val, + CAM_VREG_MAX); + goto power_up_failed; + } + if (power_setting->seq_val < num_vreg) { + CAM_DBG(CAM_SENSOR, "Enable Regulator"); + vreg_idx = power_setting->seq_val; + + soc_info->rgltr[vreg_idx] = + regulator_get(soc_info->dev, + soc_info->rgltr_name[vreg_idx]); + if (IS_ERR_OR_NULL( + soc_info->rgltr[vreg_idx])) { + rc = PTR_ERR(soc_info->rgltr[vreg_idx]); + rc = rc ? rc : -EINVAL; + + CAM_ERR(CAM_SENSOR, "%s get failed %d", + soc_info->rgltr_name[vreg_idx], + rc); + + soc_info->rgltr[vreg_idx] = NULL; + } + + rc = cam_soc_util_regulator_enable( + soc_info->rgltr[vreg_idx], + soc_info->rgltr_name[vreg_idx], + soc_info->rgltr_min_volt[vreg_idx], + soc_info->rgltr_max_volt[vreg_idx], + soc_info->rgltr_op_mode[vreg_idx], + soc_info->rgltr_delay[vreg_idx]); + + power_setting->data[0] = + soc_info->rgltr[vreg_idx]; + } + else + CAM_ERR(CAM_SENSOR, "usr_idx:%d dts_idx:%d", + power_setting->seq_val, num_vreg); + + rc = msm_cam_sensor_handle_reg_gpio( + power_setting->seq_type, + gpio_num_info, 1); + if (rc < 0) { + CAM_ERR(CAM_SENSOR, + "Error in handling VREG GPIO"); + goto power_up_failed; + } + break; + default: + CAM_ERR(CAM_SENSOR, "error power seq type %d", + power_setting->seq_type); + break; + } + if (power_setting->delay > 20) + msleep(power_setting->delay); + else if (power_setting->delay) + usleep_range(power_setting->delay * 1000, + (power_setting->delay * 1000) + 1000); + } + + ret = cam_res_mgr_shared_pinctrl_post_init(); + if (ret) + CAM_ERR(CAM_SENSOR, + "Failed to post init shared pinctrl"); + + return 0; +power_up_failed: + CAM_ERR(CAM_SENSOR, "failed"); + for (index--; index >= 0; index--) { + CAM_DBG(CAM_SENSOR, "index %d", index); + power_setting = &ctrl->power_setting[index]; + CAM_DBG(CAM_SENSOR, "type %d", + power_setting->seq_type); + switch (power_setting->seq_type) { + case SENSOR_RESET: + case SENSOR_STANDBY: + case SENSOR_CUSTOM_GPIO1: + case SENSOR_CUSTOM_GPIO2: + if (!gpio_num_info) + continue; + if (!gpio_num_info->valid + [power_setting->seq_type]) + continue; + cam_res_mgr_gpio_set_value( + gpio_num_info->gpio_num + [power_setting->seq_type], GPIOF_OUT_INIT_LOW); + break; + case SENSOR_VANA: + case SENSOR_VDIG: + case SENSOR_VIO: + case SENSOR_VAF: + case SENSOR_VAF_PWDM: + case SENSOR_CUSTOM_REG1: + case SENSOR_CUSTOM_REG2: + if (power_setting->seq_val < num_vreg) { + CAM_DBG(CAM_SENSOR, "Disable Regulator"); + vreg_idx = power_setting->seq_val; + + rc = cam_soc_util_regulator_disable( + soc_info->rgltr[vreg_idx], + soc_info->rgltr_name[vreg_idx], + soc_info->rgltr_min_volt[vreg_idx], + soc_info->rgltr_max_volt[vreg_idx], + soc_info->rgltr_op_mode[vreg_idx], + soc_info->rgltr_delay[vreg_idx]); + + power_setting->data[0] = + soc_info->rgltr[vreg_idx]; + + regulator_put( + soc_info->rgltr[vreg_idx]); + soc_info->rgltr[vreg_idx] = NULL; + } + else + CAM_ERR(CAM_SENSOR, "seq_val:%d > num_vreg: %d", + power_setting->seq_val, num_vreg); + + msm_cam_sensor_handle_reg_gpio(power_setting->seq_type, + gpio_num_info, GPIOF_OUT_INIT_LOW); + + break; + default: + CAM_ERR(CAM_SENSOR, "error power seq type %d", + power_setting->seq_type); + break; + } + if (power_setting->delay > 20) { + msleep(power_setting->delay); + } else if (power_setting->delay) { + usleep_range(power_setting->delay * 1000, + (power_setting->delay * 1000) + 1000); + } + } + + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state( + ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_suspend); + if (ret) + CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state"); + cam_res_mgr_shared_pinctrl_select_state(false); + pinctrl_put(ctrl->pinctrl_info.pinctrl); + cam_res_mgr_shared_pinctrl_put(); + } + + if (soc_info->use_shared_clk) + cam_res_mgr_shared_clk_config(false); + + ctrl->cam_pinctrl_status = 0; + + cam_sensor_util_request_gpio_table(soc_info, 0); + + return rc; +} + +static struct cam_sensor_power_setting* +msm_camera_get_power_settings(struct cam_sensor_power_ctrl_t *ctrl, + enum msm_camera_power_seq_type seq_type, + uint16_t seq_val) +{ + struct cam_sensor_power_setting *power_setting, *ps = NULL; + int idx; + + for (idx = 0; idx < ctrl->power_setting_size; idx++) { + power_setting = &ctrl->power_setting[idx]; + if (power_setting->seq_type == seq_type && + power_setting->seq_val == seq_val) { + ps = power_setting; + return ps; + } + + } + + return ps; +} + +static int cam_config_mclk_reg(struct cam_sensor_power_ctrl_t *ctrl, + struct cam_hw_soc_info *soc_info, int32_t index) +{ + int32_t num_vreg = 0, j = 0, rc = 0, idx = 0; + struct cam_sensor_power_setting *ps = NULL; + struct cam_sensor_power_setting *pd = NULL; + + num_vreg = soc_info->num_rgltr; + + pd = &ctrl->power_down_setting[index]; + + for (j = 0; j < num_vreg; j++) { + if (!strcmp(soc_info->rgltr_name[j], "cam_clk")) { + + ps = NULL; + for (idx = 0; idx < + ctrl->power_setting_size; idx++) { + if (ctrl->power_setting[idx]. + seq_type == pd->seq_type) { + ps = &ctrl->power_setting[idx]; + break; + } + } + + if (ps != NULL) { + CAM_DBG(CAM_SENSOR, "Disable Regulator"); + + rc = cam_soc_util_regulator_disable( + soc_info->rgltr[j], + soc_info->rgltr_name[j], + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + soc_info->rgltr_op_mode[j], + soc_info->rgltr_delay[j]); + + ps->data[0] = + soc_info->rgltr[j]; + + regulator_put( + soc_info->rgltr[j]); + soc_info->rgltr[j] = NULL; + } + } + } + + return rc; +} + +int msm_camera_power_down(struct cam_sensor_power_ctrl_t *ctrl, + struct cam_hw_soc_info *soc_info) +{ + int index = 0, ret = 0, num_vreg = 0, i; + struct cam_sensor_power_setting *pd = NULL; + struct cam_sensor_power_setting *ps; + struct msm_camera_gpio_num_info *gpio_num_info = NULL; + + CAM_DBG(CAM_SENSOR, "Enter"); + if (!ctrl || !soc_info) { + CAM_ERR(CAM_SENSOR, "failed ctrl %pK", ctrl); + return -EINVAL; + } + + gpio_num_info = ctrl->gpio_num_info; + num_vreg = soc_info->num_rgltr; + + if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) { + CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg); + return -EINVAL; + } + + for (index = 0; index < ctrl->power_down_setting_size; index++) { + CAM_DBG(CAM_SENSOR, "index %d", index); + pd = &ctrl->power_down_setting[index]; + ps = NULL; + CAM_DBG(CAM_SENSOR, "type %d", pd->seq_type); + switch (pd->seq_type) { + case SENSOR_MCLK: + ret = cam_config_mclk_reg(ctrl, soc_info, index); + if (ret < 0) { + CAM_ERR(CAM_SENSOR, + "config clk reg failed rc: %d", ret); + return ret; + } + //cam_soc_util_clk_disable_default(soc_info); + for (i = soc_info->num_clk - 1; i >= 0; i--) { + cam_soc_util_clk_disable(soc_info->clk[i], + soc_info->clk_name[i]); + } + + break; + case SENSOR_RESET: + case SENSOR_STANDBY: + case SENSOR_CUSTOM_GPIO1: + case SENSOR_CUSTOM_GPIO2: + + if (!gpio_num_info->valid[pd->seq_type]) + continue; + + cam_res_mgr_gpio_set_value( + gpio_num_info->gpio_num + [pd->seq_type], + (int) pd->config_val); + + break; + case SENSOR_VANA: + case SENSOR_VDIG: + case SENSOR_VIO: + case SENSOR_VAF: + case SENSOR_VAF_PWDM: + case SENSOR_CUSTOM_REG1: + case SENSOR_CUSTOM_REG2: + if (pd->seq_val == INVALID_VREG) + break; + + ps = msm_camera_get_power_settings( + ctrl, pd->seq_type, + pd->seq_val); + if (ps) { + if (pd->seq_val < num_vreg) { + CAM_DBG(CAM_SENSOR, + "Disable Regulator"); + ret = cam_soc_util_regulator_disable( + soc_info->rgltr[ps->seq_val], + soc_info->rgltr_name[ps->seq_val], + soc_info->rgltr_min_volt[ps->seq_val], + soc_info->rgltr_max_volt[ps->seq_val], + soc_info->rgltr_op_mode[ps->seq_val], + soc_info->rgltr_delay[ps->seq_val]); + + ps->data[0] = + soc_info->rgltr[ps->seq_val]; + + regulator_put( + soc_info->rgltr[ps->seq_val]); + soc_info->rgltr[ps->seq_val] = NULL; + } + else + CAM_ERR(CAM_SENSOR, + "seq_val:%d > num_vreg: %d", + pd->seq_val, + num_vreg); + } else + CAM_ERR(CAM_SENSOR, + "error in power up/down seq"); + + ret = msm_cam_sensor_handle_reg_gpio(pd->seq_type, + gpio_num_info, GPIOF_OUT_INIT_LOW); + + if (ret < 0) + CAM_ERR(CAM_SENSOR, + "Error disabling VREG GPIO"); + break; + default: + CAM_ERR(CAM_SENSOR, "error power seq type %d", + pd->seq_type); + break; + } + if (pd->delay > 20) + msleep(pd->delay); + else if (pd->delay) + usleep_range(pd->delay * 1000, + (pd->delay * 1000) + 1000); + } + + if (ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state( + ctrl->pinctrl_info.pinctrl, + ctrl->pinctrl_info.gpio_state_suspend); + if (ret) + CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state"); + + cam_res_mgr_shared_pinctrl_select_state(false); + pinctrl_put(ctrl->pinctrl_info.pinctrl); + cam_res_mgr_shared_pinctrl_put(); + } + + if (soc_info->use_shared_clk) + cam_res_mgr_shared_clk_config(false); + + ctrl->cam_pinctrl_status = 0; + + cam_sensor_util_request_gpio_table(soc_info, 0); + + return 0; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h new file mode 100644 index 000000000000..d2079b0a7bd5 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h @@ -0,0 +1,59 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_SENSOR_UTIL_H_ +#define _CAM_SENSOR_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +#define INVALID_VREG 100 + +int cam_get_dt_power_setting_data(struct device_node *of_node, + struct cam_hw_soc_info *soc_info, + struct cam_sensor_power_ctrl_t *power_info); + +int msm_camera_pinctrl_init + (struct msm_pinctrl_info *sensor_pctrl, struct device *dev); + +int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings, + struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers); + +int32_t delete_request(struct i2c_settings_array *i2c_array); +int cam_sensor_util_request_gpio_table( + struct cam_hw_soc_info *soc_info, int gpio_en); + +int cam_sensor_util_init_gpio_pin_tbl( + struct cam_hw_soc_info *soc_info, + struct msm_camera_gpio_num_info **pgpio_num_info); +int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl, + struct cam_hw_soc_info *soc_info); + +int msm_camera_power_down(struct cam_sensor_power_ctrl_t *ctrl, + struct cam_hw_soc_info *soc_info); + +int msm_camera_fill_vreg_params(struct cam_hw_soc_info *soc_info, + struct cam_sensor_power_setting *power_setting, + uint16_t power_setting_size); + +int32_t cam_sensor_update_power_settings(void *cmd_buf, + int cmd_length, struct cam_sensor_power_ctrl_t *power_info); +#endif /* _CAM_SENSOR_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_smmu/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_smmu/Makefile new file mode 100644 index 000000000000..45b06c420f3b --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_smmu/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -I$(srctree)/techpack/camera/include/uapi +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_smmu_api.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.c new file mode 100644 index 000000000000..e2883c8f79ea --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.c @@ -0,0 +1,3827 @@ +/* Copyright (c) 2014-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cam_smmu_api.h" +#include "cam_debug_util.h" + +#define SHARED_MEM_POOL_GRANULARITY 12 + +#define IOMMU_INVALID_DIR -1 +#define BYTE_SIZE 8 +#define COOKIE_NUM_BYTE 2 +#define COOKIE_SIZE (BYTE_SIZE*COOKIE_NUM_BYTE) +#define COOKIE_MASK ((1<> COOKIE_SIZE) & COOKIE_MASK) + +static int g_num_pf_handled = 4; +module_param(g_num_pf_handled, int, 0644); + +struct firmware_alloc_info { + struct device *fw_dev; + void *fw_kva; + dma_addr_t fw_dma_hdl; +}; + +struct firmware_alloc_info icp_fw; + +struct cam_smmu_work_payload { + int idx; + struct iommu_domain *domain; + struct device *dev; + unsigned long iova; + int flags; + void *token; + struct list_head list; +}; + +enum cam_protection_type { + CAM_PROT_INVALID, + CAM_NON_SECURE, + CAM_SECURE, + CAM_PROT_MAX, +}; + +enum cam_iommu_type { + CAM_SMMU_INVALID, + CAM_QSMMU, + CAM_ARM_SMMU, + CAM_SMMU_MAX, +}; + +enum cam_smmu_buf_state { + CAM_SMMU_BUFF_EXIST, + CAM_SMMU_BUFF_NOT_EXIST, +}; + +enum cam_smmu_init_dir { + CAM_SMMU_TABLE_INIT, + CAM_SMMU_TABLE_DEINIT, +}; + +struct scratch_mapping { + void *bitmap; + size_t bits; + unsigned int order; + dma_addr_t base; +}; + +struct secheap_buf_info { + struct dma_buf *buf; + struct dma_buf_attachment *attach; + struct sg_table *table; +}; + +struct cam_context_bank_info { + struct device *dev; + struct iommu_domain *domain; + dma_addr_t va_start; + size_t va_len; + const char *name; + bool is_secure; + uint8_t scratch_buf_support; + uint8_t firmware_support; + uint8_t shared_support; + uint8_t io_support; + uint8_t secheap_support; + uint8_t qdss_support; + dma_addr_t qdss_phy_addr; + bool is_fw_allocated; + bool is_secheap_allocated; + bool is_qdss_allocated; + + struct scratch_mapping scratch_map; + struct gen_pool *shared_mem_pool; + + struct cam_smmu_region_info scratch_info; + struct cam_smmu_region_info firmware_info; + struct cam_smmu_region_info shared_info; + struct cam_smmu_region_info io_info; + struct cam_smmu_region_info secheap_info; + struct cam_smmu_region_info qdss_info; + struct secheap_buf_info secheap_buf; + + struct list_head smmu_buf_list; + struct list_head smmu_buf_kernel_list; + struct mutex lock; + int handle; + enum cam_smmu_ops_param state; + + cam_smmu_client_page_fault_handler handler[CAM_SMMU_CB_MAX]; + void *token[CAM_SMMU_CB_MAX]; + int cb_count; + int secure_count; + int pf_count; + + size_t io_mapping_size; + size_t shared_mapping_size; + + /* discard iova - non-zero values are valid */ + dma_addr_t discard_iova_start; + size_t discard_iova_len; +}; + +struct cam_iommu_cb_set { + struct cam_context_bank_info *cb_info; + u32 cb_num; + u32 cb_init_count; + struct work_struct smmu_work; + struct mutex payload_list_lock; + struct list_head payload_list; + u32 non_fatal_fault; + struct dentry *dentry; + bool cb_dump_enable; +}; + +static const struct of_device_id msm_cam_smmu_dt_match[] = { + { .compatible = "qcom,msm-cam-smmu", }, + { .compatible = "qcom,msm-cam-smmu-cb", }, + { .compatible = "qcom,msm-cam-smmu-fw-dev", }, + {} +}; + +struct cam_dma_buff_info { + struct dma_buf *buf; + struct dma_buf_attachment *attach; + struct sg_table *table; + enum dma_data_direction dir; + enum cam_smmu_region_id region_id; + int iommu_dir; + int ref_count; + dma_addr_t paddr; + struct list_head list; + int ion_fd; + size_t len; + size_t phys_len; +}; + +struct cam_sec_buff_info { + struct dma_buf *buf; + enum dma_data_direction dir; + int ref_count; + dma_addr_t paddr; + struct list_head list; + int ion_fd; + size_t len; +}; + +static const char *qdss_region_name = "qdss"; + +static struct cam_iommu_cb_set iommu_cb_set; + +static enum dma_data_direction cam_smmu_translate_dir( + enum cam_smmu_map_dir dir); + +static int cam_smmu_check_handle_unique(int hdl); + +static int cam_smmu_create_iommu_handle(int idx); + +static int cam_smmu_create_add_handle_in_table(char *name, + int *hdl); + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx, + int ion_fd); + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_dma_buf(int idx, + struct dma_buf *buf); + +static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx, + int ion_fd); + +static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map, + dma_addr_t base, size_t size, + int order); + +static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping, + size_t size, + dma_addr_t *iova); + +static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping, + dma_addr_t addr, size_t size); + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx, + dma_addr_t virt_addr); + +static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd, + bool dis_delayed_unmap, enum dma_data_direction dma_dir, + dma_addr_t *paddr_ptr, size_t *len_ptr, + enum cam_smmu_region_id region_id); + +static int cam_smmu_map_kernel_buffer_and_add_to_list(int idx, + struct dma_buf *buf, enum dma_data_direction dma_dir, + dma_addr_t *paddr_ptr, size_t *len_ptr, + enum cam_smmu_region_id region_id); + +static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx, + size_t virt_len, + size_t phys_len, + unsigned int iommu_dir, + dma_addr_t *virt_addr); + +static int cam_smmu_unmap_buf_and_remove_from_list( + struct cam_dma_buff_info *mapping_info, int idx); + +static int cam_smmu_free_scratch_buffer_remove_from_list( + struct cam_dma_buff_info *mapping_info, + int idx); + +static void cam_smmu_clean_user_buffer_list(int idx); + +static void cam_smmu_clean_kernel_buffer_list(int idx); + +static void cam_smmu_dump_cb_info(int idx); + +static void cam_smmu_print_user_list(int idx); + +static void cam_smmu_print_kernel_list(int idx); + +static void cam_smmu_print_table(void); + +static int cam_smmu_probe(struct platform_device *pdev); + +static uint32_t cam_smmu_find_closest_mapping(int idx, void *vaddr); + +static void cam_smmu_page_fault_work(struct work_struct *work) +{ + int j; + int idx; + struct cam_smmu_work_payload *payload; + uint32_t buf_info; + + mutex_lock(&iommu_cb_set.payload_list_lock); + if (list_empty(&iommu_cb_set.payload_list)) { + CAM_ERR(CAM_SMMU, "Payload list empty"); + mutex_unlock(&iommu_cb_set.payload_list_lock); + return; + } + + payload = list_first_entry(&iommu_cb_set.payload_list, + struct cam_smmu_work_payload, + list); + list_del(&payload->list); + mutex_unlock(&iommu_cb_set.payload_list_lock); + + /* Dereference the payload to call the handler */ + idx = payload->idx; + buf_info = cam_smmu_find_closest_mapping(idx, (void *)payload->iova); + if (buf_info != 0) + CAM_INFO(CAM_SMMU, "closest buf 0x%x idx %d", buf_info, idx); + + for (j = 0; j < CAM_SMMU_CB_MAX; j++) { + if ((iommu_cb_set.cb_info[idx].handler[j])) { + iommu_cb_set.cb_info[idx].handler[j]( + payload->domain, + payload->dev, + payload->iova, + payload->flags, + iommu_cb_set.cb_info[idx].token[j], + buf_info); + } + } + cam_smmu_dump_cb_info(idx); + kfree(payload); +} + +static void cam_smmu_dump_cb_info(int idx) +{ + struct cam_dma_buff_info *mapping, *mapping_temp; + size_t shared_reg_len = 0, io_reg_len = 0; + size_t shared_free_len = 0, io_free_len = 0; + uint32_t i = 0; + struct cam_context_bank_info *cb_info = + &iommu_cb_set.cb_info[idx]; + + if (cb_info->shared_support) { + shared_reg_len = cb_info->shared_info.iova_len; + shared_free_len = shared_reg_len - cb_info->shared_mapping_size; + } + + if (cb_info->io_support) { + io_reg_len = cb_info->io_info.iova_len; + io_free_len = io_reg_len - cb_info->io_mapping_size; + } + + CAM_ERR(CAM_SMMU, + "********** Context bank dump for %s **********", + cb_info->name); + CAM_ERR(CAM_SMMU, + "Usage: shared_usage=%u io_usage=%u shared_free=%u io_free=%u", + (unsigned int)cb_info->shared_mapping_size, + (unsigned int)cb_info->io_mapping_size, + (unsigned int)shared_free_len, + (unsigned int)io_free_len); + + if (iommu_cb_set.cb_dump_enable) { + list_for_each_entry_safe(mapping, mapping_temp, + &iommu_cb_set.cb_info[idx].smmu_buf_list, list) { + i++; + CAM_ERR(CAM_SMMU, + "%u. ion_fd=%d start=0x%x end=0x%x len=%u region=%d", + i, mapping->ion_fd, (void *)mapping->paddr, + ((uint64_t)mapping->paddr + + (uint64_t)mapping->len), + (unsigned int)mapping->len, + mapping->region_id); + } + } +} + +static void cam_smmu_print_user_list(int idx) +{ + struct cam_dma_buff_info *mapping; + + CAM_ERR(CAM_SMMU, "index = %d", idx); + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, list) { + CAM_ERR(CAM_SMMU, + "ion_fd = %d, paddr= 0x%pK, len = %u, region = %d", + mapping->ion_fd, (void *)mapping->paddr, + (unsigned int)mapping->len, + mapping->region_id); + } +} + +static void cam_smmu_print_kernel_list(int idx) +{ + struct cam_dma_buff_info *mapping; + + CAM_ERR(CAM_SMMU, "index = %d", idx); + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) { + CAM_ERR(CAM_SMMU, + "dma_buf = %pK, paddr= 0x%pK, len = %u, region = %d", + mapping->buf, (void *)mapping->paddr, + (unsigned int)mapping->len, + mapping->region_id); + } +} + +static void cam_smmu_print_table(void) +{ + int i; + + for (i = 0; i < iommu_cb_set.cb_num; i++) { + CAM_ERR(CAM_SMMU, "i= %d, handle= %d, name_addr=%pK", i, + (int)iommu_cb_set.cb_info[i].handle, + (void *)iommu_cb_set.cb_info[i].name); + CAM_ERR(CAM_SMMU, "dev = %pK", iommu_cb_set.cb_info[i].dev); + } +} + +static uint32_t cam_smmu_find_closest_mapping(int idx, void *vaddr) +{ + struct cam_dma_buff_info *mapping, *closest_mapping = NULL; + unsigned long start_addr, end_addr, current_addr; + uint32_t buf_handle = 0; + + long delta = 0, lowest_delta = 0; + + current_addr = (unsigned long)vaddr; + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, list) { + start_addr = (unsigned long)mapping->paddr; + end_addr = (unsigned long)mapping->paddr + mapping->len; + + if (start_addr <= current_addr && current_addr <= end_addr) { + closest_mapping = mapping; + CAM_INFO(CAM_SMMU, + "Found va 0x%lx in:0x%lx-0x%lx, fd %d cb:%s", + current_addr, start_addr, + end_addr, mapping->ion_fd, + iommu_cb_set.cb_info[idx].name); + goto end; + } else { + if (start_addr > current_addr) + delta = start_addr - current_addr; + else + delta = current_addr - end_addr - 1; + + if (delta < lowest_delta || lowest_delta == 0) { + lowest_delta = delta; + closest_mapping = mapping; + } + CAM_DBG(CAM_SMMU, + "approx va %lx not in range: %lx-%lx fd = %0x", + current_addr, start_addr, + end_addr, mapping->ion_fd); + } + } + +end: + if (closest_mapping) { + buf_handle = GET_MEM_HANDLE(idx, closest_mapping->ion_fd); + CAM_INFO(CAM_SMMU, + "Closest map fd %d 0x%lx %llu-%llu 0x%lx-0x%lx buf=%pK mem %0x", + closest_mapping->ion_fd, current_addr, + mapping->len, closest_mapping->len, + (unsigned long)closest_mapping->paddr, + (unsigned long)closest_mapping->paddr + mapping->len, + closest_mapping->buf, + buf_handle); + } else + CAM_INFO(CAM_SMMU, + "Cannot find vaddr:%lx in SMMU %s virt address", + current_addr, iommu_cb_set.cb_info[idx].name); + + return buf_handle; +} + +void cam_smmu_set_client_page_fault_handler(int handle, + cam_smmu_client_page_fault_handler handler_cb, void *token) +{ + int idx, i = 0; + + if (!token || (handle == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: token is NULL or invalid handle"); + return; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return; + } + + if (handler_cb) { + if (iommu_cb_set.cb_info[idx].cb_count == CAM_SMMU_CB_MAX) { + CAM_ERR(CAM_SMMU, + "%s Should not regiester more handlers", + iommu_cb_set.cb_info[idx].name); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return; + } + + iommu_cb_set.cb_info[idx].cb_count++; + + for (i = 0; i < iommu_cb_set.cb_info[idx].cb_count; i++) { + if (iommu_cb_set.cb_info[idx].token[i] == NULL) { + iommu_cb_set.cb_info[idx].token[i] = token; + iommu_cb_set.cb_info[idx].handler[i] = + handler_cb; + break; + } + } + } else { + for (i = 0; i < CAM_SMMU_CB_MAX; i++) { + if (iommu_cb_set.cb_info[idx].token[i] == token) { + iommu_cb_set.cb_info[idx].token[i] = NULL; + iommu_cb_set.cb_info[idx].handler[i] = + NULL; + iommu_cb_set.cb_info[idx].cb_count--; + break; + } + } + if (i == CAM_SMMU_CB_MAX) + CAM_ERR(CAM_SMMU, + "Error: hdl %x no matching tokens: %s", + handle, iommu_cb_set.cb_info[idx].name); + } + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +} + +void cam_smmu_unset_client_page_fault_handler(int handle, void *token) +{ + int idx, i = 0; + + if (!token || (handle == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: token is NULL or invalid handle"); + return; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return; + } + + for (i = 0; i < CAM_SMMU_CB_MAX; i++) { + if (iommu_cb_set.cb_info[idx].token[i] == token) { + iommu_cb_set.cb_info[idx].token[i] = NULL; + iommu_cb_set.cb_info[idx].handler[i] = + NULL; + iommu_cb_set.cb_info[idx].cb_count--; + break; + } + } + if (i == CAM_SMMU_CB_MAX) + CAM_ERR(CAM_SMMU, "Error: hdl %x no matching tokens: %s", + handle, iommu_cb_set.cb_info[idx].name); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +} + +static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, + int flags, void *token) +{ + char *cb_name; + int idx; + struct cam_smmu_work_payload *payload; + + if (!token) { + CAM_ERR(CAM_SMMU, "Error: token is NULL"); + CAM_ERR(CAM_SMMU, "Error: domain = %pK, device = %pK", + domain, dev); + CAM_ERR(CAM_SMMU, "iova = %lX, flags = %d", iova, flags); + return -EINVAL; + } + + cb_name = (char *)token; + /* Check whether it is in the table */ + for (idx = 0; idx < iommu_cb_set.cb_num; idx++) { + if (!strcmp(iommu_cb_set.cb_info[idx].name, cb_name)) + break; + } + + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: index is not valid, index = %d, token = %s", + idx, cb_name); + return -EINVAL; + } + + if (++iommu_cb_set.cb_info[idx].pf_count > g_num_pf_handled) { + CAM_INFO_RATE_LIMIT(CAM_SMMU, "PF already handled %d %d %d", + g_num_pf_handled, idx, + iommu_cb_set.cb_info[idx].pf_count); + return -EINVAL; + } + + payload = kzalloc(sizeof(struct cam_smmu_work_payload), GFP_ATOMIC); + if (!payload) + return -EINVAL; + + payload->domain = domain; + payload->dev = dev; + payload->iova = iova; + payload->flags = flags; + payload->token = token; + payload->idx = idx; + + mutex_lock(&iommu_cb_set.payload_list_lock); + list_add_tail(&payload->list, &iommu_cb_set.payload_list); + mutex_unlock(&iommu_cb_set.payload_list_lock); + + cam_smmu_page_fault_work(&iommu_cb_set.smmu_work); + + return -EINVAL; +} + +static int cam_smmu_translate_dir_to_iommu_dir( + enum cam_smmu_map_dir dir) +{ + switch (dir) { + case CAM_SMMU_MAP_READ: + return IOMMU_READ; + case CAM_SMMU_MAP_WRITE: + return IOMMU_WRITE; + case CAM_SMMU_MAP_RW: + return IOMMU_READ|IOMMU_WRITE; + case CAM_SMMU_MAP_INVALID: + default: + CAM_ERR(CAM_SMMU, "Error: Direction is invalid. dir = %d", dir); + break; + }; + return IOMMU_INVALID_DIR; +} + +static enum dma_data_direction cam_smmu_translate_dir( + enum cam_smmu_map_dir dir) +{ + switch (dir) { + case CAM_SMMU_MAP_READ: + return DMA_FROM_DEVICE; + case CAM_SMMU_MAP_WRITE: + return DMA_TO_DEVICE; + case CAM_SMMU_MAP_RW: + return DMA_BIDIRECTIONAL; + case CAM_SMMU_MAP_INVALID: + default: + CAM_ERR(CAM_SMMU, "Error: Direction is invalid. dir = %d", + (int)dir); + break; + } + return DMA_NONE; +} + +void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops) +{ + unsigned int i; + int j = 0; + + for (i = 0; i < iommu_cb_set.cb_num; i++) { + iommu_cb_set.cb_info[i].handle = HANDLE_INIT; + INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_list); + INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_kernel_list); + iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH; + iommu_cb_set.cb_info[i].dev = NULL; + iommu_cb_set.cb_info[i].cb_count = 0; + iommu_cb_set.cb_info[i].pf_count = 0; + for (j = 0; j < CAM_SMMU_CB_MAX; j++) { + iommu_cb_set.cb_info[i].token[j] = NULL; + iommu_cb_set.cb_info[i].handler[j] = NULL; + } + if (ops == CAM_SMMU_TABLE_INIT) + mutex_init(&iommu_cb_set.cb_info[i].lock); + else + mutex_destroy(&iommu_cb_set.cb_info[i].lock); + } +} + +static int cam_smmu_check_handle_unique(int hdl) +{ + int i; + + if (hdl == HANDLE_INIT) { + CAM_DBG(CAM_SMMU, + "iommu handle is init number. Need to try again"); + return 1; + } + + for (i = 0; i < iommu_cb_set.cb_num; i++) { + if (iommu_cb_set.cb_info[i].handle == HANDLE_INIT) + continue; + + if (iommu_cb_set.cb_info[i].handle == hdl) { + CAM_DBG(CAM_SMMU, "iommu handle %d conflicts", + (int)hdl); + return 1; + } + } + return 0; +} + +/** + * use low 2 bytes for handle cookie + */ +static int cam_smmu_create_iommu_handle(int idx) +{ + int rand, hdl = 0; + + get_random_bytes(&rand, COOKIE_NUM_BYTE); + hdl = GET_SMMU_HDL(idx, rand); + CAM_DBG(CAM_SMMU, "create handle value = %x", (int)hdl); + return hdl; +} + +static int cam_smmu_attach_device(int idx) +{ + int rc; + struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx]; + + /* attach the mapping to device */ + rc = iommu_attach_device(cb->domain, cb->dev); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: ARM IOMMU attach failed. ret = %d", + rc); + rc = -ENODEV; + } + + return rc; +} + +static int cam_smmu_create_add_handle_in_table(char *name, + int *hdl) +{ + int i; + int handle; + + /* create handle and add in the iommu hardware table */ + for (i = 0; i < iommu_cb_set.cb_num; i++) { + if (!strcmp(iommu_cb_set.cb_info[i].name, name)) { + mutex_lock(&iommu_cb_set.cb_info[i].lock); + if (iommu_cb_set.cb_info[i].handle != HANDLE_INIT) { + if (iommu_cb_set.cb_info[i].is_secure) + iommu_cb_set.cb_info[i].secure_count++; + + mutex_unlock(&iommu_cb_set.cb_info[i].lock); + if (iommu_cb_set.cb_info[i].is_secure) { + *hdl = iommu_cb_set.cb_info[i].handle; + return 0; + } + + CAM_ERR(CAM_SMMU, + "Error: %s already got handle 0x%x", + name, iommu_cb_set.cb_info[i].handle); + + return -EINVAL; + } + + /* make sure handle is unique */ + do { + handle = cam_smmu_create_iommu_handle(i); + } while (cam_smmu_check_handle_unique(handle)); + + /* put handle in the table */ + iommu_cb_set.cb_info[i].handle = handle; + iommu_cb_set.cb_info[i].cb_count = 0; + if (iommu_cb_set.cb_info[i].is_secure) + iommu_cb_set.cb_info[i].secure_count++; + *hdl = handle; + CAM_DBG(CAM_SMMU, "%s creates handle 0x%x", + name, handle); + mutex_unlock(&iommu_cb_set.cb_info[i].lock); + return 0; + } + } + + CAM_ERR(CAM_SMMU, "Error: Cannot find name %s or all handle exist", + name); + cam_smmu_print_table(); + return -EINVAL; +} + +static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map, + dma_addr_t base, size_t size, + int order) +{ + unsigned int count = size >> (PAGE_SHIFT + order); + unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); + int err = 0; + + if (!count) { + err = -EINVAL; + CAM_ERR(CAM_SMMU, "Page count is zero, size passed = %zu", + size); + goto bail; + } + + scratch_map->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!scratch_map->bitmap) { + err = -ENOMEM; + goto bail; + } + + scratch_map->base = base; + scratch_map->bits = BITS_PER_BYTE * bitmap_size; + scratch_map->order = order; + +bail: + return err; +} + +static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping, + size_t size, + dma_addr_t *iova) +{ + unsigned int order = get_order(size); + unsigned int align = 0; + unsigned int count, start; + + count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) + + (1 << mapping->order) - 1) >> mapping->order; + + /* + * Transparently, add a guard page to the total count of pages + * to be allocated + */ + count++; + + if (order > mapping->order) + align = (1 << (order - mapping->order)) - 1; + + start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits, 0, + count, align); + + if (start > mapping->bits) + return -ENOMEM; + + bitmap_set(mapping->bitmap, start, count); + *iova = mapping->base + (start << (mapping->order + PAGE_SHIFT)); + + return 0; +} + +static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping, + dma_addr_t addr, size_t size) +{ + unsigned int start = (addr - mapping->base) >> + (mapping->order + PAGE_SHIFT); + unsigned int count = ((size >> PAGE_SHIFT) + + (1 << mapping->order) - 1) >> mapping->order; + + if (!addr) { + CAM_ERR(CAM_SMMU, "Error: Invalid address"); + return -EINVAL; + } + + if (start + count > mapping->bits) { + CAM_ERR(CAM_SMMU, "Error: Invalid page bits in scratch map"); + return -EINVAL; + } + + /* + * Transparently, add a guard page to the total count of pages + * to be freed + */ + count++; + bitmap_clear(mapping->bitmap, start, count); + + return 0; +} + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx, + dma_addr_t virt_addr) +{ + struct cam_dma_buff_info *mapping; + + list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list, + list) { + if (mapping->paddr == virt_addr) { + CAM_DBG(CAM_SMMU, "Found virtual address %lx", + (unsigned long)virt_addr); + return mapping; + } + } + + CAM_ERR(CAM_SMMU, "Error: Cannot find virtual address %lx by index %d", + (unsigned long)virt_addr, idx); + return NULL; +} + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx, + int ion_fd) +{ + struct cam_dma_buff_info *mapping; + + if (ion_fd < 0) { + CAM_ERR(CAM_SMMU, "Invalid fd %d", ion_fd); + return NULL; + } + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, + list) { + if (mapping->ion_fd == ion_fd) { + CAM_DBG(CAM_SMMU, "find ion_fd %d", ion_fd); + return mapping; + } + } + + CAM_ERR(CAM_SMMU, "Error: Cannot find entry by index %d", idx); + + return NULL; +} + +static struct cam_dma_buff_info *cam_smmu_find_mapping_by_dma_buf(int idx, + struct dma_buf *buf) +{ + struct cam_dma_buff_info *mapping; + + if (!buf) { + CAM_ERR(CAM_SMMU, "Invalid dma_buf"); + return NULL; + } + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, + list) { + if (mapping->buf == buf) { + CAM_DBG(CAM_SMMU, "find dma_buf %pK", buf); + return mapping; + } + } + + CAM_ERR(CAM_SMMU, "Error: Cannot find entry by index %d", idx); + + return NULL; +} + +static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx, + int ion_fd) +{ + struct cam_sec_buff_info *mapping; + + list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list, + list) { + if (mapping->ion_fd == ion_fd) { + CAM_DBG(CAM_SMMU, "find ion_fd %d", ion_fd); + return mapping; + } + } + CAM_ERR(CAM_SMMU, "Error: Cannot find fd %d by index %d", + ion_fd, idx); + return NULL; +} + +static void cam_smmu_clean_user_buffer_list(int idx) +{ + int ret; + struct cam_dma_buff_info *mapping_info, *temp; + + list_for_each_entry_safe(mapping_info, temp, + &iommu_cb_set.cb_info[idx].smmu_buf_list, list) { + CAM_DBG(CAM_SMMU, "Free mapping address %pK, i = %d, fd = %d", + (void *)mapping_info->paddr, idx, + mapping_info->ion_fd); + + if (mapping_info->ion_fd == 0xDEADBEEF) + /* Clean up scratch buffers */ + ret = cam_smmu_free_scratch_buffer_remove_from_list( + mapping_info, idx); + else + /* Clean up regular mapped buffers */ + ret = cam_smmu_unmap_buf_and_remove_from_list( + mapping_info, + idx); + + if (ret < 0) { + CAM_ERR(CAM_SMMU, "Buffer delete failed: idx = %d", + idx); + CAM_ERR(CAM_SMMU, + "Buffer delete failed: addr = %lx, fd = %d", + (unsigned long)mapping_info->paddr, + mapping_info->ion_fd); + /* + * Ignore this error and continue to delete other + * buffers in the list + */ + continue; + } + } +} + +static void cam_smmu_clean_kernel_buffer_list(int idx) +{ + int ret; + struct cam_dma_buff_info *mapping_info, *temp; + + list_for_each_entry_safe(mapping_info, temp, + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) { + CAM_DBG(CAM_SMMU, + "Free mapping address %pK, i = %d, dma_buf = %pK", + (void *)mapping_info->paddr, idx, + mapping_info->buf); + + /* Clean up regular mapped buffers */ + ret = cam_smmu_unmap_buf_and_remove_from_list( + mapping_info, + idx); + + if (ret < 0) { + CAM_ERR(CAM_SMMU, + "Buffer delete in kernel list failed: idx = %d", + idx); + CAM_ERR(CAM_SMMU, + "Buffer delete failed: addr = %lx, dma_buf = %pK", + (unsigned long)mapping_info->paddr, + mapping_info->buf); + /* + * Ignore this error and continue to delete other + * buffers in the list + */ + continue; + } + } +} + +static int cam_smmu_attach(int idx) +{ + int ret; + + if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_ATTACH) { + ret = -EALREADY; + } else if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) { + ret = cam_smmu_attach_device(idx); + if (ret < 0) { + CAM_ERR(CAM_SMMU, "Error: ATTACH fail"); + return -ENODEV; + } + iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH; + ret = 0; + } else { + CAM_ERR(CAM_SMMU, "Error: Not detach/attach: %d", + iommu_cb_set.cb_info[idx].state); + ret = -EINVAL; + } + + return ret; +} + +static int cam_smmu_detach_device(int idx) +{ + int rc = 0; + struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx]; + + /* detach the mapping to device if not already detached */ + if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) { + rc = -EALREADY; + } else if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_ATTACH) { + iommu_detach_device(cb->domain, cb->dev); + iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH; + } + + return rc; +} + +static int cam_smmu_alloc_iova(size_t size, + int32_t smmu_hdl, uint32_t *iova) +{ + int rc = 0; + int idx; + uint32_t vaddr = 0; + + if (!iova || !size || (smmu_hdl == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: Input args are invalid"); + return -EINVAL; + } + + CAM_DBG(CAM_SMMU, "Allocating iova size = %zu for smmu hdl=%X", + size, smmu_hdl); + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + if (iommu_cb_set.cb_info[idx].handle != smmu_hdl) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, smmu_hdl); + rc = -EINVAL; + goto get_addr_end; + } + + if (!iommu_cb_set.cb_info[idx].shared_support) { + CAM_ERR(CAM_SMMU, + "Error: Shared memory not supported for hdl = %X", + smmu_hdl); + rc = -EINVAL; + goto get_addr_end; + } + + vaddr = gen_pool_alloc(iommu_cb_set.cb_info[idx].shared_mem_pool, size); + if (!vaddr) + return -ENOMEM; + + *iova = vaddr; + +get_addr_end: + return rc; +} + +static int cam_smmu_free_iova(uint32_t addr, size_t size, + int32_t smmu_hdl) +{ + int rc = 0; + int idx; + + if (!size || (smmu_hdl == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: Input args are invalid"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + if (iommu_cb_set.cb_info[idx].handle != smmu_hdl) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, smmu_hdl); + rc = -EINVAL; + goto get_addr_end; + } + + gen_pool_free(iommu_cb_set.cb_info[idx].shared_mem_pool, addr, size); + +get_addr_end: + return rc; +} + +int cam_smmu_alloc_firmware(int32_t smmu_hdl, + dma_addr_t *iova, + uintptr_t *cpuva, + size_t *len) +{ + int rc; + int32_t idx; + size_t firmware_len = 0; + size_t firmware_start = 0; + struct iommu_domain *domain; + + if (!iova || !len || !cpuva || (smmu_hdl == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: Input args are invalid"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + rc = -EINVAL; + goto end; + } + + if (!iommu_cb_set.cb_info[idx].firmware_support) { + CAM_ERR(CAM_SMMU, + "Firmware memory not supported for this SMMU handle"); + rc = -EINVAL; + goto end; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_fw_allocated) { + CAM_ERR(CAM_SMMU, "Trying to allocate twice"); + rc = -ENOMEM; + goto unlock_and_end; + } + + firmware_len = iommu_cb_set.cb_info[idx].firmware_info.iova_len; + firmware_start = iommu_cb_set.cb_info[idx].firmware_info.iova_start; + CAM_DBG(CAM_SMMU, "Firmware area len from DT = %zu", firmware_len); + + icp_fw.fw_kva = dma_alloc_coherent(icp_fw.fw_dev, + firmware_len, + &icp_fw.fw_dma_hdl, + GFP_KERNEL); + if (!icp_fw.fw_kva) { + CAM_ERR(CAM_SMMU, "FW memory alloc failed"); + rc = -ENOMEM; + goto unlock_and_end; + } else { + CAM_DBG(CAM_SMMU, "DMA alloc returned fw = %pK, hdl = %pK", + icp_fw.fw_kva, (void *)icp_fw.fw_dma_hdl); + } + + domain = iommu_cb_set.cb_info[idx].domain; + rc = iommu_map(domain, + firmware_start, + icp_fw.fw_dma_hdl, + firmware_len, + IOMMU_READ|IOMMU_WRITE|IOMMU_PRIV); + + if (rc) { + CAM_ERR(CAM_SMMU, "Failed to map FW into IOMMU"); + rc = -ENOMEM; + goto alloc_fail; + } + iommu_cb_set.cb_info[idx].is_fw_allocated = true; + + *iova = iommu_cb_set.cb_info[idx].firmware_info.iova_start; + *cpuva = (uintptr_t)icp_fw.fw_kva; + *len = firmware_len; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + + return rc; + +alloc_fail: + dma_free_coherent(icp_fw.fw_dev, + firmware_len, + icp_fw.fw_kva, + icp_fw.fw_dma_hdl); +unlock_and_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +end: + return rc; +} +EXPORT_SYMBOL(cam_smmu_alloc_firmware); + +int cam_smmu_dealloc_firmware(int32_t smmu_hdl) +{ + int rc = 0; + int32_t idx; + size_t firmware_len = 0; + size_t firmware_start = 0; + struct iommu_domain *domain; + size_t unmapped = 0; + + if (smmu_hdl == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + rc = -EINVAL; + goto end; + } + + if (!iommu_cb_set.cb_info[idx].firmware_support) { + CAM_ERR(CAM_SMMU, + "Firmware memory not supported for this SMMU handle"); + rc = -EINVAL; + goto end; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (!iommu_cb_set.cb_info[idx].is_fw_allocated) { + CAM_ERR(CAM_SMMU, + "Trying to deallocate firmware that is not allocated"); + rc = -ENOMEM; + goto unlock_and_end; + } + + firmware_len = iommu_cb_set.cb_info[idx].firmware_info.iova_len; + firmware_start = iommu_cb_set.cb_info[idx].firmware_info.iova_start; + domain = iommu_cb_set.cb_info[idx].domain; + unmapped = iommu_unmap(domain, + firmware_start, + firmware_len); + + if (unmapped != firmware_len) { + CAM_ERR(CAM_SMMU, "Only %zu unmapped out of total %zu", + unmapped, + firmware_len); + rc = -EINVAL; + } + + dma_free_coherent(icp_fw.fw_dev, + firmware_len, + icp_fw.fw_kva, + icp_fw.fw_dma_hdl); + + icp_fw.fw_kva = 0; + icp_fw.fw_dma_hdl = 0; + + iommu_cb_set.cb_info[idx].is_fw_allocated = false; + +unlock_and_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +end: + return rc; +} +EXPORT_SYMBOL(cam_smmu_dealloc_firmware); + +int cam_smmu_alloc_qdss(int32_t smmu_hdl, + dma_addr_t *iova, + size_t *len) +{ + int rc; + int32_t idx; + size_t qdss_len = 0; + size_t qdss_start = 0; + dma_addr_t qdss_phy_addr; + struct iommu_domain *domain; + + if (!iova || !len || (smmu_hdl == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: Input args are invalid"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + rc = -EINVAL; + goto end; + } + + if (!iommu_cb_set.cb_info[idx].qdss_support) { + CAM_ERR(CAM_SMMU, + "QDSS memory not supported for this SMMU handle"); + rc = -EINVAL; + goto end; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_qdss_allocated) { + CAM_ERR(CAM_SMMU, "Trying to allocate twice"); + rc = -ENOMEM; + goto unlock_and_end; + } + + qdss_len = iommu_cb_set.cb_info[idx].qdss_info.iova_len; + qdss_start = iommu_cb_set.cb_info[idx].qdss_info.iova_start; + qdss_phy_addr = iommu_cb_set.cb_info[idx].qdss_phy_addr; + CAM_DBG(CAM_SMMU, "QDSS area len from DT = %zu", qdss_len); + + domain = iommu_cb_set.cb_info[idx].domain; + rc = iommu_map(domain, + qdss_start, + qdss_phy_addr, + qdss_len, + IOMMU_READ|IOMMU_WRITE); + + if (rc) { + CAM_ERR(CAM_SMMU, "Failed to map QDSS into IOMMU"); + goto unlock_and_end; + } + + iommu_cb_set.cb_info[idx].is_qdss_allocated = true; + + *iova = iommu_cb_set.cb_info[idx].qdss_info.iova_start; + *len = qdss_len; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + + return rc; + +unlock_and_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +end: + return rc; +} +EXPORT_SYMBOL(cam_smmu_alloc_qdss); + +int cam_smmu_dealloc_qdss(int32_t smmu_hdl) +{ + int rc = 0; + int32_t idx; + size_t qdss_len = 0; + size_t qdss_start = 0; + struct iommu_domain *domain; + size_t unmapped = 0; + + if (smmu_hdl == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + rc = -EINVAL; + goto end; + } + + if (!iommu_cb_set.cb_info[idx].qdss_support) { + CAM_ERR(CAM_SMMU, + "QDSS memory not supported for this SMMU handle"); + rc = -EINVAL; + goto end; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (!iommu_cb_set.cb_info[idx].is_qdss_allocated) { + CAM_ERR(CAM_SMMU, + "Trying to deallocate qdss that is not allocated"); + rc = -ENOMEM; + goto unlock_and_end; + } + + qdss_len = iommu_cb_set.cb_info[idx].qdss_info.iova_len; + qdss_start = iommu_cb_set.cb_info[idx].qdss_info.iova_start; + domain = iommu_cb_set.cb_info[idx].domain; + unmapped = iommu_unmap(domain, qdss_start, qdss_len); + + if (unmapped != qdss_len) { + CAM_ERR(CAM_SMMU, "Only %zu unmapped out of total %zu", + unmapped, + qdss_len); + rc = -EINVAL; + } + + iommu_cb_set.cb_info[idx].is_qdss_allocated = false; + +unlock_and_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); +end: + return rc; +} +EXPORT_SYMBOL(cam_smmu_dealloc_qdss); + +int cam_smmu_get_io_region_info(int32_t smmu_hdl, + dma_addr_t *iova, size_t *len, + dma_addr_t *discard_iova_start, size_t *discard_iova_len) +{ + int32_t idx; + + if (!iova || !len || !discard_iova_start || !discard_iova_len || + (smmu_hdl == HANDLE_INIT)) { + CAM_ERR(CAM_SMMU, "Error: Input args are invalid"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].io_support) { + CAM_ERR(CAM_SMMU, + "I/O memory not supported for this SMMU handle"); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + *iova = iommu_cb_set.cb_info[idx].io_info.iova_start; + *len = iommu_cb_set.cb_info[idx].io_info.iova_len; + *discard_iova_start = + iommu_cb_set.cb_info[idx].io_info.discard_iova_start; + *discard_iova_len = + iommu_cb_set.cb_info[idx].io_info.discard_iova_len; + + CAM_DBG(CAM_SMMU, + "I/O area for hdl = %x Region:[%pK %zu] Discard:[%pK %zu]", + smmu_hdl, *iova, *len, + *discard_iova_start, *discard_iova_len); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + + return 0; +} + +int cam_smmu_get_region_info(int32_t smmu_hdl, + enum cam_smmu_region_id region_id, + struct cam_smmu_region_info *region_info) +{ + int32_t idx; + struct cam_context_bank_info *cb = NULL; + + if (!region_info) { + CAM_ERR(CAM_SMMU, "Invalid region_info pointer"); + return -EINVAL; + } + + if (smmu_hdl == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Invalid handle"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, "Handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + cb = &iommu_cb_set.cb_info[idx]; + if (!cb) { + CAM_ERR(CAM_SMMU, "SMMU context bank pointer invalid"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EINVAL; + } + + switch (region_id) { + case CAM_SMMU_REGION_FIRMWARE: + if (!cb->firmware_support) { + CAM_ERR(CAM_SMMU, "Firmware not supported"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENODEV; + } + region_info->iova_start = cb->firmware_info.iova_start; + region_info->iova_len = cb->firmware_info.iova_len; + break; + case CAM_SMMU_REGION_SHARED: + if (!cb->shared_support) { + CAM_ERR(CAM_SMMU, "Shared mem not supported"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENODEV; + } + region_info->iova_start = cb->shared_info.iova_start; + region_info->iova_len = cb->shared_info.iova_len; + break; + case CAM_SMMU_REGION_SCRATCH: + if (!cb->scratch_buf_support) { + CAM_ERR(CAM_SMMU, "Scratch memory not supported"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENODEV; + } + region_info->iova_start = cb->scratch_info.iova_start; + region_info->iova_len = cb->scratch_info.iova_len; + break; + case CAM_SMMU_REGION_IO: + if (!cb->io_support) { + CAM_ERR(CAM_SMMU, "IO memory not supported"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENODEV; + } + region_info->iova_start = cb->io_info.iova_start; + region_info->iova_len = cb->io_info.iova_len; + break; + case CAM_SMMU_REGION_SECHEAP: + if (!cb->secheap_support) { + CAM_ERR(CAM_SMMU, "Secondary heap not supported"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENODEV; + } + region_info->iova_start = cb->secheap_info.iova_start; + region_info->iova_len = cb->secheap_info.iova_len; + break; + default: + CAM_ERR(CAM_SMMU, "Invalid region id: %d for smmu hdl: %X", + smmu_hdl, region_id); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EINVAL; + } + + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return 0; +} +EXPORT_SYMBOL(cam_smmu_get_region_info); + +int cam_smmu_reserve_sec_heap(int32_t smmu_hdl, + struct dma_buf *buf, + dma_addr_t *iova, + size_t *request_len) +{ + struct secheap_buf_info *secheap_buf = NULL; + size_t size = 0; + uint32_t sec_heap_iova = 0; + size_t sec_heap_iova_len = 0; + int idx; + int rc = 0; + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].secheap_support) { + CAM_ERR(CAM_SMMU, "Secondary heap not supported"); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + + if (iommu_cb_set.cb_info[idx].is_secheap_allocated) { + CAM_ERR(CAM_SMMU, "Trying to allocate secheap twice"); + rc = -ENOMEM; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; + } + + if (IS_ERR_OR_NULL(buf)) { + rc = PTR_ERR(buf); + CAM_ERR(CAM_SMMU, + "Error: dma get buf failed. rc = %d", rc); + goto err_out; + } + + secheap_buf = &iommu_cb_set.cb_info[idx].secheap_buf; + secheap_buf->buf = buf; + secheap_buf->attach = dma_buf_attach(secheap_buf->buf, + iommu_cb_set.cb_info[idx].dev); + if (IS_ERR_OR_NULL(secheap_buf->attach)) { + rc = PTR_ERR(secheap_buf->attach); + CAM_ERR(CAM_SMMU, "Error: dma buf attach failed"); + goto err_put; + } + + secheap_buf->table = dma_buf_map_attachment(secheap_buf->attach, + DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(secheap_buf->table)) { + rc = PTR_ERR(secheap_buf->table); + CAM_ERR(CAM_SMMU, "Error: dma buf map attachment failed"); + goto err_detach; + } + + sec_heap_iova = iommu_cb_set.cb_info[idx].secheap_info.iova_start; + sec_heap_iova_len = iommu_cb_set.cb_info[idx].secheap_info.iova_len; + size = iommu_map_sg(iommu_cb_set.cb_info[idx].domain, + sec_heap_iova, + secheap_buf->table->sgl, + secheap_buf->table->nents, + IOMMU_READ | IOMMU_WRITE); + if (size != sec_heap_iova_len) { + CAM_ERR(CAM_SMMU, "IOMMU mapping failed"); + goto err_unmap_sg; + } + + iommu_cb_set.cb_info[idx].is_secheap_allocated = true; + *iova = (uint32_t)sec_heap_iova; + *request_len = sec_heap_iova_len; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + + return rc; + +err_unmap_sg: + dma_buf_unmap_attachment(secheap_buf->attach, + secheap_buf->table, + DMA_BIDIRECTIONAL); +err_detach: + dma_buf_detach(secheap_buf->buf, + secheap_buf->attach); +err_put: + dma_buf_put(secheap_buf->buf); +err_out: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_reserve_sec_heap); + +int cam_smmu_release_sec_heap(int32_t smmu_hdl) +{ + int idx; + size_t size = 0; + uint32_t sec_heap_iova = 0; + size_t sec_heap_iova_len = 0; + struct secheap_buf_info *secheap_buf = NULL; + + idx = GET_SMMU_TABLE_IDX(smmu_hdl); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, smmu_hdl); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].secheap_support) { + CAM_ERR(CAM_SMMU, "Secondary heap not supported"); + return -EINVAL; + } + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + + if (!iommu_cb_set.cb_info[idx].is_secheap_allocated) { + CAM_ERR(CAM_SMMU, "Trying to release secheap twice"); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -ENOMEM; + } + + secheap_buf = &iommu_cb_set.cb_info[idx].secheap_buf; + sec_heap_iova = iommu_cb_set.cb_info[idx].secheap_info.iova_start; + sec_heap_iova_len = iommu_cb_set.cb_info[idx].secheap_info.iova_len; + + size = iommu_unmap(iommu_cb_set.cb_info[idx].domain, + sec_heap_iova, + sec_heap_iova_len); + if (size != sec_heap_iova_len) { + CAM_ERR(CAM_SMMU, "Failed: Unmapped = %zu, requested = %zu", + size, + sec_heap_iova_len); + } + + dma_buf_unmap_attachment(secheap_buf->attach, + secheap_buf->table, DMA_BIDIRECTIONAL); + dma_buf_detach(secheap_buf->buf, secheap_buf->attach); + dma_buf_put(secheap_buf->buf); + iommu_cb_set.cb_info[idx].is_secheap_allocated = false; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + + return 0; +} +EXPORT_SYMBOL(cam_smmu_release_sec_heap); + +static int cam_smmu_map_buffer_validate(struct dma_buf *buf, + int idx, enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr, + size_t *len_ptr, enum cam_smmu_region_id region_id, + bool dis_delayed_unmap, struct cam_dma_buff_info **mapping_info) +{ + struct dma_buf_attachment *attach = NULL; + struct sg_table *table = NULL; + struct iommu_domain *domain; + size_t size = 0; + uint32_t iova = 0; + int rc = 0; + + if (IS_ERR_OR_NULL(buf)) { + rc = PTR_ERR(buf); + CAM_ERR(CAM_SMMU, + "Error: dma get buf failed. rc = %d", rc); + goto err_out; + } + + if (!mapping_info) { + rc = -EINVAL; + CAM_ERR(CAM_SMMU, "Error: mapping_info is invalid"); + goto err_out; + } + + attach = dma_buf_attach(buf, iommu_cb_set.cb_info[idx].dev); + if (IS_ERR_OR_NULL(attach)) { + rc = PTR_ERR(attach); + CAM_ERR(CAM_SMMU, "Error: dma buf attach failed"); + goto err_put; + } + + if (region_id == CAM_SMMU_REGION_SHARED) { + table = dma_buf_map_attachment(attach, dma_dir); + if (IS_ERR_OR_NULL(table)) { + rc = PTR_ERR(table); + CAM_ERR(CAM_SMMU, "Error: dma map attachment failed"); + goto err_detach; + } + + domain = iommu_cb_set.cb_info[idx].domain; + if (!domain) { + CAM_ERR(CAM_SMMU, "CB has no domain set"); + goto err_unmap_sg; + } + + rc = cam_smmu_alloc_iova(*len_ptr, + iommu_cb_set.cb_info[idx].handle, + &iova); + + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "IOVA alloc failed for shared memory, size=%zu, idx=%d, handle=%d", + *len_ptr, idx, + iommu_cb_set.cb_info[idx].handle); + goto err_unmap_sg; + } + + size = iommu_map_sg(domain, iova, table->sgl, table->nents, + IOMMU_READ | IOMMU_WRITE); + + if (size < 0) { + CAM_ERR(CAM_SMMU, "IOMMU mapping failed"); + rc = cam_smmu_free_iova(iova, + size, iommu_cb_set.cb_info[idx].handle); + if (rc) + CAM_ERR(CAM_SMMU, "IOVA free failed"); + rc = -ENOMEM; + goto err_unmap_sg; + } else { + CAM_DBG(CAM_SMMU, + "iommu_map_sg returned iova=%pK, size=%zu", + iova, size); + *paddr_ptr = iova; + *len_ptr = size; + } + iommu_cb_set.cb_info[idx].shared_mapping_size += *len_ptr; + } else if (region_id == CAM_SMMU_REGION_IO) { + if (!dis_delayed_unmap) + attach->dma_map_attrs |= DMA_ATTR_DELAYED_UNMAP; + + table = dma_buf_map_attachment(attach, dma_dir); + if (IS_ERR_OR_NULL(table)) { + rc = PTR_ERR(table); + CAM_ERR(CAM_SMMU, "Error: dma map attachment failed"); + goto err_detach; + } + + *paddr_ptr = sg_dma_address(table->sgl); + *len_ptr = (size_t)buf->size; + iommu_cb_set.cb_info[idx].io_mapping_size += *len_ptr; + } else { + CAM_ERR(CAM_SMMU, "Error: Wrong region id passed"); + rc = -EINVAL; + goto err_unmap_sg; + } + + CAM_DBG(CAM_SMMU, + "iova=%pK, region_id=%d, paddr=%pK, len=%d, dma_map_attrs=%d", + iova, region_id, *paddr_ptr, *len_ptr, attach->dma_map_attrs); + + if (table->sgl) { + CAM_DBG(CAM_SMMU, + "DMA buf: %pK, device: %pK, attach: %pK, table: %pK", + (void *)buf, + (void *)iommu_cb_set.cb_info[idx].dev, + (void *)attach, (void *)table); + CAM_DBG(CAM_SMMU, "table sgl: %pK, rc: %d, dma_address: 0x%x", + (void *)table->sgl, rc, + (unsigned int)table->sgl->dma_address); + } else { + rc = -EINVAL; + CAM_ERR(CAM_SMMU, "Error: table sgl is null"); + goto err_unmap_sg; + } + + /* fill up mapping_info */ + *mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL); + if (!(*mapping_info)) { + rc = -ENOSPC; + goto err_alloc; + } + + (*mapping_info)->buf = buf; + (*mapping_info)->attach = attach; + (*mapping_info)->table = table; + (*mapping_info)->paddr = *paddr_ptr; + (*mapping_info)->len = *len_ptr; + (*mapping_info)->dir = dma_dir; + (*mapping_info)->ref_count = 1; + (*mapping_info)->region_id = region_id; + + if (!*paddr_ptr || !*len_ptr) { + CAM_ERR(CAM_SMMU, "Error: Space Allocation failed"); + kfree(*mapping_info); + *mapping_info = NULL; + rc = -ENOSPC; + goto err_alloc; + } + CAM_DBG(CAM_SMMU, "idx=%d, dma_buf=%pK, dev=%pK, paddr=%pK, len=%u", + idx, buf, (void *)iommu_cb_set.cb_info[idx].dev, + (void *)*paddr_ptr, (unsigned int)*len_ptr); + + return 0; + +err_alloc: + if (region_id == CAM_SMMU_REGION_SHARED) { + cam_smmu_free_iova(iova, + size, + iommu_cb_set.cb_info[idx].handle); + + iommu_unmap(iommu_cb_set.cb_info[idx].domain, + *paddr_ptr, + *len_ptr); + } +err_unmap_sg: + dma_buf_unmap_attachment(attach, table, dma_dir); +err_detach: + dma_buf_detach(buf, attach); +err_put: + dma_buf_put(buf); +err_out: + return rc; +} + + +static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd, + bool dis_delayed_unmap, enum dma_data_direction dma_dir, + dma_addr_t *paddr_ptr, size_t *len_ptr, + enum cam_smmu_region_id region_id) +{ + int rc = -1; + struct cam_dma_buff_info *mapping_info = NULL; + struct dma_buf *buf = NULL; + + /* returns the dma_buf structure related to an fd */ + buf = dma_buf_get(ion_fd); + + rc = cam_smmu_map_buffer_validate(buf, idx, dma_dir, paddr_ptr, len_ptr, + region_id, dis_delayed_unmap, &mapping_info); + + if (rc) { + CAM_ERR(CAM_SMMU, "buffer validation failure"); + return rc; + } + + mapping_info->ion_fd = ion_fd; + /* add to the list */ + list_add(&mapping_info->list, + &iommu_cb_set.cb_info[idx].smmu_buf_list); + + return 0; +} + +static int cam_smmu_map_kernel_buffer_and_add_to_list(int idx, + struct dma_buf *buf, enum dma_data_direction dma_dir, + dma_addr_t *paddr_ptr, size_t *len_ptr, + enum cam_smmu_region_id region_id) +{ + int rc = -1; + struct cam_dma_buff_info *mapping_info = NULL; + + rc = cam_smmu_map_buffer_validate(buf, idx, dma_dir, paddr_ptr, len_ptr, + region_id, false, &mapping_info); + + if (rc) { + CAM_ERR(CAM_SMMU, "buffer validation failure"); + return rc; + } + + mapping_info->ion_fd = -1; + + /* add to the list */ + list_add(&mapping_info->list, + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list); + + return 0; +} + + +static int cam_smmu_unmap_buf_and_remove_from_list( + struct cam_dma_buff_info *mapping_info, + int idx) +{ + int rc; + size_t size; + struct iommu_domain *domain; + + if ((!mapping_info->buf) || (!mapping_info->table) || + (!mapping_info->attach)) { + CAM_ERR(CAM_SMMU, + "Error: Invalid params dev = %pK, table = %pK", + (void *)iommu_cb_set.cb_info[idx].dev, + (void *)mapping_info->table); + CAM_ERR(CAM_SMMU, "Error:dma_buf = %pK, attach = %pK", + (void *)mapping_info->buf, + (void *)mapping_info->attach); + return -EINVAL; + } + + CAM_DBG(CAM_SMMU, + "region_id=%d, paddr=%pK, len=%d, dma_map_attrs=%d", + mapping_info->region_id, mapping_info->paddr, mapping_info->len, + mapping_info->attach->dma_map_attrs); + + if (mapping_info->region_id == CAM_SMMU_REGION_SHARED) { + CAM_DBG(CAM_SMMU, + "Removing SHARED buffer paddr = %pK, len = %zu", + (void *)mapping_info->paddr, mapping_info->len); + + domain = iommu_cb_set.cb_info[idx].domain; + + size = iommu_unmap(domain, + mapping_info->paddr, + mapping_info->len); + + if (size != mapping_info->len) { + CAM_ERR(CAM_SMMU, "IOMMU unmap failed"); + CAM_ERR(CAM_SMMU, "Unmapped = %zu, requested = %zu", + size, + mapping_info->len); + } + + rc = cam_smmu_free_iova(mapping_info->paddr, + mapping_info->len, + iommu_cb_set.cb_info[idx].handle); + + if (rc) + CAM_ERR(CAM_SMMU, "IOVA free failed"); + + iommu_cb_set.cb_info[idx].shared_mapping_size -= + mapping_info->len; + } else if (mapping_info->region_id == CAM_SMMU_REGION_IO) { + iommu_cb_set.cb_info[idx].io_mapping_size -= mapping_info->len; + } + + dma_buf_unmap_attachment(mapping_info->attach, + mapping_info->table, mapping_info->dir); + dma_buf_detach(mapping_info->buf, mapping_info->attach); + dma_buf_put(mapping_info->buf); + + mapping_info->buf = NULL; + + list_del_init(&mapping_info->list); + + /* free one buffer */ + kfree(mapping_info); + return 0; +} + +static enum cam_smmu_buf_state cam_smmu_check_fd_in_list(int idx, + int ion_fd, dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + struct cam_dma_buff_info *mapping; + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, list) { + if (mapping->ion_fd == ion_fd) { + *paddr_ptr = mapping->paddr; + *len_ptr = mapping->len; + return CAM_SMMU_BUFF_EXIST; + } + } + + return CAM_SMMU_BUFF_NOT_EXIST; +} + +static enum cam_smmu_buf_state cam_smmu_check_dma_buf_in_list(int idx, + struct dma_buf *buf, dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + struct cam_dma_buff_info *mapping; + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) { + if (mapping->buf == buf) { + *paddr_ptr = mapping->paddr; + *len_ptr = mapping->len; + return CAM_SMMU_BUFF_EXIST; + } + } + + return CAM_SMMU_BUFF_NOT_EXIST; +} + +static enum cam_smmu_buf_state cam_smmu_check_secure_fd_in_list(int idx, + int ion_fd, dma_addr_t *paddr_ptr, + size_t *len_ptr) +{ + struct cam_sec_buff_info *mapping; + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, + list) { + if (mapping->ion_fd == ion_fd) { + *paddr_ptr = mapping->paddr; + *len_ptr = mapping->len; + mapping->ref_count++; + return CAM_SMMU_BUFF_EXIST; + } + } + + return CAM_SMMU_BUFF_NOT_EXIST; +} + +static enum cam_smmu_buf_state cam_smmu_validate_secure_fd_in_list(int idx, + int ion_fd, dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + struct cam_sec_buff_info *mapping; + + list_for_each_entry(mapping, + &iommu_cb_set.cb_info[idx].smmu_buf_list, + list) { + if (mapping->ion_fd == ion_fd) { + *paddr_ptr = mapping->paddr; + *len_ptr = mapping->len; + return CAM_SMMU_BUFF_EXIST; + } + } + + return CAM_SMMU_BUFF_NOT_EXIST; +} + +int cam_smmu_get_handle(char *identifier, int *handle_ptr) +{ + int ret = 0; + + if (!identifier) { + CAM_ERR(CAM_SMMU, "Error: iommu hardware name is NULL"); + return -EINVAL; + } + + if (!handle_ptr) { + CAM_ERR(CAM_SMMU, "Error: handle pointer is NULL"); + return -EINVAL; + } + + /* create and put handle in the table */ + ret = cam_smmu_create_add_handle_in_table(identifier, handle_ptr); + if (ret < 0) + CAM_ERR(CAM_SMMU, "Error: %s get handle fail", identifier); + + return ret; +} +EXPORT_SYMBOL(cam_smmu_get_handle); + +int cam_smmu_ops(int handle, enum cam_smmu_ops_param ops) +{ + int ret = 0, idx; + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, "Error: Index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EINVAL; + } + + switch (ops) { + case CAM_SMMU_ATTACH: { + ret = cam_smmu_attach(idx); + break; + } + case CAM_SMMU_DETACH: { + ret = cam_smmu_detach_device(idx); + break; + } + case CAM_SMMU_VOTE: + case CAM_SMMU_DEVOTE: + default: + CAM_ERR(CAM_SMMU, "Error: idx = %d, ops = %d", idx, ops); + ret = -EINVAL; + } + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return ret; +} +EXPORT_SYMBOL(cam_smmu_ops); + +static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx, + size_t virt_len, + size_t phys_len, + unsigned int iommu_dir, + dma_addr_t *virt_addr) +{ + unsigned long nents = virt_len / phys_len; + struct cam_dma_buff_info *mapping_info = NULL; + size_t unmapped; + dma_addr_t iova = 0; + struct scatterlist *sg; + int i = 0; + int rc; + struct iommu_domain *domain = NULL; + struct page *page; + struct sg_table *table = NULL; + + CAM_DBG(CAM_SMMU, "nents = %lu, idx = %d, virt_len = %zx", + nents, idx, virt_len); + CAM_DBG(CAM_SMMU, "phys_len = %zx, iommu_dir = %d, virt_addr = %pK", + phys_len, iommu_dir, virt_addr); + + /* + * This table will go inside the 'mapping' structure + * where it will be held until put_scratch_buffer is called + */ + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) { + rc = -ENOMEM; + goto err_table_alloc; + } + + rc = sg_alloc_table(table, nents, GFP_KERNEL); + if (rc < 0) { + rc = -EINVAL; + goto err_sg_alloc; + } + + page = alloc_pages(GFP_KERNEL, get_order(phys_len)); + if (!page) { + rc = -ENOMEM; + goto err_page_alloc; + } + + /* Now we create the sg list */ + for_each_sg(table->sgl, sg, table->nents, i) + sg_set_page(sg, page, phys_len, 0); + + + /* Get the domain from within our cb_set struct and map it*/ + domain = iommu_cb_set.cb_info[idx].domain; + + rc = cam_smmu_alloc_scratch_va(&iommu_cb_set.cb_info[idx].scratch_map, + virt_len, &iova); + + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "Could not find valid iova for scratch buffer"); + goto err_iommu_map; + } + + if (iommu_map_sg(domain, + iova, + table->sgl, + table->nents, + iommu_dir) != virt_len) { + CAM_ERR(CAM_SMMU, "iommu_map_sg() failed"); + goto err_iommu_map; + } + + /* Now update our mapping information within the cb_set struct */ + mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL); + if (!mapping_info) { + rc = -ENOMEM; + goto err_mapping_info; + } + + mapping_info->ion_fd = 0xDEADBEEF; + mapping_info->buf = NULL; + mapping_info->attach = NULL; + mapping_info->table = table; + mapping_info->paddr = iova; + mapping_info->len = virt_len; + mapping_info->iommu_dir = iommu_dir; + mapping_info->ref_count = 1; + mapping_info->phys_len = phys_len; + mapping_info->region_id = CAM_SMMU_REGION_SCRATCH; + + CAM_DBG(CAM_SMMU, "paddr = %pK, len = %zx, phys_len = %zx", + (void *)mapping_info->paddr, + mapping_info->len, mapping_info->phys_len); + + list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list); + + *virt_addr = (dma_addr_t)iova; + + CAM_DBG(CAM_SMMU, "mapped virtual address = %lx", + (unsigned long)*virt_addr); + return 0; + +err_mapping_info: + unmapped = iommu_unmap(domain, iova, virt_len); + if (unmapped != virt_len) + CAM_ERR(CAM_SMMU, "Unmapped only %zx instead of %zx", + unmapped, virt_len); +err_iommu_map: + __free_pages(page, get_order(phys_len)); +err_page_alloc: + sg_free_table(table); +err_sg_alloc: + kfree(table); +err_table_alloc: + return rc; +} + +static int cam_smmu_free_scratch_buffer_remove_from_list( + struct cam_dma_buff_info *mapping_info, + int idx) +{ + int rc = 0; + size_t unmapped; + struct iommu_domain *domain = + iommu_cb_set.cb_info[idx].domain; + struct scratch_mapping *scratch_map = + &iommu_cb_set.cb_info[idx].scratch_map; + + if (!mapping_info->table) { + CAM_ERR(CAM_SMMU, + "Error: Invalid params: dev = %pK, table = %pK", + (void *)iommu_cb_set.cb_info[idx].dev, + (void *)mapping_info->table); + return -EINVAL; + } + + /* Clean up the mapping_info struct from the list */ + unmapped = iommu_unmap(domain, mapping_info->paddr, mapping_info->len); + if (unmapped != mapping_info->len) + CAM_ERR(CAM_SMMU, "Unmapped only %zx instead of %zx", + unmapped, mapping_info->len); + + rc = cam_smmu_free_scratch_va(scratch_map, + mapping_info->paddr, + mapping_info->len); + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "Error: Invalid iova while freeing scratch buffer"); + rc = -EINVAL; + } + + __free_pages(sg_page(mapping_info->table->sgl), + get_order(mapping_info->phys_len)); + sg_free_table(mapping_info->table); + kfree(mapping_info->table); + list_del_init(&mapping_info->list); + + kfree(mapping_info); + mapping_info = NULL; + + return rc; +} + +int cam_smmu_get_scratch_iova(int handle, + enum cam_smmu_map_dir dir, + dma_addr_t *paddr_ptr, + size_t virt_len, + size_t phys_len) +{ + int idx, rc; + unsigned int iommu_dir; + + if (!paddr_ptr || !virt_len || !phys_len) { + CAM_ERR(CAM_SMMU, "Error: Input pointer or lengths invalid"); + return -EINVAL; + } + + if (virt_len < phys_len) { + CAM_ERR(CAM_SMMU, "Error: virt_len > phys_len"); + return -EINVAL; + } + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + iommu_dir = cam_smmu_translate_dir_to_iommu_dir(dir); + if (iommu_dir == IOMMU_INVALID_DIR) { + CAM_ERR(CAM_SMMU, + "Error: translate direction failed. dir = %d", dir); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto error; + } + + if (!iommu_cb_set.cb_info[idx].scratch_buf_support) { + CAM_ERR(CAM_SMMU, + "Error: Context bank does not support scratch bufs"); + rc = -EINVAL; + goto error; + } + + CAM_DBG(CAM_SMMU, "smmu handle = %x, idx = %d, dir = %d", + handle, idx, dir); + CAM_DBG(CAM_SMMU, "virt_len = %zx, phys_len = %zx", + phys_len, virt_len); + + if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) { + CAM_ERR(CAM_SMMU, + "Err:Dev %s should call SMMU attach before map buffer", + iommu_cb_set.cb_info[idx].name); + rc = -EINVAL; + goto error; + } + + if (!IS_ALIGNED(virt_len, PAGE_SIZE)) { + CAM_ERR(CAM_SMMU, + "Requested scratch buffer length not page aligned"); + rc = -EINVAL; + goto error; + } + + if (!IS_ALIGNED(virt_len, phys_len)) { + CAM_ERR(CAM_SMMU, + "Requested virt length not aligned with phys length"); + rc = -EINVAL; + goto error; + } + + rc = cam_smmu_alloc_scratch_buffer_add_to_list(idx, + virt_len, + phys_len, + iommu_dir, + paddr_ptr); + if (rc < 0) + CAM_ERR(CAM_SMMU, "Error: mapping or add list fail"); + +error: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} + +int cam_smmu_put_scratch_iova(int handle, + dma_addr_t paddr) +{ + int idx; + int rc = -1; + struct cam_dma_buff_info *mapping_info; + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + /* find index in the iommu_cb_set.cb_info */ + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto handle_err; + } + + if (!iommu_cb_set.cb_info[idx].scratch_buf_support) { + CAM_ERR(CAM_SMMU, + "Error: Context bank does not support scratch buffers"); + rc = -EINVAL; + goto handle_err; + } + + /* Based on virtual address and index, we can find mapping info + * of the scratch buffer + */ + mapping_info = cam_smmu_find_mapping_by_virt_address(idx, paddr); + if (!mapping_info) { + CAM_ERR(CAM_SMMU, "Error: Invalid params"); + rc = -ENODEV; + goto handle_err; + } + + /* unmapping one buffer from device */ + rc = cam_smmu_free_scratch_buffer_remove_from_list(mapping_info, idx); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail"); + goto handle_err; + } + +handle_err: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} + +static int cam_smmu_map_stage2_buffer_and_add_to_list(int idx, int ion_fd, + enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr, + size_t *len_ptr) +{ + int rc = 0; + struct dma_buf *dmabuf = NULL; + struct dma_buf_attachment *attach = NULL; + struct sg_table *table = NULL; + struct cam_sec_buff_info *mapping_info; + + /* clean the content from clients */ + *paddr_ptr = (dma_addr_t)NULL; + *len_ptr = (size_t)0; + + dmabuf = dma_buf_get(ion_fd); + if (IS_ERR_OR_NULL((void *)(dmabuf))) { + CAM_ERR(CAM_SMMU, + "Error: dma buf get failed, idx=%d, ion_fd=%d", + idx, ion_fd); + rc = PTR_ERR(dmabuf); + goto err_out; + } + + /* + * ion_phys() is deprecated. call dma_buf_attach() and + * dma_buf_map_attachment() to get the buffer's physical + * address. + */ + attach = dma_buf_attach(dmabuf, iommu_cb_set.cb_info[idx].dev); + if (IS_ERR_OR_NULL(attach)) { + CAM_ERR(CAM_SMMU, + "Error: dma buf attach failed, idx=%d, ion_fd=%d", + idx, ion_fd); + rc = PTR_ERR(attach); + goto err_put; + } + + attach->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + + table = dma_buf_map_attachment(attach, dma_dir); + if (IS_ERR_OR_NULL(table)) { + CAM_ERR(CAM_SMMU, "Error: dma buf map attachment failed"); + rc = PTR_ERR(table); + goto err_detach; + } + + /* return addr and len to client */ + *paddr_ptr = sg_phys(table->sgl); + *len_ptr = (size_t)sg_dma_len(table->sgl); + + /* fill up mapping_info */ + mapping_info = kzalloc(sizeof(struct cam_sec_buff_info), GFP_KERNEL); + if (!mapping_info) { + rc = -ENOMEM; + goto err_unmap_sg; + } + + mapping_info->ion_fd = ion_fd; + mapping_info->paddr = *paddr_ptr; + mapping_info->len = *len_ptr; + mapping_info->dir = dma_dir; + mapping_info->ref_count = 1; + mapping_info->buf = dmabuf; + + CAM_DBG(CAM_SMMU, "idx=%d, ion_fd=%d, dev=%pK, paddr=%pK, len=%u", + idx, ion_fd, + (void *)iommu_cb_set.cb_info[idx].dev, + (void *)*paddr_ptr, (unsigned int)*len_ptr); + + /* add to the list */ + list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list); + + return 0; + +err_unmap_sg: + dma_buf_unmap_attachment(attach, table, dma_dir); +err_detach: + dma_buf_detach(dmabuf, attach); +err_put: + dma_buf_put(dmabuf); +err_out: + return rc; +} + +int cam_smmu_map_stage2_iova(int handle, + int ion_fd, enum cam_smmu_map_dir dir, + dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + int idx, rc; + enum dma_data_direction dma_dir; + enum cam_smmu_buf_state buf_state; + + if (!paddr_ptr || !len_ptr) { + CAM_ERR(CAM_SMMU, + "Error: Invalid inputs, paddr_ptr:%pK, len_ptr: %pK", + paddr_ptr, len_ptr); + return -EINVAL; + } + /* clean the content from clients */ + *paddr_ptr = (dma_addr_t)NULL; + *len_ptr = (size_t)0; + + dma_dir = cam_smmu_translate_dir(dir); + if (dma_dir == DMA_NONE) { + CAM_ERR(CAM_SMMU, + "Error: translate direction failed. dir = %d", dir); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if ((handle == HANDLE_INIT) || + (idx < 0) || + (idx >= iommu_cb_set.cb_num)) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't map secure mem to non secure cb, idx=%d", + idx); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, idx=%d, table_hdl=%x, hdl=%x", + idx, iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto get_addr_end; + } + + buf_state = cam_smmu_check_secure_fd_in_list(idx, ion_fd, paddr_ptr, + len_ptr); + if (buf_state == CAM_SMMU_BUFF_EXIST) { + CAM_DBG(CAM_SMMU, + "fd:%d already in list idx:%d, handle=%d give same addr back", + ion_fd, idx, handle); + rc = 0; + goto get_addr_end; + } + rc = cam_smmu_map_stage2_buffer_and_add_to_list(idx, ion_fd, dma_dir, + paddr_ptr, len_ptr); + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "Error: mapping or add list fail, idx=%d, handle=%d, fd=%d, rc=%d", + idx, handle, ion_fd, rc); + goto get_addr_end; + } + +get_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_map_stage2_iova); + +static int cam_smmu_secure_unmap_buf_and_remove_from_list( + struct cam_sec_buff_info *mapping_info, + int idx) +{ + if (!mapping_info) { + CAM_ERR(CAM_SMMU, "Error: List doesn't exist"); + return -EINVAL; + } + dma_buf_put(mapping_info->buf); + list_del_init(&mapping_info->list); + + CAM_DBG(CAM_SMMU, "unmap fd: %d, idx : %d", mapping_info->ion_fd, idx); + + /* free one buffer */ + kfree(mapping_info); + return 0; +} + +int cam_smmu_unmap_stage2_iova(int handle, int ion_fd) +{ + int idx, rc; + struct cam_sec_buff_info *mapping_info; + + /* find index in the iommu_cb_set.cb_info */ + idx = GET_SMMU_TABLE_IDX(handle); + if ((handle == HANDLE_INIT) || + (idx < 0) || + (idx >= iommu_cb_set.cb_num)) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't unmap secure mem from non secure cb"); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto put_addr_end; + } + + /* based on ion fd and index, we can find mapping info of buffer */ + mapping_info = cam_smmu_find_mapping_by_sec_buf_idx(idx, ion_fd); + if (!mapping_info) { + CAM_ERR(CAM_SMMU, + "Error: Invalid params! idx = %d, fd = %d", + idx, ion_fd); + rc = -EINVAL; + goto put_addr_end; + } + + mapping_info->ref_count--; + if (mapping_info->ref_count > 0) { + CAM_DBG(CAM_SMMU, + "idx: %d fd = %d ref_count: %d", + idx, ion_fd, mapping_info->ref_count); + rc = 0; + goto put_addr_end; + } + mapping_info->ref_count = 0; + + /* unmapping one buffer from device */ + rc = cam_smmu_secure_unmap_buf_and_remove_from_list(mapping_info, idx); + if (rc) { + CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail"); + goto put_addr_end; + } + +put_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_unmap_stage2_iova); + +static int cam_smmu_map_iova_validate_params(int handle, + enum cam_smmu_map_dir dir, + dma_addr_t *paddr_ptr, size_t *len_ptr, + enum cam_smmu_region_id region_id) +{ + int idx, rc = 0; + enum dma_data_direction dma_dir; + + if (!paddr_ptr || !len_ptr) { + CAM_ERR(CAM_SMMU, "Input pointers are invalid"); + return -EINVAL; + } + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Invalid handle"); + return -EINVAL; + } + + /* clean the content from clients */ + *paddr_ptr = (dma_addr_t)NULL; + if (region_id != CAM_SMMU_REGION_SHARED) + *len_ptr = (size_t)0; + + dma_dir = cam_smmu_translate_dir(dir); + if (dma_dir == DMA_NONE) { + CAM_ERR(CAM_SMMU, "translate direction failed. dir = %d", dir); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, "handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + return rc; +} + +int cam_smmu_map_user_iova(int handle, int ion_fd, bool dis_delayed_unmap, + enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr, + size_t *len_ptr, enum cam_smmu_region_id region_id) +{ + int idx, rc = 0; + enum cam_smmu_buf_state buf_state; + enum dma_data_direction dma_dir; + + rc = cam_smmu_map_iova_validate_params(handle, dir, paddr_ptr, + len_ptr, region_id); + if (rc) { + CAM_ERR(CAM_SMMU, "initial checks failed, unable to proceed"); + return rc; + } + + dma_dir = (enum dma_data_direction)dir; + idx = GET_SMMU_TABLE_IDX(handle); + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't map non-secure mem to secure cb idx=%d", + idx); + rc = -EINVAL; + goto get_addr_end; + } + + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "hdl is not valid, idx=%d, table_hdl = %x, hdl = %x", + idx, iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto get_addr_end; + } + + if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) { + CAM_ERR(CAM_SMMU, + "Err:Dev %s should call SMMU attach before map buffer", + iommu_cb_set.cb_info[idx].name); + rc = -EINVAL; + goto get_addr_end; + } + + buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr, len_ptr); + if (buf_state == CAM_SMMU_BUFF_EXIST) { + CAM_ERR(CAM_SMMU, + "fd:%d already in list idx:%d, handle=%d, give same addr back", + ion_fd, idx, handle); + rc = -EALREADY; + goto get_addr_end; + } + + rc = cam_smmu_map_buffer_and_add_to_list(idx, ion_fd, + dis_delayed_unmap, dma_dir, paddr_ptr, len_ptr, region_id); + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "mapping or add list fail, idx=%d, fd=%d, region=%d, rc=%d", + idx, ion_fd, region_id, rc); + cam_smmu_dump_cb_info(idx); + } + +get_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_map_user_iova); + +int cam_smmu_map_kernel_iova(int handle, struct dma_buf *buf, + enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr, + size_t *len_ptr, enum cam_smmu_region_id region_id) +{ + int idx, rc = 0; + enum cam_smmu_buf_state buf_state; + enum dma_data_direction dma_dir; + + rc = cam_smmu_map_iova_validate_params(handle, dir, paddr_ptr, + len_ptr, region_id); + if (rc) { + CAM_ERR(CAM_SMMU, "initial checks failed, unable to proceed"); + return rc; + } + + dma_dir = cam_smmu_translate_dir(dir); + idx = GET_SMMU_TABLE_IDX(handle); + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't map non-secure mem to secure cb"); + rc = -EINVAL; + goto get_addr_end; + } + + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, "hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto get_addr_end; + } + + if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) { + CAM_ERR(CAM_SMMU, + "Err:Dev %s should call SMMU attach before map buffer", + iommu_cb_set.cb_info[idx].name); + rc = -EINVAL; + goto get_addr_end; + } + + buf_state = cam_smmu_check_dma_buf_in_list(idx, buf, + paddr_ptr, len_ptr); + if (buf_state == CAM_SMMU_BUFF_EXIST) { + CAM_ERR(CAM_SMMU, + "dma_buf :%pK already in the list", buf); + rc = -EALREADY; + goto get_addr_end; + } + + rc = cam_smmu_map_kernel_buffer_and_add_to_list(idx, buf, dma_dir, + paddr_ptr, len_ptr, region_id); + if (rc < 0) + CAM_ERR(CAM_SMMU, "mapping or add list fail"); + +get_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_map_kernel_iova); + +int cam_smmu_get_iova(int handle, int ion_fd, + dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + int idx, rc = 0; + enum cam_smmu_buf_state buf_state; + + if (!paddr_ptr || !len_ptr) { + CAM_ERR(CAM_SMMU, "Error: Input pointers are invalid"); + return -EINVAL; + } + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + /* clean the content from clients */ + *paddr_ptr = (dma_addr_t)NULL; + *len_ptr = (size_t)0; + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + if (iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't get non-secure mem from secure cb"); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto get_addr_end; + } + + buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr, len_ptr); + if (buf_state == CAM_SMMU_BUFF_NOT_EXIST) { + CAM_ERR(CAM_SMMU, "ion_fd:%d not in the mapped list", ion_fd); + rc = -EINVAL; + goto get_addr_end; + } + +get_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_get_iova); + +int cam_smmu_get_stage2_iova(int handle, int ion_fd, + dma_addr_t *paddr_ptr, size_t *len_ptr) +{ + int idx, rc = 0; + enum cam_smmu_buf_state buf_state; + + if (!paddr_ptr || !len_ptr) { + CAM_ERR(CAM_SMMU, "Error: Input pointers are invalid"); + return -EINVAL; + } + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + /* clean the content from clients */ + *paddr_ptr = (dma_addr_t)NULL; + *len_ptr = (size_t)0; + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + if (!iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't get secure mem from non secure cb"); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto get_addr_end; + } + + buf_state = cam_smmu_validate_secure_fd_in_list(idx, + ion_fd, + paddr_ptr, + len_ptr); + + if (buf_state == CAM_SMMU_BUFF_NOT_EXIST) { + CAM_ERR(CAM_SMMU, "ion_fd:%d not in the mapped list", ion_fd); + rc = -EINVAL; + goto get_addr_end; + } + +get_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_get_stage2_iova); + +static int cam_smmu_unmap_validate_params(int handle) +{ + int idx; + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + /* find index in the iommu_cb_set.cb_info */ + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + return 0; +} + +int cam_smmu_unmap_user_iova(int handle, + int ion_fd, enum cam_smmu_region_id region_id) +{ + int idx, rc; + struct cam_dma_buff_info *mapping_info; + + rc = cam_smmu_unmap_validate_params(handle); + if (rc) { + CAM_ERR(CAM_SMMU, "unmap util validation failure"); + return rc; + } + + idx = GET_SMMU_TABLE_IDX(handle); + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't unmap non-secure mem from secure cb"); + rc = -EINVAL; + goto unmap_end; + } + + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto unmap_end; + } + + /* Based on ion_fd & index, we can find mapping info of buffer */ + mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd); + + if (!mapping_info) { + CAM_ERR(CAM_SMMU, + "Error: Invalid params idx = %d, fd = %d", + idx, ion_fd); + rc = -EINVAL; + goto unmap_end; + } + + /* Unmapping one buffer from device */ + CAM_DBG(CAM_SMMU, "SMMU: removing buffer idx = %d", idx); + rc = cam_smmu_unmap_buf_and_remove_from_list(mapping_info, idx); + if (rc < 0) + CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail"); + +unmap_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_unmap_user_iova); + +int cam_smmu_unmap_kernel_iova(int handle, + struct dma_buf *buf, enum cam_smmu_region_id region_id) +{ + int idx, rc; + struct cam_dma_buff_info *mapping_info; + + rc = cam_smmu_unmap_validate_params(handle); + if (rc) { + CAM_ERR(CAM_SMMU, "unmap util validation failure"); + return rc; + } + + idx = GET_SMMU_TABLE_IDX(handle); + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].is_secure) { + CAM_ERR(CAM_SMMU, + "Error: can't unmap non-secure mem from secure cb"); + rc = -EINVAL; + goto unmap_end; + } + + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto unmap_end; + } + + /* Based on dma_buf & index, we can find mapping info of buffer */ + mapping_info = cam_smmu_find_mapping_by_dma_buf(idx, buf); + + if (!mapping_info) { + CAM_ERR(CAM_SMMU, + "Error: Invalid params idx = %d, dma_buf = %pK", + idx, buf); + rc = -EINVAL; + goto unmap_end; + } + + /* Unmapping one buffer from device */ + CAM_DBG(CAM_SMMU, "SMMU: removing buffer idx = %d", idx); + rc = cam_smmu_unmap_buf_and_remove_from_list(mapping_info, idx); + if (rc < 0) + CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail"); + +unmap_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_unmap_kernel_iova); + + +int cam_smmu_put_iova(int handle, int ion_fd) +{ + int idx; + int rc = 0; + struct cam_dma_buff_info *mapping_info; + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + /* find index in the iommu_cb_set.cb_info */ + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + rc = -EINVAL; + goto put_addr_end; + } + + /* based on ion fd and index, we can find mapping info of buffer */ + mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd); + if (!mapping_info) { + CAM_ERR(CAM_SMMU, "Error: Invalid params idx = %d, fd = %d", + idx, ion_fd); + rc = -EINVAL; + goto put_addr_end; + } + +put_addr_end: + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return rc; +} +EXPORT_SYMBOL(cam_smmu_put_iova); + +int cam_smmu_destroy_handle(int handle) +{ + int idx; + + if (handle == HANDLE_INIT) { + CAM_ERR(CAM_SMMU, "Error: Invalid handle"); + return -EINVAL; + } + + idx = GET_SMMU_TABLE_IDX(handle); + if (idx < 0 || idx >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, + "Error: handle or index invalid. idx = %d hdl = %x", + idx, handle); + return -EINVAL; + } + + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + CAM_ERR(CAM_SMMU, + "Error: hdl is not valid, table_hdl = %x, hdl = %x", + iommu_cb_set.cb_info[idx].handle, handle); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EINVAL; + } + + if (!list_empty_careful(&iommu_cb_set.cb_info[idx].smmu_buf_list)) { + CAM_ERR(CAM_SMMU, "UMD %s buffer list is not clean", + iommu_cb_set.cb_info[idx].name); + cam_smmu_print_user_list(idx); + cam_smmu_clean_user_buffer_list(idx); + } + + if (!list_empty_careful( + &iommu_cb_set.cb_info[idx].smmu_buf_kernel_list)) { + CAM_ERR(CAM_SMMU, "KMD %s buffer list is not clean", + iommu_cb_set.cb_info[idx].name); + cam_smmu_print_kernel_list(idx); + cam_smmu_clean_kernel_buffer_list(idx); + } + + if (iommu_cb_set.cb_info[idx].is_secure) { + if (iommu_cb_set.cb_info[idx].secure_count == 0) { + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EPERM; + } + + iommu_cb_set.cb_info[idx].secure_count--; + if (iommu_cb_set.cb_info[idx].secure_count == 0) { + iommu_cb_set.cb_info[idx].cb_count = 0; + iommu_cb_set.cb_info[idx].handle = HANDLE_INIT; + } + + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return 0; + } + + iommu_cb_set.cb_info[idx].cb_count = 0; + iommu_cb_set.cb_info[idx].handle = HANDLE_INIT; + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return 0; +} +EXPORT_SYMBOL(cam_smmu_destroy_handle); + +static void cam_smmu_deinit_cb(struct cam_context_bank_info *cb) +{ + if (cb->io_support && cb->domain) { + cb->domain = NULL; + } + + if (cb->shared_support) { + gen_pool_destroy(cb->shared_mem_pool); + cb->shared_mem_pool = NULL; + } + + if (cb->scratch_buf_support) { + kfree(cb->scratch_map.bitmap); + cb->scratch_map.bitmap = NULL; + } +} + +static void cam_smmu_release_cb(struct platform_device *pdev) +{ + int i = 0; + + for (i = 0; i < iommu_cb_set.cb_num; i++) + cam_smmu_deinit_cb(&iommu_cb_set.cb_info[i]); + + devm_kfree(&pdev->dev, iommu_cb_set.cb_info); + iommu_cb_set.cb_num = 0; +} + +static int cam_smmu_setup_cb(struct cam_context_bank_info *cb, + struct device *dev) +{ + int rc = 0; + + if (!cb || !dev) { + CAM_ERR(CAM_SMMU, "Error: invalid input params"); + return -EINVAL; + } + + cb->dev = dev; + cb->is_fw_allocated = false; + cb->is_secheap_allocated = false; + + /* Create a pool with 4K granularity for supporting shared memory */ + if (cb->shared_support) { + cb->shared_mem_pool = gen_pool_create( + SHARED_MEM_POOL_GRANULARITY, -1); + + if (!cb->shared_mem_pool) + return -ENOMEM; + + rc = gen_pool_add(cb->shared_mem_pool, + cb->shared_info.iova_start, + cb->shared_info.iova_len, + -1); + + CAM_DBG(CAM_SMMU, "Shared mem start->%lX", + (unsigned long)cb->shared_info.iova_start); + CAM_DBG(CAM_SMMU, "Shared mem len->%zu", + cb->shared_info.iova_len); + + if (rc) { + CAM_ERR(CAM_SMMU, "Genpool chunk creation failed"); + gen_pool_destroy(cb->shared_mem_pool); + cb->shared_mem_pool = NULL; + return rc; + } + } + + if (cb->scratch_buf_support) { + rc = cam_smmu_init_scratch_map(&cb->scratch_map, + cb->scratch_info.iova_start, + cb->scratch_info.iova_len, + 0); + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "Error: failed to create scratch map"); + rc = -ENODEV; + goto end; + } + } + + /* create a virtual mapping */ + if (cb->io_support) { + cb->domain = iommu_get_domain_for_dev(dev); + if (IS_ERR(cb->domain)) { + CAM_ERR(CAM_SMMU, "Error: create domain Failed"); + rc = -ENODEV; + goto end; + } + + iommu_dma_enable_best_fit_algo(dev); + + if (cb->discard_iova_start) + iommu_dma_reserve_iova(dev, cb->discard_iova_start, + cb->discard_iova_len); + + cb->state = CAM_SMMU_ATTACH; + } else { + CAM_ERR(CAM_SMMU, "Context bank does not have IO region"); + rc = -ENODEV; + goto end; + } + + return rc; +end: + if (cb->shared_support) { + gen_pool_destroy(cb->shared_mem_pool); + cb->shared_mem_pool = NULL; + } + + if (cb->scratch_buf_support) { + kfree(cb->scratch_map.bitmap); + cb->scratch_map.bitmap = NULL; + } + + return rc; +} + +static int cam_alloc_smmu_context_banks(struct device *dev) +{ + struct device_node *domains_child_node = NULL; + + if (!dev) { + CAM_ERR(CAM_SMMU, "Error: Invalid device"); + return -ENODEV; + } + + iommu_cb_set.cb_num = 0; + + /* traverse thru all the child nodes and increment the cb count */ + for_each_available_child_of_node(dev->of_node, domains_child_node) { + if (of_device_is_compatible(domains_child_node, + "qcom,msm-cam-smmu-cb")) + iommu_cb_set.cb_num++; + + if (of_device_is_compatible(domains_child_node, + "qcom,qsmmu-cam-cb")) + iommu_cb_set.cb_num++; + } + + if (iommu_cb_set.cb_num == 0) { + CAM_ERR(CAM_SMMU, "Error: no context banks present"); + return -ENOENT; + } + + /* allocate memory for the context banks */ + iommu_cb_set.cb_info = devm_kzalloc(dev, + iommu_cb_set.cb_num * sizeof(struct cam_context_bank_info), + GFP_KERNEL); + + if (!iommu_cb_set.cb_info) { + CAM_ERR(CAM_SMMU, "Error: cannot allocate context banks"); + return -ENOMEM; + } + + cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_INIT); + iommu_cb_set.cb_init_count = 0; + + CAM_DBG(CAM_SMMU, "no of context banks :%d", iommu_cb_set.cb_num); + return 0; +} + +static int cam_smmu_get_discard_memory_regions(struct device_node *of_node, + dma_addr_t *discard_iova_start, size_t *discard_iova_len) +{ + uint32_t discard_iova[2] = { 0 }; + int num_values = 0; + int rc = 0; + + if (!discard_iova_start || !discard_iova_len) + return -EINVAL; + + *discard_iova_start = 0; + *discard_iova_len = 0; + + num_values = of_property_count_u32_elems(of_node, + "iova-region-discard"); + if (num_values <= 0) { + CAM_DBG(CAM_UTIL, "No discard region specified"); + return 0; + } else if (num_values != 2) { + CAM_ERR(CAM_UTIL, "Invalid discard region specified %d", + num_values); + return -EINVAL; + } + + rc = of_property_read_u32_array(of_node, + "iova-region-discard", + discard_iova, num_values); + if (rc) { + CAM_ERR(CAM_UTIL, "Can not read discard region %d", num_values); + return rc; + } else if (!discard_iova[0] || !discard_iova[1]) { + CAM_ERR(CAM_UTIL, + "Incorrect Discard region specified [0x%x 0x%x]", + discard_iova[0], discard_iova[1]); + return -EINVAL; + } + + CAM_DBG(CAM_UTIL, "Discard region [0x%x 0x%x]", + discard_iova[0], discard_iova[0] + discard_iova[1]); + + *discard_iova_start = discard_iova[0]; + *discard_iova_len = discard_iova[1]; + + return 0; +} + +static int cam_smmu_get_memory_regions_info(struct device_node *of_node, + struct cam_context_bank_info *cb) +{ + int rc = 0; + struct device_node *mem_map_node = NULL; + struct device_node *child_node = NULL; + const char *region_name; + int num_regions = 0; + + if (!of_node || !cb) { + CAM_ERR(CAM_SMMU, "Invalid argument(s)"); + return -EINVAL; + } + + mem_map_node = of_get_child_by_name(of_node, "iova-mem-map"); + cb->is_secure = of_property_read_bool(of_node, "qcom,secure-cb"); + + /* + * We always expect a memory map node, except when it is a secure + * context bank. + */ + if (!mem_map_node) { + if (cb->is_secure) + return 0; + CAM_ERR(CAM_SMMU, "iova-mem-map not present"); + return -EINVAL; + } + + for_each_available_child_of_node(mem_map_node, child_node) { + uint32_t region_start; + uint32_t region_len; + uint32_t region_id; + uint32_t qdss_region_phy_addr = 0; + + num_regions++; + rc = of_property_read_string(child_node, + "iova-region-name", ®ion_name); + if (rc < 0) { + of_node_put(mem_map_node); + CAM_ERR(CAM_SMMU, "IOVA region not found"); + return -EINVAL; + } + + rc = of_property_read_u32(child_node, + "iova-region-start", ®ion_start); + if (rc < 0) { + of_node_put(mem_map_node); + CAM_ERR(CAM_SMMU, "Failed to read iova-region-start"); + return -EINVAL; + } + + rc = of_property_read_u32(child_node, + "iova-region-len", ®ion_len); + if (rc < 0) { + of_node_put(mem_map_node); + CAM_ERR(CAM_SMMU, "Failed to read iova-region-len"); + return -EINVAL; + } + + rc = of_property_read_u32(child_node, + "iova-region-id", ®ion_id); + if (rc < 0) { + of_node_put(mem_map_node); + CAM_ERR(CAM_SMMU, "Failed to read iova-region-id"); + return -EINVAL; + } + + if (strcmp(region_name, qdss_region_name) == 0) { + rc = of_property_read_u32(child_node, + "qdss-phy-addr", &qdss_region_phy_addr); + if (rc < 0) { + of_node_put(mem_map_node); + CAM_ERR(CAM_SMMU, + "Failed to read qdss phy addr"); + return -EINVAL; + } + } + + switch (region_id) { + case CAM_SMMU_REGION_FIRMWARE: + cb->firmware_support = 1; + cb->firmware_info.iova_start = region_start; + cb->firmware_info.iova_len = region_len; + break; + case CAM_SMMU_REGION_SHARED: + cb->shared_support = 1; + cb->shared_info.iova_start = region_start; + cb->shared_info.iova_len = region_len; + break; + case CAM_SMMU_REGION_SCRATCH: + cb->scratch_buf_support = 1; + cb->scratch_info.iova_start = region_start; + cb->scratch_info.iova_len = region_len; + break; + case CAM_SMMU_REGION_IO: + cb->io_support = 1; + cb->io_info.iova_start = region_start; + cb->io_info.iova_len = region_len; + rc = cam_smmu_get_discard_memory_regions(child_node, + &cb->io_info.discard_iova_start, + &cb->io_info.discard_iova_len); + if (rc) { + CAM_ERR(CAM_SMMU, + "Invalid Discard region specified in IO region, rc=%d", + rc); + of_node_put(mem_map_node); + return -EINVAL; + } + break; + case CAM_SMMU_REGION_SECHEAP: + cb->secheap_support = 1; + cb->secheap_info.iova_start = region_start; + cb->secheap_info.iova_len = region_len; + break; + case CAM_SMMU_REGION_QDSS: + cb->qdss_support = 1; + cb->qdss_info.iova_start = region_start; + cb->qdss_info.iova_len = region_len; + cb->qdss_phy_addr = qdss_region_phy_addr; + break; + default: + CAM_ERR(CAM_SMMU, + "Incorrect region id present in DT file: %d", + region_id); + } + + CAM_DBG(CAM_SMMU, "Found label -> %s", cb->name); + CAM_DBG(CAM_SMMU, "Found region -> %s", region_name); + CAM_DBG(CAM_SMMU, "region_start -> %X", region_start); + CAM_DBG(CAM_SMMU, "region_len -> %X", region_len); + CAM_DBG(CAM_SMMU, "region_id -> %X", region_id); + } + + if (cb->io_support) { + rc = cam_smmu_get_discard_memory_regions(of_node, + &cb->discard_iova_start, + &cb->discard_iova_len); + if (rc) { + CAM_ERR(CAM_SMMU, + "Invalid Discard region specified in CB, rc=%d", + rc); + of_node_put(mem_map_node); + return -EINVAL; + } + + /* Make sure Discard region is properly specified */ + if ((cb->discard_iova_start != + cb->io_info.discard_iova_start) || + (cb->discard_iova_len != + cb->io_info.discard_iova_len)) { + CAM_ERR(CAM_SMMU, + "Mismatch Discard region specified, [0x%x 0x%x] [0x%x 0x%x]", + cb->discard_iova_start, + cb->discard_iova_len, + cb->io_info.discard_iova_start, + cb->io_info.discard_iova_len); + of_node_put(mem_map_node); + return -EINVAL; + } else if (cb->discard_iova_start && cb->discard_iova_len) { + if ((cb->discard_iova_start <= + cb->io_info.iova_start) || + (cb->discard_iova_start >= + cb->io_info.iova_start + cb->io_info.iova_len) || + (cb->discard_iova_start + cb->discard_iova_len >= + cb->io_info.iova_start + cb->io_info.iova_len)) { + CAM_ERR(CAM_SMMU, + "[%s] : Incorrect Discard region specified [0x%x 0x%x] in [0x%x 0x%x]", + cb->name, + cb->discard_iova_start, + cb->discard_iova_start + cb->discard_iova_len, + cb->io_info.iova_start, + cb->io_info.iova_start + cb->io_info.iova_len); + of_node_put(mem_map_node); + return -EINVAL; + } + + CAM_INFO(CAM_SMMU, + "[%s] : Discard region specified [0x%x 0x%x] in [0x%x 0x%x]", + cb->name, + cb->discard_iova_start, + cb->discard_iova_start + cb->discard_iova_len, + cb->io_info.iova_start, + cb->io_info.iova_start + cb->io_info.iova_len); + } + } + + of_node_put(mem_map_node); + + if (!num_regions) { + CAM_ERR(CAM_SMMU, + "No memory regions found, at least one needed"); + rc = -ENODEV; + } + + return rc; +} + +static int cam_populate_smmu_context_banks(struct device *dev, + enum cam_iommu_type type) +{ + int rc = 0; + struct cam_context_bank_info *cb; + struct device *ctx = NULL; + + if (!dev) { + CAM_ERR(CAM_SMMU, "Error: Invalid device"); + return -ENODEV; + } + + /* check the bounds */ + if (iommu_cb_set.cb_init_count >= iommu_cb_set.cb_num) { + CAM_ERR(CAM_SMMU, "Error: populate more than allocated cb"); + rc = -EBADHANDLE; + goto cb_init_fail; + } + + /* read the context bank from cb set */ + cb = &iommu_cb_set.cb_info[iommu_cb_set.cb_init_count]; + + /* set the name of the context bank */ + rc = of_property_read_string(dev->of_node, "label", &cb->name); + if (rc < 0) { + CAM_ERR(CAM_SMMU, + "Error: failed to read label from sub device"); + goto cb_init_fail; + } + + rc = cam_smmu_get_memory_regions_info(dev->of_node, + cb); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: Getting region info"); + return rc; + } + + if (cb->is_secure) { + /* increment count to next bank */ + cb->dev = dev; + iommu_cb_set.cb_init_count++; + return 0; + } + + /* set up the iommu mapping for the context bank */ + if (type == CAM_QSMMU) { + CAM_ERR(CAM_SMMU, "Error: QSMMU ctx not supported for : %s", + cb->name); + return -ENODEV; + } + + ctx = dev; + CAM_DBG(CAM_SMMU, "getting Arm SMMU ctx : %s", cb->name); + + rc = cam_smmu_setup_cb(cb, ctx); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: failed to setup cb : %s", cb->name); + goto cb_init_fail; + } + if (cb->io_support && cb->domain) + iommu_set_fault_handler(cb->domain, + cam_smmu_iommu_fault_handler, + (void *)cb->name); + + if (!dev->dma_parms) + dev->dma_parms = devm_kzalloc(dev, + sizeof(*dev->dma_parms), GFP_KERNEL); + + if (!dev->dma_parms) { + CAM_WARN(CAM_SMMU, + "Failed to allocate dma_params"); + dev->dma_parms = NULL; + goto end; + } + + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + dma_set_seg_boundary(dev, (unsigned long)DMA_BIT_MASK(64)); + +end: + /* increment count to next bank */ + iommu_cb_set.cb_init_count++; + CAM_DBG(CAM_SMMU, "X: cb init count :%d", iommu_cb_set.cb_init_count); + +cb_init_fail: + return rc; +} + +static int cam_smmu_create_debug_fs(void) +{ + iommu_cb_set.dentry = debugfs_create_dir("camera_smmu", + NULL); + + if (!iommu_cb_set.dentry) { + CAM_ERR(CAM_SMMU, "failed to create dentry"); + return -ENOMEM; + } + + if (!debugfs_create_bool("cb_dump_enable", + 0644, + iommu_cb_set.dentry, + &iommu_cb_set.cb_dump_enable)) { + CAM_ERR(CAM_SMMU, + "failed to create dump_enable_debug"); + goto err; + } + + return 0; +err: + debugfs_remove_recursive(iommu_cb_set.dentry); + return -ENOMEM; +} + +static int cam_smmu_probe(struct platform_device *pdev) +{ + int rc = 0; + struct device *dev = &pdev->dev; + + dev->dma_parms = NULL; + if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu")) { + rc = cam_alloc_smmu_context_banks(dev); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: allocating context banks"); + return -ENOMEM; + } + } + if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu-cb")) { + rc = cam_populate_smmu_context_banks(dev, CAM_ARM_SMMU); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: populating context banks"); + cam_smmu_release_cb(pdev); + return -ENOMEM; + } + return rc; + } + if (of_device_is_compatible(dev->of_node, "qcom,qsmmu-cam-cb")) { + rc = cam_populate_smmu_context_banks(dev, CAM_QSMMU); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: populating context banks"); + return -ENOMEM; + } + return rc; + } + + if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu-fw-dev")) { + icp_fw.fw_dev = &pdev->dev; + icp_fw.fw_kva = NULL; + icp_fw.fw_dma_hdl = 0; + return rc; + } + + /* probe through all the subdevices */ + rc = of_platform_populate(pdev->dev.of_node, msm_cam_smmu_dt_match, + NULL, &pdev->dev); + if (rc < 0) { + CAM_ERR(CAM_SMMU, "Error: populating devices"); + } else { + INIT_WORK(&iommu_cb_set.smmu_work, cam_smmu_page_fault_work); + mutex_init(&iommu_cb_set.payload_list_lock); + INIT_LIST_HEAD(&iommu_cb_set.payload_list); + } + + cam_smmu_create_debug_fs(); + return rc; +} + +static int cam_smmu_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* release all the context banks and memory allocated */ + cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_DEINIT); + if (dev && dev->dma_parms) { + devm_kfree(dev, dev->dma_parms); + dev->dma_parms = NULL; + } + + if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-cam-smmu")) + cam_smmu_release_cb(pdev); + + debugfs_remove_recursive(iommu_cb_set.dentry); + iommu_cb_set.dentry = NULL; + return 0; +} + +static struct platform_driver cam_smmu_driver = { + .probe = cam_smmu_probe, + .remove = cam_smmu_remove, + .driver = { + .name = "msm_cam_smmu", + .owner = THIS_MODULE, + .of_match_table = msm_cam_smmu_dt_match, + }, +}; + +static int __init cam_smmu_init_module(void) +{ + return platform_driver_register(&cam_smmu_driver); +} + +static void __exit cam_smmu_exit_module(void) +{ + platform_driver_unregister(&cam_smmu_driver); +} + +module_init(cam_smmu_init_module); +module_exit(cam_smmu_exit_module); +MODULE_DESCRIPTION("MSM Camera SMMU driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.h b/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.h new file mode 100644 index 000000000000..15671e7cd12a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_smmu/cam_smmu_api.h @@ -0,0 +1,410 @@ +/* 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 + * 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 _CAM_SMMU_API_H_ +#define _CAM_SMMU_API_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*Enum for possible CAM SMMU operations */ +enum cam_smmu_ops_param { + CAM_SMMU_ATTACH, + CAM_SMMU_DETACH, + CAM_SMMU_VOTE, + CAM_SMMU_DEVOTE, + CAM_SMMU_OPS_INVALID +}; + +enum cam_smmu_map_dir { + CAM_SMMU_MAP_READ, + CAM_SMMU_MAP_WRITE, + CAM_SMMU_MAP_RW, + CAM_SMMU_MAP_INVALID +}; + +enum cam_smmu_region_id { + CAM_SMMU_REGION_FIRMWARE, + CAM_SMMU_REGION_SHARED, + CAM_SMMU_REGION_SCRATCH, + CAM_SMMU_REGION_IO, + CAM_SMMU_REGION_SECHEAP, + CAM_SMMU_REGION_QDSS +}; + +/** + * @brief : Callback function type that gets called back on cam + * smmu page fault. + * + * @param domain : Iommu domain received in iommu page fault handler + * @param dev : Device received in iommu page fault handler + * @param iova : IOVA where page fault occurred + * @param flags : Flags received in iommu page fault handler + * @param token : Userdata given during callback registration + * @param buf_info : Closest mapped buffer info + */ +typedef void (*cam_smmu_client_page_fault_handler)(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags, void *token, + uint32_t buf_info); + +/** + * @brief : Structure to store region information + * + * @param iova_start : Start address of region + * @param iova_len : length of region + * @param discard_iova_start : iova addr start from where should not be used + * @param discard_iova_len : length of discard iova region + */ +struct cam_smmu_region_info { + dma_addr_t iova_start; + size_t iova_len; + dma_addr_t discard_iova_start; + size_t discard_iova_len; +}; + +/** + * @brief : Gets an smmu handle + * + * @param identifier: Unique identifier to be used by clients which they + * should get from device tree. CAM SMMU driver will + * not enforce how this string is obtained and will + * only validate this against the list of permitted + * identifiers + * @param handle_ptr: Based on the indentifier, CAM SMMU drivier will + * fill the handle pointed by handle_ptr + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_get_handle(char *identifier, int *handle_ptr); + +/** + * @brief : Performs IOMMU operations + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param op : Operation to be performed. Can be either CAM_SMMU_ATTACH + * or CAM_SMMU_DETACH + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_ops(int handle, enum cam_smmu_ops_param op); + +/** + * @brief : Maps user space IOVA for calling driver + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param ion_fd: ION handle identifying the memory buffer. + * @param dis_delayed_unmap: Whether to disable Delayed Unmap feature + * for this mapping + * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL, + * DMA_TO_DEVICE or DMA_FROM_DEVICE + * @dma_addr : Pointer to physical address where mapped address will be + * returned if region_id is CAM_SMMU_REGION_IO. If region_id is + * CAM_SMMU_REGION_SHARED, dma_addr is used as an input parameter + * which specifies the cpu virtual address to map. + * @len_ptr : Length of buffer mapped returned by CAM SMMU driver. + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_map_user_iova(int handle, int ion_fd, bool dis_delayed_unmap, + enum cam_smmu_map_dir dir, dma_addr_t *dma_addr, size_t *len_ptr, + enum cam_smmu_region_id region_id); + +/** + * @brief : Maps kernel space IOVA for calling driver + * + * @param handle : Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param buf : dma_buf allocated for kernel usage in mem_mgr + * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL, + * DMA_TO_DEVICE or DMA_FROM_DEVICE + * @dma_addr : Pointer to physical address where mapped address will be + * returned if region_id is CAM_SMMU_REGION_IO. If region_id is + * CAM_SMMU_REGION_SHARED, dma_addr is used as an input + * parameter which specifies the cpu virtual address to map. + * @len_ptr : Length of buffer mapped returned by CAM SMMU driver. + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_map_kernel_iova(int handle, + struct dma_buf *buf, enum cam_smmu_map_dir dir, + dma_addr_t *dma_addr, size_t *len_ptr, + enum cam_smmu_region_id region_id); + +/** + * @brief : Unmaps user space IOVA for calling driver + * + * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.) + * @param ion_fd: ION handle identifying the memory buffer. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_unmap_user_iova(int handle, + int ion_fd, enum cam_smmu_region_id region_id); + +/** + * @brief : Unmaps kernel IOVA for calling driver + * + * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.) + * @param buf : dma_buf allocated for the kernel + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_unmap_kernel_iova(int handle, + struct dma_buf *buf, enum cam_smmu_region_id region_id); + +/** + * @brief : Allocates a scratch buffer + * + * This function allocates a scratch virtual buffer of length virt_len in the + * device virtual address space mapped to phys_len physically contiguous bytes + * in that device's SMMU. + * + * virt_len and phys_len are expected to be aligned to PAGE_SIZE and with each + * other, otherwise -EINVAL is returned. + * + * -EINVAL will be returned if virt_len is less than phys_len. + * + * Passing a too large phys_len might also cause failure if that much size is + * not available for allocation in a physically contiguous way. + * + * @param handle : Handle to identify the CAMSMMU client (VFE, CPP, FD etc.) + * @param dir : Direction of mapping which will translate to IOMMU_READ + * IOMMU_WRITE or a bit mask of both. + * @param paddr_ptr: Device virtual address that the client device will be + * able to read from/write to + * @param virt_len : Virtual length of the scratch buffer + * @param phys_len : Physical length of the scratch buffer + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ + +int cam_smmu_get_scratch_iova(int handle, + enum cam_smmu_map_dir dir, + dma_addr_t *paddr_ptr, + size_t virt_len, + size_t phys_len); + +/** + * @brief : Frees a scratch buffer + * + * This function frees a scratch buffer and releases the corresponding SMMU + * mappings. + * + * @param handle : Handle to identify the CAMSMMU client (IFE, ICP, etc.) + * @param paddr : Device virtual address of client's scratch buffer that + * will be freed. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ + +int cam_smmu_put_scratch_iova(int handle, + dma_addr_t paddr); + +/** + *@brief : Destroys an smmu handle + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_destroy_handle(int handle); + +/** + * @brief : Finds index by handle in the smmu client table + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @return Index of SMMU client. Nagative in case of error. + */ +int cam_smmu_find_index_by_handle(int hdl); + +/** + * @brief : Registers smmu fault handler for client + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param handler_cb: It is triggered in IOMMU page fault + * @param token: It is input param when trigger page fault handler + */ +void cam_smmu_set_client_page_fault_handler(int handle, + cam_smmu_client_page_fault_handler handler_cb, void *token); + +/** + * @brief : Unregisters smmu fault handler for client + * + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param token: It is input param when trigger page fault handler + */ +void cam_smmu_unset_client_page_fault_handler(int handle, void *token); + +/** + * @brief Maps memory from an ION fd into IOVA space + * + * @param handle: SMMU handle identifying the context bank to map to + * @param ion_fd: ION fd of memory to map to + * @param paddr_ptr: Pointer IOVA address that will be returned + * @param len_ptr: Length of memory mapped + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_get_iova(int handle, int ion_fd, + dma_addr_t *paddr_ptr, size_t *len_ptr); + +/** + * @brief Maps memory from an ION fd into IOVA space + * + * @param handle: SMMU handle identifying the secure context bank to map to + * @param ion_fd: ION fd of memory to map to + * @param paddr_ptr: Pointer IOVA address that will be returned + * @param len_ptr: Length of memory mapped + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_get_stage2_iova(int handle, int ion_fd, + dma_addr_t *paddr_ptr, size_t *len_ptr); + +/** + * @brief Unmaps memory from context bank + * + * @param handle: SMMU handle identifying the context bank + * @param ion_fd: ION fd of memory to unmap + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_put_iova(int handle, int ion_fd); + +/** + * @brief Maps secure memory for SMMU handle + * + * @param handle: SMMU handle identifying secure context bank + * @param ion_fd: ION fd to map securely + * @param dir: DMA Direction for the mapping + * @param dma_addr: Returned IOVA address after mapping + * @param len_ptr: Length of memory mapped + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_map_stage2_iova(int handle, + int ion_fd, enum cam_smmu_map_dir dir, dma_addr_t *dma_addr, + size_t *len_ptr); + +/** + * @brief Unmaps secure memopry for SMMU handle + * + * @param handle: SMMU handle identifying secure context bank + * @param ion_fd: ION fd to unmap + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_unmap_stage2_iova(int handle, int ion_fd); + +/** + * @brief Allocates firmware for context bank + * + * @param smmu_hdl: SMMU handle identifying context bank + * @param iova: IOVA address of allocated firmware + * @param kvaddr: CPU mapped address of allocated firmware + * @param len: Length of allocated firmware memory + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_alloc_firmware(int32_t smmu_hdl, + dma_addr_t *iova, + uintptr_t *kvaddr, + size_t *len); + +/** + * @brief Deallocates firmware memory for context bank + * + * @param smmu_hdl: SMMU handle identifying the context bank + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_dealloc_firmware(int32_t smmu_hdl); + +/** + * @brief Gets region information specified by smmu handle and region id + * + * @param smmu_hdl: SMMU handle identifying the context bank + * @param region_id: Region id for which information is desired + * @param region_info: Struct populated with region information + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_get_region_info(int32_t smmu_hdl, + enum cam_smmu_region_id region_id, + struct cam_smmu_region_info *region_info); + +/** + * @brief Reserves secondary heap + * + * @param smmu_hdl: SMMU handle identifying the context bank + * @param iova: IOVA of secondary heap after reservation has completed + * @param buf: Allocated dma_buf for secondary heap + * @param request_len: Length of secondary heap after reservation has completed + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_reserve_sec_heap(int32_t smmu_hdl, + struct dma_buf *buf, + dma_addr_t *iova, + size_t *request_len); + +/** + * @brief Releases secondary heap + * + * @param smmu_hdl: SMMU handle identifying the context bank + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_release_sec_heap(int32_t smmu_hdl); + +/** + * @brief Allocates qdss for context bank + * + * @param smmu_hdl: SMMU handle identifying context bank + * @param iova: IOVA address of allocated qdss + * @param len: Length of allocated qdss memory + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_alloc_qdss(int32_t smmu_hdl, + dma_addr_t *iova, + size_t *len); + +/** + * @brief Deallocates qdss memory for context bank + * + * @param smmu_hdl: SMMU handle identifying the context bank + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_dealloc_qdss(int32_t smmu_hdl); + +/** + * @brief Get start addr & len of I/O region for a given cb + * + * @param smmu_hdl: SMMU handle identifying the context bank + * @param iova: IOVA address of allocated I/O region + * @param len: Length of allocated I/O memory + * @param discard_iova_start: Start address of io space to discard + * @param discard_iova_len: Length of io space to discard + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_get_io_region_info(int32_t smmu_hdl, + dma_addr_t *iova, size_t *len, + dma_addr_t *discard_iova_start, size_t *discard_iova_len); + +#endif /* _CAM_SMMU_API_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_sync/Makefile new file mode 100644 index 000000000000..f3813fd63d5a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_utils + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_sync.o cam_sync_util.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync.c new file mode 100644 index 000000000000..e5b83c302757 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync.c @@ -0,0 +1,1120 @@ +/* Copyright (c) 2017-2018, 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 +#include +#include +#include +#include +#include +#include "cam_sync_util.h" +#include "cam_debug_util.h" + +struct sync_device *sync_dev; + +int cam_sync_create(int32_t *sync_obj, const char *name) +{ + int rc; + long idx; + bool bit; + + do { + idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS); + if (idx >= CAM_SYNC_MAX_OBJS) + return -ENOMEM; + CAM_DBG(CAM_SYNC, "Index location available at idx: %ld", idx); + bit = test_and_set_bit(idx, sync_dev->bitmap); + } while (bit); + + spin_lock_bh(&sync_dev->row_spinlocks[idx]); + rc = cam_sync_init_object(sync_dev->sync_table, idx, name); + if (rc) { + CAM_ERR(CAM_SYNC, "Error: Unable to init row at idx = %ld", + idx); + clear_bit(idx, sync_dev->bitmap); + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + return -EINVAL; + } + + *sync_obj = idx; + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + + return rc; +} + +int cam_sync_register_callback(sync_callback cb_func, + void *userdata, int32_t sync_obj) +{ + struct sync_callback_info *sync_cb; + struct sync_callback_info *cb_info; + struct sync_table_row *row = NULL; + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0 || !cb_func) + return -EINVAL; + + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]); + row = sync_dev->sync_table + sync_obj; + + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj %d", + sync_obj); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return -EINVAL; + } + + /* Don't register if callback was registered earlier */ + list_for_each_entry(cb_info, &row->callback_list, list) { + if (cb_info->callback_func == cb_func && + cb_info->cb_data == userdata) { + CAM_ERR(CAM_SYNC, "Duplicate register for sync_obj %d", + sync_obj); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return -EALREADY; + } + } + + sync_cb = kzalloc(sizeof(*sync_cb), GFP_ATOMIC); + if (!sync_cb) { + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return -ENOMEM; + } + + /* Trigger callback if sync object is already in SIGNALED state */ + if ((row->state == CAM_SYNC_STATE_SIGNALED_SUCCESS || + row->state == CAM_SYNC_STATE_SIGNALED_ERROR) && + (!row->remaining)) { + sync_cb->callback_func = cb_func; + sync_cb->cb_data = userdata; + sync_cb->sync_obj = sync_obj; + INIT_WORK(&sync_cb->cb_dispatch_work, + cam_sync_util_cb_dispatch); + sync_cb->status = row->state; + CAM_DBG(CAM_SYNC, "Callback trigger for sync object:%d", + sync_cb->sync_obj); + queue_work(sync_dev->work_queue, + &sync_cb->cb_dispatch_work); + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return 0; + } + + sync_cb->callback_func = cb_func; + sync_cb->cb_data = userdata; + sync_cb->sync_obj = sync_obj; + INIT_WORK(&sync_cb->cb_dispatch_work, cam_sync_util_cb_dispatch); + list_add_tail(&sync_cb->list, &row->callback_list); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + + return 0; +} + +int cam_sync_deregister_callback(sync_callback cb_func, + void *userdata, int32_t sync_obj) +{ + struct sync_table_row *row = NULL; + struct sync_callback_info *sync_cb, *temp; + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]); + row = sync_dev->sync_table + sync_obj; + + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return -EINVAL; + } + + CAM_DBG(CAM_SYNC, "deregistered callback for sync object:%d", + sync_obj); + list_for_each_entry_safe(sync_cb, temp, &row->callback_list, list) { + if (sync_cb->callback_func == cb_func && + sync_cb->cb_data == userdata) { + list_del_init(&sync_cb->list); + kfree(sync_cb); + } + } + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return 0; +} + +int cam_sync_signal(int32_t sync_obj, uint32_t status) +{ + int rc; + struct sync_table_row *row = NULL; + struct sync_table_row *parent_row = NULL; + struct sync_callback_info *sync_cb; + struct sync_user_payload *payload_info; + struct sync_parent_info *parent_info; + struct list_head sync_list; + struct cam_signalable_info *list_info = NULL; + struct cam_signalable_info *temp_list_info = NULL; + + /* Objects to be signaled will be added into this list */ + INIT_LIST_HEAD(&sync_list); + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) { + CAM_ERR(CAM_SYNC, "Error: Out of range sync obj"); + return -EINVAL; + } + row = sync_dev->sync_table + sync_obj; + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + return -EINVAL; + } + + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]); + if (row->type == CAM_SYNC_TYPE_GROUP) { + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + CAM_ERR(CAM_SYNC, "Error: Signaling a GROUP sync object = %d", + sync_obj); + return -EINVAL; + } + + if (row->state != CAM_SYNC_STATE_ACTIVE) { + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + CAM_ERR(CAM_SYNC, + "Error: Sync object already signaled sync_obj = %d", + sync_obj); + return -EALREADY; + } + + if (status != CAM_SYNC_STATE_SIGNALED_SUCCESS && + status != CAM_SYNC_STATE_SIGNALED_ERROR) { + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + CAM_ERR(CAM_SYNC, + "Error: signaling with undefined status = %d", + status); + return -EINVAL; + } + + row->state = status; + rc = cam_sync_util_add_to_signalable_list(sync_obj, status, &sync_list); + if (rc < 0) { + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + CAM_ERR(CAM_SYNC, + "Error: Unable to add sync object :%d to signalable list", + sync_obj); + return rc; + } + + /* + * Now iterate over all parents of this object and if they too need to + * be signaled add them to the list + */ + list_for_each_entry(parent_info, + &row->parents_list, + list) { + parent_row = sync_dev->sync_table + parent_info->sync_id; + spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]); + parent_row->remaining--; + + parent_row->state = cam_sync_util_get_state( + parent_row->state, + status); + + if (!parent_row->remaining) { + rc = cam_sync_util_add_to_signalable_list + (parent_info->sync_id, + parent_row->state, + &sync_list); + if (rc < 0) { + spin_unlock_bh( + &sync_dev->row_spinlocks[ + parent_info->sync_id]); + spin_unlock_bh( + &sync_dev->row_spinlocks[sync_obj]); + return rc; + } + } + spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]); + } + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + + /* + * Now dispatch the various sync objects collected so far, in our + * list + */ + list_for_each_entry_safe(list_info, + temp_list_info, + &sync_list, + list) { + struct sync_table_row *signalable_row = NULL; + struct sync_callback_info *temp_sync_cb; + struct sync_user_payload *temp_payload_info; + + signalable_row = sync_dev->sync_table + list_info->sync_obj; + + spin_lock_bh(&sync_dev->row_spinlocks[list_info->sync_obj]); + if (signalable_row->state == CAM_SYNC_STATE_INVALID) { + spin_unlock_bh( + &sync_dev->row_spinlocks[list_info->sync_obj]); + continue; + } + + /* Dispatch kernel callbacks if any were registered earlier */ + + list_for_each_entry_safe(sync_cb, + temp_sync_cb, &signalable_row->callback_list, list) { + sync_cb->status = list_info->status; + list_del_init(&sync_cb->list); + queue_work(sync_dev->work_queue, + &sync_cb->cb_dispatch_work); + } + + /* Dispatch user payloads if any were registered earlier */ + list_for_each_entry_safe(payload_info, temp_payload_info, + &signalable_row->user_payload_list, list) { + spin_lock_bh(&sync_dev->cam_sync_eventq_lock); + if (!sync_dev->cam_sync_eventq) { + spin_unlock_bh( + &sync_dev->cam_sync_eventq_lock); + break; + } + spin_unlock_bh(&sync_dev->cam_sync_eventq_lock); + cam_sync_util_send_v4l2_event( + CAM_SYNC_V4L_EVENT_ID_CB_TRIG, + list_info->sync_obj, + list_info->status, + payload_info->payload_data, + CAM_SYNC_PAYLOAD_WORDS * sizeof(__u64)); + + list_del_init(&payload_info->list); + /* + * We can free the list node here because + * sending V4L event will make a deep copy + * anyway + */ + kfree(payload_info); + } + + /* + * This needs to be done because we want to unblock anyone + * who might be blocked and waiting on this sync object + */ + complete_all(&signalable_row->signaled); + + spin_unlock_bh(&sync_dev->row_spinlocks[list_info->sync_obj]); + + list_del_init(&list_info->list); + kfree(list_info); + } + + return rc; +} + +int cam_sync_merge(int32_t *sync_obj, uint32_t num_objs, int32_t *merged_obj) +{ + int rc; + long idx = 0; + bool bit; + + if (!sync_obj || !merged_obj) { + CAM_ERR(CAM_SYNC, "Invalid pointer(s)"); + return -EINVAL; + } + + rc = cam_sync_util_validate_merge(sync_obj, + num_objs); + if (rc < 0) { + CAM_ERR(CAM_SYNC, "Validation failed, Merge not allowed"); + return -EINVAL; + } + + do { + idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS); + if (idx >= CAM_SYNC_MAX_OBJS) + return -ENOMEM; + bit = test_and_set_bit(idx, sync_dev->bitmap); + } while (bit); + + spin_lock_bh(&sync_dev->row_spinlocks[idx]); + + rc = cam_sync_init_group_object(sync_dev->sync_table, + idx, sync_obj, + num_objs); + if (rc < 0) { + CAM_ERR(CAM_SYNC, "Error: Unable to init row at idx = %ld", + idx); + clear_bit(idx, sync_dev->bitmap); + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + return -EINVAL; + } + CAM_DBG(CAM_SYNC, "Init row at idx:%ld to merge objects", idx); + *merged_obj = idx; + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + + return 0; +} + +int cam_sync_get_obj_ref(int32_t sync_obj) +{ + struct sync_table_row *row = NULL; + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + row = sync_dev->sync_table + sync_obj; + + spin_lock(&sync_dev->row_spinlocks[sync_obj]); + + if (row->state != CAM_SYNC_STATE_ACTIVE) { + spin_unlock(&sync_dev->row_spinlocks[sync_obj]); + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + return -EINVAL; + } + + atomic_inc(&row->ref_cnt); + spin_unlock(&sync_dev->row_spinlocks[sync_obj]); + CAM_DBG(CAM_SYNC, "get ref for obj %d", sync_obj); + + return 0; +} + +int cam_sync_put_obj_ref(int32_t sync_obj) +{ + struct sync_table_row *row = NULL; + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + row = sync_dev->sync_table + sync_obj; + atomic_dec(&row->ref_cnt); + CAM_DBG(CAM_SYNC, "put ref for obj %d", sync_obj); + + return 0; +} + +int cam_sync_destroy(int32_t sync_obj) +{ + CAM_DBG(CAM_SYNC, "sync_obj: %i", sync_obj); + return cam_sync_deinit_object(sync_dev->sync_table, sync_obj); +} + +int cam_sync_wait(int32_t sync_obj, uint64_t timeout_ms) +{ + unsigned long timeleft; + int rc = -EINVAL; + struct sync_table_row *row = NULL; + + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + row = sync_dev->sync_table + sync_obj; + + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + return -EINVAL; + } + + timeleft = wait_for_completion_timeout(&row->signaled, + msecs_to_jiffies(timeout_ms)); + + if (!timeleft) { + CAM_ERR(CAM_SYNC, + "Error: timed out for sync obj = %d", sync_obj); + rc = -ETIMEDOUT; + } else { + switch (row->state) { + case CAM_SYNC_STATE_INVALID: + case CAM_SYNC_STATE_ACTIVE: + case CAM_SYNC_STATE_SIGNALED_ERROR: + CAM_ERR(CAM_SYNC, + "Error: Wait on invalid state = %d, obj = %d", + row->state, sync_obj); + rc = -EINVAL; + break; + case CAM_SYNC_STATE_SIGNALED_SUCCESS: + rc = 0; + break; + default: + rc = -EINVAL; + break; + } + } + + return rc; +} + +static int cam_sync_handle_create(struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_info sync_create; + int result; + + if (k_ioctl->size != sizeof(struct cam_sync_info)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&sync_create, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + result = cam_sync_create(&sync_create.sync_obj, + sync_create.name); + + if (!result) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->ioctl_ptr), + &sync_create, + k_ioctl->size)) + return -EFAULT; + + return result; +} + +static int cam_sync_handle_signal(struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_signal sync_signal; + + if (k_ioctl->size != sizeof(struct cam_sync_signal)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&sync_signal, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + return cam_sync_signal(sync_signal.sync_obj, + sync_signal.sync_state); +} + +static int cam_sync_handle_merge(struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_merge sync_merge; + uint32_t *sync_objs; + uint32_t num_objs; + uint32_t size; + int result; + + if (k_ioctl->size != sizeof(struct cam_sync_merge)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&sync_merge, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + if (sync_merge.num_objs >= CAM_SYNC_MAX_OBJS) + return -EINVAL; + + size = sizeof(uint32_t) * sync_merge.num_objs; + sync_objs = kzalloc(size, GFP_ATOMIC); + + if (!sync_objs) + return -ENOMEM; + + if (copy_from_user(sync_objs, + u64_to_user_ptr(sync_merge.sync_objs), + sizeof(uint32_t) * sync_merge.num_objs)) { + kfree(sync_objs); + return -EFAULT; + } + + num_objs = sync_merge.num_objs; + + result = cam_sync_merge(sync_objs, + num_objs, + &sync_merge.merged); + + if (!result) + if (copy_to_user( + u64_to_user_ptr(k_ioctl->ioctl_ptr), + &sync_merge, + k_ioctl->size)) { + kfree(sync_objs); + return -EFAULT; + } + + kfree(sync_objs); + + return result; +} + +static int cam_sync_handle_wait(struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_wait sync_wait; + + if (k_ioctl->size != sizeof(struct cam_sync_wait)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&sync_wait, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + k_ioctl->result = cam_sync_wait(sync_wait.sync_obj, + sync_wait.timeout_ms); + + return 0; +} + +static int cam_sync_handle_destroy(struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_info sync_create; + + if (k_ioctl->size != sizeof(struct cam_sync_info)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&sync_create, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + return cam_sync_destroy(sync_create.sync_obj); +} + +static int cam_sync_handle_register_user_payload( + struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_userpayload_info userpayload_info; + struct sync_user_payload *user_payload_kernel; + struct sync_user_payload *user_payload_iter; + struct sync_user_payload *temp_upayload_kernel; + uint32_t sync_obj; + struct sync_table_row *row = NULL; + + if (k_ioctl->size != sizeof(struct cam_sync_userpayload_info)) + return -EINVAL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + + if (copy_from_user(&userpayload_info, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + sync_obj = userpayload_info.sync_obj; + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + user_payload_kernel = kzalloc(sizeof(*user_payload_kernel), GFP_KERNEL); + if (!user_payload_kernel) + return -ENOMEM; + + memcpy(user_payload_kernel->payload_data, + userpayload_info.payload, + CAM_SYNC_PAYLOAD_WORDS * sizeof(__u64)); + + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]); + row = sync_dev->sync_table + sync_obj; + + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + kfree(user_payload_kernel); + return -EINVAL; + } + + if (row->state == CAM_SYNC_STATE_SIGNALED_SUCCESS || + row->state == CAM_SYNC_STATE_SIGNALED_ERROR) { + + cam_sync_util_send_v4l2_event(CAM_SYNC_V4L_EVENT_ID_CB_TRIG, + sync_obj, + row->state, + user_payload_kernel->payload_data, + CAM_SYNC_USER_PAYLOAD_SIZE * sizeof(__u64)); + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + kfree(user_payload_kernel); + return 0; + } + + list_for_each_entry_safe(user_payload_iter, + temp_upayload_kernel, + &row->user_payload_list, + list) { + if (user_payload_iter->payload_data[0] == + user_payload_kernel->payload_data[0] && + user_payload_iter->payload_data[1] == + user_payload_kernel->payload_data[1]) { + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + kfree(user_payload_kernel); + return -EALREADY; + } + } + + list_add_tail(&user_payload_kernel->list, &row->user_payload_list); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return 0; +} + +static int cam_sync_handle_deregister_user_payload( + struct cam_private_ioctl_arg *k_ioctl) +{ + struct cam_sync_userpayload_info userpayload_info; + struct sync_user_payload *user_payload_kernel, *temp; + uint32_t sync_obj; + struct sync_table_row *row = NULL; + + if (k_ioctl->size != sizeof(struct cam_sync_userpayload_info)) { + CAM_ERR(CAM_SYNC, "Incorrect ioctl size"); + return -EINVAL; + } + + if (!k_ioctl->ioctl_ptr) { + CAM_ERR(CAM_SYNC, "Invalid embedded ioctl ptr"); + return -EINVAL; + } + + if (copy_from_user(&userpayload_info, + u64_to_user_ptr(k_ioctl->ioctl_ptr), + k_ioctl->size)) + return -EFAULT; + + sync_obj = userpayload_info.sync_obj; + if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0) + return -EINVAL; + + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]); + row = sync_dev->sync_table + sync_obj; + + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj = %d", + sync_obj); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return -EINVAL; + } + + list_for_each_entry_safe(user_payload_kernel, temp, + &row->user_payload_list, list) { + if (user_payload_kernel->payload_data[0] == + userpayload_info.payload[0] && + user_payload_kernel->payload_data[1] == + userpayload_info.payload[1]) { + list_del_init(&user_payload_kernel->list); + kfree(user_payload_kernel); + } + } + + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]); + return 0; +} + +static long cam_sync_dev_ioctl(struct file *filep, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + int32_t rc; + struct sync_device *sync_dev = video_drvdata(filep); + struct cam_private_ioctl_arg k_ioctl; + + if (!sync_dev) { + CAM_ERR(CAM_SYNC, "sync_dev NULL"); + return -EINVAL; + } + + if (!arg) + return -EINVAL; + + if (cmd != CAM_PRIVATE_IOCTL_CMD) + return -ENOIOCTLCMD; + + k_ioctl = *(struct cam_private_ioctl_arg *)arg; + + switch (k_ioctl.id) { + case CAM_SYNC_CREATE: + rc = cam_sync_handle_create(&k_ioctl); + break; + case CAM_SYNC_DESTROY: + rc = cam_sync_handle_destroy(&k_ioctl); + break; + case CAM_SYNC_REGISTER_PAYLOAD: + rc = cam_sync_handle_register_user_payload( + &k_ioctl); + break; + case CAM_SYNC_DEREGISTER_PAYLOAD: + rc = cam_sync_handle_deregister_user_payload( + &k_ioctl); + break; + case CAM_SYNC_SIGNAL: + rc = cam_sync_handle_signal(&k_ioctl); + break; + case CAM_SYNC_MERGE: + rc = cam_sync_handle_merge(&k_ioctl); + break; + case CAM_SYNC_WAIT: + rc = cam_sync_handle_wait(&k_ioctl); + ((struct cam_private_ioctl_arg *)arg)->result = + k_ioctl.result; + break; + default: + rc = -ENOIOCTLCMD; + } + + return rc; +} + +static unsigned int cam_sync_poll(struct file *f, + struct poll_table_struct *pll_table) +{ + int rc = 0; + struct v4l2_fh *eventq = f->private_data; + + if (!eventq) + return -EINVAL; + + poll_wait(f, &eventq->wait, pll_table); + + if (v4l2_event_pending(eventq)) + rc = POLLPRI; + + return rc; +} + +static int cam_sync_open(struct file *filep) +{ + int rc; + struct sync_device *sync_dev = video_drvdata(filep); + + if (!sync_dev) { + CAM_ERR(CAM_SYNC, "Sync device NULL"); + return -ENODEV; + } + + mutex_lock(&sync_dev->table_lock); + if (sync_dev->open_cnt >= 1) { + mutex_unlock(&sync_dev->table_lock); + return -EALREADY; + } + + rc = v4l2_fh_open(filep); + if (!rc) { + sync_dev->open_cnt++; + spin_lock_bh(&sync_dev->cam_sync_eventq_lock); + sync_dev->cam_sync_eventq = filep->private_data; + spin_unlock_bh(&sync_dev->cam_sync_eventq_lock); + } else { + CAM_ERR(CAM_SYNC, "v4l2_fh_open failed : %d", rc); + } + mutex_unlock(&sync_dev->table_lock); + + return rc; +} + +static int cam_sync_close(struct file *filep) +{ + int rc = 0; + int i; + struct sync_device *sync_dev = video_drvdata(filep); + + if (!sync_dev) { + CAM_ERR(CAM_SYNC, "Sync device NULL"); + rc = -ENODEV; + return rc; + } + mutex_lock(&sync_dev->table_lock); + sync_dev->open_cnt--; + if (!sync_dev->open_cnt) { + for (i = 1; i < CAM_SYNC_MAX_OBJS; i++) { + struct sync_table_row *row = + sync_dev->sync_table + i; + + /* + * Signal all ACTIVE objects as ERR, but we don't + * care about the return status here apart from logging + * it. + */ + if (row->state == CAM_SYNC_STATE_ACTIVE) { + rc = cam_sync_signal(i, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc < 0) + CAM_ERR(CAM_SYNC, + "Cleanup signal fail idx:%d\n", + i); + } + } + + /* + * Flush the work queue to wait for pending signal callbacks to + * finish + */ + flush_workqueue(sync_dev->work_queue); + + /* + * Now that all callbacks worker threads have finished, + * destroy the sync objects + */ + for (i = 1; i < CAM_SYNC_MAX_OBJS; i++) { + struct sync_table_row *row = + sync_dev->sync_table + i; + + if (row->state != CAM_SYNC_STATE_INVALID) { + rc = cam_sync_destroy(i); + if (rc < 0) + CAM_ERR(CAM_SYNC, + "Cleanup destroy fail:idx:%d\n", + i); + } + } + } + mutex_unlock(&sync_dev->table_lock); + spin_lock_bh(&sync_dev->cam_sync_eventq_lock); + sync_dev->cam_sync_eventq = NULL; + spin_unlock_bh(&sync_dev->cam_sync_eventq_lock); + v4l2_fh_release(filep); + + return rc; +} + +int cam_sync_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_subscribe(fh, sub, CAM_SYNC_MAX_V4L2_EVENTS, NULL); +} + +int cam_sync_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} + +static const struct v4l2_ioctl_ops g_cam_sync_ioctl_ops = { + .vidioc_subscribe_event = cam_sync_subscribe_event, + .vidioc_unsubscribe_event = cam_sync_unsubscribe_event, + .vidioc_default = cam_sync_dev_ioctl, +}; + +static struct v4l2_file_operations cam_sync_v4l2_fops = { + .owner = THIS_MODULE, + .open = cam_sync_open, + .release = cam_sync_close, + .poll = cam_sync_poll, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +#if defined(CONFIG_MEDIA_CONTROLLER) +static int cam_sync_media_controller_init(struct sync_device *sync_dev, + struct platform_device *pdev) +{ + int rc; + + sync_dev->v4l2_dev.mdev = kzalloc(sizeof(struct media_device), + GFP_KERNEL); + if (!sync_dev->v4l2_dev.mdev) + return -ENOMEM; + + media_device_init(sync_dev->v4l2_dev.mdev); + strlcpy(sync_dev->v4l2_dev.mdev->model, CAM_SYNC_DEVICE_NAME, + sizeof(sync_dev->v4l2_dev.mdev->model)); + sync_dev->v4l2_dev.mdev->dev = &(pdev->dev); + + rc = media_device_register(sync_dev->v4l2_dev.mdev); + if (rc < 0) + goto register_fail; + + rc = media_entity_pads_init(&sync_dev->vdev->entity, 0, NULL); + if (rc < 0) + goto entity_fail; + + return 0; + +entity_fail: + media_device_unregister(sync_dev->v4l2_dev.mdev); +register_fail: + media_device_cleanup(sync_dev->v4l2_dev.mdev); + return rc; +} + +static void cam_sync_media_controller_cleanup(struct sync_device *sync_dev) +{ + media_entity_cleanup(&sync_dev->vdev->entity); + media_device_unregister(sync_dev->v4l2_dev.mdev); + media_device_cleanup(sync_dev->v4l2_dev.mdev); + kfree(sync_dev->v4l2_dev.mdev); +} + +static void cam_sync_init_entity(struct sync_device *sync_dev) +{ + sync_dev->vdev->entity.function = CAM_SYNC_DEVICE_TYPE; + sync_dev->vdev->entity.name = + video_device_node_name(sync_dev->vdev); +} +#else +static int cam_sync_media_controller_init(struct sync_device *sync_dev, + struct platform_device *pdev) +{ + return 0; +} + +static void cam_sync_media_controller_cleanup(struct sync_device *sync_dev) +{ +} + +static void cam_sync_init_entity(struct sync_device *sync_dev) +{ +} +#endif + +static int cam_sync_probe(struct platform_device *pdev) +{ + int rc; + int idx; + + sync_dev = kzalloc(sizeof(*sync_dev), GFP_KERNEL); + if (!sync_dev) + return -ENOMEM; + + mutex_init(&sync_dev->table_lock); + spin_lock_init(&sync_dev->cam_sync_eventq_lock); + + for (idx = 0; idx < CAM_SYNC_MAX_OBJS; idx++) + spin_lock_init(&sync_dev->row_spinlocks[idx]); + + sync_dev->vdev = video_device_alloc(); + if (!sync_dev->vdev) { + rc = -ENOMEM; + goto vdev_fail; + } + + rc = cam_sync_media_controller_init(sync_dev, pdev); + if (rc < 0) + goto mcinit_fail; + + sync_dev->vdev->v4l2_dev = &sync_dev->v4l2_dev; + + rc = v4l2_device_register(&(pdev->dev), sync_dev->vdev->v4l2_dev); + if (rc < 0) + goto register_fail; + + strlcpy(sync_dev->vdev->name, CAM_SYNC_NAME, + sizeof(sync_dev->vdev->name)); + sync_dev->vdev->release = video_device_release; + sync_dev->vdev->fops = &cam_sync_v4l2_fops; + sync_dev->vdev->ioctl_ops = &g_cam_sync_ioctl_ops; + sync_dev->vdev->minor = -1; + sync_dev->vdev->vfl_type = VFL_TYPE_GRABBER; + rc = video_register_device(sync_dev->vdev, + VFL_TYPE_GRABBER, -1); + if (rc < 0) + goto v4l2_fail; + + cam_sync_init_entity(sync_dev); + video_set_drvdata(sync_dev->vdev, sync_dev); + memset(&sync_dev->sync_table, 0, sizeof(sync_dev->sync_table)); + memset(&sync_dev->bitmap, 0, sizeof(sync_dev->bitmap)); + bitmap_zero(sync_dev->bitmap, CAM_SYNC_MAX_OBJS); + + /* + * We treat zero as invalid handle, so we will keep the 0th bit set + * always + */ + set_bit(0, sync_dev->bitmap); + + sync_dev->work_queue = alloc_workqueue(CAM_SYNC_WORKQUEUE_NAME, + WQ_HIGHPRI | WQ_UNBOUND, 1); + + if (!sync_dev->work_queue) { + CAM_ERR(CAM_SYNC, + "Error: high priority work queue creation failed"); + rc = -ENOMEM; + goto v4l2_fail; + } + + return rc; + +v4l2_fail: + v4l2_device_unregister(sync_dev->vdev->v4l2_dev); +register_fail: + cam_sync_media_controller_cleanup(sync_dev); +mcinit_fail: + video_device_release(sync_dev->vdev); +vdev_fail: + mutex_destroy(&sync_dev->table_lock); + kfree(sync_dev); + return rc; +} + +static int cam_sync_remove(struct platform_device *pdev) +{ + v4l2_device_unregister(sync_dev->vdev->v4l2_dev); + cam_sync_media_controller_cleanup(sync_dev); + video_device_release(sync_dev->vdev); + kfree(sync_dev); + sync_dev = NULL; + + return 0; +} + +static struct platform_device cam_sync_device = { + .name = "cam_sync", + .id = -1, +}; + +static struct platform_driver cam_sync_driver = { + .probe = cam_sync_probe, + .remove = cam_sync_remove, + .driver = { + .name = "cam_sync", + .owner = THIS_MODULE, + }, +}; + +static int __init cam_sync_init(void) +{ + int rc; + + rc = platform_device_register(&cam_sync_device); + if (rc) + return -ENODEV; + + return platform_driver_register(&cam_sync_driver); +} + +static void __exit cam_sync_exit(void) +{ + int idx; + + for (idx = 0; idx < CAM_SYNC_MAX_OBJS; idx++) + spin_lock_init(&sync_dev->row_spinlocks[idx]); + platform_driver_unregister(&cam_sync_driver); + platform_device_unregister(&cam_sync_device); + kfree(sync_dev); +} + +module_init(cam_sync_init); +module_exit(cam_sync_exit); +MODULE_DESCRIPTION("Camera sync driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_api.h b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_api.h new file mode 100644 index 000000000000..297f53af84fd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_api.h @@ -0,0 +1,151 @@ +/* Copyright (c) 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. + */ + +#ifndef __CAM_SYNC_API_H__ +#define __CAM_SYNC_API_H__ + +#include +#include +#include +#include +#include + +#define SYNC_DEBUG_NAME_LEN 63 +typedef void (*sync_callback)(int32_t sync_obj, int status, void *data); + +/* Kernel APIs */ + +/** + * @brief: Creates a sync object + * + * The newly created sync obj is assigned to sync_obj. + * sync object. + * + * @param sync_obj : Pointer to int referencing the sync object. + * @param name : Optional parameter associating a name with the sync object for + * debug purposes. Only first SYNC_DEBUG_NAME_LEN bytes are accepted, + * rest will be ignored. + * + * @return Status of operation. Zero in case of success. + * -EINVAL will be returned if sync_obj is an invalid pointer. + * -ENOMEM will be returned if the kernel can't allocate space for + * sync object. + */ +int cam_sync_create(int32_t *sync_obj, const char *name); + +/** + * @brief: Registers a callback with a sync object + * + * @param cb_func: Pointer to callback to be registered + * @param userdata: Opaque pointer which will be passed back with callback. + * @param sync_obj: int referencing the sync object. + * + * @return Status of operation. Zero in case of success. + * -EINVAL will be returned if userdata is invalid. + * -ENOMEM will be returned if cb_func is invalid. + * + */ +int cam_sync_register_callback(sync_callback cb_func, + void *userdata, int32_t sync_obj); + +/** + * @brief: De-registers a callback with a sync object + * + * @param cb_func: Pointer to callback to be de-registered + * @param userdata: Opaque pointer which will be passed back with callback. + * @param sync_obj: int referencing the sync object. + * + * @return Status of operation. Zero in case of success. + * -EINVAL will be returned if userdata is invalid. + * -ENOMEM will be returned if cb_func is invalid. + */ +int cam_sync_deregister_callback(sync_callback cb_func, + void *userdata, int32_t sync_obj); + +/** + * @brief: Signals a sync object with the status argument. + * + * This function will signal the sync object referenced by the sync_obj + * parameter and when doing so, will trigger callbacks in both user space and + * kernel. Callbacks will triggered asynchronously and their order of execution + * is not guaranteed. The status parameter will indicate whether the entity + * performing the signaling wants to convey an error case or a success case. + * + * @param sync_obj: int referencing the sync object. + * @param status: Status of the signaling. Can be either SYNC_SIGNAL_ERROR or + * SYNC_SIGNAL_SUCCESS. + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_signal(int32_t sync_obj, uint32_t status); + +/** + * @brief: Merges multiple sync objects + * + * This function will merge multiple sync objects into a sync group. + * + * @param sync_obj: pointer to a block of ints to be merged + * @param num_objs: Number of ints in the block + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_merge(int32_t *sync_obj, uint32_t num_objs, int32_t *merged_obj); + +/** + * @brief: get ref count of sync obj + * + * This function will increment ref count for the sync object, and the ref + * count will be decremented when this sync object is signaled. + * + * @param sync_obj: sync object + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_get_obj_ref(int32_t sync_obj); + +/** + * @brief: put ref count of sync obj + * + * This function will decrement ref count for the sync object. + * + * @param sync_obj: sync object + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_put_obj_ref(int32_t sync_obj); + +/** + * @brief: Destroys a sync object + * + * @param sync_obj: int referencing the sync object to be destroyed + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_destroy(int32_t sync_obj); + +/** + * @brief: Waits for a sync object synchronously + * + * Does a wait on the sync object identified by sync_obj for a maximum + * of timeout_ms milliseconds. Must not be called from interrupt context as + * this API can sleep. Should be called from process context only. + * + * @param sync_obj: int referencing the sync object to be waited upon + * @timeout_ms sync_obj: Timeout in ms. + * + * @return 0 upon success, -EINVAL if sync object is in bad state or arguments + * are invalid, -ETIMEDOUT if wait times out. + */ +int cam_sync_wait(int32_t sync_obj, uint64_t timeout_ms); + + +#endif /* __CAM_SYNC_API_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_private.h b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_private.h new file mode 100644 index 000000000000..7872064df5d0 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_private.h @@ -0,0 +1,200 @@ +/* Copyright (c) 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. + */ + +#ifndef __CAM_SYNC_PRIVATE_H__ +#define __CAM_SYNC_PRIVATE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_CAM_SYNC_DBG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#define CAM_SYNC_OBJ_NAME_LEN 64 +#define CAM_SYNC_MAX_OBJS 1024 +#define CAM_SYNC_MAX_V4L2_EVENTS 50 +#define CAM_SYNC_DEBUG_FILENAME "cam_debug" +#define CAM_SYNC_DEBUG_BASEDIR "cam" +#define CAM_SYNC_DEBUG_BUF_SIZE 32 +#define CAM_SYNC_PAYLOAD_WORDS 2 +#define CAM_SYNC_NAME "cam_sync" +#define CAM_SYNC_WORKQUEUE_NAME "HIPRIO_SYNC_WORK_QUEUE" + +#define CAM_SYNC_TYPE_INDV 0 +#define CAM_SYNC_TYPE_GROUP 1 + +/** + * enum sync_type - Enum to indicate the type of sync object, + * i.e. individual or group. + * + * @SYNC_TYPE_INDV : Object is an individual sync object + * @SYNC_TYPE_GROUP : Object is a group sync object + */ +enum sync_type { + SYNC_TYPE_INDV, + SYNC_TYPE_GROUP +}; + +/** + * enum sync_list_clean_type - Enum to indicate the type of list clean action + * to be peformed, i.e. specific sync ID or all list sync ids. + * + * @SYNC_CLEAN_ONE : Specific object to be cleaned in the list + * @SYNC_CLEAN_ALL : Clean all objects in the list + */ +enum sync_list_clean_type { + SYNC_LIST_CLEAN_ONE, + SYNC_LIST_CLEAN_ALL +}; + +/** + * struct sync_parent_info - Single node of information about a parent + * of a sync object, usually part of the parents linked list + * + * @sync_id : Sync object id of parent + * @list : List member used to append this node to a linked list + */ +struct sync_parent_info { + int32_t sync_id; + struct list_head list; +}; + +/** + * struct sync_parent_info - Single node of information about a child + * of a sync object, usually part of the children linked list + * + * @sync_id : Sync object id of child + * @list : List member used to append this node to a linked list + */ +struct sync_child_info { + int32_t sync_id; + struct list_head list; +}; + + +/** + * struct sync_callback_info - Single node of information about a kernel + * callback registered on a sync object + * + * @callback_func : Callback function, registered by client driver + * @cb_data : Callback data, registered by client driver + * @status........ : Status with which callback will be invoked in client + * @sync_obj : Sync id of the object for which callback is registered + * @cb_dispatch_work : Work representing the call dispatch + * @list : List member used to append this node to a linked list + */ +struct sync_callback_info { + sync_callback callback_func; + void *cb_data; + int status; + int32_t sync_obj; + struct work_struct cb_dispatch_work; + struct list_head list; +}; + +/** + * struct sync_user_payload - Single node of information about a user space + * payload registered from user space + * + * @payload_data : Payload data, opaque to kernel + * @list : List member used to append this node to a linked list + */ +struct sync_user_payload { + uint64_t payload_data[CAM_SYNC_PAYLOAD_WORDS]; + struct list_head list; +}; + +/** + * struct sync_table_row - Single row of information about a sync object, used + * for internal book keeping in the sync driver + * + * @name : Optional string representation of the sync object + * @type : Type of the sync object (individual or group) + * @sync_id : Integer id representing this sync object + * @parents_list : Linked list of parents of this sync object + * @children_list : Linked list of children of this sync object + * @state : State (INVALID, ACTIVE, SIGNALED_SUCCESS or + * SIGNALED_ERROR) + * @remaining : Count of remaining children that not been signaled + * @signaled : Completion variable on which block calls will wait + * @callback_list : Linked list of kernel callbacks registered + * @user_payload_list : LInked list of user space payloads registered + * @ref_cnt : ref count of the number of usage of the fence. + */ +struct sync_table_row { + char name[CAM_SYNC_OBJ_NAME_LEN]; + enum sync_type type; + int32_t sync_id; + /* List of parents, which are merged objects */ + struct list_head parents_list; + /* List of children, which constitute the merged object */ + struct list_head children_list; + uint32_t state; + uint32_t remaining; + struct completion signaled; + struct list_head callback_list; + struct list_head user_payload_list; + atomic_t ref_cnt; +}; + +/** + * struct cam_signalable_info - Information for a single sync object that is + * ready to be signaled + * + * @sync_obj : Sync object id of signalable object + * @status : Status with which to signal + * @list : List member used to append this node to a linked list + */ +struct cam_signalable_info { + int32_t sync_obj; + uint32_t status; + struct list_head list; +}; + +/** + * struct sync_device - Internal struct to book keep sync driver details + * + * @vdev : Video device + * @v4l2_dev : V4L2 device + * @sync_table : Table of all sync objects + * @row_spinlocks : Spinlock array, one for each row in the table + * @table_lock : Mutex used to lock the table + * @open_cnt : Count of file open calls made on the sync driver + * @work_queue : Work queue used for dispatching kernel callbacks + * @cam_sync_eventq : Event queue used to dispatch user payloads to user space + * @bitmap : Bitmap representation of all sync objects + */ +struct sync_device { + struct video_device *vdev; + struct v4l2_device v4l2_dev; + struct sync_table_row sync_table[CAM_SYNC_MAX_OBJS]; + spinlock_t row_spinlocks[CAM_SYNC_MAX_OBJS]; + struct mutex table_lock; + int open_cnt; + struct workqueue_struct *work_queue; + struct v4l2_fh *cam_sync_eventq; + spinlock_t cam_sync_eventq_lock; + DECLARE_BITMAP(bitmap, CAM_SYNC_MAX_OBJS); +}; + + +#endif /* __CAM_SYNC_PRIVATE_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.c b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.c new file mode 100644 index 000000000000..ed69829575bb --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.c @@ -0,0 +1,479 @@ +/* Copyright (c) 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 "cam_sync_util.h" + +int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev, + long *idx) +{ + int rc = 0; + + mutex_lock(&sync_dev->table_lock); + + *idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS); + + if (*idx < CAM_SYNC_MAX_OBJS) + set_bit(*idx, sync_dev->bitmap); + else + rc = -1; + + mutex_unlock(&sync_dev->table_lock); + + return rc; +} + +int cam_sync_init_object(struct sync_table_row *table, + uint32_t idx, + const char *name) +{ + struct sync_table_row *row = table + idx; + + if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS) + return -EINVAL; + + if (name) + strlcpy(row->name, name, SYNC_DEBUG_NAME_LEN); + INIT_LIST_HEAD(&row->parents_list); + INIT_LIST_HEAD(&row->children_list); + row->type = CAM_SYNC_TYPE_INDV; + row->sync_id = idx; + row->state = CAM_SYNC_STATE_ACTIVE; + row->remaining = 0; + init_completion(&row->signaled); + INIT_LIST_HEAD(&row->callback_list); + INIT_LIST_HEAD(&row->user_payload_list); + CAM_DBG(CAM_SYNC, "Sync object Initialised: sync_id:%u row_state:%u ", + row->sync_id, row->state); + + return 0; +} + +uint32_t cam_sync_util_get_group_object_state(struct sync_table_row *table, + uint32_t *sync_objs, + uint32_t num_objs) +{ + int i; + struct sync_table_row *child_row = NULL; + int success_count = 0; + int active_count = 0; + + if (!table || !sync_objs) + return CAM_SYNC_STATE_SIGNALED_ERROR; + + /* + * We need to arrive at the state of the merged object based on + * counts of error, active and success states of all children objects + */ + for (i = 0; i < num_objs; i++) { + child_row = table + sync_objs[i]; + switch (child_row->state) { + case CAM_SYNC_STATE_SIGNALED_ERROR: + return CAM_SYNC_STATE_SIGNALED_ERROR; + case CAM_SYNC_STATE_SIGNALED_SUCCESS: + success_count++; + break; + case CAM_SYNC_STATE_ACTIVE: + active_count++; + break; + default: + CAM_ERR(CAM_SYNC, + "Invalid state of child object during merge"); + return CAM_SYNC_STATE_SIGNALED_ERROR; + } + } + + if (active_count) + return CAM_SYNC_STATE_ACTIVE; + + if (success_count == num_objs) + return CAM_SYNC_STATE_SIGNALED_SUCCESS; + + return CAM_SYNC_STATE_SIGNALED_ERROR; +} + +static int cam_sync_util_get_group_object_remaining_count( + struct sync_table_row *table, + uint32_t *sync_objs, + uint32_t num_objs) +{ + int i; + struct sync_table_row *child_row = NULL; + int remaining_count = 0; + + if (!table || !sync_objs) + return -EINVAL; + + for (i = 0; i < num_objs; i++) { + child_row = table + sync_objs[i]; + if (child_row->state == CAM_SYNC_STATE_ACTIVE) + remaining_count++; + } + + return remaining_count; +} + +int cam_sync_init_group_object(struct sync_table_row *table, + uint32_t idx, + uint32_t *sync_objs, + uint32_t num_objs) +{ + int i; + int remaining; + struct sync_child_info *child_info; + struct sync_parent_info *parent_info; + struct sync_table_row *row = table + idx; + struct sync_table_row *child_row = NULL; + + INIT_LIST_HEAD(&row->parents_list); + + INIT_LIST_HEAD(&row->children_list); + + /* + * While traversing parents and children, we allocate in a loop and in + * case allocation fails, we call the clean up function which frees up + * all memory allocation thus far + */ + for (i = 0; i < num_objs; i++) { + child_info = kzalloc(sizeof(*child_info), GFP_ATOMIC); + + if (!child_info) { + cam_sync_util_cleanup_children_list(row, + SYNC_LIST_CLEAN_ALL, 0); + return -ENOMEM; + } + + child_info->sync_id = sync_objs[i]; + list_add_tail(&child_info->list, &row->children_list); + } + + for (i = 0; i < num_objs; i++) { + /* This gets us the row corresponding to the sync object */ + child_row = table + sync_objs[i]; + spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]); + parent_info = kzalloc(sizeof(*parent_info), GFP_ATOMIC); + if (!parent_info) { + cam_sync_util_cleanup_parents_list(child_row, + SYNC_LIST_CLEAN_ALL, 0); + cam_sync_util_cleanup_children_list(row, + SYNC_LIST_CLEAN_ALL, 0); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]); + return -ENOMEM; + } + parent_info->sync_id = idx; + list_add_tail(&parent_info->list, &child_row->parents_list); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]); + } + + row->type = CAM_SYNC_TYPE_GROUP; + row->sync_id = idx; + row->state = cam_sync_util_get_group_object_state(table, + sync_objs, num_objs); + remaining = cam_sync_util_get_group_object_remaining_count(table, + sync_objs, num_objs); + if (remaining < 0) { + CAM_ERR(CAM_SYNC, "Failed getting remaining count"); + return -ENODEV; + } + + row->remaining = remaining; + + init_completion(&row->signaled); + INIT_LIST_HEAD(&row->callback_list); + INIT_LIST_HEAD(&row->user_payload_list); + + if (row->state != CAM_SYNC_STATE_ACTIVE) + complete_all(&row->signaled); + + return 0; +} + +int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx) +{ + struct sync_table_row *row = table + idx; + struct sync_child_info *child_info, *temp_child; + struct sync_callback_info *sync_cb, *temp_cb; + struct sync_parent_info *parent_info, *temp_parent; + struct sync_user_payload *upayload_info, *temp_upayload; + struct sync_table_row *child_row = NULL, *parent_row = NULL; + struct list_head temp_child_list, temp_parent_list; + + if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS) + return -EINVAL; + + spin_lock_bh(&sync_dev->row_spinlocks[idx]); + if (row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Error: accessing an uninitialized sync obj: idx = %d", + idx); + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + return -EINVAL; + } + row->state = CAM_SYNC_STATE_INVALID; + + /* Object's child and parent objects will be added into this list */ + INIT_LIST_HEAD(&temp_child_list); + INIT_LIST_HEAD(&temp_parent_list); + + list_for_each_entry_safe(child_info, temp_child, &row->children_list, + list) { + if (child_info->sync_id <= 0) + continue; + + list_del_init(&child_info->list); + list_add_tail(&child_info->list, &temp_child_list); + } + + list_for_each_entry_safe(parent_info, temp_parent, &row->parents_list, + list) { + if (parent_info->sync_id <= 0) + continue; + + list_del_init(&parent_info->list); + list_add_tail(&parent_info->list, &temp_parent_list); + } + + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + + /* Cleanup the child to parent link from child list */ + while (!list_empty(&temp_child_list)) { + child_info = list_first_entry(&temp_child_list, + struct sync_child_info, list); + child_row = sync_dev->sync_table + child_info->sync_id; + + spin_lock_bh(&sync_dev->row_spinlocks[child_info->sync_id]); + + if (child_row->state == CAM_SYNC_STATE_INVALID) { + spin_unlock_bh(&sync_dev->row_spinlocks[ + child_info->sync_id]); + list_del_init(&child_info->list); + kfree(child_info); + continue; + } + + cam_sync_util_cleanup_parents_list(child_row, + SYNC_LIST_CLEAN_ONE, idx); + + spin_unlock_bh(&sync_dev->row_spinlocks[child_info->sync_id]); + + list_del_init(&child_info->list); + kfree(child_info); + } + + /* Cleanup the parent to child link */ + while (!list_empty(&temp_parent_list)) { + parent_info = list_first_entry(&temp_parent_list, + struct sync_parent_info, list); + parent_row = sync_dev->sync_table + parent_info->sync_id; + + spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]); + + if (parent_row->state == CAM_SYNC_STATE_INVALID) { + spin_unlock_bh(&sync_dev->row_spinlocks[ + parent_info->sync_id]); + list_del_init(&parent_info->list); + kfree(parent_info); + continue; + } + + cam_sync_util_cleanup_children_list(parent_row, + SYNC_LIST_CLEAN_ONE, idx); + + spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]); + + list_del_init(&parent_info->list); + kfree(parent_info); + } + + spin_lock_bh(&sync_dev->row_spinlocks[idx]); + list_for_each_entry_safe(upayload_info, temp_upayload, + &row->user_payload_list, list) { + list_del_init(&upayload_info->list); + kfree(upayload_info); + } + + list_for_each_entry_safe(sync_cb, temp_cb, + &row->callback_list, list) { + list_del_init(&sync_cb->list); + kfree(sync_cb); + } + + memset(row, 0, sizeof(*row)); + clear_bit(idx, sync_dev->bitmap); + INIT_LIST_HEAD(&row->callback_list); + INIT_LIST_HEAD(&row->parents_list); + INIT_LIST_HEAD(&row->children_list); + INIT_LIST_HEAD(&row->user_payload_list); + spin_unlock_bh(&sync_dev->row_spinlocks[idx]); + + CAM_DBG(CAM_SYNC, "Destroying sync obj:%d successful", idx); + return 0; +} + +void cam_sync_util_cb_dispatch(struct work_struct *cb_dispatch_work) +{ + struct sync_callback_info *cb_info = container_of(cb_dispatch_work, + struct sync_callback_info, + cb_dispatch_work); + + cb_info->callback_func(cb_info->sync_obj, + cb_info->status, + cb_info->cb_data); + + kfree(cb_info); +} + +void cam_sync_util_send_v4l2_event(uint32_t id, + uint32_t sync_obj, + int status, + void *payload, + int len) +{ + struct v4l2_event event; + __u64 *payload_data = NULL; + struct cam_sync_ev_header *ev_header = NULL; + + event.id = id; + event.type = CAM_SYNC_V4L_EVENT; + + ev_header = CAM_SYNC_GET_HEADER_PTR(event); + ev_header->sync_obj = sync_obj; + ev_header->status = status; + + payload_data = CAM_SYNC_GET_PAYLOAD_PTR(event, __u64); + memcpy(payload_data, payload, len); + + v4l2_event_queue(sync_dev->vdev, &event); + CAM_DBG(CAM_SYNC, "send v4l2 event for sync_obj :%d", + sync_obj); +} + +int cam_sync_util_validate_merge(uint32_t *sync_obj, uint32_t num_objs) +{ + int i; + struct sync_table_row *row = NULL; + + if (num_objs <= 1) { + CAM_ERR(CAM_SYNC, "Single object merge is not allowed"); + return -EINVAL; + } + + for (i = 0; i < num_objs; i++) { + row = sync_dev->sync_table + sync_obj[i]; + spin_lock_bh(&sync_dev->row_spinlocks[sync_obj[i]]); + if (row->type == CAM_SYNC_TYPE_GROUP || + row->state == CAM_SYNC_STATE_INVALID) { + CAM_ERR(CAM_SYNC, + "Group obj %d can't be merged or obj UNINIT", + sync_obj[i]); + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj[i]]); + return -EINVAL; + } + spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj[i]]); + } + return 0; +} + +int cam_sync_util_add_to_signalable_list(int32_t sync_obj, + uint32_t status, + struct list_head *sync_list) +{ + struct cam_signalable_info *signalable_info = NULL; + + signalable_info = kzalloc(sizeof(*signalable_info), GFP_ATOMIC); + if (!signalable_info) + return -ENOMEM; + + signalable_info->sync_obj = sync_obj; + signalable_info->status = status; + + list_add_tail(&signalable_info->list, sync_list); + CAM_DBG(CAM_SYNC, "Add sync_obj :%d with status :%d to signalable list", + sync_obj, status); + + return 0; +} + +int cam_sync_util_get_state(int current_state, + int new_state) +{ + int result = CAM_SYNC_STATE_SIGNALED_ERROR; + + if (new_state != CAM_SYNC_STATE_SIGNALED_SUCCESS && + new_state != CAM_SYNC_STATE_SIGNALED_ERROR) + return CAM_SYNC_STATE_SIGNALED_ERROR; + + switch (current_state) { + case CAM_SYNC_STATE_INVALID: + result = CAM_SYNC_STATE_SIGNALED_ERROR; + break; + + case CAM_SYNC_STATE_ACTIVE: + case CAM_SYNC_STATE_SIGNALED_SUCCESS: + if (new_state == CAM_SYNC_STATE_SIGNALED_ERROR) + result = CAM_SYNC_STATE_SIGNALED_ERROR; + else if (new_state == CAM_SYNC_STATE_SIGNALED_SUCCESS) + result = CAM_SYNC_STATE_SIGNALED_SUCCESS; + break; + + case CAM_SYNC_STATE_SIGNALED_ERROR: + result = CAM_SYNC_STATE_SIGNALED_ERROR; + break; + } + + return result; +} + +void cam_sync_util_cleanup_children_list(struct sync_table_row *row, + uint32_t list_clean_type, uint32_t sync_obj) +{ + struct sync_child_info *child_info = NULL; + struct sync_child_info *temp_child_info = NULL; + uint32_t curr_sync_obj; + + list_for_each_entry_safe(child_info, + temp_child_info, &row->children_list, list) { + if ((list_clean_type == SYNC_LIST_CLEAN_ONE) && + (child_info->sync_id != sync_obj)) + continue; + + curr_sync_obj = child_info->sync_id; + list_del_init(&child_info->list); + kfree(child_info); + + if ((list_clean_type == SYNC_LIST_CLEAN_ONE) && + (curr_sync_obj == sync_obj)) + break; + } +} + +void cam_sync_util_cleanup_parents_list(struct sync_table_row *row, + uint32_t list_clean_type, uint32_t sync_obj) +{ + struct sync_parent_info *parent_info = NULL; + struct sync_parent_info *temp_parent_info = NULL; + uint32_t curr_sync_obj; + + list_for_each_entry_safe(parent_info, + temp_parent_info, &row->parents_list, list) { + if ((list_clean_type == SYNC_LIST_CLEAN_ONE) && + (parent_info->sync_id != sync_obj)) + continue; + + curr_sync_obj = parent_info->sync_id; + list_del_init(&parent_info->list); + kfree(parent_info); + + if ((list_clean_type == SYNC_LIST_CLEAN_ONE) && + (curr_sync_obj == sync_obj)) + break; + } +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.h b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.h new file mode 100644 index 000000000000..ae7d5421e6b7 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_sync/cam_sync_util.h @@ -0,0 +1,165 @@ +/* Copyright (c) 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. + */ + +#ifndef __CAM_SYNC_UTIL_H__ +#define __CAM_SYNC_UTIL_H__ + + +#include +#include "cam_sync_private.h" +#include "cam_debug_util.h" + +extern struct sync_device *sync_dev; + +/** + * @brief: Finds an empty row in the sync table and sets its corresponding bit + * in the bit array + * + * @param sync_dev : Pointer to the sync device instance + * @param idx : Pointer to an long containing the index found in the bit + * array + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev, + long *idx); + +/** + * @brief: Function to initialize an empty row in the sync table. This should be + * called only for individual sync objects. + * + * @param table : Pointer to the sync objects table + * @param idx : Index of row to initialize + * @param name : Optional string representation of the sync object. Should be + * 63 characters or less + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_init_object(struct sync_table_row *table, + uint32_t idx, + const char *name); + +/** + * @brief: Function to uninitialize a row in the sync table + * + * @param table : Pointer to the sync objects table + * @param idx : Index of row to initialize + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx); + +/** + * @brief: Function to initialize a row in the sync table when the object is a + * group object, also known as a merged sync object + * + * @param table : Pointer to the sync objects table + * @param idx : Index of row to initialize + * @param sync_objs : Array of sync objects which will merged + * or grouped together + * @param num_objs : Number of sync objects in the array + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_init_group_object(struct sync_table_row *table, + uint32_t idx, + uint32_t *sync_objs, + uint32_t num_objs); + +int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx); + +/** + * @brief: Function to dispatch a kernel callback for a sync callback + * + * @param cb_dispatch_work : Pointer to the work_struct that needs to be + * dispatched + * + * @return None + */ +void cam_sync_util_cb_dispatch(struct work_struct *cb_dispatch_work); + +/** + * @brief: Function to send V4L event to user space + * @param id : V4L event id to send + * @param sync_obj : Sync obj for which event needs to be sent + * @param status : Status of the event + * @payload : Payload that needs to be sent to user space + * @len : Length of the payload + * + * @return None + */ +void cam_sync_util_send_v4l2_event(uint32_t id, + uint32_t sync_obj, + int status, + void *payload, + int len); + +/** + * @brief: Function to validate sync merge arguments + * + * @param sync_obj : Array of sync objects to merge + * @param num_objs : Number of sync objects in the array + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_util_validate_merge(uint32_t *sync_obj, uint32_t num_objs); + +/** + * @brief: Function which adds sync object information to the signalable list + * + * @param sync_obj : Sync object to add + * @param status : Status of above sync object + * @param list : Linked list where the information should be added to + * + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_sync_util_add_to_signalable_list(int32_t sync_obj, + uint32_t status, + struct list_head *sync_list); + +/** + * @brief: Function which gets the next state of the sync object based on the + * current state and the new state + * + * @param current_state : Current state of the sync object + * @param new_state : New state of the sync object + * + * @return Next state of the sync object + */ +int cam_sync_util_get_state(int current_state, + int new_state); + +/** + * @brief: Function to clean up the children of a sync object + * @row : Row whose child list to clean + * @list_clean_type : Clean specific object or clean all objects + * @sync_obj : Sync object to be clean if list clean type is + * SYNC_LIST_CLEAN_ONE + * + * @return None + */ +void cam_sync_util_cleanup_children_list(struct sync_table_row *row, + uint32_t list_clean_type, uint32_t sync_obj); + +/** + * @brief: Function to clean up the parents of a sync object + * @row : Row whose parent list to clean + * @list_clean_type : Clean specific object or clean all objects + * @sync_obj : Sync object to be clean if list clean type is + * SYNC_LIST_CLEAN_ONE + * + * @return None + */ +void cam_sync_util_cleanup_parents_list(struct sync_table_row *row, + uint32_t list_clean_type, uint32_t sync_obj); + +#endif /* __CAM_SYNC_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/Makefile b/drivers/media/platform/msm/camera_oneplus/cam_utils/Makefile new file mode 100644 index 000000000000..06f606caf4c3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_core/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_req_mgr/ +ccflags-y += -Idrivers/media/platform/msm/camera_oneplus/cam_smmu/ + +obj-$(CONFIG_SPECTRA_CAMERA) += cam_soc_util.o cam_io_util.o cam_packet_util.o cam_debug_util.o cam_trace.o cam_common_util.o diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.c new file mode 100644 index 000000000000..199d3ea072fd --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.c @@ -0,0 +1,35 @@ +/* Copyright (c) 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 +#include +#include + +#include "cam_common_util.h" +#include "cam_debug_util.h" + +int cam_common_util_get_string_index(const char **strings, + uint32_t num_strings, char *matching_string, uint32_t *index) +{ + int i; + + for (i = 0; i < num_strings; i++) { + if (strnstr(strings[i], matching_string, strlen(strings[i]))) { + CAM_DBG(CAM_UTIL, "matched %s : %d\n", + matching_string, i); + *index = i; + return 0; + } + } + + return -EINVAL; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.h new file mode 100644 index 000000000000..9f4a68b01052 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_common_util.h @@ -0,0 +1,54 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_COMMON_UTIL_H_ +#define _CAM_COMMON_UTIL_H_ + +#define CAM_BITS_MASK_SHIFT(x, mask, shift) (((x) & (mask)) >> shift) + +#define CAM_GET_TIMESTAMP(timestamp) ktime_get_real_ts64(&(timestamp)) +#define CAM_GET_TIMESTAMP_DIFF_IN_MICRO(ts_start, ts_end, diff_microsec) \ +({ \ + diff_microsec = 0; \ + if (ts_end.tv_nsec >= ts_start.tv_nsec) { \ + diff_microsec = \ + (ts_end.tv_nsec - ts_start.tv_nsec) / 1000; \ + diff_microsec += \ + (ts_end.tv_sec - ts_start.tv_sec) * 1000 * 1000; \ + } else { \ + diff_microsec = \ + (ts_end.tv_nsec + \ + (1000*1000*1000 - ts_start.tv_nsec)) / 1000; \ + diff_microsec += \ + (ts_end.tv_sec - ts_start.tv_sec - 1) * 1000 * 1000; \ + } \ +}) + + +/** + * cam_common_util_get_string_index() + * + * @brief Match the string from list of strings to return + * matching index + * + * @strings: Pointer to list of strings + * @num_strings: Number of strings in 'strings' + * @matching_string: String to match + * @index: Pointer to index to return matching index + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_common_util_get_string_index(const char **strings, + uint32_t num_strings, char *matching_string, uint32_t *index); + +#endif /* _CAM_COMMON_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.c new file mode 100644 index 000000000000..9b63d00e9a63 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2017, The Linux Foundataion. 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 "cam_debug_util.h" + +static uint debug_mdl; +module_param(debug_mdl, uint, 0644); + +const char *cam_get_module_name(unsigned int module_id) +{ + const char *name = NULL; + + switch (module_id) { + case CAM_CDM: + name = "CAM-CDM"; + break; + case CAM_CORE: + name = "CAM-CORE"; + break; + case CAM_CRM: + name = "CAM-CRM"; + break; + case CAM_CPAS: + name = "CAM-CPAS"; + break; + case CAM_ISP: + name = "CAM-ISP"; + break; + case CAM_SENSOR: + name = "CAM-SENSOR"; + break; + case CAM_SMMU: + name = "CAM-SMMU"; + break; + case CAM_SYNC: + name = "CAM-SYNC"; + break; + case CAM_ICP: + name = "CAM-ICP"; + break; + case CAM_JPEG: + name = "CAM-JPEG"; + break; + case CAM_FD: + name = "CAM-FD"; + break; + case CAM_LRME: + name = "CAM-LRME"; + break; + case CAM_FLASH: + name = "CAM-FLASH"; + break; + case CAM_ACTUATOR: + name = "CAM-ACTUATOR"; + break; + case CAM_CCI: + name = "CAM-CCI"; + break; + case CAM_CSIPHY: + name = "CAM-CSIPHY"; + break; + case CAM_EEPROM: + name = "CAM-EEPROM"; + break; + case CAM_UTIL: + name = "CAM-UTIL"; + break; + case CAM_CTXT: + name = "CAM-CTXT"; + break; + case CAM_HFI: + name = "CAM-HFI"; + break; + case CAM_OIS: + name = "CAM-OIS"; + break; + case CAM_REQ: + name = "CAM-REQ"; + break; + default: + name = "CAM"; + break; + } + + return name; +} + +void cam_debug_log(unsigned int module_id, const char *func, const int line, + const char *fmt, ...) +{ + char str_buffer[STR_BUFFER_MAX_LENGTH]; + va_list args; + + va_start(args, fmt); + + if (debug_mdl & module_id) { + vsnprintf(str_buffer, STR_BUFFER_MAX_LENGTH, fmt, args); + pr_info("[%d %d] CAM_DBG: %s: %s: %d: %s\n", + task_tgid_nr(current), task_pid_nr(current), + cam_get_module_name(module_id), + func, line, str_buffer); + } + + va_end(args); +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.h new file mode 100644 index 000000000000..14a6d1b04c42 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_debug_util.h @@ -0,0 +1,141 @@ +/* Copyright (c) 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 +#include + +#ifndef _CAM_DEBUG_UTIL_H_ +#define _CAM_DEBUG_UTIL_H_ + +#define CAM_CDM (1 << 0) +#define CAM_CORE (1 << 1) +#define CAM_CPAS (1 << 2) +#define CAM_ISP (1 << 3) +#define CAM_CRM (1 << 4) +#define CAM_SENSOR (1 << 5) +#define CAM_SMMU (1 << 6) +#define CAM_SYNC (1 << 7) +#define CAM_ICP (1 << 8) +#define CAM_JPEG (1 << 9) +#define CAM_FD (1 << 10) +#define CAM_LRME (1 << 11) +#define CAM_FLASH (1 << 12) +#define CAM_ACTUATOR (1 << 13) +#define CAM_CCI (1 << 14) +#define CAM_CSIPHY (1 << 15) +#define CAM_EEPROM (1 << 16) +#define CAM_UTIL (1 << 17) +#define CAM_HFI (1 << 18) +#define CAM_CTXT (1 << 19) +#define CAM_OIS (1 << 20) +#define CAM_RES (1 << 21) +#define CAM_MEM (1 << 22) + +/* CAM_REQ: Tracks a request submitted to KMD */ +#define CAM_REQ (1 << 24) + +#define STR_BUFFER_MAX_LENGTH 1024 + +/* + * cam_debug_log() + * + * @brief : Get the Module name from module ID and print + * respective debug logs + * + * @module_id : Respective Module ID which is calling this function + * @func : Function which is calling to print logs + * @line : Line number associated with the function which is calling + * to print log + * @fmt : Formatted string which needs to be print in the log + * + */ +void cam_debug_log(unsigned int module_id, const char *func, const int line, + const char *fmt, ...); + +/* + * cam_get_module_name() + * + * @brief : Get the module name from module ID + * + * @module_id : Module ID which is using this function + */ +const char *cam_get_module_name(unsigned int module_id); + +/* + * CAM_ERR + * @brief : This Macro will print error logs + * + * @__module : Respective module id which is been calling this Macro + * @fmt : Formatted string which needs to be print in log + * @args : Arguments which needs to be print in log + */ +#define CAM_ERR(__module, fmt, args...) \ + pr_err("[%d %d] CAM_ERR: %s: %s: %d " fmt "\n", \ + task_tgid_nr(current), task_pid_nr(current), \ + cam_get_module_name(__module), __func__, __LINE__, ##args) +/* + * CAM_WARN + * @brief : This Macro will print warning logs + * + * @__module : Respective module id which is been calling this Macro + * @fmt : Formatted string which needs to be print in log + * @args : Arguments which needs to be print in log + */ +#define CAM_WARN(__module, fmt, args...) \ + pr_warn("[%d %d] CAM_WARN: %s: %s: %d " fmt "\n", \ + task_tgid_nr(current), task_pid_nr(current), \ + cam_get_module_name(__module), __func__, __LINE__, ##args) +/* + * CAM_INFO + * @brief : This Macro will print Information logs + * + * @__module : Respective module id which is been calling this Macro + * @fmt : Formatted string which needs to be print in log + * @args : Arguments which needs to be print in log + */ +#define CAM_INFO(__module, fmt, args...) \ + pr_info("[%d %d] CAM_INFO: %s: %s: %d " fmt "\n", \ + task_tgid_nr(current), task_pid_nr(current), \ + cam_get_module_name(__module), __func__, __LINE__, ##args) + +/* + * CAM_INFO_RATE_LIMIT + * @brief : This Macro will print info logs with ratelimit + * + * @__module : Respective module id which is been calling this Macro + * @fmt : Formatted string which needs to be print in log + * @args : Arguments which needs to be print in log + */ +#define CAM_INFO_RATE_LIMIT(__module, fmt, args...) \ + pr_info_ratelimited("CAM_INFO: %s: %s: %d " fmt "\n", \ + cam_get_module_name(__module), __func__, __LINE__, ##args) + +/* + * CAM_DBG + * @brief : This Macro will print debug logs when enabled using GROUP + * + * @__module : Respective module id which is been calling this Macro + * @fmt : Formatted string which needs to be print in log + * @args : Arguments which needs to be print in log + */ +#define CAM_DBG(__module, fmt, args...) \ + cam_debug_log(__module, __func__, __LINE__, fmt, ##args) + +/* + * CAM_ERR_RATE_LIMIT + * @brief : This Macro will print error print logs with ratelimit + */ +#define CAM_ERR_RATE_LIMIT(__module, fmt, args...) \ + pr_info_ratelimited("CAM_ERR: %s: %s: %d " fmt "\n", \ + cam_get_module_name(__module), __func__, __LINE__, ##args) + +#endif /* _CAM_DEBUG_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.c new file mode 100644 index 000000000000..8d5f96ac816f --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.c @@ -0,0 +1,288 @@ +/* Copyright (c) 2011-2014, 2017-2018, 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 +#include +#include +#include "cam_io_util.h" +#include "cam_debug_util.h" + +int cam_io_w(uint32_t data, void __iomem *addr) +{ + if (!addr) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data); + writel_relaxed_no_log(data, addr); + + return 0; +} + +int cam_io_w_mb(uint32_t data, void __iomem *addr) +{ + if (!addr) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data); + /* Ensure previous writes are done */ + wmb(); + writel_relaxed_no_log(data, addr); + /* Ensure previous writes are done */ + wmb(); + + return 0; +} + +uint32_t cam_io_r(void __iomem *addr) +{ + uint32_t data; + + if (!addr) { + CAM_ERR(CAM_UTIL, "Invalid args"); + return 0; + } + + data = readl_relaxed(addr); + CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data); + + return data; +} + +uint32_t cam_io_r_mb(void __iomem *addr) +{ + uint32_t data; + + if (!addr) { + CAM_ERR(CAM_UTIL, "Invalid args"); + return 0; + } + + /* Ensure previous read is done */ + rmb(); + data = readl_relaxed(addr); + CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data); + /* Ensure previous read is done */ + rmb(); + + return data; +} + +int cam_io_memcpy(void __iomem *dest_addr, + void __iomem *src_addr, uint32_t len) +{ + int i; + uint32_t *d = (uint32_t *) dest_addr; + uint32_t *s = (uint32_t *) src_addr; + + if (!dest_addr || !src_addr) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "%pK %pK %d", dest_addr, src_addr, len); + + for (i = 0; i < len/4; i++) { + CAM_DBG(CAM_UTIL, "0x%pK %08x", d, *s); + writel_relaxed(*s++, d++); + } + + return 0; +} + +int cam_io_memcpy_mb(void __iomem *dest_addr, + void __iomem *src_addr, uint32_t len) +{ + int i; + uint32_t *d = (uint32_t *) dest_addr; + uint32_t *s = (uint32_t *) src_addr; + + if (!dest_addr || !src_addr) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "%pK %pK %d", dest_addr, src_addr, len); + + /* + * Do not use cam_io_w_mb to avoid double wmb() after a write + * and before the next write. + */ + wmb(); + for (i = 0; i < (len / 4); i++) { + CAM_DBG(CAM_UTIL, "0x%pK %08x", d, *s); + writel_relaxed(*s++, d++); + } + /* Ensure previous writes are done */ + wmb(); + + return 0; +} + +int cam_io_poll_value(void __iomem *addr, uint32_t wait_data, uint32_t retry, + unsigned long min_usecs, unsigned long max_usecs) +{ + uint32_t tmp, cnt = 0; + int rc = 0; + + if (!addr) + return -EINVAL; + + tmp = readl_relaxed(addr); + while ((tmp != wait_data) && (cnt++ < retry)) { + if (min_usecs > 0 && max_usecs > 0) + usleep_range(min_usecs, max_usecs); + tmp = readl_relaxed(addr); + } + + if (cnt > retry) { + CAM_DBG(CAM_UTIL, "Poll failed by value"); + rc = -EINVAL; + } + + return rc; +} + +int cam_io_poll_value_wmask(void __iomem *addr, uint32_t wait_data, + uint32_t bmask, uint32_t retry, unsigned long min_usecs, + unsigned long max_usecs) +{ + uint32_t tmp, cnt = 0; + int rc = 0; + + if (!addr) + return -EINVAL; + + tmp = readl_relaxed(addr); + while (((tmp & bmask) != wait_data) && (cnt++ < retry)) { + if (min_usecs > 0 && max_usecs > 0) + usleep_range(min_usecs, max_usecs); + tmp = readl_relaxed(addr); + } + + if (cnt > retry) { + CAM_DBG(CAM_UTIL, "Poll failed with mask"); + rc = -EINVAL; + } + + return rc; +} + +int cam_io_w_same_offset_block(const uint32_t *data, void __iomem *addr, + uint32_t len) +{ + int i; + + if (!data || !len || !addr) + return -EINVAL; + + for (i = 0; i < len; i++) { + CAM_DBG(CAM_UTIL, "i= %d len =%d val=%x addr =%pK", + i, len, data[i], addr); + writel_relaxed(data[i], addr); + } + + return 0; +} + +int cam_io_w_mb_same_offset_block(const uint32_t *data, void __iomem *addr, + uint32_t len) +{ + int i; + + if (!data || !len || !addr) + return -EINVAL; + + for (i = 0; i < len; i++) { + CAM_DBG(CAM_UTIL, "i= %d len =%d val=%x addr =%pK", + i, len, data[i], addr); + /* Ensure previous writes are done */ + wmb(); + writel_relaxed(data[i], addr); + } + + return 0; +} + +#define __OFFSET(__i) (data[__i][0]) +#define __VAL(__i) (data[__i][1]) +int cam_io_w_offset_val_block(const uint32_t data[][2], + void __iomem *addr_base, uint32_t len) +{ + int i; + + if (!data || !len || !addr_base) + return -EINVAL; + + for (i = 0; i < len; i++) { + CAM_DBG(CAM_UTIL, "i= %d len =%d val=%x addr_base =%pK reg=%x", + i, len, __VAL(i), addr_base, __OFFSET(i)); + writel_relaxed(__VAL(i), addr_base + __OFFSET(i)); + } + + return 0; +} + +int cam_io_w_mb_offset_val_block(const uint32_t data[][2], + void __iomem *addr_base, uint32_t len) +{ + int i; + + if (!data || !len || !addr_base) + return -EINVAL; + + /* Ensure write is done */ + wmb(); + for (i = 0; i < len; i++) { + CAM_DBG(CAM_UTIL, "i= %d len =%d val=%x addr_base =%pK reg=%x", + i, len, __VAL(i), addr_base, __OFFSET(i)); + writel_relaxed(__VAL(i), addr_base + __OFFSET(i)); + } + + return 0; +} + +#define BYTES_PER_REGISTER 4 +#define NUM_REGISTER_PER_LINE 4 +#define REG_OFFSET(__start, __i) (__start + (__i * BYTES_PER_REGISTER)) +int cam_io_dump(void __iomem *base_addr, uint32_t start_offset, int size) +{ + char line_str[128]; + char *p_str; + int i; + uint32_t data; + + CAM_DBG(CAM_UTIL, "addr=%pK offset=0x%x size=%d", + base_addr, start_offset, size); + + if (!base_addr || (size <= 0)) + return -EINVAL; + + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size; i++) { + if (i % NUM_REGISTER_PER_LINE == 0) { + snprintf(p_str, 12, "0x%08x: ", + REG_OFFSET(start_offset, i)); + p_str += 11; + } + data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i)); + snprintf(p_str, 9, "%08x ", data); + p_str += 8; + if ((i + 1) % NUM_REGISTER_PER_LINE == 0) { + CAM_ERR(CAM_UTIL, "%s", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + CAM_ERR(CAM_UTIL, "%s", line_str); + + return 0; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.h new file mode 100644 index 000000000000..e4f73cae85b8 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_io_util.h @@ -0,0 +1,239 @@ +/* Copyright (c) 2011-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 + * 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 _CAM_IO_UTIL_H_ +#define _CAM_IO_UTIL_H_ + +#include + +/** + * cam_io_w() + * + * @brief: Camera IO util for register write + * + * @data: Value to be written + * @addr: Address used to write the value + * + * @return: Success or Failure + */ +int cam_io_w(uint32_t data, void __iomem *addr); + +/** + * cam_io_w_mb() + * + * @brief: Camera IO util for register write with memory barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call wmb() independently in the caller. + * + * @data: Value to be written + * @addr: Address used to write the value + * + * @return: Success or Failure + */ +int cam_io_w_mb(uint32_t data, void __iomem *addr); + +/** + * cam_io_r() + * + * @brief: Camera IO util for register read + * + * @addr: Address of register to be read + * + * @return: Value read from the register address + */ +uint32_t cam_io_r(void __iomem *addr); + +/** + * cam_io_r_mb() + * + * @brief: Camera IO util for register read with memory barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call rmb() independently in the caller. + * + * @addr: Address of register to be read + * + * @return: Value read from the register address + */ +uint32_t cam_io_r_mb(void __iomem *addr); + +/** + * cam_io_memcpy() + * + * @brief: Camera IO util for memory to register copy + * + * @dest_addr: Destination register address + * @src_addr: Source regiser address + * @len: Range to be copied + * + * @return: Success or Failure + */ +int cam_io_memcpy(void __iomem *dest_addr, + void __iomem *src_addr, uint32_t len); + +/** + * cam_io_memcpy_mb() + * + * @brief: Camera IO util for memory to register copy + * with barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call wmb() independently in the caller. + * + * @dest_addr: Destination register address + * @src_addr: Source regiser address + * @len: Range to be copied + * + * @return: Success or Failure + */ +int cam_io_memcpy_mb(void __iomem *dest_addr, + void __iomem *src_addr, uint32_t len); + +/** + * cam_io_poll_value_wmask() + * + * @brief: Poll register value with bitmask. + * + * @addr: Register address to be polled + * @wait_data: Wait until @bmask read from @addr matches this data + * @bmask: Bit mask + * @retry: Number of retry + * @min_usecs: Minimum time to wait for retry + * @max_usecs: Maximum time to wait for retry + * + * @return: Success or Failure + * + * This function can sleep so it should not be called from interrupt + * handler, spin_lock etc. + */ +int cam_io_poll_value_wmask(void __iomem *addr, uint32_t wait_data, + uint32_t bmask, uint32_t retry, unsigned long min_usecs, + unsigned long max_usecs); + +/** + * cam_io_poll_value() + * + * @brief: Poll register value + * + * @addr: Register address to be polled + * @wait_data: Wait until value read from @addr matches this data + * @retry: Number of retry + * @min_usecs: Minimum time to wait for retry + * @max_usecs: Maximum time to wait for retry + * + * @return: Success or Failure + * + * This function can sleep so it should not be called from interrupt + * handler, spin_lock etc. + */ +int cam_io_poll_value(void __iomem *addr, uint32_t wait_data, uint32_t retry, + unsigned long min_usecs, unsigned long max_usecs); + +/** + * cam_io_w_same_offset_block() + * + * @brief: Write a block of data to same address + * + * @data: Block data to be written + * @addr: Register offset to be written. + * @len: Number of the data to be written + * + * @return: Success or Failure + */ +int cam_io_w_same_offset_block(const uint32_t *data, void __iomem *addr, + uint32_t len); + +/** + * cam_io_w_mb_same_offset_block() + * + * @brief: Write a block of data to same address with barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call wmb() independently in the caller. + * + * @data: Block data to be written + * @addr: Register offset to be written. + * @len: Number of the data to be written + * + * @return: Success or Failure + */ +int cam_io_w_mb_same_offset_block(const uint32_t *data, void __iomem *addr, + uint32_t len); + +/** + * cam_io_w_offset_val_block() + * + * @brief: This API is to write a block of registers + * represented by a 2 dimensional array table with + * register offset and value pair + * + * offset0, value0, + * offset1, value1, + * offset2, value2, + * and so on... + * + * @data: Pointer to 2-dimensional offset-value array + * @addr_base: Base address to which offset will be added to + * get the register address + * @len: Length of offset-value pair array to be written in + * number of uin32_t + * + * @return: Success or Failure + * + */ +int32_t cam_io_w_offset_val_block(const uint32_t data[][2], + void __iomem *addr_base, uint32_t len); + +/** + * cam_io_w_mb_offset_val_block() + * + * @brief: This API is to write a block of registers + * represented by a 2 dimensional array table with + * register offset and value pair with memory barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call wmb() independently in the caller. + * The OFFSETS NEED to be different because of the way + * barrier is used here. + * + * offset0, value0, + * offset1, value1, + * offset2, value2, + * and so on... + * + * @data: Pointer to 2-dimensional offset-value array + * @addr_base: Base address to which offset will be added to + * get the register address + * @len: Length of offset-value pair array to be written in + * number of uin32_t + * + * @return: Success or Failure + * + */ +int32_t cam_io_w_mb_offset_val_block(const uint32_t data[][2], + void __iomem *addr_base, uint32_t len); + +/** + * cam_io_dump() + * + * @brief: Camera IO util for dumping a range of register + * + * @base_addr: Start register address for the dumping + * @start_offset: Start register offset for the dump + * @size: Size specifying the range for dumping + * + * @return: Success or Failure + */ +int cam_io_dump(void __iomem *base_addr, uint32_t start_offset, int size); + +#endif /* _CAM_IO_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.c new file mode 100644 index 000000000000..8bccc55f73d1 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.c @@ -0,0 +1,270 @@ +/* Copyright (c) 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 +#include + +#include "cam_mem_mgr.h" +#include "cam_packet_util.h" +#include "cam_debug_util.h" + +int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr, + size_t *len) +{ + int rc = 0; + uintptr_t kmd_buf_addr = 0; + + rc = cam_mem_get_cpu_buf(handle, &kmd_buf_addr, len); + if (rc) { + CAM_ERR(CAM_UTIL, "Unable to get the virtual address %d", rc); + } else { + if (kmd_buf_addr && *len) { + *buf_addr = (uint32_t *)kmd_buf_addr; + } else { + CAM_ERR(CAM_UTIL, "Invalid addr and length :%ld", *len); + rc = -ENOMEM; + } + } + return rc; +} + +int cam_packet_util_validate_cmd_desc(struct cam_cmd_buf_desc *cmd_desc) +{ + + if (!cmd_desc) { + CAM_ERR(CAM_UTIL, "Invalid cmd desc"); + return -EINVAL; + } + + if ((cmd_desc->length > cmd_desc->size) || + (cmd_desc->mem_handle <= 0)) { + CAM_ERR(CAM_UTIL, "invalid cmd arg %d %d %d %d", + cmd_desc->offset, cmd_desc->length, + cmd_desc->mem_handle, cmd_desc->size); + return -EINVAL; + } + + return 0; +} + +int cam_packet_util_validate_packet(struct cam_packet *packet) +{ + if (!packet) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "num cmd buf:%d num of io config:%d kmd buf index:%d", + packet->num_cmd_buf, packet->num_io_configs, + packet->kmd_cmd_buf_index); + + if ((packet->kmd_cmd_buf_index >= packet->num_cmd_buf) || + (!packet->header.size) || + (packet->cmd_buf_offset > packet->header.size) || + (packet->io_configs_offset > packet->header.size)) { + CAM_ERR(CAM_UTIL, "invalid packet:%d %d %d %d %d", + packet->kmd_cmd_buf_index, + packet->num_cmd_buf, packet->cmd_buf_offset, + packet->io_configs_offset, packet->header.size); + return -EINVAL; + } + + return 0; +} + +int cam_packet_util_get_kmd_buffer(struct cam_packet *packet, + struct cam_kmd_buf_info *kmd_buf) +{ + int rc = 0; + size_t len = 0; + struct cam_cmd_buf_desc *cmd_desc; + uint32_t *cpu_addr; + + if (!packet || !kmd_buf) { + CAM_ERR(CAM_UTIL, "Invalid arg %pK %pK", packet, kmd_buf); + return -EINVAL; + } + + /* Take first command descriptor and add offset to it for kmd*/ + cmd_desc = (struct cam_cmd_buf_desc *) ((uint8_t *) + &packet->payload + packet->cmd_buf_offset); + cmd_desc += packet->kmd_cmd_buf_index; + + rc = cam_packet_util_validate_cmd_desc(cmd_desc); + if (rc) + return rc; + + rc = cam_packet_util_get_cmd_mem_addr(cmd_desc->mem_handle, &cpu_addr, + &len); + if (rc) + return rc; + + if (len < cmd_desc->size) { + CAM_ERR(CAM_UTIL, "invalid memory len:%ld and cmd desc size:%d", + len, cmd_desc->size); + return -EINVAL; + } + + cpu_addr += (cmd_desc->offset / 4) + (packet->kmd_cmd_buf_offset / 4); + CAM_DBG(CAM_UTIL, "total size %d, cmd size: %d, KMD buffer size: %d", + cmd_desc->size, cmd_desc->length, + cmd_desc->size - cmd_desc->length); + CAM_DBG(CAM_UTIL, "hdl 0x%x, cmd offset %d, kmd offset %d, addr 0x%pK", + cmd_desc->mem_handle, cmd_desc->offset, + packet->kmd_cmd_buf_offset, cpu_addr); + + kmd_buf->cpu_addr = cpu_addr; + kmd_buf->handle = cmd_desc->mem_handle; + kmd_buf->offset = cmd_desc->offset + packet->kmd_cmd_buf_offset; + kmd_buf->size = cmd_desc->size - cmd_desc->length; + kmd_buf->used_bytes = 0; + + return rc; +} + +int cam_packet_util_process_patches(struct cam_packet *packet, + int32_t iommu_hdl, int32_t sec_mmu_hdl) +{ + struct cam_patch_desc *patch_desc = NULL; + dma_addr_t iova_addr; + uintptr_t cpu_addr = 0; + uint32_t temp; + uint32_t *dst_cpu_addr; + uint32_t *src_buf_iova_addr; + size_t dst_buf_len; + size_t src_buf_size; + int i; + int rc = 0; + int32_t hdl; + + /* process patch descriptor */ + patch_desc = (struct cam_patch_desc *) + ((uint32_t *) &packet->payload + + packet->patch_offset/4); + CAM_DBG(CAM_UTIL, "packet = %pK patch_desc = %pK size = %lu", + (void *)packet, (void *)patch_desc, + sizeof(struct cam_patch_desc)); + + for (i = 0; i < packet->num_patches; i++) { + + hdl = cam_mem_is_secure_buf(patch_desc[i].src_buf_hdl) ? + sec_mmu_hdl : iommu_hdl; + rc = cam_mem_get_io_buf(patch_desc[i].src_buf_hdl, + hdl, &iova_addr, &src_buf_size); + if (rc < 0) { + CAM_ERR(CAM_UTIL, "unable to get src buf address"); + return rc; + } + src_buf_iova_addr = (uint32_t *)iova_addr; + temp = iova_addr; + + rc = cam_mem_get_cpu_buf(patch_desc[i].dst_buf_hdl, + &cpu_addr, &dst_buf_len); + if (rc < 0 || !cpu_addr || (dst_buf_len == 0)) { + CAM_ERR(CAM_UTIL, "unable to get dst buf address"); + return rc; + } + dst_cpu_addr = (uint32_t *)cpu_addr; + + CAM_DBG(CAM_UTIL, "i = %d patch info = %x %x %x %x", i, + patch_desc[i].dst_buf_hdl, patch_desc[i].dst_offset, + patch_desc[i].src_buf_hdl, patch_desc[i].src_offset); + + dst_cpu_addr = (uint32_t *)((uint8_t *)dst_cpu_addr + + patch_desc[i].dst_offset); + temp += patch_desc[i].src_offset; + + *dst_cpu_addr = temp; + + CAM_DBG(CAM_UTIL, + "patch is done for dst %pK with src %pK value %llx", + dst_cpu_addr, src_buf_iova_addr, + *((uint64_t *)dst_cpu_addr)); + } + + return rc; +} + +int cam_packet_util_process_generic_cmd_buffer( + struct cam_cmd_buf_desc *cmd_buf, + cam_packet_generic_blob_handler blob_handler_cb, void *user_data) +{ + int rc = 0; + uintptr_t cpu_addr = 0; + size_t buf_size; + uint32_t *blob_ptr; + uint32_t blob_type, blob_size, blob_block_size, len_read; + + if (!cmd_buf || !blob_handler_cb) { + CAM_ERR(CAM_UTIL, "Invalid args %pK %pK", + cmd_buf, blob_handler_cb); + return -EINVAL; + } + + if (!cmd_buf->length || !cmd_buf->size) { + CAM_ERR(CAM_UTIL, "Invalid cmd buf size %d %d", + cmd_buf->length, cmd_buf->size); + return -EINVAL; + } + + rc = cam_mem_get_cpu_buf(cmd_buf->mem_handle, &cpu_addr, &buf_size); + if (rc || !cpu_addr || (buf_size == 0)) { + CAM_ERR(CAM_UTIL, "Failed in Get cpu addr, rc=%d, cpu_addr=%pK", + rc, (void *)cpu_addr); + return rc; + } + + blob_ptr = (uint32_t *)(((uint8_t *)cpu_addr) + + cmd_buf->offset); + + CAM_DBG(CAM_UTIL, + "GenericCmdBuffer cpuaddr=%pK, blobptr=%pK, len=%d", + (void *)cpu_addr, (void *)blob_ptr, cmd_buf->length); + + len_read = 0; + while (len_read < cmd_buf->length) { + blob_type = + ((*blob_ptr) & CAM_GENERIC_BLOB_CMDBUFFER_TYPE_MASK) >> + CAM_GENERIC_BLOB_CMDBUFFER_TYPE_SHIFT; + blob_size = + ((*blob_ptr) & CAM_GENERIC_BLOB_CMDBUFFER_SIZE_MASK) >> + CAM_GENERIC_BLOB_CMDBUFFER_SIZE_SHIFT; + + blob_block_size = sizeof(uint32_t) + + (((blob_size + sizeof(uint32_t) - 1) / + sizeof(uint32_t)) * sizeof(uint32_t)); + + CAM_DBG(CAM_UTIL, + "Blob type=%d size=%d block_size=%d len_read=%d total=%d", + blob_type, blob_size, blob_block_size, len_read, + cmd_buf->length); + + if (len_read + blob_block_size > cmd_buf->length) { + CAM_ERR(CAM_UTIL, "Invalid Blob %d %d %d %d", + blob_type, blob_size, len_read, + cmd_buf->length); + return -EINVAL; + } + + len_read += blob_block_size; + + rc = blob_handler_cb(user_data, blob_type, blob_size, + (uint8_t *)(blob_ptr + 1)); + if (rc) { + CAM_ERR(CAM_UTIL, "Error in handling blob type %d %d", + blob_type, blob_size); + return rc; + } + + blob_ptr += (blob_block_size / sizeof(uint32_t)); + } + + return 0; +} diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.h new file mode 100644 index 000000000000..94d269313c7d --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_packet_util.h @@ -0,0 +1,130 @@ +/* Copyright (c) 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. + */ + +#ifndef _CAM_PACKET_UTIL_H_ +#define _CAM_PACKET_UTIL_H_ + +#include + +/** + * @brief KMD scratch buffer information + * + * @handle: Memory handle + * @cpu_addr: Cpu address + * @offset: Offset from the start of the buffer + * @size: Size of the buffer + * @used_bytes: Used memory in bytes + * + */ +struct cam_kmd_buf_info { + int handle; + uint32_t *cpu_addr; + uint32_t offset; + uint32_t size; + uint32_t used_bytes; +}; + +/* Generic Cmd Buffer blob callback function type */ +typedef int (*cam_packet_generic_blob_handler)(void *user_data, + uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data); + +/** + * cam_packet_util_get_cmd_mem_addr() + * + * @brief Get command buffer address + * + * @handle: Command buffer memory handle + * @buf_addr: Command buffer cpu mapped address + * @len: Command buffer length + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr, + size_t *len); + +/** + * cam_packet_util_validate_packet() + * + * @brief Validate the packet + * + * @packet: Packet to be validated + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_packet_util_validate_packet(struct cam_packet *packet); + +/** + * cam_packet_util_validate_cmd_desc() + * + * @brief Validate the packet + * + * @cmd_desc: Command descriptor to be validated + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_packet_util_validate_cmd_desc(struct cam_cmd_buf_desc *cmd_desc); + +/** + * cam_packet_util_get_kmd_buffer() + * + * @brief Get the kmd buffer from the packet command descriptor + * + * @packet: Packet data + * @kmd_buf: Extracted the KMD buffer information + * + * @return: 0 for success + * -EINVAL for Fail + */ +int cam_packet_util_get_kmd_buffer(struct cam_packet *packet, + struct cam_kmd_buf_info *kmd_buf_info); + +/** + * cam_packet_util_process_patches() + * + * @brief: Replace the handle in Packet to Address using the + * information from patches. + * + * @packet: Input packet containing Command Buffers and Patches + * @iommu_hdl: IOMMU handle of the HW Device that received the packet + * @sec_iommu_hdl: Secure IOMMU handle of the HW Device that + * received the packet + * + * @return: 0: Success + * Negative: Failure + */ +int cam_packet_util_process_patches(struct cam_packet *packet, + int32_t iommu_hdl, int32_t sec_mmu_hdl); + +/** + * cam_packet_util_process_generic_cmd_buffer() + * + * @brief: Process Generic Blob command buffer. This utility + * function process the command buffer and calls the + * blob_handle_cb callback for each blob that exists + * in the command buffer. + * + * @cmd_buf: Generic Blob Cmd Buffer handle + * @blob_handler_cb: Callback pointer to call for each blob exists in the + * command buffer + * @user_data: User data to be passed while callback + * + * @return: 0: Success + * Negative: Failure + */ +int cam_packet_util_process_generic_cmd_buffer( + struct cam_cmd_buf_desc *cmd_buf, + cam_packet_generic_blob_handler blob_handler_cb, void *user_data); + +#endif /* _CAM_PACKET_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.c new file mode 100644 index 000000000000..0b1896fc9ed6 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.c @@ -0,0 +1,1403 @@ +/* Copyright (c) 2015-2018, 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 +#include +#include +#include +#include +#include "cam_soc_util.h" +#include "cam_debug_util.h" + +int cam_soc_util_get_level_from_string(const char *string, + enum cam_vote_level *level) +{ + if (!level) + return -EINVAL; + + if (!strcmp(string, "suspend")) { + *level = CAM_SUSPEND_VOTE; + } else if (!strcmp(string, "minsvs")) { + *level = CAM_MINSVS_VOTE; + } else if (!strcmp(string, "lowsvs")) { + *level = CAM_LOWSVS_VOTE; + } else if (!strcmp(string, "svs")) { + *level = CAM_SVS_VOTE; + } else if (!strcmp(string, "svs_l1")) { + *level = CAM_SVSL1_VOTE; + } else if (!strcmp(string, "nominal")) { + *level = CAM_NOMINAL_VOTE; + } else if (!strcmp(string, "turbo")) { + *level = CAM_TURBO_VOTE; + } else { + CAM_ERR(CAM_UTIL, "Invalid string %s", string); + return -EINVAL; + } + + return 0; +} + +/** + * cam_soc_util_get_clk_level_to_apply() + * + * @brief: Get the clock level to apply. If the requested level + * is not valid, bump the level to next available valid + * level. If no higher level found, return failure. + * + * @soc_info: Device soc struct to be populated + * @req_level: Requested level + * @apply_level Level to apply + * + * @return: success or failure + */ +static int cam_soc_util_get_clk_level_to_apply( + struct cam_hw_soc_info *soc_info, enum cam_vote_level req_level, + enum cam_vote_level *apply_level) +{ + if (req_level >= CAM_MAX_VOTE) { + CAM_ERR(CAM_UTIL, "Invalid clock level parameter %d", + req_level); + return -EINVAL; + } + + if (soc_info->clk_level_valid[req_level] == true) { + *apply_level = req_level; + } else { + int i; + + for (i = (req_level + 1); i < CAM_MAX_VOTE; i++) + if (soc_info->clk_level_valid[i] == true) { + *apply_level = i; + break; + } + + if (i == CAM_MAX_VOTE) { + CAM_ERR(CAM_UTIL, + "No valid clock level found to apply, req=%d", + req_level); + return -EINVAL; + } + } + + CAM_DBG(CAM_UTIL, "Req level %d, Applying %d", + req_level, *apply_level); + + return 0; +} + +int cam_soc_util_irq_enable(struct cam_hw_soc_info *soc_info) +{ + if (!soc_info) { + CAM_ERR(CAM_UTIL, "Invalid arguments"); + return -EINVAL; + } + + if (!soc_info->irq_line) { + CAM_ERR(CAM_UTIL, "No IRQ line available"); + return -ENODEV; + } + + enable_irq(soc_info->irq_line->start); + + return 0; +} + +int cam_soc_util_irq_disable(struct cam_hw_soc_info *soc_info) +{ + if (!soc_info) { + CAM_ERR(CAM_UTIL, "Invalid arguments"); + return -EINVAL; + } + + if (!soc_info->irq_line) { + CAM_ERR(CAM_UTIL, "No IRQ line available"); + return -ENODEV; + } + + disable_irq(soc_info->irq_line->start); + + return 0; +} + +long cam_soc_util_get_clk_round_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_index, unsigned long clk_rate) +{ + if (!soc_info || (clk_index >= soc_info->num_clk) || (clk_rate == 0)) { + CAM_ERR(CAM_UTIL, "Invalid input params %pK, %d %lu", + soc_info, clk_index, clk_rate); + return clk_rate; + } + + return clk_round_rate(soc_info->clk[clk_index], clk_rate); +} + +int cam_soc_util_set_clk_flags(struct cam_hw_soc_info *soc_info, + uint32_t clk_index, unsigned long flags) +{ + if (!soc_info || (clk_index >= soc_info->num_clk)) { + CAM_ERR(CAM_UTIL, "Invalid input params %pK, %d", + soc_info, clk_index); + return -EINVAL; + } + + return clk_set_flags(soc_info->clk[clk_index], flags); +} + +int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name, + int32_t clk_rate) +{ + int rc = 0; + long clk_rate_round; + + if (!clk || !clk_name) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "set %s, rate %d", clk_name, clk_rate); + if (clk_rate > 0) { + clk_rate_round = clk_round_rate(clk, clk_rate); + CAM_DBG(CAM_UTIL, "new_rate %ld", clk_rate_round); + if (clk_rate_round < 0) { + CAM_ERR(CAM_UTIL, "round failed for clock %s rc = %ld", + clk_name, clk_rate_round); + return clk_rate_round; + } + rc = clk_set_rate(clk, clk_rate_round); + if (rc) { + CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name); + return rc; + } + } else if (clk_rate == INIT_RATE) { + clk_rate_round = clk_get_rate(clk); + CAM_DBG(CAM_UTIL, "init new_rate %ld", clk_rate_round); + if (clk_rate_round == 0) { + clk_rate_round = clk_round_rate(clk, 0); + if (clk_rate_round <= 0) { + CAM_ERR(CAM_UTIL, "round rate failed on %s", + clk_name); + return clk_rate_round; + } + } + rc = clk_set_rate(clk, clk_rate_round); + if (rc) { + CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name); + return rc; + } + } + + return rc; +} + +int cam_soc_util_clk_put(struct clk **clk) +{ + if (!(*clk)) { + CAM_ERR(CAM_UTIL, "Invalid params clk"); + return -EINVAL; + } + + clk_put(*clk); + *clk = NULL; + + return 0; +} + +static struct clk *cam_soc_util_option_clk_get(struct device_node *np, + int index) +{ + struct of_phandle_args clkspec; + struct clk *clk; + int rc; + + if (index < 0) + return ERR_PTR(-EINVAL); + + rc = of_parse_phandle_with_args(np, "clocks-option", "#clock-cells", + index, &clkspec); + if (rc) + return ERR_PTR(rc); + + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + + return clk; +} + +int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info, + const char *clk_name, struct clk **clk, int32_t *clk_index, + int32_t *clk_rate) +{ + int index = 0; + int rc = 0; + struct device_node *of_node = NULL; + + if (!soc_info || !clk_name || !clk) { + CAM_ERR(CAM_UTIL, + "Invalid params soc_info %pK clk_name %s clk %pK", + soc_info, clk_name, clk); + return -EINVAL; + } + + of_node = soc_info->dev->of_node; + + index = of_property_match_string(of_node, "clock-names-option", + clk_name); + + *clk = cam_soc_util_option_clk_get(of_node, index); + if (IS_ERR(*clk)) { + CAM_ERR(CAM_UTIL, "No clk named %s found. Dev %s", clk_name, + soc_info->dev_name); + *clk_index = -1; + return -EFAULT; + } + *clk_index = index; + + rc = of_property_read_u32_index(of_node, "clock-rates-option", + index, clk_rate); + if (rc) { + CAM_ERR(CAM_UTIL, + "Error reading clock-rates clk_name %s index %d", + clk_name, index); + cam_soc_util_clk_put(clk); + *clk_rate = 0; + return rc; + } + + /* + * Option clocks are assumed to be available to single Device here. + * Hence use INIT_RATE instead of NO_SET_RATE. + */ + *clk_rate = (*clk_rate == 0) ? (int32_t)INIT_RATE : *clk_rate; + + CAM_DBG(CAM_UTIL, "clk_name %s index %d clk_rate %d", + clk_name, *clk_index, *clk_rate); + + return 0; +} + +int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name, + int32_t clk_rate) +{ + int rc = 0; + + if (!clk || !clk_name) + return -EINVAL; + + rc = cam_soc_util_set_clk_rate(clk, clk_name, clk_rate); + if (rc) + return rc; + + rc = clk_prepare_enable(clk); + if (rc) { + CAM_ERR(CAM_UTIL, "enable failed for %s: rc(%d)", clk_name, rc); + return rc; + } + + return rc; +} + +int cam_soc_util_clk_disable(struct clk *clk, const char *clk_name) +{ + if (!clk || !clk_name) + return -EINVAL; + + CAM_DBG(CAM_UTIL, "disable %s", clk_name); + clk_disable_unprepare(clk); + + return 0; +} + +/** + * cam_soc_util_clk_enable_default() + * + * @brief: This function enables the default clocks present + * in soc_info + * + * @soc_info: Device soc struct to be populated + * @clk_level: Clk level to apply while enabling + * + * @return: success or failure + */ +int cam_soc_util_clk_enable_default(struct cam_hw_soc_info *soc_info, + enum cam_vote_level clk_level) +{ + int i, rc = 0; + enum cam_vote_level apply_level; + + if ((soc_info->num_clk == 0) || + (soc_info->num_clk >= CAM_SOC_MAX_CLK)) { + CAM_ERR(CAM_UTIL, "Invalid number of clock %d", + soc_info->num_clk); + return -EINVAL; + } + + rc = cam_soc_util_get_clk_level_to_apply(soc_info, clk_level, + &apply_level); + if (rc) + return rc; + + for (i = 0; i < soc_info->num_clk; i++) { + rc = cam_soc_util_clk_enable(soc_info->clk[i], + soc_info->clk_name[i], + soc_info->clk_rate[apply_level][i]); + if (rc) + goto clk_disable; + } + + return rc; + +clk_disable: + for (i--; i >= 0; i--) { + cam_soc_util_clk_disable(soc_info->clk[i], + soc_info->clk_name[i]); + } + + return rc; +} + +/** + * cam_soc_util_clk_disable_default() + * + * @brief: This function disables the default clocks present + * in soc_info + * + * @soc_info: device soc struct to be populated + * + * @return: success or failure + */ +void cam_soc_util_clk_disable_default(struct cam_hw_soc_info *soc_info) +{ + int i; + + if (soc_info->num_clk == 0) + return; + + for (i = soc_info->num_clk - 1; i >= 0; i--) + cam_soc_util_clk_disable(soc_info->clk[i], + soc_info->clk_name[i]); +} + +/** + * cam_soc_util_get_dt_clk_info() + * + * @brief: Parse the DT and populate the Clock properties + * + * @soc_info: device soc struct to be populated + * @src_clk_str name of src clock that has rate control + * + * @return: success or failure + */ +static int cam_soc_util_get_dt_clk_info(struct cam_hw_soc_info *soc_info) +{ + struct device_node *of_node = NULL; + int count; + int num_clk_rates, num_clk_levels; + int i, j, rc; + int32_t num_clk_level_strings; + const char *src_clk_str = NULL; + const char *clk_cntl_lvl_string = NULL; + enum cam_vote_level level; + + if (!soc_info || !soc_info->dev) + return -EINVAL; + + of_node = soc_info->dev->of_node; + + if (!of_property_read_bool(of_node, "use-shared-clk")) { + CAM_DBG(CAM_UTIL, "No shared clk parameter defined"); + soc_info->use_shared_clk = false; + } else { + soc_info->use_shared_clk = true; + } + + count = of_property_count_strings(of_node, "clock-names"); + + CAM_DBG(CAM_UTIL, "count = %d", count); + if (count > CAM_SOC_MAX_CLK) { + CAM_ERR(CAM_UTIL, "invalid count of clocks, count=%d", count); + rc = -EINVAL; + return rc; + } + if (count <= 0) { + CAM_DBG(CAM_UTIL, "No clock-names found"); + count = 0; + soc_info->num_clk = count; + return 0; + } + soc_info->num_clk = count; + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, "clock-names", + i, &(soc_info->clk_name[i])); + CAM_DBG(CAM_UTIL, "clock-names[%d] = %s", + i, soc_info->clk_name[i]); + if (rc) { + CAM_ERR(CAM_UTIL, + "i= %d count= %d reading clock-names failed", + i, count); + return rc; + } + } + + num_clk_rates = of_property_count_u32_elems(of_node, "clock-rates"); + if (num_clk_rates <= 0) { + CAM_ERR(CAM_UTIL, "reading clock-rates count failed"); + return -EINVAL; + } + + if ((num_clk_rates % soc_info->num_clk) != 0) { + CAM_ERR(CAM_UTIL, + "mismatch clk/rates, No of clocks=%d, No of rates=%d", + soc_info->num_clk, num_clk_rates); + return -EINVAL; + } + + num_clk_levels = (num_clk_rates / soc_info->num_clk); + + num_clk_level_strings = of_property_count_strings(of_node, + "clock-cntl-level"); + if (num_clk_level_strings != num_clk_levels) { + CAM_ERR(CAM_UTIL, + "Mismatch No of levels=%d, No of level string=%d", + num_clk_levels, num_clk_level_strings); + return -EINVAL; + } + + for (i = 0; i < num_clk_levels; i++) { + rc = of_property_read_string_index(of_node, + "clock-cntl-level", i, &clk_cntl_lvl_string); + if (rc) { + CAM_ERR(CAM_UTIL, + "Error reading clock-cntl-level, rc=%d", rc); + return rc; + } + + rc = cam_soc_util_get_level_from_string(clk_cntl_lvl_string, + &level); + if (rc) + return rc; + + CAM_DBG(CAM_UTIL, + "[%d] : %s %d", i, clk_cntl_lvl_string, level); + soc_info->clk_level_valid[level] = true; + for (j = 0; j < soc_info->num_clk; j++) { + rc = of_property_read_u32_index(of_node, "clock-rates", + ((i * soc_info->num_clk) + j), + &soc_info->clk_rate[level][j]); + if (rc) { + CAM_ERR(CAM_UTIL, + "Error reading clock-rates, rc=%d", + rc); + return rc; + } + + soc_info->clk_rate[level][j] = + (soc_info->clk_rate[level][j] == 0) ? + (int32_t)NO_SET_RATE : + soc_info->clk_rate[level][j]; + + CAM_DBG(CAM_UTIL, "soc_info->clk_rate[%d][%d] = %d", + level, j, + soc_info->clk_rate[level][j]); + } + } + + soc_info->src_clk_idx = -1; + rc = of_property_read_string_index(of_node, "src-clock-name", 0, + &src_clk_str); + if (rc || !src_clk_str) { + CAM_DBG(CAM_UTIL, "No src_clk_str found"); + rc = 0; + /* Bottom loop is dependent on src_clk_str. So return here */ + return rc; + } + + for (i = 0; i < soc_info->num_clk; i++) { + if (strcmp(soc_info->clk_name[i], src_clk_str) == 0) { + soc_info->src_clk_idx = i; + CAM_DBG(CAM_UTIL, "src clock = %s, index = %d", + src_clk_str, i); + break; + } + } + + return rc; +} + +int cam_soc_util_set_clk_rate_level(struct cam_hw_soc_info *soc_info, + enum cam_vote_level clk_level) +{ + int i, rc = 0; + enum cam_vote_level apply_level; + + if ((soc_info->num_clk == 0) || + (soc_info->num_clk >= CAM_SOC_MAX_CLK)) { + CAM_ERR(CAM_UTIL, "Invalid number of clock %d", + soc_info->num_clk); + return -EINVAL; + } + + rc = cam_soc_util_get_clk_level_to_apply(soc_info, clk_level, + &apply_level); + if (rc) + return rc; + + for (i = 0; i < soc_info->num_clk; i++) { + rc = cam_soc_util_set_clk_rate(soc_info->clk[i], + soc_info->clk_name[i], + soc_info->clk_rate[apply_level][i]); + if (rc) + break; + } + + return rc; +}; + +static int cam_soc_util_get_dt_gpio_req_tbl(struct device_node *of_node, + struct cam_soc_gpio_data *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int32_t rc = 0, i = 0; + uint32_t count = 0; + uint32_t *val_array = NULL; + + if (!of_get_property(of_node, "gpio-req-tbl-num", &count)) + return 0; + + count /= sizeof(uint32_t); + if (!count) { + CAM_ERR(CAM_UTIL, "gpio-req-tbl-num 0"); + return 0; + } + + val_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL); + if (!val_array) + return -ENOMEM; + + gconf->cam_gpio_req_tbl = kcalloc(count, sizeof(struct gpio), + GFP_KERNEL); + if (!gconf->cam_gpio_req_tbl) { + rc = -ENOMEM; + goto free_val_array; + } + gconf->cam_gpio_req_tbl_size = count; + + rc = of_property_read_u32_array(of_node, "gpio-req-tbl-num", + val_array, count); + if (rc) { + CAM_ERR(CAM_UTIL, "failed in reading gpio-req-tbl-num, rc = %d", + rc); + goto free_gpio_req_tbl; + } + + for (i = 0; i < count; i++) { + if (val_array[i] >= gpio_array_size) { + CAM_ERR(CAM_UTIL, "gpio req tbl index %d invalid", + val_array[i]); + goto free_gpio_req_tbl; + } + gconf->cam_gpio_req_tbl[i].gpio = gpio_array[val_array[i]]; + CAM_DBG(CAM_UTIL, "cam_gpio_req_tbl[%d].gpio = %d", i, + gconf->cam_gpio_req_tbl[i].gpio); + } + + rc = of_property_read_u32_array(of_node, "gpio-req-tbl-flags", + val_array, count); + if (rc) { + CAM_ERR(CAM_UTIL, "Failed in gpio-req-tbl-flags, rc %d", rc); + goto free_gpio_req_tbl; + } + + for (i = 0; i < count; i++) { + gconf->cam_gpio_req_tbl[i].flags = val_array[i]; + CAM_DBG(CAM_UTIL, "cam_gpio_req_tbl[%d].flags = %ld", i, + gconf->cam_gpio_req_tbl[i].flags); + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "gpio-req-tbl-label", i, + &gconf->cam_gpio_req_tbl[i].label); + if (rc) { + CAM_ERR(CAM_UTIL, "Failed rc %d", rc); + goto free_gpio_req_tbl; + } + CAM_DBG(CAM_UTIL, "cam_gpio_req_tbl[%d].label = %s", i, + gconf->cam_gpio_req_tbl[i].label); + } + + kfree(val_array); + + return rc; + +free_gpio_req_tbl: + kfree(gconf->cam_gpio_req_tbl); +free_val_array: + kfree(val_array); + gconf->cam_gpio_req_tbl_size = 0; + + return rc; +} + +static int cam_soc_util_get_gpio_info(struct cam_hw_soc_info *soc_info) +{ + int32_t rc = 0, i = 0; + uint16_t *gpio_array = NULL; + int16_t gpio_array_size = 0; + struct cam_soc_gpio_data *gconf = NULL; + struct device_node *of_node = NULL; + + if (!soc_info || !soc_info->dev) + return -EINVAL; + + of_node = soc_info->dev->of_node; + + /* Validate input parameters */ + if (!of_node) { + CAM_ERR(CAM_UTIL, "Invalid param of_node"); + return -EINVAL; + } + + gpio_array_size = of_gpio_count(of_node); + + if (gpio_array_size <= 0) + return 0; + + CAM_DBG(CAM_UTIL, "gpio count %d", gpio_array_size); + + gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), GFP_KERNEL); + if (!gpio_array) + goto free_gpio_conf; + + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + CAM_DBG(CAM_UTIL, "gpio_array[%d] = %d", i, gpio_array[i]); + } + + gconf = kzalloc(sizeof(*gconf), GFP_KERNEL); + if (!gconf) + return -ENOMEM; + + rc = cam_soc_util_get_dt_gpio_req_tbl(of_node, gconf, gpio_array, + gpio_array_size); + if (rc) { + CAM_ERR(CAM_UTIL, "failed in msm_camera_get_dt_gpio_req_tbl"); + goto free_gpio_array; + } + + gconf->cam_gpio_common_tbl = kcalloc(gpio_array_size, + sizeof(struct gpio), GFP_KERNEL); + if (!gconf->cam_gpio_common_tbl) { + rc = -ENOMEM; + goto free_gpio_array; + } + + for (i = 0; i < gpio_array_size; i++) + gconf->cam_gpio_common_tbl[i].gpio = gpio_array[i]; + + gconf->cam_gpio_common_tbl_size = gpio_array_size; + soc_info->gpio_data = gconf; + kfree(gpio_array); + + return rc; + +free_gpio_array: + kfree(gpio_array); +free_gpio_conf: + kfree(gconf); + soc_info->gpio_data = NULL; + + return rc; +} + +static int cam_soc_util_request_gpio_table( + struct cam_hw_soc_info *soc_info, bool gpio_en) +{ + int rc = 0, i = 0; + uint8_t size = 0; + struct cam_soc_gpio_data *gpio_conf = + soc_info->gpio_data; + struct gpio *gpio_tbl = NULL; + + + if (!gpio_conf) { + CAM_DBG(CAM_UTIL, "No GPIO entry"); + return 0; + } + if (gpio_conf->cam_gpio_common_tbl_size <= 0) { + CAM_ERR(CAM_UTIL, "GPIO table size is invalid"); + return -EINVAL; + } + size = gpio_conf->cam_gpio_req_tbl_size; + gpio_tbl = gpio_conf->cam_gpio_req_tbl; + + if (!gpio_tbl || !size) { + CAM_ERR(CAM_UTIL, "Invalid gpio_tbl %pK / size %d", + gpio_tbl, size); + return -EINVAL; + } + for (i = 0; i < size; i++) { + CAM_DBG(CAM_UTIL, "i=%d, gpio=%d dir=%ld", i, + gpio_tbl[i].gpio, gpio_tbl[i].flags); + } + if (gpio_en) { + for (i = 0; i < size; i++) { + rc = gpio_request_one(gpio_tbl[i].gpio, + gpio_tbl[i].flags, gpio_tbl[i].label); + if (rc) { + /* + * After GPIO request fails, contine to + * apply new gpios, outout a error message + * for driver bringup debug + */ + CAM_ERR(CAM_UTIL, "gpio %d:%s request fails", + gpio_tbl[i].gpio, gpio_tbl[i].label); + } + } + } else { + gpio_free_array(gpio_tbl, size); + } + + return rc; +} + +static int cam_soc_util_get_dt_regulator_info + (struct cam_hw_soc_info *soc_info) +{ + int rc = 0, count = 0, i = 0; + struct device_node *of_node = NULL; + + if (!soc_info || !soc_info->dev) { + CAM_ERR(CAM_UTIL, "Invalid parameters"); + return -EINVAL; + } + + of_node = soc_info->dev->of_node; + + soc_info->num_rgltr = 0; + count = of_property_count_strings(of_node, "regulator-names"); + if (count != -EINVAL) { + if (count <= 0) { + CAM_ERR(CAM_UTIL, "no regulators found"); + count = 0; + return -EINVAL; + } + + soc_info->num_rgltr = count; + + } else { + CAM_DBG(CAM_UTIL, "No regulators node found"); + return 0; + } + + for (i = 0; i < soc_info->num_rgltr; i++) { + rc = of_property_read_string_index(of_node, + "regulator-names", i, &soc_info->rgltr_name[i]); + CAM_DBG(CAM_UTIL, "rgltr_name[%d] = %s", + i, soc_info->rgltr_name[i]); + if (rc) { + CAM_ERR(CAM_UTIL, "no regulator resource at cnt=%d", i); + return -ENODEV; + } + } + + if (!of_property_read_bool(of_node, "rgltr-cntrl-support")) { + CAM_DBG(CAM_UTIL, "No regulator control parameter defined"); + soc_info->rgltr_ctrl_support = false; + return 0; + } + + soc_info->rgltr_ctrl_support = true; + + rc = of_property_read_u32_array(of_node, "rgltr-min-voltage", + soc_info->rgltr_min_volt, soc_info->num_rgltr); + if (rc) { + CAM_ERR(CAM_UTIL, "No minimum volatage value found, rc=%d", rc); + return -EINVAL; + } + + rc = of_property_read_u32_array(of_node, "rgltr-max-voltage", + soc_info->rgltr_max_volt, soc_info->num_rgltr); + if (rc) { + CAM_ERR(CAM_UTIL, "No maximum volatage value found, rc=%d", rc); + return -EINVAL; + } + + rc = of_property_read_u32_array(of_node, "rgltr-load-current", + soc_info->rgltr_op_mode, soc_info->num_rgltr); + if (rc) { + CAM_ERR(CAM_UTIL, "No Load curent found rc=%d", rc); + return -EINVAL; + } + + return rc; +} + +int cam_soc_util_get_dt_properties(struct cam_hw_soc_info *soc_info) +{ + struct device_node *of_node = NULL; + int count = 0, i = 0, rc = 0; + + if (!soc_info || !soc_info->dev) + return -EINVAL; + + of_node = soc_info->dev->of_node; + + rc = of_property_read_u32(of_node, "cell-index", &soc_info->index); + if (rc) { + CAM_ERR(CAM_UTIL, "device %s failed to read cell-index", + soc_info->dev_name); + return rc; + } + + count = of_property_count_strings(of_node, "reg-names"); + if (count <= 0) { + CAM_DBG(CAM_UTIL, "no reg-names found for: %s", + soc_info->dev_name); + count = 0; + } + soc_info->num_mem_block = count; + + for (i = 0; i < soc_info->num_mem_block; i++) { + rc = of_property_read_string_index(of_node, "reg-names", i, + &soc_info->mem_block_name[i]); + if (rc) { + CAM_ERR(CAM_UTIL, "failed to read reg-names at %d", i); + return rc; + } + soc_info->mem_block[i] = + platform_get_resource_byname(soc_info->pdev, + IORESOURCE_MEM, soc_info->mem_block_name[i]); + + if (!soc_info->mem_block[i]) { + CAM_ERR(CAM_UTIL, "no mem resource by name %s", + soc_info->mem_block_name[i]); + rc = -ENODEV; + return rc; + } + } + + if (soc_info->num_mem_block > 0) { + rc = of_property_read_u32_array(of_node, "reg-cam-base", + soc_info->mem_block_cam_base, soc_info->num_mem_block); + if (rc) { + CAM_ERR(CAM_UTIL, "Error reading register offsets"); + return rc; + } + } + + rc = of_property_read_string_index(of_node, "interrupt-names", 0, + &soc_info->irq_name); + if (rc) { + CAM_DBG(CAM_UTIL, "No interrupt line preset for: %s", + soc_info->dev_name); + rc = 0; + } else { + soc_info->irq_line = + platform_get_resource_byname(soc_info->pdev, + IORESOURCE_IRQ, soc_info->irq_name); + if (!soc_info->irq_line) { + CAM_ERR(CAM_UTIL, "no irq resource"); + rc = -ENODEV; + return rc; + } + } + + rc = cam_soc_util_get_dt_regulator_info(soc_info); + if (rc) + return rc; + + rc = cam_soc_util_get_dt_clk_info(soc_info); + if (rc) + return rc; + + rc = cam_soc_util_get_gpio_info(soc_info); + if (rc) + return rc; + + return rc; +} + +/** + * cam_soc_util_get_regulator() + * + * @brief: Get regulator resource named vdd + * + * @dev: Device associated with regulator + * @reg: Return pointer to be filled with regulator on success + * @rgltr_name: Name of regulator to get + * + * @return: 0 for Success, negative value for failure + */ +static int cam_soc_util_get_regulator(struct device *dev, + struct regulator **reg, const char *rgltr_name) +{ + int rc = 0; + *reg = regulator_get(dev, rgltr_name); + if (IS_ERR_OR_NULL(*reg)) { + rc = PTR_ERR(*reg); + rc = rc ? rc : -EINVAL; + CAM_ERR(CAM_UTIL, "Regulator %s get failed %d", rgltr_name, rc); + *reg = NULL; + } + return rc; +} + +int cam_soc_util_regulator_disable(struct regulator *rgltr, + const char *rgltr_name, uint32_t rgltr_min_volt, + uint32_t rgltr_max_volt, uint32_t rgltr_op_mode, + uint32_t rgltr_delay_ms) +{ + int32_t rc = 0; + + if (!rgltr) { + CAM_ERR(CAM_UTIL, "Invalid NULL parameter"); + return -EINVAL; + } + + rc = regulator_disable(rgltr); + if (rc) { + CAM_ERR(CAM_UTIL, "%s regulator disable failed", rgltr_name); + return rc; + } + + if (rgltr_delay_ms > 20) + msleep(rgltr_delay_ms); + else if (rgltr_delay_ms) + usleep_range(rgltr_delay_ms * 1000, + (rgltr_delay_ms * 1000) + 1000); + + if (regulator_count_voltages(rgltr) > 0) { + regulator_set_load(rgltr, 0); + regulator_set_voltage(rgltr, 0, rgltr_max_volt); + } + + return rc; +} + + +int cam_soc_util_regulator_enable(struct regulator *rgltr, + const char *rgltr_name, + uint32_t rgltr_min_volt, uint32_t rgltr_max_volt, + uint32_t rgltr_op_mode, uint32_t rgltr_delay) +{ + int32_t rc = 0; + + if (!rgltr) { + CAM_ERR(CAM_UTIL, "Invalid NULL parameter"); + return -EINVAL; + } + + if (regulator_count_voltages(rgltr) > 0) { + CAM_DBG(CAM_UTIL, "voltage min=%d, max=%d", + rgltr_min_volt, rgltr_max_volt); + + rc = regulator_set_voltage( + rgltr, rgltr_min_volt, rgltr_max_volt); + if (rc) { + CAM_ERR(CAM_UTIL, "%s set voltage failed", rgltr_name); + return rc; + } + + rc = regulator_set_load(rgltr, rgltr_op_mode); + if (rc) { + CAM_ERR(CAM_UTIL, "%s set optimum mode failed", + rgltr_name); + return rc; + } + } + + rc = regulator_enable(rgltr); + if (rc) { + CAM_ERR(CAM_UTIL, "%s regulator_enable failed", rgltr_name); + return rc; + } + + if (rgltr_delay > 20) + msleep(rgltr_delay); + else if (rgltr_delay) + usleep_range(rgltr_delay * 1000, + (rgltr_delay * 1000) + 1000); + + return rc; +} + +static int cam_soc_util_request_pinctrl( + struct cam_hw_soc_info *soc_info) { + + struct cam_soc_pinctrl_info *device_pctrl = &soc_info->pinctrl_info; + struct device *dev = soc_info->dev; + + device_pctrl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(device_pctrl->pinctrl)) { + CAM_DBG(CAM_UTIL, "Pinctrl not available"); + device_pctrl->pinctrl = NULL; + return 0; + } + device_pctrl->gpio_state_active = + pinctrl_lookup_state(device_pctrl->pinctrl, + CAM_SOC_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(device_pctrl->gpio_state_active)) { + CAM_ERR(CAM_UTIL, + "Failed to get the active state pinctrl handle"); + device_pctrl->gpio_state_active = NULL; + return -EINVAL; + } + device_pctrl->gpio_state_suspend + = pinctrl_lookup_state(device_pctrl->pinctrl, + CAM_SOC_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(device_pctrl->gpio_state_suspend)) { + CAM_ERR(CAM_UTIL, + "Failed to get the suspend state pinctrl handle"); + device_pctrl->gpio_state_suspend = NULL; + return -EINVAL; + } + return 0; +} + +static void cam_soc_util_regulator_disable_default( + struct cam_hw_soc_info *soc_info) +{ + int j = 0; + uint32_t num_rgltr = soc_info->num_rgltr; + + for (j = num_rgltr-1; j >= 0; j--) { + if (soc_info->rgltr_ctrl_support == true) { + cam_soc_util_regulator_disable(soc_info->rgltr[j], + soc_info->rgltr_name[j], + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + soc_info->rgltr_op_mode[j], + soc_info->rgltr_delay[j]); + } else { + if (soc_info->rgltr[j]) + regulator_disable(soc_info->rgltr[j]); + } + } +} + +static int cam_soc_util_regulator_enable_default( + struct cam_hw_soc_info *soc_info) +{ + int j = 0, rc = 0; + uint32_t num_rgltr = soc_info->num_rgltr; + + for (j = 0; j < num_rgltr; j++) { + if (soc_info->rgltr_ctrl_support == true) { + rc = cam_soc_util_regulator_enable(soc_info->rgltr[j], + soc_info->rgltr_name[j], + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + soc_info->rgltr_op_mode[j], + soc_info->rgltr_delay[j]); + } else { + if (soc_info->rgltr[j]) + rc = regulator_enable(soc_info->rgltr[j]); + } + + if (rc) { + CAM_ERR(CAM_UTIL, "%s enable failed", + soc_info->rgltr_name[j]); + goto disable_rgltr; + } + } + + return rc; +disable_rgltr: + + for (j--; j >= 0; j--) { + if (soc_info->rgltr_ctrl_support == true) { + cam_soc_util_regulator_disable(soc_info->rgltr[j], + soc_info->rgltr_name[j], + soc_info->rgltr_min_volt[j], + soc_info->rgltr_max_volt[j], + soc_info->rgltr_op_mode[j], + soc_info->rgltr_delay[j]); + } else { + if (soc_info->rgltr[j]) + regulator_disable(soc_info->rgltr[j]); + } + } + + return rc; +} + +int cam_soc_util_request_platform_resource( + struct cam_hw_soc_info *soc_info, + irq_handler_t handler, void *irq_data) +{ + int i = 0, rc = 0; + + if (!soc_info || !soc_info->dev) { + CAM_ERR(CAM_UTIL, "Invalid parameters"); + return -EINVAL; + } + + for (i = 0; i < soc_info->num_mem_block; i++) { + if (soc_info->reserve_mem) { + if (!request_mem_region(soc_info->mem_block[i]->start, + resource_size(soc_info->mem_block[i]), + soc_info->mem_block_name[i])){ + CAM_ERR(CAM_UTIL, + "Error Mem region request Failed:%s", + soc_info->mem_block_name[i]); + rc = -ENOMEM; + goto unmap_base; + } + } + soc_info->reg_map[i].mem_base = ioremap( + soc_info->mem_block[i]->start, + resource_size(soc_info->mem_block[i])); + if (!soc_info->reg_map[i].mem_base) { + CAM_ERR(CAM_UTIL, "i= %d base NULL", i); + rc = -ENOMEM; + goto unmap_base; + } + soc_info->reg_map[i].mem_cam_base = + soc_info->mem_block_cam_base[i]; + soc_info->reg_map[i].size = + resource_size(soc_info->mem_block[i]); + soc_info->num_reg_map++; + } + + for (i = 0; i < soc_info->num_rgltr; i++) { + if (soc_info->rgltr_name[i] == NULL) { + CAM_ERR(CAM_UTIL, "can't find regulator name"); + goto put_regulator; + } + + rc = cam_soc_util_get_regulator(soc_info->dev, + &soc_info->rgltr[i], + soc_info->rgltr_name[i]); + if (rc) + goto put_regulator; + } + + if (soc_info->irq_line) { + rc = devm_request_irq(soc_info->dev, soc_info->irq_line->start, + handler, IRQF_TRIGGER_RISING, + soc_info->irq_name, irq_data); + if (rc) { + CAM_ERR(CAM_UTIL, "irq request fail"); + rc = -EBUSY; + goto put_regulator; + } + disable_irq(soc_info->irq_line->start); + soc_info->irq_data = irq_data; + } + + /* Get Clock */ + for (i = 0; i < soc_info->num_clk; i++) { + soc_info->clk[i] = clk_get(soc_info->dev, + soc_info->clk_name[i]); + if (!soc_info->clk[i]) { + CAM_ERR(CAM_UTIL, "get failed for %s", + soc_info->clk_name[i]); + rc = -ENOENT; + goto put_clk; + } + } + + rc = cam_soc_util_request_pinctrl(soc_info); + if (rc) + CAM_DBG(CAM_UTIL, "Failed in request pinctrl, rc=%d", rc); + + rc = cam_soc_util_request_gpio_table(soc_info, true); + if (rc) { + CAM_ERR(CAM_UTIL, "Failed in request gpio table, rc=%d", rc); + goto put_clk; + } + + return rc; + +put_clk: + if (i == -1) + i = soc_info->num_clk; + for (i = i - 1; i >= 0; i--) { + if (soc_info->clk[i]) { + clk_put(soc_info->clk[i]); + soc_info->clk[i] = NULL; + } + } + + if (soc_info->irq_line) { + disable_irq(soc_info->irq_line->start); + devm_free_irq(soc_info->dev, + soc_info->irq_line->start, irq_data); + } + +put_regulator: + if (i == -1) + i = soc_info->num_rgltr; + for (i = i - 1; i >= 0; i--) { + if (soc_info->rgltr[i]) { + regulator_disable(soc_info->rgltr[i]); + regulator_put(soc_info->rgltr[i]); + soc_info->rgltr[i] = NULL; + } + } + +unmap_base: + if (i == -1) + i = soc_info->num_reg_map; + for (i = i - 1; i >= 0; i--) { + if (soc_info->reserve_mem) + release_mem_region(soc_info->mem_block[i]->start, + resource_size(soc_info->mem_block[i])); + iounmap(soc_info->reg_map[i].mem_base); + soc_info->reg_map[i].mem_base = NULL; + soc_info->reg_map[i].size = 0; + } + + return rc; +} + +int cam_soc_util_release_platform_resource(struct cam_hw_soc_info *soc_info) +{ + int i; + + if (!soc_info || !soc_info->dev) { + CAM_ERR(CAM_UTIL, "Invalid parameter"); + return -EINVAL; + } + + for (i = soc_info->num_clk - 1; i >= 0; i--) { + clk_put(soc_info->clk[i]); + soc_info->clk[i] = NULL; + } + + for (i = soc_info->num_rgltr - 1; i >= 0; i--) { + if (soc_info->rgltr[i]) { + regulator_put(soc_info->rgltr[i]); + soc_info->rgltr[i] = NULL; + } + } + + for (i = soc_info->num_reg_map - 1; i >= 0; i--) { + iounmap(soc_info->reg_map[i].mem_base); + soc_info->reg_map[i].mem_base = NULL; + soc_info->reg_map[i].size = 0; + } + + if (soc_info->irq_line) { + disable_irq(soc_info->irq_line->start); + devm_free_irq(soc_info->dev, + soc_info->irq_line->start, soc_info->irq_data); + } + + if (soc_info->pinctrl_info.pinctrl) + devm_pinctrl_put(soc_info->pinctrl_info.pinctrl); + + + /* release for gpio */ + cam_soc_util_request_gpio_table(soc_info, false); + + return 0; +} + +int cam_soc_util_enable_platform_resource(struct cam_hw_soc_info *soc_info, + bool enable_clocks, enum cam_vote_level clk_level, bool enable_irq) +{ + int rc = 0; + + if (!soc_info) + return -EINVAL; + + rc = cam_soc_util_regulator_enable_default(soc_info); + if (rc) { + CAM_ERR(CAM_UTIL, "Regulators enable failed"); + return rc; + } + + if (enable_clocks) { + rc = cam_soc_util_clk_enable_default(soc_info, clk_level); + if (rc) + goto disable_regulator; + } + + if (enable_irq) { + rc = cam_soc_util_irq_enable(soc_info); + if (rc) + goto disable_clk; + } + + if (soc_info->pinctrl_info.pinctrl && + soc_info->pinctrl_info.gpio_state_active) { + rc = pinctrl_select_state(soc_info->pinctrl_info.pinctrl, + soc_info->pinctrl_info.gpio_state_active); + + if (rc) + goto disable_irq; + } + + return rc; + +disable_irq: + if (enable_irq) + cam_soc_util_irq_disable(soc_info); + +disable_clk: + if (enable_clocks) + cam_soc_util_clk_disable_default(soc_info); + +disable_regulator: + cam_soc_util_regulator_disable_default(soc_info); + + + return rc; +} + +int cam_soc_util_disable_platform_resource(struct cam_hw_soc_info *soc_info, + bool disable_clocks, bool disble_irq) +{ + int rc = 0; + + if (!soc_info) + return -EINVAL; + + if (disble_irq) + rc |= cam_soc_util_irq_disable(soc_info); + + if (disable_clocks) + cam_soc_util_clk_disable_default(soc_info); + + cam_soc_util_regulator_disable_default(soc_info); + + if (soc_info->pinctrl_info.pinctrl && + soc_info->pinctrl_info.gpio_state_suspend) + rc = pinctrl_select_state(soc_info->pinctrl_info.pinctrl, + soc_info->pinctrl_info.gpio_state_suspend); + + return rc; +} + +int cam_soc_util_reg_dump(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset, int size) +{ + void __iomem *base_addr = NULL; + + CAM_DBG(CAM_UTIL, "base_idx %u size=%d", base_index, size); + + if (!soc_info || base_index >= soc_info->num_reg_map || + size <= 0 || (offset + size) >= + CAM_SOC_GET_REG_MAP_SIZE(soc_info, base_index)) + return -EINVAL; + + base_addr = CAM_SOC_GET_REG_MAP_START(soc_info, base_index); + + /* + * All error checking already done above, + * hence ignoring the return value below. + */ + cam_io_dump(base_addr, offset, size); + + return 0; +} + diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.h new file mode 100644 index 000000000000..4b57d54466c3 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_soc_util.h @@ -0,0 +1,619 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CAM_SOC_UTIL_H_ +#define _CAM_SOC_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cam_io_util.h" + +#define NO_SET_RATE -1 +#define INIT_RATE -2 + +/* maximum number of device block */ +#define CAM_SOC_MAX_BLOCK 4 + +/* maximum number of device base */ +#define CAM_SOC_MAX_BASE CAM_SOC_MAX_BLOCK + +/* maximum number of device regulator */ +#define CAM_SOC_MAX_REGULATOR 5 + +/* maximum number of device clock */ +#define CAM_SOC_MAX_CLK 32 + +/** + * enum cam_vote_level - Enum for voting level + * + * @CAM_SUSPEND_VOTE : Suspend vote + * @CAM_MINSVS_VOTE : Min SVS vote + * @CAM_LOWSVS_VOTE : Low SVS vote + * @CAM_SVS_VOTE : SVS vote + * @CAM_SVSL1_VOTE : SVS Plus vote + * @CAM_NOMINAL_VOTE : Nominal vote + * @CAM_TURBO_VOTE : Turbo vote + * @CAM_MAX_VOTE : Max voting level, This is invalid level. + */ +enum cam_vote_level { + CAM_SUSPEND_VOTE, + CAM_MINSVS_VOTE, + CAM_LOWSVS_VOTE, + CAM_SVS_VOTE, + CAM_SVSL1_VOTE, + CAM_NOMINAL_VOTE, + CAM_TURBO_VOTE, + CAM_MAX_VOTE, +}; + +/* pinctrl states */ +#define CAM_SOC_PINCTRL_STATE_SLEEP "cam_suspend" +#define CAM_SOC_PINCTRL_STATE_DEFAULT "cam_default" + +/** + * struct cam_soc_reg_map: Information about the mapped register space + * + * @mem_base: Starting location of MAPPED register space + * @mem_cam_base: Starting offset of this register space compared + * to ENTIRE Camera register space + * @size: Size of register space + **/ +struct cam_soc_reg_map { + void __iomem *mem_base; + uint32_t mem_cam_base; + resource_size_t size; +}; + +/** + * struct cam_soc_pinctrl_info: Information about pinctrl data + * + * @pinctrl: pintrl object + * @gpio_state_active: default pinctrl state + * @gpio_state_suspend suspend state of pinctrl + **/ +struct cam_soc_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +}; + +/** + * struct cam_soc_gpio_data: Information about the gpio pins + * + * @cam_gpio_common_tbl: It is list of al the gpios present in gpios node + * @cam_gpio_common_tbl_size: It is equal to number of gpios prsent in + * gpios node in DTSI + * @cam_gpio_req_tbl It is list of al the requesetd gpios + * @cam_gpio_req_tbl_size: It is size of requested gpios + **/ +struct cam_soc_gpio_data { + struct gpio *cam_gpio_common_tbl; + uint8_t cam_gpio_common_tbl_size; + struct gpio *cam_gpio_req_tbl; + uint8_t cam_gpio_req_tbl_size; +}; + +/** + * struct cam_hw_soc_info: Soc information pertaining to specific instance of + * Camera hardware driver module + * + * @pdev: Platform device pointer + * @device: Device pointer + * @hw_version: Camera device version + * @index: Instance id for the camera device + * @dev_name: Device Name + * @irq_name: Name of the irq associated with the device + * @irq_line: Irq resource + * @irq_data: Private data that is passed when IRQ is requested + * @num_mem_block: Number of entry in the "reg-names" + * @mem_block_name: Array of the reg block name + * @mem_block_cam_base: Array of offset of this register space compared + * to ENTIRE Camera register space + * @mem_block: Associated resource structs + * @reg_map: Array of Mapped register info for the "reg-names" + * @num_reg_map: Number of mapped register space associated + * with mem_block. num_reg_map = num_mem_block in + * most cases + * @reserve_mem: Whether to reserve memory for Mem blocks + * @num_rgltr: Number of regulators + * @rgltr_name: Array of regulator names + * @rgltr_ctrl_support: Whether regulator control is supported + * @rgltr_min_volt: Array of minimum regulator voltage + * @rgltr_max_volt: Array of maximum regulator voltage + * @rgltr_op_mode: Array of regulator operation mode + * @rgltr_type: Array of regulator names + * @rgltr: Array of associated regulator resources + * @rgltr_delay: Array of regulator delay values + * @num_clk: Number of clocks + * @clk_name: Array of clock names + * @clk: Array of associated clock resources + * @clk_rate: 2D array of clock rates representing clock rate + * values at different vote levels + * @src_clk_idx: Source clock index that is rate-controllable + * @clk_level_valid: Indicates whether corresponding level is valid + * @gpio_data: Pointer to gpio info + * @pinctrl_info: Pointer to pinctrl info + * @soc_private: Soc private data + */ +struct cam_hw_soc_info { + struct platform_device *pdev; + struct device *dev; + uint32_t hw_version; + uint32_t index; + const char *dev_name; + const char *irq_name; + struct resource *irq_line; + void *irq_data; + + uint32_t num_mem_block; + const char *mem_block_name[CAM_SOC_MAX_BLOCK]; + uint32_t mem_block_cam_base[CAM_SOC_MAX_BLOCK]; + struct resource *mem_block[CAM_SOC_MAX_BLOCK]; + struct cam_soc_reg_map reg_map[CAM_SOC_MAX_BASE]; + uint32_t num_reg_map; + uint32_t reserve_mem; + + uint32_t num_rgltr; + const char *rgltr_name[CAM_SOC_MAX_REGULATOR]; + uint32_t rgltr_ctrl_support; + uint32_t rgltr_min_volt[CAM_SOC_MAX_REGULATOR]; + uint32_t rgltr_max_volt[CAM_SOC_MAX_REGULATOR]; + uint32_t rgltr_op_mode[CAM_SOC_MAX_REGULATOR]; + uint32_t rgltr_type[CAM_SOC_MAX_REGULATOR]; + struct regulator *rgltr[CAM_SOC_MAX_REGULATOR]; + uint32_t rgltr_delay[CAM_SOC_MAX_REGULATOR]; + + uint32_t use_shared_clk; + uint32_t num_clk; + const char *clk_name[CAM_SOC_MAX_CLK]; + struct clk *clk[CAM_SOC_MAX_CLK]; + int32_t clk_rate[CAM_MAX_VOTE][CAM_SOC_MAX_CLK]; + int32_t src_clk_idx; + bool clk_level_valid[CAM_MAX_VOTE]; + + struct cam_soc_gpio_data *gpio_data; + struct cam_soc_pinctrl_info pinctrl_info; + + void *soc_private; +}; + +/* + * CAM_SOC_GET_REG_MAP_START + * + * @brief: This MACRO will get the mapped starting address + * where the register space can be accessed + * + * @__soc_info: Device soc information + * @__base_index: Index of register space in the HW block + * + * @return: Returns a pointer to the mapped register memory + */ +#define CAM_SOC_GET_REG_MAP_START(__soc_info, __base_index) \ + ((!__soc_info || __base_index >= __soc_info->num_reg_map) ? \ + NULL : __soc_info->reg_map[__base_index].mem_base) + +/* + * CAM_SOC_GET_REG_MAP_CAM_BASE + * + * @brief: This MACRO will get the cam_base of the + * register space + * + * @__soc_info: Device soc information + * @__base_index: Index of register space in the HW block + * + * @return: Returns an int32_t value. + * Failure: -1 + * Success: Starting offset of register space compared + * to entire Camera Register Map + */ +#define CAM_SOC_GET_REG_MAP_CAM_BASE(__soc_info, __base_index) \ + ((!__soc_info || __base_index >= __soc_info->num_reg_map) ? \ + -1 : __soc_info->reg_map[__base_index].mem_cam_base) + +/* + * CAM_SOC_GET_REG_MAP_SIZE + * + * @brief: This MACRO will get the size of the mapped + * register space + * + * @__soc_info: Device soc information + * @__base_index: Index of register space in the HW block + * + * @return: Returns a uint32_t value. + * Failure: 0 + * Success: Non-zero size of mapped register space + */ +#define CAM_SOC_GET_REG_MAP_SIZE(__soc_info, __base_index) \ + ((!__soc_info || __base_index >= __soc_info->num_reg_map) ? \ + 0 : __soc_info->reg_map[__base_index].size) + +/** + * cam_soc_util_get_level_from_string() + * + * @brief: Get the associated vote level for the input string + * + * @string: Input string to compare with. + * @level: Vote level corresponds to input string. + * + * @return: Success or failure + */ +int cam_soc_util_get_level_from_string(const char *string, + enum cam_vote_level *level); + +/** + * cam_soc_util_get_dt_properties() + * + * @brief: Parse the DT and populate the common properties that + * are part of the soc_info structure - register map, + * clocks, regulators, irq, etc. + * + * @soc_info: Device soc struct to be populated + * + * @return: Success or failure + */ +int cam_soc_util_get_dt_properties(struct cam_hw_soc_info *soc_info); + +/** + * cam_soc_util_request_platform_resource() + * + * @brief: Request regulator, irq, and clock resources + * + * @soc_info: Device soc information + * @handler: Irq handler function pointer + * @irq_data: Irq handler function CB data + * + * @return: Success or failure + */ +int cam_soc_util_request_platform_resource(struct cam_hw_soc_info *soc_info, + irq_handler_t handler, void *irq_data); + +/** + * cam_soc_util_release_platform_resource() + * + * @brief: Release regulator, irq, and clock resources + * + * @soc_info: Device soc information + * + * @return: Success or failure + */ +int cam_soc_util_release_platform_resource(struct cam_hw_soc_info *soc_info); + +/** + * cam_soc_util_enable_platform_resource() + * + * @brief: Enable regulator, irq resources + * + * @soc_info: Device soc information + * @enable_clocks: Boolean flag: + * TRUE: Enable all clocks in soc_info Now. + * False: Don't enable clocks Now. Driver will + * enable independently. + * @clk_level: Clock level to be applied. + * Applicable only if enable_clocks is true + * Valid range : 0 to (CAM_MAX_VOTE - 1) + * @enable_irq: Boolean flag: + * TRUE: Enable IRQ in soc_info Now. + * False: Don't enable IRQ Now. Driver will + * enable independently. + * + * @return: Success or failure + */ +int cam_soc_util_enable_platform_resource(struct cam_hw_soc_info *soc_info, + bool enable_clocks, enum cam_vote_level clk_level, bool enable_irq); + +/** + * cam_soc_util_disable_platform_resource() + * + * @brief: Disable regulator, irq resources + * + * @soc_info: Device soc information + * @disable_irq: Boolean flag: + * TRUE: Disable IRQ in soc_info Now. + * False: Don't disble IRQ Now. Driver will + * disable independently. + * + * @return: Success or failure + */ +int cam_soc_util_disable_platform_resource(struct cam_hw_soc_info *soc_info, + bool disable_clocks, bool disable_irq); + +/** + * cam_soc_util_get_clk_round_rate() + * + * @brief: Get the rounded clock rate for the given clock's + * clock rate value + * + * @soc_info: Device soc information + * @clk_index: Clock index in soc_info for which round rate is needed + * @clk_rate: Input clock rate for which rounded rate is needed + * + * @return: Rounded clock rate + */ +long cam_soc_util_get_clk_round_rate(struct cam_hw_soc_info *soc_info, + uint32_t clk_index, unsigned long clk_rate); + +/** + * cam_soc_util_set_clk_flags() + * + * @brief: Camera SOC util to set the flags for a specified clock + * + * @soc_info: Device soc information + * @clk_index: Clock index in soc_info for which flags are to be set + * @flags: Flags to set + * + * @return: Success or Failure + */ +int cam_soc_util_set_clk_flags(struct cam_hw_soc_info *soc_info, + uint32_t clk_index, unsigned long flags); + +/** + * cam_soc_util_set_clk_rate() + * + * @brief: Set the rate on a given clock. + * + * @clk: Clock that needs to be set + * @clk_name: Clocks name associated with clk + * @clk_rate: Clocks rate associated with clk + * + * @return: success or failure + */ +int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name, + int32_t clk_rate); + +/** + * cam_soc_util_get_option_clk_by_name() + * + * @brief: Get reference to optional clk using name + * + * @soc_info: Device soc information + * @clk_name: Name of clock to find reference for + * @clk: Clock reference pointer to be filled if Success + * @clk_index: Clk index in the option clk array to be returned + * @clk_rate: Clk rate in the option clk array + * + * @return: 0: Success + * Negative: Failure + */ +int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info, + const char *clk_name, struct clk **clk, int32_t *clk_index, + int32_t *clk_rate); + +/** + * cam_soc_util_clk_put() + * + * @brief: Put clock specified in params + * + * @clk: Reference to the Clock that needs to be put + * + * @return: Success or failure + */ +int cam_soc_util_clk_put(struct clk **clk); + +/** + * cam_soc_util_clk_enable() + * + * @brief: Enable clock specified in params + * + * @clk: Clock that needs to be turned ON + * @clk_name: Clocks name associated with clk + * @clk_rate: Clocks rate associated with clk + * + * @return: Success or failure + */ +int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name, + int32_t clk_rate); + +/** + * cam_soc_util_set_clk_rate_level() + * + * @brief: Apply clock rates for the requested level. + * This applies the new requested level for all + * the clocks listed in DT based on their values. + * + * @soc_info: Device soc information + * @clk_level: Clock level number to set + * + * @return: Success or failure + */ +int cam_soc_util_set_clk_rate_level(struct cam_hw_soc_info *soc_info, + enum cam_vote_level clk_level); + +/** + * cam_soc_util_clk_disable() + * + * @brief: Disable clock specified in params + * + * @clk: Clock that needs to be turned OFF + * @clk_name: Clocks name associated with clk + * + * @return: Success or failure + */ +int cam_soc_util_clk_disable(struct clk *clk, const char *clk_name); + +/** + * cam_soc_util_irq_enable() + * + * @brief: Enable IRQ in SOC + * + * @soc_info: Device soc information + * + * @return: Success or failure + */ +int cam_soc_util_irq_enable(struct cam_hw_soc_info *soc_info); + +/** + * cam_soc_util_irq_disable() + * + * @brief: Disable IRQ in SOC + * + * @soc_info: Device soc information + * + * @return: Success or failure + */ +int cam_soc_util_irq_disable(struct cam_hw_soc_info *soc_info); + +/** + * cam_soc_util_regulator_enable() + * + * @brief: Enable single regulator + * + * @rgltr Regulator that needs to be turned ON + * @rgltr_name Associated Regulator name + * @rgltr_min_volt: Requested minimum volatage + * @rgltr_max_volt: Requested maximum volatage + * @rgltr_op_mode: Requested Load + * @rgltr_delay: Requested delay needed aaftre enabling regulator + * + * @return: Success or failure + */ +int cam_soc_util_regulator_enable(struct regulator *rgltr, + const char *rgltr_name, + uint32_t rgltr_min_volt, uint32_t rgltr_max_volt, + uint32_t rgltr_op_mode, uint32_t rgltr_delay); + +/** + * cam_soc_util_regulator_enable() + * + * @brief: Disable single regulator + * + * @rgltr Regulator that needs to be turned ON + * @rgltr_name Associated Regulator name + * @rgltr_min_volt: Requested minimum volatage + * @rgltr_max_volt: Requested maximum volatage + * @rgltr_op_mode: Requested Load + * @rgltr_delay: Requested delay needed aaftre enabling regulator + * + * @return: Success or failure + */ +int cam_soc_util_regulator_disable(struct regulator *rgltr, + const char *rgltr_name, + uint32_t rgltr_min_volt, uint32_t rgltr_max_volt, + uint32_t rgltr_op_mode, uint32_t rgltr_delay); + + +/** + * cam_soc_util_w() + * + * @brief: Camera SOC util for register write + * + * @soc_info: Device soc information + * @base_index: Index of register space in the HW block + * @offset: Offset of register to be read + * @data: Value to be written + * + * @return: Success or Failure + */ +static inline int cam_soc_util_w(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset, uint32_t data) +{ + if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) + return -EINVAL; + return cam_io_w(data, + CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); +} + +/** + * cam_soc_util_w_mb() + * + * @brief: Camera SOC util for register write with memory barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call wmb() independently in the caller. + * + * @soc_info: Device soc information + * @base_index: Index of register space in the HW block + * @offset: Offset of register to be read + * @data: Value to be written + * + * @return: Success or Failure + */ +static inline int cam_soc_util_w_mb(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset, uint32_t data) +{ + if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) + return -EINVAL; + return cam_io_w_mb(data, + CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); +} + +/** + * cam_soc_util_r() + * + * @brief: Camera SOC util for register read + * + * @soc_info: Device soc information + * @base_index: Index of register space in the HW block + * @offset: Offset of register to be read + * + * @return: Value read from the register address + */ +static inline uint32_t cam_soc_util_r(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset) +{ + if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) + return 0; + return cam_io_r( + CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); +} + +/** + * cam_soc_util_r_mb() + * + * @brief: Camera SOC util for register read with memory barrier. + * Memory Barrier is only before the write to ensure the + * order. If need to ensure this write is also flushed + * call rmb() independently in the caller. + * + * @soc_info: Device soc information + * @base_index: Index of register space in the HW block + * @offset: Offset of register to be read + * + * @return: Value read from the register address + */ +static inline uint32_t cam_soc_util_r_mb(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset) +{ + if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) + return 0; + return cam_io_r_mb( + CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); +} + +/** + * cam_soc_util_reg_dump() + * + * @brief: Camera SOC util for dumping a range of register + * + * @soc_info: Device soc information + * @base_index: Index of register space in the HW block + * @offset: Start register offset for the dump + * @size: Size specifying the range for dump + * + * @return: Success or Failure + */ +int cam_soc_util_reg_dump(struct cam_hw_soc_info *soc_info, + uint32_t base_index, uint32_t offset, int size); + +void cam_soc_util_clk_disable_default(struct cam_hw_soc_info *soc_info); + +int cam_soc_util_clk_enable_default(struct cam_hw_soc_info *soc_info, + enum cam_vote_level clk_level); + +#endif /* _CAM_SOC_UTIL_H_ */ diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.c b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.c new file mode 100644 index 000000000000..08129f3c8c8a --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.c @@ -0,0 +1,16 @@ +/* Copyright (c) 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. + * + */ + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "cam_trace.h" diff --git a/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.h b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.h new file mode 100644 index 000000000000..c5cb117e5119 --- /dev/null +++ b/drivers/media/platform/msm/camera_oneplus/cam_utils/cam_trace.h @@ -0,0 +1,330 @@ +/* Copyright (c) 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. + * + */ + +#if !defined(_CAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CAM_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM camera +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE cam_trace + +#include +#include +#include "cam_req_mgr_core.h" +#include "cam_req_mgr_interface.h" +#include "cam_context.h" + +TRACE_EVENT(cam_context_state, + TP_PROTO(const char *name, struct cam_context *ctx), + TP_ARGS(name, ctx), + TP_STRUCT__entry( + __field(void*, ctx) + __field(uint32_t, state) + __string(name, name) + ), + TP_fast_assign( + __entry->ctx = ctx; + __entry->state = ctx->state; + __assign_str(name, name); + ), + TP_printk( + "%s: State ctx=%p ctx_state=%u", + __get_str(name), __entry->ctx, __entry->state + ) +); + +TRACE_EVENT(cam_isp_activated_irq, + TP_PROTO(struct cam_context *ctx, unsigned int substate, + unsigned int event, uint64_t timestamp), + TP_ARGS(ctx, substate, event, timestamp), + TP_STRUCT__entry( + __field(void*, ctx) + __field(uint32_t, state) + __field(uint32_t, substate) + __field(uint32_t, event) + __field(uint64_t, ts) + ), + TP_fast_assign( + __entry->ctx = ctx; + __entry->state = ctx->state; + __entry->substate = substate; + __entry->event = event; + __entry->ts = timestamp; + ), + TP_printk( + "ISP: IRQ ctx=%p ctx_state=%u substate=%u event=%u ts=%llu", + __entry->ctx, __entry->state, __entry->substate, + __entry->event, __entry->ts + ) +); + +TRACE_EVENT(cam_log_event, + TP_PROTO(const char *string1, const char *string2, + uint64_t val1, uint64_t val2), + TP_ARGS(string1, string2, val1, val2), + TP_STRUCT__entry( + __string(string1, string1) + __string(string2, string2) + __field(uint64_t, val1) + __field(uint64_t, val2) + ), + TP_fast_assign( + __assign_str(string1, string1); + __assign_str(string2, string2); + __entry->val1 = val1; + __entry->val2 = val2; + ), + TP_printk( + "%s: %s val1=%llu val2=%llu", + __get_str(string1), __get_str(string2), + __entry->val1, __entry->val2 + ) +); + +TRACE_EVENT(cam_icp_fw_dbg, + TP_PROTO(char *dbg_message), + TP_ARGS(dbg_message), + TP_STRUCT__entry( + __string(dbg_message, dbg_message) + ), + TP_fast_assign( + __assign_str(dbg_message, dbg_message); + ), + TP_printk( + "%s: ", + __get_str(dbg_message) + ) +); + +TRACE_EVENT(cam_buf_done, + TP_PROTO(const char *ctx_type, struct cam_context *ctx, + struct cam_ctx_request *req), + TP_ARGS(ctx_type, ctx, req), + TP_STRUCT__entry( + __string(ctx_type, ctx_type) + __field(void*, ctx) + __field(uint64_t, request) + ), + TP_fast_assign( + __assign_str(ctx_type, ctx_type); + __entry->ctx = ctx; + __entry->request = req->request_id; + ), + TP_printk( + "%5s: BufDone ctx=%p request=%llu", + __get_str(ctx_type), __entry->ctx, __entry->request + ) +); + +TRACE_EVENT(cam_apply_req, + TP_PROTO(const char *entity, uint64_t req_id), + TP_ARGS(entity, req_id), + TP_STRUCT__entry( + __string(entity, entity) + __field(uint64_t, req_id) + ), + TP_fast_assign( + __assign_str(entity, entity); + __entry->req_id = req_id; + ), + TP_printk( + "%8s: ApplyRequest request=%llu", + __get_str(entity), __entry->req_id + ) +); + +TRACE_EVENT(cam_flush_req, + TP_PROTO(struct cam_req_mgr_core_link *link, + struct cam_req_mgr_flush_info *info), + TP_ARGS(link, info), + TP_STRUCT__entry( + __field(uint32_t, type) + __field(int64_t, req_id) + __field(void*, link) + __field(void*, session) + ), + TP_fast_assign( + __entry->type = info->flush_type; + __entry->req_id = info->req_id; + __entry->link = link; + __entry->session = link->parent; + ), + TP_printk( + "FlushRequest type=%u request=%llu link=%pK session=%pK", + __entry->type, __entry->req_id, __entry->link, + __entry->session + ) +); + +TRACE_EVENT(cam_req_mgr_connect_device, + TP_PROTO(struct cam_req_mgr_core_link *link, + struct cam_req_mgr_device_info *info), + TP_ARGS(link, info), + TP_STRUCT__entry( + __string(name, info->name) + __field(uint32_t, id) + __field(uint32_t, delay) + __field(void*, link) + __field(void*, session) + ), + TP_fast_assign( + __assign_str(name, info->name); + __entry->id = info->dev_id; + __entry->delay = info->p_delay; + __entry->link = link; + __entry->session = link->parent; + ), + TP_printk( + "ReqMgr Connect name=%s id=%u pd=%d link=%pK session=%pK", + __get_str(name), __entry->id, __entry->delay, + __entry->link, __entry->session + ) +); + +TRACE_EVENT(cam_req_mgr_apply_request, + TP_PROTO(struct cam_req_mgr_core_link *link, + struct cam_req_mgr_apply_request *req, + struct cam_req_mgr_connected_device *dev), + TP_ARGS(link, req, dev), + TP_STRUCT__entry( + __string(name, dev->dev_info.name) + __field(uint32_t, dev_id) + __field(uint64_t, req_id) + __field(void*, link) + __field(void*, session) + ), + TP_fast_assign( + __assign_str(name, dev->dev_info.name); + __entry->dev_id = dev->dev_info.dev_id; + __entry->req_id = req->request_id; + __entry->link = link; + __entry->session = link->parent; + ), + TP_printk( + "ReqMgr ApplyRequest devname=%s devid=%u request=%lld link=%pK session=%pK", + __get_str(name), __entry->dev_id, __entry->req_id, + __entry->link, __entry->session + ) +); + +TRACE_EVENT(cam_req_mgr_add_req, + TP_PROTO(struct cam_req_mgr_core_link *link, + int idx, struct cam_req_mgr_add_request *add_req, + struct cam_req_mgr_req_tbl *tbl, + struct cam_req_mgr_connected_device *dev), + TP_ARGS(link, idx, add_req, tbl, dev), + TP_STRUCT__entry( + __string(name, dev->dev_info.name) + __field(uint32_t, dev_id) + __field(uint64_t, req_id) + __field(uint32_t, slot_id) + __field(uint32_t, delay) + __field(uint32_t, readymap) + __field(uint32_t, devicemap) + __field(void*, link) + __field(void*, session) + ), + TP_fast_assign( + __assign_str(name, dev->dev_info.name); + __entry->dev_id = dev->dev_info.dev_id; + __entry->req_id = add_req->req_id; + __entry->slot_id = idx; + __entry->delay = tbl->pd; + __entry->readymap = tbl->slot[idx].req_ready_map; + __entry->devicemap = tbl->dev_mask; + __entry->link = link; + __entry->session = link->parent; + ), + TP_printk( + "ReqMgr AddRequest devname=%s devid=%d request=%lld slot=%d pd=%d readymap=%x devicemap=%d link=%pk session=%pK", + __get_str(name), __entry->dev_id, __entry->req_id, + __entry->slot_id, __entry->delay, __entry->readymap, + __entry->devicemap, __entry->link, __entry->session + ) +); + +TRACE_EVENT(cam_submit_to_hw, + TP_PROTO(const char *entity, uint64_t req_id), + TP_ARGS(entity, req_id), + TP_STRUCT__entry( + __string(entity, entity) + __field(uint64_t, req_id) + ), + TP_fast_assign( + __assign_str(entity, entity); + __entry->req_id = req_id; + ), + TP_printk( + "%8s: submit request=%llu", + __get_str(entity), __entry->req_id + ) +); + +TRACE_EVENT(cam_irq_activated, + TP_PROTO(const char *entity, uint32_t irq_type), + TP_ARGS(entity, irq_type), + TP_STRUCT__entry( + __string(entity, entity) + __field(uint32_t, irq_type) + ), + TP_fast_assign( + __assign_str(entity, entity); + __entry->irq_type = irq_type; + ), + TP_printk( + "%8s: got irq type=%d", + __get_str(entity), __entry->irq_type + ) +); + +TRACE_EVENT(cam_irq_handled, + TP_PROTO(const char *entity, uint32_t irq_type), + TP_ARGS(entity, irq_type), + TP_STRUCT__entry( + __string(entity, entity) + __field(uint32_t, irq_type) + ), + TP_fast_assign( + __assign_str(entity, entity); + __entry->irq_type = irq_type; + ), + TP_printk( + "%8s: handled irq type=%d", + __get_str(entity), __entry->irq_type + ) +); + +TRACE_EVENT(cam_cdm_cb, + TP_PROTO(const char *entity, uint32_t status), + TP_ARGS(entity, status), + TP_STRUCT__entry( + __string(entity, entity) + __field(uint32_t, status) + ), + TP_fast_assign( + __assign_str(entity, entity); + __entry->status = status; + ), + TP_printk( + "%8s: cdm cb status=%d", + __get_str(entity), __entry->status + ) +); + +#endif /* _CAM_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/media/platform/msm/vidc/Kconfig b/drivers/media/platform/msm/vidc/Kconfig new file mode 100644 index 000000000000..af46985494fb --- /dev/null +++ b/drivers/media/platform/msm/vidc/Kconfig @@ -0,0 +1,8 @@ +# +# VIDEO CORE +# + +menuconfig MSM_VIDC_LEGACY_V4L2 + tristate "Qualcomm Technologies, Inc. MSM V4L2 based video driver" + depends on ARCH_QCOM && VIDEO_V4L2 + select VIDEOBUF2_CORE diff --git a/drivers/media/platform/msm/vidc/Makefile b/drivers/media/platform/msm/vidc/Makefile new file mode 100644 index 000000000000..57bf05ce5b22 --- /dev/null +++ b/drivers/media/platform/msm/vidc/Makefile @@ -0,0 +1,22 @@ +ccflags-y += -I$(srctree)/drivers/media/platform/msm/vidc/ + +msm-vidc-objs := msm_v4l2_vidc.o \ + msm_v4l2_private.o \ + msm_vidc_platform.o \ + msm_vidc_common.o \ + msm_vidc.o \ + msm_vdec.o \ + msm_venc.o \ + msm_cvp.o \ + msm_smem.o \ + msm_vidc_debug.o \ + msm_vidc_ar50_dyn_gov.o \ + msm_vidc_dyn_gov.o \ + msm_vidc_res_parse.o \ + venus_hfi.o \ + hfi_response_handler.o \ + hfi_packetization.o \ + vidc_hfi.o \ + msm_vidc_clocks.o + +obj-$(CONFIG_MSM_VIDC_LEGACY_V4L2) := msm-vidc.o diff --git a/drivers/media/platform/msm/vidc/fixedpoint.h b/drivers/media/platform/msm/vidc/fixedpoint.h new file mode 100644 index 000000000000..da0781f14c4b --- /dev/null +++ b/drivers/media/platform/msm/vidc/fixedpoint.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifdef _FIXP_ARITH_H +#error "This implementation is meant to override fixp-arith.h, don't use both" +#endif + +#ifndef __FP_H__ +#define __FP_H__ + +/* + * Normally would typedef'ed, but checkpatch doesn't like typedef. + * Also should be normally typedef'ed to intmax_t but that doesn't seem to be + * available in the kernel + */ +#define fp_t size_t + +/* (Arbitrarily) make the first 25% of the bits to be the fractional bits */ +#define FP_FRACTIONAL_BITS ((sizeof(fp_t) * 8) / 4) + +#define FP(__i, __f_n, __f_d) \ + ((((fp_t)(__i)) << FP_FRACTIONAL_BITS) + \ + (((__f_n) << FP_FRACTIONAL_BITS) / (__f_d))) + +#define FP_INT(__i) FP(__i, 0, 1) +#define FP_ONE FP_INT(1) +#define FP_ZERO FP_INT(0) + +static inline size_t fp_frac_base(void) +{ + return GENMASK(FP_FRACTIONAL_BITS - 1, 0); +} + +static inline size_t fp_frac(fp_t a) +{ + return a & GENMASK(FP_FRACTIONAL_BITS - 1, 0); +} + +static inline size_t fp_int(fp_t a) +{ + return a >> FP_FRACTIONAL_BITS; +} + +static inline size_t fp_round(fp_t a) +{ + /* is the fractional part >= frac_max / 2? */ + bool round_up = fp_frac(a) >= fp_frac_base() / 2; + + return fp_int(a) + round_up; +} + +static inline fp_t fp_mult(fp_t a, fp_t b) +{ + return (a * b) >> FP_FRACTIONAL_BITS; +} + + +static inline fp_t fp_div(fp_t a, fp_t b) +{ + return (a << FP_FRACTIONAL_BITS) / b; +} + +#endif diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c new file mode 100644 index 000000000000..fc51f6ddd5f1 --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -0,0 +1,2206 @@ +/* Copyright (c) 2012-2020, 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 "hfi_packetization.h" +#include "msm_vidc_debug.h" + +/* Set up look-up tables to convert HAL_* to HFI_*. + * + * The tables below mostly take advantage of the fact that most + * HAL_* types are defined bitwise. So if we index them normally + * when declaring the tables, we end up with huge arrays with wasted + * space. So before indexing them, we apply log2 to use a more + * sensible index. + */ + +static int entropy_mode[] = { + [ilog2(HAL_H264_ENTROPY_CAVLC)] = HFI_H264_ENTROPY_CAVLC, + [ilog2(HAL_H264_ENTROPY_CABAC)] = HFI_H264_ENTROPY_CABAC, +}; + +static int statistics_mode[] = { + [ilog2(HAL_STATISTICS_MODE_DEFAULT)] = HFI_STATISTICS_MODE_DEFAULT, + [ilog2(HAL_STATISTICS_MODE_1)] = HFI_STATISTICS_MODE_1, + [ilog2(HAL_STATISTICS_MODE_2)] = HFI_STATISTICS_MODE_2, + [ilog2(HAL_STATISTICS_MODE_3)] = HFI_STATISTICS_MODE_3, +}; + +static int color_format[] = { + [ilog2(HAL_COLOR_FORMAT_MONOCHROME)] = HFI_COLOR_FORMAT_MONOCHROME, + [ilog2(HAL_COLOR_FORMAT_NV12)] = HFI_COLOR_FORMAT_NV12, + [ilog2(HAL_COLOR_FORMAT_NV21)] = HFI_COLOR_FORMAT_NV21, + [ilog2(HAL_COLOR_FORMAT_NV12_4x4TILE)] = HFI_COLOR_FORMAT_NV12_4x4TILE, + [ilog2(HAL_COLOR_FORMAT_NV21_4x4TILE)] = HFI_COLOR_FORMAT_NV21_4x4TILE, + [ilog2(HAL_COLOR_FORMAT_YUYV)] = HFI_COLOR_FORMAT_YUYV, + [ilog2(HAL_COLOR_FORMAT_YVYU)] = HFI_COLOR_FORMAT_YVYU, + [ilog2(HAL_COLOR_FORMAT_UYVY)] = HFI_COLOR_FORMAT_UYVY, + [ilog2(HAL_COLOR_FORMAT_VYUY)] = HFI_COLOR_FORMAT_VYUY, + [ilog2(HAL_COLOR_FORMAT_RGB565)] = HFI_COLOR_FORMAT_RGB565, + [ilog2(HAL_COLOR_FORMAT_BGR565)] = HFI_COLOR_FORMAT_BGR565, + [ilog2(HAL_COLOR_FORMAT_RGB888)] = HFI_COLOR_FORMAT_RGB888, + [ilog2(HAL_COLOR_FORMAT_BGR888)] = HFI_COLOR_FORMAT_BGR888, + /* UBWC Color formats*/ + [ilog2(HAL_COLOR_FORMAT_NV12_UBWC)] = HFI_COLOR_FORMAT_NV12_UBWC, + [ilog2(HAL_COLOR_FORMAT_NV12_TP10_UBWC)] = + HFI_COLOR_FORMAT_YUV420_TP10_UBWC, + /*P010 10bit format*/ + [ilog2(HAL_COLOR_FORMAT_P010)] = HFI_COLOR_FORMAT_P010, + [ilog2(HAL_COLOR_FORMAT_NV12_512)] = HFI_COLOR_FORMAT_NV12, +}; + +static int nal_type[] = { + [ilog2(HAL_NAL_FORMAT_STARTCODES)] = HFI_NAL_FORMAT_STARTCODES, + [ilog2(HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER)] = + HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER, + [ilog2(HAL_NAL_FORMAT_ONE_BYTE_LENGTH)] = + HFI_NAL_FORMAT_ONE_BYTE_LENGTH, + [ilog2(HAL_NAL_FORMAT_TWO_BYTE_LENGTH)] = + HFI_NAL_FORMAT_TWO_BYTE_LENGTH, + [ilog2(HAL_NAL_FORMAT_FOUR_BYTE_LENGTH)] = + HFI_NAL_FORMAT_FOUR_BYTE_LENGTH, +}; + +static inline int hal_to_hfi_type(int property, int hal_type) +{ + if (hal_type <= 0 || roundup_pow_of_two(hal_type) != hal_type) { + /* + * Not a power of 2, it's not going + * to be in any of the tables anyway + */ + return -EINVAL; + } + + if (hal_type) + hal_type = ilog2(hal_type); + + switch (property) { + case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: + return (hal_type >= ARRAY_SIZE(entropy_mode)) ? + -ENOTSUPP : entropy_mode[hal_type]; + case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT: + return (hal_type >= ARRAY_SIZE(color_format)) ? + -ENOTSUPP : color_format[hal_type]; + case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: + return (hal_type >= ARRAY_SIZE(nal_type)) ? + -ENOTSUPP : nal_type[hal_type]; + case HAL_PARAM_VENC_MBI_STATISTICS_MODE: + return (hal_type >= ARRAY_SIZE(statistics_mode)) ? + -ENOTSUPP : statistics_mode[hal_type]; + default: + return -ENOTSUPP; + } +} + +enum hal_domain vidc_get_hal_domain(u32 hfi_domain) +{ + enum hal_domain hal_domain = 0; + + switch (hfi_domain) { + case HFI_VIDEO_DOMAIN_VPE: + hal_domain = HAL_VIDEO_DOMAIN_VPE; + break; + case HFI_VIDEO_DOMAIN_ENCODER: + hal_domain = HAL_VIDEO_DOMAIN_ENCODER; + break; + case HFI_VIDEO_DOMAIN_DECODER: + hal_domain = HAL_VIDEO_DOMAIN_DECODER; + break; + case HFI_VIDEO_DOMAIN_CVP: + hal_domain = HAL_VIDEO_DOMAIN_CVP; + break; + default: + dprintk(VIDC_ERR, "%s: invalid domain %x\n", + __func__, hfi_domain); + hal_domain = 0; + break; + } + return hal_domain; +} + +enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec) +{ + enum hal_video_codec hal_codec = 0; + + switch (hfi_codec) { + case HFI_VIDEO_CODEC_H264: + hal_codec = HAL_VIDEO_CODEC_H264; + break; + case HFI_VIDEO_CODEC_MPEG1: + hal_codec = HAL_VIDEO_CODEC_MPEG1; + break; + case HFI_VIDEO_CODEC_MPEG2: + hal_codec = HAL_VIDEO_CODEC_MPEG2; + break; + case HFI_VIDEO_CODEC_VP8: + hal_codec = HAL_VIDEO_CODEC_VP8; + break; + case HFI_VIDEO_CODEC_HEVC: + hal_codec = HAL_VIDEO_CODEC_HEVC; + break; + case HFI_VIDEO_CODEC_VP9: + hal_codec = HAL_VIDEO_CODEC_VP9; + break; + case HFI_VIDEO_CODEC_TME: + hal_codec = HAL_VIDEO_CODEC_TME; + break; + case HFI_VIDEO_CODEC_CVP: + hal_codec = HAL_VIDEO_CODEC_CVP; + break; + default: + dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n", + __func__, hfi_codec); + hal_codec = 0; + break; + } + return hal_codec; +} + + +u32 vidc_get_hfi_domain(enum hal_domain hal_domain) +{ + u32 hfi_domain; + + switch (hal_domain) { + case HAL_VIDEO_DOMAIN_VPE: + hfi_domain = HFI_VIDEO_DOMAIN_VPE; + break; + case HAL_VIDEO_DOMAIN_ENCODER: + hfi_domain = HFI_VIDEO_DOMAIN_ENCODER; + break; + case HAL_VIDEO_DOMAIN_DECODER: + hfi_domain = HFI_VIDEO_DOMAIN_DECODER; + break; + case HAL_VIDEO_DOMAIN_CVP: + hfi_domain = HFI_VIDEO_DOMAIN_CVP; + break; + default: + dprintk(VIDC_ERR, "%s: invalid domain 0x%x\n", + __func__, hal_domain); + hfi_domain = 0; + break; + } + return hfi_domain; +} + +u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec) +{ + u32 hfi_codec = 0; + + switch (hal_codec) { + case HAL_VIDEO_CODEC_H264: + hfi_codec = HFI_VIDEO_CODEC_H264; + break; + case HAL_VIDEO_CODEC_MPEG1: + hfi_codec = HFI_VIDEO_CODEC_MPEG1; + break; + case HAL_VIDEO_CODEC_MPEG2: + hfi_codec = HFI_VIDEO_CODEC_MPEG2; + break; + case HAL_VIDEO_CODEC_VP8: + hfi_codec = HFI_VIDEO_CODEC_VP8; + break; + case HAL_VIDEO_CODEC_HEVC: + hfi_codec = HFI_VIDEO_CODEC_HEVC; + break; + case HAL_VIDEO_CODEC_VP9: + hfi_codec = HFI_VIDEO_CODEC_VP9; + break; + case HAL_VIDEO_CODEC_TME: + hfi_codec = HFI_VIDEO_CODEC_TME; + break; + case HAL_VIDEO_CODEC_CVP: + hfi_codec = HFI_VIDEO_CODEC_CVP; + break; + default: + dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n", + __func__, hal_codec); + hfi_codec = 0; + break; + } + return hfi_codec; +} + +static void create_pkt_enable(void *pkt, u32 type, bool enable) +{ + u32 *pkt_header = pkt; + u32 *pkt_type = &pkt_header[0]; + struct hfi_enable *hfi_enable = (struct hfi_enable *)&pkt_header[1]; + + *pkt_type = type; + hfi_enable->enable = enable; +} + +int create_pkt_cmd_sys_init(struct hfi_cmd_sys_init_packet *pkt, + u32 arch_type) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SYS_INIT; + pkt->size = sizeof(struct hfi_cmd_sys_init_packet); + pkt->arch_type = arch_type; + return rc; +} + +int create_pkt_cmd_sys_pc_prep(struct hfi_cmd_sys_pc_prep_packet *pkt) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SYS_PC_PREP; + pkt->size = sizeof(struct hfi_cmd_sys_pc_prep_packet); + return rc; +} + +int create_pkt_cmd_sys_debug_config( + struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode) +{ + struct hfi_debug_config *hfi; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(struct hfi_debug_config) + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG; + hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1]; + hfi->debug_config = mode; + hfi->debug_mode = HFI_DEBUG_MODE_QUEUE; + if (msm_vidc_fw_debug_mode + <= (HFI_DEBUG_MODE_QUEUE | HFI_DEBUG_MODE_QDSS)) + hfi->debug_mode = msm_vidc_fw_debug_mode; + return 0; +} + +int create_pkt_cmd_sys_coverage_config( + struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode) +{ + if (!pkt) { + dprintk(VIDC_ERR, "In %s(), No input packet\n", __func__); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE; + pkt->rg_property_data[1] = mode; + dprintk(VIDC_DBG, "Firmware coverage mode %d\n", + pkt->rg_property_data[1]); + return 0; +} + +int create_pkt_cmd_sys_set_resource( + struct hfi_cmd_sys_set_resource_packet *pkt, + struct vidc_resource_hdr *res_hdr, + void *res_value) +{ + int rc = 0; + u32 i = 0; + + if (!pkt || !res_hdr || !res_value) { + dprintk(VIDC_ERR, + "Invalid paramas pkt %pK res_hdr %pK res_value %pK\n", + pkt, res_hdr, res_value); + return -EINVAL; + } + + pkt->packet_type = HFI_CMD_SYS_SET_RESOURCE; + pkt->size = sizeof(struct hfi_cmd_sys_set_resource_packet); + pkt->resource_handle = hash32_ptr(res_hdr->resource_handle); + + switch (res_hdr->resource_id) { + case VIDC_RESOURCE_SYSCACHE: + { + struct hfi_resource_syscache_info_type *res_sc_info = + (struct hfi_resource_syscache_info_type *) res_value; + struct hfi_resource_subcache_type *res_sc = + (struct hfi_resource_subcache_type *) + &(res_sc_info->rg_subcache_entries[0]); + + struct hfi_resource_syscache_info_type *hfi_sc_info = + (struct hfi_resource_syscache_info_type *) + &pkt->rg_resource_data[0]; + + struct hfi_resource_subcache_type *hfi_sc = + (struct hfi_resource_subcache_type *) + &(hfi_sc_info->rg_subcache_entries[0]); + + pkt->resource_type = HFI_RESOURCE_SYSCACHE; + hfi_sc_info->num_entries = res_sc_info->num_entries; + + pkt->size += (sizeof(struct hfi_resource_subcache_type)) + * hfi_sc_info->num_entries; + + for (i = 0; i < hfi_sc_info->num_entries; i++) { + hfi_sc[i] = res_sc[i]; + dprintk(VIDC_DBG, "entry hfi#%d, sc_id %d, size %d\n", + i, hfi_sc[i].sc_id, hfi_sc[i].size); + } + break; + } + default: + dprintk(VIDC_ERR, + "Invalid resource_id %d\n", res_hdr->resource_id); + rc = -ENOTSUPP; + } + + return rc; +} + +int create_pkt_cmd_sys_release_resource( + struct hfi_cmd_sys_release_resource_packet *pkt, + struct vidc_resource_hdr *res_hdr) +{ + int rc = 0; + + if (!pkt || !res_hdr) { + dprintk(VIDC_ERR, + "Invalid paramas pkt %pK res_hdr %pK\n", + pkt, res_hdr); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_release_resource_packet); + pkt->packet_type = HFI_CMD_SYS_RELEASE_RESOURCE; + pkt->resource_handle = hash32_ptr(res_hdr->resource_handle); + + switch (res_hdr->resource_id) { + case VIDC_RESOURCE_SYSCACHE: + pkt->resource_type = HFI_RESOURCE_SYSCACHE; + break; + default: + dprintk(VIDC_ERR, + "Invalid resource_id %d\n", res_hdr->resource_id); + rc = -ENOTSUPP; + } + + dprintk(VIDC_DBG, + "rel_res: pkt_type 0x%x res_type 0x%x prepared\n", + pkt->packet_type, pkt->resource_type); + + return rc; +} + +int create_pkt_cmd_sys_ping(struct hfi_cmd_sys_ping_packet *pkt) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_ping_packet); + pkt->packet_type = HFI_CMD_SYS_PING; + + return rc; +} + +inline int create_pkt_cmd_sys_session_init( + struct hfi_cmd_sys_session_init_packet *pkt, + struct hal_session *session, + u32 session_domain, u32 session_codec) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_session_init_packet); + pkt->packet_type = HFI_CMD_SYS_SESSION_INIT; + pkt->session_id = hash32_ptr(session); + pkt->session_domain = vidc_get_hfi_domain(session_domain); + pkt->session_codec = vidc_get_hfi_codec(session_codec); + if (!pkt->session_codec) + return -EINVAL; + + return rc; +} + +int create_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt, + int pkt_type, struct hal_session *session) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct vidc_hal_session_cmd_pkt); + pkt->packet_type = pkt_type; + pkt->session_id = hash32_ptr(session); + + return rc; +} + +int create_pkt_cmd_sys_power_control( + struct hfi_cmd_sys_set_property_packet *pkt, u32 enable) +{ + struct hfi_enable *hfi; + + if (!pkt) { + dprintk(VIDC_ERR, "No input packet\n"); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(struct hfi_enable) + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = enable; + return 0; +} + +static u32 get_hfi_buffer(int hal_buffer) +{ + u32 buffer; + + switch (hal_buffer) { + case HAL_BUFFER_INPUT: + buffer = HFI_BUFFER_INPUT; + break; + case HAL_BUFFER_OUTPUT: + buffer = HFI_BUFFER_OUTPUT; + break; + case HAL_BUFFER_OUTPUT2: + buffer = HFI_BUFFER_OUTPUT2; + break; + case HAL_BUFFER_EXTRADATA_INPUT: + buffer = HFI_BUFFER_EXTRADATA_INPUT; + break; + case HAL_BUFFER_EXTRADATA_OUTPUT: + buffer = HFI_BUFFER_EXTRADATA_OUTPUT; + break; + case HAL_BUFFER_EXTRADATA_OUTPUT2: + buffer = HFI_BUFFER_EXTRADATA_OUTPUT2; + break; + case HAL_BUFFER_INTERNAL_SCRATCH: + buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH; + break; + case HAL_BUFFER_INTERNAL_SCRATCH_1: + buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1; + break; + case HAL_BUFFER_INTERNAL_SCRATCH_2: + buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2; + break; + case HAL_BUFFER_INTERNAL_PERSIST: + buffer = HFI_BUFFER_INTERNAL_PERSIST; + break; + case HAL_BUFFER_INTERNAL_PERSIST_1: + buffer = HFI_BUFFER_INTERNAL_PERSIST_1; + break; + default: + dprintk(VIDC_ERR, "Invalid buffer: %#x\n", + hal_buffer); + buffer = 0; + break; + } + return buffer; +} + +static int get_hfi_extradata_index(enum hal_extradata_id index) +{ + int ret = 0; + + switch (index) { + case HAL_EXTRADATA_INTERLACE_VIDEO: + ret = HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA; + break; + case HAL_EXTRADATA_TIMESTAMP: + ret = HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA; + break; + case HAL_EXTRADATA_S3D_FRAME_PACKING: + ret = HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA; + break; + case HAL_EXTRADATA_FRAME_RATE: + ret = HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA; + break; + case HAL_EXTRADATA_PANSCAN_WINDOW: + ret = HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA; + break; + case HAL_EXTRADATA_RECOVERY_POINT_SEI: + ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_NUM_CONCEALED_MB: + ret = HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB; + break; + case HAL_EXTRADATA_ASPECT_RATIO: + case HAL_EXTRADATA_OUTPUT_CROP: + case HAL_EXTRADATA_INPUT_CROP: + ret = HFI_PROPERTY_PARAM_INDEX_EXTRADATA; + break; + case HAL_EXTRADATA_MPEG2_SEQDISP: + ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA; + break; + case HAL_EXTRADATA_STREAM_USERDATA: + ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA; + break; + case HAL_EXTRADATA_DEC_FRAME_QP: + ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA; + break; + case HAL_EXTRADATA_ENC_FRAME_QP: + ret = HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA; + break; + case HAL_EXTRADATA_LTR_INFO: + ret = HFI_PROPERTY_PARAM_VENC_LTR_INFO; + break; + case HAL_EXTRADATA_ROI_QP: + ret = HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA; + break; + case HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI: + ret = + HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI: + ret = HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_VUI_DISPLAY_INFO: + ret = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA; + break; + case HAL_EXTRADATA_VPX_COLORSPACE: + ret = HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA; + break; + case HAL_EXTRADATA_UBWC_CR_STATS_INFO: + ret = HFI_PROPERTY_PARAM_VDEC_UBWC_CR_STAT_INFO_EXTRADATA; + break; + case HAL_EXTRADATA_HDR10PLUS_METADATA: + ret = HFI_PROPERTY_PARAM_VENC_HDR10PLUS_METADATA_EXTRADATA; + break; + case HAL_EXTRADATA_ENC_DTS_METADATA: + ret = HFI_PROPERTY_PARAM_VENC_DTS_INFO; + break; + default: + dprintk(VIDC_WARN, "Extradata index not found: %d\n", index); + break; + } + return ret; +} + +static int get_hfi_extradata_id(enum hal_extradata_id index) +{ + int ret = 0; + + switch (index) { + case HAL_EXTRADATA_ASPECT_RATIO: + ret = MSM_VIDC_EXTRADATA_ASPECT_RATIO; + break; + case HAL_EXTRADATA_OUTPUT_CROP: + ret = MSM_VIDC_EXTRADATA_OUTPUT_CROP; + break; + case HAL_EXTRADATA_INPUT_CROP: + ret = MSM_VIDC_EXTRADATA_INPUT_CROP; + break; + default: + ret = get_hfi_extradata_index(index); + break; + } + return ret; +} + +static u32 get_hfi_ltr_mode(enum ltr_mode ltr_mode_type) +{ + u32 ltrmode; + + switch (ltr_mode_type) { + case HAL_LTR_MODE_DISABLE: + ltrmode = HFI_LTR_MODE_DISABLE; + break; + case HAL_LTR_MODE_MANUAL: + ltrmode = HFI_LTR_MODE_MANUAL; + break; + default: + dprintk(VIDC_ERR, "Invalid ltr mode: %#x\n", + ltr_mode_type); + ltrmode = HFI_LTR_MODE_DISABLE; + break; + } + return ltrmode; +} + +static u32 get_hfi_work_mode(enum hal_work_mode work_mode) +{ + u32 hfi_work_mode; + + switch (work_mode) { + case VIDC_WORK_MODE_1: + hfi_work_mode = HFI_WORKMODE_1; + break; + case VIDC_WORK_MODE_2: + hfi_work_mode = HFI_WORKMODE_2; + break; + default: + dprintk(VIDC_ERR, "Invalid work mode: %#x\n", + work_mode); + hfi_work_mode = HFI_WORKMODE_2; + break; + } + return hfi_work_mode; +} + +int create_pkt_cmd_session_set_buffers( + struct hfi_cmd_session_set_buffers_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info) +{ + int rc = 0; + int i = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SESSION_SET_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->buffer_size = buffer_info->buffer_size; + pkt->min_buffer_size = buffer_info->buffer_size; + pkt->num_buffers = buffer_info->num_buffers; + + if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT || + buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + pkt->extra_data_size = buffer_info->extradata_size; + + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) - + sizeof(u32) + (buffer_info->num_buffers * + sizeof(struct hfi_buffer_info)); + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + buff->buffer_addr = + (u32)buffer_info->align_device_addr; + buff->extra_data_addr = + (u32)buffer_info->extradata_addr; + } + } else { + pkt->extra_data_size = 0; + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) + + ((buffer_info->num_buffers - 1) * sizeof(u32)); + for (i = 0; i < pkt->num_buffers; i++) { + pkt->rg_buffer_info[i] = + (u32)buffer_info->align_device_addr; + } + } + + pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type); + if (!pkt->buffer_type) + return -EINVAL; + + return rc; +} + +int create_pkt_cmd_session_release_buffers( + struct hfi_cmd_session_release_buffer_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info) +{ + int rc = 0; + int i = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SESSION_RELEASE_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->buffer_size = buffer_info->buffer_size; + pkt->num_buffers = buffer_info->num_buffers; + + if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT || + buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + buff->buffer_addr = + (u32)buffer_info->align_device_addr; + buff->extra_data_addr = + (u32)buffer_info->extradata_addr; + } + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) - + sizeof(u32) + (buffer_info->num_buffers * + sizeof(struct hfi_buffer_info)); + } else { + for (i = 0; i < pkt->num_buffers; i++) { + pkt->rg_buffer_info[i] = + (u32)buffer_info->align_device_addr; + } + pkt->extra_data_size = 0; + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) + + ((buffer_info->num_buffers - 1) * sizeof(u32)); + } + pkt->response_req = buffer_info->response_required; + pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type); + if (!pkt->buffer_type) + return -EINVAL; + return rc; +} + +int create_pkt_cmd_session_register_buffer( + struct hfi_cmd_session_register_buffers_packet *pkt, + struct hal_session *session, + struct vidc_register_buffer *buffer) +{ + int rc = 0, i; + struct hfi_buffer_mapping_type *buf; + + if (!pkt || !session) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + pkt->packet_type = HFI_CMD_SESSION_REGISTER_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->client_data = buffer->client_data; + pkt->response_req = buffer->response_required; + pkt->num_buffers = 1; + pkt->size = sizeof(struct hfi_cmd_session_register_buffers_packet) - + sizeof(u32) + (pkt->num_buffers * + sizeof(struct hfi_buffer_mapping_type)); + + buf = (struct hfi_buffer_mapping_type *)pkt->buffer; + for (i = 0; i < pkt->num_buffers; i++) { + buf->index = buffer->index; + buf->device_addr = buffer->device_addr; + buf->size = buffer->size; + buf++; + } + + return rc; +} + +int create_pkt_cmd_session_unregister_buffer( + struct hfi_cmd_session_unregister_buffers_packet *pkt, + struct hal_session *session, + struct vidc_unregister_buffer *buffer) +{ + int rc = 0, i; + struct hfi_buffer_mapping_type *buf; + + if (!pkt || !session) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + pkt->packet_type = HFI_CMD_SESSION_UNREGISTER_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->client_data = buffer->client_data; + pkt->response_req = buffer->response_required; + pkt->num_buffers = 1; + pkt->size = sizeof(struct hfi_cmd_session_unregister_buffers_packet) - + sizeof(u32) + (pkt->num_buffers * + sizeof(struct hfi_buffer_mapping_type)); + + buf = (struct hfi_buffer_mapping_type *)pkt->buffer; + for (i = 0; i < pkt->num_buffers; i++) { + buf->index = buffer->index; + buf->device_addr = buffer->device_addr; + buf->size = buffer->size; + buf++; + } + + return rc; +} + +int create_pkt_cmd_session_etb_decoder( + struct hfi_cmd_session_empty_buffer_compressed_packet *pkt, + struct hal_session *session, struct vidc_frame_data *input_frame) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = + sizeof(struct hfi_cmd_session_empty_buffer_compressed_packet); + pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->session_id = hash32_ptr(session); + pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp); + pkt->flags = input_frame->flags; + pkt->mark_target = input_frame->mark_target; + pkt->mark_data = input_frame->mark_data; + pkt->offset = input_frame->offset; + pkt->alloc_len = input_frame->alloc_len; + pkt->filled_len = input_frame->filled_len; + pkt->input_tag = input_frame->input_tag; + pkt->packet_buffer = (u32)input_frame->device_addr; + + trace_msm_v4l2_vidc_buffer_event_start("ETB", + input_frame->device_addr, input_frame->timestamp, + input_frame->alloc_len, input_frame->filled_len, + input_frame->offset); + + if (!pkt->packet_buffer) + rc = -EINVAL; + return rc; +} + +int create_pkt_cmd_session_etb_encoder( + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet *pkt, + struct hal_session *session, struct vidc_frame_data *input_frame) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet); + pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->session_id = hash32_ptr(session); + pkt->view_id = 0; + pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp); + pkt->flags = input_frame->flags; + pkt->mark_target = input_frame->mark_target; + pkt->mark_data = input_frame->mark_data; + pkt->offset = input_frame->offset; + pkt->alloc_len = input_frame->alloc_len; + pkt->filled_len = input_frame->filled_len; + pkt->input_tag = input_frame->input_tag; + pkt->packet_buffer = (u32)input_frame->device_addr; + pkt->extra_data_buffer = (u32)input_frame->extradata_addr; + + trace_msm_v4l2_vidc_buffer_event_start("ETB", + input_frame->device_addr, input_frame->timestamp, + input_frame->alloc_len, input_frame->filled_len, + input_frame->offset); + + if (!pkt->packet_buffer) + rc = -EINVAL; + return rc; +} + +int create_pkt_cmd_session_ftb(struct hfi_cmd_session_fill_buffer_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *output_frame) +{ + int rc = 0; + + if (!pkt || !session || !output_frame) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_fill_buffer_packet); + pkt->packet_type = HFI_CMD_SESSION_FILL_BUFFER; + pkt->session_id = hash32_ptr(session); + + if (output_frame->buffer_type == HAL_BUFFER_OUTPUT) + pkt->stream_id = 0; + else if (output_frame->buffer_type == HAL_BUFFER_OUTPUT2) + pkt->stream_id = 1; + + if (!output_frame->device_addr) + return -EINVAL; + + pkt->packet_buffer = (u32)output_frame->device_addr; + pkt->output_tag = output_frame->output_tag; + pkt->extra_data_buffer = (u32)output_frame->extradata_addr; + pkt->alloc_len = output_frame->alloc_len; + pkt->filled_len = output_frame->filled_len; + pkt->offset = output_frame->offset; + pkt->rgData[0] = output_frame->extradata_size; + + trace_msm_v4l2_vidc_buffer_event_start("FTB", + output_frame->device_addr, output_frame->timestamp, + output_frame->alloc_len, output_frame->filled_len, + output_frame->offset); + + return rc; +} + +int create_pkt_cmd_session_get_buf_req( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_get_property_packet); + pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS; + + return rc; +} + +int create_pkt_cmd_session_flush(struct hfi_cmd_session_flush_packet *pkt, + struct hal_session *session, enum hal_flush flush_mode) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_flush_packet); + pkt->packet_type = HFI_CMD_SESSION_FLUSH; + pkt->session_id = hash32_ptr(session); + switch (flush_mode) { + case HAL_FLUSH_INPUT: + pkt->flush_type = HFI_FLUSH_INPUT; + break; + case HAL_FLUSH_OUTPUT: + pkt->flush_type = HFI_FLUSH_OUTPUT; + break; + case HAL_FLUSH_ALL: + pkt->flush_type = HFI_FLUSH_ALL; + break; + default: + dprintk(VIDC_ERR, "Invalid flush mode: %#x\n", flush_mode); + return -EINVAL; + } + return rc; +} + +int create_pkt_cmd_session_get_property( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session, enum hal_property ptype) +{ + /* Currently no get property is supported */ + dprintk(VIDC_ERR, "%s cmd:%#x not supported\n", __func__, + ptype); + return -EINVAL; +} + +int create_pkt_cmd_session_set_property( + struct hfi_cmd_session_set_property_packet *pkt, + struct hal_session *session, + enum hal_property ptype, void *pdata) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_set_property_packet); + pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + + dprintk(VIDC_DBG, "Setting HAL Property = 0x%x\n", ptype); + + switch (ptype) { + case HAL_CONFIG_FRAME_RATE: + { + u32 buffer_type; + struct hfi_frame_rate *hfi; + struct hal_frame_rate *prop = (struct hal_frame_rate *) pdata; + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE; + hfi = (struct hfi_frame_rate *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->frame_rate = prop->frame_rate; + pkt->size += sizeof(struct hfi_frame_rate); + break; + } + case HAL_CONFIG_OPERATING_RATE: + { + struct hfi_operating_rate *hfi; + struct hal_operating_rate *prop = + (struct hal_operating_rate *) pdata; + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_OPERATING_RATE; + hfi = (struct hfi_operating_rate *) &pkt->rg_property_data[1]; + hfi->operating_rate = prop->operating_rate; + pkt->size += sizeof(struct hfi_operating_rate); + break; + } + case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT: + { + u32 buffer_type; + struct hfi_uncompressed_format_select *hfi; + struct hal_uncompressed_format_select *prop = + (struct hal_uncompressed_format_select *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; + + hfi = (struct hfi_uncompressed_format_select *) + &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + hfi->format = hal_to_hfi_type( + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, + prop->format); + pkt->size += sizeof(struct hfi_uncompressed_format_select); + break; + } + case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: + { + struct hfi_uncompressed_plane_actual_constraints_info *hfi; + struct hal_uncompressed_plane_actual_constraints_info *prop = + (struct hal_uncompressed_plane_actual_constraints_info *) pdata; + u32 buffer_type; + u32 num_plane = prop->num_planes; + u32 hfi_pkt_size = + 2 * sizeof(u32) + + num_plane + * sizeof(struct hal_uncompressed_plane_constraints); + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO; + + hfi = (struct hfi_uncompressed_plane_actual_constraints_info *) + &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->num_planes = prop->num_planes; + memcpy(hfi->rg_plane_format, prop->rg_plane_format, + hfi->num_planes + *sizeof(struct hal_uncompressed_plane_constraints)); + pkt->size += hfi_pkt_size; + break; + } + case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: + break; + case HAL_PARAM_FRAME_SIZE: + { + struct hfi_frame_size *hfi; + struct hal_frame_size *prop = (struct hal_frame_size *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE; + hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->height = prop->height; + hfi->width = prop->width; + pkt->size += sizeof(struct hfi_frame_size); + break; + } + case HAL_CONFIG_REALTIME: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_REALTIME, + (((struct hal_enable *) pdata)->enable)); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_BUFFER_COUNT_ACTUAL: + { + struct hfi_buffer_count_actual *hfi; + struct hal_buffer_count_actual *prop = + (struct hal_buffer_count_actual *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; + hfi = (struct hfi_buffer_count_actual *) + &pkt->rg_property_data[1]; + hfi->buffer_count_actual = prop->buffer_count_actual; + hfi->buffer_count_min_host = prop->buffer_count_min_host; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + pkt->size += sizeof(struct hfi_buffer_count_actual); + + break; + } + case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: + { + struct hfi_nal_stream_format_select *hfi; + struct hal_nal_stream_format_select *prop = + (struct hal_nal_stream_format_select *)pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT; + hfi = (struct hfi_nal_stream_format_select *) + &pkt->rg_property_data[1]; + dprintk(VIDC_DBG, "data is :%d\n", + prop->nal_stream_format_select); + hfi->nal_stream_format_select = hal_to_hfi_type( + HAL_PARAM_NAL_STREAM_FORMAT_SELECT, + prop->nal_stream_format_select); + pkt->size += sizeof(struct hfi_nal_stream_format_select); + break; + } + case HAL_PARAM_VDEC_OUTPUT_ORDER: + { + int *data = (int *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER; + switch (*data) { + case HAL_OUTPUT_ORDER_DECODE: + pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DECODE; + break; + case HAL_OUTPUT_ORDER_DISPLAY: + pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DISPLAY; + break; + default: + dprintk(VIDC_ERR, "invalid output order: %#x\n", + *data); + break; + } + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE: + { + struct hfi_enable_picture *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE; + hfi = (struct hfi_enable_picture *) &pkt->rg_property_data[1]; + hfi->picture_type = + ((struct hfi_enable_picture *)pdata)->picture_type; + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VDEC_MULTI_STREAM: + { + struct hfi_multi_stream *hfi; + struct hal_multi_stream *prop = + (struct hal_multi_stream *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + hfi = (struct hfi_multi_stream *) &pkt->rg_property_data[1]; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + hfi->enable = prop->enable; + pkt->size += sizeof(struct hfi_multi_stream); + break; + } + case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VDEC_SYNC_FRAME_DECODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_SECURE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_SECURE_SESSION, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_CONFIG_VENC_REQUEST_IFRAME: + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME; + break; + case HAL_CONFIG_HEIC_FRAME_QUALITY: + { + struct hfi_heic_frame_quality *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY; + hfi = + (struct hfi_heic_frame_quality *) &pkt->rg_property_data[1]; + hfi->frame_quality = + ((struct hal_heic_frame_quality *)pdata)->frame_quality; + pkt->size += sizeof(u32) + + sizeof(struct hfi_heic_frame_quality); + break; + } + case HAL_CONFIG_HEIC_GRID_ENABLE: + { + struct hfi_heic_grid_enable *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE; + hfi = (struct hfi_heic_grid_enable *) &pkt->rg_property_data[1]; + hfi->grid_enable = + ((struct hal_heic_grid_enable *)pdata)->grid_enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_heic_grid_enable); + break; + } + case HAL_CONFIG_VENC_TARGET_BITRATE: + { + struct hfi_bitrate *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1]; + hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate; + hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id; + pkt->size += sizeof(struct hfi_bitrate); + break; + } + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + { + struct hfi_profile_level *hfi; + struct hal_profile_level *prop = + (struct hal_profile_level *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + hfi = (struct hfi_profile_level *) + &pkt->rg_property_data[1]; + + /* There is an assumption here that HAL level is same as + * HFI level + */ + hfi->level = prop->level; + hfi->profile = prop->profile; + if (hfi->profile <= 0) { + hfi->profile = HFI_H264_PROFILE_HIGH; + dprintk(VIDC_WARN, + "Profile %d not supported, falling back to high\n", + prop->profile); + } + + pkt->size += sizeof(struct hfi_profile_level); + break; + } + case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: + { + struct hfi_h264_entropy_control *hfi; + struct hal_h264_entropy_control *prop = + (struct hal_h264_entropy_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL; + hfi = (struct hfi_h264_entropy_control *) + &pkt->rg_property_data[1]; + hfi->entropy_mode = hal_to_hfi_type( + HAL_PARAM_VENC_H264_ENTROPY_CONTROL, + prop->entropy_mode); + + hfi->cabac_model = HFI_H264_CABAC_MODEL_0; + pkt->size += sizeof(struct hfi_h264_entropy_control); + break; + } + case HAL_PARAM_VENC_RATE_CONTROL: + { + u32 *rc; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; + rc = (u32 *)pdata; + switch ((enum hal_rate_control) *rc) { + case HAL_RATE_CONTROL_OFF: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_OFF; + break; + case HAL_RATE_CONTROL_CBR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_CFR; + break; + case HAL_RATE_CONTROL_VBR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_CFR; + break; + case HAL_RATE_CONTROL_MBR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_CFR; + break; + case HAL_RATE_CONTROL_CBR_VFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_VFR; + break; + case HAL_RATE_CONTROL_MBR_VFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_VFR; + break; + case HAL_RATE_CONTROL_CQ: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_CQ; + break; + default: + dprintk(VIDC_ERR, + "Invalid Rate control setting: %pK\n", + pdata); + break; + } + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_BITRATE_SAVINGS: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_BITRATE_SAVINGS, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL: + { + struct hfi_h264_db_control *hfi; + struct hal_h264_db_control *prop = + (struct hal_h264_db_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL; + hfi = (struct hfi_h264_db_control *) &pkt->rg_property_data[1]; + switch (prop->mode) { + case HAL_H264_DB_MODE_DISABLE: + hfi->mode = HFI_H264_DB_MODE_DISABLE; + break; + case HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY: + hfi->mode = HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; + break; + case HAL_H264_DB_MODE_ALL_BOUNDARY: + hfi->mode = HFI_H264_DB_MODE_ALL_BOUNDARY; + break; + default: + dprintk(VIDC_ERR, "Invalid deblocking mode: %#x\n", + prop->mode); + break; + } + hfi->slice_alpha_offset = prop->slice_alpha_offset; + hfi->slice_beta_offset = prop->slice_beta_offset; + pkt->size += sizeof(struct hfi_h264_db_control); + break; + } + case HAL_CONFIG_VENC_FRAME_QP: + { + struct hfi_quantization *hfi; + struct hal_quantization *hal_quant = + (struct hal_quantization *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_FRAME_QP; + hfi = (struct hfi_quantization *) &pkt->rg_property_data[1]; + hfi->qp_packed = hal_quant->qpi | hal_quant->qpp << 8 | + hal_quant->qpb << 16; + hfi->layer_id = hal_quant->layer_id; + hfi->enable = hal_quant->enable; + pkt->size += sizeof(struct hfi_quantization); + break; + } + case HAL_PARAM_VENC_SESSION_QP_RANGE: + { + struct hfi_quantization_range *hfi; + struct hal_quantization_range *hal_range = + (struct hal_quantization_range *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; + hfi = (struct hfi_quantization_range *) + &pkt->rg_property_data[1]; + + /* + * When creating the packet, pack the qp value as + * 0xbbppii, where ii = qp range for I-frames, + * pp = qp range for P-frames, etc. + */ + hfi->min_qp.qp_packed = hal_range->qpi_min | + hal_range->qpp_min << 8 | + hal_range->qpb_min << 16; + hfi->max_qp.qp_packed = hal_range->qpi_max | + hal_range->qpp_max << 8 | + hal_range->qpb_max << 16; + hfi->max_qp.layer_id = hal_range->layer_id; + hfi->min_qp.layer_id = hal_range->layer_id; + + pkt->size += sizeof(struct hfi_quantization_range); + break; + } + case HAL_CONFIG_VENC_INTRA_PERIOD: + { + struct hfi_intra_period *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD; + hfi = (struct hfi_intra_period *) &pkt->rg_property_data[1]; + memcpy(hfi, (struct hfi_intra_period *) pdata, + sizeof(struct hfi_intra_period)); + pkt->size += sizeof(struct hfi_intra_period); + + if (hfi->bframes) { + struct hfi_enable *hfi_enable; + u32 *prop_type; + + prop_type = (u32 *)((u8 *)&pkt->rg_property_data[0] + + sizeof(u32) + sizeof(struct hfi_intra_period)); + *prop_type = HFI_PROPERTY_PARAM_VENC_ADAPTIVE_B; + hfi_enable = (struct hfi_enable *)(prop_type + 1); + hfi_enable->enable = true; + pkt->num_properties = 2; + pkt->size += sizeof(struct hfi_enable) + sizeof(u32); + } + break; + } + case HAL_CONFIG_VENC_IDR_PERIOD: + { + struct hfi_idr_period *hfi; + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + hfi = (struct hfi_idr_period *) &pkt->rg_property_data[1]; + hfi->idr_period = ((struct hfi_idr_period *) pdata)->idr_period; + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_ADAPTIVE_B: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_ADAPTIVE_B, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VDEC_CONCEAL_COLOR: + { + struct hfi_conceal_color *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR; + hfi = (struct hfi_conceal_color *) &pkt->rg_property_data[1]; + if (hfi) { + hfi->conceal_color_8bit = + ((struct hfi_conceal_color *) pdata)-> + conceal_color_8bit; + hfi->conceal_color_10bit = + ((struct hfi_conceal_color *) pdata)-> + conceal_color_10bit; + } + pkt->size += sizeof(struct hfi_conceal_color); + break; + } + case HAL_PARAM_VPE_ROTATION: + { + struct hfi_vpe_rotation_type *hfi; + struct hal_vpe_rotation *prop = + (struct hal_vpe_rotation *) pdata; + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_VPE_ROTATION; + hfi = (struct hfi_vpe_rotation_type *)&pkt->rg_property_data[1]; + switch (prop->rotate) { + case 0: + hfi->rotation = HFI_ROTATE_NONE; + break; + case 90: + hfi->rotation = HFI_ROTATE_90; + break; + case 180: + hfi->rotation = HFI_ROTATE_180; + break; + case 270: + hfi->rotation = HFI_ROTATE_270; + break; + default: + dprintk(VIDC_ERR, "Invalid rotation setting: %#x\n", + prop->rotate); + rc = -EINVAL; + break; + } + switch (prop->flip) { + case HAL_FLIP_NONE: + hfi->flip = HFI_FLIP_NONE; + break; + case HAL_FLIP_HORIZONTAL: + hfi->flip = HFI_FLIP_HORIZONTAL; + break; + case HAL_FLIP_VERTICAL: + hfi->flip = HFI_FLIP_VERTICAL; + break; + case HAL_FLIP_BOTH: + hfi->flip = HFI_FLIP_HORIZONTAL | HFI_FLIP_VERTICAL; + break; + default: + dprintk(VIDC_ERR, "Invalid flip setting: %#x\n", + prop->flip); + rc = -EINVAL; + break; + } + pkt->size += sizeof(struct hfi_vpe_rotation_type); + break; + } + case HAL_PARAM_VENC_INTRA_REFRESH: + { + struct hfi_intra_refresh *hfi; + struct hal_intra_refresh *prop = + (struct hal_intra_refresh *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + hfi = (struct hfi_intra_refresh *) &pkt->rg_property_data[1]; + hfi->mbs = 0; + switch (prop->mode) { + case HAL_INTRA_REFRESH_NONE: + hfi->mode = HFI_INTRA_REFRESH_NONE; + break; + case HAL_INTRA_REFRESH_CYCLIC: + hfi->mode = HFI_INTRA_REFRESH_CYCLIC; + hfi->mbs = prop->ir_mbs; + break; + case HAL_INTRA_REFRESH_RANDOM: + hfi->mode = HFI_INTRA_REFRESH_RANDOM; + hfi->mbs = prop->ir_mbs; + break; + default: + dprintk(VIDC_ERR, + "Invalid intra refresh setting: %#x\n", + prop->mode); + break; + } + pkt->size += sizeof(struct hfi_intra_refresh); + break; + } + case HAL_PARAM_VENC_MULTI_SLICE_CONTROL: + { + struct hfi_multi_slice_control *hfi; + struct hal_multi_slice_control *prop = + (struct hal_multi_slice_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL; + hfi = (struct hfi_multi_slice_control *) + &pkt->rg_property_data[1]; + switch (prop->multi_slice) { + case HAL_MULTI_SLICE_OFF: + hfi->multi_slice = HFI_MULTI_SLICE_OFF; + break; + case HAL_MULTI_SLICE_BY_MB_COUNT: + hfi->multi_slice = HFI_MULTI_SLICE_BY_MB_COUNT; + break; + case HAL_MULTI_SLICE_BY_BYTE_COUNT: + hfi->multi_slice = HFI_MULTI_SLICE_BY_BYTE_COUNT; + break; + default: + dprintk(VIDC_ERR, "Invalid slice settings: %#x\n", + prop->multi_slice); + break; + } + hfi->slice_size = prop->slice_size; + pkt->size += sizeof(struct + hfi_multi_slice_control); + break; + } + case HAL_PARAM_INDEX_EXTRADATA: + { + struct hfi_index_extradata_config *hfi; + struct hal_extradata_enable *extra = pdata; + int id = 0; + + pkt->rg_property_data[0] = + get_hfi_extradata_index(extra->index); + hfi = (struct hfi_index_extradata_config *) + &pkt->rg_property_data[1]; + hfi->enable = extra->enable; + id = get_hfi_extradata_id(extra->index); + if (id) + hfi->index_extra_data_id = id; + else { + dprintk(VIDC_WARN, + "Failed to find extradata id: %d\n", + id); + rc = -EINVAL; + } + pkt->size += sizeof(struct hfi_index_extradata_config); + break; + } + case HAL_PARAM_VENC_SLICE_DELIVERY_MODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_VUI_TIMING_INFO: + { + struct hfi_vui_timing_info *hfi; + struct hal_vui_timing_info *timing_info = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_VUI_TIMING_INFO; + + hfi = (struct hfi_vui_timing_info *)&pkt->rg_property_data[1]; + hfi->enable = timing_info->enable; + hfi->fixed_frame_rate = timing_info->fixed_frame_rate; + hfi->time_scale = timing_info->time_scale; + + pkt->size += sizeof(struct hfi_vui_timing_info); + break; + } + case HAL_PARAM_VENC_GENERATE_AUDNAL: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_LTRMODE: + { + struct hfi_ltr_mode *hfi; + struct hal_ltr_mode *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_LTRMODE; + hfi = (struct hfi_ltr_mode *) &pkt->rg_property_data[1]; + hfi->ltr_mode = get_hfi_ltr_mode(hal->mode); + hfi->ltr_count = hal->count; + hfi->trust_mode = hal->trust_mode; + pkt->size += sizeof(struct hfi_ltr_mode); + break; + } + case HAL_CONFIG_VENC_USELTRFRAME: + { + struct hfi_ltr_use *hfi; + struct hal_ltr_use *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_USELTRFRAME; + hfi = (struct hfi_ltr_use *) &pkt->rg_property_data[1]; + hfi->frames = hal->frames; + hfi->ref_ltr = hal->ref_ltr; + hfi->use_constrnt = hal->use_constraint; + pkt->size += sizeof(struct hfi_ltr_use); + break; + } + case HAL_CONFIG_VENC_MARKLTRFRAME: + { + struct hfi_ltr_mark *hfi; + struct hal_ltr_mark *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME; + hfi = (struct hfi_ltr_mark *) &pkt->rg_property_data[1]; + hfi->mark_frame = hal->mark_frame; + pkt->size += sizeof(struct hfi_ltr_mark); + break; + } + case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32); + break; + } + case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VPE_COLOR_SPACE_CONVERSION: + { + struct hfi_vpe_color_space_conversion *hfi = NULL; + struct hal_vpe_color_space_conversion *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION; + hfi = (struct hfi_vpe_color_space_conversion *) + &pkt->rg_property_data[1]; + + hfi->input_color_primaries = hal->input_color_primaries; + if (hal->custom_matrix_enabled) + /* Bit Mask to enable all custom values */ + hfi->custom_matrix_enabled = 0x7; + else + hfi->custom_matrix_enabled = 0x0; + memcpy(hfi->csc_matrix, hal->csc_matrix, + sizeof(hfi->csc_matrix)); + memcpy(hfi->csc_bias, hal->csc_bias, sizeof(hfi->csc_bias)); + memcpy(hfi->csc_limit, hal->csc_limit, sizeof(hfi->csc_limit)); + pkt->size += sizeof(struct hfi_vpe_color_space_conversion); + break; + } + case HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_CONFIG_VENC_PERF_MODE: + { + u32 hfi_perf_mode = 0; + enum hal_perf_mode hal_perf_mode = *(enum hal_perf_mode *)pdata; + + switch (hal_perf_mode) { + case HAL_PERF_MODE_POWER_SAVE: + hfi_perf_mode = HFI_VENC_PERFMODE_POWER_SAVE; + break; + case HAL_PERF_MODE_POWER_MAX_QUALITY: + hfi_perf_mode = HFI_VENC_PERFMODE_MAX_QUALITY; + break; + default: + return -ENOTSUPP; + } + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_PERF_MODE; + pkt->rg_property_data[1] = hfi_perf_mode; + pkt->size += sizeof(u32); + break; + } + case HAL_PARAM_VENC_HIER_P_HYBRID_MODE: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE; + pkt->rg_property_data[1] = + ((struct hfi_hybrid_hierp *)pdata)->layers ?: 0xFF; + pkt->size += sizeof(u32) + + sizeof(struct hfi_hybrid_hierp); + break; + } + case HAL_PARAM_VENC_MBI_STATISTICS_MODE: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MBI_DUMPING; + pkt->rg_property_data[1] = hal_to_hfi_type( + HAL_PARAM_VENC_MBI_STATISTICS_MODE, + *(u32 *)pdata); + pkt->size += sizeof(u32); + break; + } + case HAL_CONFIG_VENC_BASELAYER_PRIORITYID: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32); + break; + } + case HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO: + { + struct hfi_aspect_ratio *hfi = NULL; + struct hal_aspect_ratio *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO; + hfi = (struct hfi_aspect_ratio *) + &pkt->rg_property_data[1]; + memcpy(hfi, hal, + sizeof(struct hfi_aspect_ratio)); + pkt->size += sizeof(struct hfi_aspect_ratio); + break; + } + case HAL_PARAM_VENC_BITRATE_TYPE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_TRANSFORM_8x8: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_VIDEO_SIGNAL_INFO: + { + struct hal_video_signal_info *hal = pdata; + struct hfi_video_signal_metadata *signal_info = + (struct hfi_video_signal_metadata *) + &pkt->rg_property_data[1]; + + signal_info->enable = true; + signal_info->video_format = MSM_VIDC_NTSC; + signal_info->video_full_range = hal->full_range; + signal_info->color_description = MSM_VIDC_COLOR_DESC_PRESENT; + signal_info->color_primaries = hal->color_space; + signal_info->transfer_characteristics = hal->transfer_chars; + signal_info->matrix_coeffs = hal->matrix_coeffs; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO; + pkt->size += sizeof(*signal_info); + break; + } + case HAL_PARAM_VENC_IFRAMESIZE_TYPE: + { + enum hal_iframesize_type hal = + *(enum hal_iframesize_type *)pdata; + struct hfi_iframe_size *hfi = (struct hfi_iframe_size *) + &pkt->rg_property_data[1]; + + switch (hal) { + case HAL_IFRAMESIZE_TYPE_DEFAULT: + hfi->type = HFI_IFRAME_SIZE_DEFAULT; + break; + case HAL_IFRAMESIZE_TYPE_MEDIUM: + hfi->type = HFI_IFRAME_SIZE_MEDIUM; + break; + case HAL_IFRAMESIZE_TYPE_HUGE: + hfi->type = HFI_IFRAME_SIZE_HIGH; + break; + case HAL_IFRAMESIZE_TYPE_UNLIMITED: + hfi->type = HFI_IFRAME_SIZE_UNLIMITED; + break; + default: + return -ENOTSUPP; + } + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_VENC_IFRAMESIZE; + pkt->size += sizeof(struct hfi_iframe_size); + break; + } + case HAL_PARAM_BUFFER_SIZE_MINIMUM: + { + struct hfi_buffer_size_minimum *hfi; + struct hal_buffer_size_minimum *prop = + (struct hal_buffer_size_minimum *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM; + + hfi = (struct hfi_buffer_size_minimum *) + &pkt->rg_property_data[1]; + hfi->buffer_size = prop->buffer_size; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + pkt->size += sizeof(struct hfi_buffer_size_minimum); + break; + } + case HAL_PARAM_SYNC_BASED_INTERRUPT: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_LOW_LATENCY: + { + struct hfi_enable *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hal_enable *) pdata)->enable; + pkt->size += sizeof(u32); + break; + } + case HAL_CONFIG_VENC_BLUR_RESOLUTION: + { + struct hfi_frame_size *hfi; + struct hal_frame_size *prop = (struct hal_frame_size *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE; + hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->height = prop->height; + hfi->width = prop->width; + pkt->size += sizeof(struct hfi_frame_size); + break; + } + case HAL_PARAM_VIDEO_CORES_USAGE: + { + struct hal_videocores_usage_info *hal = pdata; + struct hfi_videocores_usage_type *core_info = + (struct hfi_videocores_usage_type *) + &pkt->rg_property_data[1]; + + core_info->video_core_enable_mask = hal->video_core_enable_mask; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + pkt->size += sizeof(*core_info); + break; + } + case HAL_PARAM_VIDEO_WORK_MODE: + { + struct hal_video_work_mode *hal = pdata; + struct hfi_video_work_mode *work_mode = + (struct hfi_video_work_mode *) + &pkt->rg_property_data[1]; + + work_mode->video_work_mode = get_hfi_work_mode( + hal->video_work_mode); + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_WORK_MODE; + pkt->size += sizeof(*work_mode); + break; + } + case HAL_PARAM_VIDEO_WORK_ROUTE: + { + struct hal_video_work_route *hal = pdata; + struct hfi_video_work_route *prop = + (struct hfi_video_work_route *) + &pkt->rg_property_data[1]; + prop->video_work_route = + hal->video_work_route; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_WORK_ROUTE; + pkt->size += sizeof(*prop); + break; + } + case HAL_PARAM_VENC_HDR10_PQ_SEI: + { + struct hfi_hdr10_pq_sei *hfi; + struct hal_hdr10_pq_sei *prop = + (struct hal_hdr10_pq_sei *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI; + hfi = (struct hfi_hdr10_pq_sei *) + &pkt->rg_property_data[1]; + + memcpy(hfi, prop, sizeof(*hfi)); + pkt->size += sizeof(struct hfi_hdr10_pq_sei); + break; + } + case HAL_CONFIG_VENC_VBV_HRD_BUF_SIZE: + { + struct hfi_vbv_hdr_buf_size *hfi; + struct hal_vbv_hdr_buf_size *prop = + (struct hal_vbv_hdr_buf_size *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_VBV_HRD_BUF_SIZE; + hfi = (struct hfi_vbv_hdr_buf_size *) + &pkt->rg_property_data[1]; + + hfi->vbv_hdr_buf_size = prop->vbv_hdr_buf_size; + pkt->size += sizeof(struct hfi_vbv_hdr_buf_size); + break; + } + /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ + case HAL_CONFIG_BUFFER_REQUIREMENTS: + case HAL_CONFIG_PRIORITY: + case HAL_CONFIG_BATCH_INFO: + case HAL_PARAM_METADATA_PASS_THROUGH: + case HAL_SYS_IDLE_INDICATOR: + case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED: + case HAL_PARAM_CHROMA_SITE: + case HAL_PARAM_PROPERTIES_SUPPORTED: + case HAL_PARAM_PROFILE_LEVEL_SUPPORTED: + case HAL_PARAM_CAPABILITY_SUPPORTED: + case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + case HAL_PARAM_MULTI_VIEW_FORMAT: + case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE: + case HAL_PARAM_CODEC_SUPPORTED: + case HAL_PARAM_VDEC_MULTI_VIEW_SELECT: + case HAL_PARAM_VDEC_MB_QUANTIZATION: + case HAL_PARAM_VDEC_NUM_CONCEALED_MB: + case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING: + case HAL_CONFIG_BUFFER_COUNT_ACTUAL: + case HAL_CONFIG_VDEC_MULTI_STREAM: + case HAL_PARAM_VENC_MULTI_SLICE_INFO: + case HAL_CONFIG_VENC_TIMESTAMP_SCALE: + default: + dprintk(VIDC_ERR, "DEFAULT: Calling %#x\n", ptype); + rc = -ENOTSUPP; + break; + } + return rc; +} + +static int get_hfi_ssr_type(enum hal_ssr_trigger_type type) +{ + int rc = HFI_TEST_SSR_HW_WDOG_IRQ; + + switch (type) { + case SSR_ERR_FATAL: + rc = HFI_TEST_SSR_SW_ERR_FATAL; + break; + case SSR_SW_DIV_BY_ZERO: + rc = HFI_TEST_SSR_SW_DIV_BY_ZERO; + break; + case SSR_HW_WDOG_IRQ: + rc = HFI_TEST_SSR_HW_WDOG_IRQ; + break; + default: + dprintk(VIDC_WARN, + "SSR trigger type not recognized, using WDOG.\n"); + } + return rc; +} + +int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type, + struct hfi_cmd_sys_test_ssr_packet *pkt) +{ + if (!pkt) { + dprintk(VIDC_ERR, "Invalid params, device: %pK\n", pkt); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_sys_test_ssr_packet); + pkt->packet_type = HFI_CMD_SYS_TEST_SSR; + pkt->trigger_type = get_hfi_ssr_type(type); + return 0; +} + +int create_pkt_cmd_sys_image_version( + struct hfi_cmd_sys_get_property_packet *pkt) +{ + if (!pkt) { + dprintk(VIDC_ERR, "%s invalid param :%pK\n", __func__, pkt); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_sys_get_property_packet); + pkt->packet_type = HFI_CMD_SYS_GET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION; + return 0; +} + +int create_pkt_cmd_sys_ubwc_config(struct hfi_cmd_sys_set_property_packet *pkt, + struct msm_vidc_ubwc_config *config) +{ + if (!pkt) { + dprintk(VIDC_ERR, "%s invalid param :%pK\n", __func__, pkt); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + config->nSize + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_UBWC_CONFIG; + + if (config->nSize == sizeof(struct msm_vidc_ubwc_config)) + memcpy(&pkt->rg_property_data[1], config, config->nSize); + else + memcpy(&pkt->rg_property_data[1], &(config->v1), config->nSize); + + dprintk(VIDC_DBG, + "UBWC config nSize: %u, MaxChannels: %u, MalLength: %u, %u, HBB: %u\n", + config->nSize, + config->v1.nMaxChannels, + config->v1.nMalLength, + config->v1.nHighestBankBit); + dprintk(VIDC_DBG, + "MaxChannelsOverride: %u, MalLengthOverride: %u, HBBOverride: %u\n", + config->v1.sOverrideBitInfo.bMaxChannelsOverride, + config->v1.sOverrideBitInfo.bMalLengthOverride, + config->v1.sOverrideBitInfo.bHBBOverride); + + return 0; +} + +int create_pkt_cmd_session_sync_process( + struct hfi_cmd_session_sync_process_packet *pkt, + struct hal_session *session) +{ + if (!pkt || !session) + return -EINVAL; + + *pkt = (struct hfi_cmd_session_sync_process_packet) {0}; + pkt->size = sizeof(*pkt); + pkt->packet_type = HFI_CMD_SESSION_SYNC; + pkt->session_id = hash32_ptr(session); + pkt->sync_id = 0; + + return 0; +} + +static struct hfi_packetization_ops hfi_default = { + .sys_init = create_pkt_cmd_sys_init, + .sys_pc_prep = create_pkt_cmd_sys_pc_prep, + .sys_power_control = create_pkt_cmd_sys_power_control, + .sys_set_resource = create_pkt_cmd_sys_set_resource, + .sys_debug_config = create_pkt_cmd_sys_debug_config, + .sys_coverage_config = create_pkt_cmd_sys_coverage_config, + .sys_release_resource = create_pkt_cmd_sys_release_resource, + .sys_ping = create_pkt_cmd_sys_ping, + .sys_image_version = create_pkt_cmd_sys_image_version, + .ssr_cmd = create_pkt_ssr_cmd, + .sys_ubwc_config = create_pkt_cmd_sys_ubwc_config, + .session_init = create_pkt_cmd_sys_session_init, + .session_cmd = create_pkt_cmd_session_cmd, + .session_set_buffers = create_pkt_cmd_session_set_buffers, + .session_release_buffers = create_pkt_cmd_session_release_buffers, + .session_register_buffer = create_pkt_cmd_session_register_buffer, + .session_unregister_buffer = create_pkt_cmd_session_unregister_buffer, + .session_etb_decoder = create_pkt_cmd_session_etb_decoder, + .session_etb_encoder = create_pkt_cmd_session_etb_encoder, + .session_ftb = create_pkt_cmd_session_ftb, + .session_get_buf_req = create_pkt_cmd_session_get_buf_req, + .session_flush = create_pkt_cmd_session_flush, + .session_get_property = create_pkt_cmd_session_get_property, + .session_set_property = create_pkt_cmd_session_set_property, +}; + +struct hfi_packetization_ops *hfi_get_pkt_ops_handle( + enum hfi_packetization_type type) +{ + dprintk(VIDC_DBG, "%s selected\n", + type == HFI_PACKETIZATION_4XX ? + "4xx packetization" : "Unknown hfi"); + + switch (type) { + case HFI_PACKETIZATION_4XX: + return &hfi_default; + } + + return NULL; +} diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.h b/drivers/media/platform/msm/vidc/hfi_packetization.h new file mode 100644 index 000000000000..5479a7f1da43 --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_packetization.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2012-2019, 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 __HFI_PACKETIZATION__ +#define __HFI_PACKETIZATION__ + +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi.h" +#include "vidc_hfi_api.h" + +#define call_hfi_pkt_op(q, op, ...) \ + (((q) && (q)->pkt_ops && (q)->pkt_ops->op) ? \ + ((q)->pkt_ops->op(__VA_ARGS__)) : 0) + +enum hfi_packetization_type { + HFI_PACKETIZATION_4XX, +}; + +struct hfi_packetization_ops { + int (*sys_init)(struct hfi_cmd_sys_init_packet *pkt, u32 arch_type); + int (*sys_pc_prep)(struct hfi_cmd_sys_pc_prep_packet *pkt); + int (*sys_power_control)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 enable); + int (*sys_set_resource)( + struct hfi_cmd_sys_set_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr, + void *resource_value); + int (*sys_debug_config)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode); + int (*sys_coverage_config)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode); + int (*sys_release_resource)( + struct hfi_cmd_sys_release_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr); + int (*sys_ping)(struct hfi_cmd_sys_ping_packet *pkt); + int (*sys_image_version)(struct hfi_cmd_sys_get_property_packet *pkt); + int (*ssr_cmd)(enum hal_ssr_trigger_type type, + struct hfi_cmd_sys_test_ssr_packet *pkt); + int (*sys_ubwc_config)(struct hfi_cmd_sys_set_property_packet *pkt, + struct msm_vidc_ubwc_config *config); + int (*session_init)( + struct hfi_cmd_sys_session_init_packet *pkt, + struct hal_session *session, + u32 session_domain, u32 session_codec); + int (*session_cmd)(struct vidc_hal_session_cmd_pkt *pkt, + int pkt_type, struct hal_session *session); + int (*session_set_buffers)( + struct hfi_cmd_session_set_buffers_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info); + int (*session_release_buffers)( + struct hfi_cmd_session_release_buffer_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info); + int (*session_register_buffer)( + struct hfi_cmd_session_register_buffers_packet *pkt, + struct hal_session *session, + struct vidc_register_buffer *buffer); + int (*session_unregister_buffer)( + struct hfi_cmd_session_unregister_buffers_packet *pkt, + struct hal_session *session, + struct vidc_unregister_buffer *buffer); + int (*session_etb_decoder)( + struct hfi_cmd_session_empty_buffer_compressed_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *input_frame); + int (*session_etb_encoder)( + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *pkt, struct hal_session *session, + struct vidc_frame_data *input_frame); + int (*session_ftb)(struct hfi_cmd_session_fill_buffer_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *output_frame); + int (*session_get_buf_req)( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session); + int (*session_flush)(struct hfi_cmd_session_flush_packet *pkt, + struct hal_session *session, enum hal_flush flush_mode); + int (*session_get_property)( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session, enum hal_property ptype); + int (*session_set_property)( + struct hfi_cmd_session_set_property_packet *pkt, + struct hal_session *session, + enum hal_property ptype, void *pdata); + int (*session_sync_process)( + struct hfi_cmd_session_sync_process_packet *pkt, + struct hal_session *session); +}; + +struct hfi_packetization_ops *hfi_get_pkt_ops_handle( + enum hfi_packetization_type); +#endif diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c new file mode 100644 index 000000000000..a35945e0c500 --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -0,0 +1,2135 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi_io.h" +#include "msm_vidc_debug.h" +#include "vidc_hfi.h" + +static enum vidc_status hfi_parse_init_done_properties( + struct msm_vidc_capability *capability, + u32 num_sessions, u8 *data_ptr, u32 num_properties, + u32 rem_bytes); + +static enum vidc_status hfi_map_err_status(u32 hfi_err) +{ + enum vidc_status vidc_err; + + switch (hfi_err) { + case HFI_ERR_NONE: + case HFI_ERR_SESSION_SAME_STATE_OPERATION: + vidc_err = VIDC_ERR_NONE; + break; + case HFI_ERR_SYS_FATAL: + vidc_err = VIDC_ERR_HW_FATAL; + break; + case HFI_ERR_SYS_NOC_ERROR: + vidc_err = VIDC_ERR_NOC_ERROR; + break; + case HFI_ERR_SYS_VERSION_MISMATCH: + case HFI_ERR_SYS_INVALID_PARAMETER: + case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE: + case HFI_ERR_SESSION_INVALID_PARAMETER: + case HFI_ERR_SESSION_INVALID_SESSION_ID: + case HFI_ERR_SESSION_INVALID_STREAM_ID: + vidc_err = VIDC_ERR_BAD_PARAM; + break; + case HFI_ERR_SYS_INSUFFICIENT_RESOURCES: + case HFI_ERR_SYS_UNSUPPORTED_DOMAIN: + case HFI_ERR_SYS_UNSUPPORTED_CODEC: + case HFI_ERR_SESSION_UNSUPPORTED_PROPERTY: + case HFI_ERR_SESSION_UNSUPPORTED_SETTING: + case HFI_ERR_SESSION_INSUFFICIENT_RESOURCES: + case HFI_ERR_SESSION_UNSUPPORTED_STREAM: + vidc_err = VIDC_ERR_NOT_SUPPORTED; + break; + case HFI_ERR_SYS_MAX_SESSIONS_REACHED: + vidc_err = VIDC_ERR_MAX_CLIENTS; + break; + case HFI_ERR_SYS_SESSION_IN_USE: + vidc_err = VIDC_ERR_CLIENT_PRESENT; + break; + case HFI_ERR_SESSION_FATAL: + vidc_err = VIDC_ERR_CLIENT_FATAL; + break; + case HFI_ERR_SESSION_BAD_POINTER: + vidc_err = VIDC_ERR_BAD_PARAM; + break; + case HFI_ERR_SESSION_INCORRECT_STATE_OPERATION: + vidc_err = VIDC_ERR_BAD_STATE; + break; + case HFI_ERR_SESSION_STREAM_CORRUPT: + case HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED: + vidc_err = VIDC_ERR_BITSTREAM_ERR; + break; + case HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED: + vidc_err = VIDC_ERR_IFRAME_EXPECTED; + break; + case HFI_ERR_SESSION_START_CODE_NOT_FOUND: + vidc_err = VIDC_ERR_START_CODE_NOT_FOUND; + break; + case HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING: + default: + vidc_err = VIDC_ERR_FAIL; + break; + } + return vidc_err; +} + +static int get_hal_pixel_depth(u32 hfi_bit_depth) +{ + switch (hfi_bit_depth) { + case HFI_BITDEPTH_8: return MSM_VIDC_BIT_DEPTH_8; + case HFI_BITDEPTH_9: + case HFI_BITDEPTH_10: return MSM_VIDC_BIT_DEPTH_10; + } + dprintk(VIDC_ERR, "Unsupported bit depth: %d\n", hfi_bit_depth); + return MSM_VIDC_BIT_DEPTH_UNSUPPORTED; +} + +static inline int validate_pkt_size(u32 rem_size, u32 msg_size) +{ + if (rem_size < msg_size) { + dprintk(VIDC_ERR, "%s: bad_packet_size: %d\n", + __func__, rem_size); + return false; + } + return true; +} + +static int hfi_process_sess_evt_seq_changed(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_event event_notify = {0}; + u32 num_properties_changed; + struct hfi_frame_size *frame_sz; + struct hfi_profile_level *profile_level; + struct hfi_bit_depth *pixel_depth; + struct hfi_pic_struct *pic_struct; + struct hfi_buffer_requirements *buf_req; + struct hfi_index_extradata_input_crop_payload *crop_info; + struct hfi_dpb_counts *dpb_counts; + u32 rem_size, entropy_mode = 0; + u8 *data_ptr; + int prop_id; + int luma_bit_depth, chroma_bit_depth; + struct hfi_colour_space *colour_info; + + if (!validate_pkt_size(pkt->size, + sizeof(struct hfi_msg_event_notify_packet))) + return -E2BIG; + + event_notify.device_id = device_id; + event_notify.session_id = (void *)(uintptr_t)pkt->session_id; + event_notify.status = VIDC_ERR_NONE; + num_properties_changed = pkt->event_data2; + switch (pkt->event_data1) { + case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES: + event_notify.hal_event_type = + HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES; + break; + case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES: + event_notify.hal_event_type = + HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES; + break; + default: + break; + } + + if (num_properties_changed) { + data_ptr = (u8 *) &pkt->rg_ext_event_data[0]; + rem_size = pkt->size - sizeof(struct + hfi_msg_event_notify_packet) + sizeof(u32); + do { + if (!validate_pkt_size(rem_size, sizeof(u32))) + return -E2BIG; + prop_id = (int) *((u32 *)data_ptr); + rem_size -= sizeof(u32); + switch (prop_id) { + case HFI_PROPERTY_PARAM_FRAME_SIZE: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_frame_size))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + frame_sz = + (struct hfi_frame_size *) data_ptr; + event_notify.width = frame_sz->width; + event_notify.height = frame_sz->height; + dprintk(VIDC_DBG, "height: %d width: %d\n", + frame_sz->height, frame_sz->width); + data_ptr += + sizeof(struct hfi_frame_size); + rem_size -= sizeof(struct hfi_frame_size); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_profile_level))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + profile_level = + (struct hfi_profile_level *) data_ptr; + event_notify.profile = profile_level->profile; + event_notify.level = profile_level->level; + dprintk(VIDC_DBG, "profile: %d level: %d\n", + profile_level->profile, + profile_level->level); + data_ptr += + sizeof(struct hfi_profile_level); + rem_size -= sizeof(struct hfi_profile_level); + break; + case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_bit_depth))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + pixel_depth = (struct hfi_bit_depth *) data_ptr; + /* + * Luma and chroma can have different bitdepths. + * Driver should rely on luma and chroma + * bitdepth for determining output bitdepth + * type. + * + * pixel_depth->bitdepth will include luma + * bitdepth info in bits 0..15 and chroma + * bitdept in bits 16..31. + */ + luma_bit_depth = get_hal_pixel_depth( + pixel_depth->bit_depth & + GENMASK(15, 0)); + chroma_bit_depth = get_hal_pixel_depth( + (pixel_depth->bit_depth & + GENMASK(31, 16)) >> 16); + if (luma_bit_depth == MSM_VIDC_BIT_DEPTH_10 || + chroma_bit_depth == + MSM_VIDC_BIT_DEPTH_10) + event_notify.bit_depth = + MSM_VIDC_BIT_DEPTH_10; + else + event_notify.bit_depth = luma_bit_depth; + dprintk(VIDC_DBG, + "bitdepth(%d), luma_bit_depth(%d), chroma_bit_depth(%d)\n", + event_notify.bit_depth, luma_bit_depth, + chroma_bit_depth); + data_ptr += sizeof(struct hfi_bit_depth); + rem_size -= sizeof(struct hfi_bit_depth); + break; + case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_pic_struct))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + pic_struct = (struct hfi_pic_struct *) data_ptr; + event_notify.pic_struct = + pic_struct->progressive_only; + dprintk(VIDC_DBG, + "Progressive only flag: %d\n", + pic_struct->progressive_only); + data_ptr += + sizeof(struct hfi_pic_struct); + rem_size -= sizeof(struct hfi_pic_struct); + break; + case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_dpb_counts))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + dpb_counts = (struct hfi_dpb_counts *) data_ptr; + event_notify.max_dpb_count = + dpb_counts->max_dpb_count; + event_notify.max_ref_count = + dpb_counts->max_ref_count; + event_notify.max_dec_buffering = + dpb_counts->max_dec_buffering; + dprintk(VIDC_DBG, + "DPB Counts: dpb %d ref %d buff %d\n", + dpb_counts->max_dpb_count, + dpb_counts->max_ref_count, + dpb_counts->max_dec_buffering); + data_ptr += + sizeof(struct hfi_dpb_counts); + rem_size -= sizeof(struct hfi_dpb_counts); + break; + case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_colour_space))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + colour_info = + (struct hfi_colour_space *) data_ptr; + event_notify.colour_space = + colour_info->colour_space; + dprintk(VIDC_DBG, + "Colour space value is: %d\n", + colour_info->colour_space); + data_ptr += + sizeof(struct hfi_colour_space); + rem_size -= sizeof(struct hfi_colour_space); + break; + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + if (!validate_pkt_size(rem_size, sizeof(u32))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + entropy_mode = *(u32 *)data_ptr; + event_notify.entropy_mode = entropy_mode; + dprintk(VIDC_DBG, + "Entropy Mode: 0x%x\n", entropy_mode); + data_ptr += + sizeof(u32); + rem_size -= sizeof(u32); + break; + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_buffer_requirements))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + buf_req = + (struct hfi_buffer_requirements *) + data_ptr; + event_notify.capture_buf_count = + buf_req->buffer_count_min; + dprintk(VIDC_DBG, + "Capture Count : 0x%x\n", + event_notify.capture_buf_count); + data_ptr += + sizeof(struct hfi_buffer_requirements); + rem_size -= + sizeof(struct hfi_buffer_requirements); + break; + case HFI_INDEX_EXTRADATA_INPUT_CROP: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_index_extradata_input_crop_payload))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + crop_info = (struct + hfi_index_extradata_input_crop_payload *) + data_ptr; + event_notify.crop_data.left = crop_info->left; + event_notify.crop_data.top = crop_info->top; + event_notify.crop_data.width = crop_info->width; + event_notify.crop_data.height = + crop_info->height; + dprintk(VIDC_DBG, + "CROP info : Left = %d Top = %d\n", + crop_info->left, + crop_info->top); + dprintk(VIDC_DBG, + "CROP info : Width = %d Height = %d\n", + crop_info->width, + crop_info->height); + data_ptr += + sizeof(struct + hfi_index_extradata_input_crop_payload); + rem_size -= sizeof(struct + hfi_index_extradata_input_crop_payload); + break; + default: + dprintk(VIDC_ERR, + "%s cmd: %#x not supported\n", + __func__, prop_id); + break; + } + num_properties_changed--; + } while (num_properties_changed > 0); + } + + info->response_type = HAL_SESSION_EVENT_CHANGE; + info->response.event = event_notify; + + return 0; +} + +static int hfi_process_evt_release_buffer_ref(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_event event_notify = {0}; + struct hfi_msg_release_buffer_ref_event_packet *data; + + dprintk(VIDC_DBG, + "RECEIVED: EVENT_NOTIFY - release_buffer_reference\n"); + if (sizeof(struct hfi_msg_event_notify_packet) + > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); + return -E2BIG; + } + if (pkt->size < sizeof(struct hfi_msg_event_notify_packet) - sizeof(u32) + + sizeof(struct hfi_msg_release_buffer_ref_event_packet)) { + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", + __func__, pkt->size); + return -E2BIG; + } + + data = (struct hfi_msg_release_buffer_ref_event_packet *) + pkt->rg_ext_event_data; + + event_notify.device_id = device_id; + event_notify.session_id = (void *)(uintptr_t)pkt->session_id; + event_notify.status = VIDC_ERR_NONE; + event_notify.hal_event_type = HAL_EVENT_RELEASE_BUFFER_REFERENCE; + event_notify.packet_buffer = data->packet_buffer; + event_notify.extra_data_buffer = data->extra_data_buffer; + event_notify.output_tag = data->output_tag; + + info->response_type = HAL_SESSION_EVENT_CHANGE; + info->response.event = event_notify; + + return 0; +} + +static int hfi_process_sys_error(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device_id; + cmd_done.status = hfi_map_err_status(pkt->event_data1); + + info->response_type = HAL_SYS_ERROR; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_error(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->event_data1); + info->response.cmd = cmd_done; + dprintk(VIDC_INFO, "Received: SESSION_ERROR with event id : %#x %#x\n", + pkt->event_data1, pkt->event_data2); + switch (pkt->event_data1) { + /* Ignore below errors */ + case HFI_ERR_SESSION_INVALID_SCALE_FACTOR: + case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED: + dprintk(VIDC_INFO, "Non Fatal: HFI_EVENT_SESSION_ERROR\n"); + info->response_type = HAL_RESPONSE_UNUSED; + break; + default: + dprintk(VIDC_ERR, + "%s: session %x data1 %#x, data2 %#x\n", __func__, + pkt->session_id, pkt->event_data1, pkt->event_data2); + info->response_type = HAL_SESSION_ERROR; + break; + } + + return 0; +} + +static int hfi_process_event_notify(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_event_notify_packet *pkt = _pkt; + dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n"); + + if (pkt->size < sizeof(struct hfi_msg_event_notify_packet)) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -E2BIG; + } + + switch (pkt->event_id) { + case HFI_EVENT_SYS_ERROR: + dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, %#x\n", + pkt->event_data1, pkt->event_data2); + return hfi_process_sys_error(device_id, pkt, info); + case HFI_EVENT_SESSION_ERROR: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%#x]\n", + pkt->session_id); + return hfi_process_session_error(device_id, pkt, info); + + case HFI_EVENT_SESSION_SEQUENCE_CHANGED: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%#x]\n", + pkt->session_id); + return hfi_process_sess_evt_seq_changed(device_id, pkt, info); + + case HFI_EVENT_RELEASE_BUFFER_REFERENCE: + dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%#x]\n", + pkt->session_id); + return hfi_process_evt_release_buffer_ref(device_id, pkt, info); + + case HFI_EVENT_SESSION_PROPERTY_CHANGED: + default: + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + + return 0; + } +} + +static int hfi_process_sys_init_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_init_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + enum vidc_status status = VIDC_ERR_NONE; + + dprintk(VIDC_DBG, "RECEIVED: SYS_INIT_DONE\n"); + if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) { + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", __func__, + pkt->size); + return -E2BIG; + } + if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_sys_init_done: no_properties\n"); + status = VIDC_ERR_FAIL; + goto err_no_prop; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) { + dprintk(VIDC_ERR, "%s: status %#x\n", + __func__, status); + goto err_no_prop; + } + +err_no_prop: + cmd_done.device_id = device_id; + cmd_done.session_id = NULL; + cmd_done.status = (u32)status; + cmd_done.size = sizeof(struct vidc_hal_sys_init_done); + + info->response_type = HAL_SYS_INIT_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_sys_rel_resource_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_release_resource_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + enum vidc_status status = VIDC_ERR_NONE; + u32 pkt_size; + + dprintk(VIDC_DBG, "RECEIVED: SYS_RELEASE_RESOURCE_DONE\n"); + pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet); + if (pkt_size > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_sys_rel_resource_done: bad size: %d\n", + pkt->size); + return -E2BIG; + } + + status = hfi_map_err_status(pkt->error_type); + cmd_done.device_id = device_id; + cmd_done.session_id = NULL; + cmd_done.status = (u32) status; + cmd_done.size = 0; + + info->response_type = HAL_SYS_RELEASE_RESOURCE_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +enum hal_capability get_hal_cap_type(u32 capability_type) +{ + enum hal_capability hal_cap = 0; + + switch (capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + hal_cap = HAL_CAPABILITY_FRAME_WIDTH; + break; + case HFI_CAPABILITY_FRAME_HEIGHT: + hal_cap = HAL_CAPABILITY_FRAME_HEIGHT; + break; + case HFI_CAPABILITY_MBS_PER_FRAME: + hal_cap = HAL_CAPABILITY_MBS_PER_FRAME; + break; + case HFI_CAPABILITY_MBS_PER_SECOND: + hal_cap = HAL_CAPABILITY_MBS_PER_SECOND; + break; + case HFI_CAPABILITY_FRAMERATE: + hal_cap = HAL_CAPABILITY_FRAMERATE; + break; + case HFI_CAPABILITY_SCALE_X: + hal_cap = HAL_CAPABILITY_SCALE_X; + break; + case HFI_CAPABILITY_SCALE_Y: + hal_cap = HAL_CAPABILITY_SCALE_Y; + break; + case HFI_CAPABILITY_BITRATE: + hal_cap = HAL_CAPABILITY_BITRATE; + break; + case HFI_CAPABILITY_BFRAME: + hal_cap = HAL_CAPABILITY_BFRAME; + break; + case HFI_CAPABILITY_PEAKBITRATE: + hal_cap = HAL_CAPABILITY_PEAKBITRATE; + break; + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_ENC_LTR_COUNT: + hal_cap = HAL_CAPABILITY_ENC_LTR_COUNT; + break; + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + hal_cap = HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD; + break; + case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_LCU_SIZE: + hal_cap = HAL_CAPABILITY_LCU_SIZE; + break; + case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE: + hal_cap = HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE; + break; + case HFI_CAPABILITY_EXTRADATA: + hal_cap = HAL_CAPABILITY_EXTRADATA; + break; + case HFI_CAPABILITY_PROFILE: + hal_cap = HAL_CAPABILITY_PROFILE; + break; + case HFI_CAPABILITY_LEVEL: + hal_cap = HAL_CAPABILITY_LEVEL; + break; + case HFI_CAPABILITY_I_FRAME_QP: + hal_cap = HAL_CAPABILITY_I_FRAME_QP; + break; + case HFI_CAPABILITY_P_FRAME_QP: + hal_cap = HAL_CAPABILITY_P_FRAME_QP; + break; + case HFI_CAPABILITY_B_FRAME_QP: + hal_cap = HAL_CAPABILITY_B_FRAME_QP; + break; + case HFI_CAPABILITY_RATE_CONTROL_MODES: + hal_cap = HAL_CAPABILITY_RATE_CONTROL_MODES; + break; + case HFI_CAPABILITY_BLUR_WIDTH: + hal_cap = HAL_CAPABILITY_BLUR_WIDTH; + break; + case HFI_CAPABILITY_BLUR_HEIGHT: + hal_cap = HAL_CAPABILITY_BLUR_HEIGHT; + break; + case HFI_CAPABILITY_ROTATION: + hal_cap = HAL_CAPABILITY_ROTATION; + break; + case HFI_CAPABILITY_COLOR_SPACE_CONVERSION: + hal_cap = HAL_CAPABILITY_COLOR_SPACE_CONVERSION; + break; + case HFI_CAPABILITY_SLICE_DELIVERY_MODES: + hal_cap = HAL_CAPABILITY_SLICE_DELIVERY_MODES; + break; + case HFI_CAPABILITY_SLICE_BYTE: + hal_cap = HAL_CAPABILITY_SLICE_BYTE; + break; + case HFI_CAPABILITY_SLICE_MB: + hal_cap = HAL_CAPABILITY_SLICE_MB; + break; + case HFI_CAPABILITY_SECURE: + hal_cap = HAL_CAPABILITY_SECURE; + break; + case HFI_CAPABILITY_MAX_NUM_B_FRAMES: + hal_cap = HAL_CAPABILITY_MAX_NUM_B_FRAMES; + break; + case HFI_CAPABILITY_MAX_VIDEOCORES: + hal_cap = HAL_CAPABILITY_MAX_VIDEOCORES; + break; + case HFI_CAPABILITY_MAX_WORKMODES: + hal_cap = HAL_CAPABILITY_MAX_WORKMODES; + break; + case HFI_CAPABILITY_UBWC_CR_STATS: + hal_cap = HAL_CAPABILITY_UBWC_CR_STATS; + break; + default: + dprintk(VIDC_DBG, "%s: unknown capablity %#x\n", + __func__, capability_type); + break; + } + + return hal_cap; +} + +static inline void copy_cap_prop( + struct hfi_capability_supported *in, + struct msm_vidc_capability *capability) +{ + struct hal_capability_supported *out = NULL; + + if (!in || !capability) { + dprintk(VIDC_ERR, "%s Invalid input parameters\n", + __func__); + return; + } + + switch (in->capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + out = &capability->width; + break; + case HFI_CAPABILITY_FRAME_HEIGHT: + out = &capability->height; + break; + case HFI_CAPABILITY_MBS_PER_FRAME: + out = &capability->mbs_per_frame; + break; + case HFI_CAPABILITY_MBS_PER_SECOND: + out = &capability->mbs_per_sec; + break; + case HFI_CAPABILITY_FRAMERATE: + out = &capability->frame_rate; + break; + case HFI_CAPABILITY_SCALE_X: + out = &capability->scale_x; + break; + case HFI_CAPABILITY_SCALE_Y: + out = &capability->scale_y; + break; + case HFI_CAPABILITY_BITRATE: + out = &capability->bitrate; + break; + case HFI_CAPABILITY_BFRAME: + out = &capability->bframe; + break; + case HFI_CAPABILITY_PEAKBITRATE: + out = &capability->peakbitrate; + break; + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + out = &capability->hier_p; + break; + case HFI_CAPABILITY_ENC_LTR_COUNT: + out = &capability->ltr_count; + break; + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + out = &capability->secure_output2_threshold; + break; + case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS: + out = &capability->hier_b; + break; + case HFI_CAPABILITY_LCU_SIZE: + out = &capability->lcu_size; + break; + case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS: + out = &capability->hier_p_hybrid; + break; + case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE: + out = &capability->mbs_per_sec_power_save; + break; + case HFI_CAPABILITY_EXTRADATA: + out = &capability->extradata; + break; + case HFI_CAPABILITY_PROFILE: + out = &capability->profile; + break; + case HFI_CAPABILITY_LEVEL: + out = &capability->level; + break; + case HFI_CAPABILITY_I_FRAME_QP: + out = &capability->i_qp; + break; + case HFI_CAPABILITY_P_FRAME_QP: + out = &capability->p_qp; + break; + case HFI_CAPABILITY_B_FRAME_QP: + out = &capability->b_qp; + break; + case HFI_CAPABILITY_RATE_CONTROL_MODES: + out = &capability->rc_modes; + break; + case HFI_CAPABILITY_BLUR_WIDTH: + out = &capability->blur_width; + break; + case HFI_CAPABILITY_BLUR_HEIGHT: + out = &capability->blur_height; + break; + case HFI_CAPABILITY_ROTATION: + out = &capability->rotation; + break; + case HFI_CAPABILITY_COLOR_SPACE_CONVERSION: + out = &capability->color_space_caps; + break; + case HFI_CAPABILITY_SLICE_DELIVERY_MODES: + out = &capability->slice_delivery_mode; + break; + case HFI_CAPABILITY_SLICE_BYTE: + out = &capability->slice_bytes; + break; + case HFI_CAPABILITY_SLICE_MB: + out = &capability->slice_mbs; + break; + case HFI_CAPABILITY_SECURE: + out = &capability->secure; + break; + case HFI_CAPABILITY_MAX_NUM_B_FRAMES: + out = &capability->max_num_b_frames; + break; + case HFI_CAPABILITY_MAX_VIDEOCORES: + out = &capability->max_video_cores; + break; + case HFI_CAPABILITY_MAX_WORKMODES: + out = &capability->max_work_modes; + break; + case HFI_CAPABILITY_UBWC_CR_STATS: + out = &capability->ubwc_cr_stats; + break; + default: + dprintk(VIDC_DBG, "%s: unknown capablity %#x\n", + __func__, in->capability_type); + break; + } + + if (out) { + out->capability_type = get_hal_cap_type(in->capability_type); + out->min = in->min; + out->max = in->max; + out->step_size = in->step_size; + } +} + +static int hfi_fill_codec_info(u8 *data_ptr, + struct vidc_hal_sys_init_done *sys_init_done, u32 rem_size) +{ + u32 i; + u32 codecs = 0, codec_count = 0, size = 0; + struct msm_vidc_capability *capability; + u32 prop_id = *((u32 *)data_ptr); + u8 *orig_data_ptr = data_ptr; + + if (prop_id == HFI_PROPERTY_PARAM_CODEC_SUPPORTED) { + struct hfi_codec_supported *prop; + + if (!validate_pkt_size(rem_size - sizeof(u32), + sizeof(struct hfi_codec_supported))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + prop = (struct hfi_codec_supported *) data_ptr; + sys_init_done->dec_codec_supported = + prop->decoder_codec_supported; + sys_init_done->enc_codec_supported = + prop->encoder_codec_supported; + size = sizeof(struct hfi_codec_supported) + sizeof(u32); + rem_size -= + sizeof(struct hfi_codec_supported) + sizeof(u32); + } else { + dprintk(VIDC_WARN, + "%s: prop_id %#x, expected codec_supported property\n", + __func__, prop_id); + } + + codecs = sys_init_done->dec_codec_supported; + for (i = 0; i < 8 * sizeof(codecs); i++) { + if ((1 << i) & codecs) { + capability = + &sys_init_done->capabilities[codec_count++]; + capability->codec = + vidc_get_hal_codec((1 << i) & codecs); + capability->domain = + vidc_get_hal_domain(HFI_VIDEO_DOMAIN_DECODER); + if (codec_count == VIDC_MAX_DECODE_SESSIONS) { + dprintk(VIDC_ERR, + "Max supported decoder sessions reached"); + break; + } + } + } + codecs = sys_init_done->enc_codec_supported; + for (i = 0; i < 8 * sizeof(codecs); i++) { + if ((1 << i) & codecs) { + capability = + &sys_init_done->capabilities[codec_count++]; + capability->codec = + vidc_get_hal_codec((1 << i) & codecs); + capability->domain = + vidc_get_hal_domain(HFI_VIDEO_DOMAIN_ENCODER); + if (codec_count == VIDC_MAX_SESSIONS) { + dprintk(VIDC_ERR, + "Max supported sessions reached"); + break; + } + } + } + sys_init_done->codec_count = codec_count; + + if (!validate_pkt_size(rem_size, sizeof(u32))) + return -E2BIG; + prop_id = *((u32 *)(orig_data_ptr + size)); + if (prop_id == HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED) { + struct hfi_max_sessions_supported *prop; + + if (!validate_pkt_size(rem_size - sizeof(u32), sizeof(struct + hfi_max_sessions_supported))) + return -E2BIG; + prop = (struct hfi_max_sessions_supported *) + (orig_data_ptr + size + sizeof(u32)); + + sys_init_done->max_sessions_supported = prop->max_sessions; + size += sizeof(struct hfi_max_sessions_supported) + sizeof(u32); + rem_size -= + sizeof(struct hfi_max_sessions_supported) + sizeof(u32); + dprintk(VIDC_DBG, "max_sessions_supported %d\n", + prop->max_sessions); + } + return size; +} + +static int copy_profile_caps_to_sessions(struct hfi_profile_level *prof, + u32 profile_count, struct msm_vidc_capability *capabilities, + u32 num_sessions, u32 codecs, u32 domain) +{ + u32 i = 0, j = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + /* + * iterate over num_sessions and copy all the profile capabilities + * to matching sessions. + */ + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + capability->profile_level.profile_count = profile_count; + for (j = 0; j < profile_count; j++) { + /* HFI and HAL follow same enums, hence no conversion */ + capability->profile_level.profile_level[j].profile = + prof[j].profile; + capability->profile_level.profile_level[j].level = + prof[j].level; + } + } + + return 0; +} + +static int copy_caps_to_sessions(struct hfi_capability_supported *cap, + u32 num_caps, struct msm_vidc_capability *capabilities, + u32 num_sessions, u32 codecs, u32 domain) +{ + u32 i = 0, j = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + /* + * iterate over num_sessions and copy all the capabilities + * to matching sessions. + */ + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + for (j = 0; j < num_caps; j++) + copy_cap_prop(&cap[j], capability); + } + + return 0; +} + +static int copy_nal_stream_format_caps_to_sessions(u32 nal_stream_format_value, + struct msm_vidc_capability *capabilities, u32 num_sessions, + u32 codecs, u32 domain) +{ + u32 i = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + capability->nal_stream_format.nal_stream_format_supported = + nal_stream_format_value; + } + + return 0; +} + +static enum vidc_status hfi_parse_init_done_properties( + struct msm_vidc_capability *capabilities, + u32 num_sessions, u8 *data_ptr, u32 num_properties, + u32 rem_bytes) +{ + enum vidc_status status = VIDC_ERR_NONE; + u32 prop_id, next_offset; + u32 codecs = 0, domain = 0; + +#define VALIDATE_PROPERTY_STRUCTURE_SIZE(pkt_size, property_size) ({\ + if (pkt_size < property_size) { \ + status = VIDC_ERR_BAD_PARAM; \ + break; \ + } \ +}) + +#define VALIDATE_PROPERTY_PAYLOAD_SIZE(pkt_size, payload_size, \ + property_count) ({\ + if (pkt_size/payload_size < property_count) { \ + status = VIDC_ERR_BAD_PARAM; \ + break; \ + } \ +}) + + while (status == VIDC_ERR_NONE && num_properties && + rem_bytes >= sizeof(u32)) { + + prop_id = *((u32 *)data_ptr); + next_offset = sizeof(u32); + + switch (prop_id) { + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: + { + struct hfi_codec_mask_supported *prop = + (struct hfi_codec_mask_supported *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + + codecs = prop->codecs; + domain = prop->video_domains; + next_offset += sizeof(struct hfi_codec_mask_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + { + struct hfi_capability_supported_info *prop = + (struct hfi_capability_supported_info *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - sizeof(u32), + sizeof(struct hfi_capability_supported), + prop->num_capabilities); + + next_offset += sizeof(u32) + + prop->num_capabilities * + sizeof(struct hfi_capability_supported); + + copy_caps_to_sessions(&prop->rg_data[0], + prop->num_capabilities, + capabilities, num_sessions, + codecs, domain); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + { + struct hfi_uncompressed_format_supported *prop = + (struct hfi_uncompressed_format_supported *) + (data_ptr + next_offset); + u32 num_format_entries; + char *fmt_ptr; + struct hfi_uncompressed_plane_info *plane_info; + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + + num_format_entries = prop->format_entries; + next_offset = sizeof(*prop); + fmt_ptr = (char *)&prop->rg_format_info[0]; + + while (num_format_entries) { + u32 bytes_to_skip; + + plane_info = + (struct hfi_uncompressed_plane_info *) fmt_ptr; + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*plane_info)); + + bytes_to_skip = sizeof(*plane_info) - + sizeof(struct + hfi_uncompressed_plane_constraints) + + plane_info->num_planes * + sizeof(struct + hfi_uncompressed_plane_constraints); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + bytes_to_skip); + + fmt_ptr += bytes_to_skip; + next_offset += bytes_to_skip; + num_format_entries--; + } + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: + { + struct hfi_properties_supported *prop = + (struct hfi_properties_supported *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - sizeof(*prop) + + sizeof(u32), sizeof(u32), + prop->num_properties); + + next_offset += sizeof(*prop) - sizeof(u32) + + prop->num_properties * sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + { + struct hfi_profile_level_supported *prop = + (struct hfi_profile_level_supported *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - + sizeof(u32), + sizeof(struct hfi_profile_level), + prop->profile_count); + + next_offset += sizeof(u32) + + prop->profile_count * + sizeof(struct hfi_profile_level); + + if (prop->profile_count > MAX_PROFILE_COUNT) { + prop->profile_count = MAX_PROFILE_COUNT; + dprintk(VIDC_WARN, + "prop count exceeds max profile count\n"); + break; + } + + copy_profile_caps_to_sessions( + &prop->rg_profile_level[0], + prop->profile_count, capabilities, + num_sessions, codecs, domain); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + { + struct hfi_nal_stream_format_supported *prop = + (struct hfi_nal_stream_format_supported *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + + copy_nal_stream_format_caps_to_sessions( + prop->nal_stream_format_supported, + capabilities, num_sessions, + codecs, domain); + + next_offset += + sizeof(struct hfi_nal_stream_format_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(u32)); + next_offset += sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(struct hfi_intra_refresh)); + next_offset += + sizeof(struct hfi_intra_refresh); + num_properties--; + break; + } + case HFI_PROPERTY_TME_VERSION_SUPPORTED: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(u32)); + capabilities->tme_version = + *((u32 *)(data_ptr + next_offset)); + next_offset += + sizeof(u32); + num_properties--; + break; + } + default: + dprintk(VIDC_DBG, + "%s: default case - data_ptr %pK, prop_id 0x%x\n", + __func__, data_ptr, prop_id); + break; + } + + if (rem_bytes > next_offset) { + rem_bytes -= next_offset; + data_ptr += next_offset; + } else { + rem_bytes = 0; + } + } + + return status; +} + +enum vidc_status hfi_process_sys_init_done_prop_read( + struct hfi_msg_sys_init_done_packet *pkt, + struct vidc_hal_sys_init_done *sys_init_done) +{ + enum vidc_status status = VIDC_ERR_NONE; + int bytes_read; + u32 rem_bytes, num_properties; + u8 *data_ptr; + + if (!pkt || !sys_init_done) { + dprintk(VIDC_ERR, + "hfi_msg_sys_init_done: Invalid input\n"); + return VIDC_ERR_FAIL; + } + if (pkt->size < sizeof(struct hfi_msg_sys_init_done_packet)) { + dprintk(VIDC_ERR, "%s: bad_packet_size: %d\n", + __func__, pkt->size); + return VIDC_ERR_FAIL; + } + + rem_bytes = pkt->size - sizeof(struct + hfi_msg_sys_init_done_packet) + sizeof(u32); + + if (!rem_bytes) { + dprintk(VIDC_ERR, + "hfi_msg_sys_init_done: missing_prop_info\n"); + return VIDC_ERR_FAIL; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) { + dprintk(VIDC_ERR, "%s: status %#x\n", __func__, status); + return status; + } + + data_ptr = (u8 *) &pkt->rg_property_data[0]; + num_properties = pkt->num_properties; + dprintk(VIDC_DBG, + "%s: data_start %pK, num_properties %#x\n", + __func__, data_ptr, num_properties); + if (!num_properties) { + sys_init_done->capabilities = NULL; + dprintk(VIDC_DBG, + "Venus didn't set any properties in SYS_INIT_DONE"); + return status; + } + bytes_read = hfi_fill_codec_info(data_ptr, sys_init_done, rem_bytes); + if (bytes_read < 0) + return VIDC_ERR_FAIL; + data_ptr += bytes_read; + rem_bytes -= bytes_read; + num_properties--; + + status = hfi_parse_init_done_properties( + sys_init_done->capabilities, + VIDC_MAX_SESSIONS, data_ptr, num_properties, + rem_bytes); + if (status) { + dprintk(VIDC_ERR, "%s: parse status %#x\n", + __func__, status); + return status; + } + + return status; +} + +static void hfi_process_sess_get_prop_buf_req( + struct hfi_msg_session_property_info_packet *prop, + struct buffer_requirements *buffreq) +{ + struct hfi_buffer_requirements *hfi_buf_req; + u32 req_bytes; + + if (!prop) { + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_prop: %pK\n", + prop); + return; + } + + req_bytes = prop->size - sizeof( + struct hfi_msg_session_property_info_packet); + if (!req_bytes || req_bytes % sizeof(struct hfi_buffer_requirements) || + !prop->rg_property_data[1]) { + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_pkt: %d\n", + req_bytes); + return; + } + + hfi_buf_req = (struct hfi_buffer_requirements *) + &prop->rg_property_data[1]; + + if (!hfi_buf_req) { + dprintk(VIDC_ERR, "%s - invalid buffer req pointer\n", + __func__); + return; + } + + while (req_bytes) { + dprintk(VIDC_DBG, "got buffer requirements for: %d\n", + hfi_buf_req->buffer_type); + switch (hfi_buf_req->buffer_type) { + case HFI_BUFFER_INPUT: + memcpy(&buffreq->buffer[0], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[0].buffer_type = HAL_BUFFER_INPUT; + break; + case HFI_BUFFER_OUTPUT: + memcpy(&buffreq->buffer[1], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[1].buffer_type = HAL_BUFFER_OUTPUT; + break; + case HFI_BUFFER_OUTPUT2: + memcpy(&buffreq->buffer[2], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[2].buffer_type = HAL_BUFFER_OUTPUT2; + break; + case HFI_BUFFER_EXTRADATA_INPUT: + memcpy(&buffreq->buffer[3], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[3].buffer_type = + HAL_BUFFER_EXTRADATA_INPUT; + break; + case HFI_BUFFER_EXTRADATA_OUTPUT: + memcpy(&buffreq->buffer[4], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[4].buffer_type = + HAL_BUFFER_EXTRADATA_OUTPUT; + break; + case HFI_BUFFER_EXTRADATA_OUTPUT2: + memcpy(&buffreq->buffer[5], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[5].buffer_type = + HAL_BUFFER_EXTRADATA_OUTPUT2; + break; + case HFI_BUFFER_COMMON_INTERNAL_SCRATCH: + memcpy(&buffreq->buffer[6], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[6].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH; + break; + case HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1: + memcpy(&buffreq->buffer[7], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[7].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH_1; + break; + case HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2: + memcpy(&buffreq->buffer[8], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[8].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH_2; + break; + case HFI_BUFFER_INTERNAL_PERSIST: + memcpy(&buffreq->buffer[9], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[9].buffer_type = + HAL_BUFFER_INTERNAL_PERSIST; + break; + case HFI_BUFFER_INTERNAL_PERSIST_1: + memcpy(&buffreq->buffer[10], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[10].buffer_type = + HAL_BUFFER_INTERNAL_PERSIST_1; + break; + case HFI_BUFFER_COMMON_INTERNAL_RECON: + memcpy(&buffreq->buffer[11], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[11].buffer_type = + HAL_BUFFER_INTERNAL_RECON; + break; + default: + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d\n", + hfi_buf_req->buffer_type); + break; + } + req_bytes -= sizeof(struct hfi_buffer_requirements); + hfi_buf_req++; + } +} + +static int hfi_process_session_prop_info(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_property_info_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct buffer_requirements buff_req = { { {0} } }; + + dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%#x]\n", + pkt->session_id); + + if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: bad_pkt_size\n"); + return -E2BIG; + } else if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: no_properties\n"); + return -EINVAL; + } + + switch (pkt->rg_property_data[0]) { + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + hfi_process_sess_get_prop_buf_req(pkt, &buff_req); + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data.property.buf_req = buff_req; + cmd_done.size = sizeof(buff_req); + + info->response_type = HAL_SESSION_PROPERTY_INFO; + info->response.cmd = cmd_done; + + return 0; + default: + dprintk(VIDC_DBG, + "hal_process_session_prop_info: unknown_prop_id: %x\n", + pkt->rg_property_data[0]); + return -ENOTSUPP; + } +} + +static int hfi_process_session_init_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_init_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct vidc_hal_session_init_done session_init_done = { {0} }; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%x]\n", pkt->session_id); + + if (sizeof(struct hfi_msg_sys_session_init_done_packet) > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data.session_init_done = session_init_done; + cmd_done.size = sizeof(struct vidc_hal_session_init_done); + + info->response_type = HAL_SESSION_INIT_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_load_res_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_load_resources_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%#x]\n", + pkt->session_id); + + if (sizeof(struct hfi_msg_session_load_resources_done_packet) != + pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_load_res_done: bad packet size: %d\n", + pkt->size); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_LOAD_RESOURCE_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_flush_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_flush_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%#x]\n", + pkt->session_id); + + if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_flush_done: bad packet size: %d\n", + pkt->size); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = sizeof(u32); + + switch (pkt->flush_type) { + case HFI_FLUSH_OUTPUT: + cmd_done.data.flush_type = HAL_FLUSH_OUTPUT; + break; + case HFI_FLUSH_INPUT: + cmd_done.data.flush_type = HAL_FLUSH_INPUT; + break; + case HFI_FLUSH_ALL: + cmd_done.data.flush_type = HAL_FLUSH_ALL; + break; + default: + dprintk(VIDC_ERR, + "%s: invalid flush type!", __func__); + return -EINVAL; + } + + info->response_type = HAL_SESSION_FLUSH_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_etb_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_empty_buffer_done_packet *pkt = _pkt; + struct msm_vidc_cb_data_done data_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%#x]\n", pkt->session_id); + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_empty_buffer_done_packet)) + goto bad_packet_size; + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.input_done.input_tag = pkt->input_tag; + data_done.input_done.recon_stats.buffer_index = + pkt->ubwc_cr_stats.frame_index; + memcpy(&data_done.input_done.recon_stats.ubwc_stats_info, + &pkt->ubwc_cr_stats.ubwc_stats_info, + sizeof(data_done.input_done.recon_stats.ubwc_stats_info)); + data_done.input_done.recon_stats.complexity_number = + pkt->ubwc_cr_stats.complexity_number; + data_done.input_done.offset = pkt->offset; + data_done.input_done.filled_len = pkt->filled_len; + data_done.input_done.flags = pkt->flags; + data_done.input_done.packet_buffer = pkt->packet_buffer; + data_done.input_done.extra_data_buffer = pkt->extra_data_buffer; + data_done.input_done.status = + hfi_map_err_status(pkt->error_type); + + trace_msm_v4l2_vidc_buffer_event_end("ETB", + (u32)pkt->packet_buffer, -1, -1, + pkt->filled_len, pkt->offset); + + info->response_type = HAL_SESSION_ETB_DONE; + info->response.data = data_done; + + return 0; +bad_packet_size: + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; +} + +static int hfi_process_session_ftb_done( + u32 device_id, void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct vidc_hal_msg_pkt_hdr *msg_hdr = _pkt; + struct msm_vidc_cb_data_done data_done = {0}; + bool is_decoder = false, is_encoder = false; + + if (!msg_hdr) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + is_encoder = msg_hdr->size == sizeof(struct + hfi_msg_session_fill_buffer_done_compressed_packet) + 4; + is_decoder = msg_hdr->size == sizeof(struct + hfi_msg_session_fbd_uncompressed_plane0_packet) + 4; + + if (!(is_encoder ^ is_decoder)) { + dprintk(VIDC_ERR, "Ambiguous packet (%#x) received (size %d)\n", + msg_hdr->packet, msg_hdr->size); + return -EBADHANDLE; + } + + if (is_encoder) { + struct hfi_msg_session_fill_buffer_done_compressed_packet *pkt = + (struct hfi_msg_session_fill_buffer_done_compressed_packet *) + msg_hdr; + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n", + pkt->session_id); + if (sizeof(struct + hfi_msg_session_fill_buffer_done_compressed_packet) + > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_ftb_done: bad_pkt_size\n"); + return -E2BIG; + } else if (pkt->error_type != HFI_ERR_NONE) { + dprintk(VIDC_ERR, + "got buffer back with error %x\n", + pkt->error_type); + /* Proceed with the FBD */ + } + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.clnt_data = 0; + + data_done.output_done.timestamp_hi = pkt->time_stamp_hi; + data_done.output_done.timestamp_lo = pkt->time_stamp_lo; + data_done.output_done.flags1 = pkt->flags; + data_done.output_done.input_tag = pkt->input_tag; + data_done.output_done.output_tag = pkt->output_tag; + data_done.output_done.mark_target = pkt->mark_target; + data_done.output_done.mark_data = pkt->mark_data; + data_done.output_done.stats = pkt->stats; + data_done.output_done.offset1 = pkt->offset; + data_done.output_done.alloc_len1 = pkt->alloc_len; + data_done.output_done.filled_len1 = pkt->filled_len; + data_done.output_done.picture_type = pkt->picture_type; + data_done.output_done.packet_buffer1 = pkt->packet_buffer; + data_done.output_done.extra_data_buffer = + pkt->extra_data_buffer; + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT; + } else /* if (is_decoder) */ { + struct hfi_msg_session_fbd_uncompressed_plane0_packet *pkt = + (struct hfi_msg_session_fbd_uncompressed_plane0_packet *) + msg_hdr; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n", + pkt->session_id); + if (sizeof( + struct hfi_msg_session_fbd_uncompressed_plane0_packet) > + pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_ftb_done: bad_pkt_size\n"); + return -E2BIG; + } + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.clnt_data = 0; + + data_done.output_done.stream_id = pkt->stream_id; + data_done.output_done.view_id = pkt->view_id; + data_done.output_done.timestamp_hi = pkt->time_stamp_hi; + data_done.output_done.timestamp_lo = pkt->time_stamp_lo; + data_done.output_done.flags1 = pkt->flags; + data_done.output_done.mark_target = pkt->mark_target; + data_done.output_done.mark_data = pkt->mark_data; + data_done.output_done.stats = pkt->stats; + data_done.output_done.alloc_len1 = pkt->alloc_len; + data_done.output_done.filled_len1 = pkt->filled_len; + data_done.output_done.offset1 = pkt->offset; + data_done.output_done.frame_width = pkt->frame_width; + data_done.output_done.frame_height = pkt->frame_height; + data_done.output_done.start_x_coord = pkt->start_x_coord; + data_done.output_done.start_y_coord = pkt->start_y_coord; + data_done.output_done.input_tag = pkt->input_tag; + data_done.output_done.input_tag1 = pkt->input_tag2; + data_done.output_done.output_tag = pkt->output_tag; + data_done.output_done.picture_type = pkt->picture_type; + data_done.output_done.packet_buffer1 = pkt->packet_buffer; + data_done.output_done.extra_data_buffer = + pkt->extra_data_buffer; + + if (!pkt->stream_id) + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT; + else if (pkt->stream_id == 1) + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT2; + } + + trace_msm_v4l2_vidc_buffer_event_end("FTB", + (u32)data_done.output_done.packet_buffer1, + (((u64)data_done.output_done.timestamp_hi) << 32) + + ((u64)data_done.output_done.timestamp_lo), + data_done.output_done.alloc_len1, + data_done.output_done.filled_len1, + data_done.output_done.offset1); + + info->response_type = HAL_SESSION_FTB_DONE; + info->response.data = data_done; + + return 0; +} + +static int hfi_process_session_start_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_start_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_start_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_START_DONE; + info->response.cmd = cmd_done; + return 0; +} + +static int hfi_process_session_stop_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_stop_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_stop_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_STOP_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_rel_res_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_release_resources_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_release_resources_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_RELEASE_RESOURCE_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_rel_buf_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_release_buffers_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_release_buffers_done_packet)) { + dprintk(VIDC_ERR, "bad packet/packet size %d\n", + pkt ? pkt->size : 0); + return -E2BIG; + } + dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%#x]\n", + pkt->session_id); + + cmd_done.device_id = device_id; + cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done); + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data.buffer_info.buffer_addr = *pkt->rg_buffer_info; + cmd_done.size = sizeof(struct hal_buffer_info); + + info->response_type = HAL_SESSION_RELEASE_BUFFER_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_register_buffer_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_register_buffers_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_register_buffers_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; + } + dprintk(VIDC_DBG, "RECEIVED: SESSION_REGISTER_BUFFERS_DONE[%#x]\n", + pkt->session_id); + + cmd_done.device_id = device_id; + cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done); + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data.regbuf.client_data = pkt->client_data; + + info->response_type = HAL_SESSION_REGISTER_BUFFER_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_unregister_buffer_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_unregister_buffers_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_unregister_buffers_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; + } + dprintk(VIDC_DBG, "RECEIVED: SESSION_UNREGISTER_BUFFERS_DONE[%#x]\n", + pkt->session_id); + + cmd_done.device_id = device_id; + cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done); + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data.unregbuf.client_data = pkt->client_data; + + info->response_type = HAL_SESSION_UNREGISTER_BUFFER_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_end_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_end_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%#x]\n", pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_sys_session_end_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_END_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static int hfi_process_session_abort_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_abort_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_sys_session_abort_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size: %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; + } + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + info->response_type = HAL_SESSION_ABORT_DONE; + info->response.cmd = cmd_done; + + return 0; +} + +static void hfi_process_sys_get_prop_image_version( + struct hfi_msg_sys_property_info_packet *pkt) +{ + int i = 0; + size_t smem_block_size = 0; + u8 *smem_table_ptr; + char version[256]; + const u32 version_string_size = 128; + const u32 smem_image_index_venus = 14 * 128; + u8 *str_image_version; + int req_bytes; + + req_bytes = pkt->size - sizeof(*pkt); + if (req_bytes < version_string_size || + !pkt->rg_property_data[1] || + pkt->num_properties > 1) { + dprintk(VIDC_ERR, "%s: bad_pkt: %d\n", __func__, req_bytes); + return; + } + str_image_version = (u8 *)&pkt->rg_property_data[1]; + /* + * The version string returned by firmware includes null + * characters at the start and in between. Replace the null + * characters with space, to print the version info. + */ + for (i = 0; i < version_string_size; i++) { + if (str_image_version[i] != '\0') + version[i] = str_image_version[i]; + else + version[i] = ' '; + } + version[i] = '\0'; + dprintk(VIDC_DBG, "F/W version: %s\n", version); + + smem_table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, &smem_block_size); + if ((smem_image_index_venus + version_string_size) <= smem_block_size && + smem_table_ptr) + memcpy(smem_table_ptr + smem_image_index_venus, + str_image_version, version_string_size); +} + +static int hfi_process_sys_property_info(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_property_info_packet *pkt = _pkt; + if (!pkt) { + dprintk(VIDC_ERR, "%s: invalid param\n", __func__); + return -EINVAL; + } else if (pkt->size < sizeof(*pkt)) { + dprintk(VIDC_ERR, + "%s: bad_pkt_size\n", __func__); + return -E2BIG; + } else if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "%s: no_properties\n", __func__); + return -EINVAL; + } + + switch (pkt->rg_property_data[0]) { + case HFI_PROPERTY_SYS_IMAGE_VERSION: + hfi_process_sys_get_prop_image_version(pkt); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + return 0; + default: + dprintk(VIDC_DBG, + "%s: unknown_prop_id: %x\n", + __func__, pkt->rg_property_data[0]); + return -ENOTSUPP; + } + +} + +static int hfi_process_ignore(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + + return 0; +} + +int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct msm_vidc_cb_info *info) +{ + typedef int (*pkt_func_def)(u32, void *, struct msm_vidc_cb_info *info); + pkt_func_def pkt_func = NULL; + + if (!info || !msg_hdr || msg_hdr->size < VIDC_IFACEQ_MIN_PKT_SIZE) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -EINVAL; + } + + dprintk(VIDC_DBG, "Parse response %#x\n", msg_hdr->packet); + switch (msg_hdr->packet) { + case HFI_MSG_EVENT_NOTIFY: + pkt_func = (pkt_func_def)hfi_process_event_notify; + break; + case HFI_MSG_SYS_INIT_DONE: + pkt_func = (pkt_func_def)hfi_process_sys_init_done; + break; + case HFI_MSG_SYS_SESSION_INIT_DONE: + pkt_func = (pkt_func_def)hfi_process_session_init_done; + break; + case HFI_MSG_SYS_PROPERTY_INFO: + pkt_func = (pkt_func_def)hfi_process_sys_property_info; + break; + case HFI_MSG_SYS_SESSION_END_DONE: + pkt_func = (pkt_func_def)hfi_process_session_end_done; + break; + case HFI_MSG_SESSION_LOAD_RESOURCES_DONE: + pkt_func = (pkt_func_def)hfi_process_session_load_res_done; + break; + case HFI_MSG_SESSION_START_DONE: + pkt_func = (pkt_func_def)hfi_process_session_start_done; + break; + case HFI_MSG_SESSION_STOP_DONE: + pkt_func = (pkt_func_def)hfi_process_session_stop_done; + break; + case HFI_MSG_SESSION_EMPTY_BUFFER_DONE: + pkt_func = (pkt_func_def)hfi_process_session_etb_done; + break; + case HFI_MSG_SESSION_FILL_BUFFER_DONE: + pkt_func = (pkt_func_def)hfi_process_session_ftb_done; + break; + case HFI_MSG_SESSION_FLUSH_DONE: + pkt_func = (pkt_func_def)hfi_process_session_flush_done; + break; + case HFI_MSG_SESSION_PROPERTY_INFO: + pkt_func = (pkt_func_def)hfi_process_session_prop_info; + break; + case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE: + pkt_func = (pkt_func_def)hfi_process_session_rel_res_done; + break; + case HFI_MSG_SYS_RELEASE_RESOURCE: + pkt_func = (pkt_func_def)hfi_process_sys_rel_resource_done; + break; + case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE: + pkt_func = (pkt_func_def)hfi_process_session_rel_buf_done; + break; + case HFI_MSG_SESSION_REGISTER_BUFFERS_DONE: + pkt_func = (pkt_func_def) + hfi_process_session_register_buffer_done; + break; + case HFI_MSG_SESSION_UNREGISTER_BUFFERS_DONE: + pkt_func = (pkt_func_def) + hfi_process_session_unregister_buffer_done; + break; + case HFI_MSG_SYS_SESSION_ABORT_DONE: + pkt_func = (pkt_func_def)hfi_process_session_abort_done; + break; + case HFI_MSG_SESSION_SYNC_DONE: + pkt_func = (pkt_func_def)hfi_process_ignore; + break; + default: + dprintk(VIDC_DBG, "Unable to parse message: %#x\n", + msg_hdr->packet); + break; + } + + return pkt_func ? + pkt_func(device_id, (void *)msg_hdr, info) : -ENOTSUPP; +} diff --git a/drivers/media/platform/msm/vidc/msm_cvp.c b/drivers/media/platform/msm/vidc/msm_cvp.c new file mode 100644 index 000000000000..8616f2a9a231 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_cvp.c @@ -0,0 +1,631 @@ +/* Copyright (c) 2018, 2020, 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 "msm_cvp.h" + +#define MSM_VIDC_NOMINAL_CYCLES (444 * 1000 * 1000) +#define MSM_VIDC_UHD60E_VPSS_CYCLES (111 * 1000 * 1000) +#define MSM_VIDC_UHD60E_ISE_CYCLES (175 * 1000 * 1000) +#define MAX_CVP_VPSS_CYCLES (MSM_VIDC_NOMINAL_CYCLES - \ + MSM_VIDC_UHD60E_VPSS_CYCLES) +#define MAX_CVP_ISE_CYCLES (MSM_VIDC_NOMINAL_CYCLES - \ + MSM_VIDC_UHD60E_ISE_CYCLES) + +static void print_client_buffer(u32 tag, const char *str, + struct msm_vidc_inst *inst, struct msm_cvp_buffer *cbuf) +{ + if (!(tag & msm_vidc_debug) || !inst || !cbuf) + return; + + dprintk(tag, + "%s: %x : idx %2d fd %d off %d size %d type %d flags 0x%x\n", + str, hash32_ptr(inst->session), cbuf->index, cbuf->fd, + cbuf->offset, cbuf->size, cbuf->type, cbuf->flags); +} + +static void print_cvp_buffer(u32 tag, const char *str, + struct msm_vidc_inst *inst, struct msm_vidc_cvp_buffer *cbuf) +{ + if (!(tag & msm_vidc_debug) || !inst || !cbuf) + return; + + dprintk(tag, + "%s: %x : idx %2d fd %d off %d daddr %x size %d type %d flags 0x%x\n", + str, hash32_ptr(inst->session), cbuf->buf.index, cbuf->buf.fd, + cbuf->buf.offset, cbuf->smem.device_addr, cbuf->buf.size, + cbuf->buf.type, cbuf->buf.flags); +} + +static enum hal_buffer get_hal_buftype(const char *str, unsigned int type) +{ + enum hal_buffer buftype = HAL_BUFFER_NONE; + + if (type == MSM_CVP_BUFTYPE_INPUT) + buftype = HAL_BUFFER_INPUT; + else if (type == MSM_CVP_BUFTYPE_OUTPUT) + buftype = HAL_BUFFER_OUTPUT; + else if (type == MSM_CVP_BUFTYPE_INTERNAL_1) + buftype = HAL_BUFFER_INTERNAL_SCRATCH_1; + else if (type == MSM_CVP_BUFTYPE_INTERNAL_2) + buftype = HAL_BUFFER_INTERNAL_SCRATCH_1; + else + dprintk(VIDC_ERR, "%s: unknown buffer type %#x\n", + str, type); + + return buftype; +} + +void handle_session_register_buffer_done(enum hal_command_response cmd, + void *resp) +{ + struct msm_vidc_cb_cmd_done *response = resp; + struct msm_vidc_inst *inst; + struct msm_vidc_cvp_buffer *cbuf; + struct v4l2_event event = {0}; + u32 *data; + bool found; + + if (!response) { + dprintk(VIDC_ERR, "%s: invalid response\n", __func__); + return; + } + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid session %pK\n", __func__, + response->session_id); + return; + } + + mutex_lock(&inst->cvpbufs.lock); + found = false; + list_for_each_entry(cbuf, &inst->cvpbufs.list, list) { + if (response->data.regbuf.client_data == + cbuf->smem.device_addr) { + found = true; + break; + } + } + mutex_unlock(&inst->cvpbufs.lock); + if (!found) { + dprintk(VIDC_ERR, "%s: client_data %x not found\n", + __func__, response->data.regbuf.client_data); + goto exit; + } + print_cvp_buffer(VIDC_DBG, "register_done", inst, cbuf); + + event.type = V4L2_EVENT_MSM_VIDC_REGISTER_BUFFER_DONE; + data = (u32 *)event.u.data; + data[0] = cbuf->buf.index; + data[1] = cbuf->buf.type; + data[2] = cbuf->buf.fd; + data[3] = cbuf->buf.offset; + v4l2_event_queue_fh(&inst->event_handler, &event); + +exit: + put_inst(inst); +} + +void handle_session_unregister_buffer_done(enum hal_command_response cmd, + void *resp) +{ + int rc; + struct msm_vidc_cb_cmd_done *response = resp; + struct msm_vidc_inst *inst; + struct msm_vidc_cvp_buffer *cbuf, *dummy; + struct v4l2_event event = {0}; + u32 *data; + bool found; + + if (!response) { + dprintk(VIDC_ERR, "%s: invalid response\n", __func__); + return; + } + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid session %pK\n", __func__, + response->session_id); + return; + } + + mutex_lock(&inst->cvpbufs.lock); + found = false; + list_for_each_entry_safe(cbuf, dummy, &inst->cvpbufs.list, list) { + if (response->data.unregbuf.client_data == + cbuf->smem.device_addr) { + found = true; + break; + } + } + if (!found) { + dprintk(VIDC_ERR, "%s: client_data %x not found\n", + __func__, response->data.unregbuf.client_data); + goto exit; + } + print_cvp_buffer(VIDC_DBG, "unregister_done", inst, cbuf); + + rc = msm_smem_unmap_dma_buf(inst, &cbuf->smem); + if (rc) { + print_cvp_buffer(VIDC_ERR, "unmap fail", inst, cbuf); + goto exit; + } + + event.type = V4L2_EVENT_MSM_VIDC_UNREGISTER_BUFFER_DONE; + data = (u32 *)event.u.data; + data[0] = cbuf->buf.index; + data[1] = cbuf->buf.type; + data[2] = cbuf->buf.fd; + data[3] = cbuf->buf.offset; + v4l2_event_queue_fh(&inst->event_handler, &event); + + list_del(&cbuf->list); + kfree(cbuf); + cbuf = NULL; +exit: + mutex_unlock(&inst->cvpbufs.lock); + put_inst(inst); +} + +static void print_cvp_cycles(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + struct msm_vidc_inst *temp; + + if (!inst || !inst->core) + return; + core = inst->core; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp->session_type == MSM_VIDC_CVP) { + dprintk(VIDC_ERR, "session %#x, vpss %d ise %d\n", + hash32_ptr(temp->session), + temp->clk_data.vpss_cycles, + temp->clk_data.ise_cycles); + } + } + mutex_unlock(&core->lock); +} + +static bool msm_cvp_check_session_supported(struct msm_vidc_inst *inst, + u32 vpss_cycles, u32 ise_cycles) +{ + struct msm_vidc_core *core; + struct msm_vidc_inst *temp; + u32 total_vpss_cycles = 0; + u32 total_ise_cycles = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return false; + } + core = inst->core; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp->session_type == MSM_VIDC_CVP) { + total_vpss_cycles += inst->clk_data.vpss_cycles; + total_ise_cycles += inst->clk_data.ise_cycles; + } + } + mutex_unlock(&core->lock); + + if ((total_vpss_cycles > MAX_CVP_VPSS_CYCLES) || + (total_ise_cycles > MAX_CVP_ISE_CYCLES)) + return false; + + return true; +} + +static int msm_cvp_scale_clocks_and_bus(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + rc = msm_vidc_set_clocks(inst->core); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed set_clocks for inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + + rc = msm_comm_vote_bus(inst->core); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed vote_bus for inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + +exit: + return rc; +} + +static int msm_cvp_get_session_info(struct msm_vidc_inst *inst, + struct msm_cvp_session_info *session) +{ + int rc = 0; + + if (!inst || !inst->core || !session) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + session->session_id = hash32_ptr(inst->session); + dprintk(VIDC_DBG, "%s: id 0x%x\n", __func__, session->session_id); + + return rc; +} + +static int msm_cvp_request_power(struct msm_vidc_inst *inst, + struct msm_cvp_request_power *power) +{ + int rc = 0; + + if (!inst || !power) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + dprintk(VIDC_DBG, + "%s: clock_cycles_a %d, clock_cycles_b %d, ddr_bw %d sys_cache_bw %d\n", + __func__, power->clock_cycles_a, power->clock_cycles_b, + power->ddr_bw, power->sys_cache_bw); + + rc = msm_cvp_check_session_supported(inst, power->clock_cycles_a, + power->clock_cycles_b); + if (!rc) { + dprintk(VIDC_ERR, + "%s: session %#x rejected, cycles: vpss %d, ise %d\n", + __func__, hash32_ptr(inst->session), + power->clock_cycles_a, power->clock_cycles_b); + print_cvp_cycles(inst); + msm_comm_kill_session(inst); + return -EOVERFLOW; + } + + inst->clk_data.min_freq = max(power->clock_cycles_a, + power->clock_cycles_b); + /* convert client provided bps into kbps as expected by driver */ + inst->clk_data.ddr_bw = power->ddr_bw / 1000; + inst->clk_data.sys_cache_bw = power->sys_cache_bw / 1000; + rc = msm_cvp_scale_clocks_and_bus(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to scale clocks and bus for inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + + if (!inst->clk_data.min_freq && !inst->clk_data.ddr_bw && + !inst->clk_data.sys_cache_bw) { + rc = msm_cvp_inst_pause(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to pause inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + } else { + rc = msm_cvp_inst_resume(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to resume inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + } + +exit: + return rc; +} + +static int msm_cvp_register_buffer(struct msm_vidc_inst *inst, + struct msm_cvp_buffer *buf) +{ + int rc = 0; + bool found; + struct hfi_device *hdev; + struct msm_vidc_cvp_buffer *cbuf; + struct vidc_register_buffer vbuf; + + if (!inst || !inst->core || !buf) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + print_client_buffer(VIDC_DBG, "register", inst, buf); + + mutex_lock(&inst->cvpbufs.lock); + found = false; + list_for_each_entry(cbuf, &inst->cvpbufs.list, list) { + if (cbuf->buf.index == buf->index && + cbuf->buf.fd == buf->fd && + cbuf->buf.offset == buf->offset) { + found = true; + break; + } + } + mutex_unlock(&inst->cvpbufs.lock); + if (found) { + print_client_buffer(VIDC_ERR, "duplicate", inst, buf); + return -EINVAL; + } + + cbuf = kzalloc(sizeof(struct msm_vidc_cvp_buffer), GFP_KERNEL); + if (!cbuf) { + dprintk(VIDC_ERR, "%s: cbuf alloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(&cbuf->buf, buf, sizeof(struct msm_cvp_buffer)); + cbuf->smem.buffer_type = get_hal_buftype(__func__, buf->type); + cbuf->smem.fd = buf->fd; + cbuf->smem.offset = buf->offset; + cbuf->smem.size = buf->size; + rc = msm_smem_map_dma_buf(inst, &cbuf->smem); + if (rc) { + print_client_buffer(VIDC_ERR, "map failed", inst, buf); + goto exit; + } + + memset(&vbuf, 0, sizeof(struct vidc_register_buffer)); + vbuf.index = buf->index; + vbuf.type = get_hal_buftype(__func__, buf->type); + vbuf.size = buf->size; + vbuf.device_addr = cbuf->smem.device_addr; + vbuf.client_data = cbuf->smem.device_addr; + vbuf.response_required = true; + rc = call_hfi_op(hdev, session_register_buffer, + (void *)inst->session, &vbuf); + if (rc) { + print_cvp_buffer(VIDC_ERR, "register failed", inst, cbuf); + goto exit; + } + mutex_lock(&inst->cvpbufs.lock); + list_add_tail(&cbuf->list, &inst->cvpbufs.list); + mutex_unlock(&inst->cvpbufs.lock); + return rc; + +exit: + if (cbuf->smem.device_addr) + msm_smem_unmap_dma_buf(inst, &cbuf->smem); + kfree(cbuf); + cbuf = NULL; + + return rc; +} + +static int msm_cvp_unregister_buffer(struct msm_vidc_inst *inst, + struct msm_cvp_buffer *buf) +{ + int rc = 0; + bool found; + struct hfi_device *hdev; + struct msm_vidc_cvp_buffer *cbuf; + struct vidc_unregister_buffer vbuf; + + if (!inst || !inst->core || !buf) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + print_client_buffer(VIDC_DBG, "unregister", inst, buf); + + mutex_lock(&inst->cvpbufs.lock); + found = false; + list_for_each_entry(cbuf, &inst->cvpbufs.list, list) { + if (cbuf->buf.index == buf->index && + cbuf->buf.fd == buf->fd && + cbuf->buf.offset == buf->offset) { + found = true; + break; + } + } + if (!found) { + print_client_buffer(VIDC_ERR, "invalid", inst, buf); + mutex_unlock(&inst->cvpbufs.lock); + return -EINVAL; + } + + memset(&vbuf, 0, sizeof(struct vidc_unregister_buffer)); + vbuf.index = cbuf->buf.index; + vbuf.type = get_hal_buftype(__func__, cbuf->buf.type); + vbuf.size = cbuf->buf.size; + vbuf.device_addr = cbuf->smem.device_addr; + vbuf.client_data = cbuf->smem.device_addr; + vbuf.response_required = true; + rc = call_hfi_op(hdev, session_unregister_buffer, + (void *)inst->session, &vbuf); + if (rc) + print_cvp_buffer(VIDC_ERR, "unregister failed", inst, cbuf); + + mutex_unlock(&inst->cvpbufs.lock); + return rc; +} + +int msm_vidc_cvp(struct msm_vidc_inst *inst, struct msm_vidc_arg *arg) +{ + int rc = 0; + + if (!inst || !arg) { + dprintk(VIDC_ERR, "%s: invalid args\n", __func__); + return -EINVAL; + } + + switch (arg->type) { + case MSM_CVP_GET_SESSION_INFO: + { + struct msm_cvp_session_info *session = + (struct msm_cvp_session_info *)&arg->data.session; + + rc = msm_cvp_get_session_info(inst, session); + break; + } + case MSM_CVP_REQUEST_POWER: + { + struct msm_cvp_request_power *power = + (struct msm_cvp_request_power *)&arg->data.req_power; + + rc = msm_cvp_request_power(inst, power); + break; + } + case MSM_CVP_REGISTER_BUFFER: + { + struct msm_cvp_buffer *buf = + (struct msm_cvp_buffer *)&arg->data.regbuf; + + rc = msm_cvp_register_buffer(inst, buf); + break; + } + case MSM_CVP_UNREGISTER_BUFFER: + { + struct msm_cvp_buffer *buf = + (struct msm_cvp_buffer *)&arg->data.unregbuf; + + rc = msm_cvp_unregister_buffer(inst, buf); + break; + } + default: + dprintk(VIDC_ERR, "%s: unknown arg type 0x%x\n", + __func__, arg->type); + rc = -ENOTSUPP; + break; + } + + return rc; +} + +static struct msm_vidc_ctrl msm_cvp_ctrls[] = { + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE, + .name = "Secure mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, +}; + +int msm_cvp_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops) +{ + return msm_comm_ctrl_init(inst, msm_cvp_ctrls, + ARRAY_SIZE(msm_cvp_ctrls), ctrl_ops); +} + +int msm_cvp_inst_pause(struct msm_vidc_inst *inst) +{ + int rc; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_pause, (void *)inst->session); + if (rc) + dprintk(VIDC_ERR, "%s: failed to pause inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + + return rc; +} + +int msm_cvp_inst_resume(struct msm_vidc_inst *inst) +{ + int rc; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_resume, (void *)inst->session); + if (rc) + dprintk(VIDC_ERR, "%s: failed to resume inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + + return rc; +} + +int msm_cvp_inst_deinit(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct msm_vidc_cvp_buffer *cbuf, *temp; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + dprintk(VIDC_DBG, "%s: inst %pK (%#x)\n", __func__, + inst, hash32_ptr(inst->session)); + + rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE); + if (rc) + dprintk(VIDC_ERR, "%s: close failed\n", __func__); + + mutex_lock(&inst->cvpbufs.lock); + list_for_each_entry_safe(cbuf, temp, &inst->cvpbufs.list, list) { + print_cvp_buffer(VIDC_ERR, "unregistered", inst, cbuf); + rc = msm_smem_unmap_dma_buf(inst, &cbuf->smem); + if (rc) + dprintk(VIDC_ERR, "%s: unmap failed\n", __func__); + list_del(&cbuf->list); + kfree(cbuf); + } + mutex_unlock(&inst->cvpbufs.lock); + + inst->clk_data.min_freq = 0; + inst->clk_data.ddr_bw = 0; + inst->clk_data.sys_cache_bw = 0; + rc = msm_cvp_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: failed to scale_clocks_and_bus\n", + __func__); + + return rc; +} + +int msm_cvp_inst_init(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + dprintk(VIDC_DBG, "%s: inst %pK (%#x)\n", __func__, + inst, hash32_ptr(inst->session)); + + /* set default frequency */ + inst->clk_data.core_id = VIDC_CORE_ID_2; + inst->clk_data.min_freq = 1000; + inst->clk_data.ddr_bw = 1000; + inst->clk_data.sys_cache_bw = 1000; + + return rc; +} diff --git a/drivers/media/platform/msm/vidc/msm_cvp.h b/drivers/media/platform/msm/vidc/msm_cvp.h new file mode 100644 index 000000000000..f8dc75f443db --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_cvp.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2018, 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_VIDC_CVP_H_ +#define _MSM_VIDC_CVP_H_ + +#include "msm_vidc_internal.h" +#include "msm_vidc_common.h" +#include "msm_vidc_clocks.h" +#include "msm_vidc_debug.h" + +void handle_session_register_buffer_done(enum hal_command_response cmd, + void *resp); +void handle_session_unregister_buffer_done(enum hal_command_response cmd, + void *resp); +int msm_vidc_cvp(struct msm_vidc_inst *inst, struct msm_vidc_arg *arg); +int msm_cvp_inst_init(struct msm_vidc_inst *inst); +int msm_cvp_inst_deinit(struct msm_vidc_inst *inst); +int msm_cvp_inst_pause(struct msm_vidc_inst *inst); +int msm_cvp_inst_resume(struct msm_vidc_inst *inst); +int msm_cvp_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c new file mode 100644 index 000000000000..efee3b3091e1 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -0,0 +1,606 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" + + +static int msm_dma_get_device_address(struct dma_buf *dbuf, unsigned long align, + dma_addr_t *iova, unsigned long *buffer_size, + unsigned long flags, enum hal_buffer buffer_type, + unsigned long session_type, struct msm_vidc_platform_resources *res, + struct dma_mapping_info *mapping_info) +{ + int rc = 0; + struct dma_buf_attachment *attach; + struct sg_table *table = NULL; + struct context_bank_info *cb = NULL; + + if (!dbuf || !iova || !buffer_size || !mapping_info) { + dprintk(VIDC_ERR, "Invalid params: %pK, %pK, %pK, %pK\n", + dbuf, iova, buffer_size, mapping_info); + return -EINVAL; + } + + if (is_iommu_present(res)) { + cb = msm_smem_get_context_bank( + session_type, (flags & SMEM_SECURE), + res, buffer_type); + if (!cb) { + dprintk(VIDC_ERR, + "%s: Failed to get context bank device\n", + __func__); + rc = -EIO; + goto mem_map_failed; + } + + /* Check if the dmabuf size matches expected size */ + if (dbuf->size < *buffer_size) { + rc = -EINVAL; + dprintk(VIDC_ERR, + "Size mismatch: Dmabuf size: %zu Expected Size: %lu", + dbuf->size, *buffer_size); + msm_vidc_res_handle_fatal_hw_error(res, + true); + goto mem_buf_size_mismatch; + } + + /* Prepare a dma buf for dma on the given device */ + attach = dma_buf_attach(dbuf, cb->dev); + if (IS_ERR_OR_NULL(attach)) { + rc = PTR_ERR(attach) ?: -ENOMEM; + dprintk(VIDC_ERR, "Failed to attach dmabuf\n"); + goto mem_buf_attach_failed; + } + + /* + * Get the scatterlist for the given attachment + * Mapping of sg is taken care by map attachment + */ + attach->dma_map_attrs = DMA_ATTR_DELAYED_UNMAP; + /* + * We do not need dma_map function to perform cache operations + * on the whole buffer size and hence pass skip sync flag. + * We do the required cache operations separately for the + * required buffer size + */ + attach->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + if (res->sys_cache_present) + attach->dma_map_attrs |= + DMA_ATTR_IOMMU_USE_UPSTREAM_HINT; + + table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(table)) { + rc = PTR_ERR(table) ?: -ENOMEM; + dprintk(VIDC_ERR, "Failed to map table\n"); + goto mem_map_table_failed; + } + + /* debug trace's need to be updated later */ + trace_msm_smem_buffer_iommu_op_start("MAP", 0, 0, + align, *iova, *buffer_size); + + if (table->sgl) { + *iova = table->sgl->dma_address; + *buffer_size = table->sgl->dma_length; + } else { + dprintk(VIDC_ERR, "sgl is NULL\n"); + rc = -ENOMEM; + goto mem_map_sg_failed; + } + + mapping_info->dev = cb->dev; + mapping_info->domain = cb->domain; + mapping_info->table = table; + mapping_info->attach = attach; + mapping_info->buf = dbuf; + mapping_info->cb_info = (void *)cb; + + trace_msm_smem_buffer_iommu_op_end("MAP", 0, 0, + align, *iova, *buffer_size); + } else { + dprintk(VIDC_DBG, "iommu not present, use phys mem addr\n"); + } + + return 0; +mem_map_sg_failed: + dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL); +mem_map_table_failed: + dma_buf_detach(dbuf, attach); +mem_buf_size_mismatch: +mem_buf_attach_failed: +mem_map_failed: + return rc; +} + +static int msm_dma_put_device_address(u32 flags, + struct dma_mapping_info *mapping_info, + enum hal_buffer buffer_type) +{ + int rc = 0; + + if (!mapping_info) { + dprintk(VIDC_WARN, "Invalid mapping_info\n"); + return -EINVAL; + } + + if (!mapping_info->dev || !mapping_info->table || + !mapping_info->buf || !mapping_info->attach || + !mapping_info->cb_info) { + dprintk(VIDC_WARN, "Invalid params\n"); + return -EINVAL; + } + + trace_msm_smem_buffer_iommu_op_start("UNMAP", 0, 0, 0, 0, 0); + dma_buf_unmap_attachment(mapping_info->attach, + mapping_info->table, DMA_BIDIRECTIONAL); + dma_buf_detach(mapping_info->buf, mapping_info->attach); + trace_msm_smem_buffer_iommu_op_end("UNMAP", 0, 0, 0, 0, 0); + + mapping_info->dev = NULL; + mapping_info->domain = NULL; + mapping_info->table = NULL; + mapping_info->attach = NULL; + mapping_info->buf = NULL; + mapping_info->cb_info = NULL; + + + return rc; +} + +struct dma_buf *msm_smem_get_dma_buf(int fd) +{ + struct dma_buf *dma_buf; + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + dprintk(VIDC_ERR, "Failed to get dma_buf for %d, error %ld\n", + fd, PTR_ERR(dma_buf)); + dma_buf = NULL; + } + + return dma_buf; +} + +void msm_smem_put_dma_buf(void *dma_buf) +{ + if (!dma_buf) { + dprintk(VIDC_ERR, "%s: NULL dma_buf\n", __func__); + return; + } + + dma_buf_put((struct dma_buf *)dma_buf); + + return; +} + +int msm_smem_map_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem) +{ + int rc = 0; + + dma_addr_t iova = 0; + u32 temp = 0; + unsigned long buffer_size = 0; + unsigned long align = SZ_4K; + struct dma_buf *dbuf; + unsigned long ion_flags = 0; + + if (!inst || !smem) { + dprintk(VIDC_ERR, "%s: Invalid params: %pK %pK\n", + __func__, inst, smem); + rc = -EINVAL; + goto exit; + } + + if (smem->refcount) { + smem->refcount++; + goto exit; + } + + dbuf = msm_smem_get_dma_buf(smem->fd); + if (!dbuf) { + rc = -EINVAL; + goto exit; + } + + smem->dma_buf = dbuf; + + rc = dma_buf_get_flags(dbuf, &ion_flags); + if (rc) { + dprintk(VIDC_ERR, "Failed to get dma buf flags: %d\n", rc); + goto exit; + } + if (ion_flags & ION_FLAG_CACHED) + smem->flags |= SMEM_CACHED; + + if (ion_flags & ION_FLAG_SECURE) + smem->flags |= SMEM_SECURE; + + buffer_size = smem->size; + + rc = msm_dma_get_device_address(dbuf, align, &iova, &buffer_size, + smem->flags, smem->buffer_type, inst->session_type, + &(inst->core->resources), &smem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc); + goto exit; + } + temp = (u32)iova; + if ((dma_addr_t)temp != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", &iova, temp); + rc = -EINVAL; + goto exit; + } + + smem->device_addr = (u32)iova + smem->offset; + + smem->refcount++; +exit: + return rc; +} + +int msm_smem_unmap_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem) +{ + int rc = 0; + + if (!inst || !smem) { + dprintk(VIDC_ERR, "%s: Invalid params: %pK %pK\n", + __func__, inst, smem); + rc = -EINVAL; + goto exit; + } + + if (smem->refcount) { + smem->refcount--; + } else { + dprintk(VIDC_WARN, + "unmap called while refcount is zero already\n"); + return -EINVAL; + } + + if (smem->refcount) + goto exit; + + rc = msm_dma_put_device_address(smem->flags, &smem->mapping_info, + smem->buffer_type); + if (rc) { + dprintk(VIDC_ERR, "Failed to put device address: %d\n", rc); + goto exit; + } + + msm_smem_put_dma_buf(smem->dma_buf); + + smem->device_addr = 0x0; + smem->dma_buf = NULL; + +exit: + return rc; +} + +static int get_secure_flag_for_buffer_type( + u32 session_type, enum hal_buffer buffer_type) +{ + switch (buffer_type) { + case HAL_BUFFER_INPUT: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_PIXEL; + else + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_OUTPUT: + case HAL_BUFFER_OUTPUT2: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_BITSTREAM; + else + return ION_FLAG_CP_PIXEL; + case HAL_BUFFER_INTERNAL_SCRATCH: + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_INTERNAL_SCRATCH_1: + return ION_FLAG_CP_NON_PIXEL; + case HAL_BUFFER_INTERNAL_SCRATCH_2: + return ION_FLAG_CP_PIXEL; + case HAL_BUFFER_INTERNAL_PERSIST: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_NON_PIXEL; + else + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_INTERNAL_PERSIST_1: + return ION_FLAG_CP_NON_PIXEL; + default: + WARN(1, "No matching secure flag for buffer type : %x\n", + buffer_type); + return -EINVAL; + } +} + +static int alloc_dma_mem(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + struct msm_vidc_platform_resources *res, u32 session_type, + struct msm_smem *mem) +{ + dma_addr_t iova = 0; + unsigned long buffer_size = 0; + unsigned long heap_mask = 0; + int rc = 0; + int ion_flags = 0; + struct dma_buf *dbuf = NULL; + + if (!res) { + dprintk(VIDC_ERR, "%s: NULL res\n", __func__); + return -EINVAL; + } + + align = ALIGN(align, SZ_4K); + size = ALIGN(size, SZ_4K); + + if (is_iommu_present(res)) { + if (flags & SMEM_ADSP) { + dprintk(VIDC_DBG, "Allocating from ADSP heap\n"); + heap_mask = ION_HEAP(ION_ADSP_HEAP_ID); + } else { + heap_mask = ION_HEAP(ION_SYSTEM_HEAP_ID); + } + } else { + dprintk(VIDC_DBG, + "allocate shared memory from adsp heap size %zx align %d\n", + size, align); + heap_mask = ION_HEAP(ION_ADSP_HEAP_ID); + } + + if (flags & SMEM_CACHED) + ion_flags |= ION_FLAG_CACHED; + + if ((flags & SMEM_SECURE) || + (buffer_type == HAL_BUFFER_INTERNAL_PERSIST && + session_type == MSM_VIDC_ENCODER)) { + int secure_flag = + get_secure_flag_for_buffer_type( + session_type, buffer_type); + if (secure_flag < 0) { + rc = secure_flag; + goto fail_shared_mem_alloc; + } + + ion_flags |= ION_FLAG_SECURE | secure_flag; + heap_mask = ION_HEAP(ION_SECURE_HEAP_ID); + + if (res->slave_side_cp) { + heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID); + size = ALIGN(size, SZ_1M); + align = ALIGN(size, SZ_1M); + } + flags |= SMEM_SECURE; + } + + trace_msm_smem_buffer_dma_op_start("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + dbuf = ion_alloc(size, heap_mask, ion_flags); + if (IS_ERR_OR_NULL(dbuf)) { + dprintk(VIDC_ERR, + "Failed to allocate shared memory = %zx, %#x\n", + size, flags); + rc = -ENOMEM; + goto fail_shared_mem_alloc; + } + trace_msm_smem_buffer_dma_op_end("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + + mem->flags = flags; + mem->buffer_type = buffer_type; + mem->offset = 0; + mem->size = size; + mem->dma_buf = dbuf; + mem->kvaddr = NULL; + + rc = msm_dma_get_device_address(dbuf, align, &iova, + &buffer_size, flags, buffer_type, + session_type, res, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", + rc); + goto fail_device_address; + } + mem->device_addr = (u32)iova; + if ((dma_addr_t)mem->device_addr != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", + &iova, mem->device_addr); + goto fail_device_address; + } + + if (map_kernel) { + dma_buf_begin_cpu_access(dbuf, DMA_BIDIRECTIONAL); + mem->kvaddr = dma_buf_vmap(dbuf); + if (!mem->kvaddr) { + dprintk(VIDC_ERR, + "Failed to map shared mem in kernel\n"); + rc = -EIO; + goto fail_map; + } + } + + dprintk(VIDC_DBG, + "%s: dma_buf = %pK, device_addr = %x, size = %d, kvaddr = %pK, buffer_type = %#x, flags = %#lx\n", + __func__, mem->dma_buf, mem->device_addr, mem->size, + mem->kvaddr, mem->buffer_type, mem->flags); + return rc; + +fail_map: + if (map_kernel) + dma_buf_end_cpu_access(dbuf, DMA_BIDIRECTIONAL); +fail_device_address: + dma_buf_put(dbuf); +fail_shared_mem_alloc: + return rc; +} + +static int free_dma_mem(struct msm_smem *mem) +{ + int rc = 0; + + dprintk(VIDC_DBG, + "%s: dma_buf = %pK, device_addr = %x, size = %d, kvaddr = %pK, buffer_type = %#x\n", + __func__, mem->dma_buf, mem->device_addr, mem->size, + mem->kvaddr, mem->buffer_type); + + if (mem->device_addr) { + msm_dma_put_device_address(mem->flags, + &mem->mapping_info, mem->buffer_type); + mem->device_addr = 0x0; + } + + if (mem->kvaddr) { + dma_buf_vunmap(mem->dma_buf, mem->kvaddr); + mem->kvaddr = NULL; + dma_buf_end_cpu_access(mem->dma_buf, DMA_BIDIRECTIONAL); + } + + if (mem->dma_buf) { + trace_msm_smem_buffer_dma_op_start("FREE", + (u32)mem->buffer_type, -1, mem->size, -1, + mem->flags, -1); + dma_buf_put(mem->dma_buf); + mem->dma_buf = NULL; + trace_msm_smem_buffer_dma_op_end("FREE", (u32)mem->buffer_type, + -1, mem->size, -1, mem->flags, -1); + } + + return rc; +} + +int msm_smem_alloc(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + void *res, u32 session_type, struct msm_smem *smem) +{ + int rc = 0; + + if (!smem || !size) { + dprintk(VIDC_ERR, "%s: NULL smem or %d size\n", + __func__, (u32)size); + return -EINVAL; + } + + rc = alloc_dma_mem(size, align, flags, buffer_type, map_kernel, + (struct msm_vidc_platform_resources *)res, + session_type, smem); + + return rc; +} + +int msm_smem_free(struct msm_smem *smem) +{ + int rc = 0; + + if (!smem) { + dprintk(VIDC_ERR, "NULL smem passed\n"); + return -EINVAL; + } + rc = free_dma_mem(smem); + + return rc; +}; + +int msm_smem_cache_operations(struct dma_buf *dbuf, + enum smem_cache_ops cache_op, unsigned long offset, unsigned long size) +{ + int rc = 0; + unsigned long flags = 0; + + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* Return if buffer doesn't support caching */ + rc = dma_buf_get_flags(dbuf, &flags); + if (rc) { + dprintk(VIDC_ERR, "%s: dma_buf_get_flags failed, err %d\n", + __func__, rc); + return rc; + } else if (!(flags & ION_FLAG_CACHED)) { + return rc; + } + + switch (cache_op) { + case SMEM_CACHE_CLEAN: + case SMEM_CACHE_CLEAN_INVALIDATE: + rc = dma_buf_begin_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + if (rc) + break; + rc = dma_buf_end_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + break; + case SMEM_CACHE_INVALIDATE: + rc = dma_buf_begin_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + if (rc) + break; + rc = dma_buf_end_cpu_access_partial(dbuf, DMA_FROM_DEVICE, + offset, size); + break; + default: + dprintk(VIDC_ERR, "%s: cache (%d) operation not supported\n", + __func__, cache_op); + rc = -EINVAL; + break; + } + + return rc; +} + +struct context_bank_info *msm_smem_get_context_bank(u32 session_type, + bool is_secure, struct msm_vidc_platform_resources *res, + enum hal_buffer buffer_type) +{ + struct context_bank_info *cb = NULL, *match = NULL; + + /* + * HAL_BUFFER_INPUT is directly mapped to bitstream CB in DT + * as the buffer type structure was initially designed + * just for decoder. For Encoder, input should be mapped to + * yuvpixel CB. Persist is mapped to nonpixel CB. + * So swap the buffer types just in this local scope. + */ + if (is_secure && session_type == MSM_VIDC_ENCODER) { + if (buffer_type == HAL_BUFFER_INPUT) + buffer_type = HAL_BUFFER_OUTPUT; + else if (buffer_type == HAL_BUFFER_OUTPUT) + buffer_type = HAL_BUFFER_INPUT; + else if (buffer_type == HAL_BUFFER_INTERNAL_PERSIST) + buffer_type = HAL_BUFFER_INTERNAL_PERSIST_1; + } + + list_for_each_entry(cb, &res->context_banks, list) { + if (cb->is_secure == is_secure && + cb->buffer_type & buffer_type) { + match = cb; + break; + } + } + if (!match) + dprintk(VIDC_ERR, + "%s: cb not found for buffer_type %x, is_secure %d\n", + __func__, buffer_type, is_secure); + + return match; +} + diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_private.c b/drivers/media/platform/msm/vidc/msm_v4l2_private.c new file mode 100644 index 000000000000..c9400a08a4e3 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_v4l2_private.c @@ -0,0 +1,234 @@ +/* Copyright (c) 2018-2019, 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 "msm_v4l2_private.h" + +static int convert_from_user(struct msm_vidc_arg *kp, unsigned long arg) +{ + int rc = 0; + int i; + struct msm_vidc_arg __user *up = (struct msm_vidc_arg __user *)arg; + + if (!kp || !up) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + if (get_user(kp->type, &up->type)) + return -EFAULT; + + switch (kp->type) { + case MSM_CVP_GET_SESSION_INFO: + { + struct msm_cvp_session_info *k, *u; + + k = &kp->data.session; + u = &up->data.session; + if (get_user(k->session_id, &u->session_id)) + return -EFAULT; + for (i = 0; i < 10; i++) + if (get_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_REQUEST_POWER: + { + struct msm_cvp_request_power *k, *u; + + k = &kp->data.req_power; + u = &up->data.req_power; + if (get_user(k->clock_cycles_a, &u->clock_cycles_a) || + get_user(k->clock_cycles_b, &u->clock_cycles_b) || + get_user(k->ddr_bw, &u->ddr_bw) || + get_user(k->sys_cache_bw, &u->sys_cache_bw)) + return -EFAULT; + for (i = 0; i < 8; i++) + if (get_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_REGISTER_BUFFER: + { + struct msm_cvp_buffer *k, *u; + + k = &kp->data.regbuf; + u = &up->data.regbuf; + if (get_user(k->type, &u->type) || + get_user(k->index, &u->index) || + get_user(k->fd, &u->fd) || + get_user(k->size, &u->size) || + get_user(k->offset, &u->offset) || + get_user(k->pixelformat, &u->pixelformat) || + get_user(k->flags, &u->flags)) + return -EFAULT; + for (i = 0; i < 5; i++) + if (get_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_UNREGISTER_BUFFER: + { + struct msm_cvp_buffer *k, *u; + + k = &kp->data.unregbuf; + u = &up->data.unregbuf; + if (get_user(k->type, &u->type) || + get_user(k->index, &u->index) || + get_user(k->fd, &u->fd) || + get_user(k->size, &u->size) || + get_user(k->offset, &u->offset) || + get_user(k->pixelformat, &u->pixelformat) || + get_user(k->flags, &u->flags)) + return -EFAULT; + for (i = 0; i < 5; i++) + if (get_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + default: + dprintk(VIDC_ERR, "%s: unknown cmd type 0x%x\n", + __func__, kp->type); + rc = -EINVAL; + break; + } + + return rc; +} + +static int convert_to_user(struct msm_vidc_arg *kp, unsigned long arg) +{ + int rc = 0; + int i; + struct msm_vidc_arg __user *up = (struct msm_vidc_arg __user *)arg; + + if (!kp || !up) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + if (put_user(kp->type, &up->type)) + return -EFAULT; + + switch (kp->type) { + case MSM_CVP_GET_SESSION_INFO: + { + struct msm_cvp_session_info *k, *u; + + k = &kp->data.session; + u = &up->data.session; + if (put_user(k->session_id, &u->session_id)) + return -EFAULT; + for (i = 0; i < 10; i++) + if (put_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_REQUEST_POWER: + { + struct msm_cvp_request_power *k, *u; + + k = &kp->data.req_power; + u = &up->data.req_power; + if (put_user(k->clock_cycles_a, &u->clock_cycles_a) || + put_user(k->clock_cycles_b, &u->clock_cycles_b) || + put_user(k->ddr_bw, &u->ddr_bw) || + put_user(k->sys_cache_bw, &u->sys_cache_bw)) + return -EFAULT; + for (i = 0; i < 8; i++) + if (put_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_REGISTER_BUFFER: + { + struct msm_cvp_buffer *k, *u; + + k = &kp->data.regbuf; + u = &up->data.regbuf; + if (put_user(k->type, &u->type) || + put_user(k->index, &u->index) || + put_user(k->fd, &u->fd) || + put_user(k->size, &u->size) || + put_user(k->offset, &u->offset) || + put_user(k->pixelformat, &u->pixelformat) || + put_user(k->flags, &u->flags)) + return -EFAULT; + for (i = 0; i < 5; i++) + if (put_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + case MSM_CVP_UNREGISTER_BUFFER: + { + struct msm_cvp_buffer *k, *u; + + k = &kp->data.unregbuf; + u = &up->data.unregbuf; + if (put_user(k->type, &u->type) || + put_user(k->index, &u->index) || + put_user(k->fd, &u->fd) || + put_user(k->size, &u->size) || + put_user(k->offset, &u->offset) || + put_user(k->pixelformat, &u->pixelformat) || + put_user(k->flags, &u->flags)) + return -EFAULT; + for (i = 0; i < 5; i++) + if (put_user(k->reserved[i], &u->reserved[i])) + return -EFAULT; + break; + } + default: + dprintk(VIDC_ERR, "%s: unknown cmd type 0x%x\n", + __func__, kp->type); + rc = -EINVAL; + break; + } + + return rc; +} + +long msm_v4l2_private(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int rc; + struct msm_vidc_inst *inst; + struct msm_vidc_arg karg; + + if (!filp || !filp->private_data) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + inst = container_of(filp->private_data, struct msm_vidc_inst, + event_handler); + memset(&karg, 0, sizeof(struct msm_vidc_arg)); + + /* + * the arg points to user space memory and needs + * to be converted to kernel space before using it. + * Check do_video_ioctl() for more details. + */ + if (convert_from_user(&karg, arg)) + return -EFAULT; + + rc = msm_vidc_private((void *)inst, cmd, &karg); + if (rc) { + dprintk(VIDC_ERR, "%s: failed cmd type %x\n", + __func__, karg.type); + return -EINVAL; + } + + if (convert_to_user(&karg, arg)) + return -EFAULT; + + return rc; +} diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_private.h b/drivers/media/platform/msm/vidc/msm_v4l2_private.h new file mode 100644 index 000000000000..fa4472b527e4 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_v4l2_private.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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_V4L2_PRIVATE_H_ +#define _MSM_V4L2_PRIVATE_H_ + +#include +#include "msm_vidc_debug.h" + +long msm_v4l2_private(struct file *file, unsigned int cmd, unsigned long arg); + +#endif diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c new file mode 100644 index 000000000000..99e790891836 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -0,0 +1,919 @@ +/* Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc.h" +#include "msm_vidc_common.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_internal.h" +#include "msm_vidc_res_parse.h" +#include "msm_vidc_resources.h" +#include "vidc_hfi_api.h" +#include "msm_v4l2_private.h" +#include "msm_vidc_clocks.h" +#include + +#define BASE_DEVICE_NUMBER 32 + +struct msm_vidc_drv *vidc_driver; + + +static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh) +{ + if (!filp->private_data) + return NULL; + return container_of(filp->private_data, + struct msm_vidc_inst, event_handler); +} + +static int msm_v4l2_open(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct msm_video_device *vid_dev = + container_of(vdev, struct msm_video_device, vdev); + struct msm_vidc_core *core = video_drvdata(filp); + struct msm_vidc_inst *vidc_inst; + + trace_msm_v4l2_vidc_open_start("msm v4l2_open start"); + vidc_inst = msm_vidc_open(core->id, vid_dev->type); + if (!vidc_inst) { + dprintk(VIDC_ERR, + "Failed to create video instance, core: %d, type = %d\n", + core->id, vid_dev->type); + return -ENOMEM; + } + clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags); + filp->private_data = &(vidc_inst->event_handler); + trace_msm_v4l2_vidc_open_end("msm v4l2_open end"); + return 0; +} + +static int msm_v4l2_close(struct file *filp) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst; + + trace_msm_v4l2_vidc_close_start("msm v4l2_close start"); + vidc_inst = get_vidc_inst(filp, NULL); + + rc = msm_vidc_close(vidc_inst); + filp->private_data = NULL; + trace_msm_v4l2_vidc_close_end("msm v4l2_close end"); + return rc; +} + +static int msm_v4l2_querycap(struct file *filp, void *fh, + struct v4l2_capability *cap) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, fh); + + return msm_vidc_querycap((void *)vidc_inst, cap); +} + +int msm_v4l2_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_enum_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_s_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_s_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_s_ext_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_ext_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_g_ext_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_ext_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_reqbufs((void *)vidc_inst, b); +} + +int msm_v4l2_qbuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + return msm_vidc_qbuf(get_vidc_inst(file, fh), b); +} + +int msm_v4l2_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + return msm_vidc_dqbuf(get_vidc_inst(file, fh), b); +} + +int msm_v4l2_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_streamon((void *)vidc_inst, i); +} + +int msm_v4l2_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_streamoff((void *)vidc_inst, i); +} + +static int msm_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_vidc_inst *vidc_inst = container_of(fh, + struct msm_vidc_inst, event_handler); + + return msm_vidc_subscribe_event((void *)vidc_inst, sub); +} + +static int msm_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_vidc_inst *vidc_inst = container_of(fh, + struct msm_vidc_inst, event_handler); + + return msm_vidc_unsubscribe_event((void *)vidc_inst, sub); +} + +static int msm_v4l2_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dec) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)dec); +} + +static int msm_v4l2_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)enc); +} +static int msm_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_s_parm(vidc_inst, a); +} +static int msm_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + return 0; +} + +static int msm_v4l2_g_crop(struct file *file, void *fh, + struct v4l2_crop *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_crop(vidc_inst, a); +} + +static int msm_v4l2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_enum_framesizes((void *)vidc_inst, fsize); +} + +static int msm_v4l2_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_query_ctrl((void *)vidc_inst, ctrl); +} + +static long msm_v4l2_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_private((void *)vidc_inst, cmd, arg); +} + +static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { + .vidioc_querycap = msm_v4l2_querycap, + .vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = msm_v4l2_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = msm_v4l2_s_fmt, + .vidioc_s_fmt_vid_out_mplane = msm_v4l2_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = msm_v4l2_g_fmt, + .vidioc_g_fmt_vid_out_mplane = msm_v4l2_g_fmt, + .vidioc_reqbufs = msm_v4l2_reqbufs, + .vidioc_qbuf = msm_v4l2_qbuf, + .vidioc_dqbuf = msm_v4l2_dqbuf, + .vidioc_streamon = msm_v4l2_streamon, + .vidioc_streamoff = msm_v4l2_streamoff, + .vidioc_s_ctrl = msm_v4l2_s_ctrl, + .vidioc_g_ctrl = msm_v4l2_g_ctrl, + .vidioc_queryctrl = msm_v4l2_queryctrl, + .vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl, + .vidioc_g_ext_ctrls = msm_v4l2_g_ext_ctrl, + .vidioc_subscribe_event = msm_v4l2_subscribe_event, + .vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event, + .vidioc_decoder_cmd = msm_v4l2_decoder_cmd, + .vidioc_encoder_cmd = msm_v4l2_encoder_cmd, + .vidioc_s_parm = msm_v4l2_s_parm, + .vidioc_g_parm = msm_v4l2_g_parm, + .vidioc_g_crop = msm_v4l2_g_crop, + .vidioc_enum_framesizes = msm_v4l2_enum_framesizes, + .vidioc_default = msm_v4l2_default, +}; + +static const struct v4l2_ioctl_ops msm_v4l2_enc_ioctl_ops = { +}; + +static unsigned int msm_v4l2_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, NULL); + + return msm_vidc_poll((void *)vidc_inst, filp, pt); +} + +static const struct v4l2_file_operations msm_v4l2_vidc_fops = { + .owner = THIS_MODULE, + .open = msm_v4l2_open, + .release = msm_v4l2_close, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = msm_v4l2_private, +#endif + .poll = msm_v4l2_poll, +}; + +void msm_vidc_release_video_device(struct video_device *pvdev) +{ +} + +static int read_platform_resources(struct msm_vidc_core *core, + struct platform_device *pdev) +{ + int rc = 0; + + if (!core || !pdev) { + dprintk(VIDC_ERR, "%s: Invalid params %pK %pK\n", + __func__, core, pdev); + return -EINVAL; + } + + core->hfi_type = VIDC_HFI_VENUS; + core->resources.pdev = pdev; + if (pdev->dev.of_node) { + /* Target supports DT, parse from it */ + rc = read_platform_resources_from_drv_data(core); + rc = read_platform_resources_from_dt(&core->resources); + } else { + dprintk(VIDC_ERR, "pdev node is NULL\n"); + rc = -EINVAL; + } + return rc; +} + +static int msm_vidc_initialize_core(struct platform_device *pdev, + struct msm_vidc_core *core) +{ + int i = 0; + int rc = 0; + + if (!core) + return -EINVAL; + rc = read_platform_resources(core, pdev); + if (rc) { + dprintk(VIDC_ERR, "Failed to get platform resources\n"); + return rc; + } + + INIT_LIST_HEAD(&core->instances); + mutex_init(&core->lock); + + core->state = VIDC_CORE_UNINIT; + for (i = SYS_MSG_INDEX(SYS_MSG_START); + i <= SYS_MSG_INDEX(SYS_MSG_END); i++) { + init_completion(&core->completions[i]); + } + + INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler); + INIT_WORK(&core->ssr_work, msm_vidc_ssr_handler); + + msm_vidc_init_core_clk_ops(core); + return rc; +} + +static ssize_t msm_vidc_link_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct msm_vidc_core *core = dev_get_drvdata(dev); + + if (core) + if (dev == &core->vdev[MSM_VIDC_DECODER].vdev.dev) + return snprintf(buf, PAGE_SIZE, "venus_dec"); + else if (dev == &core->vdev[MSM_VIDC_ENCODER].vdev.dev) + return snprintf(buf, PAGE_SIZE, "venus_enc"); + else if (dev == &core->vdev[MSM_VIDC_CVP].vdev.dev) + return snprintf(buf, PAGE_SIZE, "venus_cvp"); + else + return 0; + else + return 0; +} + +static DEVICE_ATTR(link_name, 0444, msm_vidc_link_name_show, NULL); + +static ssize_t store_pwr_collapse_delay(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val = 0; + int rc = 0; + struct msm_vidc_core *core = NULL; + + rc = kstrtoul(buf, 0, &val); + if (rc) + return rc; + else if (!val) + return -EINVAL; + + core = get_vidc_core(MSM_VIDC_CORE_VENUS); + if (!core) + return -EINVAL; + core->resources.msm_vidc_pwr_collapse_delay = val; + return count; +} + +static ssize_t show_pwr_collapse_delay(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct msm_vidc_core *core = NULL; + + core = get_vidc_core(MSM_VIDC_CORE_VENUS); + if (!core) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%u\n", + core->resources.msm_vidc_pwr_collapse_delay); +} + +static DEVICE_ATTR(pwr_collapse_delay, 0644, show_pwr_collapse_delay, + store_pwr_collapse_delay); + +static ssize_t show_thermal_level(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", vidc_driver->thermal_level); +} + +static ssize_t store_thermal_level(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0, val = 0; + + rc = kstrtoint(buf, 0, &val); + if (rc || val < 0) { + dprintk(VIDC_WARN, + "Invalid thermal level value: %s\n", buf); + return -EINVAL; + } + dprintk(VIDC_DBG, "Thermal level old %d new %d\n", + vidc_driver->thermal_level, val); + + if (val == vidc_driver->thermal_level) + return count; + vidc_driver->thermal_level = val; + + msm_comm_handle_thermal_event(); + return count; +} + +static DEVICE_ATTR(thermal_level, 0644, show_thermal_level, + store_thermal_level); + +static ssize_t show_sku_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", + vidc_driver->sku_version); +} + +static ssize_t store_sku_version(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + dprintk(VIDC_WARN, "store platform version is not allowed\n"); + return count; +} + +static DEVICE_ATTR(sku_version, 0444, show_sku_version, + store_sku_version); + +static struct attribute *msm_vidc_core_attrs[] = { + &dev_attr_pwr_collapse_delay.attr, + &dev_attr_thermal_level.attr, + &dev_attr_sku_version.attr, + NULL +}; + +static struct attribute_group msm_vidc_core_attr_group = { + .attrs = msm_vidc_core_attrs, +}; + +static const struct of_device_id msm_vidc_dt_match[] = { + {.compatible = "qcom,msm-vidc"}, + {.compatible = "qcom,msm-vidc,context-bank"}, + {.compatible = "qcom,msm-vidc,bus"}, + {.compatible = "qcom,msm-vidc,mem-cdsp"}, + {} +}; +static int msm_vidc_register_video_device(enum session_type sess_type, + int nr, struct msm_vidc_core *core, struct device *dev) +{ + int rc = 0; + + core->vdev[sess_type].vdev.release = + msm_vidc_release_video_device; + core->vdev[sess_type].vdev.fops = &msm_v4l2_vidc_fops; + core->vdev[sess_type].vdev.ioctl_ops = &msm_v4l2_ioctl_ops; + core->vdev[sess_type].vdev.vfl_dir = VFL_DIR_M2M; + core->vdev[sess_type].type = sess_type; + core->vdev[sess_type].vdev.v4l2_dev = &core->v4l2_dev; + rc = video_register_device(&core->vdev[sess_type].vdev, + VFL_TYPE_GRABBER, nr); + if (rc) { + dprintk(VIDC_ERR, "Failed to register the video device\n"); + return rc; + } + video_set_drvdata(&core->vdev[sess_type].vdev, core); + dev = &core->vdev[sess_type].vdev.dev; + rc = device_create_file(dev, &dev_attr_link_name); + if (rc) { + dprintk(VIDC_ERR, "Failed to create video device file\n"); + video_unregister_device(&core->vdev[sess_type].vdev); + return rc; + } + return 0; +} +static int msm_vidc_probe_vidc_device(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_core *core; + struct device *dev; + int nr = BASE_DEVICE_NUMBER; + + if (!vidc_driver) { + dprintk(VIDC_ERR, "Invalid vidc driver\n"); + return -EINVAL; + } + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->platform_data = vidc_get_drv_data(&pdev->dev); + dev_set_drvdata(&pdev->dev, core); + rc = msm_vidc_initialize_core(pdev, core); + if (rc) { + dprintk(VIDC_ERR, "Failed to init core\n"); + goto err_core_init; + } + rc = sysfs_create_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); + if (rc) { + dprintk(VIDC_ERR, + "Failed to create attributes\n"); + goto err_core_init; + } + + core->id = MSM_VIDC_CORE_VENUS; + + rc = v4l2_device_register(&pdev->dev, &core->v4l2_dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to register v4l2 device\n"); + goto err_v4l2_register; + } + + /* setup the decoder device */ + rc = msm_vidc_register_video_device(MSM_VIDC_DECODER, + nr, core, dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to register video decoder\n"); + goto err_dec; + } + + /* setup the encoder device */ + rc = msm_vidc_register_video_device(MSM_VIDC_ENCODER, + nr + 1, core, dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to register video encoder\n"); + goto err_enc; + } + + /* setup the cvp device */ + if (core->resources.domain_cvp) { + rc = msm_vidc_register_video_device(MSM_VIDC_CVP, + nr + 2, core, dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to register video CVP\n"); + goto err_cvp; + } + } + + /* finish setting up the 'core' */ + mutex_lock(&vidc_driver->lock); + if (vidc_driver->num_cores + 1 > MSM_VIDC_CORES_MAX) { + mutex_unlock(&vidc_driver->lock); + dprintk(VIDC_ERR, "Maximum cores already exist, core_no = %d\n", + vidc_driver->num_cores); + goto err_cores_exceeded; + } + vidc_driver->num_cores++; + mutex_unlock(&vidc_driver->lock); + + core->device = vidc_hfi_initialize(core->hfi_type, core->id, + &core->resources, &handle_cmd_response); + if (IS_ERR_OR_NULL(core->device)) { + mutex_lock(&vidc_driver->lock); + vidc_driver->num_cores--; + mutex_unlock(&vidc_driver->lock); + + rc = PTR_ERR(core->device) ?: -EBADHANDLE; + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "Failed to create HFI device\n"); + else + dprintk(VIDC_DBG, "msm_vidc: request probe defer\n"); + goto err_cores_exceeded; + } + + mutex_lock(&vidc_driver->lock); + list_add_tail(&core->list, &vidc_driver->cores); + mutex_unlock(&vidc_driver->lock); + + core->debugfs_root = msm_vidc_debugfs_init_core( + core, vidc_driver->debugfs_root); + + vidc_driver->sku_version = core->resources.sku_version; + + dprintk(VIDC_DBG, "populating sub devices\n"); + /* + * Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank. + * When msm_vidc_probe is called for each sub-device, parse the + * context-bank details and store it in core->resources.context_banks + * list. + */ + rc = of_platform_populate(pdev->dev.of_node, msm_vidc_dt_match, NULL, + &pdev->dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to trigger probe for sub-devices\n"); + goto err_fail_sub_device_probe; + } + + return rc; + +err_fail_sub_device_probe: + vidc_hfi_deinitialize(core->hfi_type, core->device); +err_cores_exceeded: + if (core->resources.domain_cvp) { + device_remove_file(&core->vdev[MSM_VIDC_CVP].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_CVP].vdev); + } +err_cvp: + device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev); +err_enc: + device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev); +err_dec: + v4l2_device_unregister(&core->v4l2_dev); +err_v4l2_register: + sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); +err_core_init: + dev_set_drvdata(&pdev->dev, NULL); + kfree(core); + return rc; +} + +static int msm_vidc_probe_mem_cdsp(struct platform_device *pdev) +{ + return read_mem_cdsp_resources_from_dt(pdev); +} + +static int msm_vidc_probe_context_bank(struct platform_device *pdev) +{ + return read_context_bank_resources_from_dt(pdev); +} + +static int msm_vidc_probe_bus(struct platform_device *pdev) +{ + return read_bus_resources_from_dt(pdev); +} + +static int msm_vidc_probe(struct platform_device *pdev) +{ + /* + * Sub devices probe will be triggered by of_platform_populate() towards + * the end of the probe function after msm-vidc device probe is + * completed. Return immediately after completing sub-device probe. + */ + if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-vidc")) { + return msm_vidc_probe_vidc_device(pdev); + } else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,msm-vidc,bus")) { + return msm_vidc_probe_bus(pdev); + } else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,msm-vidc,context-bank")) { + return msm_vidc_probe_context_bank(pdev); + } else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,msm-vidc,mem-cdsp")) { + return msm_vidc_probe_mem_cdsp(pdev); + } + + /* How did we end up here? */ + MSM_VIDC_ERROR(1); + return -EINVAL; +} + +static int msm_vidc_remove(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "%s invalid input %pK", __func__, pdev); + return -EINVAL; + } + + core = dev_get_drvdata(&pdev->dev); + if (!core) { + dprintk(VIDC_ERR, "%s invalid core", __func__); + return -EINVAL; + } + + vidc_hfi_deinitialize(core->hfi_type, core->device); + if (core->resources.domain_cvp) { + device_remove_file(&core->vdev[MSM_VIDC_CVP].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_CVP].vdev); + } + device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev); + device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev); + v4l2_device_unregister(&core->v4l2_dev); + + msm_vidc_free_platform_resources(&core->resources); + sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); + dev_set_drvdata(&pdev->dev, NULL); + mutex_destroy(&core->lock); + kfree(core); + return rc; +} + +static int msm_vidc_pm_suspend(struct device *dev) +{ + int rc = 0; + struct msm_vidc_core *core; + + /* + * Bail out if + * - driver possibly not probed yet + * - not the main device. We don't support power management on + * subdevices (e.g. context banks) + */ + if (!dev || !dev->driver || + !of_device_is_compatible(dev->of_node, "qcom,msm-vidc")) + return 0; + + core = dev_get_drvdata(dev); + if (!core) { + dprintk(VIDC_ERR, "%s invalid core\n", __func__); + return -EINVAL; + } + + rc = msm_vidc_suspend(core->id); + if (rc == -ENOTSUPP) + rc = 0; + else if (rc) + dprintk(VIDC_WARN, "Failed to suspend: %d\n", rc); + + + return rc; +} + +static int msm_vidc_pm_resume(struct device *dev) +{ + dprintk(VIDC_INFO, "%s\n", __func__); + return 0; +} + +int msm_vidc_freeze_core(struct msm_vidc_core *core) +{ + int rc = 0; + int max_retry = 300; + struct hfi_device *hdev; + + hdev = core->device; + + mutex_lock(&core->lock); + + dprintk(VIDC_WARN, "%s: fatal SSR intended to dismantle vidc\n", + __func__); + + core->ssr_type = SSR_ERR_FATAL; + + if (core->state == VIDC_CORE_INIT_DONE) { + dprintk(VIDC_INFO, "%s: ssr type %d\n", __func__, + core->ssr_type); + /* + * In current implementation user-initiated SSR triggers + * a fatal error from hardware. However, there is no way + * to know if fatal error is due to SSR or not. Handle + * user SSR as non-fatal. + */ + + core->trigger_ssr = true; + rc = call_hfi_op(hdev, core_trigger_ssr, + hdev->hfi_device_data, core->ssr_type); + + if (rc) { + dprintk(VIDC_ERR, "%s: trigger_ssr failed\n", __func__); + core->trigger_ssr = false; + } + + } else { + dprintk(VIDC_WARN, "%s: video core %pK not initialized\n", + __func__, core); + } + mutex_unlock(&core->lock); + + while ((core->state != VIDC_CORE_UNINIT) && (max_retry > 0)) { + msleep(20); + max_retry--; + } + + mutex_lock(&core->lock); + core->trigger_ssr = false; + mutex_unlock(&core->lock); + + return rc; +} + +static int msm_vidc_pm_freeze(struct device *dev) +{ + int rc = 0; + struct msm_vidc_core *core; + + if (!dev || !dev->driver) + return 0; + + core = dev_get_drvdata(dev); + if (!core) + return 0; + + if (of_device_is_compatible(dev->of_node, "qcom,msm-vidc")) + rc = msm_vidc_freeze_core(core); + + dprintk(VIDC_INFO, "%s: done\n", __func__); + + return rc; +} + +static const struct dev_pm_ops msm_vidc_pm_ops = { + .suspend = msm_vidc_pm_suspend, + .resume = msm_vidc_pm_resume, + .freeze = msm_vidc_pm_freeze, +}; + +MODULE_DEVICE_TABLE(of, msm_vidc_dt_match); + +static struct platform_driver msm_vidc_driver = { + .probe = msm_vidc_probe, + .remove = msm_vidc_remove, + .driver = { + .name = "msm_vidc_v4l2", + .owner = THIS_MODULE, + .of_match_table = msm_vidc_dt_match, + .pm = &msm_vidc_pm_ops, + }, +}; + +static int __init msm_vidc_init(void) +{ + int rc = 0; + + vidc_driver = kzalloc(sizeof(*vidc_driver), + GFP_KERNEL); + if (!vidc_driver) { + dprintk(VIDC_ERR, + "Failed to allocate memroy for msm_vidc_drv\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&vidc_driver->cores); + mutex_init(&vidc_driver->lock); + vidc_driver->debugfs_root = msm_vidc_debugfs_init_drv(); + if (!vidc_driver->debugfs_root) + dprintk(VIDC_ERR, + "Failed to create debugfs for msm_vidc\n"); + + rc = platform_driver_register(&msm_vidc_driver); + if (rc) { + dprintk(VIDC_ERR, + "Failed to register platform driver\n"); + debugfs_remove_recursive(vidc_driver->debugfs_root); + kfree(vidc_driver); + vidc_driver = NULL; + } + + return rc; +} + +static void __exit msm_vidc_exit(void) +{ + platform_driver_unregister(&msm_vidc_driver); + debugfs_remove_recursive(vidc_driver->debugfs_root); + mutex_destroy(&vidc_driver->lock); + kfree(vidc_driver); + vidc_driver = NULL; +} + +module_init(msm_vidc_init); +module_exit(msm_vidc_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c new file mode 100644 index 000000000000..f0c40928aa6e --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -0,0 +1,1461 @@ +/* Copyright (c) 2012-2021, 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 +#include +#include "msm_vidc_internal.h" +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_clocks.h" + +#define MSM_VDEC_DVC_NAME "msm_vdec_8974" +#define MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS MIN_NUM_OUTPUT_BUFFERS +#define MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS MIN_NUM_CAPTURE_BUFFERS +#define MIN_NUM_DEC_OUTPUT_BUFFERS 4 +#define MIN_NUM_DEC_CAPTURE_BUFFERS 4 +// Y=16(0-9bits), Cb(10-19bits)=Cr(20-29bits)=128, black by default +#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8020010 +#define MB_SIZE_IN_PIXEL (16 * 16) +#define OPERATING_FRAME_RATE_STEP (1 << 16) +#define MAX_VP9D_INST_COUNT 6 +#define MAX_4K_MBPF 38736 /* (4096 * 2304 / 256) */ +#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4)) +#define MAX_5k_MBPF 64800 /*(5760 * 2880 / 256) */ + +static const char *const mpeg_video_stream_format[] = { + "NAL Format Start Codes", + "NAL Format One NAL Per Buffer", + "NAL Format One Byte Length", + "NAL Format Two Byte Length", + "NAL Format Four Byte Length", + NULL +}; +static const char *const mpeg_video_output_order[] = { + "Display Order", + "Decode Order", + NULL +}; +static const char *const mpeg_vidc_video_alloc_mode_type[] = { + "Buffer Allocation Static", + "Buffer Allocation Dynamic Buffer" +}; + +static const char *const perf_level[] = { + "Nominal", + "Performance", + "Turbo" +}; + +static const char *const vp8_profile_level[] = { + "Unused", + "0.0", + "1.0", + "2.0", + "3.0", +}; + +static const char *const vp9_profile[] = { + "Unused", + "0", + "2_10", +}; + +static const char *const vp9_level[] = { + "Unused", + "1.0", + "1.1", + "2.0", + "2.1", + "3.0", + "3.1", + "4.0", + "4.1", + "5.0", + "5.1", +#ifdef VDEC_VP9_LEVEL61_AVAILABLE + "6.0", + "6.1", +#endif +}; + +static const char *const mpeg2_profile[] = { + "Simple", + "Main", + "High", +}; + +static const char *const mpeg2_level[] = { + "0", + "1", + "2", + "3", +}; +static const char *const mpeg_vidc_video_entropy_mode[] = { + "CAVLC Entropy Mode", + "CABAC Entropy Mode", +}; + +static const char *const mpeg_vidc_video_dpb_color_format[] = { + "DPB Color Format None", + "DPB Color Format UBWC", + "DPB Color Format UBWC TP10", +}; + +static struct msm_vidc_ctrl msm_vdec_ctrls[] = { + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER, + .name = "Output Order", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY, + .maximum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE, + .default_value = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) | + (1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE) + ), + .qmenu = mpeg_video_output_order, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE, + .name = "Picture Type Decoding", + .type = V4L2_CTRL_TYPE_BITMASK, + .minimum = 0, + .maximum = (V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_I | + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_P | + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_B), + .default_value = (V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_I | + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_P | + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_B), + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE, + .name = "Sync Frame Decode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE, + .name = "Secure mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA, + .name = "Extradata Type", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO, + .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI) | + (1 << + V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO) + ), + .qmenu = mpeg_video_vidc_extradata, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE, + .name = "Video decoder multi stream", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY, + .maximum = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY, + .default_value = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY, + .menu_skip_mask = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .name = "H264 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .name = "H264 Level", + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, + .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .menu_skip_mask = ( + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN) + ), + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + .name = "VP8 Profile Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0, + .menu_skip_mask = 0, + .qmenu = vp8_profile_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP9_PROFILE, + .name = "VP9 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P2_10, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P0, + .menu_skip_mask = 0, + .qmenu = vp9_profile, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL, + .name = "VP9 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51, + .menu_skip_mask = 0, + .qmenu = vp9_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE, + .name = "MPEG2 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH, + .default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH) + ), + .qmenu = mpeg2_profile, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL, + .name = "MPEG2 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0, + .maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3, + .default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3) + ), + .qmenu = mpeg2_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT, + .name = "Picture concealed color 8bit", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x0, + .maximum = 0xff3fcff, + .default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT, + .name = "Picture concealed color 10bit", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x0, + .maximum = 0x3fffffff, + .default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT, + .name = "Buffer size limit", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + .name = "CAPTURE Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_NUM_CAPTURE_BUFFERS, + .maximum = MAX_NUM_CAPTURE_BUFFERS, + .default_value = MIN_NUM_CAPTURE_BUFFERS, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + { + .id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + .name = "OUTPUT Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_NUM_OUTPUT_BUFFERS, + .maximum = MAX_NUM_OUTPUT_BUFFERS, + .default_value = MIN_NUM_OUTPUT_BUFFERS, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + .name = "Entropy Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .step = 0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + ), + .qmenu = mpeg_vidc_video_entropy_mode, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, + .name = "Session Priority", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE, + .name = "Set Decoder Operating rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE, + .name = "Set Decoder Frame rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE, + .name = "Low Latency Mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, +}; + +#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls) + +static u32 get_frame_size_compressed_full_yuv(int plane, + u32 max_mbs_per_frame, u32 size_per_mb) +{ + u32 frame_size; + + if (max_mbs_per_frame > MAX_4K_MBPF) + frame_size = (max_mbs_per_frame * size_per_mb * 3 / 2) / 4; + else + frame_size = (max_mbs_per_frame * size_per_mb * 3 / 2); + + /* multiply by 10/8 (1.25) to get size for 10 bit case */ + frame_size = frame_size + (frame_size >> 2); + + return frame_size; +} + +static u32 get_frame_size_compressed(int plane, + u32 max_mbs_per_frame, u32 size_per_mb) +{ + u32 frame_size; + + if (max_mbs_per_frame > MAX_4K_MBPF) + frame_size = (max_mbs_per_frame * size_per_mb * 3 / 2) / 4; + else + frame_size = (max_mbs_per_frame * size_per_mb * 3/2)/2; + + /* multiply by 10/8 (1.25) to get size for 10 bit case */ + frame_size = frame_size + (frame_size >> 2); + + return frame_size; +} + +static u32 get_frame_size(struct msm_vidc_inst *inst, + const struct msm_vidc_format *fmt, + int fmt_type, int plane) +{ + u32 frame_size = 0, num_mbs = 0; + u32 max_mbps = 0; + + if (fmt_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.mbs_per_frame.max, + MB_SIZE_IN_PIXEL); + if (inst->flags & VIDC_SECURE) { + num_mbs = msm_vidc_get_mbs_per_frame(inst); + dprintk(VIDC_DBG, + "wxh= %dx%d num_mbs = %d max_mbpf = %d\n", + inst->prop.width[OUTPUT_PORT], + inst->prop.height[OUTPUT_PORT], + num_mbs, inst->capability.mbs_per_frame.max); + + max_mbps = inst->capability.mbs_per_frame.max; + if (num_mbs < NUM_MBS_720P && max_mbps <= MAX_5k_MBPF) + frame_size = ALIGN(frame_size, SZ_4K); + else + frame_size = ALIGN(frame_size/2, SZ_4K); + + dprintk(VIDC_DBG, + "Change secure input buffer size to %u\n", + frame_size); + } + + if (inst->buffer_size_limit && + (inst->buffer_size_limit < frame_size)) { + frame_size = inst->buffer_size_limit; + dprintk(VIDC_DBG, "input buffer size limited to %d\n", + frame_size); + } else { + dprintk(VIDC_DBG, "set input buffer size to %d\n", + frame_size); + } + } else if (fmt_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.height.max, + inst->capability.width.max); + dprintk(VIDC_DBG, "set output buffer size to %d\n", + frame_size); + } else { + dprintk(VIDC_WARN, "Wrong format type\n"); + } + return frame_size; +} + +struct msm_vidc_format vdec_formats[] = { + { + .name = "YCbCr Semiplanar 4:2:0", + .description = "Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12, + .get_frame_size = get_frame_size_nv12, + .type = CAPTURE_PORT, + }, + { + .name = "YCbCr Semiplanar 4:2:0 10bit", + .description = "Y/CbCr 4:2:0 10bit", + .fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS, + .get_frame_size = get_frame_size_p010, + .type = CAPTURE_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0", + .description = "UBWC Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12_UBWC, + .get_frame_size = get_frame_size_nv12_ubwc, + .type = CAPTURE_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0 10bit", + .description = "UBWC Y/CbCr 4:2:0 10bit", + .fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC, + .get_frame_size = get_frame_size_tp10_ubwc, + .type = CAPTURE_PORT, + }, + { + .name = "Mpeg2", + .description = "Mpeg2 compressed format", + .fourcc = V4L2_PIX_FMT_MPEG2, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + .defer_outputs = false, + .input_min_count = 4, + .output_min_count = 6, + }, + { + .name = "H264", + .description = "H264 compressed format", + .fourcc = V4L2_PIX_FMT_H264, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + .defer_outputs = false, + .input_min_count = 4, + .output_min_count = 8, + }, + { + .name = "HEVC", + .description = "HEVC compressed format", + .fourcc = V4L2_PIX_FMT_HEVC, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + .defer_outputs = false, + .input_min_count = 4, + .output_min_count = 8, + }, + { + .name = "VP8", + .description = "VP8 compressed format", + .fourcc = V4L2_PIX_FMT_VP8, + .get_frame_size = get_frame_size_compressed_full_yuv, + .type = OUTPUT_PORT, + .defer_outputs = false, + .input_min_count = 4, + .output_min_count = 6, + }, + { + .name = "VP9", + .description = "VP9 compressed format", + .fourcc = V4L2_PIX_FMT_VP9, + .get_frame_size = get_frame_size_compressed_full_yuv, + .type = OUTPUT_PORT, + .defer_outputs = true, + .input_min_count = 4, + .output_min_count = 9, + }, +}; + +struct msm_vidc_format_constraint dec_pix_format_constraints[] = { + { + .fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS, + .num_planes = 2, + .y_stride_multiples = 256, + .y_max_stride = 8192, + .y_min_plane_buffer_height_multiple = 32, + .y_buffer_alignment = 256, + .uv_stride_multiples = 256, + .uv_max_stride = 8192, + .uv_min_plane_buffer_height_multiple = 16, + .uv_buffer_alignment = 256, + }, +}; + +static bool msm_vidc_check_for_vp9d_overload(struct msm_vidc_core *core) +{ + u32 vp9d_instance_count = 0; + struct msm_vidc_inst *inst = NULL; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type == MSM_VIDC_DECODER && + inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9) + vp9d_instance_count++; + } + mutex_unlock(&core->lock); + + if (vp9d_instance_count > MAX_VP9D_INST_COUNT) + return true; + return false; +} + +int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + struct msm_vidc_format *fmt = NULL; +#ifdef VDEC_FORMAT_CONSTRAINTS_SUPPORTED + struct msm_vidc_format_constraint *fmt_constraint = NULL; +#endif + struct hal_frame_size frame_sz; + unsigned int extra_idx = 0; + int rc = 0; + int ret = 0; + int i; + int max_input_size = 0; + + if (!inst || !f) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto err_invalid_fmt; + } + + if (inst->fmts[fmt->type].fourcc == f->fmt.pix_mp.pixelformat && + inst->prop.width[CAPTURE_PORT] == f->fmt.pix_mp.width && + inst->prop.height[CAPTURE_PORT] == + f->fmt.pix_mp.height) { + dprintk(VIDC_DBG, "No change in CAPTURE port params\n"); + return 0; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width; + inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height; + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto err_invalid_fmt; + } + + msm_comm_set_color_format(inst, + msm_comm_get_hal_output_buffer(inst), + f->fmt.pix_mp.pixelformat); + +#ifdef VDEC_FORMAT_CONSTRAINTS_SUPPORTED + fmt_constraint = + msm_comm_get_pixel_fmt_constraints(dec_pix_format_constraints, + ARRAY_SIZE(dec_pix_format_constraints), + f->fmt.pix_mp.pixelformat); + + if (!fmt_constraint) { + dprintk(VIDC_INFO, + "Format constraint not required for %d on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + } else { + rc = msm_comm_set_color_format_constraints(inst, + msm_comm_get_hal_output_buffer(inst), + fmt_constraint); + if (rc) { + dprintk(VIDC_ERR, + "Set constraint for %d failed on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto err_invalid_fmt; + } + } +#endif + + inst->clk_data.opb_fourcc = f->fmt.pix_mp.pixelformat; + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + ret = msm_comm_try_set_prop(inst, + HAL_PARAM_FRAME_SIZE, &frame_sz); + } + + f->fmt.pix_mp.plane_fmt[0].sizeimage = + inst->fmts[fmt->type].get_frame_size(0, + f->fmt.pix_mp.height, f->fmt.pix_mp.width); + + extra_idx = EXTRADATA_IDX(inst->bufq[fmt->type].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + VENUS_EXTRADATA_SIZE( + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + } + + f->fmt.pix_mp.num_planes = inst->bufq[fmt->type].num_planes; + for (i = 0; i < inst->bufq[fmt->type].num_planes; i++) { + inst->bufq[CAPTURE_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), + f->fmt.pix_mp.pixelformat, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on OUTPUT port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto err_invalid_fmt; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9) { + if (msm_vidc_check_for_vp9d_overload(inst->core)) { + dprintk(VIDC_ERR, "VP9 Decode overload\n"); + rc = -ENOTSUPP; + goto err_invalid_fmt; + } + } + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + goto err_invalid_fmt; + } + + if (inst->fmts[fmt->type].fourcc == f->fmt.pix_mp.pixelformat && + inst->prop.width[OUTPUT_PORT] == f->fmt.pix_mp.width && + inst->prop.height[OUTPUT_PORT] == + f->fmt.pix_mp.height) { + dprintk(VIDC_DBG, "No change in OUTPUT port params\n"); + return 0; + } + inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; + inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto err_invalid_fmt; + } + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->prop.width[OUTPUT_PORT]; + frame_sz.height = inst->prop.height[OUTPUT_PORT]; + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); + + max_input_size = get_frame_size( + inst, &inst->fmts[fmt->type], f->type, 0); + if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size || + !f->fmt.pix_mp.plane_fmt[0].sizeimage) { + f->fmt.pix_mp.plane_fmt[0].sizeimage = max_input_size; + } + + f->fmt.pix_mp.num_planes = inst->bufq[fmt->type].num_planes; + for (i = 0; i < inst->bufq[fmt->type].num_planes; ++i) { + inst->bufq[OUTPUT_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + } +err_invalid_fmt: + return rc; +} + +int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) +{ + const struct msm_vidc_format *fmt = NULL; + int rc = 0; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, f = %pK\n", inst, f); + return -EINVAL; + } + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(vdec_formats, + ARRAY_SIZE(vdec_formats), f->index, CAPTURE_PORT); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(vdec_formats, + ARRAY_SIZE(vdec_formats), f->index, OUTPUT_PORT); + f->flags = V4L2_FMT_FLAG_COMPRESSED; + } + + memset(f->reserved, 0, sizeof(f->reserved)); + if (fmt) { + strlcpy(f->description, fmt->description, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + } else { + dprintk(VIDC_DBG, "No more formats found\n"); + rc = -EINVAL; + } + return rc; +} + +int msm_vdec_inst_init(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct msm_vidc_core *core; + struct msm_vidc_format *fmt = NULL; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "Invalid input = %pK\n", inst); + return -EINVAL; + } + core = inst->core; + inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT; + inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH; + inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT; + inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH; + inst->capability.height.min = MIN_SUPPORTED_HEIGHT; + inst->capability.height.max = DEFAULT_HEIGHT; + inst->capability.width.min = MIN_SUPPORTED_WIDTH; + inst->capability.width.max = DEFAULT_WIDTH; + inst->capability.secure_output2_threshold.min = 0; + inst->capability.secure_output2_threshold.max = 0; + inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC; + inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_DYNAMIC; + inst->stream_output_mode = HAL_VIDEO_DECODER_PRIMARY; + /* To start with, both ports are 1 plane each */ + inst->bufq[OUTPUT_PORT].num_planes = 1; + inst->bufq[CAPTURE_PORT].num_planes = 1; + inst->prop.fps = DEFAULT_FPS; + inst->clk_data.operating_rate = 0; + if (core->resources.decode_batching) { + struct msm_vidc_inst *temp; + + inst->batch.size = MAX_DEC_BATCH_SIZE; + inst->decode_batching = true; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp != inst && + temp->state != MSM_VIDC_CORE_INVALID && + is_decode_session(temp) && + !is_thumbnail_session(temp)) { + inst->decode_batching = false; + dprintk(VIDC_DBG, + "decode-batching disabled in multiple sessions\n"); + break; + } + } + mutex_unlock(&core->lock); + } + + /* By default, initialize CAPTURE port to UBWC YUV format */ + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), V4L2_PIX_FMT_NV12_UBWC, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "vdec_formats corrupted\n"); + return -EINVAL; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + inst->buff_req.buffer[1].buffer_type = HAL_BUFFER_INPUT; + inst->buff_req.buffer[1].buffer_count_min_host = + inst->buff_req.buffer[1].buffer_count_actual = + MIN_NUM_DEC_OUTPUT_BUFFERS; + inst->buff_req.buffer[2].buffer_type = HAL_BUFFER_OUTPUT; + inst->buff_req.buffer[2].buffer_count_min_host = + inst->buff_req.buffer[2].buffer_count_actual = + MIN_NUM_DEC_CAPTURE_BUFFERS; + inst->buff_req.buffer[3].buffer_type = HAL_BUFFER_OUTPUT2; + inst->buff_req.buffer[3].buffer_count_min_host = + inst->buff_req.buffer[3].buffer_count_actual = + MIN_NUM_DEC_CAPTURE_BUFFERS; + inst->buff_req.buffer[4].buffer_type = HAL_BUFFER_EXTRADATA_INPUT; + inst->buff_req.buffer[5].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT; + inst->buff_req.buffer[6].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT2; + inst->buff_req.buffer[7].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH; + inst->buff_req.buffer[8].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_1; + inst->buff_req.buffer[9].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_2; + inst->buff_req.buffer[10].buffer_type = HAL_BUFFER_INTERNAL_PERSIST; + inst->buff_req.buffer[11].buffer_type = HAL_BUFFER_INTERNAL_PERSIST_1; + inst->buff_req.buffer[12].buffer_type = HAL_BUFFER_INTERNAL_CMD_QUEUE; + inst->buff_req.buffer[13].buffer_type = HAL_BUFFER_INTERNAL_RECON; + + /* By default, initialize OUTPUT port to H264 decoder */ + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), V4L2_PIX_FMT_H264, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "vdec_formats corrupted\n"); + return -EINVAL; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + return rc; +} + +static struct v4l2_ctrl *get_ctrl_from_cluster(int id, + struct v4l2_ctrl **cluster, int ncontrols) +{ + int c; + + for (c = 0; c < ncontrols; ++c) + if (cluster[c]->id == id) + return cluster[c]; + return NULL; +} + +int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0, fourcc = 0; + struct hal_enable_picture enable_picture; + struct hal_enable hal_property; + enum hal_property property_id = 0; + u32 property_val = 0; + void *pdata = NULL; + struct hfi_device *hdev; + struct hal_extradata_enable extra; + struct hal_multi_stream multi_stream; + struct v4l2_ctrl *temp_ctrl = NULL; + struct hal_profile_level profile_level; + struct hal_frame_size frame_sz; + struct hal_buffer_requirements *bufreq; + struct hal_buffer_requirements *bufreq_out2; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + /* Small helper macro for quickly getting a control and err checking */ +#define TRY_GET_CTRL(__ctrl_id) ({ \ + struct v4l2_ctrl *__temp; \ + __temp = get_ctrl_from_cluster( \ + __ctrl_id, \ + ctrl->cluster, ctrl->ncontrols); \ + if (!__temp) { \ + dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \ + #__ctrl_id, __ctrl_id); \ + /* Clusters are hardcoded, if we can't find */ \ + /* something then things are massively screwed up */ \ + MSM_VIDC_ERROR(1); \ + } \ + __temp; \ + }) + + v4l2_ctrl_unlock(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER: + property_id = HAL_PARAM_VDEC_OUTPUT_ORDER; + property_val = ctrl->val; + pdata = &property_val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE: + property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE; + enable_picture.picture_type = ctrl->val; + pdata = &enable_picture; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE: + switch (ctrl->val) { + case V4L2_MPEG_MSM_VIDC_DISABLE: + inst->flags &= ~VIDC_THUMBNAIL; + break; + case V4L2_MPEG_MSM_VIDC_ENABLE: + inst->flags |= VIDC_THUMBNAIL; + break; + } + + property_id = HAL_PARAM_VDEC_SYNC_FRAME_DECODE; + hal_property.enable = ctrl->val; + pdata = &hal_property; + msm_dcvs_try_enable(inst); + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_INPUT); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements : %x\n", + HAL_BUFFER_OUTPUT); + return -EINVAL; + } + bufreq->buffer_count_min = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + bufreq->buffer_count_min_host = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + bufreq->buffer_count_actual = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements: %x\n", + HAL_BUFFER_OUTPUT); + return -EINVAL; + } + + bufreq->buffer_count_min = + MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS; + bufreq->buffer_count_min_host = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + bufreq->buffer_count_actual = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT2); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements: %x\n", + HAL_BUFFER_OUTPUT2); + return -EINVAL; + } + + bufreq->buffer_count_min = + MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS; + bufreq->buffer_count_min_host = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + bufreq->buffer_count_actual = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + + } else { + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements: %x\n", + HAL_BUFFER_OUTPUT); + return -EINVAL; + } + bufreq->buffer_count_min = + MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS; + bufreq->buffer_count_min_host = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + bufreq->buffer_count_actual = + MIN_NUM_THUMBNAIL_MODE_OUTPUT_BUFFERS; + + } + + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE: + property_id = HAL_PARAM_SECURE; + inst->flags |= VIDC_SECURE; + property_val = !!(inst->flags & VIDC_SECURE); + pdata = &property_val; + dprintk(VIDC_DBG, "Setting secure mode to: %d\n", + !!(inst->flags & VIDC_SECURE)); + if (msm_comm_check_for_inst_overload(inst->core)) { + dprintk(VIDC_ERR, + "Secure Instance reached Max limit, rejecting session\n"); + return -ENOTSUPP; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA: + property_id = HAL_PARAM_INDEX_EXTRADATA; + extra.index = msm_comm_get_hal_extradata_index(ctrl->val); + switch (ctrl->val) { + case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO: + case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP: + case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING: + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE: + case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW: + case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI: + case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP: + case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA: + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP: + case V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP: + case V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI: + case V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI: + case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY: + case V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE: + case V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO: + inst->bufq[CAPTURE_PORT].num_planes = 2; + inst->bufq[CAPTURE_PORT].plane_sizes[EXTRADATA_IDX(2)] = + VENUS_EXTRADATA_SIZE( + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + break; + default: + rc = -ENOTSUPP; + break; + } + extra.enable = 1; + pdata = &extra; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE: + if (ctrl->val && !(inst->capability.pixelprocess_capabilities & + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) { + dprintk(VIDC_ERR, "Downscaling not supported: %#x\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY: + /* Release DPBs if it was previously split mode */ + rc = msm_comm_release_output_buffers(inst, false); + if (rc) + dprintk(VIDC_ERR, + "%s Release output buffers failed\n", + __func__); + + multi_stream.buffer_type = HAL_BUFFER_OUTPUT; + multi_stream.enable = true; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed : Enabling OUTPUT port : %d\n", + rc); + break; + } + multi_stream.buffer_type = HAL_BUFFER_OUTPUT2; + multi_stream.enable = false; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed:Disabling OUTPUT2 port : %d\n", + rc); + break; + } + /* + * If stream output mode was secondary earlier then + * populate output bufreqs with output2 bufreqs + */ + if (is_secondary_output_mode(inst)) { + msm_comm_copy_bufreqs(inst, HAL_BUFFER_OUTPUT2, + HAL_BUFFER_OUTPUT); + msm_comm_copy_bufreqs(inst, + HAL_BUFFER_EXTRADATA_OUTPUT2, + HAL_BUFFER_EXTRADATA_OUTPUT); + } + + /* reset output2 buffer requirements */ + msm_comm_reset_bufreqs(inst, HAL_BUFFER_OUTPUT2); + msm_comm_reset_bufreqs(inst, + HAL_BUFFER_EXTRADATA_OUTPUT2); + + msm_comm_set_stream_output_mode(inst, + HAL_VIDEO_DECODER_PRIMARY); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY: + switch (inst->bit_depth) { + case MSM_VIDC_BIT_DEPTH_8: + fourcc = V4L2_PIX_FMT_NV12_UBWC; + break; + case MSM_VIDC_BIT_DEPTH_10: + fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC; + break; + default: + fourcc = V4L2_PIX_FMT_NV12_UBWC; + dprintk(VIDC_ERR, + "Invalid bit depth. Setting DPB as NV12UBWC"); + break; + } + + rc = msm_comm_set_color_format(inst, + HAL_BUFFER_OUTPUT, fourcc); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting output color format : %d\n", + __func__, rc); + break; + } + inst->clk_data.dpb_fourcc = fourcc; + + multi_stream.buffer_type = HAL_BUFFER_OUTPUT2; + multi_stream.enable = true; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed :Enabling OUTPUT2 port : %d\n", + rc); + break; + } + + multi_stream.buffer_type = HAL_BUFFER_OUTPUT; + multi_stream.enable = false; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed disabling OUTPUT port : %d\n", + rc); + break; + } + + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + pdata = &frame_sz; + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed setting OUTPUT2 size : %d\n", + rc); + break; + } + + /* Populate output2 bufreqs with output bufreqs */ + msm_comm_copy_bufreqs(inst, HAL_BUFFER_OUTPUT, + HAL_BUFFER_OUTPUT2); + msm_comm_copy_bufreqs(inst, + HAL_BUFFER_EXTRADATA_OUTPUT, + HAL_BUFFER_EXTRADATA_OUTPUT2); + + bufreq_out2 = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT2); + if (!bufreq_out2) + break; + + rc = msm_comm_set_buffer_count(inst, + bufreq_out2->buffer_count_min, + bufreq_out2->buffer_count_actual, + HAL_BUFFER_OUTPUT2); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set opb buffer count to FW\n", + __func__); + break; + } + + msm_comm_set_stream_output_mode(inst, + HAL_VIDEO_DECODER_SECONDARY); + break; + default: + dprintk(VIDC_ERR, + "Failed : Unsupported multi stream setting\n"); + rc = -ENOTSUPP; + break; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + property_id = + HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + property_id = + HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT: + dprintk(VIDC_DBG, + "Limiting input buffer size from %u to %u\n", + inst->buffer_size_limit, ctrl->val); + inst->buffer_size_limit = ctrl->val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: + property_id = HAL_CONFIG_REALTIME; + hal_property.enable = ctrl->val; + pdata = &hal_property; + switch (ctrl->val) { + case V4L2_MPEG_MSM_VIDC_DISABLE: + inst->flags &= ~VIDC_REALTIME; + break; + case V4L2_MPEG_MSM_VIDC_ENABLE: + inst->flags |= VIDC_REALTIME; + break; + default: + dprintk(VIDC_WARN, + "inst(%pK) invalid priority ctrl value %#x\n", + inst, ctrl->val); + break; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE: + if (((ctrl->val >> 16) < inst->capability.frame_rate.min || + (ctrl->val >> 16) > inst->capability.frame_rate.max) && + ctrl->val != INT_MAX) { + if (!is_realtime_session(inst)) { + if ((ctrl->val >> 16) < + inst->capability.frame_rate.min) { + inst->clk_data.operating_rate = + inst->capability.frame_rate.min << 16; + } else { + inst->clk_data.operating_rate = + inst->capability.frame_rate.max << 16; + } + dprintk(VIDC_DBG, + "inst(%pK) operating rate capped from %d to %d\n", + inst, ctrl->val >> 16, + inst->clk_data.operating_rate >> 16); + inst->operating_rate_set = true; + } else { + dprintk(VIDC_ERR, "Invalid operating rate %u\n", + (ctrl->val >> 16)); + rc = -ENOTSUPP; + } + } else if (ctrl->val == INT_MAX) { + dprintk(VIDC_DBG, + "inst(%pK) Request for turbo mode\n", inst); + inst->clk_data.turbo_mode = true; + inst->operating_rate_set = true; + } else if (msm_vidc_validate_operating_rate(inst, ctrl->val)) { + dprintk(VIDC_ERR, "Failed to set operating rate\n"); + rc = -ENOTSUPP; + } else { + dprintk(VIDC_DBG, + "inst(%pK) operating rate changed from %d to %d\n", + inst, inst->clk_data.operating_rate >> 16, + ctrl->val >> 16); + inst->clk_data.operating_rate = ctrl->val; + inst->operating_rate_set = true; + inst->clk_data.turbo_mode = false; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE: + if (ctrl->val == V4L2_MPEG_MSM_VIDC_ENABLE) + hal_property.enable = 1; + else + hal_property.enable = 0; + inst->clk_data.low_latency_mode = (bool) hal_property.enable; + break; + default: + break; + } + + v4l2_ctrl_lock(ctrl); +#undef TRY_GET_CTRL + + if (!rc && property_id) { + dprintk(VIDC_DBG, + "Control: %x : Name = %s, ID = 0x%x Value = %d\n", + hash32_ptr(inst->session), ctrl->name, + ctrl->id, ctrl->val); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, property_id, pdata); + } + + return rc; +} + +int msm_vdec_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0, i = 0; + struct v4l2_ext_control *ext_control; + struct v4l2_control control; + struct hal_conceal_color conceal_color = {0}; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device || !ctrl) { + dprintk(VIDC_ERR, + "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + v4l2_try_ext_ctrls(&inst->ctrl_handler, ctrl); + + ext_control = ctrl->controls; + + for (i = 0; i < ctrl->count; i++) { + switch (ext_control[i].id) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE: + control.value = ext_control[i].value; + control.id = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE; + rc = msm_comm_s_ctrl(inst, &control); + if (rc) + dprintk(VIDC_ERR, + "%s Failed setting stream output mode : %d\n", + __func__, rc); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT: + conceal_color.conceal_color_8bit = ext_control[i].value; + i++; + switch (ext_control[i].id) { + case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT: + conceal_color.conceal_color_10bit = + ext_control[i].value; + dprintk(VIDC_DBG, + "conceal color: 8bit=0x%x 10bit=0x%x", + conceal_color.conceal_color_8bit, + conceal_color.conceal_color_10bit); + rc = call_hfi_op(hdev, session_set_property, + inst->session, + HAL_PARAM_VDEC_CONCEAL_COLOR, + &conceal_color); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting conceal color", + __func__); + } + break; + default: + dprintk(VIDC_ERR, + "%s Could not find CONCEAL_COLOR_10BIT ext_control", + __func__); + rc = -ENOTSUPP; + break; + } + + break; + default: + dprintk(VIDC_ERR + , "%s Unsupported set control %d", + __func__, ext_control[i].id); + rc = -ENOTSUPP; + break; + } + } + + return rc; +} + +int msm_vdec_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops) +{ + return msm_comm_ctrl_init(inst, msm_vdec_ctrls, + ARRAY_SIZE(msm_vdec_ctrls), ctrl_ops); +} diff --git a/drivers/media/platform/msm/vidc/msm_vdec.h b/drivers/media/platform/msm/vidc/msm_vdec.h new file mode 100644 index 000000000000..7617182c9b95 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vdec.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2012, 2015-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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_VDEC_H_ +#define _MSM_VDEC_H_ + +#include "msm_vidc.h" +#include "msm_vidc_internal.h" +#define MSM_VDEC_DVC_NAME "msm_vidc_vdec" + +int msm_vdec_inst_init(struct msm_vidc_inst *inst); +int msm_vdec_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops); +int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_vdec_s_fmt(void *instance, struct v4l2_format *f); +int msm_vdec_s_ctrl(void *instance, struct v4l2_ctrl *ctrl); +int msm_vdec_g_ctrl(void *instance, struct v4l2_ctrl *ctrl); +int msm_vdec_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); + +#endif diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c new file mode 100644 index 000000000000..e02fbd765f96 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -0,0 +1,2965 @@ +/* Copyright (c) 2012-2021, 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 +#include "msm_vidc_internal.h" +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_clocks.h" + +#define MSM_VENC_DVC_NAME "msm_venc_8974" +#define MIN_BIT_RATE 32000 +#define MAX_BIT_RATE 300000000 +#define DEFAULT_BIT_RATE 64000 +#define BIT_RATE_STEP 1 +#define DEFAULT_FRAME_RATE 15 +#define OPERATING_FRAME_RATE_STEP (1 << 16) +#define MAX_SLICE_BYTE_SIZE ((MAX_BIT_RATE)>>3) +#define MIN_SLICE_BYTE_SIZE 512 +#define MAX_SLICE_MB_SIZE ((4096 * 2304) >> 8) +#define I_FRAME_QP 127 +#define P_FRAME_QP 127 +#define B_FRAME_QP 127 +#define MAX_INTRA_REFRESH_MBS ((4096 * 2304) >> 8) +#define MAX_NUM_B_FRAMES 4 +#define MAX_LTR_FRAME_COUNT 10 +#define MAX_HYBRID_HIER_P_LAYERS 6 + +#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY +#define MIN_TIME_RESOLUTION 1 +#define MAX_TIME_RESOLUTION 0xFFFFFF +#define DEFAULT_TIME_RESOLUTION 0x7530 +#define MIN_NUM_ENC_OUTPUT_BUFFERS 4 +#define MIN_NUM_ENC_CAPTURE_BUFFERS 5 + +static const char *const mpeg_video_rate_control[] = { + "VBR CFR", + "CBR CFR", + "MBR CFR", + "RC OFF", + "CBR VFR", + "MBR VFR", + "CQ", + NULL +}; + +static const char *const mpeg_video_flip[] = { + "No Flip", + "Horizontal Flip", + "Vertical Flip", + "Both", + NULL +}; + +static const char *const h264_video_entropy_cabac_model[] = { + "Model 0", + "Model 1", + "Model 2", + NULL +}; + +static const char *const hevc_tier_level[] = { + "Main Tier Level 1", + "Main Tier Level 2", + "Main Tier Level 2.1", + "Main Tier Level 3", + "Main Tier Level 3.1", + "Main Tier Level 4", + "Main Tier Level 4.1", + "Main Tier Level 5", + "Main Tier Level 5.1", + "Main Tier Level 5.2", + "Main Tier Level 6", + "Main Tier Level 6.1", + "Main Tier Level 6.2", + "High Tier Level 1", + "High Tier Level 2", + "High Tier Level 2.1", + "High Tier Level 3", + "High Tier Level 3.1", + "High Tier Level 4", + "High Tier Level 4.1", + "High Tier Level 5", + "High Tier Level 5.1", + "High Tier Level 5.2", + "High Tier Level 6", + "High Tier Level 6.1", + "High Tier Level 6.2", + "Level unknown", +}; + +static const char *const tme_profile[] = { + "0", + "1", + "2", + "3", +}; + +static const char *const tme_level[] = { + "Integer", +}; + +static const char *const hevc_profile[] = { + "Main", + "Main10", + "Main Still Pic", +}; + +static const char *const vp8_profile_level[] = { + "Unused", + "0.0", + "1.0", + "2.0", + "3.0", +}; + +static const char *const perf_level[] = { + "Nominal", + "Performance", + "Turbo" +}; + +static const char *const mbi_statistics[] = { + "Camcorder Default", + "Mode 1", + "Mode 2", + "Mode 3" +}; + +static const char *const timestamp_mode[] = { + "Honor", + "Ignore", +}; + +static const char *const iframe_sizes[] = { + "Default", + "Medium", + "Huge", + "Unlimited" +}; + +static const char *const mpeg_video_stream_format[] = { + "NAL Format Start Codes", + "NAL Format One NAL Per Buffer", + "NAL Format One Byte Length", + "NAL Format Two Byte Length", + "NAL Format Four Byte Length", + NULL +}; + +static const char *const roi_map_type[] = { + "None", + "2-bit", + "2-byte" +}; + +static struct msm_vidc_ctrl msm_venc_ctrls[] = { + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD, + .name = "IDR Period", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = INT_MAX, + .default_value = DEFAULT_FRAME_RATE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, + .name = "Intra Period for P frames", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 2*DEFAULT_FRAME_RATE-1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP, + .name = "I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP, + .name = "P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP, + .name = "B Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN, + .name = "I Frame Quantization Range Minimum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN, + .name = "P Frame Quantization Range Minimum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN, + .name = "B Frame Quantization Range Minimum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX, + .name = "I Frame Quantization Range Maximum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX, + .name = "P Frame Quantization Range Maximum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX, + .name = "B Frame Quantization Range Maximum", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK, + .name = "QP mask for diff frame types", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 7, + .default_value = 7, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, + .name = "Intra Period for B frames", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ADAPTIVE_B, + .name = "Adaptive B frames", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + }, + { + .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, + .name = "CAPTURE Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_NUM_CAPTURE_BUFFERS, + .maximum = MAX_NUM_CAPTURE_BUFFERS, + .default_value = MIN_NUM_CAPTURE_BUFFERS, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + { + .id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + .name = "OUTPUT Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_NUM_OUTPUT_BUFFERS, + .maximum = MAX_NUM_OUTPUT_BUFFERS, + .default_value = MIN_NUM_OUTPUT_BUFFERS, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME, + .name = "Request I Frame", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + .name = "Video Framerate and Bitrate Control", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .maximum = V4L2_MPEG_VIDEO_BITRATE_MODE_CQ, + .default_value = V4L2_MPEG_VIDEO_BITRATE_MODE_RC_OFF, + .step = 0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_MBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_RC_OFF) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) + ), + .qmenu = mpeg_video_rate_control, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY, + .name = "Frame quality", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_FRAME_QUALITY, + .maximum = MAX_FRAME_QUALITY, + .default_value = DEFAULT_FRAME_QUALITY, + .step = FRAME_QUALITY_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_IMG_GRID_ENABLE, + .name = "Image grid enable", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE, + .name = "Bit Rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + .name = "Peak Bit Rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + .name = "Entropy Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .name = "H264 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .name = "H264 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN, + .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + .name = "VP8 Profile Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0, + .menu_skip_mask = 0, + .qmenu = vp8_profile_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + .name = "HEVC Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN, + .maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC, + .default_value = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC) + ), + .qmenu = hevc_profile, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + .name = "HEVC Tier and Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1, + .maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN, + .default_value = + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN) + ), + .qmenu = hevc_tier_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE, + .name = "TME Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_0, + .maximum = V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_3, + .default_value = + V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_3) + ), + .qmenu = tme_profile, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL, + .name = "TME Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER, + .maximum = V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER, + .default_value = V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER) + ), + .qmenu = tme_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION, + .name = "TME Payload Version", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 0xFFFFFFF, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + .qmenu = NULL, + }, + { + .id = V4L2_CID_ROTATE, + .name = "Rotation", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 270, + .default_value = 0, + .step = 90, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + .name = "Slice Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE) | + (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) | + (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, + .name = "Slice Byte Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_SLICE_BYTE_SIZE, + .maximum = MAX_SLICE_BYTE_SIZE, + .default_value = MIN_SLICE_BYTE_SIZE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + .name = "Slice MB Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = MAX_SLICE_MB_SIZE, + .default_value = 1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE, + .name = "Slice delivery mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM, + .name = "Random Intra Refresh MBs", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE_CYCLIC, + .name = "Cyclic Intra Refresh MBs", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + .name = "H.264 Loop Filter Alpha Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + .name = "H.264 Loop Filter Beta Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + .name = "H.264 Loop Filter Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, + .maximum = L_MODE, + .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED) | + (1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED) | + (1 << L_MODE) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, + .name = "Sequence Header Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + .default_value = + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME) + ), + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE, + .name = "Secure mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA, + .name = "Extradata Type", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP, + .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP) + ), + .qmenu = mpeg_video_vidc_extradata, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO, + .name = "H264 VUI Timing Info", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER, + .name = "AU Delimiter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME, + .name = "H264 Use LTR", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (MAX_LTR_FRAME_COUNT - 1), + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT, + .name = "Ltr Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_LTR_FRAME_COUNT, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME, + .name = "H264 Mark LTR", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (MAX_LTR_FRAME_COUNT - 1), + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS, + .name = "Set Hier P num layers", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE, + .name = "VP8 Error Resilience mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS, + .name = "Set Hier B num layers", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 3, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE, + .name = "Set Hybrid Hier P mode", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 5, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS, + .name = "Set Max Hier P num layers sessions", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID, + .name = "Set Base Layer ID for Hier-P", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LAYER_ID, + .name = "Layer ID for different settings", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MSM_VIDC_ALL_LAYER_ID, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH, + .name = "SAR Width", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 4096, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT, + .name = "SAR Height", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 2160, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, + .name = "Session Priority", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE, + .name = "Layer wise bitrate for H264/H265 Hybrid HP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE, + .name = "Set Encoder Operating rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE, + .name = "Set Encoder Frame rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE, + .name = "BITRATE TYPE", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC, + .name = "Set VPE Color space conversion coefficients", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE, + .name = "Low Latency Mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH, + .name = "Set Blur width", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2048, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT, + .name = "Set Blur height", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2048, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + .name = "Transform 8x8", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE, + .name = "Set Color space", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_BT709_5, + .maximum = MSM_VIDC_BT2020, + .default_value = MSM_VIDC_BT601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE, + .name = "Set Color space range", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS, + .name = "Set Color space transfer characterstics", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_TRANSFER_BT709_5, + .maximum = MSM_VIDC_TRANSFER_HLG, + .default_value = MSM_VIDC_TRANSFER_601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS, + .name = "Set Color space matrix coefficients", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_MATRIX_BT_709_5, + .maximum = MSM_VIDC_MATRIX_BT_2020_CONST, + .default_value = MSM_VIDC_MATRIX_601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE, + .name = "Bounds of I-frame size", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED)), + .qmenu = iframe_sizes, + }, + { + .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + .name = "Frame Rate based Rate Control", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_CUSTOM_MATRIX, + .name = "Enable/Disable CSC Custom Matrix", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FLIP, + .name = "Flip", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH) + ), + .qmenu = mpeg_video_flip, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_HDR_INFO, + .name = "Enable/Disable HDR INFO", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00, + .name = "RGB PRIMARIES[0][0]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01, + .name = "RGB PRIMARIES[0][1]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10, + .name = "RGB PRIMARIES[1][0]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11, + .name = "RGB PRIMARIES[1][1]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20, + .name = "RGB PRIMARIES[2][0]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21, + .name = "RGB PRIMARIES[2][1]", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X, + .name = "WHITE POINT X", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y, + .name = "WHITE POINT Y", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM, + .name = "MAX DISPLAY LUMINANCE", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM, + .name = "MIN DISPLAY LUMINANCE", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_MAX_CLL, + .name = "MAX CLL", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_MAX_FLL, + .name = "MAX FLL", + .type = V4L2_CTRL_TYPE_U32, + .minimum = 0, + .maximum = UINT_MAX, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT, + .name = "NAL Format", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .maximum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH, + .default_value = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH) + ), + .qmenu = mpeg_video_stream_format, + }, +#ifdef VENC_BITRATE_SAVINGS_AVAILABLE + { + .id = V4L2_CID_MPEG_VIDC_VENC_BITRATE_SAVINGS, + .name = "Enable/Disable bitrate savings", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + }, +#endif +#ifdef VENC_ROI_TYPE_AVAILABLE + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE, + .name = "ROI Type", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_NONE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BYTE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_NONE, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_NONE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BIT) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BYTE) + ), + .qmenu = roi_map_type, + }, +#endif + { + .id = V4L2_CID_MPEG_VIDC_VENC_COMPLEXITY, + .name = "Encoder complexity", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 100, + .default_value = 100, + .step = 1, + .qmenu = NULL, + }, +}; + +#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls) + +static u32 get_frame_size_compressed(int plane, u32 height, u32 width) +{ + int sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2; + + return ALIGN(sz, SZ_4K); +} + +static struct msm_vidc_format venc_formats[] = { + { + .name = "YCbCr Semiplanar 4:2:0", + .description = "Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12, + .get_frame_size = get_frame_size_nv12, + .type = OUTPUT_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0", + .description = "UBWC Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12_UBWC, + .get_frame_size = get_frame_size_nv12_ubwc, + .type = OUTPUT_PORT, + }, + { + .name = "H264", + .description = "H264 compressed format", + .fourcc = V4L2_PIX_FMT_H264, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + .input_min_count = 4, + .output_min_count = 4, + }, + { + .name = "VP8", + .description = "VP8 compressed format", + .fourcc = V4L2_PIX_FMT_VP8, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + .input_min_count = 4, + .output_min_count = 4, + }, + { + .name = "HEVC", + .description = "HEVC compressed format", + .fourcc = V4L2_PIX_FMT_HEVC, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + .input_min_count = 4, + .output_min_count = 4, + }, + { + .name = "YCrCb Semiplanar 4:2:0", + .description = "Y/CrCb 4:2:0", + .fourcc = V4L2_PIX_FMT_NV21, + .get_frame_size = get_frame_size_nv21, + .type = OUTPUT_PORT, + }, + { + .name = "TP10 UBWC 4:2:0", + .description = "TP10 UBWC 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC, + .get_frame_size = get_frame_size_tp10_ubwc, + .type = OUTPUT_PORT, + }, + { + .name = "TME", + .description = "TME MBI format", + .fourcc = V4L2_PIX_FMT_TME, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + .input_min_count = 4, + .output_min_count = 4, + }, + { + .name = "YCbCr Semiplanar 4:2:0 10bit", + .description = "Y/CbCr 4:2:0 10bit", + .fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS, + .get_frame_size = get_frame_size_p010, + .type = OUTPUT_PORT, + }, + { + .name = "YCbCr Semiplanar 4:2:0 512 aligned", + .description = "Y/CbCr 4:2:0 512 aligned", + .fourcc = V4L2_PIX_FMT_NV12_512, + .get_frame_size = get_frame_size_nv12_512, + .type = OUTPUT_PORT, + }, +}; + +struct msm_vidc_format_constraint enc_pix_format_constraints[] = { + { + .fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS, + .num_planes = 2, + .y_stride_multiples = 256, + .y_max_stride = 8192, + .y_min_plane_buffer_height_multiple = 32, + .y_buffer_alignment = 256, + .uv_stride_multiples = 256, + .uv_max_stride = 8192, + .uv_min_plane_buffer_height_multiple = 16, + .uv_buffer_alignment = 256, + }, + { + .fourcc = V4L2_PIX_FMT_NV12_512, + .num_planes = 2, + .y_stride_multiples = 512, + .y_max_stride = 8192, + .y_min_plane_buffer_height_multiple = 512, + .y_buffer_alignment = 512, + .uv_stride_multiples = 512, + .uv_max_stride = 8192, + .uv_min_plane_buffer_height_multiple = 256, + .uv_buffer_alignment = 256, + }, +}; + + +static int msm_venc_set_csc(struct msm_vidc_inst *inst, + u32 color_primaries, u32 custom_matrix); + +static struct v4l2_ctrl *get_ctrl_from_cluster(int id, + struct v4l2_ctrl **cluster, int ncontrols) +{ + int c; + + for (c = 0; c < ncontrols; ++c) + if (cluster[c]->id == id) + return cluster[c]; + return NULL; +} + +int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hal_request_iframe request_iframe; + struct hal_bitrate bitrate; +#ifndef CONFIG_ARCH_SDM845 + struct hal_operating_rate operating_rate; +#endif + struct hal_profile_level profile_level; + enum hal_h264_entropy h264_entropy; + struct hal_intra_period intra_period; + struct hal_idr_period idr_period; + struct hal_intra_refresh intra_refresh; + struct hal_multi_slice_control multi_slice_control; + struct hal_h264_db_control h264_db_control; + struct hal_enable enable; + struct hal_quantization quant; + u32 property_id = 0, property_val = 0; + void *pdata = NULL; + struct v4l2_ctrl *temp_ctrl = NULL; + struct hfi_device *hdev; + struct hal_extradata_enable extra; + struct hal_ltr_use use_ltr; + struct hal_ltr_mark mark_ltr; + struct hal_hybrid_hierp hyb_hierp; + u32 hier_p_layers = 0; + int max_hierp_layers; + int baselayerid = 0; + struct hal_video_signal_info signal_info = {0}; + struct hal_vui_timing_info vui_timing_info = {0}; + enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT; + u32 color_primaries, custom_matrix; + struct hal_nal_stream_format_select stream_format; + struct hal_heic_frame_quality frame_quality; + struct hal_heic_grid_enable grid_enable; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + /* Small helper macro for quickly getting a control and err checking */ +#define TRY_GET_CTRL(__ctrl_id) ({ \ + struct v4l2_ctrl *__temp; \ + __temp = get_ctrl_from_cluster( \ + __ctrl_id, \ + ctrl->cluster, ctrl->ncontrols); \ + if (!__temp) { \ + dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \ + #__ctrl_id, __ctrl_id); \ + /* Clusters are hardcoded, if we can't find */ \ + /* something then things are massively screwed up */ \ + MSM_VIDC_ERROR(1); \ + } \ + __temp; \ + }) + + /* + * Unlock the control prior to setting to the hardware. Otherwise + * lower level code that attempts to do a get_ctrl() will end up + * deadlocking. + */ + v4l2_ctrl_unlock(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD: + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264 && + inst->fmts[CAPTURE_PORT].fourcc != + V4L2_PIX_FMT_H264_NO_SC && + inst->fmts[CAPTURE_PORT].fourcc != + V4L2_PIX_FMT_HEVC) { + dprintk(VIDC_ERR, + "Control %#x only valid for H264 and HEVC\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + + property_id = HAL_CONFIG_VENC_IDR_PERIOD; + idr_period.idr_period = ctrl->val; + pdata = &idr_period; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES: + case V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES: + { + int num_p, num_b; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES); + num_b = temp_ctrl->val; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES); + num_p = temp_ctrl->val; + + if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES) + num_p = ctrl->val; + else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES) + num_b = ctrl->val; + + if ((num_b < inst->capability.bframe.min) || + (num_b > inst->capability.bframe.max)) { + dprintk(VIDC_ERR, + "Error setting num b frames %d min, max supported is %d, %d\n", + num_b, inst->capability.bframe.min, + inst->capability.bframe.max); + rc = -ENOTSUPP; + break; + } + + property_id = HAL_CONFIG_VENC_INTRA_PERIOD; + intra_period.pframes = num_p; + intra_period.bframes = num_b; + + pdata = &intra_period; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_ADAPTIVE_B: + property_id = HAL_PARAM_VENC_ADAPTIVE_B; + enable.enable = ctrl->val; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME: + property_id = HAL_CONFIG_VENC_REQUEST_IFRAME; + request_iframe.enable = true; + pdata = &request_iframe; + break; + case V4L2_CID_MPEG_VIDC_VENC_COMPLEXITY: + if (is_realtime_session(inst)) + dprintk(VIDC_DBG, + "Client is setting complexity for RT session\n"); + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + { + struct hal_buffer_requirements *buff_req_buffer = NULL; + struct v4l2_ctrl *hybrid_hp = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE); + if ((ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR) + && hybrid_hp->val) { + dprintk(VIDC_ERR, + "CBR_VFR not allowed with Hybrid HP\n"); + rc = -ENOTSUPP; + break; + } + if ((ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) && + inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) { + dprintk(VIDC_ERR, "CQ supported only for HEVC\n"); + rc = -ENOTSUPP; + break; + } + property_id = HAL_PARAM_VENC_RATE_CONTROL; + property_val = ctrl->val; + pdata = &property_val; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ || + ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_RC_OFF) { + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + + if (!rc) { + dprintk(VIDC_DBG, + "Control: %x : Name = %s, ID = 0x%x Value = %d\n", + hash32_ptr(inst->session), ctrl->name, + ctrl->id, ctrl->val); + } else { + dprintk(VIDC_ERR, + "Failed to set rate control mode\n"); + break; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + break; + } + buff_req_buffer = + get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + + inst->bufq[CAPTURE_PORT].plane_sizes[0] + = buff_req_buffer ? buff_req_buffer->buffer_size : 0; + dprintk(VIDC_INFO, + "Get updated output buffer size %d\n", + inst->bufq[CAPTURE_PORT].plane_sizes[0]); + property_id = 0; + } + + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY: + { + if (ctrl->val < MIN_FRAME_QUALITY || + ctrl->val > MAX_FRAME_QUALITY) { + dprintk(VIDC_ERR, + "Frame quality value %d is not supported\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + property_id = HAL_CONFIG_HEIC_FRAME_QUALITY; + frame_quality.frame_quality = ctrl->val; + inst->frame_quality = ctrl->val; + pdata = &frame_quality; + break; + } + case V4L2_CID_MPEG_VIDC_IMG_GRID_ENABLE: + { + property_id = HAL_CONFIG_HEIC_GRID_ENABLE; + grid_enable.grid_enable = ctrl->val; + inst->grid_enable = ctrl->val; + pdata = &grid_enable; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE: + { + property_id = HAL_CONFIG_VENC_TARGET_BITRATE; + bitrate.bit_rate = ctrl->val; + bitrate.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &bitrate; + inst->clk_data.bitrate = ctrl->val; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + { + struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_BITRATE); + + if (ctrl->val < avg_bitrate->val) { + dprintk(VIDC_ERR, + "Peak bitrate (%d) is lower than average bitrate (%d)\n", + ctrl->val, avg_bitrate->val); + rc = -EINVAL; + break; + } else if (ctrl->val < avg_bitrate->val * 2) { + dprintk(VIDC_WARN, + "Peak bitrate (%d) ideally should be twice the average bitrate (%d)\n", + ctrl->val, avg_bitrate->val); + } + + property_id = HAL_CONFIG_VENC_MAX_BITRATE; + bitrate.bit_rate = ctrl->val; + bitrate.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &bitrate; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + property_id = HAL_PARAM_VENC_H264_ENTROPY_CONTROL; + h264_entropy = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, ctrl->val); + pdata = &h264_entropy; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + dprintk(VIDC_DBG, "\nprofile: %d\n", + profile_level.profile); + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + dprintk(VIDC_DBG, "\nLevel: %d\n", + profile_level.level); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = HAL_VP8_PROFILE_MAIN; + profile_level.level = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + inst->profile = profile_level.profile; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE: + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = msm_comm_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_ROTATE: + { + if (ctrl->val != 0 && ctrl->val != 90 + && ctrl->val != 180 && ctrl->val != 270) { + dprintk(VIDC_ERR, "Invalid rotation angle"); + rc = -ENOTSUPP; + } + dprintk(VIDC_DBG, "Rotation %d\n", ctrl->val); + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP: + { + dprintk(VIDC_DBG, "Flip %d\n", ctrl->val); + break; + } + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: { + int temp = 0; + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC && + inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264) { + return rc; + } + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: + temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB; + break; + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: + temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + default: + temp = 0; + break; + } + + if (temp) + temp_ctrl = TRY_GET_CTRL(temp); + + property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL; + multi_slice_control.multi_slice = ctrl->val; + multi_slice_control.slice_size = temp ? temp_ctrl->val : 0; + + pdata = &multi_slice_control; + break; + } + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC && + inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264) { + return rc; + } + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + + property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL; + multi_slice_control.multi_slice = temp_ctrl->val; + multi_slice_control.slice_size = ctrl->val; + pdata = &multi_slice_control; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE: { + bool codecs_supported = + inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC || + inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264 || + inst->fmts[CAPTURE_PORT].fourcc == + V4L2_PIX_FMT_H264_NO_SC; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + if (codecs_supported && temp_ctrl->val == + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + property_id = HAL_PARAM_VENC_SLICE_DELIVERY_MODE; + enable.enable = true; + } else { + dprintk(VIDC_WARN, + "Failed : slice delivery mode is not valid\n"); + enable.enable = false; + } + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE_CYCLIC: + { + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.mode = HAL_INTRA_REFRESH_CYCLIC; + intra_refresh.ir_mbs = ctrl->val; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM: + { + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.mode = HAL_INTRA_REFRESH_RANDOM; + intra_refresh.ir_mbs = ctrl->val; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + { + struct v4l2_ctrl *alpha, *beta; + + alpha = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA); + beta = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA); + + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = alpha->val; + h264_db_control.slice_beta_offset = beta->val; + h264_db_control.mode = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + ctrl->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + { + struct v4l2_ctrl *mode, *beta; + + mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE); + beta = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA); + + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = ctrl->val; + h264_db_control.slice_beta_offset = beta->val; + h264_db_control.mode = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + mode->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + { + struct v4l2_ctrl *mode, *alpha; + + mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE); + alpha = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA); + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = alpha->val; + h264_db_control.slice_beta_offset = ctrl->val; + h264_db_control.mode = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + mode->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + property_id = HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER; + + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE: + enable.enable = 0; + break; + case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME: + enable.enable = 1; + break; + default: + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE: + inst->flags |= VIDC_SECURE; + property_id = HAL_PARAM_SECURE; + property_val = !!(inst->flags & VIDC_SECURE); + pdata = &property_val; + dprintk(VIDC_INFO, "Setting secure mode to: %d\n", + !!(inst->flags & VIDC_SECURE)); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA: { + struct hal_buffer_requirements *buff_req_buffer = NULL; + int extra_idx = 0; + + property_id = HAL_PARAM_INDEX_EXTRADATA; + extra.index = msm_comm_get_hal_extradata_index(ctrl->val); + extra.enable = 1; + + switch (ctrl->val) { + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: + case V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA: + case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: + inst->bufq[OUTPUT_PORT].num_planes = 2; + break; + case V4L2_MPEG_VIDC_EXTRADATA_LTR: + case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP: + case V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS: + inst->bufq[CAPTURE_PORT].num_planes = 2; + break; + default: + rc = -ENOTSUPP; + break; + } + + pdata = &extra; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + break; + } + + extra_idx = EXTRADATA_IDX(inst->bufq[OUTPUT_PORT].num_planes); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + + inst->bufq[OUTPUT_PORT].plane_sizes[extra_idx] = + buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + } + + extra_idx = EXTRADATA_IDX(inst->bufq[CAPTURE_PORT].num_planes); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + + inst->bufq[CAPTURE_PORT].plane_sizes[extra_idx] = + buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + } + property_id = 0; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER: + property_id = HAL_PARAM_VENC_GENERATE_AUDNAL; + + switch (ctrl->val) { + case V4L2_MPEG_MSM_VIDC_DISABLE: + enable.enable = 0; + break; + case V4L2_MPEG_MSM_VIDC_ENABLE: + enable.enable = 1; + break; + default: + rc = -ENOTSUPP; + break; + } + + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME: + property_id = HAL_CONFIG_VENC_USELTRFRAME; + use_ltr.ref_ltr = ctrl->val; + use_ltr.use_constraint = true; + use_ltr.frames = 0; + pdata = &use_ltr; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME: + property_id = HAL_CONFIG_VENC_MARKLTRFRAME; + mark_ltr.mark_frame = ctrl->val; + pdata = &mark_ltr; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS: + property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES; + hier_p_layers = ctrl->val; + if (hier_p_layers > inst->capability.hier_p.max) { + dprintk(VIDC_ERR, + "Error setting hier p num layers %d max supported is %d\n", + hier_p_layers, inst->capability.hier_p.max); + rc = -ENOTSUPP; + break; + } + pdata = &hier_p_layers; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE: + property_id = HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE; + enable.enable = ctrl->val; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE: + { + struct v4l2_ctrl *rate_control; + + rate_control = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + if ((rate_control->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR) + && ctrl->val) { + dprintk(VIDC_ERR, + "Hybrid HP not allowed with CBR_VFR\n"); + rc = -ENOTSUPP; + break; + } + property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE; + hyb_hierp.layers = ctrl->val; + pdata = &hyb_hierp; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS: + property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS; + max_hierp_layers = ctrl->val; + if (max_hierp_layers > inst->capability.hier_p.max) { + dprintk(VIDC_ERR, + "Error max HP layers(%d)>max supported(%d)\n", + max_hierp_layers, inst->capability.hier_p.max); + rc = -ENOTSUPP; + break; + } + pdata = &max_hierp_layers; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID: + property_id = HAL_CONFIG_VENC_BASELAYER_PRIORITYID; + baselayerid = ctrl->val; + pdata = &baselayerid; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP: { + struct v4l2_ctrl *qpp, *qpb, *mask; + + property_id = HAL_CONFIG_VENC_FRAME_QP; + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP); + mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK); + + quant.qpi = ctrl->val; + quant.qpp = qpp->val; + quant.qpb = qpb->val; + quant.enable = mask->val; + quant.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &quant; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpb, *mask; + + property_id = HAL_CONFIG_VENC_FRAME_QP; + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP); + mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK); + + quant.qpp = ctrl->val; + quant.qpi = qpi->val; + quant.qpb = qpb->val; + quant.enable = mask->val; + quant.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &quant; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP: { + struct v4l2_ctrl *qpp, *qpi, *mask; + + property_id = HAL_CONFIG_VENC_FRAME_QP; + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP); + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP); + mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK); + + quant.qpb = ctrl->val; + quant.qpp = qpp->val; + quant.qpi = qpi->val; + quant.enable = mask->val; + quant.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &quant; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK: { + struct v4l2_ctrl *qpi, *qpp, *qpb; + + property_id = HAL_CONFIG_VENC_FRAME_QP; + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP); + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP); + + quant.qpi = qpi->val; + quant.qpp = qpp->val; + quant.qpb = qpb->val; + quant.enable = ctrl->val; + quant.layer_id = MSM_VIDC_ALL_LAYER_ID; + pdata = &quant; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: + property_id = HAL_CONFIG_REALTIME; + enable.enable = ctrl->val; + pdata = &enable; + switch (ctrl->val) { + case V4L2_MPEG_MSM_VIDC_DISABLE: + inst->flags &= ~VIDC_REALTIME; + break; + case V4L2_MPEG_MSM_VIDC_ENABLE: + inst->flags |= VIDC_REALTIME; + break; + default: + dprintk(VIDC_WARN, + "inst(%pK) invalid priority ctrl value %#x\n", + inst, ctrl->val); + break; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE: + if (((ctrl->val >> 16) < inst->capability.frame_rate.min || + (ctrl->val >> 16) > inst->capability.frame_rate.max) && + ctrl->val != INT_MAX) { + if (!is_realtime_session(inst)) { + if ((ctrl->val >> 16) < + inst->capability.frame_rate.min) { + inst->clk_data.operating_rate = + inst->capability.frame_rate.min << 16; + } else { + inst->clk_data.operating_rate = + inst->capability.frame_rate.max << 16; + } + dprintk(VIDC_DBG, + "inst(%pK) operating rate capped from %d to %d\n", + inst, ctrl->val >> 16, + inst->clk_data.operating_rate >> 16); + } else { + dprintk(VIDC_ERR, "Invalid operating rate %u\n", + (ctrl->val >> 16)); + rc = -ENOTSUPP; + } + } else if (ctrl->val == INT_MAX) { + dprintk(VIDC_DBG, "inst(%pK) Request for turbo mode\n", + inst); + inst->clk_data.turbo_mode = true; + } else if (msm_vidc_validate_operating_rate(inst, ctrl->val)) { + dprintk(VIDC_ERR, "Failed to set operating rate\n"); + rc = -ENOTSUPP; + } else { + dprintk(VIDC_DBG, + "inst(%pK) operating rate changed from %d to %d\n", + inst, inst->clk_data.operating_rate >> 16, + ctrl->val >> 16); + inst->clk_data.operating_rate = ctrl->val; + inst->clk_data.turbo_mode = false; +#ifndef CONFIG_ARCH_SDM845 + property_id = HAL_CONFIG_OPERATING_RATE; + operating_rate.operating_rate = + inst->clk_data.operating_rate; + pdata = &operating_rate; +#endif + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE: + { + property_id = HAL_PARAM_VENC_BITRATE_TYPE; + enable.enable = ctrl->val; + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE: + { + signal_info.color_space = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE: + { + signal_info.full_range = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS: + { + signal_info.transfer_chars = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS: + { + signal_info.matrix_coeffs = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC: + if (ctrl->val != V4L2_MPEG_MSM_VIDC_ENABLE) + break; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + color_primaries = temp_ctrl->val; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_CUSTOM_MATRIX); + custom_matrix = temp_ctrl->val; + rc = msm_venc_set_csc(inst, color_primaries, custom_matrix); + if (rc) + dprintk(VIDC_ERR, "fail to set csc: %d\n", rc); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_CUSTOM_MATRIX: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + color_primaries = temp_ctrl->val; + rc = msm_venc_set_csc(inst, color_primaries, ctrl->val); + if (rc) + dprintk(VIDC_ERR, "fail to set csc: %d\n", rc); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE: + { + property_id = HAL_PARAM_VENC_LOW_LATENCY; + if (ctrl->val == V4L2_MPEG_MSM_VIDC_ENABLE) + enable.enable = 1; + else + enable.enable = 0; + pdata = &enable; + inst->clk_data.low_latency_mode = (bool) enable.enable; + msm_dcvs_try_enable(inst); + break; + } + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8; + switch (ctrl->val) { + case V4L2_MPEG_MSM_VIDC_ENABLE: + enable.enable = 1; + break; + case V4L2_MPEG_MSM_VIDC_DISABLE: + enable.enable = 0; + break; + default: + dprintk(VIDC_ERR, + "Invalid H264 8x8 transform control value %d\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE: + property_id = HAL_PARAM_VENC_IFRAMESIZE_TYPE; + iframesize_type = msm_comm_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE, + ctrl->val); + pdata = &iframesize_type; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + { + property_id = HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP; + enable.enable = ctrl->val; + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO: + { + struct v4l2_ctrl *rc_mode; + bool cfr = false; + + property_id = HAL_PARAM_VENC_VUI_TIMING_INFO; + pdata = &vui_timing_info; + + if (ctrl->val != V4L2_MPEG_MSM_VIDC_ENABLE) { + vui_timing_info.enable = 0; + break; + } + + rc_mode = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + + switch (rc_mode->val) { + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: + case V4L2_MPEG_VIDEO_BITRATE_MODE_MBR: + cfr = true; + break; + default: + cfr = false; + } + + vui_timing_info.enable = 1; + vui_timing_info.fixed_frame_rate = cfr; + vui_timing_info.time_scale = NSEC_PER_SEC; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT: + { + property_id = HAL_PARAM_NAL_STREAM_FORMAT_SELECT; + stream_format.nal_stream_format_select = BIT(ctrl->val); + pdata = &stream_format; + break; + } + case V4L2_CID_MPEG_VIDC_VENC_BITRATE_SAVINGS: + { + property_id = HAL_PARAM_VENC_BITRATE_SAVINGS; + enable.enable = ctrl->val; + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT: + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH: + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT: + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH: + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT: + case V4L2_CID_MPEG_VIDC_VIDEO_LAYER_ID: + case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE: + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN: + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN: + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN: + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX: + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX: + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX: + case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20: + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21: + case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X: + case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y: + case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM: + case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM: + case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL: + case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL: + dprintk(VIDC_DBG, "Set the control : %#x using ext ctrl\n", + ctrl->id); + break; + default: + dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id); + rc = -ENOTSUPP; + break; + } + + v4l2_ctrl_lock(ctrl); +#undef TRY_GET_CTRL + + if (!rc && property_id) { + dprintk(VIDC_DBG, + "Control: %x : Name = %s, ID = 0x%x Value = %d\n", + hash32_ptr(inst->session), ctrl->name, + ctrl->id, ctrl->val); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + } + + return rc; +} + +int msm_venc_ext_layer_id_update(struct v4l2_ext_control *control, + u32 *property_id, + void **pdata, + struct hal_quantization *qp, + struct hal_quantization_range *qp_range, + struct hal_bitrate *bitrate) +{ + switch (control->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP: + qp->qpi = control->value; + *property_id = + HAL_CONFIG_VENC_FRAME_QP; + *pdata = qp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP: + qp->qpp = control->value; + *property_id = + HAL_CONFIG_VENC_FRAME_QP; + *pdata = qp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP: + qp->qpb = control->value; + *property_id = + HAL_CONFIG_VENC_FRAME_QP; + *pdata = qp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK: + qp->enable = control->value; + *property_id = + HAL_CONFIG_VENC_FRAME_QP; + *pdata = qp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN: + qp_range->qpi_min = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN: + qp_range->qpp_min = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN: + qp_range->qpb_min = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX: + qp_range->qpi_max = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX: + qp_range->qpp_max = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX: + qp_range->qpb_max = control->value; + *property_id = + HAL_PARAM_VENC_SESSION_QP_RANGE; + *pdata = qp_range; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE: + bitrate->bit_rate = control->value; + *property_id = + HAL_CONFIG_VENC_TARGET_BITRATE; + *pdata = bitrate; + break; + } + return 0; +} + +int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0, i; + struct v4l2_ext_control *control; + struct hfi_device *hdev; + struct hal_ltr_mode ltr_mode; + u32 property_id = 0; + void *pdata = NULL; + struct msm_vidc_capability *cap = NULL; + struct hal_aspect_ratio sar; + struct hal_bitrate bitrate; + struct hal_frame_size blur_res; + struct hal_quantization_range qp_range; + struct hal_quantization qp; + struct msm_vidc_mastering_display_colour_sei_payload *mdisp_sei = NULL; + struct msm_vidc_content_light_level_sei_payload *cll_sei = NULL; + + if (!inst || !inst->core || !inst->core->device || !ctrl) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + /* This will check the range for contols and clip if necessary */ + v4l2_try_ext_ctrls(&inst->ctrl_handler, ctrl); + + hdev = inst->core->device; + cap = &inst->capability; + + control = ctrl->controls; + + mdisp_sei = &(inst->hdr10_sei_params.disp_color_sei); + cll_sei = &(inst->hdr10_sei_params.cll_sei); + + for (i = 0; i < ctrl->count; i++) { + switch (control[i].id) { + case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT: + ltr_mode.count = control[i].value; + if (ltr_mode.count > cap->ltr_count.max) { + dprintk(VIDC_ERR, + "Invalid LTR count %d. Supported max: %d\n", + ltr_mode.count, + cap->ltr_count.max); + rc = -EINVAL; + } + ltr_mode.mode = HAL_LTR_MODE_MANUAL; + ltr_mode.trust_mode = 1; + property_id = HAL_PARAM_VENC_LTRMODE; + pdata = <r_mode; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH: + sar.aspect_width = control[i].value; + property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO; + pdata = &sar; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT: + sar.aspect_height = control[i].value; + property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO; + pdata = &sar; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH: + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + blur_res.width = control[i].value; + blur_res.buffer_type = HAL_BUFFER_INPUT; + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + pdata = &blur_res; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT: + blur_res.height = control[i].value; + blur_res.buffer_type = HAL_BUFFER_INPUT; + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + pdata = &blur_res; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_LAYER_ID: + qp.layer_id = control[i].value; + /* Enable QP for all frame types by default */ + qp.enable = 7; + qp_range.layer_id = control[i].value; + bitrate.layer_id = control[i].value; + i++; + while (i < ctrl->count) { + msm_venc_ext_layer_id_update( + &control[i], + &property_id, + &pdata, + &qp, + &qp_range, + &bitrate); + i++; + } + break; + case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO: + if (control[i].value == + V4L2_MPEG_MSM_VIDC_DISABLE || + !mdisp_sei || !cll_sei) + break; + i++; + while (i < ctrl->count) { + switch (control[i].id) { + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00: + mdisp_sei->nDisplayPrimariesX[0] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01: + mdisp_sei->nDisplayPrimariesY[0] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10: + mdisp_sei->nDisplayPrimariesX[1] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11: + mdisp_sei->nDisplayPrimariesY[1] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20: + mdisp_sei->nDisplayPrimariesX[2] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21: + mdisp_sei->nDisplayPrimariesY[2] = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X: + mdisp_sei->nWhitePointX = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y: + mdisp_sei->nWhitePointY = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM: + mdisp_sei->nMaxDisplayMasteringLuminance + = control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM: + mdisp_sei->nMinDisplayMasteringLuminance + = control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL: + cll_sei->nMaxContentLight = + control[i].value; + break; + case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL: + cll_sei->nMaxPicAverageLight = + control[i].value; + break; + default: + dprintk(VIDC_ERR, + "Unknown Ctrl:%d, not part of HDR Info", + control[i].id); + } + i++; + } + property_id = + HAL_PARAM_VENC_HDR10_PQ_SEI; + pdata = &inst->hdr10_sei_params; + break; + default: + dprintk(VIDC_ERR, "Invalid id set: %d\n", + control[i].id); + rc = -ENOTSUPP; + break; + } + if (rc) + break; + } + + if (!rc && property_id) { + dprintk(VIDC_DBG, "Control: HAL property=%x\n", property_id); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + } + return rc; +} + +int msm_venc_inst_init(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct msm_vidc_format *fmt = NULL; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid input = %pK\n", inst); + return -EINVAL; + } + inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT; + inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH; + inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT; + inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH; + inst->capability.height.min = MIN_SUPPORTED_HEIGHT; + inst->capability.height.max = DEFAULT_HEIGHT; + inst->capability.width.min = MIN_SUPPORTED_WIDTH; + inst->capability.width.max = DEFAULT_WIDTH; + inst->capability.secure_output2_threshold.min = 0; + inst->capability.secure_output2_threshold.max = 0; + inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_DYNAMIC; + inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC; + inst->prop.fps = DEFAULT_FPS; + inst->capability.pixelprocess_capabilities = 0; + /* To start with, both ports are 1 plane each */ + inst->bufq[OUTPUT_PORT].num_planes = 1; + inst->bufq[CAPTURE_PORT].num_planes = 1; + inst->clk_data.operating_rate = 0; + + inst->buff_req.buffer[1].buffer_type = HAL_BUFFER_INPUT; + inst->buff_req.buffer[1].buffer_count_min_host = + inst->buff_req.buffer[1].buffer_count_actual = + MIN_NUM_ENC_OUTPUT_BUFFERS; + inst->buff_req.buffer[2].buffer_type = HAL_BUFFER_OUTPUT; + inst->buff_req.buffer[2].buffer_count_min_host = + inst->buff_req.buffer[2].buffer_count_actual = + MIN_NUM_ENC_CAPTURE_BUFFERS; + inst->buff_req.buffer[3].buffer_type = HAL_BUFFER_OUTPUT2; + inst->buff_req.buffer[3].buffer_count_min_host = + inst->buff_req.buffer[3].buffer_count_actual = + MIN_NUM_ENC_CAPTURE_BUFFERS; + inst->buff_req.buffer[4].buffer_type = HAL_BUFFER_EXTRADATA_INPUT; + inst->buff_req.buffer[5].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT; + inst->buff_req.buffer[6].buffer_type = HAL_BUFFER_EXTRADATA_OUTPUT2; + inst->buff_req.buffer[7].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH; + inst->buff_req.buffer[8].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_1; + inst->buff_req.buffer[9].buffer_type = HAL_BUFFER_INTERNAL_SCRATCH_2; + inst->buff_req.buffer[10].buffer_type = HAL_BUFFER_INTERNAL_PERSIST; + inst->buff_req.buffer[11].buffer_type = HAL_BUFFER_INTERNAL_PERSIST_1; + inst->buff_req.buffer[12].buffer_type = HAL_BUFFER_INTERNAL_CMD_QUEUE; + inst->buff_req.buffer[13].buffer_type = HAL_BUFFER_INTERNAL_RECON; + + /* By default, initialize OUTPUT port to UBWC YUV format */ + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), V4L2_PIX_FMT_NV12_UBWC, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "venc_formats corrupted\n"); + return -EINVAL; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + /* By default, initialize CAPTURE port to H264 encoder */ + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), V4L2_PIX_FMT_H264, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "venc_formats corrupted\n"); + return -EINVAL; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + return rc; +} + +int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) +{ + const struct msm_vidc_format *fmt = NULL; + int rc = 0; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, f = %pK\n", inst, f); + return -EINVAL; + } + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(venc_formats, + ARRAY_SIZE(venc_formats), f->index, CAPTURE_PORT); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(venc_formats, + ARRAY_SIZE(venc_formats), f->index, OUTPUT_PORT); + f->flags = V4L2_FMT_FLAG_COMPRESSED; + } + + memset(f->reserved, 0, sizeof(f->reserved)); + if (fmt) { + strlcpy(f->description, fmt->description, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + } else { + dprintk(VIDC_DBG, "No more formats found\n"); + rc = -EINVAL; + } + return rc; +} + +static int msm_venc_set_csc(struct msm_vidc_inst *inst, + u32 color_primaries, u32 custom_matrix) +{ + int rc = 0; + int count = 0; + struct hal_vpe_color_space_conversion vpe_csc; + struct msm_vidc_platform_resources *resources; + u32 *bias_coeff = NULL; + u32 *csc_limit = NULL; + u32 *csc_matrix = NULL; + + resources = &(inst->core->resources); + bias_coeff = + resources->csc_coeff_data->vpe_csc_custom_bias_coeff; + csc_limit = + resources->csc_coeff_data->vpe_csc_custom_limit_coeff; + csc_matrix = + resources->csc_coeff_data->vpe_csc_custom_matrix_coeff; + + vpe_csc.input_color_primaries = color_primaries; + /* Custom bias, matrix & limit */ + vpe_csc.custom_matrix_enabled = custom_matrix; + + if (vpe_csc.custom_matrix_enabled && bias_coeff != NULL + && csc_limit != NULL && csc_matrix != NULL) { + while (count < HAL_MAX_MATRIX_COEFFS) { + if (count < HAL_MAX_BIAS_COEFFS) + vpe_csc.csc_bias[count] = + bias_coeff[count]; + if (count < HAL_MAX_LIMIT_COEFFS) + vpe_csc.csc_limit[count] = + csc_limit[count]; + vpe_csc.csc_matrix[count] = + csc_matrix[count]; + count = count + 1; + } + } + rc = msm_comm_try_set_prop(inst, + HAL_PARAM_VPE_COLOR_SPACE_CONVERSION, &vpe_csc); + if (rc) + dprintk(VIDC_ERR, "Setting VPE coefficients failed\n"); + + return rc; +} + +int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + struct msm_vidc_format *fmt = NULL; + struct msm_vidc_format_constraint *fmt_constraint = NULL; + int rc = 0; + struct hfi_device *hdev; + int extra_idx = 0, i = 0; + struct hal_buffer_requirements *buff_req_buffer; + struct hal_frame_size frame_sz; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, format = %pK\n", inst, f); + return -EINVAL; + } + + if (!inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto exit; + } + + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + goto exit; + } + + inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width; + inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height; + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto exit; + } + + frame_sz.buffer_type = HAL_BUFFER_OUTPUT; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + dprintk(VIDC_DBG, "CAPTURE port width = %d, height = %d\n", + frame_sz.width, frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set framesize for CAPTURE port\n"); + goto exit; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + return rc; + } + + /* + * Get CAPTURE plane size from HW. This may change based on + * settings like Slice delivery mode. HW should decide howmuch + * it needs. + */ + + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + + f->fmt.pix_mp.plane_fmt[0].sizeimage = buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + + /* + * Get CAPTURE plane Extradata size from HW. This may change + * with no of Extradata's enabled. HW should decide howmuch + * it needs. + */ + + extra_idx = EXTRADATA_IDX(inst->bufq[fmt->type].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + } + + f->fmt.pix_mp.num_planes = inst->bufq[fmt->type].num_planes; + for (i = 0; i < inst->bufq[fmt->type].num_planes; i++) { + inst->bufq[fmt->type].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + /* + * Input extradata buffer size may change upon updating + * CAPTURE plane buffer size. + */ + + extra_idx = EXTRADATA_IDX(inst->bufq[OUTPUT_PORT].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + inst->bufq[OUTPUT_PORT].plane_sizes[extra_idx] = + buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + } + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct hal_frame_size frame_sz; + + inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; + inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto exit; + } + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->prop.width[OUTPUT_PORT]; + frame_sz.height = inst->prop.height[OUTPUT_PORT]; + dprintk(VIDC_DBG, "OUTPUT port width = %d, height = %d\n", + frame_sz.width, frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set framesize for Output port\n"); + goto exit; + } + + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on OUTPUT port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto exit; + } + inst->clk_data.opb_fourcc = f->fmt.pix_mp.pixelformat; + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + f->fmt.pix_mp.plane_fmt[0].sizeimage = + inst->fmts[fmt->type].get_frame_size(0, + f->fmt.pix_mp.height, f->fmt.pix_mp.width); + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + return rc; + } + + /* + * Get OUTPUT plane Extradata size from HW. This may change + * with no of Extradata's enabled. HW should decide howmuch + * it needs. + */ + + extra_idx = EXTRADATA_IDX(inst->bufq[fmt->type].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + } + + f->fmt.pix_mp.num_planes = inst->bufq[fmt->type].num_planes; + + for (i = 0; i < inst->bufq[fmt->type].num_planes; i++) { + inst->bufq[fmt->type].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc); + + fmt_constraint = + msm_comm_get_pixel_fmt_constraints(enc_pix_format_constraints, + ARRAY_SIZE(enc_pix_format_constraints), + f->fmt.pix_mp.pixelformat); + + if (!fmt_constraint) { + dprintk(VIDC_INFO, + "Format constraint not required for %d on OUTPUT port\n", + f->fmt.pix_mp.pixelformat); + } else { + rc = msm_comm_set_color_format_constraints(inst, + HAL_BUFFER_INPUT, + fmt_constraint); + if (rc) { + dprintk(VIDC_ERR, + "Set constraint for %d failed on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto exit; + } + } + + } else { + dprintk(VIDC_ERR, "%s - Unsupported buf type: %d\n", + __func__, f->type); + rc = -EINVAL; + goto exit; + } +exit: + return rc; +} + +int msm_venc_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops) +{ + return msm_comm_ctrl_init(inst, msm_venc_ctrls, + ARRAY_SIZE(msm_venc_ctrls), ctrl_ops); +} diff --git a/drivers/media/platform/msm/vidc/msm_venc.h b/drivers/media/platform/msm/vidc/msm_venc.h new file mode 100644 index 000000000000..df6f9baf19a6 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_venc.h @@ -0,0 +1,28 @@ +/* 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 + * 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_VENC_H_ +#define _MSM_VENC_H_ + +#include "msm_vidc.h" +#include "msm_vidc_internal.h" +#define MSM_VENC_DVC_NAME "msm_vidc_venc" + +int msm_venc_inst_init(struct msm_vidc_inst *inst); +int msm_venc_ctrl_init(struct msm_vidc_inst *inst, + const struct v4l2_ctrl_ops *ctrl_ops); +int msm_venc_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_venc_s_fmt(void *instance, struct v4l2_format *f); +int msm_venc_s_ctrl(void *instance, struct v4l2_ctrl *ctrl); +int msm_venc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); + +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c new file mode 100644 index 000000000000..27aa2bd039e7 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -0,0 +1,2272 @@ +/* Copyright (c) 2012-2021, 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 +#include +#include +#include "msm_vidc.h" +#include "msm_vidc_internal.h" +#include "msm_vidc_debug.h" +#include "msm_vdec.h" +#include "msm_venc.h" +#include "msm_cvp.h" +#include "msm_vidc_common.h" +#include +#include "vidc_hfi_api.h" +#include "msm_vidc_clocks.h" +#include + +#define MAX_EVENTS 30 + +static int try_get_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ctrl *ctrl); + +static int get_poll_flags(void *instance) +{ + struct msm_vidc_inst *inst = instance; + struct vb2_queue *outq = &inst->bufq[OUTPUT_PORT].vb2_bufq; + struct vb2_queue *capq = &inst->bufq[CAPTURE_PORT].vb2_bufq; + struct vb2_buffer *out_vb = NULL; + struct vb2_buffer *cap_vb = NULL; + unsigned long flags; + int rc = 0; + + if (v4l2_event_pending(&inst->event_handler)) + rc |= POLLPRI; + + spin_lock_irqsave(&capq->done_lock, flags); + if (!list_empty(&capq->done_list)) + cap_vb = list_first_entry(&capq->done_list, struct vb2_buffer, + done_entry); + if (cap_vb && (cap_vb->state == VB2_BUF_STATE_DONE + || cap_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&capq->done_lock, flags); + + spin_lock_irqsave(&outq->done_lock, flags); + if (!list_empty(&outq->done_list)) + out_vb = list_first_entry(&outq->done_list, struct vb2_buffer, + done_entry); + if (out_vb && (out_vb->state == VB2_BUF_STATE_DONE + || out_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLOUT | POLLWRNORM; + spin_unlock_irqrestore(&outq->done_lock, flags); + + return rc; +} + +int msm_vidc_poll(void *instance, struct file *filp, + struct poll_table_struct *wait) +{ + struct msm_vidc_inst *inst = instance; + struct vb2_queue *outq = NULL; + struct vb2_queue *capq = NULL; + + if (!inst) + return -EINVAL; + + outq = &inst->bufq[OUTPUT_PORT].vb2_bufq; + capq = &inst->bufq[CAPTURE_PORT].vb2_bufq; + + poll_wait(filp, &inst->event_handler.wait, wait); + poll_wait(filp, &capq->done_wq, wait); + poll_wait(filp, &outq->done_wq, wait); + return get_poll_flags(inst); +} +EXPORT_SYMBOL(msm_vidc_poll); + +int msm_vidc_querycap(void *instance, struct v4l2_capability *cap) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !cap) + return -EINVAL; + + strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver)); + cap->bus_info[0] = 0; + cap->version = MSM_VIDC_VERSION; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + memset(cap->reserved, 0, sizeof(cap->reserved)); + + if (inst->session_type == MSM_VIDC_DECODER) + strlcpy(cap->card, MSM_VDEC_DVC_NAME, sizeof(cap->card)); + else if (inst->session_type == MSM_VIDC_ENCODER) + strlcpy(cap->card, MSM_VENC_DVC_NAME, sizeof(cap->card)); + else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(msm_vidc_querycap); + +int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_enum_fmt(instance, f); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_enum_fmt(instance, f); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_enum_fmt); + +static void msm_vidc_ctrl_get_range(struct v4l2_queryctrl *ctrl, + struct hal_capability_supported *capability) + +{ + ctrl->maximum = capability->max; + ctrl->minimum = capability->min; +} + +int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl) +{ + struct msm_vidc_inst *inst = instance; + struct hal_profile_level_supported *prof_lev_supp; + struct hal_profile_level *prof_lev; + int rc = 0, i = 0, profile_mask = 0, v4l2_prof_value = 0, max_level = 0; + + if (!inst || !ctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE: + msm_vidc_ctrl_get_range(ctrl, + &inst->capability.hier_p_hybrid); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.hier_b); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.hier_p); + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE: + case V4L2_CID_MPEG_VIDEO_BITRATE: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.bitrate); + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.peakbitrate); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.blur_width); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.blur_height); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.bframe); + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.slice_mbs); + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.slice_bytes); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE_CAPS: + msm_vidc_ctrl_get_range(ctrl, + &inst->capability.color_space_caps); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_CAPS: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.rotation); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE: + case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE: + msm_vidc_ctrl_get_range(ctrl, &inst->capability.frame_rate); + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_VP9_PROFILE: + { + prof_lev_supp = &inst->capability.profile_level; + for (i = 0; i < prof_lev_supp->profile_count; i++) { + v4l2_prof_value = msm_comm_hal_to_v4l2(ctrl->id, + prof_lev_supp->profile_level[i].profile); + if (v4l2_prof_value == -EINVAL) { + dprintk(VIDC_WARN, "Invalid profile"); + rc = -EINVAL; + } + profile_mask |= (1 << v4l2_prof_value); + } + ctrl->flags = profile_mask; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL: + { + prof_lev_supp = &inst->capability.profile_level; + for (i = 0; i < prof_lev_supp->profile_count; i++) { + prof_lev = &prof_lev_supp->profile_level[i]; + if (max_level < prof_lev->level) + max_level = prof_lev->level; + } + ctrl->maximum = msm_comm_hal_to_v4l2(ctrl->id, max_level); + if (ctrl->maximum == -EINVAL) { + dprintk(VIDC_WARN, "Invalid max level"); + rc = -EINVAL; + } + break; + } + default: + rc = -EINVAL; + } + return rc; +} +EXPORT_SYMBOL(msm_vidc_query_ctrl); + +int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) +{ + int rc = 0; + struct msm_vidc_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + rc = msm_vdec_s_fmt(instance, f); + if (inst->session_type == MSM_VIDC_ENCODER) + rc = msm_venc_s_fmt(instance, f); + + dprintk(VIDC_DBG, + "s_fmt: %x : type %d wxh %dx%d pixelfmt %#x num_planes %d size[0] %d size[1] %d in_reconfig %d\n", + hash32_ptr(inst->session), f->type, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.num_planes, + f->fmt.pix_mp.plane_fmt[0].sizeimage, + f->fmt.pix_mp.plane_fmt[1].sizeimage, inst->in_reconfig); + return rc; +} +EXPORT_SYMBOL(msm_vidc_s_fmt); + +int msm_vidc_g_fmt(void *instance, struct v4l2_format *f) +{ + struct msm_vidc_inst *inst = instance; + int i, rc = 0, color_format = 0; + enum vidc_ports port; + u32 num_planes; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, format = %pK\n", inst, f); + return -EINVAL; + } + if (inst->in_reconfig) { + inst->prop.height[OUTPUT_PORT] = inst->reconfig_height; + inst->prop.width[OUTPUT_PORT] = inst->reconfig_width; + } + + port = f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + OUTPUT_PORT : CAPTURE_PORT; + + f->fmt.pix_mp.pixelformat = inst->fmts[port].fourcc; + f->fmt.pix_mp.height = inst->prop.height[port]; + f->fmt.pix_mp.width = inst->prop.width[port]; + num_planes = f->fmt.pix_mp.num_planes = inst->bufq[port].num_planes; + for (i = 0; i < num_planes; ++i) + f->fmt.pix_mp.plane_fmt[i].sizeimage = + inst->bufq[port].plane_sizes[i]; + switch (inst->fmts[port].fourcc) { + case V4L2_PIX_FMT_NV12: + color_format = COLOR_FMT_NV12; + break; + case V4L2_PIX_FMT_NV12_512: + color_format = COLOR_FMT_NV12_512; + break; + case V4L2_PIX_FMT_NV12_UBWC: + color_format = COLOR_FMT_NV12_UBWC; + break; + case V4L2_PIX_FMT_NV12_TP10_UBWC: + color_format = COLOR_FMT_NV12_BPP10_UBWC; + break; + case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS: + color_format = COLOR_FMT_P010; + break; + default: + dprintk(VIDC_DBG, + "Invalid : g_fmt called on %s port with Invalid fourcc 0x%x\n", + port == OUTPUT_PORT ? "OUTPUT" : "CAPTURE", + inst->fmts[port].fourcc); + goto exit; + } + + f->fmt.pix_mp.plane_fmt[0].bytesperline = VENUS_Y_STRIDE(color_format, + inst->prop.width[port]); + f->fmt.pix_mp.plane_fmt[0].reserved[0] = VENUS_Y_SCANLINES(color_format, + inst->prop.height[port]); + f->fmt.pix_mp.plane_fmt[0].sizeimage = VENUS_BUFFER_SIZE(color_format, + inst->prop.width[port], inst->prop.height[port]); + + dprintk(VIDC_DBG, + "g_fmt: %x : type %d wxh %dx%d pixelfmt %#x num_planes %d size[0] %d size[1] %d in_reconfig %d\n", + hash32_ptr(inst->session), f->type, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.num_planes, + f->fmt.pix_mp.plane_fmt[0].sizeimage, + f->fmt.pix_mp.plane_fmt[1].sizeimage, inst->in_reconfig); +exit: + return rc; +} +EXPORT_SYMBOL(msm_vidc_g_fmt); + +int msm_vidc_s_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return msm_comm_s_ctrl(instance, control); +} +EXPORT_SYMBOL(msm_vidc_s_ctrl); + +int msm_vidc_g_crop(void *instance, struct v4l2_crop *crop) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !crop) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_ENCODER) { + dprintk(VIDC_ERR, + "Session = %pK : Encoder Crop is not implemented yet\n", + inst); + return -EPERM; + } + + crop->c.left = inst->prop.crop_info.left; + crop->c.top = inst->prop.crop_info.top; + crop->c.width = inst->prop.crop_info.width; + crop->c.height = inst->prop.crop_info.height; + + return 0; +} +EXPORT_SYMBOL(msm_vidc_g_crop); + +int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_vidc_inst *inst = instance; + struct v4l2_ctrl *ctrl = NULL; + int rc = 0; + + if (!inst || !control) + return -EINVAL; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, control->id); + if (ctrl) { + rc = try_get_ctrl(inst, ctrl); + if (!rc) + control->value = ctrl->val; + } + + return rc; +} +EXPORT_SYMBOL(msm_vidc_g_ctrl); + +int msm_vidc_g_ext_ctrl(void *instance, struct v4l2_ext_controls *control) +{ + struct msm_vidc_inst *inst = instance; + struct v4l2_ext_control *ext_control; + int i = 0, rc = 0; + + if (!inst || !control) + return -EINVAL; + + ext_control = control->controls; + + for (i = 0; i < control->count; i++) { + switch (ext_control[i].id) { + default: + dprintk(VIDC_ERR, + "This control %x is not supported yet\n", + ext_control[i].id); + break; + } + } + return rc; +} +EXPORT_SYMBOL(msm_vidc_g_ext_ctrl); + +int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_ext_ctrl(instance, control); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_ext_ctrl(instance, control); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_s_ext_ctrl); + +int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b) +{ + struct msm_vidc_inst *inst = instance; + struct buf_queue *q = NULL; + int rc = 0; + + if (!inst || !b) + return -EINVAL; + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", + b->type); + return -EINVAL; + } + + mutex_lock(&q->lock); + rc = vb2_reqbufs(&q->vb2_bufq, b); + mutex_unlock(&q->lock); + + if (rc) + dprintk(VIDC_ERR, "Failed to get reqbufs, %d\n", rc); + return rc; +} +EXPORT_SYMBOL(msm_vidc_reqbufs); + +static bool valid_v4l2_buffer(struct v4l2_buffer *b, + struct msm_vidc_inst *inst) +{ + enum vidc_ports port = + !V4L2_TYPE_IS_MULTIPLANAR(b->type) ? MAX_PORT_NUM : + b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? CAPTURE_PORT : + b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? OUTPUT_PORT : + MAX_PORT_NUM; + + return port != MAX_PORT_NUM && + inst->bufq[port].num_planes == b->length; +} + +int msm_vidc_release_buffer(void *instance, int type, unsigned int index) +{ + int rc = 0; + struct msm_vidc_inst *inst = instance; + struct msm_vidc_buffer *mbuf, *dummy; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid inst\n", __func__); + return -EINVAL; + } + + if (!inst->in_reconfig && + inst->state > MSM_VIDC_LOAD_RESOURCES && + inst->state < MSM_VIDC_RELEASE_RESOURCES_DONE) { + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to move inst: %pK to rel res done\n", + __func__, inst); + } + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(mbuf, dummy, &inst->registeredbufs.list, + list) { + struct vb2_buffer *vb2 = &mbuf->vvb.vb2_buf; + + if (vb2->type != type || vb2->index != index) + continue; + + if (mbuf->flags & MSM_VIDC_FLAG_RBR_PENDING) { + print_vidc_buffer(VIDC_DBG, + "skip rel buf (rbr pending)", inst, mbuf); + continue; + } + + print_vidc_buffer(VIDC_DBG, "release buf", inst, mbuf); + msm_comm_unmap_vidc_buffer(inst, mbuf); + list_del(&mbuf->list); + kref_put_mbuf(mbuf); + } + mutex_unlock(&inst->registeredbufs.lock); + + return rc; +} +EXPORT_SYMBOL(msm_vidc_release_buffer); + +int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0, i = 0; + struct buf_queue *q = NULL; + struct vidc_tag_data tag_data; + u32 cr = 0; + + if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst)) { + dprintk(VIDC_ERR, "%s: invalid params, inst %pK\n", + __func__, inst); + return -EINVAL; + } + + if (!IS_ALIGNED(b->m.planes[0].length, SZ_4K)) { + dprintk(VIDC_ERR, "qbuf: %x: buffer size not 4K aligned - %u\n", + hash32_ptr(inst->session), b->m.planes[0].length); + return -EINVAL; + } + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", b->type); + return -EINVAL; + } + mutex_lock(&q->lock); + + if ((inst->out_flush && b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || inst->in_flush) { + dprintk(VIDC_ERR, + "%s: %x: in flush, discarding qbuf, type %u, index %u\n", + __func__, hash32_ptr(inst->session), b->type, b->index); + return -EINVAL; + } + + for (i = 0; i < b->length; i++) { + b->m.planes[i].m.fd = b->m.planes[i].reserved[0]; + b->m.planes[i].data_offset = b->m.planes[i].reserved[1]; + } + + /* Compression ratio is valid only for Encoder YUV buffers. */ + if (inst->session_type == MSM_VIDC_ENCODER && + b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + cr = b->m.planes[0].reserved[2]; + msm_comm_update_input_cr(inst, b->index, cr); + } + + if (inst->session_type == MSM_VIDC_DECODER && + b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + msm_comm_store_mark_data(&inst->etb_data, b->index, + b->m.planes[0].reserved[3], b->m.planes[0].reserved[4]); + } + + tag_data.index = b->index; + tag_data.type = b->type; + tag_data.input_tag = b->m.planes[0].reserved[5]; + tag_data.output_tag = b->m.planes[0].reserved[6]; + msm_comm_store_tags(inst, &tag_data); + + rc = vb2_qbuf(&q->vb2_bufq, b); + if (rc) + dprintk(VIDC_ERR, "Failed to qbuf, %d\n", rc); + + mutex_unlock(&q->lock); + return rc; +} +EXPORT_SYMBOL(msm_vidc_qbuf); + +int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0, i = 0; + struct buf_queue *q = NULL; + struct vidc_tag_data tag_data; + + if (!inst || !b || !valid_v4l2_buffer(b, inst)) { + dprintk(VIDC_ERR, "%s: invalid params, inst %pK\n", + __func__, inst); + return -EINVAL; + } + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", b->type); + return -EINVAL; + } + + mutex_lock(&q->lock); + rc = vb2_dqbuf(&q->vb2_bufq, b, true); + mutex_unlock(&q->lock); + if (rc == -EAGAIN) { + return rc; + } else if (rc) { + dprintk(VIDC_ERR, "Failed to dqbuf, %d\n", rc); + return rc; + } + + for (i = 0; i < b->length; i++) { + b->m.planes[i].reserved[0] = b->m.planes[i].m.fd; + b->m.planes[i].reserved[1] = b->m.planes[i].data_offset; + } + + if (inst->session_type == MSM_VIDC_DECODER && + b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + msm_comm_fetch_mark_data(&inst->fbd_data, b->index, + &b->m.planes[0].reserved[3], + &b->m.planes[0].reserved[4]); + } + + tag_data.index = b->index; + tag_data.type = b->type; + + if (msm_comm_fetch_tags(inst, &tag_data)) { + b->m.planes[0].reserved[5] = tag_data.input_tag; + b->m.planes[0].reserved[6] = tag_data.output_tag; + } else { + b->m.planes[0].reserved[5] = 0; + b->m.planes[0].reserved[6] = 0; + } + + return rc; +} +EXPORT_SYMBOL(msm_vidc_dqbuf); + +int msm_vidc_streamon(void *instance, enum v4l2_buf_type i) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + struct buf_queue *q; + + if (!inst) + return -EINVAL; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + dprintk(VIDC_DBG, "Calling streamon\n"); + mutex_lock(&q->lock); + rc = vb2_streamon(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) { + dprintk(VIDC_ERR, "streamon failed on port: %d\n", i); + msm_comm_kill_session(inst); + } + return rc; +} +EXPORT_SYMBOL(msm_vidc_streamon); + +int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + struct buf_queue *q; + + if (!inst) + return -EINVAL; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + + if (!inst->in_reconfig) { + dprintk(VIDC_DBG, "%s: inst %pK release resources\n", + __func__, inst); + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) + dprintk(VIDC_ERR, + "%s: inst %pK move to rel res done failed\n", + __func__, inst); + } + + dprintk(VIDC_DBG, "Calling streamoff\n"); + mutex_lock(&q->lock); + rc = vb2_streamoff(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "streamoff failed on port: %d\n", i); + return rc; +} +EXPORT_SYMBOL(msm_vidc_streamoff); + +int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize) +{ + struct msm_vidc_inst *inst = instance; + struct msm_vidc_capability *capability = NULL; + + if (!inst || !fsize) { + dprintk(VIDC_ERR, "%s: invalid parameter: %pK %pK\n", + __func__, inst, fsize); + return -EINVAL; + } + if (!inst->core) + return -EINVAL; + + capability = &inst->capability; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = capability->width.min; + fsize->stepwise.max_width = capability->width.max; + fsize->stepwise.step_width = capability->width.step_size; + fsize->stepwise.min_height = capability->height.min; + fsize->stepwise.max_height = capability->height.max; + fsize->stepwise.step_height = capability->height.step_size; + return 0; +} +EXPORT_SYMBOL(msm_vidc_enum_framesizes); + +static void *vidc_get_userptr(struct device *dev, unsigned long vaddr, + unsigned long size, enum dma_data_direction dma_dir) +{ + return (void *)0xdeadbeef; +} + +static void vidc_put_userptr(void *buf_priv) +{ +} + +static const struct vb2_mem_ops msm_vidc_vb2_mem_ops = { + .get_userptr = vidc_get_userptr, + .put_userptr = vidc_put_userptr, +}; + +static void msm_vidc_cleanup_buffer(struct vb2_buffer *vb) +{ + int rc = 0; + struct buf_queue *q = NULL; + struct msm_vidc_inst *inst = NULL; + + if (!vb) { + dprintk(VIDC_ERR, "%s : Invalid vb pointer %pK", + __func__, vb); + return; + } + + inst = vb2_get_drv_priv(vb->vb2_queue); + if (!inst) { + dprintk(VIDC_ERR, "%s : Invalid inst pointer", + __func__); + return; + } + + q = msm_comm_get_vb2q(inst, vb->type); + if (!q) { + dprintk(VIDC_ERR, + "%s : Failed to find buffer queue for type = %d\n", + __func__, vb->type); + return; + } + + if (q->vb2_bufq.streaming) { + dprintk(VIDC_DBG, "%d PORT is streaming\n", + vb->type); + return; + } + + rc = msm_vidc_release_buffer(inst, vb->type, vb->index); + if (rc) + dprintk(VIDC_ERR, "%s : Failed to release buffers : %d\n", + __func__, rc); +} + +static int msm_vidc_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct msm_vidc_inst *inst; + int i, rc = 0; + struct hal_buffer_requirements *bufreq; + enum hal_buffer buffer_type; + + if (!q || !num_buffers || !num_planes + || !sizes || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK, %pK, %pK\n", + q, num_buffers, num_planes); + return -EINVAL; + } + inst = q->drv_priv; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_INPUT); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements : %x\n", + HAL_BUFFER_INPUT); + return -EINVAL; + } + if (*num_buffers < bufreq->buffer_count_min_host) { + dprintk(VIDC_DBG, + "Client passed num buffers %d less than the min_host count %d\n", + *num_buffers, bufreq->buffer_count_min_host); + } + *num_planes = inst->bufq[OUTPUT_PORT].num_planes; + if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS || + *num_buffers > MAX_NUM_OUTPUT_BUFFERS) + bufreq->buffer_count_actual = *num_buffers = + MIN_NUM_OUTPUT_BUFFERS; + for (i = 0; i < *num_planes; i++) + sizes[i] = inst->bufq[OUTPUT_PORT].plane_sizes[i]; + + bufreq->buffer_count_actual = *num_buffers; + rc = msm_comm_set_buffer_count(inst, + bufreq->buffer_count_min, + bufreq->buffer_count_actual, HAL_BUFFER_INPUT); + } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: { + buffer_type = msm_comm_get_hal_output_buffer(inst); + bufreq = get_buff_req_buffer(inst, + buffer_type); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed : No buffer requirements : %x\n", + buffer_type); + return -EINVAL; + } + if (inst->session_type != MSM_VIDC_DECODER && + inst->state > MSM_VIDC_LOAD_RESOURCES_DONE) { + if (*num_buffers < bufreq->buffer_count_min_host) { + dprintk(VIDC_DBG, + "Client passed num buffers %d less than the min_host count %d\n", + *num_buffers, + bufreq->buffer_count_min_host); + } + } + *num_planes = inst->bufq[CAPTURE_PORT].num_planes; + if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS || + *num_buffers > MAX_NUM_CAPTURE_BUFFERS) + bufreq->buffer_count_actual = *num_buffers = + MIN_NUM_CAPTURE_BUFFERS; + + for (i = 0; i < *num_planes; i++) + sizes[i] = inst->bufq[CAPTURE_PORT].plane_sizes[i]; + + bufreq->buffer_count_actual = *num_buffers; + rc = msm_comm_set_buffer_count(inst, + bufreq->buffer_count_min, + bufreq->buffer_count_actual, buffer_type); + } + break; + default: + dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type); + rc = -EINVAL; + break; + } + + dprintk(VIDC_DBG, + "queue_setup: %x : type %d num_buffers %d num_planes %d sizes[0] %d sizes[1] %d\n", + hash32_ptr(inst->session), q->type, *num_buffers, + *num_planes, sizes[0], sizes[1]); + return rc; +} + +static inline int msm_vidc_verify_buffer_counts(struct msm_vidc_inst *inst) +{ + int rc = 0, i = 0; + + /* For decoder No need to sanity till LOAD_RESOURCES */ + if (inst->session_type == MSM_VIDC_DECODER && + (inst->state < MSM_VIDC_LOAD_RESOURCES_DONE || + inst->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)) { + dprintk(VIDC_DBG, + "No need to verify buffer counts : %pK\n", inst); + return 0; + } + + for (i = 0; i < HAL_BUFFER_MAX; i++) { + struct hal_buffer_requirements *req = &inst->buff_req.buffer[i]; + + if (req && (msm_comm_get_hal_output_buffer(inst) == + req->buffer_type)) { + dprintk(VIDC_DBG, "Verifying Buffer : %d\n", + req->buffer_type); + if (req->buffer_count_actual < + req->buffer_count_min_host || + req->buffer_count_min_host < + req->buffer_count_min) { + + dprintk(VIDC_ERR, + "Invalid data : Counts mismatch\n"); + dprintk(VIDC_ERR, + "Min Count = %d ", + req->buffer_count_min); + dprintk(VIDC_ERR, + "Min Host Count = %d ", + req->buffer_count_min_host); + dprintk(VIDC_ERR, + "Min Actual Count = %d\n", + req->buffer_count_actual); + rc = -EINVAL; + break; + } + } + } + return rc; +} + +int msm_vidc_set_internal_config(struct msm_vidc_inst *inst) +{ + int rc = 0; + u32 rc_mode; + bool set_rc = false; + struct hal_vbv_hdr_buf_size hrd_buf_size; + struct hal_enable latency; + struct hfi_device *hdev; + struct hal_multi_slice_control multi_slice_control; + u32 codec; + u32 mbps, mb_per_frame, fps, bitrate; + u32 slice_val, slice_mode, max_avg_slicesize; + u32 output_width, output_height; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__); + return -EINVAL; + } + + if (inst->session_type != MSM_VIDC_ENCODER) + return rc; + + hdev = inst->core->device; + + codec = inst->fmts[CAPTURE_PORT].fourcc; + rc_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + latency.enable = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE); + + if (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR) { + rc_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_MBR; + set_rc = true; + } else if (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + latency.enable == V4L2_MPEG_MSM_VIDC_ENABLE && + codec != V4L2_PIX_FMT_VP8) { + rc_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + set_rc = true; + } + + if (set_rc) { + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VENC_RATE_CONTROL, + (void *)&rc_mode); + } + + output_height = inst->prop.height[CAPTURE_PORT]; + output_width = inst->prop.width[CAPTURE_PORT]; + fps = inst->prop.fps; + mbps = NUM_MBS_PER_SEC(output_height, output_width, fps); + if ((rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || + rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR) && + (codec != V4L2_PIX_FMT_VP8)) { + if ((rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && + mbps <= CBR_MB_LIMIT) || + (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR && + mbps <= CBR_VFR_MB_LIMIT)) + hrd_buf_size.vbv_hdr_buf_size = 500; + else + hrd_buf_size.vbv_hdr_buf_size = 1000; + dprintk(VIDC_DBG, "Enable hdr_buf_size %d :\n", + hrd_buf_size.vbv_hdr_buf_size); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_CONFIG_VENC_VBV_HRD_BUF_SIZE, + (void *)&hrd_buf_size); + + latency.enable = V4L2_MPEG_MSM_VIDC_ENABLE; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VENC_LOW_LATENCY, + (void *)&latency); + + inst->clk_data.low_latency_mode = latency.enable; + } + + /* Update Slice Config */ + slice_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + + if ((codec == V4L2_PIX_FMT_H264 || codec == V4L2_PIX_FMT_HEVC) && + slice_mode != V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE) { + bitrate = inst->clk_data.bitrate; + mb_per_frame = NUM_MBS_PER_FRAME(output_height, output_width); + + if (rc_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_RC_OFF && + rc_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR && + rc_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) { + slice_mode = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE; + slice_val = 0; + } else if (slice_mode == + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (output_width > 3840 || output_height > 3840 || + mb_per_frame > NUM_MBS_PER_FRAME(3840, 2160) || + fps > 60) { + slice_mode = + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE; + slice_val = 0; + } else { + slice_val = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB); + slice_val = max(slice_val, mb_per_frame / 10); + } + } else { + if (output_width > 1920 || output_height > 1920 || + mb_per_frame > NUM_MBS_PER_FRAME(1920, 1088) || + fps > 30) { + slice_mode = + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE; + slice_val = 0; + } else { + slice_val = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES); + max_avg_slicesize = ((bitrate / fps) / 8) / 10; + slice_val = + max(slice_val, max_avg_slicesize); + } + } + + multi_slice_control.multi_slice = slice_mode; + multi_slice_control.slice_size = slice_val; + + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VENC_MULTI_SLICE_CONTROL, + (void *)&multi_slice_control); + } + return rc; +} + +static int msm_vidc_set_rotation(struct msm_vidc_inst *inst) +{ + int rc = 0; + int value = 0; + struct hfi_device *hdev; + struct hal_vpe_rotation vpe_rotation; + struct hal_frame_size frame_sz; + + hdev = inst->core->device; + + /* Set rotation and flip first */ + value = msm_comm_g_ctrl_for_id(inst, V4L2_CID_ROTATE); + if (value < 0) { + dprintk(VIDC_ERR, "Get control for rotation failed\n"); + return value; + } + vpe_rotation.rotate = value; + value = msm_comm_g_ctrl_for_id(inst, V4L2_CID_MPEG_VIDC_VIDEO_FLIP); + if (value < 0) { + dprintk(VIDC_ERR, "Get control for flip failed\n"); + return value; + } + vpe_rotation.flip = value; + dprintk(VIDC_DBG, "Set rotation = %d, flip = %d for capture port.\n", + vpe_rotation.rotate, vpe_rotation.flip); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, + HAL_PARAM_VPE_ROTATION, &vpe_rotation); + if (rc) { + dprintk(VIDC_ERR, "Set rotation/flip at start stream failed\n"); + return rc; + } + + /* flip the output resolution if required */ + if (vpe_rotation.rotate == 90 || vpe_rotation.rotate == 270) { + frame_sz.buffer_type = HAL_BUFFER_OUTPUT; + frame_sz.width = inst->prop.height[CAPTURE_PORT]; + frame_sz.height = inst->prop.width[CAPTURE_PORT]; + dprintk(VIDC_DBG, "CAPTURE port width = %d, height = %d\n", + frame_sz.width, frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set framesize for CAPTURE port\n"); + return rc; + } + } + return rc; +} + +static inline int start_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_buffer_size_minimum b; + struct hal_buffer_requirements *bufreq; + u32 rc_mode; + int value = 0; + + dprintk(VIDC_DBG, "%s: %x : inst %pK\n", __func__, + hash32_ptr(inst->session), inst); + hdev = inst->core->device; + + if (inst->session_type == MSM_VIDC_ENCODER) { + rc = msm_vidc_set_rotation(inst); + if (rc) { + dprintk(VIDC_ERR, + "Set rotation for encoder failed\n"); + goto fail_start; + } + } + + rc_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + /* HEIC HW/FWK tiling encode is supported only for CQ RC mode */ + if (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) { + if (!heic_encode_session_supported(inst)) { + dprintk(VIDC_ERR, + "HEIC Encode session not supported\n"); + return -ENOTSUPP; + } + } + + value = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VENC_BITRATE_SAVINGS); + if (!value && rc_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { + struct hal_enable enable; + + dprintk(VIDC_INFO, + "Force enable bitrate savings for non-VBR_CFR\n"); + enable.enable = 1; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_VENC_BITRATE_SAVINGS, + &enable); + } + + /* Check if current session is under HW capability */ + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "This session is not supported %pK\n", inst); + goto fail_start; + } + + rc = msm_vidc_check_scaling_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "This session scaling is not supported %pK\n", inst); + goto fail_start; + } + + rc = msm_vidc_set_internal_config(inst); + if (rc) { + dprintk(VIDC_ERR, + "Set internal config failed %pK\n", inst); + goto fail_start; + } + + /* Decide work route for current session */ + rc = call_core_op(inst->core, decide_work_route, inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to decide work route for session %pK\n", inst); + goto fail_start; + } + + /* Decide work mode for current session */ + rc = call_core_op(inst->core, decide_work_mode, inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to decide work mode for session %pK\n", inst); + goto fail_start; + } + + if (inst->session_type == MSM_VIDC_DECODER && + !inst->operating_rate_set && !is_realtime_session(inst)) { + inst->clk_data.turbo_mode = true; + dprintk(VIDC_INFO, + "inst(%pK) setting turbo mode "); + } + + /* Assign Core and LP mode for current session */ + rc = msm_vidc_decide_core_and_power_mode(inst); + if (rc) { + dprintk(VIDC_ERR, + "This session can't be submitted to HW %pK\n", inst); + goto fail_start; + } + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + b.buffer_type = HAL_BUFFER_OUTPUT2; + } else { + b.buffer_type = HAL_BUFFER_OUTPUT; + } + + rc = msm_comm_try_get_bufreqs(inst); + + b.buffer_size = inst->bufq[CAPTURE_PORT].plane_sizes[0]; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); + + /* Verify if buffer counts are correct */ + rc = msm_vidc_verify_buffer_counts(inst); + if (rc) { + dprintk(VIDC_ERR, + "This session has mis-match buffer counts%pK\n", inst); + goto fail_start; + } + + if (inst->session_type == MSM_VIDC_DECODER && + msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) { + dprintk(VIDC_ERR, "Buffer requirements failed\n"); + goto fail_start; + } + /* For DPB buffers, Always use min count */ + rc = msm_comm_set_buffer_count(inst, + bufreq->buffer_count_min, + bufreq->buffer_count_min, + HAL_BUFFER_OUTPUT); + if (rc) { + dprintk(VIDC_ERR, + "failed to set buffer count\n"); + goto fail_start; + } + } + + rc = msm_comm_set_scratch_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set scratch buffers: %d\n", rc); + goto fail_start; + } + rc = msm_comm_set_persist_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set persist buffers: %d\n", rc); + goto fail_start; + } + + rc = msm_comm_set_recon_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set recon buffers: %d\n", rc); + goto fail_start; + } + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_comm_set_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set output buffers: %d\n", rc); + goto fail_start; + } + } + + if (is_batching_allowed(inst)) + inst->batch.enable = true; + else + inst->batch.enable = false; + dprintk(VIDC_DBG, "%s: batching %s for inst %pK (%#x)\n", + __func__, inst->batch.enable ? "enabled" : "disabled", + inst, hash32_ptr(inst->session)); + + msm_dcvs_try_enable(inst); + + /* + * For seq_changed_insufficient, driver should set session_continue + * to firmware after the following sequence + * - driver raises insufficient event to v4l2 client + * - all output buffers have been flushed and freed + * - v4l2 client queries buffer requirements and splits/combines OPB-DPB + * - v4l2 client sets new set of buffers to firmware + * - v4l2 client issues CONTINUE to firmware to resume decoding of + * submitted ETBs. + */ + rc = msm_comm_session_continue(inst); + if (rc) + goto fail_start; + + msm_comm_scale_clocks_and_bus(inst); + + rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto fail_start; + } + + msm_clock_data_reset(inst); + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", rc); + goto fail_start; + } + } + +fail_start: + if (rc) + dprintk(VIDC_ERR, "%s: inst %pK session %x failed to start\n", + __func__, inst, hash32_ptr(inst->session)); + return rc; +} + +static int msm_vidc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct msm_vidc_inst *inst; + int rc = 0; + struct hfi_device *hdev; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); + return -EINVAL; + } + inst = q->drv_priv; + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n", + q->type, inst); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + default: + dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type); + rc = -EINVAL; + goto stream_start_failed; + } + if (rc) { + dprintk(VIDC_ERR, + "Streamon failed on: %d capability for inst: %pK\n", + q->type, inst); + goto stream_start_failed; + } + + rc = msm_comm_qbufs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to commit buffers queued before STREAM_ON to hardware: %d\n", + rc); + goto stream_start_failed; + } + + rc = msm_vidc_send_pending_eos_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed : Send pending EOS buffs for Inst = %pK, %d\n", + inst, rc); + goto stream_start_failed; + } + +stream_start_failed: + if (rc) { + struct msm_vidc_buffer *temp, *next; + struct vb2_buffer *vb; + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(temp, next, &inst->registeredbufs.list, + list) { + if (temp->vvb.vb2_buf.type != q->type) + continue; + /* + * queued_list lock is already acquired before + * vb2_stream so no need to acquire it again. + */ + list_for_each_entry(vb, &q->queued_list, queued_entry) { + if (msm_comm_compare_vb2_planes(inst, temp, + vb)) { + print_vb2_buffer(VIDC_ERR, "return vb", + inst, vb); + vb2_buffer_done(vb, + VB2_BUF_STATE_QUEUED); + break; + } + } + msm_comm_unmap_vidc_buffer(inst, temp); + list_del(&temp->list); + kref_put_mbuf(temp); + } + mutex_unlock(&inst->registeredbufs.lock); + } + return rc; +} + +static inline int stop_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + + dprintk(VIDC_DBG, "%s: %x : inst %pK\n", __func__, + hash32_ptr(inst->session), inst); + + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) + dprintk(VIDC_ERR, + "Failed to move inst: %pK to state %d\n", + inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + + msm_clock_data_reset(inst); + + return rc; +} + +static void msm_vidc_stop_streaming(struct vb2_queue *q) +{ + struct msm_vidc_inst *inst; + int rc = 0; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); + return; + } + + inst = q->drv_priv; + dprintk(VIDC_DBG, "Streamoff called on: %d capability\n", q->type); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) + rc = stop_streaming(inst); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (!inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) + rc = stop_streaming(inst); + break; + default: + dprintk(VIDC_ERR, + "Q-type is not supported: %d\n", q->type); + rc = -EINVAL; + break; + } + + msm_comm_scale_clocks_and_bus(inst); + + if (rc) + dprintk(VIDC_ERR, + "Failed STOP Streaming inst = %pK on cap = %d\n", + inst, q->type); +} + +static int msm_vidc_queue_buf(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc = 0; + struct msm_vidc_buffer *mbuf; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + mbuf = msm_comm_get_vidc_buffer(inst, vb2); + if (IS_ERR_OR_NULL(mbuf)) { + /* + * if the buffer has RBR_PENDING flag (-EEXIST) then don't queue + * it now, it will be queued via msm_comm_qbuf_rbr() as part of + * RBR event processing. + */ + if (PTR_ERR(mbuf) == -EEXIST) + return 0; + dprintk(VIDC_ERR, "%s: failed to get vidc-buf\n", __func__); + return -EINVAL; + } + if (!kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__); + return -EINVAL; + } + rc = msm_comm_qbuf(inst, mbuf); + if (rc) + dprintk(VIDC_ERR, "%s: failed qbuf\n", __func__); + kref_put_mbuf(mbuf); + + return rc; +} + +static int msm_vidc_queue_buf_decode_batch(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc; + struct msm_vidc_buffer *mbuf; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + mbuf = msm_comm_get_vidc_buffer(inst, vb2); + if (IS_ERR_OR_NULL(mbuf)) { + dprintk(VIDC_ERR, "%s: failed to get vidc-buf\n", __func__); + return -EINVAL; + } + if (!kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__); + return -EINVAL; + } + /* + * If this buffer has RBR_EPNDING then it will not be queued + * but it may trigger full batch queuing in below function. + */ + rc = msm_comm_qbuf_decode_batch(inst, mbuf); + if (rc) + dprintk(VIDC_ERR, "%s: failed qbuf\n", __func__); + kref_put_mbuf(mbuf); + + return rc; +} + +static int msm_vidc_queue_buf_batch(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_DECODER && + vb2->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + rc = msm_vidc_queue_buf_decode_batch(inst, vb2); + else + rc = msm_vidc_queue_buf(inst, vb2); + + return rc; +} + +static void msm_vidc_buf_queue(struct vb2_buffer *vb2) +{ + int rc = 0; + struct msm_vidc_inst *inst = NULL; + + inst = vb2_get_drv_priv(vb2->vb2_queue); + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid inst\n", __func__); + return; + } + + if (inst->batch.enable) + rc = msm_vidc_queue_buf_batch(inst, vb2); + else + rc = msm_vidc_queue_buf(inst, vb2); + if (rc) { + print_vb2_buffer(VIDC_ERR, "failed vb2-qbuf", inst, vb2); + vb2_buffer_done(vb2, VB2_BUF_STATE_DONE); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + } +} + +static const struct vb2_ops msm_vidc_vb2q_ops = { + .queue_setup = msm_vidc_queue_setup, + .start_streaming = msm_vidc_start_streaming, + .buf_queue = msm_vidc_buf_queue, + .buf_cleanup = msm_vidc_cleanup_buffer, + .stop_streaming = msm_vidc_stop_streaming, +}; + +static inline int vb2_bufq_init(struct msm_vidc_inst *inst, + enum v4l2_buf_type type, enum session_type sess) +{ + struct vb2_queue *q = NULL; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + q = &inst->bufq[CAPTURE_PORT].vb2_bufq; + } else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + q = &inst->bufq[OUTPUT_PORT].vb2_bufq; + } else { + dprintk(VIDC_ERR, "buf_type = %d not recognised\n", type); + return -EINVAL; + } + + q->type = type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + q->ops = &msm_vidc_vb2q_ops; + + q->mem_ops = &msm_vidc_vb2_mem_ops; + q->drv_priv = inst; + q->allow_zero_bytesused = !V4L2_TYPE_IS_OUTPUT(type); + q->copy_timestamp = 1; + return vb2_queue_init(q); +} + +static int setup_event_queue(void *inst, + struct video_device *pvdev) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + v4l2_fh_init(&vidc_inst->event_handler, pvdev); + v4l2_fh_add(&vidc_inst->event_handler); + + return rc; +} + +int msm_vidc_subscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_subscribe(&vidc_inst->event_handler, + sub, MAX_EVENTS, NULL); + return rc; +} +EXPORT_SYMBOL(msm_vidc_subscribe_event); + +int msm_vidc_unsubscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_unsubscribe(&vidc_inst->event_handler, sub); + return rc; +} +EXPORT_SYMBOL(msm_vidc_unsubscribe_event); + +int msm_vidc_dqevent(void *inst, struct v4l2_event *event) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !event) + return -EINVAL; + + rc = v4l2_event_dequeue(&vidc_inst->event_handler, event, false); + return rc; +} +EXPORT_SYMBOL(msm_vidc_dqevent); + +int msm_vidc_private(void *vidc_inst, unsigned int cmd, + struct msm_vidc_arg *arg) +{ + int rc = 0; + struct msm_vidc_inst *inst = (struct msm_vidc_inst *)vidc_inst; + + if (cmd != VIDIOC_VIDEO_CMD) { + dprintk(VIDC_ERR, + "%s: invalid private cmd %#x\n", __func__, cmd); + return -ENOIOCTLCMD; + } + + if (!inst || !arg) { + dprintk(VIDC_ERR, "%s: invalid args\n", __func__); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_CVP) { + rc = msm_vidc_cvp(inst, arg); + } else { + dprintk(VIDC_ERR, + "%s: private cmd %#x not supported for session_type %d\n", + __func__, cmd, inst->session_type); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(msm_vidc_private); +static int msm_vidc_try_set_ctrl(void *instance, struct v4l2_ctrl *ctrl) +{ + struct msm_vidc_inst *inst = instance; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_ctrl(instance, ctrl); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_ctrl(instance, ctrl); + return -EINVAL; +} + +static int msm_vidc_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + + int rc = 0, c = 0; + struct msm_vidc_inst *inst; + const char *ctrl_name = NULL; + + if (!ctrl) { + dprintk(VIDC_ERR, "%s invalid parameters for ctrl\n", __func__); + return -EINVAL; + } + + inst = container_of(ctrl->handler, + struct msm_vidc_inst, ctrl_handler); + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters for inst\n", __func__); + return -EINVAL; + } + + for (c = 0; c < ctrl->ncontrols; ++c) { + if (ctrl->cluster[c]->is_new) { + rc = msm_vidc_try_set_ctrl(inst, ctrl->cluster[c]); + if (rc) { + dprintk(VIDC_ERR, "Failed setting %x\n", + ctrl->cluster[c]->id); + break; + } + } + } + if (rc) { + ctrl_name = v4l2_ctrl_get_name(ctrl->id); + dprintk(VIDC_ERR, "Failed setting control: Inst = %pK (%s)\n", + inst, ctrl_name ? ctrl_name : "Invalid ctrl"); + } + + return rc; +} +static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hal_buffer_requirements *bufreq = NULL; + enum hal_buffer buffer_type; + + switch (ctrl->id) { + + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctrl->val = msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + inst->profile); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + ctrl->val = msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + inst->profile); + break; + case V4L2_CID_MPEG_VIDC_IMG_GRID_ENABLE: + ctrl->val = inst->grid_enable; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctrl->val = msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + inst->level); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + ctrl->val = msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + inst->level); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + ctrl->val = msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + inst->level); + break; + + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + ctrl->val = inst->entropy_mode; + break; + + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + buffer_type = msm_comm_get_hal_output_buffer(inst); + bufreq = get_buff_req_buffer(inst, + buffer_type); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed to find bufreqs for buffer type = %d\n", + buffer_type); + return -EINVAL; + } + ctrl->val = bufreq->buffer_count_min_host; + dprintk(VIDC_DBG, "g_min: %x : hal_buffer %d min buffers %d\n", + hash32_ptr(inst->session), buffer_type, ctrl->val); + break; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_INPUT); + if (!bufreq) { + dprintk(VIDC_ERR, + "Failed to find bufreqs for buffer type = %d\n", + HAL_BUFFER_INPUT); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_DECODER && + !(inst->flags & VIDC_THUMBNAIL) && + inst->fmts[OUTPUT_PORT].fourcc == + V4L2_PIX_FMT_VP9 && + bufreq->buffer_count_min_host < + MIN_NUM_OUTPUT_BUFFERS_VP9) + bufreq->buffer_count_min_host = + MIN_NUM_OUTPUT_BUFFERS_VP9; + + ctrl->val = bufreq->buffer_count_min_host; + dprintk(VIDC_DBG, "g_min: %x : hal_buffer %d min buffers %d\n", + hash32_ptr(inst->session), HAL_BUFFER_INPUT, ctrl->val); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION: + ctrl->val = inst->capability.tme_version; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT: + ctrl->val = + inst->capability.nal_stream_format.nal_stream_format_supported; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE: + if (!inst->core || !inst->core->platform_data) + return -EINVAL; + + ctrl->val = (inst->core->platform_data->vpu_ver == + VPU_VERSION_4) ? + V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BIT : + V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BYTE; + break; + default: + /* + * Other controls aren't really volatile, shouldn't need to + * modify ctrl->value + */ + break; + } + + return rc; +} + +static int msm_vidc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int rc = 0, c = 0; + struct msm_vidc_inst *inst; + struct v4l2_ctrl *master; + + if (!ctrl) { + dprintk(VIDC_ERR, "%s invalid parameters for ctrl\n", __func__); + return -EINVAL; + } + + inst = container_of(ctrl->handler, + struct msm_vidc_inst, ctrl_handler); + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters for inst\n", __func__); + return -EINVAL; + } + master = ctrl->cluster[0]; + if (!master) { + dprintk(VIDC_ERR, "%s invalid parameters for master\n", + __func__); + return -EINVAL; + } + + for (c = 0; c < master->ncontrols; ++c) { + if (master->cluster[c]->flags & V4L2_CTRL_FLAG_VOLATILE) { + rc = try_get_ctrl(inst, master->cluster[c]); + if (rc) { + dprintk(VIDC_ERR, "Failed getting %x\n", + master->cluster[c]->id); + return rc; + } + } + } + if (rc) + dprintk(VIDC_ERR, "Failed getting control: Inst = %pK (%s)\n", + inst, v4l2_ctrl_get_name(ctrl->id)); + return rc; +} + +static const struct v4l2_ctrl_ops msm_vidc_ctrl_ops = { + + .s_ctrl = msm_vidc_op_s_ctrl, + .g_volatile_ctrl = msm_vidc_op_g_volatile_ctrl, +}; + +static void batch_timer_callback(struct timer_list *t) +{ + struct msm_vidc_inst *inst = from_timer(inst, t, batch_timer); + + if (!inst->batch.enable) + return; + + schedule_work(&inst->batch_work); +} + +void *msm_vidc_open(int core_id, int session_type) +{ + struct msm_vidc_inst *inst = NULL; + struct msm_vidc_core *core = NULL; + int rc = 0; + int i = 0; + + if (core_id >= MSM_VIDC_CORES_MAX || + session_type >= MSM_VIDC_MAX_DEVICES) { + dprintk(VIDC_ERR, "Invalid input, core_id = %d, session = %d\n", + core_id, session_type); + goto err_invalid_core; + } + core = get_vidc_core(core_id); + if (!core) { + dprintk(VIDC_ERR, + "Failed to find core for core_id = %d\n", core_id); + goto err_invalid_core; + } + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) { + dprintk(VIDC_ERR, "Failed to allocate memory\n"); + rc = -ENOMEM; + goto err_invalid_core; + } + + pr_info(VIDC_DBG_TAG "Opening video instance: %pK, %d\n", + "info", inst, session_type); + mutex_init(&inst->sync_lock); + mutex_init(&inst->bufq[CAPTURE_PORT].lock); + mutex_init(&inst->bufq[OUTPUT_PORT].lock); + mutex_init(&inst->lock); + + INIT_MSM_VIDC_LIST(&inst->scratchbufs); + INIT_MSM_VIDC_LIST(&inst->freqs); + INIT_MSM_VIDC_LIST(&inst->input_crs); + INIT_MSM_VIDC_LIST(&inst->buffer_tags); + INIT_MSM_VIDC_LIST(&inst->persistbufs); + INIT_MSM_VIDC_LIST(&inst->pending_getpropq); + INIT_MSM_VIDC_LIST(&inst->outputbufs); + INIT_MSM_VIDC_LIST(&inst->registeredbufs); + INIT_MSM_VIDC_LIST(&inst->cvpbufs); + INIT_MSM_VIDC_LIST(&inst->reconbufs); + INIT_MSM_VIDC_LIST(&inst->eosbufs); + INIT_MSM_VIDC_LIST(&inst->etb_data); + INIT_MSM_VIDC_LIST(&inst->fbd_data); + + kref_init(&inst->kref); + + inst->session_type = session_type; + inst->state = MSM_VIDC_CORE_UNINIT_DONE; + inst->core = core; + inst->clk_data.min_freq = 0; + inst->clk_data.curr_freq = 0; + inst->clk_data.ddr_bw = 0; + inst->clk_data.sys_cache_bw = 0; + inst->clk_data.bitrate = 0; + inst->operating_rate_set = false; + inst->clk_data.work_route = 1; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->bit_depth = MSM_VIDC_BIT_DEPTH_8; + inst->pic_struct = MSM_VIDC_PIC_STRUCT_PROGRESSIVE; + inst->colour_space = MSM_VIDC_BT601_6_525; + inst->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + inst->level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + inst->entropy_mode = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC; + inst->max_filled_length = 0; + for (i = SESSION_MSG_INDEX(SESSION_MSG_START); + i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) { + init_completion(&inst->completions[i]); + } + + if (session_type == MSM_VIDC_DECODER) { + msm_vdec_inst_init(inst); + rc = msm_vdec_ctrl_init(inst, &msm_vidc_ctrl_ops); + } else if (session_type == MSM_VIDC_ENCODER) { + msm_venc_inst_init(inst); + rc = msm_venc_ctrl_init(inst, &msm_vidc_ctrl_ops); + } else if (session_type == MSM_VIDC_CVP) { + msm_cvp_inst_init(inst); + rc = msm_cvp_ctrl_init(inst, &msm_vidc_ctrl_ops); + } + if (rc) { + dprintk(VIDC_ERR, "Failed control initialization\n"); + goto fail_bufq_capture; + } + + rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + session_type); + if (rc) { + dprintk(VIDC_ERR, + "Failed to initialize vb2 queue on capture port\n"); + goto fail_bufq_capture; + } + rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + session_type); + if (rc) { + dprintk(VIDC_ERR, + "Failed to initialize vb2 queue on capture port\n"); + goto fail_bufq_output; + } + + setup_event_queue(inst, &core->vdev[session_type].vdev); + + mutex_lock(&core->lock); + list_add_tail(&inst->list, &core->instances); + mutex_unlock(&core->lock); + + rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move video instance to init state\n"); + goto fail_init; + } + + if (msm_comm_check_for_inst_overload(core)) { + dprintk(VIDC_ERR, + "Instance count reached Max limit, rejecting session"); + goto fail_init; + } + + msm_comm_scale_clocks_and_bus(inst); + + inst->debugfs_root = + msm_vidc_debugfs_init_inst(inst, core->debugfs_root); + + if (inst->session_type == MSM_VIDC_CVP) { + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move video instance to open done state\n"); + goto fail_init; + } + } + + INIT_WORK(&inst->batch_work, msm_vidc_batch_handler); + timer_setup(&inst->batch_timer, + batch_timer_callback, 0); + + return inst; +fail_init: + mutex_lock(&core->lock); + list_del(&inst->list); + mutex_unlock(&core->lock); + + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq); +fail_bufq_output: + vb2_queue_release(&inst->bufq[CAPTURE_PORT].vb2_bufq); +fail_bufq_capture: + msm_comm_ctrl_deinit(inst); + mutex_destroy(&inst->sync_lock); + mutex_destroy(&inst->bufq[CAPTURE_PORT].lock); + mutex_destroy(&inst->bufq[OUTPUT_PORT].lock); + mutex_destroy(&inst->lock); + + DEINIT_MSM_VIDC_LIST(&inst->scratchbufs); + DEINIT_MSM_VIDC_LIST(&inst->persistbufs); + DEINIT_MSM_VIDC_LIST(&inst->pending_getpropq); + DEINIT_MSM_VIDC_LIST(&inst->outputbufs); + DEINIT_MSM_VIDC_LIST(&inst->cvpbufs); + DEINIT_MSM_VIDC_LIST(&inst->registeredbufs); + DEINIT_MSM_VIDC_LIST(&inst->eosbufs); + DEINIT_MSM_VIDC_LIST(&inst->freqs); + DEINIT_MSM_VIDC_LIST(&inst->input_crs); + DEINIT_MSM_VIDC_LIST(&inst->buffer_tags); + DEINIT_MSM_VIDC_LIST(&inst->etb_data); + DEINIT_MSM_VIDC_LIST(&inst->fbd_data); + + kfree(inst); + inst = NULL; +err_invalid_core: + return inst; +} +EXPORT_SYMBOL(msm_vidc_open); + +static void msm_vidc_cleanup_instance(struct msm_vidc_inst *inst) +{ + struct msm_vidc_buffer *temp, *dummy; + struct getprop_buf *temp_prop, *dummy_prop; + struct list_head *ptr, *next; + enum vidc_ports ports[] = {OUTPUT_PORT, CAPTURE_PORT}; + int c = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return; + } + + for (c = 0; c < ARRAY_SIZE(ports); ++c) { + enum vidc_ports port = ports[c]; + + mutex_lock(&inst->bufq[port].lock); + list_for_each_safe(ptr, next, + &inst->bufq[port].vb2_bufq.queued_list) { + struct vb2_buffer *vb = container_of(ptr, + struct vb2_buffer, queued_entry); + if (vb->state == VB2_BUF_STATE_ACTIVE) { + vb->planes[0].bytesused = 0; + print_vb2_buffer(VIDC_ERR, "undequeud vb2", + inst, vb); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + } + } + mutex_unlock(&inst->bufq[port].lock); + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(temp, dummy, &inst->registeredbufs.list, + list) { + print_vidc_buffer(VIDC_ERR, "undequeud buf", inst, temp); + msm_comm_unmap_vidc_buffer(inst, temp); + list_del(&temp->list); + kref_put_mbuf(temp); + } + mutex_unlock(&inst->registeredbufs.lock); + + del_timer(&inst->batch_timer); + + cancel_work_sync(&inst->batch_work); + + msm_comm_free_freq_table(inst); + + msm_comm_free_input_cr_table(inst); + + if (msm_comm_release_scratch_buffers(inst, false)) + dprintk(VIDC_ERR, + "Failed to release scratch buffers\n"); + + if (msm_comm_release_recon_buffers(inst)) + dprintk(VIDC_ERR, + "Failed to release recon buffers\n"); + + if (msm_comm_release_persist_buffers(inst)) + dprintk(VIDC_ERR, + "Failed to release persist buffers\n"); + + if (msm_comm_release_mark_data(inst)) + dprintk(VIDC_ERR, + "Failed to release mark_data buffers\n"); + + msm_comm_free_buffer_tags(inst); + + msm_comm_release_eos_buffers(inst); + + if (msm_comm_release_output_buffers(inst, true)) + dprintk(VIDC_ERR, + "Failed to release output buffers\n"); + + if (inst->extradata_handle) + msm_comm_smem_free(inst, inst->extradata_handle); + + mutex_lock(&inst->pending_getpropq.lock); + if (!list_empty(&inst->pending_getpropq.list)) { + dprintk(VIDC_ERR, + "pending_getpropq not empty for instance %pK\n", + inst); + list_for_each_entry_safe(temp_prop, dummy_prop, + &inst->pending_getpropq.list, list) { + kfree(temp_prop->data); + list_del(&temp_prop->list); + kfree(temp_prop); + } + } + mutex_unlock(&inst->pending_getpropq.lock); +} + +int msm_vidc_destroy(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + int i = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + core = inst->core; + + mutex_lock(&core->lock); + /* inst->list lives in core->instances */ + list_del(&inst->list); + mutex_unlock(&core->lock); + + msm_comm_ctrl_deinit(inst); + + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + + for (i = 0; i < MAX_PORT_NUM; i++) + vb2_queue_release(&inst->bufq[i].vb2_bufq); + + DEINIT_MSM_VIDC_LIST(&inst->scratchbufs); + DEINIT_MSM_VIDC_LIST(&inst->persistbufs); + DEINIT_MSM_VIDC_LIST(&inst->pending_getpropq); + DEINIT_MSM_VIDC_LIST(&inst->outputbufs); + DEINIT_MSM_VIDC_LIST(&inst->cvpbufs); + DEINIT_MSM_VIDC_LIST(&inst->registeredbufs); + DEINIT_MSM_VIDC_LIST(&inst->eosbufs); + DEINIT_MSM_VIDC_LIST(&inst->freqs); + DEINIT_MSM_VIDC_LIST(&inst->input_crs); + DEINIT_MSM_VIDC_LIST(&inst->etb_data); + DEINIT_MSM_VIDC_LIST(&inst->fbd_data); + + mutex_destroy(&inst->sync_lock); + mutex_destroy(&inst->bufq[CAPTURE_PORT].lock); + mutex_destroy(&inst->bufq[OUTPUT_PORT].lock); + mutex_destroy(&inst->lock); + + msm_vidc_debugfs_deinit_inst(inst); + + pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n", + "info", inst); + kfree(inst); + return 0; +} + +static void close_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + + msm_vidc_destroy(inst); +} + +int msm_vidc_close(void *instance) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + /* + * Make sure that HW stop working on these buffers that + * we are going to free. + */ + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) + dprintk(VIDC_ERR, + "Failed to move inst %pK to rel resource done state\n", + inst); + + /* + * deinit instance after REL_RES_DONE to ensure hardware + * released all buffers. + */ + if (inst->session_type == MSM_VIDC_CVP) + msm_cvp_inst_deinit(inst); + + msm_vidc_cleanup_instance(inst); + + rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst %pK to uninit state\n", inst); + rc = msm_comm_force_cleanup(inst); + } + + msm_comm_session_clean(inst); + + kref_put(&inst->kref, close_helper); + return 0; +} +EXPORT_SYMBOL(msm_vidc_close); + +int msm_vidc_suspend(int core_id) +{ + return msm_comm_suspend(core_id); +} +EXPORT_SYMBOL(msm_vidc_suspend); + diff --git a/drivers/media/platform/msm/vidc/msm_vidc.h b/drivers/media/platform/msm/vidc/msm_vidc.h new file mode 100644 index 000000000000..bc0c03bd2c3e --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2012-2018, 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_VIDC_H_ +#define _MSM_VIDC_H_ + +#include +#include +#include +#include +#include +#include + +#define HAL_BUFFER_MAX 0xe + +enum smem_type { + SMEM_DMA = 1, +}; + +enum smem_prop { + SMEM_UNCACHED = 0x1, + SMEM_CACHED = 0x2, + SMEM_SECURE = 0x4, + SMEM_ADSP = 0x8, +}; + +/* NOTE: if you change this enum you MUST update the + * "buffer-type-tz-usage-table" for any affected target + * in arch/arm/boot/dts/.dtsi + */ +enum hal_buffer { + HAL_BUFFER_NONE = 0x0, + HAL_BUFFER_INPUT = 0x1, + HAL_BUFFER_OUTPUT = 0x2, + HAL_BUFFER_OUTPUT2 = 0x4, + HAL_BUFFER_EXTRADATA_INPUT = 0x8, + HAL_BUFFER_EXTRADATA_OUTPUT = 0x10, + HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x20, + HAL_BUFFER_INTERNAL_SCRATCH = 0x40, + HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x80, + HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x100, + HAL_BUFFER_INTERNAL_PERSIST = 0x200, + HAL_BUFFER_INTERNAL_PERSIST_1 = 0x400, + HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x800, + HAL_BUFFER_INTERNAL_RECON = 0x1000, +}; + +struct dma_mapping_info { + struct device *dev; + struct iommu_domain *domain; + struct sg_table *table; + struct dma_buf_attachment *attach; + struct dma_buf *buf; + void *cb_info; +}; + +struct msm_smem { + u32 refcount; + int fd; + void *dma_buf; + void *kvaddr; + u32 device_addr; + dma_addr_t dma_handle; + unsigned int offset; + unsigned int size; + unsigned long flags; + enum hal_buffer buffer_type; + struct dma_mapping_info mapping_info; +}; + +enum smem_cache_ops { + SMEM_CACHE_CLEAN, + SMEM_CACHE_INVALIDATE, + SMEM_CACHE_CLEAN_INVALIDATE, +}; + +enum core_id { + MSM_VIDC_CORE_VENUS = 0, + MSM_VIDC_CORE_Q6, + MSM_VIDC_CORES_MAX, +}; +enum session_type { + MSM_VIDC_ENCODER = 0, + MSM_VIDC_DECODER, + MSM_VIDC_CVP, + MSM_VIDC_UNKNOWN, + MSM_VIDC_MAX_DEVICES = MSM_VIDC_UNKNOWN, +}; + +union msm_v4l2_cmd { + struct v4l2_decoder_cmd dec; + struct v4l2_encoder_cmd enc; +}; + +void *msm_vidc_open(int core_id, int session_type); +int msm_vidc_close(void *instance); +int msm_vidc_suspend(int core_id); +int msm_vidc_querycap(void *instance, struct v4l2_capability *cap); +int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_vidc_s_fmt(void *instance, struct v4l2_format *f); +int msm_vidc_g_fmt(void *instance, struct v4l2_format *f); +int msm_vidc_s_ctrl(void *instance, struct v4l2_control *a); +int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_vidc_g_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_vidc_g_ctrl(void *instance, struct v4l2_control *a); +int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_vidc_release_buffer(void *instance, int buffer_type, + unsigned int buffer_index); +int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b); +int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b); +int msm_vidc_streamon(void *instance, enum v4l2_buf_type i); +int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl); +int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i); +int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd); +int msm_vidc_poll(void *instance, struct file *filp, + struct poll_table_struct *pt); +int msm_vidc_subscribe_event(void *instance, + const struct v4l2_event_subscription *sub); +int msm_vidc_unsubscribe_event(void *instance, + const struct v4l2_event_subscription *sub); +int msm_vidc_dqevent(void *instance, struct v4l2_event *event); +int msm_vidc_g_crop(void *instance, struct v4l2_crop *a); +int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize); +int msm_vidc_private(void *vidc_inst, unsigned int cmd, + struct msm_vidc_arg *arg); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_ar50_dyn_gov.c b/drivers/media/platform/msm/vidc/msm_vidc_ar50_dyn_gov.c new file mode 100644 index 000000000000..6f0e89cc4c0e --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_ar50_dyn_gov.c @@ -0,0 +1,898 @@ +/* Copyright (c) 2015-2019, 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 +#include "msm_vidc_debug.h" +#include "fixedpoint.h" +#include "msm_vidc_internal.h" +#include "vidc_hfi_api.h" +#define COMPRESSION_RATIO_MAX 5 + +static bool debug_ar50; +module_param(debug_ar50, bool, 0644); + +enum vidc_bus_type { + PERF, + DDR, + LLCC, +}; + +/* + * Minimum dimensions for which to calculate bandwidth. + * This means that anything bandwidth(0, 0) == + * bandwidth(BASELINE_DIMENSIONS.width, BASELINE_DIMENSIONS.height) + */ +static const struct { + int height, width; +} BASELINE_DIMENSIONS = { + .width = 1280, + .height = 720, +}; + +/* converts Mbps to bps (the "b" part can be bits or bytes based on context) */ +#define kbps(__mbps) ((__mbps) * 1000) +#define bps(__mbps) (kbps(__mbps) * 1000) + +#define GENERATE_COMPRESSION_PROFILE(__bpp, __worst) { \ + .bpp = __bpp, \ + .ratio = __worst, \ +} + +/* + * The below table is a structural representation of the following table: + * Resolution | Bitrate | Compression Ratio | + * ............|............|.........................................| + * Width Height|Average High|Avg_8bpc Worst_8bpc Avg_10bpc Worst_10bpc| + * 1280 720| 7 14| 1.69 1.28 1.49 1.23| + * 1920 1080| 20 40| 1.69 1.28 1.49 1.23| + * 2560 1440| 32 64| 2.2 1.26 1.97 1.22| + * 3840 2160| 42 84| 2.2 1.26 1.97 1.22| + * 4096 2160| 44 88| 2.2 1.26 1.97 1.22| + * 4096 2304| 48 96| 2.2 1.26 1.97 1.22| + */ +static struct lut { + int frame_size; /* width x height */ + int frame_rate; + unsigned long bitrate; + struct { + int bpp; + fp_t ratio; + } compression_ratio[COMPRESSION_RATIO_MAX]; +} const LUT[] = { + { + .frame_size = 1280 * 720, + .frame_rate = 30, + .bitrate = 14, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1280 * 720, + .frame_rate = 60, + .bitrate = 22, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1920 * 1088, + .frame_rate = 30, + .bitrate = 40, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1920 * 1088, + .frame_rate = 60, + .bitrate = 64, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 2560 * 1440, + .frame_rate = 30, + .bitrate = 64, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 2560 * 1440, + .frame_rate = 60, + .bitrate = 102, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 3840 * 2160, + .frame_rate = 30, + .bitrate = 84, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 3840 * 2160, + .frame_rate = 60, + .bitrate = 134, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2160, + .frame_rate = 30, + .bitrate = 88, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2160, + .frame_rate = 60, + .bitrate = 141, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2304, + .frame_rate = 30, + .bitrate = 96, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2304, + .frame_rate = 60, + .bitrate = 154, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, +}; + +static u32 get_type_frm_name(char *name) +{ + if (!strcmp(name, "venus-ar50-llcc")) + return LLCC; + else if (!strcmp(name, "venus-ar50-ddr")) + return DDR; + else + return PERF; +} + +static struct lut const *__lut(int width, int height, int fps) +{ + int frame_size = height * width, c = 0; + + do { + if (LUT[c].frame_size >= frame_size && LUT[c].frame_rate >= fps) + return &LUT[c]; + } while (++c < ARRAY_SIZE(LUT)); + + return &LUT[ARRAY_SIZE(LUT) - 1]; +} + +static fp_t __compression_ratio(struct lut const *entry, int bpp) +{ + int c = 0; + + for (c = 0; c < COMPRESSION_RATIO_MAX; ++c) { + if (entry->compression_ratio[c].bpp == bpp) + return entry->compression_ratio[c].ratio; + } + + WARN(true, "Shouldn't be here, LUT possibly corrupted?\n"); + return FP_ZERO; /* impossible */ +} + +#define DUMP_HEADER_MAGIC 0xdeadbeef +#define DUMP_FP_FMT "%FP" /* special format for fp_t */ +struct dump { + char *key; + char *format; + size_t val; +}; + +static void __dump(struct dump dump[], int len) +{ + int c = 0; + + for (c = 0; c < len; ++c) { + char format_line[128] = "", formatted_line[128] = ""; + + if (dump[c].val == DUMP_HEADER_MAGIC) { + snprintf(formatted_line, sizeof(formatted_line), "%s\n", + dump[c].key); + } else { + bool fp_format = !strcmp(dump[c].format, DUMP_FP_FMT); + + if (!fp_format) { + snprintf(format_line, sizeof(format_line), + " %-35s: %s\n", dump[c].key, + dump[c].format); + snprintf(formatted_line, sizeof(formatted_line), + format_line, dump[c].val); + } else { + size_t integer_part, fractional_part; + + integer_part = fp_int(dump[c].val); + fractional_part = fp_frac(dump[c].val); + snprintf(formatted_line, sizeof(formatted_line), + " %-35s: %zd + %zd/%zd\n", + dump[c].key, integer_part, + fractional_part, + fp_frac_base()); + + + } + } + + dprintk(VIDC_DBG, "%s", formatted_line); + } +} + +static unsigned long __calculate_vpe(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + return 0; +} + +static bool __ubwc(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12_UBWC: + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + return true; + default: + return false; + } +} + +static int __bpp(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12: + case HAL_COLOR_FORMAT_NV21: + case HAL_COLOR_FORMAT_NV12_UBWC: + return 8; + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + case HAL_COLOR_FORMAT_P010: + return 10; + default: + dprintk(VIDC_ERR, + "What's this? We don't support this colorformat (%x)", + f); + return INT_MAX; + } +} + +static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Decoder parameters */ + int width, height, lcu_size, dpb_bpp, opb_bpp, fps, opb_factor; + bool unified_dpb_opb, dpb_compression_enabled, opb_compression_enabled, + llc_ref_read_l2_cache_enabled = false, + llc_vpss_ds_line_buf_enabled = false; + fp_t dpb_opb_scaling_ratio, dpb_read_compression_factor, + dpb_write_compression_factor, opb_compression_factor, + qsmmu_bw_overhead_factor, height_ratio; + + /* Derived parameters */ + int lcu_per_frame, tnbr_per_lcu, colocated_bytes_per_lcu; + unsigned long bitrate; + + fp_t bins_to_bit_factor, dpb_write_factor, ten_bpc_packing_factor, + ten_bpc_bpp_factor, vsp_read_factor, vsp_write_factor, + bw_for_1x_8bpc, dpb_bw_for_1x, + motion_vector_complexity = 0, row_cache_penalty = 0, opb_bw = 0, + dpb_total = 0; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + line_buffer_read, line_buffer_write, recon_read, + recon_write, opb_read, opb_write, dpb_read, dpb_write, + total; + } ddr = {0}; + + struct { + fp_t dpb_read, opb_read, total; + } llc = {0}; + + unsigned long ret = 0; + unsigned int integer_part, frac_part; + + width = max(d->input_width, BASELINE_DIMENSIONS.width); + height = max(d->input_height, BASELINE_DIMENSIONS.height); + + lcu_size = d->lcu_size; + + dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; + opb_bpp = d->num_formats >= 2 ? __bpp(d->color_formats[1]) : dpb_bpp; + + fps = d->fps; + + unified_dpb_opb = d->num_formats == 1; + + dpb_opb_scaling_ratio = fp_div(FP_INT( + (int)(d->input_width * d->input_height)), + FP_INT((int)(d->output_width * d->output_height))); + height_ratio = fp_div(d->input_height, d->output_height); + + dpb_compression_enabled = d->num_formats >= 1 && + __ubwc(d->color_formats[0]); + opb_compression_enabled = d->num_formats >= 2 && + __ubwc(d->color_formats[1]); + + /* + * Convert Q16 number into Integer and Fractional part upto 2 places. + * Ex : 105752 / 65536 = 1.61; 1.61 in Q16 = 105752; + * Integer part = 105752 / 65536 = 1; + * Reminder = 105752 - 1 * 65536 = 40216; + * Fractional part = 40216 * 100 / 65536 = 61; + * Now converto to FP(1, 61, 100) for below code. + */ + + integer_part = d->compression_ratio >> 16; + frac_part = + ((d->compression_ratio - (integer_part << 16)) * 100) >> 16; + + dpb_read_compression_factor = FP(integer_part, frac_part, 100); + + integer_part = d->complexity_factor >> 16; + frac_part = + ((d->complexity_factor - (integer_part << 16)) * 100) >> 16; + + motion_vector_complexity = FP(integer_part, frac_part, 100); + + dpb_write_compression_factor = dpb_read_compression_factor; + + opb_compression_factor = !opb_compression_enabled ? FP_ONE : + dpb_write_compression_factor; + + llc_ref_read_l2_cache_enabled = llc_vpss_ds_line_buf_enabled = false; + if (d->use_sys_cache) { + llc_ref_read_l2_cache_enabled = true; + llc_vpss_ds_line_buf_enabled = true; + } + + /* Derived parameters setup */ + lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size); + + bitrate = (d->bitrate + 1000000 - 1) / 1000000; + + bins_to_bit_factor = d->work_mode == VIDC_WORK_MODE_1 ? + FP_INT(0) : FP_INT(4); + + vsp_read_factor = bins_to_bit_factor + FP_INT(2); + + dpb_write_factor = FP(1, 5, 100); + + ten_bpc_packing_factor = FP(1, 67, 1000); + ten_bpc_bpp_factor = FP(1, 1, 4); + + vsp_write_factor = bins_to_bit_factor; + + tnbr_per_lcu = lcu_size == 16 ? 128 : + lcu_size == 32 ? 64 : 128; + + colocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; + + /* ........................................ for DDR */ + ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), + vsp_read_factor), FP_INT(8)); + ddr.vsp_write = fp_div(fp_mult(FP_INT(bitrate), + vsp_write_factor), FP_INT(8)); + + ddr.collocated_read = FP_INT(lcu_per_frame * + colocated_bytes_per_lcu * fps / bps(1)); + ddr.collocated_write = FP_INT(lcu_per_frame * + colocated_bytes_per_lcu * fps / bps(1)); + + ddr.line_buffer_read = FP_INT(tnbr_per_lcu * + lcu_per_frame * fps / bps(1)); + ddr.line_buffer_write = ddr.line_buffer_read; + + bw_for_1x_8bpc = fp_div(FP_INT((int)(width * height)), FP_INT(32 * 8)); + + bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc, + fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000))); + + dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc : + fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor, + ten_bpc_bpp_factor)); + + ddr.dpb_read = fp_div(fp_mult(fp_mult(dpb_bw_for_1x, + motion_vector_complexity), dpb_write_factor), + dpb_read_compression_factor); + + ddr.dpb_write = fp_div(fp_mult(dpb_bw_for_1x, dpb_write_factor), + dpb_write_compression_factor); + dpb_total = ddr.dpb_read + ddr.dpb_write; + if (llc_ref_read_l2_cache_enabled) { + row_cache_penalty = FP(1, 30, 100); + ddr.dpb_read = fp_div(ddr.dpb_read, row_cache_penalty); + llc.dpb_read = dpb_total - ddr.dpb_write - ddr.dpb_read; + } + + opb_factor = dpb_bpp == 8 ? 8 : 4; + + ddr.opb_read = unified_dpb_opb ? 0 : opb_compression_enabled ? + fp_div(fp_mult(fp_div(dpb_bw_for_1x, dpb_opb_scaling_ratio), + FP_INT(opb_factor)), height_ratio) : 0; + ddr.opb_write = unified_dpb_opb ? 0 : opb_compression_enabled ? + ddr.dpb_read : fp_div(fp_div(fp_mult(dpb_bw_for_1x, + FP(1, 50, 100)), dpb_opb_scaling_ratio), + opb_compression_factor); + + if (llc_vpss_ds_line_buf_enabled) { + llc.opb_read = ddr.opb_read; + ddr.opb_write -= ddr.opb_read; + ddr.opb_read = 0; + } + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.line_buffer_read + ddr.line_buffer_write + + ddr.opb_read + ddr.opb_write + + ddr.dpb_read + ddr.dpb_write; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + llc.total = llc.dpb_read + llc.opb_read + ddr.total; + + /* Dump all the variables for easier debugging */ + if (debug_ar50) { + struct dump dump[] = { + {"DECODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"LCU size", "%d", lcu_size}, + {"DPB bitdepth", "%d", dpb_bpp}, + {"frame rate", "%d", fps}, + {"DPB/OPB unified", "%d", unified_dpb_opb}, + {"DPB/OPB downscaling ratio", DUMP_FP_FMT, + dpb_opb_scaling_ratio}, + {"DPB compression", "%d", dpb_compression_enabled}, + {"OPB compression", "%d", opb_compression_enabled}, + {"DPB Read compression factor", DUMP_FP_FMT, + dpb_read_compression_factor}, + {"DPB Write compression factor", DUMP_FP_FMT, + dpb_write_compression_factor}, + {"OPB compression factor", DUMP_FP_FMT, + opb_compression_factor}, + {"frame width", "%d", width}, + {"frame height", "%d", height}, + + {"DERIVED PARAMETERS (1)", "", DUMP_HEADER_MAGIC}, + {"LCUs/frame", "%d", lcu_per_frame}, + {"bitrate (Mbit/sec)", "%d", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"DPB write factor", DUMP_FP_FMT, dpb_write_factor}, + {"10bpc packing factor", DUMP_FP_FMT, + ten_bpc_packing_factor}, + {"10bpc,BPP factor", DUMP_FP_FMT, ten_bpc_bpp_factor}, + {"VSP read factor", DUMP_FP_FMT, vsp_read_factor}, + {"VSP write factor", DUMP_FP_FMT, vsp_write_factor}, + {"TNBR/LCU", "%d", tnbr_per_lcu}, + {"colocated bytes/LCU", "%d", colocated_bytes_per_lcu}, + {"B/W for 1x (NV12 8bpc)", DUMP_FP_FMT, bw_for_1x_8bpc}, + {"DPB B/W For 1x (NV12)", DUMP_FP_FMT, dpb_bw_for_1x}, + + {"DERIVED PARAMETERS (2)", "", DUMP_HEADER_MAGIC}, + {"MV complexity", DUMP_FP_FMT, motion_vector_complexity}, + {"row cache penalty", DUMP_FP_FMT, row_cache_penalty}, + {"qsmmu_bw_overhead_factor", DUMP_FP_FMT, + qsmmu_bw_overhead_factor}, + {"OPB B/W (single instance)", DUMP_FP_FMT, opb_bw}, + + {"INTERMEDIATE DDR B/W", "", DUMP_HEADER_MAGIC}, + {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, + {"VSP write", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, + {"recon read", DUMP_FP_FMT, ddr.recon_read}, + {"recon write", DUMP_FP_FMT, ddr.recon_write}, + {"OPB read", DUMP_FP_FMT, ddr.opb_read}, + {"OPB write", DUMP_FP_FMT, ddr.opb_write}, + {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, + {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, + {"LLC DPB read", DUMP_FP_FMT, llc.dpb_read}, + {"LLC OPB read", DUMP_FP_FMT, llc.opb_read}, + + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + case LLCC: + ret = kbps(fp_round(llc.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + +static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Encoder Parameters */ + + int width, height, fps, dpb_bpp, lcu_per_frame, lcu_size, + vertical_tile_width, colocated_bytes_per_lcu, bitrate, + ref_overlap_bw_factor; + enum hal_uncompressed_format dpb_color_format, original_color_format; + bool dpb_compression_enabled, original_compression_enabled, + work_mode_1, low_power, rotation, cropping_or_scaling, + b_frames_enabled = false, + llc_dual_core_ref_read_buf_enabled = false, + llc_top_line_buf_enabled = false, + llc_ref_chroma_cache_enabled = false; + fp_t dpb_compression_factor, original_compression_factor, + input_compression_factor, qsmmu_bw_overhead_factor, + ref_y_bw_factor, ref_cb_cr_bw_factor, ten_bpc_bpp_factor, + bw_for_1x_8bpc, dpb_bw_for_1x, ref_cb_cr_read, + bins_to_bit_factor, ref_y_read, ten_bpc_packing_factor, + dpb_write_factor, ref_overlap_bw, llc_ref_y_read, + llc_ref_cb_cr_read; + fp_t integer_part, frac_part; + unsigned long ret = 0; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + line_buffer_read, line_buffer_write, original_read, + original_write, dpb_read, dpb_write, total; + } ddr = {0}; + + struct { + fp_t dpb_read, line_buffer, total; + } llc = {0}; + + /* Encoder Parameters setup */ + ten_bpc_packing_factor = FP(1, 67, 1000); + ten_bpc_bpp_factor = FP(1, 1, 4); + rotation = false; + cropping_or_scaling = false; + vertical_tile_width = 960; + ref_y_bw_factor = FP(1, 30, 100); + ref_cb_cr_bw_factor = FP(1, 50, 100); + dpb_write_factor = FP(1, 8, 100); + + + /* Derived Parameters */ + lcu_size = d->lcu_size; + fps = d->fps; + b_frames_enabled = d->b_frames_enabled; + width = max(d->input_width, BASELINE_DIMENSIONS.width); + height = max(d->input_height, BASELINE_DIMENSIONS.height); + bitrate = d->bitrate > 0 ? d->bitrate / 1000000 : + __lut(width, height, fps)->bitrate; + lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size); + + dpb_color_format = HAL_COLOR_FORMAT_NV12_UBWC; + original_color_format = d->num_formats >= 1 ? + d->color_formats[0] : HAL_UNUSED_COLOR; + + dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; + + dpb_compression_enabled = __ubwc(dpb_color_format); + original_compression_enabled = __ubwc(original_color_format); + + work_mode_1 = d->work_mode == VIDC_WORK_MODE_1; + low_power = d->power_mode == VIDC_POWER_LOW; + bins_to_bit_factor = work_mode_1 ? + FP_INT(0) : FP_INT(4); + + if (d->use_sys_cache) { + llc_dual_core_ref_read_buf_enabled = true; + llc_ref_chroma_cache_enabled = true; + } + + /* + * Convert Q16 number into Integer and Fractional part upto 2 places. + * Ex : 105752 / 65536 = 1.61; 1.61 in Q16 = 105752; + * Integer part = 105752 / 65536 = 1; + * Reminder = 105752 - 1 * 65536 = 40216; + * Fractional part = 40216 * 100 / 65536 = 61; + * Now converto to FP(1, 61, 100) for below code. + */ + + integer_part = d->compression_ratio >> 16; + frac_part = + ((d->compression_ratio - (integer_part * 65536)) * 100) >> 16; + + dpb_compression_factor = FP(integer_part, frac_part, 100); + + integer_part = d->input_cr >> 16; + frac_part = + ((d->input_cr - (integer_part * 65536)) * 100) >> 16; + + input_compression_factor = FP(integer_part, frac_part, 100); + + /* use input cr if it is valid (not 1), otherwise use lut */ + original_compression_factor = + !original_compression_enabled ? FP_ONE : + input_compression_factor != FP_ONE ? input_compression_factor : + __compression_ratio(__lut(width, height, fps), dpb_bpp); + + ddr.vsp_read = fp_mult(fp_div(FP_INT(bitrate), FP_INT(8)), + bins_to_bit_factor); + ddr.vsp_write = ddr.vsp_read + fp_div(FP_INT(bitrate), FP_INT(8)); + + colocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; + + ddr.collocated_read = FP_INT(lcu_per_frame * + colocated_bytes_per_lcu * fps / bps(1)); + + ddr.collocated_write = ddr.collocated_read; + + ddr.line_buffer_read = FP_INT(16 * lcu_per_frame * fps / bps(1)); + + ddr.line_buffer_write = ddr.line_buffer_read; + + llc.line_buffer = ddr.line_buffer_read + ddr.line_buffer_write; + if (llc_top_line_buf_enabled) + ddr.line_buffer_read = ddr.line_buffer_write = FP_INT(0); + + llc.line_buffer -= (ddr.line_buffer_read + ddr.line_buffer_write); + + bw_for_1x_8bpc = fp_div(FP_INT((int)(width * height)), FP_INT(32 * 8)); + + bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc, + fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000))); + + dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc : + fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor, + ten_bpc_bpp_factor)); + + ddr.original_read = fp_div(fp_mult(FP(1, 50, 100), dpb_bw_for_1x), + input_compression_factor); + + ddr.original_write = FP_ZERO; + + ref_y_bw_factor = + width == vertical_tile_width ? FP_INT(1) : ref_y_bw_factor; + + ref_y_read = fp_mult(ref_y_bw_factor, dpb_bw_for_1x); + + ref_y_read = fp_div(ref_y_read, dpb_compression_factor); + + ref_y_read = + b_frames_enabled ? fp_mult(ref_y_read, FP_INT(2)) : ref_y_read; + + llc_ref_y_read = ref_y_read; + if (llc_dual_core_ref_read_buf_enabled) + ref_y_read = fp_div(ref_y_read, FP_INT(2)); + + llc_ref_y_read -= ref_y_read; + + ref_cb_cr_read = fp_mult(ref_cb_cr_bw_factor, dpb_bw_for_1x) / 2; + + ref_cb_cr_read = fp_div(ref_cb_cr_read, dpb_compression_factor); + + ref_cb_cr_read = + b_frames_enabled ? fp_mult(ref_cb_cr_read, FP_INT(2)) : + ref_cb_cr_read; + + llc_ref_cb_cr_read = ref_cb_cr_read; + + if (llc_ref_chroma_cache_enabled) + ref_cb_cr_read = fp_div(ref_cb_cr_read, ref_cb_cr_bw_factor); + + if (llc_dual_core_ref_read_buf_enabled) + ref_cb_cr_read = fp_div(ref_cb_cr_read, FP_INT(2)); + + llc_ref_cb_cr_read -= ref_cb_cr_read; + + ddr.dpb_write = fp_mult(dpb_write_factor, dpb_bw_for_1x); + + ddr.dpb_write = fp_mult(ddr.dpb_write, FP(1, 50, 100)); + + ddr.dpb_write = fp_div(ddr.dpb_write, dpb_compression_factor); + + ref_overlap_bw_factor = + width <= vertical_tile_width ? FP_INT(0) : FP_INT(1); + + ref_overlap_bw = fp_mult(ddr.dpb_write, ref_overlap_bw_factor); + + ref_overlap_bw = fp_div(ref_overlap_bw, dpb_write_factor); + + ref_overlap_bw = fp_mult(ref_overlap_bw, + (dpb_write_factor - FP_INT(1))); + + ddr.dpb_read = ref_y_read + ref_cb_cr_read + ref_overlap_bw; + + llc.dpb_read = llc_ref_y_read + llc_ref_cb_cr_read; + + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.line_buffer_read + ddr.line_buffer_write + + ddr.original_read + ddr.original_write + + ddr.dpb_read + ddr.dpb_write; + + llc.total = llc.dpb_read + llc.line_buffer + ddr.total; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + + if (debug_ar50) { + struct dump dump[] = { + {"ENCODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"width", "%d", width}, + {"height", "%d", height}, + {"DPB format", "%#x", dpb_color_format}, + {"original frame format", "%#x", original_color_format}, + {"fps", "%d", fps}, + {"DPB compression enable", "%d", dpb_compression_enabled}, + {"original compression enable", "%d", + original_compression_enabled}, + {"low power mode", "%d", low_power}, + {"Work Mode", "%d", work_mode_1}, + {"DPB compression factor", DUMP_FP_FMT, + dpb_compression_factor}, + {"original compression factor", DUMP_FP_FMT, + original_compression_factor}, + {"rotation", "%d", rotation}, + {"cropping or scaling", "%d", cropping_or_scaling}, + + {"DERIVED PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"LCU size", "%d", lcu_size}, + {"bitrate (Mbit/sec)", "%lu", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"qsmmu_bw_overhead_factor", + DUMP_FP_FMT, qsmmu_bw_overhead_factor}, + + {"INTERMEDIATE B/W DDR", "", DUMP_HEADER_MAGIC}, + {"ref_y_read", DUMP_FP_FMT, ref_y_read}, + {"ref_cb_cr_read", DUMP_FP_FMT, ref_cb_cr_read}, + {"ref_overlap_bw", DUMP_FP_FMT, ref_overlap_bw}, + {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, + {"VSP write", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, + {"original read", DUMP_FP_FMT, ddr.original_read}, + {"original write", DUMP_FP_FMT, ddr.original_write}, + {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, + {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, + {"LLC DPB read", DUMP_FP_FMT, llc.dpb_read}, + {"LLC Line buffer", DUMP_FP_FMT, llc.line_buffer}, + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + case LLCC: + ret = kbps(fp_round(llc.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + +static unsigned long __calculate(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + unsigned long value = 0; + + switch (d->domain) { + case HAL_VIDEO_DOMAIN_VPE: + value = __calculate_vpe(d, type); + break; + case HAL_VIDEO_DOMAIN_ENCODER: + value = __calculate_encoder(d, type); + break; + case HAL_VIDEO_DOMAIN_DECODER: + value = __calculate_decoder(d, type); + break; + default: + dprintk(VIDC_ERR, "Unknown Domain"); + } + + return value; +} + +unsigned long __calc_bw_ar50(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data) +{ + unsigned long ab_kbps = 0, c = 0; + enum vidc_bus_type type; + + if (!vidc_data || !vidc_data->data_count || !vidc_data->data) + goto exit; + + for (c = 0; c < vidc_data->data_count; ++c) { + if (vidc_data->data->power_mode == VIDC_POWER_TURBO) { + ab_kbps = INT_MAX; + goto exit; + } + } + + type = get_type_frm_name(bus->name); + + for (c = 0; c < vidc_data->data_count; ++c) + ab_kbps += __calculate(&vidc_data->data[c], type); + +exit: + trace_msm_vidc_perf_bus_vote(bus->name, ab_kbps); + return ab_kbps; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c new file mode 100644 index 000000000000..ed7c97bbf782 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c @@ -0,0 +1,1774 @@ +/* Copyright (c) 2017-2021, 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 "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_clocks.h" + +#define MSM_VIDC_MIN_UBWC_COMPLEXITY_FACTOR (1 << 16) +#define MSM_VIDC_MAX_UBWC_COMPLEXITY_FACTOR (4 << 16) + +#define MSM_VIDC_MIN_UBWC_COMPRESSION_RATIO (1 << 16) +#define MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO (5 << 16) + +static unsigned long msm_vidc_calc_freq_ar50(struct msm_vidc_inst *inst, + u32 filled_len); +static int msm_vidc_decide_work_mode_ar50(struct msm_vidc_inst *inst); +static unsigned long msm_vidc_calc_freq(struct msm_vidc_inst *inst, + u32 filled_len); + +struct msm_vidc_core_ops core_ops_vpu4 = { + .calc_freq = msm_vidc_calc_freq_ar50, + .decide_work_route = NULL, + .decide_work_mode = msm_vidc_decide_work_mode_ar50, +}; + +struct msm_vidc_core_ops core_ops_vpu5 = { + .calc_freq = msm_vidc_calc_freq, + .decide_work_route = msm_vidc_decide_work_route, + .decide_work_mode = msm_vidc_decide_work_mode, +}; + +static inline void msm_dcvs_print_dcvs_stats(struct clock_data *dcvs) +{ + dprintk(VIDC_PROF, + "DCVS: Load_Low %d, Load Norm %d, Load High %d\n", + dcvs->load_low, + dcvs->load_norm, + dcvs->load_high); + + dprintk(VIDC_PROF, + "DCVS: min_threshold %d, max_threshold %d\n", + dcvs->min_threshold, dcvs->max_threshold); +} + +static inline unsigned long int get_ubwc_compression_ratio( + struct ubwc_cr_stats_info_type ubwc_stats_info) +{ + unsigned long int sum = 0, weighted_sum = 0; + unsigned long int compression_ratio = 0; + + weighted_sum = + 32 * ubwc_stats_info.cr_stats_info0 + + 64 * ubwc_stats_info.cr_stats_info1 + + 96 * ubwc_stats_info.cr_stats_info2 + + 128 * ubwc_stats_info.cr_stats_info3 + + 160 * ubwc_stats_info.cr_stats_info4 + + 192 * ubwc_stats_info.cr_stats_info5 + + 256 * ubwc_stats_info.cr_stats_info6; + + sum = + ubwc_stats_info.cr_stats_info0 + + ubwc_stats_info.cr_stats_info1 + + ubwc_stats_info.cr_stats_info2 + + ubwc_stats_info.cr_stats_info3 + + ubwc_stats_info.cr_stats_info4 + + ubwc_stats_info.cr_stats_info5 + + ubwc_stats_info.cr_stats_info6; + + compression_ratio = (weighted_sum && sum) ? + ((256 * sum) << 16) / weighted_sum : compression_ratio; + + return compression_ratio; +} + +int msm_vidc_get_mbs_per_frame(struct msm_vidc_inst *inst) +{ + int height, width; + + if (!inst->in_reconfig) { + height = max(inst->prop.height[CAPTURE_PORT], + inst->prop.height[OUTPUT_PORT]); + width = max(inst->prop.width[CAPTURE_PORT], + inst->prop.width[OUTPUT_PORT]); + } else { + height = inst->reconfig_height; + width = inst->reconfig_width; + } + + return NUM_MBS_PER_FRAME(height, width); +} + +static int msm_vidc_get_fps(struct msm_vidc_inst *inst) +{ + int fps; + + if ((inst->clk_data.operating_rate >> 16) > inst->prop.fps) + fps = (inst->clk_data.operating_rate >> 16) ? + (inst->clk_data.operating_rate >> 16) : 1; + else + fps = inst->prop.fps; + + return fps; +} + +void update_recon_stats(struct msm_vidc_inst *inst, + struct recon_stats_type *recon_stats) +{ + struct recon_buf *binfo; + u32 CR = 0, CF = 0; + u32 frame_size; + + CR = get_ubwc_compression_ratio(recon_stats->ubwc_stats_info); + + frame_size = (msm_vidc_get_mbs_per_frame(inst) / (32 * 8) * 3) / 2; + + if (frame_size) + CF = recon_stats->complexity_number / frame_size; + else + CF = MSM_VIDC_MAX_UBWC_COMPLEXITY_FACTOR; + + mutex_lock(&inst->reconbufs.lock); + list_for_each_entry(binfo, &inst->reconbufs.list, list) { + if (binfo->buffer_index == + recon_stats->buffer_index) { + binfo->CR = CR; + binfo->CF = CF; + } + } + mutex_unlock(&inst->reconbufs.lock); +} + +static int fill_dynamic_stats(struct msm_vidc_inst *inst, + struct vidc_bus_vote_data *vote_data) +{ + struct recon_buf *binfo, *nextb; + struct vidc_input_cr_data *temp, *next; + u32 min_cf = MSM_VIDC_MAX_UBWC_COMPLEXITY_FACTOR, max_cf = 0; + u32 min_input_cr = MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO, + max_input_cr = 0; + u32 min_cr = MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO, max_cr = 0; + + mutex_lock(&inst->reconbufs.lock); + list_for_each_entry_safe(binfo, nextb, &inst->reconbufs.list, list) { + if (binfo->CR) { + min_cr = min(min_cr, binfo->CR); + max_cr = max(max_cr, binfo->CR); + } + if (binfo->CF) { + min_cf = min(min_cf, binfo->CF); + max_cf = max(max_cf, binfo->CF); + } + } + mutex_unlock(&inst->reconbufs.lock); + + mutex_lock(&inst->input_crs.lock); + list_for_each_entry_safe(temp, next, &inst->input_crs.list, list) { + min_input_cr = min(min_input_cr, temp->input_cr); + max_input_cr = max(max_input_cr, temp->input_cr); + } + mutex_unlock(&inst->input_crs.lock); + + /* Sanitize CF values from HW . */ + max_cf = min_t(u32, max_cf, MSM_VIDC_MAX_UBWC_COMPLEXITY_FACTOR); + min_cf = max_t(u32, min_cf, MSM_VIDC_MIN_UBWC_COMPLEXITY_FACTOR); + max_cr = min_t(u32, max_cr, MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO); + min_cr = max_t(u32, min_cr, MSM_VIDC_MIN_UBWC_COMPRESSION_RATIO); + max_input_cr = min_t(u32, + max_input_cr, MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO); + min_input_cr = max_t(u32, + min_input_cr, MSM_VIDC_MIN_UBWC_COMPRESSION_RATIO); + + vote_data->compression_ratio = min_cr; + vote_data->complexity_factor = max_cf; + vote_data->input_cr = min_input_cr; + vote_data->use_dpb_read = false; + + dprintk(VIDC_PROF, + "Input CR = %d Recon CR = %d Complexity Factor = %d\n", + vote_data->input_cr, vote_data->compression_ratio, + vote_data->complexity_factor); + + return 0; +} + +int msm_comm_vote_bus(struct msm_vidc_core *core) +{ + int rc = 0, vote_data_count = 0, i = 0; + struct hfi_device *hdev; + struct msm_vidc_inst *inst = NULL; + struct vidc_bus_vote_data *vote_data = NULL; + bool is_turbo = false; + + if (!core || !core->device) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, core); + return -EINVAL; + } + hdev = core->device; + + vote_data = kzalloc(sizeof(struct vidc_bus_vote_data) * + MAX_SUPPORTED_INSTANCES, GFP_ATOMIC); + if (!vote_data) { + dprintk(VIDC_DBG, + "vote_data allocation with GFP_ATOMIC failed\n"); + vote_data = kzalloc(sizeof(struct vidc_bus_vote_data) * + MAX_SUPPORTED_INSTANCES, GFP_KERNEL); + if (!vote_data) { + dprintk(VIDC_DBG, + "vote_data allocation failed\n"); + return -EINVAL; + } + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + int codec = 0; + struct msm_vidc_buffer *temp, *next; + u32 filled_len = 0; + u32 device_addr = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", + __func__); + mutex_unlock(&core->lock); + return -EINVAL; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(temp, next, + &inst->registeredbufs.list, list) { + if (temp->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + filled_len = max(filled_len, + temp->vvb.vb2_buf.planes[0].bytesused); + device_addr = temp->smem[0].device_addr; + } + if (inst->session_type == MSM_VIDC_ENCODER && + (temp->vvb.flags & + V4L2_QCOM_BUF_FLAG_PERF_MODE)) { + is_turbo = true; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + if ((!filled_len || !device_addr) && + (inst->session_type != MSM_VIDC_CVP)) { + dprintk(VIDC_DBG, "%s: no input for session %x\n", + __func__, hash32_ptr(inst->session)); + continue; + } + + ++vote_data_count; + + switch (inst->session_type) { + case MSM_VIDC_DECODER: + codec = inst->fmts[OUTPUT_PORT].fourcc; + break; + case MSM_VIDC_ENCODER: + codec = inst->fmts[CAPTURE_PORT].fourcc; + break; + case MSM_VIDC_CVP: + codec = V4L2_PIX_FMT_CVP; + break; + default: + dprintk(VIDC_ERR, "%s: invalid session_type %#x\n", + __func__, inst->session_type); + break; + } + + memset(&(vote_data[i]), 0x0, sizeof(struct vidc_bus_vote_data)); + + vote_data[i].domain = get_hal_domain(inst->session_type); + vote_data[i].codec = get_hal_codec(codec); + vote_data[i].input_width = inst->prop.width[OUTPUT_PORT]; + vote_data[i].input_height = inst->prop.height[OUTPUT_PORT]; + vote_data[i].output_width = inst->prop.width[CAPTURE_PORT]; + vote_data[i].output_height = inst->prop.height[CAPTURE_PORT]; + vote_data[i].rotation = + msm_comm_g_ctrl_for_id(inst, V4L2_CID_ROTATE); + vote_data[i].lcu_size = (codec == V4L2_PIX_FMT_HEVC || + codec == V4L2_PIX_FMT_VP9) ? 32 : 16; + vote_data[i].b_frames_enabled = + msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES) != 0; + + vote_data[i].fps = msm_vidc_get_fps(inst); + if (inst->session_type == MSM_VIDC_ENCODER) { + vote_data[i].bitrate = inst->clk_data.bitrate; + /* scale bitrate if operating rate is larger than fps */ + if (vote_data[i].fps > inst->prop.fps + && inst->prop.fps) { + vote_data[i].bitrate = vote_data[i].bitrate / + inst->prop.fps * vote_data[i].fps; + } + } else if (inst->session_type == MSM_VIDC_DECODER) { + vote_data[i].bitrate = + filled_len * vote_data[i].fps * 8; + } + + vote_data[i].power_mode = 0; + if (inst->clk_data.buffer_counter < DCVS_FTB_WINDOW && + inst->session_type != MSM_VIDC_CVP) + vote_data[i].power_mode = VIDC_POWER_TURBO; + if (msm_vidc_clock_voting || is_turbo) + vote_data[i].power_mode = VIDC_POWER_TURBO; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_PRIMARY) { + vote_data[i].color_formats[0] = + msm_comm_get_hal_uncompressed( + inst->clk_data.opb_fourcc); + vote_data[i].num_formats = 1; + } else { + vote_data[i].color_formats[0] = + msm_comm_get_hal_uncompressed( + inst->clk_data.dpb_fourcc); + vote_data[i].color_formats[1] = + msm_comm_get_hal_uncompressed( + inst->clk_data.opb_fourcc); + vote_data[i].num_formats = 2; + } + vote_data[i].work_mode = inst->clk_data.work_mode; + fill_dynamic_stats(inst, &vote_data[i]); + + if (core->resources.sys_cache_res_set) + vote_data[i].use_sys_cache = true; + + if (inst->session_type == MSM_VIDC_CVP) { + vote_data[i].domain = + get_hal_domain(inst->session_type); + vote_data[i].ddr_bw = inst->clk_data.ddr_bw; + vote_data[i].sys_cache_bw = + inst->clk_data.sys_cache_bw; + } + + i++; + } + mutex_unlock(&core->lock); + if (vote_data_count) + rc = call_hfi_op(hdev, vote_bus, hdev->hfi_device_data, + vote_data, vote_data_count); + + kfree(vote_data); + return rc; +} + +static int msm_dcvs_scale_clocks(struct msm_vidc_inst *inst, + unsigned long freq) +{ + int rc = 0; + int bufs_with_fw = 0; + int bufs_with_client = 0; + struct hal_buffer_requirements *buf_reqs; + struct clock_data *dcvs; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return -EINVAL; + } + + /* assume no increment or decrement is required initially */ + inst->clk_data.dcvs_flags = 0; + + if (!inst->clk_data.dcvs_mode || inst->batch.enable) { + dprintk(VIDC_DBG, "Skip DCVS (dcvs %d, batching %d)\n", + inst->clk_data.dcvs_mode, inst->batch.enable); + /* update load (freq) with normal value */ + inst->clk_data.load = inst->clk_data.load_norm; + return 0; + } + + dcvs = &inst->clk_data; + + if (is_decode_session(inst)) + bufs_with_fw = msm_comm_num_queued_bufs(inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + else + bufs_with_fw = msm_comm_num_queued_bufs(inst, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + /* +1 as one buffer is going to be queued after the function */ + bufs_with_fw += 1; + + buf_reqs = get_buff_req_buffer(inst, dcvs->buffer_type); + if (!buf_reqs) { + dprintk(VIDC_ERR, "%s: invalid buf type %d\n", + __func__, dcvs->buffer_type); + return -EINVAL; + } + bufs_with_client = buf_reqs->buffer_count_actual - bufs_with_fw; + + /* + * PMS decides clock level based on below algo + + * Limits : + * max_threshold : Client extra allocated buffers. Client + * reserves these buffers for it's smooth flow. + * min_output_buf : HW requested buffers for it's smooth + * flow of buffers. + * min_threshold : Driver requested extra buffers for PMS. + + * 1) When buffers outside FW are reaching client's extra buffers, + * FW is slow and will impact pipeline, Increase clock. + * 2) When pending buffers with FW are same as FW requested, + * pipeline has cushion to absorb FW slowness, Decrease clocks. + * 3) When none of 1) or 2) FW is just fast enough to maintain + * pipeline, request Right Clocks. + */ + + if (bufs_with_client <= dcvs->max_threshold) { + dcvs->load = dcvs->load_high; + dcvs->dcvs_flags |= MSM_VIDC_DCVS_INCR; + } else if (bufs_with_fw < buf_reqs->buffer_count_min) { + dcvs->load = dcvs->load_low; + dcvs->dcvs_flags |= MSM_VIDC_DCVS_DECR; + } else { + dcvs->load = dcvs->load_norm; + dcvs->dcvs_flags = 0; + } + + dprintk(VIDC_PROF, + "DCVS: %x : total bufs %d outside fw %d max threshold %d with fw %d min bufs %d flags %#x\n", + hash32_ptr(inst->session), buf_reqs->buffer_count_actual, + bufs_with_client, dcvs->max_threshold, bufs_with_fw, + buf_reqs->buffer_count_min, dcvs->dcvs_flags); + return rc; +} + +static void msm_vidc_update_freq_entry(struct msm_vidc_inst *inst, + unsigned long freq, u32 device_addr, bool is_turbo) +{ + struct vidc_freq_data *temp, *next; + bool found = false; + + mutex_lock(&inst->freqs.lock); + list_for_each_entry_safe(temp, next, &inst->freqs.list, list) { + if (temp->device_addr == device_addr) { + temp->freq = freq; + found = true; + break; + } + } + + if (!found) { + temp = kzalloc(sizeof(*temp), GFP_KERNEL); + if (!temp) { + dprintk(VIDC_WARN, "%s: malloc failure.\n", __func__); + goto exit; + } + temp->freq = freq; + temp->device_addr = device_addr; + list_add_tail(&temp->list, &inst->freqs.list); + } + temp->turbo = !!is_turbo; +exit: + mutex_unlock(&inst->freqs.lock); +} + +void msm_vidc_clear_freq_entry(struct msm_vidc_inst *inst, + u32 device_addr) +{ + struct vidc_freq_data *temp, *next; + + mutex_lock(&inst->freqs.lock); + list_for_each_entry_safe(temp, next, &inst->freqs.list, list) { + if (temp->device_addr == device_addr) + temp->freq = 0; + } + mutex_unlock(&inst->freqs.lock); + + inst->clk_data.buffer_counter++; +} + +static unsigned long msm_vidc_max_freq(struct msm_vidc_core *core) +{ + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + unsigned long freq = 0; + + allowed_clks_tbl = core->resources.allowed_clks_tbl; + freq = allowed_clks_tbl[0].clock_rate; + dprintk(VIDC_PROF, "Max rate = %lu\n", freq); + return freq; +} + +void msm_comm_free_freq_table(struct msm_vidc_inst *inst) +{ + struct vidc_freq_data *temp, *next; + + mutex_lock(&inst->freqs.lock); + list_for_each_entry_safe(temp, next, &inst->freqs.list, list) { + list_del(&temp->list); + kfree(temp); + } + INIT_LIST_HEAD(&inst->freqs.list); + mutex_unlock(&inst->freqs.lock); +} + +void msm_comm_free_input_cr_table(struct msm_vidc_inst *inst) +{ + struct vidc_input_cr_data *temp, *next; + + mutex_lock(&inst->input_crs.lock); + list_for_each_entry_safe(temp, next, &inst->input_crs.list, list) { + list_del(&temp->list); + kfree(temp); + } + INIT_LIST_HEAD(&inst->input_crs.list); + mutex_unlock(&inst->input_crs.lock); +} + +void msm_comm_update_input_cr(struct msm_vidc_inst *inst, + u32 index, u32 cr) +{ + struct vidc_input_cr_data *temp, *next; + bool found = false; + + mutex_lock(&inst->input_crs.lock); + list_for_each_entry_safe(temp, next, &inst->input_crs.list, list) { + if (temp->index == index) { + temp->input_cr = cr; + found = true; + break; + } + } + + if (!found) { + temp = kzalloc(sizeof(*temp), GFP_KERNEL); + if (!temp) { + dprintk(VIDC_WARN, "%s: malloc failure.\n", __func__); + goto exit; + } + temp->index = index; + temp->input_cr = cr; + list_add_tail(&temp->list, &inst->input_crs.list); + } +exit: + mutex_unlock(&inst->input_crs.lock); +} + +static unsigned long msm_vidc_calc_freq_ar50(struct msm_vidc_inst *inst, + u32 filled_len) +{ + unsigned long freq = 0; + unsigned long vpp_cycles = 0, vsp_cycles = 0; + unsigned long fw_cycles = 0, fw_vpp_cycles = 0; + u32 vpp_cycles_per_mb; + u32 mbs_per_second; + struct msm_vidc_core *core = NULL; + int i = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + u64 rate = 0, fps; + struct clock_data *dcvs = NULL; + + core = inst->core; + dcvs = &inst->clk_data; + + mbs_per_second = msm_comm_get_inst_load_per_core(inst, + LOAD_CALC_NO_QUIRKS); + + fps = msm_vidc_get_fps(inst); + + /* + * Calculate vpp, vsp cycles separately for encoder and decoder. + * Even though, most part is common now, in future it may change + * between them. + */ + + fw_cycles = fps * inst->core->resources.fw_cycles; + fw_vpp_cycles = fps * inst->core->resources.fw_vpp_cycles; + + if (inst->session_type == MSM_VIDC_ENCODER) { + vpp_cycles_per_mb = inst->flags & VIDC_LOW_POWER ? + inst->clk_data.entry->low_power_cycles : + inst->clk_data.entry->vpp_cycles; + + vpp_cycles = mbs_per_second * vpp_cycles_per_mb; + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(vpp_cycles / 20, fw_vpp_cycles); + + vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles; + + /* 10 / 7 is overhead factor */ + vsp_cycles += (inst->clk_data.bitrate * 10) / 7; + } else if (inst->session_type == MSM_VIDC_DECODER) { + vpp_cycles = mbs_per_second * inst->clk_data.entry->vpp_cycles; + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(vpp_cycles / 20, fw_vpp_cycles); + + vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles; + /* 10 / 7 is overhead factor */ + vsp_cycles += div_u64((fps * filled_len * 8 * 10), 7); + + } else { + dprintk(VIDC_ERR, "Unknown session type = %s\n", __func__); + return msm_vidc_max_freq(inst->core); + } + + freq = max(vpp_cycles, vsp_cycles); + freq = max(freq, fw_cycles); + + dprintk(VIDC_DBG, "Update DCVS Load\n"); + allowed_clks_tbl = core->resources.allowed_clks_tbl; + for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) { + rate = allowed_clks_tbl[i].clock_rate; + if (rate >= freq) + break; + } + + dcvs->load_norm = rate; + dcvs->load_low = i < (core->resources.allowed_clks_tbl_size - 1) ? + allowed_clks_tbl[i+1].clock_rate : dcvs->load_norm; + dcvs->load_high = i > 0 ? allowed_clks_tbl[i-1].clock_rate : + dcvs->load_norm; + + msm_dcvs_print_dcvs_stats(dcvs); + + dprintk(VIDC_PROF, "%s Inst %pK : Filled Len = %d Freq = %lu\n", + __func__, inst, filled_len, freq); + + return freq; +} + +static unsigned long msm_vidc_calc_freq(struct msm_vidc_inst *inst, + u32 filled_len) +{ + unsigned long freq = 0; + unsigned long sw_overhead = 0; + unsigned long vpp_cycles = 0, vsp_cycles = 0; + unsigned long fw_cycles = 0, fw_vpp_cycles = 0; + u32 vpp_cycles_per_mb; + u32 mbs_per_second; + struct msm_vidc_core *core = NULL; + int i = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + u64 rate = 0, fps; + struct clock_data *dcvs = NULL; + u32 operating_rate, vsp_factor_num = 10, vsp_factor_den = 5; + + core = inst->core; + dcvs = &inst->clk_data; + + mbs_per_second = msm_comm_get_inst_load_per_core(inst, + LOAD_CALC_NO_QUIRKS); + + fps = msm_vidc_get_fps(inst); + + /* + * Calculate vpp, vsp, fw cycles separately for encoder and decoder. + * Even though, most part is common now, in future it may change + * between them. + */ + + fw_cycles = fps * inst->core->resources.fw_cycles; + fw_vpp_cycles = fps * inst->core->resources.fw_vpp_cycles; + + if (inst->session_type == MSM_VIDC_ENCODER) { + vpp_cycles_per_mb = inst->flags & VIDC_LOW_POWER ? + inst->clk_data.entry->low_power_cycles : + inst->clk_data.entry->vpp_cycles; + + vpp_cycles = mbs_per_second * vpp_cycles_per_mb / + inst->clk_data.work_route; + vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles; + + /* bitrate is based on fps, scale it using operating rate */ + operating_rate = inst->clk_data.operating_rate >> 16; + if (operating_rate > inst->prop.fps && inst->prop.fps) { + vsp_factor_num *= operating_rate; + vsp_factor_den *= inst->prop.fps; + } + vsp_cycles += div_u64(((u64)inst->clk_data.bitrate * + vsp_factor_num), vsp_factor_den); + + /* sw overhead factor */ + sw_overhead = div_u64((u64)vsp_cycles * fw_vpp_cycles, + vpp_cycles); + vsp_cycles += max(vsp_cycles/20, sw_overhead); + + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(vpp_cycles / 20, fw_vpp_cycles); + + if (inst->clk_data.work_route > 1) + vpp_cycles += (vpp_cycles * 14 / 1000); + + } else if (inst->session_type == MSM_VIDC_DECODER) { + vpp_cycles = mbs_per_second * inst->clk_data.entry->vpp_cycles / + inst->clk_data.work_route; + + vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles; + + /* vsp perf is about 0.5 bits/cycle */ + vsp_cycles += div_u64((fps * filled_len * 8 * 10), 5); + + /* sw overhead factor */ + sw_overhead = div_u64(((u64)vsp_cycles * fw_vpp_cycles), + vpp_cycles); + vsp_cycles += max(vsp_cycles/20, sw_overhead); + + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(vpp_cycles / 20, fw_vpp_cycles); + + /* 1.059 pipeline overhead factor */ + if (inst->clk_data.work_route > 1) + vpp_cycles += vpp_cycles/17; + + } else { + dprintk(VIDC_ERR, "Unknown session type = %s\n", __func__); + return msm_vidc_max_freq(inst->core); + } + + freq = max(vpp_cycles, vsp_cycles); + freq = max(freq, fw_cycles); + + allowed_clks_tbl = core->resources.allowed_clks_tbl; + for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) { + rate = allowed_clks_tbl[i].clock_rate; + if (rate >= freq) + break; + } + + dcvs->load_norm = rate; + dcvs->load_low = i < (core->resources.allowed_clks_tbl_size - 1) ? + allowed_clks_tbl[i+1].clock_rate : dcvs->load_norm; + dcvs->load_high = i > 0 ? allowed_clks_tbl[i-1].clock_rate : + dcvs->load_norm; + + dprintk(VIDC_PROF, + "%s: inst %pK: %x : filled len %d required freq %lu load_norm %d\n", + __func__, inst, hash32_ptr(inst->session), + filled_len, freq, dcvs->load_norm); + + return freq; +} + +int msm_vidc_set_clocks(struct msm_vidc_core *core) +{ + struct hfi_device *hdev; + unsigned long freq_core_1 = 0, freq_core_2 = 0, rate = 0; + unsigned long freq_core_max = 0; + struct msm_vidc_inst *inst = NULL; + struct msm_vidc_buffer *temp, *next; + u32 device_addr, filled_len; + int rc = 0, i = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + bool increment, decrement; + + hdev = core->device; + allowed_clks_tbl = core->resources.allowed_clks_tbl; + if (!allowed_clks_tbl) { + dprintk(VIDC_ERR, + "%s Invalid parameters\n", __func__); + return -EINVAL; + } + + mutex_lock(&core->lock); + increment = false; + decrement = true; + list_for_each_entry(inst, &core->instances, list) { + device_addr = 0; + filled_len = 0; + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(temp, next, + &inst->registeredbufs.list, list) { + if (temp->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + filled_len = max(filled_len, + temp->vvb.vb2_buf.planes[0].bytesused); + device_addr = temp->smem[0].device_addr; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + if (!filled_len || !device_addr) { + dprintk(VIDC_DBG, "%s no input for session %x\n", + __func__, hash32_ptr(inst->session)); + continue; + } + + if (inst->clk_data.core_id == VIDC_CORE_ID_1) + freq_core_1 += inst->clk_data.min_freq; + else if (inst->clk_data.core_id == VIDC_CORE_ID_2) + freq_core_2 += inst->clk_data.min_freq; + else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core_1 += inst->clk_data.min_freq; + freq_core_2 += inst->clk_data.min_freq; + } + + freq_core_max = max_t(unsigned long, freq_core_1, freq_core_2); + + if (msm_vidc_clock_voting) { + dprintk(VIDC_PROF, + "msm_vidc_clock_voting %d\n", + msm_vidc_clock_voting); + freq_core_max = msm_vidc_clock_voting; + decrement = false; + break; + } + + if (inst->clk_data.turbo_mode) { + dprintk(VIDC_PROF, + "Found an instance with Turbo request\n"); + freq_core_max = msm_vidc_max_freq(core); + decrement = false; + break; + } + /* increment even if one session requested for it */ + if (inst->clk_data.dcvs_flags & MSM_VIDC_DCVS_INCR) + increment = true; + /* decrement only if all sessions requested for it */ + if (!(inst->clk_data.dcvs_flags & MSM_VIDC_DCVS_DECR)) + decrement = false; + } + + /* + * keep checking from lowest to highest rate until + * table rate >= requested rate + */ + for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) { + rate = allowed_clks_tbl[i].clock_rate; + if (rate >= freq_core_max) + break; + } + if (increment) { + if (i > 0) + rate = allowed_clks_tbl[i-1].clock_rate; + } else if (decrement) { + if (i < (core->resources.allowed_clks_tbl_size - 1)) + rate = allowed_clks_tbl[i+1].clock_rate; + } + + core->min_freq = freq_core_max; + core->curr_freq = rate; + mutex_unlock(&core->lock); + + dprintk(VIDC_PROF, + "%s: clock rate %lu requested %lu increment %d decrement %d\n", + __func__, core->curr_freq, core->min_freq, + increment, decrement); + rc = call_hfi_op(hdev, scale_clocks, + hdev->hfi_device_data, core->curr_freq); + + return rc; +} + +int msm_vidc_validate_operating_rate(struct msm_vidc_inst *inst, + u32 operating_rate) +{ + struct msm_vidc_inst *temp; + struct msm_vidc_core *core; + unsigned long max_freq, freq_left, op_rate_possible, load, cycles; + unsigned long mbs_per_second, freq_core0 = 0, freq_core1 = 0, freq; + int rc = 0; + u32 curr_operating_rate = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return -EINVAL; + } + core = inst->core; + curr_operating_rate = + max(inst->clk_data.operating_rate >> 16, inst->prop.fps); + operating_rate = operating_rate >> 16; + + /* always allow decreasing operating rate*/ + if (curr_operating_rate >= operating_rate) + return 0; + + mutex_lock(&core->lock); + max_freq = msm_vidc_max_freq(core); + list_for_each_entry(temp, &core->instances, list) { + if (temp == inst || + temp->state < MSM_VIDC_START_DONE || + temp->state >= MSM_VIDC_RELEASE_RESOURCES_DONE) + continue; + + if (temp->clk_data.core_id == VIDC_CORE_ID_1) + freq_core0 += temp->clk_data.min_freq; + else if (temp->clk_data.core_id == VIDC_CORE_ID_2) + freq_core1 += temp->clk_data.min_freq; + else if (temp->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core0 += temp->clk_data.min_freq; + freq_core1 += temp->clk_data.min_freq; + } + } + + if (inst->clk_data.core_id == VIDC_CORE_ID_1) + freq = freq_core0; + else if (inst->clk_data.core_id == VIDC_CORE_ID_2) + freq = freq_core1; + else + freq = max(freq_core0, freq_core1); + + freq_left = max_freq > freq ? max_freq - freq : 0; + + mbs_per_second = msm_comm_get_inst_load_per_core(inst, + LOAD_CALC_NO_QUIRKS); + + cycles = inst->clk_data.entry->vpp_cycles; + if (inst->session_type == MSM_VIDC_ENCODER) + cycles = inst->flags & VIDC_LOW_POWER ? + inst->clk_data.entry->low_power_cycles : + cycles; + + if (inst->clk_data.work_route) + cycles /= inst->clk_data.work_route; + + load = cycles * mbs_per_second; + + op_rate_possible = load ? (freq_left * curr_operating_rate / load) : 0; + + + if (op_rate_possible >= operating_rate || + msm_vidc_clock_voting || + inst->clk_data.buffer_counter < DCVS_FTB_WINDOW) { + dprintk(VIDC_DBG, + "Requestd operating rate is valid %u\n", + operating_rate); + rc = 0; + } else { + dprintk(VIDC_DBG, + "Current load is high for requested settings. Cannot set operating rate to %u\n", + operating_rate); + rc = -EINVAL; + } + mutex_unlock(&core->lock); + + return rc; +} + +int msm_comm_scale_clocks(struct msm_vidc_inst *inst) +{ + struct msm_vidc_buffer *temp, *next; + unsigned long freq = 0; + u32 filled_len = 0; + u32 device_addr = 0; + bool is_turbo = false; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(temp, next, &inst->registeredbufs.list, list) { + if (temp->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + filled_len = max(filled_len, + temp->vvb.vb2_buf.planes[0].bytesused); + if (inst->session_type == MSM_VIDC_ENCODER && + (temp->vvb.flags & + V4L2_QCOM_BUF_FLAG_PERF_MODE)) { + is_turbo = true; + } + device_addr = temp->smem[0].device_addr; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + if (!filled_len || !device_addr) { + dprintk(VIDC_DBG, "%s no input for session %x\n", + __func__, hash32_ptr(inst->session)); + return 0; + } + + freq = call_core_op(inst->core, calc_freq, inst, filled_len); + inst->clk_data.min_freq = freq; + /* update dcvs flags */ + msm_dcvs_scale_clocks(inst, freq); + + if (inst->clk_data.buffer_counter < DCVS_FTB_WINDOW || is_turbo || + msm_vidc_clock_voting) { + inst->clk_data.min_freq = msm_vidc_max_freq(inst->core); + inst->clk_data.dcvs_flags = 0; + } + + msm_vidc_update_freq_entry(inst, freq, device_addr, is_turbo); + + msm_vidc_set_clocks(inst->core); + + return 0; +} + +int msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return -EINVAL; + } + core = inst->core; + hdev = core->device; + + if (msm_comm_scale_clocks(inst)) { + dprintk(VIDC_WARN, + "Failed to scale clocks. Performance might be impacted\n"); + } + if (msm_comm_vote_bus(core)) { + dprintk(VIDC_WARN, + "Failed to scale DDR bus. Performance might be impacted\n"); + } + return 0; +} + +int msm_dcvs_try_enable(struct msm_vidc_inst *inst) +{ + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: Invalid args: %p\n", __func__, inst); + return -EINVAL; + } + + if (msm_vidc_clock_voting || + !inst->core->resources.dcvs || + inst->flags & VIDC_THUMBNAIL || + inst->clk_data.low_latency_mode || + inst->batch.enable || + inst->grid_enable) { + dprintk(VIDC_PROF, "DCVS disabled: %pK\n", inst); + inst->clk_data.dcvs_mode = false; + return false; + } + inst->clk_data.dcvs_mode = true; + dprintk(VIDC_PROF, "DCVS enabled: %pK\n", inst); + + return true; +} + +int msm_comm_init_clocks_and_bus_data(struct msm_vidc_inst *inst) +{ + int rc = 0, j = 0; + int fourcc, count; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_CVP) { + dprintk(VIDC_DBG, "%s: cvp session\n", __func__); + return 0; + } + + count = inst->core->resources.codec_data_count; + fourcc = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + for (j = 0; j < count; j++) { + if (inst->core->resources.codec_data[j].session_type == + inst->session_type && + inst->core->resources.codec_data[j].fourcc == + fourcc) { + inst->clk_data.entry = + &inst->core->resources.codec_data[j]; + break; + } + } + + if (!inst->clk_data.entry) { + dprintk(VIDC_ERR, "%s No match found\n", __func__); + rc = -EINVAL; + } + + return rc; +} + +void msm_clock_data_reset(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + int i = 0, rc = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + u64 total_freq = 0, rate = 0, load; + int cycles; + struct clock_data *dcvs; + struct hal_buffer_requirements *buf_req; + + dprintk(VIDC_DBG, "Init DCVS Load\n"); + + if (!inst || !inst->core || !inst->clk_data.entry) { + dprintk(VIDC_ERR, "%s Invalid args: Inst = %pK\n", + __func__, inst); + return; + } + + core = inst->core; + dcvs = &inst->clk_data; + load = msm_comm_get_inst_load_per_core(inst, LOAD_CALC_NO_QUIRKS); + cycles = inst->clk_data.entry->vpp_cycles; + allowed_clks_tbl = core->resources.allowed_clks_tbl; + if (inst->session_type == MSM_VIDC_ENCODER) { + cycles = inst->flags & VIDC_LOW_POWER ? + inst->clk_data.entry->low_power_cycles : + cycles; + + dcvs->buffer_type = HAL_BUFFER_INPUT; + dcvs->min_threshold = + msm_vidc_get_extra_buff_count(inst, HAL_BUFFER_INPUT); + buf_req = get_buff_req_buffer(inst, HAL_BUFFER_INPUT); + if (buf_req) + dcvs->max_threshold = + buf_req->buffer_count_actual - + buf_req->buffer_count_min_host + 2; + else + dprintk(VIDC_ERR, + "%s: No bufer req for buffer type %x\n", + __func__, HAL_BUFFER_INPUT); + + } else if (inst->session_type == MSM_VIDC_DECODER) { + dcvs->buffer_type = msm_comm_get_hal_output_buffer(inst); + buf_req = get_buff_req_buffer(inst, dcvs->buffer_type); + if (buf_req) + dcvs->max_threshold = + buf_req->buffer_count_actual - + buf_req->buffer_count_min_host + 2; + else + dprintk(VIDC_ERR, + "%s: No bufer req for buffer type %x\n", + __func__, dcvs->buffer_type); + + dcvs->min_threshold = + msm_vidc_get_extra_buff_count(inst, dcvs->buffer_type); + } else { + dprintk(VIDC_ERR, "%s: invalid session type %#x\n", + __func__, inst->session_type); + return; + } + + total_freq = cycles * load; + + for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) { + rate = allowed_clks_tbl[i].clock_rate; + if (rate >= total_freq) + break; + } + + dcvs->load = dcvs->load_norm = rate; + + dcvs->load_low = i < (core->resources.allowed_clks_tbl_size - 1) ? + allowed_clks_tbl[i+1].clock_rate : dcvs->load_norm; + dcvs->load_high = i > 0 ? allowed_clks_tbl[i-1].clock_rate : + dcvs->load_norm; + + inst->clk_data.buffer_counter = 0; + + msm_dcvs_print_dcvs_stats(dcvs); + + rc = msm_comm_scale_clocks_and_bus(inst); + + if (rc) + dprintk(VIDC_ERR, "%s Failed to scale Clocks and Bus\n", + __func__); +} + +static bool is_output_buffer(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + return buffer_type == HAL_BUFFER_OUTPUT2; + } else { + return buffer_type == HAL_BUFFER_OUTPUT; + } +} + +int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + int count = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return 0; + } + /* + * no extra buffers for thumbnail session because + * neither dcvs nor batching will be enabled + */ + if (is_thumbnail_session(inst)) + return 0; + + /* Add DCVS extra buffer count */ + if (inst->core->resources.dcvs) { + if (is_decode_session(inst) && + is_output_buffer(inst, buffer_type)) { + count += DCVS_DEC_EXTRA_OUTPUT_BUFFERS; + } else if ((is_encode_session(inst) && + buffer_type == HAL_BUFFER_INPUT)) { + count += DCVS_ENC_EXTRA_INPUT_BUFFERS; + } + } + + /* + * if platform supports decode batching ensure minimum + * batch size count of extra buffers added on output port + */ + if (is_output_buffer(inst, buffer_type)) { + if (is_batching_allowed(inst) && + count < inst->batch.size) + count = inst->batch.size; + } + + return count; +} + +int msm_vidc_decide_work_route(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_video_work_route pdata; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + hdev = inst->core->device; + + pdata.video_work_route = 2; + if (inst->session_type == MSM_VIDC_DECODER) { + switch (inst->fmts[OUTPUT_PORT].fourcc) { + case V4L2_PIX_FMT_MPEG2: + pdata.video_work_route = 1; + break; + case V4L2_PIX_FMT_H264: + if (inst->pic_struct != + MSM_VIDC_PIC_STRUCT_PROGRESSIVE) + pdata.video_work_route = 1; + break; + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + u32 slice_mode = 0; + u32 rc_mode = 0; + u32 output_width, output_height, fps, mbps; + + switch (inst->fmts[CAPTURE_PORT].fourcc) { + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_TME: + pdata.video_work_route = 1; + goto decision_done; + } + + rc_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + if (rc_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) { + pdata.video_work_route = 2; + goto decision_done; + } + slice_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + output_height = inst->prop.height[CAPTURE_PORT]; + output_width = inst->prop.width[CAPTURE_PORT]; + fps = inst->prop.fps; + mbps = NUM_MBS_PER_SEC(output_height, output_width, fps); + if (slice_mode == + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES || + (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && + mbps <= CBR_MB_LIMIT) || + (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR && + mbps <= CBR_VFR_MB_LIMIT)) { + pdata.video_work_route = 1; + dprintk(VIDC_DBG, "Configured work route = 1"); + } + } else { + return -EINVAL; + } + +decision_done: + + inst->clk_data.work_route = pdata.video_work_route; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VIDEO_WORK_ROUTE, + (void *)&pdata); + if (rc) + dprintk(VIDC_WARN, + " Failed to configure work route %pK\n", inst); + + return rc; +} + +static int msm_vidc_decide_work_mode_ar50(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_video_work_mode pdata; + struct hal_enable latency; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + hdev = inst->core->device; + if (inst->clk_data.low_latency_mode) { + pdata.video_work_mode = VIDC_WORK_MODE_1; + goto decision_done; + } + + if (inst->session_type == MSM_VIDC_DECODER) { + pdata.video_work_mode = VIDC_WORK_MODE_2; + switch (inst->fmts[OUTPUT_PORT].fourcc) { + case V4L2_PIX_FMT_MPEG2: + pdata.video_work_mode = VIDC_WORK_MODE_1; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + if (inst->prop.height[OUTPUT_PORT] * + inst->prop.width[OUTPUT_PORT] <= + 1280 * 720) + pdata.video_work_mode = VIDC_WORK_MODE_1; + break; + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + u32 rc_mode = 0; + + pdata.video_work_mode = VIDC_WORK_MODE_1; + rc_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + if (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR || + rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_MBR || + rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR) + pdata.video_work_mode = VIDC_WORK_MODE_2; + } else { + return -EINVAL; + } + +decision_done: + + inst->clk_data.work_mode = pdata.video_work_mode; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VIDEO_WORK_MODE, + (void *)&pdata); + if (rc) + dprintk(VIDC_WARN, + " Failed to configure Work Mode %pK\n", inst); + + /* For WORK_MODE_1, set Low Latency mode by default to HW. */ + + if (inst->session_type == MSM_VIDC_ENCODER && + inst->clk_data.work_mode == VIDC_WORK_MODE_1) { + latency.enable = 1; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VENC_LOW_LATENCY, + (void *)&latency); + } + + rc = msm_comm_scale_clocks_and_bus(inst); + + return rc; +} + +int msm_vidc_decide_work_mode(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_video_work_mode pdata; + struct hal_enable latency; + u32 yuv_size = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + hdev = inst->core->device; + + if (inst->clk_data.low_latency_mode) { + pdata.video_work_mode = VIDC_WORK_MODE_1; + dprintk(VIDC_DBG, "Configured work mode = 1"); + goto decision_done; + } + + if (inst->session_type == MSM_VIDC_DECODER) { + pdata.video_work_mode = VIDC_WORK_MODE_2; + switch (inst->fmts[OUTPUT_PORT].fourcc) { + case V4L2_PIX_FMT_MPEG2: + pdata.video_work_mode = VIDC_WORK_MODE_1; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_VP9: + yuv_size = inst->prop.height[OUTPUT_PORT] * + inst->prop.width[OUTPUT_PORT]; + if ((inst->pic_struct != + MSM_VIDC_PIC_STRUCT_PROGRESSIVE) || + (yuv_size <= 1280 * 720)) + pdata.video_work_mode = VIDC_WORK_MODE_1; + break; + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + u32 codec = inst->fmts[CAPTURE_PORT].fourcc; + + pdata.video_work_mode = VIDC_WORK_MODE_2; + + switch (codec) { + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_TME: + pdata.video_work_mode = VIDC_WORK_MODE_1; + goto decision_done; + } + + } else { + return -EINVAL; + } + +decision_done: + + inst->clk_data.work_mode = pdata.video_work_mode; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VIDEO_WORK_MODE, + (void *)&pdata); + if (rc) + dprintk(VIDC_WARN, + " Failed to configure Work Mode %pK\n", inst); + + /* For WORK_MODE_1, set Low Latency mode by default to HW. */ + + if (inst->session_type == MSM_VIDC_ENCODER && + inst->clk_data.work_mode == VIDC_WORK_MODE_1) { + latency.enable = 1; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, HAL_PARAM_VENC_LOW_LATENCY, + (void *)&latency); + } + + return rc; +} + +static inline int msm_vidc_power_save_mode_enable(struct msm_vidc_inst *inst, + bool enable) +{ + u32 rc = 0, mbs_per_frame, mbs_per_sec; + u32 prop_id = 0; + void *pdata = NULL; + struct hfi_device *hdev = NULL; + enum hal_perf_mode venc_mode; + u32 rc_mode = 0; + u32 hq_mbs_per_sec = 0; + struct msm_vidc_core *core; + struct msm_vidc_inst *instance = NULL; + int complexity; + + core = inst->core; + hdev = inst->core->device; + if (inst->session_type != MSM_VIDC_ENCODER) { + dprintk(VIDC_DBG, + "%s : Not an encoder session. Nothing to do\n", + __func__); + return 0; + } + mbs_per_frame = msm_vidc_get_mbs_per_frame(inst); + mbs_per_sec = mbs_per_frame * msm_vidc_get_fps(inst); + + if (mbs_per_frame > inst->core->resources.max_hq_mbs_per_frame || + mbs_per_sec > inst->core->resources.max_hq_mbs_per_sec) { + enable = true; + } + if (!enable) { + mutex_lock(&core->lock); + list_for_each_entry(instance, &core->instances, list) { + if (instance->clk_data.core_id && + !(instance->flags & VIDC_LOW_POWER)) + hq_mbs_per_sec += + msm_comm_get_inst_load_per_core( + instance, LOAD_CALC_NO_QUIRKS); + } + mutex_unlock(&core->lock); + if (hq_mbs_per_sec > inst->core->resources.max_hq_mbs_per_sec) + enable = true; + } + /* Power saving always disabled for CQ RC mode. */ + rc_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE); + if (rc_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) + enable = false; + + complexity = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VENC_COMPLEXITY); + if (!is_realtime_session(inst) && !complexity) + enable = true; + prop_id = HAL_CONFIG_VENC_PERF_MODE; + venc_mode = enable ? HAL_PERF_MODE_POWER_SAVE : + HAL_PERF_MODE_POWER_MAX_QUALITY; + pdata = &venc_mode; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, prop_id, pdata); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set power save mode for inst: %pK\n", + __func__, inst); + goto fail_power_mode_set; + } + inst->flags = enable ? + inst->flags | VIDC_LOW_POWER : + inst->flags & ~VIDC_LOW_POWER; + + dprintk(VIDC_PROF, + "Power Save Mode for inst: %pK Enable = %d\n", inst, enable); +fail_power_mode_set: + return rc; +} + +static int msm_vidc_move_core_to_power_save_mode(struct msm_vidc_core *core, + u32 core_id) +{ + struct msm_vidc_inst *inst = NULL; + + dprintk(VIDC_PROF, "Core %d : Moving all inst to LP mode\n", core_id); + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->clk_data.core_id == core_id && + inst->session_type == MSM_VIDC_ENCODER) + msm_vidc_power_save_mode_enable(inst, true); + } + mutex_unlock(&core->lock); + + return 0; +} + +static u32 get_core_load(struct msm_vidc_core *core, + u32 core_id, bool lp_mode, bool real_time) +{ + struct msm_vidc_inst *inst = NULL; + u32 current_inst_mbs_per_sec = 0, load = 0; + bool real_time_mode = false; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + u32 cycles, lp_cycles; + + real_time_mode = inst->flags & VIDC_REALTIME ? true : false; + if (!(inst->clk_data.core_id & core_id)) + continue; + if (real_time_mode != real_time) + continue; + if (inst->session_type == MSM_VIDC_DECODER) { + cycles = lp_cycles = inst->clk_data.entry->vpp_cycles; + } else if (inst->session_type == MSM_VIDC_ENCODER) { + lp_mode |= inst->flags & VIDC_LOW_POWER; + cycles = lp_mode ? + inst->clk_data.entry->low_power_cycles : + inst->clk_data.entry->vpp_cycles; + } else { + continue; + } + current_inst_mbs_per_sec = msm_comm_get_inst_load_per_core(inst, + LOAD_CALC_NO_QUIRKS); + load += current_inst_mbs_per_sec * cycles / + inst->clk_data.work_route; + } + mutex_unlock(&core->lock); + + return load; +} + +int msm_vidc_decide_core_and_power_mode(struct msm_vidc_inst *inst) +{ + int rc = 0, hier_mode = 0; + struct hfi_device *hdev; + struct msm_vidc_core *core; + unsigned long max_freq, lp_cycles = 0; + struct hal_videocores_usage_info core_info; + u32 core0_load = 0, core1_load = 0, core0_lp_load = 0, + core1_lp_load = 0; + u32 current_inst_load = 0, current_inst_lp_load = 0, + min_load = 0, min_lp_load = 0; + u32 min_core_id, min_lp_core_id; + u32 complexity; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "%s Invalid args: Inst = %pK\n", + __func__, inst); + return -EINVAL; + } + + core = inst->core; + hdev = core->device; + max_freq = msm_vidc_max_freq(inst->core); + inst->clk_data.core_id = 0; + + core0_load = get_core_load(core, VIDC_CORE_ID_1, false, true); + core1_load = get_core_load(core, VIDC_CORE_ID_2, false, true); + core0_lp_load = get_core_load(core, VIDC_CORE_ID_1, true, true); + core1_lp_load = get_core_load(core, VIDC_CORE_ID_2, true, true); + + min_load = min(core0_load, core1_load); + min_core_id = core0_load < core1_load ? + VIDC_CORE_ID_1 : VIDC_CORE_ID_2; + min_lp_load = min(core0_lp_load, core1_lp_load); + min_lp_core_id = core0_lp_load < core1_lp_load ? + VIDC_CORE_ID_1 : VIDC_CORE_ID_2; + + lp_cycles = inst->session_type == MSM_VIDC_ENCODER ? + inst->clk_data.entry->low_power_cycles : + inst->clk_data.entry->vpp_cycles; + /* + * Incase there is only 1 core enabled, mark it as the core + * with min load. This ensures that this core is selected and + * video session is set to run on the enabled core. + */ + if (inst->capability.max_video_cores.max <= VIDC_CORE_ID_1) { + min_core_id = min_lp_core_id = VIDC_CORE_ID_1; + min_load = core0_load; + min_lp_load = core0_lp_load; + } + + current_inst_load = (msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS) * + inst->clk_data.entry->vpp_cycles)/inst->clk_data.work_route; + + current_inst_lp_load = (msm_comm_get_inst_load(inst, + LOAD_CALC_NO_QUIRKS) * lp_cycles)/inst->clk_data.work_route; + + dprintk(VIDC_DBG, "Core 0 RT Load = %d Core 1 RT Load = %d\n", + core0_load, core1_load); + dprintk(VIDC_DBG, "Core 0 RT LP Load = %d Core 1 RT LP Load = %d\n", + core0_lp_load, core1_lp_load); + dprintk(VIDC_DBG, "Max Load = %lu\n", max_freq); + dprintk(VIDC_DBG, "Current Load = %d Current LP Load = %d\n", + current_inst_load, current_inst_lp_load); + + /* Hier mode can be normal HP or Hybrid HP. */ + + hier_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS); + hier_mode |= msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE); + + /* Try for preferred core based on settings. */ + if (inst->session_type == MSM_VIDC_ENCODER && hier_mode && + inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) { + if (current_inst_load / 2 + core0_load <= max_freq && + current_inst_load / 2 + core1_load <= max_freq) { + if (inst->clk_data.work_mode == VIDC_WORK_MODE_2) { + inst->clk_data.core_id = VIDC_CORE_ID_3; + msm_vidc_power_save_mode_enable(inst, false); + goto decision_done; + } + } + } + + if (inst->session_type == MSM_VIDC_ENCODER && hier_mode && + inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) { + if (current_inst_lp_load / 2 + + core0_lp_load <= max_freq && + current_inst_lp_load / 2 + + core1_lp_load <= max_freq) { + if (inst->clk_data.work_mode == VIDC_WORK_MODE_2) { + inst->clk_data.core_id = VIDC_CORE_ID_3; + msm_vidc_power_save_mode_enable(inst, true); + goto decision_done; + } + } + } + + if (current_inst_load + min_load < max_freq) { + inst->clk_data.core_id = min_core_id; + dprintk(VIDC_DBG, + "Selected normally : Core ID = %d\n", + inst->clk_data.core_id); + msm_vidc_power_save_mode_enable(inst, false); + } else if (current_inst_lp_load + min_load < max_freq) { + /* Move current instance to LP and return */ + inst->clk_data.core_id = min_core_id; + dprintk(VIDC_DBG, + "Selected by moving current to LP : Core ID = %d\n", + inst->clk_data.core_id); + msm_vidc_power_save_mode_enable(inst, true); + + } else if (current_inst_lp_load + min_lp_load < max_freq) { + /* Move all instances to LP mode and return */ + inst->clk_data.core_id = min_lp_core_id; + dprintk(VIDC_DBG, + "Moved all inst's to LP: Core ID = %d\n", + inst->clk_data.core_id); + msm_vidc_move_core_to_power_save_mode(core, min_lp_core_id); + } else { + complexity = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VENC_COMPLEXITY); + if (!is_realtime_session(inst)) { + if (inst->session_type == MSM_VIDC_ENCODER) + msm_vidc_power_save_mode_enable(inst, + (complexity == 0)); + inst->clk_data.core_id = min_core_id; + dprintk(VIDC_DBG, "Supporting NRT session"); + goto decision_done; + + } else { + rc = -EINVAL; + dprintk(VIDC_ERR, + "Sorry ... Core Can't support this load\n"); + } + return rc; + } + +decision_done: + core_info.video_core_enable_mask = inst->clk_data.core_id; + dprintk(VIDC_DBG, + "Core Enable Mask %d\n", core_info.video_core_enable_mask); + + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, + HAL_PARAM_VIDEO_CORES_USAGE, &core_info); + if (rc) + dprintk(VIDC_WARN, + " Failed to configure CORE ID %pK\n", inst); + + rc = msm_comm_scale_clocks_and_bus(inst); + + msm_print_core_status(core, VIDC_CORE_ID_1); + msm_print_core_status(core, VIDC_CORE_ID_2); + + return rc; +} + +void msm_vidc_init_core_clk_ops(struct msm_vidc_core *core) +{ + if (!core) + return; + + if (core->platform_data->vpu_ver == VPU_VERSION_4) + core->core_ops = &core_ops_vpu4; + else + core->core_ops = &core_ops_vpu5; +} + +void msm_print_core_status(struct msm_vidc_core *core, u32 core_id) +{ + struct msm_vidc_inst *inst = NULL; + + dprintk(VIDC_PROF, "Instances running on core %u", core_id); + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + + if ((inst->clk_data.core_id != core_id) && + (inst->clk_data.core_id != VIDC_CORE_ID_3)) + continue; + + dprintk(VIDC_PROF, + "inst %pK (%4ux%4u) to (%4ux%4u) %3u %s %s %s %s %lu\n", + inst, + inst->prop.width[OUTPUT_PORT], + inst->prop.height[OUTPUT_PORT], + inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT], + inst->prop.fps, + inst->session_type == MSM_VIDC_ENCODER ? "ENC" : "DEC", + inst->clk_data.work_mode == VIDC_WORK_MODE_1 ? + "WORK_MODE_1" : "WORK_MODE_2", + inst->flags & VIDC_LOW_POWER ? "LP" : "HQ", + inst->flags & VIDC_REALTIME ? "RealTime" : "NonRTime", + inst->clk_data.min_freq); + } + mutex_unlock(&core->lock); +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h new file mode 100644 index 000000000000..c25bf8103765 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2018, 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_VIDC_CLOCKS_H_ +#define _MSM_VIDC_CLOCKS_H_ +#include "msm_vidc_internal.h" + +/* extra o/p buffers in case of encoder dcvs */ +#define DCVS_ENC_EXTRA_INPUT_BUFFERS 4 + +/* extra o/p buffers in case of decoder dcvs */ +#define DCVS_DEC_EXTRA_OUTPUT_BUFFERS 4 + +void msm_clock_data_reset(struct msm_vidc_inst *inst); +int msm_vidc_validate_operating_rate(struct msm_vidc_inst *inst, + u32 operating_rate); +int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type); +int msm_vidc_set_clocks(struct msm_vidc_core *core); +int msm_comm_vote_bus(struct msm_vidc_core *core); +int msm_dcvs_try_enable(struct msm_vidc_inst *inst); +int msm_vidc_get_mbs_per_frame(struct msm_vidc_inst *inst); +int msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst); +int msm_comm_init_clocks_and_bus_data(struct msm_vidc_inst *inst); +void msm_comm_free_freq_table(struct msm_vidc_inst *inst); +int msm_vidc_decide_work_route(struct msm_vidc_inst *inst); +int msm_vidc_decide_work_mode(struct msm_vidc_inst *inst); +int msm_vidc_decide_core_and_power_mode(struct msm_vidc_inst *inst); +void msm_print_core_status(struct msm_vidc_core *core, u32 core_id); +void msm_vidc_clear_freq_entry(struct msm_vidc_inst *inst, + u32 device_addr); +void msm_comm_free_input_cr_table(struct msm_vidc_inst *inst); +void msm_comm_update_input_cr(struct msm_vidc_inst *inst, u32 index, + u32 cr); +void update_recon_stats(struct msm_vidc_inst *inst, + struct recon_stats_type *recon_stats); +void msm_vidc_init_core_clk_ops(struct msm_vidc_core *core); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c new file mode 100644 index 000000000000..b9e5f089f278 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -0,0 +1,7278 @@ +/* Copyright (c) 2012-2020, 2021 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 +#include +#include +#include +#include +#include +#include +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_clocks.h" +#include "msm_cvp.h" + +#define MSM_VIDC_QBUF_BATCH_TIMEOUT 300 +#define IS_ALREADY_IN_STATE(__p, __d) (\ + (__p >= __d)\ +) + +#define V4L2_EVENT_SEQ_CHANGED_SUFFICIENT \ + V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT +#define V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT \ + V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT +#define V4L2_EVENT_RELEASE_BUFFER_REFERENCE \ + V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE +#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY + +const char *const mpeg_video_vidc_extradata[] = { + "Extradata none", + "Extradata MB Quantization", + "Extradata Interlace Video", + "Extradata enc DTS", + "Reserved", + "Extradata timestamp", + "Extradata S3D Frame Packing", + "Extradata Frame Rate", + "Extradata Panscan Window", + "Extradata Recovery point SEI", + "Extradata Multislice info", + "Extradata number of concealed MB", + "Extradata metadata filler", + "Extradata input crop", + "Extradata digital zoom", + "Extradata aspect ratio", + "Extradata mpeg2 seqdisp", + "Extradata stream userdata", + "Extradata frame QP", + "Extradata frame bits info", + "Extradata LTR", + "Extradata macroblock metadata", + "Extradata VQZip SEI", + "Extradata HDR10+ Metadata", + "Extradata ROI QP", + "Extradata output crop", + "Extradata display colour SEI", + "Extradata light level SEI", + "Extradata PQ Info", + "Extradata display VUI", + "Extradata vpx color space", + "Extradata UBWC CR stats info", + "Extradata enc frame QP", +}; + +static void handle_session_error(enum hal_command_response cmd, void *data); +static void msm_vidc_print_running_insts(struct msm_vidc_core *core); + +int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id) +{ + int rc = 0; + struct v4l2_control ctrl = { + .id = id, + }; + + rc = msm_comm_g_ctrl(inst, &ctrl); + return rc ? rc : ctrl.value; +} + +static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst, + int num_ctrls) +{ + int c = 0; + struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) * + num_ctrls, GFP_KERNEL); + + if (!cluster || !inst) { + kfree(cluster); + return NULL; + } + + for (c = 0; c < num_ctrls; c++) + cluster[c] = inst->ctrls[c]; + + return cluster; +} + +int msm_comm_hal_to_v4l2(int id, int value) +{ + switch (id) { + /* H264 */ + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case HAL_H264_PROFILE_BASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case HAL_H264_PROFILE_CONSTRAINED_BASE: + return + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE; + case HAL_H264_PROFILE_MAIN: + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case HAL_H264_PROFILE_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + case HAL_H264_PROFILE_STEREO_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH; + case HAL_H264_PROFILE_MULTIVIEW_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH; + case HAL_H264_PROFILE_CONSTRAINED_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case HAL_H264_LEVEL_1: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + case HAL_H264_LEVEL_1b: + return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + case HAL_H264_LEVEL_11: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + case HAL_H264_LEVEL_12: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + case HAL_H264_LEVEL_13: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + case HAL_H264_LEVEL_2: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + case HAL_H264_LEVEL_21: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + case HAL_H264_LEVEL_22: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + case HAL_H264_LEVEL_3: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + case HAL_H264_LEVEL_31: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + case HAL_H264_LEVEL_32: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + case HAL_H264_LEVEL_4: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + case HAL_H264_LEVEL_41: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case HAL_H264_LEVEL_42: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case HAL_H264_LEVEL_5: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case HAL_H264_LEVEL_51: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + case HAL_H264_LEVEL_52: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_2; + case HAL_H264_LEVEL_6: + return V4L2_MPEG_VIDEO_H264_LEVEL_6_0; + case HAL_H264_LEVEL_61: + return V4L2_MPEG_VIDEO_H264_LEVEL_6_1; + case HAL_H264_LEVEL_62: + return V4L2_MPEG_VIDEO_H264_LEVEL_6_2; + default: + goto unknown_value; + } + + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + switch (value) { + case HAL_H264_ENTROPY_CAVLC: + return V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC; + case HAL_H264_ENTROPY_CABAC: + return V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + switch (value) { + case HAL_HEVC_PROFILE_MAIN: + return V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN; + case HAL_HEVC_PROFILE_MAIN10: + return V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10; + case HAL_HEVC_PROFILE_MAIN_STILL_PIC: + return V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + switch (value) { + case HAL_HEVC_MAIN_TIER_LEVEL_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1; + case HAL_HEVC_MAIN_TIER_LEVEL_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2; + case HAL_HEVC_MAIN_TIER_LEVEL_2_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1; + case HAL_HEVC_MAIN_TIER_LEVEL_3: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3; + case HAL_HEVC_MAIN_TIER_LEVEL_3_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1; + case HAL_HEVC_MAIN_TIER_LEVEL_4: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4; + case HAL_HEVC_MAIN_TIER_LEVEL_4_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1; + case HAL_HEVC_MAIN_TIER_LEVEL_5: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5; + case HAL_HEVC_MAIN_TIER_LEVEL_5_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1; + case HAL_HEVC_MAIN_TIER_LEVEL_5_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2; + case HAL_HEVC_MAIN_TIER_LEVEL_6: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6; + case HAL_HEVC_MAIN_TIER_LEVEL_6_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1; + case HAL_HEVC_MAIN_TIER_LEVEL_6_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2; + case HAL_HEVC_HIGH_TIER_LEVEL_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1; + case HAL_HEVC_HIGH_TIER_LEVEL_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2; + case HAL_HEVC_HIGH_TIER_LEVEL_2_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1; + case HAL_HEVC_HIGH_TIER_LEVEL_3: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3; + case HAL_HEVC_HIGH_TIER_LEVEL_3_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1; + case HAL_HEVC_HIGH_TIER_LEVEL_4: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4; + case HAL_HEVC_HIGH_TIER_LEVEL_4_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1; + case HAL_HEVC_HIGH_TIER_LEVEL_5: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5; + case HAL_HEVC_HIGH_TIER_LEVEL_5_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1; + case HAL_HEVC_HIGH_TIER_LEVEL_5_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2; + case HAL_HEVC_HIGH_TIER_LEVEL_6: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6; + case HAL_HEVC_HIGH_TIER_LEVEL_6_1: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1; + case HAL_HEVC_HIGH_TIER_LEVEL_6_2: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_2; + case HAL_HEVC_TIER_LEVEL_UNKNOWN: + return V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + switch (value) { + case HAL_VP8_LEVEL_VERSION_0: + return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0; + case HAL_VP8_LEVEL_VERSION_1: + return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1; + case HAL_VP8_LEVEL_VERSION_2: + return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2; + case HAL_VP8_LEVEL_VERSION_3: + return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3; + case HAL_VP8_LEVEL_UNUSED: + return V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP9_PROFILE: + switch (value) { + case HAL_VP9_PROFILE_P0: + return V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P0; + case HAL_VP9_PROFILE_P2_10: + return V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P2_10; + case HAL_VP9_PROFILE_UNUSED: + return V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_UNUSED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL: + switch (value) { + case HAL_VP9_LEVEL_1: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_1; + case HAL_VP9_LEVEL_11: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_11; + case HAL_VP9_LEVEL_2: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_2; + case HAL_VP9_LEVEL_21: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_21; + case HAL_VP9_LEVEL_3: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_3; + case HAL_VP9_LEVEL_31: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_31; + case HAL_VP9_LEVEL_4: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_4; + case HAL_VP9_LEVEL_41: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_41; + case HAL_VP9_LEVEL_5: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_5; + case HAL_VP9_LEVEL_51: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51; + case HAL_VP9_LEVEL_6: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6; + case HAL_VP9_LEVEL_61: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61; + case HAL_VP9_LEVEL_UNUSED: + return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE: + switch (value) { + case HAL_MPEG2_PROFILE_SIMPLE: + return V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE; + case HAL_MPEG2_PROFILE_MAIN: + return V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL: + /* This mapping is not defined properly in V4L2 */ + switch (value) { + case HAL_MPEG2_LEVEL_LL: + return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0; + case HAL_MPEG2_LEVEL_ML: + return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1; + case HAL_MPEG2_LEVEL_HL: + return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2; + default: + goto unknown_value; + } + } + +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +int msm_comm_v4l2_to_hal(int id, int value) +{ + switch (id) { + /* H264 */ + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return HAL_H264_PROFILE_BASELINE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + return HAL_H264_PROFILE_CONSTRAINED_BASE; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return HAL_H264_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return HAL_H264_PROFILE_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH: + return HAL_H264_PROFILE_STEREO_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH: + return HAL_H264_PROFILE_MULTIVIEW_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH: + return HAL_H264_PROFILE_CONSTRAINED_HIGH; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return HAL_H264_LEVEL_1; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return HAL_H264_LEVEL_1b; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return HAL_H264_LEVEL_11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return HAL_H264_LEVEL_12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return HAL_H264_LEVEL_13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return HAL_H264_LEVEL_2; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return HAL_H264_LEVEL_21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return HAL_H264_LEVEL_22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return HAL_H264_LEVEL_3; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return HAL_H264_LEVEL_31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return HAL_H264_LEVEL_32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return HAL_H264_LEVEL_4; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return HAL_H264_LEVEL_41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return HAL_H264_LEVEL_42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return HAL_H264_LEVEL_5; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return HAL_H264_LEVEL_51; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_2: + return HAL_H264_LEVEL_52; + case V4L2_MPEG_VIDEO_H264_LEVEL_6_0: + return HAL_H264_LEVEL_6; + case V4L2_MPEG_VIDEO_H264_LEVEL_6_1: + return HAL_H264_LEVEL_61; + case V4L2_MPEG_VIDEO_H264_LEVEL_6_2: + return HAL_H264_LEVEL_62; + case V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN: + return HAL_H264_LEVEL_UNKNOWN; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC: + return HAL_H264_ENTROPY_CAVLC; + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: + return HAL_H264_ENTROPY_CABAC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0: + return HAL_VP8_LEVEL_VERSION_0; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1: + return HAL_VP8_LEVEL_VERSION_1; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2: + return HAL_VP8_LEVEL_VERSION_2; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3: + return HAL_VP8_LEVEL_VERSION_3; + case V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED: + return HAL_VP8_LEVEL_UNUSED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN: + return HAL_HEVC_PROFILE_MAIN; + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10: + return HAL_HEVC_PROFILE_MAIN10; + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC: + return HAL_HEVC_PROFILE_MAIN_STILL_PIC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1: + return HAL_HEVC_MAIN_TIER_LEVEL_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2: + return HAL_HEVC_MAIN_TIER_LEVEL_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1: + return HAL_HEVC_MAIN_TIER_LEVEL_2_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3: + return HAL_HEVC_MAIN_TIER_LEVEL_3; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1: + return HAL_HEVC_MAIN_TIER_LEVEL_3_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4: + return HAL_HEVC_MAIN_TIER_LEVEL_4; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1: + return HAL_HEVC_MAIN_TIER_LEVEL_4_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5: + return HAL_HEVC_MAIN_TIER_LEVEL_5; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1: + return HAL_HEVC_MAIN_TIER_LEVEL_5_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2: + return HAL_HEVC_MAIN_TIER_LEVEL_5_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6: + return HAL_HEVC_MAIN_TIER_LEVEL_6; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1: + return HAL_HEVC_MAIN_TIER_LEVEL_6_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2: + return HAL_HEVC_MAIN_TIER_LEVEL_6_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1: + return HAL_HEVC_HIGH_TIER_LEVEL_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2: + return HAL_HEVC_HIGH_TIER_LEVEL_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1: + return HAL_HEVC_HIGH_TIER_LEVEL_2_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3: + return HAL_HEVC_HIGH_TIER_LEVEL_3; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1: + return HAL_HEVC_HIGH_TIER_LEVEL_3_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4: + return HAL_HEVC_HIGH_TIER_LEVEL_4; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1: + return HAL_HEVC_HIGH_TIER_LEVEL_4_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5: + return HAL_HEVC_HIGH_TIER_LEVEL_5; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1: + return HAL_HEVC_HIGH_TIER_LEVEL_5_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2: + return HAL_HEVC_HIGH_TIER_LEVEL_5_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6: + return HAL_HEVC_HIGH_TIER_LEVEL_6; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1: + return HAL_HEVC_HIGH_TIER_LEVEL_6_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_2: + return HAL_HEVC_HIGH_TIER_LEVEL_6_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN: + return HAL_HEVC_TIER_LEVEL_UNKNOWN; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_0: + return HAL_TME_PROFILE_0; + case V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_1: + return HAL_TME_PROFILE_1; + case V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_2: + return HAL_TME_PROFILE_2; + case V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_3: + return HAL_TME_PROFILE_3; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER: + return HAL_TME_LEVEL_INTEGER; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE: + return HAL_FLIP_NONE; + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI: + return HAL_FLIP_HORIZONTAL; + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT: + return HAL_FLIP_VERTICAL; + case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH: + return HAL_FLIP_BOTH; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: + return HAL_H264_DB_MODE_DISABLE; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: + return HAL_H264_DB_MODE_ALL_BOUNDARY; + case L_MODE: + return HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT: + return HAL_IFRAMESIZE_TYPE_DEFAULT; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM: + return HAL_IFRAMESIZE_TYPE_MEDIUM; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE: + return HAL_IFRAMESIZE_TYPE_HUGE; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED: + return HAL_IFRAMESIZE_TYPE_UNLIMITED; + default: + goto unknown_value; + } + } + +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +int msm_comm_get_v4l2_profile(int fourcc, int profile) +{ + switch (fourcc) { + case V4L2_PIX_FMT_H264: + return msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + profile); + case V4L2_PIX_FMT_HEVC: + return msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + profile); + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_VP9: + case V4L2_PIX_FMT_MPEG2: + return 0; + default: + dprintk(VIDC_WARN, "Unknown codec id %x\n", fourcc); + return 0; + } +} + +int msm_comm_get_v4l2_level(int fourcc, int level) +{ + switch (fourcc) { + case V4L2_PIX_FMT_H264: + return msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + level); + case V4L2_PIX_FMT_HEVC: + return msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + level); + case V4L2_PIX_FMT_VP8: + return msm_comm_hal_to_v4l2( + V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + level); + case V4L2_PIX_FMT_VP9: + case V4L2_PIX_FMT_MPEG2: + return 0; + default: + dprintk(VIDC_WARN, "Unknown codec id %x\n", fourcc); + return 0; + } +} + +int msm_comm_ctrl_init(struct msm_vidc_inst *inst, + struct msm_vidc_ctrl *drv_ctrls, u32 num_ctrls, + const struct v4l2_ctrl_ops *ctrl_ops) +{ + int idx = 0; + struct v4l2_ctrl_config ctrl_cfg = {0}; + int ret_val = 0; + + if (!inst || !drv_ctrls || !ctrl_ops || !num_ctrls) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + inst->ctrls = kcalloc(num_ctrls, sizeof(struct v4l2_ctrl *), + GFP_KERNEL); + if (!inst->ctrls) { + dprintk(VIDC_ERR, "%s - failed to allocate ctrl\n", __func__); + return -ENOMEM; + } + + ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls); + + if (ret_val) { + dprintk(VIDC_ERR, "CTRL ERR: Control handler init failed, %d\n", + inst->ctrl_handler.error); + return ret_val; + } + + for (; idx < num_ctrls; idx++) { + struct v4l2_ctrl *ctrl = NULL; + + if (IS_PRIV_CTRL(drv_ctrls[idx].id)) { + /*add private control*/ + ctrl_cfg.def = drv_ctrls[idx].default_value; + ctrl_cfg.flags = 0; + ctrl_cfg.id = drv_ctrls[idx].id; + ctrl_cfg.max = drv_ctrls[idx].maximum; + ctrl_cfg.min = drv_ctrls[idx].minimum; + ctrl_cfg.menu_skip_mask = + drv_ctrls[idx].menu_skip_mask; + ctrl_cfg.name = drv_ctrls[idx].name; + ctrl_cfg.ops = ctrl_ops; + ctrl_cfg.step = drv_ctrls[idx].step; + ctrl_cfg.type = drv_ctrls[idx].type; + ctrl_cfg.qmenu = drv_ctrls[idx].qmenu; + + ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler, + &ctrl_cfg, NULL); + } else { + if (drv_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) { + ctrl = v4l2_ctrl_new_std_menu( + &inst->ctrl_handler, + ctrl_ops, + drv_ctrls[idx].id, + drv_ctrls[idx].maximum, + drv_ctrls[idx].menu_skip_mask, + drv_ctrls[idx].default_value); + } else { + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, + ctrl_ops, + drv_ctrls[idx].id, + drv_ctrls[idx].minimum, + drv_ctrls[idx].maximum, + drv_ctrls[idx].step, + drv_ctrls[idx].default_value); + } + } + + if (!ctrl) { + dprintk(VIDC_ERR, "%s - invalid ctrl %s\n", __func__, + drv_ctrls[idx].name); + return -EINVAL; + } + + ret_val = inst->ctrl_handler.error; + if (ret_val) { + dprintk(VIDC_ERR, + "Error adding ctrl (%s) to ctrl handle, %d\n", + drv_ctrls[idx].name, inst->ctrl_handler.error); + return ret_val; + } + + ctrl->flags |= drv_ctrls[idx].flags; + inst->ctrls[idx] = ctrl; + } + + /* Construct a super cluster of all controls */ + inst->cluster = get_super_cluster(inst, num_ctrls); + if (!inst->cluster) { + dprintk(VIDC_WARN, + "Failed to setup super cluster\n"); + return -EINVAL; + } + + v4l2_ctrl_cluster(num_ctrls, inst->cluster); + + return ret_val; +} + +int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + kfree(inst->ctrls); + kfree(inst->cluster); + v4l2_ctrl_handler_free(&inst->ctrl_handler); + + return 0; +} + +int msm_comm_set_stream_output_mode(struct msm_vidc_inst *inst, + enum multi_stream mode) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + if (!is_decode_session(inst)) { + dprintk(VIDC_DBG, "%s: not a decode session %x\n", + __func__, hash32_ptr(inst->session)); + return -EINVAL; + } + + if (mode == HAL_VIDEO_DECODER_SECONDARY) + inst->stream_output_mode = HAL_VIDEO_DECODER_SECONDARY; + else + inst->stream_output_mode = HAL_VIDEO_DECODER_PRIMARY; + + return 0; +} + +enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params, return default mode\n", + __func__); + return HAL_VIDEO_DECODER_PRIMARY; + } + + if (!is_decode_session(inst)) + return HAL_VIDEO_DECODER_PRIMARY; + + if (inst->stream_output_mode == HAL_VIDEO_DECODER_SECONDARY) + return HAL_VIDEO_DECODER_SECONDARY; + else + return HAL_VIDEO_DECODER_PRIMARY; +} + +static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst) +{ + int output_port_mbs, capture_port_mbs; + int fps; + + output_port_mbs = inst->in_reconfig ? + NUM_MBS_PER_FRAME(inst->reconfig_width, + inst->reconfig_height) : + NUM_MBS_PER_FRAME(inst->prop.width[OUTPUT_PORT], + inst->prop.height[OUTPUT_PORT]); + + capture_port_mbs = NUM_MBS_PER_FRAME(inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT]); + + if ((inst->clk_data.operating_rate >> 16) > inst->prop.fps) + fps = (inst->clk_data.operating_rate >> 16) ? + inst->clk_data.operating_rate >> 16 : 1; + else + fps = inst->prop.fps; + + return max(output_port_mbs, capture_port_mbs) * fps; +} + +int msm_comm_get_inst_load(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks) +{ + int load = 0; + + mutex_lock(&inst->lock); + + if (!(inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_STOP_DONE)) + goto exit; + + load = msm_comm_get_mbs_per_sec(inst); + + if (is_thumbnail_session(inst)) { + if (quirks & LOAD_CALC_IGNORE_THUMBNAIL_LOAD) + load = 0; + } + + if (is_turbo_session(inst)) { + if (!(quirks & LOAD_CALC_IGNORE_TURBO_LOAD)) + load = inst->core->resources.max_load; + } + + /* Clock and Load calculations for REALTIME/NON-REALTIME + * OPERATING RATE SET/NO OPERATING RATE SET + * + * | OPERATING RATE SET | OPERATING RATE NOT SET | + * ----------------|--------------------- |------------------------| + * REALTIME | load = res * op_rate | load = res * fps | + * | clk = res * op_rate | clk = res * fps | + * ----------------|----------------------|------------------------| + * NON-REALTIME | load = res * 1 fps | load = res * 1 fps | + * | clk = res * op_rate | clk = res * fps | + * ----------------|----------------------|------------------------| + */ + + if (!is_realtime_session(inst) && + (quirks & LOAD_CALC_IGNORE_NON_REALTIME_LOAD)) { + if (!inst->prop.fps) { + dprintk(VIDC_INFO, "instance:%pK fps = 0\n", inst); + load = 0; + } else { + load = msm_comm_get_mbs_per_sec(inst) / inst->prop.fps; + } + } + +exit: + mutex_unlock(&inst->lock); + return load; +} + +int msm_comm_get_inst_load_per_core(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks) +{ + int load = msm_comm_get_inst_load(inst, quirks); + + if (inst->clk_data.core_id == VIDC_CORE_ID_3) + load = load / 2; + + return load; +} + +int msm_comm_get_load(struct msm_vidc_core *core, + enum session_type type, enum load_calc_quirks quirks) +{ + struct msm_vidc_inst *inst = NULL; + int num_mbs_per_sec = 0; + + if (!core) { + dprintk(VIDC_ERR, "Invalid args: %pK\n", core); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type != type) + continue; + + num_mbs_per_sec += msm_comm_get_inst_load(inst, quirks); + } + mutex_unlock(&core->lock); + + return num_mbs_per_sec; +} + +enum hal_domain get_hal_domain(int session_type) +{ + enum hal_domain domain; + + switch (session_type) { + case MSM_VIDC_ENCODER: + domain = HAL_VIDEO_DOMAIN_ENCODER; + break; + case MSM_VIDC_DECODER: + domain = HAL_VIDEO_DOMAIN_DECODER; + break; + case MSM_VIDC_CVP: + domain = HAL_VIDEO_DOMAIN_CVP; + break; + default: + dprintk(VIDC_ERR, "Wrong domain %d\n", session_type); + domain = HAL_UNUSED_DOMAIN; + break; + } + + return domain; +} + +enum hal_video_codec get_hal_codec(int fourcc) +{ + enum hal_video_codec codec; + + switch (fourcc) { + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_NO_SC: + codec = HAL_VIDEO_CODEC_H264; + break; + case V4L2_PIX_FMT_H264_MVC: + codec = HAL_VIDEO_CODEC_MVC; + break; + case V4L2_PIX_FMT_MPEG1: + codec = HAL_VIDEO_CODEC_MPEG1; + break; + case V4L2_PIX_FMT_MPEG2: + codec = HAL_VIDEO_CODEC_MPEG2; + break; + case V4L2_PIX_FMT_VP8: + codec = HAL_VIDEO_CODEC_VP8; + break; + case V4L2_PIX_FMT_VP9: + codec = HAL_VIDEO_CODEC_VP9; + break; + case V4L2_PIX_FMT_HEVC: + codec = HAL_VIDEO_CODEC_HEVC; + break; + case V4L2_PIX_FMT_TME: + codec = HAL_VIDEO_CODEC_TME; + break; + case V4L2_PIX_FMT_CVP: + codec = HAL_VIDEO_CODEC_CVP; + break; + default: + dprintk(VIDC_ERR, "Wrong codec: %#x\n", fourcc); + codec = HAL_UNUSED_CODEC; + break; + } + + return codec; +} + +enum hal_uncompressed_format msm_comm_get_hal_uncompressed(int fourcc) +{ + enum hal_uncompressed_format format = HAL_UNUSED_COLOR; + + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + format = HAL_COLOR_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV12_512: + format = HAL_COLOR_FORMAT_NV12_512; + break; + case V4L2_PIX_FMT_NV21: + format = HAL_COLOR_FORMAT_NV21; + break; + case V4L2_PIX_FMT_NV12_UBWC: + format = HAL_COLOR_FORMAT_NV12_UBWC; + break; + case V4L2_PIX_FMT_NV12_TP10_UBWC: + format = HAL_COLOR_FORMAT_NV12_TP10_UBWC; + break; + case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS: + format = HAL_COLOR_FORMAT_P010; + break; + default: + format = HAL_UNUSED_COLOR; + break; + } + + return format; +} + +struct msm_vidc_core *get_vidc_core(int core_id) +{ + struct msm_vidc_core *core; + int found = 0; + + if (core_id > MSM_VIDC_CORES_MAX) { + dprintk(VIDC_ERR, "Core id = %d is greater than max = %d\n", + core_id, MSM_VIDC_CORES_MAX); + return NULL; + } + mutex_lock(&vidc_driver->lock); + list_for_each_entry(core, &vidc_driver->cores, list) { + if (core->id == core_id) { + found = 1; + break; + } + } + mutex_unlock(&vidc_driver->lock); + if (found) + return core; + return NULL; +} + +const struct msm_vidc_format *msm_comm_get_pixel_fmt_index( + const struct msm_vidc_format fmt[], int size, int index, int fmt_type) +{ + int i, k = 0; + + if (!fmt || index < 0) { + dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK, index = %d\n", + fmt, index); + return NULL; + } + for (i = 0; i < size; i++) { + if (fmt[i].type != fmt_type) + continue; + if (k == index) + break; + k++; + } + if (i == size) { + dprintk(VIDC_INFO, "Format not found\n"); + return NULL; + } + return &fmt[i]; +} +struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc( + struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type) +{ + int i; + + if (!fmt) { + dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK\n", fmt); + return NULL; + } + for (i = 0; i < size; i++) { + if (fmt[i].fourcc == fourcc) + break; + } + if (i == size) { + dprintk(VIDC_INFO, "Format not found\n"); + return NULL; + } + return &fmt[i]; +} + +struct msm_vidc_format_constraint *msm_comm_get_pixel_fmt_constraints( + struct msm_vidc_format_constraint fmt[], int size, int fourcc) +{ + int i; + + if (!fmt) { + dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK\n", fmt); + return NULL; + } + for (i = 0; i < size; i++) { + if (fmt[i].fourcc == fourcc) + break; + } + if (i == size) { + dprintk(VIDC_INFO, "Format constraint not found.\n"); + return NULL; + } + return &fmt[i]; +} + +struct buf_queue *msm_comm_get_vb2q( + struct msm_vidc_inst *inst, enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return &inst->bufq[CAPTURE_PORT]; + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return &inst->bufq[OUTPUT_PORT]; + return NULL; +} + +static void handle_sys_init_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_core *core; + struct vidc_hal_sys_init_done *sys_init_msg; + u32 index; + + if (!IS_HAL_SYS_CMD(cmd)) { + dprintk(VIDC_ERR, "%s - invalid cmd\n", __func__); + return; + } + + index = SYS_MSG_INDEX(cmd); + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for sys init\n"); + return; + } + core = get_vidc_core(response->device_id); + if (!core) { + dprintk(VIDC_ERR, "Wrong device_id received\n"); + return; + } + sys_init_msg = &response->data.sys_init_done; + if (!sys_init_msg) { + dprintk(VIDC_ERR, "sys_init_done message not proper\n"); + return; + } + + core->enc_codec_supported = sys_init_msg->enc_codec_supported; + core->dec_codec_supported = sys_init_msg->dec_codec_supported; + + /* This should come from sys_init_done */ + core->resources.max_inst_count = + sys_init_msg->max_sessions_supported ? + min_t(u32, sys_init_msg->max_sessions_supported, + MAX_SUPPORTED_INSTANCES) : MAX_SUPPORTED_INSTANCES; + + core->resources.max_secure_inst_count = + core->resources.max_secure_inst_count ? + core->resources.max_secure_inst_count : + core->resources.max_inst_count; + + if (core->id == MSM_VIDC_CORE_VENUS && + (core->dec_codec_supported & HAL_VIDEO_CODEC_H264)) + core->dec_codec_supported |= + HAL_VIDEO_CODEC_MVC; + + core->codec_count = sys_init_msg->codec_count; + memcpy(core->capabilities, sys_init_msg->capabilities, + sys_init_msg->codec_count * sizeof(struct msm_vidc_capability)); + + dprintk(VIDC_DBG, + "%s: supported_codecs[%d]: enc = %#x, dec = %#x\n", + __func__, core->codec_count, core->enc_codec_supported, + core->dec_codec_supported); + + complete(&(core->completions[index])); +} + +static void put_inst_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + + msm_vidc_destroy(inst); +} + +void put_inst(struct msm_vidc_inst *inst) +{ + if (!inst) + return; + + kref_put(&inst->kref, put_inst_helper); +} + +struct msm_vidc_inst *get_inst(struct msm_vidc_core *core, + void *session_id) +{ + struct msm_vidc_inst *inst = NULL; + bool matches = false; + + if (!core || !session_id) + return NULL; + + mutex_lock(&core->lock); + /* + * This is as good as !list_empty(!inst->list), but at this point + * we don't really know if inst was kfree'd via close syscall before + * hardware could respond. So manually walk thru the list of active + * sessions + */ + list_for_each_entry(inst, &core->instances, list) { + if (inst == session_id) { + /* + * Even if the instance is valid, we really shouldn't + * be receiving or handling callbacks when we've deleted + * our session with HFI + */ + matches = !!inst->session; + break; + } + } + + /* + * kref_* is atomic_int backed, so no need for inst->lock. But we can + * always acquire inst->lock and release it in put_inst for a stronger + * locking system. + */ + inst = (matches && kref_get_unless_zero(&inst->kref)) ? inst : NULL; + mutex_unlock(&core->lock); + + return inst; +} + +static void handle_session_release_buf_done(enum hal_command_response cmd, + void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + struct internal_buf *buf; + struct list_head *ptr, *next; + struct hal_buffer_info *buffer; + u32 buf_found = false; + u32 address; + + if (!response) { + dprintk(VIDC_ERR, "Invalid release_buf_done response\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + buffer = &response->data.buffer_info; + address = buffer->buffer_addr; + + mutex_lock(&inst->scratchbufs.lock); + list_for_each_safe(ptr, next, &inst->scratchbufs.list) { + buf = list_entry(ptr, struct internal_buf, list); + if (address == buf->smem.device_addr) { + dprintk(VIDC_DBG, "releasing scratch: %x\n", + buf->smem.device_addr); + buf_found = true; + } + } + mutex_unlock(&inst->scratchbufs.lock); + + mutex_lock(&inst->persistbufs.lock); + list_for_each_safe(ptr, next, &inst->persistbufs.list) { + buf = list_entry(ptr, struct internal_buf, list); + if (address == buf->smem.device_addr) { + dprintk(VIDC_DBG, "releasing persist: %x\n", + buf->smem.device_addr); + buf_found = true; + } + } + mutex_unlock(&inst->persistbufs.lock); + + if (!buf_found) + dprintk(VIDC_ERR, "invalid buffer received from firmware"); + if (IS_HAL_SESSION_CMD(cmd)) + complete(&inst->completions[SESSION_MSG_INDEX(cmd)]); + else + dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); + + put_inst(inst); +} + +static void handle_sys_release_res_done( + enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_core *core; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for sys init\n"); + return; + } + core = get_vidc_core(response->device_id); + if (!core) { + dprintk(VIDC_ERR, "Wrong device_id received\n"); + return; + } + complete(&core->completions[ + SYS_MSG_INDEX(HAL_SYS_RELEASE_RESOURCE_DONE)]); +} + +void change_inst_state(struct msm_vidc_inst *inst, enum instance_state state) +{ + if (!inst) { + dprintk(VIDC_ERR, "Invalid parameter %s\n", __func__); + return; + } + mutex_lock(&inst->lock); + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_DBG, + "Inst: %pK is in bad state can't change state to %d\n", + inst, state); + goto exit; + } + dprintk(VIDC_DBG, "Moved inst: %pK from state: %d to state: %d\n", + inst, inst->state, state); + inst->state = state; +exit: + mutex_unlock(&inst->lock); +} + +static int signal_session_msg_receipt(enum hal_command_response cmd, + struct msm_vidc_inst *inst) +{ + if (!inst) { + dprintk(VIDC_ERR, "Invalid(%pK) instance id\n", inst); + return -EINVAL; + } + if (IS_HAL_SESSION_CMD(cmd)) { + complete(&inst->completions[SESSION_MSG_INDEX(cmd)]); + } else { + dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); + return -EINVAL; + } + return 0; +} + +static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst, + enum hal_command_response cmd) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!IS_HAL_SESSION_CMD(cmd)) { + dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); + return -EINVAL; + } + hdev = (struct hfi_device *)(inst->core->device); + rc = wait_for_completion_timeout( + &inst->completions[SESSION_MSG_INDEX(cmd)], + msecs_to_jiffies( + inst->core->resources.msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, "Wait interrupted or timed out: %d\n", + SESSION_MSG_INDEX(cmd)); + msm_comm_kill_session(inst); + rc = -EIO; + } else { + rc = 0; + } + return rc; +} + +static int wait_for_state(struct msm_vidc_inst *inst, + enum instance_state flipped_state, + enum instance_state desired_state, + enum hal_command_response hal_cmd) +{ + int rc = 0; + + if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) { + dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto err_same_state; + } + dprintk(VIDC_DBG, "Waiting for hal_cmd: %d\n", hal_cmd); + rc = wait_for_sess_signal_receipt(inst, hal_cmd); + if (!rc) + change_inst_state(inst, desired_state); +err_same_state: + return rc; +} + +void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type) +{ + struct v4l2_event event = {.id = 0, .type = event_type}; + + v4l2_event_queue_fh(&inst->event_handler, &event); +} + +static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst) +{ + enum hal_command_response cmd = HAL_SESSION_ERROR; + struct msm_vidc_cb_cmd_done response = {0}; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return; + } + dprintk(VIDC_ERR, "%s: Too many clients\n", __func__); + response.session_id = inst; + response.status = VIDC_ERR_MAX_CLIENTS; + handle_session_error(cmd, (void *)&response); +} + +static void print_cap(const char *type, + struct hal_capability_supported *cap) +{ + dprintk(VIDC_DBG, + "%-24s: %-8d %-8d %-8d\n", + type, cap->min, cap->max, cap->step_size); +} + +static int msm_vidc_comm_update_ctrl(struct msm_vidc_inst *inst, + u32 id, struct hal_capability_supported *capability) +{ + struct v4l2_ctrl *ctrl = NULL; + int rc = 0; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, id); + if (ctrl) { + v4l2_ctrl_modify_range(ctrl, capability->min, + capability->max, ctrl->step, + ctrl->default_value); + dprintk(VIDC_DBG, + "%s: Updated Range = %lld --> %lld Def value = %lld\n", + ctrl->name, ctrl->minimum, ctrl->maximum, + ctrl->default_value); + } else { + dprintk(VIDC_ERR, + "Failed to find Conrol %d\n", id); + rc = -EINVAL; + } + + return rc; + } + +static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst) +{ + if (inst->session_type == MSM_VIDC_ENCODER) { + if (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) == + HAL_VIDEO_CODEC_TME) + return; + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE, + &inst->capability.hier_p_hybrid); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS, + &inst->capability.hier_b); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS, + &inst->capability.hier_p); + msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_BITRATE, + &inst->capability.bitrate); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE, + &inst->capability.bitrate); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + &inst->capability.peakbitrate); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP, + &inst->capability.i_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP, + &inst->capability.p_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP, + &inst->capability.b_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN, + &inst->capability.i_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN, + &inst->capability.p_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN, + &inst->capability.b_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX, + &inst->capability.i_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX, + &inst->capability.p_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX, + &inst->capability.b_qp); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH, + &inst->capability.blur_width); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT, + &inst->capability.blur_height); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, + &inst->capability.slice_bytes); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + &inst->capability.slice_mbs); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT, + &inst->capability.ltr_count); + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, + &inst->capability.bframe); + } + msm_vidc_comm_update_ctrl(inst, + V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE, + &inst->capability.frame_rate); +} + +static void handle_session_init_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst = NULL; + struct msm_vidc_capability *capability = NULL; + struct hfi_device *hdev; + struct msm_vidc_core *core; + struct hal_profile_level *profile_level; + u32 i, codec; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for session init\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + if (response->status) { + dprintk(VIDC_ERR, + "Session init response from FW : %#x\n", + response->status); + if (response->status == VIDC_ERR_MAX_CLIENTS) + msm_comm_generate_max_clients_error(inst); + else + msm_comm_generate_session_error(inst); + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); + return; + } + + if (inst->session_type == MSM_VIDC_CVP) { + dprintk(VIDC_DBG, "%s: cvp session %#x\n", + __func__, hash32_ptr(inst->session)); + signal_session_msg_receipt(cmd, inst); + put_inst(inst); + return; + } + + core = inst->core; + hdev = inst->core->device; + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + /* check if capabilities are available for this session */ + for (i = 0; i < VIDC_MAX_SESSIONS; i++) { + if (core->capabilities[i].codec == + get_hal_codec(codec) && + core->capabilities[i].domain == + get_hal_domain(inst->session_type)) { + capability = &core->capabilities[i]; + break; + } + } + + if (capability) { + dprintk(VIDC_DBG, + "%s: capabilities for codec 0x%x, domain %#x\n", + __func__, capability->codec, capability->domain); + memcpy(&inst->capability, capability, + sizeof(struct msm_vidc_capability)); + } else { + dprintk(VIDC_ERR, + "Watch out : Some property may fail inst %pK\n", inst); + dprintk(VIDC_ERR, + "Caps N/A for codec 0x%x, domain %#x\n", + inst->capability.codec, inst->capability.domain); + } + inst->capability.pixelprocess_capabilities = + call_hfi_op(hdev, get_core_capabilities, hdev->hfi_device_data); + + dprintk(VIDC_DBG, + "Capability type : min max step size\n"); + print_cap("width", &inst->capability.width); + print_cap("height", &inst->capability.height); + print_cap("mbs_per_frame", &inst->capability.mbs_per_frame); + print_cap("mbs_per_sec", &inst->capability.mbs_per_sec); + print_cap("frame_rate", &inst->capability.frame_rate); + print_cap("bitrate", &inst->capability.bitrate); + print_cap("peak_bitrate", &inst->capability.peakbitrate); + print_cap("scale_x", &inst->capability.scale_x); + print_cap("scale_y", &inst->capability.scale_y); + print_cap("hier_p", &inst->capability.hier_p); + print_cap("ltr_count", &inst->capability.ltr_count); + print_cap("bframe", &inst->capability.bframe); + print_cap("secure_output2_threshold", + &inst->capability.secure_output2_threshold); + print_cap("hier_b", &inst->capability.hier_b); + print_cap("lcu_size", &inst->capability.lcu_size); + print_cap("hier_p_hybrid", &inst->capability.hier_p_hybrid); + print_cap("mbs_per_sec_low_power", + &inst->capability.mbs_per_sec_power_save); + print_cap("extradata", &inst->capability.extradata); + print_cap("profile", &inst->capability.profile); + print_cap("level", &inst->capability.level); + print_cap("i_qp", &inst->capability.i_qp); + print_cap("p_qp", &inst->capability.p_qp); + print_cap("b_qp", &inst->capability.b_qp); + print_cap("rc_modes", &inst->capability.rc_modes); + print_cap("blur_width", &inst->capability.blur_width); + print_cap("blur_height", &inst->capability.blur_height); + print_cap("rotation", &inst->capability.rotation); + print_cap("color_space_caps", &inst->capability.color_space_caps); + print_cap("slice_delivery_mode", &inst->capability.slice_delivery_mode); + print_cap("slice_bytes", &inst->capability.slice_bytes); + print_cap("slice_mbs", &inst->capability.slice_mbs); + print_cap("secure", &inst->capability.secure); + print_cap("max_num_b_frames", &inst->capability.max_num_b_frames); + print_cap("max_video_cores", &inst->capability.max_video_cores); + print_cap("max_work_modes", &inst->capability.max_work_modes); + print_cap("ubwc_cr_stats", &inst->capability.ubwc_cr_stats); + + dprintk(VIDC_DBG, "profile count : %u\n", + inst->capability.profile_level.profile_count); + for (i = 0; i < inst->capability.profile_level.profile_count; i++) { + profile_level = + &inst->capability.profile_level.profile_level[i]; + dprintk(VIDC_DBG, "profile : %u\n", profile_level->profile); + dprintk(VIDC_DBG, "level : %u\n", profile_level->level); + } + + signal_session_msg_receipt(cmd, inst); + + /* + * Update controls after informing session_init_done to avoid + * timeouts. + */ + + msm_vidc_comm_update_ctrl_limits(inst); + put_inst(inst); +} + +static void msm_vidc_queue_rbr_event(struct msm_vidc_inst *inst, + int fd, u32 offset, u32 output_tag) +{ + struct v4l2_event buf_event = {0}; + u32 *ptr; + + buf_event.type = V4L2_EVENT_RELEASE_BUFFER_REFERENCE; + ptr = (u32 *)buf_event.u.data; + ptr[0] = fd; + ptr[1] = offset; + ptr[2] = output_tag; + + v4l2_event_queue_fh(&inst->event_handler, &buf_event); +} + +static void handle_event_change(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_inst *inst = NULL; + struct msm_vidc_cb_event *event_notify = data; + int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + struct v4l2_event seq_changed_event = {0}; + int rc = 0; + struct hfi_device *hdev; + u32 *ptr = NULL; + struct hal_buffer_requirements *bufreq; + int extra_buff_count = 0; + + if (!event_notify) { + dprintk(VIDC_WARN, "Got an empty event from hfi\n"); + return; + } + + inst = get_inst(get_vidc_core(event_notify->device_id), + event_notify->session_id); + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + goto err_bad_event; + } + hdev = inst->core->device; + + switch (event_notify->hal_event_type) { + case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES: + event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; + break; + case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES: + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + break; + case HAL_EVENT_RELEASE_BUFFER_REFERENCE: + { + struct msm_vidc_buffer *mbuf; + u32 planes[VIDEO_MAX_PLANES] = {0}; + + dprintk(VIDC_DBG, + "%s: inst: %pK data_buffer: %x extradata_buffer: %x\n", + __func__, inst, event_notify->packet_buffer, + event_notify->extra_data_buffer); + + planes[0] = event_notify->packet_buffer; + planes[1] = event_notify->extra_data_buffer; + mbuf = msm_comm_get_buffer_using_device_planes(inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, planes); + if (!mbuf || !kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, + "%s: data_addr %x, extradata_addr %x not found\n", + __func__, planes[0], planes[1]); + } else { + mbuf->output_tag = event_notify->output_tag; + handle_release_buffer_reference(inst, mbuf); + kref_put_mbuf(mbuf); + } + goto err_bad_event; + } + default: + break; + } + + /* Bit depth and pic struct changed event are combined into a single + * event (insufficient event) for the userspace. Currently bitdepth + * changes is only for HEVC and interlaced support is for all + * codecs except HEVC + * event data is now as follows: + * u32 *ptr = seq_changed_event.u.data; + * ptr[0] = height + * ptr[1] = width + * ptr[2] = bit depth + * ptr[3] = pic struct (progressive or interlaced) + * ptr[4] = colour space + * ptr[5] = crop_data(top) + * ptr[6] = crop_data(left) + * ptr[7] = crop_data(height) + * ptr[8] = crop_data(width) + * ptr[9] = profile + * ptr[10] = level + */ + + inst->entropy_mode = event_notify->entropy_mode; + inst->profile = event_notify->profile; + inst->level = event_notify->level; + inst->prop.crop_info.left = + event_notify->crop_data.left; + inst->prop.crop_info.top = + event_notify->crop_data.top; + inst->prop.crop_info.height = + event_notify->crop_data.height; + inst->prop.crop_info.width = + event_notify->crop_data.width; + /* HW returns progressive_only flag in pic_struct. */ + inst->pic_struct = + event_notify->pic_struct ? + MSM_VIDC_PIC_STRUCT_PROGRESSIVE : + MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED; + + ptr = (u32 *)seq_changed_event.u.data; + ptr[0] = event_notify->height; + ptr[1] = event_notify->width; + ptr[2] = event_notify->bit_depth; + ptr[3] = event_notify->pic_struct; + ptr[4] = event_notify->colour_space; + ptr[5] = event_notify->crop_data.top; + ptr[6] = event_notify->crop_data.left; + ptr[7] = event_notify->crop_data.height; + ptr[8] = event_notify->crop_data.width; + ptr[9] = msm_comm_get_v4l2_profile( + inst->fmts[OUTPUT_PORT].fourcc, + event_notify->profile); + ptr[10] = msm_comm_get_v4l2_level( + inst->fmts[OUTPUT_PORT].fourcc, + event_notify->level); + ptr[11] = event_notify->max_dpb_count; + ptr[12] = event_notify->max_ref_count; + ptr[13] = event_notify->max_dec_buffering; + + dprintk(VIDC_DBG, + "Event payload: height = %u width = %u profile = %u level = %u\n", + event_notify->height, event_notify->width, + ptr[9], ptr[10]); + + dprintk(VIDC_DBG, + "Event payload: bit_depth = %u pic_struct = %u colour_space = %u\n", + event_notify->bit_depth, event_notify->pic_struct, + event_notify->colour_space); + + dprintk(VIDC_DBG, + "Event payload: CROP top = %u left = %u Height = %u Width = %u\n", + event_notify->crop_data.top, + event_notify->crop_data.left, + event_notify->crop_data.height, + event_notify->crop_data.width); + + mutex_lock(&inst->lock); + inst->in_reconfig = true; + inst->reconfig_height = event_notify->height; + inst->reconfig_width = event_notify->width; + inst->bit_depth = event_notify->bit_depth; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) + return; + + /* No need to add extra buffers to DPBs */ + bufreq->buffer_count_min = event_notify->capture_buf_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_min; + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT2); + if (!bufreq) + return; + + extra_buff_count = msm_vidc_get_extra_buff_count(inst, + HAL_BUFFER_OUTPUT2); + bufreq->buffer_count_min = event_notify->capture_buf_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_min + + extra_buff_count; + } else { + + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) + return; + + extra_buff_count = msm_vidc_get_extra_buff_count(inst, + HAL_BUFFER_OUTPUT); + bufreq->buffer_count_min = event_notify->capture_buf_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_min + + extra_buff_count; + } + dprintk(VIDC_DBG, "%s: buffer[%d] count: min %d min_host %d\n", + __func__, bufreq->buffer_type, bufreq->buffer_count_min, + bufreq->buffer_count_min_host); + + mutex_unlock(&inst->lock); + + if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) { + dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n"); + } else { + dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n"); + dprintk(VIDC_DBG, + "event_notify->height = %d event_notify->width = %d\n", + event_notify->height, + event_notify->width); + } + + rc = msm_vidc_check_session_supported(inst); + if (!rc) { + seq_changed_event.type = event; + v4l2_event_queue_fh(&inst->event_handler, &seq_changed_event); + } else if (rc == -ENOTSUPP) { + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED); + } else if (rc == -EBUSY) { + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_HW_OVERLOAD); + } + +err_bad_event: + put_inst(inst); +} + +static void handle_session_prop_info(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct getprop_buf *getprop; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for prop info\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + getprop = kzalloc(sizeof(*getprop), GFP_KERNEL); + if (!getprop) { + dprintk(VIDC_ERR, "%s: getprop kzalloc failed\n", __func__); + goto err_prop_info; + } + + getprop->data = kmemdup(&response->data.property, + sizeof(union hal_get_property), GFP_KERNEL); + if (!getprop->data) { + dprintk(VIDC_ERR, "%s: kmemdup failed\n", __func__); + kfree(getprop); + goto err_prop_info; + } + + mutex_lock(&inst->pending_getpropq.lock); + list_add_tail(&getprop->list, &inst->pending_getpropq.list); + mutex_unlock(&inst->pending_getpropq.lock); + + signal_session_msg_receipt(cmd, inst); +err_prop_info: + put_inst(inst); +} + +static void handle_load_resource_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for load resource\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + if (response->status) { + dprintk(VIDC_ERR, + "Load resource response from FW : %#x\n", + response->status); + msm_comm_generate_session_error(inst); + } + + put_inst(inst); +} + +static void handle_start_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, "Failed to get valid response for start\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); +} + +static void handle_stop_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, "Failed to get valid response for stop\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); +} + +static void handle_release_res_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for release resource\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); +} + +void msm_comm_validate_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + u32 buffers_owned_by_driver = 0; + struct hal_buffer_requirements *output_buf; + + output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + return; + } + mutex_lock(&inst->outputbufs.lock); + if (list_empty(&inst->outputbufs.list)) { + dprintk(VIDC_DBG, "%s: no OUTPUT buffers allocated\n", + __func__); + mutex_unlock(&inst->outputbufs.lock); + return; + } + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + if (binfo->buffer_ownership != DRIVER) { + dprintk(VIDC_DBG, + "This buffer is with FW %x\n", + binfo->smem.device_addr); + continue; + } + buffers_owned_by_driver++; + } + mutex_unlock(&inst->outputbufs.lock); + + if (buffers_owned_by_driver != output_buf->buffer_count_actual) { + dprintk(VIDC_WARN, + "OUTPUT Buffer count mismatch %d of %d\n", + buffers_owned_by_driver, + output_buf->buffer_count_actual); + msm_vidc_handle_hw_error(inst->core); + } +} + +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + struct hfi_device *hdev; + struct vidc_frame_data frame_data = {0}; + struct hal_buffer_requirements *output_buf, *extra_buf; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + return 0; + } + dprintk(VIDC_DBG, + "output: num = %d, size = %d\n", + output_buf->buffer_count_actual, + output_buf->buffer_size); + + extra_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); + + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + if (binfo->buffer_ownership != DRIVER) + continue; + if (binfo->mark_remove) + continue; + frame_data.alloc_len = output_buf->buffer_size; + frame_data.filled_len = 0; + frame_data.offset = 0; + frame_data.device_addr = binfo->smem.device_addr; + frame_data.flags = 0; + frame_data.extradata_addr = binfo->smem.device_addr + + output_buf->buffer_size; + frame_data.buffer_type = HAL_BUFFER_OUTPUT; + frame_data.extradata_size = extra_buf ? + extra_buf->buffer_size : 0; + rc = call_hfi_op(hdev, session_ftb, + (void *) inst->session, &frame_data); + binfo->buffer_ownership = FIRMWARE; + } + mutex_unlock(&inst->outputbufs.lock); + + return 0; +} + +static void handle_session_flush(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + struct v4l2_event flush_event = {0}; + u32 *ptr = NULL; + enum hal_flush flush_type; + int rc; + + if (!response) { + dprintk(VIDC_ERR, "Failed to get valid response for flush\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + if (response->data.flush_type & HAL_FLUSH_INPUT) + mutex_lock(&inst->bufq[OUTPUT_PORT].lock); + if (response->data.flush_type & HAL_FLUSH_OUTPUT) + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + + if (!(inst->fmts[OUTPUT_PORT].defer_outputs && + inst->in_reconfig)) + msm_comm_validate_output_buffers(inst); + + if (!inst->in_reconfig) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", + rc); + } + } + } + flush_event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE; + ptr = (u32 *)flush_event.u.data; + + flush_type = response->data.flush_type; + switch (flush_type) { + case HAL_FLUSH_INPUT: + inst->in_flush = false; + ptr[0] = V4L2_QCOM_CMD_FLUSH_OUTPUT; + break; + case HAL_FLUSH_OUTPUT: + inst->out_flush = false; + ptr[0] = V4L2_QCOM_CMD_FLUSH_CAPTURE; + break; + case HAL_FLUSH_ALL: + inst->in_flush = false; + inst->out_flush = false; + ptr[0] |= V4L2_QCOM_CMD_FLUSH_CAPTURE; + ptr[0] |= V4L2_QCOM_CMD_FLUSH_OUTPUT; + break; + default: + dprintk(VIDC_ERR, "Invalid flush type received!"); + goto exit; + } + + dprintk(VIDC_DBG, + "Notify flush complete, flush_type: %x\n", flush_type); + v4l2_event_queue_fh(&inst->event_handler, &flush_event); + +exit: + if (response->data.flush_type & HAL_FLUSH_OUTPUT) + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); + if (response->data.flush_type & HAL_FLUSH_INPUT) + mutex_unlock(&inst->bufq[OUTPUT_PORT].lock); + + put_inst(inst); +} + +static void handle_session_error(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct hfi_device *hdev = NULL; + struct msm_vidc_inst *inst = NULL; + int event = V4L2_EVENT_MSM_VIDC_SYS_ERROR; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for session error\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + hdev = inst->core->device; + dprintk(VIDC_ERR, "Session error received for inst %pK session %x\n", + inst, hash32_ptr(inst->session)); + + if (response->status == VIDC_ERR_MAX_CLIENTS) { + dprintk(VIDC_WARN, "Too many clients, rejecting %pK", inst); + event = V4L2_EVENT_MSM_VIDC_MAX_CLIENTS; + + /* + * Clean the HFI session now. Since inst->state is moved to + * INVALID, forward thread doesn't know FW has valid session + * or not. This is the last place driver knows that there is + * no session in FW. Hence clean HFI session now. + */ + + msm_comm_session_clean(inst); + } else if (response->status == VIDC_ERR_NOT_SUPPORTED) { + dprintk(VIDC_WARN, "Unsupported bitstream in %pK", inst); + event = V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED; + } else { + dprintk(VIDC_WARN, "Unknown session error (%d) for %pK\n", + response->status, inst); + event = V4L2_EVENT_MSM_VIDC_SYS_ERROR; + } + + /* change state before sending error to client */ + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + msm_vidc_queue_v4l2_event(inst, event); + put_inst(inst); +} + +static void msm_comm_clean_notify_client(struct msm_vidc_core *core) +{ + struct msm_vidc_inst *inst = NULL; + + if (!core) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return; + } + + dprintk(VIDC_WARN, "%s: Core %pK\n", __func__, core); + mutex_lock(&core->lock); + + list_for_each_entry(inst, &core->instances, list) { + mutex_lock(&inst->lock); + inst->state = MSM_VIDC_CORE_INVALID; + mutex_unlock(&inst->lock); + dprintk(VIDC_WARN, + "%s Send sys error for inst %pK\n", __func__, inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + } + mutex_unlock(&core->lock); +} + +static void handle_sys_error(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_core *core = NULL; + struct hfi_device *hdev = NULL; + struct msm_vidc_inst *inst = NULL; + int rc = 0; + + subsystem_crashed("venus"); + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for sys error\n"); + return; + } + + core = get_vidc_core(response->device_id); + if (!core) { + dprintk(VIDC_ERR, + "Got SYS_ERR but unable to identify core\n"); + return; + } + hdev = core->device; + + mutex_lock(&core->lock); + if (core->state == VIDC_CORE_UNINIT) { + dprintk(VIDC_ERR, + "%s: Core %pK already moved to state %d\n", + __func__, core, core->state); + mutex_unlock(&core->lock); + return; + } + + dprintk(VIDC_WARN, "SYS_ERROR received for core %pK\n", core); + msm_vidc_noc_error_info(core); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + list_for_each_entry(inst, &core->instances, list) { + dprintk(VIDC_WARN, + "%s: Send sys error for inst %pK\n", __func__, inst); + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR); + if (!core->trigger_ssr) + msm_comm_print_inst_info(inst); + } + + /* handle the hw error before core released to get full debug info */ + msm_vidc_handle_hw_error(core); + + dprintk(VIDC_DBG, "Calling core_release\n"); + rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, "core_release failed\n"); + mutex_unlock(&core->lock); + return; + } + core->state = VIDC_CORE_UNINIT; + mutex_unlock(&core->lock); + + dprintk(VIDC_WARN, "SYS_ERROR handled.\n"); +} + +void msm_comm_session_clean(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev = NULL; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return; + } + if (!inst->session) { + dprintk(VIDC_DBG, "%s: inst %pK session already cleaned\n", + __func__, inst); + return; + } + + hdev = inst->core->device; + mutex_lock(&inst->lock); + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_clean, + (void *)inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Session clean failed :%pK\n", inst); + } + inst->session = NULL; + mutex_unlock(&inst->lock); +} + +static void handle_session_close(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for session close\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + show_stats(inst); + put_inst(inst); +} + +struct vb2_buffer *msm_comm_get_vb_using_vidc_buffer( + struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf) +{ + u32 port = 0; + struct vb2_buffer *vb = NULL; + struct vb2_queue *q = NULL; + bool found = false; + + if (mbuf->vvb.vb2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + port = CAPTURE_PORT; + } else if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + port = OUTPUT_PORT; + } else { + dprintk(VIDC_ERR, "%s: invalid type %d\n", + __func__, mbuf->vvb.vb2_buf.type); + return NULL; + } + + WARN_ON(!mutex_is_locked(&inst->bufq[port].lock)); + found = false; + q = &inst->bufq[port].vb2_bufq; + if (!q->streaming) { + dprintk(VIDC_ERR, "port %d is not streaming", port); + goto unlock; + } + list_for_each_entry(vb, &q->queued_list, queued_entry) { + if (vb->state != VB2_BUF_STATE_ACTIVE) + continue; + if (msm_comm_compare_vb2_planes(inst, mbuf, vb)) { + found = true; + break; + } + } +unlock: + if (!found) { + print_vidc_buffer(VIDC_ERR, "vb2 not found for", inst, mbuf); + return NULL; + } + + return vb; +} + +int msm_comm_vb2_buffer_done(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + struct vb2_buffer *vb2; + struct vb2_v4l2_buffer *vbuf; + u32 i, port; + int rc = 0; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return -EINVAL; + } + + if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + port = CAPTURE_PORT; + else if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + port = OUTPUT_PORT; + else + return -EINVAL; + + /* + * access vb2 buffer under q->lock and if streaming only to + * ensure the buffer was not free'd by vb2 framework while + * we are accessing it here. + */ + mutex_lock(&inst->bufq[port].lock); + vb2 = msm_comm_get_vb_using_vidc_buffer(inst, mbuf); + if (!vb2) { + rc = -EINVAL; + dprintk(VIDC_ERR, "%s:port %d buffer not found\n", + __func__, port); + goto unlock; + } + + if (inst->bufq[port].vb2_bufq.streaming) { + vbuf = to_vb2_v4l2_buffer(vb2); + vbuf->flags = mbuf->vvb.flags; + vb2->timestamp = mbuf->vvb.vb2_buf.timestamp; + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + vb2->planes[i].bytesused = + mbuf->vvb.vb2_buf.planes[i].bytesused; + vb2->planes[i].data_offset = + mbuf->vvb.vb2_buf.planes[i].data_offset; + } + vb2_buffer_done(vb2, VB2_BUF_STATE_DONE); + } else { + dprintk(VIDC_ERR, "%s: port %d is not streaming\n", + __func__, port); + } +unlock: + mutex_unlock(&inst->bufq[port].lock); + return rc; +} + +bool heic_encode_session_supported(struct msm_vidc_inst *inst) +{ + u32 slice_mode; + u32 idr_period; + u32 n_bframes; + u32 n_pframes; + + slice_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + idr_period = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD); + n_bframes = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES); + n_pframes = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES); + + /* + * HEIC Encode is supported for Constant Quality RC mode only. + * All configurations below except grid_enable are required for any + * HEIC session including FWK tiled HEIC encode. + * grid_enable flag along with dimension check enables HW tiling. + */ + if (inst->session_type == MSM_VIDC_ENCODER && + get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) == + HAL_VIDEO_CODEC_HEVC && + inst->frame_quality >= MIN_FRAME_QUALITY && + inst->frame_quality <= MAX_FRAME_QUALITY && + slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE && + idr_period == 1 && + n_bframes == 0 && + n_pframes == 0) { + if (inst->grid_enable > 0) { + if (inst->prop.width[CAPTURE_PORT] < + HEIC_GRID_DIMENSION || + inst->prop.height[CAPTURE_PORT] < + HEIC_GRID_DIMENSION) + return false; + } + return true; + } else { + return false; + } +} + +static bool is_eos_buffer(struct msm_vidc_inst *inst, u32 device_addr) +{ + struct eos_buf *temp, *next; + bool found = false; + + mutex_lock(&inst->eosbufs.lock); + list_for_each_entry_safe(temp, next, &inst->eosbufs.list, list) { + if (temp->smem.device_addr == device_addr) { + found = true; + temp->is_queued = 0; + list_del(&temp->list); + msm_comm_smem_free(inst, &temp->smem); + kfree(temp); + break; + } + } + mutex_unlock(&inst->eosbufs.lock); + + return found; +} + +static void handle_ebd(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_data_done *response = data; + struct msm_vidc_buffer *mbuf; + struct vb2_buffer *vb; + struct msm_vidc_inst *inst; + struct vidc_hal_ebd *empty_buf_done; + struct vidc_tag_data tag_data; + u32 planes[VIDEO_MAX_PLANES] = {0}; + u32 extra_idx = 0; + + if (!response) { + dprintk(VIDC_ERR, "Invalid response from vidc_hal\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + empty_buf_done = (struct vidc_hal_ebd *)&response->input_done; + /* If this is internal EOS buffer, handle it in driver */ + if (is_eos_buffer(inst, empty_buf_done->packet_buffer)) { + dprintk(VIDC_DBG, "Received EOS buffer 0x%x\n", + empty_buf_done->packet_buffer); + goto exit; + } + + planes[0] = empty_buf_done->packet_buffer; + planes[1] = empty_buf_done->extra_data_buffer; + + mbuf = msm_comm_get_buffer_using_device_planes(inst, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, planes); + if (!mbuf || !kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, + "%s: data_addr %x, extradata_addr %x not found\n", + __func__, planes[0], planes[1]); + goto exit; + } + mbuf->flags &= ~MSM_VIDC_FLAG_QUEUED; + vb = &mbuf->vvb.vb2_buf; + + vb->planes[0].bytesused = response->input_done.filled_len; + if (vb->planes[0].bytesused > vb->planes[0].length) + dprintk(VIDC_INFO, "bytesused overflow length\n"); + + vb->planes[0].data_offset = response->input_done.offset; + if (vb->planes[0].data_offset > vb->planes[0].length) + dprintk(VIDC_INFO, "data_offset overflow length\n"); + + if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) { + dprintk(VIDC_INFO, "Failed : Unsupported input stream\n"); + mbuf->vvb.flags |= V4L2_QCOM_BUF_INPUT_UNSUPPORTED; + } + if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) { + dprintk(VIDC_INFO, "Failed : Corrupted input stream\n"); + mbuf->vvb.flags |= V4L2_BUF_FLAG_DATA_CORRUPT; + } + + extra_idx = EXTRADATA_IDX(inst->bufq[OUTPUT_PORT].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) + vb->planes[extra_idx].bytesused = vb->planes[extra_idx].length; + + tag_data.index = vb->index; + tag_data.input_tag = empty_buf_done->input_tag; + tag_data.output_tag = empty_buf_done->output_tag; + tag_data.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + msm_comm_store_tags(inst, &tag_data); + + update_recon_stats(inst, &empty_buf_done->recon_stats); + msm_vidc_clear_freq_entry(inst, mbuf->smem[0].device_addr); + /* + * dma cache operations need to be performed before dma_unmap + * which is done inside msm_comm_put_vidc_buffer() + */ + msm_comm_dqbuf_cache_operations(inst, mbuf); + /* + * put_buffer should be done before vb2_buffer_done else + * client might queue the same buffer before it is unmapped + * in put_buffer. + */ + msm_comm_put_vidc_buffer(inst, mbuf); + msm_comm_vb2_buffer_done(inst, mbuf); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD); + kref_put_mbuf(mbuf); +exit: + put_inst(inst); +} + +static int handle_multi_stream_buffers(struct msm_vidc_inst *inst, + u32 dev_addr) +{ + struct internal_buf *binfo; + struct msm_smem *smem; + bool found = false; + + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + smem = &binfo->smem; + if (smem && dev_addr == smem->device_addr) { + if (binfo->buffer_ownership == DRIVER) { + dprintk(VIDC_ERR, + "FW returned same buffer: %x\n", + dev_addr); + break; + } + binfo->buffer_ownership = DRIVER; + found = true; + break; + } + } + mutex_unlock(&inst->outputbufs.lock); + + if (!found) { + dprintk(VIDC_ERR, + "Failed to find output buffer in queued list: %x\n", + dev_addr); + } + + return 0; +} + +enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst) +{ + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) + return HAL_BUFFER_OUTPUT2; + else + return HAL_BUFFER_OUTPUT; +} + +static void handle_fbd(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_data_done *response = data; + struct msm_vidc_buffer *mbuf; + struct msm_vidc_inst *inst; + struct vb2_buffer *vb; + struct vidc_hal_fbd *fill_buf_done; + struct vidc_tag_data tag_data; + enum hal_buffer buffer_type; + u64 time_usec = 0; + u32 planes[VIDEO_MAX_PLANES] = {0}; + u32 extra_idx; + + if (!response) { + dprintk(VIDC_ERR, "Invalid response from vidc_hal\n"); + return; + } + + inst = get_inst(get_vidc_core(response->device_id), + response->session_id); + if (!inst) { + dprintk(VIDC_WARN, "Got a response for an inactive session\n"); + return; + } + + fill_buf_done = (struct vidc_hal_fbd *)&response->output_done; + planes[0] = fill_buf_done->packet_buffer1; + planes[1] = fill_buf_done->extra_data_buffer; + + buffer_type = msm_comm_get_hal_output_buffer(inst); + if (fill_buf_done->buffer_type == buffer_type) { + mbuf = msm_comm_get_buffer_using_device_planes(inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, planes); + if (!mbuf || !kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, + "%s: data_addr %x, extradata_addr %x not found\n", + __func__, planes[0], planes[1]); + goto exit; + } + } else { + if (handle_multi_stream_buffers(inst, + fill_buf_done->packet_buffer1)) + dprintk(VIDC_ERR, + "Failed : Output buffer not found %pa\n", + &fill_buf_done->packet_buffer1); + goto exit; + } + mbuf->flags &= ~MSM_VIDC_FLAG_QUEUED; + vb = &mbuf->vvb.vb2_buf; + + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME) + fill_buf_done->filled_len1 = 0; + vb->planes[0].bytesused = fill_buf_done->filled_len1; + if (vb->planes[0].bytesused > vb->planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow bytesused = %d; length = %d\n", + vb->planes[0].bytesused, + vb->planes[0].length); + vb->planes[0].data_offset = fill_buf_done->offset1; + if (vb->planes[0].data_offset > vb->planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow data_offset = %d; length = %d\n", + vb->planes[0].data_offset, + vb->planes[0].length); + + time_usec = fill_buf_done->timestamp_hi; + time_usec = (time_usec << 32) | fill_buf_done->timestamp_lo; + + vb->timestamp = (time_usec * NSEC_PER_USEC); + + if (inst->session_type == MSM_VIDC_DECODER) { + msm_comm_store_mark_data(&inst->fbd_data, vb->index, + fill_buf_done->mark_data, fill_buf_done->mark_target); + } + if (inst->session_type == MSM_VIDC_ENCODER) { + if (inst->max_filled_length < fill_buf_done->filled_len1) + inst->max_filled_length = fill_buf_done->filled_len1; + } + + tag_data.index = vb->index; + tag_data.input_tag = fill_buf_done->input_tag; + tag_data.output_tag = fill_buf_done->output_tag; + tag_data.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + msm_comm_store_tags(inst, &tag_data); + + extra_idx = EXTRADATA_IDX(inst->bufq[CAPTURE_PORT].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) + vb->planes[extra_idx].bytesused = vb->planes[extra_idx].length; + + mbuf->vvb.flags = 0; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY) + mbuf->vvb.flags |= V4L2_QCOM_BUF_FLAG_READONLY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS) + mbuf->vvb.flags |= V4L2_QCOM_BUF_FLAG_EOS; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG) + mbuf->vvb.flags |= V4L2_QCOM_BUF_FLAG_CODECCONFIG; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME) + mbuf->vvb.flags |= V4L2_BUF_FLAG_KEYFRAME; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT) + mbuf->vvb.flags |= V4L2_BUF_FLAG_DATA_CORRUPT; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_ENDOFSUBFRAME) + mbuf->vvb.flags |= V4L2_QCOM_BUF_END_OF_SUBFRAME; + switch (fill_buf_done->picture_type) { + case HAL_PICTURE_P: + mbuf->vvb.flags |= V4L2_BUF_FLAG_PFRAME; + break; + case HAL_PICTURE_B: + mbuf->vvb.flags |= V4L2_BUF_FLAG_BFRAME; + break; + case HAL_FRAME_NOTCODED: + case HAL_UNUSED_PICT: + /* Do we need to care about these? */ + case HAL_FRAME_YUV: + break; + default: + break; + } + + /* + * dma cache operations need to be performed before dma_unmap + * which is done inside msm_comm_put_vidc_buffer() + */ + msm_comm_dqbuf_cache_operations(inst, mbuf); + /* + * put_buffer should be done before vb2_buffer_done else + * client might queue the same buffer before it is unmapped + * in put_buffer. + */ + msm_comm_put_vidc_buffer(inst, mbuf); + msm_comm_vb2_buffer_done(inst, mbuf); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD); + kref_put_mbuf(mbuf); + +exit: + put_inst(inst); +} + +void handle_cmd_response(enum hal_command_response cmd, void *data) +{ + dprintk(VIDC_DBG, "Command response = %d\n", cmd); + switch (cmd) { + case HAL_SYS_INIT_DONE: + handle_sys_init_done(cmd, data); + break; + case HAL_SYS_RELEASE_RESOURCE_DONE: + handle_sys_release_res_done(cmd, data); + break; + case HAL_SESSION_INIT_DONE: + handle_session_init_done(cmd, data); + break; + case HAL_SESSION_PROPERTY_INFO: + handle_session_prop_info(cmd, data); + break; + case HAL_SESSION_LOAD_RESOURCE_DONE: + handle_load_resource_done(cmd, data); + break; + case HAL_SESSION_START_DONE: + handle_start_done(cmd, data); + break; + case HAL_SESSION_ETB_DONE: + handle_ebd(cmd, data); + break; + case HAL_SESSION_FTB_DONE: + handle_fbd(cmd, data); + break; + case HAL_SESSION_STOP_DONE: + handle_stop_done(cmd, data); + break; + case HAL_SESSION_RELEASE_RESOURCE_DONE: + handle_release_res_done(cmd, data); + break; + case HAL_SESSION_END_DONE: + case HAL_SESSION_ABORT_DONE: + handle_session_close(cmd, data); + break; + case HAL_SESSION_EVENT_CHANGE: + handle_event_change(cmd, data); + break; + case HAL_SESSION_FLUSH_DONE: + handle_session_flush(cmd, data); + break; + case HAL_SYS_WATCHDOG_TIMEOUT: + case HAL_SYS_ERROR: + handle_sys_error(cmd, data); + break; + case HAL_SESSION_ERROR: + handle_session_error(cmd, data); + break; + case HAL_SESSION_RELEASE_BUFFER_DONE: + handle_session_release_buf_done(cmd, data); + break; + case HAL_SESSION_REGISTER_BUFFER_DONE: + handle_session_register_buffer_done(cmd, data); + break; + case HAL_SESSION_UNREGISTER_BUFFER_DONE: + handle_session_unregister_buffer_done(cmd, data); + break; + default: + dprintk(VIDC_DBG, "response unhandled: %d\n", cmd); + break; + } +} + +static inline enum msm_vidc_thermal_level msm_comm_vidc_thermal_level(int level) +{ + switch (level) { + case 0: + return VIDC_THERMAL_NORMAL; + case 1: + return VIDC_THERMAL_LOW; + case 2: + return VIDC_THERMAL_HIGH; + default: + return VIDC_THERMAL_CRITICAL; + } +} + +static bool is_core_turbo(struct msm_vidc_core *core, unsigned long freq) +{ + int i = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + u32 max_freq = 0; + + allowed_clks_tbl = core->resources.allowed_clks_tbl; + for (i = 0; i < core->resources.allowed_clks_tbl_size; i++) { + if (max_freq < allowed_clks_tbl[i].clock_rate) + max_freq = allowed_clks_tbl[i].clock_rate; + } + return freq >= max_freq; +} + +static bool is_thermal_permissible(struct msm_vidc_core *core) +{ + enum msm_vidc_thermal_level tl; + unsigned long freq = 0; + bool is_turbo = false; + + if (!core->resources.thermal_mitigable) + return true; + + if (msm_vidc_thermal_mitigation_disabled) { + dprintk(VIDC_DBG, + "Thermal mitigation not enabled. debugfs %d\n", + msm_vidc_thermal_mitigation_disabled); + return true; + } + + tl = msm_comm_vidc_thermal_level(vidc_driver->thermal_level); + freq = core->curr_freq; + + is_turbo = is_core_turbo(core, freq); + dprintk(VIDC_DBG, + "Core freq %ld Thermal level %d Turbo mode %d\n", + freq, tl, is_turbo); + + if (is_turbo && tl >= VIDC_THERMAL_LOW) { + dprintk(VIDC_ERR, + "Video session not allowed. Turbo mode %d Thermal level %d\n", + is_turbo, tl); + return false; + } + return true; +} + +bool is_batching_allowed(struct msm_vidc_inst *inst) +{ + bool allowed = false; + + if (!inst || !inst->core) + return false; + + /* + * Enable decode batching based on below conditions + * - platform supports batching + * - decode session and H264/HEVC/VP9 format + * - session resolution <= 1080p + * - low latency not enabled + * - not a thumbnail session + * - UBWC color format + */ + if (inst->decode_batching && is_decode_session(inst) && + (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264 || + inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_HEVC || + inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9) && + (msm_vidc_get_mbs_per_frame(inst) <= + NUM_MBS_PER_FRAME(MAX_DEC_BATCH_HEIGHT, MAX_DEC_BATCH_WIDTH)) && + !inst->clk_data.low_latency_mode && + !is_thumbnail_session(inst) && + (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_NV12_UBWC || + inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_NV12_TP10_UBWC)) + allowed = true; + + return allowed; +} + +static int msm_comm_session_abort(struct msm_vidc_inst *inst) +{ + int rc = 0, abort_completion = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE); + + dprintk(VIDC_WARN, "%s: inst %pK session %x\n", __func__, + inst, hash32_ptr(inst->session)); + rc = call_hfi_op(hdev, session_abort, (void *)inst->session); + if (rc) { + dprintk(VIDC_ERR, + "%s session_abort failed rc: %d\n", __func__, rc); + goto exit; + } + rc = wait_for_completion_timeout( + &inst->completions[abort_completion], + msecs_to_jiffies( + inst->core->resources.msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, "%s: inst %pK session %x abort timed out\n", + __func__, inst, hash32_ptr(inst->session)); + msm_comm_generate_sys_error(inst); + rc = -EBUSY; + } else { + rc = 0; + } +exit: + return rc; +} + +static void handle_thermal_event(struct msm_vidc_core *core) +{ + int rc = 0; + struct msm_vidc_inst *inst; + + if (!core || !core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return; + } + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (!inst->session) + continue; + + mutex_unlock(&core->lock); + if (inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_CLOSE_DONE) { + dprintk(VIDC_WARN, "%s: abort inst %pK\n", + __func__, inst); + rc = msm_comm_session_abort(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s session_abort failed rc: %d\n", + __func__, rc); + goto err_sess_abort; + } + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + dprintk(VIDC_WARN, + "%s Send sys error for inst %pK\n", + __func__, inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + } else { + msm_comm_generate_session_error(inst); + } + mutex_lock(&core->lock); + } + mutex_unlock(&core->lock); + return; + +err_sess_abort: + msm_comm_clean_notify_client(core); +} + +void msm_comm_handle_thermal_event(void) +{ + struct msm_vidc_core *core; + + list_for_each_entry(core, &vidc_driver->cores, list) { + if (!is_thermal_permissible(core)) { + dprintk(VIDC_WARN, + "Thermal level critical, stop all active sessions!\n"); + handle_thermal_event(core); + } + } +} + +int msm_comm_check_core_init(struct msm_vidc_core *core) +{ + int rc = 0; + + mutex_lock(&core->lock); + if (core->state >= VIDC_CORE_INIT_DONE) { + dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n", + core->id, core->state); + goto exit; + } + dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n"); + rc = wait_for_completion_timeout( + &core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)], + msecs_to_jiffies(core->resources.msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, "%s: Wait interrupted or timed out: %d\n", + __func__, SYS_MSG_INDEX(HAL_SYS_INIT_DONE)); + rc = -EIO; + goto exit; + } else { + core->state = VIDC_CORE_INIT_DONE; + rc = 0; + } + dprintk(VIDC_DBG, "SYS_INIT_DONE!!!\n"); +exit: + mutex_unlock(&core->lock); + return rc; +} + +static int msm_comm_init_core_done(struct msm_vidc_inst *inst) +{ + int rc = 0; + + rc = msm_comm_check_core_init(inst->core); + if (rc) { + dprintk(VIDC_ERR, "%s - failed to initialize core\n", __func__); + msm_comm_generate_sys_error(inst); + return rc; + } + change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE); + return rc; +} + +static int msm_comm_init_core(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + struct msm_vidc_core *core; + + if (!inst || !inst->core || !inst->core->device) + return -EINVAL; + + core = inst->core; + hdev = core->device; + mutex_lock(&core->lock); + if (core->state >= VIDC_CORE_INIT) { + dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n", + core->id, core->state); + goto core_already_inited; + } + if (!core->capabilities) { + core->capabilities = kcalloc(VIDC_MAX_SESSIONS, + sizeof(struct msm_vidc_capability), GFP_KERNEL); + if (!core->capabilities) { + dprintk(VIDC_ERR, + "%s: failed to allocate capabilities\n", + __func__); + rc = -ENOMEM; + goto fail_cap_alloc; + } + } else { + dprintk(VIDC_WARN, + "%s: capabilities memory is expected to be freed\n", + __func__); + } + dprintk(VIDC_DBG, "%s: core %pK\n", __func__, core); + rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, "Failed to init core, id = %d\n", + core->id); + goto fail_core_init; + } + core->state = VIDC_CORE_INIT; + core->smmu_fault_handled = false; + core->trigger_ssr = false; + +core_already_inited: + change_inst_state(inst, MSM_VIDC_CORE_INIT); + mutex_unlock(&core->lock); + + rc = msm_comm_scale_clocks_and_bus(inst); + return rc; + +fail_core_init: + kfree(core->capabilities); +fail_cap_alloc: + core->capabilities = NULL; + core->state = VIDC_CORE_UNINIT; + mutex_unlock(&core->lock); + return rc; +} + +static int msm_vidc_deinit_core(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + core = inst->core; + hdev = core->device; + + mutex_lock(&core->lock); + if (core->state == VIDC_CORE_UNINIT) { + dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n", + core->id, core->state); + goto core_already_uninited; + } + mutex_unlock(&core->lock); + + msm_comm_scale_clocks_and_bus(inst); + + mutex_lock(&core->lock); + + if (!core->resources.never_unload_fw) { + cancel_delayed_work(&core->fw_unload_work); + + /* + * Delay unloading of firmware. This is useful + * in avoiding firmware download delays in cases where we + * will have a burst of back to back video playback sessions + * e.g. thumbnail generation. + */ + schedule_delayed_work(&core->fw_unload_work, + msecs_to_jiffies(core->state == VIDC_CORE_INIT_DONE ? + core->resources.msm_vidc_firmware_unload_delay : 0)); + + dprintk(VIDC_DBG, "firmware unload delayed by %u ms\n", + core->state == VIDC_CORE_INIT_DONE ? + core->resources.msm_vidc_firmware_unload_delay : 0); + } + +core_already_uninited: + change_inst_state(inst, MSM_VIDC_CORE_UNINIT); + mutex_unlock(&core->lock); + return 0; +} + +int msm_comm_force_cleanup(struct msm_vidc_inst *inst) +{ + msm_comm_kill_session(inst); + return msm_vidc_deinit_core(inst); +} + +static int msm_comm_session_init_done(int flipped_state, + struct msm_vidc_inst *inst) +{ + int rc; + + dprintk(VIDC_DBG, "inst %pK: waiting for session init done\n", inst); + rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE, + HAL_SESSION_INIT_DONE); + if (rc) { + dprintk(VIDC_ERR, "Session init failed for inst %pK\n", inst); + msm_comm_generate_sys_error(inst); + return rc; + } + + return rc; +} + +static int msm_comm_init_buffer_count(struct msm_vidc_inst *inst) +{ + int extra_buff_count = 0; + struct hal_buffer_requirements *bufreq; + int rc = 0; + int port; + + if (!is_decode_session(inst) && !is_encode_session(inst)) + return 0; + + if (is_decode_session(inst)) + port = OUTPUT_PORT; + else + port = CAPTURE_PORT; + + /* Update input buff counts */ + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_INPUT); + if (!bufreq) + return -EINVAL; + + extra_buff_count = msm_vidc_get_extra_buff_count(inst, + HAL_BUFFER_INPUT); + bufreq->buffer_count_min = inst->fmts[port].input_min_count; + /* batching needs minimum batch size count of input buffers */ + if (is_batching_allowed(inst) && + bufreq->buffer_count_min < inst->batch.size) + bufreq->buffer_count_min = inst->batch.size; + bufreq->buffer_count_min_host = bufreq->buffer_count_actual = + bufreq->buffer_count_min + extra_buff_count; + + dprintk(VIDC_DBG, "%s: %x : input min %d min_host %d actual %d\n", + __func__, hash32_ptr(inst->session), + bufreq->buffer_count_min, bufreq->buffer_count_min_host, + bufreq->buffer_count_actual); + + rc = msm_comm_set_buffer_count(inst, + bufreq->buffer_count_min, + bufreq->buffer_count_actual, HAL_BUFFER_INPUT); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set in buffer count to FW\n", + __func__); + return -EINVAL; + } + + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_INPUT); + if (!bufreq) + return -EINVAL; + + bufreq->buffer_count_min = inst->fmts[port].input_min_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_actual = + bufreq->buffer_count_min + extra_buff_count; + + /* Update output buff count */ + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (!bufreq) + return -EINVAL; + + extra_buff_count = msm_vidc_get_extra_buff_count(inst, + HAL_BUFFER_OUTPUT); + bufreq->buffer_count_min = inst->fmts[port].output_min_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_actual = + bufreq->buffer_count_min + extra_buff_count; + + dprintk(VIDC_DBG, "%s: %x : output min %d min_host %d actual %d\n", + __func__, hash32_ptr(inst->session), + bufreq->buffer_count_min, bufreq->buffer_count_min_host, + bufreq->buffer_count_actual); + + rc = msm_comm_set_buffer_count(inst, + bufreq->buffer_count_min, + bufreq->buffer_count_actual, HAL_BUFFER_OUTPUT); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set out buffer count to FW\n", + __func__); + return -EINVAL; + } + + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); + if (!bufreq) + return -EINVAL; + + bufreq->buffer_count_min = inst->fmts[port].output_min_count; + bufreq->buffer_count_min_host = bufreq->buffer_count_actual = + bufreq->buffer_count_min + extra_buff_count; + + return 0; +} + +static int msm_comm_session_init(int flipped_state, + struct msm_vidc_inst *inst) +{ + int rc = 0; + int fourcc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) { + dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + if (inst->session_type == MSM_VIDC_DECODER) { + fourcc = inst->fmts[OUTPUT_PORT].fourcc; + } else if (inst->session_type == MSM_VIDC_ENCODER) { + fourcc = inst->fmts[CAPTURE_PORT].fourcc; + } else if (inst->session_type == MSM_VIDC_CVP) { + fourcc = V4L2_PIX_FMT_CVP; + } else { + dprintk(VIDC_ERR, "Invalid session\n"); + return -EINVAL; + } + + rc = msm_comm_init_clocks_and_bus_data(inst); + if (rc) { + dprintk(VIDC_ERR, "Failed to initialize clocks and bus data\n"); + goto exit; + } + + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_init, hdev->hfi_device_data, + inst, get_hal_domain(inst->session_type), + get_hal_codec(fourcc), + &inst->session); + + if (rc || !inst->session) { + dprintk(VIDC_ERR, + "Failed to call session init for: %pK, %pK, %d, %d\n", + inst->core->device, inst, + inst->session_type, fourcc); + rc = -EINVAL; + goto exit; + } + + rc = msm_comm_init_buffer_count(inst); + if (rc) { + dprintk(VIDC_ERR, "Failed to initialize buff counts\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_OPEN); + +exit: + return rc; +} + +static void msm_vidc_print_running_insts(struct msm_vidc_core *core) +{ + struct msm_vidc_inst *temp; + int op_rate = 0; + + dprintk(VIDC_ERR, "Running instances:\n"); + dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s|%4s\n", + "type", "w", "h", "fps", "opr", "prop"); + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp->state >= MSM_VIDC_OPEN_DONE && + temp->state < MSM_VIDC_STOP_DONE) { + char properties[4] = ""; + + if (is_thumbnail_session(temp)) + strlcat(properties, "N", sizeof(properties)); + + if (is_turbo_session(temp)) + strlcat(properties, "T", sizeof(properties)); + + if (is_realtime_session(temp)) + strlcat(properties, "R", sizeof(properties)); + + if (temp->clk_data.operating_rate) + op_rate = temp->clk_data.operating_rate >> 16; + else + op_rate = temp->prop.fps; + + dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4d|%4s\n", + temp->session_type, + max(temp->prop.width[CAPTURE_PORT], + temp->prop.width[OUTPUT_PORT]), + max(temp->prop.height[CAPTURE_PORT], + temp->prop.height[OUTPUT_PORT]), + temp->prop.fps, op_rate, properties); + } + } + mutex_unlock(&core->lock); +} + +static int msm_vidc_load_resources(int flipped_state, + struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + int num_mbs_per_sec = 0, max_load_adj = 0; + struct msm_vidc_core *core; + enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD | + LOAD_CALC_IGNORE_THUMBNAIL_LOAD | + LOAD_CALC_IGNORE_NON_REALTIME_LOAD; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "%s: inst %pK is in invalid state\n", __func__, inst); + return -EINVAL; + } + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) { + dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + core = inst->core; + + num_mbs_per_sec = + msm_comm_get_load(core, MSM_VIDC_DECODER, quirks) + + msm_comm_get_load(core, MSM_VIDC_ENCODER, quirks); + + max_load_adj = core->resources.max_load + + inst->capability.mbs_per_frame.max; + + if (num_mbs_per_sec > max_load_adj) { + dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n", + num_mbs_per_sec, max_load_adj); + msm_vidc_print_running_insts(core); + msm_comm_kill_session(inst); + return -EBUSY; + } + + hdev = core->device; + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_load_res, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Failed to send load resources\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_LOAD_RESOURCES); +exit: + return rc; +} + +static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "%s: inst %pK is in invalid\n", __func__, inst); + return -EINVAL; + } + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_start, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Failed to send start\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_START); +exit: + return rc; +} + +static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "%s: inst %pK is in invalid state\n", __func__, inst); + return -EINVAL; + } + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_stop, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, "%s: inst %pK session_stop failed\n", + __func__, inst); + goto exit; + } + change_inst_state(inst, MSM_VIDC_STOP); +exit: + return rc; +} + +static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "%s: inst %pK is in invalid state\n", __func__, inst); + return -EINVAL; + } + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_release_res, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Failed to send release resources\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES); +exit: + return rc; +} + +static int msm_comm_session_close(int flipped_state, + struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "%s: inst %pK\n", __func__, inst); + rc = call_hfi_op(hdev, session_end, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Failed to send close\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_CLOSE); +exit: + return rc; +} + +int msm_comm_suspend(int core_id) +{ + struct hfi_device *hdev; + struct msm_vidc_core *core; + int rc = 0; + + core = get_vidc_core(core_id); + if (!core) { + dprintk(VIDC_ERR, + "%s: Failed to find core for core_id = %d\n", + __func__, core_id); + return -EINVAL; + } + + hdev = (struct hfi_device *)core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__); + return -EINVAL; + } + + rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data); + if (rc) + dprintk(VIDC_WARN, "Failed to suspend\n"); + + return rc; +} + +static int get_flipped_state(int present_state, + int desired_state) +{ + int flipped_state = present_state; + + if (flipped_state < MSM_VIDC_STOP + && desired_state > MSM_VIDC_STOP) { + flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state); + flipped_state &= 0xFFFE; + flipped_state = flipped_state - 1; + } else if (flipped_state > MSM_VIDC_STOP + && desired_state < MSM_VIDC_STOP) { + flipped_state = MSM_VIDC_STOP - + (flipped_state - MSM_VIDC_STOP + 1); + flipped_state &= 0xFFFE; + flipped_state = flipped_state - 1; + } + return flipped_state; +} + +int msm_comm_reset_bufreqs(struct msm_vidc_inst *inst, enum hal_buffer buf_type) +{ + struct hal_buffer_requirements *bufreqs; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + bufreqs = get_buff_req_buffer(inst, buf_type); + if (!bufreqs) { + dprintk(VIDC_ERR, "%s: invalid buf type %d\n", + __func__, buf_type); + return -EINVAL; + } + bufreqs->buffer_size = bufreqs->buffer_region_size = + bufreqs->buffer_count_min = bufreqs->buffer_count_min_host = + bufreqs->buffer_count_actual = bufreqs->contiguous = + bufreqs->buffer_alignment = 0; + + return 0; +} + +int msm_comm_copy_bufreqs(struct msm_vidc_inst *inst, enum hal_buffer src_type, + enum hal_buffer dst_type) +{ + struct hal_buffer_requirements *src_bufreqs; + struct hal_buffer_requirements *dst_bufreqs; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + src_bufreqs = get_buff_req_buffer(inst, src_type); + dst_bufreqs = get_buff_req_buffer(inst, dst_type); + if (!src_bufreqs || !dst_bufreqs) { + dprintk(VIDC_ERR, "%s: invalid buf type: src %d dst %d\n", + __func__, src_type, dst_type); + return -EINVAL; + } + dst_bufreqs->buffer_size = src_bufreqs->buffer_size; + dst_bufreqs->buffer_region_size = src_bufreqs->buffer_region_size; + dst_bufreqs->buffer_count_min = src_bufreqs->buffer_count_min; + dst_bufreqs->buffer_count_min_host = src_bufreqs->buffer_count_min_host; + dst_bufreqs->buffer_count_actual = src_bufreqs->buffer_count_actual; + dst_bufreqs->contiguous = src_bufreqs->contiguous; + dst_bufreqs->buffer_alignment = src_bufreqs->buffer_alignment; + + return 0; +} + +struct hal_buffer_requirements *get_buff_req_buffer( + struct msm_vidc_inst *inst, enum hal_buffer buffer_type) +{ + int i; + + for (i = 0; i < HAL_BUFFER_MAX; i++) { + if (inst->buff_req.buffer[i].buffer_type == buffer_type) + return &inst->buff_req.buffer[i]; + } + dprintk(VIDC_ERR, "Failed to get buff req for : %x", buffer_type); + return NULL; +} + +static int set_output_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + int rc = 0; + struct internal_buf *binfo = NULL; + u32 smem_flags = SMEM_UNCACHED, buffer_size; + struct hal_buffer_requirements *output_buf, *extradata_buf; + int i; + struct hfi_device *hdev; + struct hal_buffer_size_minimum b; + + hdev = inst->core->device; + + output_buf = get_buff_req_buffer(inst, buffer_type); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + + /* For DPB buffers, Always use FW count */ + output_buf->buffer_count_actual = output_buf->buffer_count_min_host = + output_buf->buffer_count_min; + + dprintk(VIDC_DBG, + "output: num = %d, size = %d\n", + output_buf->buffer_count_actual, + output_buf->buffer_size); + + buffer_size = output_buf->buffer_size; + b.buffer_type = buffer_type; + b.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); + + extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); + if (extradata_buf) { + dprintk(VIDC_DBG, + "extradata: num = %d, size = %d\n", + extradata_buf->buffer_count_actual, + extradata_buf->buffer_size); + buffer_size += extradata_buf->buffer_size; + } else { + dprintk(VIDC_DBG, + "This extradata buffer not required, buffer_type: %x\n", + buffer_type); + } + + if (inst->flags & VIDC_SECURE) + smem_flags |= SMEM_SECURE; + + if (output_buf->buffer_size) { + for (i = 0; i < output_buf->buffer_count_actual; + i++) { + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto fail_kzalloc; + } + rc = msm_comm_smem_alloc(inst, + buffer_size, 1, smem_flags, + buffer_type, 0, &binfo->smem); + if (rc) { + dprintk(VIDC_ERR, + "Failed to allocate output memory\n"); + goto err_no_mem; + } + binfo->buffer_type = buffer_type; + binfo->buffer_ownership = DRIVER; + dprintk(VIDC_DBG, "Output buffer address: %#x\n", + binfo->smem.device_addr); + + if (inst->buffer_mode_set[CAPTURE_PORT] == + HAL_BUFFER_MODE_STATIC) { + struct vidc_buffer_addr_info buffer_info = {0}; + + buffer_info.buffer_size = + output_buf->buffer_size; + buffer_info.buffer_type = buffer_type; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = + binfo->smem.device_addr; + buffer_info.extradata_addr = + binfo->smem.device_addr + + output_buf->buffer_size; + if (extradata_buf) + buffer_info.extradata_size = + extradata_buf->buffer_size; + rc = call_hfi_op(hdev, session_set_buffers, + (void *) inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_ERR, + "%s : session_set_buffers failed\n", + __func__); + goto fail_set_buffers; + } + } + mutex_lock(&inst->outputbufs.lock); + list_add_tail(&binfo->list, &inst->outputbufs.list); + mutex_unlock(&inst->outputbufs.lock); + } + } + return rc; +fail_set_buffers: + msm_comm_smem_free(inst, &binfo->smem); +err_no_mem: + kfree(binfo); +fail_kzalloc: + return rc; +} + +static inline char *get_buffer_name(enum hal_buffer buffer_type) +{ + switch (buffer_type) { + case HAL_BUFFER_INPUT: return "input"; + case HAL_BUFFER_OUTPUT: return "output"; + case HAL_BUFFER_OUTPUT2: return "output_2"; + case HAL_BUFFER_EXTRADATA_INPUT: return "input_extra"; + case HAL_BUFFER_EXTRADATA_OUTPUT: return "output_extra"; + case HAL_BUFFER_EXTRADATA_OUTPUT2: return "output2_extra"; + case HAL_BUFFER_INTERNAL_SCRATCH: return "scratch"; + case HAL_BUFFER_INTERNAL_SCRATCH_1: return "scratch_1"; + case HAL_BUFFER_INTERNAL_SCRATCH_2: return "scratch_2"; + case HAL_BUFFER_INTERNAL_PERSIST: return "persist"; + case HAL_BUFFER_INTERNAL_PERSIST_1: return "persist_1"; + case HAL_BUFFER_INTERNAL_CMD_QUEUE: return "queue"; + default: return "????"; + } +} + +static int set_internal_buf_on_fw(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, + struct msm_smem *handle, bool reuse) +{ + struct vidc_buffer_addr_info buffer_info; + struct hfi_device *hdev; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device || !handle) { + dprintk(VIDC_ERR, "%s - invalid params\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + buffer_info.buffer_size = handle->size; + buffer_info.buffer_type = buffer_type; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = handle->device_addr; + dprintk(VIDC_DBG, "%s %s buffer : %x\n", + reuse ? "Reusing" : "Allocated", + get_buffer_name(buffer_type), + buffer_info.align_device_addr); + + rc = call_hfi_op(hdev, session_set_buffers, + (void *) inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_ERR, + "vidc_hal_session_set_buffers failed\n"); + return rc; + } + return 0; +} + +static bool reuse_internal_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, struct msm_vidc_list *buf_list) +{ + struct internal_buf *buf; + int rc = 0; + bool reused = false; + + if (!inst || !buf_list) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return false; + } + + mutex_lock(&buf_list->lock); + list_for_each_entry(buf, &buf_list->list, list) { + if (buf->buffer_type != buffer_type) + continue; + + /* + * Persist buffer size won't change with resolution. If they + * are in queue means that they are already allocated and + * given to HW. HW can use them without reallocation. These + * buffers are not released as part of port reconfig. So + * driver no need to set them again. + */ + + if (buffer_type != HAL_BUFFER_INTERNAL_PERSIST + && buffer_type != HAL_BUFFER_INTERNAL_PERSIST_1) { + + rc = set_internal_buf_on_fw(inst, buffer_type, + &buf->smem, true); + if (rc) { + dprintk(VIDC_ERR, + "%s: session_set_buffers failed\n", + __func__); + reused = false; + break; + } + } + reused = true; + dprintk(VIDC_DBG, + "Re-using internal buffer type : %d\n", buffer_type); + } + mutex_unlock(&buf_list->lock); + return reused; +} + +static int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst, + struct hal_buffer_requirements *internal_bufreq, + struct msm_vidc_list *buf_list) +{ + struct internal_buf *binfo; + u32 smem_flags = SMEM_UNCACHED; + int rc = 0; + int i = 0; + + if (!inst || !internal_bufreq || !buf_list) + return -EINVAL; + + if (!internal_bufreq->buffer_size) + return 0; + + if (inst->flags & VIDC_SECURE) + smem_flags |= SMEM_SECURE; + + for (i = 0; i < internal_bufreq->buffer_count_actual; i++) { + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto fail_kzalloc; + } + rc = msm_comm_smem_alloc(inst, internal_bufreq->buffer_size, + 1, smem_flags, internal_bufreq->buffer_type, + 0, &binfo->smem); + if (rc) { + dprintk(VIDC_ERR, + "Failed to allocate scratch memory\n"); + goto err_no_mem; + } + + binfo->buffer_type = internal_bufreq->buffer_type; + + rc = set_internal_buf_on_fw(inst, internal_bufreq->buffer_type, + &binfo->smem, false); + if (rc) + goto fail_set_buffers; + + mutex_lock(&buf_list->lock); + list_add_tail(&binfo->list, &buf_list->list); + mutex_unlock(&buf_list->lock); + } + return rc; + +fail_set_buffers: + msm_comm_smem_free(inst, &binfo->smem); +err_no_mem: + kfree(binfo); +fail_kzalloc: + return rc; + +} + +static int set_internal_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, struct msm_vidc_list *buf_list) +{ + struct hal_buffer_requirements *internal_buf; + + internal_buf = get_buff_req_buffer(inst, buffer_type); + if (!internal_buf) { + dprintk(VIDC_DBG, + "This internal buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + + dprintk(VIDC_DBG, "Buffer type %s: num = %d, size = %d\n", + get_buffer_name(buffer_type), + internal_buf->buffer_count_actual, internal_buf->buffer_size); + + /* + * Try reusing existing internal buffers first. + * If it's not possible to reuse, allocate new buffers. + */ + if (reuse_internal_buffers(inst, buffer_type, buf_list)) + return 0; + + return allocate_and_set_internal_bufs(inst, internal_buf, + buf_list); +} + +int msm_comm_try_state(struct msm_vidc_inst *inst, int state) +{ + int rc = 0; + int flipped_state; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params %pK", __func__, inst); + return -EINVAL; + } + dprintk(VIDC_DBG, + "Trying to move inst: %pK (%#x) from: %#x to %#x\n", + inst, hash32_ptr(inst->session), inst->state, state); + + mutex_lock(&inst->sync_lock); + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: inst %pK is in invalid\n", + __func__, inst); + rc = -EINVAL; + goto exit; + } + + flipped_state = get_flipped_state(inst->state, state); + dprintk(VIDC_DBG, + "inst: %pK (%#x) flipped_state = %#x\n", + inst, hash32_ptr(inst->session), flipped_state); + switch (flipped_state) { + case MSM_VIDC_CORE_UNINIT_DONE: + case MSM_VIDC_CORE_INIT: + rc = msm_comm_init_core(inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_CORE_INIT_DONE: + rc = msm_comm_init_core_done(inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_OPEN: + rc = msm_comm_session_init(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_OPEN_DONE: + rc = msm_comm_session_init_done(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_LOAD_RESOURCES: + rc = msm_vidc_load_resources(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_LOAD_RESOURCES_DONE: + case MSM_VIDC_START: + rc = msm_vidc_start(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_START_DONE: + rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE, + HAL_SESSION_START_DONE); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_STOP: + rc = msm_vidc_stop(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_STOP_DONE: + rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE, + HAL_SESSION_STOP_DONE); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + dprintk(VIDC_DBG, "Moving to Stop Done state\n"); + case MSM_VIDC_RELEASE_RESOURCES: + rc = msm_vidc_release_res(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_RELEASE_RESOURCES_DONE: + rc = wait_for_state(inst, flipped_state, + MSM_VIDC_RELEASE_RESOURCES_DONE, + HAL_SESSION_RELEASE_RESOURCE_DONE); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + dprintk(VIDC_DBG, + "Moving to release resources done state\n"); + case MSM_VIDC_CLOSE: + rc = msm_comm_session_close(flipped_state, inst); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + case MSM_VIDC_CLOSE_DONE: + rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE, + HAL_SESSION_END_DONE); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + msm_comm_session_clean(inst); + case MSM_VIDC_CORE_UNINIT: + case MSM_VIDC_CORE_INVALID: + dprintk(VIDC_DBG, "Sending core uninit\n"); + rc = msm_vidc_deinit_core(inst); + if (rc || state == get_flipped_state(inst->state, state)) + break; + default: + dprintk(VIDC_ERR, "State not recognized\n"); + rc = -EINVAL; + break; + } + +exit: + mutex_unlock(&inst->sync_lock); + + if (rc) { + dprintk(VIDC_ERR, + "Failed to move from state: %d to %d\n", + inst->state, state); + msm_comm_kill_session(inst); + } else { + trace_msm_vidc_common_state_change((void *)inst, + inst->state, state); + } + return rc; +} + +int msm_vidc_send_pending_eos_buffers(struct msm_vidc_inst *inst) +{ + struct vidc_frame_data data = {0}; + struct hfi_device *hdev; + struct eos_buf *binfo = NULL, *temp = NULL; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + mutex_lock(&inst->eosbufs.lock); + list_for_each_entry_safe(binfo, temp, &inst->eosbufs.list, list) { + if (binfo->is_queued) + continue; + + data.alloc_len = binfo->smem.size; + data.device_addr = binfo->smem.device_addr; + data.buffer_type = HAL_BUFFER_INPUT; + data.filled_len = 0; + data.offset = 0; + data.flags = HAL_BUFFERFLAG_EOS; + data.timestamp = 0; + data.extradata_addr = data.device_addr; + data.extradata_size = 0; + dprintk(VIDC_DBG, "Queueing EOS buffer 0x%x\n", + data.device_addr); + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_etb, inst->session, + &data); + binfo->is_queued = 1; + } + mutex_unlock(&inst->eosbufs.lock); + + return rc; +} + +int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd) +{ + struct msm_vidc_inst *inst = instance; + struct v4l2_decoder_cmd *dec = NULL; + struct v4l2_encoder_cmd *enc = NULL; + struct msm_vidc_core *core; + int which_cmd = 0, flags = 0, rc = 0; + + if (!inst || !inst->core || !cmd) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + core = inst->core; + if (inst->session_type == MSM_VIDC_ENCODER) { + enc = (struct v4l2_encoder_cmd *)cmd; + which_cmd = enc->cmd; + flags = enc->flags; + } else if (inst->session_type == MSM_VIDC_DECODER) { + dec = (struct v4l2_decoder_cmd *)cmd; + which_cmd = dec->cmd; + flags = dec->flags; + } + + + switch (which_cmd) { + case V4L2_QCOM_CMD_FLUSH: + rc = msm_comm_flush(inst, flags); + if (rc) { + dprintk(VIDC_ERR, + "Failed to flush buffers: %d\n", rc); + } + break; + case V4L2_QCOM_CMD_SESSION_CONTINUE: + { + rc = msm_comm_session_continue(inst); + break; + } + /* This case also for V4L2_ENC_CMD_STOP */ + case V4L2_DEC_CMD_STOP: + { + struct eos_buf *binfo = NULL; + u32 smem_flags = SMEM_UNCACHED; + + if (inst->state != MSM_VIDC_START_DONE) { + dprintk(VIDC_DBG, + "Inst = %pK is not ready for EOS\n", inst); + break; + } + + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "%s: Out of memory\n", __func__); + rc = -ENOMEM; + break; + } + + if (inst->flags & VIDC_SECURE) + smem_flags |= SMEM_SECURE; + + rc = msm_comm_smem_alloc(inst, + SZ_4K, 1, smem_flags, + HAL_BUFFER_INPUT, 0, &binfo->smem); + if (rc) { + kfree(binfo); + dprintk(VIDC_ERR, + "Failed to allocate output memory\n"); + rc = -ENOMEM; + break; + } + + mutex_lock(&inst->eosbufs.lock); + list_add_tail(&binfo->list, &inst->eosbufs.list); + mutex_unlock(&inst->eosbufs.lock); + rc = msm_vidc_send_pending_eos_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed pending_eos_buffers sending\n"); + list_del(&binfo->list); + kfree(binfo); + break; + } + break; + } + default: + dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd); + rc = -ENOTSUPP; + break; + } + return rc; +} + +static void populate_frame_data(struct vidc_frame_data *data, + struct msm_vidc_buffer *mbuf, struct msm_vidc_inst *inst) +{ + u64 time_usec; + int extra_idx; + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + struct vidc_tag_data tag_data; + + if (!inst || !mbuf || !data) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK %pK\n", + __func__, inst, mbuf, data); + return; + } + + vb = &mbuf->vvb.vb2_buf; + vbuf = to_vb2_v4l2_buffer(vb); + + time_usec = vb->timestamp; + do_div(time_usec, NSEC_PER_USEC); + + data->alloc_len = vb->planes[0].length; + data->device_addr = mbuf->smem[0].device_addr; + data->timestamp = time_usec; + data->flags = 0; + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + data->buffer_type = HAL_BUFFER_INPUT; + data->filled_len = vb->planes[0].bytesused; + data->offset = vb->planes[0].data_offset; + + if (vbuf->flags & V4L2_QCOM_BUF_FLAG_EOS) + data->flags |= HAL_BUFFERFLAG_EOS; + + if (vbuf->flags & V4L2_QCOM_BUF_FLAG_CODECCONFIG) + data->flags |= HAL_BUFFERFLAG_CODECCONFIG; + + if (inst->session_type == MSM_VIDC_DECODER) { + msm_comm_fetch_mark_data(&inst->etb_data, vb->index, + &data->mark_data, &data->mark_target); + } + + } else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + data->buffer_type = msm_comm_get_hal_output_buffer(inst); + } + + tag_data.index = vb->index; + tag_data.type = vb->type; + + if (msm_comm_fetch_tags(inst, &tag_data)) { + data->input_tag = tag_data.input_tag; + data->output_tag = tag_data.output_tag; + } else { + data->input_tag = 0; + data->output_tag = 0; + } + + + extra_idx = EXTRADATA_IDX(vb->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + data->extradata_addr = mbuf->smem[extra_idx].device_addr; + data->extradata_size = vb->planes[extra_idx].length; + data->flags |= HAL_BUFFERFLAG_EXTRADATA; + } +} + +enum hal_buffer get_hal_buffer_type(unsigned int type, + unsigned int plane_num) +{ + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (plane_num == 0) + return HAL_BUFFER_INPUT; + else + return HAL_BUFFER_EXTRADATA_INPUT; + } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (plane_num == 0) + return HAL_BUFFER_OUTPUT; + else + return HAL_BUFFER_EXTRADATA_OUTPUT; + } else { + return -EINVAL; + } +} + +int msm_comm_num_queued_bufs(struct msm_vidc_inst *inst, u32 type) +{ + int count = 0; + struct msm_vidc_buffer *mbuf; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return 0; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (mbuf->vvb.vb2_buf.type != type) + continue; + if (!(mbuf->flags & MSM_VIDC_FLAG_QUEUED)) + continue; + count++; + } + mutex_unlock(&inst->registeredbufs.lock); + + return count; +} + +static int num_pending_qbufs(struct msm_vidc_inst *inst, u32 type) +{ + int count = 0; + struct msm_vidc_buffer *mbuf; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return 0; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (mbuf->vvb.vb2_buf.type != type) + continue; + /* Count only deferred buffers */ + if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED)) + continue; + count++; + } + mutex_unlock(&inst->registeredbufs.lock); + + return count; +} + +static int msm_comm_qbuf_to_hfi(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + struct hfi_device *hdev; + enum msm_vidc_debugfs_event e; + struct vidc_frame_data frame_data = {0}; + + if (!inst || !inst->core || !inst->core->device || !mbuf) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + populate_frame_data(&frame_data, mbuf, inst); + /* mbuf is not deferred anymore */ + mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED; + + if (mbuf->vvb.vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + e = MSM_VIDC_DEBUGFS_EVENT_ETB; + rc = call_hfi_op(hdev, session_etb, inst->session, &frame_data); + } else if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + e = MSM_VIDC_DEBUGFS_EVENT_FTB; + rc = call_hfi_op(hdev, session_ftb, inst->session, &frame_data); + } else { + dprintk(VIDC_ERR, "%s: invalid qbuf type %d:\n", __func__, + mbuf->vvb.vb2_buf.type); + rc = -EINVAL; + } + if (rc) { + dprintk(VIDC_ERR, "%s: Failed to qbuf: %d\n", __func__, rc); + goto err_bad_input; + } + mbuf->flags |= MSM_VIDC_FLAG_QUEUED; + msm_vidc_debugfs_update(inst, e); + +err_bad_input: + return rc; +} + +void msm_vidc_batch_handler(struct work_struct *work) +{ + int rc = 0; + struct msm_vidc_inst *inst; + + inst = container_of(work, struct msm_vidc_inst, batch_work); + + inst = get_inst(get_vidc_core(MSM_VIDC_CORE_VENUS), inst); + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: invalid state\n", __func__); + goto exit; + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + dprintk(VIDC_INFO, + "%s: queing batch pending buffers to firmware\n", __func__); + + rc = msm_comm_qbufs_batch(inst, NULL); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed batch-qbuf to hfi: %d\n", + __func__, rc); + } + +exit: + put_inst(inst); +} + +static int msm_comm_qbuf_in_rbr(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__); + return -EINVAL; + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + print_vidc_buffer(VIDC_DBG, "qbuf in rbr", inst, mbuf); + rc = msm_comm_qbuf_to_hfi(inst, mbuf); + if (rc) + dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", __func__, rc); + + return rc; +} + +int msm_comm_qbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__); + return -EINVAL; + } + + if (inst->state != MSM_VIDC_START_DONE) { + mbuf->flags |= MSM_VIDC_FLAG_DEFERRED; + print_vidc_buffer(VIDC_DBG, "qbuf deferred", inst, mbuf); + return 0; + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + print_vidc_buffer(VIDC_DBG, "qbuf", inst, mbuf); + rc = msm_comm_qbuf_to_hfi(inst, mbuf); + if (rc) + dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", __func__, rc); + + return rc; +} + +int msm_comm_qbufs(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct msm_vidc_buffer *mbuf; + + if (!inst) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + if (inst->state != MSM_VIDC_START_DONE) { + dprintk(VIDC_DBG, "%s: inst not in start state: %d\n", + __func__, inst->state); + return 0; + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + /* Queue only deferred buffers */ + if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED)) + continue; + print_vidc_buffer(VIDC_DBG, "qbufs", inst, mbuf); + rc = msm_comm_qbuf_to_hfi(inst, mbuf); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", + __func__, rc); + break; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + return rc; +} + +int msm_comm_qbufs_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + struct msm_vidc_buffer *buf; + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(buf, &inst->registeredbufs.list, list) { + /* Don't queue if buffer is not CAPTURE_MPLANE */ + if (buf->vvb.vb2_buf.type != + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + goto loop_end; + /* Don't queue if buffer is not a deferred buffer */ + if (!(buf->flags & MSM_VIDC_FLAG_DEFERRED)) + goto loop_end; + /* Don't queue if RBR event is pending on this buffer */ + if (buf->flags & MSM_VIDC_FLAG_RBR_PENDING) + goto loop_end; + + print_vidc_buffer(VIDC_DBG, "batch-qbuf", inst, buf); + rc = msm_comm_qbuf_to_hfi(inst, buf); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed batch qbuf to hfi: %d\n", + __func__, rc); + break; + } +loop_end: + /* Queue pending buffers till the current buffer only */ + if (buf == mbuf) + break; + } + mutex_unlock(&inst->registeredbufs.lock); + + return rc; +} + +/* + * msm_comm_qbuf_decode_batch - count the buffers which are not queued to + * firmware yet (count includes rbr pending buffers too) and + * queue the buffers at once if full batch count reached. + * Don't queue rbr pending buffers as they would be queued + * when rbr event arrived from firmware. + */ +int msm_comm_qbuf_decode_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + u32 count = 0; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__); + return -EINVAL; + } + + if (inst->state != MSM_VIDC_START_DONE) { + mbuf->flags |= MSM_VIDC_FLAG_DEFERRED; + print_vidc_buffer(VIDC_DBG, "qbuf deferred", inst, mbuf); + return 0; + } + + /* + * Don't defer buffers initially to avoid startup latency increase + * due to batching + */ + if (inst->clk_data.buffer_counter > SKIP_BATCH_WINDOW) { + mod_timer(&inst->batch_timer, jiffies + + msecs_to_jiffies(MSM_VIDC_QBUF_BATCH_TIMEOUT)); + count = num_pending_qbufs(inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (count < inst->batch.size) { + print_vidc_buffer(VIDC_DBG, + "batch-qbuf deferred", inst, mbuf); + return 0; + } + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + rc = msm_comm_qbufs_batch(inst, mbuf); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", + __func__, rc); + } + + return rc; +} + +int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst) +{ + int rc = 0, i = 0; + union hal_get_property hprop; + enum hal_buffer int_buf[] = { + HAL_BUFFER_INTERNAL_SCRATCH, + HAL_BUFFER_INTERNAL_SCRATCH_1, + HAL_BUFFER_INTERNAL_SCRATCH_2, + HAL_BUFFER_INTERNAL_PERSIST, + HAL_BUFFER_INTERNAL_PERSIST_1, + HAL_BUFFER_INTERNAL_RECON, + }; + + memset(&hprop, 0x0, sizeof(hprop)); + + rc = msm_comm_try_get_prop(inst, HAL_PARAM_GET_BUFFER_REQUIREMENTS, + &hprop); + if (rc) { + dprintk(VIDC_ERR, "Failed getting buffer requirements: %d", rc); + return rc; + } + + /* reset internal buffers */ + for (i = 0; i < ARRAY_SIZE(int_buf); i++) + msm_comm_reset_bufreqs(inst, int_buf[i]); + + dprintk(VIDC_DBG, "Buffer requirements from HW:\n"); + dprintk(VIDC_DBG, "%15s %8s %8s %8s %8s\n", + "buffer type", "count", "mincount_host", "mincount_fw", "size"); + for (i = 0; i < HAL_BUFFER_MAX; i++) { + struct hal_buffer_requirements req = hprop.buf_req.buffer[i]; + struct hal_buffer_requirements *curr_req; + + /* + * For decoder we can ignore the buffer counts that firmware + * sends for inp/out buffers. + * FW buffer counts for these are used only in reconfig + */ + curr_req = get_buff_req_buffer(inst, req.buffer_type); + if (!curr_req) + return -EINVAL; + + if (req.buffer_type == HAL_BUFFER_INPUT || + req.buffer_type == HAL_BUFFER_OUTPUT || + req.buffer_type == HAL_BUFFER_OUTPUT2 || + req.buffer_type == HAL_BUFFER_EXTRADATA_INPUT || + req.buffer_type == HAL_BUFFER_EXTRADATA_OUTPUT || + req.buffer_type == HAL_BUFFER_EXTRADATA_OUTPUT2) { + curr_req->buffer_size = req.buffer_size; + curr_req->buffer_region_size = req.buffer_region_size; + curr_req->contiguous = req.contiguous; + curr_req->buffer_alignment = req.buffer_alignment; + } else { + memcpy(curr_req, &req, + sizeof(struct hal_buffer_requirements)); + } + + if (req.buffer_type != HAL_BUFFER_NONE) { + dprintk(VIDC_DBG, "%15s %8d %8d %8d %8d\n", + get_buffer_name(req.buffer_type), + req.buffer_count_actual, + req.buffer_count_min_host, + req.buffer_count_min, req.buffer_size); + } + } + + dprintk(VIDC_DBG, "Buffer requirements driver adjusted:\n"); + dprintk(VIDC_DBG, "%15s %8s %8s %8s %8s\n", + "buffer type", "count", "mincount_host", "mincount_fw", "size"); + for (i = 0; i < HAL_BUFFER_MAX; i++) { + struct hal_buffer_requirements req = inst->buff_req.buffer[i]; + + if (req.buffer_type != HAL_BUFFER_NONE) { + dprintk(VIDC_DBG, "%15s %8d %8d %8d %8d\n", + get_buffer_name(req.buffer_type), + req.buffer_count_actual, + req.buffer_count_min_host, + req.buffer_count_min, req.buffer_size); + } + } + return rc; +} + +int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, + union hal_get_property *hprop) +{ + int rc = 0; + struct hfi_device *hdev; + struct getprop_buf *buf; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + mutex_lock(&inst->sync_lock); + if (inst->state < MSM_VIDC_OPEN_DONE || + inst->state >= MSM_VIDC_CLOSE) { + + /* No need to check inst->state == MSM_VIDC_INVALID since + * INVALID is > CLOSE_DONE. When core went to INVALID state, + * we put all the active instances in INVALID. So > CLOSE_DONE + * is enough check to have. + */ + + dprintk(VIDC_ERR, + "In Wrong state to call Buf Req: Inst %pK or Core %pK\n", + inst, inst->core); + rc = -EAGAIN; + mutex_unlock(&inst->sync_lock); + goto exit; + } + mutex_unlock(&inst->sync_lock); + + switch (ptype) { + case HAL_PARAM_GET_BUFFER_REQUIREMENTS: + rc = call_hfi_op(hdev, session_get_buf_req, inst->session); + break; + default: + rc = -EAGAIN; + break; + } + + if (rc) { + dprintk(VIDC_ERR, "Can't query hardware for property: %d\n", + rc); + goto exit; + } + + rc = wait_for_completion_timeout(&inst->completions[ + SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)], + msecs_to_jiffies( + inst->core->resources.msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, + "%s: Wait interrupted or timed out [%pK]: %d\n", + __func__, inst, + SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)); + msm_comm_kill_session(inst); + rc = -ETIMEDOUT; + goto exit; + } else { + /* wait_for_completion_timeout returns jiffies before expiry */ + rc = 0; + } + + mutex_lock(&inst->pending_getpropq.lock); + if (!list_empty(&inst->pending_getpropq.list)) { + buf = list_first_entry(&inst->pending_getpropq.list, + struct getprop_buf, list); + *hprop = *(union hal_get_property *)buf->data; + kfree(buf->data); + list_del(&buf->list); + kfree(buf); + } else { + dprintk(VIDC_ERR, "%s getprop list empty\n", __func__); + rc = -EINVAL; + } + mutex_unlock(&inst->pending_getpropq.lock); +exit: + return rc; +} + +int msm_comm_release_output_buffers(struct msm_vidc_inst *inst, + bool force_release) +{ + struct msm_smem *handle; + struct internal_buf *buf, *dummy; + struct vidc_buffer_addr_info buffer_info; + int rc = 0; + struct msm_vidc_core *core; + struct hfi_device *hdev; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return -EINVAL; + } + mutex_lock(&inst->outputbufs.lock); + if (list_empty(&inst->outputbufs.list)) { + dprintk(VIDC_DBG, "%s - No OUTPUT buffers allocated\n", + __func__); + mutex_unlock(&inst->outputbufs.lock); + return 0; + } + mutex_unlock(&inst->outputbufs.lock); + + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %pK\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev); + return -EINVAL; + } + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry_safe(buf, dummy, &inst->outputbufs.list, list) { + handle = &buf->smem; + + if ((buf->buffer_ownership == FIRMWARE) && !force_release) { + dprintk(VIDC_INFO, "DPB is with f/w. Can't free it\n"); + /* + * mark this buffer to avoid sending it to video h/w + * again, this buffer belongs to old resolution and + * it will be removed when video h/w returns it. + */ + buf->mark_remove = true; + continue; + } + + buffer_info.buffer_size = handle->size; + buffer_info.buffer_type = buf->buffer_type; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = handle->device_addr; + if (inst->buffer_mode_set[CAPTURE_PORT] == + HAL_BUFFER_MODE_STATIC) { + buffer_info.response_required = false; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_WARN, + "Rel output buf fail:%x, %d\n", + buffer_info.align_device_addr, + buffer_info.buffer_size); + } + } + + list_del(&buf->list); + msm_comm_smem_free(inst, &buf->smem); + kfree(buf); + } + + mutex_unlock(&inst->outputbufs.lock); + return rc; +} + +static enum hal_buffer scratch_buf_sufficient(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + struct hal_buffer_requirements *bufreq = NULL; + struct internal_buf *buf; + int count = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s - invalid param\n", __func__); + goto not_sufficient; + } + + bufreq = get_buff_req_buffer(inst, buffer_type); + if (!bufreq) + goto not_sufficient; + + /* Check if current scratch buffers are sufficient */ + mutex_lock(&inst->scratchbufs.lock); + + list_for_each_entry(buf, &inst->scratchbufs.list, list) { + if (buf->buffer_type == buffer_type && + buf->smem.size >= bufreq->buffer_size) + count++; + } + mutex_unlock(&inst->scratchbufs.lock); + + if (count != bufreq->buffer_count_actual) + goto not_sufficient; + + dprintk(VIDC_DBG, + "Existing scratch buffer is sufficient for buffer type %#x\n", + buffer_type); + + return buffer_type; + +not_sufficient: + return HAL_BUFFER_NONE; +} + +int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst, + bool check_for_reuse) +{ + struct msm_smem *handle; + struct internal_buf *buf, *dummy; + struct vidc_buffer_addr_info buffer_info; + int rc = 0; + struct msm_vidc_core *core; + struct hfi_device *hdev; + enum hal_buffer sufficiency = HAL_BUFFER_NONE; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %pK\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev); + return -EINVAL; + } + + if (check_for_reuse) { + sufficiency |= scratch_buf_sufficient(inst, + HAL_BUFFER_INTERNAL_SCRATCH); + + sufficiency |= scratch_buf_sufficient(inst, + HAL_BUFFER_INTERNAL_SCRATCH_1); + + sufficiency |= scratch_buf_sufficient(inst, + HAL_BUFFER_INTERNAL_SCRATCH_2); + } + + mutex_lock(&inst->scratchbufs.lock); + list_for_each_entry_safe(buf, dummy, &inst->scratchbufs.list, list) { + handle = &buf->smem; + buffer_info.buffer_size = handle->size; + buffer_info.buffer_type = buf->buffer_type; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = handle->device_addr; + buffer_info.response_required = true; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (!rc) { + mutex_unlock(&inst->scratchbufs.lock); + rc = wait_for_sess_signal_receipt(inst, + HAL_SESSION_RELEASE_BUFFER_DONE); + if (rc) + dprintk(VIDC_WARN, + "%s: wait for signal failed, rc %d\n", + __func__, rc); + mutex_lock(&inst->scratchbufs.lock); + } else { + dprintk(VIDC_WARN, + "Rel scrtch buf fail:%x, %d\n", + buffer_info.align_device_addr, + buffer_info.buffer_size); + } + + /*If scratch buffers can be reused, do not free the buffers*/ + if (sufficiency & buf->buffer_type) + continue; + + list_del(&buf->list); + msm_comm_smem_free(inst, handle); + kfree(buf); + } + + mutex_unlock(&inst->scratchbufs.lock); + return rc; +} + +void msm_comm_release_eos_buffers(struct msm_vidc_inst *inst) +{ + struct eos_buf *buf, *next; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return; + } + + mutex_lock(&inst->eosbufs.lock); + list_for_each_entry_safe(buf, next, &inst->eosbufs.list, list) { + list_del(&buf->list); + msm_comm_smem_free(inst, &buf->smem); + kfree(buf); + } + INIT_LIST_HEAD(&inst->eosbufs.list); + mutex_unlock(&inst->eosbufs.lock); +} + + +int msm_comm_release_recon_buffers(struct msm_vidc_inst *inst) +{ + struct recon_buf *buf, *next; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return -EINVAL; + } + + mutex_lock(&inst->reconbufs.lock); + list_for_each_entry_safe(buf, next, &inst->reconbufs.list, list) { + list_del(&buf->list); + kfree(buf); + } + INIT_LIST_HEAD(&inst->reconbufs.list); + mutex_unlock(&inst->reconbufs.lock); + + return 0; +} + +int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst) +{ + struct msm_smem *handle; + struct list_head *ptr, *next; + struct internal_buf *buf; + struct vidc_buffer_addr_info buffer_info; + int rc = 0; + struct msm_vidc_core *core; + struct hfi_device *hdev; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %pK\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev); + return -EINVAL; + } + + mutex_lock(&inst->persistbufs.lock); + list_for_each_safe(ptr, next, &inst->persistbufs.list) { + buf = list_entry(ptr, struct internal_buf, list); + handle = &buf->smem; + buffer_info.buffer_size = handle->size; + buffer_info.buffer_type = buf->buffer_type; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = handle->device_addr; + buffer_info.response_required = true; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (!rc) { + mutex_unlock(&inst->persistbufs.lock); + rc = wait_for_sess_signal_receipt(inst, + HAL_SESSION_RELEASE_BUFFER_DONE); + if (rc) + dprintk(VIDC_WARN, + "%s: wait for signal failed, rc %d\n", + __func__, rc); + mutex_lock(&inst->persistbufs.lock); + } else { + dprintk(VIDC_WARN, + "Rel prst buf fail:%x, %d\n", + buffer_info.align_device_addr, + buffer_info.buffer_size); + } + list_del(&buf->list); + msm_comm_smem_free(inst, handle); + kfree(buf); + } + mutex_unlock(&inst->persistbufs.lock); + return rc; +} + +int msm_comm_try_set_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, void *pdata) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid input: %pK\n", inst); + return -EINVAL; + } + + if (!inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + mutex_lock(&inst->sync_lock); + if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) { + dprintk(VIDC_ERR, "Not in proper state to set property\n"); + rc = -EAGAIN; + goto exit; + } + rc = call_hfi_op(hdev, session_set_property, (void *)inst->session, + ptype, pdata); + if (rc) + dprintk(VIDC_ERR, "Failed to set hal property for framesize\n"); +exit: + mutex_unlock(&inst->sync_lock); + return rc; +} + +int msm_comm_set_buffer_count(struct msm_vidc_inst *inst, + int host_count, int act_count, enum hal_buffer type) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_buffer_count_actual buf_count; + + hdev = inst->core->device; + + buf_count.buffer_type = type; + buf_count.buffer_count_actual = act_count; + buf_count.buffer_count_min_host = host_count; + dprintk(VIDC_DBG, "%s: %x : hal_buffer %d min_host %d actual %d\n", + __func__, hash32_ptr(inst->session), type, + host_count, act_count); + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_COUNT_ACTUAL, &buf_count); + if (rc) + dprintk(VIDC_ERR, + "Failed to set actual buffer count %d for buffer type %d\n", + act_count, type); + return rc; +} + +int msm_comm_set_output_buffers(struct msm_vidc_inst *inst) +{ + int rc = 0; + bool force_release = true; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (inst->fmts[OUTPUT_PORT].defer_outputs) + force_release = false; + + if (msm_comm_release_output_buffers(inst, force_release)) + dprintk(VIDC_WARN, "Failed to release output buffers\n"); + + rc = set_output_buffers(inst, HAL_BUFFER_OUTPUT); + if (rc) + goto error; + return rc; +error: + msm_comm_release_output_buffers(inst, true); + return rc; +} + +int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (msm_comm_release_scratch_buffers(inst, true)) + dprintk(VIDC_WARN, "Failed to release scratch buffers\n"); + + rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH, + &inst->scratchbufs); + if (rc) + goto error; + + rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1, + &inst->scratchbufs); + if (rc) + goto error; + + rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2, + &inst->scratchbufs); + if (rc) + goto error; + + return rc; +error: + msm_comm_release_scratch_buffers(inst, false); + return rc; +} + +int msm_comm_set_recon_buffers(struct msm_vidc_inst *inst) +{ + int rc = 0, i = 0; + struct hal_buffer_requirements *internal_buf; + struct recon_buf *binfo; + struct msm_vidc_list *buf_list = &inst->reconbufs; + + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_ENCODER) + internal_buf = get_buff_req_buffer(inst, + HAL_BUFFER_INTERNAL_RECON); + else if (inst->session_type == MSM_VIDC_DECODER) + internal_buf = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + else + return -EINVAL; + + if (!internal_buf || !internal_buf->buffer_count_actual) { + dprintk(VIDC_DBG, "Inst : %pK Recon buffers not required\n", + inst); + return 0; + } + + msm_comm_release_recon_buffers(inst); + + for (i = 0; i < internal_buf->buffer_count_actual; i++) { + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto fail_kzalloc; + } + + binfo->buffer_index = i; + mutex_lock(&buf_list->lock); + list_add_tail(&binfo->list, &buf_list->list); + mutex_unlock(&buf_list->lock); + } + +fail_kzalloc: + return rc; +} + +int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST, + &inst->persistbufs); + if (rc) + goto error; + + rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1, + &inst->persistbufs); + if (rc) + goto error; + return rc; +error: + msm_comm_release_persist_buffers(inst); + return rc; +} + +static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst) +{ + struct list_head *ptr, *next; + enum vidc_ports ports[] = {OUTPUT_PORT, CAPTURE_PORT}; + int c = 0; + + /* before flush ensure venus released all buffers */ + msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + + for (c = 0; c < ARRAY_SIZE(ports); ++c) { + enum vidc_ports port = ports[c]; + + mutex_lock(&inst->bufq[port].lock); + list_for_each_safe(ptr, next, + &inst->bufq[port].vb2_bufq.queued_list) { + struct vb2_buffer *vb = container_of(ptr, + struct vb2_buffer, queued_entry); + if (vb->state == VB2_BUF_STATE_ACTIVE) { + vb->planes[0].bytesused = 0; + print_vb2_buffer(VIDC_ERR, "flush in invalid", + inst, vb); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } else { + dprintk(VIDC_WARN, + "%s VB is in state %d not in ACTIVE state\n" + , __func__, vb->state); + } + } + mutex_unlock(&inst->bufq[port].lock); + } + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE); +} + +int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags) +{ + int i, rc = 0; + bool ip_flush = false; + bool op_flush = false; + struct msm_vidc_buffer *mbuf, *next; + struct msm_vidc_core *core; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "Invalid params, inst %pK\n", inst); + return -EINVAL; + } + + if (inst->state < MSM_VIDC_OPEN_DONE) { + dprintk(VIDC_ERR, + "Invalid state to call flush, inst %pK, state %#x\n", + inst, inst->state); + return -EINVAL; + } + + core = inst->core; + hdev = core->device; + + ip_flush = !!(flags & V4L2_QCOM_CMD_FLUSH_OUTPUT); + op_flush = !!(flags & V4L2_QCOM_CMD_FLUSH_CAPTURE); + + if (ip_flush && !op_flush) { + dprintk(VIDC_WARN, + "Input only flush not supported, making it flush all\n"); + op_flush = true; + goto exit; + } + + if ((inst->in_flush && ip_flush) || (inst->out_flush && op_flush)) { + dprintk(VIDC_WARN, "%s: %x : Already in flush\n", + __func__, hash32_ptr(inst->session)); + goto exit; + } + + msm_clock_data_reset(inst); + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core %pK and inst %pK are in bad state\n", + core, inst); + msm_comm_flush_in_invalid_state(inst); + goto exit; + } + + if (ip_flush) + mutex_lock(&inst->bufq[OUTPUT_PORT].lock); + if (op_flush) + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + + /* enable in flush */ + inst->in_flush = ip_flush; + inst->out_flush = op_flush; + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(mbuf, next, &inst->registeredbufs.list, list) { + /* don't flush input buffers if input flush is not requested */ + if (!ip_flush && mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + continue; + + /* flush only deferred or rbr pending buffers */ + if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED || + mbuf->flags & MSM_VIDC_FLAG_RBR_PENDING)) + continue; + + /* + * flush buffers which are queued by client already, + * the refcount will be two or more for those buffers. + */ + if (!(mbuf->smem[0].refcount >= 2)) + continue; + + print_vidc_buffer(VIDC_DBG, "flush buf", inst, mbuf); + msm_comm_flush_vidc_buffer(inst, mbuf); + + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "dqbuf: unmap failed.", inst, mbuf); + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "dqbuf: unmap failed..", inst, mbuf); + } + if (!mbuf->smem[0].refcount) { + list_del(&mbuf->list); + kref_put_mbuf(mbuf); + } else { + /* buffer is no more a deferred buffer */ + mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + hdev = inst->core->device; + if (ip_flush) { + dprintk(VIDC_DBG, "Send flush on all ports to firmware\n"); + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_ALL); + } else { + dprintk(VIDC_DBG, "Send flush on output port to firmware\n"); + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_OUTPUT); + } + + if (op_flush) + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); + if (ip_flush) + mutex_unlock(&inst->bufq[OUTPUT_PORT].lock); + + if (rc) { + dprintk(VIDC_ERR, + "Sending flush to firmware failed, flush out all buffers\n"); + msm_comm_flush_in_invalid_state(inst); + /* disable in_flush & out_flush */ + inst->in_flush = false; + inst->out_flush = false; + } + +exit: + return rc; +} + +enum hal_extradata_id msm_comm_get_hal_extradata_index( + enum v4l2_mpeg_vidc_extradata index) +{ + int ret = 0; + + switch (index) { + case V4L2_MPEG_VIDC_EXTRADATA_NONE: + ret = HAL_EXTRADATA_NONE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO: + ret = HAL_EXTRADATA_INTERLACE_VIDEO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP: + ret = HAL_EXTRADATA_TIMESTAMP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING: + ret = HAL_EXTRADATA_S3D_FRAME_PACKING; + break; + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE: + ret = HAL_EXTRADATA_FRAME_RATE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW: + ret = HAL_EXTRADATA_PANSCAN_WINDOW; + break; + case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI: + ret = HAL_EXTRADATA_RECOVERY_POINT_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: + ret = HAL_EXTRADATA_NUM_CONCEALED_MB; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + ret = HAL_EXTRADATA_ASPECT_RATIO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP: + ret = HAL_EXTRADATA_MPEG2_SEQDISP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA: + ret = HAL_EXTRADATA_STREAM_USERDATA; + break; + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP: + ret = HAL_EXTRADATA_DEC_FRAME_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP: + ret = HAL_EXTRADATA_ENC_FRAME_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_LTR: + ret = HAL_EXTRADATA_LTR_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: + ret = HAL_EXTRADATA_ROI_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP: + ret = HAL_EXTRADATA_OUTPUT_CROP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI: + ret = HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI: + ret = HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY: + ret = HAL_EXTRADATA_VUI_DISPLAY_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE: + ret = HAL_EXTRADATA_VPX_COLORSPACE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO: + ret = HAL_EXTRADATA_UBWC_CR_STATS_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA: + ret = HAL_EXTRADATA_HDR10PLUS_METADATA; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS: + ret = HAL_EXTRADATA_ENC_DTS_METADATA; + break; + case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: + ret = HAL_EXTRADATA_INPUT_CROP; + break; + default: + dprintk(VIDC_WARN, "Extradata not found: %d\n", index); + break; + } + return ret; +}; + +int msm_vidc_noc_error_info(struct msm_vidc_core *core) +{ + struct hfi_device *hdev; + + if (!core || !core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameters: %pK\n", + __func__, core); + return -EINVAL; + } + + if (!core->resources.non_fatal_pagefaults) + return 0; + + if (!core->smmu_fault_handled) + return 0; + + hdev = core->device; + call_hfi_op(hdev, noc_error_info, hdev->hfi_device_data); + + return 0; +} + +int msm_vidc_trigger_ssr(struct msm_vidc_core *core, + enum hal_ssr_trigger_type type) +{ + if (!core) { + dprintk(VIDC_WARN, "%s: Invalid parameters\n", __func__); + return -EINVAL; + } + core->ssr_type = type; + schedule_work(&core->ssr_work); + return 0; +} + +void msm_vidc_ssr_handler(struct work_struct *work) +{ + int rc; + struct msm_vidc_core *core; + struct hfi_device *hdev; + + core = container_of(work, struct msm_vidc_core, ssr_work); + if (!core || !core->device) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return; + } + hdev = core->device; + + mutex_lock(&core->lock); + if (core->state == VIDC_CORE_INIT_DONE) { + dprintk(VIDC_WARN, "%s: ssr type %d\n", __func__, + core->ssr_type); + /* + * In current implementation user-initiated SSR triggers + * a fatal error from hardware. However, there is no way + * to know if fatal error is due to SSR or not. Handle + * user SSR as non-fatal. + */ + core->trigger_ssr = true; + rc = call_hfi_op(hdev, core_trigger_ssr, + hdev->hfi_device_data, core->ssr_type); + if (rc) { + dprintk(VIDC_ERR, "%s: trigger_ssr failed\n", + __func__); + core->trigger_ssr = false; + } + } else { + dprintk(VIDC_WARN, "%s: video core %pK not initialized\n", + __func__, core); + } + mutex_unlock(&core->lock); +} + +static int msm_vidc_load_supported(struct msm_vidc_inst *inst) +{ + int num_mbs_per_sec = 0, max_load_adj = 0; + enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD | + LOAD_CALC_IGNORE_THUMBNAIL_LOAD | + LOAD_CALC_IGNORE_NON_REALTIME_LOAD; + + if (inst->state == MSM_VIDC_OPEN_DONE) { + max_load_adj = inst->core->resources.max_load; + num_mbs_per_sec = msm_comm_get_load(inst->core, + MSM_VIDC_DECODER, quirks); + num_mbs_per_sec += msm_comm_get_load(inst->core, + MSM_VIDC_ENCODER, quirks); + if (num_mbs_per_sec > max_load_adj) { + dprintk(VIDC_ERR, + "H/W is overloaded. needed: %d max: %d\n", + num_mbs_per_sec, + max_load_adj); + msm_vidc_print_running_insts(inst->core); + return -EBUSY; + } + } + return 0; +} + +int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst) +{ + u32 x_min, x_max, y_min, y_max; + u32 input_height, input_width, output_height, output_width; + + if (inst->grid_enable > 0) { + dprintk(VIDC_DBG, "Skip scaling check for HEIC\n"); + return 0; + } + + input_height = inst->prop.height[OUTPUT_PORT]; + input_width = inst->prop.width[OUTPUT_PORT]; + output_height = inst->prop.height[CAPTURE_PORT]; + output_width = inst->prop.width[CAPTURE_PORT]; + + if (!input_height || !input_width || !output_height || !output_width) { + dprintk(VIDC_ERR, + "Invalid : Input height = %d width = %d", + input_height, input_width); + dprintk(VIDC_ERR, + " output height = %d width = %d\n", + output_height, output_width); + return -ENOTSUPP; + } + + if (!inst->capability.scale_x.min || + !inst->capability.scale_x.max || + !inst->capability.scale_y.min || + !inst->capability.scale_y.max) { + + if (input_width * input_height != + output_width * output_height) { + dprintk(VIDC_ERR, + "%s: scaling is not supported (%dx%d != %dx%d)\n", + __func__, input_width, input_height, + output_width, output_height); + return -ENOTSUPP; + } + + dprintk(VIDC_DBG, "%s: supported WxH = %dx%d\n", + __func__, input_width, input_height); + return 0; + } + + x_min = (1<<16)/inst->capability.scale_x.min; + y_min = (1<<16)/inst->capability.scale_y.min; + x_max = inst->capability.scale_x.max >> 16; + y_max = inst->capability.scale_y.max >> 16; + + if (input_height > output_height) { + if (input_height > x_min * output_height) { + dprintk(VIDC_ERR, + "Unsupported height min height %d vs %d\n", + input_height / x_min, output_height); + return -ENOTSUPP; + } + } else { + if (output_height > x_max * input_height) { + dprintk(VIDC_ERR, + "Unsupported height max height %d vs %d\n", + x_max * input_height, output_height); + return -ENOTSUPP; + } + } + if (input_width > output_width) { + if (input_width > y_min * output_width) { + dprintk(VIDC_ERR, + "Unsupported width min width %d vs %d\n", + input_width / y_min, output_width); + return -ENOTSUPP; + } + } else { + if (output_width > y_max * input_width) { + dprintk(VIDC_ERR, + "Unsupported width max width %d vs %d\n", + y_max * input_width, output_width); + return -ENOTSUPP; + } + } + return 0; +} + +static bool is_image_session(struct msm_vidc_inst *inst) +{ + if (inst->session_type == MSM_VIDC_ENCODER && + get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) == + HAL_VIDEO_CODEC_HEVC) + return (inst->profile == HAL_HEVC_PROFILE_MAIN_STILL_PIC || + inst->grid_enable); + else + return false; +} + +static int msm_vidc_check_image_session_capabilities(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct msm_vidc_image_capability *capability = NULL; + + u32 output_height = ALIGN(inst->prop.height[CAPTURE_PORT], 512); + u32 output_width = ALIGN(inst->prop.width[CAPTURE_PORT], 512); + + if (inst->grid_enable) + capability = inst->core->platform_data->heic_image_capability; + else + capability = inst->core->platform_data->hevc_image_capability; + + if (!capability) + return -EINVAL; + + if (output_width < capability->width.min || + output_height < capability->height.min) { + dprintk(VIDC_ERR, + "HEIC Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n", + output_width, + output_height, + capability->width.min, + capability->height.min); + rc = -ENOTSUPP; + } + if (!rc && (output_width > capability->width.max || + output_height > capability->height.max)) { + dprintk(VIDC_ERR, + "HEIC Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n", + output_width, + output_height, + capability->width.max, + capability->height.max); + rc = -ENOTSUPP; + } + if (!rc && output_height * output_width > + capability->width.max * capability->height.max) { + dprintk(VIDC_ERR, + "HEIC Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n", + output_width, output_height, + capability->width.max, capability->height.max); + rc = -ENOTSUPP; + } + + return rc; +} + +int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) +{ + struct msm_vidc_capability *capability; + int rc = 0; + struct hfi_device *hdev; + struct msm_vidc_core *core; + u32 output_height, output_width, input_height, input_width; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__); + return -EINVAL; + } + capability = &inst->capability; + hdev = inst->core->device; + core = inst->core; + rc = msm_vidc_load_supported(inst); + if (rc) { + dprintk(VIDC_WARN, + "%s: Hardware is overloaded\n", __func__); + return rc; + } + + if (!is_thermal_permissible(core)) { + dprintk(VIDC_WARN, + "Thermal level critical, stop all active sessions!\n"); + return -ENOTSUPP; + } + + output_height = inst->prop.height[CAPTURE_PORT]; + output_width = inst->prop.width[CAPTURE_PORT]; + input_height = inst->prop.height[OUTPUT_PORT]; + input_width = inst->prop.width[OUTPUT_PORT]; + + if (inst->session_type == MSM_VIDC_ENCODER && (input_width % 2 != 0 || + input_height % 2 != 0 || output_width % 2 != 0 || + output_height % 2 != 0)) { + dprintk(VIDC_ERR, + "Height and Width should be even numbers for NV12\n"); + dprintk(VIDC_ERR, + "Input WxH = (%u)x(%u), Output WxH = (%u)x(%u)\n", + input_width, input_height, + output_width, output_height); + rc = -ENOTSUPP; + } + + if (is_image_session(inst)) { + rc = msm_vidc_check_image_session_capabilities(inst); + return rc; + } + + output_height = ALIGN(inst->prop.height[CAPTURE_PORT], 16); + output_width = ALIGN(inst->prop.width[CAPTURE_PORT], 16); + + if (!rc) { + if (output_width < capability->width.min || + output_height < capability->height.min) { + dprintk(VIDC_ERR, + "Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n", + output_width, + output_height, + capability->width.min, + capability->height.min); + rc = -ENOTSUPP; + } + if (!rc && output_width > capability->width.max) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u\n", + output_width, + capability->width.max); + rc = -ENOTSUPP; + } + + if (!rc && output_height * output_width > + capability->width.max * capability->height.max) { + dprintk(VIDC_ERR, + "Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n", + output_width, output_height, + capability->width.max, capability->height.max); + rc = -ENOTSUPP; + } + } + if (rc) { + dprintk(VIDC_ERR, + "%s: Resolution unsupported\n", __func__); + } + return rc; +} + +void msm_comm_generate_session_error(struct msm_vidc_inst *inst) +{ + enum hal_command_response cmd = HAL_SESSION_ERROR; + struct msm_vidc_cb_cmd_done response = {0}; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return; + } + dprintk(VIDC_WARN, "%s: inst %pK\n", __func__, inst); + response.session_id = inst; + response.status = VIDC_ERR_FAIL; + handle_session_error(cmd, (void *)&response); +} + +void msm_comm_generate_sys_error(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + enum hal_command_response cmd = HAL_SYS_ERROR; + struct msm_vidc_cb_cmd_done response = {0}; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return; + } + dprintk(VIDC_WARN, "%s: inst %pK\n", __func__, inst); + core = inst->core; + response.device_id = (u32) core->id; + handle_sys_error(cmd, (void *) &response); + +} + +int msm_comm_kill_session(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return -EINVAL; + } else if (!inst->session) { + dprintk(VIDC_ERR, "%s: no session to kill for inst %pK\n", + __func__, inst); + return 0; + } + + dprintk(VIDC_ERR, "%s: inst %pK, session %x state %d\n", __func__, + inst, hash32_ptr(inst->session), inst->state); + /* + * We're internally forcibly killing the session, if fw is aware of + * the session send session_abort to firmware to clean up and release + * the session, else just kill the session inside the driver. + */ + if ((inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_CLOSE_DONE) || + inst->state == MSM_VIDC_CORE_INVALID) { + rc = msm_comm_session_abort(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: inst %pK session %x abort failed\n", + __func__, inst, hash32_ptr(inst->session)); + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + } + } + + change_inst_state(inst, MSM_VIDC_CLOSE_DONE); + msm_comm_session_clean(inst); + + dprintk(VIDC_WARN, "%s: inst %pK session %x handled\n", __func__, + inst, hash32_ptr(inst->session)); + return rc; +} + +int msm_comm_smem_alloc(struct msm_vidc_inst *inst, + size_t size, u32 align, u32 flags, enum hal_buffer buffer_type, + int map_kernel, struct msm_smem *smem) +{ + int rc = 0; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst); + return -EINVAL; + } + rc = msm_smem_alloc(size, align, flags, buffer_type, map_kernel, + &(inst->core->resources), inst->session_type, + smem); + return rc; +} + +void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem) +{ + if (!inst || !inst->core || !mem) { + dprintk(VIDC_ERR, + "%s: invalid params: %pK %pK\n", __func__, inst, mem); + return; + } + msm_smem_free(mem); +} + +void msm_vidc_fw_unload_handler(struct work_struct *work) +{ + struct msm_vidc_core *core = NULL; + struct hfi_device *hdev = NULL; + int rc = 0; + + core = container_of(work, struct msm_vidc_core, fw_unload_work.work); + if (!core || !core->device) { + dprintk(VIDC_ERR, "%s - invalid work or core handle\n", + __func__); + return; + } + + hdev = core->device; + + mutex_lock(&core->lock); + if (list_empty(&core->instances) && + core->state != VIDC_CORE_UNINIT) { + if (core->state > VIDC_CORE_INIT) { + dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n"); + rc = call_hfi_op(hdev, core_release, + hdev->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, + "Failed to release core, id = %d\n", + core->id); + mutex_unlock(&core->lock); + return; + } + } + core->state = VIDC_CORE_UNINIT; + kfree(core->capabilities); + core->capabilities = NULL; + } + mutex_unlock(&core->lock); +} + +int msm_comm_set_color_format(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, int fourcc) +{ + struct hal_uncompressed_format_select hal_fmt = {0}; + enum hal_uncompressed_format format = HAL_UNUSED_COLOR; + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s - invalid param\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + format = msm_comm_get_hal_uncompressed(fourcc); + if (format == HAL_UNUSED_COLOR) { + dprintk(VIDC_ERR, "Using unsupported colorformat %#x\n", + fourcc); + rc = -ENOTSUPP; + goto exit; + } + + hal_fmt.buffer_type = buffer_type; + hal_fmt.format = format; + + rc = call_hfi_op(hdev, session_set_property, inst->session, + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, &hal_fmt); + if (rc) + dprintk(VIDC_ERR, + "Failed to set input color format\n"); + else + dprintk(VIDC_DBG, "Setting uncompressed colorformat to %#x\n", + format); + +exit: + return rc; +} + +int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a) +{ + u32 property_id = 0; + u64 us_per_frame = 0; + void *pdata; + int rc = 0, fps = 0; + struct hal_frame_rate frame_rate; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device || !a) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + property_id = HAL_CONFIG_FRAME_RATE; + + if (a->parm.output.timeperframe.denominator) { + switch (a->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + us_per_frame = a->parm.output.timeperframe.numerator * + (u64)USEC_PER_SEC; + do_div(us_per_frame, + a->parm.output.timeperframe.denominator); + break; + default: + dprintk(VIDC_ERR, + "Scale clocks : Unknown buffer type %d\n", + a->type); + break; + } + } + + if (!us_per_frame) { + dprintk(VIDC_ERR, + "Failed to scale clocks : time between frames is 0\n"); + rc = -EINVAL; + goto exit; + } + + fps = us_per_frame > USEC_PER_SEC ? + 0 : USEC_PER_SEC / (u32)us_per_frame; + + if (fps % 15 == 14 || fps % 24 == 23) + fps = fps + 1; + else if ((fps > 1) && (fps % 24 == 1 || fps % 15 == 1)) + fps = fps - 1; + + if (fps < inst->capability.frame_rate.min || + fps > inst->capability.frame_rate.max) { + dprintk(VIDC_ERR, + "FPS is out of limits : fps = %d Min = %d, Max = %d\n", + fps, inst->capability.frame_rate.min, + inst->capability.frame_rate.max); + rc = -EINVAL; + goto exit; + } + + dprintk(VIDC_PROF, "reported fps changed for %pK: %d->%d\n", + inst, inst->prop.fps, fps); + inst->prop.fps = fps; + if (inst->session_type == MSM_VIDC_ENCODER && + get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) != + HAL_VIDEO_CODEC_TME) { + frame_rate.frame_rate = inst->prop.fps * BIT(16); + frame_rate.buffer_type = HAL_BUFFER_OUTPUT; + pdata = &frame_rate; + rc = call_hfi_op(hdev, session_set_property, + inst->session, property_id, pdata); + if (rc) + dprintk(VIDC_WARN, + "Failed to set frame rate %d\n", rc); + } +exit: + return rc; +} + +void msm_comm_print_inst_info(struct msm_vidc_inst *inst) +{ + struct msm_vidc_buffer *mbuf; + struct internal_buf *buf; + bool is_decode = false; + enum vidc_ports port; + bool is_secure = false; + + if (!inst) { + dprintk(VIDC_ERR, "%s - invalid param %pK\n", + __func__, inst); + return; + } + + is_decode = inst->session_type == MSM_VIDC_DECODER; + port = is_decode ? OUTPUT_PORT : CAPTURE_PORT; + is_secure = inst->flags & VIDC_SECURE; + dprintk(VIDC_ERR, + "%s session, %s, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n", + is_decode ? "Decode" : "Encode", + is_secure ? "Secure" : "Non-Secure", + inst->fmts[port].name, + inst->prop.height[port], inst->prop.width[port], + inst->prop.fps, inst->prop.bitrate, + !inst->bit_depth ? "8" : "10"); + + dprintk(VIDC_ERR, + "---Buffer details for inst: %pK of type: %d---\n", + inst, inst->session_type); + mutex_lock(&inst->registeredbufs.lock); + dprintk(VIDC_ERR, "registered buffer list:\n"); + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) + print_vidc_buffer(VIDC_ERR, "buf", inst, mbuf); + mutex_unlock(&inst->registeredbufs.lock); + + mutex_lock(&inst->scratchbufs.lock); + dprintk(VIDC_ERR, "scratch buffer list:\n"); + list_for_each_entry(buf, &inst->scratchbufs.list, list) + dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n", + buf->buffer_type, buf->smem.device_addr, + buf->smem.size); + mutex_unlock(&inst->scratchbufs.lock); + + mutex_lock(&inst->persistbufs.lock); + dprintk(VIDC_ERR, "persist buffer list:\n"); + list_for_each_entry(buf, &inst->persistbufs.list, list) + dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n", + buf->buffer_type, buf->smem.device_addr, + buf->smem.size); + mutex_unlock(&inst->persistbufs.lock); + + mutex_lock(&inst->outputbufs.lock); + dprintk(VIDC_ERR, "dpb buffer list:\n"); + list_for_each_entry(buf, &inst->outputbufs.list, list) + dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n", + buf->buffer_type, buf->smem.device_addr, + buf->smem.size); + mutex_unlock(&inst->outputbufs.lock); +} + +int msm_comm_session_continue(void *instance) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) + return -EINVAL; + hdev = inst->core->device; + mutex_lock(&inst->lock); + if (inst->state >= MSM_VIDC_RELEASE_RESOURCES_DONE || + inst->state < MSM_VIDC_START_DONE) { + dprintk(VIDC_DBG, + "Inst %pK : Not in valid state to call %s\n", + inst, __func__); + goto sess_continue_fail; + } + if (inst->session_type == MSM_VIDC_DECODER && inst->in_reconfig) { + dprintk(VIDC_DBG, "send session_continue\n"); + rc = call_hfi_op(hdev, session_continue, + (void *)inst->session); + if (rc) { + dprintk(VIDC_ERR, + "failed to send session_continue\n"); + rc = -EINVAL; + goto sess_continue_fail; + } + inst->in_reconfig = false; + inst->prop.height[CAPTURE_PORT] = inst->reconfig_height; + inst->prop.width[CAPTURE_PORT] = inst->reconfig_width; + inst->prop.height[OUTPUT_PORT] = inst->reconfig_height; + inst->prop.width[OUTPUT_PORT] = inst->reconfig_width; + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", + rc); + goto sess_continue_fail; + } + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + dprintk(VIDC_DBG, + "session_continue not supported for encoder"); + } else { + dprintk(VIDC_ERR, + "session_continue called in wrong state for decoder"); + } + +sess_continue_fail: + mutex_unlock(&inst->lock); + return rc; +} + +u32 get_frame_size_nv12(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height); +} + +u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height); +} + +u32 get_frame_size_rgba(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_RGBA8888, width, height); +} + +u32 get_frame_size_nv21(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV21, width, height); +} + +u32 get_frame_size_tp10_ubwc(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height); +} + +u32 get_frame_size_p010(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_P010, width, height); +} + +u32 get_frame_size_nv12_512(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_512, width, height); +} + +void print_vidc_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + struct vb2_buffer *vb2 = NULL; + + if (!(tag & msm_vidc_debug) || !inst || !mbuf) + return; + + vb2 = &mbuf->vvb.vb2_buf; + + if (vb2->num_planes == 1) + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d daddr %x size %d filled %d flags 0x%x ts %lld refcnt %d mflags 0x%x\n", + str, vb2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + vb2->index, vb2->planes[0].m.fd, + vb2->planes[0].data_offset, mbuf->smem[0].device_addr, + vb2->planes[0].length, vb2->planes[0].bytesused, + mbuf->vvb.flags, mbuf->vvb.vb2_buf.timestamp, + mbuf->smem[0].refcount, mbuf->flags); + else + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d daddr %x size %d filled %d flags 0x%x ts %lld refcnt %d mflags 0x%x, extradata: fd %d off %d daddr %x size %d filled %d refcnt %d\n", + str, vb2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + vb2->index, vb2->planes[0].m.fd, + vb2->planes[0].data_offset, mbuf->smem[0].device_addr, + vb2->planes[0].length, vb2->planes[0].bytesused, + mbuf->vvb.flags, mbuf->vvb.vb2_buf.timestamp, + mbuf->smem[0].refcount, mbuf->flags, + vb2->planes[1].m.fd, vb2->planes[1].data_offset, + mbuf->smem[1].device_addr, vb2->planes[1].length, + vb2->planes[1].bytesused, mbuf->smem[1].refcount); +} + +void print_vb2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + if (!(tag & msm_vidc_debug) || !inst || !vb2) + return; + + if (vb2->num_planes == 1) + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d size %d filled %d\n", + str, vb2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + vb2->index, vb2->planes[0].m.fd, + vb2->planes[0].data_offset, vb2->planes[0].length, + vb2->planes[0].bytesused); + else + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d size %d filled %d, extradata: fd %d off %d size %d filled %d\n", + str, vb2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + vb2->index, vb2->planes[0].m.fd, + vb2->planes[0].data_offset, vb2->planes[0].length, + vb2->planes[0].bytesused, vb2->planes[1].m.fd, + vb2->planes[1].data_offset, vb2->planes[1].length, + vb2->planes[1].bytesused); +} + +void print_v4l2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct v4l2_buffer *v4l2) +{ + if (!(tag & msm_vidc_debug) || !inst || !v4l2) + return; + + if (v4l2->length == 1) + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d size %d filled %d\n", + str, v4l2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + v4l2->index, v4l2->m.planes[0].m.fd, + v4l2->m.planes[0].data_offset, + v4l2->m.planes[0].length, + v4l2->m.planes[0].bytesused); + else + dprintk(tag, + "%s: %s: %x : idx %2d fd %d off %d size %d filled %d, extradata: fd %d off %d size %d filled %d\n", + str, v4l2->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + "OUTPUT" : "CAPTURE", hash32_ptr(inst->session), + v4l2->index, v4l2->m.planes[0].m.fd, + v4l2->m.planes[0].data_offset, + v4l2->m.planes[0].length, + v4l2->m.planes[0].bytesused, + v4l2->m.planes[1].m.fd, + v4l2->m.planes[1].data_offset, + v4l2->m.planes[1].length, + v4l2->m.planes[1].bytesused); +} + +bool msm_comm_compare_vb2_plane(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2, u32 i) +{ + struct vb2_buffer *vb; + + if (!inst || !mbuf || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n", + __func__, inst, mbuf, vb2); + return false; + } + + vb = &mbuf->vvb.vb2_buf; + if (vb->planes[i].m.fd == vb2->planes[i].m.fd && + vb->planes[i].length == vb2->planes[i].length) { + return true; + } + + return false; +} + +bool msm_comm_compare_vb2_planes(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2) +{ + int i = 0; + struct vb2_buffer *vb; + + if (!inst || !mbuf || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n", + __func__, inst, mbuf, vb2); + return false; + } + + vb = &mbuf->vvb.vb2_buf; + + if (vb->num_planes != vb2->num_planes) + return false; + + for (i = 0; i < vb->num_planes; i++) { + if (!msm_comm_compare_vb2_plane(inst, mbuf, vb2, i)) + return false; + } + + return true; +} + +bool msm_comm_compare_dma_plane(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, unsigned long *dma_planes, u32 i) +{ + if (!inst || !mbuf || !dma_planes) { + dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n", + __func__, inst, mbuf, dma_planes); + return false; + } + + if ((unsigned long)mbuf->smem[i].dma_buf == dma_planes[i]) + return true; + + return false; +} + +bool msm_comm_compare_dma_planes(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, unsigned long *dma_planes) +{ + int i = 0; + struct vb2_buffer *vb; + + if (!inst || !mbuf || !dma_planes) { + dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n", + __func__, inst, mbuf, dma_planes); + return false; + } + + vb = &mbuf->vvb.vb2_buf; + for (i = 0; i < vb->num_planes; i++) { + if (!msm_comm_compare_dma_plane(inst, mbuf, dma_planes, i)) + return false; + } + + return true; +} + + +bool msm_comm_compare_device_plane(struct msm_vidc_buffer *mbuf, + u32 type, u32 *planes, u32 i) +{ + if (!mbuf || !planes) { + dprintk(VIDC_ERR, "%s: invalid params, %pK %pK\n", + __func__, mbuf, planes); + return false; + } + + if (mbuf->vvb.vb2_buf.type == type && + mbuf->smem[i].device_addr == planes[i]) + return true; + + return false; +} + +bool msm_comm_compare_device_planes(struct msm_vidc_buffer *mbuf, + u32 type, u32 *planes) +{ + int i = 0; + + if (!mbuf || !planes) + return false; + + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + if (!msm_comm_compare_device_plane(mbuf, type, planes, i)) + return false; + } + + return true; +} + +struct msm_vidc_buffer *msm_comm_get_buffer_using_device_planes( + struct msm_vidc_inst *inst, u32 type, u32 *planes) +{ + struct msm_vidc_buffer *mbuf; + bool found = false; + + mutex_lock(&inst->registeredbufs.lock); + found = false; + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (msm_comm_compare_device_planes(mbuf, type, planes)) { + found = true; + break; + } + } + mutex_unlock(&inst->registeredbufs.lock); + if (!found) { + dprintk(VIDC_ERR, + "%s: data_addr %x, extradata_addr %x not found\n", + __func__, planes[0], planes[1]); + mbuf = NULL; + } + + return mbuf; +} + +int msm_comm_flush_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + struct vb2_buffer *vb; + u32 port; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return -EINVAL; + } + + vb = msm_comm_get_vb_using_vidc_buffer(inst, mbuf); + if (!vb) { + print_vidc_buffer(VIDC_ERR, + "vb not found for buf", inst, mbuf); + return -EINVAL; + } + + if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + port = CAPTURE_PORT; + else if (mbuf->vvb.vb2_buf.type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + port = OUTPUT_PORT; + else + return -EINVAL; + + if (inst->bufq[port].vb2_bufq.streaming) { + vb->planes[0].bytesused = 0; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } else { + dprintk(VIDC_ERR, "%s: port %d is not streaming\n", + __func__, port); + } + + return 0; +} + +int msm_comm_qbuf_cache_operations(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0, i; + struct vb2_buffer *vb; + bool skip; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return -EINVAL; + } + vb = &mbuf->vvb.vb2_buf; + + for (i = 0; i < vb->num_planes; i++) { + unsigned long offset, size; + enum smem_cache_ops cache_op; + + skip = true; + if (inst->session_type == MSM_VIDC_DECODER) { + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + offset = vb->planes[i].data_offset; + size = vb->planes[i].bytesused; + cache_op = SMEM_CACHE_CLEAN_INVALIDATE; + } + } else if (vb->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = 0; + size = vb->planes[i].length; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = vb->planes[i].data_offset; + size = vb->planes[i].bytesused; + cache_op = SMEM_CACHE_CLEAN_INVALIDATE; + } + } else if (vb->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + offset = 0; + size = vb->planes[i].length; + if (inst->max_filled_length) + size = inst->max_filled_length; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } + + if (!skip) { + rc = msm_smem_cache_operations(mbuf->smem[i].dma_buf, + cache_op, offset, size); + if (rc) + print_vidc_buffer(VIDC_ERR, + "qbuf cache ops failed", inst, mbuf); + } + } + + return rc; +} + +int msm_comm_dqbuf_cache_operations(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0, i; + struct vb2_buffer *vb; + bool skip; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return -EINVAL; + } + vb = &mbuf->vvb.vb2_buf; + + for (i = 0; i < vb->num_planes; i++) { + unsigned long offset, size; + enum smem_cache_ops cache_op; + + skip = true; + if (inst->session_type == MSM_VIDC_DECODER) { + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* bitstream and extradata */ + /* we do not need cache operations */ + } else if (vb->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = vb->planes[i].data_offset; + size = vb->planes[i].bytesused; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* yuv and extradata */ + /* we do not need cache operations */ + } else if (vb->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + /* + * Include vp8e header bytes as well + * by making offset equal to zero + */ + offset = 0; + size = vb->planes[i].bytesused + + vb->planes[i].data_offset; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } + + if (!skip) { + rc = msm_smem_cache_operations(mbuf->smem[i].dma_buf, + cache_op, offset, size); + if (rc) + print_vidc_buffer(VIDC_ERR, + "dqbuf cache ops failed", inst, mbuf); + } + } + + return rc; +} + +struct msm_vidc_buffer *msm_comm_get_vidc_buffer(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc = 0; + struct vb2_v4l2_buffer *vbuf; + struct vb2_buffer *vb; + unsigned long dma_planes[VB2_MAX_PLANES] = {0}; + struct msm_vidc_buffer *mbuf = NULL; + bool found = false; + int i = 0, planes = 0; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return NULL; + } + + for (planes = 0; planes < vb2->num_planes; planes++) { + /* + * always compare dma_buf addresses which is guaranteed + * to be same across the processes (duplicate fds). + */ + dma_planes[planes] = (unsigned long)msm_smem_get_dma_buf( + vb2->planes[planes].m.fd); + if (!dma_planes[planes]) + goto put_ref; + } + + mutex_lock(&inst->registeredbufs.lock); + /* + * for encoder input, client may queue the same buffer with different + * fd before driver returned old buffer to the client. This buffer + * should be treated as new buffer Search the list with fd so that + * it will be treated as new msm_vidc_buffer. + */ + if (is_encode_session(inst) && vb2->type == + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (msm_comm_compare_vb2_planes(inst, mbuf, vb2)) { + found = true; + break; + } + } + } else { + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (msm_comm_compare_dma_planes(inst, mbuf, + dma_planes)) { + found = true; + break; + } + } + } + + if (!found) { + /* this is new vb2_buffer */ + mbuf = kzalloc(sizeof(struct msm_vidc_buffer), GFP_KERNEL); + if (!mbuf) { + dprintk(VIDC_ERR, "%s: alloc msm_vidc_buffer failed\n", + __func__); + rc = -ENOMEM; + goto exit; + } + kref_init(&mbuf->kref); + } + + /* Initially assume all the buffer are going to be deferred */ + mbuf->flags |= MSM_VIDC_FLAG_DEFERRED; + + vbuf = to_vb2_v4l2_buffer(vb2); + memcpy(&mbuf->vvb, vbuf, sizeof(struct vb2_v4l2_buffer)); + vb = &mbuf->vvb.vb2_buf; + + for (i = 0; i < vb->num_planes; i++) { + mbuf->smem[i].buffer_type = get_hal_buffer_type(vb->type, i); + mbuf->smem[i].fd = vb->planes[i].m.fd; + mbuf->smem[i].offset = vb->planes[i].data_offset; + mbuf->smem[i].size = vb->planes[i].length; + rc = msm_smem_map_dma_buf(inst, &mbuf->smem[i]); + if (rc) { + dprintk(VIDC_ERR, "%s: map failed.\n", __func__); + goto exit; + } + /* increase refcount as we get both fbd and rbr */ + rc = msm_smem_map_dma_buf(inst, &mbuf->smem[i]); + if (rc) { + dprintk(VIDC_ERR, "%s: map failed..\n", __func__); + goto exit; + } + } + /* dma cache operations need to be performed after dma_map */ + msm_comm_qbuf_cache_operations(inst, mbuf); + + /* special handling for decoder */ + if (inst->session_type == MSM_VIDC_DECODER) { + if (found) { + rc = -EEXIST; + } else { + bool found_plane0 = false; + struct msm_vidc_buffer *temp; + /* + * client might have queued same plane[0] but different + * plane[1] search plane[0] and if found don't queue the + * buffer, the buffer will be queued when rbr event + * arrived. + */ + list_for_each_entry(temp, &inst->registeredbufs.list, + list) { + if (msm_comm_compare_dma_plane(inst, temp, + dma_planes, 0)) { + found_plane0 = true; + break; + } + } + if (found_plane0) + rc = -EEXIST; + } + if (rc == -EEXIST) { + print_vidc_buffer(VIDC_DBG, + "existing qbuf", inst, mbuf); + /* enable RBR pending */ + mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING; + } + } + + /* add the new buffer to list */ + if (!found) + list_add_tail(&mbuf->list, &inst->registeredbufs.list); + +exit: + if (rc == -EEXIST) { + print_vidc_buffer(VIDC_DBG, "qbuf upon rbr", inst, mbuf); + } else if (rc) { + dprintk(VIDC_ERR, "%s: rc %d\n", __func__, rc); + msm_comm_unmap_vidc_buffer(inst, mbuf); + if (!found) + kref_put_mbuf(mbuf); + } + mutex_unlock(&inst->registeredbufs.lock); +put_ref: + while (planes) + msm_smem_put_dma_buf((struct dma_buf *)dma_planes[--planes]); + + return rc ? ((rc == -EEXIST && !inst->batch.enable) ? + ERR_PTR(rc) : mbuf) : mbuf; +} + +void msm_comm_put_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + struct msm_vidc_buffer *temp; + bool found = false; + int i = 0; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return; + } + + mutex_lock(&inst->registeredbufs.lock); + /* check if mbuf was not removed by any chance */ + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (msm_comm_compare_vb2_planes(inst, mbuf, + &temp->vvb.vb2_buf)) { + found = true; + break; + } + } + if (!found) { + print_vidc_buffer(VIDC_ERR, "buf was removed", inst, mbuf); + goto unlock; + } + + print_vidc_buffer(VIDC_DBG, "dqbuf", inst, mbuf); + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "dqbuf: unmap failed.", inst, mbuf); + + if (!(mbuf->vvb.flags & V4L2_QCOM_BUF_FLAG_READONLY)) { + /* rbr won't come for this buffer */ + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "dqbuf: unmap failed..", inst, mbuf); + } else { + /* RBR event expected */ + mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING; + } + } + /* + * remove the entry if plane[0].refcount is zero else + * don't remove as client queued same buffer that's why + * plane[0].refcount is not zero + */ + if (!mbuf->smem[0].refcount) { + list_del(&mbuf->list); + kref_put_mbuf(mbuf); + } +unlock: + mutex_unlock(&inst->registeredbufs.lock); +} + +void handle_release_buffer_reference(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + struct msm_vidc_buffer *temp; + bool found = false; + int i = 0; + u32 planes[VIDEO_MAX_PLANES] = {0}; + + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + mutex_lock(&inst->registeredbufs.lock); + found = false; + /* check if mbuf was not removed by any chance */ + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (msm_comm_compare_vb2_planes(inst, mbuf, + &temp->vvb.vb2_buf)) { + found = true; + break; + } + } + if (found) { + /* save device_addr */ + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) + planes[i] = mbuf->smem[i].device_addr; + + /* send RBR event to client */ + msm_vidc_queue_rbr_event(inst, + mbuf->vvb.vb2_buf.planes[0].m.fd, + mbuf->vvb.vb2_buf.planes[0].data_offset, + mbuf->output_tag); + + /* clear RBR_PENDING flag */ + mbuf->flags &= ~MSM_VIDC_FLAG_RBR_PENDING; + + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "rbr unmap failed.", inst, mbuf); + } + /* refcount is not zero if client queued the same buffer */ + if (!mbuf->smem[0].refcount) { + list_del(&mbuf->list); + kref_put_mbuf(mbuf); + mbuf = NULL; + } + } else { + print_vidc_buffer(VIDC_ERR, "mbuf not found", inst, mbuf); + goto unlock; + } + + /* + * 1. client might have pushed same planes in which case mbuf will be + * same and refcounts are positive and buffer wouldn't have been + * removed from the registeredbufs list. + * 2. client might have pushed same planes[0] but different planes[1] + * in which case mbuf will be different. + * 3. in either case we can search mbuf->smem[0].device_addr in the list + * and if found queue it to video hw (if not flushing). + */ + found = false; + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (msm_comm_compare_device_plane(temp, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, planes, 0)) { + mbuf = temp; + found = true; + break; + } + } + if (!found) + goto unlock; + + /* buffer found means client queued the buffer already */ + if (inst->in_reconfig || inst->out_flush) { + print_vidc_buffer(VIDC_DBG, "rbr flush buf", inst, mbuf); + msm_comm_flush_vidc_buffer(inst, mbuf); + msm_comm_unmap_vidc_buffer(inst, mbuf); + /* remove from list */ + list_del(&mbuf->list); + kref_put_mbuf(mbuf); + + /* don't queue the buffer */ + found = false; + } + /* clear required flags as the buffer is going to be queued */ + if (found) { + mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED; + mbuf->flags &= ~MSM_VIDC_FLAG_RBR_PENDING; + } + +unlock: + mutex_unlock(&inst->registeredbufs.lock); + + if (found) { + rc = msm_comm_qbuf_in_rbr(inst, mbuf); + if (rc) + print_vidc_buffer(VIDC_ERR, + "rbr qbuf failed", inst, mbuf); + } + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); +} + +int msm_comm_unmap_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0, i; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, mbuf); + return -EINVAL; + } + if (mbuf->vvb.vb2_buf.num_planes > VIDEO_MAX_PLANES) { + dprintk(VIDC_ERR, "%s: invalid num_planes %d\n", __func__, + mbuf->vvb.vb2_buf.num_planes); + return -EINVAL; + } + + for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) { + u32 refcount = mbuf->smem[i].refcount; + + while (refcount) { + if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i])) + print_vidc_buffer(VIDC_ERR, + "unmap failed for buf", inst, mbuf); + refcount--; + } + } + + return rc; +} + +static void kref_free_mbuf(struct kref *kref) +{ + struct msm_vidc_buffer *mbuf = container_of(kref, + struct msm_vidc_buffer, kref); + + kfree(mbuf); +} + +void kref_put_mbuf(struct msm_vidc_buffer *mbuf) +{ + if (!mbuf) + return; + + kref_put(&mbuf->kref, kref_free_mbuf); +} + +bool kref_get_mbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf) +{ + struct msm_vidc_buffer *temp; + bool matches = false; + bool ret = false; + + if (!inst || !mbuf) + return false; + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (temp == mbuf) { + matches = true; + break; + } + } + ret = (matches && kref_get_unless_zero(&mbuf->kref)) ? true : false; + mutex_unlock(&inst->registeredbufs.lock); + + return ret; +} + +void msm_comm_free_buffer_tags(struct msm_vidc_inst *inst) +{ + struct vidc_tag_data *temp, *next; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params %pK\n", + __func__, inst); + return; + } + + mutex_lock(&inst->buffer_tags.lock); + list_for_each_entry_safe(temp, next, &inst->buffer_tags.list, list) { + list_del(&temp->list); + kfree(temp); + } + INIT_LIST_HEAD(&inst->buffer_tags.list); + mutex_unlock(&inst->buffer_tags.lock); +} + +void msm_comm_store_tags(struct msm_vidc_inst *inst, + struct vidc_tag_data *tag_data) +{ + + struct vidc_tag_data *temp, *next; + struct vidc_tag_data *pdata = NULL; + bool found = false; + + if (!inst || !tag_data) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, tag_data); + return; + } + + + mutex_lock(&inst->buffer_tags.lock); + list_for_each_entry_safe(temp, next, &inst->buffer_tags.list, list) { + if (temp->index == tag_data->index && + temp->type == tag_data->type) { + temp->input_tag = tag_data->input_tag; + temp->output_tag = tag_data->output_tag; + found = true; + break; + } + } + + if (!found) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dprintk(VIDC_WARN, "%s: malloc failure.\n", __func__); + goto exit; + } + pdata->input_tag = tag_data->input_tag; + pdata->output_tag = tag_data->output_tag; + pdata->index = tag_data->index; + pdata->type = tag_data->type; + list_add_tail(&pdata->list, &inst->buffer_tags.list); + } + +exit: + mutex_unlock(&inst->buffer_tags.lock); +} + +bool msm_comm_fetch_tags(struct msm_vidc_inst *inst, + struct vidc_tag_data *tag_data) +{ + struct vidc_tag_data *temp, *next; + + if (!inst || !tag_data) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n", + __func__, inst, tag_data); + return false; + } + mutex_lock(&inst->buffer_tags.lock); + list_for_each_entry_safe(temp, next, &inst->buffer_tags.list, list) { + if (temp->index == tag_data->index && + temp->type == tag_data->type) { + tag_data->input_tag = temp->input_tag; + tag_data->output_tag = temp->output_tag; + mutex_unlock(&inst->buffer_tags.lock); + return true; + } + } + mutex_unlock(&inst->buffer_tags.lock); + + return false; +} + +void msm_comm_store_mark_data(struct msm_vidc_list *data_list, + u32 index, u32 mark_data, u32 mark_target) +{ + struct msm_vidc_buf_data *pdata = NULL; + bool found = false; + + if (!data_list) { + dprintk(VIDC_ERR, "%s: invalid params %pK\n", + __func__, data_list); + return; + } + + mutex_lock(&data_list->lock); + list_for_each_entry(pdata, &data_list->list, list) { + if (pdata->index == index) { + pdata->mark_data = mark_data; + pdata->mark_target = mark_target; + found = true; + break; + } + } + + if (!found) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dprintk(VIDC_WARN, "%s: malloc failure.\n", __func__); + goto exit; + } + pdata->index = index; + pdata->mark_data = mark_data; + pdata->mark_target = mark_target; + list_add_tail(&pdata->list, &data_list->list); + } + +exit: + mutex_unlock(&data_list->lock); +} + +void msm_comm_fetch_mark_data(struct msm_vidc_list *data_list, + u32 index, u32 *mark_data, u32 *mark_target) +{ + struct msm_vidc_buf_data *pdata = NULL; + + if (!data_list || !mark_data || !mark_target) { + dprintk(VIDC_ERR, "%s: invalid params %pK %pK %pK\n", + __func__, data_list, mark_data, mark_target); + return; + } + + *mark_data = *mark_target = 0; + mutex_lock(&data_list->lock); + list_for_each_entry(pdata, &data_list->list, list) { + if (pdata->index == index) { + *mark_data = pdata->mark_data; + *mark_target = pdata->mark_target; + /* clear after fetch */ + pdata->mark_data = pdata->mark_target = 0; + break; + } + } + mutex_unlock(&data_list->lock); +} + +int msm_comm_release_mark_data(struct msm_vidc_inst *inst) +{ + struct msm_vidc_buf_data *pdata, *next; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params %pK\n", + __func__, inst); + return -EINVAL; + } + + mutex_lock(&inst->etb_data.lock); + list_for_each_entry_safe(pdata, next, &inst->etb_data.list, list) { + list_del(&pdata->list); + kfree(pdata); + } + mutex_unlock(&inst->etb_data.lock); + + mutex_lock(&inst->fbd_data.lock); + list_for_each_entry_safe(pdata, next, &inst->fbd_data.list, list) { + list_del(&pdata->list); + kfree(pdata); + } + mutex_unlock(&inst->fbd_data.lock); + + return 0; +} + +int msm_comm_set_color_format_constraints(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, + struct msm_vidc_format_constraint *pix_constraint) +{ + struct hal_uncompressed_plane_actual_constraints_info + *pconstraint = NULL; + u32 num_planes = 2; + u32 size = 0; + int rc = 0; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s - invalid param\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + size = sizeof(buffer_type) + + sizeof(u32) + + num_planes + * sizeof(struct hal_uncompressed_plane_constraints); + + pconstraint = kzalloc(size, GFP_KERNEL); + if (!pconstraint) { + dprintk(VIDC_ERR, "No memory cannot alloc constrain\n"); + rc = -ENOMEM; + goto exit; + } + + pconstraint->buffer_type = buffer_type; + pconstraint->num_planes = pix_constraint->num_planes; + //set Y plan constraints + dprintk(VIDC_INFO, "Set Y plan constraints.\n"); + pconstraint->rg_plane_format[0].stride_multiples = + pix_constraint->y_stride_multiples; + pconstraint->rg_plane_format[0].max_stride = + pix_constraint->y_max_stride; + pconstraint->rg_plane_format[0].min_plane_buffer_height_multiple = + pix_constraint->y_min_plane_buffer_height_multiple; + pconstraint->rg_plane_format[0].buffer_alignment = + pix_constraint->y_buffer_alignment; + + //set UV plan constraints + dprintk(VIDC_INFO, "Set UV plan constraints.\n"); + pconstraint->rg_plane_format[1].stride_multiples = + pix_constraint->uv_stride_multiples; + pconstraint->rg_plane_format[1].max_stride = + pix_constraint->uv_max_stride; + pconstraint->rg_plane_format[1].min_plane_buffer_height_multiple = + pix_constraint->uv_min_plane_buffer_height_multiple; + pconstraint->rg_plane_format[1].buffer_alignment = + pix_constraint->uv_buffer_alignment; + + rc = call_hfi_op(hdev, + session_set_property, + inst->session, + HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO, + pconstraint); + if (rc) + dprintk(VIDC_ERR, + "Failed to set input color format constraint\n"); + else + dprintk(VIDC_DBG, "Set color format constraint success\n"); + +exit: + if (pconstraint) + kfree(pconstraint); + return rc; +} + +bool msm_comm_check_for_inst_overload(struct msm_vidc_core *core) +{ + u32 instance_count = 0; + u32 secure_instance_count = 0; + struct msm_vidc_inst *inst = NULL; + bool overload = false; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + instance_count++; + if (inst->flags & VIDC_SECURE) + secure_instance_count++; + } + mutex_unlock(&core->lock); + + /* Instance count includes current instance as well. */ + + if ((instance_count > core->resources.max_inst_count) || + (secure_instance_count > core->resources.max_secure_inst_count)) + overload = true; + return overload; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h new file mode 100644 index 000000000000..2119e06d7309 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -0,0 +1,264 @@ +/* Copyright (c) 2012-2019, 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_VIDC_COMMON_H_ +#define _MSM_VIDC_COMMON_H_ +#include "msm_vidc_internal.h" + +#define MAX_DEC_BATCH_SIZE 6 +#define MAX_DEC_BATCH_WIDTH 1920 +#define MAX_DEC_BATCH_HEIGHT 1088 +#define SKIP_BATCH_WINDOW 100 +#define MIN_FRAME_QUALITY 0 +#define MAX_FRAME_QUALITY 100 +#define DEFAULT_FRAME_QUALITY 80 +#define FRAME_QUALITY_STEP 1 +#define HEIC_GRID_DIMENSION 512 +#define CBR_MB_LIMIT (((1280+15)/16)*((720+15)/16)*30) +#define CBR_VFR_MB_LIMIT (((1280+15)/16)*((720+15)/16)*30) + +struct vb2_buf_entry { + struct list_head list; + struct vb2_buffer *vb; +}; + +struct getprop_buf { + struct list_head list; + void *data; +}; + +extern const char *const mpeg_video_vidc_extradata[]; + +enum load_calc_quirks { + LOAD_CALC_NO_QUIRKS = 0, + LOAD_CALC_IGNORE_TURBO_LOAD = 1 << 0, + LOAD_CALC_IGNORE_THUMBNAIL_LOAD = 1 << 1, + LOAD_CALC_IGNORE_NON_REALTIME_LOAD = 1 << 2, +}; + +static inline bool is_turbo_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_TURBO); +} + +static inline bool is_thumbnail_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_THUMBNAIL); +} + +static inline bool is_low_power_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_LOW_POWER); +} + +static inline bool is_realtime_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_REALTIME); +} + +static inline bool is_decode_session(struct msm_vidc_inst *inst) +{ + return inst->session_type == MSM_VIDC_DECODER; +} + +static inline bool is_encode_session(struct msm_vidc_inst *inst) +{ + return inst->session_type == MSM_VIDC_ENCODER; +} + +static inline bool is_primary_output_mode(struct msm_vidc_inst *inst) +{ + return inst->stream_output_mode == HAL_VIDEO_DECODER_PRIMARY; +} + +static inline bool is_secondary_output_mode(struct msm_vidc_inst *inst) +{ + return inst->stream_output_mode == HAL_VIDEO_DECODER_SECONDARY; +} + +static inline int msm_comm_g_ctrl(struct msm_vidc_inst *inst, + struct v4l2_control *ctrl) +{ + return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); +} + +static inline int msm_comm_s_ctrl(struct msm_vidc_inst *inst, + struct v4l2_control *ctrl) +{ + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); +} +bool is_batching_allowed(struct msm_vidc_inst *inst); +enum hal_buffer get_hal_buffer_type(unsigned int type, + unsigned int plane_num); +void put_inst(struct msm_vidc_inst *inst); +struct msm_vidc_inst *get_inst(struct msm_vidc_core *core, + void *session_id); +void change_inst_state(struct msm_vidc_inst *inst, enum instance_state state); +struct msm_vidc_core *get_vidc_core(int core_id); +const struct msm_vidc_format *msm_comm_get_pixel_fmt_index( + const struct msm_vidc_format fmt[], int size, int index, int fmt_type); +struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc( + struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type); +struct msm_vidc_format_constraint *msm_comm_get_pixel_fmt_constraints( + struct msm_vidc_format_constraint fmt[], int size, int fourcc); +int msm_comm_set_color_format_constraints(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, + struct msm_vidc_format_constraint *pix_constraint); +struct buf_queue *msm_comm_get_vb2q( + struct msm_vidc_inst *inst, enum v4l2_buf_type type); +int msm_comm_try_state(struct msm_vidc_inst *inst, int state); +int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst); +int msm_comm_try_set_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, void *pdata); +int msm_comm_try_get_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, union hal_get_property *hprop); +int msm_comm_set_recon_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_buffer_count(struct msm_vidc_inst *inst, + int host_count, int act_count, enum hal_buffer type); +int msm_comm_set_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_qbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf); +int msm_comm_qbufs(struct msm_vidc_inst *inst); +void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst); +int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags); +int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst, + bool check_for_reuse); +int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst); +int msm_comm_release_recon_buffers(struct msm_vidc_inst *inst); +void msm_comm_release_eos_buffers(struct msm_vidc_inst *inst); +int msm_comm_release_output_buffers(struct msm_vidc_inst *inst, + bool force_release); +void msm_comm_validate_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_force_cleanup(struct msm_vidc_inst *inst); +int msm_comm_suspend(int core_id); +enum hal_extradata_id msm_comm_get_hal_extradata_index( + enum v4l2_mpeg_vidc_extradata index); +int msm_comm_reset_bufreqs(struct msm_vidc_inst *inst, + enum hal_buffer buf_type); +int msm_comm_copy_bufreqs(struct msm_vidc_inst *inst, + enum hal_buffer src_type, enum hal_buffer dst_type); +struct hal_buffer_requirements *get_buff_req_buffer( + struct msm_vidc_inst *inst, u32 buffer_type); +#define IS_PRIV_CTRL(idx) (\ + (V4L2_CTRL_ID2WHICH(idx) == V4L2_CTRL_CLASS_MPEG) && \ + V4L2_CTRL_DRIVER_PRIV(idx)) +void msm_comm_session_clean(struct msm_vidc_inst *inst); +int msm_comm_kill_session(struct msm_vidc_inst *inst); +void msm_comm_generate_session_error(struct msm_vidc_inst *inst); +void msm_comm_generate_sys_error(struct msm_vidc_inst *inst); +enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst); +int msm_comm_set_stream_output_mode(struct msm_vidc_inst *inst, + enum multi_stream mode); +enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst); +int msm_comm_smem_alloc(struct msm_vidc_inst *inst, size_t size, u32 align, + u32 flags, enum hal_buffer buffer_type, int map_kernel, + struct msm_smem *smem); +void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *smem); +int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst, + struct msm_smem *mem, enum smem_cache_ops cache_ops); +enum hal_video_codec get_hal_codec(int fourcc); +enum hal_domain get_hal_domain(int session_type); +int msm_comm_check_core_init(struct msm_vidc_core *core); +int msm_comm_get_inst_load(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks); +int msm_comm_get_inst_load_per_core(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks); +int msm_comm_get_load(struct msm_vidc_core *core, + enum session_type type, enum load_calc_quirks quirks); +int msm_comm_set_color_format(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, int fourcc); +int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl); +int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl); +int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id); +int msm_comm_ctrl_init(struct msm_vidc_inst *inst, + struct msm_vidc_ctrl *drv_ctrls, u32 num_ctrls, + const struct v4l2_ctrl_ops *ctrl_ops); +int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst); +void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst); +int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); +bool msm_comm_turbo_session(struct msm_vidc_inst *inst); +void msm_comm_print_inst_info(struct msm_vidc_inst *inst); +int msm_comm_v4l2_to_hal(int id, int value); +int msm_comm_hal_to_v4l2(int id, int value); +int msm_comm_get_v4l2_profile(int fourcc, int profile); +int msm_comm_get_v4l2_level(int fourcc, int level); +int msm_comm_session_continue(void *instance); +int msm_vidc_send_pending_eos_buffers(struct msm_vidc_inst *inst); +enum hal_uncompressed_format msm_comm_get_hal_uncompressed(int fourcc); +u32 get_frame_size_nv12(int plane, u32 height, u32 width); +u32 get_frame_size_nv12_512(int plane, u32 height, u32 width); +u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width); +u32 get_frame_size_rgba(int plane, u32 height, u32 width); +u32 get_frame_size_nv21(int plane, u32 height, u32 width); +u32 get_frame_size_tp10_ubwc(int plane, u32 height, u32 width); +u32 get_frame_size_p010(int plane, u32 height, u32 width); +struct vb2_buffer *msm_comm_get_vb_using_vidc_buffer( + struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf); +struct msm_vidc_buffer *msm_comm_get_buffer_using_device_planes( + struct msm_vidc_inst *inst, u32 type, u32 *planes); +struct msm_vidc_buffer *msm_comm_get_vidc_buffer(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2); +void msm_comm_put_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +void handle_release_buffer_reference(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_vb2_buffer_done(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_flush_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_unmap_vidc_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +bool msm_comm_compare_dma_plane(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, unsigned long *dma_planes, u32 i); +bool msm_comm_compare_dma_planes(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, unsigned long *dma_planes); +bool msm_comm_compare_vb2_plane(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2, u32 i); +bool msm_comm_compare_vb2_planes(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2); +bool msm_comm_compare_device_plane(struct msm_vidc_buffer *mbuf, + u32 type, u32 *planes, u32 i); +bool msm_comm_compare_device_planes(struct msm_vidc_buffer *mbuf, + u32 type, u32 *planes); +void msm_comm_store_tags(struct msm_vidc_inst *inst, + struct vidc_tag_data *tag_data); +bool msm_comm_fetch_tags(struct msm_vidc_inst *inst, + struct vidc_tag_data *tag_data); +void msm_comm_free_buffer_tags(struct msm_vidc_inst *inst); +int msm_comm_qbuf_cache_operations(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_dqbuf_cache_operations(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +void print_vidc_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +void print_vb2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct vb2_buffer *vb2); +void print_v4l2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst, + struct v4l2_buffer *v4l2); +void kref_put_mbuf(struct msm_vidc_buffer *mbuf); +bool kref_get_mbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf); +void msm_comm_store_mark_data(struct msm_vidc_list *data_list, + u32 index, u32 mark_data, u32 mark_target); +void msm_comm_fetch_mark_data(struct msm_vidc_list *data_list, + u32 index, u32 *mark_data, u32 *mark_target); +int msm_comm_release_mark_data(struct msm_vidc_inst *inst); +int msm_comm_qbufs_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_qbuf_decode_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); +int msm_comm_num_queued_bufs(struct msm_vidc_inst *inst, u32 type); +bool msm_comm_check_for_inst_overload(struct msm_vidc_core *core); +void msm_vidc_batch_handler(struct work_struct *work); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c new file mode 100644 index 000000000000..8d81a1e70288 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -0,0 +1,605 @@ +/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define CREATE_TRACE_POINTS +#define MAX_SSR_STRING_LEN 10 +#define MAX_DEBUG_LEVEL_STRING_LEN 15 +#include "msm_vidc_debug.h" +#include "vidc_hfi_api.h" + +int msm_vidc_debug = VIDC_ERR | VIDC_WARN | VIDC_FW; +EXPORT_SYMBOL(msm_vidc_debug); + +int msm_vidc_debug_out = VIDC_OUT_PRINTK; +EXPORT_SYMBOL(msm_vidc_debug_out); + +/* 0x18 = HFI_DEBUG_MSG_FATAL | HFI_DEBUG_MSG_ERROR */ +int msm_vidc_fw_debug = 0x18; +int msm_vidc_fw_debug_mode = 1; +int msm_vidc_fw_low_power_mode = 1; +bool msm_vidc_fw_coverage = !true; +bool msm_vidc_thermal_mitigation_disabled = !true; +int msm_vidc_clock_voting = !1; +bool msm_vidc_syscache_disable = !true; + +#define MAX_DBG_BUF_SIZE 4096 + +#define DYNAMIC_BUF_OWNER(__binfo) ({ \ + atomic_read(&__binfo->ref_count) >= 2 ? "video driver" : "firmware";\ +}) + +struct core_inst_pair { + struct msm_vidc_core *core; + struct msm_vidc_inst *inst; +}; + +static int core_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static u32 write_str(char *buffer, + size_t size, const char *fmt, ...) +{ + va_list args; + u32 len; + + va_start(args, fmt); + len = vscnprintf(buffer, size, fmt, args); + va_end(args); + return len; +} + +static ssize_t core_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_vidc_core *core = file->private_data; + struct hfi_device *hdev; + struct hal_fw_info fw_info = { {0} }; + char *dbuf, *cur, *end; + int i = 0, rc = 0; + ssize_t len = 0; + + if (!core || !core->device) { + dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); + return 0; + } + + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + return -ENOMEM; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; + hdev = core->device; + + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "CORE %d: %pK\n", core->id, core); + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "Core state: %d\n", core->state); + rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info); + if (rc) { + dprintk(VIDC_WARN, "Failed to read FW info\n"); + goto err_fw_info; + } + + cur += write_str(cur, end - cur, + "FW version : %s\n", &fw_info.version); + cur += write_str(cur, end - cur, + "base addr: 0x%x\n", fw_info.base_addr); + cur += write_str(cur, end - cur, + "register_base: 0x%x\n", fw_info.register_base); + cur += write_str(cur, end - cur, + "register_size: %u\n", fw_info.register_size); + cur += write_str(cur, end - cur, "irq: %u\n", fw_info.irq); + +err_fw_info: + for (i = SYS_MSG_START; i < SYS_MSG_END; i++) { + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, + completion_done(&core->completions[SYS_MSG_INDEX(i)]) ? + "pending" : "done"); + } + len = simple_read_from_buffer(buf, count, ppos, + dbuf, cur - dbuf); + + kfree(dbuf); + return len; +} + +static const struct file_operations core_info_fops = { + .open = core_info_open, + .read = core_info_read, +}; + +static int trigger_ssr_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long ssr_trigger_val = 0; + int rc = 0; + struct msm_vidc_core *core = filp->private_data; + size_t size = MAX_SSR_STRING_LEN; + char kbuf[MAX_SSR_STRING_LEN + 1] = {0}; + + if (!buf) + return -EINVAL; + + if (!count) + goto exit; + + if (count < size) + size = count; + + if (copy_from_user(kbuf, buf, size)) { + dprintk(VIDC_WARN, "%s User memory fault\n", __func__); + rc = -EFAULT; + goto exit; + } + + rc = kstrtoul(kbuf, 0, &ssr_trigger_val); + if (rc) { + dprintk(VIDC_WARN, "returning error err %d\n", rc); + rc = -EINVAL; + } else { + msm_vidc_trigger_ssr(core, ssr_trigger_val); + rc = count; + } +exit: + return rc; +} + +static const struct file_operations ssr_fops = { + .open = trigger_ssr_open, + .write = trigger_ssr_write, +}; + +static ssize_t debug_level_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + int rc = 0; + struct msm_vidc_core *core = filp->private_data; + char kbuf[MAX_DEBUG_LEVEL_STRING_LEN] = {0}; + + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(kbuf) || count == 0) { + dprintk(VIDC_ERR, "returning error - pos %lld, count %ld\n", + *ppos, count); + rc = -EINVAL; + } + + rc = simple_write_to_buffer(kbuf, sizeof(kbuf) - 1, ppos, buf, count); + if (rc < 0) { + dprintk(VIDC_ERR, "%s User memory fault\n", __func__); + rc = -EFAULT; + goto exit; + } + + rc = kstrtoint(kbuf, 0, &msm_vidc_debug); + if (rc) { + dprintk(VIDC_ERR, "returning error err %d\n", rc); + rc = -EINVAL; + } else { + core->resources.msm_vidc_hw_rsp_timeout = + (msm_vidc_debug > (VIDC_ERR | VIDC_WARN)) ? 1500 : 1000; + rc = count; + dprintk(VIDC_DBG, "debug timeout updated to - %d\n", + core->resources.msm_vidc_hw_rsp_timeout); + } +exit: + return rc; +} + +static ssize_t debug_level_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + size_t len; + char kbuf[MAX_DEBUG_LEVEL_STRING_LEN]; + + len = scnprintf(kbuf, sizeof(kbuf), "0x%08x\n", msm_vidc_debug); + return simple_read_from_buffer(buf, count, ppos, kbuf, len); +} + +static const struct file_operations debug_level_fops = { + .open = simple_open, + .write = debug_level_write, + .read = debug_level_read, +}; + +struct dentry *msm_vidc_debugfs_init_drv(void) +{ + bool ok = false; + struct dentry *dir = NULL; + + dir = debugfs_create_dir("msm_vidc", NULL); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + goto failed_create_dir; + } + +#define __debugfs_create(__type, __name, __value) ({ \ + struct dentry *f = debugfs_create_##__type(__name, 0644, \ + dir, __value); \ + if (IS_ERR_OR_NULL(f)) { \ + dprintk(VIDC_ERR, "Failed creating debugfs file '%pd/%s'\n", \ + dir, __name); \ + f = NULL; \ + } \ + f; \ +}) + + ok = + __debugfs_create(x32, "fw_level", &msm_vidc_fw_debug) && + __debugfs_create(u32, "fw_debug_mode", &msm_vidc_fw_debug_mode) && + __debugfs_create(bool, "fw_coverage", &msm_vidc_fw_coverage) && + __debugfs_create(u32, "fw_low_power_mode", + &msm_vidc_fw_low_power_mode) && + __debugfs_create(u32, "debug_output", &msm_vidc_debug_out) && + __debugfs_create(bool, "disable_thermal_mitigation", + &msm_vidc_thermal_mitigation_disabled) && + __debugfs_create(u32, "core_clock_voting", + &msm_vidc_clock_voting) && + __debugfs_create(bool, "disable_video_syscache", + &msm_vidc_syscache_disable); + +#undef __debugfs_create + + if (!ok) + goto failed_create_dir; + + return dir; + +failed_create_dir: + if (dir) + debugfs_remove_recursive(vidc_driver->debugfs_root); + + return NULL; +} + +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, + struct dentry *parent) +{ + struct dentry *dir = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + + if (!core) { + dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); + goto failed_create_dir; + } + + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id); + dir = debugfs_create_dir(debugfs_name, parent); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); + goto failed_create_dir; + } + if (!debugfs_create_file("info", 0444, dir, core, &core_info_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } + if (!debugfs_create_file("trigger_ssr", 0200, + dir, core, &ssr_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } + if (!debugfs_create_file("debug_level", 0644, + parent, core, &debug_level_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } +failed_create_dir: + return dir; +} + +static int inst_info_open(struct inode *inode, struct file *file) +{ + dprintk(VIDC_INFO, "Open inode ptr: %pK\n", inode->i_private); + file->private_data = inode->i_private; + return 0; +} + +static int publish_unreleased_reference(struct msm_vidc_inst *inst, + char **dbuf, char *end) +{ + struct msm_vidc_buffer *temp = NULL; + char *cur = *dbuf; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid param\n", __func__); + return -EINVAL; + } + + if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) { + cur += write_str(cur, end - cur, "Pending buffer references\n"); + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + struct vb2_buffer *vb2 = &temp->vvb.vb2_buf; + + if (vb2->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + cur += write_str(cur, end - cur, + "\tbuffer: %#x fd[0] = %d size %d refcount = %d\n", + temp->smem[0].device_addr, + vb2->planes[0].m.fd, + vb2->planes[0].length, + temp->smem[0].refcount); + } + } + mutex_unlock(&inst->registeredbufs.lock); + } + + *dbuf = cur; + return 0; +} + +static void put_inst_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + + msm_vidc_destroy(inst); +} + +static ssize_t inst_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct core_inst_pair *idata = file->private_data; + struct msm_vidc_core *core; + struct msm_vidc_inst *inst, *temp = NULL; + char *dbuf, *cur, *end; + int i, j; + ssize_t len = 0; + + if (!idata || !idata->core || !idata->inst) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return 0; + } + + core = idata->core; + inst = idata->inst; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp == inst) + break; + } + inst = ((temp == inst) && kref_get_unless_zero(&inst->kref)) ? + inst : NULL; + mutex_unlock(&core->lock); + + if (!inst) { + dprintk(VIDC_ERR, "%s: Instance has become obsolete", __func__); + return 0; + } + + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + len = -ENOMEM; + goto failed_alloc; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; + + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst, + inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder"); + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "core: %pK\n", inst->core); + cur += write_str(cur, end - cur, "height: %d\n", + inst->prop.height[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "width: %d\n", + inst->prop.width[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "fps: %d\n", inst->prop.fps); + cur += write_str(cur, end - cur, "state: %d\n", inst->state); + cur += write_str(cur, end - cur, "secure: %d\n", + !!(inst->flags & VIDC_SECURE)); + cur += write_str(cur, end - cur, "-----------Formats-------------\n"); + for (i = 0; i < MAX_PORT_NUM; i++) { + cur += write_str(cur, end - cur, "capability: %s\n", + i == OUTPUT_PORT ? "Output" : "Capture"); + cur += write_str(cur, end - cur, "name : %s\n", + inst->fmts[i].name); + cur += write_str(cur, end - cur, "planes : %d\n", + inst->bufq[i].num_planes); + cur += write_str(cur, end - cur, + "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ? + "Output" : "Capture"); + switch (inst->buffer_mode_set[i]) { + case HAL_BUFFER_MODE_STATIC: + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "static"); + break; + case HAL_BUFFER_MODE_DYNAMIC: + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "dynamic"); + break; + default: + cur += write_str(cur, end - cur, + "buffer mode : unsupported\n"); + } + + cur += write_str(cur, end - cur, "count: %u\n", + inst->bufq[i].vb2_bufq.num_buffers); + + for (j = 0; j < inst->bufq[i].num_planes; j++) + cur += write_str(cur, end - cur, + "size for plane %d: %u\n", + j, inst->bufq[i].plane_sizes[j]); + + if (i < MAX_PORT_NUM - 1) + cur += write_str(cur, end - cur, "\n"); + } + cur += write_str(cur, end - cur, "-------------------------------\n"); + for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) { + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, + completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ? + "pending" : "done"); + } + cur += write_str(cur, end - cur, "ETB Count: %d\n", inst->count.etb); + cur += write_str(cur, end - cur, "EBD Count: %d\n", inst->count.ebd); + cur += write_str(cur, end - cur, "FTB Count: %d\n", inst->count.ftb); + cur += write_str(cur, end - cur, "FBD Count: %d\n", inst->count.fbd); + + publish_unreleased_reference(inst, &cur, end); + len = simple_read_from_buffer(buf, count, ppos, + dbuf, cur - dbuf); + + kfree(dbuf); +failed_alloc: + kref_put(&inst->kref, put_inst_helper); + return len; +} + +static int inst_info_release(struct inode *inode, struct file *file) +{ + dprintk(VIDC_INFO, "Release inode ptr: %pK\n", inode->i_private); + file->private_data = NULL; + return 0; +} + +static const struct file_operations inst_info_fops = { + .open = inst_info_open, + .read = inst_info_read, + .release = inst_info_release, +}; + +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, + struct dentry *parent) +{ + struct dentry *dir = NULL, *info = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + struct core_inst_pair *idata = NULL; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst); + goto exit; + } + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst); + + idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL); + if (!idata) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + goto exit; + } + + idata->core = inst->core; + idata->inst = inst; + + dir = debugfs_create_dir(debugfs_name, parent); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); + goto failed_create_dir; + } + + info = debugfs_create_file("info", 0444, dir, + idata, &inst_info_fops); + if (!info) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_file; + } + + dir->d_inode->i_private = info->d_inode->i_private; + inst->debug.pdata[FRAME_PROCESSING].sampling = true; + return dir; + +failed_create_file: + debugfs_remove_recursive(dir); + dir = NULL; +failed_create_dir: + kfree(idata); +exit: + return dir; +} + +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst) +{ + struct dentry *dentry = NULL; + + if (!inst || !inst->debugfs_root) + return; + + dentry = inst->debugfs_root; + if (dentry->d_inode) { + dprintk(VIDC_INFO, "Destroy %pK\n", dentry->d_inode->i_private); + kfree(dentry->d_inode->i_private); + dentry->d_inode->i_private = NULL; + } + debugfs_remove_recursive(dentry); + inst->debugfs_root = NULL; +} + +void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, + enum msm_vidc_debugfs_event e) +{ + struct msm_vidc_debug *d = &inst->debug; + char a[64] = "Frame processing"; + + switch (e) { + case MSM_VIDC_DEBUGFS_EVENT_ETB: + inst->count.etb++; + if (inst->count.ebd && inst->count.ftb > inst->count.fbd) { + d->pdata[FRAME_PROCESSING].name[0] = '\0'; + tic(inst, FRAME_PROCESSING, a); + } + break; + case MSM_VIDC_DEBUGFS_EVENT_EBD: + inst->count.ebd++; + if (inst->count.ebd && inst->count.ebd == inst->count.etb) { + toc(inst, FRAME_PROCESSING); + dprintk(VIDC_PROF, "EBD: FW needs input buffers\n"); + } + if (inst->count.ftb == inst->count.fbd) + dprintk(VIDC_PROF, "EBD: FW needs output buffers\n"); + break; + case MSM_VIDC_DEBUGFS_EVENT_FTB: { + inst->count.ftb++; + if (inst->count.ebd && inst->count.etb > inst->count.ebd) { + d->pdata[FRAME_PROCESSING].name[0] = '\0'; + tic(inst, FRAME_PROCESSING, a); + } + } + break; + case MSM_VIDC_DEBUGFS_EVENT_FBD: + inst->count.fbd++; + inst->debug.samples++; + if (inst->count.fbd && + inst->count.fbd == inst->count.ftb) { + toc(inst, FRAME_PROCESSING); + dprintk(VIDC_PROF, "FBD: FW needs output buffers\n"); + } + if (inst->count.etb == inst->count.ebd) + dprintk(VIDC_PROF, "FBD: FW needs input buffers\n"); + break; + default: + dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e); + break; + } +} + +int msm_vidc_check_ratelimit(void) +{ + static DEFINE_RATELIMIT_STATE(_rs, + VIDC_DBG_SESSION_RATELIMIT_INTERVAL, + VIDC_DBG_SESSION_RATELIMIT_BURST); + return __ratelimit(&_rs); +} + diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h new file mode 100644 index 000000000000..17e6ed52429b --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -0,0 +1,216 @@ +/* Copyright (c) 2012-2018, 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_VIDC_DEBUG__ +#define __MSM_VIDC_DEBUG__ +#include +#include +#include "msm_vidc_internal.h" +#include "trace/events/msm_vidc_events.h" + +#ifndef VIDC_DBG_LABEL +#define VIDC_DBG_LABEL "msm_vidc" +#endif + +/* + * This enforces a rate limit: not more than 6 messages + * in every 1s. + */ + +#define VIDC_DBG_SESSION_RATELIMIT_INTERVAL (1 * HZ) +#define VIDC_DBG_SESSION_RATELIMIT_BURST 6 + +#define VIDC_DBG_TAG VIDC_DBG_LABEL ": %4s: " + +/* To enable messages OR these values and + * echo the result to debugfs file. + * + * To enable all messages set debug_level = 0x101F + */ + +enum vidc_msg_prio { + VIDC_ERR = 0x0001, + VIDC_WARN = 0x0002, + VIDC_INFO = 0x0004, + VIDC_DBG = 0x0008, + VIDC_PROF = 0x0010, + VIDC_PKT = 0x0020, + VIDC_FW = 0x1000, +}; + +enum vidc_msg_out { + VIDC_OUT_PRINTK = 0, +}; + +enum msm_vidc_debugfs_event { + MSM_VIDC_DEBUGFS_EVENT_ETB, + MSM_VIDC_DEBUGFS_EVENT_EBD, + MSM_VIDC_DEBUGFS_EVENT_FTB, + MSM_VIDC_DEBUGFS_EVENT_FBD, +}; + +extern int msm_vidc_debug; +extern int msm_vidc_debug_out; +extern int msm_vidc_fw_debug; +extern int msm_vidc_fw_debug_mode; +extern int msm_vidc_fw_low_power_mode; +extern bool msm_vidc_fw_coverage; +extern bool msm_vidc_thermal_mitigation_disabled; +extern int msm_vidc_clock_voting; +extern bool msm_vidc_syscache_disable; + +#define dprintk(__level, __fmt, arg...) \ + do { \ + if (msm_vidc_debug & __level) { \ + if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \ + pr_info(VIDC_DBG_TAG __fmt, \ + get_debug_level_str(__level), \ + ## arg); \ + } \ + } \ + } while (0) + +#define dprintk_ratelimit(__level, __fmt, arg...) \ + do { \ + if (msm_vidc_debug & __level) { \ + if (msm_vidc_debug_out == VIDC_OUT_PRINTK && \ + msm_vidc_check_ratelimit()) { \ + pr_info(VIDC_DBG_TAG __fmt, \ + get_debug_level_str(__level), \ + ## arg); \ + } \ + } \ + } while (0) + +#define MSM_VIDC_ERROR(value) \ + do { if (value) \ + dprintk(VIDC_DBG, "BugOn"); \ + BUG_ON(value); \ + } while (0) + + +struct dentry *msm_vidc_debugfs_init_drv(void); +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, + struct dentry *parent); +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, + struct dentry *parent); +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst); +void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, + enum msm_vidc_debugfs_event e); +int msm_vidc_check_ratelimit(void); + +static inline char *get_debug_level_str(int level) +{ + switch (level) { + case VIDC_ERR: + return "err"; + case VIDC_WARN: + return "warn"; + case VIDC_INFO: + return "info"; + case VIDC_DBG: + return "dbg"; + case VIDC_PROF: + return "prof"; + case VIDC_PKT: + return "pkt"; + case VIDC_FW: + return "fw"; + default: + return "???"; + } +} + +static inline void tic(struct msm_vidc_inst *i, enum profiling_points p, + char *b) +{ + struct timeval __ddl_tv; + + if (!i->debug.pdata[p].name[0]) + memcpy(i->debug.pdata[p].name, b, 64); + if ((msm_vidc_debug & VIDC_PROF) && + i->debug.pdata[p].sampling) { + do_gettimeofday(&__ddl_tv); + i->debug.pdata[p].start = + (__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000); + i->debug.pdata[p].sampling = false; + } +} + +static inline void toc(struct msm_vidc_inst *i, enum profiling_points p) +{ + struct timeval __ddl_tv; + + if ((msm_vidc_debug & VIDC_PROF) && + !i->debug.pdata[p].sampling) { + do_gettimeofday(&__ddl_tv); + i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000) + + (__ddl_tv.tv_usec / 1000); + i->debug.pdata[p].cumulative += i->debug.pdata[p].stop - + i->debug.pdata[p].start; + i->debug.pdata[p].sampling = true; + } +} + +static inline void show_stats(struct msm_vidc_inst *i) +{ + int x; + + for (x = 0; x < MAX_PROFILING_POINTS; x++) { + if (i->debug.pdata[x].name[0] && + (msm_vidc_debug & VIDC_PROF)) { + if (i->debug.samples) { + dprintk(VIDC_PROF, "%s averaged %d ms/sample\n", + i->debug.pdata[x].name, + i->debug.pdata[x].cumulative / + i->debug.samples); + } + + dprintk(VIDC_PROF, "%s Samples: %d\n", + i->debug.pdata[x].name, + i->debug.samples); + } + } +} + +static inline void msm_vidc_res_handle_fatal_hw_error( + struct msm_vidc_platform_resources *resources, + bool enable_fatal) +{ + enable_fatal &= resources->debug_timeout; + MSM_VIDC_ERROR(enable_fatal); +} + +static inline void msm_vidc_handle_hw_error(struct msm_vidc_core *core) +{ + bool enable_fatal = true; + + /* + * In current implementation user-initiated SSR triggers + * a fatal error from hardware. However, there is no way + * to know if fatal error is due to SSR or not. Handle + * user SSR as non-fatal. + */ + if (core->trigger_ssr) { + core->trigger_ssr = false; + enable_fatal = false; + } + + /* Video driver can decide FATAL handling of HW errors + * based on multiple factors. This condition check will + * be enhanced later. + */ + msm_vidc_res_handle_fatal_hw_error(&core->resources, enable_fatal); +} + +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc/msm_vidc_dyn_gov.c new file mode 100644 index 000000000000..7571cda9f0e9 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_dyn_gov.c @@ -0,0 +1,942 @@ +/* Copyright (c) 2015-2020, 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 +#include "msm_vidc_debug.h" +#include "fixedpoint.h" +#include "msm_vidc_internal.h" +#include "vidc_hfi_api.h" +#define COMPRESSION_RATIO_MAX 5 + +static bool debug; +module_param(debug, bool, 0644); + +enum vidc_bus_type { + PERF, + DDR, + LLCC, +}; + +/* + * Minimum dimensions for which to calculate bandwidth. + * This means that anything bandwidth(0, 0) == + * bandwidth(BASELINE_DIMENSIONS.width, BASELINE_DIMENSIONS.height) + */ +static const struct { + int height, width; +} BASELINE_DIMENSIONS = { + .width = 1280, + .height = 720, +}; + +/* converts Mbps to bps (the "b" part can be bits or bytes based on context) */ +#define kbps(__mbps) ((__mbps) * 1000) +#define bps(__mbps) (kbps(__mbps) * 1000) + +#define GENERATE_COMPRESSION_PROFILE(__bpp, __worst) { \ + .bpp = __bpp, \ + .ratio = __worst, \ +} + +/* + * The below table is a structural representation of the following table: + * Resolution | Bitrate | Compression Ratio | + * ............|............|.........................................| + * Width Height|Average High|Avg_8bpc Worst_8bpc Avg_10bpc Worst_10bpc| + * 1280 720| 7 14| 1.69 1.28 1.49 1.23| + * 1920 1080| 20 40| 1.69 1.28 1.49 1.23| + * 2560 1440| 32 64| 2.2 1.26 1.97 1.22| + * 3840 2160| 42 84| 2.2 1.26 1.97 1.22| + * 4096 2160| 44 88| 2.2 1.26 1.97 1.22| + * 4096 2304| 48 96| 2.2 1.26 1.97 1.22| + */ +static struct lut { + int frame_size; /* width x height */ + int frame_rate; + unsigned long bitrate; + struct { + int bpp; + fp_t ratio; + } compression_ratio[COMPRESSION_RATIO_MAX]; +} const LUT[] = { + { + .frame_size = 1280 * 720, + .frame_rate = 30, + .bitrate = 14, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1280 * 720, + .frame_rate = 60, + .bitrate = 22, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1920 * 1088, + .frame_rate = 30, + .bitrate = 40, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 1920 * 1088, + .frame_rate = 60, + .bitrate = 64, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 23, 100)), + } + }, + { + .frame_size = 2560 * 1440, + .frame_rate = 30, + .bitrate = 64, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 2560 * 1440, + .frame_rate = 60, + .bitrate = 102, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 3840 * 2160, + .frame_rate = 30, + .bitrate = 84, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 3840 * 2160, + .frame_rate = 60, + .bitrate = 134, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2160, + .frame_rate = 30, + .bitrate = 88, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2160, + .frame_rate = 60, + .bitrate = 141, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2304, + .frame_rate = 30, + .bitrate = 96, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2304, + .frame_rate = 60, + .bitrate = 154, + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 22, 100)), + } + }, +}; + +static u32 get_type_frm_name(char *name) +{ + if (!strcmp(name, "venus-llcc")) + return LLCC; + else if (!strcmp(name, "venus-ddr")) + return DDR; + else + return PERF; +} + +static struct lut const *__lut(int width, int height, int fps) +{ + int frame_size = height * width, c = 0; + + do { + if (LUT[c].frame_size >= frame_size && LUT[c].frame_rate >= fps) + return &LUT[c]; + } while (++c < ARRAY_SIZE(LUT)); + + return &LUT[ARRAY_SIZE(LUT) - 1]; +} + +static fp_t __compression_ratio(struct lut const *entry, int bpp) +{ + int c = 0; + + for (c = 0; c < COMPRESSION_RATIO_MAX; ++c) { + if (entry->compression_ratio[c].bpp == bpp) + return entry->compression_ratio[c].ratio; + } + + WARN(true, "Shouldn't be here, LUT possibly corrupted?\n"); + return FP_ZERO; /* impossible */ +} + +#define DUMP_HEADER_MAGIC 0xdeadbeef +#define DUMP_FP_FMT "%FP" /* special format for fp_t */ +struct dump { + char *key; + char *format; + size_t val; +}; + +static void __dump(struct dump dump[], int len) +{ + int c = 0; + + for (c = 0; c < len; ++c) { + char format_line[128] = "", formatted_line[128] = ""; + + if (dump[c].val == DUMP_HEADER_MAGIC) { + snprintf(formatted_line, sizeof(formatted_line), "%s\n", + dump[c].key); + } else { + bool fp_format = !strcmp(dump[c].format, DUMP_FP_FMT); + + if (!fp_format) { + snprintf(format_line, sizeof(format_line), + " %-35s: %s\n", dump[c].key, + dump[c].format); + snprintf(formatted_line, sizeof(formatted_line), + format_line, dump[c].val); + } else { + size_t integer_part, fractional_part; + + integer_part = fp_int(dump[c].val); + fractional_part = fp_frac(dump[c].val); + snprintf(formatted_line, sizeof(formatted_line), + " %-35s: %zd + %zd/%zd\n", + dump[c].key, integer_part, + fractional_part, + fp_frac_base()); + + + } + } + + dprintk(VIDC_DBG, "%s", formatted_line); + } +} + +static unsigned long __calculate_vpe(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + return 0; +} + +static unsigned long __calculate_cvp(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + unsigned long ret = 0; + + switch (type) { + case DDR: + ret = d->ddr_bw; + break; + case LLCC: + ret = d->sys_cache_bw; + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + break; + } + + return ret; +} + +static bool __ubwc(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12_UBWC: + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + return true; + default: + return false; + } +} + +static int __bpp(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12: + case HAL_COLOR_FORMAT_NV21: + case HAL_COLOR_FORMAT_NV12_UBWC: + return 8; + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + case HAL_COLOR_FORMAT_P010: + return 10; + default: + dprintk(VIDC_ERR, + "What's this? We don't support this colorformat (%x)", + f); + return INT_MAX; + } +} + +static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Decoder parameters */ + int width, height, lcu_size, fps, dpb_bpp; + bool unified_dpb_opb, dpb_compression_enabled = true, + opb_compression_enabled = false, + llc_ref_read_l2_cache_enabled = false, + llc_top_line_buf_enabled = false; + fp_t dpb_read_compression_factor, dpb_opb_scaling_ratio, + dpb_write_compression_factor, opb_write_compression_factor, + qsmmu_bw_overhead_factor; + bool is_h264_category = true; + + /* Derived parameters */ + int lcu_per_frame, collocated_bytes_per_lcu, tnbr_per_lcu; + unsigned long bitrate; + + fp_t bins_to_bit_factor, vsp_read_factor, vsp_write_factor, + dpb_factor, dpb_write_factor, + y_bw_no_ubwc_8bpp, y_bw_no_ubwc_10bpp, y_bw_10bpp_p010, + motion_vector_complexity = 0; + fp_t dpb_total = 0; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + dpb_read, dpb_write, opb_read, opb_write, + line_buffer_read, line_buffer_write, + total; + } ddr = {0}; + + struct { + fp_t dpb_read, line_buffer_read, line_buffer_write, total; + } llc = {0}; + + unsigned long ret = 0; + unsigned int integer_part, frac_part; + + width = max(d->input_width, BASELINE_DIMENSIONS.width); + height = max(d->input_height, BASELINE_DIMENSIONS.height); + + fps = d->fps; + + lcu_size = d->lcu_size; + + dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; + + unified_dpb_opb = d->num_formats == 1; + + dpb_opb_scaling_ratio = fp_div(FP_INT(d->input_width * d->input_height), + FP_INT(d->output_width * d->output_height)); + + opb_compression_enabled = d->num_formats >= 2 && + __ubwc(d->color_formats[1]); + + /* + * convert q16 number into integer and fractional part upto 2 places. + * ex : 105752 / 65536 = 1.61; 1.61 in q16 = 105752; + * integer part = 105752 / 65536 = 1; + * reminder = 105752 - 1 * 65536 = 40216; + * fractional part = 40216 * 100 / 65536 = 61; + * now converto to fp(1, 61, 100) for below code. + */ + + integer_part = d->compression_ratio >> 16; + frac_part = + ((d->compression_ratio - (integer_part << 16)) * 100) >> 16; + + dpb_read_compression_factor = FP(integer_part, frac_part, 100); + + integer_part = d->complexity_factor >> 16; + frac_part = + ((d->complexity_factor - (integer_part << 16)) * 100) >> 16; + + motion_vector_complexity = FP(integer_part, frac_part, 100); + + dpb_write_compression_factor = dpb_read_compression_factor; + opb_write_compression_factor = opb_compression_enabled ? + dpb_write_compression_factor : FP_ONE; + + if (d->codec == HAL_VIDEO_CODEC_HEVC || + d->codec == HAL_VIDEO_CODEC_VP9) { + /* H264, VP8, MPEG2 use the same settings */ + /* HEVC, VP9 use the same setting */ + is_h264_category = false; + } + if (d->use_sys_cache) { + llc_ref_read_l2_cache_enabled = true; + if (is_h264_category) + llc_top_line_buf_enabled = true; + } + + /* Derived parameters setup */ + lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size); + + bitrate = (d->bitrate + 1000000 - 1) / 1000000; + + bins_to_bit_factor = FP_INT(4); + vsp_write_factor = bins_to_bit_factor; + vsp_read_factor = bins_to_bit_factor + FP_INT(2); + + collocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; + + dpb_factor = FP(1, 50, 100); + dpb_write_factor = FP(1, 5, 100); + + tnbr_per_lcu = lcu_size == 16 ? 128 : + lcu_size == 32 ? 64 : 128; + + /* .... For DDR & LLC ...... */ + ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), + vsp_read_factor), FP_INT(8)); + ddr.vsp_write = fp_div(fp_mult(FP_INT(bitrate), + vsp_write_factor), FP_INT(8)); + + ddr.collocated_read = fp_div(FP_INT(lcu_per_frame * + collocated_bytes_per_lcu * fps), FP_INT(bps(1))); + ddr.collocated_write = ddr.collocated_read; + + y_bw_no_ubwc_8bpp = fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT((int)fps)), + FP_INT(1000 * 1000)); + y_bw_no_ubwc_10bpp = fp_div(fp_mult(y_bw_no_ubwc_8bpp, FP_INT(256)), + FP_INT(192)); + y_bw_10bpp_p010 = y_bw_no_ubwc_8bpp * 2; + + ddr.dpb_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.dpb_read = fp_div(fp_mult(ddr.dpb_read, + fp_mult(dpb_factor, motion_vector_complexity)), + dpb_read_compression_factor); + + ddr.dpb_write = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.dpb_write = fp_div(fp_mult(ddr.dpb_write, + fp_mult(dpb_factor, dpb_write_factor)), + dpb_write_compression_factor); + + dpb_total = ddr.dpb_read + ddr.dpb_write; + + if (llc_ref_read_l2_cache_enabled) { + ddr.dpb_read = fp_div(ddr.dpb_read, is_h264_category ? + FP(1, 15, 100) : FP(1, 30, 100)); + llc.dpb_read = dpb_total - ddr.dpb_write - ddr.dpb_read; + } + + ddr.opb_read = FP_ZERO; + ddr.opb_write = unified_dpb_opb ? FP_ZERO : (dpb_bpp == 8 ? + y_bw_no_ubwc_8bpp : (opb_compression_enabled ? + y_bw_no_ubwc_10bpp : y_bw_10bpp_p010)); + ddr.opb_write = fp_div(fp_mult(dpb_factor, ddr.opb_write), + fp_mult(dpb_opb_scaling_ratio, opb_write_compression_factor)); + + ddr.line_buffer_read = FP_INT(tnbr_per_lcu * + lcu_per_frame * fps / bps(1)); + ddr.line_buffer_write = ddr.line_buffer_read; + if (llc_top_line_buf_enabled) { + llc.line_buffer_read = ddr.line_buffer_read; + llc.line_buffer_write = ddr.line_buffer_write; + ddr.line_buffer_write = ddr.line_buffer_read = FP_ZERO; + } + + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.dpb_read + ddr.dpb_write + + ddr.opb_read + ddr.opb_write + + ddr.line_buffer_read + ddr.line_buffer_write; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + llc.total = llc.dpb_read + llc.line_buffer_read + + llc.line_buffer_write + ddr.total; + + /* Dump all the variables for easier debugging */ + if (debug) { + struct dump dump[] = { + {"DECODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"lcu size", "%d", lcu_size}, + {"dpb bitdepth", "%d", dpb_bpp}, + {"frame rate", "%d", fps}, + {"dpb/opb unified", "%d", unified_dpb_opb}, + {"dpb/opb downscaling ratio", DUMP_FP_FMT, + dpb_opb_scaling_ratio}, + {"dpb compression", "%d", dpb_compression_enabled}, + {"opb compression", "%d", opb_compression_enabled}, + {"dpb read compression factor", DUMP_FP_FMT, + dpb_read_compression_factor}, + {"dpb write compression factor", DUMP_FP_FMT, + dpb_write_compression_factor}, + {"frame width", "%d", width}, + {"frame height", "%d", height}, + {"llc ref read l2 cache enabled", "%d", + llc_ref_read_l2_cache_enabled}, + {"llc top line buf enabled", "%d", + llc_top_line_buf_enabled}, + + {"DERIVED PARAMETERS (1)", "", DUMP_HEADER_MAGIC}, + {"lcus/frame", "%d", lcu_per_frame}, + {"bitrate (Mbit/sec)", "%d", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"dpb write factor", DUMP_FP_FMT, dpb_write_factor}, + {"vsp read factor", DUMP_FP_FMT, vsp_read_factor}, + {"vsp write factor", DUMP_FP_FMT, vsp_write_factor}, + {"tnbr/lcu", "%d", tnbr_per_lcu}, + {"collocated bytes/LCU", "%d", collocated_bytes_per_lcu}, + {"bw for NV12 8bpc)", DUMP_FP_FMT, y_bw_no_ubwc_8bpp}, + {"bw for NV12 10bpc)", DUMP_FP_FMT, y_bw_no_ubwc_10bpp}, + + {"DERIVED PARAMETERS (2)", "", DUMP_HEADER_MAGIC}, + {"mv complexity", DUMP_FP_FMT, motion_vector_complexity}, + {"qsmmu_bw_overhead_factor", DUMP_FP_FMT, + qsmmu_bw_overhead_factor}, + + {"INTERMEDIATE DDR B/W", "", DUMP_HEADER_MAGIC}, + {"vsp read", DUMP_FP_FMT, ddr.vsp_read}, + {"vsp write", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, + {"opb read", DUMP_FP_FMT, ddr.opb_read}, + {"opb write", DUMP_FP_FMT, ddr.opb_write}, + {"dpb read", DUMP_FP_FMT, ddr.dpb_read}, + {"dpb write", DUMP_FP_FMT, ddr.dpb_write}, + {"dpb total", DUMP_FP_FMT, dpb_total}, + {"INTERMEDIATE LLC B/W", "", DUMP_HEADER_MAGIC}, + {"llc dpb read", DUMP_FP_FMT, llc.dpb_read}, + {"llc line buffer read", DUMP_FP_FMT, llc.line_buffer_read}, + {"llc line buffer write", DUMP_FP_FMT, llc.line_buffer_write}, + + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + case LLCC: + ret = kbps(fp_round(llc.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + +static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Encoder Parameters */ + int width, height, fps, lcu_size, bitrate, lcu_per_frame, + collocated_bytes_per_lcu, tnbr_per_lcu, dpb_bpp, + original_color_format, vertical_tile_width, rotation; + bool work_mode_1, original_compression_enabled, + low_power, cropping_or_scaling, + b_frames_enabled = false, + llc_ref_chroma_cache_enabled = false, + llc_top_line_buf_enabled = false, + llc_vpss_rot_line_buf_enabled = false; + + fp_t bins_to_bit_factor, dpb_compression_factor, + original_compression_factor, + original_compression_factor_y, + y_bw_no_ubwc_8bpp, y_bw_no_ubwc_10bpp, y_bw_10bpp_p010, + input_compression_factor, + downscaling_ratio, + ref_y_read_bw_factor, ref_cbcr_read_bw_factor, + recon_write_bw_factor, mese_read_factor, + total_ref_read_crcb, + qsmmu_bw_overhead_factor; + fp_t integer_part, frac_part; + unsigned long ret = 0; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + ref_read_y, ref_read_crcb, ref_write, + ref_write_overlap, orig_read, + line_buffer_read, line_buffer_write, + mese_read, mese_write, + total; + } ddr = {0}; + + struct { + fp_t ref_read_crcb, line_buffer, total; + } llc = {0}; + + /* Encoder Parameters setup */ + rotation = d->rotation; + cropping_or_scaling = false; + vertical_tile_width = 960; + recon_write_bw_factor = FP(1, 8, 100); + ref_y_read_bw_factor = FP(1, 30, 100); + ref_cbcr_read_bw_factor = FP(1, 50, 100); + + + /* Derived Parameters */ + fps = d->fps; + width = max(d->output_width, BASELINE_DIMENSIONS.width); + height = max(d->output_height, BASELINE_DIMENSIONS.height); + downscaling_ratio = fp_div(FP_INT(d->input_width * d->input_height), + FP_INT(d->output_width * d->output_height)); + downscaling_ratio = max(downscaling_ratio, FP_ONE); + bitrate = d->bitrate > 0 ? (d->bitrate + 1000000 - 1) / 1000000 : + __lut(width, height, fps)->bitrate; + lcu_size = d->lcu_size; + lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size); + tnbr_per_lcu = 16; + + y_bw_no_ubwc_8bpp = fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT(fps)), + FP_INT(1000 * 1000)); + y_bw_no_ubwc_10bpp = fp_div(fp_mult(y_bw_no_ubwc_8bpp, + FP_INT(256)), FP_INT(192)); + y_bw_10bpp_p010 = y_bw_no_ubwc_8bpp * 2; + + b_frames_enabled = d->b_frames_enabled; + original_color_format = d->num_formats >= 1 ? + d->color_formats[0] : HAL_UNUSED_COLOR; + + dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; + + original_compression_enabled = __ubwc(original_color_format); + + work_mode_1 = d->work_mode == VIDC_WORK_MODE_1; + low_power = d->power_mode == VIDC_POWER_LOW; + bins_to_bit_factor = FP_INT(4); + + if (d->use_sys_cache) { + llc_ref_chroma_cache_enabled = true; + llc_top_line_buf_enabled = true, + llc_vpss_rot_line_buf_enabled = true; + } + + /* + * Convert Q16 number into Integer and Fractional part upto 2 places. + * Ex : 105752 / 65536 = 1.61; 1.61 in Q16 = 105752; + * Integer part = 105752 / 65536 = 1; + * Reminder = 105752 - 1 * 65536 = 40216; + * Fractional part = 40216 * 100 / 65536 = 61; + * Now converto to FP(1, 61, 100) for below code. + */ + + integer_part = d->compression_ratio >> 16; + frac_part = + ((d->compression_ratio - (integer_part * 65536)) * 100) >> 16; + + dpb_compression_factor = FP(integer_part, frac_part, 100); + + integer_part = d->input_cr >> 16; + frac_part = + ((d->input_cr - (integer_part * 65536)) * 100) >> 16; + + input_compression_factor = FP(integer_part, frac_part, 100); + + original_compression_factor = original_compression_factor_y = + !original_compression_enabled ? FP_ONE : + __compression_ratio(__lut(width, height, fps), dpb_bpp); + /* use input cr if it is valid (not 1), otherwise use lut */ + if (original_compression_enabled && + input_compression_factor != FP_ONE) { + original_compression_factor = input_compression_factor; + /* Luma usually has lower compression factor than Chroma, + * input cf is overall cf, add 1.08 factor for Luma cf + */ + original_compression_factor_y = + input_compression_factor > FP(1, 8, 100) ? + fp_div(input_compression_factor, FP(1, 8, 100)) : + input_compression_factor; + } + + mese_read_factor = fp_div(FP_INT((width * height * fps)/4), + original_compression_factor_y); + mese_read_factor = fp_div(fp_mult(mese_read_factor, FP(2, 53, 100)), + FP_INT(1000 * 1000)); + + ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), bins_to_bit_factor), + FP_INT(8)); + ddr.vsp_write = ddr.vsp_read + fp_div(FP_INT(bitrate), FP_INT(8)); + + collocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; + + ddr.collocated_read = fp_div(FP_INT(lcu_per_frame * + collocated_bytes_per_lcu * fps), FP_INT(bps(1))); + + ddr.collocated_write = ddr.collocated_read; + + ddr.ref_read_y = ddr.ref_read_crcb = dpb_bpp == 8 ? + y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + + if (width != vertical_tile_width) { + ddr.ref_read_y = fp_mult(ddr.ref_read_y, + ref_y_read_bw_factor); + } + + ddr.ref_read_y = fp_div(ddr.ref_read_y, dpb_compression_factor); + if (b_frames_enabled) + ddr.ref_read_y = fp_mult(ddr.ref_read_y, FP_INT(2)); + + ddr.ref_read_crcb = fp_mult(ddr.ref_read_crcb, FP(0, 50, 100)); + ddr.ref_read_crcb = fp_div(ddr.ref_read_crcb, dpb_compression_factor); + if (b_frames_enabled) + ddr.ref_read_crcb = fp_mult(ddr.ref_read_crcb, FP_INT(2)); + + if (llc_ref_chroma_cache_enabled) { + total_ref_read_crcb = ddr.ref_read_crcb; + ddr.ref_read_crcb = fp_div(ddr.ref_read_crcb, + ref_cbcr_read_bw_factor); + llc.ref_read_crcb = total_ref_read_crcb - ddr.ref_read_crcb; + } + + ddr.ref_write = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.ref_write = fp_mult(ddr.ref_write, + (fp_div(FP(1, 50, 100), dpb_compression_factor))); + + ddr.ref_write_overlap = fp_div(fp_mult(ddr.ref_write, + (recon_write_bw_factor - FP_ONE)), + recon_write_bw_factor); + + ddr.orig_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : + (original_compression_enabled ? y_bw_no_ubwc_10bpp : + y_bw_10bpp_p010); + ddr.orig_read = fp_div(fp_mult(fp_mult(ddr.orig_read, FP(1, 50, 100)), + downscaling_ratio), original_compression_factor); + if (rotation == 90 || rotation == 270) + ddr.orig_read *= lcu_size == 32 ? (dpb_bpp == 8 ? 1 : 3) : 2; + + ddr.line_buffer_read = FP_INT(tnbr_per_lcu * lcu_per_frame * + fps / bps(1)); + + ddr.line_buffer_write = ddr.line_buffer_read; + if (llc_top_line_buf_enabled) { + llc.line_buffer = ddr.line_buffer_read + ddr.line_buffer_write; + ddr.line_buffer_read = ddr.line_buffer_write = FP_ZERO; + } + + ddr.mese_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.mese_read = fp_div(fp_mult(ddr.mese_read, FP(1, 37, 100)), + original_compression_factor_y) + mese_read_factor; + + ddr.mese_write = FP_INT((width * height)/512) + + fp_div(FP_INT((width * height)/4), + original_compression_factor_y) + + FP_INT((width * height)/128); + ddr.mese_write = fp_div(fp_mult(ddr.mese_write, FP_INT(fps)), + FP_INT(1000 * 1000)); + + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.ref_read_y + ddr.ref_read_crcb + + ddr.ref_write + ddr.ref_write_overlap + + ddr.orig_read + + ddr.line_buffer_read + ddr.line_buffer_write + + ddr.mese_read + ddr.mese_write; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + llc.total = llc.ref_read_crcb + llc.line_buffer + ddr.total; + + if (debug) { + struct dump dump[] = { + {"ENCODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"width", "%d", width}, + {"height", "%d", height}, + {"fps", "%d", fps}, + {"dpb bitdepth", "%d", dpb_bpp}, + {"input downscaling ratio", DUMP_FP_FMT, downscaling_ratio}, + {"rotation", "%d", rotation}, + {"cropping or scaling", "%d", cropping_or_scaling}, + {"low power mode", "%d", low_power}, + {"work Mode", "%d", work_mode_1}, + {"B frame enabled", "%d", b_frames_enabled}, + {"original frame format", "%#x", original_color_format}, + {"original compression enabled", "%d", + original_compression_enabled}, + {"dpb compression factor", DUMP_FP_FMT, + dpb_compression_factor}, + {"input compression factor", DUMP_FP_FMT, + input_compression_factor}, + {"llc ref chroma cache enabled", DUMP_FP_FMT, + llc_ref_chroma_cache_enabled}, + {"llc top line buf enabled", DUMP_FP_FMT, + llc_top_line_buf_enabled}, + {"llc vpss rot line buf enabled ", DUMP_FP_FMT, + llc_vpss_rot_line_buf_enabled}, + + {"DERIVED PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"lcu size", "%d", lcu_size}, + {"bitrate (Mbit/sec)", "%lu", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"original compression factor", DUMP_FP_FMT, + original_compression_factor}, + {"original compression factor y", DUMP_FP_FMT, + original_compression_factor_y}, + {"mese read factor", DUMP_FP_FMT, + mese_read_factor}, + {"qsmmu_bw_overhead_factor", + DUMP_FP_FMT, qsmmu_bw_overhead_factor}, + {"bw for NV12 8bpc)", DUMP_FP_FMT, y_bw_no_ubwc_8bpp}, + {"bw for NV12 10bpc)", DUMP_FP_FMT, y_bw_no_ubwc_10bpp}, + + {"INTERMEDIATE B/W DDR", "", DUMP_HEADER_MAGIC}, + {"vsp read", DUMP_FP_FMT, ddr.vsp_read}, + {"vsp write", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"ref read y", DUMP_FP_FMT, ddr.ref_read_y}, + {"ref read crcb", DUMP_FP_FMT, ddr.ref_read_crcb}, + {"ref write", DUMP_FP_FMT, ddr.ref_write}, + {"ref write overlap", DUMP_FP_FMT, ddr.ref_write_overlap}, + {"original read", DUMP_FP_FMT, ddr.orig_read}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, + {"mese read", DUMP_FP_FMT, ddr.mese_read}, + {"mese write", DUMP_FP_FMT, ddr.mese_write}, + {"INTERMEDIATE LLC B/W", "", DUMP_HEADER_MAGIC}, + {"llc ref read crcb", DUMP_FP_FMT, llc.ref_read_crcb}, + {"llc line buffer", DUMP_FP_FMT, llc.line_buffer}, + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + case LLCC: + ret = kbps(fp_round(llc.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + +static unsigned long __calculate(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + unsigned long value = 0; + + switch (d->domain) { + case HAL_VIDEO_DOMAIN_VPE: + value = __calculate_vpe(d, type); + break; + case HAL_VIDEO_DOMAIN_ENCODER: + value = __calculate_encoder(d, type); + break; + case HAL_VIDEO_DOMAIN_DECODER: + value = __calculate_decoder(d, type); + break; + case HAL_VIDEO_DOMAIN_CVP: + value = __calculate_cvp(d, type); + break; + default: + dprintk(VIDC_ERR, "Unknown Domain"); + } + + return value; +} + +unsigned long __calc_bw(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data) +{ + unsigned long ab_kbps = 0, c = 0; + enum vidc_bus_type type; + + if (!vidc_data || !vidc_data->data_count || !vidc_data->data) + goto exit; + + for (c = 0; c < vidc_data->data_count; ++c) { + if (vidc_data->data->power_mode == VIDC_POWER_TURBO) { + ab_kbps = INT_MAX; + goto exit; + } + } + + type = get_type_frm_name(bus->name); + + for (c = 0; c < vidc_data->data_count; ++c) + ab_kbps += __calculate(&vidc_data->data[c], type); + +exit: + trace_msm_vidc_perf_bus_vote(bus->name, ab_kbps); + return ab_kbps; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h new file mode 100644 index 000000000000..47b369a62184 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -0,0 +1,582 @@ +/* Copyright (c) 2012-2021, 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_VIDC_INTERNAL_H_ +#define _MSM_VIDC_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc.h" +#include +#include "vidc_hfi_api.h" +#include + +#define MSM_VIDC_DRV_NAME "msm_vidc_driver" +#define MSM_VIDC_VERSION ((0 << 16) + (4 << 8) + 19) +#define MAX_DEBUGFS_NAME 50 +#define DEFAULT_TIMEOUT 3 +#define DEFAULT_HEIGHT 1088 +#define DEFAULT_WIDTH 1920 +#define MIN_SUPPORTED_WIDTH 32 +#define MIN_SUPPORTED_HEIGHT 32 +#define DEFAULT_FPS 15 +#define MIN_NUM_OUTPUT_BUFFERS 1 +#define MIN_NUM_OUTPUT_BUFFERS_VP9 6 +#define MIN_NUM_CAPTURE_BUFFERS 1 +#define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME +#define MAX_NUM_CAPTURE_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME + +#define MAX_SUPPORTED_INSTANCES 16 + +/* Maintains the number of FTB's between each FBD over a window */ +#define DCVS_FTB_WINDOW 16 + +#define V4L2_EVENT_VIDC_BASE 10 + +#define SYS_MSG_START HAL_SYS_INIT_DONE +#define SYS_MSG_END HAL_SYS_ERROR +#define SESSION_MSG_START HAL_SESSION_EVENT_CHANGE +#define SESSION_MSG_END HAL_SESSION_ERROR +#define SYS_MSG_INDEX(__msg) (__msg - SYS_MSG_START) +#define SESSION_MSG_INDEX(__msg) (__msg - SESSION_MSG_START) + + +#define MAX_NAME_LENGTH 64 + +#define EXTRADATA_IDX(__num_planes) ((__num_planes) ? (__num_planes) - 1 : 0) + +#define NUM_MBS_PER_SEC(__height, __width, __fps) \ + (NUM_MBS_PER_FRAME(__height, __width) * __fps) + +#define NUM_MBS_PER_FRAME(__height, __width) \ + ((ALIGN(__height, 16) / 16) * (ALIGN(__width, 16) / 16)) + +#define call_core_op(c, op, args...) \ + (((c) && (c)->core_ops && (c)->core_ops->op) ? \ + ((c)->core_ops->op(args)) : 0) + +struct msm_vidc_inst; + +enum vidc_ports { + OUTPUT_PORT, + CAPTURE_PORT, + MAX_PORT_NUM +}; + +enum vidc_core_state { + VIDC_CORE_UNINIT = 0, + VIDC_CORE_INIT, + VIDC_CORE_INIT_DONE, +}; + +/* + * Do not change the enum values unless + * you know what you are doing + */ +enum instance_state { + MSM_VIDC_CORE_UNINIT_DONE = 0x0001, + MSM_VIDC_CORE_INIT, + MSM_VIDC_CORE_INIT_DONE, + MSM_VIDC_OPEN, + MSM_VIDC_OPEN_DONE, + MSM_VIDC_LOAD_RESOURCES, + MSM_VIDC_LOAD_RESOURCES_DONE, + MSM_VIDC_START, + MSM_VIDC_START_DONE, + MSM_VIDC_STOP, + MSM_VIDC_STOP_DONE, + MSM_VIDC_RELEASE_RESOURCES, + MSM_VIDC_RELEASE_RESOURCES_DONE, + MSM_VIDC_CLOSE, + MSM_VIDC_CLOSE_DONE, + MSM_VIDC_CORE_UNINIT, + MSM_VIDC_CORE_INVALID +}; + +struct buf_info { + struct list_head list; + struct vb2_buffer *buf; +}; + +struct msm_vidc_list { + struct list_head list; + struct mutex lock; +}; + +static inline void INIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist) +{ + mutex_init(&mlist->lock); + INIT_LIST_HEAD(&mlist->list); +} + +static inline void DEINIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist) +{ + mutex_destroy(&mlist->lock); +} + +enum buffer_owner { + DRIVER, + FIRMWARE, + CLIENT, + MAX_OWNER +}; + +struct vidc_freq_data { + struct list_head list; + u32 device_addr; + unsigned long freq; + bool turbo; +}; + +struct vidc_input_cr_data { + struct list_head list; + u32 index; + u32 input_cr; +}; + +struct vidc_tag_data { + struct list_head list; + u32 index; + u32 type; + u32 input_tag; + u32 output_tag; +}; + +struct recon_buf { + struct list_head list; + u32 buffer_index; + u32 CR; + u32 CF; +}; + +struct eos_buf { + struct list_head list; + struct msm_smem smem; + u32 is_queued; +}; + +struct internal_buf { + struct list_head list; + enum hal_buffer buffer_type; + struct msm_smem smem; + enum buffer_owner buffer_ownership; + bool mark_remove; +}; + +struct msm_vidc_csc_coeff { + u32 *vpe_csc_custom_matrix_coeff; + u32 *vpe_csc_custom_bias_coeff; + u32 *vpe_csc_custom_limit_coeff; +}; + +struct msm_vidc_buf_data { + struct list_head list; + u32 index; + u32 mark_data; + u32 mark_target; +}; + +struct msm_vidc_common_data { + char key[128]; + int value; +}; + +struct msm_vidc_codec_data { + u32 fourcc; + enum session_type session_type; + int vpp_cycles; + int vsp_cycles; + int low_power_cycles; +}; + +enum efuse_purpose { + SKU_VERSION = 0, +}; + +enum sku_version { + SKU_VERSION_0 = 0, + SKU_VERSION_1, + SKU_VERSION_2, +}; + +struct msm_vidc_efuse_data { + u32 start_address; + u32 size; + u32 mask; + u32 shift; + enum efuse_purpose purpose; +}; + +struct msm_vidc_capability_range { + u32 min; + u32 max; +}; + +struct msm_vidc_image_capability { + struct msm_vidc_capability_range width; + struct msm_vidc_capability_range height; +}; + +enum vpu_version { + VPU_VERSION_4 = 1, + VPU_VERSION_5, +}; + +#define IS_VPU_4(ver) \ + (ver == VPU_VERSION_4) + +#define IS_VPU_5(ver) \ + (ver == VPU_VERSION_5) + +struct msm_vidc_platform_data { + struct msm_vidc_common_data *common_data; + unsigned int common_data_length; + struct msm_vidc_codec_data *codec_data; + unsigned int codec_data_length; + struct msm_vidc_csc_coeff csc_data; + struct msm_vidc_efuse_data *efuse_data; + unsigned int efuse_data_length; + struct msm_vidc_ubwc_config *ubwc_config; + unsigned int ubwc_config_length; + struct msm_vidc_image_capability *heic_image_capability; + struct msm_vidc_image_capability *hevc_image_capability; + unsigned int sku_version; + uint32_t vpu_ver; +}; + +struct msm_vidc_format { + char name[MAX_NAME_LENGTH]; + u8 description[32]; + u32 fourcc; + int type; + u32 (*get_frame_size)(int plane, u32 height, u32 width); + bool defer_outputs; + u32 input_min_count; + u32 output_min_count; +}; + +struct msm_vidc_format_constraint { + u32 fourcc; + u32 num_planes; + u32 y_stride_multiples; + u32 y_max_stride; + u32 y_min_plane_buffer_height_multiple; + u32 y_buffer_alignment; + u32 uv_stride_multiples; + u32 uv_max_stride; + u32 uv_min_plane_buffer_height_multiple; + u32 uv_buffer_alignment; +}; + +struct msm_vidc_drv { + struct mutex lock; + struct list_head cores; + int num_cores; + struct dentry *debugfs_root; + int thermal_level; + u32 sku_version; +}; + +struct msm_video_device { + int type; + struct video_device vdev; +}; + +struct session_crop { + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct session_prop { + u32 width[MAX_PORT_NUM]; + u32 height[MAX_PORT_NUM]; + struct session_crop crop_info; + u32 fps; + u32 bitrate; +}; + +struct buf_queue { + struct vb2_queue vb2_bufq; + struct mutex lock; + unsigned int plane_sizes[VB2_MAX_PLANES]; + int num_planes; +}; + +enum profiling_points { + SYS_INIT = 0, + SESSION_INIT, + LOAD_RESOURCES, + FRAME_PROCESSING, + FW_IDLE, + MAX_PROFILING_POINTS, +}; + +struct buf_count { + int etb; + int ftb; + int fbd; + int ebd; +}; + +struct batch_mode { + bool enable; + u32 size; +}; + +enum dcvs_flags { + MSM_VIDC_DCVS_INCR = BIT(0), + MSM_VIDC_DCVS_DECR = BIT(1), +}; + +struct clock_data { + int buffer_counter; + int load; + int load_low; + int load_norm; + int load_high; + int min_threshold; + int max_threshold; + enum hal_buffer buffer_type; + bool dcvs_mode; + unsigned long bitrate; + unsigned long min_freq; + unsigned long curr_freq; + u32 vpss_cycles; + u32 ise_cycles; + u32 ddr_bw; + u32 sys_cache_bw; + u32 operating_rate; + struct msm_vidc_codec_data *entry; + u32 core_id; + u32 dpb_fourcc; + u32 opb_fourcc; + enum hal_work_mode work_mode; + bool low_latency_mode; + bool turbo_mode; + u32 work_route; + u32 dcvs_flags; +}; + +struct profile_data { + int start; + int stop; + int cumulative; + char name[64]; + int sampling; + int average; +}; + +struct msm_vidc_debug { + struct profile_data pdata[MAX_PROFILING_POINTS]; + int profile; + int samples; +}; + +enum msm_vidc_modes { + VIDC_SECURE = BIT(0), + VIDC_TURBO = BIT(1), + VIDC_THUMBNAIL = BIT(2), + VIDC_LOW_POWER = BIT(3), + VIDC_REALTIME = BIT(4), +}; + +struct msm_vidc_core_ops { + unsigned long (*calc_freq)(struct msm_vidc_inst *inst, u32 filled_len); + int (*decide_work_route)(struct msm_vidc_inst *inst); + int (*decide_work_mode)(struct msm_vidc_inst *inst); +}; + +struct msm_vidc_core { + struct list_head list; + struct mutex lock; + int id; + struct hfi_device *device; + struct msm_vidc_platform_data *platform_data; + struct msm_video_device vdev[MSM_VIDC_MAX_DEVICES]; + struct v4l2_device v4l2_dev; + struct list_head instances; + struct dentry *debugfs_root; + enum vidc_core_state state; + struct completion completions[SYS_MSG_END - SYS_MSG_START + 1]; + enum msm_vidc_hfi_type hfi_type; + struct msm_vidc_platform_resources resources; + u32 enc_codec_supported; + u32 dec_codec_supported; + u32 codec_count; + struct msm_vidc_capability *capabilities; + struct delayed_work fw_unload_work; + struct work_struct ssr_work; + enum hal_ssr_trigger_type ssr_type; + bool smmu_fault_handled; + bool trigger_ssr; + unsigned long min_freq; + unsigned long curr_freq; + struct msm_vidc_core_ops *core_ops; +}; + +struct msm_vidc_inst { + struct list_head list; + struct mutex sync_lock, lock; + struct msm_vidc_core *core; + enum session_type session_type; + void *session; + struct session_prop prop; + enum instance_state state; + struct msm_vidc_format fmts[MAX_PORT_NUM]; + struct buf_queue bufq[MAX_PORT_NUM]; + struct msm_vidc_list freqs; + struct msm_vidc_list input_crs; + struct msm_vidc_list buffer_tags; + struct msm_vidc_list scratchbufs; + struct msm_vidc_list persistbufs; + struct msm_vidc_list pending_getpropq; + struct msm_vidc_list outputbufs; + struct msm_vidc_list reconbufs; + struct msm_vidc_list eosbufs; + struct msm_vidc_list registeredbufs; + struct msm_vidc_list cvpbufs; + struct msm_vidc_list etb_data; + struct msm_vidc_list fbd_data; + struct buffer_requirements buff_req; + struct v4l2_ctrl_handler ctrl_handler; + struct completion completions[SESSION_MSG_END - SESSION_MSG_START + 1]; + struct v4l2_ctrl **cluster; + struct v4l2_fh event_handler; + struct msm_smem *extradata_handle; + bool in_reconfig; + u32 reconfig_width; + u32 reconfig_height; + struct dentry *debugfs_root; + void *priv; + struct msm_vidc_debug debug; + struct buf_count count; + struct clock_data clk_data; + enum msm_vidc_modes flags; + struct msm_vidc_capability capability; + u32 buffer_size_limit; + enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM]; + enum multi_stream stream_output_mode; + struct v4l2_ctrl **ctrls; + int bit_depth; + struct kref kref; + bool in_flush; + bool out_flush; + u32 pic_struct; + u32 colour_space; + u32 profile; + u32 level; + u32 entropy_mode; + u32 grid_enable; + u32 frame_quality; + struct msm_vidc_codec_data *codec_data; + struct hal_hdr10_pq_sei hdr10_sei_params; + struct batch_mode batch; + struct timer_list batch_timer; + struct work_struct batch_work; + bool decode_batching; + u32 max_filled_length; + bool operating_rate_set; +}; + +extern struct msm_vidc_drv *vidc_driver; + +struct msm_vidc_ctrl_cluster { + struct v4l2_ctrl **cluster; + struct list_head list; +}; + +struct msm_vidc_ctrl { + u32 id; + char name[MAX_NAME_LENGTH]; + enum v4l2_ctrl_type type; + s64 minimum; + s64 maximum; + s64 default_value; + u32 step; + u64 menu_skip_mask; + u32 flags; + const char * const *qmenu; +}; + +void handle_cmd_response(enum hal_command_response cmd, void *data); +int msm_vidc_trigger_ssr(struct msm_vidc_core *core, + enum hal_ssr_trigger_type type); +int msm_vidc_freeze_core(struct msm_vidc_core *core); +int msm_vidc_noc_error_info(struct msm_vidc_core *core); +bool heic_encode_session_supported(struct msm_vidc_inst *inst); +int msm_vidc_check_session_supported(struct msm_vidc_inst *inst); +int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst); +void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type); + +enum msm_vidc_flags { + MSM_VIDC_FLAG_DEFERRED = BIT(0), + MSM_VIDC_FLAG_RBR_PENDING = BIT(1), + MSM_VIDC_FLAG_QUEUED = BIT(2), +}; + +struct msm_vidc_buffer { + struct list_head list; + struct kref kref; + struct msm_smem smem[VIDEO_MAX_PLANES]; + struct vb2_v4l2_buffer vvb; + enum msm_vidc_flags flags; + u32 output_tag; +}; + +struct msm_vidc_cvp_buffer { + struct list_head list; + struct msm_smem smem; + struct msm_cvp_buffer buf; +}; + +void msm_comm_handle_thermal_event(void); +int msm_smem_alloc(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + void *res, u32 session_type, struct msm_smem *smem); +int msm_smem_free(struct msm_smem *smem); + +struct context_bank_info *msm_smem_get_context_bank(u32 session_type, + bool is_secure, struct msm_vidc_platform_resources *res, + enum hal_buffer buffer_type); +int msm_smem_map_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem); +int msm_smem_unmap_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem); +struct dma_buf *msm_smem_get_dma_buf(int fd); +void msm_smem_put_dma_buf(void *dma_buf); +int msm_smem_cache_operations(struct dma_buf *dbuf, + enum smem_cache_ops cache_op, unsigned long offset, unsigned long size); +void msm_vidc_fw_unload_handler(struct work_struct *work); +void msm_vidc_ssr_handler(struct work_struct *work); +/* + * XXX: normally should be in msm_vidc.h, but that's meant for public APIs, + * whereas this is private + */ +int msm_vidc_destroy(struct msm_vidc_inst *inst); +void *vidc_get_drv_data(struct device *dev); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c new file mode 100644 index 000000000000..16ff0836ff63 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c @@ -0,0 +1,1136 @@ +/* Copyright (c) 2017-2021, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc_internal.h" +#include "msm_vidc_debug.h" +#include "vidc_hfi_helper.h" + +#define CODEC_ENTRY(n, p, vsp, vpp, lp) \ +{ \ + .fourcc = n, \ + .session_type = p, \ + .vsp_cycles = vsp, \ + .vpp_cycles = vpp, \ + .low_power_cycles = lp \ +} + +#define UBWC_CONFIG(sz, type, mco, mlo, hbbo, rs1, mc, ml, hbb, rs2) \ +{ \ + .nSize = sz, \ + .ePacketType = type, \ + .v1.sOverrideBitInfo.bMaxChannelsOverride = mco, \ + .v1.sOverrideBitInfo.bMalLengthOverride = mlo, \ + .v1.sOverrideBitInfo.bHBBOverride = hbbo, \ + .v1.sOverrideBitInfo.reserved1 = rs1, \ + .v1.nMaxChannels = mc, \ + .v1.nMalLength = ml, \ + .v1.nHighestBankBit = hbb, \ + .v1.reserved2 = {rs2} \ +} + +#define EFUSE_ENTRY(sa, s, m, sh, p) \ +{ \ + .start_address = sa, \ + .size = s, \ + .mask = m, \ + .shift = sh, \ + .purpose = p \ +} + +static struct msm_vidc_codec_data default_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 125, 675, 320), +}; + +/* Update with atoll data */ +static struct msm_vidc_codec_data atoll_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200), +}; + +/* Update with SM6150 data */ +static struct msm_vidc_codec_data sm6150_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200), +}; + +/* Update with trinket data */ +static struct msm_vidc_codec_data trinket_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200), +}; + +/* Update with 855 data */ +static struct msm_vidc_codec_data sm8150_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 10, 200, 200), +}; + +static struct msm_vidc_codec_data sdmmagpie_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 10, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 10, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 10, 200, 200), +}; + +static struct msm_vidc_codec_data sdm845_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200), +}; + +static struct msm_vidc_codec_data sdm670_codec_data[] = { + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320), + CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540), + CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200), + CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200), +}; + +/* + * Custom conversion coefficients for resolution: 176x144 negative + * coeffs are converted to s4.9 format + * (e.g. -22 converted to ((1 << 13) - 22) + * 3x3 transformation matrix coefficients in s4.9 fixed point format + */ +static u32 vpe_csc_custom_matrix_coeff[HAL_MAX_MATRIX_COEFFS] = { + 470, 8170, 8148, 0, 490, 50, 0, 34, 483 +}; + +/* offset coefficients in s9 fixed point format */ +static u32 vpe_csc_custom_bias_coeff[HAL_MAX_BIAS_COEFFS] = { + 34, 0, 4 +}; + +/* clamping value for Y/U/V([min,max] for Y/U/V) */ +static u32 vpe_csc_custom_limit_coeff[HAL_MAX_LIMIT_COEFFS] = { + 16, 235, 16, 240, 16, 240 +}; + +static struct msm_vidc_common_data default_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, +}; + +static struct msm_vidc_common_data atoll_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 1944000, + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 733003, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 225975, + }, +}; + +static struct msm_vidc_common_data atoll_common_data_v1[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 1216800, /* UHD@30 +1080@30 */ + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 733003, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 225975, + }, +}; + +static struct msm_vidc_common_data sm6150_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 1944000, + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 733003, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 225975, + }, +}; + +static struct msm_vidc_common_data trinket_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 6, + }, + { + .key = "qcom,max-hw-load", + .value = 1944000, + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 733003, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 225975, + }, +}; + +static struct msm_vidc_common_data sm8150_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 3916800, /* + * 1920x1088/256 MBs@480fps. It is less + * any other usecases (ex: + * 3840x2160@120fps, 4096x2160@96ps, + * 7680x4320@30fps) + */ + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,debug-timeout", + .value = 0, + }, + { + .key = "qcom,domain-cvp", + .value = 1, + }, + { + .key = "qcom,decode-batching", + .value = 1, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 760000, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 166667, + }, +}; + +static struct msm_vidc_common_data sdmmagpie_common_data_v0[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 3110400, /* 4096x2160@90 */ + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,debug-timeout", + .value = 0, + }, + { + .key = "qcom,domain-cvp", + .value = 1, + }, + { + .key = "qcom,decode-batching", + .value = 1, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 760000, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 166667, + }, +}; + +static struct msm_vidc_common_data sdmmagpie_common_data_v1[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 3, + }, + { + .key = "qcom,max-hw-load", + .value = 1281600, /* 4k@30 Decode + 1080p@30 Encode */ + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,debug-timeout", + .value = 0, + }, + { + .key = "qcom,domain-cvp", + .value = 1, + }, + { + .key = "qcom,decode-batching", + .value = 1, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 760000, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 166667, + }, +}; + +static struct msm_vidc_common_data sdm845_common_data[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,domain-attr-cache-pagetables", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 2, + }, + { + .key = "qcom,max-hw-load", + .value = 3110400, /* 4096x2160@90 */ + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 1500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 1000, + }, + { + .key = "qcom,debug-timeout", + .value = 0, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, + { + .key = "qcom,fw-cycles", + .value = 733003, + }, + { + .key = "qcom,fw-vpp-cycles", + .value = 225975, + }, +}; + +static struct msm_vidc_common_data sdm670_common_data_v0[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 6, + }, + { + .key = "qcom,max-hw-load", + .value = 1944000, + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 250, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, +}; + +static struct msm_vidc_common_data sdm670_common_data_v1[] = { + { + .key = "qcom,never-unload-fw", + .value = 1, + }, + { + .key = "qcom,sw-power-collapse", + .value = 1, + }, + { + .key = "qcom,domain-attr-non-fatal-faults", + .value = 1, + }, + { + .key = "qcom,max-secure-instances", + .value = 6, + }, + { + .key = "qcom,max-hw-load", + .value = 1216800, + }, + { + .key = "qcom,max-hq-mbs-per-frame", + .value = 8160, + }, + { + .key = "qcom,max-hq-mbs-per-sec", + .value = 244800, /* 1920 x 1088 @ 30 fps */ + }, + { + .key = "qcom,max-b-frame-size", + .value = 8160, + }, + { + .key = "qcom,max-b-frames-per-sec", + .value = 60, + }, + { + .key = "qcom,power-collapse-delay", + .value = 500, + }, + { + .key = "qcom,hw-resp-timeout", + .value = 250, + }, + { + .key = "qcom,dcvs", + .value = 1, + }, +}; + +static struct msm_vidc_efuse_data sdm670_efuse_data[] = { + EFUSE_ENTRY(0x007801A0, 4, 0x00008000, 0x0f, SKU_VERSION), +}; + +static struct msm_vidc_efuse_data sdmmagpie_efuse_data[] = { + EFUSE_ENTRY(0x00786018, 4, 0x00000400, 0x0a, SKU_VERSION), +}; + +static struct msm_vidc_efuse_data atoll_efuse_data[] = { + EFUSE_ENTRY(0x007801D4, 4, 0x08000000, 0x1b, SKU_VERSION), +}; + +static struct msm_vidc_ubwc_config trinket_ubwc_data[] = { + UBWC_CONFIG(sizeof(struct msm_vidc_ubwc_config_v1), + HFI_PROPERTY_SYS_UBWC_CONFIG, 0, 1, 0, 0, 0, 64, 0, 0), +}; + +static struct msm_vidc_ubwc_config sdmshrike_ubwc_data[] = { + UBWC_CONFIG(sizeof(struct msm_vidc_ubwc_config), + HFI_PROPERTY_SYS_UBWC_CONFIG, 1, 0, 1, 0, 8, 0, 16, 0), +}; + +static struct msm_vidc_image_capability default_heic_image_capability = { + {512, 8192}, {512, 8192} +}; + +static struct msm_vidc_image_capability default_hevc_image_capability = { + {512, 512}, {512, 512} +}; + +static struct msm_vidc_platform_data default_data = { + .codec_data = default_codec_data, + .codec_data_length = ARRAY_SIZE(default_codec_data), + .common_data = default_common_data, + .common_data_length = ARRAY_SIZE(default_common_data), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_5, +}; + +static struct msm_vidc_platform_data atoll_data = { + .codec_data = atoll_codec_data, + .codec_data_length = ARRAY_SIZE(atoll_codec_data), + .common_data = atoll_common_data, + .common_data_length = ARRAY_SIZE(atoll_common_data), + .ubwc_config = NULL, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = atoll_efuse_data, + .efuse_data_length = ARRAY_SIZE(atoll_efuse_data), + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_4, +}; + +static struct msm_vidc_platform_data sm6150_data = { + .codec_data = sm6150_codec_data, + .codec_data_length = ARRAY_SIZE(sm6150_codec_data), + .common_data = sm6150_common_data, + .common_data_length = ARRAY_SIZE(sm6150_common_data), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = NULL, + .hevc_image_capability = NULL, + .sku_version = 0, + .vpu_ver = VPU_VERSION_4, +}; + +static struct msm_vidc_platform_data trinket_data = { + .codec_data = trinket_codec_data, + .codec_data_length = ARRAY_SIZE(trinket_codec_data), + .common_data = trinket_common_data, + .common_data_length = ARRAY_SIZE(trinket_common_data), + .ubwc_config = trinket_ubwc_data, + .ubwc_config_length = ARRAY_SIZE(trinket_ubwc_data), + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_4, +}; + +static struct msm_vidc_platform_data sm8150_data = { + .codec_data = sm8150_codec_data, + .codec_data_length = ARRAY_SIZE(sm8150_codec_data), + .common_data = sm8150_common_data, + .common_data_length = ARRAY_SIZE(sm8150_common_data), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_5, +}; + +static struct msm_vidc_platform_data sdmshrike_data = { + .codec_data = sm8150_codec_data, + .codec_data_length = ARRAY_SIZE(sm8150_codec_data), + .common_data = sm8150_common_data, + .common_data_length = ARRAY_SIZE(sm8150_common_data), + .ubwc_config = sdmshrike_ubwc_data, + .ubwc_config_length = ARRAY_SIZE(sdmshrike_ubwc_data), + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_5, +}; + +static struct msm_vidc_platform_data sdmmagpie_data = { + .codec_data = sdmmagpie_codec_data, + .codec_data_length = ARRAY_SIZE(sdmmagpie_codec_data), + .common_data = sdmmagpie_common_data_v0, + .common_data_length = ARRAY_SIZE(sdmmagpie_common_data_v0), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = sdmmagpie_efuse_data, + .efuse_data_length = ARRAY_SIZE(sdmmagpie_efuse_data), + .heic_image_capability = &default_heic_image_capability, + .hevc_image_capability = &default_hevc_image_capability, + .sku_version = 0, + .vpu_ver = VPU_VERSION_5, +}; + +static struct msm_vidc_platform_data sdm845_data = { + .codec_data = sdm845_codec_data, + .codec_data_length = ARRAY_SIZE(sdm845_codec_data), + .common_data = sdm845_common_data, + .common_data_length = ARRAY_SIZE(sdm845_common_data), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = NULL, + .efuse_data_length = 0, + .heic_image_capability = NULL, + .hevc_image_capability = NULL, + .sku_version = 0, + .vpu_ver = VPU_VERSION_4, +}; + +static struct msm_vidc_platform_data sdm670_data = { + .codec_data = sdm670_codec_data, + .codec_data_length = ARRAY_SIZE(sdm670_codec_data), + .common_data = sdm670_common_data_v0, + .common_data_length = ARRAY_SIZE(sdm670_common_data_v0), + .ubwc_config = 0, + .ubwc_config_length = 0, + .csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff, + .csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff, + .csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff, + .efuse_data = sdm670_efuse_data, + .efuse_data_length = ARRAY_SIZE(sdm670_efuse_data), + .heic_image_capability = NULL, + .hevc_image_capability = NULL, + .sku_version = 0, + .vpu_ver = VPU_VERSION_4, +}; + +static const struct of_device_id msm_vidc_dt_match[] = { + { + .compatible = "qcom,atoll-vidc", + .data = &atoll_data, + }, + { + .compatible = "qcom,sm6150-vidc", + .data = &sm6150_data, + }, + { + .compatible = "qcom,trinket-vidc", + .data = &trinket_data, + }, + { + .compatible = "qcom,sm8150-vidc", + .data = &sm8150_data, + }, + { + .compatible = "qcom,sdmshrike-vidc", + .data = &sdmshrike_data, + }, + { + .compatible = "qcom,sdmmagpie-vidc", + .data = &sdmmagpie_data, + }, + { + .compatible = "qcom,sdm845-vidc", + .data = &sdm845_data, + }, + { + .compatible = "qcom,sdm670-vidc", + .data = &sdm670_data, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, msm_vidc_dt_match); + +static int msm_vidc_read_efuse( + struct msm_vidc_platform_data *data, struct device *dev) +{ + void __iomem *base; + uint32_t i; + struct msm_vidc_efuse_data *efuse_data = data->efuse_data; + uint32_t efuse_data_count = data->efuse_data_length; + + for (i = 0; i < efuse_data_count; i++) { + + switch ((efuse_data[i]).purpose) { + + case SKU_VERSION: + base = devm_ioremap(dev, (efuse_data[i]).start_address, + (efuse_data[i]).size); + if (!base) { + dprintk(VIDC_ERR, + "failed efuse ioremap: res->start %#x, size %d\n", + (efuse_data[i]).start_address, + (efuse_data[i]).size); + return -EINVAL; + } else { + u32 efuse = 0; + + efuse = readl_relaxed(base); + data->sku_version = + (efuse & (efuse_data[i]).mask) >> + (efuse_data[i]).shift; + dprintk(VIDC_DBG, + "efuse 0x%x, platform version 0x%x\n", + efuse, data->sku_version); + + devm_iounmap(dev, base); + } + break; + + default: + break; + } + } + return 0; +} + +void *vidc_get_drv_data(struct device *dev) +{ + struct msm_vidc_platform_data *driver_data = NULL; + const struct of_device_id *match; + int rc = 0; + + if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) { + driver_data = &default_data; + goto exit; + } + + match = of_match_node(msm_vidc_dt_match, dev->of_node); + + if (match) + driver_data = (struct msm_vidc_platform_data *)match->data; + + if (!of_find_property(dev->of_node, "sku-index", NULL) || + !driver_data) { + goto exit; + } else if (!strcmp(match->compatible, "qcom,sdm670-vidc")) { + rc = msm_vidc_read_efuse(driver_data, dev); + if (rc) + goto exit; + + if (driver_data->sku_version == SKU_VERSION_1) { + driver_data->common_data = sdm670_common_data_v1; + driver_data->common_data_length = + ARRAY_SIZE(sdm670_common_data_v1); + } + } else if (!strcmp(match->compatible, "qcom,sdmmagpie-vidc")) { + rc = msm_vidc_read_efuse(driver_data, dev); + if (rc) + goto exit; + + if (driver_data->sku_version == SKU_VERSION_1) { + driver_data->common_data = sdmmagpie_common_data_v1; + driver_data->common_data_length = + ARRAY_SIZE(sdmmagpie_common_data_v1); + } + } else if (!strcmp(match->compatible, "qcom,atoll-vidc")) { + rc = msm_vidc_read_efuse(driver_data, dev); + if (rc) + goto exit; + + if (driver_data->sku_version == SKU_VERSION_1) { + /* atoll SKU does not differentiate for any param in + * devicetree.Keeping the same index for different SKU + * so as to parse same DT node. + */ + driver_data->sku_version = 0; + driver_data->common_data = atoll_common_data_v1; + driver_data->common_data_length = + ARRAY_SIZE(atoll_common_data_v1); + } + } + +exit: + return driver_data; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c new file mode 100644 index 000000000000..c60ead7eb47f --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -0,0 +1,1328 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" +#include "msm_vidc_res_parse.h" +#include "soc/qcom/secure_buffer.h" + +enum clock_properties { + CLOCK_PROP_HAS_SCALING = 1 << 0, + CLOCK_PROP_HAS_MEM_RETENTION = 1 << 1, +}; + +#define PERF_GOV "performance" + +static inline struct device *msm_iommu_get_ctx(const char *ctx_name) +{ + return NULL; +} + +static int msm_vidc_populate_legacy_context_bank( + struct msm_vidc_platform_resources *res); + +static size_t get_u32_array_num_elements(struct device_node *np, + char *name) +{ + int len; + size_t num_elements = 0; + + if (!of_get_property(np, name, &len)) { + dprintk(VIDC_ERR, "Failed to read %s from device tree\n", + name); + goto fail_read; + } + + num_elements = len / sizeof(u32); + if (num_elements <= 0) { + dprintk(VIDC_ERR, "%s not specified in device tree\n", + name); + goto fail_read; + } + return num_elements; + +fail_read: + return 0; +} + +static inline void msm_vidc_free_allowed_clocks_table( + struct msm_vidc_platform_resources *res) +{ + res->allowed_clks_tbl = NULL; +} + +static inline void msm_vidc_free_cycles_per_mb_table( + struct msm_vidc_platform_resources *res) +{ + res->clock_freq_tbl.clk_prof_entries = NULL; +} + +static inline void msm_vidc_free_reg_table( + struct msm_vidc_platform_resources *res) +{ + res->reg_set.reg_tbl = NULL; +} + +static inline void msm_vidc_free_qdss_addr_table( + struct msm_vidc_platform_resources *res) +{ + res->qdss_addr_set.addr_tbl = NULL; +} + +static inline void msm_vidc_free_bus_vectors( + struct msm_vidc_platform_resources *res) +{ + kfree(res->bus_set.bus_tbl); + res->bus_set.bus_tbl = NULL; + res->bus_set.count = 0; +} + +static inline void msm_vidc_free_buffer_usage_table( + struct msm_vidc_platform_resources *res) +{ + res->buffer_usage_set.buffer_usage_tbl = NULL; +} + +static inline void msm_vidc_free_regulator_table( + struct msm_vidc_platform_resources *res) +{ + int c = 0; + + for (c = 0; c < res->regulator_set.count; ++c) { + struct regulator_info *rinfo = + &res->regulator_set.regulator_tbl[c]; + + rinfo->name = NULL; + } + + res->regulator_set.regulator_tbl = NULL; + res->regulator_set.count = 0; +} + +static inline void msm_vidc_free_clock_table( + struct msm_vidc_platform_resources *res) +{ + res->clock_set.clock_tbl = NULL; + res->clock_set.count = 0; +} + +static inline void msm_vidc_free_cx_ipeak_context( + struct msm_vidc_platform_resources *res) +{ + cx_ipeak_unregister(res->cx_ipeak_context); + res->cx_ipeak_context = NULL; +} + +void msm_vidc_free_platform_resources( + struct msm_vidc_platform_resources *res) +{ + msm_vidc_free_clock_table(res); + msm_vidc_free_regulator_table(res); + msm_vidc_free_allowed_clocks_table(res); + msm_vidc_free_reg_table(res); + msm_vidc_free_qdss_addr_table(res); + msm_vidc_free_bus_vectors(res); + msm_vidc_free_buffer_usage_table(res); + msm_vidc_free_cx_ipeak_context(res); +} + +static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res) +{ + struct reg_set *reg_set; + struct platform_device *pdev = res->pdev; + int i; + int rc = 0; + + if (!of_find_property(pdev->dev.of_node, "qcom,reg-presets", NULL)) { + /* + * qcom,reg-presets is an optional property. It likely won't be + * present if we don't have any register settings to program + */ + dprintk(VIDC_DBG, "qcom,reg-presets not found\n"); + return 0; + } + + reg_set = &res->reg_set; + reg_set->count = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,reg-presets"); + reg_set->count /= sizeof(*reg_set->reg_tbl) / sizeof(u32); + + if (!reg_set->count) { + dprintk(VIDC_DBG, "no elements in reg set\n"); + return rc; + } + + reg_set->reg_tbl = devm_kzalloc(&pdev->dev, reg_set->count * + sizeof(*(reg_set->reg_tbl)), GFP_KERNEL); + if (!reg_set->reg_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc register table\n", + __func__); + return -ENOMEM; + } + + if (of_property_read_u32_array(pdev->dev.of_node, "qcom,reg-presets", + (u32 *)reg_set->reg_tbl, reg_set->count * 2)) { + dprintk(VIDC_ERR, "Failed to read register table\n"); + msm_vidc_free_reg_table(res); + return -EINVAL; + } + for (i = 0; i < reg_set->count; i++) { + dprintk(VIDC_DBG, + "reg = %x, value = %x\n", + reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value + ); + } + return rc; +} +static int msm_vidc_load_qdss_table(struct msm_vidc_platform_resources *res) +{ + struct addr_set *qdss_addr_set; + struct platform_device *pdev = res->pdev; + int i; + int rc = 0; + + if (!of_find_property(pdev->dev.of_node, "qcom,qdss-presets", NULL)) { + /* + * qcom,qdss-presets is an optional property. It likely won't be + * present if we don't have any register settings to program + */ + dprintk(VIDC_DBG, "qcom,qdss-presets not found\n"); + return rc; + } + + qdss_addr_set = &res->qdss_addr_set; + qdss_addr_set->count = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,qdss-presets"); + qdss_addr_set->count /= sizeof(*qdss_addr_set->addr_tbl) / sizeof(u32); + + if (!qdss_addr_set->count) { + dprintk(VIDC_DBG, "no elements in qdss reg set\n"); + return rc; + } + + qdss_addr_set->addr_tbl = devm_kzalloc(&pdev->dev, + qdss_addr_set->count * sizeof(*qdss_addr_set->addr_tbl), + GFP_KERNEL); + if (!qdss_addr_set->addr_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc register table\n", + __func__); + rc = -ENOMEM; + goto err_qdss_addr_tbl; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, "qcom,qdss-presets", + (u32 *)qdss_addr_set->addr_tbl, qdss_addr_set->count * 2); + if (rc) { + dprintk(VIDC_ERR, "Failed to read qdss address table\n"); + msm_vidc_free_qdss_addr_table(res); + rc = -EINVAL; + goto err_qdss_addr_tbl; + } + + for (i = 0; i < qdss_addr_set->count; i++) { + dprintk(VIDC_DBG, "qdss addr = %x, value = %x\n", + qdss_addr_set->addr_tbl[i].start, + qdss_addr_set->addr_tbl[i].size); + } +err_qdss_addr_tbl: + return rc; +} + +static int msm_vidc_load_subcache_info(struct msm_vidc_platform_resources *res) +{ + int rc = 0, num_subcaches = 0, c; + struct platform_device *pdev = res->pdev; + struct subcache_set *subcaches = &res->subcache_set; + + num_subcaches = of_property_count_strings(pdev->dev.of_node, + "cache-slice-names"); + if (num_subcaches <= 0) { + dprintk(VIDC_DBG, "No subcaches found\n"); + goto err_load_subcache_table_fail; + } + + subcaches->subcache_tbl = devm_kzalloc(&pdev->dev, + sizeof(*subcaches->subcache_tbl) * num_subcaches, GFP_KERNEL); + if (!subcaches->subcache_tbl) { + dprintk(VIDC_ERR, + "Failed to allocate memory for subcache tbl\n"); + rc = -ENOMEM; + goto err_load_subcache_table_fail; + } + + subcaches->count = num_subcaches; + dprintk(VIDC_DBG, "Found %d subcaches\n", num_subcaches); + + for (c = 0; c < num_subcaches; ++c) { + struct subcache_info *vsc = &res->subcache_set.subcache_tbl[c]; + + of_property_read_string_index(pdev->dev.of_node, + "cache-slice-names", c, &vsc->name); + } + + res->sys_cache_present = true; + + return 0; + +err_load_subcache_table_fail: + res->sys_cache_present = false; + subcaches->count = 0; + subcaches->subcache_tbl = NULL; + + return rc; +} + +/** + * msm_vidc_load_u32_table() - load dtsi table entries + * @pdev: A pointer to the platform device. + * @of_node: A pointer to the device node. + * @table_name: A pointer to the dtsi table entry name. + * @struct_size: The size of the structure which is nothing but + * a single entry in the dtsi table. + * @table: A pointer to the table pointer which needs to be + * filled by the dtsi table entries. + * @num_elements: Number of elements pointer which needs to be filled + * with the number of elements in the table. + * + * This is a generic implementation to load single or multiple array + * table from dtsi. The array elements should be of size equal to u32. + * + * Return: Return '0' for success else appropriate error value. + */ +int msm_vidc_load_u32_table(struct platform_device *pdev, + struct device_node *of_node, char *table_name, int struct_size, + u32 **table, u32 *num_elements) +{ + int rc = 0, num_elemts = 0; + u32 *ptbl = NULL; + + if (!of_find_property(of_node, table_name, NULL)) { + dprintk(VIDC_DBG, "%s not found\n", table_name); + return 0; + } + + num_elemts = get_u32_array_num_elements(of_node, table_name); + if (!num_elemts) { + dprintk(VIDC_ERR, "no elements in %s\n", table_name); + return 0; + } + num_elemts /= struct_size / sizeof(u32); + + ptbl = devm_kzalloc(&pdev->dev, num_elemts * struct_size, GFP_KERNEL); + if (!ptbl) { + dprintk(VIDC_ERR, "Failed to alloc table %s\n", table_name); + return -ENOMEM; + } + + if (of_property_read_u32_array(of_node, table_name, ptbl, + num_elemts * struct_size / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read %s\n", table_name); + return -EINVAL; + } + + *table = ptbl; + if (num_elements) + *num_elements = num_elemts; + + return rc; +} +EXPORT_SYMBOL(msm_vidc_load_u32_table); + +/* A comparator to compare loads (needed later on) */ +static int cmp(const void *a, const void *b) +{ + /* want to sort in reverse so flip the comparison */ + return ((struct allowed_clock_rates_table *)b)->clock_rate - + ((struct allowed_clock_rates_table *)a)->clock_rate; +} + +static int msm_vidc_load_allowed_clocks_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, + "qcom,allowed-clock-rates", NULL)) { + dprintk(VIDC_DBG, "qcom,allowed-clock-rates not found\n"); + return 0; + } + + rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node, + "qcom,allowed-clock-rates", + sizeof(*res->allowed_clks_tbl), + (u32 **)&res->allowed_clks_tbl, + &res->allowed_clks_tbl_size); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to read allowed clocks table\n", __func__); + return rc; + } + + sort(res->allowed_clks_tbl, res->allowed_clks_tbl_size, + sizeof(*res->allowed_clks_tbl), cmp, NULL); + + return 0; +} + +static int msm_vidc_populate_mem_cdsp(struct device *dev, + struct msm_vidc_platform_resources *res) +{ + res->mem_cdsp.dev = dev; + + return 0; +} + +static int msm_vidc_populate_bus(struct device *dev, + struct msm_vidc_platform_resources *res) +{ + struct bus_set *buses = &res->bus_set; + const char *temp_name = NULL; + struct bus_info *bus = NULL, *temp_table; + u32 range[2]; + int rc = 0; + + temp_table = krealloc(buses->bus_tbl, sizeof(*temp_table) * + (buses->count + 1), GFP_KERNEL); + if (!temp_table) { + dprintk(VIDC_ERR, "%s: Failed to allocate memory", __func__); + rc = -ENOMEM; + goto err_bus; + } + + buses->bus_tbl = temp_table; + bus = &buses->bus_tbl[buses->count]; + + memset(bus, 0x0, sizeof(struct bus_info)); + + rc = of_property_read_string(dev->of_node, "label", &temp_name); + if (rc) { + dprintk(VIDC_ERR, "'label' not found in node\n"); + goto err_bus; + } + /* need a non-const version of name, hence copying it over */ + bus->name = devm_kstrdup(dev, temp_name, GFP_KERNEL); + if (!bus->name) { + rc = -ENOMEM; + goto err_bus; + } + + rc = of_property_read_u32(dev->of_node, "qcom,bus-master", + &bus->master); + if (rc) { + dprintk(VIDC_ERR, "'qcom,bus-master' not found in node\n"); + goto err_bus; + } + + rc = of_property_read_u32(dev->of_node, "qcom,bus-slave", &bus->slave); + if (rc) { + dprintk(VIDC_ERR, "'qcom,bus-slave' not found in node\n"); + goto err_bus; + } + + rc = of_property_read_string(dev->of_node, "qcom,mode", + &bus->mode); + + if (!strcmp(bus->mode, PERF_GOV)) + bus->is_prfm_gov_used = true; + + if (strstr(bus->mode, "ar50")) + bus->is_ar50_gov_used = true; + + rc = of_property_read_u32_array(dev->of_node, "qcom,bus-range-kbps", + range, ARRAY_SIZE(range)); + if (rc) { + rc = 0; + dprintk(VIDC_DBG, + "'qcom,range' not found defaulting to <0 INT_MAX>\n"); + range[0] = 0; + range[1] = INT_MAX; + } + + bus->range[0] = range[0]; /* min */ + bus->range[1] = range[1]; /* max */ + + buses->count++; + bus->dev = dev; + dprintk(VIDC_DBG, "Found bus %s [%d->%d] with mode %s\n", + bus->name, bus->master, bus->slave, bus->mode); +err_bus: + return rc; +} + +static int msm_vidc_load_buffer_usage_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + struct buffer_usage_set *buffer_usage_set = &res->buffer_usage_set; + + if (!of_find_property(pdev->dev.of_node, + "qcom,buffer-type-tz-usage-table", NULL)) { + /* + * qcom,buffer-type-tz-usage-table is an optional property. It + * likely won't be present if the core doesn't support content + * protection + */ + dprintk(VIDC_DBG, "buffer-type-tz-usage-table not found\n"); + return 0; + } + + buffer_usage_set->count = get_u32_array_num_elements( + pdev->dev.of_node, "qcom,buffer-type-tz-usage-table"); + buffer_usage_set->count /= + sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32); + if (!buffer_usage_set->count) { + dprintk(VIDC_DBG, "no elements in buffer usage set\n"); + return 0; + } + + buffer_usage_set->buffer_usage_tbl = devm_kzalloc(&pdev->dev, + buffer_usage_set->count * + sizeof(*buffer_usage_set->buffer_usage_tbl), + GFP_KERNEL); + if (!buffer_usage_set->buffer_usage_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc buffer usage table\n", + __func__); + rc = -ENOMEM; + goto err_load_buf_usage; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,buffer-type-tz-usage-table", + (u32 *)buffer_usage_set->buffer_usage_tbl, + buffer_usage_set->count * + sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32)); + if (rc) { + dprintk(VIDC_ERR, "Failed to read buffer usage table\n"); + goto err_load_buf_usage; + } + + return 0; +err_load_buf_usage: + msm_vidc_free_buffer_usage_table(res); + return rc; +} + +static int msm_vidc_load_regulator_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + struct regulator_set *regulators = &res->regulator_set; + struct device_node *domains_parent_node = NULL; + struct property *domains_property = NULL; + int reg_count = 0; + + regulators->count = 0; + regulators->regulator_tbl = NULL; + + domains_parent_node = pdev->dev.of_node; + for_each_property_of_node(domains_parent_node, domains_property) { + const char *search_string = "-supply"; + char *supply; + bool matched = false; + + /* check if current property is possibly a regulator */ + supply = strnstr(domains_property->name, search_string, + strlen(domains_property->name) + 1); + matched = supply && (*(supply + strlen(search_string)) == '\0'); + if (!matched) + continue; + + reg_count++; + } + + regulators->regulator_tbl = devm_kzalloc(&pdev->dev, + sizeof(*regulators->regulator_tbl) * + reg_count, GFP_KERNEL); + + if (!regulators->regulator_tbl) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator table\n"); + goto err_reg_tbl_alloc; + } + + for_each_property_of_node(domains_parent_node, domains_property) { + const char *search_string = "-supply"; + char *supply; + bool matched = false; + struct device_node *regulator_node = NULL; + struct regulator_info *rinfo = NULL; + + /* check if current property is possibly a regulator */ + supply = strnstr(domains_property->name, search_string, + strlen(domains_property->name) + 1); + matched = supply && (supply[strlen(search_string)] == '\0'); + if (!matched) + continue; + + /* make sure prop isn't being misused */ + regulator_node = of_parse_phandle(domains_parent_node, + domains_property->name, 0); + if (IS_ERR(regulator_node)) { + dprintk(VIDC_WARN, "%s is not a phandle\n", + domains_property->name); + continue; + } + regulators->count++; + + /* populate regulator info */ + rinfo = ®ulators->regulator_tbl[regulators->count - 1]; + rinfo->name = devm_kzalloc(&pdev->dev, + (supply - domains_property->name) + 1, GFP_KERNEL); + if (!rinfo->name) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator name\n"); + goto err_reg_name_alloc; + } + strlcpy(rinfo->name, domains_property->name, + (supply - domains_property->name) + 1); + + rinfo->has_hw_power_collapse = of_property_read_bool( + regulator_node, "qcom,support-hw-trigger"); + + dprintk(VIDC_DBG, "Found regulator %s: h/w collapse = %s\n", + rinfo->name, + rinfo->has_hw_power_collapse ? "yes" : "no"); + } + + if (!regulators->count) + dprintk(VIDC_DBG, "No regulators found"); + + return 0; + +err_reg_name_alloc: +err_reg_tbl_alloc: + msm_vidc_free_regulator_table(res); + return rc; +} + +static int msm_vidc_load_clock_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0, num_clocks = 0, c = 0; + struct platform_device *pdev = res->pdev; + int *clock_props = NULL; + struct clock_set *clocks = &res->clock_set; + + num_clocks = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (num_clocks <= 0) { + dprintk(VIDC_DBG, "No clocks found\n"); + clocks->count = 0; + rc = 0; + goto err_load_clk_table_fail; + } + + clock_props = devm_kzalloc(&pdev->dev, num_clocks * + sizeof(*clock_props), GFP_KERNEL); + if (!clock_props) { + dprintk(VIDC_ERR, "No memory to read clock properties\n"); + rc = -ENOMEM; + goto err_load_clk_table_fail; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,clock-configs", clock_props, + num_clocks); + if (rc) { + dprintk(VIDC_ERR, "Failed to read clock properties: %d\n", rc); + goto err_load_clk_prop_fail; + } + + clocks->clock_tbl = devm_kzalloc(&pdev->dev, sizeof(*clocks->clock_tbl) + * num_clocks, GFP_KERNEL); + if (!clocks->clock_tbl) { + dprintk(VIDC_ERR, "Failed to allocate memory for clock tbl\n"); + rc = -ENOMEM; + goto err_load_clk_prop_fail; + } + + clocks->count = num_clocks; + dprintk(VIDC_DBG, "Found %d clocks\n", num_clocks); + + for (c = 0; c < num_clocks; ++c) { + struct clock_info *vc = &res->clock_set.clock_tbl[c]; + + of_property_read_string_index(pdev->dev.of_node, + "clock-names", c, &vc->name); + + if (clock_props[c] & CLOCK_PROP_HAS_SCALING) { + vc->has_scaling = true; + } else { + vc->count = 0; + vc->has_scaling = false; + } + + if (clock_props[c] & CLOCK_PROP_HAS_MEM_RETENTION) + vc->has_mem_retention = true; + else + vc->has_mem_retention = false; + + dprintk(VIDC_DBG, "Found clock %s: scale-able = %s\n", vc->name, + vc->count ? "yes" : "no"); + } + + + return 0; + +err_load_clk_prop_fail: +err_load_clk_table_fail: + return rc; +} + +static int msm_vidc_load_reset_table( + struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + struct reset_set *rst = &res->reset_set; + int num_clocks = 0, c = 0; + + num_clocks = of_property_count_strings(pdev->dev.of_node, + "reset-names"); + if (num_clocks <= 0) { + dprintk(VIDC_DBG, "No reset clocks found\n"); + rst->count = 0; + return 0; + } + + rst->reset_tbl = devm_kcalloc(&pdev->dev, num_clocks, + sizeof(*rst->reset_tbl), GFP_KERNEL); + if (!rst->reset_tbl) + return -ENOMEM; + + rst->count = num_clocks; + dprintk(VIDC_DBG, "Found %d reset clocks\n", num_clocks); + + for (c = 0; c < num_clocks; ++c) { + struct reset_info *rc = &res->reset_set.reset_tbl[c]; + + of_property_read_string_index(pdev->dev.of_node, + "reset-names", c, &rc->name); + } + + return 0; +} + +static int msm_decide_dt_node( + struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + int rc = 0; + u32 sku_index = 0; + + rc = of_property_read_u32(pdev->dev.of_node, "sku-index", + &sku_index); + if (rc) { + dprintk(VIDC_DBG, "'sku_index' not found in node\n"); + return 0; + } + + if (sku_index != res->sku_version) { + dprintk(VIDC_DBG, + "Failed to parser dt: sku_index %d res->sku_version - %d\n", + sku_index, res->sku_version); + return -EINVAL; + } + + return 0; +} + +static int find_key_value(struct msm_vidc_platform_data *platform_data, + const char *key) +{ + int i = 0; + struct msm_vidc_common_data *common_data = platform_data->common_data; + int size = platform_data->common_data_length; + + for (i = 0; i < size; i++) { + if (!strcmp(common_data[i].key, key)) + return common_data[i].value; + } + return 0; +} + +int read_platform_resources_from_drv_data( + struct msm_vidc_core *core) +{ + struct msm_vidc_platform_data *platform_data; + struct msm_vidc_platform_resources *res; + int rc = 0; + + if (!core || !core->platform_data) { + dprintk(VIDC_ERR, "%s Invalid data\n", __func__); + return -ENOENT; + } + platform_data = core->platform_data; + res = &core->resources; + + res->codec_data_count = platform_data->codec_data_length; + res->codec_data = platform_data->codec_data; + + res->sku_version = platform_data->sku_version; + + res->fw_name = "venus"; + + dprintk(VIDC_DBG, "Firmware filename: %s\n", res->fw_name); + + res->max_load = find_key_value(platform_data, + "qcom,max-hw-load"); + + res->max_hq_mbs_per_frame = find_key_value(platform_data, + "qcom,max-hq-mbs-per-frame"); + + res->max_hq_mbs_per_sec = find_key_value(platform_data, + "qcom,max-hq-mbs-per-sec"); + + res->sw_power_collapsible = find_key_value(platform_data, + "qcom,sw-power-collapse"); + + res->never_unload_fw = find_key_value(platform_data, + "qcom,never-unload-fw"); + + res->debug_timeout = find_key_value(platform_data, + "qcom,debug-timeout"); + + res->pm_qos_latency_us = find_key_value(platform_data, + "qcom,pm-qos-latency-us"); + + res->max_secure_inst_count = find_key_value(platform_data, + "qcom,max-secure-instances"); + + res->slave_side_cp = find_key_value(platform_data, + "qcom,slave-side-cp"); + res->thermal_mitigable = find_key_value(platform_data, + "qcom,enable-thermal-mitigation"); + res->msm_vidc_pwr_collapse_delay = find_key_value(platform_data, + "qcom,power-collapse-delay"); + res->msm_vidc_firmware_unload_delay = find_key_value(platform_data, + "qcom,fw-unload-delay"); + res->msm_vidc_hw_rsp_timeout = find_key_value(platform_data, + "qcom,hw-resp-timeout"); + res->domain_cvp = find_key_value(platform_data, + "qcom,domain-cvp"); + res->non_fatal_pagefaults = find_key_value(platform_data, + "qcom,domain-attr-non-fatal-faults"); + res->cache_pagetables = find_key_value(platform_data, + "qcom,domain-attr-cache-pagetables"); + res->decode_batching = find_key_value(platform_data, + "qcom,decode-batching"); + res->dcvs = find_key_value(platform_data, + "qcom,dcvs"); + res->fw_cycles = find_key_value(platform_data, + "qcom,fw-cycles"); + res->fw_vpp_cycles = find_key_value(platform_data, + "qcom,fw-vpp-cycles"); + + res->csc_coeff_data = &platform_data->csc_data; + + res->vpu_ver = platform_data->vpu_ver; + + res->ubwc_config = platform_data->ubwc_config; + res->ubwc_config_length = platform_data->ubwc_config_length; + + return rc; + +} + +static int msm_vidc_populate_cx_ipeak_context( + struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + int rc = 0; + + if (of_find_property(pdev->dev.of_node, + "qcom,cx-ipeak-data", NULL)) { + res->cx_ipeak_context = cx_ipeak_register( + pdev->dev.of_node, "qcom,cx-ipeak-data"); + } + + if (IS_ERR(res->cx_ipeak_context)) { + rc = PTR_ERR(res->cx_ipeak_context); + if (rc == -EPROBE_DEFER) + dprintk(VIDC_INFO, + "cx-ipeak register failed. Deferring probe!"); + else + dprintk(VIDC_ERR, + "cx-ipeak register failed. rc: %d", rc); + + res->cx_ipeak_context = NULL; + goto err_cx_ipeak; + } + + if (res->cx_ipeak_context) + dprintk(VIDC_INFO, "cx-ipeak register successful"); + else + dprintk(VIDC_INFO, "cx-ipeak register not implemented"); + + of_property_read_u32(pdev->dev.of_node, + "qcom,clock-freq-threshold", + &res->clk_freq_threshold); + dprintk(VIDC_DBG, "cx ipeak threshold frequency = %u\n", + res->clk_freq_threshold); + + return rc; + +err_cx_ipeak: + return rc; +} + +int read_platform_resources_from_dt( + struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + struct resource *kres = NULL; + int rc = 0; + uint32_t firmware_base = 0; + + if (!pdev->dev.of_node) { + dprintk(VIDC_ERR, "DT node not found\n"); + return -ENOENT; + } + + rc = msm_decide_dt_node(res); + if (rc) + return rc; + + + INIT_LIST_HEAD(&res->context_banks); + + res->firmware_base = (phys_addr_t)firmware_base; + + kres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res->register_base = kres ? kres->start : -1; + res->register_size = kres ? (kres->end + 1 - kres->start) : -1; + + kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + res->irq = kres ? kres->start : -1; + + rc = msm_vidc_load_subcache_info(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load subcache info: %d\n", rc); + + rc = msm_vidc_load_qdss_table(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load qdss reg table: %d\n", rc); + + rc = msm_vidc_load_reg_table(res); + if (rc) { + dprintk(VIDC_ERR, "Failed to load reg table: %d\n", rc); + goto err_load_reg_table; + } + + rc = msm_vidc_load_buffer_usage_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load buffer usage table: %d\n", rc); + goto err_load_buffer_usage_table; + } + + rc = msm_vidc_load_regulator_table(res); + if (rc) { + dprintk(VIDC_ERR, "Failed to load list of regulators %d\n", rc); + goto err_load_regulator_table; + } + + rc = msm_vidc_load_clock_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load clock table: %d\n", rc); + goto err_load_clock_table; + } + + rc = msm_vidc_load_allowed_clocks_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load allowed clocks table: %d\n", rc); + goto err_load_allowed_clocks_table; + } + + if (of_device_is_compatible(pdev->dev.of_node, + "qcom,sa6155p-vidc")) { + res->max_load = 2073600; + dprintk(VIDC_INFO, "msm_vidc: Use higher max_load on Auto\n"); + } + + rc = msm_vidc_load_reset_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load reset table: %d\n", rc); + goto err_load_reset_table; + } + + rc = msm_vidc_populate_legacy_context_bank(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to setup context banks %d\n", rc); + goto err_setup_legacy_cb; + } + + rc = msm_vidc_populate_cx_ipeak_context(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to setup cx-ipeak %d\n", rc); + goto err_register_cx_ipeak; + } + +return rc; + +err_load_reset_table: +err_register_cx_ipeak: +err_setup_legacy_cb: + msm_vidc_free_allowed_clocks_table(res); +err_load_allowed_clocks_table: + msm_vidc_free_clock_table(res); +err_load_clock_table: + msm_vidc_free_regulator_table(res); +err_load_regulator_table: + msm_vidc_free_buffer_usage_table(res); +err_load_buffer_usage_table: + msm_vidc_free_reg_table(res); +err_load_reg_table: + return rc; +} + +static int msm_vidc_setup_context_bank(struct msm_vidc_platform_resources *res, + struct context_bank_info *cb, struct device *dev) +{ + int rc = 0; + struct bus_type *bus; + + if (!dev || !cb || !res) { + dprintk(VIDC_ERR, + "%s: Invalid Input params\n", __func__); + return -EINVAL; + } + cb->dev = dev; + + bus = cb->dev->bus; + if (IS_ERR_OR_NULL(bus)) { + dprintk(VIDC_ERR, "%s - failed to get bus type\n", __func__); + rc = PTR_ERR(bus) ?: -ENODEV; + goto remove_cb; + } + + cb->domain = iommu_get_domain_for_dev(cb->dev); + + /* + * configure device segment size and segment boundary to ensure + * iommu mapping returns one mapping (which is required for partial + * cache operations) + */ + if (!dev->dma_parms) + dev->dma_parms = + devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL); + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + dma_set_seg_boundary(dev, (unsigned long)DMA_BIT_MASK(64)); + + dprintk(VIDC_DBG, "Attached %s and created mapping\n", dev_name(dev)); + dprintk(VIDC_DBG, + "Context bank name:%s, buffer_type: %#x, is_secure: %d, address range start: %#x, size: %#x, dev: %pK, domain: %pK", + cb->name, cb->buffer_type, cb->is_secure, cb->addr_range.start, + cb->addr_range.size, cb->dev, cb->domain); + +remove_cb: + return rc; +} + +int msm_vidc_smmu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags, void *token) +{ + struct msm_vidc_core *core = token; + struct msm_vidc_inst *inst; + + if (!domain || !core) { + dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n", + __func__, domain, core); + return -EINVAL; + } + + if (core->smmu_fault_handled) { + if (core->resources.non_fatal_pagefaults) { + dprintk_ratelimit(VIDC_ERR, + "%s: non-fatal pagefault address: %lx\n", + __func__, iova); + return 0; + } + } + + dprintk(VIDC_ERR, "%s - faulting address: %lx\n", __func__, iova); + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + msm_comm_print_inst_info(inst); + } + core->smmu_fault_handled = true; + mutex_unlock(&core->lock); + /* + * Return -EINVAL to elicit the default behaviour of smmu driver. + * If we return -EINVAL, then smmu driver assumes page fault handler + * is not installed and prints a list of useful debug information like + * FAR, SID etc. This information is not printed if we return 0. + */ + return -EINVAL; +} + +static int msm_vidc_populate_context_bank(struct device *dev, + struct msm_vidc_core *core) +{ + int rc = 0; + struct context_bank_info *cb = NULL; + struct device_node *np = NULL; + + if (!dev || !core) { + dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__); + return -EINVAL; + } + + np = dev->of_node; + cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + dprintk(VIDC_ERR, "%s - Failed to allocate cb\n", __func__); + return -ENOMEM; + } + + INIT_LIST_HEAD(&cb->list); + list_add_tail(&cb->list, &core->resources.context_banks); + + rc = of_property_read_string(np, "label", &cb->name); + if (rc) { + dprintk(VIDC_DBG, + "Failed to read cb label from device tree\n"); + rc = 0; + } + + dprintk(VIDC_DBG, "%s: context bank has name %s\n", __func__, cb->name); + rc = of_property_read_u32_array(np, "virtual-addr-pool", + (u32 *)&cb->addr_range, 2); + if (rc) { + dprintk(VIDC_ERR, + "Could not read addr pool for context bank : %s %d\n", + cb->name, rc); + goto err_setup_cb; + } + + cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank"); + dprintk(VIDC_DBG, "context bank %s : secure = %d\n", + cb->name, cb->is_secure); + + /* setup buffer type for each sub device*/ + rc = of_property_read_u32(np, "buffer-types", &cb->buffer_type); + if (rc) { + dprintk(VIDC_ERR, "failed to load buffer_type info %d\n", rc); + rc = -ENOENT; + goto err_setup_cb; + } + dprintk(VIDC_DBG, + "context bank %s address start = %x address size = %x buffer_type = %x\n", + cb->name, cb->addr_range.start, + cb->addr_range.size, cb->buffer_type); + + rc = msm_vidc_setup_context_bank(&core->resources, cb, dev); + if (rc) { + dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc); + goto err_setup_cb; + } + + iommu_set_fault_handler(cb->domain, + msm_vidc_smmu_fault_handler, (void *)core); + + return 0; + +err_setup_cb: + list_del(&cb->list); + return rc; +} + +static int msm_vidc_populate_legacy_context_bank( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = NULL; + struct device_node *domains_parent_node = NULL; + struct device_node *domains_child_node = NULL; + struct device_node *ctx_node = NULL; + struct context_bank_info *cb; + + if (!res || !res->pdev) { + dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__); + return -EINVAL; + } + pdev = res->pdev; + + domains_parent_node = of_find_node_by_name(pdev->dev.of_node, + "qcom,vidc-iommu-domains"); + if (!domains_parent_node) { + dprintk(VIDC_DBG, + "%s legacy iommu domains not present\n", __func__); + return 0; + } + + /* set up each context bank for legacy DT bindings*/ + for_each_child_of_node(domains_parent_node, + domains_child_node) { + cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + dprintk(VIDC_ERR, + "%s - Failed to allocate cb\n", __func__); + return -ENOMEM; + } + INIT_LIST_HEAD(&cb->list); + list_add_tail(&cb->list, &res->context_banks); + + ctx_node = of_parse_phandle(domains_child_node, + "qcom,vidc-domain-phandle", 0); + if (!ctx_node) { + dprintk(VIDC_ERR, + "%s Unable to parse pHandle\n", __func__); + rc = -EBADHANDLE; + goto err_setup_cb; + } + + rc = of_property_read_string(ctx_node, "label", &(cb->name)); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not find label\n", __func__); + goto err_setup_cb; + } + + rc = of_property_read_u32_array(ctx_node, + "qcom,virtual-addr-pool", (u32 *)&cb->addr_range, 2); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not read addr pool for group : %s (%d)\n", + __func__, cb->name, rc); + goto err_setup_cb; + } + + cb->is_secure = + of_property_read_bool(ctx_node, "qcom,secure-domain"); + + rc = of_property_read_u32(domains_child_node, + "qcom,vidc-buffer-types", &cb->buffer_type); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not read buffer type (%d)\n", + __func__, rc); + goto err_setup_cb; + } + + cb->dev = msm_iommu_get_ctx(cb->name); + if (IS_ERR_OR_NULL(cb->dev)) { + dprintk(VIDC_ERR, "%s could not get device for cb %s\n", + __func__, cb->name); + rc = -ENOENT; + goto err_setup_cb; + } + + rc = msm_vidc_setup_context_bank(res, cb, cb->dev); + if (rc) { + dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc); + goto err_setup_cb; + } + dprintk(VIDC_DBG, + "%s: context bank %s secure %d addr start = %#x addr size = %#x buffer_type = %#x\n", + __func__, cb->name, cb->is_secure, cb->addr_range.start, + cb->addr_range.size, cb->buffer_type); + } + return rc; + +err_setup_cb: + list_del(&cb->list); + return rc; +} + +int read_context_bank_resources_from_dt(struct platform_device *pdev) +{ + struct msm_vidc_core *core; + int rc = 0; + + if (!pdev) { + dprintk(VIDC_ERR, "Invalid platform device\n"); + return -EINVAL; + } else if (!pdev->dev.parent) { + dprintk(VIDC_ERR, "Failed to find a parent for %s\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + core = dev_get_drvdata(pdev->dev.parent); + if (!core) { + dprintk(VIDC_ERR, "Failed to find cookie in parent device %s", + dev_name(pdev->dev.parent)); + return -EINVAL; + } + + rc = msm_vidc_populate_context_bank(&pdev->dev, core); + if (rc) + dprintk(VIDC_ERR, "Failed to probe context bank\n"); + else + dprintk(VIDC_DBG, "Successfully probed context bank\n"); + + return rc; +} + +int read_bus_resources_from_dt(struct platform_device *pdev) +{ + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "Invalid platform device\n"); + return -EINVAL; + } else if (!pdev->dev.parent) { + dprintk(VIDC_ERR, "Failed to find a parent for %s\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + core = dev_get_drvdata(pdev->dev.parent); + if (!core) { + dprintk(VIDC_ERR, "Failed to find cookie in parent device %s", + dev_name(pdev->dev.parent)); + return -EINVAL; + } + + return msm_vidc_populate_bus(&pdev->dev, &core->resources); +} + +int read_mem_cdsp_resources_from_dt(struct platform_device *pdev) +{ + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "%s: invalid platform device\n", __func__); + return -EINVAL; + } else if (!pdev->dev.parent) { + dprintk(VIDC_ERR, "Failed to find a parent for %s\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + core = dev_get_drvdata(pdev->dev.parent); + if (!core) { + dprintk(VIDC_ERR, "Failed to find cookie in parent device %s", + dev_name(pdev->dev.parent)); + return -EINVAL; + } + + return msm_vidc_populate_mem_cdsp(&pdev->dev, &core->resources); +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h new file mode 100644 index 000000000000..2d7a1b126068 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h @@ -0,0 +1,39 @@ + +/* Copyright (c) 2012-2018, 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 DT_PARSE +#define DT_PARSE +#include +#include "msm_vidc_resources.h" +#include "msm_vidc_common.h" +void msm_vidc_free_platform_resources( + struct msm_vidc_platform_resources *res); + +int read_hfi_type(struct platform_device *pdev); + +int read_platform_resources_from_drv_data( + struct msm_vidc_core *core); +int read_platform_resources_from_dt( + struct msm_vidc_platform_resources *res); + +int read_context_bank_resources_from_dt(struct platform_device *pdev); + +int read_bus_resources_from_dt(struct platform_device *pdev); +int read_mem_cdsp_resources_from_dt(struct platform_device *pdev); + +int msm_vidc_load_u32_table(struct platform_device *pdev, + struct device_node *of_node, char *table_name, int struct_size, + u32 **table, u32 *num_elements); + +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h new file mode 100644 index 000000000000..b85131e1c527 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -0,0 +1,262 @@ +/* Copyright (c) 2013-2020, 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_VIDC_RESOURCES_H__ +#define __MSM_VIDC_RESOURCES_H__ + +#include +#include "msm_vidc.h" +#include +#include "soc/qcom/cx_ipeak.h" + +#define MAX_BUFFER_TYPES 32 + +struct dcvs_table { + u32 load; + u32 load_low; + u32 load_high; + u32 supported_codecs; +}; + +struct dcvs_limit { + u32 min_mbpf; + u32 fps; +}; + +struct reg_value_pair { + u32 reg; + u32 value; +}; + +struct reg_set { + struct reg_value_pair *reg_tbl; + int count; +}; + +struct addr_range { + u32 start; + u32 size; +}; + +struct addr_set { + struct addr_range *addr_tbl; + int count; +}; + +struct context_bank_info { + struct list_head list; + const char *name; + u32 buffer_type; + bool is_secure; + struct addr_range addr_range; + struct device *dev; + struct iommu_domain *domain; +}; + +struct buffer_usage_table { + u32 buffer_type; + u32 tz_usage; +}; + +struct buffer_usage_set { + struct buffer_usage_table *buffer_usage_tbl; + u32 count; +}; + +struct regulator_info { + struct regulator *regulator; + bool has_hw_power_collapse; + char *name; +}; + +struct regulator_set { + struct regulator_info *regulator_tbl; + u32 count; +}; + +struct clock_info { + const char *name; + struct clk *clk; + u32 count; + bool has_scaling; + bool has_mem_retention; +}; + +struct clock_set { + struct clock_info *clock_tbl; + u32 count; +}; + +struct bus_info { + char *name; + int master; + int slave; + unsigned int range[2]; + struct device *dev; + struct msm_bus_client_handle *client; + bool is_ar50_gov_used; + bool is_prfm_gov_used; + const char *mode; +}; + +struct bus_set { + struct bus_info *bus_tbl; + u32 count; +}; + +struct reset_info { + struct reset_control *rst; + const char *name; +}; + +struct reset_set { + struct reset_info *reset_tbl; + u32 count; +}; + +struct allowed_clock_rates_table { + u32 clock_rate; +}; + +struct clock_profile_entry { + u32 codec_mask; + u32 vpp_cycles; + u32 vsp_cycles; + u32 low_power_cycles; +}; + +struct clock_freq_table { + struct clock_profile_entry *clk_prof_entries; + u32 count; +}; + +struct subcache_info { + const char *name; + bool isactive; + bool isset; + struct llcc_slice_desc *subcache; +}; + +struct subcache_set { + struct subcache_info *subcache_tbl; + u32 count; +}; + +struct msm_vidc_mem_cdsp { + struct device *dev; +}; + +struct msm_vidc_platform_resources { + phys_addr_t firmware_base; + phys_addr_t register_base; + uint32_t register_size; + uint32_t irq; + uint32_t sku_version; + struct allowed_clock_rates_table *allowed_clks_tbl; + u32 allowed_clks_tbl_size; + struct clock_freq_table clock_freq_tbl; + struct dcvs_table *dcvs_tbl; + uint32_t dcvs_tbl_size; + struct dcvs_limit *dcvs_limit; + bool sys_cache_present; + bool sys_cache_res_set; + struct subcache_set subcache_set; + struct reg_set reg_set; + struct addr_set qdss_addr_set; + struct buffer_usage_set buffer_usage_set; + uint32_t max_load; + uint32_t max_hq_mbs_per_frame; + uint32_t max_hq_mbs_per_sec; + struct platform_device *pdev; + struct regulator_set regulator_set; + struct clock_set clock_set; + struct bus_set bus_set; + struct reset_set reset_set; + bool sw_power_collapsible; + bool slave_side_cp; + struct list_head context_banks; + bool thermal_mitigable; + const char *fw_name; + const char *hfi_version; + bool never_unload_fw; + bool debug_timeout; + uint32_t pm_qos_latency_us; + uint32_t max_inst_count; + uint32_t max_secure_inst_count; + int msm_vidc_hw_rsp_timeout; + int msm_vidc_firmware_unload_delay; + uint32_t msm_vidc_pwr_collapse_delay; + bool domain_cvp; + bool non_fatal_pagefaults; + bool cache_pagetables; + bool decode_batching; + bool dcvs; + struct msm_vidc_codec_data *codec_data; + int codec_data_count; + struct msm_vidc_csc_coeff *csc_coeff_data; + struct msm_vidc_mem_cdsp mem_cdsp; + uint32_t vpu_ver; + uint32_t fw_cycles; + uint32_t fw_vpp_cycles; + uint32_t clk_freq_threshold; + struct cx_ipeak_client *cx_ipeak_context; + struct msm_vidc_ubwc_config *ubwc_config; + uint32_t ubwc_config_length; +}; + + +/** + * The version 1 HFI strcuture for the UBWC configuration + * @bMaxChannelsOverride : enable - 1 /disable - 0 max channel override + * @bMalLengthOverride : enable - 1 /disable - 0 HBB override + * @bHBBOverride : enable - 1 /disable - 0 mal length override + * @nMaxChannels: Num DDR channels 4/8 channel, + * This is to control mircotilling mode. + * @nMalLength : UBWC compression ratio granularity 32B/64B MAL + * @nHighestBankBit : Valid range 13-19 + */ + +struct msm_vidc_ubwc_config_v1 { + struct { + u32 bMaxChannelsOverride : 1; + u32 bMalLengthOverride : 1; + u32 bHBBOverride : 1; + u32 reserved1 : 29; + } sOverrideBitInfo; + + u32 nMaxChannels; + u32 nMalLength; + u32 nHighestBankBit; + u32 reserved2[2]; +}; + +/** + * The version 2 HFI strcuture for the UBWC configuration + * @nSize : the size of the packet in bytes + * @ePacketType: HFI_PROPERTY_SYS_UBWC_CONFIG + * @v1 : The same UBWC config parameters as the version 1 + */ + +struct msm_vidc_ubwc_config { + u32 nSize; + u32 ePacketType; + struct msm_vidc_ubwc_config_v1 v1; +}; + +static inline bool is_iommu_present(struct msm_vidc_platform_resources *res) +{ + return !list_empty(&res->context_banks); +} + +#endif + diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c new file mode 100644 index 000000000000..9e506bd703ac --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -0,0 +1,5247 @@ +/* Copyright (c) 2012-2020, 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 +#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 "hfi_packetization.h" +#include "msm_vidc_debug.h" +#include "venus_hfi.h" +#include "vidc_hfi_io.h" + +#define FIRMWARE_SIZE 0X00A00000 +#define REG_ADDR_OFFSET_BITMASK 0x000FFFFF +#define QDSS_IOVA_START 0x80001000 +#define MIN_PAYLOAD_SIZE 3 + +#define VERSION_HANA (0x5 << 28 | 0x10 << 16) + +static struct hal_device_data hal_ctxt; + +#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8 +struct tzbsp_memprot { + u32 cp_start; + u32 cp_size; + u32 cp_nonpixel_start; + u32 cp_nonpixel_size; +}; + +struct tzbsp_resp { + int ret; +}; + +#define TZBSP_VIDEO_SET_STATE 0xa + +/* Poll interval in uS */ +#define POLL_INTERVAL_US 50 + +enum tzbsp_video_state { + TZBSP_VIDEO_STATE_SUSPEND = 0, + TZBSP_VIDEO_STATE_RESUME = 1, + TZBSP_VIDEO_STATE_RESTORE_THRESHOLD = 2, +}; + +struct tzbsp_video_set_state_req { + u32 state; /* should be tzbsp_video_state enum value */ + u32 spare; /* reserved for future, should be zero */ +}; + +const struct msm_vidc_gov_data DEFAULT_BUS_VOTE = { + .data = NULL, + .data_count = 0, +}; + +const int max_packets = 480; /* 16 sessions x 30 packets */ + +static void venus_hfi_pm_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(venus_hfi_pm_work, venus_hfi_pm_handler); +static inline int __resume(struct venus_hfi_device *device); +static inline int __suspend(struct venus_hfi_device *device); +static int __disable_regulators(struct venus_hfi_device *device); +static int __enable_regulators(struct venus_hfi_device *device); +static inline int __prepare_enable_clks(struct venus_hfi_device *device); +static inline void __disable_unprepare_clks(struct venus_hfi_device *device); +static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet); +static int __initialize_packetization(struct venus_hfi_device *device); +static struct hal_session *__get_session(struct venus_hfi_device *device, + u32 session_id); +static bool __is_session_valid(struct venus_hfi_device *device, + struct hal_session *session, const char *func); +static int __set_clocks(struct venus_hfi_device *device, u32 freq); +static int __iface_cmdq_write(struct venus_hfi_device *device, + void *pkt); +static int __load_fw(struct venus_hfi_device *device); +static void __unload_fw(struct venus_hfi_device *device); +static int __tzbsp_set_video_state(enum tzbsp_video_state state); +static int __enable_subcaches(struct venus_hfi_device *device); +static int __set_subcaches(struct venus_hfi_device *device); +static int __release_subcaches(struct venus_hfi_device *device); +static int __disable_subcaches(struct venus_hfi_device *device); +static int __power_collapse(struct venus_hfi_device *device, bool force); +static int venus_hfi_noc_error_info(void *dev); + +static void interrupt_init_vpu4(struct venus_hfi_device *device); +static void clock_config_on_enable_vpu4(struct venus_hfi_device *device); +static void interrupt_init_vpu5(struct venus_hfi_device *device); +static void setup_dsp_uc_memmap_vpu5(struct venus_hfi_device *device); +static void clock_config_on_enable_vpu5(struct venus_hfi_device *device); + +struct venus_hfi_vpu_ops vpu4_ops = { + .interrupt_init = interrupt_init_vpu4, + .setup_dsp_uc_memmap = NULL, + .clock_config_on_enable = clock_config_on_enable_vpu4, +}; + +struct venus_hfi_vpu_ops vpu5_ops = { + .interrupt_init = interrupt_init_vpu5, + .setup_dsp_uc_memmap = setup_dsp_uc_memmap_vpu5, + .clock_config_on_enable = clock_config_on_enable_vpu5, +}; + +/** + * Utility function to enforce some of our assumptions. Spam calls to this + * in hotspots in code to double check some of the assumptions that we hold. + */ +static inline void __strict_check(struct venus_hfi_device *device) +{ + msm_vidc_res_handle_fatal_hw_error(device->res, + !mutex_is_locked(&device->lock)); +} + +static inline void __set_state(struct venus_hfi_device *device, + enum venus_hfi_state state) +{ + device->state = state; +} + +static inline bool __core_in_valid_state(struct venus_hfi_device *device) +{ + return device->state != VENUS_STATE_DEINIT; +} + +static inline bool is_sys_cache_present(struct venus_hfi_device *device) +{ + return device->res->sys_cache_present; +} + +static void __dump_packet(u8 *packet, enum vidc_msg_prio log_level) +{ + u32 c = 0, packet_size = *(u32 *)packet; + const int row_size = 32; + /* + * row must contain enough for 0xdeadbaad * 8 to be converted into + * "de ad ba ab " * 8 + '\0' + */ + char row[96]; /*char row[3 * row_size];*/ + + for (c = 0; c * row_size < packet_size; ++c) { + int bytes_to_read = ((c + 1) * row_size > packet_size) ? + packet_size % row_size : row_size; + hex_dump_to_buffer(packet + c * row_size, bytes_to_read, + row_size, 4, row, sizeof(row), false); + dprintk(log_level, "%s\n", row); + } +} + +static void __sim_modify_cmd_packet(u8 *packet, struct venus_hfi_device *device) +{ + struct hfi_cmd_sys_session_init_packet *sys_init; + struct hal_session *session = NULL; + u8 i; + phys_addr_t fw_bias = 0; + + if (!device || !packet) { + dprintk(VIDC_ERR, "Invalid Param\n"); + return; + } else if (!device->hal_data->firmware_base + || is_iommu_present(device->res)) { + return; + } + + fw_bias = device->hal_data->firmware_base; + sys_init = (struct hfi_cmd_sys_session_init_packet *)packet; + + session = __get_session(device, sys_init->session_id); + if (!session) { + dprintk(VIDC_DBG, "%s :Invalid session id: %x\n", + __func__, sys_init->session_id); + return; + } + + switch (sys_init->packet_type) { + case HFI_CMD_SESSION_EMPTY_BUFFER: + if (session->is_decoder) { + struct hfi_cmd_session_empty_buffer_compressed_packet + *pkt = (struct + hfi_cmd_session_empty_buffer_compressed_packet + *) packet; + pkt->packet_buffer -= fw_bias; + } else { + struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *pkt = (struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *) packet; + pkt->packet_buffer -= fw_bias; + } + break; + case HFI_CMD_SESSION_FILL_BUFFER: + { + struct hfi_cmd_session_fill_buffer_packet *pkt = + (struct hfi_cmd_session_fill_buffer_packet *)packet; + pkt->packet_buffer -= fw_bias; + break; + } + case HFI_CMD_SESSION_SET_BUFFERS: + { + struct hfi_cmd_session_set_buffers_packet *pkt = + (struct hfi_cmd_session_set_buffers_packet *)packet; + if (pkt->buffer_type == HFI_BUFFER_OUTPUT || + pkt->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + buff->buffer_addr -= fw_bias; + if (buff->extra_data_addr >= fw_bias) + buff->extra_data_addr -= fw_bias; + } else { + for (i = 0; i < pkt->num_buffers; i++) + pkt->rg_buffer_info[i] -= fw_bias; + } + break; + } + case HFI_CMD_SESSION_RELEASE_BUFFERS: + { + struct hfi_cmd_session_release_buffer_packet *pkt = + (struct hfi_cmd_session_release_buffer_packet *)packet; + + if (pkt->buffer_type == HFI_BUFFER_OUTPUT || + pkt->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + buff->buffer_addr -= fw_bias; + buff->extra_data_addr -= fw_bias; + } else { + for (i = 0; i < pkt->num_buffers; i++) + pkt->rg_buffer_info[i] -= fw_bias; + } + break; + } + case HFI_CMD_SESSION_REGISTER_BUFFERS: + { + struct hfi_cmd_session_register_buffers_packet *pkt = + (struct hfi_cmd_session_register_buffers_packet *) + packet; + struct hfi_buffer_mapping_type *buf = + (struct hfi_buffer_mapping_type *)pkt->buffer; + for (i = 0; i < pkt->num_buffers; i++) + buf[i].device_addr -= fw_bias; + break; + } + default: + break; + } +} + +static int __dsp_send_hfi_queue(struct venus_hfi_device *device) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!device->dsp_iface_q_table.mem_data.dma_handle) { + dprintk(VIDC_ERR, "%s: invalid dsm_handle\n", __func__); + return -EINVAL; + } + + if (device->dsp_flags & DSP_INIT) { + dprintk(VIDC_DBG, "%s: dsp already inited\n", __func__); + return 0; + } + + dprintk(VIDC_DBG, "%s: hfi queue %#llx size %d\n", + __func__, device->dsp_iface_q_table.mem_data.dma_handle, + device->dsp_iface_q_table.mem_data.size); + rc = fastcvpd_video_send_cmd_hfi_queue( + (phys_addr_t *)device->dsp_iface_q_table.mem_data.dma_handle, + device->dsp_iface_q_table.mem_data.size); + if (rc) { + dprintk(VIDC_ERR, "%s: dsp init failed\n", __func__); + return rc; + } + + device->dsp_flags |= DSP_INIT; + dprintk(VIDC_DBG, "%s: dsp inited\n", __func__); + return rc; +} + +static int __dsp_suspend(struct venus_hfi_device *device, bool force, u32 flags) +{ + int rc; + struct hal_session *temp; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_INIT)) + return 0; + + if (device->dsp_flags & DSP_SUSPEND) + return 0; + + list_for_each_entry(temp, &device->sess_head, list) { + /* if forceful suspend, don't check session pause info */ + if (force) + continue; + if (temp->domain == HAL_VIDEO_DOMAIN_CVP) { + /* don't suspend if cvp session is not paused */ + if (!(temp->flags & SESSION_PAUSE)) { + dprintk(VIDC_DBG, + "%s: cvp session %x not paused\n", + __func__, hash32_ptr(temp)); + return -EBUSY; + } + } + } + + dprintk(VIDC_DBG, "%s: suspend dsp\n", __func__); + rc = fastcvpd_video_suspend(flags); + if (rc) { + dprintk(VIDC_ERR, "%s: dsp suspend failed with error %d\n", + __func__, rc); + return -EINVAL; + } + + device->dsp_flags |= DSP_SUSPEND; + dprintk(VIDC_DBG, "%s: dsp suspended\n", __func__); + return 0; +} + +static int __dsp_resume(struct venus_hfi_device *device, u32 flags) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_SUSPEND)) { + dprintk(VIDC_DBG, "%s: dsp not suspended\n", __func__); + return 0; + } + + dprintk(VIDC_DBG, "%s: resume dsp\n", __func__); + rc = fastcvpd_video_resume(flags); + if (rc) { + dprintk(VIDC_ERR, + "%s: dsp resume failed with error %d\n", + __func__, rc); + return rc; + } + + device->dsp_flags &= ~DSP_SUSPEND; + dprintk(VIDC_DBG, "%s: dsp resumed\n", __func__); + return rc; +} + +static int __dsp_shutdown(struct venus_hfi_device *device, u32 flags) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_INIT)) { + dprintk(VIDC_DBG, "%s: dsp not inited\n", __func__); + return 0; + } + + dprintk(VIDC_DBG, "%s: shutdown dsp\n", __func__); + rc = fastcvpd_video_shutdown(flags); + if (rc) { + dprintk(VIDC_ERR, + "%s: dsp shutdown failed with error %d\n", + __func__, rc); + WARN_ON(1); + } + + device->dsp_flags &= ~DSP_INIT; + dprintk(VIDC_DBG, "%s: dsp shutdown successful\n", __func__); + return rc; +} + +static int __session_pause(struct venus_hfi_device *device, + struct hal_session *session) +{ + int rc = 0; + + /* ignore if session paused already */ + if (session->flags & SESSION_PAUSE) + return 0; + + session->flags |= SESSION_PAUSE; + dprintk(VIDC_DBG, "%s: cvp session %x paused\n", __func__, + hash32_ptr(session)); + + return rc; +} + +static int __session_resume(struct venus_hfi_device *device, + struct hal_session *session) +{ + int rc = 0; + + /* ignore if session already resumed */ + if (!(session->flags & SESSION_PAUSE)) + return 0; + + session->flags &= ~SESSION_PAUSE; + dprintk(VIDC_DBG, "%s: cvp session %x resumed\n", __func__, + hash32_ptr(session)); + + rc = __resume(device); + if (rc) { + dprintk(VIDC_ERR, "%s: resume failed\n", __func__); + goto exit; + } + + if (device->dsp_flags & DSP_SUSPEND) { + dprintk(VIDC_ERR, "%s: dsp not resumed\n", __func__); + rc = -EINVAL; + goto exit; + } + +exit: + return rc; +} + +static int venus_hfi_session_pause(void *sess) +{ + int rc; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + rc = __session_pause(device, session); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_resume(void *sess) +{ + int rc; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + rc = __session_resume(device, session); + mutex_unlock(&device->lock); + + return rc; +} + +static int __acquire_regulator(struct regulator_info *rinfo, + struct venus_hfi_device *device) +{ + int rc = 0; + + if (rinfo->has_hw_power_collapse) { + rc = regulator_set_mode(rinfo->regulator, + REGULATOR_MODE_NORMAL); + if (rc) { + /* + * This is somewhat fatal, but nothing we can do + * about it. We can't disable the regulator w/o + * getting it back under s/w control + */ + dprintk(VIDC_WARN, + "Failed to acquire regulator control: %s\n", + rinfo->name); + } else { + + dprintk(VIDC_DBG, + "Acquire regulator control from HW: %s\n", + rinfo->name); + + } + } + + if (!regulator_is_enabled(rinfo->regulator)) { + dprintk(VIDC_WARN, "Regulator is not enabled %s\n", + rinfo->name); + msm_vidc_res_handle_fatal_hw_error(device->res, true); + } + + return rc; +} + +static int __hand_off_regulator(struct regulator_info *rinfo) +{ + int rc = 0; + + if (rinfo->has_hw_power_collapse) { + rc = regulator_set_mode(rinfo->regulator, + REGULATOR_MODE_FAST); + if (rc) { + dprintk(VIDC_WARN, + "Failed to hand off regulator control: %s\n", + rinfo->name); + } else { + dprintk(VIDC_DBG, + "Hand off regulator control to HW: %s\n", + rinfo->name); + } + } + + return rc; +} + +static int __hand_off_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo; + int rc = 0, c = 0; + + venus_hfi_for_each_regulator(device, rinfo) { + rc = __hand_off_regulator(rinfo); + /* + * If one regulator hand off failed, driver should take + * the control for other regulators back. + */ + if (rc) + goto err_reg_handoff_failed; + c++; + } + + return rc; +err_reg_handoff_failed: + venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c) + __acquire_regulator(rinfo, device); + + return rc; +} + +static int __write_queue(struct vidc_iface_q_info *qinfo, u8 *packet, + bool *rx_req_is_set) +{ + struct hfi_queue_header *queue; + u32 packet_size_in_words, new_write_idx; + u32 empty_space, read_idx, write_idx; + u32 *write_ptr; + + if (!qinfo || !packet) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } else if (!qinfo->q_array.align_virtual_addr) { + dprintk(VIDC_WARN, "Queues have already been freed\n"); + return -EINVAL; + } + + queue = (struct hfi_queue_header *) qinfo->q_hdr; + if (!queue) { + dprintk(VIDC_ERR, "queue not present\n"); + return -ENOENT; + } + + if (msm_vidc_debug & VIDC_PKT) { + dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo); + __dump_packet(packet, VIDC_PKT); + } + + packet_size_in_words = (*(u32 *)packet) >> 2; + if (!packet_size_in_words || packet_size_in_words > + qinfo->q_array.mem_size>>2) { + dprintk(VIDC_ERR, "Invalid packet size\n"); + return -ENODATA; + } + + read_idx = queue->qhdr_read_idx; + write_idx = queue->qhdr_write_idx; + + empty_space = (write_idx >= read_idx) ? + ((qinfo->q_array.mem_size>>2) - (write_idx - read_idx)) : + (read_idx - write_idx); + if (empty_space <= packet_size_in_words) { + queue->qhdr_tx_req = 1; + dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n", + empty_space, packet_size_in_words); + return -ENOTEMPTY; + } + + queue->qhdr_tx_req = 0; + + new_write_idx = write_idx + packet_size_in_words; + write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + + (write_idx << 2)); + if (write_ptr < (u32 *)qinfo->q_array.align_virtual_addr || + write_ptr > (u32 *)(qinfo->q_array.align_virtual_addr + + qinfo->q_array.mem_size)) { + dprintk(VIDC_ERR, "Invalid write index"); + return -ENODATA; + } + + if (new_write_idx < (qinfo->q_array.mem_size >> 2)) { + memcpy(write_ptr, packet, packet_size_in_words << 2); + } else { + new_write_idx -= qinfo->q_array.mem_size >> 2; + memcpy(write_ptr, packet, (packet_size_in_words - + new_write_idx) << 2); + memcpy((void *)qinfo->q_array.align_virtual_addr, + packet + ((packet_size_in_words - new_write_idx) << 2), + new_write_idx << 2); + } + + /* + * Memory barrier to make sure packet is written before updating the + * write index + */ + mb(); + queue->qhdr_write_idx = new_write_idx; + if (rx_req_is_set) + *rx_req_is_set = queue->qhdr_rx_req == 1; + /* + * Memory barrier to make sure write index is updated before an + * interrupt is raised on venus. + */ + mb(); + return 0; +} + +static void __hal_sim_modify_msg_packet(u8 *packet, + struct venus_hfi_device *device) +{ + struct hfi_msg_sys_session_init_done_packet *init_done; + struct hal_session *session = NULL; + phys_addr_t fw_bias = 0; + + if (!device || !packet) { + dprintk(VIDC_ERR, "Invalid Param\n"); + return; + } else if (!device->hal_data->firmware_base + || is_iommu_present(device->res)) { + return; + } + + fw_bias = device->hal_data->firmware_base; + init_done = (struct hfi_msg_sys_session_init_done_packet *)packet; + session = __get_session(device, init_done->session_id); + + if (!session) { + dprintk(VIDC_DBG, "%s: Invalid session id: %x\n", + __func__, init_done->session_id); + return; + } + + switch (init_done->packet_type) { + case HFI_MSG_SESSION_FILL_BUFFER_DONE: + if (session->is_decoder) { + struct + hfi_msg_session_fbd_uncompressed_plane0_packet + *pkt_uc = (struct + hfi_msg_session_fbd_uncompressed_plane0_packet + *) packet; + pkt_uc->packet_buffer += fw_bias; + } else { + struct + hfi_msg_session_fill_buffer_done_compressed_packet + *pkt = (struct + hfi_msg_session_fill_buffer_done_compressed_packet + *) packet; + pkt->packet_buffer += fw_bias; + } + break; + case HFI_MSG_SESSION_EMPTY_BUFFER_DONE: + { + struct hfi_msg_session_empty_buffer_done_packet *pkt = + (struct hfi_msg_session_empty_buffer_done_packet *)packet; + pkt->packet_buffer += fw_bias; + break; + } + case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE: + { + struct + hfi_msg_session_get_sequence_header_done_packet + *pkt = + (struct hfi_msg_session_get_sequence_header_done_packet *) + packet; + pkt->sequence_header += fw_bias; + break; + } + default: + break; + } +} + +static int __read_queue(struct vidc_iface_q_info *qinfo, u8 *packet, + u32 *pb_tx_req_is_set) +{ + struct hfi_queue_header *queue; + u32 packet_size_in_words, new_read_idx; + u32 *read_ptr; + u32 receive_request = 0; + u32 read_idx, write_idx; + int rc = 0; + + if (!qinfo || !packet || !pb_tx_req_is_set) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } else if (!qinfo->q_array.align_virtual_addr) { + dprintk(VIDC_WARN, "Queues have already been freed\n"); + return -EINVAL; + } + + /* + * Memory barrier to make sure data is valid before + *reading it + */ + mb(); + queue = (struct hfi_queue_header *) qinfo->q_hdr; + + if (!queue) { + dprintk(VIDC_ERR, "Queue memory is not allocated\n"); + return -ENOMEM; + } + + /* + * Do not set receive request for debug queue, if set, + * Venus generates interrupt for debug messages even + * when there is no response message available. + * In general debug queue will not become full as it + * is being emptied out for every interrupt from Venus. + * Venus will anyway generates interrupt if it is full. + */ + if (queue->qhdr_type & HFI_Q_ID_CTRL_TO_HOST_MSG_Q) + receive_request = 1; + + read_idx = queue->qhdr_read_idx; + write_idx = queue->qhdr_write_idx; + + if (read_idx == write_idx) { + queue->qhdr_rx_req = receive_request; + /* + * mb() to ensure qhdr is updated in main memory + * so that venus reads the updated header values + */ + mb(); + *pb_tx_req_is_set = 0; + dprintk(VIDC_DBG, + "%s queue is empty, rx_req = %u, tx_req = %u, read_idx = %u\n", + receive_request ? "message" : "debug", + queue->qhdr_rx_req, queue->qhdr_tx_req, + queue->qhdr_read_idx); + return -ENODATA; + } + + read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + + (read_idx << 2)); + if (read_ptr < (u32 *)qinfo->q_array.align_virtual_addr || + read_ptr > (u32 *)(qinfo->q_array.align_virtual_addr + + qinfo->q_array.mem_size - sizeof(*read_ptr))) { + dprintk(VIDC_ERR, "Invalid read index\n"); + return -ENODATA; + } + + packet_size_in_words = (*read_ptr) >> 2; + if (!packet_size_in_words) { + dprintk(VIDC_ERR, "Zero packet size\n"); + return -ENODATA; + } + + new_read_idx = read_idx + packet_size_in_words; + if (((packet_size_in_words << 2) <= VIDC_IFACEQ_VAR_HUGE_PKT_SIZE) && + read_idx <= (qinfo->q_array.mem_size >> 2)) { + if (new_read_idx < (qinfo->q_array.mem_size >> 2)) { + memcpy(packet, read_ptr, + packet_size_in_words << 2); + } else { + new_read_idx -= (qinfo->q_array.mem_size >> 2); + memcpy(packet, read_ptr, + (packet_size_in_words - new_read_idx) << 2); + memcpy(packet + ((packet_size_in_words - + new_read_idx) << 2), + (u8 *)qinfo->q_array.align_virtual_addr, + new_read_idx << 2); + } + } else { + dprintk(VIDC_WARN, + "BAD packet received, read_idx: %#x, pkt_size: %d\n", + read_idx, packet_size_in_words << 2); + dprintk(VIDC_WARN, "Dropping this packet\n"); + new_read_idx = write_idx; + rc = -ENODATA; + } + + if (new_read_idx != write_idx) + queue->qhdr_rx_req = 0; + else + queue->qhdr_rx_req = receive_request; + + queue->qhdr_read_idx = new_read_idx; + /* + * mb() to ensure qhdr is updated in main memory + * so that venus reads the updated header values + */ + mb(); + + *pb_tx_req_is_set = (queue->qhdr_tx_req == 1) ? 1 : 0; + + if ((msm_vidc_debug & VIDC_PKT) && + !(queue->qhdr_type & HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q)) { + dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo); + __dump_packet(packet, VIDC_PKT); + } + + return rc; +} + +static int __smem_alloc(struct venus_hfi_device *dev, + struct vidc_mem_addr *mem, u32 size, u32 align, + u32 flags, u32 usage) +{ + struct msm_smem *alloc = &mem->mem_data; + int rc = 0; + + if (!dev || !mem || !size) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + dprintk(VIDC_INFO, "start to alloc size: %d, flags: %d\n", size, flags); + rc = msm_smem_alloc( + size, align, flags, usage, 1, (void *)dev->res, + MSM_VIDC_UNKNOWN, alloc); + if (rc) { + dprintk(VIDC_ERR, "Alloc failed\n"); + rc = -ENOMEM; + goto fail_smem_alloc; + } + + dprintk(VIDC_DBG, "%s: ptr = %pK, size = %d\n", __func__, + alloc->kvaddr, size); + + mem->mem_size = alloc->size; + mem->align_virtual_addr = alloc->kvaddr; + mem->align_device_addr = alloc->device_addr; + + return rc; +fail_smem_alloc: + return rc; +} + +static void __smem_free(struct venus_hfi_device *dev, struct msm_smem *mem) +{ + if (!dev || !mem) { + dprintk(VIDC_ERR, "invalid param %pK %pK\n", dev, mem); + return; + } + + msm_smem_free(mem); +} + +static void __write_register(struct venus_hfi_device *device, + u32 reg, u32 value) +{ + u32 hwiosymaddr = reg; + u8 *base_addr; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return; + } + + __strict_check(device); + + if (!device->power_enabled) { + dprintk(VIDC_WARN, + "HFI Write register failed : Power is OFF\n"); + msm_vidc_res_handle_fatal_hw_error(device->res, true); + return; + } + + base_addr = device->hal_data->register_base; + dprintk(VIDC_DBG, "Base addr: %pK, written to: %#x, Value: %#x...\n", + base_addr, hwiosymaddr, value); + base_addr += hwiosymaddr; + writel_relaxed(value, base_addr); + + /* + * Memory barrier to make sure value is written into the register. + */ + wmb(); +} + +static int __read_register(struct venus_hfi_device *device, u32 reg) +{ + int rc = 0; + u8 *base_addr; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + __strict_check(device); + + if (!device->power_enabled) { + dprintk(VIDC_WARN, + "HFI Read register failed : Power is OFF\n"); + msm_vidc_res_handle_fatal_hw_error(device->res, true); + return -EINVAL; + } + + base_addr = device->hal_data->register_base; + + rc = readl_relaxed(base_addr + reg); + /* + * Memory barrier to make sure value is read correctly from the + * register. + */ + rmb(); + dprintk(VIDC_DBG, "Base addr: %pK, read from: %#x, value: %#x...\n", + base_addr, reg, rc); + + return rc; +} + +static void __set_registers(struct venus_hfi_device *device) +{ + struct reg_set *reg_set; + int i; + + if (!device->res) { + dprintk(VIDC_ERR, + "device resources null, cannot set registers\n"); + return; + } + + reg_set = &device->res->reg_set; + for (i = 0; i < reg_set->count; i++) { + __write_register(device, reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value); + } +} + +/* + * The existence of this function is a hack for 8996 (or certain Venus versions) + * to overcome a hardware bug. Whenever the GDSCs momentarily power collapse + * (after calling __hand_off_regulators()), the values of the threshold + * registers (typically programmed by TZ) are incorrectly reset. As a result + * reprogram these registers at certain agreed upon points. + */ +static void __set_threshold_registers(struct venus_hfi_device *device) +{ + u32 version = __read_register(device, VIDC_WRAPPER_HW_VERSION); + + version &= ~GENMASK(15, 0); + if (version != (0x3 << 28 | 0x43 << 16)) + return; + + if (__tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESTORE_THRESHOLD)) + dprintk(VIDC_ERR, "Failed to restore threshold values\n"); +} + +static int __vote_bandwidth(struct bus_info *bus, + unsigned long *freq) +{ + int rc = 0; + uint64_t ab = 0; + + if (*freq) + *freq = clamp_t(typeof(*freq), *freq, bus->range[0], + bus->range[1]); + + /* Bus Driver expects values in Bps */ + ab = *freq * 1000; + dprintk(VIDC_PROF, "Voting bus %s to ab %llu\n", bus->name, ab); + rc = msm_bus_scale_update_bw(bus->client, ab, 0); + if (rc) + dprintk(VIDC_ERR, "Failed voting bus %s to ab %llu, rc=%d\n", + bus->name, ab, rc); + + return rc; +} + +static int __unvote_buses(struct venus_hfi_device *device) +{ + int rc = 0; + struct bus_info *bus = NULL; + unsigned long freq = 0, zero = 0; + + kfree(device->bus_vote.data); + device->bus_vote.data = NULL; + device->bus_vote.data_count = 0; + + venus_hfi_for_each_bus(device, bus) { + if (!bus->is_prfm_gov_used) { + if (bus->is_ar50_gov_used) + freq = __calc_bw_ar50(bus, &device->bus_vote); + else + freq = __calc_bw(bus, &device->bus_vote); + rc = __vote_bandwidth(bus, &freq); + } + else + rc = __vote_bandwidth(bus, &zero); + + if (rc) + goto err_unknown_device; + } + +err_unknown_device: + return rc; +} + +static int __vote_buses(struct venus_hfi_device *device, + struct vidc_bus_vote_data *data, int num_data) +{ + int rc = 0; + struct bus_info *bus = NULL; + struct vidc_bus_vote_data *new_data = NULL; + unsigned long freq = 0; + + if (!num_data) { + dprintk(VIDC_DBG, "No vote data available\n"); + goto no_data_count; + } else if (!data) { + dprintk(VIDC_ERR, "Invalid voting data\n"); + return -EINVAL; + } + + new_data = kmemdup(data, num_data * sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + dprintk(VIDC_ERR, "Can't alloc memory to cache bus votes\n"); + rc = -ENOMEM; + goto err_no_mem; + } + +no_data_count: + kfree(device->bus_vote.data); + device->bus_vote.data = new_data; + device->bus_vote.data_count = num_data; + + venus_hfi_for_each_bus(device, bus) { + if (bus && bus->client) { + if (!bus->is_prfm_gov_used) { + if (bus->is_ar50_gov_used) + freq = __calc_bw_ar50(bus, &device->bus_vote); + else + freq = __calc_bw(bus, &device->bus_vote); + } else { + freq = bus->range[1]; + dprintk(VIDC_DBG, "%s %s perf Vote %u\n", + __func__, bus->name, + bus->range[1]); + } + rc = __vote_bandwidth(bus, &freq); + } else { + dprintk(VIDC_ERR, "No BUS to Vote\n"); + } + } + +err_no_mem: + return rc; +} + +static int venus_hfi_vote_buses(void *dev, struct vidc_bus_vote_data *d, int n) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) + return -EINVAL; + + mutex_lock(&device->lock); + rc = __vote_buses(device, d, n); + mutex_unlock(&device->lock); + + return rc; + +} +static int __core_set_resource(struct venus_hfi_device *device, + struct vidc_resource_hdr *resource_hdr, void *resource_value) +{ + struct hfi_cmd_sys_set_resource_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + + if (!device || !resource_hdr || !resource_value) { + dprintk(VIDC_ERR, "set_res: Invalid Params\n"); + return -EINVAL; + } + + pkt = (struct hfi_cmd_sys_set_resource_packet *) packet; + + rc = call_hfi_pkt_op(device, sys_set_resource, + pkt, resource_hdr, resource_value); + if (rc) { + dprintk(VIDC_ERR, "set_res: failed to create packet\n"); + goto err_create_pkt; + } + + rc = __iface_cmdq_write(device, pkt); + if (rc) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int __core_release_resource(struct venus_hfi_device *device, + struct vidc_resource_hdr *resource_hdr) +{ + struct hfi_cmd_sys_release_resource_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + + if (!device || !resource_hdr) { + dprintk(VIDC_ERR, "release_res: Invalid Params\n"); + return -EINVAL; + } + + pkt = (struct hfi_cmd_sys_release_resource_packet *) packet; + + rc = call_hfi_pkt_op(device, sys_release_resource, + pkt, resource_hdr); + + if (rc) { + dprintk(VIDC_ERR, "release_res: failed to create packet\n"); + goto err_create_pkt; + } + + rc = __iface_cmdq_write(device, pkt); + if (rc) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int __tzbsp_set_video_state(enum tzbsp_video_state state) +{ + struct tzbsp_video_set_state_req cmd = {0}; + int tzbsp_rsp = 0; + int rc = 0; + struct scm_desc desc = {0}; + + desc.args[0] = cmd.state = state; + desc.args[1] = cmd.spare = 0; + desc.arginfo = SCM_ARGS(2); + + rc = scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, + TZBSP_VIDEO_SET_STATE), &desc); + tzbsp_rsp = desc.ret[0]; + + if (rc) { + dprintk(VIDC_ERR, "Failed scm_call %d\n", rc); + return rc; + } + + dprintk(VIDC_DBG, "Set state %d, resp %d\n", state, tzbsp_rsp); + if (tzbsp_rsp) { + dprintk(VIDC_ERR, + "Failed to set video core state to suspend: %d\n", + tzbsp_rsp); + return -EINVAL; + } + + return 0; +} + +static inline int __boot_firmware(struct venus_hfi_device *device) +{ + int rc = 0; + u32 ctrl_init_val = 0, ctrl_status = 0, count = 0, max_tries = 1000; + + ctrl_init_val = BIT(0); + if (device->res->domain_cvp) + ctrl_init_val |= BIT(1); + + __write_register(device, VIDC_CTRL_INIT, ctrl_init_val); + while (!ctrl_status && count < max_tries) { + ctrl_status = __read_register(device, VIDC_CTRL_STATUS); + if ((ctrl_status & VIDC_CTRL_ERROR_STATUS__M) == 0x4) { + dprintk(VIDC_ERR, "invalid setting for UC_REGION\n"); + break; + } + + usleep_range(50, 100); + count++; + } + + if (count >= max_tries) { + dprintk(VIDC_ERR, "Error booting up vidc firmware\n"); + rc = -ETIME; + } + return rc; +} + +static int venus_hfi_suspend(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } else if (!device->res->sw_power_collapsible) { + return -ENOTSUPP; + } + + dprintk(VIDC_DBG, "Suspending Venus\n"); + mutex_lock(&device->lock); + rc = __power_collapse(device, true); + if (rc) { + dprintk(VIDC_WARN, "%s: Venus is busy\n", __func__); + rc = -EBUSY; + } + mutex_unlock(&device->lock); + + /* Cancel pending delayed works if any */ + if (!rc) + cancel_delayed_work(&venus_hfi_pm_work); + + return rc; +} + +static int venus_hfi_flush_debug_queue(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + if (!device->power_enabled) { + dprintk(VIDC_WARN, "%s: venus power off\n", __func__); + rc = -EINVAL; + goto exit; + } + __flush_debug_queue(device, NULL); +exit: + mutex_unlock(&device->lock); + return rc; +} + +static enum hal_default_properties venus_hfi_get_default_properties(void *dev) +{ + enum hal_default_properties prop = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + prop = HAL_VIDEO_DYNAMIC_BUF_MODE; + + mutex_unlock(&device->lock); + return prop; +} + +static int __set_clk_rate(struct venus_hfi_device *device, + struct clock_info *cl, u64 rate) +{ + int rc = 0; + u64 threshold_freq = device->res->clk_freq_threshold; + struct cx_ipeak_client *ipeak = device->res->cx_ipeak_context; + struct clk *clk = cl->clk; + + if (device->clk_freq < threshold_freq && rate >= threshold_freq) { + rc = cx_ipeak_update(ipeak, true); + if (rc) { + dprintk(VIDC_ERR, + "cx_ipeak_update failed! ipeak %pK\n", ipeak); + return rc; + } + dprintk(VIDC_PROF, "cx_ipeak_update: up, clk freq = %u\n", + device->clk_freq); + } + + rc = clk_set_rate(clk, rate); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set clock rate %llu %s: %d\n", + __func__, rate, cl->name, rc); + return rc; + } + + if (device->clk_freq >= threshold_freq && rate < threshold_freq) { + rc = cx_ipeak_update(ipeak, false); + if (rc) { + dprintk(VIDC_ERR, + "cx_ipeak_update failed! ipeak %pK\n", ipeak); + device->clk_freq = rate; + return rc; + } + dprintk(VIDC_PROF, "cx_ipeak_update: down, clk freq = %u\n", + device->clk_freq); + } + + device->clk_freq = rate; + + return rc; +} + +static int __set_clocks(struct venus_hfi_device *device, u32 freq) +{ + struct clock_info *cl; + int rc = 0; + + venus_hfi_for_each_clock(device, cl) { + if (cl->has_scaling) {/* has_scaling */ + rc = __set_clk_rate(device, cl, freq); + if (rc) + return rc; + + trace_msm_vidc_perf_clock_scale(cl->name, freq); + dprintk(VIDC_PROF, "Scaling clock %s to %u\n", + cl->name, freq); + } + } + + return 0; +} + +static int venus_hfi_scale_clocks(void *dev, u32 freq) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) { + dprintk(VIDC_ERR, "Invalid args: %pK\n", device); + return -EINVAL; + } + + mutex_lock(&device->lock); + + if (__resume(device)) { + dprintk(VIDC_ERR, "Resume from power collapse failed\n"); + rc = -ENODEV; + goto exit; + } + + rc = __set_clocks(device, freq); +exit: + mutex_unlock(&device->lock); + + return rc; +} + +static int __scale_clocks(struct venus_hfi_device *device) +{ + int rc = 0; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + u32 rate = 0; + + allowed_clks_tbl = device->res->allowed_clks_tbl; + + dprintk(VIDC_DBG, "%s: NULL scale data\n", __func__); + rate = device->clk_freq ? device->clk_freq : + allowed_clks_tbl[0].clock_rate; + + rc = __set_clocks(device, rate); + return rc; +} + +/* Writes into cmdq without raising an interrupt */ +static int __iface_cmdq_write_relaxed(struct venus_hfi_device *device, + void *pkt, bool *requires_interrupt) +{ + struct vidc_iface_q_info *q_info; + struct vidc_hal_cmd_pkt_hdr *cmd_packet; + int result = -E2BIG; + + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_ERR, "%s - fw not in init state\n", __func__); + result = -EINVAL; + goto err_q_null; + } + + cmd_packet = (struct vidc_hal_cmd_pkt_hdr *)pkt; + device->last_packet_type = cmd_packet->packet_type; + + q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; + if (!q_info) { + dprintk(VIDC_ERR, "cannot write to shared Q's\n"); + goto err_q_null; + } + + if (!q_info->q_array.align_virtual_addr) { + dprintk(VIDC_ERR, "cannot write to shared CMD Q's\n"); + result = -ENODATA; + goto err_q_null; + } + + __sim_modify_cmd_packet((u8 *)pkt, device); + if (__resume(device)) { + dprintk(VIDC_ERR, "%s: Power on failed\n", __func__); + goto err_q_write; + } + + if (!__write_queue(q_info, (u8 *)pkt, requires_interrupt)) { + if (device->res->sw_power_collapsible) { + cancel_delayed_work(&venus_hfi_pm_work); + if (!queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, + msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay))) { + dprintk(VIDC_DBG, + "PM work already scheduled\n"); + } + } + + result = 0; + } else { + dprintk(VIDC_ERR, "__iface_cmdq_write: queue full\n"); + } + +err_q_write: +err_q_null: + return result; +} + +static int __iface_cmdq_write(struct venus_hfi_device *device, void *pkt) +{ + bool needs_interrupt = false; + int rc = __iface_cmdq_write_relaxed(device, pkt, &needs_interrupt); + + if (!rc && needs_interrupt) { + /* Consumer of cmdq prefers that we raise an interrupt */ + rc = 0; + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + } + + return rc; +} + +static int __iface_msgq_read(struct venus_hfi_device *device, void *pkt) +{ + u32 tx_req_is_set = 0; + int rc = 0; + struct vidc_iface_q_info *q_info; + + if (!pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__); + rc = -EINVAL; + goto read_error_null; + } + + q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + if (q_info->q_array.align_virtual_addr == 0) { + dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n"); + rc = -ENODATA; + goto read_error_null; + } + + if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + __hal_sim_modify_msg_packet((u8 *)pkt, device); + if (tx_req_is_set) + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +read_error_null: + return rc; +} + +static int __iface_dbgq_read(struct venus_hfi_device *device, void *pkt) +{ + u32 tx_req_is_set = 0; + int rc = 0; + struct vidc_iface_q_info *q_info; + + if (!pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + q_info = &device->iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + if (q_info->q_array.align_virtual_addr == 0) { + dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n"); + rc = -ENODATA; + goto dbg_error_null; + } + + if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + if (tx_req_is_set) + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +dbg_error_null: + return rc; +} + +static void __set_queue_hdr_defaults(struct hfi_queue_header *q_hdr) +{ + q_hdr->qhdr_status = 0x1; + q_hdr->qhdr_type = VIDC_IFACEQ_DFLT_QHDR; + q_hdr->qhdr_q_size = VIDC_IFACEQ_QUEUE_SIZE / 4; + q_hdr->qhdr_pkt_size = 0; + q_hdr->qhdr_rx_wm = 0x1; + q_hdr->qhdr_tx_wm = 0x1; + q_hdr->qhdr_rx_req = 0x1; + q_hdr->qhdr_tx_req = 0x0; + q_hdr->qhdr_rx_irq_status = 0x0; + q_hdr->qhdr_tx_irq_status = 0x0; + q_hdr->qhdr_read_idx = 0x0; + q_hdr->qhdr_write_idx = 0x0; +} + +static void __interface_dsp_queues_release(struct venus_hfi_device *device) +{ + int i; + struct msm_smem *mem_data = &device->dsp_iface_q_table.mem_data; + struct context_bank_info *cb = mem_data->mapping_info.cb_info; + + if (!device->dsp_iface_q_table.align_virtual_addr) { + dprintk(VIDC_ERR, "%s: already released\n", __func__); + return; + } + + dma_unmap_single_attrs(cb->dev, mem_data->device_addr, + mem_data->size, DMA_BIDIRECTIONAL, 0); + dma_free_coherent(device->res->mem_cdsp.dev, mem_data->size, + mem_data->kvaddr, mem_data->dma_handle); + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + device->dsp_iface_queues[i].q_hdr = NULL; + device->dsp_iface_queues[i].q_array.align_virtual_addr = NULL; + device->dsp_iface_queues[i].q_array.align_device_addr = 0; + } + device->dsp_iface_q_table.align_virtual_addr = NULL; + device->dsp_iface_q_table.align_device_addr = 0; +} + +static int __interface_dsp_queues_init(struct venus_hfi_device *dev) +{ + int rc = 0; + u32 i; + struct hfi_queue_table_header *q_tbl_hdr; + struct hfi_queue_header *q_hdr; + struct vidc_iface_q_info *iface_q; + int offset = 0; + phys_addr_t fw_bias = 0; + size_t q_size; + struct msm_smem *mem_data; + void *kvaddr; + dma_addr_t dma_handle; + dma_addr_t iova; + struct context_bank_info *cb; + + q_size = ALIGN(QUEUE_SIZE, SZ_1M); + mem_data = &dev->dsp_iface_q_table.mem_data; + + /* Allocate dsp queues from ADSP device memory */ + kvaddr = dma_alloc_coherent(dev->res->mem_cdsp.dev, q_size, + &dma_handle, GFP_KERNEL); + if (IS_ERR_OR_NULL(kvaddr)) { + dprintk(VIDC_ERR, "%s: failed dma allocation\n", __func__); + goto fail_dma_alloc; + } + cb = msm_smem_get_context_bank(MSM_VIDC_UNKNOWN, 0, + dev->res, HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (!cb) { + dprintk(VIDC_ERR, + "%s: failed to get context bank\n", __func__); + goto fail_dma_map; + } + iova = dma_map_single_attrs(cb->dev, phys_to_virt(dma_handle), + q_size, DMA_BIDIRECTIONAL, 0); + if (dma_mapping_error(cb->dev, iova)) { + dprintk(VIDC_ERR, "%s: failed dma mapping\n", __func__); + goto fail_dma_map; + } + dprintk(VIDC_DBG, + "%s: kvaddr %pK dma_handle %#llx iova %#llx size %ld\n", + __func__, kvaddr, dma_handle, iova, q_size); + + memset(mem_data, 0, sizeof(struct msm_smem)); + mem_data->kvaddr = kvaddr; + mem_data->device_addr = iova; + mem_data->dma_handle = dma_handle; + mem_data->size = q_size; + mem_data->buffer_type = HAL_BUFFER_INTERNAL_CMD_QUEUE; + mem_data->mapping_info.cb_info = cb; + + if (!is_iommu_present(dev->res)) + fw_bias = dev->hal_data->firmware_base; + + dev->dsp_iface_q_table.align_virtual_addr = kvaddr; + dev->dsp_iface_q_table.align_device_addr = iova - fw_bias; + dev->dsp_iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE; + offset = dev->dsp_iface_q_table.mem_size; + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + iface_q = &dev->dsp_iface_queues[i]; + iface_q->q_array.align_device_addr = iova + offset - fw_bias; + iface_q->q_array.align_virtual_addr = kvaddr + offset; + iface_q->q_array.mem_size = VIDC_IFACEQ_QUEUE_SIZE; + offset += iface_q->q_array.mem_size; + iface_q->q_hdr = VIDC_IFACEQ_GET_QHDR_START_ADDR( + dev->dsp_iface_q_table.align_virtual_addr, i); + __set_queue_hdr_defaults(iface_q->q_hdr); + } + + q_tbl_hdr = (struct hfi_queue_table_header *) + dev->dsp_iface_q_table.align_virtual_addr; + q_tbl_hdr->qtbl_version = 0; + q_tbl_hdr->device_addr = (void *)dev; + strlcpy(q_tbl_hdr->name, "msm_v4l2_vidc", sizeof(q_tbl_hdr->name)); + q_tbl_hdr->qtbl_size = VIDC_IFACEQ_TABLE_SIZE; + q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_table_header); + q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header); + q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ; + q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ; + + iface_q = &dev->dsp_iface_queues[VIDC_IFACEQ_CMDQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_HOST_TO_CTRL_CMD_Q; + + iface_q = &dev->dsp_iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_MSG_Q; + + iface_q = &dev->dsp_iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q; + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + q_hdr->qhdr_rx_req = 0; + return rc; + +fail_dma_map: + dma_free_coherent(dev->res->mem_cdsp.dev, q_size, kvaddr, dma_handle); +fail_dma_alloc: + return -ENOMEM; +} + +static void __interface_queues_release(struct venus_hfi_device *device) +{ + int i; + struct hfi_mem_map_table *qdss; + struct hfi_mem_map *mem_map; + int num_entries = device->res->qdss_addr_set.count; + unsigned long mem_map_table_base_addr; + struct context_bank_info *cb; + + if (device->qdss.align_virtual_addr) { + qdss = (struct hfi_mem_map_table *) + device->qdss.align_virtual_addr; + qdss->mem_map_num_entries = num_entries; + mem_map_table_base_addr = + device->qdss.align_device_addr + + sizeof(struct hfi_mem_map_table); + qdss->mem_map_table_base_addr = + (u32)mem_map_table_base_addr; + if ((unsigned long)qdss->mem_map_table_base_addr != + mem_map_table_base_addr) { + dprintk(VIDC_ERR, + "Invalid mem_map_table_base_addr %#lx", + mem_map_table_base_addr); + } + + mem_map = (struct hfi_mem_map *)(qdss + 1); + cb = msm_smem_get_context_bank(MSM_VIDC_UNKNOWN, + false, device->res, HAL_BUFFER_INTERNAL_CMD_QUEUE); + + for (i = 0; cb && i < num_entries; i++) { + iommu_unmap(cb->domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + + __smem_free(device, &device->qdss.mem_data); + } + + __smem_free(device, &device->iface_q_table.mem_data); + __smem_free(device, &device->sfr.mem_data); + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + device->iface_queues[i].q_hdr = NULL; + device->iface_queues[i].q_array.align_virtual_addr = NULL; + device->iface_queues[i].q_array.align_device_addr = 0; + } + + device->iface_q_table.align_virtual_addr = NULL; + device->iface_q_table.align_device_addr = 0; + + device->qdss.align_virtual_addr = NULL; + device->qdss.align_device_addr = 0; + + device->sfr.align_virtual_addr = NULL; + device->sfr.align_device_addr = 0; + + device->mem_addr.align_virtual_addr = NULL; + device->mem_addr.align_device_addr = 0; + + if (device->res->domain_cvp) + __interface_dsp_queues_release(device); +} + +static int __get_qdss_iommu_virtual_addr(struct venus_hfi_device *dev, + struct hfi_mem_map *mem_map, struct iommu_domain *domain) +{ + int i; + int rc = 0; + dma_addr_t iova = QDSS_IOVA_START; + int num_entries = dev->res->qdss_addr_set.count; + struct addr_range *qdss_addr_tbl = dev->res->qdss_addr_set.addr_tbl; + + if (!num_entries) + return -ENODATA; + + for (i = 0; i < num_entries; i++) { + if (domain) { + rc = iommu_map(domain, iova, + qdss_addr_tbl[i].start, + qdss_addr_tbl[i].size, + IOMMU_READ | IOMMU_WRITE); + + if (rc) { + dprintk(VIDC_ERR, + "IOMMU QDSS mapping failed for addr %#x\n", + qdss_addr_tbl[i].start); + rc = -ENOMEM; + break; + } + } else { + iova = qdss_addr_tbl[i].start; + } + + mem_map[i].virtual_addr = (u32)iova; + mem_map[i].physical_addr = qdss_addr_tbl[i].start; + mem_map[i].size = qdss_addr_tbl[i].size; + mem_map[i].attr = 0x0; + + iova += mem_map[i].size; + } + + if (i < num_entries) { + dprintk(VIDC_ERR, + "QDSS mapping failed, Freeing other entries %d\n", i); + + for (--i; domain && i >= 0; i--) { + iommu_unmap(domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + } + + return rc; +} + +static void __setup_ucregion_memory_map(struct venus_hfi_device *device) +{ + __write_register(device, VIDC_UC_REGION_ADDR, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, VIDC_UC_REGION_SIZE, SHARED_QSIZE); + __write_register(device, VIDC_QTBL_ADDR, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, VIDC_QTBL_INFO, 0x01); + if (device->sfr.align_device_addr) + __write_register(device, VIDC_SFR_ADDR, + (u32)device->sfr.align_device_addr); + if (device->qdss.align_device_addr) + __write_register(device, VIDC_MMAP_ADDR, + (u32)device->qdss.align_device_addr); + call_venus_op(device, setup_dsp_uc_memmap, device); +} + +static int __interface_queues_init(struct venus_hfi_device *dev) +{ + struct hfi_queue_table_header *q_tbl_hdr; + struct hfi_queue_header *q_hdr; + u32 i; + int rc = 0; + struct hfi_mem_map_table *qdss; + struct hfi_mem_map *mem_map; + struct vidc_iface_q_info *iface_q; + struct hfi_sfr_struct *vsfr; + struct vidc_mem_addr *mem_addr; + int offset = 0; + int num_entries = dev->res->qdss_addr_set.count; + phys_addr_t fw_bias = 0; + size_t q_size; + unsigned long mem_map_table_base_addr; + struct context_bank_info *cb; + + q_size = SHARED_QSIZE - ALIGNED_SFR_SIZE - ALIGNED_QDSS_SIZE; + mem_addr = &dev->mem_addr; + if (!is_iommu_present(dev->res)) + fw_bias = dev->hal_data->firmware_base; + rc = __smem_alloc(dev, mem_addr, q_size, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_ERR, "iface_q_table_alloc_fail\n"); + goto fail_alloc_queue; + } + + dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr; + dev->iface_q_table.align_device_addr = mem_addr->align_device_addr - + fw_bias; + dev->iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE; + dev->iface_q_table.mem_data = mem_addr->mem_data; + offset += dev->iface_q_table.mem_size; + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + iface_q = &dev->iface_queues[i]; + iface_q->q_array.align_device_addr = mem_addr->align_device_addr + + offset - fw_bias; + iface_q->q_array.align_virtual_addr = + mem_addr->align_virtual_addr + offset; + iface_q->q_array.mem_size = VIDC_IFACEQ_QUEUE_SIZE; + offset += iface_q->q_array.mem_size; + iface_q->q_hdr = VIDC_IFACEQ_GET_QHDR_START_ADDR( + dev->iface_q_table.align_virtual_addr, i); + __set_queue_hdr_defaults(iface_q->q_hdr); + } + + if ((msm_vidc_fw_debug_mode & HFI_DEBUG_MODE_QDSS) && num_entries) { + rc = __smem_alloc(dev, mem_addr, + ALIGNED_QDSS_SIZE, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_WARN, + "qdss_alloc_fail: QDSS messages logging will not work\n"); + dev->qdss.align_device_addr = 0; + } else { + dev->qdss.align_device_addr = + mem_addr->align_device_addr - fw_bias; + dev->qdss.align_virtual_addr = + mem_addr->align_virtual_addr; + dev->qdss.mem_size = ALIGNED_QDSS_SIZE; + dev->qdss.mem_data = mem_addr->mem_data; + } + } + + rc = __smem_alloc(dev, mem_addr, + ALIGNED_SFR_SIZE, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work\n"); + dev->sfr.align_device_addr = 0; + } else { + dev->sfr.align_device_addr = mem_addr->align_device_addr - + fw_bias; + dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr; + dev->sfr.mem_size = ALIGNED_SFR_SIZE; + dev->sfr.mem_data = mem_addr->mem_data; + vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr; + vsfr->bufSize = ALIGNED_SFR_SIZE; + } + + q_tbl_hdr = (struct hfi_queue_table_header *) + dev->iface_q_table.align_virtual_addr; + q_tbl_hdr->qtbl_version = 0; + q_tbl_hdr->device_addr = (void *)dev; + strlcpy(q_tbl_hdr->name, "msm_v4l2_vidc", sizeof(q_tbl_hdr->name)); + q_tbl_hdr->qtbl_size = VIDC_IFACEQ_TABLE_SIZE; + q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_table_header); + q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header); + q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ; + q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ; + + iface_q = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_HOST_TO_CTRL_CMD_Q; + + iface_q = &dev->iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_MSG_Q; + + iface_q = &dev->iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q; + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + q_hdr->qhdr_rx_req = 0; + + if (dev->qdss.align_virtual_addr) { + qdss = (struct hfi_mem_map_table *)dev->qdss.align_virtual_addr; + qdss->mem_map_num_entries = num_entries; + mem_map_table_base_addr = dev->qdss.align_device_addr + + sizeof(struct hfi_mem_map_table); + qdss->mem_map_table_base_addr = mem_map_table_base_addr; + + mem_map = (struct hfi_mem_map *)(qdss + 1); + cb = msm_smem_get_context_bank(MSM_VIDC_UNKNOWN, false, + dev->res, HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (!cb) { + dprintk(VIDC_ERR, + "%s: failed to get context bank\n", __func__); + return -EINVAL; + } + + rc = __get_qdss_iommu_virtual_addr(dev, mem_map, cb->domain); + if (rc) { + dprintk(VIDC_ERR, + "IOMMU mapping failed, Freeing qdss memdata\n"); + __smem_free(dev, &dev->qdss.mem_data); + dev->qdss.align_virtual_addr = NULL; + dev->qdss.align_device_addr = 0; + } + } + + + if (dev->res->domain_cvp) { + rc = __interface_dsp_queues_init(dev); + if (rc) { + dprintk(VIDC_ERR, "dsp_queues_init failed\n"); + goto fail_alloc_queue; + } + } + + __setup_ucregion_memory_map(dev); + return 0; +fail_alloc_queue: + return -ENOMEM; +} + +static int __sys_set_debug(struct venus_hfi_device *device, u32 debug) +{ + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + rc = call_hfi_pkt_op(device, sys_debug_config, pkt, debug); + if (rc) { + dprintk(VIDC_WARN, + "Debug mode setting to FW failed\n"); + return -ENOTEMPTY; + } + + if (__iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int __sys_set_ubwc_config(void *device) +{ + int rc = 0; + struct venus_hfi_device *dev = device; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE] = {0}; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + if (!dev->res->ubwc_config) + return rc; + + rc = call_hfi_pkt_op(dev, sys_ubwc_config, pkt, dev->res->ubwc_config); + if (rc) { + dprintk(VIDC_WARN, + "UBWC Config setting to FW failed\n"); + return -ENOTEMPTY; + } + + if (__iface_cmdq_write(dev, pkt)) + return -ENOTEMPTY; + + return 0; +} + +static int __sys_set_coverage(struct venus_hfi_device *device, u32 mode) +{ + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + rc = call_hfi_pkt_op(device, sys_coverage_config, + pkt, mode); + if (rc) { + dprintk(VIDC_WARN, + "Coverage mode setting to FW failed\n"); + return -ENOTEMPTY; + } + + if (__iface_cmdq_write(device, pkt)) { + dprintk(VIDC_WARN, "Failed to send coverage pkt to f/w\n"); + return -ENOTEMPTY; + } + + return 0; +} + +static int __sys_set_power_control(struct venus_hfi_device *device, + bool enable) +{ + struct regulator_info *rinfo; + bool supported = false; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + venus_hfi_for_each_regulator(device, rinfo) { + if (rinfo->has_hw_power_collapse) { + supported = true; + break; + } + } + + if (!supported) + return 0; + + call_hfi_pkt_op(device, sys_power_control, pkt, enable); + if (__iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int venus_hfi_core_init(void *device) +{ + int rc = 0; + struct hfi_cmd_sys_init_packet pkt; + struct hfi_cmd_sys_get_property_packet version_pkt; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "Invalid device\n"); + return -ENODEV; + } + + dev = device; + + dprintk(VIDC_DBG, "Core initializing\n"); + + mutex_lock(&dev->lock); + + dev->bus_vote.data = + kzalloc(sizeof(struct vidc_bus_vote_data), GFP_KERNEL); + if (!dev->bus_vote.data) { + dprintk(VIDC_ERR, "Bus vote data memory is not allocated\n"); + rc = -ENOMEM; + goto err_no_mem; + } + + dev->bus_vote.data_count = 1; + dev->bus_vote.data->power_mode = VIDC_POWER_TURBO; + + rc = __load_fw(dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to load Venus FW\n"); + goto err_load_fw; + } + + __set_state(dev, VENUS_STATE_INIT); + + dprintk(VIDC_DBG, "Dev_Virt: %pa, Reg_Virt: %pK\n", + &dev->hal_data->firmware_base, + dev->hal_data->register_base); + + + rc = __interface_queues_init(dev); + if (rc) { + dprintk(VIDC_ERR, "failed to init queues\n"); + rc = -ENOMEM; + goto err_core_init; + } + + rc = __boot_firmware(dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to start core\n"); + rc = -ENODEV; + goto err_core_init; + } + + rc = call_hfi_pkt_op(dev, sys_init, &pkt, HFI_VIDEO_ARCH_OX); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys init pkt\n"); + goto err_core_init; + } + + if (__iface_cmdq_write(dev, &pkt)) { + rc = -ENOTEMPTY; + goto err_core_init; + } + + rc = call_hfi_pkt_op(dev, sys_image_version, &version_pkt); + if (rc || __iface_cmdq_write(dev, &version_pkt)) + dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n"); + + __sys_set_debug(device, msm_vidc_fw_debug); + + rc = __sys_set_ubwc_config(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to set ubwc config\n"); + goto err_core_init; + } + + __enable_subcaches(device); + __set_subcaches(device); + __dsp_send_hfi_queue(device); + + if (dev->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + dev->qos.type = PM_QOS_REQ_AFFINE_IRQ; + dev->qos.irq = dev->hal_data->irq; +#endif + pm_qos_add_request(&dev->qos, PM_QOS_CPU_DMA_LATENCY, + dev->res->pm_qos_latency_us); + } + dprintk(VIDC_DBG, "Core inited successfully\n"); + mutex_unlock(&dev->lock); + return rc; +err_core_init: + __set_state(dev, VENUS_STATE_DEINIT); + __unload_fw(dev); +err_load_fw: +err_no_mem: + dprintk(VIDC_ERR, "Core init failed\n"); + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_core_release(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + struct hal_session *session, *next; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + mutex_lock(&device->lock); + dprintk(VIDC_DBG, "Core releasing\n"); + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) + pm_qos_remove_request(&device->qos); + + __resume(device); + __set_state(device, VENUS_STATE_DEINIT); + __dsp_shutdown(device, 0); + + __unload_fw(device); + + /* unlink all sessions from device */ + list_for_each_entry_safe(session, next, &device->sess_head, list) + list_del(&session->list); + + dprintk(VIDC_DBG, "Core released successfully\n"); + mutex_unlock(&device->lock); + + return rc; +} + +static void __core_clear_interrupt(struct venus_hfi_device *device) +{ + u32 intr_status = 0, mask = 0; + + if (!device) { + dprintk(VIDC_ERR, "%s: NULL device\n", __func__); + return; + } + + intr_status = __read_register(device, VIDC_WRAPPER_INTR_STATUS); + mask = (VIDC_WRAPPER_INTR_STATUS_A2H_BMSK | + VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK | + VIDC_CTRL_INIT_IDLE_MSG_BMSK); + + if (intr_status & mask) { + device->intr_status |= intr_status; + device->reg_count++; + dprintk(VIDC_DBG, + "INTERRUPT for device: %pK: times: %d interrupt_status: %d\n", + device, device->reg_count, intr_status); + } else { + device->spur_count++; + } + + __write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1); + __write_register(device, VIDC_WRAPPER_INTR_CLEAR, intr_status); +} + +static int venus_hfi_core_ping(void *device) +{ + struct hfi_cmd_sys_ping_packet pkt; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + dev = device; + mutex_lock(&dev->lock); + + rc = call_hfi_pkt_op(dev, sys_ping, &pkt); + if (rc) { + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_core_trigger_ssr(void *device, + enum hal_ssr_trigger_type type) +{ + struct hfi_cmd_sys_test_ssr_packet pkt; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + dev = device; + mutex_lock(&dev->lock); + + rc = call_hfi_pkt_op(dev, ssr_cmd, type, &pkt); + if (rc) { + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_session_set_property(void *sess, + enum hal_property ptype, void *pdata) +{ + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + struct hfi_cmd_session_set_property_packet *pkt = + (struct hfi_cmd_session_set_property_packet *) &packet; + struct hal_session *session = sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session || !session->device || !pdata) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + dprintk(VIDC_INFO, "in set_prop,with prop id: %#x\n", ptype); + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_set_prop; + } + + rc = call_hfi_pkt_op(device, session_set_property, + pkt, session, ptype, pdata); + + if (rc == -ENOTSUPP) { + dprintk(VIDC_DBG, + "set property: unsupported prop id: %#x\n", ptype); + rc = 0; + goto err_set_prop; + } else if (rc) { + dprintk(VIDC_ERR, "set property: failed to create packet\n"); + rc = -EINVAL; + goto err_set_prop; + } + + if (__iface_cmdq_write(session->device, pkt)) { + rc = -ENOTEMPTY; + goto err_set_prop; + } + +err_set_prop: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_get_property(void *sess, + enum hal_property ptype) +{ + struct hfi_cmd_session_get_property_packet pkt = {0}; + struct hal_session *session = sess; + int rc = 0; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + dprintk(VIDC_INFO, "%s: property id: %d\n", __func__, ptype); + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_create_pkt; + } + + rc = call_hfi_pkt_op(device, session_get_property, + &pkt, session, ptype); + if (rc) { + dprintk(VIDC_ERR, "get property profile: pkt failed\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) { + rc = -ENOTEMPTY; + dprintk(VIDC_ERR, "%s cmdq_write error\n", __func__); + } + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static void __set_default_sys_properties(struct venus_hfi_device *device) +{ + if (__sys_set_debug(device, msm_vidc_fw_debug)) + dprintk(VIDC_WARN, "Setting fw_debug msg ON failed\n"); + if (__sys_set_power_control(device, msm_vidc_fw_low_power_mode)) + dprintk(VIDC_WARN, "Setting h/w power collapse ON failed\n"); +} + +static void __session_clean(struct hal_session *session) +{ + struct hal_session *temp, *next; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_WARN, "%s: invalid params\n", __func__); + return; + } + device = session->device; + dprintk(VIDC_DBG, "deleted the session: %pK\n", session); + /* + * session might have been removed from the device list in + * core_release, so check and remove if it is in the list + */ + list_for_each_entry_safe(temp, next, &device->sess_head, list) { + if (session == temp) { + list_del(&session->list); + break; + } + } + /* Poison the session handle with zeros */ + *session = (struct hal_session){ {0} }; + kfree(session); +} + +static int venus_hfi_session_clean(void *session) +{ + struct hal_session *sess_close; + struct venus_hfi_device *device; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess_close = session; + device = sess_close->device; + + if (!device) { + dprintk(VIDC_ERR, "Invalid device handle %s\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + __session_clean(sess_close); + + mutex_unlock(&device->lock); + return 0; +} + +static int venus_hfi_session_init(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type, + void **new_session) +{ + struct hfi_cmd_sys_session_init_packet pkt; + struct venus_hfi_device *dev; + struct hal_session *s; + + if (!device || !new_session) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + dev = device; + mutex_lock(&dev->lock); + + s = kzalloc(sizeof(struct hal_session), GFP_KERNEL); + if (!s) { + dprintk(VIDC_ERR, "new session fail: Out of memory\n"); + goto err_session_init_fail; + } + + s->session_id = session_id; + s->is_decoder = (session_type == HAL_VIDEO_DOMAIN_DECODER); + s->device = dev; + s->codec = codec_type; + s->domain = session_type; + dprintk(VIDC_DBG, + "%s: inst %pK, session %pK, codec 0x%x, domain 0x%x\n", + __func__, session_id, s, s->codec, s->domain); + + list_add_tail(&s->list, &dev->sess_head); + + __set_default_sys_properties(device); + + if (call_hfi_pkt_op(dev, session_init, &pkt, + s, session_type, codec_type)) { + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); + goto err_session_init_fail; + } + + *new_session = s; + if (__iface_cmdq_write(dev, &pkt)) + goto err_session_init_fail; + + mutex_unlock(&dev->lock); + return 0; + +err_session_init_fail: + if (s) + __session_clean(s); + *new_session = NULL; + mutex_unlock(&dev->lock); + return -EINVAL; +} + +static int __send_session_cmd(struct hal_session *session, int pkt_type) +{ + struct vidc_hal_session_cmd_pkt pkt; + int rc = 0; + struct venus_hfi_device *device = session->device; + + if (!__is_session_valid(device, session, __func__)) + return -EINVAL; + + rc = call_hfi_pkt_op(device, session_cmd, + &pkt, pkt_type, session); + if (rc == -EPERM) + return 0; + + if (rc) { + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_end(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + + if (msm_vidc_fw_coverage) { + if (__sys_set_coverage(sess->device, msm_vidc_fw_coverage)) + dprintk(VIDC_WARN, "Fw_coverage msg ON failed\n"); + } + + rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_END); + + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_abort(void *sess) +{ + struct hal_session *session = sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + device = session->device; + + mutex_lock(&device->lock); + + __flush_debug_queue(device, NULL); + rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_ABORT); + + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_set_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct hfi_cmd_session_set_buffers_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer_info) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_create_pkt; + } + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) { + /* + * Hardware doesn't care about input buffers being + * published beforehand + */ + rc = 0; + goto err_create_pkt; + } + + pkt = (struct hfi_cmd_session_set_buffers_packet *)packet; + + rc = call_hfi_pkt_op(device, session_set_buffers, + pkt, session, buffer_info); + if (rc) { + dprintk(VIDC_ERR, "set buffers: failed to create packet\n"); + goto err_create_pkt; + } + + dprintk(VIDC_INFO, "set buffers: %#x\n", buffer_info->buffer_type); + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_release_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct hfi_cmd_session_release_buffer_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer_info) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_create_pkt; + } + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) { + rc = 0; + goto err_create_pkt; + } + + pkt = (struct hfi_cmd_session_release_buffer_packet *) packet; + + rc = call_hfi_pkt_op(device, session_release_buffers, + pkt, session, buffer_info); + if (rc) { + dprintk(VIDC_ERR, "release buffers: failed to create packet\n"); + goto err_create_pkt; + } + + dprintk(VIDC_INFO, "Release buffers: %#x\n", buffer_info->buffer_type); + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_register_buffer(void *sess, + struct vidc_register_buffer *buffer) +{ + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + struct hfi_cmd_session_register_buffers_packet *pkt; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto exit; + } + pkt = (struct hfi_cmd_session_register_buffers_packet *)packet; + rc = call_hfi_pkt_op(device, session_register_buffer, pkt, + session, buffer); + if (rc) { + dprintk(VIDC_ERR, "%s: failed to create packet\n", __func__); + goto exit; + } + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +exit: + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_unregister_buffer(void *sess, + struct vidc_unregister_buffer *buffer) +{ + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + struct hfi_cmd_session_unregister_buffers_packet *pkt; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto exit; + } + pkt = (struct hfi_cmd_session_unregister_buffers_packet *)packet; + rc = call_hfi_pkt_op(device, session_unregister_buffer, pkt, + session, buffer); + if (rc) { + dprintk(VIDC_ERR, "%s: failed to create packet\n", __func__); + goto exit; + } + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +exit: + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_load_res(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_LOAD_RESOURCES); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_release_res(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_RELEASE_RESOURCES); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_start(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_START); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_continue(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_CONTINUE); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_stop(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_STOP); + mutex_unlock(&device->lock); + + return rc; +} + +static int __session_etb(struct hal_session *session, + struct vidc_frame_data *input_frame, bool relaxed) +{ + int rc = 0; + struct venus_hfi_device *device = session->device; + + if (!__is_session_valid(device, session, __func__)) + return -EINVAL; + + if (session->is_decoder) { + struct hfi_cmd_session_empty_buffer_compressed_packet pkt; + + rc = call_hfi_pkt_op(device, session_etb_decoder, + &pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb decoder: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + if (rc) + goto err_create_pkt; + } else { + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + pkt; + + rc = call_hfi_pkt_op(device, session_etb_encoder, + &pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb encoder: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + if (rc) + goto err_create_pkt; + } + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_etb(void *sess, + struct vidc_frame_data *input_frame) +{ + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !input_frame) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + rc = __session_etb(session, input_frame, false); + mutex_unlock(&device->lock); + return rc; +} + +static int __session_ftb(struct hal_session *session, + struct vidc_frame_data *output_frame, bool relaxed) +{ + int rc = 0; + struct venus_hfi_device *device = session->device; + struct hfi_cmd_session_fill_buffer_packet pkt; + + if (!__is_session_valid(device, session, __func__)) + return -EINVAL; + + rc = call_hfi_pkt_op(device, session_ftb, + &pkt, session, output_frame); + if (rc) { + dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_ftb(void *sess, + struct vidc_frame_data *output_frame) +{ + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !output_frame) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + rc = __session_ftb(session, output_frame, false); + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_process_batch(void *sess, + int num_etbs, struct vidc_frame_data etbs[], + int num_ftbs, struct vidc_frame_data ftbs[]) +{ + int rc = 0, c = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + struct hfi_cmd_session_sync_process_packet pkt; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: Invalid Params\n", __func__); + return -EINVAL; + } + + device = session->device; + + mutex_lock(&device->lock); + + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_etbs_and_ftbs; + } + + for (c = 0; c < num_ftbs; ++c) { + rc = __session_ftb(session, &ftbs[c], true); + if (rc) { + dprintk(VIDC_ERR, "Failed to queue batched ftb: %d\n", + rc); + goto err_etbs_and_ftbs; + } + } + + for (c = 0; c < num_etbs; ++c) { + rc = __session_etb(session, &etbs[c], true); + if (rc) { + dprintk(VIDC_ERR, "Failed to queue batched etb: %d\n", + rc); + goto err_etbs_and_ftbs; + } + } + + rc = call_hfi_pkt_op(device, session_sync_process, &pkt, session); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sync packet\n"); + goto err_etbs_and_ftbs; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + +err_etbs_and_ftbs: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_get_buf_req(void *sess) +{ + struct hfi_cmd_session_get_property_packet pkt; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "invalid session"); + return -ENODEV; + } + + device = session->device; + mutex_lock(&device->lock); + + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_create_pkt; + } + rc = call_hfi_pkt_op(device, session_get_buf_req, + &pkt, session); + if (rc) { + dprintk(VIDC_ERR, + "Session get buf req: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode) +{ + struct hfi_cmd_session_flush_packet pkt; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "invalid session"); + return -ENODEV; + } + + device = session->device; + mutex_lock(&device->lock); + + if (!__is_session_valid(device, session, __func__)) { + rc = -EINVAL; + goto err_create_pkt; + } + rc = call_hfi_pkt_op(device, session_flush, + &pkt, session, flush_mode); + if (rc) { + dprintk(VIDC_ERR, "Session flush: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int __check_core_registered(struct hal_device_data core, + phys_addr_t fw_addr, u8 *reg_addr, u32 reg_size, + phys_addr_t irq) +{ + struct venus_hfi_device *device; + struct hal_data *hal_data; + struct list_head *curr, *next; + + if (!core.dev_count) { + dprintk(VIDC_INFO, "no device Registered\n"); + return -EINVAL; + } + + list_for_each_safe(curr, next, &core.dev_head) { + device = list_entry(curr, + struct venus_hfi_device, list); + hal_data = device->hal_data; + if (device && hal_data->irq == irq && + (CONTAINS(hal_data->firmware_base, + FIRMWARE_SIZE, fw_addr) || + CONTAINS(fw_addr, FIRMWARE_SIZE, + hal_data->firmware_base) || + CONTAINS(hal_data->register_base, + reg_size, reg_addr) || + CONTAINS(reg_addr, reg_size, + hal_data->register_base) || + OVERLAPS(hal_data->register_base, + reg_size, reg_addr, reg_size) || + OVERLAPS(reg_addr, reg_size, + hal_data->register_base, + reg_size) || + OVERLAPS(hal_data->firmware_base, + FIRMWARE_SIZE, fw_addr, + FIRMWARE_SIZE) || + OVERLAPS(fw_addr, FIRMWARE_SIZE, + hal_data->firmware_base, + FIRMWARE_SIZE))) { + return 0; + } + + dprintk(VIDC_INFO, "Device not registered\n"); + return -EINVAL; + } + return -EINVAL; +} + +static void __process_fatal_error( + struct venus_hfi_device *device) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device->device_id; + device->callback(HAL_SYS_ERROR, &cmd_done); +} + +static int __prepare_pc(struct venus_hfi_device *device) +{ + int rc = 0; + struct hfi_cmd_sys_pc_prep_packet pkt; + + rc = call_hfi_pkt_op(device, sys_pc_prep, &pkt); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n"); + goto err_pc_prep; + } + + if (__iface_cmdq_write(device, &pkt)) + rc = -ENOTEMPTY; + if (rc) + dprintk(VIDC_ERR, "Failed to prepare venus for power off"); +err_pc_prep: + return rc; +} + +static void venus_hfi_pm_handler(struct work_struct *work) +{ + int rc = 0; + struct venus_hfi_device *device = list_first_entry( + &hal_ctxt.dev_head, struct venus_hfi_device, list); + + if (!device) { + dprintk(VIDC_ERR, "%s: NULL device\n", __func__); + return; + } + + dprintk(VIDC_PROF, + "Entering %s\n", __func__); + /* + * It is ok to check this variable outside the lock since + * it is being updated in this context only + */ + if (device->skip_pc_count >= VIDC_MAX_PC_SKIP_COUNT) { + dprintk(VIDC_WARN, "Failed to PC for %d times\n", + device->skip_pc_count); + device->skip_pc_count = 0; + __process_fatal_error(device); + return; + } + + mutex_lock(&device->lock); + rc = __power_collapse(device, false); + mutex_unlock(&device->lock); + switch (rc) { + case 0: + device->skip_pc_count = 0; + /* Cancel pending delayed works if any */ + cancel_delayed_work(&venus_hfi_pm_work); + dprintk(VIDC_PROF, "%s: power collapse successful!\n", + __func__); + break; + case -EBUSY: + device->skip_pc_count = 0; + dprintk(VIDC_DBG, "%s: retry PC as dsp is busy\n", __func__); + queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay)); + break; + case -EAGAIN: + device->skip_pc_count++; + dprintk(VIDC_WARN, "%s: retry power collapse (count %d)\n", + __func__, device->skip_pc_count); + queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay)); + break; + default: + dprintk(VIDC_ERR, "%s: power collapse failed\n", __func__); + break; + } +} + +static int __power_collapse(struct venus_hfi_device *device, bool force) +{ + int rc = 0; + u32 wfi_status = 0, idle_status = 0, pc_ready = 0; + u32 flags = 0; + int count = 0; + const int max_tries = 10; + + if (!device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + if (!device->power_enabled) { + dprintk(VIDC_DBG, "%s: Power already disabled\n", + __func__); + goto exit; + } + + rc = __core_in_valid_state(device); + if (!rc) { + dprintk(VIDC_WARN, + "Core is in bad state, Skipping power collapse\n"); + return -EINVAL; + } + + rc = __dsp_suspend(device, force, flags); + if (rc == -EBUSY) + goto exit; + else if (rc) + goto skip_power_off; + + pc_ready = __read_register(device, VIDC_CTRL_STATUS) & + VIDC_CTRL_STATUS_PC_READY; + if (!pc_ready) { + wfi_status = __read_register(device, + VIDC_WRAPPER_CPU_STATUS); + idle_status = __read_register(device, + VIDC_CTRL_STATUS); + if (!(wfi_status & BIT(0))) { + dprintk(VIDC_WARN, + "Skipping PC as wfi_status (%#x) bit not set\n", + wfi_status); + goto skip_power_off; + } + if (!(idle_status & BIT(30))) { + dprintk(VIDC_WARN, + "Skipping PC as idle_status (%#x) bit not set\n", + idle_status); + goto skip_power_off; + } + + rc = __prepare_pc(device); + if (rc) { + dprintk(VIDC_WARN, "Failed PC %d\n", rc); + goto skip_power_off; + } + + while (count < max_tries) { + wfi_status = __read_register(device, + VIDC_WRAPPER_CPU_STATUS); + pc_ready = __read_register(device, + VIDC_CTRL_STATUS); + if ((wfi_status & BIT(0)) && (pc_ready & + VIDC_CTRL_STATUS_PC_READY)) + break; + usleep_range(150, 250); + count++; + } + + if (count == max_tries) { + dprintk(VIDC_ERR, + "Skip PC. Core is not in right state (%#x, %#x)\n", + wfi_status, pc_ready); + goto skip_power_off; + } + } + + __flush_debug_queue(device, device->raw_packet); + + rc = __suspend(device); + if (rc) + dprintk(VIDC_ERR, "Failed __suspend\n"); + +exit: + return rc; + +skip_power_off: + dprintk(VIDC_WARN, "Skip PC(%#x, %#x, %#x)\n", + wfi_status, idle_status, pc_ready); + + return -EAGAIN; +} + +static void print_sfr_message(struct venus_hfi_device *device) +{ + struct hfi_sfr_struct *vsfr = NULL; + u32 vsfr_size = 0; + void *p = NULL; + + vsfr = (struct hfi_sfr_struct *)device->sfr.align_virtual_addr; + if (vsfr) { + if (vsfr->bufSize != device->sfr.mem_size) { + dprintk(VIDC_ERR, "Invalid SFR buf size %d actual %d\n", + vsfr->bufSize, device->sfr.mem_size); + return; + } + vsfr_size = vsfr->bufSize - sizeof(u32); + p = memchr(vsfr->rg_data, '\0', vsfr_size); + /* SFR isn't guaranteed to be NULL terminated */ + if (p == NULL) + vsfr->rg_data[vsfr_size - 1] = '\0'; + + dprintk(VIDC_ERR, "SFR Message from FW: %s\n", + vsfr->rg_data); + } +} + +static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) +{ + bool local_packet = false; + enum vidc_msg_prio log_level = VIDC_FW; + + if (!device) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return; + } + + if (!packet) { + packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_KERNEL); + if (!packet) { + dprintk(VIDC_ERR, "In %s() Fail to allocate mem\n", + __func__); + return; + } + + local_packet = true; + + /* + * Local packek is used when something FATAL occurred. + * It is good to print these logs by default. + */ + + log_level = VIDC_ERR; + } + +#define SKIP_INVALID_PKT(pkt_size, payload_size, pkt_hdr_size) ({ \ + if (pkt_size < pkt_hdr_size || \ + payload_size < MIN_PAYLOAD_SIZE || \ + payload_size > \ + (pkt_size - pkt_hdr_size + sizeof(u8))) { \ + dprintk(VIDC_ERR, \ + "%s: invalid msg size - %d\n", \ + __func__, pkt->msg_size); \ + continue; \ + } \ + }) + + while (!__iface_dbgq_read(device, packet)) { + struct hfi_packet_header *pkt = + (struct hfi_packet_header *) packet; + + if (pkt->size < sizeof(struct hfi_packet_header)) { + dprintk(VIDC_ERR, "Invalid pkt size - %s\n", + __func__); + continue; + } + + if (pkt->packet_type == HFI_MSG_SYS_COV) { + struct hfi_msg_sys_coverage_packet *pkt = + (struct hfi_msg_sys_coverage_packet *) packet; + int stm_size = 0; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + + stm_size = stm_log_inv_ts(0, 0, + pkt->rg_msg_data, pkt->msg_size); + if (stm_size == 0) + dprintk(VIDC_ERR, + "In %s, stm_log returned size of 0\n", + __func__); + + } else if (pkt->packet_type == HFI_MSG_SYS_DEBUG) { + struct hfi_msg_sys_debug_packet *pkt = + (struct hfi_msg_sys_debug_packet *) packet; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + + /* + * All fw messages starts with new line character. This + * causes dprintk to print this message in two lines + * in the kernel log. Ignoring the first character + * from the message fixes this to print it in a single + * line. + */ + pkt->rg_msg_data[pkt->msg_size-1] = '\0'; + dprintk(log_level, "%s", &pkt->rg_msg_data[1]); + } + } +#undef SKIP_INVALID_PKT + + if (local_packet) + kfree(packet); +} + +static bool __is_session_valid(struct venus_hfi_device *device, + struct hal_session *session, const char *func) +{ + struct hal_session *temp = NULL; + + if (!device || !session) + goto invalid; + + list_for_each_entry(temp, &device->sess_head, list) + if (session == temp) + return true; + +invalid: + dprintk(VIDC_WARN, "%s: device %pK, invalid session %pK\n", + func, device, session); + return false; +} + +static struct hal_session *__get_session(struct venus_hfi_device *device, + u32 session_id) +{ + struct hal_session *temp = NULL; + + list_for_each_entry(temp, &device->sess_head, list) { + if (session_id == hash32_ptr(temp)) + return temp; + } + + return NULL; +} + +static int __response_handler(struct venus_hfi_device *device) +{ + struct msm_vidc_cb_info *packets; + int packet_count = 0; + u8 *raw_packet = NULL; + bool requeue_pm_work = true; + + if (!device || device->state != VENUS_STATE_INIT) + return 0; + + packets = device->response_pkt; + + raw_packet = device->raw_packet; + + if (!raw_packet || !packets) { + dprintk(VIDC_ERR, + "%s: Invalid args : Res packet = %p, Raw packet = %p\n", + __func__, packets, raw_packet); + return 0; + } + + if (device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK) { + struct msm_vidc_cb_info info = { + .response_type = HAL_SYS_WATCHDOG_TIMEOUT, + .response.cmd = { + .device_id = device->device_id, + } + }; + + print_sfr_message(device); + + dprintk(VIDC_ERR, "Received watchdog timeout\n"); + packets[packet_count++] = info; + goto exit; + } + + /* Bleed the msg queue dry of packets */ + while (!__iface_msgq_read(device, raw_packet)) { + void **session_id = NULL; + struct msm_vidc_cb_info *info = &packets[packet_count++]; + struct vidc_hal_sys_init_done sys_init_done = {0}; + int rc = 0; + + rc = hfi_process_msg_packet(device->device_id, + (struct vidc_hal_msg_pkt_hdr *)raw_packet, info); + if (rc) { + dprintk(VIDC_WARN, + "Corrupt/unknown packet found, discarding\n"); + --packet_count; + continue; + } + + /* Process the packet types that we're interested in */ + switch (info->response_type) { + case HAL_SYS_ERROR: + print_sfr_message(device); + break; + case HAL_SYS_RELEASE_RESOURCE_DONE: + dprintk(VIDC_DBG, "Received SYS_RELEASE_RESOURCE\n"); + break; + case HAL_SYS_INIT_DONE: + dprintk(VIDC_DBG, "Received SYS_INIT_DONE\n"); + + sys_init_done.capabilities = + device->sys_init_capabilities; + hfi_process_sys_init_done_prop_read( + (struct hfi_msg_sys_init_done_packet *) + raw_packet, &sys_init_done); + info->response.cmd.data.sys_init_done = sys_init_done; + break; + case HAL_SESSION_LOAD_RESOURCE_DONE: + /* + * Work around for H/W bug, need to re-program these + * registers as part of a handshake agreement with the + * firmware. This strictly only needs to be done for + * decoder secure sessions, but there's no harm in doing + * so for all sessions as it's at worst a NO-OP. + */ + __set_threshold_registers(device); + break; + default: + break; + } + + /* For session-related packets, validate session */ + switch (info->response_type) { + case HAL_SESSION_LOAD_RESOURCE_DONE: + case HAL_SESSION_INIT_DONE: + case HAL_SESSION_END_DONE: + case HAL_SESSION_ABORT_DONE: + case HAL_SESSION_START_DONE: + case HAL_SESSION_STOP_DONE: + case HAL_SESSION_FLUSH_DONE: + case HAL_SESSION_SUSPEND_DONE: + case HAL_SESSION_RESUME_DONE: + case HAL_SESSION_SET_PROP_DONE: + case HAL_SESSION_GET_PROP_DONE: + case HAL_SESSION_RELEASE_BUFFER_DONE: + case HAL_SESSION_REGISTER_BUFFER_DONE: + case HAL_SESSION_UNREGISTER_BUFFER_DONE: + case HAL_SESSION_RELEASE_RESOURCE_DONE: + case HAL_SESSION_PROPERTY_INFO: + session_id = &info->response.cmd.session_id; + break; + case HAL_SESSION_ERROR: + case HAL_SESSION_ETB_DONE: + case HAL_SESSION_FTB_DONE: + session_id = &info->response.data.session_id; + break; + case HAL_SESSION_EVENT_CHANGE: + session_id = &info->response.event.session_id; + break; + case HAL_RESPONSE_UNUSED: + default: + session_id = NULL; + break; + } + + /* + * hfi_process_msg_packet provides a session_id that's a hashed + * value of struct hal_session, we need to coerce the hashed + * value back to pointer that we can use. Ideally, hfi_process\ + * _msg_packet should take care of this, but it doesn't have + * required information for it + */ + if (session_id) { + struct hal_session *session = NULL; + + if (upper_32_bits((uintptr_t)*session_id) != 0) { + dprintk(VIDC_ERR, + "Upper 32-bits != 0 for sess_id=%pK\n", + *session_id); + } + session = __get_session(device, + (u32)(uintptr_t)*session_id); + if (!session) { + dprintk(VIDC_ERR, + "Received a packet (%#x) for an unrecognized session (%pK), discarding\n", + info->response_type, + *session_id); + --packet_count; + continue; + } + + *session_id = session->session_id; + } + + if (packet_count >= max_packets) { + dprintk(VIDC_WARN, + "Too many packets in message queue to handle at once, deferring read\n"); + break; + } + + /* do not read packets after sys error packet */ + if (info->response_type == HAL_SYS_ERROR) + break; + } + + if (requeue_pm_work && device->res->sw_power_collapsible) { + cancel_delayed_work(&venus_hfi_pm_work); + if (!queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, + msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay))) { + dprintk(VIDC_ERR, "PM work already scheduled\n"); + } + } + +exit: + __flush_debug_queue(device, raw_packet); + + return packet_count; +} + +static void venus_hfi_core_work_handler(struct work_struct *work) +{ + struct venus_hfi_device *device = list_first_entry( + &hal_ctxt.dev_head, struct venus_hfi_device, list); + int num_responses = 0, i = 0; + u32 intr_status; + + mutex_lock(&device->lock); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - Core not in init state\n", __func__); + goto err_no_work; + } + + if (!device->callback) { + dprintk(VIDC_ERR, "No interrupt callback function: %pK\n", + device); + goto err_no_work; + } + + if (__resume(device)) { + dprintk(VIDC_ERR, "%s: Power enable failed\n", __func__); + goto err_no_work; + } + + __core_clear_interrupt(device); + num_responses = __response_handler(device); + +err_no_work: + + /* Keep the interrupt status before releasing device lock */ + intr_status = device->intr_status; + mutex_unlock(&device->lock); + + /* + * Issue the callbacks outside of the locked contex to preserve + * re-entrancy. + */ + + for (i = 0; !IS_ERR_OR_NULL(device->response_pkt) && + i < num_responses; ++i) { + struct msm_vidc_cb_info *r = &device->response_pkt[i]; + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_ERR, + "Ignore responses from %d to %d as device is in invalid state", + (i + 1), num_responses); + break; + } + dprintk(VIDC_DBG, "Processing response %d of %d, type %d\n", + (i + 1), num_responses, r->response_type); + device->callback(r->response_type, &r->response); + } + + /* We need re-enable the irq which was disabled in ISR handler */ + if (!(intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + enable_irq(device->hal_data->irq); + + /* + * XXX: Don't add any code beyond here. Reacquiring locks after release + * it above doesn't guarantee the atomicity that we're aiming for. + */ +} + +static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler); + +static irqreturn_t venus_hfi_isr(int irq, void *dev) +{ + struct venus_hfi_device *device = dev; + + disable_irq_nosync(irq); + queue_work(device->vidc_workq, &venus_hfi_work); + return IRQ_HANDLED; +} + +static int __init_regs_and_interrupts(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + struct hal_data *hal = NULL; + int rc = 0; + + rc = __check_core_registered(hal_ctxt, res->firmware_base, + (u8 *)(uintptr_t)res->register_base, + res->register_size, res->irq); + if (!rc) { + dprintk(VIDC_ERR, "Core present/Already added\n"); + rc = -EEXIST; + goto err_core_init; + } + + dprintk(VIDC_DBG, "HAL_DATA will be assigned now\n"); + hal = (struct hal_data *) + kzalloc(sizeof(struct hal_data), GFP_KERNEL); + if (!hal) { + dprintk(VIDC_ERR, "Failed to alloc\n"); + rc = -ENOMEM; + goto err_core_init; + } + + hal->irq = res->irq; + hal->firmware_base = res->firmware_base; + hal->register_base = devm_ioremap_nocache(&res->pdev->dev, + res->register_base, res->register_size); + hal->register_size = res->register_size; + if (!hal->register_base) { + dprintk(VIDC_ERR, + "could not map reg addr %pa of size %d\n", + &res->register_base, res->register_size); + goto error_irq_fail; + } + + device->hal_data = hal; + rc = request_irq(res->irq, venus_hfi_isr, IRQF_TRIGGER_HIGH, + "msm_vidc", device); + if (unlikely(rc)) { + dprintk(VIDC_ERR, "() :request_irq failed\n"); + goto error_irq_fail; + } + + disable_irq_nosync(res->irq); + dprintk(VIDC_INFO, + "firmware_base = %pa, register_base = %pa, register_size = %d\n", + &res->firmware_base, &res->register_base, + res->register_size); + + return rc; + +error_irq_fail: + kfree(hal); +err_core_init: + return rc; + +} + +static inline void __deinit_clocks(struct venus_hfi_device *device) +{ + struct clock_info *cl; + + device->clk_freq = 0; + venus_hfi_for_each_clock_reverse(device, cl) { + if (cl->clk) { + clk_put(cl->clk); + cl->clk = NULL; + } + } +} + +static inline int __init_clocks(struct venus_hfi_device *device) +{ + int rc = 0; + struct clock_info *cl = NULL; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + venus_hfi_for_each_clock(device, cl) { + + dprintk(VIDC_DBG, "%s: scalable? %d, count %d\n", + cl->name, cl->has_scaling, cl->count); + } + + venus_hfi_for_each_clock(device, cl) { + if (!cl->clk) { + cl->clk = clk_get(&device->res->pdev->dev, cl->name); + if (IS_ERR_OR_NULL(cl->clk)) { + dprintk(VIDC_ERR, + "Failed to get clock: %s\n", cl->name); + rc = PTR_ERR(cl->clk) ?: -EINVAL; + cl->clk = NULL; + goto err_clk_get; + } + } + } + device->clk_freq = 0; + return 0; + +err_clk_get: + __deinit_clocks(device); + return rc; +} + +static int __handle_reset_clk(struct msm_vidc_platform_resources *res, + enum reset_state state) +{ + int i, rc = 0; + struct reset_control *rst; + struct reset_set *rst_set = &res->reset_set; + + if (!rst_set->reset_tbl) + return 0; + + for (i = 0; i < rst_set->count; i++) { + rst = rst_set->reset_tbl[i].rst; + dprintk(VIDC_DBG, "%s reset_state name = %s state %d\n", + __func__, rst_set->reset_tbl[i].name, state); + switch (state) { + case INIT: + if (rst) + continue; + + rst = devm_reset_control_get(&res->pdev->dev, + rst_set->reset_tbl[i].name); + if (IS_ERR(rst)) + rc = PTR_ERR(rst); + + rst_set->reset_tbl[i].rst = rst; + break; + case ASSERT: + if (!rst) + goto no_init; + + rc = reset_control_assert(rst); + break; + case DEASSERT: + if (!rst) + goto no_init; + + rc = reset_control_deassert(rst); + break; + default: + dprintk(VIDC_ERR, "Invalid reset request\n"); + } + + if (rc) + return rc; + } + return 0; +no_init: + dprintk(VIDC_ERR, "%s reset_state name = %s failed state %d\n", + __func__, rst_set->reset_tbl[i].name, state); + return PTR_ERR(rst); +} + +static inline void __disable_unprepare_clks(struct venus_hfi_device *device) +{ + struct clock_info *cl; + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return; + } + + venus_hfi_for_each_clock_reverse(device, cl) { + dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n", + cl->name); + rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH); + if (rc) { + dprintk(VIDC_WARN, + "Failed set flag NORETAIN_PERIPH %s\n", + cl->name); + } + rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM); + if (rc) { + dprintk(VIDC_WARN, + "Failed set flag NORETAIN_MEM %s\n", + cl->name); + } + clk_disable_unprepare(cl->clk); + } +} + +static inline int __prepare_ahb2axi_bridge(struct venus_hfi_device *device) +{ + int rc; + + if (!device) { + dprintk(VIDC_ERR, "NULL device\n"); + return -EINVAL; + } + + if (device->res->vpu_ver != VPU_VERSION_5) + return 0; + + rc = __handle_reset_clk(device->res, ASSERT); + if (rc) { + dprintk(VIDC_ERR, "failed to assert reset clocks\n"); + return rc; + } + + /* wait for deassert */ + usleep_range(150, 250); + + rc = __handle_reset_clk(device->res, DEASSERT); + if (rc) { + dprintk(VIDC_ERR, "failed to deassert reset clocks\n"); + return rc; + } + + return 0; +} + +static inline int __unprepare_ahb2axi_bridge(struct venus_hfi_device *device, + u32 version) +{ + int rc; + + if (!device) { + dprintk(VIDC_ERR, "NULL device\n"); + return -EINVAL; + } + + /* reset axi0 and axi1 as needed only for specific video hardware */ + version &= ~GENMASK(15, 0); + if (version != VERSION_HANA) + return -EINVAL; + + dprintk(VIDC_ERR, + "reset axi cbcr to recover\n"); + + rc = __handle_reset_clk(device->res, ASSERT); + if (rc) { + dprintk(VIDC_ERR, "failed to assert reset clocks\n"); + return rc; + } + + /* wait for deassert */ + usleep_range(150, 250); + + rc = __handle_reset_clk(device->res, DEASSERT); + if (rc) { + dprintk(VIDC_ERR, "failed to deassert reset clocks\n"); + return rc; + } + + return 0; +} + +static inline int __prepare_enable_clks(struct venus_hfi_device *device) +{ + struct clock_info *cl = NULL, *cl_fail = NULL; + int rc = 0, c = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + venus_hfi_for_each_clock(device, cl) { + /* + * For the clocks we control, set the rate prior to preparing + * them. Since we don't really have a load at this point, scale + * it to the lowest frequency possible + */ + if (cl->has_scaling) + __set_clk_rate(device, cl, + clk_round_rate(cl->clk, 0)); + + rc = clk_set_flags(cl->clk, CLKFLAG_RETAIN_PERIPH); + if (rc) { + dprintk(VIDC_WARN, + "Failed set flag RETAIN_PERIPH %s\n", + cl->name); + } + rc = clk_set_flags(cl->clk, CLKFLAG_RETAIN_MEM); + if (rc) { + dprintk(VIDC_WARN, + "Failed set flag RETAIN_MEM %s\n", + cl->name); + } + rc = clk_prepare_enable(cl->clk); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks\n"); + cl_fail = cl; + goto fail_clk_enable; + } + + c++; + dprintk(VIDC_DBG, "Clock: %s prepared and enabled\n", cl->name); + } + + call_venus_op(device, clock_config_on_enable, device); + return rc; + +fail_clk_enable: + venus_hfi_for_each_clock_reverse_continue(device, cl, c) { + dprintk(VIDC_ERR, "Clock: %s disable and unprepare\n", + cl->name); + clk_disable_unprepare(cl->clk); + } + + return rc; +} + +static void __deinit_bus(struct venus_hfi_device *device) +{ + struct bus_info *bus = NULL; + + if (!device) + return; + + kfree(device->bus_vote.data); + device->bus_vote = DEFAULT_BUS_VOTE; + + venus_hfi_for_each_bus_reverse(device, bus) { + msm_bus_scale_unregister(bus->client); + bus->client = NULL; + } +} + +static int __init_bus(struct venus_hfi_device *device) +{ + struct bus_info *bus = NULL; + int rc = 0; + + if (!device) + return -EINVAL; + + venus_hfi_for_each_bus(device, bus) { + if (!strcmp(bus->mode, "msm-vidc-llcc")) { + if (msm_vidc_syscache_disable) { + dprintk(VIDC_DBG, + "Skipping LLC bus init %s: %s\n", + bus->name, bus->mode); + continue; + } + } + bus->client = msm_bus_scale_register(bus->master, bus->slave, + bus->name, false); + if (IS_ERR_OR_NULL(bus->client)) { + rc = PTR_ERR(bus->client) ?: -EBADHANDLE; + dprintk(VIDC_ERR, "Failed to register bus %s: %d\n", + bus->name, rc); + bus->client = NULL; + goto err_add_dev; + } + } + + return 0; + +err_add_dev: + __deinit_bus(device); + return rc; +} + +static void __deinit_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo = NULL; + + venus_hfi_for_each_regulator_reverse(device, rinfo) { + if (rinfo->regulator) { + regulator_put(rinfo->regulator); + rinfo->regulator = NULL; + } + } +} + +static int __init_regulators(struct venus_hfi_device *device) +{ + int rc = 0; + struct regulator_info *rinfo = NULL; + + venus_hfi_for_each_regulator(device, rinfo) { + rinfo->regulator = regulator_get(&device->res->pdev->dev, + rinfo->name); + if (IS_ERR_OR_NULL(rinfo->regulator)) { + rc = PTR_ERR(rinfo->regulator) ?: -EBADHANDLE; + dprintk(VIDC_ERR, "Failed to get regulator: %s\n", + rinfo->name); + rinfo->regulator = NULL; + goto err_reg_get; + } + } + + return 0; + +err_reg_get: + __deinit_regulators(device); + return rc; +} + +static void __deinit_subcaches(struct venus_hfi_device *device) +{ + struct subcache_info *sinfo = NULL; + + if (!device) { + dprintk(VIDC_ERR, "deinit_subcaches: invalid device %pK\n", + device); + goto exit; + } + + if (!is_sys_cache_present(device)) + goto exit; + + venus_hfi_for_each_subcache_reverse(device, sinfo) { + if (sinfo->subcache) { + dprintk(VIDC_DBG, "deinit_subcaches: %s\n", + sinfo->name); + llcc_slice_putd(sinfo->subcache); + sinfo->subcache = NULL; + } + } + +exit: + return; +} + +static int __init_subcaches(struct venus_hfi_device *device) +{ + int rc = 0; + struct subcache_info *sinfo = NULL; + + if (!device) { + dprintk(VIDC_ERR, "init_subcaches: invalid device %pK\n", + device); + return -EINVAL; + } + + if (!is_sys_cache_present(device)) + return 0; + + venus_hfi_for_each_subcache(device, sinfo) { + if (!strcmp("vidsc0", sinfo->name)) { + sinfo->subcache = llcc_slice_getd(LLCC_VIDSC0); + } else if (!strcmp("vidsc1", sinfo->name)) { + sinfo->subcache = llcc_slice_getd(LLCC_VIDSC1); + } else if (!strcmp("vidscfw", sinfo->name)) { + sinfo->subcache = llcc_slice_getd(LLCC_VIDFW); + } else { + dprintk(VIDC_ERR, "Invalid subcache name %s\n", + sinfo->name); + } + if (IS_ERR_OR_NULL(sinfo->subcache)) { + rc = PTR_ERR(sinfo->subcache) ? + PTR_ERR(sinfo->subcache) : -EBADHANDLE; + dprintk(VIDC_ERR, + "init_subcaches: invalid subcache: %s rc %d\n", + sinfo->name, rc); + sinfo->subcache = NULL; + goto err_subcache_get; + } + dprintk(VIDC_DBG, "init_subcaches: %s\n", + sinfo->name); + } + + return 0; + +err_subcache_get: + __deinit_subcaches(device); + return rc; +} + +static int __init_resources(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + + rc = __init_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to get all regulators\n"); + return -ENODEV; + } + + rc = __init_clocks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init clocks\n"); + rc = -ENODEV; + goto err_init_clocks; + } + + rc = __handle_reset_clk(res, INIT); + if (rc) { + dprintk(VIDC_ERR, "Failed to init reset clocks\n"); + rc = -ENODEV; + goto err_init_reset_clk; + } + + rc = __init_bus(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init bus: %d\n", rc); + goto err_init_bus; + } + + rc = __init_subcaches(device); + if (rc) + dprintk(VIDC_WARN, "Failed to init subcaches: %d\n", rc); + + device->sys_init_capabilities = + kzalloc(sizeof(struct msm_vidc_capability) + * VIDC_MAX_SESSIONS, GFP_KERNEL); + + return rc; + +err_init_reset_clk: +err_init_bus: + __deinit_clocks(device); +err_init_clocks: + __deinit_regulators(device); + return rc; +} + +static void __deinit_resources(struct venus_hfi_device *device) +{ + __deinit_subcaches(device); + __deinit_bus(device); + __deinit_clocks(device); + __deinit_regulators(device); + kfree(device->sys_init_capabilities); + device->sys_init_capabilities = NULL; +} + +static int __protect_cp_mem(struct venus_hfi_device *device) +{ + struct tzbsp_memprot memprot; + unsigned int resp = 0; + int rc = 0; + struct context_bank_info *cb; + struct scm_desc desc = {0}; + + if (!device) + return -EINVAL; + + memprot.cp_start = 0x0; + memprot.cp_size = 0x0; + memprot.cp_nonpixel_start = 0x0; + memprot.cp_nonpixel_size = 0x0; + + list_for_each_entry(cb, &device->res->context_banks, list) { + if (!strcmp(cb->name, "venus_ns")) { + desc.args[1] = memprot.cp_size = + cb->addr_range.start; + dprintk(VIDC_DBG, "%s memprot.cp_size: %#x\n", + __func__, memprot.cp_size); + } + + if (!strcmp(cb->name, "venus_sec_non_pixel")) { + desc.args[2] = memprot.cp_nonpixel_start = + cb->addr_range.start; + desc.args[3] = memprot.cp_nonpixel_size = + cb->addr_range.size; + dprintk(VIDC_DBG, + "%s memprot.cp_nonpixel_start: %#x size: %#x\n", + __func__, memprot.cp_nonpixel_start, + memprot.cp_nonpixel_size); + } + } + + desc.arginfo = SCM_ARGS(4); + rc = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, + TZBSP_MEM_PROTECT_VIDEO_VAR), &desc); + resp = desc.ret[0]; + + if (rc) { + dprintk(VIDC_ERR, "Failed to protect memory(%d) response: %d\n", + rc, resp); + } + + trace_venus_hfi_var_done( + memprot.cp_start, memprot.cp_size, + memprot.cp_nonpixel_start, memprot.cp_nonpixel_size); + return rc; +} + +static int __disable_regulator(struct regulator_info *rinfo, + struct venus_hfi_device *device) +{ + int rc = 0; + + dprintk(VIDC_DBG, "Disabling regulator %s\n", rinfo->name); + + /* + * This call is needed. Driver needs to acquire the control back + * from HW in order to disable the regualtor. Else the behavior + * is unknown. + */ + + rc = __acquire_regulator(rinfo, device); + if (rc) { + /* + * This is somewhat fatal, but nothing we can do + * about it. We can't disable the regulator w/o + * getting it back under s/w control + */ + dprintk(VIDC_WARN, + "Failed to acquire control on %s\n", + rinfo->name); + + goto disable_regulator_failed; + } + + rc = regulator_disable(rinfo->regulator); + if (rc) { + dprintk(VIDC_WARN, + "Failed to disable %s: %d\n", + rinfo->name, rc); + goto disable_regulator_failed; + } + + return 0; +disable_regulator_failed: + + /* Bring attention to this issue */ + msm_vidc_res_handle_fatal_hw_error(device->res, true); + return rc; +} + +static int __enable_hw_power_collapse(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!msm_vidc_fw_low_power_mode) { + dprintk(VIDC_DBG, "Not enabling hardware power collapse\n"); + return 0; + } + + rc = __hand_off_regulators(device); + if (rc) + dprintk(VIDC_WARN, + "%s : Failed to enable HW power collapse %d\n", + __func__, rc); + return rc; +} + +static int __enable_regulators(struct venus_hfi_device *device) +{ + int rc = 0, c = 0; + struct regulator_info *rinfo; + + dprintk(VIDC_DBG, "Enabling regulators\n"); + + venus_hfi_for_each_regulator(device, rinfo) { + rc = regulator_enable(rinfo->regulator); + if (rc) { + dprintk(VIDC_ERR, + "Failed to enable %s: %d\n", + rinfo->name, rc); + goto err_reg_enable_failed; + } + + dprintk(VIDC_DBG, "Enabled regulator %s\n", + rinfo->name); + c++; + } + + return 0; + +err_reg_enable_failed: + venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c) + __disable_regulator(rinfo, device); + + return rc; +} + +static int __disable_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo; + int rc = 0; + + dprintk(VIDC_DBG, "Disabling regulators\n"); + + venus_hfi_for_each_regulator_reverse(device, rinfo) + __disable_regulator(rinfo, device); + + return rc; +} + +static int __enable_subcaches(struct venus_hfi_device *device) +{ + int rc = 0; + u32 c = 0; + struct subcache_info *sinfo; + + if (msm_vidc_syscache_disable || !is_sys_cache_present(device)) + return 0; + + /* Activate subcaches */ + venus_hfi_for_each_subcache(device, sinfo) { + rc = llcc_slice_activate(sinfo->subcache); + if (rc) { + dprintk(VIDC_WARN, "Failed to activate %s: %d\n", + sinfo->name, rc); + msm_vidc_res_handle_fatal_hw_error(device->res, true); + goto err_activate_fail; + } + sinfo->isactive = true; + dprintk(VIDC_DBG, "Activated subcache %s\n", sinfo->name); + c++; + } + + dprintk(VIDC_DBG, "Activated %d Subcaches to Venus\n", c); + + return 0; + +err_activate_fail: + __release_subcaches(device); + __disable_subcaches(device); + return 0; +} + +static int __set_subcaches(struct venus_hfi_device *device) +{ + int rc = 0; + u32 c = 0; + struct subcache_info *sinfo; + u32 resource[VIDC_MAX_SUBCACHE_SIZE]; + struct hfi_resource_syscache_info_type *sc_res_info; + struct hfi_resource_subcache_type *sc_res; + struct vidc_resource_hdr rhdr; + + if (device->res->sys_cache_res_set) { + dprintk(VIDC_DBG, "Subcaches already set to Venus\n"); + return 0; + } + + memset((void *)resource, 0x0, (sizeof(u32) * VIDC_MAX_SUBCACHE_SIZE)); + + sc_res_info = (struct hfi_resource_syscache_info_type *)resource; + sc_res = &(sc_res_info->rg_subcache_entries[0]); + + venus_hfi_for_each_subcache(device, sinfo) { + if (sinfo->isactive == true) { + sc_res[c].size = sinfo->subcache->slice_size; + sc_res[c].sc_id = sinfo->subcache->slice_id; + c++; + } + } + + /* Set resource to Venus for activated subcaches */ + if (c) { + dprintk(VIDC_DBG, "Setting %d Subcaches\n", c); + + rhdr.resource_handle = sc_res_info; /* cookie */ + rhdr.resource_id = VIDC_RESOURCE_SYSCACHE; + + sc_res_info->num_entries = c; + + rc = __core_set_resource(device, &rhdr, (void *)sc_res_info); + if (rc) { + dprintk(VIDC_WARN, "Failed to set subcaches %d\n", rc); + goto err_fail_set_subacaches; + } + + venus_hfi_for_each_subcache(device, sinfo) { + if (sinfo->isactive == true) + sinfo->isset = true; + } + + dprintk(VIDC_DBG, "Set Subcaches done to Venus\n"); + device->res->sys_cache_res_set = true; + } + + return 0; + +err_fail_set_subacaches: + __disable_subcaches(device); + + return 0; +} + +static int __release_subcaches(struct venus_hfi_device *device) +{ + struct subcache_info *sinfo; + int rc = 0; + u32 c = 0; + u32 resource[VIDC_MAX_SUBCACHE_SIZE]; + struct hfi_resource_syscache_info_type *sc_res_info; + struct hfi_resource_subcache_type *sc_res; + struct vidc_resource_hdr rhdr; + + if (msm_vidc_syscache_disable || !is_sys_cache_present(device)) + return 0; + + memset((void *)resource, 0x0, (sizeof(u32) * VIDC_MAX_SUBCACHE_SIZE)); + + sc_res_info = (struct hfi_resource_syscache_info_type *)resource; + sc_res = &(sc_res_info->rg_subcache_entries[0]); + + /* Release resource command to Venus */ + venus_hfi_for_each_subcache_reverse(device, sinfo) { + if (sinfo->isset == true) { + /* Update the entry */ + sc_res[c].size = sinfo->subcache->slice_size; + sc_res[c].sc_id = sinfo->subcache->slice_id; + c++; + sinfo->isset = false; + } + } + + if (c > 0) { + dprintk(VIDC_DBG, "Releasing %d subcaches\n", c); + rhdr.resource_handle = sc_res_info; /* cookie */ + rhdr.resource_id = VIDC_RESOURCE_SYSCACHE; + + rc = __core_release_resource(device, &rhdr); + if (rc) + dprintk(VIDC_WARN, + "Failed to release %d subcaches\n", c); + } + + device->res->sys_cache_res_set = false; + + return 0; +} + +static int __disable_subcaches(struct venus_hfi_device *device) +{ + struct subcache_info *sinfo; + int rc = 0; + + if (msm_vidc_syscache_disable || !is_sys_cache_present(device)) + return 0; + + /* De-activate subcaches */ + venus_hfi_for_each_subcache_reverse(device, sinfo) { + if (sinfo->isactive == true) { + dprintk(VIDC_DBG, "De-activate subcache %s\n", + sinfo->name); + rc = llcc_slice_deactivate(sinfo->subcache); + if (rc) { + dprintk(VIDC_WARN, + "Failed to de-activate %s: %d\n", + sinfo->name, rc); + } + sinfo->isactive = false; + } + } + + return 0; +} + +static void interrupt_init_vpu5(struct venus_hfi_device *device) +{ + u32 mask_val = 0; + + /* All interrupts should be disabled initially 0x1F6 : Reset value */ + mask_val = __read_register(device, VIDC_WRAPPER_INTR_MASK); + + /* Write 0 to unmask CPU and WD interrupts */ + mask_val &= ~(VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK | + VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK); + __write_register(device, VIDC_WRAPPER_INTR_MASK, mask_val); +} + +static void interrupt_init_vpu4(struct venus_hfi_device *device) +{ + __write_register(device, VIDC_WRAPPER_INTR_MASK, + VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK); +} + +static void clock_config_on_enable_vpu4(struct venus_hfi_device *device) +{ + __write_register(device, VIDC_WRAPPER_CLOCK_CONFIG, 0); + __write_register(device, VIDC_WRAPPER_CPU_CLOCK_CONFIG, 0); +} + +static void setup_dsp_uc_memmap_vpu5(struct venus_hfi_device *device) +{ + /* initialize DSP QTBL & UCREGION with CPU queues */ + __write_register(device, HFI_DSP_QTBL_ADDR, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, HFI_DSP_UC_REGION_ADDR, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, HFI_DSP_UC_REGION_SIZE, SHARED_QSIZE); + if (device->res->domain_cvp) { + __write_register(device, HFI_DSP_QTBL_ADDR, + (u32)device->dsp_iface_q_table.align_device_addr); + __write_register(device, HFI_DSP_UC_REGION_ADDR, + (u32)device->dsp_iface_q_table.align_device_addr); + __write_register(device, HFI_DSP_UC_REGION_SIZE, + device->dsp_iface_q_table.mem_data.size); + } +} + +static void clock_config_on_enable_vpu5(struct venus_hfi_device *device) +{ + __write_register(device, VIDC_WRAPPER_CPU_CGC_DIS, 0); + __write_register(device, VIDC_WRAPPER_CPU_CLOCK_CONFIG, 0); +} + +static int __venus_power_on(struct venus_hfi_device *device) +{ + int rc = 0; + + + if (device->power_enabled) + return 0; + + device->power_enabled = true; + /* Vote for all hardware resources */ + rc = __vote_buses(device, device->bus_vote.data, + device->bus_vote.data_count); + if (rc) { + dprintk(VIDC_ERR, "Failed to vote buses, err: %d\n", rc); + goto fail_vote_buses; + } + + rc = __enable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable GDSC, err = %d\n", rc); + goto fail_enable_gdsc; + } + + rc = __prepare_ahb2axi_bridge(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable ahb2axi: %d\n", rc); + goto fail_enable_clks; + } + + rc = __prepare_enable_clks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc); + goto fail_enable_clks; + } + + rc = __scale_clocks(device); + if (rc) { + dprintk(VIDC_WARN, + "Failed to scale clocks, performance might be affected\n"); + rc = 0; + } + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + __set_registers(device); + + call_venus_op(device, interrupt_init, device); + device->intr_status = 0; + enable_irq(device->hal_data->irq); + + /* + * Hand off control of regulators to h/w _after_ enabling clocks. + * Note that the GDSC will turn off when switching from normal + * (s/w triggered) to fast (HW triggered) unless the h/w vote is + * present. Since Venus isn't up yet, the GDSC will be off briefly. + */ + if (__enable_hw_power_collapse(device)) + dprintk(VIDC_ERR, "Failed to enabled inter-frame PC\n"); + + return rc; + +fail_enable_clks: + __disable_regulators(device); +fail_enable_gdsc: + __unvote_buses(device); +fail_vote_buses: + device->power_enabled = false; + return rc; +} + +static void __venus_power_off(struct venus_hfi_device *device, bool axi_reset) +{ + u32 version; + + if (!device->power_enabled) + return; + + if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + disable_irq_nosync(device->hal_data->irq); + device->intr_status = 0; + + if (axi_reset) + version = __read_register(device, VIDC_WRAPPER_HW_VERSION); + + __disable_unprepare_clks(device); + + if (axi_reset) + __unprepare_ahb2axi_bridge(device, version); + + if (__disable_regulators(device)) + dprintk(VIDC_WARN, "Failed to disable regulators\n"); + + if (__unvote_buses(device)) + dprintk(VIDC_WARN, "Failed to unvote for buses\n"); + device->power_enabled = false; +} + +static inline int __suspend(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } else if (!device->power_enabled) { + dprintk(VIDC_DBG, "Power already disabled\n"); + return 0; + } + + dprintk(VIDC_PROF, "Entering suspend\n"); + + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) + pm_qos_remove_request(&device->qos); + + rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); + if (rc) { + dprintk(VIDC_WARN, "Failed to suspend video core %d\n", rc); + goto err_tzbsp_suspend; + } + + __disable_subcaches(device); + + __venus_power_off(device, false); + dprintk(VIDC_PROF, "Venus power off\n"); + return rc; + +err_tzbsp_suspend: + return rc; +} + +static inline int __resume(struct venus_hfi_device *device) +{ + int rc = 0; + u32 flags = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } else if (device->power_enabled) { + goto exit; + } else if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "venus_hfi_device in deinit state."); + return -EINVAL; + } + + dprintk(VIDC_PROF, "Resuming from power collapse\n"); + rc = __venus_power_on(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to power on venus\n"); + goto err_venus_power_on; + } + + /* Reboot the firmware */ + rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME); + if (rc) { + dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc); + goto err_set_video_state; + } + + __setup_ucregion_memory_map(device); + /* Wait for boot completion */ + rc = __boot_firmware(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to reset venus core\n"); + goto err_reset_core; + } + + /* + * Work around for H/W bug, need to reprogram these registers once + * firmware is out reset + */ + __set_threshold_registers(device); + + if (device->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + device->qos.type = PM_QOS_REQ_AFFINE_IRQ; + device->qos.irq = device->hal_data->irq; +#endif + pm_qos_add_request(&device->qos, PM_QOS_CPU_DMA_LATENCY, + device->res->pm_qos_latency_us); + } + + __sys_set_debug(device, msm_vidc_fw_debug); + + __enable_subcaches(device); + __set_subcaches(device); + __dsp_resume(device, flags); + + dprintk(VIDC_PROF, "Resumed from power collapse\n"); +exit: + /* Don't reset skip_pc_count for SYS_PC_PREP cmd */ + if (device->last_packet_type != HFI_CMD_SYS_PC_PREP) + device->skip_pc_count = 0; + return rc; +err_reset_core: + __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); +err_set_video_state: + __venus_power_off(device, false); +err_venus_power_on: + dprintk(VIDC_ERR, "Failed to resume from power collapse\n"); + return rc; +} + +static int __load_fw(struct venus_hfi_device *device) +{ + int rc = 0; + + /* Initialize resources */ + rc = __init_resources(device, device->res); + if (rc) { + dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); + goto fail_init_res; + } + + rc = __initialize_packetization(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to initialize packetization\n"); + goto fail_init_pkt; + } + trace_msm_v4l2_vidc_fw_load_start("msm_v4l2_vidc venus_fw load start"); + + rc = __venus_power_on(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to power on venus in in load_fw\n"); + goto fail_venus_power_on; + } + + if (!device->res->firmware_base) { + if (!device->resources.fw.cookie) + device->resources.fw.cookie = + subsystem_get_with_fwname("venus", + device->res->fw_name); + + if (IS_ERR_OR_NULL(device->resources.fw.cookie)) { + dprintk(VIDC_ERR, "Failed to download firmware\n"); + device->resources.fw.cookie = NULL; + rc = -ENOMEM; + goto fail_load_fw; + } + } else { + dprintk(VIDC_ERR, "Firmware base must be 0\n"); + } + + if (!device->res->firmware_base) { + rc = __protect_cp_mem(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to protect memory\n"); + goto fail_protect_mem; + } + } + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end"); + return rc; +fail_protect_mem: + if (device->resources.fw.cookie) + subsystem_put(device->resources.fw.cookie); + device->resources.fw.cookie = NULL; +fail_load_fw: + __venus_power_off(device, true); +fail_venus_power_on: +fail_init_pkt: + __deinit_resources(device); +fail_init_res: + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end"); + return rc; +} + +static void __unload_fw(struct venus_hfi_device *device) +{ + if (!device->resources.fw.cookie) + return; + + cancel_delayed_work(&venus_hfi_pm_work); + if (device->state != VENUS_STATE_DEINIT) + flush_workqueue(device->venus_pm_workq); + + __vote_buses(device, NULL, 0); + subsystem_put(device->resources.fw.cookie); + __interface_queues_release(device); + __venus_power_off(device, true); + device->resources.fw.cookie = NULL; + __deinit_resources(device); + + dprintk(VIDC_PROF, "Firmware unloaded successfully\n"); +} + +static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info) +{ + int i = 0, j = 0; + struct venus_hfi_device *device = dev; + size_t smem_block_size = 0; + u8 *smem_table_ptr; + char version[VENUS_VERSION_LENGTH] = ""; + const u32 smem_image_index_venus = 14 * 128; + + if (!device || !fw_info) { + dprintk(VIDC_ERR, + "%s Invalid parameter: device = %pK fw_info = %pK\n", + __func__, device, fw_info); + return -EINVAL; + } + + mutex_lock(&device->lock); + + smem_table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, &smem_block_size); + if (smem_table_ptr && + ((smem_image_index_venus + + VENUS_VERSION_LENGTH) <= smem_block_size)) + memcpy(version, + smem_table_ptr + smem_image_index_venus, + VENUS_VERSION_LENGTH); + + while (version[i++] != 'V' && i < VENUS_VERSION_LENGTH) + ; + + if (i == VENUS_VERSION_LENGTH - 1) { + dprintk(VIDC_WARN, "Venus version string is not proper\n"); + fw_info->version[0] = '\0'; + goto fail_version_string; + } + + for (i--; i < VENUS_VERSION_LENGTH && j < VENUS_VERSION_LENGTH - 1; i++) + fw_info->version[j++] = version[i]; + fw_info->version[j] = '\0'; + +fail_version_string: + dprintk(VIDC_DBG, "F/W version retrieved : %s\n", fw_info->version); + fw_info->base_addr = device->hal_data->firmware_base; + fw_info->register_base = device->res->register_base; + fw_info->register_size = device->hal_data->register_size; + fw_info->irq = device->hal_data->irq; + + mutex_unlock(&device->lock); + return 0; +} + +static int venus_hfi_get_core_capabilities(void *dev) +{ + struct venus_hfi_device *device = dev; + int rc = 0; + + if (!device) + return -EINVAL; + + mutex_lock(&device->lock); + + rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY | + HAL_VIDEO_ENCODER_SCALING_CAPABILITY | + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY | + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY; + + mutex_unlock(&device->lock); + + return rc; +} + +static void __noc_error_info(struct venus_hfi_device *device, u32 core_type) +{ + u32 noc_base_offs, val; + + if (!device) { + dprintk(VIDC_ERR, "%s: null device\n", __func__); + return; + } + if (!core_type) { + noc_base_offs = + VCODEC_CORE0_VIDEO_NOC_BASE_OFFS; + } else if (core_type == 1) { + noc_base_offs = + CVP_NOC_BASE_OFFS; + } else { + dprintk(VIDC_ERR, "%s: invalid core_type %u\n", + __func__, core_type); + return; + } + + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_SWID_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_SWID_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_SWID_HIGH_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_SWID_HIGH: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_MAINCTL_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_MAINCTL_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG0_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_HIGH_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG0_HIGH: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG1_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_HIGH_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG1_HIGH: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG2_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_HIGH_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG2_HIGH: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_LOW_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG3_LOW: %#x\n", core_type, val); + val = __read_register(device, noc_base_offs + + VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_HIGH_OFFS); + dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG3_HIGH: %#x\n", core_type, val); +} + +static int venus_hfi_noc_error_info(void *dev) +{ + struct venus_hfi_device *device; + const u32 vcodec = 0, cvp = 1; + + if (!dev) { + dprintk(VIDC_ERR, "%s: null device\n", __func__); + return -EINVAL; + } + device = dev; + + mutex_lock(&device->lock); + dprintk(VIDC_ERR, "%s: non error information\n", __func__); + + if (__read_register(device, VCODEC_CORE0_VIDEO_NOC_BASE_OFFS + + VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS)) + __noc_error_info(device, vcodec); + + if (device->res->vpu_ver == VPU_VERSION_5) { + if (__read_register(device, CVP_NOC_BASE_OFFS + + VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS)) + __noc_error_info(device, cvp); + } + + mutex_unlock(&device->lock); + + return 0; +} + +static int __initialize_packetization(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device || !device->res) { + dprintk(VIDC_ERR, "%s - invalid param\n", __func__); + return -EINVAL; + } + + device->packetization_type = HFI_PACKETIZATION_4XX; + + device->pkt_ops = hfi_get_pkt_ops_handle(device->packetization_type); + if (!device->pkt_ops) { + rc = -EINVAL; + dprintk(VIDC_ERR, "Failed to get pkt_ops handle\n"); + } + + return rc; +} + +void __init_venus_ops(struct venus_hfi_device *device) +{ + if (device->res->vpu_ver == VPU_VERSION_4) + device->vpu_ops = &vpu4_ops; + else + device->vpu_ops = &vpu5_ops; +} + +static struct venus_hfi_device *__add_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + struct venus_hfi_device *hdevice = NULL; + int rc = 0; + + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid Parameters\n"); + return NULL; + } + + dprintk(VIDC_INFO, "entered , device_id: %d\n", device_id); + + hdevice = (struct venus_hfi_device *) + kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL); + if (!hdevice) { + dprintk(VIDC_ERR, "failed to allocate new device\n"); + goto exit; + } + + hdevice->response_pkt = kmalloc_array(max_packets, + sizeof(*hdevice->response_pkt), GFP_KERNEL); + if (!hdevice->response_pkt) { + dprintk(VIDC_ERR, "failed to allocate response_pkt\n"); + goto err_cleanup; + } + + hdevice->raw_packet = + kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_KERNEL); + if (!hdevice->raw_packet) { + dprintk(VIDC_ERR, "failed to allocate raw packet\n"); + goto err_cleanup; + } + + rc = __init_regs_and_interrupts(hdevice, res); + if (rc) + goto err_cleanup; + + hdevice->res = res; + hdevice->device_id = device_id; + hdevice->callback = callback; + + __init_venus_ops(hdevice); + + hdevice->vidc_workq = create_singlethread_workqueue( + "msm_vidc_workerq_venus"); + if (!hdevice->vidc_workq) { + dprintk(VIDC_ERR, ": create vidc workq failed\n"); + goto err_cleanup; + } + + hdevice->venus_pm_workq = create_singlethread_workqueue( + "pm_workerq_venus"); + if (!hdevice->venus_pm_workq) { + dprintk(VIDC_ERR, ": create pm workq failed\n"); + goto err_cleanup; + } + + if (!hal_ctxt.dev_count) + INIT_LIST_HEAD(&hal_ctxt.dev_head); + + mutex_init(&hdevice->lock); + INIT_LIST_HEAD(&hdevice->list); + INIT_LIST_HEAD(&hdevice->sess_head); + list_add_tail(&hdevice->list, &hal_ctxt.dev_head); + hal_ctxt.dev_count++; + + return hdevice; + +err_cleanup: + if (hdevice->vidc_workq) + destroy_workqueue(hdevice->vidc_workq); + kfree(hdevice->response_pkt); + kfree(hdevice->raw_packet); + kfree(hdevice); +exit: + return NULL; +} + +static struct venus_hfi_device *__get_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid params: %pK %pK\n", res, callback); + return NULL; + } + + return __add_device(device_id, res, callback); +} + +void venus_hfi_delete_device(void *device) +{ + struct venus_hfi_device *close, *tmp, *dev; + + if (!device) + return; + + dev = (struct venus_hfi_device *) device; + + list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) { + if (close->hal_data->irq == dev->hal_data->irq) { + hal_ctxt.dev_count--; + list_del(&close->list); + mutex_destroy(&close->lock); + destroy_workqueue(close->vidc_workq); + destroy_workqueue(close->venus_pm_workq); + free_irq(dev->hal_data->irq, close); + iounmap(dev->hal_data->register_base); + kfree(close->hal_data); + kfree(close->response_pkt); + kfree(close->raw_packet); + kfree(close); + break; + } + } +} + +static void venus_init_hfi_callbacks(struct hfi_device *hdev) +{ + hdev->core_init = venus_hfi_core_init; + hdev->core_release = venus_hfi_core_release; + hdev->core_ping = venus_hfi_core_ping; + hdev->core_trigger_ssr = venus_hfi_core_trigger_ssr; + hdev->session_init = venus_hfi_session_init; + hdev->session_end = venus_hfi_session_end; + hdev->session_abort = venus_hfi_session_abort; + hdev->session_clean = venus_hfi_session_clean; + hdev->session_set_buffers = venus_hfi_session_set_buffers; + hdev->session_release_buffers = venus_hfi_session_release_buffers; + hdev->session_register_buffer = venus_hfi_session_register_buffer; + hdev->session_unregister_buffer = venus_hfi_session_unregister_buffer; + hdev->session_load_res = venus_hfi_session_load_res; + hdev->session_release_res = venus_hfi_session_release_res; + hdev->session_start = venus_hfi_session_start; + hdev->session_continue = venus_hfi_session_continue; + hdev->session_stop = venus_hfi_session_stop; + hdev->session_etb = venus_hfi_session_etb; + hdev->session_ftb = venus_hfi_session_ftb; + hdev->session_process_batch = venus_hfi_session_process_batch; + hdev->session_get_buf_req = venus_hfi_session_get_buf_req; + hdev->session_flush = venus_hfi_session_flush; + hdev->session_set_property = venus_hfi_session_set_property; + hdev->session_get_property = venus_hfi_session_get_property; + hdev->session_pause = venus_hfi_session_pause; + hdev->session_resume = venus_hfi_session_resume; + hdev->scale_clocks = venus_hfi_scale_clocks; + hdev->vote_bus = venus_hfi_vote_buses; + hdev->get_fw_info = venus_hfi_get_fw_info; + hdev->get_core_capabilities = venus_hfi_get_core_capabilities; + hdev->suspend = venus_hfi_suspend; + hdev->flush_debug_queue = venus_hfi_flush_debug_queue; + hdev->noc_error_info = venus_hfi_noc_error_info; + hdev->get_default_properties = venus_hfi_get_default_properties; +} + +int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + int rc = 0; + + if (!hdev || !res || !callback) { + dprintk(VIDC_ERR, "Invalid params: %pK %pK %pK\n", + hdev, res, callback); + rc = -EINVAL; + goto err_venus_hfi_init; + } + + hdev->hfi_device_data = __get_device(device_id, res, callback); + + if (IS_ERR_OR_NULL(hdev->hfi_device_data)) { + rc = PTR_ERR(hdev->hfi_device_data) ?: -EINVAL; + goto err_venus_hfi_init; + } + + venus_init_hfi_callbacks(hdev); + +err_venus_hfi_init: + return rc; +} + diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h new file mode 100644 index 000000000000..1d6d0e033a23 --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_hfi.h @@ -0,0 +1,301 @@ +/* Copyright (c) 2012-2019, 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 __H_VENUS_HFI_H__ +#define __H_VENUS_HFI_H__ + +#include +#include +#include +#include +#include +#include "vidc_hfi_api.h" +#include "vidc_hfi_helper.h" +#include "vidc_hfi_api.h" +#include "vidc_hfi.h" +#include "msm_vidc_resources.h" +#include "hfi_packetization.h" + +#define HFI_MASK_QHDR_TX_TYPE 0xFF000000 +#define HFI_MASK_QHDR_RX_TYPE 0x00FF0000 +#define HFI_MASK_QHDR_PRI_TYPE 0x0000FF00 +#define HFI_MASK_QHDR_Q_ID_TYPE 0x000000FF +#define HFI_Q_ID_HOST_TO_CTRL_CMD_Q 0x00 +#define HFI_Q_ID_CTRL_TO_HOST_MSG_Q 0x01 +#define HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q 0x02 +#define HFI_MASK_QHDR_STATUS 0x000000FF + +#define VIDC_MAX_UNCOMPRESSED_FMT_PLANES 3 + +#define VIDC_IFACEQ_NUMQ 3 +#define VIDC_IFACEQ_CMDQ_IDX 0 +#define VIDC_IFACEQ_MSGQ_IDX 1 +#define VIDC_IFACEQ_DBGQ_IDX 2 +#define VIDC_IFACEQ_MAX_BUF_COUNT 50 +#define VIDC_IFACE_MAX_PARALLEL_CLNTS 16 +#define VIDC_IFACEQ_DFLT_QHDR 0x01010000 + +#define VIDC_MAX_NAME_LENGTH 64 +#define VIDC_MAX_PC_SKIP_COUNT 10 +#define VIDC_MAX_SUBCACHES 4 +#define VIDC_MAX_SUBCACHE_SIZE 52 + +extern unsigned long __calc_bw(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data); +extern unsigned long __calc_bw_ar50(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data); + +struct hfi_queue_table_header { + u32 qtbl_version; + u32 qtbl_size; + u32 qtbl_qhdr0_offset; + u32 qtbl_qhdr_size; + u32 qtbl_num_q; + u32 qtbl_num_active_q; + void *device_addr; + char name[256]; +}; + +struct hfi_queue_header { + u32 qhdr_status; + u32 qhdr_start_addr; + u32 qhdr_type; + u32 qhdr_q_size; + u32 qhdr_pkt_size; + u32 qhdr_pkt_drop_cnt; + u32 qhdr_rx_wm; + u32 qhdr_tx_wm; + u32 qhdr_rx_req; + u32 qhdr_tx_req; + u32 qhdr_rx_irq_status; + u32 qhdr_tx_irq_status; + u32 qhdr_read_idx; + u32 qhdr_write_idx; +}; + +struct hfi_mem_map_table { + u32 mem_map_num_entries; + u32 mem_map_table_base_addr; +}; + +struct hfi_mem_map { + u32 virtual_addr; + u32 physical_addr; + u32 size; + u32 attr; +}; + +#define VIDC_IFACEQ_TABLE_SIZE (sizeof(struct hfi_queue_table_header) \ + + sizeof(struct hfi_queue_header) * VIDC_IFACEQ_NUMQ) + +#define VIDC_IFACEQ_QUEUE_SIZE (VIDC_IFACEQ_MAX_PKT_SIZE * \ + VIDC_IFACEQ_MAX_BUF_COUNT * VIDC_IFACE_MAX_PARALLEL_CLNTS) + +#define VIDC_IFACEQ_GET_QHDR_START_ADDR(ptr, i) \ + (void *)((ptr + sizeof(struct hfi_queue_table_header)) + \ + (i * sizeof(struct hfi_queue_header))) + +#define QDSS_SIZE 4096 +#define SFR_SIZE 4096 + +#define QUEUE_SIZE (VIDC_IFACEQ_TABLE_SIZE + \ + (VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ)) + +#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K) +#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K) +#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K) +#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \ + ALIGNED_QDSS_SIZE, SZ_1M) + +enum vidc_hw_reg { + VIDC_HWREG_CTRL_STATUS = 0x1, + VIDC_HWREG_QTBL_INFO = 0x2, + VIDC_HWREG_QTBL_ADDR = 0x3, + VIDC_HWREG_CTRLR_RESET = 0x4, + VIDC_HWREG_IFACEQ_FWRXREQ = 0x5, + VIDC_HWREG_IFACEQ_FWTXREQ = 0x6, + VIDC_HWREG_VHI_SOFTINTEN = 0x7, + VIDC_HWREG_VHI_SOFTINTSTATUS = 0x8, + VIDC_HWREG_VHI_SOFTINTCLR = 0x9, + VIDC_HWREG_HVI_SOFTINTEN = 0xA, +}; + +struct vidc_mem_addr { + u32 align_device_addr; + u8 *align_virtual_addr; + u32 mem_size; + struct msm_smem mem_data; +}; + +struct vidc_iface_q_info { + void *q_hdr; + struct vidc_mem_addr q_array; +}; + +/* + * These are helper macros to iterate over various lists within + * venus_hfi_device->res. The intention is to cut down on a lot of boiler-plate + * code + */ + +/* Read as "for each 'thing' in a set of 'thingies'" */ +#define venus_hfi_for_each_thing(__device, __thing, __thingy) \ + venus_hfi_for_each_thing_continue(__device, __thing, __thingy, 0) + +#define venus_hfi_for_each_thing_reverse(__device, __thing, __thingy) \ + venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \ + (__device)->res->__thingy##_set.count - 1) + +/* TODO: the __from parameter technically not required since we can figure it + * out with some pointer magic (i.e. __thing - __thing##_tbl[0]). If this macro + * sees extensive use, probably worth cleaning it up but for now omitting it + * since it introduces unneccessary complexity. + */ +#define venus_hfi_for_each_thing_continue(__device, __thing, __thingy, __from) \ + for (__thing = &(__device)->res->\ + __thingy##_set.__thingy##_tbl[__from]; \ + __thing < &(__device)->res->__thingy##_set.__thingy##_tbl[0] + \ + ((__device)->res->__thingy##_set.count - __from); \ + ++__thing) + +#define venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \ + __from) \ + for (__thing = &(__device)->res->\ + __thingy##_set.__thingy##_tbl[__from]; \ + __thing >= &(__device)->res->__thingy##_set.__thingy##_tbl[0]; \ + --__thing) + +/* Regular set helpers */ +#define venus_hfi_for_each_regulator(__device, __rinfo) \ + venus_hfi_for_each_thing(__device, __rinfo, regulator) + +#define venus_hfi_for_each_regulator_reverse(__device, __rinfo) \ + venus_hfi_for_each_thing_reverse(__device, __rinfo, regulator) + +#define venus_hfi_for_each_regulator_reverse_continue(__device, __rinfo, \ + __from) \ + venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \ + regulator, __from) + +/* Clock set helpers */ +#define venus_hfi_for_each_clock(__device, __cinfo) \ + venus_hfi_for_each_thing(__device, __cinfo, clock) + +#define venus_hfi_for_each_clock_reverse(__device, __cinfo) \ + venus_hfi_for_each_thing_reverse(__device, __cinfo, clock) + +#define venus_hfi_for_each_clock_reverse_continue(__device, __rinfo, \ + __from) \ + venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \ + clock, __from) + +/* Bus set helpers */ +#define venus_hfi_for_each_bus(__device, __binfo) \ + venus_hfi_for_each_thing(__device, __binfo, bus) +#define venus_hfi_for_each_bus_reverse(__device, __binfo) \ + venus_hfi_for_each_thing_reverse(__device, __binfo, bus) + +/* Subcache set helpers */ +#define venus_hfi_for_each_subcache(__device, __sinfo) \ + venus_hfi_for_each_thing(__device, __sinfo, subcache) +#define venus_hfi_for_each_subcache_reverse(__device, __sinfo) \ + venus_hfi_for_each_thing_reverse(__device, __sinfo, subcache) + +#define call_venus_op(d, op, args...) \ + (((d) && (d)->vpu_ops && (d)->vpu_ops->op) ? \ + ((d)->vpu_ops->op(args)):0) + +/* Internal data used in vidc_hal not exposed to msm_vidc*/ +struct hal_data { + u32 irq; + phys_addr_t firmware_base; + u8 __iomem *register_base; + u32 register_size; +}; + +struct venus_resources { + struct msm_vidc_fw fw; +}; + +enum dsp_flag { + DSP_INIT = BIT(0), + DSP_SUSPEND = BIT(1), +}; + +enum venus_hfi_state { + VENUS_STATE_DEINIT = 1, + VENUS_STATE_INIT, +}; + +enum reset_state { + INIT = 1, + ASSERT, + DEASSERT, +}; + +struct venus_hfi_device; + +struct venus_hfi_vpu_ops { + void (*interrupt_init)(struct venus_hfi_device *ptr); + void (*setup_dsp_uc_memmap)(struct venus_hfi_device *device); + void (*clock_config_on_enable)(struct venus_hfi_device *device); +}; + +struct venus_hfi_device { + struct list_head list; + struct list_head sess_head; + u32 intr_status; + u32 device_id; + u32 clk_freq; + u32 last_packet_type; + unsigned long clk_bitrate; + unsigned long scaled_rate; + struct msm_vidc_gov_data bus_vote; + bool power_enabled; + struct mutex lock; + msm_vidc_callback callback; + struct vidc_mem_addr iface_q_table; + struct vidc_mem_addr dsp_iface_q_table; + struct vidc_mem_addr qdss; + struct vidc_mem_addr sfr; + struct vidc_mem_addr mem_addr; + struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ]; + struct vidc_iface_q_info dsp_iface_queues[VIDC_IFACEQ_NUMQ]; + u32 dsp_flags; + struct hal_data *hal_data; + struct workqueue_struct *vidc_workq; + struct workqueue_struct *venus_pm_workq; + int spur_count; + int reg_count; + struct venus_resources resources; + struct msm_vidc_platform_resources *res; + enum venus_hfi_state state; + struct hfi_packetization_ops *pkt_ops; + enum hfi_packetization_type packetization_type; + struct msm_vidc_cb_info *response_pkt; + u8 *raw_packet; + struct pm_qos_request qos; + unsigned int skip_pc_count; + struct msm_vidc_capability *sys_init_capabilities; + struct venus_hfi_vpu_ops *vpu_ops; +}; + +void venus_hfi_delete_device(void *device); + +int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback); +bool venus_hfi_is_session_supported(unsigned long sessions_supported, + enum vidc_vote_data_session session_type); + +#endif diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.c b/drivers/media/platform/msm/vidc/vidc_hfi.c new file mode 100644 index 000000000000..273471a944ef --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi.c @@ -0,0 +1,73 @@ +/* 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 + * 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 "msm_vidc_debug.h" +#include "vidc_hfi_api.h" +#include "venus_hfi.h" + +struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, + u32 device_id, struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + struct hfi_device *hdev = NULL; + int rc = 0; + + hdev = (struct hfi_device *) + kzalloc(sizeof(struct hfi_device), GFP_KERNEL); + if (!hdev) { + dprintk(VIDC_ERR, "%s: failed to allocate hdev\n", __func__); + return NULL; + } + + switch (hfi_type) { + case VIDC_HFI_VENUS: + rc = venus_hfi_initialize(hdev, device_id, res, callback); + break; + default: + dprintk(VIDC_ERR, "Unsupported host-firmware interface\n"); + goto err_hfi_init; + } + + if (rc) { + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "%s device init failed rc = %d", + __func__, rc); + goto err_hfi_init; + } + + return hdev; + +err_hfi_init: + kfree(hdev); + return ERR_PTR(rc); +} + +void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type, + struct hfi_device *hdev) +{ + if (!hdev) { + dprintk(VIDC_ERR, "%s invalid device %pK", __func__, hdev); + return; + } + + switch (hfi_type) { + case VIDC_HFI_VENUS: + venus_hfi_delete_device(hdev->hfi_device_data); + break; + default: + dprintk(VIDC_ERR, "Unsupported host-firmware interface\n"); + } + + kfree(hdev); +} + diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h new file mode 100644 index 000000000000..41d10d5caf43 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi.h @@ -0,0 +1,870 @@ +/* Copyright (c) 2012-2019, 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 __H_VIDC_HFI_H__ +#define __H_VIDC_HFI_H__ + +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi_api.h" + +#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3) +#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4) +#define HFI_EVENT_SESSION_LTRUSE_FAILED (HFI_OX_BASE + 0x5) +#define HFI_EVENT_RELEASE_BUFFER_REFERENCE (HFI_OX_BASE + 0x6) + +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES \ + (HFI_OX_BASE + 0x1) +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES \ + (HFI_OX_BASE + 0x2) + +#define HFI_BUFFERFLAG_EOS 0x00000001 +#define HFI_BUFFERFLAG_STARTTIME 0x00000002 +#define HFI_BUFFERFLAG_DECODEONLY 0x00000004 +#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020 +#define HFI_BUFFERFLAG_EXTRADATA 0x00000040 +#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080 +#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100 +#define HFI_BUFFERFLAG_READONLY 0x00000200 +#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 +#define HFI_BUFFERFLAG_EOSEQ 0x00200000 +#define HFI_BUFFER_FLAG_MBAFF 0x08000000 +#define HFI_BUFFERFLAG_VPE_YUV_601_709_CSC_CLAMP \ + 0x10000000 +#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000 +#define HFI_BUFFERFLAG_TEI 0x40000000 +#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000 + + +#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING \ + (HFI_OX_BASE + 0x1001) +#define HFI_ERR_SESSION_SAME_STATE_OPERATION \ + (HFI_OX_BASE + 0x1002) +#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED \ + (HFI_OX_BASE + 0x1003) +#define HFI_ERR_SESSION_START_CODE_NOT_FOUND \ + (HFI_OX_BASE + 0x1004) + + +#define HFI_BUFFER_MODE_DYNAMIC (HFI_OX_BASE + 0x3) + +#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1) +#define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2) +#define HFI_FLUSH_ALL (HFI_OX_BASE + 0x4) + +#define HFI_EXTRADATA_NONE 0x00000000 +#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001 +#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002 +#define HFI_EXTRADATA_TIMESTAMP 0x00000005 +#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006 +#define HFI_EXTRADATA_FRAME_RATE 0x00000007 +#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008 +#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 +#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D +#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000E +#define HFI_EXTRADATA_FRAME_QP 0x0000000F +#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010 +#define HFI_EXTRADATA_VPX_COLORSPACE 0x00000014 +#define HFI_EXTRADATA_UBWC_CR_STAT_INFO 0x00000019 +#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000 +#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001 +#define HFI_EXTRADATA_INDEX 0x7F100002 +#define HFI_EXTRADATA_METADATA_LTR 0x7F100004 +#define HFI_EXTRADATA_METADATA_FILLER 0x7FE00002 + +#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000E +#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000F +#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7F100003 + +struct hfi_buffer_alloc_mode { + u32 buffer_type; + u32 buffer_mode; +}; + + +struct hfi_index_extradata_config { + int enable; + u32 index_extra_data_id; +}; + +struct hfi_extradata_header { + u32 size; + u32 version; + u32 port_index; + u32 type; + u32 data_size; + u8 rg_data[1]; +}; + +#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01 +#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02 +#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04 +#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08 +#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10 +#define HFI_INTERLACE_FRAME_MBAFF 0x20 + +#define HFI_PROPERTY_SYS_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000) + +#define HFI_PROPERTY_PARAM_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000) +#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL \ + (HFI_PROPERTY_PARAM_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO \ + (HFI_PROPERTY_PARAM_OX_START + 0x002) +#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA \ + (HFI_PROPERTY_PARAM_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA \ + (HFI_PROPERTY_PARAM_OX_START + 0x009) +#define HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM \ + (HFI_PROPERTY_PARAM_OX_START + 0x00C) +#define HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT \ + (HFI_PROPERTY_PARAM_OX_START + 0x00E) + +#define HFI_PROPERTY_CONFIG_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000) +#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS \ + (HFI_PROPERTY_CONFIG_OX_START + 0x001) +#define HFI_PROPERTY_CONFIG_REALTIME \ + (HFI_PROPERTY_CONFIG_OX_START + 0x002) +#define HFI_PROPERTY_CONFIG_PRIORITY \ + (HFI_PROPERTY_CONFIG_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_VDEC_OX_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000) +#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x004) +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x005) +#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007) +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A) +#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B) +#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C) +#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D) +#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013) +#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014) +#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015) +#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016) +#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x017) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019) +#define HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01B) +#define HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001C) +#define HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001D) +#define HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001E) +#define HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001F) +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_REMAPPING_INFO_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x0020) +#define HFI_PROPERTY_PARAM_VDEC_DOWN_SCALAR \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x0021) +#define HFI_PROPERTY_PARAM_VDEC_UBWC_CR_STAT_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x0022) + +#define HFI_PROPERTY_CONFIG_VDEC_OX_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x4000) +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x002) +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x003) +#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x004) + +#define HFI_PROPERTY_PARAM_VENC_OX_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000) +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x002) +#define HFI_PROPERTY_PARAM_VENC_LTR_INFO \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_VENC_MBI_DUMPING \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x005) +#define HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x008) +#define HFI_PROPERTY_PARAM_VENC_HDR10PLUS_METADATA_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x00A) +#define HFI_PROPERTY_PARAM_VENC_DTS_INFO \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x00C) + +#define HFI_PROPERTY_CONFIG_VENC_OX_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000) +#define HFI_PROPERTY_PARAM_VPE_OX_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000) + +#define HFI_PROPERTY_CONFIG_VPE_OX_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000) + +struct hfi_batch_info { + u32 input_batch_count; + u32 output_batch_count; +}; + +struct hfi_buffer_count_actual { + u32 buffer_type; + u32 buffer_count_actual; + u32 buffer_count_min_host; +}; + +struct hfi_buffer_size_minimum { + u32 buffer_type; + u32 buffer_size; +}; + +struct hfi_buffer_requirements { + u32 buffer_type; + u32 buffer_size; + u32 buffer_region_size; + u32 buffer_count_min; + u32 buffer_count_min_host; + u32 buffer_count_actual; + u32 contiguous; + u32 buffer_alignment; +}; + +struct hfi_data_payload { + u32 size; + u8 rg_data[1]; +}; + +struct hfi_enable_picture { + u32 picture_type; +}; + +struct hfi_mb_error_map { + u32 error_map_size; + u8 rg_error_map[1]; +}; + +struct hfi_metadata_pass_through { + int enable; + u32 size; +}; + +struct hfi_multi_view_select { + u32 view_index; +}; + +struct hfi_hybrid_hierp { + u32 layers; +}; + +#define HFI_PRIORITY_LOW 10 +#define HFI_PRIOIRTY_MEDIUM 20 +#define HFI_PRIORITY_HIGH 30 + +#define HFI_OUTPUT_ORDER_DISPLAY (HFI_OX_BASE + 0x1) +#define HFI_OUTPUT_ORDER_DECODE (HFI_OX_BASE + 0x2) + +#define HFI_RATE_CONTROL_OFF (HFI_OX_BASE + 0x1) +#define HFI_RATE_CONTROL_VBR_VFR (HFI_OX_BASE + 0x2) +#define HFI_RATE_CONTROL_VBR_CFR (HFI_OX_BASE + 0x3) +#define HFI_RATE_CONTROL_CBR_VFR (HFI_OX_BASE + 0x4) +#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5) +#define HFI_RATE_CONTROL_MBR_CFR (HFI_OX_BASE + 0x6) +#define HFI_RATE_CONTROL_MBR_VFR (HFI_OX_BASE + 0x7) +#define HFI_RATE_CONTROL_CQ (HFI_OX_BASE + 0x8) + + +struct hfi_uncompressed_plane_actual_constraints_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_constraints rg_plane_format[1]; +}; + +#define HFI_CMD_SYS_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000) +#define HFI_CMD_SYS_SESSION_ABORT (HFI_CMD_SYS_OX_START + 0x001) +#define HFI_CMD_SYS_PING (HFI_CMD_SYS_OX_START + 0x002) + +#define HFI_CMD_SESSION_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000) +#define HFI_CMD_SESSION_LOAD_RESOURCES (HFI_CMD_SESSION_OX_START + 0x001) +#define HFI_CMD_SESSION_START (HFI_CMD_SESSION_OX_START + 0x002) +#define HFI_CMD_SESSION_STOP (HFI_CMD_SESSION_OX_START + 0x003) +#define HFI_CMD_SESSION_EMPTY_BUFFER (HFI_CMD_SESSION_OX_START + 0x004) +#define HFI_CMD_SESSION_FILL_BUFFER (HFI_CMD_SESSION_OX_START + 0x005) +#define HFI_CMD_SESSION_SUSPEND (HFI_CMD_SESSION_OX_START + 0x006) +#define HFI_CMD_SESSION_RESUME (HFI_CMD_SESSION_OX_START + 0x007) +#define HFI_CMD_SESSION_FLUSH (HFI_CMD_SESSION_OX_START + 0x008) +#define HFI_CMD_SESSION_GET_PROPERTY (HFI_CMD_SESSION_OX_START + 0x009) +#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER \ + (HFI_CMD_SESSION_OX_START + 0x00A) +#define HFI_CMD_SESSION_RELEASE_BUFFERS \ + (HFI_CMD_SESSION_OX_START + 0x00B) +#define HFI_CMD_SESSION_RELEASE_RESOURCES \ + (HFI_CMD_SESSION_OX_START + 0x00C) +#define HFI_CMD_SESSION_CONTINUE (HFI_CMD_SESSION_OX_START + 0x00D) +#define HFI_CMD_SESSION_SYNC (HFI_CMD_SESSION_OX_START + 0x00E) + +#define HFI_CMD_SESSION_CVP_START \ + (HFI_DOMAIN_BASE_CVP + HFI_ARCH_COMMON_OFFSET + \ + HFI_CMD_START_OFFSET + 0x1000) +#define HFI_CMD_SESSION_REGISTER_BUFFERS \ + (HFI_CMD_SESSION_CVP_START + 0x0A0) +#define HFI_CMD_SESSION_UNREGISTER_BUFFERS \ + (HFI_CMD_SESSION_CVP_START + 0x0A1) + +#define HFI_MSG_SYS_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000) +#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2) +#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4) + +#define HFI_MSG_SESSION_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000) +#define HFI_MSG_SESSION_LOAD_RESOURCES_DONE (HFI_MSG_SESSION_OX_START + 0x1) +#define HFI_MSG_SESSION_START_DONE (HFI_MSG_SESSION_OX_START + 0x2) +#define HFI_MSG_SESSION_STOP_DONE (HFI_MSG_SESSION_OX_START + 0x3) +#define HFI_MSG_SESSION_SUSPEND_DONE (HFI_MSG_SESSION_OX_START + 0x4) +#define HFI_MSG_SESSION_RESUME_DONE (HFI_MSG_SESSION_OX_START + 0x5) +#define HFI_MSG_SESSION_FLUSH_DONE (HFI_MSG_SESSION_OX_START + 0x6) +#define HFI_MSG_SESSION_EMPTY_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x7) +#define HFI_MSG_SESSION_FILL_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x8) +#define HFI_MSG_SESSION_PROPERTY_INFO (HFI_MSG_SESSION_OX_START + 0x9) +#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE \ + (HFI_MSG_SESSION_OX_START + 0xA) +#define HFI_MSG_SESSION_RELEASE_BUFFERS_DONE \ + (HFI_MSG_SESSION_OX_START + 0xC) + +#define HFI_MSG_SESSION_CVP_START \ + (HFI_DOMAIN_BASE_CVP + HFI_ARCH_COMMON_OFFSET + \ + HFI_MSG_START_OFFSET + 0x1000) +#define HFI_MSG_SESSION_REGISTER_BUFFERS_DONE \ + (HFI_MSG_SESSION_CVP_START + 0x0A0) +#define HFI_MSG_SESSION_UNREGISTER_BUFFERS_DONE \ + (HFI_MSG_SESSION_CVP_START + 0x0A1) + +#define VIDC_IFACEQ_MAX_PKT_SIZE 1024 +#define VIDC_IFACEQ_MED_PKT_SIZE 768 +#define VIDC_IFACEQ_MIN_PKT_SIZE 8 +#define VIDC_IFACEQ_VAR_SMALL_PKT_SIZE 100 +#define VIDC_IFACEQ_VAR_LARGE_PKT_SIZE 512 +#define VIDC_IFACEQ_VAR_HUGE_PKT_SIZE (1024*12) + + +struct hfi_cmd_sys_session_abort_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_sys_ping_packet { + u32 size; + u32 packet_type; + u32 client_data; +}; + +struct hfi_cmd_session_load_resources_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_start_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_stop_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_empty_buffer_compressed_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 view_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 rgData[1]; +}; + +struct hfi_cmd_session_fill_buffer_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 stream_id; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 output_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_flush_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 flush_type; +}; + +struct hfi_cmd_session_suspend_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_resume_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_get_property_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_session_release_buffer_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 buffer_type; + u32 buffer_size; + u32 extra_data_size; + int response_req; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_cmd_session_release_resources_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_msg_sys_session_abort_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_sys_ping_ack_packet { + u32 size; + u32 packet_type; + u32 client_data; +}; + +struct hfi_msg_sys_property_info_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_session_load_resources_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_start_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_stop_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_suspend_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_resume_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_flush_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 flush_type; +}; + +struct hfi_ubwc_cr_stats_info_type { + u32 cr_stats_info0; + u32 cr_stats_info1; + u32 cr_stats_info2; + u32 cr_stats_info3; + u32 cr_stats_info4; + u32 cr_stats_info5; + u32 cr_stats_info6; +}; + +struct hfi_frame_cr_stats_type { + u32 frame_index; + struct hfi_ubwc_cr_stats_info_type ubwc_stats_info; + u32 complexity_number; +}; + +struct hfi_msg_session_empty_buffer_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 offset; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 flags; + struct hfi_frame_cr_stats_type ubwc_cr_stats; + /* no usage of sync_frame flag in EBD, rgData[1] is not used */ + u32 rgData[1]; +}; + +struct hfi_msg_session_fill_buffer_done_compressed_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 error_type; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[0]; +}; + +struct hfi_msg_session_fbd_uncompressed_plane0_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 stream_id; + u32 view_id; + u32 error_type; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 frame_width; + u32 frame_height; + u32 start_x_coord; + u32 start_y_coord; + u32 input_tag; + u32 input_tag2; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[0]; +}; + +struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 rgData[0]; +}; + +struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 rgData[0]; +}; + +struct hfi_msg_session_property_info_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_session_release_resources_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_release_buffers_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_msg_session_register_buffers_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 client_data; + u32 error_type; +}; + +struct hfi_msg_session_unregister_buffers_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 client_data; + u32 error_type; +}; + +struct hfi_extradata_mb_quantization_payload { + u8 rg_mb_qp[1]; +}; + +struct hfi_extradata_timestamp_payload { + u32 time_stamp_low; + u32 time_stamp_high; +}; + + +struct hfi_extradata_s3d_frame_packing_payload { + u32 fpa_id; + int cancel_flag; + u32 fpa_type; + int quin_cunx_flag; + u32 content_interprtation_type; + int spatial_flipping_flag; + int frame0_flipped_flag; + int field_views_flag; + int current_frame_isFrame0_flag; + int frame0_self_contained_flag; + int frame1_self_contained_flag; + u32 frame0_graid_pos_x; + u32 frame0_graid_pos_y; + u32 frame1_graid_pos_x; + u32 frame1_graid_pos_y; + u32 fpa_reserved_byte; + u32 fpa_repetition_period; + int fpa_extension_flag; +}; + +struct hfi_extradata_interlace_video_payload { + u32 format; +}; + +struct hfi_conceal_color_type { + u32 value_8bit; + u32 value_10bit; +}; + +struct hfi_extradata_num_concealed_mb_payload { + u32 num_mb_concealed; +}; + +struct hfi_extradata_sliceinfo { + u32 offset_in_stream; + u32 slice_length; +}; + +struct hfi_extradata_multislice_info_payload { + u32 num_slices; + struct hfi_extradata_sliceinfo rg_slice_info[1]; +}; + +struct hfi_index_extradata_input_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct hfi_index_extradata_output_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 display_width; + u32 display_height; + u32 width; + u32 height; +}; + +struct hfi_index_extradata_digital_zoom_payload { + u32 size; + u32 version; + u32 port_index; + int width; + int height; +}; + +struct hfi_index_extradata_aspect_ratio_payload { + u32 size; + u32 version; + u32 port_index; + u32 aspect_width; + u32 aspect_height; +}; + +struct hfi_extradata_frame_type_payload { + u32 frame_rate; +}; + +struct hfi_extradata_recovery_point_sei_payload { + u32 flag; +}; + +struct hfi_cmd_session_continue_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +enum session_flags { + SESSION_PAUSE = BIT(1), +}; + +struct hal_session { + struct list_head list; + void *session_id; + bool is_decoder; + enum hal_video_codec codec; + enum hal_domain domain; + u32 flags; + void *device; +}; + +struct hal_device_data { + struct list_head dev_head; + int dev_count; +}; + +struct msm_vidc_fw { + void *cookie; +}; + +int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct msm_vidc_cb_info *info); + +enum vidc_status hfi_process_sys_init_done_prop_read( + struct hfi_msg_sys_init_done_packet *pkt, + struct vidc_hal_sys_init_done *sys_init_done); + +enum vidc_status hfi_process_session_init_done_prop_read( + struct hfi_msg_sys_session_init_done_packet *pkt, + struct vidc_hal_session_init_done *session_init_done); + +#endif + diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h new file mode 100644 index 000000000000..798f57ba8bc2 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -0,0 +1,1523 @@ +/* Copyright (c) 2012-2020, 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 __VIDC_HFI_API_H__ +#define __VIDC_HFI_API_H__ + +#include +#include +#include +#include +#include +#include "msm_vidc.h" +#include "msm_vidc_resources.h" + +#define CONTAINS(__a, __sz, __t) (\ + (__t >= __a) && \ + (__t < __a + __sz) \ +) + +#define OVERLAPS(__t, __tsz, __a, __asz) (\ + (__t <= __a) && \ + (__t + __tsz >= __a + __asz) \ +) + +#define HAL_BUFFERFLAG_EOS 0x00000001 +#define HAL_BUFFERFLAG_STARTTIME 0x00000002 +#define HAL_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HAL_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define HAL_BUFFERFLAG_SYNCFRAME 0x00000020 +#define HAL_BUFFERFLAG_EXTRADATA 0x00000040 +#define HAL_BUFFERFLAG_CODECCONFIG 0x00000080 +#define HAL_BUFFERFLAG_READONLY 0x00000200 +#define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 +#define HAL_BUFFERFLAG_MBAFF 0x08000000 +#define HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP 0x10000000 +#define HAL_BUFFERFLAG_DROP_FRAME 0x20000000 +#define HAL_BUFFERFLAG_TS_DISCONTINUITY 0x40000000 +#define HAL_BUFFERFLAG_TS_ERROR 0x80000000 + + + +#define HAL_DEBUG_MSG_LOW 0x00000001 +#define HAL_DEBUG_MSG_MEDIUM 0x00000002 +#define HAL_DEBUG_MSG_HIGH 0x00000004 +#define HAL_DEBUG_MSG_ERROR 0x00000008 +#define HAL_DEBUG_MSG_FATAL 0x00000010 +#define MAX_PROFILE_COUNT 16 + +#define HAL_MAX_MATRIX_COEFFS 9 +#define HAL_MAX_BIAS_COEFFS 3 +#define HAL_MAX_LIMIT_COEFFS 6 +#define VENUS_VERSION_LENGTH 128 + +/* 16 encoder and 16 decoder sessions */ +#define VIDC_MAX_SESSIONS 32 +#define VIDC_MAX_DECODE_SESSIONS 16 +#define VIDC_MAX_ENCODE_SESSIONS 16 + + +enum vidc_status { + VIDC_ERR_NONE = 0x0, + VIDC_ERR_FAIL = 0x80000000, + VIDC_ERR_ALLOC_FAIL, + VIDC_ERR_ILLEGAL_OP, + VIDC_ERR_BAD_PARAM, + VIDC_ERR_BAD_HANDLE, + VIDC_ERR_NOT_SUPPORTED, + VIDC_ERR_BAD_STATE, + VIDC_ERR_MAX_CLIENTS, + VIDC_ERR_IFRAME_EXPECTED, + VIDC_ERR_HW_FATAL, + VIDC_ERR_BITSTREAM_ERR, + VIDC_ERR_INDEX_NOMORE, + VIDC_ERR_SEQHDR_PARSE_FAIL, + VIDC_ERR_INSUFFICIENT_BUFFER, + VIDC_ERR_BAD_POWER_STATE, + VIDC_ERR_NO_VALID_SESSION, + VIDC_ERR_TIMEOUT, + VIDC_ERR_CMDQFULL, + VIDC_ERR_START_CODE_NOT_FOUND, + VIDC_ERR_NOC_ERROR, + VIDC_ERR_CLIENT_PRESENT = 0x90000001, + VIDC_ERR_CLIENT_FATAL, + VIDC_ERR_CMD_QUEUE_FULL, + VIDC_ERR_UNUSED = 0x10000000 +}; + +enum hal_extradata_id { + HAL_EXTRADATA_NONE, + HAL_EXTRADATA_INTERLACE_VIDEO, + HAL_EXTRADATA_TIMESTAMP, + HAL_EXTRADATA_S3D_FRAME_PACKING, + HAL_EXTRADATA_FRAME_RATE, + HAL_EXTRADATA_PANSCAN_WINDOW, + HAL_EXTRADATA_RECOVERY_POINT_SEI, + HAL_EXTRADATA_INDEX, + HAL_EXTRADATA_NUM_CONCEALED_MB, + HAL_EXTRADATA_ASPECT_RATIO, + HAL_EXTRADATA_MPEG2_SEQDISP, + HAL_EXTRADATA_STREAM_USERDATA, + HAL_EXTRADATA_DEC_FRAME_QP, + HAL_EXTRADATA_ENC_FRAME_QP, + HAL_EXTRADATA_LTR_INFO, + HAL_EXTRADATA_ROI_QP, + HAL_EXTRADATA_OUTPUT_CROP, + HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI, + HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI, + HAL_EXTRADATA_VUI_DISPLAY_INFO, + HAL_EXTRADATA_VPX_COLORSPACE, + HAL_EXTRADATA_UBWC_CR_STATS_INFO, + HAL_EXTRADATA_HDR10PLUS_METADATA, + HAL_EXTRADATA_ENC_DTS_METADATA, + HAL_EXTRADATA_INPUT_CROP, +}; + +enum hal_property { + HAL_CONFIG_FRAME_RATE = 0x04000001, + HAL_CONFIG_OPERATING_RATE, + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, + HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO, + HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO, + HAL_PARAM_INDEX_EXTRADATA, + HAL_PARAM_FRAME_SIZE, + HAL_CONFIG_REALTIME, + HAL_PARAM_BUFFER_COUNT_ACTUAL, + HAL_PARAM_BUFFER_SIZE_MINIMUM, + HAL_PARAM_NAL_STREAM_FORMAT_SELECT, + HAL_PARAM_VDEC_OUTPUT_ORDER, + HAL_PARAM_VDEC_PICTURE_TYPE_DECODE, + HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO, + HAL_PARAM_VDEC_MULTI_STREAM, + HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT, + HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING, + HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER, + HAL_CONFIG_VDEC_MB_ERROR_MAP, + HAL_CONFIG_VENC_REQUEST_IFRAME, + HAL_CONFIG_VENC_TARGET_BITRATE, + HAL_PARAM_PROFILE_LEVEL_CURRENT, + HAL_PARAM_VENC_H264_ENTROPY_CONTROL, + HAL_PARAM_VENC_RATE_CONTROL, + HAL_PARAM_VENC_H264_DEBLOCK_CONTROL, + HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF, + HAL_PARAM_VENC_SESSION_QP_RANGE, + HAL_CONFIG_VENC_INTRA_PERIOD, + HAL_CONFIG_VENC_IDR_PERIOD, + HAL_PARAM_VENC_ADAPTIVE_B, + HAL_PARAM_VPE_ROTATION, + HAL_PARAM_VENC_INTRA_REFRESH, + HAL_PARAM_VENC_MULTI_SLICE_CONTROL, + HAL_SYS_DEBUG_CONFIG, + HAL_CONFIG_BUFFER_REQUIREMENTS, + HAL_CONFIG_PRIORITY, + HAL_CONFIG_BATCH_INFO, + HAL_PARAM_METADATA_PASS_THROUGH, + HAL_SYS_IDLE_INDICATOR, + HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED, + HAL_PARAM_INTERLACE_FORMAT_SUPPORTED, + HAL_PARAM_CHROMA_SITE, + HAL_PARAM_PROPERTIES_SUPPORTED, + HAL_PARAM_PROFILE_LEVEL_SUPPORTED, + HAL_PARAM_CAPABILITY_SUPPORTED, + HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED, + HAL_PARAM_MULTI_VIEW_FORMAT, + HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE, + HAL_PARAM_CODEC_SUPPORTED, + HAL_PARAM_VDEC_MULTI_VIEW_SELECT, + HAL_PARAM_VDEC_MB_QUANTIZATION, + HAL_PARAM_VDEC_NUM_CONCEALED_MB, + HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING, + HAL_PARAM_VENC_SLICE_DELIVERY_MODE, + HAL_CONFIG_BUFFER_COUNT_ACTUAL, + HAL_CONFIG_VDEC_MULTI_STREAM, + HAL_PARAM_VENC_MULTI_SLICE_INFO, + HAL_CONFIG_VENC_TIMESTAMP_SCALE, + HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER, + HAL_PARAM_VDEC_SYNC_FRAME_DECODE, + HAL_CONFIG_VENC_MAX_BITRATE, + HAL_PARAM_VENC_VUI_TIMING_INFO, + HAL_PARAM_VENC_GENERATE_AUDNAL, + HAL_PARAM_BUFFER_ALLOC_MODE, + HAL_PARAM_VDEC_FRAME_ASSEMBLY, + HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY, + HAL_PARAM_VDEC_CONCEAL_COLOR, + HAL_PARAM_VDEC_SCS_THRESHOLD, + HAL_PARAM_GET_BUFFER_REQUIREMENTS, + HAL_PARAM_VENC_LTRMODE, + HAL_CONFIG_VENC_MARKLTRFRAME, + HAL_CONFIG_VENC_USELTRFRAME, + HAL_CONFIG_VENC_LTRPERIOD, + HAL_CONFIG_VENC_HIER_P_NUM_FRAMES, + HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS, + HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP, + HAL_PARAM_VENC_SEARCH_RANGE, + HAL_PARAM_VPE_COLOR_SPACE_CONVERSION, + HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE, + HAL_CONFIG_VENC_PERF_MODE, + HAL_PARAM_VDEC_NON_SECURE_OUTPUT2, + HAL_PARAM_VENC_HIER_P_HYBRID_MODE, + HAL_PARAM_VENC_MBI_STATISTICS_MODE, + HAL_PARAM_SYNC_BASED_INTERRUPT, + HAL_CONFIG_VENC_FRAME_QP, + HAL_CONFIG_VENC_BASELAYER_PRIORITYID, + HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO, + HAL_CONFIG_VDEC_ENTROPY, + HAL_PARAM_VENC_BITRATE_TYPE, + HAL_PARAM_VENC_LOW_LATENCY, + HAL_CONFIG_VENC_BLUR_RESOLUTION, + HAL_PARAM_VENC_H264_TRANSFORM_8x8, + HAL_PARAM_VENC_VIDEO_SIGNAL_INFO, + HAL_PARAM_VENC_IFRAMESIZE_TYPE, + HAL_PARAM_VIDEO_CORES_USAGE, + HAL_PARAM_VIDEO_WORK_MODE, + HAL_PARAM_SECURE, + HAL_PARAM_VENC_HDR10_PQ_SEI, + HAL_PARAM_VIDEO_WORK_ROUTE, + HAL_CONFIG_VENC_VBV_HRD_BUF_SIZE, + HAL_CONFIG_HEIC_FRAME_QUALITY, + HAL_CONFIG_HEIC_GRID_ENABLE, + HAL_PARAM_VENC_BITRATE_SAVINGS, +}; + +enum hal_domain { + HAL_VIDEO_DOMAIN_VPE, + HAL_VIDEO_DOMAIN_ENCODER, + HAL_VIDEO_DOMAIN_DECODER, + HAL_VIDEO_DOMAIN_CVP, + HAL_UNUSED_DOMAIN = 0x10000000, +}; + +enum multi_stream { + HAL_VIDEO_DECODER_NONE = 0x00000000, + HAL_VIDEO_DECODER_PRIMARY = 0x00000001, + HAL_VIDEO_DECODER_SECONDARY = 0x00000002, + HAL_VIDEO_DECODER_BOTH_OUTPUTS = 0x00000004, + HAL_VIDEO_UNUSED_OUTPUTS = 0x10000000, +}; + +enum hal_core_capabilities { + HAL_VIDEO_ENCODER_ROTATION_CAPABILITY = 0x00000001, + HAL_VIDEO_ENCODER_SCALING_CAPABILITY = 0x00000002, + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY = 0x00000004, + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY = 0x00000008, + HAL_VIDEO_UNUSED_CAPABILITY = 0x10000000, +}; + +enum hal_default_properties { + HAL_VIDEO_DYNAMIC_BUF_MODE = 0x00000001, + HAL_VIDEO_CONTINUE_DATA_TRANSFER = 0x00000002, +}; + +enum hal_video_codec { + HAL_VIDEO_CODEC_UNKNOWN = 0x00000000, + HAL_VIDEO_CODEC_MVC = 0x00000001, + HAL_VIDEO_CODEC_H264 = 0x00000002, + HAL_VIDEO_CODEC_H263 = 0x00000004, + HAL_VIDEO_CODEC_MPEG1 = 0x00000008, + HAL_VIDEO_CODEC_MPEG2 = 0x00000010, + HAL_VIDEO_CODEC_MPEG4 = 0x00000020, + HAL_VIDEO_CODEC_DIVX_311 = 0x00000040, + HAL_VIDEO_CODEC_DIVX = 0x00000080, + HAL_VIDEO_CODEC_VC1 = 0x00000100, + HAL_VIDEO_CODEC_SPARK = 0x00000200, + HAL_VIDEO_CODEC_VP6 = 0x00000400, + HAL_VIDEO_CODEC_VP7 = 0x00000800, + HAL_VIDEO_CODEC_VP8 = 0x00001000, + HAL_VIDEO_CODEC_HEVC = 0x00002000, + HAL_VIDEO_CODEC_VP9 = 0x00004000, + HAL_VIDEO_CODEC_TME = 0x00008000, + HAL_VIDEO_CODEC_CVP = 0x00010000, + HAL_VIDEO_CODEC_HEVC_HYBRID = 0x80000000, + HAL_UNUSED_CODEC = 0x10000000, +}; + +enum hal_mpeg2_profile { + HAL_UNUSED_MPEG2_PROFILE = 0x00000000, + HAL_MPEG2_PROFILE_SIMPLE = 0x00000001, + HAL_MPEG2_PROFILE_MAIN = 0x00000002, +}; + +enum hal_mpeg2_level { + HAL_UNUSED_MEPG2_LEVEL = 0x00000000, + HAL_MPEG2_LEVEL_LL = 0x00000001, + HAL_MPEG2_LEVEL_ML = 0x00000002, + HAL_MPEG2_LEVEL_HL = 0x00000004, +}; + +enum hal_h264_profile { + HAL_UNUSED_H264_PROFILE = 0x00000000, + HAL_H264_PROFILE_BASELINE = 0x00000001, + HAL_H264_PROFILE_MAIN = 0x00000002, + HAL_H264_PROFILE_HIGH = 0x00000004, + HAL_H264_PROFILE_STEREO_HIGH = 0x00000008, + HAL_H264_PROFILE_MULTIVIEW_HIGH = 0x00000010, + HAL_H264_PROFILE_CONSTRAINED_BASE = 0x00000020, + HAL_H264_PROFILE_CONSTRAINED_HIGH = 0x00000040, +}; + +enum hal_h264_level { + HAL_H264_LEVEL_UNKNOWN = 0x00000000, + HAL_H264_LEVEL_1 = 0x00000001, + HAL_H264_LEVEL_1b = 0x00000002, + HAL_H264_LEVEL_11 = 0x00000004, + HAL_H264_LEVEL_12 = 0x00000008, + HAL_H264_LEVEL_13 = 0x00000010, + HAL_H264_LEVEL_2 = 0x00000020, + HAL_H264_LEVEL_21 = 0x00000040, + HAL_H264_LEVEL_22 = 0x00000080, + HAL_H264_LEVEL_3 = 0x00000100, + HAL_H264_LEVEL_31 = 0x00000200, + HAL_H264_LEVEL_32 = 0x00000400, + HAL_H264_LEVEL_4 = 0x00000800, + HAL_H264_LEVEL_41 = 0x00001000, + HAL_H264_LEVEL_42 = 0x00002000, + HAL_H264_LEVEL_5 = 0x00004000, + HAL_H264_LEVEL_51 = 0x00008000, + HAL_H264_LEVEL_52 = 0x00010000, + HAL_H264_LEVEL_6 = 0x00020000, + HAL_H264_LEVEL_61 = 0x00040000, + HAL_H264_LEVEL_62 = 0x00080000, +}; + +enum hal_hevc_profile { + HAL_UNUSED_HEVC_PROFILE = 0x00000000, + HAL_HEVC_PROFILE_MAIN = 0x00000001, + HAL_HEVC_PROFILE_MAIN10 = 0x00000002, + HAL_HEVC_PROFILE_MAIN_STILL_PIC = 0x00000004, +}; + +enum hal_hevc_level { + HAL_HEVC_TIER_LEVEL_UNKNOWN = 0x00000000, + HAL_HEVC_MAIN_TIER_LEVEL_1 = 0x10000001, + HAL_HEVC_MAIN_TIER_LEVEL_2 = 0x10000002, + HAL_HEVC_MAIN_TIER_LEVEL_2_1 = 0x10000004, + HAL_HEVC_MAIN_TIER_LEVEL_3 = 0x10000008, + HAL_HEVC_MAIN_TIER_LEVEL_3_1 = 0x10000010, + HAL_HEVC_MAIN_TIER_LEVEL_4 = 0x10000020, + HAL_HEVC_MAIN_TIER_LEVEL_4_1 = 0x10000040, + HAL_HEVC_MAIN_TIER_LEVEL_5 = 0x10000080, + HAL_HEVC_MAIN_TIER_LEVEL_5_1 = 0x10000100, + HAL_HEVC_MAIN_TIER_LEVEL_5_2 = 0x10000200, + HAL_HEVC_MAIN_TIER_LEVEL_6 = 0x10000400, + HAL_HEVC_MAIN_TIER_LEVEL_6_1 = 0x10000800, + HAL_HEVC_MAIN_TIER_LEVEL_6_2 = 0x10001000, + HAL_HEVC_HIGH_TIER_LEVEL_1 = 0x20000001, + HAL_HEVC_HIGH_TIER_LEVEL_2 = 0x20000002, + HAL_HEVC_HIGH_TIER_LEVEL_2_1 = 0x20000004, + HAL_HEVC_HIGH_TIER_LEVEL_3 = 0x20000008, + HAL_HEVC_HIGH_TIER_LEVEL_3_1 = 0x20000010, + HAL_HEVC_HIGH_TIER_LEVEL_4 = 0x20000020, + HAL_HEVC_HIGH_TIER_LEVEL_4_1 = 0x20000040, + HAL_HEVC_HIGH_TIER_LEVEL_5 = 0x20000080, + HAL_HEVC_HIGH_TIER_LEVEL_5_1 = 0x20000100, + HAL_HEVC_HIGH_TIER_LEVEL_5_2 = 0x20000200, + HAL_HEVC_HIGH_TIER_LEVEL_6 = 0x20000400, + HAL_HEVC_HIGH_TIER_LEVEL_6_1 = 0x20000800, + HAL_HEVC_HIGH_TIER_LEVEL_6_2 = 0x20001000, +}; + +enum hal_hevc_tier { + HAL_HEVC_TIER_MAIN = 0x00000001, + HAL_HEVC_TIER_HIGH = 0x00000002, + HAL_UNUSED_HEVC_TIER = 0x10000000, +}; + +enum hal_vp8_profile { + HAL_VP8_PROFILE_UNUSED = 0x00000000, + HAL_VP8_PROFILE_MAIN = 0x00000001, +}; + +enum hal_vp8_level { + HAL_VP8_LEVEL_UNUSED = 0x00000000, + HAL_VP8_LEVEL_VERSION_0 = 0x00000001, + HAL_VP8_LEVEL_VERSION_1 = 0x00000002, + HAL_VP8_LEVEL_VERSION_2 = 0x00000004, + HAL_VP8_LEVEL_VERSION_3 = 0x00000008, +}; + +enum hal_tme_profile { + HAL_TME_PROFILE_0 = 0x00000001, + HAL_TME_PROFILE_1 = 0x00000002, + HAL_TME_PROFILE_2 = 0x00000004, + HAL_TME_PROFILE_3 = 0x00000008, +}; + +enum hal_tme_level { + HAL_TME_LEVEL_INTEGER = 0x00000001, +}; + +enum hal_vp9_profile { + HAL_VP9_PROFILE_UNUSED = 0x00000000, + HAL_VP9_PROFILE_P0 = 0x00000001, + HAL_VP9_PROFILE_P2_10 = 0x00000004, +}; + +enum hal_vp9_level { + HAL_VP9_LEVEL_UNUSED = 0x00000000, + HAL_VP9_LEVEL_1 = 0x00000001, + HAL_VP9_LEVEL_11 = 0x00000002, + HAL_VP9_LEVEL_2 = 0x00000004, + HAL_VP9_LEVEL_21 = 0x00000008, + HAL_VP9_LEVEL_3 = 0x00000010, + HAL_VP9_LEVEL_31 = 0x00000020, + HAL_VP9_LEVEL_4 = 0x00000040, + HAL_VP9_LEVEL_41 = 0x00000080, + HAL_VP9_LEVEL_5 = 0x00000100, + HAL_VP9_LEVEL_51 = 0x00000200, + HAL_VP9_LEVEL_6 = 0x00000400, + HAL_VP9_LEVEL_61 = 0x00000800, +}; + +struct hal_frame_rate { + enum hal_buffer buffer_type; + u32 frame_rate; +}; + +struct hal_operating_rate { + u32 operating_rate; +}; + +enum hal_uncompressed_format { + HAL_COLOR_FORMAT_MONOCHROME = 0x00000001, + HAL_COLOR_FORMAT_NV12 = 0x00000002, + HAL_COLOR_FORMAT_NV21 = 0x00000004, + HAL_COLOR_FORMAT_NV12_4x4TILE = 0x00000008, + HAL_COLOR_FORMAT_NV21_4x4TILE = 0x00000010, + HAL_COLOR_FORMAT_YUYV = 0x00000020, + HAL_COLOR_FORMAT_YVYU = 0x00000040, + HAL_COLOR_FORMAT_UYVY = 0x00000080, + HAL_COLOR_FORMAT_VYUY = 0x00000100, + HAL_COLOR_FORMAT_RGB565 = 0x00000200, + HAL_COLOR_FORMAT_BGR565 = 0x00000400, + HAL_COLOR_FORMAT_RGB888 = 0x00000800, + HAL_COLOR_FORMAT_BGR888 = 0x00001000, + HAL_COLOR_FORMAT_NV12_UBWC = 0x00002000, + HAL_COLOR_FORMAT_NV12_TP10_UBWC = 0x00004000, + HAL_COLOR_FORMAT_RGBA8888 = 0x00008000, + HAL_COLOR_FORMAT_RGBA8888_UBWC = 0x00010000, + HAL_COLOR_FORMAT_P010 = 0x00020000, + HAL_COLOR_FORMAT_NV12_512 = 0x00040000, + HAL_UNUSED_COLOR = 0x10000000, +}; + +enum hal_statistics_mode_type { + HAL_STATISTICS_MODE_DEFAULT = 0x00000001, + HAL_STATISTICS_MODE_1 = 0x00000002, + HAL_STATISTICS_MODE_2 = 0x00000004, + HAL_STATISTICS_MODE_3 = 0x00000008, +}; + +enum hal_ssr_trigger_type { + SSR_ERR_FATAL = 1, + SSR_SW_DIV_BY_ZERO, + SSR_HW_WDOG_IRQ, +}; + +struct hal_uncompressed_format_select { + enum hal_buffer buffer_type; + enum hal_uncompressed_format format; +}; + +struct hal_uncompressed_plane_actual { + int actual_stride; + u32 actual_plane_buffer_height; +}; + +struct hal_uncompressed_plane_actual_info { + enum hal_buffer buffer_type; + u32 num_planes; + struct hal_uncompressed_plane_actual rg_plane_format[1]; +}; + +struct hal_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hal_uncompressed_plane_actual_constraints_info { + enum hal_buffer buffer_type; + u32 num_planes; + struct hal_uncompressed_plane_constraints rg_plane_format[1]; +}; + +struct hal_frame_size { + enum hal_buffer buffer_type; + u32 width; + u32 height; +}; + +struct hal_enable { + bool enable; +}; + +struct hal_buffer_count_actual { + enum hal_buffer buffer_type; + u32 buffer_count_actual; + u32 buffer_count_min_host; +}; + +struct hal_buffer_size_minimum { + enum hal_buffer buffer_type; + u32 buffer_size; +}; + +struct hal_buffer_display_hold_count_actual { + enum hal_buffer buffer_type; + u32 hold_count; +}; + +enum hal_nal_stream_format { + HAL_NAL_FORMAT_STARTCODES = 0x00000001, + HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER = 0x00000002, + HAL_NAL_FORMAT_ONE_BYTE_LENGTH = 0x00000004, + HAL_NAL_FORMAT_TWO_BYTE_LENGTH = 0x00000008, + HAL_NAL_FORMAT_FOUR_BYTE_LENGTH = 0x00000010, +}; + +enum hal_output_order { + HAL_OUTPUT_ORDER_DISPLAY, + HAL_OUTPUT_ORDER_DECODE, + HAL_UNUSED_OUTPUT = 0x10000000, +}; + +enum hal_picture { + HAL_PICTURE_I = 0x01, + HAL_PICTURE_P = 0x02, + HAL_PICTURE_B = 0x04, + HAL_PICTURE_IDR = 0x08, + HAL_PICTURE_CRA = 0x10, + HAL_FRAME_NOTCODED = 0x7F002000, + HAL_FRAME_YUV = 0x7F004000, + HAL_UNUSED_PICT = 0x10000000, +}; + +struct hal_extradata_enable { + u32 enable; + enum hal_extradata_id index; +}; + +struct hal_enable_picture { + u32 picture_type; +}; + +struct hal_multi_stream { + enum hal_buffer buffer_type; + u32 enable; + u32 width; + u32 height; +}; + +struct hal_display_picture_buffer_count { + u32 enable; + u32 count; +}; + +struct hal_mb_error_map { + u32 error_map_size; + u8 rg_error_map[1]; +}; + +struct hal_request_iframe { + u32 enable; +}; + +struct hal_bitrate { + u32 bit_rate; + u32 layer_id; +}; + +struct hal_profile_level { + u32 profile; + u32 level; +}; + +struct hal_profile_level_supported { + u32 profile_count; + struct hal_profile_level profile_level[MAX_PROFILE_COUNT]; +}; + +enum hal_h264_entropy { + HAL_H264_ENTROPY_CAVLC = 1, + HAL_H264_ENTROPY_CABAC = 2, + HAL_UNUSED_ENTROPY = 0x10000000, +}; + +struct hal_h264_entropy_control { + enum hal_h264_entropy entropy_mode; +}; + +enum hal_rate_control { + HAL_RATE_CONTROL_VBR, + HAL_RATE_CONTROL_CBR, + HAL_RATE_CONTROL_MBR, + HAL_RATE_CONTROL_OFF, + HAL_RATE_CONTROL_CBR_VFR, + HAL_RATE_CONTROL_MBR_VFR, + HAL_RATE_CONTROL_CQ, + HAL_UNUSED_RC = 0x10000000, +}; + +enum hal_h264_db_mode { + HAL_H264_DB_MODE_DISABLE, + HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY, + HAL_H264_DB_MODE_ALL_BOUNDARY, + HAL_UNUSED_H264_DB = 0x10000000, +}; + +struct hal_h264_db_control { + enum hal_h264_db_mode mode; + int slice_alpha_offset; + int slice_beta_offset; +}; + +struct hal_temporal_spatial_tradeoff { + u32 ts_factor; +}; + +struct hal_quantization { + u32 qpi; + u32 qpp; + u32 qpb; + u32 layer_id; + u32 enable; +}; + +struct hal_quantization_range { + u32 qpi_min; + u32 qpp_min; + u32 qpb_min; + u32 qpi_max; + u32 qpp_max; + u32 qpb_max; + u32 layer_id; +}; + +struct hal_intra_period { + u32 pframes; + u32 bframes; +}; + +struct hal_idr_period { + u32 idr_period; +}; + +struct hal_heic_frame_quality { + u32 frame_quality; +}; + +struct hal_heic_grid_enable { + u32 grid_enable; +}; + +enum hal_flip { + HAL_FLIP_NONE, + HAL_FLIP_VERTICAL, + HAL_FLIP_HORIZONTAL, + HAL_FLIP_BOTH, + HAL_UNUSED_FLIP = 0x10000000, +}; + +struct hal_vpe_rotation { + u32 rotate; + enum hal_flip flip; +}; + +enum hal_intra_refresh_mode { + HAL_INTRA_REFRESH_NONE, + HAL_INTRA_REFRESH_CYCLIC, + HAL_INTRA_REFRESH_RANDOM, + HAL_UNUSED_INTRA = 0x10000000, +}; + +struct hal_intra_refresh { + enum hal_intra_refresh_mode mode; + u32 ir_mbs; +}; + +enum hal_multi_slice { + HAL_MULTI_SLICE_OFF, + HAL_MULTI_SLICE_BY_MB_COUNT, + HAL_MULTI_SLICE_BY_BYTE_COUNT, + HAL_MULTI_SLICE_GOB, + HAL_UNUSED_SLICE = 0x10000000, +}; + +struct hal_multi_slice_control { + enum hal_multi_slice multi_slice; + u32 slice_size; +}; + +struct hal_debug_config { + u32 debug_config; +}; + +struct hal_buffer_requirements { + enum hal_buffer buffer_type; + u32 buffer_size; + u32 buffer_region_size; + u32 buffer_count_min; + u32 buffer_count_min_host; + u32 buffer_count_actual; + u32 contiguous; + u32 buffer_alignment; +}; + +enum hal_priority {/* Priority increases with number */ + HAL_PRIORITY_LOW = 10, + HAL_PRIOIRTY_MEDIUM = 20, + HAL_PRIORITY_HIGH = 30, + HAL_UNUSED_PRIORITY = 0x10000000, +}; + +struct hal_batch_info { + u32 input_batch_count; + u32 output_batch_count; +}; + +struct hal_metadata_pass_through { + u32 enable; + u32 size; +}; + +struct hal_uncompressed_format_supported { + enum hal_buffer buffer_type; + u32 format_entries; + u32 rg_format_info[1]; +}; + +enum hal_interlace_format { + HAL_INTERLACE_FRAME_PROGRESSIVE = 0x01, + HAL_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02, + HAL_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04, + HAL_INTERLACE_FRAME_TOPFIELDFIRST = 0x08, + HAL_INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10, + HAL_UNUSED_INTERLACE = 0x10000000, +}; + +struct hal_interlace_format_supported { + enum hal_buffer buffer_type; + enum hal_interlace_format format; +}; + +enum hal_chroma_site { + HAL_CHROMA_SITE_0, + HAL_CHROMA_SITE_1, + HAL_UNUSED_CHROMA = 0x10000000, +}; + +struct hal_properties_supported { + u32 num_properties; + u32 rg_properties[1]; +}; + +enum hal_capability { + HAL_CAPABILITY_FRAME_WIDTH = 0x1, + HAL_CAPABILITY_FRAME_HEIGHT, + HAL_CAPABILITY_MBS_PER_FRAME, + HAL_CAPABILITY_MBS_PER_SECOND, + HAL_CAPABILITY_FRAMERATE, + HAL_CAPABILITY_SCALE_X, + HAL_CAPABILITY_SCALE_Y, + HAL_CAPABILITY_BITRATE, + HAL_CAPABILITY_BFRAME, + HAL_CAPABILITY_PEAKBITRATE, + HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS, + HAL_CAPABILITY_ENC_LTR_COUNT, + HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD, + HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS, + HAL_CAPABILITY_LCU_SIZE, + HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, + HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE, + HAL_CAPABILITY_EXTRADATA, + HAL_CAPABILITY_PROFILE, + HAL_CAPABILITY_LEVEL, + HAL_CAPABILITY_I_FRAME_QP, + HAL_CAPABILITY_P_FRAME_QP, + HAL_CAPABILITY_B_FRAME_QP, + HAL_CAPABILITY_RATE_CONTROL_MODES, + HAL_CAPABILITY_BLUR_WIDTH, + HAL_CAPABILITY_BLUR_HEIGHT, + HAL_CAPABILITY_SLICE_DELIVERY_MODES, + HAL_CAPABILITY_SLICE_BYTE, + HAL_CAPABILITY_SLICE_MB, + HAL_CAPABILITY_SECURE, + HAL_CAPABILITY_MAX_NUM_B_FRAMES, + HAL_CAPABILITY_MAX_VIDEOCORES, + HAL_CAPABILITY_MAX_WORKMODES, + HAL_CAPABILITY_UBWC_CR_STATS, + HAL_CAPABILITY_ROTATION, + HAL_CAPABILITY_COLOR_SPACE_CONVERSION, + HAL_UNUSED_CAPABILITY = 0x10000000, +}; + +struct hal_capability_supported { + enum hal_capability capability_type; + u32 min; + u32 max; + u32 step_size; +}; + +struct hal_capability_supported_info { + u32 num_capabilities; + struct hal_capability_supported rg_data[1]; +}; + +struct hal_nal_stream_format_supported { + u32 nal_stream_format_supported; +}; + +struct hal_nal_stream_format_select { + u32 nal_stream_format_select; +}; + +struct hal_multi_view_format { + u32 views; + u32 rg_view_order[1]; +}; + +enum hal_buffer_layout_type { + HAL_BUFFER_LAYOUT_TOP_BOTTOM, + HAL_BUFFER_LAYOUT_SEQ, + HAL_UNUSED_BUFFER_LAYOUT = 0x10000000, +}; + +struct hal_aspect_ratio { + u32 aspect_width; + u32 aspect_height; +}; + +struct hal_codec_supported { + u32 decoder_codec_supported; + u32 encoder_codec_supported; +}; + +struct hal_multi_view_select { + u32 view_index; +}; + +struct hal_timestamp_scale { + u32 time_stamp_scale; +}; + + +struct hal_vui_timing_info { + u32 enable; + u32 fixed_frame_rate; + u32 time_scale; +}; + +struct hal_preserve_text_quality { + u32 enable; +}; + +enum hal_core_id { + VIDC_CORE_ID_DEFAULT = 0, + VIDC_CORE_ID_1 = 1, /* 0b01 */ + VIDC_CORE_ID_2 = 2, /* 0b10 */ + VIDC_CORE_ID_3 = 3, /* 0b11 */ + VIDC_CORE_ID_UNUSED = 0x10000000, +}; + +struct hal_videocores_usage_info { + u32 video_core_enable_mask; +}; + +enum hal_work_mode { + VIDC_WORK_MODE_1 = 1, + VIDC_WORK_MODE_2 = 2, + VIDC_WORK_MODE_UNUSED = 0x10000000, +}; + +struct hal_video_work_mode { + u32 video_work_mode; +}; + +struct hal_video_work_route { + u32 video_work_route; +}; + +struct hal_vpe_color_space_conversion { + u32 input_color_primaries; + u32 custom_matrix_enabled; + u32 csc_matrix[HAL_MAX_MATRIX_COEFFS]; + u32 csc_bias[HAL_MAX_BIAS_COEFFS]; + u32 csc_limit[HAL_MAX_LIMIT_COEFFS]; +}; + +struct hal_video_signal_info { + u32 color_space; + u32 transfer_chars; + u32 matrix_coeffs; + bool full_range; +}; + +enum hal_iframesize_type { + HAL_IFRAMESIZE_TYPE_DEFAULT, + HAL_IFRAMESIZE_TYPE_MEDIUM, + HAL_IFRAMESIZE_TYPE_HUGE, + HAL_IFRAMESIZE_TYPE_UNLIMITED, +}; + +enum vidc_resource_id { + VIDC_RESOURCE_NONE, + VIDC_RESOURCE_SYSCACHE, + VIDC_UNUSED_RESOURCE = 0x10000000, +}; + +struct vidc_resource_hdr { + enum vidc_resource_id resource_id; + void *resource_handle; +}; + +struct vidc_register_buffer { + enum hal_buffer type; + u32 index; + u32 size; + u32 device_addr; + u32 response_required; + u32 client_data; +}; + +struct vidc_unregister_buffer { + enum hal_buffer type; + u32 index; + u32 size; + u32 device_addr; + u32 response_required; + u32 client_data; +}; + +struct vidc_buffer_addr_info { + enum hal_buffer buffer_type; + u32 buffer_size; + u32 num_buffers; + u32 align_device_addr; + u32 extradata_addr; + u32 extradata_size; + u32 response_required; +}; + +/* Needs to be exactly the same as hfi_buffer_info */ +struct hal_buffer_info { + u32 buffer_addr; + u32 extra_data_addr; +}; + +struct vidc_frame_plane_config { + u32 left; + u32 top; + u32 width; + u32 height; + u32 stride; + u32 scan_lines; +}; + +struct vidc_uncompressed_frame_config { + struct vidc_frame_plane_config luma_plane; + struct vidc_frame_plane_config chroma_plane; +}; + +struct vidc_frame_data { + enum hal_buffer buffer_type; + u32 device_addr; + u32 extradata_addr; + int64_t timestamp; + u32 flags; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 mark_target; + u32 mark_data; + u32 input_tag; + u32 output_tag; + u32 clnt_data; + u32 extradata_size; +}; + +struct hal_fw_info { + char version[VENUS_VERSION_LENGTH]; + phys_addr_t base_addr; + int register_base; + int register_size; + int irq; +}; + +enum hal_flush { + HAL_FLUSH_INPUT = BIT(0), + HAL_FLUSH_OUTPUT = BIT(1), + HAL_FLUSH_ALL = HAL_FLUSH_INPUT | HAL_FLUSH_OUTPUT, +}; + +enum hal_event_type { + HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES, + HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES, + HAL_EVENT_RELEASE_BUFFER_REFERENCE, + HAL_UNUSED_SEQCHG = 0x10000000, +}; + +enum buffer_mode_type { + HAL_BUFFER_MODE_DYNAMIC = 0x100, + HAL_BUFFER_MODE_STATIC = 0x001, +}; + +struct hal_buffer_alloc_mode { + enum hal_buffer buffer_type; + enum buffer_mode_type buffer_mode; +}; + +enum ltr_mode { + HAL_LTR_MODE_DISABLE, + HAL_LTR_MODE_MANUAL, +}; + +struct hal_ltr_mode { + enum ltr_mode mode; + u32 count; + u32 trust_mode; +}; + +struct hal_ltr_use { + u32 ref_ltr; + u32 use_constraint; + u32 frames; +}; + +struct hal_ltr_mark { + u32 mark_frame; +}; + +enum hal_perf_mode { + HAL_PERF_MODE_POWER_SAVE, + HAL_PERF_MODE_POWER_MAX_QUALITY, +}; + +struct hal_hybrid_hierp { + u32 layers; +}; + +struct hal_scs_threshold { + u32 threshold_value; +}; + +struct buffer_requirements { + struct hal_buffer_requirements buffer[HAL_BUFFER_MAX]; +}; + +struct hal_conceal_color { + u32 conceal_color_8bit; + u32 conceal_color_10bit; +}; + +union hal_get_property { + struct hal_frame_rate frame_rate; + struct hal_uncompressed_format_select format_select; + struct hal_uncompressed_plane_actual plane_actual; + struct hal_uncompressed_plane_actual_info plane_actual_info; + struct hal_uncompressed_plane_constraints plane_constraints; + struct hal_uncompressed_plane_actual_constraints_info + plane_constraints_info; + struct hal_frame_size frame_size; + struct hal_enable enable; + struct hal_buffer_count_actual buffer_count_actual; + struct hal_extradata_enable extradata_enable; + struct hal_enable_picture enable_picture; + struct hal_multi_stream multi_stream; + struct hal_display_picture_buffer_count display_picture_buffer_count; + struct hal_mb_error_map mb_error_map; + struct hal_request_iframe request_iframe; + struct hal_bitrate bitrate; + struct hal_profile_level profile_level; + struct hal_profile_level_supported profile_level_supported; + struct hal_h264_db_control h264_db_control; + struct hal_temporal_spatial_tradeoff temporal_spatial_tradeoff; + struct hal_quantization quantization; + struct hal_quantization_range quantization_range; + struct hal_intra_period intra_period; + struct hal_idr_period idr_period; + struct hal_vpe_rotation vpe_rotation; + struct hal_intra_refresh intra_refresh; + struct hal_multi_slice_control multi_slice_control; + struct hal_debug_config debug_config; + struct hal_batch_info batch_info; + struct hal_metadata_pass_through metadata_pass_through; + struct hal_uncompressed_format_supported uncompressed_format_supported; + struct hal_interlace_format_supported interlace_format_supported; + struct hal_properties_supported properties_supported; + struct hal_capability_supported capability_supported; + struct hal_capability_supported_info capability_supported_info; + struct hal_nal_stream_format_supported nal_stream_format_supported; + struct hal_nal_stream_format_select nal_stream_format_select; + struct hal_multi_view_format multi_view_format; + struct hal_codec_supported codec_supported; + struct hal_multi_view_select multi_view_select; + struct hal_timestamp_scale timestamp_scale; + struct hal_vui_timing_info vui_timing_info; + struct hal_preserve_text_quality preserve_text_quality; + struct hal_buffer_info buffer_info; + struct hal_buffer_alloc_mode buffer_alloc_mode; + struct buffer_requirements buf_req; + enum hal_h264_entropy h264_entropy; + struct hal_conceal_color conceal_color; +}; + +/* HAL Response */ +#define IS_HAL_SYS_CMD(cmd) ((cmd) >= HAL_SYS_INIT_DONE && \ + (cmd) <= HAL_SYS_ERROR) +#define IS_HAL_SESSION_CMD(cmd) ((cmd) >= HAL_SESSION_EVENT_CHANGE && \ + (cmd) <= HAL_SESSION_ERROR) +enum hal_command_response { + /* SYSTEM COMMANDS_DONE*/ + HAL_SYS_INIT_DONE, + HAL_SYS_SET_RESOURCE_DONE, + HAL_SYS_RELEASE_RESOURCE_DONE, + HAL_SYS_PING_ACK_DONE, + HAL_SYS_PC_PREP_DONE, + HAL_SYS_IDLE, + HAL_SYS_DEBUG, + HAL_SYS_WATCHDOG_TIMEOUT, + HAL_SYS_ERROR, + /* SESSION COMMANDS_DONE */ + HAL_SESSION_EVENT_CHANGE, + HAL_SESSION_LOAD_RESOURCE_DONE, + HAL_SESSION_INIT_DONE, + HAL_SESSION_END_DONE, + HAL_SESSION_ABORT_DONE, + HAL_SESSION_START_DONE, + HAL_SESSION_STOP_DONE, + HAL_SESSION_ETB_DONE, + HAL_SESSION_FTB_DONE, + HAL_SESSION_FLUSH_DONE, + HAL_SESSION_SUSPEND_DONE, + HAL_SESSION_RESUME_DONE, + HAL_SESSION_SET_PROP_DONE, + HAL_SESSION_GET_PROP_DONE, + HAL_SESSION_RELEASE_BUFFER_DONE, + HAL_SESSION_REGISTER_BUFFER_DONE, + HAL_SESSION_UNREGISTER_BUFFER_DONE, + HAL_SESSION_RELEASE_RESOURCE_DONE, + HAL_SESSION_PROPERTY_INFO, + HAL_SESSION_ERROR, + HAL_RESPONSE_UNUSED = 0x10000000, +}; + +struct ubwc_cr_stats_info_type { + u32 cr_stats_info0; + u32 cr_stats_info1; + u32 cr_stats_info2; + u32 cr_stats_info3; + u32 cr_stats_info4; + u32 cr_stats_info5; + u32 cr_stats_info6; +}; + +struct recon_stats_type { + u32 buffer_index; + u32 complexity_number; + struct ubwc_cr_stats_info_type ubwc_stats_info; +}; + +struct vidc_hal_ebd { + u32 timestamp_hi; + u32 timestamp_lo; + u32 flags; + enum vidc_status status; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 offset; + u32 input_tag; + u32 output_tag; + u32 alloc_len; + u32 filled_len; + enum hal_picture picture_type; + struct recon_stats_type recon_stats; + u32 packet_buffer; + u32 extra_data_buffer; +}; + +struct vidc_hal_fbd { + u32 stream_id; + u32 view_id; + u32 timestamp_hi; + u32 timestamp_lo; + u32 flags1; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 alloc_len1; + u32 filled_len1; + u32 offset1; + u32 frame_width; + u32 frame_height; + u32 start_x_coord; + u32 start_y_coord; + u32 input_tag; + u32 input_tag1; + u32 output_tag; + enum hal_picture picture_type; + u32 packet_buffer1; + u32 extra_data_buffer; + u32 flags2; + u32 alloc_len2; + u32 filled_len2; + u32 offset2; + u32 packet_buffer2; + u32 flags3; + u32 alloc_len3; + u32 filled_len3; + u32 offset3; + u32 packet_buffer3; + enum hal_buffer buffer_type; +}; + +struct msm_vidc_capability { + enum hal_domain domain; + enum hal_video_codec codec; + struct hal_capability_supported width; + struct hal_capability_supported height; + struct hal_capability_supported mbs_per_frame; + struct hal_capability_supported mbs_per_sec; + struct hal_capability_supported frame_rate; + struct hal_capability_supported scale_x; + struct hal_capability_supported scale_y; + struct hal_capability_supported bitrate; + struct hal_capability_supported bframe; + struct hal_capability_supported peakbitrate; + struct hal_capability_supported hier_p; + struct hal_capability_supported ltr_count; + struct hal_capability_supported secure_output2_threshold; + struct hal_capability_supported hier_b; + struct hal_capability_supported lcu_size; + struct hal_capability_supported hier_p_hybrid; + struct hal_capability_supported mbs_per_sec_power_save; + struct hal_capability_supported extradata; + struct hal_capability_supported profile; + struct hal_capability_supported level; + struct hal_capability_supported i_qp; + struct hal_capability_supported p_qp; + struct hal_capability_supported b_qp; + struct hal_capability_supported rc_modes; + struct hal_capability_supported blur_width; + struct hal_capability_supported blur_height; + struct hal_capability_supported color_space_caps; + struct hal_capability_supported rotation; + struct hal_capability_supported slice_delivery_mode; + struct hal_capability_supported slice_bytes; + struct hal_capability_supported slice_mbs; + struct hal_capability_supported secure; + struct hal_capability_supported max_num_b_frames; + struct hal_capability_supported max_video_cores; + struct hal_capability_supported max_work_modes; + struct hal_capability_supported ubwc_cr_stats; + struct hal_profile_level_supported profile_level; + struct hal_uncompressed_format_supported uncomp_format; + struct hal_interlace_format_supported HAL_format; + struct hal_nal_stream_format_supported nal_stream_format; + struct hal_intra_refresh intra_refresh; + enum buffer_mode_type alloc_mode_out; + enum buffer_mode_type alloc_mode_in; + u32 pixelprocess_capabilities; + u32 tme_version; +}; + +struct vidc_hal_sys_init_done { + u32 dec_codec_supported; + u32 enc_codec_supported; + u32 codec_count; + struct msm_vidc_capability *capabilities; + u32 max_sessions_supported; +}; + +struct vidc_hal_session_init_done { + struct msm_vidc_capability capability; +}; + +struct msm_vidc_cb_cmd_done { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 size; + union { + struct vidc_resource_hdr resource_hdr; + struct vidc_buffer_addr_info buffer_addr_info; + struct vidc_frame_plane_config frame_plane_config; + struct vidc_uncompressed_frame_config uncompressed_frame_config; + struct vidc_frame_data frame_data; + struct vidc_hal_ebd ebd; + struct vidc_hal_fbd fbd; + struct vidc_hal_sys_init_done sys_init_done; + struct vidc_hal_session_init_done session_init_done; + struct hal_buffer_info buffer_info; + struct vidc_register_buffer regbuf; + struct vidc_unregister_buffer unregbuf; + union hal_get_property property; + enum hal_flush flush_type; + } data; +}; + +struct hal_index_extradata_input_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct msm_vidc_cb_event { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 height; + u32 width; + int bit_depth; + u32 hal_event_type; + u32 packet_buffer; + u32 extra_data_buffer; + u32 output_tag; + u32 pic_struct; + u32 colour_space; + u32 profile; + u32 level; + u32 entropy_mode; + u32 capture_buf_count; + u32 max_dpb_count; + u32 max_ref_count; + u32 max_dec_buffering; + struct hal_index_extradata_input_crop_payload crop_data; +}; + +struct msm_vidc_cb_data_done { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 size; + u32 clnt_data; + union { + struct vidc_hal_ebd input_done; + struct vidc_hal_fbd output_done; + }; +}; + +struct msm_vidc_cb_info { + enum hal_command_response response_type; + union { + struct msm_vidc_cb_cmd_done cmd; + struct msm_vidc_cb_event event; + struct msm_vidc_cb_data_done data; + } response; +}; + +enum msm_vidc_hfi_type { + VIDC_HFI_VENUS, +}; + +enum msm_vidc_thermal_level { + VIDC_THERMAL_NORMAL = 0, + VIDC_THERMAL_LOW, + VIDC_THERMAL_HIGH, + VIDC_THERMAL_CRITICAL +}; + +enum vidc_vote_data_session { + VIDC_BUS_VOTE_DATA_SESSION_INVALID = 0, + /* + * No declarations exist. Values generated by VIDC_VOTE_DATA_SESSION_VAL + * describe the enumerations e.g.: + * + * enum vidc_bus_vote_data_session_type h264_decoder_session = + * VIDC_VOTE_DATA_SESSION_VAL(HAL_VIDEO_CODEC_H264, + * HAL_VIDEO_DOMAIN_DECODER); + */ +}; + +/* + * Careful modifying VIDC_VOTE_DATA_SESSION_VAL(). + * + * This macro assigns two bits to each codec: the lower bit denoting the codec + * type, and the higher bit denoting session type. + */ +static inline enum vidc_vote_data_session VIDC_VOTE_DATA_SESSION_VAL( + enum hal_video_codec c, enum hal_domain d) { + if (d != HAL_VIDEO_DOMAIN_ENCODER && d != HAL_VIDEO_DOMAIN_DECODER) + return VIDC_BUS_VOTE_DATA_SESSION_INVALID; + + return (1 << ilog2(c) * 2) | ((d - 1) << (ilog2(c) * 2 + 1)); +} + +struct msm_vidc_gov_data { + struct vidc_bus_vote_data *data; + u32 data_count; +}; + +enum msm_vidc_power_mode { + VIDC_POWER_NORMAL = 0, + VIDC_POWER_LOW, + VIDC_POWER_TURBO +}; + +struct vidc_bus_vote_data { + enum hal_domain domain; + enum hal_video_codec codec; + enum hal_uncompressed_format color_formats[2]; + int num_formats; /* 1 = DPB-OPB unified; 2 = split */ + int input_height, input_width, fps, bitrate; + int output_height, output_width; + int rotation; + int compression_ratio; + int complexity_factor; + int input_cr; + u32 ddr_bw; + u32 sys_cache_bw; + bool use_dpb_read; + unsigned int lcu_size; + enum msm_vidc_power_mode power_mode; + enum hal_work_mode work_mode; + bool use_sys_cache; + bool b_frames_enabled; +}; + +struct vidc_clk_scale_data { + enum vidc_vote_data_session session[VIDC_MAX_SESSIONS]; + enum msm_vidc_power_mode power_mode[VIDC_MAX_SESSIONS]; + u32 load[VIDC_MAX_SESSIONS]; + int num_sessions; +}; + +struct hal_cmd_sys_get_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hal_hdr10_pq_sei { + struct msm_vidc_mastering_display_colour_sei_payload disp_color_sei; + struct msm_vidc_content_light_level_sei_payload cll_sei; +}; + +struct hal_vbv_hdr_buf_size { + u32 vbv_hdr_buf_size; +}; + +#define call_hfi_op(q, op, args...) \ + (((q) && (q)->op) ? ((q)->op(args)) : 0) + +struct hfi_device { + void *hfi_device_data; + + /*Add function pointers for all the hfi functions below*/ + int (*core_init)(void *device); + int (*core_release)(void *device); + int (*core_ping)(void *device); + int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type); + int (*session_init)(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type, + void **new_session); + int (*session_end)(void *session); + int (*session_abort)(void *session); + int (*session_set_buffers)(void *sess, + struct vidc_buffer_addr_info *buffer_info); + int (*session_release_buffers)(void *sess, + struct vidc_buffer_addr_info *buffer_info); + int (*session_register_buffer)(void *sess, + struct vidc_register_buffer *buffer); + int (*session_unregister_buffer)(void *sess, + struct vidc_unregister_buffer *buffer); + int (*session_load_res)(void *sess); + int (*session_release_res)(void *sess); + int (*session_start)(void *sess); + int (*session_continue)(void *sess); + int (*session_stop)(void *sess); + int (*session_etb)(void *sess, struct vidc_frame_data *input_frame); + int (*session_ftb)(void *sess, struct vidc_frame_data *output_frame); + int (*session_process_batch)(void *sess, + int num_etbs, struct vidc_frame_data etbs[], + int num_ftbs, struct vidc_frame_data ftbs[]); + int (*session_get_buf_req)(void *sess); + int (*session_flush)(void *sess, enum hal_flush flush_mode); + int (*session_set_property)(void *sess, enum hal_property ptype, + void *pdata); + int (*session_get_property)(void *sess, enum hal_property ptype); + int (*session_pause)(void *sess); + int (*session_resume)(void *sess); + int (*scale_clocks)(void *dev, u32 freq); + int (*vote_bus)(void *dev, struct vidc_bus_vote_data *data, + int num_data); + int (*get_fw_info)(void *dev, struct hal_fw_info *fw_info); + int (*session_clean)(void *sess); + int (*get_core_capabilities)(void *dev); + int (*suspend)(void *dev); + int (*flush_debug_queue)(void *dev); + int (*noc_error_info)(void *dev); + enum hal_default_properties (*get_default_properties)(void *dev); +}; + +typedef void (*hfi_cmd_response_callback) (enum hal_command_response cmd, + void *data); +typedef void (*msm_vidc_callback) (u32 response, void *callback); + +struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, + u32 device_id, struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback); +void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type, + struct hfi_device *hdev); +u32 vidc_get_hfi_domain(enum hal_domain hal_domain); +u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec); +enum hal_domain vidc_get_hal_domain(u32 hfi_domain); +enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec); + +#endif /*__VIDC_HFI_API_H__ */ diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h new file mode 100644 index 000000000000..4b1d838ac138 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -0,0 +1,1165 @@ +/* Copyright (c) 2012-2019, 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 __H_VIDC_HFI_HELPER_H__ +#define __H_VIDC_HFI_HELPER_H__ + +#define HFI_COMMON_BASE (0) +#define HFI_OX_BASE (0x01000000) + +#define HFI_VIDEO_DOMAIN_ENCODER (HFI_COMMON_BASE + 0x1) +#define HFI_VIDEO_DOMAIN_DECODER (HFI_COMMON_BASE + 0x2) +#define HFI_VIDEO_DOMAIN_VPE (HFI_COMMON_BASE + 0x4) +#define HFI_VIDEO_DOMAIN_CVP (HFI_COMMON_BASE + 0x8) + +#define HFI_DOMAIN_BASE_COMMON (HFI_COMMON_BASE + 0) +#define HFI_DOMAIN_BASE_VDEC (HFI_COMMON_BASE + 0x01000000) +#define HFI_DOMAIN_BASE_VENC (HFI_COMMON_BASE + 0x02000000) +#define HFI_DOMAIN_BASE_VPE (HFI_COMMON_BASE + 0x03000000) +#define HFI_DOMAIN_BASE_CVP (HFI_COMMON_BASE + 0x04000000) + +#define HFI_VIDEO_ARCH_OX (HFI_COMMON_BASE + 0x1) + +#define HFI_ARCH_COMMON_OFFSET (0) +#define HFI_ARCH_OX_OFFSET (0x00200000) + +#define HFI_CMD_START_OFFSET (0x00010000) +#define HFI_MSG_START_OFFSET (0x00020000) + +#define HFI_ERR_NONE HFI_COMMON_BASE +#define HFI_ERR_SYS_FATAL (HFI_COMMON_BASE + 0x1) +#define HFI_ERR_SYS_INVALID_PARAMETER (HFI_COMMON_BASE + 0x2) +#define HFI_ERR_SYS_VERSION_MISMATCH (HFI_COMMON_BASE + 0x3) +#define HFI_ERR_SYS_INSUFFICIENT_RESOURCES (HFI_COMMON_BASE + 0x4) +#define HFI_ERR_SYS_MAX_SESSIONS_REACHED (HFI_COMMON_BASE + 0x5) +#define HFI_ERR_SYS_UNSUPPORTED_CODEC (HFI_COMMON_BASE + 0x6) +#define HFI_ERR_SYS_SESSION_IN_USE (HFI_COMMON_BASE + 0x7) +#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE (HFI_COMMON_BASE + 0x8) +#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x9) +#define HFI_ERR_SYS_NOC_ERROR (HFI_COMMON_BASE + 0x11) +#define HFI_ERR_SESSION_FATAL (HFI_COMMON_BASE + 0x1001) +#define HFI_ERR_SESSION_INVALID_PARAMETER (HFI_COMMON_BASE + 0x1002) +#define HFI_ERR_SESSION_BAD_POINTER (HFI_COMMON_BASE + 0x1003) +#define HFI_ERR_SESSION_INVALID_SESSION_ID (HFI_COMMON_BASE + 0x1004) +#define HFI_ERR_SESSION_INVALID_STREAM_ID (HFI_COMMON_BASE + 0x1005) +#define HFI_ERR_SESSION_INCORRECT_STATE_OPERATION \ + (HFI_COMMON_BASE + 0x1006) +#define HFI_ERR_SESSION_UNSUPPORTED_PROPERTY (HFI_COMMON_BASE + 0x1007) + +#define HFI_ERR_SESSION_UNSUPPORTED_SETTING (HFI_COMMON_BASE + 0x1008) + +#define HFI_ERR_SESSION_INSUFFICIENT_RESOURCES (HFI_COMMON_BASE + 0x1009) + +#define HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED \ + (HFI_COMMON_BASE + 0x100A) + +#define HFI_ERR_SESSION_STREAM_CORRUPT (HFI_COMMON_BASE + 0x100B) +#define HFI_ERR_SESSION_ENC_OVERFLOW (HFI_COMMON_BASE + 0x100C) +#define HFI_ERR_SESSION_UNSUPPORTED_STREAM (HFI_COMMON_BASE + 0x100D) +#define HFI_ERR_SESSION_CMDSIZE (HFI_COMMON_BASE + 0x100E) +#define HFI_ERR_SESSION_UNSUPPORT_CMD (HFI_COMMON_BASE + 0x100F) +#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE (HFI_COMMON_BASE + 0x1010) +#define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL (HFI_COMMON_BASE + 0x1011) +#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR (HFI_COMMON_BASE + 0x1012) +#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED (HFI_COMMON_BASE + 0x1013) + +#define HFI_EVENT_SYS_ERROR (HFI_COMMON_BASE + 0x1) +#define HFI_EVENT_SESSION_ERROR (HFI_COMMON_BASE + 0x2) + +#define HFI_VIDEO_CODEC_H264 0x00000002 +#define HFI_VIDEO_CODEC_MPEG1 0x00000008 +#define HFI_VIDEO_CODEC_MPEG2 0x00000010 +#define HFI_VIDEO_CODEC_VP8 0x00001000 +#define HFI_VIDEO_CODEC_HEVC 0x00002000 +#define HFI_VIDEO_CODEC_VP9 0x00004000 +#define HFI_VIDEO_CODEC_TME 0x00008000 +#define HFI_VIDEO_CODEC_CVP 0x00010000 + +#define HFI_PROFILE_UNKNOWN 0x00000000 +#define HFI_LEVEL_UNKNOWN 0x00000000 + +#define HFI_H264_PROFILE_BASELINE 0x00000001 +#define HFI_H264_PROFILE_MAIN 0x00000002 +#define HFI_H264_PROFILE_HIGH 0x00000004 +#define HFI_H264_PROFILE_STEREO_HIGH 0x00000008 +#define HFI_H264_PROFILE_MULTIVIEW_HIGH 0x00000010 +#define HFI_H264_PROFILE_CONSTRAINED_BASE 0x00000020 +#define HFI_H264_PROFILE_CONSTRAINED_HIGH 0x00000040 + +#define HFI_LEVEL_UNKNOWN 0x00000000 +#define HFI_H264_LEVEL_1 0x00000001 +#define HFI_H264_LEVEL_1b 0x00000002 +#define HFI_H264_LEVEL_11 0x00000004 +#define HFI_H264_LEVEL_12 0x00000008 +#define HFI_H264_LEVEL_13 0x00000010 +#define HFI_H264_LEVEL_2 0x00000020 +#define HFI_H264_LEVEL_21 0x00000040 +#define HFI_H264_LEVEL_22 0x00000080 +#define HFI_H264_LEVEL_3 0x00000100 +#define HFI_H264_LEVEL_31 0x00000200 +#define HFI_H264_LEVEL_32 0x00000400 +#define HFI_H264_LEVEL_4 0x00000800 +#define HFI_H264_LEVEL_41 0x00001000 +#define HFI_H264_LEVEL_42 0x00002000 +#define HFI_H264_LEVEL_5 0x00004000 +#define HFI_H264_LEVEL_51 0x00008000 +#define HFI_H264_LEVEL_52 0x00010000 +#define HFI_H264_LEVEL_6 0x00020000 +#define HFI_H264_LEVEL_61 0x00040000 +#define HFI_H264_LEVEL_62 0x00080000 + +#define HFI_MPEG2_PROFILE_SIMPLE 0x00000001 +#define HFI_MPEG2_PROFILE_MAIN 0x00000002 + +#define HFI_MPEG2_LEVEL_LL 0x00000001 +#define HFI_MPEG2_LEVEL_ML 0x00000002 +#define HFI_MPEG2_LEVEL_HL 0x00000004 + +#define HFI_VP8_PROFILE_MAIN 0x00000001 + +#define HFI_VP8_LEVEL_VERSION_0 0x00000001 +#define HFI_VP8_LEVEL_VERSION_1 0x00000002 +#define HFI_VP8_LEVEL_VERSION_2 0x00000004 +#define HFI_VP8_LEVEL_VERSION_3 0x00000008 + +#define HFI_HEVC_PROFILE_MAIN 0x00000001 +#define HFI_HEVC_PROFILE_MAIN10 0x00000002 +#define HFI_HEVC_PROFILE_MAIN_STILL_PIC 0x00000004 + +#define HFI_HEVC_LEVEL_1 0x00000001 +#define HFI_HEVC_LEVEL_2 0x00000002 +#define HFI_HEVC_LEVEL_21 0x00000004 +#define HFI_HEVC_LEVEL_3 0x00000008 +#define HFI_HEVC_LEVEL_31 0x00000010 +#define HFI_HEVC_LEVEL_4 0x00000020 +#define HFI_HEVC_LEVEL_41 0x00000040 +#define HFI_HEVC_LEVEL_5 0x00000080 +#define HFI_HEVC_LEVEL_51 0x00000100 +#define HFI_HEVC_LEVEL_52 0x00000200 +#define HFI_HEVC_LEVEL_6 0x00000400 +#define HFI_HEVC_LEVEL_61 0x00000800 +#define HFI_HEVC_LEVEL_62 0x00001000 + +#define HFI_HEVC_TIER_MAIN 0x1 +#define HFI_HEVC_TIER_HIGH0 0x2 + +#define HFI_TME_PROFILE_DEFAULT 0x00000001 +#define HFI_TME_PROFILE_FRC 0x00000002 +#define HFI_TME_PROFILE_ASW 0x00000004 +#define HFI_TME_PROFILE_DFS_BOKEH 0x00000008 + +#define HFI_TME_LEVEL_INTEGER 0x00000001 + +#define HFI_BUFFER_INPUT (HFI_COMMON_BASE + 0x1) +#define HFI_BUFFER_OUTPUT (HFI_COMMON_BASE + 0x2) +#define HFI_BUFFER_OUTPUT2 (HFI_COMMON_BASE + 0x3) +#define HFI_BUFFER_INTERNAL_PERSIST (HFI_COMMON_BASE + 0x4) +#define HFI_BUFFER_INTERNAL_PERSIST_1 (HFI_COMMON_BASE + 0x5) +#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH (HFI_COMMON_BASE + 0x6) +#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1 (HFI_COMMON_BASE + 0x7) +#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2 (HFI_COMMON_BASE + 0x8) +#define HFI_BUFFER_COMMON_INTERNAL_RECON (HFI_COMMON_BASE + 0x9) +#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_COMMON_BASE + 0xA) +#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_COMMON_BASE + 0xB) +#define HFI_BUFFER_EXTRADATA_INPUT (HFI_COMMON_BASE + 0xC) + +#define HFI_BITDEPTH_8 (HFI_COMMON_BASE + 0x0) +#define HFI_BITDEPTH_9 (HFI_COMMON_BASE + 0x1) +#define HFI_BITDEPTH_10 (HFI_COMMON_BASE + 0x2) + +#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1 +#define HFI_VENC_PERFMODE_POWER_SAVE 0x2 + +#define HFI_WORKMODE_1 (HFI_COMMON_BASE + 0x1) +#define HFI_WORKMODE_2 (HFI_COMMON_BASE + 0x2) + +struct hfi_buffer_info { + u32 buffer_addr; + u32 extra_data_addr; +}; + +#define HFI_PROPERTY_SYS_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000) +#define HFI_PROPERTY_SYS_DEBUG_CONFIG \ + (HFI_PROPERTY_SYS_COMMON_START + 0x001) +#define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO \ + (HFI_PROPERTY_SYS_COMMON_START + 0x002) +#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ \ + (HFI_PROPERTY_SYS_COMMON_START + 0x003) +#define HFI_PROPERTY_SYS_IDLE_INDICATOR \ + (HFI_PROPERTY_SYS_COMMON_START + 0x004) +#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL \ + (HFI_PROPERTY_SYS_COMMON_START + 0x005) +#define HFI_PROPERTY_SYS_IMAGE_VERSION \ + (HFI_PROPERTY_SYS_COMMON_START + 0x006) +#define HFI_PROPERTY_SYS_CONFIG_COVERAGE \ + (HFI_PROPERTY_SYS_COMMON_START + 0x007) +#define HFI_PROPERTY_SYS_UBWC_CONFIG \ + (HFI_PROPERTY_SYS_COMMON_START + 0x008) + +#define HFI_PROPERTY_PARAM_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000) +#define HFI_PROPERTY_PARAM_FRAME_SIZE \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x003) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x004) +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x005) +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x006) +#define HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x007) +#define HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x008) +#define HFI_PROPERTY_PARAM_CODEC_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00A) +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00B) +#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00C) +#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00E) +#define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x010) +#define HFI_PROPERTY_PARAM_SECURE_SESSION \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x011) +#define HFI_PROPERTY_PARAM_WORK_MODE \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x015) +#define HFI_PROPERTY_TME_VERSION_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x016) +#define HFI_PROPERTY_PARAM_WORK_ROUTE \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x017) + +#define HFI_PROPERTY_CONFIG_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000) +#define HFI_PROPERTY_CONFIG_FRAME_RATE \ + (HFI_PROPERTY_CONFIG_COMMON_START + 0x001) +#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE \ + (HFI_PROPERTY_CONFIG_COMMON_START + 0x002) +#define HFI_PROPERTY_CONFIG_OPERATING_RATE \ + (HFI_PROPERTY_CONFIG_COMMON_START + 0x003) + +#define HFI_PROPERTY_PARAM_VDEC_COMMON_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x3000) +#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x007) +#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x00A) +#define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x00B) + + +#define HFI_PROPERTY_CONFIG_VDEC_COMMON_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x4000) + +#define HFI_PROPERTY_PARAM_VENC_COMMON_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x5000) +#define HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x003) +#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x004) +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_VENC_OPEN_GOP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00C) +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00D) +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00E) +#define HFI_PROPERTY_PARAM_VENC_VBV_HRD_BUF_SIZE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00F) +#define HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x010) +#define HFI_PROPERTY_PARAM_VENC_H264_SPS_ID \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014) +#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015) +#define HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016) +#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017) +#define HFI_PROPERTY_PARAM_VENC_NUMREF \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018) +#define HFI_PROPERTY_PARAM_VENC_LTRMODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C) +#define HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D) +#define HFI_PROPERTY_PARAM_VENC_VUI_TIMING_INFO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E) +#define HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022) +#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023) +#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025) +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) +#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x027) +#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x029) +#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02C) +#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02F) +#define HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x031) +#define HFI_PROPERTY_PARAM_VENC_IFRAMESIZE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034) +#define HFI_PROPERTY_PARAM_VENC_SEND_OUTPUT_FOR_SKIPPED_FRAMES \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x035) +#define HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x036) +#define HFI_PROPERTY_PARAM_VENC_ADAPTIVE_B \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x037) +#define HFI_PROPERTY_PARAM_VENC_BITRATE_SAVINGS \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x038) + +#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000) +#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x001) +#define HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x002) +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x003) +#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x004) +#define HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x005) +#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x008) +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009) +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A) +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B) +#define HFI_PROPERTY_CONFIG_VENC_VBV_HRD_BUF_SIZE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00D) +#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00E) +#define HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00F) +#define HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x010) +#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x012) +#define HFI_PROPERTY_CONFIG_HEIC_FRAME_CROP_INFO \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x013) +#define HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x014) +#define HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x015) + +#define HFI_PROPERTY_PARAM_VPE_COMMON_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000) +#define HFI_PROPERTY_PARAM_VPE_ROTATION \ + (HFI_PROPERTY_PARAM_VPE_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION \ + (HFI_PROPERTY_PARAM_VPE_COMMON_START + 0x002) + +#define HFI_PROPERTY_CONFIG_VPE_COMMON_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000) + +struct hfi_pic_struct { + u32 progressive_only; +}; + +struct hfi_bitrate { + u32 bit_rate; + u32 layer_id; +}; + +struct hfi_colour_space { + u32 colour_space; +}; + +#define HFI_CAPABILITY_FRAME_WIDTH (HFI_COMMON_BASE + 0x1) +#define HFI_CAPABILITY_FRAME_HEIGHT (HFI_COMMON_BASE + 0x2) +#define HFI_CAPABILITY_MBS_PER_FRAME (HFI_COMMON_BASE + 0x3) +#define HFI_CAPABILITY_MBS_PER_SECOND (HFI_COMMON_BASE + 0x4) +#define HFI_CAPABILITY_FRAMERATE (HFI_COMMON_BASE + 0x5) +#define HFI_CAPABILITY_SCALE_X (HFI_COMMON_BASE + 0x6) +#define HFI_CAPABILITY_SCALE_Y (HFI_COMMON_BASE + 0x7) +#define HFI_CAPABILITY_BITRATE (HFI_COMMON_BASE + 0x8) +#define HFI_CAPABILITY_BFRAME (HFI_COMMON_BASE + 0x9) +#define HFI_CAPABILITY_PEAKBITRATE (HFI_COMMON_BASE + 0xa) +#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x10) +#define HFI_CAPABILITY_ENC_LTR_COUNT (HFI_COMMON_BASE + 0x11) +#define HFI_CAPABILITY_CP_OUTPUT2_THRESH (HFI_COMMON_BASE + 0x12) +#define HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x13) +#define HFI_CAPABILITY_LCU_SIZE (HFI_COMMON_BASE + 0x14) +#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x15) +#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE (HFI_COMMON_BASE + 0x16) +#define HFI_CAPABILITY_EXTRADATA (HFI_COMMON_BASE + 0X17) +#define HFI_CAPABILITY_PROFILE (HFI_COMMON_BASE + 0X18) +#define HFI_CAPABILITY_LEVEL (HFI_COMMON_BASE + 0X19) +#define HFI_CAPABILITY_I_FRAME_QP (HFI_COMMON_BASE + 0X20) +#define HFI_CAPABILITY_P_FRAME_QP (HFI_COMMON_BASE + 0X21) +#define HFI_CAPABILITY_B_FRAME_QP (HFI_COMMON_BASE + 0X22) +#define HFI_CAPABILITY_RATE_CONTROL_MODES (HFI_COMMON_BASE + 0X23) +#define HFI_CAPABILITY_BLUR_WIDTH (HFI_COMMON_BASE + 0X24) +#define HFI_CAPABILITY_BLUR_HEIGHT (HFI_COMMON_BASE + 0X25) +#define HFI_CAPABILITY_SLICE_DELIVERY_MODES (HFI_COMMON_BASE + 0X26) +#define HFI_CAPABILITY_SLICE_BYTE (HFI_COMMON_BASE + 0X27) +#define HFI_CAPABILITY_SLICE_MB (HFI_COMMON_BASE + 0X28) +#define HFI_CAPABILITY_SECURE (HFI_COMMON_BASE + 0X29) +#define HFI_CAPABILITY_MAX_NUM_B_FRAMES (HFI_COMMON_BASE + 0X2A) +#define HFI_CAPABILITY_MAX_VIDEOCORES (HFI_COMMON_BASE + 0X2B) +#define HFI_CAPABILITY_MAX_WORKMODES (HFI_COMMON_BASE + 0X2C) +#define HFI_CAPABILITY_UBWC_CR_STATS (HFI_COMMON_BASE + 0X2D) +#define HFI_CAPABILITY_ROTATION (HFI_COMMON_BASE + 0X2F) +#define HFI_CAPABILITY_COLOR_SPACE_CONVERSION (HFI_COMMON_BASE + 0X30) +#define HFI_CAPABILITY_MAX_WORKROUTES (HFI_COMMON_BASE + 0X31) +#define HFI_CAPABILITY_CQ_QUALITY_LEVEL (HFI_COMMON_BASE + 0X32) + +struct hfi_capability_supported { + u32 capability_type; + u32 min; + u32 max; + u32 step_size; +}; + +struct hfi_capability_supported_info { + u32 num_capabilities; + struct hfi_capability_supported rg_data[1]; +}; + +#define HFI_DEBUG_MSG_LOW 0x00000001 +#define HFI_DEBUG_MSG_MEDIUM 0x00000002 +#define HFI_DEBUG_MSG_HIGH 0x00000004 +#define HFI_DEBUG_MSG_ERROR 0x00000008 +#define HFI_DEBUG_MSG_FATAL 0x00000010 +#define HFI_DEBUG_MSG_PERF 0x00000020 + +#define HFI_DEBUG_MODE_QUEUE 0x00000001 +#define HFI_DEBUG_MODE_QDSS 0x00000002 + +struct hfi_debug_config { + u32 debug_config; + u32 debug_mode; +}; + +struct hfi_enable { + u32 enable; +}; + +#define HFI_H264_DB_MODE_DISABLE (HFI_COMMON_BASE + 0x1) +#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY \ + (HFI_COMMON_BASE + 0x2) +#define HFI_H264_DB_MODE_ALL_BOUNDARY (HFI_COMMON_BASE + 0x3) + +struct hfi_h264_db_control { + u32 mode; + u32 slice_alpha_offset; + u32 slice_beta_offset; +}; + +#define HFI_H264_ENTROPY_CAVLC (HFI_COMMON_BASE + 0x1) +#define HFI_H264_ENTROPY_CABAC (HFI_COMMON_BASE + 0x2) + +#define HFI_H264_CABAC_MODEL_0 (HFI_COMMON_BASE + 0x1) +#define HFI_H264_CABAC_MODEL_1 (HFI_COMMON_BASE + 0x2) +#define HFI_H264_CABAC_MODEL_2 (HFI_COMMON_BASE + 0x3) + +struct hfi_h264_entropy_control { + u32 entropy_mode; + u32 cabac_model; +}; + +struct hfi_frame_rate { + u32 buffer_type; + u32 frame_rate; +}; + +struct hfi_heic_frame_quality { + u32 frame_quality; + u32 reserved[3]; +}; + +struct hfi_heic_grid_enable { + u32 grid_enable; +}; + +struct hfi_operating_rate { + u32 operating_rate; +}; + +#define HFI_INTRA_REFRESH_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_INTRA_REFRESH_CYCLIC (HFI_COMMON_BASE + 0x2) +#define HFI_INTRA_REFRESH_RANDOM (HFI_COMMON_BASE + 0x5) + +struct hfi_intra_refresh { + u32 mode; + u32 mbs; +}; + +struct hfi_idr_period { + u32 idr_period; +}; + +struct hfi_vpe_rotation_type { + u32 rotation; + u32 flip; +}; + +struct hfi_conceal_color { + u32 conceal_color_8bit; + u32 conceal_color_10bit; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + +struct hfi_multi_stream { + u32 buffer_type; + u32 enable; +}; + +struct hfi_multi_view_format { + u32 views; + u32 rg_view_order[1]; +}; + +#define HFI_MULTI_SLICE_OFF (HFI_COMMON_BASE + 0x1) +#define HFI_MULTI_SLICE_BY_MB_COUNT (HFI_COMMON_BASE + 0x2) +#define HFI_MULTI_SLICE_BY_BYTE_COUNT (HFI_COMMON_BASE + 0x3) + +struct hfi_multi_slice_control { + u32 multi_slice; + u32 slice_size; +}; + +#define HFI_NAL_FORMAT_STARTCODES 0x00000001 +#define HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER 0x00000002 +#define HFI_NAL_FORMAT_ONE_BYTE_LENGTH 0x00000004 +#define HFI_NAL_FORMAT_TWO_BYTE_LENGTH 0x00000008 +#define HFI_NAL_FORMAT_FOUR_BYTE_LENGTH 0x00000010 + +struct hfi_nal_stream_format_supported { + u32 nal_stream_format_supported; +}; + +struct hfi_nal_stream_format_select { + u32 nal_stream_format_select; +}; +#define HFI_PICTURE_TYPE_I 0x01 +#define HFI_PICTURE_TYPE_P 0x02 +#define HFI_PICTURE_TYPE_B 0x04 +#define HFI_PICTURE_TYPE_IDR 0x08 +#define HFI_PICTURE_TYPE_CRA 0x10 + +struct hfi_profile_level { + u32 profile; + u32 level; +}; + +struct hfi_dpb_counts { + u32 max_dpb_count; + u32 max_ref_count; + u32 max_dec_buffering; +}; + +struct hfi_profile_level_supported { + u32 profile_count; + struct hfi_profile_level rg_profile_level[1]; +}; + +struct hfi_quality_vs_speed { + u32 quality_vs_speed; +}; + +struct hfi_quantization { + u32 qp_packed; + u32 layer_id; + u32 enable; + u32 reserved[3]; +}; + +struct hfi_quantization_range { + struct hfi_quantization min_qp; + struct hfi_quantization max_qp; + u32 reserved[4]; +}; + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 + +struct hfi_ltr_mode { + u32 ltr_mode; + u32 ltr_count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + +struct hfi_frame_size { + u32 buffer_type; + u32 width; + u32 height; +}; + +struct hfi_videocores_usage_type { + u32 video_core_enable_mask; +}; + +struct hfi_video_work_mode { + u32 video_work_mode; +}; + +struct hfi_video_work_route { + u32 video_work_route; +}; + +struct hfi_video_signal_metadata { + u32 enable; + u32 video_format; + u32 video_full_range; + u32 color_description; + u32 color_primaries; + u32 transfer_characteristics; + u32 matrix_coeffs; +}; + +struct hfi_vui_timing_info { + u32 enable; + u32 fixed_frame_rate; + u32 time_scale; +}; + +struct hfi_bit_depth { + u32 buffer_type; + u32 bit_depth; +}; + +/* Base Offset for UBWC color formats */ +#define HFI_COLOR_FORMAT_UBWC_BASE (0x8000) +/* Base Offset for 10-bit color formats */ +#define HFI_COLOR_FORMAT_10_BIT_BASE (0x4000) + +#define HFI_COLOR_FORMAT_MONOCHROME (HFI_COMMON_BASE + 0x1) +#define HFI_COLOR_FORMAT_NV12 (HFI_COMMON_BASE + 0x2) +#define HFI_COLOR_FORMAT_NV21 (HFI_COMMON_BASE + 0x3) +#define HFI_COLOR_FORMAT_NV12_4x4TILE (HFI_COMMON_BASE + 0x4) +#define HFI_COLOR_FORMAT_NV21_4x4TILE (HFI_COMMON_BASE + 0x5) +#define HFI_COLOR_FORMAT_YUYV (HFI_COMMON_BASE + 0x6) +#define HFI_COLOR_FORMAT_YVYU (HFI_COMMON_BASE + 0x7) +#define HFI_COLOR_FORMAT_UYVY (HFI_COMMON_BASE + 0x8) +#define HFI_COLOR_FORMAT_VYUY (HFI_COMMON_BASE + 0x9) +#define HFI_COLOR_FORMAT_RGB565 (HFI_COMMON_BASE + 0xA) +#define HFI_COLOR_FORMAT_BGR565 (HFI_COMMON_BASE + 0xB) +#define HFI_COLOR_FORMAT_RGB888 (HFI_COMMON_BASE + 0xC) +#define HFI_COLOR_FORMAT_BGR888 (HFI_COMMON_BASE + 0xD) +#define HFI_COLOR_FORMAT_YUV444 (HFI_COMMON_BASE + 0xE) +#define HFI_COLOR_FORMAT_RGBA8888 (HFI_COMMON_BASE + 0x10) + +#define HFI_COLOR_FORMAT_YUV420_TP10 \ + (HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12) +#define HFI_COLOR_FORMAT_P010 \ + (HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12 + 0x1) + +#define HFI_COLOR_FORMAT_NV12_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_NV12) + +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_YUV420_TP10) + +#define HFI_COLOR_FORMAT_RGBA8888_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_RGBA8888) + +#define HFI_MAX_MATRIX_COEFFS 9 +#define HFI_MAX_BIAS_COEFFS 3 +#define HFI_MAX_LIMIT_COEFFS 6 + +#define HFI_STATISTICS_MODE_DEFAULT 0x10 +#define HFI_STATISTICS_MODE_1 0x11 +#define HFI_STATISTICS_MODE_2 0x12 +#define HFI_STATISTICS_MODE_3 0x13 + +struct hfi_uncompressed_format_select { + u32 buffer_type; + u32 format; +}; + +struct hfi_uncompressed_format_supported { + u32 buffer_type; + u32 format_entries; + u32 rg_format_info[1]; +}; + +struct hfi_uncompressed_plane_actual { + u32 actual_stride; + u32 actual_plane_buffer_height; +}; + +struct hfi_uncompressed_plane_actual_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_actual rg_plane_format[1]; +}; + +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints rg_plane_format[1]; +}; + +struct hfi_codec_supported { + u32 decoder_codec_supported; + u32 encoder_codec_supported; +}; + +struct hfi_properties_supported { + u32 num_properties; + u32 rg_properties[1]; +}; + +struct hfi_max_sessions_supported { + u32 max_sessions; +}; + +struct hfi_vpe_color_space_conversion { + u32 input_color_primaries; + u32 custom_matrix_enabled; + u32 csc_matrix[HFI_MAX_MATRIX_COEFFS]; + u32 csc_bias[HFI_MAX_BIAS_COEFFS]; + u32 csc_limit[HFI_MAX_LIMIT_COEFFS]; +}; + +#define HFI_ROTATE_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_ROTATE_90 (HFI_COMMON_BASE + 0x2) +#define HFI_ROTATE_180 (HFI_COMMON_BASE + 0x3) +#define HFI_ROTATE_270 (HFI_COMMON_BASE + 0x4) + +#define HFI_FLIP_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_FLIP_HORIZONTAL (HFI_COMMON_BASE + 0x2) +#define HFI_FLIP_VERTICAL (HFI_COMMON_BASE + 0x4) + +#define HFI_RESOURCE_SYSCACHE 0x00000002 + +struct hfi_resource_subcache_type { + u32 size; + u32 sc_id; +}; + +struct hfi_resource_syscache_info_type { + u32 num_entries; + struct hfi_resource_subcache_type rg_subcache_entries[1]; +}; + +struct hfi_property_sys_image_version_info_type { + u32 string_size; + u8 str_image_version[1]; +}; + +struct hfi_venc_config_advanced { + u8 pipe2d; + u8 hw_mode; + u8 low_delay_enforce; + u8 worker_vppsg_delay; + u32 close_gop; + u32 h264_constrain_intra_pred; + u32 h264_transform_8x8_flag; + u32 multi_refp_en; + u32 qmatrix_en; + u8 vpp_info_packet_mode; + u8 ref_tile_mode; + u8 bitstream_flush_mode; + u32 vppsg_vspap_fb_sync_delay; + u32 rc_initial_delay; + u32 peak_bitrate_constraint; + u32 ds_display_frame_width; + u32 ds_display_frame_height; + u32 perf_tune_param_ptr; + u32 input_x_offset; + u32 input_y_offset; + u32 input_roi_width; + u32 input_roi_height; + u32 vsp_fifo_dma_sel; + u32 h264_num_ref_frames; +}; + +struct hfi_vbv_hrd_bufsize { + u32 buffer_size; +}; + +struct hfi_codec_mask_supported { + u32 codecs; + u32 video_domains; +}; + +struct hfi_aspect_ratio { + u32 aspect_width; + u32 aspect_height; +}; + +#define HFI_IFRAME_SIZE_DEFAULT (HFI_COMMON_BASE + 0x1) +#define HFI_IFRAME_SIZE_MEDIUM (HFI_COMMON_BASE + 0x2) +#define HFI_IFRAME_SIZE_HIGH (HFI_COMMON_BASE + 0x3) +#define HFI_IFRAME_SIZE_UNLIMITED (HFI_COMMON_BASE + 0x4) +struct hfi_iframe_size { + u32 type; +}; + + +#define HFI_CMD_SYS_COMMON_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + HFI_CMD_START_OFFSET \ + + 0x0000) +#define HFI_CMD_SYS_INIT (HFI_CMD_SYS_COMMON_START + 0x001) +#define HFI_CMD_SYS_PC_PREP (HFI_CMD_SYS_COMMON_START + 0x002) +#define HFI_CMD_SYS_SET_RESOURCE (HFI_CMD_SYS_COMMON_START + 0x003) +#define HFI_CMD_SYS_RELEASE_RESOURCE (HFI_CMD_SYS_COMMON_START + 0x004) +#define HFI_CMD_SYS_SET_PROPERTY (HFI_CMD_SYS_COMMON_START + 0x005) +#define HFI_CMD_SYS_GET_PROPERTY (HFI_CMD_SYS_COMMON_START + 0x006) +#define HFI_CMD_SYS_SESSION_INIT (HFI_CMD_SYS_COMMON_START + 0x007) +#define HFI_CMD_SYS_SESSION_END (HFI_CMD_SYS_COMMON_START + 0x008) +#define HFI_CMD_SYS_SET_BUFFERS (HFI_CMD_SYS_COMMON_START + 0x009) +#define HFI_CMD_SYS_TEST_START (HFI_CMD_SYS_COMMON_START + 0x100) + +#define HFI_CMD_SESSION_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_CMD_START_OFFSET + 0x1000) +#define HFI_CMD_SESSION_SET_PROPERTY \ + (HFI_CMD_SESSION_COMMON_START + 0x001) +#define HFI_CMD_SESSION_SET_BUFFERS \ + (HFI_CMD_SESSION_COMMON_START + 0x002) +#define HFI_CMD_SESSION_GET_SEQUENCE_HEADER \ + (HFI_CMD_SESSION_COMMON_START + 0x003) + +#define HFI_MSG_SYS_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_MSG_START_OFFSET + 0x0000) +#define HFI_MSG_SYS_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x1) +#define HFI_MSG_SYS_PC_PREP_DONE (HFI_MSG_SYS_COMMON_START + 0x2) +#define HFI_MSG_SYS_RELEASE_RESOURCE (HFI_MSG_SYS_COMMON_START + 0x3) +#define HFI_MSG_SYS_DEBUG (HFI_MSG_SYS_COMMON_START + 0x4) +#define HFI_MSG_SYS_SESSION_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x6) +#define HFI_MSG_SYS_SESSION_END_DONE (HFI_MSG_SYS_COMMON_START + 0x7) +#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_COMMON_START + 0x8) +#define HFI_MSG_SYS_COV (HFI_MSG_SYS_COMMON_START + 0x9) +#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_COMMON_START + 0xA) +#define HFI_MSG_SESSION_SYNC_DONE (HFI_MSG_SESSION_OX_START + 0xD) + +#define HFI_MSG_SESSION_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_MSG_START_OFFSET + 0x1000) +#define HFI_MSG_EVENT_NOTIFY (HFI_MSG_SESSION_COMMON_START + 0x1) +#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE \ + (HFI_MSG_SESSION_COMMON_START + 0x2) + +#define HFI_CMD_SYS_TEST_SSR (HFI_CMD_SYS_TEST_START + 0x1) +#define HFI_TEST_SSR_SW_ERR_FATAL 0x1 +#define HFI_TEST_SSR_SW_DIV_BY_ZERO 0x2 +#define HFI_TEST_SSR_HW_WDOG_IRQ 0x3 + +struct vidc_hal_cmd_pkt_hdr { + u32 size; + u32 packet_type; +}; + +struct vidc_hal_msg_pkt_hdr { + u32 size; + u32 packet; +}; + +struct vidc_hal_session_cmd_pkt { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_packet_header { + u32 size; + u32 packet_type; +}; + +struct hfi_cmd_sys_init_packet { + u32 size; + u32 packet_type; + u32 arch_type; +}; + +struct hfi_cmd_sys_pc_prep_packet { + u32 size; + u32 packet_type; +}; + +struct hfi_cmd_sys_set_resource_packet { + u32 size; + u32 packet_type; + u32 resource_handle; + u32 resource_type; + u32 rg_resource_data[1]; +}; + +struct hfi_cmd_sys_release_resource_packet { + u32 size; + u32 packet_type; + u32 resource_type; + u32 resource_handle; +}; + +struct hfi_cmd_sys_set_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_sys_get_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_sys_session_init_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 session_domain; + u32 session_codec; +}; + +struct hfi_cmd_sys_session_end_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_sys_set_buffers_packet { + u32 size; + u32 packet_type; + u32 buffer_type; + u32 buffer_size; + u32 num_buffers; + u32 rg_buffer_addr[1]; +}; + +struct hfi_cmd_session_set_property_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_session_set_buffers_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 buffer_type; + u32 buffer_size; + u32 extra_data_size; + u32 min_buffer_size; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_buffer_mapping_type { + u32 index; + u32 device_addr; + u32 size; +}; + +struct hfi_cmd_session_register_buffers_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 client_data; + u32 response_req; + u32 num_buffers; + struct hfi_buffer_mapping_type buffer[1]; +}; + +struct hfi_cmd_session_unregister_buffers_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 client_data; + u32 response_req; + u32 num_buffers; + struct hfi_buffer_mapping_type buffer[1]; +}; + +struct hfi_cmd_session_sync_process_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 sync_id; + u32 rg_data[1]; +}; + +struct hfi_msg_event_notify_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 event_id; + u32 event_data1; + u32 event_data2; + u32 rg_ext_event_data[1]; +}; + +struct hfi_msg_release_buffer_ref_event_packet { + u32 packet_buffer; + u32 extra_data_buffer; + u32 output_tag; +}; + +struct hfi_msg_sys_init_done_packet { + u32 size; + u32 packet_type; + u32 error_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_sys_pc_prep_done_packet { + u32 size; + u32 packet_type; + u32 error_type; +}; + +struct hfi_msg_sys_release_resource_done_packet { + u32 size; + u32 packet_type; + u32 resource_handle; + u32 error_type; +}; + +struct hfi_msg_sys_session_init_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_sys_session_end_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_get_sequence_header_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 header_len; + u32 sequence_header; +}; + +struct hfi_msg_sys_debug_packet { + u32 size; + u32 packet_type; + u32 msg_type; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 rg_msg_data[1]; +}; + +struct hfi_msg_sys_coverage_packet { + u32 size; + u32 packet_type; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 rg_msg_data[1]; +}; + +enum HFI_VENUS_QTBL_STATUS { + HFI_VENUS_QTBL_DISABLED = 0x00, + HFI_VENUS_QTBL_ENABLED = 0x01, + HFI_VENUS_QTBL_INITIALIZING = 0x02, + HFI_VENUS_QTBL_DEINITIALIZING = 0x03 +}; + +enum HFI_VENUS_CTRL_INIT_STATUS { + HFI_VENUS_CTRL_NOT_INIT = 0x0, + HFI_VENUS_CTRL_READY = 0x1, + HFI_VENUS_CTRL_ERROR_FATAL = 0x2 +}; + +struct hfi_sfr_struct { + u32 bufSize; + u8 rg_data[1]; +}; + +struct hfi_cmd_sys_test_ssr_packet { + u32 size; + u32 packet_type; + u32 trigger_type; +}; + +struct hfi_mastering_display_colour_sei_payload { + u32 display_primariesX[3]; + u32 display_primariesY[3]; + u32 white_pointX; + u32 white_pointY; + u32 max_display_mastering_luminance; + u32 min_display_mastering_luminance; +}; + +struct hfi_content_light_level_sei_payload { + u32 max_content_light; + u32 max_pic_average_light; +}; + +struct hfi_hdr10_pq_sei { + struct hfi_mastering_display_colour_sei_payload mdisp_info; + struct hfi_content_light_level_sei_payload cll_info; +}; + +struct hfi_vbv_hdr_buf_size { + u32 vbv_hdr_buf_size; +}; + +#endif diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h new file mode 100644 index 000000000000..cbe30758103d --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h @@ -0,0 +1,196 @@ +/* Copyright (c) 2012-2020, 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 __VIDC_HFI_IO_H__ +#define __VIDC_HFI_IO_H__ + +#include + +#define VIDC_VBIF_BASE_OFFS 0x00080000 + +#define VIDC_CPU_BASE_OFFS 0x000C0000 +#define VIDEO_CC_BASE_OFFS 0x00100000 +#define VIDC_CPU_CS_BASE_OFFS (VIDC_CPU_BASE_OFFS + 0x00012000) +#define VIDC_CPU_IC_BASE_OFFS (VIDC_CPU_BASE_OFFS + 0x0001F000) + +#define VIDC_CPU_CS_REMAP_OFFS (VIDC_CPU_CS_BASE_OFFS + 0x00) +#define VIDC_CPU_CS_TIMER_CONTROL (VIDC_CPU_CS_BASE_OFFS + 0x04) +#define VIDC_CPU_CS_A2HSOFTINTEN (VIDC_CPU_CS_BASE_OFFS + 0x10) +#define VIDC_CPU_CS_A2HSOFTINTENCLR (VIDC_CPU_CS_BASE_OFFS + 0x14) +#define VIDC_CPU_CS_A2HSOFTINT (VIDC_CPU_CS_BASE_OFFS + 0x18) +#define VIDC_CPU_CS_A2HSOFTINTCLR (VIDC_CPU_CS_BASE_OFFS + 0x1C) +#define VIDC_CPU_CS_VMIMSG (VIDC_CPU_CS_BASE_OFFS + 0x34) +#define VIDC_CPU_CS_VMIMSGAG0 (VIDC_CPU_CS_BASE_OFFS + 0x38) +#define VIDC_CPU_CS_VMIMSGAG1 (VIDC_CPU_CS_BASE_OFFS + 0x3C) +#define VIDC_CPU_CS_VMIMSGAG2 (VIDC_CPU_CS_BASE_OFFS + 0x40) +#define VIDC_CPU_CS_VMIMSGAG3 (VIDC_CPU_CS_BASE_OFFS + 0x44) +#define VIDC_CPU_CS_SCIACMD (VIDC_CPU_CS_BASE_OFFS + 0x48) + +/* HFI_CTRL_STATUS */ +#define VIDC_CPU_CS_SCIACMDARG0 (VIDC_CPU_CS_BASE_OFFS + 0x4C) +#define VIDC_CPU_CS_SCIACMDARG0_BMSK 0xff +#define VIDC_CPU_CS_SCIACMDARG0_SHFT 0x0 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK 0xfe +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_SHFT 0x1 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_BMSK 0x1 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_SHFT 0x0 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY 0x100 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK 0x40000000 + +/* HFI_QTBL_INFO */ +#define VIDC_CPU_CS_SCIACMDARG1 (VIDC_CPU_CS_BASE_OFFS + 0x50) + +/* HFI_QTBL_ADDR */ +#define VIDC_CPU_CS_SCIACMDARG2 (VIDC_CPU_CS_BASE_OFFS + 0x54) + +/* HFI_VERSION_INFO */ +#define VIDC_CPU_CS_SCIACMDARG3 (VIDC_CPU_CS_BASE_OFFS + 0x58) + +/* VIDC_SFR_ADDR */ +#define VIDC_CPU_CS_SCIBCMD (VIDC_CPU_CS_BASE_OFFS + 0x5C) + +/* VIDC_MMAP_ADDR */ +#define VIDC_CPU_CS_SCIBCMDARG0 (VIDC_CPU_CS_BASE_OFFS + 0x60) + +/* VIDC_UC_REGION_ADDR */ +#define VIDC_CPU_CS_SCIBARG1 (VIDC_CPU_CS_BASE_OFFS + 0x64) + +/* VIDC_UC_REGION_ADDR */ +#define VIDC_CPU_CS_SCIBARG2 (VIDC_CPU_CS_BASE_OFFS + 0x68) + +#define VIDC_CPU_CS_SCIBARG3 (VIDC_CPU_CS_BASE_OFFS + 0x6C) + +#define VIDC_CPU_IC_IRQSTATUS (VIDC_CPU_IC_BASE_OFFS + 0x00) +#define VIDC_CPU_IC_FIQSTATUS (VIDC_CPU_IC_BASE_OFFS + 0x04) +#define VIDC_CPU_IC_RAWINTR (VIDC_CPU_IC_BASE_OFFS + 0x08) +#define VIDC_CPU_IC_INTSELECT (VIDC_CPU_IC_BASE_OFFS + 0x0C) +#define VIDC_CPU_IC_INTENABLE (VIDC_CPU_IC_BASE_OFFS + 0x10) +#define VIDC_CPU_IC_INTENACLEAR (VIDC_CPU_IC_BASE_OFFS + 0x14) +#define VIDC_CPU_IC_SOFTINT (VIDC_CPU_IC_BASE_OFFS + 0x18) +#define VIDC_CPU_IC_SOFTINT_H2A_BMSK 0x8000 +#define VIDC_CPU_IC_SOFTINT_H2A_SHFT 0xF +#define VIDC_CPU_IC_SOFTINTCLEAR (VIDC_CPU_IC_BASE_OFFS + 0x1C) + +/* + * -------------------------------------------------------------------------- + * MODULE: vidc_wrapper + * -------------------------------------------------------------------------- + */ +#define VIDC_WRAPPER_BASE_OFFS 0x000E0000 + +#define VIDC_WRAPPER_HW_VERSION (VIDC_WRAPPER_BASE_OFFS + 0x00) +#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000 +#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28 +#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xFFF0000 +#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16 +#define VIDC_WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xFFFF +#define VIDC_WRAPPER_CLOCK_CONFIG (VIDC_WRAPPER_BASE_OFFS + 0x04) + +#define VIDC_WRAPPER_INTR_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x0C) +#define VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_STATUS_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_STATUS_A2H_BMSK 0x4 +#define VIDC_WRAPPER_INTR_STATUS_A2H_SHFT 0x2 + +#define VIDC_WRAPPER_INTR_MASK (VIDC_WRAPPER_BASE_OFFS + 0x10) +#define VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_MASK_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK 0x8 +#define VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK 0x4 +#define VIDC_WRAPPER_INTR_MASK_A2HCPU_SHFT 0x2 + +#define VIDC_WRAPPER_INTR_CLEAR (VIDC_WRAPPER_BASE_OFFS + 0x14) +#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_CLEAR_A2H_BMSK 0x4 +#define VIDC_WRAPPER_INTR_CLEAR_A2H_SHFT 0x2 + +#define VIDC_WRAPPER_CPU_CLOCK_CONFIG (VIDC_WRAPPER_BASE_OFFS + 0x2000) +#define VIDC_WRAPPER_CPU_CGC_DIS (VIDC_WRAPPER_BASE_OFFS + 0x2010) +#define VIDC_WRAPPER_CPU_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x2014) +#define VIDC_VENUS_VBIF_CLK_ON (VIDC_VBIF_BASE_OFFS + 0x4) +#define VENUS_VBIF_AXI_HALT_CTRL0 (VIDC_VBIF_BASE_OFFS + 0x208) +#define VENUS_VBIF_AXI_HALT_CTRL1 (VIDC_VBIF_BASE_OFFS + 0x20C) + +#define VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0) +#define VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0) +#define VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US 500000 + + +#define VIDC_CTRL_INIT VIDC_CPU_CS_SCIACMD + +#define VIDC_CTRL_STATUS VIDC_CPU_CS_SCIACMDARG0 +#define VIDC_CTRL_ERROR_STATUS__M \ + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK +#define VIDC_CTRL_INIT_IDLE_MSG_BMSK \ + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK +#define VIDC_CTRL_STATUS_PC_READY \ + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY + + +#define VIDC_QTBL_INFO VIDC_CPU_CS_SCIACMDARG1 + +#define VIDC_QTBL_ADDR VIDC_CPU_CS_SCIACMDARG2 + +#define VIDC_VERSION_INFO VIDC_CPU_CS_SCIACMDARG3 + +#define VIDC_SFR_ADDR VIDC_CPU_CS_SCIBCMD +#define VIDC_MMAP_ADDR VIDC_CPU_CS_SCIBCMDARG0 +#define VIDC_UC_REGION_ADDR VIDC_CPU_CS_SCIBARG1 +#define VIDC_UC_REGION_SIZE VIDC_CPU_CS_SCIBARG2 + +/* HFI_DSP_QTBL_ADDR + * 31:3 - HFI_DSP_QTBL_ADDR + * 4-byte aligned Address + */ +#define HFI_DSP_QTBL_ADDR VIDC_CPU_CS_VMIMSG + +/* HFI_DSP_UC_REGION_ADDR + * 31:20 - HFI_DSP_UC_REGION_ADDR + * 1MB aligned address. + * Uncached Region start Address. This region covers + * HFI DSP QTable, + * HFI DSP Queue Headers, + * HFI DSP Queues, + */ +#define HFI_DSP_UC_REGION_ADDR VIDC_CPU_CS_VMIMSGAG0 + +/* HFI_DSP_UC_REGION_SIZE + * 31:20 - HFI_DSP_UC_REGION_SIZE + * Multiples of 1MB. + * Size of the DSP_UC_REGION Uncached Region + */ +#define HFI_DSP_UC_REGION_SIZE VIDC_CPU_CS_VMIMSGAG1 + +/* + * -------------------------------------------------------------------------- + * MODULE: vcodec noc error log registers + * -------------------------------------------------------------------------- + */ +#define VCODEC_CORE0_VIDEO_NOC_BASE_OFFS 0x00004000 +#define CVP_NOC_BASE_OFFS 0x0000C000 +#define VCODEC_COREX_VIDEO_NOC_ERR_SWID_LOW_OFFS 0x0500 +#define VCODEC_COREX_VIDEO_NOC_ERR_SWID_HIGH_OFFS 0x0504 +#define VCODEC_COREX_VIDEO_NOC_ERR_MAINCTL_LOW_OFFS 0x0508 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS 0x0510 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRCLR_LOW_OFFS 0x0518 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_LOW_OFFS 0x0520 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_HIGH_OFFS 0x0524 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_LOW_OFFS 0x0528 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_HIGH_OFFS 0x052C +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_LOW_OFFS 0x0530 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_HIGH_OFFS 0x0534 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_LOW_OFFS 0x0538 +#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_HIGH_OFFS 0x053C + +#endif diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d21bdc0d8f20..d05d43f2e269 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -202,6 +202,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Variable Bitrate", "Constant Bitrate", "Maximum Bitrate", + "RC OFF", "CBR VFR", "MBR VFR", "Constant Quality", @@ -312,6 +313,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) static const char * const header_mode[] = { "Separate Buffer", "Joined With 1st Frame", + "Joined with I Frame", NULL, }; static const char * const multi_slice[] = { diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9d0090df5eac..1fce390718c0 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -32,6 +32,10 @@ #include "of_private.h" +#ifdef CONFIG_PARAM_READ_WRITE +void init_param_mem_base_size(phys_addr_t base, unsigned long size); +#endif + /* * of_fdt_limit_memory - limit the number of regions in the /memory node * @limit: maximum entries @@ -675,6 +679,11 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", uname, &base, (unsigned long)(size / SZ_1M)); + #ifdef CONFIG_PARAM_READ_WRITE + if (!strncmp(uname, "param_mem", 9)) + init_param_mem_base_size(base, size); + #endif + len -= t_len; if (first) { fdt_reserved_mem_save_node(node, uname, base, size); diff --git a/drivers/oneplus/Kconfig b/drivers/oneplus/Kconfig new file mode 100755 index 000000000000..07d7c0370e85 --- /dev/null +++ b/drivers/oneplus/Kconfig @@ -0,0 +1,4 @@ +source "drivers/oneplus/coretech/Kconfig" +source "drivers/oneplus/drivers/Kconfig" +source "drivers/oneplus/fs/Kconfig" +source "drivers/oneplus/op_freezer/Kconfig" diff --git a/drivers/oneplus/Makefile b/drivers/oneplus/Makefile new file mode 100755 index 000000000000..7a4e000d8f6f --- /dev/null +++ b/drivers/oneplus/Makefile @@ -0,0 +1,4 @@ +obj-y += coretech/ +obj-y += drivers/ +obj-y += fs/ +obj-$(CONFIG_OP_FREEZER) += op_freezer/ diff --git a/drivers/oneplus/coretech/Kconfig b/drivers/oneplus/coretech/Kconfig new file mode 100644 index 000000000000..02b9660d85f1 --- /dev/null +++ b/drivers/oneplus/coretech/Kconfig @@ -0,0 +1,46 @@ +config OPCHAIN + default n + bool "Oneplus CoreTech helper, used for opchain module" + +config DEFRAG_HELPER + default n + bool "Oneplus CoreTech helper, used for defrag module" + +config CRPL_HELPER + default n + bool "Oneplus CoreTech helper, used for crpl module" + +config ADJ_CHAIN + default n + bool "Chain task adj for victim select" + help + To chain task adj for any purpose + +config HOUSTON + default n + bool "houston rocket play your temperature guard" + help + Realtime temperature monitor + +config MEMPLUS + default n + bool "memory+ feature" + help + Memory+ feature + +config SMART_BOOST + bool "support smart boost feature" + default n + help + Smart Boost feature +config TPD + default n + bool "a task placement decision for cpu limitation" + help + Task affinity by decison +config CPUFREQ_BOUNCING + default n + bool "A power optimization feature to control excessive heating of devices" +config OPLUS_FEATURE_SUGOV_TL + default n + bool "A power optimization feature target loads" diff --git a/drivers/oneplus/coretech/Makefile b/drivers/oneplus/coretech/Makefile new file mode 100755 index 000000000000..558662334db5 --- /dev/null +++ b/drivers/oneplus/coretech/Makefile @@ -0,0 +1,10 @@ +obj-y += coretech_helper.o +obj-$(CONFIG_ADJ_CHAIN) += adj_chain/ +obj-$(CONFIG_CRPL) += crpl/ +obj-$(CONFIG_DEFRAG_HELPER) += defrag/ +obj-$(CONFIG_HOUSTON) += houston/ +obj-$(CONFIG_MEMPLUS) += memplus/ +obj-$(CONFIG_OPCHAIN) += uxcore/ uxcore/core/ +obj-$(CONFIG_SMART_BOOST) += smartboost/ smartboost/core/ +obj-$(CONFIG_TPD) += tpd/ +obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing/ diff --git a/drivers/oneplus/coretech/adj_chain/Makefile b/drivers/oneplus/coretech/adj_chain/Makefile new file mode 100644 index 000000000000..8360bc80ce00 --- /dev/null +++ b/drivers/oneplus/coretech/adj_chain/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ADJ_CHAIN) += adj_chain.o diff --git a/drivers/oneplus/coretech/adj_chain/adj_chain.c b/drivers/oneplus/coretech/adj_chain/adj_chain.c new file mode 100644 index 000000000000..2ed5341a071e --- /dev/null +++ b/drivers/oneplus/coretech/adj_chain/adj_chain.c @@ -0,0 +1,86 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +struct list_head adj_chain[ADJ_CHAIN_MAX + 1]; +EXPORT_SYMBOL(adj_chain); + +int adj_chain_ready = 0; +EXPORT_SYMBOL(adj_chain_ready); + +int adj_chain_hist_high = 0; +EXPORT_SYMBOL(adj_chain_hist_high); + +static void __adj_chain_detach(struct task_struct *p) +{ + p->adj_chain_status |= 1 << AC_DETACH; + list_del_rcu(&p->adj_chain_tasks); + p->adj_chain_status &= ~(1 << AC_DETACH); +} + +static void __adj_chain_attach(struct task_struct *p) +{ + p->adj_chain_status |= 1 << AC_ATTACH; + list_add_tail_rcu(&p->adj_chain_tasks, &adj_chain[__adjc(get_oom_score_adj(p))]); + p->adj_chain_status &= ~(1 << AC_ATTACH); + if (__adjc(get_oom_score_adj(p)) > adj_chain_hist_high) + adj_chain_hist_high = __adjc(get_oom_score_adj(p)); +} + +void adj_chain_update_oom_score_adj(struct task_struct *p) +{ + if (likely(adj_chain_ready)) { + /* sync with system task_list */ + write_lock_irq(&tasklist_lock); + spin_lock(¤t->sighand->siglock); + if (!thread_group_leader(p)) { + pr_warn("%s '%d' not update from group leader: %s '%d', leader: %s '%d'\n", + current->comm, current->pid, + p->comm, p->pid, p->group_leader->comm, p->group_leader->pid); + p = p->group_leader; + } + if (unlikely(p->flags & PF_EXITING)) + goto done; + p->adj_chain_status |= 1 << AC_UPDATE_ADJ; + __adj_chain_detach(p); + __adj_chain_attach(p); + p->adj_chain_status &= ~(1 << AC_UPDATE_ADJ); +done: + spin_unlock(¤t->sighand->siglock); + write_unlock_irq(&tasklist_lock); + } +} +EXPORT_SYMBOL(adj_chain_update_oom_score_adj); + +void adj_chain_attach(struct task_struct *p) +{ + if (likely(adj_chain_ready)) { + __adj_chain_attach(p); + } +} +EXPORT_SYMBOL(adj_chain_attach); + +void adj_chain_detach(struct task_struct *p) +{ + if (likely(adj_chain_ready)) { + __adj_chain_detach(p); + } +} +EXPORT_SYMBOL(adj_chain_detach); + +static int init_adj_chain(void) +{ + int i = 0; + for (i = 0; i <= ADJ_CHAIN_MAX; ++i) { + INIT_LIST_HEAD(&adj_chain[i]); + } + pr_info("adj_chain init completed\n"); + adj_chain_ready = 1; + return 0; +} + +pure_initcall(init_adj_chain); +MODULE_DESCRIPTION("Oneplus adj chain"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/oneplus/coretech/coretech_helper.c b/drivers/oneplus/coretech/coretech_helper.c new file mode 100644 index 000000000000..7c78c2251d68 --- /dev/null +++ b/drivers/oneplus/coretech/coretech_helper.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011-2013,2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include + +s64 ctech_get_time(void) +{ + return ktime_to_ms(ktime_get()); +} +EXPORT_SYMBOL(ctech_get_time); diff --git a/drivers/oneplus/coretech/coretech_helper.h b/drivers/oneplus/coretech/coretech_helper.h new file mode 100644 index 000000000000..3bbeb78aed50 --- /dev/null +++ b/drivers/oneplus/coretech/coretech_helper.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_CORETECH_HELPER_H +#define _LINUX_CORETECH_HELPER_H + +extern s64 ctech_get_time(void); + +#endif diff --git a/drivers/oneplus/coretech/cpufreq_bouncing/Makefile b/drivers/oneplus/coretech/cpufreq_bouncing/Makefile new file mode 100644 index 000000000000..88aeadb4728b --- /dev/null +++ b/drivers/oneplus/coretech/cpufreq_bouncing/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing.o diff --git a/drivers/oneplus/coretech/cpufreq_bouncing/cpufreq_bouncing.c b/drivers/oneplus/coretech/cpufreq_bouncing/cpufreq_bouncing.c new file mode 100644 index 000000000000..848184810906 --- /dev/null +++ b/drivers/oneplus/coretech/cpufreq_bouncing/cpufreq_bouncing.c @@ -0,0 +1,485 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define NSEC_TO_MSEC(val) (val / NSEC_PER_MSEC) +#define MSEC_TO_NSEC(val) (val * NSEC_PER_MSEC) +#define CLUS_MAX 2 + +struct cpufreq_bouncing { + /* statistics */ + u64 last_ts; + u64 last_freq_update_ts; + u64 acc; + + /* restriction */ + int limit_freq; + int limit_level; + u64 limit_thres; + + /* freqs */ + int freq_sorting; + int freq_levels; + unsigned int *freqs; // quick mapping + + /* config */ + bool enable; + int cur_level; + + /* config: ceil */ + int max_level; + int down_speed; + s64 down_limit_ns; + unsigned int max_freq; + + /* config: floor */ + int min_level; + int up_speed; + s64 up_limit_ns; + unsigned int min_freq; +} cb_stuff[CLUS_MAX] = { + /* silver */ + { + }, + /* gold */ + { + .enable = true, + .down_limit_ns = 50 * NSEC_PER_MSEC, + .up_limit_ns = 50 * NSEC_PER_MSEC, + .limit_thres = 30 * NSEC_PER_MSEC, + .limit_freq = 2169600, + .limit_level = 16, + .down_speed = 2, + .up_speed = 1, + } +}; + +/* init config */ +static bool enable = false; +module_param_named(enable, enable, bool, 0644); + +static bool debug = false; +module_param_named(debug, debug, bool, 0644); + +static unsigned int decay = 80; +module_param_named(decay, decay, uint, 0644); + +static DEFINE_PER_CPU(struct cpufreq_bouncing*, cbs); + +static int cb_pol_idx = 0; + +static bool cb_switch = false; + +static inline struct cpufreq_bouncing* cb_get(int cpu) +{ + if (likely(cb_switch)) + return per_cpu(cbs, cpu); + + return NULL; +} + +/* module parameters */ +static int cb_config_store(const char *buf, const struct kernel_param *kp) +{ + /* + for limit_thres, down/up limit will use ms as unit + format: + echo "2,0,5,3000,3,2000,3,2000" > config + */ + struct pack { + int clus; + bool enable; + + int limit_level; + u64 limit_thres_ms; + + int down_speed; + s64 down_limit_ms; + + int up_speed; + s64 up_limit_ms; + } v; + struct cpufreq_bouncing *cb; + + if (debug) + pr_info("%s\n", buf); + + if (sscanf(buf, "%d,%d,%d,%llu,%d,%lld,%d,%lld\n", + &v.clus, + &v.enable, + &v.limit_level, + &v.limit_thres_ms, + &v.down_speed, + &v.down_limit_ms, + &v.up_speed, + &v.up_limit_ms) != 8) + goto out; + + if (v.clus < 0 || v.clus >= cb_pol_idx) + goto out; + + cb = &cb_stuff[v.clus]; + + if (v.limit_level < 0 || v.limit_level > cb->max_level) + goto out; + + if (v.down_speed < 0 || v.down_speed > cb->freq_levels) + goto out; + + if (v.up_speed < 0 || v.up_speed > cb->freq_levels) + goto out; + + if (v.down_limit_ms < 0 || v.up_limit_ms < 0 || + v.limit_thres_ms < 0) + goto out; + + /* begin update config */ + cb->enable = false; + cb->last_ts = 0; + cb->last_freq_update_ts = 0; + cb->acc = 0; + cb->limit_level = v.limit_level; + if (cb->freqs) + cb->limit_freq = cb->freqs[cb->limit_level]; + cb->limit_thres = MSEC_TO_NSEC(v.limit_thres_ms); + cb->down_speed = v.down_speed; + cb->down_limit_ns = MSEC_TO_NSEC(v.down_limit_ms); + cb->up_speed = v.up_speed; + cb->up_limit_ns = MSEC_TO_NSEC(v.up_limit_ms); + cb->enable = v.enable; + + return 0; +out: + pr_warn("config: invalid:%s\n", buf); + return 0; +} + +static struct kernel_param_ops cb_config_ops = { + .set = cb_config_store, +}; +module_param_cb(config, &cb_config_ops, NULL, 0220); + +static int cb_dump_show(char *buf, const struct kernel_param *kp) +{ + struct cpufreq_bouncing *cb; + int i, j, cnt = 0; + + for (i = 0; i < min(CLUS_MAX, cb_pol_idx); ++i) { + cb = &cb_stuff[i]; + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "=====\n"); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "clus %d (switch %d)\n", i, cb_switch); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_ts: %llu\n", cb->last_ts); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_freq_update_ts: %llu\n", cb->last_freq_update_ts); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "acc: %llu\n", cb->acc); + + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_freq: %d\n", cb->limit_freq); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_level: %d\n", cb->limit_level); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_thres: %llu\n", cb->limit_thres); + + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "enable: %d\n", cb->enable); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "cur_level: %d\n", cb->cur_level); + + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "max_freq: %u %d\n", cb->max_freq, cb->max_level); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_speed: %d\n", cb->down_speed); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_limit_ns: %lld\n", cb->down_limit_ns); + + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "min_freq: %u %d\n", cb->min_freq, cb->min_level); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_speed: %d\n", cb->up_speed); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_limit_ns: %lld\n", cb->up_limit_ns); + + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_sorting: %d\n", cb->freq_sorting); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_levels: %d\n", cb->freq_levels); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq idx:"); + for (j = 0; j < cb->freq_levels; ++j) + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", j); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq clk:"); + for (j = 0; j < cb->freq_levels; ++j) + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", cb->freqs[j]); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); + } + + return cnt; +} + +static struct kernel_param_ops cb_dump_ops = { + .get = cb_dump_show, +}; +module_param_cb(dump, &cb_dump_ops, NULL, 0444); + + +unsigned int cb_get_cap(int cpu) +{ + struct cpufreq_bouncing *cb = cb_get(cpu); + + /* return UINT_MAX as no limited */ + if (unlikely(!cb)) + return UINT_MAX; + + if (unlikely(cb->freqs)) + return UINT_MAX; + + return cb->freqs[cb->cur_level]; +} + +unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) +{ + struct cpufreq_bouncing *cb; + unsigned int capped = freq; + int cpu; + + if (!enable) + return freq; + + /* can remove since we're calling from sugov_fast_switch path */ +// if (!pol->fast_switch_enabled) +// return freq; + + cpu = cpumask_first(pol->related_cpus); + cb = cb_get(cpu); + + if (unlikely(!cb)) + return freq; + + if (!cb->enable) + return freq; + + /* don't have quick mapping */ + if (unlikely(!cb->freqs)) + return freq; + + capped = min(freq, cb->freqs[cb->cur_level]); + + if (debug) + pr_info("cpu %d, orig %u, capped %d\n", cpu, freq, capped); + + return capped; +} + +static inline bool clus_isolated(int cpu) +{ + struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); + int i; + + if (unlikely(!pol)) + return true; + + for_each_cpu(i, pol->related_cpus) + if (!cpu_isolated(i)) + return false; + + return true; +} + +void cb_reset(int cpu, u64 time) +{ + struct cpufreq_bouncing *cb; + + if (!enable) + return; + + cb = cb_get(cpu); + if (!cb) + return; + + /* reset only when cluster has no active cpu */ + if (!clus_isolated(cpu)) + return; + + cb->last_ts = time; + cb->last_freq_update_ts = time; + cb->acc = 0; + cb->cur_level = cb->max_level; +} + +void cb_update(struct cpufreq_policy *pol, u64 time) +{ + // TODO can skip a bit? + struct cpufreq_bouncing *cb; + u64 delta, update_delta; + int cpu; + + if (!enable) + return; + + cpu = cpumask_first(pol->related_cpus); + cb = cb_get(cpu); + + if (unlikely(!cb)) + return; + + if (!cb->enable) + return; + +// if (unlikely(!pol->fast_switch_enabled)) +// return; + + /* for first update */ + if (unlikely(!cb->last_ts)) + cb->last_ts = cb->last_freq_update_ts = time; + + // FIXME check overflow + // NOTE pol->cur should always updated + delta = time - cb->last_ts; + update_delta = time - cb->last_freq_update_ts; + + /* check cpufreq */ + if (pol->cur >= cb->limit_freq) { + /* accumulate delta time */ + cb->acc += delta; + } else { + /* decay accumulate time */ + cb->acc = cb->acc * decay / 100;; + } + + /* check if need to update limitation */ + if (cb->acc >= cb->limit_thres) { + /* check last update */ + if (update_delta >= cb->down_limit_ns) { + cb->cur_level -= cb->down_speed; + cb->last_freq_update_ts = time; + } + } else { + /* check last update */ + if (update_delta >= cb->up_limit_ns) { + cb->cur_level += cb->up_speed; + cb->last_freq_update_ts = time; + } + } + + /* cap level */ + cb->cur_level = max(cb->limit_level, min(cb->cur_level, cb->max_level)); + + if (debug) + pr_info("cpu %d update: ts now %llu last %llu last_update %llu delta %llu update_d %llu cur %u acc %llu, cur_level %d\n", + cpu, + NSEC_TO_MSEC(time), + NSEC_TO_MSEC(cb->last_ts), + NSEC_TO_MSEC(cb->last_freq_update_ts), + NSEC_TO_MSEC(delta), + NSEC_TO_MSEC(update_delta), + pol->cur, + NSEC_TO_MSEC(cb->acc), + cb->cur_level); + + cb->last_ts = time; +} + +static int __cpufreq_policy_parser(int cpu, int cb_idx) +{ + struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); + struct cpufreq_frequency_table *table, *pos; + struct cpufreq_bouncing* cb; + + unsigned int freq = 0, max_freq = 0, min_freq = UINT_MAX; + int idx, freq_levels = 0; + + if (unlikely(!pol)) { + pr_err("cpu %d can't find realted cpufreq policy\n", cpu); + return -1; + } + + if (cb_idx >= CLUS_MAX) { + pr_err("clus %d out of limit\n", cb_idx); + return -1; + } + + cb = &cb_stuff[cb_idx]; + table = pol->freq_table; + cb->freq_sorting = pol->freq_table_sorted; + /* get freq_levels */ + cpufreq_for_each_valid_entry(pos, table) { + ++freq_levels; + freq = pos->frequency; + if (freq > max_freq) { + max_freq = freq; + cb->max_level = freq_levels; + cb->cur_level = freq_levels; + cb->max_freq = max_freq; + } + if (freq < min_freq) { + min_freq = freq; + cb->min_level = freq_levels; + cb->min_freq = min_freq; + } + } + + cb->freq_levels = freq_levels; + cb->freqs = (unsigned int *) kmalloc(sizeof(unsigned int) * freq_levels, GFP_KERNEL); + if (cb->freqs) { + /* setup freqs */ + idx = 0; + table = pol->freq_table; + cpufreq_for_each_valid_entry(pos, table) { + freq = pos->frequency; + cb->freqs[idx] = freq; + ++idx; + } + } else { + pr_err("can't alloc memory for freqs\n"); + cb->enable = false; + } + return cpu + cpumask_weight(pol->related_cpus); +} + +static void cb_parse_cpufreq(void) +{ + int i = 0, j, prev = 0; + bool valid = true; + + while (i != nr_cpu_ids && cb_pol_idx != CLUS_MAX) { + i = __cpufreq_policy_parser(i, cb_pol_idx); + if (i < 0) + break; + for (j = prev; j < i; ++j) + per_cpu(cbs, j) = &cb_stuff[cb_pol_idx]; + prev = i; + ++cb_pol_idx; + } + + for (i = 0; i < nr_cpu_ids && valid; ++i) { + if (!per_cpu(cbs, i)) { + pr_warn("break on cpu%d\n", i); + valid = false; + } + } + + cb_switch = valid; + smp_wmb(); +} + +static void cb_clean_up(void) +{ + struct cpufreq_bouncing *cb; + int i; + enable = false; + smp_wmb(); + + for (i = 0; i < CLUS_MAX; ++i) { + cb = &cb_stuff[i]; + if (cb->freqs) { + kfree(cb->freqs); + cb->freqs = NULL; + } + } +} + +static int __init cb_init(void) +{ + pr_info("cpufreq bouncing init\n"); + cb_parse_cpufreq(); + return 0; +} + +static void __exit cb_exit(void) +{ + cb_clean_up(); + pr_info("cpufreq bouncing exit\n"); +} +device_initcall(cb_init); + diff --git a/drivers/oneplus/coretech/houston/Makefile b/drivers/oneplus/coretech/houston/Makefile new file mode 100644 index 000000000000..6235862bf602 --- /dev/null +++ b/drivers/oneplus/coretech/houston/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HOUSTON) += houston.o diff --git a/drivers/oneplus/coretech/houston/houston.c b/drivers/oneplus/coretech/houston/houston.c new file mode 100644 index 000000000000..4ec7e0b6e6d0 --- /dev/null +++ b/drivers/oneplus/coretech/houston/houston.c @@ -0,0 +1,653 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "houston.h" +#include "../drivers/gpu/msm/kgsl.h" +#include "../drivers/gpu/msm/kgsl_pwrctrl.h" +#include + +#define HT_POLLING_MIN_INTERVAL (100) +#define HT_REPORT_PERIOD_MAX (18000) + +/* customize your monitor */ +enum { + HT_CLUS_FREQ_0_MIN, + HT_CLUS_FREQ_0_CUR, + HT_CLUS_FREQ_0_MAX, + HT_CLUS_FREQ_1_MIN, + HT_CLUS_FREQ_1_CUR, + HT_CLUS_FREQ_1_MAX, + HT_BAT_VOLT_NOW, + HT_BAT_CURR_NOW, + HT_CPU_0, + HT_CPU_1, + HT_CPU_2, + HT_CPU_3, + HT_CPU_4, + HT_CPU_5, + HT_CPU_6, + HT_CPU_7, + HT_SEN_0, + HT_SEN_1, + HT_SEN_2, + HT_FPS_PROCESS, + HT_FPS_LAYER, + HT_FPS_PID, + HT_FPS_ALIGN, + HT_FPS_1, + HT_FPS_2, + HT_FPS_3, + HT_FPS_4, + HT_FPS_5, + HT_FPS_6, + HT_FPS_7, + HT_FPS_8, + HT_FPS_MISS_LAYER, + HT_MONITOR_SIZE +}; + +const char *ht_monitor_case[HT_MONITOR_SIZE] = { + "clus_0_min", + "clus_0_cur", + "clus_0_max", + "clus_1_min", + "clus_1_cur", + "clus_1_max", + "voltage_now", + "current_now", + "cpu0-silver-usr", + "cpu1-silver-usr", + "cpu2-silver-usr", + "cpu3-silver-usr", + "cpu0-gold-usr", + "cpu1-gold-usr", + "cpu2-gold-usr", + "cpu3-gold-usr", + "xo-therm-adc", + "msm-therm-adc", + "quiet-therm-adc", + "process name", + "layer name", + "pid", + "fps_align", + "actualFps", + "predictFps", + "Vsync", + "gameFps", + "NULL", + "NULL", + "NULL", + "NULL", + "missedLayer" +}; + +/* + * houston monitor + * @data: sample data + * @layer: sample data for frame info + * @process: sample data for frame process info + */ +struct sample_data { + u64 data[MAX_REPORT_PERIOD][HT_MONITOR_SIZE]; + char layer[MAX_REPORT_PERIOD][FPS_LAYER_LEN]; + char process[MAX_REPORT_PERIOD][FPS_PROCESS_NAME_LEN]; +}; + +struct ht_monitor { + struct power_supply *psy; + struct thermal_zone_device *tzd[HT_MONITOR_SIZE]; + struct task_struct *thread; + struct sample_data *buf; +} monitor = { + .psy = NULL, + .thread = NULL, + .buf = NULL, +}; + +static struct game_fps_data_struct +{ + u64 fps; + u64 enqueue_time; +} game_fps_data; + +static atomic_t cached_fps[2]; + +static atomic64_t fps_align_ns; + +static struct kgsl_pwrctrl *gpwr; + +static unsigned int ht_all_mask; + +static unsigned int report_period = 600; +module_param_named(period, report_period, uint, 0644); + +static unsigned int filter_mask = 0; +module_param_named(filter_mask, filter_mask, uint, 0644); + +static unsigned int disable_mask = 0; +module_param_named(disable_mask, disable_mask, uint, 0644); + +/* div should not be 0, check before assign */ +static unsigned int report_div[HT_MONITOR_SIZE]; +module_param_array_named(div, report_div, uint, NULL, 0644); + +static unsigned int sample_period = 3000; + +static int ht_sample_period_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int val; + + if (sscanf(buf, "%u\n", &val) <= 0) + return -EINVAL; + + if (val < HT_POLLING_MIN_INTERVAL) { + pr_err("Not allowed to polling too fast\n"); + return -EINVAL; + } + + sample_period = val; + + return 0; +} + +static int ht_sample_period_show(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sample_period); +} + +static struct kernel_param_ops ht_sample_period_ops = { + .set = ht_sample_period_store, + .get = ht_sample_period_show, +}; +module_param_cb(sample_period, &ht_sample_period_ops, NULL, 0644); + +/* fps */ +static int game_fps_pid = -1; +module_param_named(game_fps_pid, game_fps_pid, int, 0664); + +/* NOTE better not use this directly */ +int sidx; +int smps[HT_REPORT_PERIOD_MAX][HT_MONITOR_SIZE]; // samples + +/* TODO set ht_tzd_idx to atomic */ +static int ht_tzd_idx = HT_CPU_0; +static bool __read_mostly keep_alive = false; +static bool __read_mostly ht_reporting = false; + +struct track { + struct thermal_zone_device *tzd; + int slope; + int offset; + bool disabled; +}; + +struct ht_rocket { + struct power_supply *psy; + struct track trk[HT_MONITOR_SIZE]; + struct task_struct *monitor_thread; + struct task_struct *stable_thread; + bool running; +} rocket; + +static inline int ht_next_sample_idx(void) +{ + ++sidx; + sidx %= HT_REPORT_PERIOD_MAX; + return sidx; +} + +static inline void ht_set_all_mask(void) +{ + int i; + for (i = 0; i < HT_MONITOR_SIZE; ++i) + ht_all_mask |= (1 << i); +} + +static inline bool ht_is_all_disabled(unsigned int mask) +{ + return ht_all_mask == mask; +} + +static inline bool ht_is_all_filter(unsigned int mask) +{ + return ht_all_mask == mask; +} + +static inline int ht_mapping_tags(char *name) +{ + int i; + + for (i = 0; i < HT_MONITOR_SIZE; ++i) + if (!strcmp(name, ht_monitor_case[i])) + return i; + return HT_MONITOR_SIZE; +} + +void ht_register_thermal_zone_device(struct thermal_zone_device *tzd) +{ + int idx; + + /* tzd is guaranteed has value */ + pr_info("tzd: %s id: %d\n", tzd->type, tzd->id); + + idx = ht_mapping_tags(tzd->type); + + if (idx == HT_MONITOR_SIZE) + return; + + if (ht_tzd_idx < HT_MONITOR_SIZE) { + ++ht_tzd_idx; + rocket.trk[idx].tzd = tzd; + } +} + +void ht_register_power_supply(struct power_supply *psy) +{ + if (!psy) + return; + + pr_info("ht power supply list %s\n", psy->desc->name); + if (!strcmp(psy->desc->name, "battery")) { + rocket.psy = psy; + pr_info("ht power supply registed %s\n", psy->desc->name); + } +} + +void ht_register_kgsl_pwrctrl(void *pwr) +{ + gpwr = (struct kgsl_pwrctrl *) pwr; + pr_info("Registered lgsl pwrctrl\n"); +} + +static int ht_fps_data_sync_store(const char *buf, const struct kernel_param *kp) +{ + u64 fps_data[FPS_COLS] = {0}; + static long long fps_align; + static int cur_idx; + int ret; + + if (strlen(buf) >= FPS_DATA_BUF_SIZE) + return 0; + + ret = sscanf(buf, "%llu,%llu,%llu,%llu,%llu,%lld\n", + &fps_data[0], &fps_data[1], &fps_data[2], &fps_data[3], + &fps_data[4], &fps_align); + if (ret != 6) { + pr_err("fps data params invalid. got %d inputs instead of %d,%s has been ignored\n", + (FPS_COLS+1), ret, buf); + return 0; + } + + game_fps_data.enqueue_time = fps_align; + game_fps_data.fps = fps_data[3]; + + pr_info("fps data params: %llu %llu %llu %llu %llu %lld\n", + fps_data[0], fps_data[1], fps_data[2], + fps_data[3], fps_data[4], fps_align); + + atomic_set(&cached_fps[0], (fps_data[7])); + atomic_set(&cached_fps[1], ((fps_data[1] + 5)/10)); + atomic64_set(&fps_align_ns, fps_align); + + if (!monitor.buf) + return 0; + + if (cur_idx != sidx) { + cur_idx = sidx; + monitor.buf->layer[cur_idx][0] = '\0'; + monitor.buf->process[cur_idx][0] = '\0'; + monitor.buf->data[cur_idx][HT_FPS_1] = 0; + monitor.buf->data[cur_idx][HT_FPS_2] = 0; + monitor.buf->data[cur_idx][HT_FPS_3] = 0; + monitor.buf->data[cur_idx][HT_FPS_4] = 0; + monitor.buf->data[cur_idx][HT_FPS_5] = 0; + monitor.buf->data[cur_idx][HT_FPS_6] = 0; + monitor.buf->data[cur_idx][HT_FPS_7] = 0; + monitor.buf->data[cur_idx][HT_FPS_8] = 0; + monitor.buf->data[cur_idx][HT_FPS_MISS_LAYER] = 0; + } else if (monitor.buf->layer[cur_idx]) { + monitor.buf->data[cur_idx][HT_FPS_MISS_LAYER] += 1; + return 0; + } + + monitor.buf->data[cur_idx][HT_FPS_1] = fps_data[0]; + monitor.buf->data[cur_idx][HT_FPS_2] = fps_data[1]; + monitor.buf->data[cur_idx][HT_FPS_3] = fps_data[2]; + monitor.buf->data[cur_idx][HT_FPS_4] = fps_data[3]; + monitor.buf->data[cur_idx][HT_FPS_PID] = fps_data[4]; + monitor.buf->data[cur_idx][HT_FPS_ALIGN] = fps_align; + + return 0; +} + +static struct kernel_param_ops ht_fps_data_sync_ops = { + .set = ht_fps_data_sync_store, +}; +module_param_cb(fps_data_sync, &ht_fps_data_sync_ops, NULL, 0220); + +static int get_gpu_percentage(void) +{ + struct kgsl_clk_stats *stats = NULL; + + if (gpwr) { + stats = &gpwr->clk_stats; + return stats->total_old != 0 ? (stats->busy_old * 100 / stats->total_old) : 0; + } + return 0; +} + +static int game_info_show(char *buf, const struct kernel_param *kp) +{ + int gpu, cpu, fps; + static u64 last_enqueue_time; + + if (game_fps_pid == -1) { + gpu = -1; + cpu = -1; + fps = -1; + } else { + cpu = ohm_get_cur_cpuload(true); + gpu = get_gpu_percentage(); + fps = game_fps_data.fps / 10; + + if (fps < 0 || fps > 150) { + pr_err("fps: %d is out of range", fps); + fps = 0; + } + if (game_fps_data.enqueue_time == last_enqueue_time) { + pr_err("Did not update the fps data"); + fps = 0; + } + last_enqueue_time = game_fps_data.enqueue_time; + } + return snprintf(buf, PAGE_SIZE, "%d %d %d\n", gpu, cpu, fps); +} + +static struct kernel_param_ops game_info_ops = { + .get = game_info_show, +}; +module_param_cb(game_info, &game_info_ops, NULL, 0664); + +#define SILVER_CLUS_IDX 0 +#define GOLDEN_CLUS_IDX 4 +static void ht_collect_resources(void) +{ + int temp, i, idx = ht_next_sample_idx(), ret; + unsigned int d_mask = disable_mask; + struct thermal_zone_device* tzd; + struct cpufreq_policy *pol_0, *pol_1, *pol; + union power_supply_propval prop = {0, }; + + if (ht_is_all_disabled(d_mask)) + return; + + /* temperature part */ + for (i = HT_CPU_0; i < ht_tzd_idx; ++i) { + if (d_mask & (1 << i)) { + smps[idx][i] = 0; + continue; + } + + temp = -1; + tzd = rocket.trk[i].tzd; + if (likely(tzd)) { + ret = thermal_zone_get_temp(tzd, &temp); + if (ret) { + if (ret != -EAGAIN) + pr_debug("failed to read out thermal zone with idx %d\n", i); + temp = -1; + } + } + smps[idx][i] = temp; + } + + /* cpufreq part */ + pol_0 = cpufreq_cpu_get(SILVER_CLUS_IDX); + pol_1 = cpufreq_cpu_get(GOLDEN_CLUS_IDX); + for (i = 0; i < HT_BAT_VOLT_NOW; ++i) { + if (d_mask & (1 << i)) { + smps[idx][i] = 0; + continue; + } + + pol = (i < HT_CLUS_FREQ_1_MIN)? pol_0: pol_1; + if (likely(pol)) { + switch(i) { + case HT_CLUS_FREQ_0_MIN: + case HT_CLUS_FREQ_1_MIN: + smps[idx][i] = pol->min; break; + case HT_CLUS_FREQ_0_CUR: + case HT_CLUS_FREQ_1_CUR: + smps[idx][i] = pol->cur; break; + case HT_CLUS_FREQ_0_MAX: + case HT_CLUS_FREQ_1_MAX: + smps[idx][i] = pol->max; break; + default: + smps[idx][i] = 0; + } + } else { + pr_debug("failed to read cpufreq with idx %d\n", i); + smps[idx][i] = 0; + } + } + if (pol_0) cpufreq_cpu_put(pol_0); + if (pol_1) cpufreq_cpu_put(pol_1); + + /* battery part */ + ret = power_supply_get_property(rocket.psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + smps[idx][HT_BAT_VOLT_NOW] = ret >= 0? prop.intval: 0; + ret = power_supply_get_property(rocket.psy, POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + smps[idx][HT_BAT_CURR_NOW] = ret >= 0? prop.intval: 0; +} + +static inline void ht_wake_up(void) +{ + wake_up_process(rocket.monitor_thread); + wake_up_process(rocket.stable_thread); +} + +/* main thread for monitor */ +static int ht_monitor(void *arg) +{ + bool not_stable = false; + while (likely(keep_alive)) { + /* reset for every time evaluation */ + not_stable = false; + + if (!rocket.running) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + continue; + } + + if (likely(!pm_freezing)) + ht_collect_resources(); + + /* check tsens data to make decision */ + if (not_stable) { + pr_err("wakeup stable_thread\n"); + wake_up_process(rocket.stable_thread); + } + msleep(sample_period); + } + + return 0; +} + +/* try to stable something, maybe temperature or ...? */ +static int ht_stable(void *arg) +{ + while (likely(keep_alive)) { + if (!rocket.running) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + continue; + } + + /* TODO put some thoughts please */ + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + } + + return 0; +} + +static int ht_registed_show(char* buf, const struct kernel_param *kp) +{ + int i, offset = 0; + struct thermal_zone_device* tzd; + + if (ht_tzd_idx == 0) + return 0; + + for (i = 0; i < ht_tzd_idx; ++i) { + tzd = rocket.trk[i].tzd; + if (tzd) + offset += snprintf(buf + offset, PAGE_SIZE - offset, "%s, id: %d\n", tzd->type, tzd->id); + else if (i < HT_CPU_0) + offset += snprintf(buf + offset, PAGE_SIZE - offset, "%s\n", ht_monitor_case[i]); + } + + return offset; +} + +static struct kernel_param_ops ht_registed_ops = { + .get = ht_registed_show, +}; +module_param_cb(ht_registed, &ht_registed_ops, NULL, 0644); + +static int ht_reset_show(char *buf, const struct kernel_param *kp) +{ + memset(smps, 0, sizeof(int) * HT_REPORT_PERIOD_MAX * HT_MONITOR_SIZE); + return snprintf(buf, PAGE_SIZE, "sample data reset\n"); +} + +static struct kernel_param_ops ht_reset_ops = { + .get = ht_reset_show, +}; +module_param_cb(reset, &ht_reset_ops, NULL, 0644); + +static int ht_report_proc_show(struct seq_file *m, void *v) +{ + unsigned int i, cnt = 0, j; + unsigned int f_mask = filter_mask, d_mask = disable_mask; + unsigned int report_cnt = 0; + struct thermal_zone_device* tzd; + + report_cnt = report_period; + + /* cap max */ + if (unlikely(report_cnt > HT_REPORT_PERIOD_MAX)) + report_cnt = HT_REPORT_PERIOD_MAX; + + if (!report_cnt) + return 0; + + ht_reporting = true; + + if (ht_is_all_filter(f_mask) || ht_is_all_disabled(d_mask)) + goto out; + + /* mark */ + for (i = 0; i < ht_tzd_idx; ++i) { + if (f_mask & (1 << i) || d_mask & (1 << i)) + continue; + tzd = rocket.trk[i].tzd; + if (tzd) + seq_printf(m, "%s\t", tzd->type); + else if (i < HT_CPU_0) + seq_printf(m, "%s\t", ht_monitor_case[i]); + } + seq_printf(m, "\n"); + + /* fill gap */ + i = sidx + HT_REPORT_PERIOD_MAX - report_cnt + 1; + + /* value */ + for (; cnt < report_cnt; ++cnt, ++i) { + i %= HT_REPORT_PERIOD_MAX; + + for (j = 0; j < HT_MONITOR_SIZE; ++j) { + if (f_mask & (1 << j) || d_mask & (1 << j)) + continue; + + seq_printf(m, "%d\t", smps[i][j]/ (report_div[j]? report_div[j]: 1)); + } + seq_printf(m, "\n"); + } + +out: + ht_reporting = false; + return 0; +} + +static int ht_report_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ht_report_proc_show, NULL); +} + +static const struct file_operations ht_report_proc_fops = { + .open= ht_report_proc_open, + .read= seq_read, + .llseek= seq_lseek, + .release= single_release, +}; + +static int ht_init(void) +{ + int i; + + /* Init cached fps info */ + atomic_set(&cached_fps[0], 60); + atomic_set(&cached_fps[1], 60); + atomic64_set(&fps_align_ns, 0); + + for (i = 0; i < HT_MONITOR_SIZE; ++i) + report_div[i] = (i >= HT_CPU_0)? 100: 1; + + ht_set_all_mask(); + + /* default disabled, enable while you need it */ + disable_mask = ht_all_mask; + + rocket.monitor_thread = kthread_create(ht_monitor, NULL, "ht_rocket"); + if (IS_ERR(rocket.monitor_thread)) { + pr_err("Can't create ht monitor thread\n"); + goto out; + } + + rocket.stable_thread = kthread_create(ht_stable, NULL, "ht_stable"); + if (IS_ERR(rocket.stable_thread)) { + pr_err("Can't create ht stable thread\n"); + wake_up_process(rocket.monitor_thread); + goto out; + } + + keep_alive = true; + rocket.running = true; + ht_wake_up(); + proc_create("ht_report", S_IFREG | 0400, NULL, &ht_report_proc_fops); + + return 0; +out: + return -EINVAL; +} + +pure_initcall(ht_init); +MODULE_AUTHOR("tedlin "); +MODULE_DESCRIPTION("Oneplus Houston"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/oneplus/coretech/houston/houston.h b/drivers/oneplus/coretech/houston/houston.h new file mode 100644 index 000000000000..5ec8d6925daa --- /dev/null +++ b/drivers/oneplus/coretech/houston/houston.h @@ -0,0 +1,17 @@ +#ifndef __INCLUDE_HOUSTON__ +#define __INCLUDE_HOUSTON__ + +#ifndef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +#define MAX_REPORT_PERIOD 18000 + +#define FPS_COLS 5 +#define FPS_LAYER_LEN 128 +#define FPS_PROCESS_NAME_LEN 64 +#define FPS_DATA_BUF_SIZE 256 + +extern int ohm_get_cur_cpuload(bool ctrl); + +#endif diff --git a/drivers/oneplus/coretech/memplus/Makefile b/drivers/oneplus/coretech/memplus/Makefile new file mode 100644 index 000000000000..83b47d521522 --- /dev/null +++ b/drivers/oneplus/coretech/memplus/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MEMPLUS) += memplus_core.o memplus_helper.o diff --git a/drivers/oneplus/coretech/memplus/memplus.h b/drivers/oneplus/coretech/memplus/memplus.h new file mode 100644 index 000000000000..bce7bc299fcd --- /dev/null +++ b/drivers/oneplus/coretech/memplus/memplus.h @@ -0,0 +1,22 @@ +#ifndef _MEMORY_PLUS_H +#define _MEMORY_PLUS_H +#include +#define AID_APP 10000 /* first app user */ + +enum { + RECLAIM_STANDBY, + RECLAIM_QUEUE, + RECLAIM_DONE, + SWAPIN_QUEUE, +}; + +enum { + TYPE_NORMAL, + TYPE_FREQUENT, + TYPE_SYS_IGNORE, + TYPE_WILL_NEED, + TYPE_END +}; +#define MEMPLUS_TYPE_MASK 0x7 + +#endif /* _MEMORY_PLUS_H */ diff --git a/drivers/oneplus/coretech/memplus/memplus_core.c b/drivers/oneplus/coretech/memplus/memplus_core.c new file mode 100644 index 000000000000..cef5eba4f9f4 --- /dev/null +++ b/drivers/oneplus/coretech/memplus/memplus_core.c @@ -0,0 +1,871 @@ +#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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "memplus.h" + +#define PF_NO_TAIL(page, enforce) ({ \ + VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \ + compound_head(page);}) + +#define RD_SIZE 128 +#define DEBUG_TIME_INFO 0 +#define FEAT_RECLAIM_LIMIT 0 +struct reclaim_stat { + unsigned nr_dirty; + unsigned nr_unqueued_dirty; + unsigned nr_congested; + unsigned nr_writeback; + unsigned nr_immediate; + unsigned nr_activate; + unsigned nr_ref_keep; + unsigned nr_unmap_fail; +}; + +struct mp_reclaim_param { + struct vm_area_struct *vma; + /* Number of pages scanned */ + int nr_scanned; +#if FEAT_RECLAIM_LIMIT + unsigned long start_jiffies; +#endif + /* max pages to reclaim */ + int nr_to_reclaim; + /* pages reclaimed */ + int nr_reclaimed; + int type; +}; + +struct reclaim_data { + pid_t pid; + int prev_adj; +}; + +struct reclaim_info { + struct reclaim_data rd[RD_SIZE]; + int i_idx; + int o_idx; + int count; +}; + +struct reclaim_info ri = { {{0}}, 0, 0, 0 }; +struct reclaim_info si = { {{0}}, 0, 0, 0 }; +static struct task_struct *reclaimd_tsk = NULL; +static struct task_struct *swapind_tsk = NULL; +static DEFINE_SPINLOCK(rd_lock); + +/* -1 = system free to use swap, 0 = disable retention, swap not available, 1 = enable retention */ +static int vm_memory_plus __read_mostly = 0; +static unsigned long memplus_add_to_swap = 0; +unsigned long coretech_reclaim_pagelist(struct list_head *page_list, struct vm_area_struct *vma, void *sc); +unsigned long swapout_to_zram(struct list_head *page_list, struct vm_area_struct *vma); +unsigned long swapout_to_disk(struct list_head *page_list, struct vm_area_struct *vma); +extern bool enough_swap_size(unsigned long req_size, int swap_bdv); + +bool ctech_current_is_swapind(void) { + return current == swapind_tsk; +} + +bool ctech_memplus_check_isolate_page(struct page*page) +{ + return (memplus_enabled() && (!PageSwapCache(page) || PageWillneed(page))); +} + +static bool is_fast_entry(swp_entry_t entry) +{ + struct swap_info_struct *p; + bool ret = false; + unsigned long offset, type; + + if (!entry.val) + goto out; + type = swp_type(entry); + p = swap_info[type]; + if (!(p->flags & SWP_USED)) + goto out; + offset = swp_offset(entry); + if (offset >= p->max) + goto out; + + spin_lock(&p->lock); + if (p->flags & SWP_FAST) + ret = true; + spin_unlock(&p->lock); +out: + return ret; +} + +__always_inline void ctech_memplus_move_swapcache_to_anon_lru(struct page *page) +{ + struct zone *zone = page_zone(page); + unsigned long flag; + + if (memplus_enabled()) { + spin_lock_irqsave(zone_lru_lock(zone), flag); + if (PageLRU(page)) { + struct lruvec *lruvec; + enum lru_list lru, oldlru = page_lru(page); + + clear_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); + lru = page_lru(page); + lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat); + list_move(&page->lru, &lruvec->lists[lru]); + update_lru_size(lruvec, oldlru, page_zonenum(page), -hpage_nr_pages(page)); + update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page)); + } else + clear_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); + spin_unlock_irqrestore(zone_lru_lock(zone), flag); + }else + clear_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} + +__always_inline void ctech_memplus_move_anon_to_swapcache_lru(struct page *page) +{ + struct zone *zone = page_zone(page); + unsigned long flag; + + if (memplus_enabled()) { + spin_lock_irqsave(zone_lru_lock(zone), flag); + if (likely(!PageLRU(page))) + set_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); + else { + struct lruvec *lruvec; + enum lru_list lru, oldlru = page_lru(page); + + set_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); + lru = page_lru(page); + lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat); + list_move(&page->lru, &lruvec->lists[lru]); + update_lru_size(lruvec, oldlru, page_zonenum(page), -hpage_nr_pages(page)); + update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page)); + } + spin_unlock_irqrestore(zone_lru_lock(zone), flag); + } else + set_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} +extern int do_swap_page(struct fault_env *fe, pte_t orig_pte); +static int memplus_swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start, + unsigned long end, struct mm_walk *walk) +{ + pte_t *orig_pte; + struct vm_area_struct *vma = walk->private; + unsigned long index; + int ret = 0; + + if (pmd_none_or_trans_huge_or_clear_bad(pmd)) + return 0; + + for (index = start; index != end; index += PAGE_SIZE) { + pte_t pte; + swp_entry_t entry; + struct page *page; + spinlock_t *ptl; + + if (!list_empty(&vma->vm_mm->mmap_sem.wait_list)) + return -1; + + orig_pte = pte_offset_map_lock(vma->vm_mm, pmd, start, &ptl); + pte = *(orig_pte + ((index - start) / PAGE_SIZE)); + pte_unmap_unlock(orig_pte, ptl); + + if (pte_present(pte) || pte_none(pte)) + continue; + entry = pte_to_swp_entry(pte); + if (unlikely(non_swap_entry(entry))) + continue; + + if (is_fast_entry(entry)) { + struct fault_env fe = { + .vma = vma, + .pte = &pte, + .orig_pte = pte, + .address = index, + .flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT, + .pmd = pmd, + .vma_flags = vma->vm_flags, + .vma_page_prot = vma->vm_page_prot, + }; + + ret = do_swap_page(&fe, pte); + if (ret & VM_FAULT_ERROR) { + printk(KERN_ERR "%s: do_swap_page ERROR\n", __func__); + return -1; + } + continue; + } else + page = read_swap_cache_async(entry, GFP_HIGHUSER_MOVABLE, + vma, index); + if (page) + put_page(page); + } + + return 0; +} + +static void enqueue_reclaim_data(pid_t nr, int prev_adj, struct reclaim_info *info) +{ + int idx; + struct task_struct *waken_task; + + waken_task = (info == &ri? reclaimd_tsk : swapind_tsk); + if (!waken_task) + return; + + spin_lock(&rd_lock); + if (info->count < RD_SIZE) { + info->count++; + idx = info->i_idx++ % RD_SIZE; + info->rd[idx].pid = nr; + info->rd[idx].prev_adj = prev_adj; + } + spin_unlock(&rd_lock); + BUG_ON(info->count > RD_SIZE || info->count < 0); + + if (waken_task->state == TASK_INTERRUPTIBLE) + wake_up_process(waken_task); +} + +bool ctech_memplus_enabled(void) +{ + return vm_memory_plus > 0 && total_swap_pages; +} + +__always_inline bool ctech__memplus_enabled(void) +{ + return vm_memory_plus > 0; +} + +static void __memplus_state_check(int cur_adj, int prev_adj, struct task_struct* task) +{ + int uid = task_uid(task).val; + + if (!memplus_enabled()) + return; + + /* special case that SmartBoost toggle disables this feature */ + if (vm_memory_plus == 2) + goto queue_swapin; + + if (task->signal->memplus_type == TYPE_SYS_IGNORE) + return; + + if (task->signal->memplus_type == TYPE_WILL_NEED) + goto queue_swapin; + + if (unlikely((prev_adj == -1) || (cur_adj == prev_adj))) + return; + + if (uid < AID_APP) { + //trace_printk("QUIT-reclaim %s (pid %d) (adj %d -> %d) (uid %d)\n", task->comm, task->pid, prev_adj, cur_adj, uid); + return; + } + + if (cur_adj >= 800 && time_after_eq(jiffies, task->signal->reclaim_timeout)) { + spin_lock(&task->signal->reclaim_state_lock); + /* reclaim should not kick-in within 2 secs */ + task->signal->reclaim_timeout = jiffies + 2*HZ; + + if (task->signal->swapin_should_readahead_m == RECLAIM_STANDBY) { + task->signal->swapin_should_readahead_m = RECLAIM_QUEUE; + //trace_printk("Q-reclaim %s (pid %d) (adj %d -> %d) (uid %d)\n", task->comm, task->pid, prev_adj, cur_adj, uid); + enqueue_reclaim_data(task->pid, prev_adj, &ri); + } + spin_unlock(&task->signal->reclaim_state_lock); + } else if (cur_adj == 0) { +queue_swapin: + spin_lock(&task->signal->reclaim_state_lock); + /* swapin kicked in, don't reclaim within 2 secs */ + task->signal->reclaim_timeout = jiffies + 2*HZ; + + if (task->signal->swapin_should_readahead_m == RECLAIM_QUEUE) { + task->signal->swapin_should_readahead_m = RECLAIM_STANDBY; + } else if (task->signal->swapin_should_readahead_m == RECLAIM_DONE) { + task->signal->swapin_should_readahead_m = SWAPIN_QUEUE; + //trace_printk("Q-swapin %s (pid %d) (adj %d -> %d) (uid %d)\n", task->comm, task->pid, prev_adj, cur_adj, uid); + enqueue_reclaim_data(task->pid, prev_adj, &si); + } + spin_unlock(&task->signal->reclaim_state_lock); + } +} + +void ctech_memplus_state_check(bool legacy, int oom_adj, struct task_struct *task, int type, int update) +{ + int oldadj = task->signal->oom_score_adj; + + if (update) { + if (type >= TYPE_END || type < 0) + return; + task->signal->memplus_type = type; + + if (type == TYPE_WILL_NEED) + oom_adj = 0; + else if ((type & MEMPLUS_TYPE_MASK) < TYPE_SYS_IGNORE) + oom_adj = oldadj; + else + return; + } + + if (!legacy && (oom_adj >= 800 || oom_adj == 0)) + __memplus_state_check(oom_adj, oldadj, task); +} + +static bool dequeue_reclaim_data(struct reclaim_data *data, struct reclaim_info *info) +{ + int idx; + bool has_data = false; + + spin_lock(&rd_lock); + if (info->count > 0) { + has_data = true; + info->count--; + idx = info->o_idx++ % RD_SIZE; + *data = info->rd[idx]; + } + spin_unlock(&rd_lock); + BUG_ON(info->count > RD_SIZE || info->count < 0); + + return has_data; +} + +static ssize_t swapin_anon(struct task_struct *task, int prev_adj) +{ + struct mm_struct *mm; + struct vm_area_struct *vma; + struct mm_walk walk = {}; + int task_anon = 0, task_swap = 0, err = 0; + //u64 time_ns = 0; +#if DEBUG_TIME_INFO + struct timespec ts1, ts2; + getnstimeofday(&ts1); +#endif + +retry: + /* TODO: do we need to use p = find_lock_task_mm(tsk); in case main thread got killed */ + mm = get_task_mm(task); + if (!mm) + goto out; + + /* system pid may reach its max value and this pid was reused by other process */ + if (unlikely(task->signal->swapin_should_readahead_m != SWAPIN_QUEUE)) { + mmput(mm); + return 0; + } + + task_anon = get_mm_counter(mm, MM_ANONPAGES); + task_swap = get_mm_counter(mm, MM_SWAPENTS); + + /* swapin only for large APP, flip 33000, bow 60000, eightpoll 16000 */ + if (task_swap <= 10000) { + mmput(mm); + //trace_printk("SMALL swapin: this task is too small\n"); + goto out; + } + + walk.mm = mm; + walk.pmd_entry = memplus_swapin_walk_pmd_entry; + + down_read(&mm->mmap_sem); + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (vma->memplus_flags) + continue; + + if (is_vm_hugetlb_page(vma)) + continue; + + if (vma->vm_file) + continue; + + /* if mlocked, don't reclaim it */ + if (vma->vm_flags & VM_LOCKED) + continue; + + walk.private = vma; + err = walk_page_range(vma->vm_start, vma->vm_end, &walk); + if (err == -1) + break; + vma->memplus_flags = 1; + } + + flush_tlb_mm(mm); + up_read(&mm->mmap_sem); + mmput(mm); + if (err) { + err = 0; + //schedule(); + goto retry; + } +out: + /* TODO */ + lru_add_drain(); /* Push any new pages onto the LRU now */ +#if DEBUG_TIME_INFO + getnstimeofday(&ts2); + ts2 = timespec_sub(ts2, ts1); + time_ns = timespec_to_ns(&ts2); +#endif + //trace_printk("%s (pid %d)(size %d-%d) (adj %d -> %d) consumed %llu ms %llu us\n", task->comm, task->pid, task_anon, task_swap, prev_adj, task->signal->oom_score_adj, (time_ns/1000000), (time_ns/1000)%1000); + + spin_lock(&task->signal->reclaim_state_lock); + task->signal->swapin_should_readahead_m = RECLAIM_STANDBY; + spin_unlock(&task->signal->reclaim_state_lock); + + return 0; +} + +//TODO: blk_plug don't seem to work +static int swapind_fn(void *p) +{ + struct reclaim_data data; + struct task_struct *tsk; + + set_freezable(); + for ( ; ; ) { + while (!pm_freezing && dequeue_reclaim_data(&data, &si)) { + rcu_read_lock(); + tsk = find_task_by_vpid(data.pid); + + /* KTHREAD is almost impossible to hit this */ + //if (tsk->flags & PF_KTHREAD) { + // rcu_read_unlock(); + // continue; + //} + + if (!tsk) { + rcu_read_unlock(); + continue; + } + + get_task_struct(tsk); + rcu_read_unlock(); + + swapin_anon(tsk, data.prev_adj); + put_task_struct(tsk); + } + + set_current_state(TASK_INTERRUPTIBLE); + freezable_schedule(); + + if (kthread_should_stop()) + break; + } + + return 0; +} + +static int memplus_reclaim_pte(pmd_t *pmd, unsigned long addr, + unsigned long end, struct mm_walk *walk) +{ + struct mp_reclaim_param *rp = walk->private; + struct vm_area_struct *vma = rp->vma; + pte_t *pte, ptent; + spinlock_t *ptl; + struct page *page; + LIST_HEAD(page_list); + int isolated; + int reclaimed = 0; + int reclaim_type = rp->type; + + split_huge_pmd(vma, addr, pmd); + if (pmd_trans_unstable(pmd) || !rp->nr_to_reclaim) + return 0; +cont: + isolated = 0; + pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); + for (; addr != end; pte++, addr += PAGE_SIZE) { + ptent = *pte; + if (!pte_present(ptent)) + continue; + + page = vm_normal_page(vma, addr, ptent); + if (!page) + continue; + + ClearPageWillneed(page); + + if ((reclaim_type == TYPE_NORMAL) && PageSwapCache(page)) + continue; + + /* About 11% of pages have more than 1 map_count + * only take care mapcount == 1 is good enough */ + if (page_mapcount(page) != 1) + continue; + + if (isolate_lru_page(page)) + continue; + + if (PageAnon(page) && !PageSwapBacked(page)) { + putback_lru_page(page); + continue; + } + + list_add(&page->lru, &page_list); + inc_node_page_state(page, NR_ISOLATED_ANON + + page_is_file_cache(page)); + isolated++; + rp->nr_scanned++; + + if ((isolated >= SWAP_CLUSTER_MAX) || !rp->nr_to_reclaim) + break; + } + pte_unmap_unlock(pte - 1, ptl); + + memplus_add_to_swap += isolated; + + if (reclaim_type == TYPE_NORMAL) + reclaimed = swapout_to_disk(&page_list, vma); + else if (reclaim_type == TYPE_FREQUENT) + reclaimed = swapout_to_zram(&page_list, vma); + + rp->nr_reclaimed += reclaimed; + rp->nr_to_reclaim -= reclaimed; + if (rp->nr_to_reclaim < 0) + rp->nr_to_reclaim = 0; + +#if FEAT_RECLAIM_LIMIT + /* TODO: early quit */ + /* timeout (range from 10~20ms), emergency quit back to reclaim_anon() */ + /* statistics shows 90% of reclaim finish within 60ms, should be a good timeout value */ + /* statistics shows 80% of reclaim finish within 26ms, should be a good timeout value */ + /* statistics shows 77% of reclaim finish within 20ms, should be a good timeout value */ + /* statistics shows 68% of reclaim finish within 10ms, should be a good timeout value */ + //if (time_after_eq(jiffies, rp->start_jiffies + 2)) { + // rp->nr_to_reclaim = 0; + // return 1; + //} + + /* this will make black screen shorter */ + //if (rp->nr_reclaimed > 2000) { + // rp->nr_to_reclaim = 0; + // return 1; + //} +#endif + + if (rp->nr_to_reclaim && (addr != end)) + goto cont; + + /* TODO: is there other reschedule point we can add */ + cond_resched(); + + return 0; +} + +/* get_task_struct before using this function */ +static ssize_t reclaim_anon(struct task_struct *task, int prev_adj) +{ + struct mm_struct *mm; + struct vm_area_struct *vma; + struct mm_walk reclaim_walk = {}; + struct mp_reclaim_param rp; + int task_anon = 0, task_swap = 0; + int a_task_anon = 0, a_task_swap = 0; + + //u64 time_ns = 0; +#if DEBUG_TIME_INFO + struct timespec ts1, ts2; + getnstimeofday(&ts1); +#endif +#if FEAT_RECLAIM_LIMIT + rp.start_jiffies = jiffies; +#endif + + spin_lock(&task->signal->reclaim_state_lock); + + /*TODO: additional handle for PF_EXITING do_exit()->exit_signal()*/ + if (task->signal->swapin_should_readahead_m != RECLAIM_QUEUE) { + //trace_printk("EXIT reclaim: this task is either (reclaimed) or (adj 0 swapin)\n"); + spin_unlock(&task->signal->reclaim_state_lock); + goto out; + } + task->signal->swapin_should_readahead_m = RECLAIM_DONE; + spin_unlock(&task->signal->reclaim_state_lock); + + /* if available swap is less than 4MB, quit early */ + if (!enough_swap_size(1024, task->signal->memplus_type)) + goto out; + + /* TODO: do we need to use p = find_lock_task_mm(tsk); in case main thread got killed */ + mm = get_task_mm(task); + if (!mm) + goto out; + + task_anon = get_mm_counter(mm, MM_ANONPAGES); + task_swap = get_mm_counter(mm, MM_SWAPENTS); + + reclaim_walk.mm = mm; + reclaim_walk.pmd_entry = memplus_reclaim_pte; + + rp.nr_to_reclaim = INT_MAX; + rp.nr_reclaimed = 0; + rp.nr_scanned = 0; + + /* if app is larger than 200MB, override its property to frequent */ + if (task_anon + task_swap > 51200) { + rp.type = TYPE_FREQUENT; + } else + rp.type = task->signal->memplus_type; + + reclaim_walk.private = &rp; + + down_read(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (is_vm_hugetlb_page(vma)) + continue; + + if (vma->vm_file) + continue; + + /* if mlocked, don't reclaim it */ + if (vma->vm_flags & VM_LOCKED) + continue; + + if (task->signal->swapin_should_readahead_m != RECLAIM_DONE) + break; + + rp.vma = vma; + walk_page_range(vma->vm_start, vma->vm_end, + &reclaim_walk); + + vma->memplus_flags = 0; + if (!rp.nr_to_reclaim) + break; + } + + flush_tlb_mm(mm); + up_read(&mm->mmap_sem); + a_task_anon = get_mm_counter(mm, MM_ANONPAGES); + a_task_swap = get_mm_counter(mm, MM_SWAPENTS); + mmput(mm); +out: +#if DEBUG_TIME_INFO + getnstimeofday(&ts2); + ts2 = timespec_sub(ts2, ts1); + time_ns = timespec_to_ns(&ts2); +#endif + /* it's possible that rp data isn't initialized because mm don't exist */ + //trace_printk("%s (pid %d)(size %d-%d to %d-%d) (adj %d -> %d) reclaimed %d scan %d consumed %llu ms %llu us\n" + // , task->comm, task->pid, task_anon, task_swap, a_task_anon, a_task_swap + // , prev_adj, task->signal->oom_score_adj, rp.nr_reclaimed, rp.nr_scanned + // , (time_ns/1000000), (time_ns/1000)%1000); + + /* TODO : return proper value */ + return 0; +} + +//TODO: should we mark reclaimd/swapind freezable? +static int reclaimd_fn(void *p) +{ + struct reclaim_data data; + struct task_struct *tsk; + + set_freezable(); + for ( ; ; ) { + while (!pm_freezing && dequeue_reclaim_data(&data, &ri)) { + rcu_read_lock(); + tsk = find_task_by_vpid(data.pid); + + /* KTHREAD is almost impossible to hit this */ + //if (tsk->flags & PF_KTHREAD) { + // rcu_read_unlock(); + // continue; + //} + + if (!tsk) { + rcu_read_unlock(); + continue; + } + + get_task_struct(tsk); + rcu_read_unlock(); + + do { + msleep(30); + } while (swapind_tsk && (swapind_tsk->state == TASK_RUNNING)); + + reclaim_anon(tsk, data.prev_adj); + put_task_struct(tsk); + } + + set_current_state(TASK_INTERRUPTIBLE); + freezable_schedule(); + + if (kthread_should_stop()) + break; + } + + return 0; +} + +void memplus_stop(void) +{ + if (reclaimd_tsk) { + kthread_stop(reclaimd_tsk); + reclaimd_tsk = NULL; + } + if (swapind_tsk) { + kthread_stop(swapind_tsk); + swapind_tsk = NULL; + } +} + +static int __init memplus_init(void) +{ + //TODO: priority tuning for reclaimd/swapind + //struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO -1 }; + //struct sched_param param = { .sched_priority = 1 }; + struct memplus_cb_set set; + + reclaimd_tsk = kthread_run(reclaimd_fn, 0, "reclaimd"); + if (IS_ERR(reclaimd_tsk)) { + pr_err("Failed to start reclaimd\n"); + reclaimd_tsk = NULL; + } + + swapind_tsk = kthread_run(swapind_fn, 0, "swapind"); + if (IS_ERR(swapind_tsk)) { + pr_err("Failed to start swapind\n"); + swapind_tsk = NULL; + } else { + //if (sched_setscheduler_nocheck(swapind_tsk, SCHED_FIFO, ¶m)) { + // pr_warn("%s: failed to set SCHED_FIFO\n", __func__); + //} + } + + set.current_is_swapind_cb = ctech_current_is_swapind; + set.memplus_check_isolate_page_cb = ctech_memplus_check_isolate_page; + set.memplus_enabled_cb = ctech_memplus_enabled; + set.memplus_move_anon_to_swapcache_lru_cb = ctech_memplus_move_anon_to_swapcache_lru; + set.memplus_move_swapcache_to_anon_lru_cb = ctech_memplus_move_swapcache_to_anon_lru; + set.memplus_state_check_cb = ctech_memplus_state_check; + set.__memplus_enabled_cb = ctech__memplus_enabled; + + register_cb_set(&set); + return 0; +} + +unsigned long memplus_scan(void) +{ + struct pagevec pvec; + pgoff_t index = 0, indices[PAGEVEC_SIZE]; + int i, iso_count = 0; + struct address_space *spaces; + struct swap_info_struct *sis, *next; + unsigned int total_swapcache = total_swapcache_pages(); + LIST_HEAD(page_list); + + if (!total_swapcache) + return 0; + + spin_lock(&swap_lock); + plist_for_each_entry_safe(sis, next, &swap_avail_head, avail_list) { + spaces = &swapper_spaces[sis->type]; + if (!spaces) + continue; + + index = 0; + pagevec_init(&pvec, 0); + while (pagevec_lookup_entries(&pvec, spaces, index, (pgoff_t)PAGEVEC_SIZE, indices)) { + for (i = 0; i < pagevec_count(&pvec); i++) { + struct page *page = pvec.pages[i]; + + index = indices[i]; + + if (radix_tree_exceptional_entry(page)) { + continue; + } + + if (!PageSwapCache(page)) + continue; + + if (PageWriteback(page)) + continue; + + if (isolate_lru_page(page)) + continue; + + if (PageAnon(page) && !PageSwapBacked(page)) { + putback_lru_page(page); + continue; + } + + ClearPageWillneed(page); + list_add(&page->lru, &page_list); + inc_node_page_state(page, NR_ISOLATED_ANON + + page_is_file_cache(page)); + iso_count ++; + } + pagevec_remove_exceptionals(&pvec); + pagevec_release(&pvec); + index++; + } + } + spin_unlock(&swap_lock); + return coretech_reclaim_pagelist(&page_list, NULL, NULL); +} + +static int memory_plus_test_worstcase_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int val; + unsigned long freed; + + if (sscanf(buf, "%u\n", &val) <= 0) + return -EINVAL; + + if (val == 1) { + freed = memplus_scan(); + pr_info("memory_plus_test_worstcase_store: freed = %ld\n", freed); + } + + return 0; +} + +static struct kernel_param_ops memory_plus_test_worstcase_ops = { + .set = memory_plus_test_worstcase_store, +}; + +module_param_cb(memory_plus_test_worstcase, &memory_plus_test_worstcase_ops, NULL, 0200); + +module_param_named(memory_plus_enabled, vm_memory_plus, uint, S_IRUGO | S_IWUSR); +module_param_named(memplus_add_to_swap, memplus_add_to_swap, ulong, S_IRUGO | S_IWUSR); + +module_init(memplus_init) diff --git a/drivers/oneplus/coretech/memplus/memplus_helper.c b/drivers/oneplus/coretech/memplus/memplus_helper.c new file mode 100644 index 000000000000..886126e9951a --- /dev/null +++ b/drivers/oneplus/coretech/memplus/memplus_helper.c @@ -0,0 +1,61 @@ +#include +#include + +static struct memplus_cb_set cb_set; +#define PF_NO_TAIL(page, enforce) ({ \ + VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \ + compound_head(page); }) + +bool memplus_enabled(void) +{ + if (cb_set.memplus_enabled_cb) + return cb_set.memplus_enabled_cb(); + return false; +} +bool __memplus_enabled(void) +{ + if (cb_set.__memplus_enabled_cb) + return cb_set.__memplus_enabled_cb(); + return false; +} +bool current_is_swapind(void) +{ + if (cb_set.current_is_swapind_cb) + return cb_set.current_is_swapind_cb(); + return false; +} + +void memplus_move_swapcache_to_anon_lru(struct page *page) +{ + if (cb_set.memplus_move_swapcache_to_anon_lru_cb) + cb_set.memplus_move_swapcache_to_anon_lru_cb(page); + else + clear_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} +void memplus_move_anon_to_swapcache_lru(struct page *page) +{ + if (cb_set.memplus_move_anon_to_swapcache_lru_cb) + cb_set.memplus_move_anon_to_swapcache_lru_cb(page); + else + set_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} +void memplus_state_check(bool legacy, int oom_adj, + struct task_struct *task, int type, int update) +{ + if (cb_set.memplus_state_check_cb) + cb_set.memplus_state_check_cb(legacy, + oom_adj, task, type, update); +} +bool memplus_check_isolate_page(struct page *page) +{ + if (cb_set.memplus_check_isolate_page_cb) + return cb_set.memplus_check_isolate_page_cb(page); + return false; +} + +void register_cb_set(struct memplus_cb_set *set) +{ + cb_set = *set; +} + +#undef PF_NO_TAIL diff --git a/drivers/oneplus/coretech/smartboost/Makefile b/drivers/oneplus/coretech/smartboost/Makefile new file mode 100644 index 000000000000..6091ef65fe33 --- /dev/null +++ b/drivers/oneplus/coretech/smartboost/Makefile @@ -0,0 +1 @@ +obj-y += smartboost_helper.o diff --git a/drivers/oneplus/coretech/smartboost/core/Makefile b/drivers/oneplus/coretech/smartboost/core/Makefile new file mode 100644 index 000000000000..a36d13e65523 --- /dev/null +++ b/drivers/oneplus/coretech/smartboost/core/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SMART_BOOST) += smartboost_core.o diff --git a/drivers/oneplus/coretech/smartboost/core/smartboost_core.c b/drivers/oneplus/coretech/smartboost/core/smartboost_core.c new file mode 100644 index 000000000000..8281a4f6059d --- /dev/null +++ b/drivers/oneplus/coretech/smartboost/core/smartboost_core.c @@ -0,0 +1,763 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../fs/proc/internal.h" + +static int sysctl_page_cache_reside_switch; +unsigned long inactive_nr, active_nr; +unsigned long priority_nr[3]; +#define SMART_BOOST_PUTBACK_LRU 2 +#define VMPRESSURE_COUNT 5 +static atomic64_t vmpress[VMPRESSURE_COUNT]; + +static LIST_HEAD(hotcount_prio_list); +static DEFINE_RWLOCK(prio_list_lock); + +struct hotcount_prio_node { + unsigned int hotcount; + uid_t uid; + struct list_head list; +}; + +static unsigned int find_node_uid_prio(struct hotcount_prio_node **node, + struct list_head **prio_pos, + uid_t uid, + unsigned int hotcount) +{ + struct hotcount_prio_node *pos; + unsigned int ret_hotcount = 0; + + read_lock(&prio_list_lock); + list_for_each_entry(pos, &hotcount_prio_list, list) { + + if (*node && *prio_pos) + break; + + if (pos->uid == uid) { + *node = pos; + ret_hotcount = pos->hotcount; + } + if ((!(*prio_pos)) && + (pos->hotcount > hotcount)) + *prio_pos = &pos->list; + } + + if (!(*prio_pos)) + *prio_pos = &hotcount_prio_list; + + read_unlock(&prio_list_lock); + + return ret_hotcount; +} + +static void insert_prio_node(unsigned int new_hotcount, uid_t uid) +{ + struct hotcount_prio_node *node = NULL; + struct list_head *prio_pos = NULL; + unsigned int old_hotcount; + + old_hotcount = find_node_uid_prio(&node, &prio_pos, uid, new_hotcount); + + if (node) { + if (old_hotcount == new_hotcount) + return; + + write_lock(&prio_list_lock); + + if (&node->list == prio_pos) { + node->hotcount = new_hotcount; + goto unlock; + } + list_del(&node->list); + } else { + node = (struct hotcount_prio_node *) + kmalloc(sizeof(struct hotcount_prio_node), GFP_KERNEL); + if (!node) { + pr_err("no memory to insert prio_node!\n"); + return; + } + node->uid = uid; + write_lock(&prio_list_lock); + } + + node->hotcount = new_hotcount; + list_add_tail(&node->list, prio_pos); +unlock: + write_unlock(&prio_list_lock); +} + +static void delete_prio_node(uid_t uid) +{ + struct hotcount_prio_node *pos; + int found = 0; + + read_lock(&prio_list_lock); + list_for_each_entry(pos, &hotcount_prio_list, list) + if (pos->uid == uid) { + found = 1; + break; + } + read_unlock(&prio_list_lock); + + if (found) { + write_lock(&prio_list_lock); + list_del(&pos->list); + write_unlock(&prio_list_lock); + kfree(pos); + } +} + +static void print_prio_chain(struct seq_file *m) +{ + struct hotcount_prio_node *pos; + + read_lock(&prio_list_lock); + list_for_each_entry(pos, &hotcount_prio_list, list) + seq_printf(m, "%d(%d)\t", pos->uid, pos->hotcount); + read_unlock(&prio_list_lock); + + seq_putc(m, '\n'); +} + +#define UID_HASH_ORDER 5 +#define uid_hashfn(nr) hash_long((unsigned long)nr, UID_HASH_ORDER) + +static struct uid_node **alloc_uid_hash_table(void) +{ + struct uid_node **hash_table; + int size = (1 << UID_HASH_ORDER) * sizeof(struct uid_node *); + + if (size <= PAGE_SIZE) + hash_table = kzalloc(size, GFP_ATOMIC); + else + hash_table = (struct uid_node **)__get_free_pages( + GFP_ATOMIC | __GFP_ZERO, get_order(size)); + if (!hash_table) + return NULL; + return hash_table; +} + +static struct uid_node *alloc_uid_node(uid_t uid) +{ + struct uid_node *uid_nd; + + uid_nd = kzalloc(sizeof(struct uid_node), GFP_ATOMIC); + if (!uid_nd) + return NULL; + uid_nd->uid = uid; + uid_nd->hot_count = 0; /* initialize a new UID's count */ + uid_nd->next = NULL; + INIT_LIST_HEAD(&uid_nd->page_cache_list); + return uid_nd; +} + +static struct uid_node *insert_uid_node(struct uid_node **hash_table, uid_t uid) +{ + struct uid_node *puid; + unsigned int index, sise = 1 << UID_HASH_ORDER; + + index = uid_hashfn((unsigned long)uid); + if (index >= sise) + return NULL; + puid = alloc_uid_node(uid); + + if (!puid) + return NULL; + + rcu_assign_pointer(puid->next, hash_table[index]); + rcu_assign_pointer(hash_table[index], puid); + return puid; +} + +static struct uid_node *find_uid_node(uid_t uid, struct lruvec *lruvec) +{ + struct uid_node *uid_nd, *ret = NULL; + unsigned int index; + + index = uid_hashfn((unsigned int)uid); + + if (lruvec->uid_hash == NULL) + return NULL; + if (index >= (1 << UID_HASH_ORDER)) + return NULL; + for (uid_nd = rcu_dereference(lruvec->uid_hash[index]); + uid_nd != NULL; uid_nd = rcu_dereference(uid_nd->next)) { + if (uid_nd->uid == uid) { + ret = uid_nd; + break; + } + } + return ret; +} + +void free_hash_table(struct lruvec *lruvec) +{ + int i, table_num; + struct uid_node *puid, **np; + + table_num = 1 << UID_HASH_ORDER; + for (i = 0; i < table_num; i++) { + np = &lruvec->uid_hash[i]; + while ((puid = rcu_dereference(*np)) != NULL) { + rcu_assign_pointer(*np, rcu_dereference(puid->next)); + kfree_rcu(puid, rcu); + } + } +} + +__always_inline +bool ctech_smb_update_uid_lru_size(struct page *page, + struct lruvec *lruvec, enum lru_list lru) +{ + struct pglist_data *pgdata = lruvec_pgdat(lruvec); + + if (is_file_lru(lru) && PageUIDLRU(page)) { + ClearPageUIDLRU(page); + __mod_zone_page_state( + &pgdata->node_zones[page_zonenum(page)], + NR_ZONE_UID_LRU, -hpage_nr_pages(page)); + return true; + } else + return false; +} + + +static void _uid_lru_add_fn(struct page *page, struct lruvec *lruvec) +{ + struct uid_node *uid_nd; + unsigned long flag; + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + uid_t uid = __task_cred(current)->user->uid.val; + + if (!pgdat) + pgdat = page_pgdat(page); + + get_page(page); + spin_lock_irqsave(&pgdat->lru_lock, flag); + VM_BUG_ON_PAGE(PageAnon(page), page); + VM_BUG_ON_PAGE(PageLRU(page), page); + VM_BUG_ON_PAGE(PageUIDLRU(page), page); + SetPageUIDLRU(page); + SetPageLRU(page); + uid_nd = find_uid_node(uid, lruvec); + if (uid_nd == NULL) { + if (lruvec->uid_hash == NULL) + lruvec->uid_hash = alloc_uid_hash_table(); + uid_nd = insert_uid_node(lruvec->uid_hash, uid); + } + list_add(&page->lru, &uid_nd->page_cache_list); + mod_zone_page_state(page_zone(page), NR_ZONE_UID_LRU, + hpage_nr_pages(page)); + spin_unlock_irqrestore(&pgdat->lru_lock, flag); + put_page(page); +} + +static void __uid_lru_cache_add(struct page *page) +{ + struct pglist_data *pagepgdat = page_pgdat(page); + struct lruvec *lruvec; + + lruvec = mem_cgroup_page_lruvec(page, pagepgdat); + _uid_lru_add_fn(page, lruvec); +} + +static unsigned long isolate_uid_lru_pages(struct page *page) +{ + int ret = -EBUSY; + + //VM_BUG_ON_PAGE(!page_count(page), page); + WARN_RATELIMIT(PageTail(page), "trying to isolate tail page"); + + if (PageLRU(page)) { + struct zone *zone = page_zone(page); + struct lruvec *lruvec; + int lru = page_lru(page); + + if (unlikely(!get_page_unless_zero(page))) + return ret; + + if (PageUnevictable(page)) + return ret; + + lruvec = mem_cgroup_page_lruvec(page, zone->zone_pgdat); + ClearPageLRU(page); + del_page_from_lru_list(page, lruvec, lru); + ret = 0; + } + + return ret; +} + +static bool cache_is_low(void) +{ + unsigned long cache = + global_node_page_state(NR_FILE_PAGES) - total_swapcache_pages(); + + if (cache < get_max_minfree()) + return true; + + return false; +} + +bool ctech_smb_uid_lru_add(struct page *page) +{ + if (!sysctl_page_cache_reside_switch) + return false; + + if (cache_is_low()) + return false; + + if (!current->group_leader->hot_count) + return false; + + VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page); + VM_BUG_ON_PAGE(PageLRU(page), page); + __uid_lru_cache_add(page); + + return true; +} + +static unsigned long +smb_isolate_pages_by_uid(struct list_head *page_list, uid_t uid) +{ + LIST_HEAD(putback_page_list); + struct pglist_data *pgdat; + struct mem_cgroup *memcg; + struct uid_node *node; + struct lruvec *lruvec; + unsigned long nr_isolate = 0, nr_isolate_failed = 0; + struct page *page; + + for_each_online_pgdat(pgdat) { + memcg = mem_cgroup_iter(NULL, NULL, NULL); + do { + lruvec = mem_cgroup_lruvec(pgdat, memcg); + if (!lruvec) + goto next; + spin_lock_irq(&pgdat->lru_lock); + node = find_uid_node(uid, lruvec); + if (!node) { + spin_unlock_irq(&pgdat->lru_lock); + goto next; + } + + while (!list_empty(&node->page_cache_list)) { + page = lru_to_page(&node->page_cache_list); + VM_BUG_ON_PAGE(!PageUIDLRU(page), page); + + if (isolate_uid_lru_pages(page)) { + list_move(&page->lru, + &putback_page_list); + nr_isolate_failed++; + continue; + } + + ClearPageActive(page); + list_add(&page->lru, page_list); + nr_isolate++; + inc_node_page_state(page, NR_ISOLATED_ANON + + page_is_file_cache(page)); + } + + list_splice_init(&putback_page_list, + &node->page_cache_list); + spin_unlock_irq(&pgdat->lru_lock); +next: + memcg = mem_cgroup_iter(NULL, memcg, NULL); + } while (memcg); + } + return nr_isolate; +} + + +static unsigned long uid_lru_size(void) +{ + return global_page_state(NR_ZONE_UID_LRU); +} + +static int suitable_reclaim_check(struct lruvec *lruvec) +{ + unsigned long active = global_page_state(NR_ZONE_ACTIVE_FILE); + unsigned long inactive = global_page_state(NR_ZONE_INACTIVE_FILE); + unsigned long total_uid_lru_nr = uid_lru_size(); + + if ((active + inactive) > get_max_minfree()) + return ((active + inactive) << 3) < total_uid_lru_nr; + else + return SMART_BOOST_PUTBACK_LRU; +} + +static bool suitable_isolate_in_direct_reclaim(int priority, + bool enough_list_reclaimed) +{ + bool need_isolate = false; + + if (current_is_kswapd()) + need_isolate = false; + + if (priority <= 11 && !enough_list_reclaimed) + need_isolate = true; + + return need_isolate; +} + +unsigned long ctech_smb_isolate_list_or_putbcak(struct list_head *page_list, + struct lruvec *lruvec, struct pglist_data *pgdat, int priority, + bool enough_list_reclaimed) +{ + LIST_HEAD(putback_list); + unsigned long nr_isolated = 0, nr_isolate_failed = 0; + unsigned long uid_size = uid_lru_size(); + long nr_to_shrink = uid_size >> priority; + int stat = suitable_reclaim_check(lruvec); + struct hotcount_prio_node *pos; + struct page *page; + + if (!sysctl_page_cache_reside_switch) + return 0; + + if (!stat && + !suitable_isolate_in_direct_reclaim(priority,enough_list_reclaimed)) + return 0; + + if (uid_size <= 0) + return 0; + + if (stat == SMART_BOOST_PUTBACK_LRU) + nr_to_shrink = uid_size; + + read_lock(&prio_list_lock); + spin_lock_irq(&pgdat->lru_lock); + list_for_each_entry(pos, &hotcount_prio_list, list) { + struct uid_node *tmp_uid_list = find_uid_node(pos->uid, lruvec); + + if (!nr_to_shrink) + break; + + if (tmp_uid_list == NULL) + continue; + + while (!list_empty(&tmp_uid_list->page_cache_list)) { + page = lru_to_page(&tmp_uid_list->page_cache_list); + VM_BUG_ON_PAGE(!PageUIDLRU(page), page); + + if (isolate_uid_lru_pages(page)) { + list_move(&page->lru, &putback_list); + nr_isolate_failed++; + continue; + } + + ClearPageActive(page); + list_add(&page->lru, page_list); + nr_to_shrink--; + nr_isolated++; + if (!nr_to_shrink) + break; + } + + list_splice_init(&putback_list, &tmp_uid_list->page_cache_list); + } + spin_unlock_irq(&pgdat->lru_lock); + read_unlock(&prio_list_lock); + + if (stat == SMART_BOOST_PUTBACK_LRU) + while (!list_empty(page_list)) { + page = lru_to_page(page_list); + list_del(&page->lru); + putback_lru_page(page); + nr_isolated--; + } + + return nr_isolated; + +} + +static void uid_lru_info_show_print(struct seq_file *m, pg_data_t *pgdat) +{ + int i; + struct uid_node **table; + struct list_head *pos; + unsigned long nr_pages; + struct mem_cgroup *memcg; + + seq_puts(m, "vmpressure:\n0_20\t20_40\t40_60\t60_80\t80_100\n"); + for (i = 0; i < VMPRESSURE_COUNT; i++) + seq_printf(m, "%lu\t", atomic64_read(&vmpress[i])); + + seq_printf(m, "\nNode %d\n", pgdat->node_id); + seq_puts(m, "uid_lru_list priority:\n"); + print_prio_chain(m); + seq_puts(m, "uid\thot_count\tpages\n"); + + memcg = mem_cgroup_iter(NULL, NULL, NULL); + do { + struct lruvec *lruvec = mem_cgroup_lruvec(pgdat, memcg); + + if (!lruvec) + goto next; + + table = lruvec->uid_hash; + if (!table) + goto next; + + for (i = 0; i < (1 << 5); i++) { + struct uid_node *node = rcu_dereference(table[i]); + + if (!node) + continue; + + do { + nr_pages = 0; + list_for_each(pos, &node->page_cache_list) + nr_pages++; + seq_printf(m, "%d\t%d\t%lu\n", + node->uid, + node->hot_count, + nr_pages); + } while ((node = rcu_dereference(node->next)) != NULL); + } +next: + memcg = mem_cgroup_iter(NULL, memcg, NULL); + } while (memcg); + seq_putc(m, '\n'); +} +/* + * Output information about zones in @pgdat. + */ +static int uid_lru_info_show(struct seq_file *m, void *arg) +{ + pg_data_t *pgdat = (pg_data_t *)arg; + + uid_lru_info_show_print(m, pgdat); + + return 0; +} +static void *uid_lru_info_start(struct seq_file *m, loff_t *pos) +{ + pg_data_t *pgdat; + loff_t node = *pos; + + for (pgdat = first_online_pgdat(); + pgdat && node; + pgdat = next_online_pgdat(pgdat)) + --node; + + return pgdat; +} + +static void *uid_lru_info_next(struct seq_file *m, void *arg, loff_t *pos) +{ + pg_data_t *pgdat = (pg_data_t *)arg; + + (*pos)++; + return next_online_pgdat(pgdat); +} + +static void uid_lru_info_stop(struct seq_file *m, void *arg) +{ +} + +static const struct seq_operations uid_lru_info_op = { + .start = uid_lru_info_start, + .next = uid_lru_info_next, + .stop = uid_lru_info_stop, + .show = uid_lru_info_show, +}; + +static int uid_lru_info_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &uid_lru_info_op); +} + +static const struct file_operations proc_uid_lru_info_file_operations = { + .open = uid_lru_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int smb_vmpressure_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + unsigned long pressure = action; + + if (pressure < 20) + atomic64_inc(&vmpress[0]); + else if (pressure < 40) + atomic64_inc(&vmpress[1]); + else if (pressure < 60) + atomic64_inc(&vmpress[2]); + else if (pressure < 80) + atomic64_inc(&vmpress[3]); + else + atomic64_inc(&vmpress[4]); + + return 0; +} + +static struct notifier_block smb_vmpressure_statistic = { + .notifier_call = smb_vmpressure_notifier, +}; + +static void smart_boost_reclaim_by_uid(uid_t uid) +{ + LIST_HEAD(page_list); + unsigned long nr_isolate = 0; + unsigned long nr_reclaimed = 0; + + nr_isolate = smb_isolate_pages_by_uid(&page_list, uid); + if (!nr_isolate) + return; + + nr_reclaimed = coretech_reclaim_pagelist(&page_list, NULL, NULL); + + pr_err("clean uid(%d) pagecache:%lu\n", uid, nr_reclaimed); +} + +static ssize_t page_hot_count_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task; + char buffer[PROC_NUMBUF]; + size_t len; + int page_hot_count; + + task = get_proc_task(file_inode(file)); + + if (!task) + return -ESRCH; + + page_hot_count = task->hot_count; + + put_task_struct(task); + + len = snprintf(buffer, sizeof(buffer), "%d\n", page_hot_count); + return simple_read_from_buffer(buf, count, ppos, buffer, len); +} + +static struct cgroup_subsys_state * +task_get_css_noretry(struct task_struct *task, int subsys_id) +{ + struct cgroup_subsys_state *css; + + rcu_read_lock(); + css = task_css(task, subsys_id); + + if (unlikely(!css_tryget_online(css))) { + rcu_read_unlock(); + return NULL; + } + rcu_read_unlock(); + return css; +} + +static ssize_t page_hot_count_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task; + char buffer[PROC_NUMBUF]; + int page_hot_count; + int err; + uid_t uid; + + memset(buffer, 0, sizeof(buffer)); + + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) { + err = -EFAULT; + goto out; + } + + err = kstrtoint(strstrip(buffer), 0, &page_hot_count); + if (err) + goto out; + + task = get_proc_task(file_inode(file)); + if (!task) { + err = -ESRCH; + goto out; + } + + task->hot_count = page_hot_count; + uid = __task_cred(task)->user->uid.val; + + if (!page_hot_count) { + struct cgroup_subsys_state *pos; + struct css_task_iter it; + struct task_struct *tsk; + struct cgroup_subsys_state *parent; + struct cgroup_subsys_state *css; + + css = task_get_css_noretry(task, cpuacct_cgrp_id); + if (css) { + parent = css->parent; + rcu_read_lock(); + css_for_each_child(pos, parent) { + css_task_iter_start(&pos->cgroup->self, &it); + while ((tsk = css_task_iter_next(&it))) + tsk->hot_count = 0; + css_task_iter_end(&it); + } + rcu_read_unlock(); + } + smart_boost_reclaim_by_uid(uid); + delete_prio_node(uid); + } else { + insert_prio_node(page_hot_count, uid); + } + + put_task_struct(task); + +out: + return err < 0 ? err : count; +} + +const struct file_operations proc_page_hot_count_operations = { + .read = page_hot_count_read, + .write = page_hot_count_write, +}; + +static int __init smartboost_init(void) +{ + struct smb_cb_set set; + + vmpressure_notifier_register(&smb_vmpressure_statistic); + + proc_create("uid_lru_info", 0444, NULL, + &proc_uid_lru_info_file_operations); + + set.smb_uid_lru_add_cb = ctech_smb_uid_lru_add; + set.smb_isolate_list_or_putbcak_cb = ctech_smb_isolate_list_or_putbcak; + set.smb_update_uid_lru_size_cb = ctech_smb_update_uid_lru_size; + smb_register_cb_set(&set); + + return 0; +} + +module_param_named(page_cache_reside_switch, + sysctl_page_cache_reside_switch, + uint, 0644); + +module_init(smartboost_init) + diff --git a/drivers/oneplus/coretech/smartboost/smartboost_helper.c b/drivers/oneplus/coretech/smartboost/smartboost_helper.c new file mode 100644 index 000000000000..6d5019c223c1 --- /dev/null +++ b/drivers/oneplus/coretech/smartboost/smartboost_helper.c @@ -0,0 +1,38 @@ +#include +#include + +struct smb_cb_set smb_cbs; + +bool smb_uid_lru_add(struct page *page) +{ + if (smb_cbs.smb_uid_lru_add_cb) + return smb_cbs.smb_uid_lru_add_cb(page); + else + return false; +} + +unsigned long smb_isolate_list_or_putbcak(struct list_head *page_list, + struct lruvec *lruvec, struct pglist_data *pgdat, int priority, + bool enough_list_reclaimed) +{ + if (smb_cbs.smb_isolate_list_or_putbcak_cb) + return smb_cbs.smb_isolate_list_or_putbcak_cb(page_list, + lruvec, pgdat, priority, enough_list_reclaimed); + else + return 0; +} + +bool smb_update_uid_lru_size(struct page *page, + struct lruvec *lruvec, enum lru_list lru) +{ + if (smb_cbs.smb_update_uid_lru_size_cb) + return smb_cbs.smb_update_uid_lru_size_cb(page, lruvec, lru); + else + return false; +} + +void smb_register_cb_set(struct smb_cb_set *set) +{ + smb_cbs = *set; +} + diff --git a/drivers/oneplus/coretech/tpd/Makefile b/drivers/oneplus/coretech/tpd/Makefile new file mode 100644 index 000000000000..804712c82f34 --- /dev/null +++ b/drivers/oneplus/coretech/tpd/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TPD) += tpd.o diff --git a/drivers/oneplus/coretech/tpd/tpd.c b/drivers/oneplus/coretech/tpd/tpd.c new file mode 100755 index 000000000000..a9c51750f851 --- /dev/null +++ b/drivers/oneplus/coretech/tpd/tpd.c @@ -0,0 +1,882 @@ +#include +#include +#include +#include +#include +#include + +/* + * Task Placement Decision + * + * two main rules as below: + * 1. trigger tpd with tgid and thread's name which will be limited + * through online config, framework can tag threads and limit cpu placement + * of tagged threads that are also foreground task. + * tpd_cmds + * 2. trigger tpd with tgid and thread_id + * control the placement limitation by itself, set tpd_ctl = 1 will limit + * cpu placement of tagged threads, but need release by itself to set tpd_enable = 0, + * tpd_ctl = 0 and task->tpd = 0 + * tpd_id + * 3. control the placement limitation by itself, set tpd_ctl = 1 will limit + * cpu placement of tagged threads, but need release by itself to set tpd_enable = 0, + * tpd_ctl = 0 and task->tpd = 0 + * tpd_dynamic + */ +extern bool is_fg(int uid); + +struct tgid_list_entry { + int pid; + struct list_head node; +}; + +struct monitor_gp { + int decision; /* cpu affinity */ + spinlock_t tgid_list_lock; /* used to check dynamic tpd task */ + struct list_head tgid_head;/* used to check dynamic tpd task */ + spinlock_t miss_list_lock; /* used to check if need re-tag or not */ + unsigned int miss_list_tgid[MAX_MISS_LIST];/* used to check if need re-tag or not */ + char miss_list[MAX_MISS_LIST][TASK_COMM_LEN];/* used to check if need re-tag or not */ + int not_yet; + int cur_idx; +}; + +/* monitor group for dynamic tpd threads */ +static struct monitor_gp mgp[TPD_GROUP_MAX]; + +#define MAX_CLUSTERS 3 +static int cluster_total; +struct tpd_cpuinfo { + int first_cpu; + cpumask_t related_cpus; +}; +static struct tpd_cpuinfo clusters[MAX_CLUSTERS]; + +static atomic_t tpd_enable_rc = ATOMIC_INIT(0); +static int tpd_enable_rc_show(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tpd_enable_rc)); +} + +static struct kernel_param_ops tpd_enable_rc_ops = { + .get = tpd_enable_rc_show, +}; + +module_param_cb(tpd_en_rc, &tpd_enable_rc_ops, NULL, 0664); + +static int tpd_log_lv = 2; +module_param_named(log_lv, tpd_log_lv, int, 0664); + +static atomic_t tpd_ctl = ATOMIC_INIT(0); /* used to ignore fg task checking */ + +static bool should_update_tpd_enable(int enable) { + + bool ret = true; + int refcnt = 0; + + if (enable) { + if (atomic_inc_return(&tpd_enable_rc) > 1) + ret = false; + } else { + refcnt = atomic_read(&tpd_enable_rc); + if (refcnt < 1) { + ret = false; + goto out; + } + + /* tpd_enable ref count must greater or equal tpd_ctl */ + if (refcnt - 1 < atomic_read(&tpd_ctl)) { + ret = false; + goto out; + } + + if (atomic_dec_return(&tpd_enable_rc) > 0) { + tpd_loge("tpd cannot disable"); + ret = false; + } + } + +out: + tpd_logv("should_update_tpd_enable? %d", ret); + + return ret; +} + +static int tpd_enable = 0; +static int tpd_enable_store(const char *buf, const struct kernel_param *kp) +{ + int val; + + if (sscanf(buf, "%d\n", &val) <= 0) + return 0; + + if (should_update_tpd_enable(val)) + tpd_enable = val; + + return 0; +} + +static int tpd_enable_show(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", tpd_enable); +} + +static struct kernel_param_ops tpd_enable_ops = { + .set = tpd_enable_store, + .get = tpd_enable_show, +}; + +module_param_cb(tpd_enable, &tpd_enable_ops, NULL, 0664); + +bool is_tpd_enable(void) +{ + return tpd_enable; +} + +static int miss_list_show(char *buf, const struct kernel_param *kp) +{ + int i, j; + int cnt = 0; + + for (j = TPD_GROUP_MEDIAPROVIDER; j < TPD_GROUP_MAX; ++j) { + spin_lock(&mgp[j].miss_list_lock); + if (mgp[j].not_yet > 0) { + for (i = 0; i < MAX_MISS_LIST; ++i) { + if(strlen(mgp[j].miss_list[i]) > 0) { + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "%s %d\n", mgp[j].miss_list[i], mgp[j].miss_list_tgid[i]); + } + } + } + spin_unlock(&mgp[j].miss_list_lock); + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); + } + return cnt; +} + +static struct kernel_param_ops miss_list_ops = { + .get = miss_list_show, +}; + +module_param_cb(miss_list, &miss_list_ops, NULL, 0664); + +bool is_dynamic_tpd_task(struct task_struct *tsk) +{ + int gid, len = 0, i; + bool ret = false; + struct monitor_gp *group; + struct tgid_list_entry *p; + struct task_struct *leader; + + /* this is for all tpd tasks reset(dynamic/not dynamic tpd tasks) */ + if (!tpd_enable) { + tsk->tpd = 0; + tsk->dtpdg = -1; + tsk->dtpd = 0; + return ret; + } + + /* return if current has dead */ + if (current->exit_state) + return ret; + + rcu_read_lock(); + leader = find_task_by_vpid(tsk->tgid); + rcu_read_unlock(); + + if (leader == NULL) + return ret; + + gid = leader->dtpdg; + /* not dynamic tpd task will return */ + if (gid < TPD_GROUP_MEDIAPROVIDER || gid >= TPD_GROUP_MAX) + return ret; + + group = &mgp[gid]; + + switch (gid) { + case TPD_GROUP_MEDIAPROVIDER: + + spin_lock(&group->tgid_list_lock); + + /* no dynamic tpd task enable of this dynamic tpd group id */ + if (list_empty(&group->tgid_head)) { + if (tsk->dtpd) { + tsk->dtpd = 0; + tsk->tpd = 0; + } + spin_unlock(&group->tgid_list_lock); + return ret; + } + + list_for_each_entry(p, &group->tgid_head, node) { + if (leader->pid != p->pid) + continue; + + /* parent of thread was dynamic tpd task */ + /* dynamic tpd task has already tagged */ + if (tsk->dtpd && tsk->tpd) { + ret = true; + break; + } + + /* start tagging process */ +#ifdef CONFIG_IM + /*binder thread of media provider */ + if (im_binder(tsk)) { + tsk->dtpd = 1; /* dynamic tpd */ + tsk->tpd = group->decision; + ret = true; + break; + } +#endif + +#ifdef CONFIG_ONEPLUS_FG_OPT + #if 0 + /* fuse related thread of media provider */ + if (tsk->fuse_boost) { + tsk->dtpd = 1; /* dynamic tpd */ + tsk->tpd = group->decision; + ret = true; + break; + } + #endif +#endif + /* re-tag missed thread, only one name with one thread */ + spin_lock(&group->miss_list_lock); + if (group->not_yet > 0) { + for (i = 0; i < MAX_MISS_LIST; ++i) { + len = strlen(group->miss_list[i]); + if (len == 0) + continue; + if (group->miss_list_tgid[i] != p->pid) + continue; + if (!strncmp(tsk->comm, group->miss_list[i], len)) { + strcpy(group->miss_list[i], ""); + group->miss_list_tgid[i] = 0; + tsk->dtpd = 1; + tsk->tpd = group->decision; + group->not_yet--; + ret = true; + break; + } + } + + if (ret) { + spin_unlock(&group->miss_list_lock); + break; + } + } + spin_unlock(&group->miss_list_lock); + /* end tagging process */ + } + spin_unlock(&group->tgid_list_lock); + + /* reset flag if dynamic tpd task removed (tgid was removed) */ + if (!ret) { + if (tsk->dtpd) { + tsk->dtpd = 0; + tsk->tpd = 0; + } + } + break; + default: + break; + } + + return ret; +} + +static void set_tpd_ctl(int force) +{ + if (force) + atomic_inc(&tpd_ctl); + else { + if (atomic_read(&tpd_ctl) > 0) + atomic_dec(&tpd_ctl); + } +} + +static int tpd_ctl_store(const char *buf, const struct kernel_param *kp) +{ + int val; + + if (sscanf(buf, "%d\n", &val) <= 0) + return 0; + + set_tpd_ctl(val); + + return 0; +} + +static int tpd_ctl_show(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tpd_ctl)); +} + +static struct kernel_param_ops tpd_ctl_ops = { + .set = tpd_ctl_store, + .get = tpd_ctl_show, +}; + +module_param_cb(tpd_ctl, &tpd_ctl_ops, NULL, 0664); + +static inline void tagging(struct task_struct *tsk, int decision) +{ + if (tsk == NULL) { + tpd_loge("task cannot set"); + return; + } + + tpd_logv("%s task: %s pid:%d decision:%d\n", __func__, tsk->comm, tsk->pid, decision); + + tsk->tpd = decision; +} + +static inline void tagging_by_name(struct task_struct *tsk, char* name, int decision, int *cnt) +{ + size_t tlen = 0, len = 0; + + tlen = strlen(name); + if (tlen == 0) + return; + + len = strlen(tsk->comm); + + if (len != tlen) + return; + + if (!strncmp(tsk->comm, name, tlen)) { + tpd_logv("%s task: %s pid:%d decision:%d name=%s\n", __func__, tsk->comm, tsk->pid, decision, name); + tsk->tpd = decision; + *cnt = *cnt + 1; + } +} + +static void tag_from_tgid(unsigned int tgid, int decision, char* thread_name, int *cnt) +{ + struct task_struct *p, *t; + + rcu_read_lock(); + p = find_task_by_vpid(tgid); + if (p) { + for_each_thread(p, t) + tagging_by_name(t, thread_name, decision, cnt); + } + rcu_read_unlock(); + +} + +static inline void dy_tagging_by_name(struct task_struct *tsk, const char* name, int decision, int dtpd, int *cnt) +{ + size_t tlen = 0, len = 0; + + tlen = strlen(name); + if (tlen == 0) + return; + + len = strlen(tsk->comm); + + if (len != tlen) + return; + + if (!strncmp(tsk->comm, name, tlen)) { + tpd_logv("%s task: %s pid:%d decision:%d name=%s, dtpd=%d\n", __func__, tsk->comm, tsk->pid, decision, name, dtpd); + tsk->tpd = decision; + tsk->dtpd = dtpd; + *cnt = *cnt + 1; + } +} + +static void dy_tag_from_tgid(unsigned int tgid, int decision, const char* thread_name, int dtpd, int *cnt) +{ + struct task_struct *p, *t; + + rcu_read_lock(); + p = find_task_by_vpid(tgid); + if (p) { + for_each_thread(p, t) + dy_tagging_by_name(t, thread_name, decision, dtpd, cnt); + } + rcu_read_unlock(); + +} + +/* set dtpd group id in the main thread */ +static void tag_dtpdg(unsigned int tgid, unsigned int dtpdg) +{ + struct task_struct *p; + + rcu_read_lock(); + p = find_task_by_vpid(tgid); + if (p) { + p->dtpdg = dtpdg; + } + rcu_read_unlock(); + +} + +static int tpd_cmd_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int tgid = 0; + int tp_decision = -1; + char threads[MAX_THREAD_INPUT][TASK_COMM_LEN] = {{0}, {0}, {0}, {0}, {0}, {0}}; + int ret, i, cnt = 0; + + ret = sscanf(buf, "%u %d %s %s %s %s %s %s\n", + &tgid, &tp_decision, + threads[0], threads[1], threads[2], threads[3], threads[4], threads[5]); + + tpd_logi("tpd params: %u %d %s %s %s %s %s %s, from %s %d, total=%d\n", + tgid, tp_decision, threads[0], threads[1], threads[2], threads[3], threads[4], threads[5], + current->comm, current->pid, ret); + + for (i = 0; i < MAX_THREAD_INPUT; i++) { + if (strlen(threads[i]) > 0) + tag_from_tgid(tgid, tp_decision, threads[i], &cnt); + } + + tpd_logv("tpd tagging count = %d\n", cnt); + + return 0; +} + +static struct kernel_param_ops tpd_cmd_ops = { + .set = tpd_cmd_store, +}; +module_param_cb(tpd_cmds, &tpd_cmd_ops, NULL, 0664); + +static void tag_from_tid(unsigned int pid, unsigned int tid, int decision) +{ + struct task_struct *p; + + rcu_read_lock(); + p = find_task_by_vpid(tid); + if (p) { + if (p->group_leader && (p->group_leader->pid == pid)) { + tpd_logv("tpd tagging task pid= %d\n", pid); + tagging(p, decision); + } + } else { + tpd_loge("cannot find task!!! pid = %d", tid); + } + rcu_read_unlock(); +} + +static int tpd_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int pid = 0; + unsigned int tid = 0; + int tpdenable = 0; + int tp_decision = -1; + int ret; + + ret = sscanf(buf, "%u,%u,%d,%d\n", + &pid, &tid, &tpdenable, &tp_decision); + + tpd_logi("tpd param pid:%u tid:%u, tpd_enable:%d decision:%d from %s %d\n", + pid, tid, tpdenable, tp_decision, current->comm, current->pid); + + if (ret != 4) { + tpd_loge("Invalid params!!!!!!"); + return 0; + } + + tag_from_tid(pid, tid, tpdenable ? tp_decision : 0); + + set_tpd_ctl(tpdenable); + + /* update tpd_enable ref cnt*/ + if (should_update_tpd_enable(tpdenable)) + tpd_enable = tpdenable; + + return 0; +} + +static struct kernel_param_ops tpd_ops = { + .set = tpd_store, +}; +module_param_cb(tpd_id, &tpd_ops, NULL, 0664); + +static void tgid_list_add(struct monitor_gp *_mgp, int pid) +{ + struct tgid_list_entry *p; + + p = kmalloc(sizeof(struct tgid_list_entry), GFP_KERNEL); + if (p == NULL) + return; + p->pid = pid; + INIT_LIST_HEAD(&p->node); + + spin_lock(&_mgp->tgid_list_lock); + list_add_tail(&p->node, &_mgp->tgid_head); + tpd_logv("add main thread: %d", pid); + spin_unlock(&_mgp->tgid_list_lock); +} + +void tpd_tglist_del(struct task_struct *tsk) +{ + struct tgid_list_entry *p, *next; + struct monitor_gp *group; + + if (!tsk->pid) + return; + + if (tsk->dtpdg < 0 || tsk->dtpdg >= TPD_GROUP_MAX) + return; + + group = &mgp[tsk->dtpdg]; + + spin_lock(&group->tgid_list_lock); + + if (list_empty(&group->tgid_head)) + goto unlock; + + list_for_each_entry_safe(p, next, &group->tgid_head, node) { + if (p != NULL && (p->pid == tsk->pid)) { + list_del_init(&p->node); + tpd_logv("remove task: %d", tsk->pid); + kfree(p); + break; + } + } + + /* if no tgid in list, disable tpd_ctl && try to disable tpd */ + if (list_empty(&group->tgid_head)) { + set_tpd_ctl(0); + if (should_update_tpd_enable(0)) + tpd_enable = 0; + } + +unlock: + spin_unlock(&group->tgid_list_lock); +} + +/* return true if list size is from one to empty */ +static bool tgid_list_del(struct monitor_gp *_mgp, int pid) +{ + struct tgid_list_entry *p, *next; + bool ret = false; + + spin_lock(&_mgp->tgid_list_lock); + /* do nothing if list empty */ + if (list_empty(&_mgp->tgid_head)) + goto unlock; + + list_for_each_entry_safe(p, next, &_mgp->tgid_head, node) { + if (p != NULL && (p->pid == pid)) { + list_del_init(&p->node); + tpd_logv("remove main thread: %d", pid); + kfree(p); + break; + } + } + /* re-check if list is empty after list deletion */ + if (list_empty(&_mgp->tgid_head)) + ret = true; + +unlock: + spin_unlock(&_mgp->tgid_list_lock); + return ret; +} + +static int list_show(char *buf, const struct kernel_param *kp) +{ + struct tgid_list_entry *p; + int cnt = 0; + int i; + + for (i = 0; i < TPD_GROUP_MAX; ++i) { + spin_lock(&mgp[i].tgid_list_lock); + + if (list_empty(&mgp[i].tgid_head)) + goto unlock; + + list_for_each_entry(p, &mgp[i].tgid_head, node) { + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "%d %d\n", i, p->pid); + } +unlock: + spin_unlock(&mgp[i].tgid_list_lock); + } + + return cnt; +} + +static struct kernel_param_ops tgid_list_ops = { + .get = list_show, +}; + +module_param_cb(tgid_list, &tgid_list_ops, NULL, 0664); + +#define MONITOR_THREAD_NUM 1 +static int tpd_process_trigger_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int tgid = 0; + int tpdenable = 0; + int tp_decision = -1; + int tpd_group = -1; + int ret, i, cnt = 0, j; + const char *threads[MONITOR_THREAD_NUM] = {"bg"}; + struct monitor_gp *group; + + ret = sscanf(buf, "%d,%u,%d,%d\n", + &tpd_group, &tgid, &tpdenable, &tp_decision); + + tpd_logi("tpd param group:%d pid:%u tpd_enable:%d decision:%d from %s %d\n", + tpd_group, tgid, tpdenable, tp_decision, current->comm, current->pid); + + if (ret != 4) { + tpd_loge("Invalid params!!!!!!"); + return 0; + } + + if (tpd_group >= TPD_GROUP_MAX || tpd_group < 0) { + tpd_loge("Invalid group!!!!!!"); + return 0; + } + + group = &mgp[tpd_group]; + + if (!tpdenable) { + if (tgid_list_del(group, tgid)) + group->decision = 0; + + tag_dtpdg(tgid, -1); + } else { + group->decision = tp_decision; + tgid_list_add(group, tgid); + tag_dtpdg(tgid, tpd_group); + } + + set_tpd_ctl(tpdenable); + + + /* tagged by name from group: media provider */ + if (tpd_group == TPD_GROUP_MEDIAPROVIDER) { + for(i = 0; i < MONITOR_THREAD_NUM; i++) { + cnt = 0; + dy_tag_from_tgid(tgid, tpdenable ? tp_decision : 0, threads[i], tpdenable, &cnt); + /* can't find thread to tag/un-tag */ + if (cnt == 0) { + spin_lock(&group->miss_list_lock); + if (tpdenable) { + /* need re-tag, add thread name into miss_list */ + strncpy(group->miss_list[group->cur_idx], threads[i], strlen(threads[i])); + group->miss_list_tgid[group->cur_idx] = tgid; + group->cur_idx = (group->cur_idx + 1) % MAX_MISS_LIST; + group->not_yet++; + } else { + /* dynmic tpd disabled when miss_list still have thread need to tag, we clear the miss list */ + for (j = 0; j < MAX_MISS_LIST; ++j) { + if (group->miss_list_tgid[j] == tgid) { + strcpy(group->miss_list[j], ""); + group->miss_list_tgid[j] = 0; + group->not_yet--; + } + } + } + spin_unlock(&group->miss_list_lock); + } + } + } + + tpd_logv("tagging count = %d, tpd enable set:%d", cnt, tpdenable); + + /* update tpd_enable by ref cnt */ + if (should_update_tpd_enable(tpdenable)) + tpd_enable = tpdenable; + + return 0; +} + +static struct kernel_param_ops tpd_pt_ops = { + .set = tpd_process_trigger_store, +}; +module_param_cb(tpd_dynamic, &tpd_pt_ops, NULL, 0664); + +int tpd_suggested_cpu(struct task_struct* tsk, int request_cpu) +{ + int suggest_cpu = request_cpu; + int uid = task_uid(tsk).val; + + if (!(is_fg(uid) || atomic_read(&tpd_ctl))) + goto out; + + switch (tsk->tpd) { + case TPD_TYPE_S: + case TPD_TYPE_GS: + case TPD_TYPE_PS: + case TPD_TYPE_PGS: + suggest_cpu = clusters[0].first_cpu;; + break; + case TPD_TYPE_G: + case TPD_TYPE_PG: + suggest_cpu = clusters[1].first_cpu;; + break; + case TPD_TYPE_P: + if (cluster_total == MAX_CLUSTERS) + suggest_cpu = clusters[2].first_cpu; + else + suggest_cpu = clusters[1].first_cpu; + break; + default: + tpd_loge("suggest cpu unexpected case: tpd = %d\n", tsk->tpd); + break; + } +out: + tpd_logi("pid = %d: comm = %s, tpd = %d, suggest_cpu = %d, task is fg? %d, tpd_ctl = %d\n", tsk->pid, tsk->comm, + tsk->tpd, suggest_cpu, is_fg(uid), atomic_read(&tpd_ctl)); + return suggest_cpu; +} + +int tpd_suggested(struct task_struct* tsk, int request_cluster) +{ + int suggest_cluster = request_cluster; + int uid = task_uid(tsk).val; + + if (!(is_fg(uid) || atomic_read(&tpd_ctl))) + goto out; + + switch (tsk->tpd) { + case TPD_TYPE_S: + case TPD_TYPE_GS: + case TPD_TYPE_PS: + case TPD_TYPE_PGS: + suggest_cluster = 0; + break; + case TPD_TYPE_G: + case TPD_TYPE_PG: + suggest_cluster = 1; + break; + case TPD_TYPE_P: + if (cluster_total == MAX_CLUSTERS) + suggest_cluster = 2; + else + suggest_cluster = 1; + break; + default: + break; + } +out: + tpd_logi("pid = %d: comm = %s, tpd = %d, suggest_cpu = %d, task is fg? %d, tpd_ctl = %d\n", tsk->pid, tsk->comm, + tsk->tpd, suggest_cluster, is_fg(uid), atomic_read(&tpd_ctl)); + return suggest_cluster; +} + +void tpd_mask(struct task_struct* tsk, cpumask_t *request) +{ + int i = 0, j, x; + int tmp_tpd; + int uid = task_uid(tsk).val; + + cpumask_t mask = CPU_MASK_NONE; + + if (!(is_fg(uid) || atomic_read(&tpd_ctl))) { + if (!is_fg(uid)) + tpd_logv("task is not fg!!!\n"); + return; + } + + tmp_tpd = tsk->tpd; + while (tmp_tpd > 0) { + if (tmp_tpd & 1) { + for_each_cpu(j, &clusters[i].related_cpus) + cpumask_set_cpu(j, &mask); + i++; + } + tmp_tpd = tmp_tpd >> 1; + } + + cpumask_copy(request, &mask); + tpd_logi("tpd_mask: related_cpus = "); + for_each_cpu(x, request) + tpd_logi("%d ", x); + + tpd_logi("pid = %d: comm = %s, tpd = %d, task is fg? %d, tpd_ctl = %d\n", tsk->pid, tsk->comm, + tsk->tpd, is_fg(uid), atomic_read(&tpd_ctl)); +} + +bool tpd_check(struct task_struct *tsk, int dest_cpu) +{ + bool mismatch = false; + int uid = task_uid(tsk).val; + + if (!(is_fg(uid) || atomic_read(&tpd_ctl))) + goto out; + + switch (tsk->tpd) { + case TPD_TYPE_S: + if (!cpumask_test_cpu(dest_cpu, &clusters[0].related_cpus)) + mismatch = true; + break; + case TPD_TYPE_G: + if (!cpumask_test_cpu(dest_cpu, &clusters[1].related_cpus)) + mismatch = true; + break; + case TPD_TYPE_GS: + /* if no gold plus cores, mid = max*/ + if (cluster_total == 3 && cpumask_test_cpu(dest_cpu, &clusters[2].related_cpus)) + mismatch = true; + break; + case TPD_TYPE_P: + if (cluster_total == 3 && !cpumask_test_cpu(dest_cpu, &clusters[2].related_cpus)) + mismatch = true; + break; + case TPD_TYPE_PS: + if (cpumask_test_cpu(dest_cpu, &clusters[1].related_cpus)) + mismatch = true; + break; + case TPD_TYPE_PG: + if (cpumask_test_cpu(dest_cpu, &clusters[0].related_cpus)) + mismatch = true; + break; + default: + break; + } + +out: + tpd_logi("task:%d comm:%s dst: %d should migrate = %d, task is fg? %d\n", tsk->pid, tsk->comm, dest_cpu, !mismatch, is_fg(uid)); + + return mismatch; +} + +void tpd_init_policy(struct cpufreq_policy *policy) +{ + struct tpd_cpuinfo *cpu_info; + int i; + + cpu_info = &clusters[cluster_total]; + cpu_info->first_cpu = policy->cpu; + cpumask_copy(&cpu_info->related_cpus, policy->related_cpus); + cluster_total++; + + tpd_logd("policy->cpu = %d, related_cpus = ", policy->cpu); + for_each_cpu(i, policy->related_cpus) + tpd_logd("%d ", i); +} + +static void tpd_mgp_init() +{ + int i, j; + + for (i = 0; i < TPD_GROUP_MAX; ++i) { + mgp[i].decision = 0; + spin_lock_init(&mgp[i].tgid_list_lock); + INIT_LIST_HEAD(&mgp[i].tgid_head); + spin_lock_init(&mgp[i].miss_list_lock); + for (j = 0; j < MAX_MISS_LIST; j++) { + mgp[i].miss_list_tgid[j] = 0; + strcpy(mgp[i].miss_list[j], ""); + } + mgp[i].not_yet = 0; + mgp[i].cur_idx = 0; + } +} + +static int tpd_init(void) +{ + tpd_mgp_init(); + tpd_logv("tpd init\n"); + return 0; +} + +pure_initcall(tpd_init); diff --git a/drivers/oneplus/coretech/uxcore/Makefile b/drivers/oneplus/coretech/uxcore/Makefile new file mode 100755 index 000000000000..6a11f257f930 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/Makefile @@ -0,0 +1 @@ +obj-y += opchain_helper.o diff --git a/drivers/oneplus/coretech/uxcore/core/Kconfig b/drivers/oneplus/coretech/uxcore/core/Kconfig new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/drivers/oneplus/coretech/uxcore/core/Makefile b/drivers/oneplus/coretech/uxcore/core/Makefile new file mode 100755 index 000000000000..daa046bcd0fa --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/Makefile @@ -0,0 +1,3 @@ +obj-y += opchain_struct_offset_helper.o +opchain-objs := opchain_proxy.o opchain_core.o +obj-y += opchain.o diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_core.c b/drivers/oneplus/coretech/uxcore/core/opchain_core.c new file mode 100644 index 000000000000..5a1ac3bececc --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_core.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2015-2017, The OnePlus corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "../drivers/oneplus/coretech/uxcore/opchain_define.h" +#include "opchain_proxy.h" +#include "opchain_struct_offset_helper.h" +#include + + + +/* +1 for group leader*/ +/* 6 fore * 3 histories*/ +/* ******************************** + **rep 0***Render ***Binder***** + **rep 1***Leader ***SF ***** + **type ***UT_ETASK***UTASK ***** +*/ +#define UX_ENTRY_LEN 2 +#define UX_TOTAL_ENTRIES 18 +#define UX_GROUP_OTHER_ENTRIES 6 +#define R_MAGIC_ID_0 ((unsigned int)0x50656E4F) +#define R_MAGIC_ID_1 ((unsigned int)0x3F73756C) +#define MAGIC_SIZE (sizeof(R_MAGIC_ID_0) + sizeof(R_MAGIC_ID_1) + 1) +#define CLAIMSTONS 16000000 +#define UX_MIGRATE_LOAD_ADJ 20 + +#define UXTAG(t) TASK_UTASK_TAG_R(t) +#define GUXTAG(t) TASK_UTASK_TAG_R(TASK_GROUP_LEADER_R(t)) +#define UXTIME(t) TASK_UTASK_TAG_BASE_R(t) +#define GUXTIME(t) TASK_UTASK_TAG_BASE_R(TASK_GROUP_LEADER_R(t)) +#define CHAIN_REP(pos, sub) (ux_chain.caches[pos].rep[sub]) +#define CHAIN_TYPE(pos) (ux_chain.caches[pos].type) + +#define opc_claim_bit_test(claim, cpu) (claim & ((1 << cpu) | (1 << (cpu + NUMS_CPU)))) + +static unsigned int binder_tag; +static unsigned long ux_realm_util; +static unsigned long ux_realm_claim_util; + +static struct { + atomic_t lru_pos; + struct { + /*pid*/ + int rep[UX_ENTRY_LEN]; + int type; + } caches[UX_TOTAL_ENTRIES]; +} ux_chain = { + .lru_pos = ATOMIC_INIT(0), + .caches = {{{ 0 }, 0 } } +}; +static struct { + atomic_t claim_counts; + void *last_claimant; + unsigned long long last_pass_time; +} claim_store[] = { + [0 ... 31] = { ATOMIC_INIT(0), NULL, (unsigned long long)0 } /* max 32 cores supported, DONT extend this! */ +}; + +static unsigned int ctech_opc_get_claims(void **rq); +static int ctech_opc_get_claim_on_cpu(int cpu, void *rq); + +#if UX_DEBUG +#include +#include +#include "opchain_helper.h" +int opchain_status_show_core(char *buf, const struct kernel_param *kp) +{ + unsigned int most_recent = atomic_read(&ux_chain.lru_pos), size = 0; + char *buf_new; + int iters = UX_TOTAL_ENTRIES; + + buf_new = buf; + + for (; iters > 0; iters--) { + unsigned int pos = most_recent-- % UX_TOTAL_ENTRIES; + if (!pos && !(CHAIN_REP(pos, 0) || CHAIN_REP(pos, 1))) + break; + if (CHAIN_TYPE(pos)) { + printk(pr_fmt("%d, %d %d\n"), CHAIN_REP(pos, 0), CHAIN_REP(pos, 1), CHAIN_TYPE(pos)); + size = snprintf(buf_new, PAGE_SIZE - size, "%d, %d %d\n", CHAIN_REP(pos, 0), CHAIN_REP(pos, 1), CHAIN_TYPE(pos)); + buf_new += size; + size = buf_new - buf; + } + else { + printk(pr_fmt("%d, %d\n"), CHAIN_REP(pos, 0), CHAIN_REP(pos, 1)); + size = snprintf(buf_new, PAGE_SIZE - size, "%d, %d\n", CHAIN_REP(pos, 0), CHAIN_REP(pos, 1)); + buf_new += size; + size = buf_new - buf; + } + } + size += snprintf(buf_new, PAGE_SIZE - size, + "tag %u", atomic_read(&ux_chain.lru_pos)); + buf_new += size; + size = buf_new - buf; + printk(pr_fmt("tag %u\n"), atomic_read(&ux_chain.lru_pos)); + size += snprintf(buf_new, PAGE_SIZE - size, + "claims %x\n", opc_get_claims()); + buf_new += size; + size = buf_new - buf; + size += snprintf(buf_new, PAGE_SIZE - size, + "claim_count %d %d %d %d\n", opc_get_claim_on_cpu(0), opc_get_claim_on_cpu(1), opc_get_claim_on_cpu(2), opc_get_claim_on_cpu(3)); + + return size; +} +#endif + +static inline bool ctech_is_major_utask(int pid, u32 tag) +{ + return (CHAIN_REP((tag % UX_TOTAL_ENTRIES), 0) == pid); +} + +static unsigned int ctech_is_opc_task(void *rq, void *t, int type) +{ + unsigned long long tag = UXTAG(t); + unsigned long long avl_entries = 0; + + if (!tag || !chain_on) + return false; + + /*100ms*/ + if (latest_threshold && (type & UT_CLK_BASE) && + (RQ_CLOCK_R(rq) - UXTIME(t) > latest_threshold)) + return false; + + if ((type & UT_ETASK) && + !(ctech_is_major_utask(TASK_PID_R(t), tag) && + (CHAIN_TYPE(tag % UX_TOTAL_ENTRIES) & UT_ETASK))) + return false; + + if (type & UT_LATEST_ONE) + avl_entries = UX_GROUP_OTHER_ENTRIES; + else + avl_entries = UX_TOTAL_ENTRIES; + tag = tag + avl_entries - atomic_read(&ux_chain.lru_pos); + if (tag <= avl_entries) { + return true; + } + else + return false; +} + +static inline void ctech_ux_clock_base_mark(void *rq, void *t) +{ + GUXTIME(t) = UXTIME(t) = RQ_CLOCK_R(rq); +} + +static void ctech_ux_mark(void *rq, void *t, int ux_group) +{ + unsigned int cache_inpos, latest; + + latest = atomic_read(&ux_chain.lru_pos); + cache_inpos = latest % UX_TOTAL_ENTRIES; + + if (!ctech_is_opc_task(rq, t, UT_LATEST_ONE)) { + UXTAG(t) = atomic_inc_return(&ux_chain.lru_pos); + GUXTAG(t) = UXTAG(t); + cache_inpos = UXTAG(t) % UX_TOTAL_ENTRIES; + if (!ux_group) + binder_tag = UXTAG(t); + if (CHAIN_REP(cache_inpos, 0) != TASK_PID_R(t)) { + CHAIN_REP(cache_inpos, 0) = TASK_PID_R(t); + CHAIN_TYPE(cache_inpos) = ux_group; + } + if (CHAIN_REP(cache_inpos, 1) != TASK_TGID_R(t)) + CHAIN_REP(cache_inpos, 1) = TASK_TGID_R(t); +#if UX_DEBUG + printk(KERN_DEBUG pr_fmt("UX tag%llu, %d:%s, %d:%s %d %llu\n"), UXTAG(t), TASK_PID_R(t), TASK_COMM_R(t), TASK_TGID_R(t), TASK_COMM_R(TASK_GROUP_LEADER_R(t)), ux_group, RQ_CLOCK_R(rq)); +#endif + } +#if UX_DEBUG + else { + printk(KERN_DEBUG pr_fmt("UX tag%llu, only update time base %d:%s, %d:%s %llu\n"), UXTAG(t), TASK_PID_R(t), TASK_COMM_R(t), TASK_TGID_R(t), TASK_COMM_R(TASK_GROUP_LEADER_R(t)), RQ_CLOCK_R(rq)); + } +#endif +} + +static void ctech_opc_add_to_chain(void *rq, void *t) +{ + ctech_ux_mark(rq, t, UT_ETASK); + ctech_ux_clock_base_mark(rq, t); +} + +static void ctech_opc_binder_parse(void *rq, void *cur, + unsigned int dsize,unsigned int *data, + int send) +{ + /* dont move forward to cache diverse histories as many as possible */ + if (!TASK_EXIT_STATE_R(TASK_GROUP_LEADER_R(cur)) && + dsize > MAGIC_SIZE && + data[0] == R_MAGIC_ID_0 && + data[1] == R_MAGIC_ID_1) { + if (chain_on && send) { + ctech_ux_mark(rq, cur, UT_ETASK); + ctech_ux_clock_base_mark(rq, cur); + } + /* + else { + ctech_ux_mark(current, UTASK); + ctech_ux_clock_base_mark(current, render_base); + } + */ + } +} + +static inline int atomic_dec_if_positive_ported(atomic_t *v) +{ + int c, old, dec; + c = atomic_read(v); + for (;;) { + dec = c - 1; + if (unlikely(dec < 0)) + break; + old = atomic_cmpxchg((v), c, dec); + if (likely(old == c)) + break; + c = old; + } + return dec; +} + +static inline unsigned int audit_claim_cpu_range(int cpu) { + return (cpu >= 0 && cpu < ARRAY_SIZE(claim_store)); +} + +static void ctech_opc_task_switch( + unsigned int enqueue, int cpu, void *p, void *rq, unsigned long long clock) +{ + if (likely(p) && likely(audit_claim_cpu_range(cpu))) { + if (enqueue) { + if (ctech_is_opc_task(rq, p, UT_FORE)) { + atomic_inc(&claim_store[cpu].claim_counts); + claim_store[cpu].last_claimant = p; + TASK_ETASK_CLAIM_R(p) = 1; + if (TASK_CLAIM_CPU_R(p) != -1 && TASK_CLAIM_CPU_R(p) != cpu + && claim_store[TASK_CLAIM_CPU_R(p)].last_claimant == p) { + claim_store[TASK_CLAIM_CPU_R(p)].last_pass_time = 0; + TASK_CLAIM_CPU_R(p) = -1; + } + } + } else { + if (TASK_ETASK_CLAIM_R(p)) { + /* remove claim */ + TASK_ETASK_CLAIM_R(p) = 0; + if (!atomic_dec_if_positive_ported(&claim_store[cpu].claim_counts)) + TASK_CLAIM_CPU_R(p) = cpu; + claim_store[cpu].last_pass_time = clock; + } + } + } +} + +/* return value: + * > 0, return # of renders if any renders claim CPU + * = 0, no render + * < 0, if there's any render within a threshold (16ms) */ +static int ctech_opc_get_claim_on_cpu(int cpu, void *rq) +{ + if (!chain_on) + return 0; + + if (likely(audit_claim_cpu_range(cpu))) { + int render_counts = atomic_read(&claim_store[cpu].claim_counts); + u64 rq_time = RQ_CLOCK_R(rq); + + if (render_counts) { + return render_counts; + } else if (rq_time >= claim_store[cpu].last_pass_time && + (rq_time - claim_store[cpu].last_pass_time) <= CLAIMSTONS) { + return OP_CLAIM_S; + } + } + /* zero weight owing to no foreground ux tasks */ + return 0; +} + +static unsigned int ctech_opc_get_claims(void **rqs) +{ + unsigned int claims = 0; + int idx, num_cpus, t_claim; + + if (chain_on) { + for (idx = 0, num_cpus = NUMS_CPU; idx < num_cpus; idx++) { + t_claim = ctech_opc_get_claim_on_cpu(idx, rqs[idx]); + if (t_claim > 0) + claims |= (1 << idx); + else if (t_claim == OP_CLAIM_S) + claims |= (1 << (idx + num_cpus)); + } + } + + return claims; +} + +static unsigned int ctech_opc_is_slave_task(void *rq, void *t) +{ + if (TASK_UTASK_SLAVE_R(t)) { + if (RQ_CLOCK_R(rq) - UXTIME(t) < CLAIMSTONS) + return true; + else + TASK_UTASK_SLAVE_R(t) = 0; + } + + return false; +} + +/* return value: + * OP_PATH_NORMAL(-2) means normal case + * OP_PATH_OCCUPIED(-1) means prev_cpu is occuping by other renders, ignore it + * 0,1,2,3... means this task is render, keep running on previous CPU + * OP_PATH_CLAIM (-3) means there's render within 10ms, so this task should go somewhere else + * of course, this task would not meet any above conditions. + * + * Assume dormant min_cpus is 2. +*/ +static int ctech_opc_select_path(void **rqs, void *w_rq, void *t_rq, void *waker, void *t, int prev_cpu) +{ + unsigned int claims = ctech_opc_get_claims(rqs), last_cpu = NUMS_CPU - 1; + unsigned int t_is_ux_top = ctech_is_opc_task(t_rq, t, UT_FORE); + //int i; + + if (!chain_on) + return OP_PATH_NORMAL; + + if (t_is_ux_top) { + /* + if (prev_cpu >= MIN_POWER_CPU && CPU_VIRTUAL_PLUG_IN(prev_cpu)) + return prev_cpu; + for (i = NUMS_CPU - 1; i >= FIRST_BIG_CORE; i--) + if (CPU_VIRTUAL_PLUG_IN(i) && cpumask_test_cpu(i, TASK_CPUS_ALLOWED_ADDR(t)) && !opc_claim_bit_test(claims, i) && opc_idle_get_state_idx(i) == -1) + return i; + */ + return prev_cpu; + } else if ((ctech_is_opc_task(w_rq, waker, UT_FORE) || ctech_opc_is_slave_task(w_rq, waker)) && TASK_GROUP_LEADER_R(t) == TASK_GROUP_LEADER_R(waker)){ + TASK_UTASK_SLAVE_R(t) = true; + UXTIME(t) = UXTIME(waker); + return OP_PATH_SLAVE; + } + + if (claims & (1 << prev_cpu)) + return OP_PATH_OCCUPIED; + + if (claims & (1 << (prev_cpu + last_cpu + 1))) + return OP_PATH_CLAIM; + + return OP_PATH_NORMAL; +} + +unsigned long ctech_opc_cpu_util(unsigned long util, int cpu, void *t, void *rq, int prev_cpu) +{ + /*TODO: render real demand*/ + int get_claim = ctech_opc_get_claim_on_cpu(cpu, rq); + + if (!get_claim || TASK_UTASK_SLAVE_R(t)) + return util; + + if (get_claim <= 1 && prev_cpu != cpu) + util += ux_realm_claim_util; + else if (util < 1024) + util += get_claim * ux_realm_util; + + return (util > 1024) ? 1024 : util; +} + +static int ctech_opc_check_uxtop_cpu(int uxtop, int cpu) +{ + if (!uxtop || cpu >= FIRST_BIG_CORE) + return true; + return false; +} + +void __init ctech_opchain_init(struct opchain_cb *cb, unsigned long util) +{ + cb->is_opc_task_t = ctech_is_opc_task; + cb->opc_binder_pass_t = ctech_opc_binder_parse; + cb->opc_task_switch_t = ctech_opc_task_switch; + cb->opc_get_claim_on_cpu_t = ctech_opc_get_claim_on_cpu; + cb->opc_get_claims_t = ctech_opc_get_claims; + cb->opc_select_path_t = ctech_opc_select_path; + cb->opc_cpu_util_t = ctech_opc_cpu_util; + cb->opc_add_to_chain_t = ctech_opc_add_to_chain; + cb->opc_check_uxtop_cpu_t = ctech_opc_check_uxtop_cpu; + ux_realm_util = util; + ux_realm_claim_util = ux_realm_util >> 1; +} diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_core.h b/drivers/oneplus/coretech/uxcore/core/opchain_core.h new file mode 100755 index 000000000000..11c77b644c81 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_core.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_OPCHAIN_CORE_H +#define _LINUX_OPCHAIN_CORE_H + +#if UX_DEBUG +extern int opchain_status_show_core(char *buf, const struct kernel_param *kp); +#endif +extern void __init ctech_opchain_init(struct opchain_cb *cb, unsigned long util); +#endif diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_proxy.c b/drivers/oneplus/coretech/uxcore/core/opchain_proxy.c new file mode 100644 index 000000000000..9cb81dbea688 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_proxy.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015-2017, The OnePlus corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "../kernel/sched/sched.h" +#include "../drivers/oneplus/coretech/uxcore/opchain_define.h" +#include "opchain_core.h" +#include "../drivers/oneplus/coretech/uxcore/opchain_helper.h" + +unsigned int __read_mostly boost; +unsigned int __read_mostly boost_tl; +unsigned int __read_mostly boost_sample_time = 1; +unsigned int __read_mostly chain_on = 0; +unsigned int __read_mostly latest_ms = 100; +unsigned int __read_mostly latest_threshold = 100000000; + +#if UX_DEBUG +static int opchain_status_show(char *buf, const struct kernel_param *kp) +{ + return opchain_status_show_core(buf, kp); +} + +static const struct kernel_param_ops param_ops_opchain_status = { + .get = opchain_status_show, +}; +module_param_cb(opchain_status, ¶m_ops_opchain_status, NULL, 0644); +#endif + +static int latest_ms_show(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%u", latest_ms); +} + +static int latest_ms_store(const char *buf, const struct kernel_param *kp) +{ + unsigned int val; + + if (sscanf(buf, "%u\n", &val) <= 0) + return -EINVAL; + latest_ms = val; + latest_threshold = val * 1000000; + return 0; +} + +static const struct kernel_param_ops param_ops_latest_ms = { + .get = latest_ms_show, + .set = latest_ms_store, +}; + +module_param(boost, uint, 0644); +module_param(boost_sample_time, uint, 0644); +module_param(boost_tl, uint, 0644); +module_param(chain_on, uint, 0644); +module_param_cb(latest_ms, ¶m_ops_latest_ms, NULL, 0644); + +static int __init opchain_init(void) +{ + ctech_opchain_init(&uxcore_api, opc_get_orig_capacity(MIN_POWER_CPU)); + + return 0; +} + +module_init(opchain_init); + +static void __exit opchain_exit_module(void) +{ + opc_exit_module(); +} + +module_exit(opchain_exit_module); diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_proxy.h b/drivers/oneplus/coretech/uxcore/core/opchain_proxy.h new file mode 100755 index 000000000000..b9c310127661 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_proxy.h @@ -0,0 +1,9 @@ +#ifndef _LINUX_OPCHAIN_PROXY_H +#define _LINUX_OPCHAIN_PROXY_H + +extern unsigned int boost; +extern unsigned int boost_tl; +extern unsigned int boost_sample_time; +extern unsigned int chain_on; +extern unsigned int latest_threshold; +#endif diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.c b/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.c new file mode 100755 index 000000000000..6b4eb6a88f94 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015-2017, The OnePlus corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "opchain_struct_offset_helper.h" +#include "../kernel/sched/sched.h" + +/* struct task_struct */ +unsigned int opchain_task_struct_offset[__TASK_OFFSET_MAX] = { + [TASK_OFFSET_WAKEE_FLIPS] = offsetof(struct task_struct, wakee_flips), + [TASK_OFFSET_CPUS_ALLOWED] = offsetof(struct task_struct, cpus_allowed), + [TASK_OFFSET_PID] = offsetof(struct task_struct, pid), + [TASK_OFFSET_TGID] = offsetof(struct task_struct, tgid), + [TASK_OFFSET_GROUP_LEADER] = offsetof(struct task_struct, group_leader), + [TASK_OFFSET_COMM] = offsetof(struct task_struct, comm), + [TASK_OFFSET_UTASK_TAG] = offsetof(struct task_struct, utask_tag), + [TASK_OFFSET_UTASK_TAG_BASE] = offsetof(struct task_struct, utask_tag_base), + [TASK_OFFSET_ETASK_CLAIM] = offsetof(struct task_struct, etask_claim), + [TASK_OFFSET_CLAIM_CPU] = offsetof(struct task_struct, claim_cpu), + [TASK_OFFSET_UTASK_SLAVE] = offsetof(struct task_struct, utask_slave), + [TASK_OFFSET_EXIT_STATE] = offsetof(struct task_struct, exit_state) +}; +gen_type_offset_impl(task_struct); + +/* struct rq */ +unsigned int opchain_rq_offset[__RQ_OFFSET_MAX] = { +#ifdef CONFIG_SMP + [RQ_OFFSET_CPU_CAPACITY_ORIG] = offsetof(struct rq, cpu_capacity_orig), + [RQ_OFFSET_CPU] = offsetof(struct rq, cpu), +#endif +#ifdef CONFIG_SCHED_HMP + [RQ_OFFSET_WINDOW_START] = offsetof(struct rq, window_start), +#endif + [RQ_OFFSET_CLOCK] = offsetof(struct rq, clock) +}; +gen_type_offset_impl(rq); diff --git a/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.h b/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.h new file mode 100755 index 000000000000..feff6f495cf3 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/core/opchain_struct_offset_helper.h @@ -0,0 +1,68 @@ +#ifndef _OPCHAIN_STRUCT_OFFSET_HELPER_INC_ +#define _OPCHAIN_STRUCT_OFFSET_HELPER_INC_ + +/* define macro to extern function declaration */ +#define gen_type_offset(type) \ + extern unsigned int opchain_get_##type##_offset(int m); + +/* define macro to create offset impl and export get offset/value symbol */ +#define gen_type_offset_impl(type) \ + unsigned int opchain_get_##type##_offset(int m) { \ + return opchain_##type##_offset[m]; } \ + EXPORT_SYMBOL(opchain_get_##type##_offset); + +/* enum of struct task struct */ +enum { + TASK_OFFSET_WAKEE_FLIPS, + TASK_OFFSET_CPUS_ALLOWED, + TASK_OFFSET_PID, + TASK_OFFSET_TGID, + TASK_OFFSET_GROUP_LEADER, + TASK_OFFSET_COMM, + TASK_OFFSET_UTASK_TAG, + TASK_OFFSET_UTASK_TAG_BASE, + TASK_OFFSET_ETASK_CLAIM, + TASK_OFFSET_CLAIM_CPU, + TASK_OFFSET_UTASK_SLAVE, + TASK_OFFSET_EXIT_STATE, + + __TASK_OFFSET_MAX +}; +#define TASK_WAKEE_FLIPS_R(t) (*(unsigned long *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_WAKEE_FLIPS))) +#define TASK_CPUS_ALLOWED_ADDR(t) (cpumask_t *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_CPUS_ALLOWED)) +#define TASK_PID_R(t) (*(pid_t *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_PID))) +#define TASK_TGID_R(t) (*(pid_t *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_TGID))) +#define TASK_GROUP_LEADER_R(t) (*(struct task_struct **)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_GROUP_LEADER))) +#define TASK_COMM_R(t) ((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_COMM)) +#define TASK_UTASK_TAG_R(t) (*(u64 *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_UTASK_TAG))) +#define TASK_UTASK_TAG_BASE_R(t) (*(u64 *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_UTASK_TAG_BASE))) +#define TASK_ETASK_CLAIM_R(t) (*(int *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_ETASK_CLAIM))) +#define TASK_CLAIM_CPU_R(t) (*(int *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_CLAIM_CPU))) +#define TASK_UTASK_SLAVE_R(t) (*(bool *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_UTASK_SLAVE))) +#define TASK_EXIT_STATE_R(t) (*(bool *)((char *)t + opchain_get_task_struct_offset(TASK_OFFSET_EXIT_STATE))) +gen_type_offset(task_struct); + +/* enum of struct rq */ +enum { + RQ_OFFSET_CLOCK, +#ifdef CONFIG_SMP + RQ_OFFSET_CPU_CAPACITY_ORIG, + RQ_OFFSET_CPU, +#endif +#ifdef CONFIG_SCHED_HMP + RQ_OFFSET_WINDOW_START, +#endif + + __RQ_OFFSET_MAX +}; +#define RQ_CLOCK_R(rq) (*(u64 *)((char *)rq + opchain_get_rq_offset(RQ_OFFSET_CLOCK))) +#ifdef CONFIG_SMP +#define RQ_CPU_CAPACITY_ORIG_R(rq) (*(unsigned long *)((char *)rq + opchain_get_rq_offset(RQ_OFFSET_CPU_CAPACITY_ORIG))) +#define RQ_CPU_R(rq) (*(int *)((char *)rq + opchain_get_rq_offset(RQ_OFFSET_CPU))) +#endif +#ifdef CONFIG_SCHED_HMP +#define RQ_WINDOW_START_R(rq) (*(u64 *)((char *)rq + opchain_get_rq_offset(RQ_OFFSET_WINDOW_START))) +#endif +gen_type_offset(rq); + +#endif //_OPCHAIN_STRUCT_OFFSET_HELPER_INC_ diff --git a/drivers/oneplus/coretech/uxcore/opchain_define.h b/drivers/oneplus/coretech/uxcore/opchain_define.h new file mode 100755 index 000000000000..0205b0e99e82 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/opchain_define.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef _LINUX_OPCHAIN_DEFINE_H +#define _LINUX_OPCHAIN_DEFINE_H + +#define UX_DEBUG 0 +#define UTASK 0 +#define UT_CLK_BASE 0x01 +#define UT_ETASK 0x02 +#define UT_LATEST_ONE 0x04 +#define UT_FORE (UT_CLK_BASE | UT_ETASK) + +#define OP_CLAIM_S -1 +#define OP_PATH_SLAVE -4 +#define OP_PATH_CLAIM -3 +#define OP_PATH_NORMAL -2 +#define OP_PATH_OCCUPIED -1 +#define MIN_POWER_CPU 0 + +#define ONESEC_NANO 1000000000 + +#if 1 +/* for MSM8998, SDM845*/ +#define FIRST_BIG_CORE 4 +#define NUMS_CPU 8 +#define CPU_VIRTUAL_PLUG_IN(i) (opc_cpu_active(i) && !opc_cpu_isolated(i)) +#else +/* for MSM8996*/ +#define FIRST_BIG_CORE 2 +#define NUMS_CPU 4 +#define CPU_VIRTUAL_PLUG_IN(i) (opc_cpu_active(i)) +#endif + +struct opchain_cb { + unsigned int (*is_opc_task_t)(void *rq, void *t, int type); + void (*opc_binder_pass_t)(void *rq, void* cur, unsigned int dsize, unsigned int *data, int send); + void (*opc_task_switch_t)(unsigned int enqueue, int cpu, void *p, void *rq, unsigned long long clock); + int (*opc_get_claim_on_cpu_t)(int cpu, void *rq); + unsigned int (*opc_get_claims_t)(void **rqs); + int (*opc_select_path_t)(void **rqs, void *w_rq, void *t_rq, void *cur, void *t, int prev_cpu); + unsigned long (*opc_cpu_util_t)(unsigned long util, int cpu, void *t, void *rq, int op_path); + void (*opc_add_to_chain_t)(void *rq, void *t); + int (*opc_check_uxtop_cpu_t)(int uxtop, int cpu); +}; +#endif diff --git a/drivers/oneplus/coretech/uxcore/opchain_helper.c b/drivers/oneplus/coretech/uxcore/opchain_helper.c new file mode 100755 index 000000000000..60a581ca0e24 --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/opchain_helper.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015-2017, The OnePlus corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "../kernel/sched/sched.h" +#include "opchain_define.h" +#include "../drivers/oneplus/coretech/uxcore/core/opchain_proxy.h" + +#define t_rq(t) task_rq(t) +#define c_rq(cpu) cpu_rq(cpu) + +struct opchain_cb uxcore_api = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +EXPORT_SYMBOL(uxcore_api); + +unsigned int *opc_boost_tl; +EXPORT_SYMBOL(opc_boost_tl); + +bool is_opc_task(struct task_struct *t, int type) +{ + if (uxcore_api.is_opc_task_t) + return uxcore_api.is_opc_task_t((void *)t_rq(t), (void *)t, type); + return 0; +} +EXPORT_SYMBOL(is_opc_task); + +void opc_binder_pass(size_t data_size, uint32_t *data, int send) +{ + if (uxcore_api.opc_binder_pass_t) + uxcore_api.opc_binder_pass_t((void *)t_rq(current), (void *)current, data_size, data, send); +} +EXPORT_SYMBOL(opc_binder_pass); + +void opc_task_switch(unsigned int enqueue, int cpu, struct task_struct *p, u64 clock) { + if (uxcore_api.opc_task_switch_t) + uxcore_api.opc_task_switch_t(enqueue, cpu, (void *)p, (void *)t_rq(p), clock); +} +EXPORT_SYMBOL(opc_task_switch); + +int opc_get_claim_on_cpu(int cpu) +{ + if (uxcore_api.opc_get_claim_on_cpu_t) + return uxcore_api.opc_get_claim_on_cpu_t(cpu, (void *)c_rq(cpu)); + return 0; +} +EXPORT_SYMBOL(opc_get_claim_on_cpu); + +unsigned int opc_get_claims(void) +{ + void *rqs[NUMS_CPU]; + int idx; + + if (uxcore_api.opc_get_claims_t) { + for (idx = 0; idx < NUMS_CPU; idx++) { + rqs[idx] = (void *)c_rq(idx); + } + return uxcore_api.opc_get_claims_t(rqs); + } + return 0; +} +EXPORT_SYMBOL(opc_get_claims); + +int opc_select_path(struct task_struct *cur, struct task_struct *t, int prev_cpu) +{ + void *rqs[NUMS_CPU]; + int idx; + + if (uxcore_api.opc_select_path_t) { + for (idx = 0; idx < NUMS_CPU; idx++) { + rqs[idx] = (void *)c_rq(idx); + } + return uxcore_api.opc_select_path_t(rqs, (void *)t_rq(cur), (void *)t_rq(t), (void *)cur, (void *)t, prev_cpu); + } + return OP_PATH_NORMAL; +} +EXPORT_SYMBOL(opc_select_path); + +unsigned long opc_cpu_util(unsigned long util, int cpu, struct task_struct *t, int op_path) +{ + if (uxcore_api.opc_cpu_util_t) + return uxcore_api.opc_cpu_util_t(util, cpu, (void *)t, (void *)c_rq(cpu), op_path); + return util; +} +EXPORT_SYMBOL(opc_cpu_util); + +void opc_add_to_chain(struct task_struct *t) +{ + if (uxcore_api.opc_add_to_chain_t) + uxcore_api.opc_add_to_chain_t((void *)t_rq(t), (void *)t); +} +EXPORT_SYMBOL(opc_add_to_chain); + +bool opc_check_uxtop_cpu(int uxtop, int cpu) +{ + if (uxcore_api.opc_check_uxtop_cpu_t) + return uxcore_api.opc_check_uxtop_cpu_t(uxtop, cpu); + return true; +} +EXPORT_SYMBOL(opc_check_uxtop_cpu); + +unsigned long __init opc_get_orig_capacity(int cpu) +{ + return cpu_rq(cpu)->cpu_capacity_orig; +} +EXPORT_SYMBOL(opc_get_orig_capacity); + +bool opc_utask_slave(struct task_struct *t) +{ + return t->utask_slave; +} +EXPORT_SYMBOL(opc_utask_slave); + +void __exit opc_exit_module(void) +{ + uxcore_api.opc_binder_pass_t = NULL; + uxcore_api.is_opc_task_t = NULL; + uxcore_api.opc_task_switch_t = NULL; + uxcore_api.opc_get_claim_on_cpu_t = NULL; + uxcore_api.opc_get_claims_t = NULL; + uxcore_api.opc_select_path_t = NULL; + uxcore_api.opc_cpu_util_t = NULL; + uxcore_api.opc_add_to_chain_t = NULL; + uxcore_api.opc_check_uxtop_cpu_t = NULL; + opc_boost_tl = NULL; +} +EXPORT_SYMBOL(opc_exit_module); diff --git a/drivers/oneplus/coretech/uxcore/opchain_helper.h b/drivers/oneplus/coretech/uxcore/opchain_helper.h new file mode 100755 index 000000000000..f31a6d2091dd --- /dev/null +++ b/drivers/oneplus/coretech/uxcore/opchain_helper.h @@ -0,0 +1,40 @@ +#ifndef _LINUX_OPCHAIN_HELPER_H +#define _LINUX_OPCHAIN_HELPER_H +#include "opchain_define.h" + +#ifdef CONFIG_OPCHAIN +extern struct opchain_cb uxcore_api; +extern void opc_binder_pass(size_t data_size, uint32_t *data, int send); +extern bool is_opc_task(struct task_struct *t, int type); +extern void opc_task_switch(bool enqueue, int cpu, struct task_struct *p, u64 clock); +extern int opc_get_claim_on_cpu(int cpu); +extern unsigned int opc_get_claims(void); +extern int opc_select_path(struct task_struct *cur, struct task_struct *t, int prev_cpu); +extern unsigned long opc_cpu_util(unsigned long util, int cpu, struct task_struct *t, int op_path); +extern bool opc_fps_check(int lvl); +extern void *opc_task_rq(void *t); +extern struct rq *opc_cpu_rq(int cpu); +extern unsigned int opc_task_load(struct task_struct *p); +extern int opc_cpu_active(int cpu); +extern int opc_cpu_isolated(int cpu); +extern unsigned int *opc_boost_tl; +bool opc_check_uxtop_cpu(int uxtop, int cpu); +bool opc_utask_slave(struct task_struct *t); +extern unsigned long __init opc_get_orig_capacity(int cpu); +extern void __exit opc_exit_module(void); +#define UTASK_SLAVE(t) opc_utask_slave(t) + +#else +#define UTASK_SLAVE(t) 0 +static inline void opc_binder_pass(size_t data_size, uint32_t *data, int send) {} +static inline bool is_opc_task(struct task_struct *t, int type) { return 0; } +static inline void opc_task_switch(bool enqueue, int cpu, struct task_struct *p, u64 clock) {} +static inline int opc_get_claim_on_cpu(int cpu) { return 0; } +static inline unsigned int opc_get_claims(void) { return 0; } +static inline int opc_select_path(struct task_struct *cur, struct task_struct *t, int prev_cpu) { return OP_PATH_NORMAL; } +static inline unsigned long opc_cpu_util(unsigned long util, int cpu, struct task_struct *t, int op_path) { return util; } +static inline bool opc_fps_check(int lvl) { return false;} +static inline void opc_add_to_chain(struct task_struct *t) {} +static inline bool opc_check_uxtop_cpu(int uxtop, int cpu) { return true; } +#endif +#endif diff --git a/drivers/oneplus/drivers/Kconfig b/drivers/oneplus/drivers/Kconfig new file mode 100644 index 000000000000..c702953620fc --- /dev/null +++ b/drivers/oneplus/drivers/Kconfig @@ -0,0 +1,38 @@ +config OEM_DEBUG_SUPPORT + default n + bool + +config OEM_SYSRQ_X + default n + depends on OEM_DEBUG_SUPPORT + bool "echo x > /proc/sysrq-trigger to get init process stacktrace" + +config OEM_TRACE_SUPPORT + default n + depends on OEM_DEBUG_SUPPORT + bool "OEM debug function, enable it will register the device, which under /dev/otracer." + +config OEM_FORCE_DUMP + default n + bool "OEM force dump function, it will enable goto the force dump" + +config PARAM_READ_WRITE + bool "Param partition read/write support" + default y + help + if you want to read/write the param partition in kernel, + then you must say Y here. + +config DEBUG_PARAM_DUMP + bool "Param debug support" + default n + help + if you want to dump contents of param partition for debug purpose + +config OEM_BOOT_MODE + bool "OEM boot mode driver" + default n + help + Say y here to enable the boot mode driver + +source "drivers/oneplus/drivers/input/fingerprint/Kconfig" diff --git a/drivers/oneplus/drivers/Makefile b/drivers/oneplus/drivers/Makefile new file mode 100644 index 000000000000..2c75f051edbb --- /dev/null +++ b/drivers/oneplus/drivers/Makefile @@ -0,0 +1,7 @@ +obj-y += boot_mode/ +obj-y += debugdriver/ +obj-y += input/fingerprint/ +obj-y += oem_debug/ +obj-y += oem_trace/ +obj-y += param_read_write/ +obj-y += sysrq/ diff --git a/drivers/oneplus/drivers/boot_mode/Makefile b/drivers/oneplus/drivers/boot_mode/Makefile new file mode 100644 index 000000000000..1550d0bf4e59 --- /dev/null +++ b/drivers/oneplus/drivers/boot_mode/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OEM_BOOT_MODE) += boot_mode.o diff --git a/drivers/oneplus/drivers/boot_mode/boot_mode.c b/drivers/oneplus/drivers/boot_mode/boot_mode.c new file mode 100644 index 000000000000..1c58341e1c6e --- /dev/null +++ b/drivers/oneplus/drivers/boot_mode/boot_mode.c @@ -0,0 +1,77 @@ +#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 = strnstr(boot_command_line, + "androidboot.ftm_mode=", strlen(boot_command_line)); + char *substrnormal = strnstr(boot_command_line, + "androidboot.mode=", strlen(boot_command_line)); + 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); + diff --git a/drivers/oneplus/drivers/debugdriver/Makefile b/drivers/oneplus/drivers/debugdriver/Makefile new file mode 100644 index 000000000000..2d8317eb850c --- /dev/null +++ b/drivers/oneplus/drivers/debugdriver/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DEBUG_DRIVER) += debugdriver.o diff --git a/drivers/oneplus/drivers/debugdriver/debugdriver.c b/drivers/oneplus/drivers/debugdriver/debugdriver.c new file mode 100644 index 000000000000..d2cb39266f75 --- /dev/null +++ b/drivers/oneplus/drivers/debugdriver/debugdriver.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 op, Inc. + * Author: Siba Prasad + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXLEN 255 + +static int sys_value; +struct mutex sys_mutex1; +spinlock_t sys_spinlock; +unsigned long global_mutex; +unsigned long global_spinlock; + +dev_t dev; +static struct class *dev_class; +static struct cdev sys_cdev; +struct kobject *kobj_ref; +uint8_t *kernel_buffer; + +static int __init sysdriver_init(void); +static void __exit sysdriver_exit(void); + +static struct task_struct *sys_thread1; +static struct task_struct *sys_thread2; +static struct task_struct *sys_thread3; +static struct task_struct *sys_thread4; + +/*************** Driver Functions **********************/ +static int sysdebug_open(struct inode *inode, struct file *file); +static int sysdebug_release(struct inode *inode, struct file *file); +static ssize_t sysdebug_read(struct file *filp, char __user *buf, + size_t len, loff_t *off); +static ssize_t sysdebug_write(struct file *filp, const char *buf, + size_t len, loff_t *off); + +/*************** Sysfs Functions **********************/ +static ssize_t sysfs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t sysfs_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); + +static int sys_null(void); +static int sys_stackoverflow(void); +static int sys_bufoverflow(void); +static int sys_free(void); +static int sys_doublefree(void); +static int sys_mutexlock(void); +static int sys_spin(void); + +static int thread_function1(void *data); +static int thread_function2(void *data); +static int thread_function3(void *data); +static int thread_function4(void *data); + +static struct kobj_attribute sysvalue_attr = __ATTR(sys_value, 0660, + sysfs_show, sysfs_store); + +static int thread_function1(void *data) +{ + while (!kthread_should_stop()) { + mutex_lock(&sys_mutex1); + global_mutex++; + pr_info("In Thread Function1 %lu\n", global_mutex); + mutex_unlock(&sys_mutex1); + msleep(1000); + } + do_exit(0); +} + +static int thread_function2(void *data) +{ + while (!kthread_should_stop()) { + mutex_lock(&sys_mutex1); + global_mutex++; + pr_info("In Thread Function2 %lu\n", global_mutex); + mutex_lock(&sys_mutex1); + mutex_unlock(&sys_mutex1); + msleep(1000); + } + do_exit(0); +} + +static int thread_function3(void *data) +{ + while (!kthread_should_stop()) { + spin_lock(&sys_spinlock); + global_spinlock++; + pr_info("In Thread Function3 %lu\n", global_spinlock); + spin_lock(&sys_spinlock); + spin_unlock(&sys_spinlock); + } + do_exit(0); +} + +static int thread_function4(void *data) +{ + while (!kthread_should_stop()) { + spin_lock(&sys_spinlock); + global_spinlock++; + pr_info("In Thread Function4 %lu\n", global_spinlock); + spin_unlock(&sys_spinlock); + } + do_exit(0); +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = sysdebug_read, + .write = sysdebug_write, + .open = sysdebug_open, + .release = sysdebug_release, +}; + +/* Creata a group of attributes helps to create and destroy them all at once */ +static struct attribute *attrs[] = { + &sysvalue_attr.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +/* + * By specifying a name, a subdirectory will be created for the + * attributes with the directory being the name of the attribute group. + */ +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static ssize_t sysfs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + pr_info("Sysfs- Read!!!\n"); + return snprintf(buf, MAXLEN, "%d\nChoose BUG:\n" + "1 = Null pointer dereference\n2 = Stack overflow\n" + "3 = Use-after-free\n4 = Double free\n" + "5 = Buffer overflow\n6 = Mutex lock\n" + "7 = Spinlock\n", sys_value); +} + +static ssize_t sysfs_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + + pr_info("Sysfs- Write!!!\n"); + ret = sscanf(buf, "%d\n", &sys_value); + + if (ret != 1) + return -EINVAL; + + if (sys_value < 0) { + pr_err("Value is not valid...\n"); + return -EINVAL; + } else if (sys_value == 1) { + pr_info("Calling Null pointer error...\n"); + sys_null(); + } else if (sys_value == 2) { + pr_info("Calling Stack Overflow error...\n"); + sys_stackoverflow(); + } else if (sys_value == 3) { + pr_info("Calling Use-after-free error...\n"); + sys_free(); + } else if (sys_value == 4) { + pr_info("Calling Double free error...\n"); + sys_doublefree(); + } else if (sys_value == 5) { + pr_info("Calling Buffer Overflow error...\n"); + sys_bufoverflow(); + } else if (sys_value == 6) { + pr_info("Calling Mutex lock error...\n"); + sys_mutexlock(); + } else if (sys_value == 7) { + pr_info("Calling Spinlock error...\n"); + sys_spin(); + } else { + return count; + } + return count; +} + +static int sys_null(void) +{ + struct inode { + int i_ino; + }; + + struct inode *p = NULL; + + pr_info("Inside NULL function\n"); + p->i_ino = 1; + return 0; +} + +static void fun(int x) +{ + pr_info("Inside Fun_Overflow function\n"); + + if (x == 1) + return; + x = 6; + fun(x); +} + +static int sys_stackoverflow(void) +{ + int x = 5; + + pr_info("Inside Stack Overflow function\n"); + fun(x); + return 0; +} + +static int sys_bufoverflow(void) +{ + char *ptr = kmalloc(100, GFP_KERNEL); + + pr_info("Inside buffer overflow function\n"); + pr_info("slub_debug catches buffer overflow\n"); + memset(ptr, 'x', 300); + kfree(ptr); + return 0; +} + +static int sys_free(void) +{ + char *ptr = kmalloc(100, GFP_KERNEL); + + pr_info("Inside use-after-free function\n"); + kfree(ptr); + pr_info("slub_debug catches use after free\n"); + memset(ptr, 'c', 100); + return 0; +} + +static int sys_doublefree(void) +{ + char *ptr = kmalloc(100, GFP_KERNEL); + + pr_info("Inside double free function\n"); + kfree(ptr); + pr_info("slub_debug catches double free\n"); + kfree(ptr); + return 0; +} + +static int sys_mutexlock(void) +{ + /* Creating Thread 1 */ + sys_thread1 = kthread_run(thread_function1, NULL, "sys Thread1"); + + if (sys_thread1) { + pr_info("Kthread1 Created Successfully...\n"); + } else { + pr_err("Cannot create kthread1\n"); + return 0; + } + + /* Creating Thread 2 */ + sys_thread2 = kthread_run(thread_function2, NULL, "sys Thread2"); + + if (sys_thread2) { + pr_info("Kthread2 Created Successfully...\n"); + } else { + pr_err("Cannot create kthread2\n"); + return 0; + } + return 0; +} + +static int sys_spin(void) +{ + /* Creating Thread 3 */ + sys_thread3 = kthread_run(thread_function3, NULL, "sys Thread3"); + + if (sys_thread3) { + pr_info("Kthread3 Created Successfully...\n"); + } else { + pr_err("Cannot create kthread3\n"); + return 0; + } + + /* Creating Thread 4 */ + sys_thread4 = kthread_run(thread_function4, NULL, "sys Thread4"); + + if (sys_thread4) { + pr_info("Kthread4 Created Successfully...\n"); + } else { + pr_err("Cannot create kthread4\n"); + return 0; + } + return 0; +} + +static int sysdebug_open(struct inode *inode, struct file *file) +{ + pr_info("Device File Opened...!!!\n"); + return 0; +} + +static int sysdebug_release(struct inode *inode, struct file *file) +{ + pr_info("Device File Closed...!!!\n"); + return 0; +} + +static ssize_t sysdebug_read(struct file *filp, char __user *buf, + size_t len, loff_t *off) +{ + pr_info("Read function\n"); + return 0; +} +static ssize_t sysdebug_write(struct file *filp, const char __user *buf, + size_t len, loff_t *off) +{ + pr_info("Write Function\n"); + return len; +} + +static int __init sysdriver_init(void) +{ + /*Allocating Major number*/ + int err = alloc_chrdev_region(&dev, 0, 1, "debugmaj_Dev"); + + if (err < 0) { + pr_err("Cannot allocate major number\n"); + return err; + } + pr_info("Major = %d Minor = %d\n", MAJOR(dev), MINOR(dev)); + + /*Creating cdev structure*/ + cdev_init(&sys_cdev, &fops); + + /*Adding character device to the system*/ + if ((cdev_add(&sys_cdev, dev, 1)) < 0) { + pr_err("Cannot add the device to the system\n"); + goto remove_class; + } + + /*Creating struct class*/ + dev_class = class_create(THIS_MODULE, "debug_class"); + if (dev_class == NULL) { + pr_err("Cannot create the struct class\n"); + goto remove_class; + } + + /*Creating device*/ + if ((device_create(dev_class, NULL, dev, NULL, "debug_device")) + == NULL) { + pr_err("Cannot create the Device 1\n"); + goto remove_device; + } + + /*Creating a directory in /sys/kernel/ */ + kobj_ref = kobject_create_and_add("debug_sysfs", kernel_kobj); + if (!kobj_ref) + return -ENOMEM; + + /*Creating sysfs file for my_value*/ + if (sysfs_create_group(kobj_ref, &attr_group)) { + pr_err("Cannot create sysfs file......\n"); + goto remove_sysfs; + } + + spin_lock_init(&sys_spinlock); + mutex_init(&sys_mutex1); + pr_info("Debug Driver Insert...Done!!!\n"); + return 0; + +remove_sysfs: + kobject_put(kobj_ref); + sysfs_remove_group(kernel_kobj, &attr_group); +remove_device: + class_destroy(dev_class); +remove_class: + unregister_chrdev_region(dev, 1); + cdev_del(&sys_cdev); + return -EINVAL; +} + +void __exit sysdriver_exit(void) +{ + kthread_stop(sys_thread1); + kthread_stop(sys_thread2); + kthread_stop(sys_thread3); + kthread_stop(sys_thread4); + kobject_put(kobj_ref); + sysfs_remove_group(kernel_kobj, &attr_group); + device_destroy(dev_class, dev); + class_destroy(dev_class); + cdev_del(&sys_cdev); + unregister_chrdev_region(dev, 1); + pr_info("Debug Driver Remove...Done!!!\n"); +} + +module_init(sysdriver_init); +module_exit(sysdriver_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Siba Prasad"); +MODULE_DESCRIPTION("Debug driver using sysfs entry"); diff --git a/drivers/oneplus/drivers/input/fingerprint/Kconfig b/drivers/oneplus/drivers/input/fingerprint/Kconfig new file mode 100755 index 000000000000..2c5ebec52b07 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/Kconfig @@ -0,0 +1,27 @@ +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 + +config FINGERPRINT_SILEAD + tristate "silead fingerprint sensor support" + depends on SPI_MASTER +endif diff --git a/drivers/oneplus/drivers/input/fingerprint/Makefile b/drivers/oneplus/drivers/input/fingerprint/Makefile new file mode 100755 index 000000000000..1489636478fb --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/Makefile @@ -0,0 +1,5 @@ +#fingerprint_detect should before fpc1022 +obj-$(CONFIG_FINGERPRINT_DETECT) += fingerprint_detect/ +obj-$(CONFIG_FINGERPRINT_FPC) += fpc/ +obj-$(CONFIG_FINGERPRINT_GOODIX) += goodix/ +obj-$(CONFIG_FINGERPRINT_SILEAD) += silead/ diff --git a/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/Makefile b/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/Makefile new file mode 100644 index 000000000000..c9846540e94f --- /dev/null +++ b/drivers/oneplus/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/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c b/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c new file mode 100755 index 000000000000..592f14a0bb53 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c @@ -0,0 +1,318 @@ + +#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->id_en_init = + pinctrl_lookup_state(fp_dev->fp_pinctrl, "fp_id_init"); + if (IS_ERR_OR_NULL(fp_dev->id_en_init)) { + dev_err(dev, "Cannot get en active pinstate\n"); + ret = PTR_ERR(fp_dev->id_en_init); + goto err; + } + + ret = pinctrl_select_state(fp_dev->fp_pinctrl, + fp_dev->id_en_init); + if (ret) { + dev_err(dev, "can not set %s pins\n", "id_en_init"); + goto err; + } + +err: + fp_dev->fp_pinctrl = NULL; + fp_dev->id_en_init = NULL; + return ret; +} + +int fp_pinctrl_id(struct fingerprint_detect_data *fp_dev, int en) +{ + 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; + } + + if (en == 1) { + fp_dev->id_state_up = + pinctrl_lookup_state(fp_dev->fp_pinctrl, "fp_id_up"); + if (IS_ERR_OR_NULL(fp_dev->id_state_up)) { + dev_err(dev, "Cannot fp_id_up pinstate\n"); + ret = PTR_ERR(fp_dev->id_state_up); + goto err; + } + ret = pinctrl_select_state(fp_dev->fp_pinctrl, + fp_dev->id_state_up); + if (ret) { + dev_err(dev, "can not set %s pins\n", "fp_id_up"); + goto err; + } + } else { + fp_dev->id_state_down = + pinctrl_lookup_state(fp_dev->fp_pinctrl, "fp_id_down"); + if (IS_ERR_OR_NULL(fp_dev->id_state_down)) { + dev_err(dev, "Cannot get fp_id_down pinstate\n"); + ret = PTR_ERR(fp_dev->id_state_down); + goto err; + } + ret = pinctrl_select_state(fp_dev->fp_pinctrl, + fp_dev->id_state_down); + if (ret) { + dev_err(dev, "can not set %s pins\n", "fp_id_down"); + goto err; + } + } + +err: + fp_dev->fp_pinctrl = NULL; + fp_dev->id_state_up = NULL; + fp_dev->id_state_down = NULL; + return ret; +} + +static int fingerprint_detect_probe(struct platform_device *pdev) +{ + int id0 = 0, id1 = 0, id2 = 0; + int rc = 0; + int i; + 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,enchilada")) + fp_detect->project_version = 0x02; + else if (of_property_read_bool(fp_detect->dev->of_node, "oem,fajta")) + fp_detect->project_version = 0x03; + else + fp_detect->project_version = 0x01; + + if (fp_detect->project_version < 0x03) { + 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); + } + + if (fp_detect->project_version < 0x03) { + 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(GPIO91) ID1(GPIO92) ID1(GPIO95) + * fpc1245 + * O-film 1 1 1 + * Primax 1 0 0 + * truly 0 0 1 + * + + * goodix 1 1 0 + * Primax 0 0 0 + * truly 0 1 1 + *fingerchip/ + * qtech 0 1 0 + * Goodix 1 0 1 + */ + + for (i = 1; i < 4; i++) { + usleep_range(5000, 10000); + rc = fp_pinctrl_id(fp_detect, i%2); + if (rc) + goto exit; + id0 = gpio_get_value(fp_detect->id0_gpio); + if (fp_detect->project_version < 0x03) { + id1 = gpio_get_value(fp_detect->id1_gpio); + id2 = gpio_get_value(fp_detect->id2_gpio); + } + pr_info("%s: %d%d%d\n", __func__, id0, id1, id2); + } + + if (fp_detect->project_version < 0x03) { + if (id0 && id1 && id2) { + push_component_info(FINGERPRINTS, + "fpc1228", "FPC"); + 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, "gf5228", "goodix"); + fp_detect->sensor_version = 0x03; + } else if (id0 && id1 && !id2) { + if (fp_detect->project_version == 0x02) { + push_component_info(FINGERPRINTS, + "goodix5228", "goodix"); + fp_detect->sensor_version = 0x03; + } else { + push_component_info(FINGERPRINTS, + "goodix5228", "goodix"); + fp_detect->sensor_version = 0x03; + } + } else if (!id0 && !id1 && !id2) { + push_component_info(FINGERPRINTS, + "fpc1263", "FPC(Primax)"); + fp_detect->sensor_version = 0x02; + } else if (!id0 && id1 && id2) { + if (fp_detect->project_version == 0x02) { + 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, "goodix", "goodix"); + } + } else { + if (id0) { + push_component_info(FINGERPRINTS, + "goodix9508", "goodix"); + fp_detect->sensor_version = 0x04; + } else { + push_component_info(FINGERPRINTS, + "sileadgsl7000", "silead"); + fp_detect->sensor_version = 0x05; + } + } + 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/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h b/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h new file mode 100644 index 000000000000..2775e09a7d12 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h @@ -0,0 +1,18 @@ +#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_en_init; + struct pinctrl_state *id_state_up; + struct pinctrl_state *id_state_down; + int sensor_version; + int project_version; +}; +extern int fp_version; +#endif + diff --git a/drivers/oneplus/drivers/input/fingerprint/fpc/Makefile b/drivers/oneplus/drivers/input/fingerprint/fpc/Makefile new file mode 100644 index 000000000000..43c12d18eb3e --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/fpc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_FPC) += fpc1020_tee.o diff --git a/drivers/oneplus/drivers/input/fingerprint/fpc/fpc1020_tee.c b/drivers/oneplus/drivers/input/fingerprint/fpc/fpc1020_tee.c new file mode 100644 index 000000000000..4da2b4a8092a --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/fpc/fpc1020_tee.c @@ -0,0 +1,545 @@ +/* + * 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, controlling GPIOs such as sensor reset + * line, sensor IRQ line. + * + * 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 driver will NOT send any 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 "../fingerprint_detect/fingerprint_detect.h" + +#define FPC_TTW_HOLD_TIME 1000 + +#define RESET_LOW_SLEEP_MIN_US 5000 +#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100) +#define RESET_HIGH_SLEEP1_MIN_US 100 +#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100) +#define RESET_HIGH_SLEEP2_MIN_US 5000 +#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100) +#define PWR_ON_SLEEP_MIN_US 100 +#define PWR_ON_SLEEP_MAX_US (PWR_ON_SLEEP_MIN_US + 900) + +#define NUM_PARAMS_REG_ENABLE_SET 2 + +static const char * const pctl_names[] = { + "fp_reset_low", + "fp_reset_high", + //"fpc1020_irq_active", +}; + +struct fpc1020_data { + struct device *dev; + + struct pinctrl *fingerprint_pinctrl; + struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)]; + + struct wakeup_source *ttw_wl; + int irq_gpio; + int rst_gpio; + struct mutex lock; /* To set/get exported values in sysfs */ + bool prepared; + atomic_t wakeup_enabled; /* Used both in ISR and non-ISR */ + struct input_dev *input_dev; +}; + +/** + * sysfs node for controlling clocks. + * + * This is disabled in platform variant of this driver but kept for + * backwards compatibility. Only prints a debug print that it is + * disabled. + */ +static ssize_t clk_enable_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + dev_dbg(dev, + "clk_enable sysfs node not enabled in platform driver\n"); + + return count; +} +static DEVICE_ATTR(clk_enable, S_IWUSR, NULL, clk_enable_set); + +/** + * Will try to select the set of pins (GPIOS) defined in a pin control node of + * the device tree named @p name. + * + * The node can contain several eg. GPIOs that is controlled when selecting it. + * The node may activate or deactivate the pins it contains, the action is + * defined in the device tree node itself and not here. The states used + * internally is fetched at probe time. + * + * @see pctl_names + * @see fpc1020_probe + */ +static int select_pin_ctl(struct fpc1020_data *fpc1020, const char *name) +{ + size_t i; + int rc; + struct device *dev = fpc1020->dev; + + for (i = 0; i < ARRAY_SIZE(fpc1020->pinctrl_state); i++) { + const char *n = pctl_names[i]; + + if (!strncmp(n, name, strlen(n))) { + rc = pinctrl_select_state(fpc1020->fingerprint_pinctrl, + fpc1020->pinctrl_state[i]); + if (rc) + dev_err(dev, "cannot select '%s'\n", name); + else + dev_dbg(dev, "Selected '%s'\n", name); + goto exit; + } + } + + rc = -EINVAL; + dev_err(dev, "%s:'%s' not found\n", __func__, name); + +exit: + return rc; +} + +static ssize_t pinctl_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int rc; + + mutex_lock(&fpc1020->lock); + rc = select_pin_ctl(fpc1020, buf); + mutex_unlock(&fpc1020->lock); + + return rc ? rc : count; +} +static DEVICE_ATTR(pinctl_set, S_IWUSR, NULL, pinctl_set); + +static int hw_reset(struct fpc1020_data *fpc1020) +{ + int irq_gpio; + struct device *dev = fpc1020->dev; + int rc = select_pin_ctl(fpc1020, "fp_reset_high"); + + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP1_MIN_US, RESET_HIGH_SLEEP1_MAX_US); + + rc = select_pin_ctl(fpc1020, "fp_reset_low"); + if (rc) + goto exit; + usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US); + + rc = select_pin_ctl(fpc1020, "fp_reset_high"); + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP2_MIN_US, RESET_HIGH_SLEEP2_MAX_US); + + irq_gpio = gpio_get_value(fpc1020->irq_gpio); + dev_info(dev, "IRQ after reset %d\n", irq_gpio); + +exit: + return rc; +} + +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"))) { + mutex_lock(&fpc1020->lock); + rc = hw_reset(fpc1020); + mutex_unlock(&fpc1020->lock); + } else { + return -EINVAL; + } + + return rc ? rc : count; +} +static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, hw_reset_set); + +/** + * sysfs node for controlling whether the driver is allowed + * to wake up the platform on interrupt. + */ +static ssize_t wakeup_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + ssize_t ret = count; + + mutex_lock(&fpc1020->lock); + if (!strncmp(buf, "enable", strlen("enable"))) + atomic_set(&fpc1020->wakeup_enabled, 1); + else if (!strncmp(buf, "disable", strlen("disable"))) + atomic_set(&fpc1020->wakeup_enabled, 0); + else + ret = -EINVAL; + mutex_unlock(&fpc1020->lock); + + return ret; +} +static DEVICE_ATTR(wakeup_enable, S_IWUSR, NULL, wakeup_enable_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 *dev, + struct device_attribute *attr, + char *buf) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int irq = gpio_get_value(fpc1020->irq_gpio); + + return scnprintf(buf, 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 *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + 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_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 struct attribute *attributes[] = { + &dev_attr_pinctl_set.attr, + &dev_attr_hw_reset.attr, + &dev_attr_wakeup_enable.attr, + &dev_attr_clk_enable.attr, + &dev_attr_irq.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; +} + + +static irqreturn_t fpc1020_irq_handler(int irq, void *handle) +{ + struct fpc1020_data *fpc1020 = handle; + + dev_dbg(fpc1020->dev, "%s\n", __func__); + + if (atomic_read(&fpc1020->wakeup_enabled)) { + __pm_wakeup_event(fpc1020->ttw_wl, FPC_TTW_HOLD_TIME); + } + + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); + + return IRQ_HANDLED; +} + +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); + 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_dbg(dev, "%s %d\n", label, *gpio); + + return 0; +} + +static int fpc1020_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc = 0; + size_t i; + int irqf; + 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; + } + + fpc1020->dev = dev; + platform_set_drvdata(pdev, fpc1020); + + if (!np) { + dev_err(dev, "no of node found\n"); + rc = -EINVAL; + goto exit; + } + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,irq-gpio", + &fpc1020->irq_gpio); + if (rc) + goto exit; + rc = fpc1020_request_named_gpio(fpc1020, "fpc,reset-gpio", + &fpc1020->rst_gpio); + if (rc) + goto exit; + + fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(fpc1020->fingerprint_pinctrl)) { + if (PTR_ERR(fpc1020->fingerprint_pinctrl) == -EPROBE_DEFER) { + dev_info(dev, "pinctrl not ready\n"); + rc = -EPROBE_DEFER; + goto exit; + } + dev_err(dev, "Target does not use pinctrl\n"); + fpc1020->fingerprint_pinctrl = NULL; + rc = -EINVAL; + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(fpc1020->pinctrl_state); i++) { + const char *n = pctl_names[i]; + struct pinctrl_state *state = + pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n); + if (IS_ERR(state)) { + dev_err(dev, "cannot find '%s'\n", n); + rc = -EINVAL; + goto exit; + } + dev_info(dev, "found pin control %s\n", n); + fpc1020->pinctrl_state[i] = state; + } + + rc = select_pin_ctl(fpc1020, "fp_reset_low"); + if (rc) + goto exit; + + //rc = select_pin_ctl(fpc1020, "fpc1020_irq_active"); + //if (rc) + // goto exit; + + rc = gpio_direction_input(fpc1020->irq_gpio); + + if (rc) { + dev_err(fpc1020->dev, + "gpio_direction_input (irq) failed.\n"); + goto exit; + } + + + atomic_set(&fpc1020->wakeup_enabled, 0); + + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) { + irqf |= IRQF_NO_SUSPEND; + device_init_wakeup(dev, 1); + } + + 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_dbg(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio)); + + /* Request that the interrupt should be wakeable */ + enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio)); + + fpc1020->ttw_wl = wakeup_source_register(NULL, "fpc_ttw_wl"); + + rc = fpc1020_input_init(fpc1020); + if (rc){ + dev_err(dev, "could not init fpc1020 input device\n"); + goto exit; + } + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto exit; + } + + rc = hw_reset(fpc1020); + + dev_info(dev, "%s: ok\n", __func__); + +exit: + return rc; +} + +static int fpc1020_remove(struct platform_device *pdev) +{ + struct fpc1020_data *fpc1020 = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); + mutex_destroy(&fpc1020->lock); + wakeup_source_unregister(fpc1020->ttw_wl); + dev_info(&pdev->dev, "%s\n", __func__); + + return 0; +} + +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, + .remove = fpc1020_remove, +}; + +static int __init fpc1020_init(void) +{ + int rc = platform_driver_register(&fpc1020_driver); + + if (!rc) + pr_info("%s OK\n", __func__); + else + pr_err("%s %d\n", __func__, rc); + + return rc; +} + +static void __exit fpc1020_exit(void) +{ + pr_info("%s\n", __func__); + platform_driver_unregister(&fpc1020_driver); +} + +module_init(fpc1020_init); +module_exit(fpc1020_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Aleksej Makarov"); +MODULE_AUTHOR("Henrik Tillman "); +MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver."); diff --git a/drivers/oneplus/drivers/input/fingerprint/goodix/Makefile b/drivers/oneplus/drivers/input/fingerprint/goodix/Makefile new file mode 100644 index 000000000000..9faa4a643604 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/goodix/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_GOODIX) += gf_spi.o platform.o netlink.o diff --git a/drivers/oneplus/drivers/input/fingerprint/goodix/gf_spi.c b/drivers/oneplus/drivers/input/fingerprint/goodix/gf_spi.c new file mode 100644 index 000000000000..83e03d8c6f91 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/goodix/gf_spi.c @@ -0,0 +1,1125 @@ +/* + * 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 + +#define CONFIG_MSM_RDM_NOTIFY +#undef CONFIG_FB + +#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 8 + +#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 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 irqreturn_t gf_irq(int irq, void *handle) +{ + struct gf_dev *gf_dev = &gf; +#if defined(GF_NETLINK_ENABLE) + char msg = GF_NET_EVENT_IRQ; + //wake_lock_timeout(&fp_wakelock, msecs_to_jiffies(WAKELOCK_HOLD_TIME)); + __pm_wakeup_event(gf_dev->fp_wakelock, WAKELOCK_HOLD_TIME); + sendnlmsg(&msg); +#elif defined (GF_FASYNC) + if (gf_dev->async) + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); +#endif + return IRQ_HANDLED; +} + +static int irq_setup(struct gf_dev *gf_dev) +{ + int status; + + gf_dev->irq = gf_irq_num(gf_dev); + status = request_threaded_irq(gf_dev->irq, NULL, gf_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "gf", gf_dev); + + if (status) { + pr_err("failed to request IRQ:%d\n", gf_dev->irq); + return status; + } + enable_irq_wake(gf_dev->irq); + gf_dev->irq_enabled = 1; + + return status; +} + +/*static void irq_cleanup(struct gf_dev *gf_dev) +{ + gf_dev->irq_enabled = 0; + disable_irq(gf_dev->irq); + disable_irq_wake(gf_dev->irq); + free_irq(gf_dev->irq, gf_dev); +}*/ + +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 if (GF_KEY_LONGPRESS == gf_key->key) { + key_input = GF_KEY_INPUT_LONG_PRESS; + } 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_LONGPRESS == 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, 0); + 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: + //irq_cleanup(gf_dev); + //gf_cleanup(gf_dev); + 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 int gf_open(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev = &gf; + 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 0 //zoulian@20170727 parse dts move to probe + if (gf_dev->users == 1) { + status = gf_parse_dts(gf_dev); + if (status) + goto err_parse_dt; + + status = irq_setup(gf_dev); + if (status) + goto err_irq; + } + #endif + //gf_hw_reset(gf_dev, 5); + gf_dev->device_available = 1; + } + } else { + pr_info("No device for minor %d\n", iminor(inode)); + } + mutex_unlock(&device_list_lock); + + return status; +#if 0 //zoulian@20170727 parse dts move to probe +err_irq: + gf_cleanup(gf_dev); +err_parse_dt: + return status; +#endif +} + +#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 = &gf; + 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 ssize_t screen_state_get(struct device *device, + struct device_attribute *attribute, + char *buffer) +{ + struct gf_dev *gfDev = dev_get_drvdata(device); + + return scnprintf(buffer, PAGE_SIZE, "%i\n", gfDev->screen_state); +} + +static DEVICE_ATTR(screen_state, 0400, screen_state_get, NULL); + +static ssize_t proximity_state_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct gf_dev *gf_dev = dev_get_drvdata(dev); + int rc, val; + + rc = kstrtoint(buf, 10, &val); + if (rc) + return -EINVAL; + + gf_dev->proximity_state = !!val; + + if (gf_dev->proximity_state) { + gf_disable_irq(gf_dev); + } else { + gf_enable_irq(gf_dev); + } + + return count; +} + +static DEVICE_ATTR(proximity_state, S_IWUSR, NULL, proximity_state_set); + +static struct attribute *gf_attributes[] = { + &dev_attr_screen_state.attr, + &dev_attr_proximity_state.attr, + NULL +}; + +static const struct attribute_group gf_attribute_group = { + .attrs = gf_attributes, +}; + +int gf_opticalfp_irq_handler(int event) +{ + struct gf_dev *gf_dev = &gf; + char msg = 0; + + pr_info("[info]:%s, event %d", __func__, event); + + if (gf.spi == NULL) { + return 0; + } + if (event == 1) { + msg = GF_NET_EVENT_TP_TOUCHDOWN; + sendnlmsg(&msg); + } else if (event == 0) { + msg = GF_NET_EVENT_TP_TOUCHUP; + sendnlmsg(&msg); + } + + __pm_wakeup_event(gf_dev->fp_wakelock, 10*HZ); + + return 0; +} +EXPORT_SYMBOL(gf_opticalfp_irq_handler); + +#if defined(CONFIG_FB) +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 msg = 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) + msg = GF_NET_EVENT_FB_BLACK; + sendnlmsg(&msg); +#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) + msg = GF_NET_EVENT_FB_UNBLACK; + sendnlmsg(&msg); +#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, +}; +#elif defined(CONFIG_MSM_RDM_NOTIFY) +static int goodix_fb_state_chg_callback( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct gf_dev *gf_dev; + struct msm_drm_notifier *evdata = data; + unsigned int blank; + char msg = 0; + + if (val != MSM_DRM_EARLY_EVENT_BLANK && + val != MSM_DRM_ONSCREENFINGERPRINT_EVENT) + return 0; + + if (evdata->id != MSM_DRM_PRIMARY_DISPLAY) + return 0; + + pr_info("[info] %s go to the msm_drm_notifier_callback value = %d\n", + __func__, (int)val); + blank = *(int *)(evdata->data); + if (val == MSM_DRM_ONSCREENFINGERPRINT_EVENT) { + pr_info("[%s] UI ready enter\n", __func__); + + switch (blank) { + case 0: + pr_info("[%s] UI disappear\n", __func__); + msg = GF_NET_EVENT_UI_DISAPPEAR; + sendnlmsg(&msg); + break; + case 1: + pr_info("[%s] UI ready\n", __func__); + msg = GF_NET_EVENT_UI_READY; + sendnlmsg(&msg); + break; + default: + pr_info("[%s] Unknown EVENT\n", __func__); + break; + } + return 0; + } + gf_dev = container_of(nb, struct gf_dev, msm_drm_notif); + if (evdata && evdata->data && val == + MSM_DRM_EARLY_EVENT_BLANK && gf_dev) { + blank = *(int *)(evdata->data); + switch (blank) { + case MSM_DRM_BLANK_POWERDOWN: + if (gf_dev->device_available == 1) { + gf_dev->fb_black = 1; +#if defined(GF_NETLINK_ENABLE) + msg = GF_NET_EVENT_FB_BLACK; + sendnlmsg(&msg); +#elif defined(GF_FASYNC) + if (gf_dev->async) { + kill_fasync(&gf_dev->async, + SIGIO, POLL_IN); + } +#endif + } + gf_dev->screen_state = 0; + sysfs_notify(&gf_dev->spi->dev.kobj, + NULL, dev_attr_screen_state.attr.name); + break; + case MSM_DRM_BLANK_UNBLANK: + if (gf_dev->device_available == 1) { + gf_dev->fb_black = 0; +#if defined(GF_NETLINK_ENABLE) + msg = GF_NET_EVENT_FB_UNBLACK; + sendnlmsg(&msg); +#elif defined(GF_FASYNC) + if (gf_dev->async) + kill_fasync(&gf_dev->async, + SIGIO, POLL_IN); +#endif + } + gf_dev->screen_state = 1; + sysfs_notify(&gf_dev->spi->dev.kobj, + NULL, dev_attr_screen_state.attr.name); + break; + default: + pr_info("%s defalut\n", __func__); + break; + } + } + return NOTIFY_OK; +} +#endif + +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 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); + status = gf_parse_dts(gf_dev); + if (status) + goto err_parse_dt; + /* + * liuyan mv wakelock here + * it should before irq + */ + gf_dev->fp_wakelock = wakeup_source_register(NULL, "fp_wakelock"); + status = irq_setup(gf_dev); + if (status) + goto err_irq; + + status = gf_pinctrl_init(gf_dev); + if (status) + goto err_irq; + 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 + +#if defined(CONFIG_FB) + gf_dev->notifier = goodix_noti_block; + fb_register_client(&gf_dev->notifier); +#elif defined(CONFIG_MSM_RDM_NOTIFY) + gf_dev->msm_drm_notif.notifier_call = goodix_fb_state_chg_callback; + status = msm_drm_register_client(&gf_dev->msm_drm_notif); + if (status) + pr_err("Unable to register msm_drm_notifier: %d\n", status); +#endif + + #ifdef USE_SPI_BUS + spi_set_drvdata(spi, gf_dev); + #else + platform_set_drvdata(pdev, gf_dev); + #endif + status = sysfs_create_group(&gf_dev->spi->dev.kobj, + &gf_attribute_group); + if (status) { + pr_err("%s:could not create sysfs\n", __func__); + goto error_input; + } + pr_info("version V%d.%d.%02d\n", VER_MAJOR, VER_MINOR, PATCH_LEVEL); + + return status; + +#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); + } +err_irq: + gf_cleanup(gf_dev); +err_parse_dt: +error_hw: + 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; + + wakeup_source_unregister(gf_dev->fp_wakelock); + +#if defined(CONFIG_FB) + fb_unregister_client(&gf_dev->notifier); +#elif defined(CONFIG_MSM_RDM_NOTIFY) + if (msm_drm_unregister_client(&gf_dev->msm_drm_notif)) + pr_err("Error occurred while unregistering msm_drm_notifier.\n"); +#endif + if (gf_dev->input) + 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); + 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 ((fp_version != 0x03) && (fp_version != 0x04)) + 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/oneplus/drivers/input/fingerprint/goodix/gf_spi.h b/drivers/oneplus/drivers/input/fingerprint/goodix/gf_spi.h new file mode 100644 index 000000000000..463767d7d410 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/goodix/gf_spi.h @@ -0,0 +1,187 @@ +/* + * driver definition for sensor driver + * + * Coypright (c) 2017 Goodix + */ +#ifndef __GF_SPI_H +#define __GF_SPI_H + +#define CONFIG_MSM_RDM_NOTIFY +#undef CONFIG_FB +#if defined(CONFIG_MSM_RDM_NOTIFY) +#include +#endif + +#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 +#define GF_KEY_INPUT_LONG_PRESS BTN_B + + +#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_LONGPRESS, +} 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 GF_NET_EVENT_TP_TOUCHDOWN 4 +#define GF_NET_EVENT_TP_TOUCHUP 5 +#define GF_NET_EVENT_UI_READY 6 +#define GF_NET_EVENT_UI_DISAPPEAR 7 +#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; + + struct regulator *vdd_3v2; + int regulator_vdd_vmin; + int regulator_vdd_vmax; + int regulator_vdd_current; + +#ifdef GF_FASYNC + struct fasync_struct *async; +#endif +#if defined(CONFIG_FB) + struct notifier_block notifier; +#elif defined(CONFIG_MSM_RDM_NOTIFY) + struct notifier_block msm_drm_notif; +#endif + 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 screen_state; + int proximity_state; /* 0:far 1:near */ + struct wakeup_source *fp_wakelock; +}; +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 *msg); +int netlink_init(void); +void netlink_exit(void); +extern int gf_opticalfp_irq_handler(int event); +#endif /*__GF_SPI_H*/ diff --git a/drivers/oneplus/drivers/input/fingerprint/goodix/netlink.c b/drivers/oneplus/drivers/input/fingerprint/goodix/netlink.c new file mode 100644 index 000000000000..112536dfce09 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/goodix/netlink.c @@ -0,0 +1,100 @@ +/* + * netlink interface + * + * Copyright (c) 2017 Goodix + */ +#include +#include +#include +#include +#include +#include +#include +#include "gf_spi.h" + +#define NETLINK_TEST 25 +#define MAX_MSGSIZE 32 + +static int pid = -1; +struct sock *gf_nl_sk = NULL; + +void sendnlmsg(char *msg) +{ + struct sk_buff *skb_1; + struct nlmsghdr *nlh; + int len = NLMSG_SPACE(MAX_MSGSIZE); + int ret = 0; + if (!msg || !gf_nl_sk || !pid) { + return ; + } + skb_1 = alloc_skb(len, GFP_KERNEL); + if (!skb_1) { + pr_err("alloc_skb error\n"); + return; + } + + nlh = nlmsg_put(skb_1, 0, 0, 0, MAX_MSGSIZE, 0); + + NETLINK_CB(skb_1).portid = 0; + NETLINK_CB(skb_1).dst_group = 0; + + memcpy(NLMSG_DATA(nlh), msg, sizeof(char)); + pr_debug("send message: %d\n", *(char *)NLMSG_DATA(nlh)); + + ret = netlink_unicast(gf_nl_sk, skb_1, pid, MSG_DONTWAIT); + if (!ret) { + //kfree_skb(skb_1); + pr_err("send msg from kernel to usespace 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/oneplus/drivers/input/fingerprint/goodix/platform.c b/drivers/oneplus/drivers/input/fingerprint/goodix/platform.c new file mode 100644 index 000000000000..9992fc89c42e --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/goodix/platform.c @@ -0,0 +1,202 @@ +/* + * 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 +#include + +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; + struct device *dev = &gf_dev->spi->dev; + struct device_node *np = dev->of_node; + u32 voltage_supply[2]; + u32 current_supply; + + gf_dev->reset_gpio = of_get_named_gpio(np, "fp-gpio-reset", 0); + if (gf_dev->reset_gpio < 0) { + pr_err("falied to get reset gpio!\n"); + return gf_dev->reset_gpio; + } + + rc = devm_gpio_request(dev, gf_dev->reset_gpio, "goodix_reset"); + if (rc) { + pr_err("failed to request reset gpio, rc = %d\n", rc); + goto err_reset; + } + gpio_direction_output(gf_dev->reset_gpio, 0); + + gf_dev->irq_gpio = of_get_named_gpio(np, "fp-gpio-irq", 0); + if (gf_dev->irq_gpio < 0) { + pr_err("falied to get irq gpio!\n"); + return gf_dev->irq_gpio; + } + + rc = devm_gpio_request(dev, gf_dev->irq_gpio, "goodix_irq"); + if (rc) { + pr_err("failed to request irq gpio, rc = %d\n", rc); + goto err_irq; + } + gpio_direction_input(gf_dev->irq_gpio); + +/***********power regulator_get****************/ + gf_dev->vdd_3v2 = regulator_get(dev, "vdd-3v2"); + if (IS_ERR(gf_dev->vdd_3v2)) { + rc = PTR_ERR(gf_dev->vdd_3v2); + pr_err("Regulator get failed vdd rc=%d\n", rc); + } + + + rc = of_property_read_u32_array(np, "vdd-voltage", voltage_supply, 2); + if (rc < 0) + pr_err("%s: Failed to get regulator vdd voltage\n", __func__); + + gf_dev->regulator_vdd_vmin = voltage_supply[0]; + gf_dev->regulator_vdd_vmax = voltage_supply[1]; + + rc = regulator_set_voltage(gf_dev->vdd_3v2, + gf_dev->regulator_vdd_vmin, gf_dev->regulator_vdd_vmax); + if (rc < 0) + pr_err("%s:00Failed to set regulator voltage vdd\n", __func__); + + + rc = of_property_read_u32(np, "vdd-current", + ¤t_supply); + if (rc < 0) + pr_err("%s: Failed to get regulator vdd current\n", __func__); + + gf_dev->regulator_vdd_current = current_supply; + + rc = regulator_set_load(gf_dev->vdd_3v2, + gf_dev->regulator_vdd_current); + if (rc < 0) + pr_err("%s: Failed to set regulator current vdd\n", __func__); + + + rc = regulator_enable(gf_dev->vdd_3v2); + if (rc) + pr_err("Regulator vdd enable failed rc=%d\n", rc); + + if (get_boot_mode() == MSM_BOOT_MODE__FACTORY) + { + rc = regulator_disable(gf_dev->vdd_3v2); + if (rc) + pr_err("Regulator vdd disable failed rc=%d\n", rc); + } + + return rc; + +err_irq: + devm_gpio_free(dev, gf_dev->reset_gpio); +err_reset: + 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; + + 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/oneplus/drivers/input/fingerprint/silead/Makefile b/drivers/oneplus/drivers/input/fingerprint/silead/Makefile new file mode 100755 index 000000000000..326ea620a159 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/silead/Makefile @@ -0,0 +1,3 @@ +ccflags-$(CONFIG_FINGERPRINT_SILEAD) += -DBSP_SIL_PLAT_QCOM +ccflags-$(CONFIG_FINGERPRINT_SILEAD) += -DBSP_SIL_POWER_SUPPLY_REGULATOR +obj-$(CONFIG_FINGERPRINT_SILEAD) += silead_fp_platform.o \ No newline at end of file diff --git a/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp.h b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp.h new file mode 100755 index 000000000000..16b86b8fed89 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp.h @@ -0,0 +1,240 @@ +/* + * @file silead_fp.h + * @brief Contains silead_fp device head file. + * + * + * Copyright 2016-2018 Slead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/28 0.1.1 Disable netlink if netlink id = 0 + * Bill Yu 2018/6/1 0.1.2 Support wakelock + * Bill Yu 2018/6/5 0.1.3 Support chip enter power down + * Bill Yu 2018/6/7 0.1.4 Support create proc node + * + */ + +#ifndef __SILEAD_FP_H__ +#define __SILEAD_FP_H__ + +#ifndef _LINUX_WAKELOCK_H +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init(&lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} +#endif /* _LINUX_WAKELOCK_H */ + +enum _pwdn_mode_t { + SIFP_PWDN_NONE = 0, + SIFP_PWDN_POWEROFF = 1, /* shutdown the avdd power supply */ + SIFP_PWDN_FLASH = 2, /* shutdown avdd 200ms for H/W full reset */ + SIFP_PWDN_MAX, +}; +enum _netlink_cmd_t { + SIFP_NETLINK_START = 0, + SIFP_NETLINK_IRQ = 1, + SIFP_NETLINK_SCR_OFF, + SIFP_NETLINK_SCR_ON, + SIFP_NETLINK_CONNECT, + SIFP_NETLINK_DISCONNECT, + SIFP_NETLINK_UI_READY, + SIFP_NETLINK_TP_TOUCHDOWN, + SIFP_NETLINK_TP_TOUCHUP, + SIFP_NETLINK_MAX, +}; + +enum _fp_nav_key_v_t { + NAV_KEY_UNKNOWN = 0, + NAV_KEY_START = 1, + NAV_KEY_UP = NAV_KEY_START, + NAV_KEY_DOWN, + NAV_KEY_RIGHT, + NAV_KEY_LEFT, + NAV_KEY_CLICK, + NAV_KEY_DCLICK, + NAV_KEY_LONGPRESS, + NAV_KEY_CLICK_DOWN, + NAV_KEY_CLICK_UP, + NAV_KEY_MAX, + NAV_KEY_WAITMORE = 1000, +}; + +#define IS_KEY_VALID(k) ((k) > NAV_KEY_UNKNOWN && (k) < NAV_KEY_MAX) + +enum _fp_nav_key_f_t { + NAV_KEY_FLAG_UP = 0, + NAV_KEY_FLAG_DOWN, + NAV_KEY_FLAG_CLICK, +}; + +struct fp_dev_key_t { + int value; + uint32_t flag; /* key down = 1, key up = 0, key down+up = 2 */ +}; + +#define DEVNAME_LEN 16 +struct fp_dev_init_t { + uint8_t mode; + uint8_t bits; + uint16_t delay; + uint32_t speed; + char dev[DEVNAME_LEN]; + uint8_t nl_id; + uint8_t dev_id; + uint16_t reserve; + uint32_t reg; + char ta[DEVNAME_LEN]; +}; + +struct fp_underscreen_info { + uint8_t touch_state; + uint8_t area_rate; + uint16_t x; + uint16_t y; +}; + +struct fp_dev_debug_t { + uint8_t cmd[4]; +}; + +struct fp_dev_kmap_t { + uint16_t k[NAV_KEY_MAX-NAV_KEY_START]; /* Up/Down/Right/Left/Click/Double Click/Longpress */ +}; + +#define PROC_VND_ID_LEN 32 + +#define SIFP_IOC_MAGIC 's' + +#define SIFP_IOC_RESET _IOW(SIFP_IOC_MAGIC, 10, u8) + +#define SIFP_IOC_ENABLE_IRQ _IO(SIFP_IOC_MAGIC, 11) +#define SIFP_IOC_DISABLE_IRQ _IO(SIFP_IOC_MAGIC, 12) +#define SIFP_IOC_WAIT_IRQ _IOR(SIFP_IOC_MAGIC, 13, u8) +#define SIFP_IOC_CLR_IRQ _IO(SIFP_IOC_MAGIC, 14) +//#define SPFP_IOC_EXIT _IOR(SIFP_IOC_MAGIC, 1, u8) + +#define SIFP_IOC_KEY_EVENT _IOW(SIFP_IOC_MAGIC, 15, struct fp_dev_key_t) +#define SIFP_IOC_INIT _IOR(SIFP_IOC_MAGIC, 16, struct fp_dev_init_t) +#define SIFP_IOC_DEINIT _IO(SIFP_IOC_MAGIC, 17) +#define SIFP_IOC_IRQ_STATUS _IOR(SIFP_IOC_MAGIC, 18, u8) +#define SIFP_IOC_DEBUG _IOR(SIFP_IOC_MAGIC, 19, struct fp_dev_debug_t) +#define SIFP_IOC_SCR_STATUS _IOR(SIFP_IOC_MAGIC, 20, u8) +#define SIFP_IOC_GET_VER _IOR(SIFP_IOC_MAGIC, 21, char[10]) +#define SIFP_IOC_SET_KMAP _IOW(SIFP_IOC_MAGIC, 22, uint16_t[7]) +#define SIFP_IOC_ACQ_SPI _IO(SIFP_IOC_MAGIC, 23) +#define SIFP_IOC_RLS_SPI _IO(SIFP_IOC_MAGIC, 24) +#define SIFP_IOC_PKG_SIZE _IOR(SIFP_IOC_MAGIC, 25, u8) +#define SIFP_IOC_DBG_LEVEL _IOWR(SIFP_IOC_MAGIC,26, u8) +#define SIFP_IOC_WAKELOCK _IOW(SIFP_IOC_MAGIC, 27, u8) +#define SIFP_IOC_PWDN _IOW(SIFP_IOC_MAGIC, 28, u8) +#define SIFP_IOC_PROC_NODE _IOW(SIFP_IOC_MAGIC, 29, char[PROC_VND_ID_LEN]) +#define SIFP_IOC_GET_TP_TOUCH_INFO _IOR(SIFP_IOC_MAGIC, 30, struct fp_underscreen_info) +#define SIFP_IOC_SET_TP_MSG_REPORT_MODE _IOW(SIFP_IOC_MAGIC, 31, u8) + +#define SIFP_IOC_REPORT_KEY _IOW(SIFP_IOC_MAGIC, 32, uint8_t) + +#define RESET_TIME 1 /* Default chip reset wait time(ms) */ +#define RESET_TIME_MULTIPLE 1 /* Multiple for reset time multiple*wait_time */ +#define SIFP_NETLINK_ROUTE 30 +#define NL_MSG_LEN 16 + +//#define PROC_DIR "fp" /* if defined, create node under /proc/fp/xxx */ +//#define PROC_NODE "fp_id" /* proc node name */ //remove by chenran + +#if (SIFP_NETLINK_ROUTE > 0) + #define BSP_SIL_NETLINK +#endif + +#if !defined(BSP_SIL_PLAT_MTK) && !defined(BSP_SIL_PLAT_QCOM) + #define BSP_SIL_PLAT_COMM +#endif /* ! BSP_SIL_PLAT_MTK & ! BSP_SIL_PLAT_QCOM */ + +/* Todo: enable correct power supply mode */ +#if !defined(BSP_SIL_POWER_SUPPLY_REGULATOR) +#define BSP_SIL_POWER_SUPPLY_REGULATOR +//#define BSP_SIL_POWER_SUPPLY_PINCTRL +//#define BSP_SIL_POWER_SUPPLY_GPIO +#endif + +/* AVDD voltage range 2.8v ~ 3.3v */ +#define AVDD_MAX 3000000 +#define AVDD_MIN 3000000 + +/* VDDIO voltage range 1.8v ~ AVDD */ +#define VDDIO_MAX 1800000 +#define VDDIO_MIN 1800000 + +#define CURRENT 50000 + +#if defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) || defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_GPIO) || defined(BSP_SIL_POWER_SUPPLY_GPIO) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) + #error "Don't define multiple power supply mode!" +#endif + +#ifdef BSP_SIL_PLAT_MTK + #include "silead_fp_mtk.h" + #define PLAT_H "silead_fp_mtk.c" + + #define DEVICE "/dev/spidev1.0" + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 1 +#elif defined(BSP_SIL_PLAT_QCOM) + #define QSEE_V4 /* Enable it if QSEE v4 or higher */ + #include "silead_fp_qcom.h" + #define PLAT_H "silead_fp_qcom.c" + + #define DEVICE "/dev/spidev0.0" + #define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "sileadta" +#else + #include "silead_fp_comm.h" + #define PLAT_H "silead_fp_comm.c" + + #define DEVICE "/dev/spidev0.0" + #define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "" +#endif /* BSP_SIL_PLAT_XXX */ + +extern int opticalfp_irq_handler(struct fp_underscreen_info* tp_info); +#endif /* __SILEAD_FP_H__ */ + +/* End of file silead_fp.h */ diff --git a/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_platform.c b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_platform.c new file mode 100755 index 000000000000..c70e2cc0ca3f --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_platform.c @@ -0,0 +1,1555 @@ +/************************************************************************************ + * @file silead_fp_platform.c + * @brief Contains silead_fp device implementation. + * + * + * Copyright 2016-2018 Slead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/4/2 0.1.6 Init version + * Bill Yu 2018/5/2 0.1.7 Fix compile error for some platform + * Bill Yu 2018/5/20 0.1.8 Default wait 3ms after reset + * Bill Yu 2018/5/28 0.1.9 Support poll/read if netlink id = 0 + * Bill Yu 2018/6/1 0.2.0 Support wakelock + * Bill Yu 2018/6/5 0.2.1 Support chip enter power down + * Bill Yu 2018/6/7 0.2.2 Support create proc node + * + * 2018/06/24 add fix for coverity 63621 63600 + * 2018/06/26 add silead power_on and power_offf for deep_sleep + * 2018/06/30 add for modify kernel warning (power on) + * 2018/07/07 add for tp irq and lcd notify + * 2018/07/21 add for get tp info + ***********************************************************************************/ +#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 +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) +#include +#endif +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#else +#include +#endif + +#include "silead_fp.h" +#include +#include +#include "../fingerprint_detect/fingerprint_detect.h" +#include "../drivers/gpu/drm/msm/sde/sde_trace.h" + +#define FP_DEV_NAME "silead_fp" +#define FP_DEV_MAJOR 0 /* assigned */ + +#define FP_CLASS_NAME "silead_fp" +#define FP_INPUT_NAME "fp-keys" + +#define FP_DEV_VERSION "v0.2.3" +#define LOG_TAG "[+silead_fp-] " + +#define BSP_SIL_IRQ_ASYNC /* IRQ use asynchrous mode. */ +static struct silfp_data *optical_fp; +static struct fp_underscreen_info fp_tpinfo; +static unsigned int lasttouchmode = 0; + +#ifndef BSP_SIL_NETLINK +struct silfp_msg_list { + unsigned char msg; + struct list_head list; +}; +#endif /* !BSP_SIL_NETLINK */ + +struct silfp_data { + dev_t devt; + struct cdev cdev; + spinlock_t spi_lock; + struct platform_device *spi; + struct list_head device_entry; + + unsigned users; + + struct device *dev; + int ref; + + struct input_dev *input; + + spinlock_t irq_lock; + int int_port; + int irq; + s32 irq_is_disable; + int irq_ignore; + s32 power_is_off; + int rst_port; + struct work_struct work; + struct completion done; + struct wake_lock wakelock; + struct wake_lock wakelock_hal; + +#ifdef BSP_SIL_NETLINK + /* for netlink use */ + struct sock *nl_sk; +#else + spinlock_t read_lock; + wait_queue_head_t read_queue; + struct list_head msg_q; +#endif + struct fp_dev_kmap_t keymap_cust; + + int scr_off; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend es; +#else + struct notifier_block msm_drm_notif; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + /* for power supply */ +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + struct regulator *avdd_ldo; + struct regulator *vddio_ldo; +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + int avdd_port; + int vddio_port; +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +#ifdef PROC_NODE + struct proc_dir_entry *proc_root; + struct proc_dir_entry *proc_entry; +#endif /* PROC_NODE */ + + /* for spi enable/disable */ + atomic_t spionoff_count; + + struct fp_plat_t pin; + + atomic_t init; + struct pinctrl *sl_pinctrl; + struct pinctrl_state *gpio_state_enable; +}; + +typedef enum _fp_spi_speet_t { + SPEED_1M=1*1000*1000, + SPEED_LOW = SPEED_1M, + SPEED_5M=5*1000*1000, + SPEED_MEDIUM = SPEED_5M, + SPEED_8M=8*1000*1000, + SPEED_9M=9*1000*1000, + SPEED_HIGH=SPEED_8M, + SPEED_10M=10*1000*1000, +} fp_spi_speet_t ; + +static struct fp_dev_init_t silfp_dev_init_d = { + .mode = 0, + .bits = 8, + .speed = SPEED_HIGH, + .delay = 100, + .dev = DEVICE, + .nl_id = SIFP_NETLINK_ROUTE, + .dev_id = 0, +}; + +/* on some platform, home key has been redefine to KEY_HOMEPAGE! */ +/* double check the define on customer board!!! + out/target/product/.../system/usr/keylayout/Generic.kl + kernel/include/uapi/linux/input.h + + KEY_HOMEPAGE + KEY_HOME + KEY_MENU + KEY_BACK + KEY_POWER + KEY_CAMERA + KEY_VOLUMEUP + KEY_VOLUMEDOWN */ + +typedef struct _key_map { + int key_orig; + int key_new; +} nav_keymap_t; + +static nav_keymap_t keymap[] = { + { NAV_KEY_UP, KEY_UP, }, /* KEY_RESERVED, ignore this key */ + { NAV_KEY_DOWN, KEY_DOWN, }, + { NAV_KEY_RIGHT, KEY_RIGHT, }, + { NAV_KEY_LEFT, KEY_LEFT, }, + { NAV_KEY_CLICK, KEY_HOMEPAGE, }, + { NAV_KEY_DCLICK, KEY_HOMEPAGE, }, + { NAV_KEY_LONGPRESS,KEY_HOMEPAGE, }, +}; + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +#ifdef BSP_SIL_NETLINK +static int pid; +#endif /* BSP_SIL_NETLINK */ + +#ifdef PROC_NODE +static char vendor_name[PROC_VND_ID_LEN]; +#endif /* PROC_NODE */ + + +struct class *silfp_class; + +static struct workqueue_struct *silfp_wq; + +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay); +static void silfp_irq_disable(struct silfp_data *fp_dev); +static void silfp_irq_enable(struct silfp_data *fp_dev); +static int silfp_wait_irq(struct silfp_data *fp_dev); +static int silfp_irq_status(struct silfp_data *fp_dev); +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info); +static int silfp_resource_deinit(struct silfp_data *fp_dev); +static void silfp_power_deinit(struct silfp_data *fp_dev); +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd); +static void silfp_hw_poweron(struct silfp_data *fp_dev); + +/* -------------------------------------------------------------------- */ +/* debug settings */ +/* -------------------------------------------------------------------- */ +typedef enum { + ERR_LOG=0, + DBG_LOG, + INFO_LOG, + ALL_LOG, +} fp_debug_level_t; + +/* debug log level */ +static fp_debug_level_t debug_level = DBG_LOG; + +#define LOG_MSG_DEBUG(level, fmt, args...) do { \ + if (debug_level >= level) {\ + pr_warn(LOG_TAG fmt, ##args); \ + } \ + } while (0) + +#include PLAT_H + +/* -------------------------------------------------------------------- */ +/* netlink functions */ +/* -------------------------------------------------------------------- */ +#ifdef BSP_SIL_NETLINK +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + struct nlmsghdr *nlh = NULL; + struct sk_buff *skb = NULL; + int ret; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] send cmd %d\n", __func__, cmd); + if ( !fp_dev->nl_sk) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid socket\n", __func__); + return; + } + + if (! pid) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid PID\n", __func__); + return; + } + + /*alloc data buffer for sending to native*/ + /*malloc data space at least 1500 bytes, which is ethernet data length*/ + skb = alloc_skb(NL_MSG_LEN, GFP_ATOMIC); + if (skb == NULL) { + return; + } + + nlh = nlmsg_put(skb, 0, 0, 0, NL_MSG_LEN, 0); + if (!nlh) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] nlmsg_put() failed\n", __func__); + kfree_skb(skb); + return; + } + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + + *(char *)NLMSG_DATA(nlh) = cmd; + ret = netlink_unicast(fp_dev->nl_sk, skb, pid, MSG_DONTWAIT); + if (ret == 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] send failed\n", __func__); + kfree_skb(skb); + return; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] sent, len=%d\n", __func__, ret); +} + +static void silfp_netlink_recv(struct sk_buff *__skb) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + char str[128]; + + skb = skb_get(__skb); + if (!skb ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] skb NULL\n", __func__); + return; + } + + /* presume there is 5byte payload at leaset */ + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + pid = nlh->nlmsg_pid; + LOG_MSG_DEBUG(INFO_LOG, "[%s] PID=%d, msg=%s\n", __func__, pid, str); + } else { + LOG_MSG_DEBUG(ERR_LOG, "[%s] data len incorrect\n", __func__); + } + + kfree_skb(skb); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + struct netlink_kernel_cfg cfg; + + memset(&cfg, 0, sizeof(struct netlink_kernel_cfg)); + cfg.input = silfp_netlink_recv; + + fp_dev->nl_sk = netlink_kernel_create(&init_net, SIFP_NETLINK_ROUTE, &cfg); + if (fp_dev->nl_sk == NULL) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] netlink create failed\n", __func__); + return -1; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] netlink create success\n", __func__); + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + if (fp_dev->nl_sk != NULL) { + netlink_kernel_release(fp_dev->nl_sk); + fp_dev->nl_sk = NULL; + return 0; + } + + LOG_MSG_DEBUG(ERR_LOG, "[%s] no netlink socket\n", __func__); + return -1; +} +#else +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + unsigned long flags; + struct silfp_msg_list *list; + + list = kzalloc(sizeof(*list), GFP_ATOMIC); + if (!list) { + return; + } + + list->msg = (unsigned char)cmd; + + LOG_MSG_DEBUG(INFO_LOG, "silfp_poll cmd %d\n", cmd); + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_add_tail(&list->list, &fp_dev->msg_q); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + wake_up_interruptible(&fp_dev->read_queue); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + spin_lock_init(&fp_dev->read_lock); + init_waitqueue_head(&fp_dev->read_queue); + INIT_LIST_HEAD(&fp_dev->msg_q); + + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + struct silfp_msg_list *list, *next; + unsigned long flags; + + if (fp_dev && (&fp_dev->msg_q)) { + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_for_each_entry_safe(list, next, &fp_dev->msg_q, list) { + list_del(&list->list); + kfree(list); + } + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + } + return 0; +} + +static unsigned int silfp_poll(struct file *fd, poll_table *wait) +{ + struct silfp_data *fp_dev; + unsigned int mask = 0; + + if (!fd) { + return -EINVAL; + } + + fp_dev = fd->private_data; + poll_wait(fd, &fp_dev->read_queue, wait); + + if(!list_empty(&fp_dev->msg_q)) { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + +static ssize_t silfp_read(struct file *fd, char __user *buf, size_t len,loff_t *ptr) +{ + struct silfp_data *fp_dev; + struct silfp_msg_list *list; + unsigned long flags; + int32_t msglen = sizeof(char); + + if (!fd || !buf || (len < msglen)) { + return -EINVAL; + } + + fp_dev = fd->private_data; + if (!&fp_dev->msg_q) { + return -EINVAL; + } + + spin_lock_irqsave(&fp_dev->read_lock, flags); + while(list_empty(&fp_dev->msg_q)) { + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + if (wait_event_interruptible(fp_dev->read_queue, !list_empty(&fp_dev->msg_q))) { + return -EINVAL; + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + } + /* pick the first one */ + list = list_first_entry(&fp_dev->msg_q, + struct silfp_msg_list, list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + if (!list) { + return -EFAULT; + } + + if (copy_to_user(buf, &list->msg, msglen)) { + LOG_MSG_DEBUG(ERR_LOG, "copy_to fail\n"); + msglen = -EFAULT; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] %d\n", __func__, list->msg); + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_del(&list->list); + kfree(list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + return msglen; +} +#endif /* BSP_SIL_NETLINK */ + +/*liuyan 2017/12/7 add for detect screen state*/ +static ssize_t screen_state_get(struct device *device, + struct device_attribute *attribute, + char *buffer) +{ + struct silfp_data *fp_dev = dev_get_drvdata(device); + + return scnprintf(buffer, PAGE_SIZE, "%i\n", !fp_dev->scr_off); +} + +static DEVICE_ATTR(screen_state, 0400, screen_state_get, NULL); + +static struct attribute *sl_attributes[] = { + &dev_attr_screen_state.attr, + NULL +}; + +static const struct attribute_group sl_attribute_group = { + .attrs = sl_attributes, +}; + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void silfp_early_suspend(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); +} + +static void silfp_late_resume(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); +} +#else +static int silfp_fb_callback(struct notifier_block *notif, + unsigned long event, void *data) +{ + struct silfp_data *fp_dev = container_of(notif, struct silfp_data, msm_drm_notif); + struct msm_drm_notifier *evdata = data; + unsigned int blank; + int retval = 0; + + /* If we aren't interested in this event, skip it immediately ... */ + if (event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_ONSCREENFINGERPRINT_EVENT){ + + + if (evdata->id != MSM_DRM_PRIMARY_DISPLAY) + return 0; + + + + + blank = *(int *)evdata->data; + //printk(KERN_ERR"GZM FP1 blank = %d\n",blank); + //printk(KERN_ERR"GZM FP1 event = %ld\n",event); + //printk(KERN_ERR"GZM FP1 evdata->id = %d\n",evdata->id); + + if (event == MSM_DRM_ONSCREENFINGERPRINT_EVENT ) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] UI ready enter\n", __func__); + //printk(KERN_ERR"GZM FP2 blank =%d\n",blank); + + switch (blank) { + case 0: + LOG_MSG_DEBUG(INFO_LOG, "[%s] UI disappear\n", __func__); + //printk(KERN_ERR"GZM FP3\n"); + //silfp_netlink_send(fp_dev, SIFP_NETLINK_UI_OFF);//SIFP_NETLINK_UI_OFF donothing now + break; + case 1: + //printk(KERN_ERR"GZM FP4\n"); + LOG_MSG_DEBUG(INFO_LOG, "[%s] UI ready \n", __func__); + SDE_ATRACE_BEGIN("fb_ui_ready"); + silfp_netlink_send(fp_dev, SIFP_NETLINK_UI_READY); + SDE_ATRACE_END("fb_ui_ready"); + break; + default: + LOG_MSG_DEBUG(INFO_LOG, "[%s] Unknown MSM_DRM_ONSCREENFINGERPRINT_EVENT\n", __func__); + break; + } + return retval; + } + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, blank=0x%x\n", __func__, blank); + + switch (blank) { + case MSM_DRM_BLANK_UNBLANK: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD ON\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); + sysfs_notify(&fp_dev->spi->dev.kobj, + NULL, dev_attr_screen_state.attr.name); + break; + + case MSM_DRM_BLANK_POWERDOWN: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD OFF\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); + sysfs_notify(&fp_dev->spi->dev.kobj, + NULL, dev_attr_screen_state.attr.name); + break; + + default: + LOG_MSG_DEBUG(INFO_LOG, "[%s] Unknown notifier\n", __func__); + break; + } + return retval; + } + else + return 0; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* -------------------------------------------------------------------- */ +/* IRQ related functions */ +/* -------------------------------------------------------------------- */ +static void silfp_irq_disable(struct silfp_data *fp_dev) +{ + unsigned long irqflags; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (!fp_dev->irq_is_disable) { + fp_dev->irq_is_disable = 1; + disable_irq_nosync(fp_dev->irq); + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq disabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static void silfp_irq_enable(struct silfp_data *fp_dev) +{ + unsigned long irqflags = 0; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (fp_dev->irq_is_disable) { + enable_irq(fp_dev->irq); + fp_dev->irq_is_disable = 0; + reinit_completion(&fp_dev->done); + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq enabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static int silfp_wait_irq(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + wait_for_completion(&fp_dev->done); + //return wait_for_completion_timeout(&fp_dev->done, msecs_to_jiffies(3000)); + return 1; +} + +static int silfp_irq_status(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + if (fp_dev->int_port) { + return gpio_get_value(fp_dev->int_port); + } + return -1; +} + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id) +{ + struct silfp_data *fp_dev = (struct silfp_data *)dev_id; + + if (fp_dev->irq_ignore) { + return IRQ_HANDLED; + } + +#ifdef BSP_SIL_IRQ_CONFIRM + if (gpio_get_value(fp_dev->int_port)) { +#else + if (true) { +#endif /* BSP_SIL_IRQ_CONFIRM */ + wake_lock_timeout(&fp_dev->wakelock, 10*HZ); /* set a little long for a poor MCU */ +#ifdef BSP_SIL_IRQ_ASYNC + queue_work(silfp_wq, &fp_dev->work); +#else + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +#endif /* BSP_SIL_IRQ_ASYNC */ + complete(&fp_dev->done); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq ignore\n", __func__); + } + + return IRQ_HANDLED; +} + +int opticalfp_irq_handler(struct fp_underscreen_info* tp_info) +{ + + // wake_lock_timeout(&fp_dev->wakelock, 10*HZ); /* set a little long for a poor MCU */ + + //silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); + //static unsigned int lasttouchmode = 0; + + if (optical_fp == NULL) + return 0; + fp_tpinfo = *tp_info; + LOG_MSG_DEBUG(INFO_LOG, "fp_tpinfo %d, %d, %d, %d \n" , + fp_tpinfo.touch_state, fp_tpinfo.area_rate, fp_tpinfo.x, fp_tpinfo.y); + + //complete(&fp_dev->done); + LOG_MSG_DEBUG(INFO_LOG, "[%s] touchmode = %d, lasttouchmode =%d \n", __func__, tp_info->touch_state, lasttouchmode); + if(tp_info->touch_state== lasttouchmode){ + return 0; + } + if(1 == tp_info->touch_state){ + silfp_netlink_send(optical_fp, SIFP_NETLINK_TP_TOUCHDOWN); + lasttouchmode = tp_info->touch_state; + }else{ + silfp_netlink_send(optical_fp, SIFP_NETLINK_TP_TOUCHUP); + lasttouchmode = tp_info->touch_state; + } + + wake_lock_timeout(&optical_fp->wakelock, 10*HZ); + + return 0; +} + +EXPORT_SYMBOL(opticalfp_irq_handler); + +static void silfp_work_func(struct work_struct *work) +{ + struct silfp_data *fp_dev = container_of(work, struct silfp_data, work); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] running\n", __func__); + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +} + +/* -------------------------------------------------------------------- */ +/* key event functions */ +/* -------------------------------------------------------------------- */ +static int silfp_keyevent(struct silfp_data *fp_dev, struct fp_dev_key_t *pkey) +{ + int ret = -EFAULT; + int i; + + //LOG_MSG_DEBUG(INFO_LOG, "[%s] key %d, flag %d\n", __func__,pkey->value,pkey->flag); + if (!fp_dev->input) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] invalid input device\n",__func__); + return -1; + } + if ( IS_KEY_VALID(pkey->value) ) { + /* Translate Click Down/Up key to Click key. */ + switch( pkey->value ) { + case NAV_KEY_CLICK_DOWN: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_DOWN; + break; + case NAV_KEY_CLICK_UP: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_UP; + break; + default: + break; + } + + /* Check the custom define keymap */ + if ( fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START] ) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] custom-key %d\n", __func__,fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]); + if ( KEY_RESERVED != fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START] ) { + if ( NAV_KEY_FLAG_CLICK == pkey->flag ) { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + for ( i = 0; ret && i < ARRAY_SIZE(keymap); i++ ) { + if ( keymap[i].key_orig == pkey->value ) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] key %d\n", __func__,keymap[i].key_new); + if ( KEY_RESERVED != keymap[i].key_new ) { + if ( NAV_KEY_FLAG_CLICK == pkey->flag ) { + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, keymap[i].key_new, pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + if ( ret ) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] unregister custom-key %d\n", __func__,pkey->value); + input_report_key(fp_dev->input, pkey->value, pkey->flag); + input_sync(fp_dev->input); + ret = 0; + } + return ret; +} + +/* -------------------------------------------------------------------- */ +/* proc node functions */ +/* -------------------------------------------------------------------- */ +#ifdef PROC_NODE +static int silfp_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", vendor_name); + return 0; +} + +static int silfp_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, silfp_proc_show, NULL); +} + +static const struct file_operations silfp_proc_fops = { + .owner = THIS_MODULE, + .read = seq_read, + .open = silfp_proc_open, + .release = single_release, +}; + +static int silfp_proc_create_node(struct silfp_data *fp_dev) +{ + if (fp_dev->proc_entry == NULL) { + fp_dev->proc_root = NULL; +#ifdef PROC_DIR + fp_dev->proc_root = proc_mkdir(PROC_DIR, NULL); + if (fp_dev->proc_root == NULL) { + LOG_MSG_DEBUG(ERR_LOG, "Create dir %s under /proc error!\n", PROC_DIR); + goto err_out; + } +#endif /* PROC_DIR */ + fp_dev->proc_entry = proc_create(PROC_NODE, 0666, fp_dev->proc_root, &silfp_proc_fops); + if (fp_dev->proc_entry == NULL) { + LOG_MSG_DEBUG(ERR_LOG, "Create entry %s under /proc/ error!\n", PROC_NODE); + goto err_out1; + } + } + + return 0; + +err_out1: + remove_proc_entry(PROC_NODE, fp_dev->proc_root); +#ifdef PROC_DIR + remove_proc_entry(PROC_DIR, NULL); +err_out: +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + return -ENOMEM; +} + +static int silfp_proc_init(struct silfp_data *fp_dev) +{ + if (fp_dev) { + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } + return 0; + +} + +static void silfp_proc_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev) { + if (fp_dev->proc_entry) { + remove_proc_entry(PROC_NODE, fp_dev->proc_root); + } +#ifdef PROC_DIR + if (fp_dev->proc_root) { + remove_proc_entry(PROC_DIR, NULL); + } +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } +} +#endif /* PROC_NODE */ + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_input_init(struct silfp_data *fp_dev) +{ + int i, status = 0; + + /*register device within input system.*/ + fp_dev->input = input_allocate_device(); + if (!fp_dev->input) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_allocate_device() fail!\n", __func__); + status = -ENOMEM; + return status; + } + + __set_bit(EV_KEY, fp_dev->input->evbit); + //__set_bit(KEY_Q, fp_dev->input->keybit); // it will cause Android think this is a physical keyboard. + __set_bit(KEY_HOME, fp_dev->input->keybit); + __set_bit(KEY_HOMEPAGE, fp_dev->input->keybit); + + __set_bit(KEY_MENU, fp_dev->input->keybit); + __set_bit(KEY_BACK, fp_dev->input->keybit); + __set_bit(KEY_CAMERA, fp_dev->input->keybit); + + __set_bit(KEY_F2, fp_dev->input->keybit); + + for ( i = 0; i < ARRAY_SIZE(keymap); i++ ) { + if ( keymap[i].key_new != KEY_RESERVED ) { + __set_bit(keymap[i].key_new, fp_dev->input->keybit); + } + } + + for ( i = 0; i < ARRAY_SIZE(fp_dev->keymap_cust.k); i++ ) { + if (fp_dev->keymap_cust.k[i] && (fp_dev->keymap_cust.k[i] != KEY_RESERVED)) { + __set_bit(fp_dev->keymap_cust.k[i], fp_dev->input->keybit); + } + } + + fp_dev->input->name = FP_INPUT_NAME; + if (input_register_device(fp_dev->input)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_register_device() fail!\n", __func__); + input_free_device(fp_dev->input); + fp_dev->input = NULL; + status = -ENODEV; + } + return status; +} + +static int silfp_input_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + input_unregister_device(fp_dev->input); + fp_dev->input = NULL; + } + return 0; +} + +static int silfp_input_reinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + silfp_input_deinit(fp_dev); + } + return silfp_input_init(fp_dev); +} + +static int silfp_init(struct silfp_data *fp_dev) +{ + int status = 0; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + init_completion(&fp_dev->done); + spin_lock_init(&fp_dev->irq_lock); + INIT_WORK(&fp_dev->work, silfp_work_func); + + /* netlink interface init */ + status = silfp_netlink_init(fp_dev); + if (status == -1) { + return status; + } + +#if defined(CONFIG_HAS_EARLYSUSPEND) + LOG_MSG_DEBUG(INFO_LOG, "[%s] register_early_suspend\n", __func__); + fp_dev->es.level = (EARLY_SUSPEND_LEVEL_DISABLE_FB - 1); + fp_dev->es.suspend = silfp_early_suspend; + fp_dev->es.resume = silfp_late_resume; + register_early_suspend(&fp_dev->es); +#else + fp_dev->msm_drm_notif.notifier_call = silfp_fb_callback; + status = msm_drm_register_client(&fp_dev->msm_drm_notif); + if (status) + LOG_MSG_DEBUG(INFO_LOG,"Unable to register msm_drm_notifier: %d\n", status); + else + LOG_MSG_DEBUG(INFO_LOG,"register msm_drm_notifier: %d\n", status); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + atomic_set(&fp_dev->spionoff_count,0); + + return status; +} + +static int silfp_resource_deinit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + + if (atomic_read(&fp_dev->init)) { + atomic_dec(&fp_dev->init); + + if (!atomic_read(&fp_dev->init)) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] no more users, free GPIOs\n", __func__); + silfp_irq_disable(fp_dev); + free_irq(fp_dev->irq, fp_dev); + + gpio_direction_input(fp_dev->int_port); +#if !defined(BSP_SIL_PLAT_MTK) + gpio_free(fp_dev->int_port); + if (fp_dev->rst_port > 0 ) { + gpio_free(fp_dev->rst_port); + } +#endif /* !BSP_SIL_PLAT_MTK */ + fp_dev->int_port = 0; + fp_dev->rst_port = 0; + + silfp_input_deinit(fp_dev); + silfp_power_deinit(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ + } + silfp_netlink_send(fp_dev, SIFP_NETLINK_DISCONNECT); + } + return 0; +} + +static void silfp_exit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (fp_dev->es.suspend) { + unregister_early_suspend(&fp_dev->es); + } +#else + if (msm_drm_unregister_client(&fp_dev->msm_drm_notif)) + pr_err("Error occurred while unregistering msm_drm_notifier.\n"); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + silfp_set_spi(fp_dev, false); /* release SPI resources */ + + if (silfp_wq) { + destroy_workqueue(silfp_wq); + silfp_wq = NULL; + } + silfp_netlink_destroy(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ +} + +static void silfp_wakelock_ctl(struct silfp_data *fp_dev, unsigned char lock) +{ + if (lock) { + wake_lock_timeout(&fp_dev->wakelock_hal, 10*HZ); + } else { + wake_unlock(&fp_dev->wakelock_hal); + } +} +/*-------------------------------------------------------------------------*/ + +static long +silfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + int retval = 0; + struct silfp_data *fp_dev; + struct fp_dev_key_t key; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SIFP_IOC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) { + err = !access_ok(VERIFY_WRITE, + (void __user *)arg, _IOC_SIZE(cmd)); + } + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) { + err = !access_ok(VERIFY_READ, + (void __user *)arg, _IOC_SIZE(cmd)); + } + if (err) { + return -EFAULT; + } + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + fp_dev = filp->private_data; + + switch (cmd) { + case SIFP_IOC_INIT: + silfp_resource_init(fp_dev,&silfp_dev_init_d); + if (copy_to_user((void __user *)arg, (void *)&silfp_dev_init_d, sizeof(struct fp_dev_init_t))) { + retval = -EFAULT; + } + + break; + case SIFP_IOC_DEINIT: + silfp_resource_deinit(fp_dev); + break; + + case SIFP_IOC_RESET: { + unsigned char delay = RESET_TIME; + LOG_MSG_DEBUG(INFO_LOG, "[%s] chip reset\n", __func__); + if (arg) { + if (copy_from_user(&delay, (void __user *)arg, sizeof(char))) { + retval = -EFAULT; + break; + } + } + + if (fp_dev->power_is_off) { + silfp_hw_poweron(fp_dev); + mdelay(5); + } + fp_dev->irq_ignore = 1; + if (fp_dev->irq_is_disable) { + silfp_irq_enable(fp_dev); + silfp_hw_reset(fp_dev, delay); + silfp_irq_disable(fp_dev); + } else { + silfp_hw_reset(fp_dev, delay); + } + fp_dev->irq_ignore = 0; + } + break; + + case SIFP_IOC_ENABLE_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] enable irq\n", __func__); + silfp_irq_enable(fp_dev); + break; + + case SIFP_IOC_DISABLE_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] disable irq\n", __func__); + silfp_irq_disable(fp_dev); + break; + + case SIFP_IOC_CLR_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] clear irq\n", __func__); + reinit_completion(&fp_dev->done); + break; + + case SIFP_IOC_WAIT_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] wait irq\n", __func__); + retval = __put_user(silfp_wait_irq(fp_dev)?1:0, (__u8 __user *)arg); + break; + + case SIFP_IOC_IRQ_STATUS: + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq status\n", __func__); + retval = __put_user((char)silfp_irq_status(fp_dev), (__u8 __user *)arg); + break; + + case SIFP_IOC_KEY_EVENT: + if (copy_from_user(&key, (struct fp_dev_key_t *)arg, sizeof(struct fp_dev_key_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] copy key fail?\n",__func__); + retval = -EFAULT; + } else { + retval = silfp_keyevent(fp_dev,&key); + } + break; + + case SIFP_IOC_SCR_STATUS: + if (arg) { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] put v = %d\n",(u8)(!fp_dev->scr_off)); + retval = __put_user((u8)(!fp_dev->scr_off), (__u8 __user *)arg); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] send v = %d\n",(u8)(!fp_dev->scr_off)); + silfp_netlink_send(fp_dev, fp_dev->scr_off?SIFP_NETLINK_SCR_OFF:SIFP_NETLINK_SCR_ON); + } + break; + + case SIFP_IOC_GET_VER: + if ( copy_to_user((void __user *)arg, (void *)FP_DEV_VERSION, sizeof(char)*7)) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_GET_VER] copy_to fail\n"); + retval = -EFAULT; + } + break; + + case SIFP_IOC_SET_KMAP: + if (!arg) { + retval = -EFAULT; + break; + } + if (copy_from_user(&fp_dev->keymap_cust.k, (void __user *)arg, sizeof(struct fp_dev_kmap_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_SET_KMAP] copy_from fail\n"); + retval = -EFAULT; + break; + } + + if (silfp_input_reinit(fp_dev)) { + retval = -EFAULT; + } + break; + + case SIFP_IOC_ACQ_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] acq spi\n", __func__); + retval = silfp_set_spi(fp_dev,true); + break; + + case SIFP_IOC_RLS_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] release spi\n", __func__); + retval = silfp_set_spi(fp_dev,false); + break; + + case SIFP_IOC_PKG_SIZE: + if (arg) { + LOG_MSG_DEBUG(DBG_LOG, "[IOC_PKG_SIZE] %d\n",(u8)(PKG_SIZE)); + retval = __put_user((u8)(PKG_SIZE), (__u8 __user *)arg); + } + break; + + case SIFP_IOC_DBG_LEVEL: + if (arg) { + unsigned char level = 0; + if (copy_from_user(&level, (void __user *)arg, sizeof(char)) || level > (uint8_t)ALL_LOG ) { + retval = -EFAULT; + break; + } + LOG_MSG_DEBUG(ERR_LOG, "debug level %d-->%d\n",debug_level,level); + retval = __put_user((u8)(debug_level), (__u8 __user *)arg); + debug_level = (fp_debug_level_t)level; + } + break; + + case SIFP_IOC_WAKELOCK: + if (arg) { + unsigned char lock = 0; + if (copy_from_user(&lock, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_WAKELOCK] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_wakelock_ctl(fp_dev, lock); + } + break; + + case SIFP_IOC_PWDN: + if (arg) { + unsigned char flag_avdd = 0; + if (copy_from_user(&flag_avdd, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PWDN] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_pwdn(fp_dev, flag_avdd); + } else { + silfp_pwdn(fp_dev, SIFP_PWDN_NONE); + } + + break; + +#ifdef PROC_NODE + case SIFP_IOC_PROC_NODE: + if (arg) { + if (copy_from_user(vendor_name, (void __user *)arg, PROC_VND_ID_LEN)) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PROC_NODE] copy_from fail\n"); + retval = -EFAULT; + break; + } + retval = silfp_proc_create_node(fp_dev); + } + break; +#endif /* PROC_NODE */ + + case SIFP_IOC_GET_TP_TOUCH_INFO: + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_GET_TP_TOUCH_INFO]enter\n"); + if (arg) { + if (copy_to_user((void __user *)arg, (void *)&fp_tpinfo, sizeof(fp_tpinfo))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_GET_TP_TOUCH_INFO] copy_to fail\n"); + retval = -EFAULT; + } + } + break; + case SIFP_IOC_SET_TP_MSG_REPORT_MODE: + LOG_MSG_DEBUG(INFO_LOG, "[SET_TP_MSG_REPORT_MODE]enter,arg =%d,lasttouchmode=%d\n", (int)arg, (int)lasttouchmode); + if(1 == lasttouchmode) { + lasttouchmode = arg;//arg =0 + LOG_MSG_DEBUG(INFO_LOG, " success lasttouchmode = %d\n", lasttouchmode); + } else { + LOG_MSG_DEBUG(ERR_LOG, "arg null,or mode is 0, do nothing!\n"); + } + break; + + case SIFP_IOC_REPORT_KEY: + LOG_MSG_DEBUG(INFO_LOG, "[SIFP_IOC_REPORT_KEY]enter,arg =%d\n", (int)arg); + input_report_key(fp_dev->input, KEY_F2, 1); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, KEY_F2, 0); + input_sync(fp_dev->input); + break; + default: + retval = -ENOTTY; + break; + } + + return retval; +} + +#ifdef CONFIG_COMPAT +static long +silfp_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return silfp_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define silfp_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int silfp_open(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(fp_dev, &device_list, device_entry) { + if (fp_dev->devt == inode->i_rdev) { + status = 0; + break; + } + } + if (status == 0) { + fp_dev->users++; + filp->private_data = fp_dev; + nonseekable_open(inode, filp); + } else { + LOG_MSG_DEBUG(ERR_LOG, "silfp: nothing for minor %d\n", iminor(inode)); + } + + mutex_unlock(&device_list_lock); + + return status; +} + +static int silfp_release(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = 0; + + mutex_lock(&device_list_lock); + fp_dev = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + fp_dev->users--; + if (!fp_dev->users) { + int dofree = 0; + + if (dofree) + kfree(fp_dev); + } + mutex_unlock(&device_list_lock); + + return status; +} + +static const struct file_operations silfp_dev_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 = silfp_ioctl, + .compat_ioctl = silfp_compat_ioctl, + .open = silfp_open, + .release = silfp_release, +#ifndef BSP_SIL_NETLINK + .read = silfp_read, + .poll = silfp_poll, +#endif /* !BSP_SIL_NETLINK */ +}; + +/*-------------------------------------------------------------------------*/ + +/* The main reason to have this class is to make mdev/udev create the + * /dev/spidevB.C character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +/*-------------------------------------------------------------------------*/ + +static int silfp_probe(struct platform_device *spi) +{ + struct silfp_data *fp_dev; + int status = 0; + //unsigned long minor; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] find silfp fingerprint.\n", __func__); + + /* Allocate driver data */ + fp_dev = kzalloc(sizeof(*fp_dev), GFP_KERNEL); + if (!fp_dev) { + return -ENOMEM; + } + + /* Initialize the driver data */ + fp_dev->spi = spi; + spin_lock_init(&fp_dev->spi_lock); + + wake_lock_init(&fp_dev->wakelock,WAKE_LOCK_SUSPEND,"silfp_wakelock"); + wake_lock_init(&fp_dev->wakelock_hal,WAKE_LOCK_SUSPEND,"silfp_wakelock_hal"); + INIT_LIST_HEAD(&fp_dev->device_entry); + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + + if (FP_DEV_MAJOR > 0) { + fp_dev->devt = MKDEV(FP_DEV_MAJOR, fp_dev->ref++); + status = register_chrdev_region(fp_dev->devt, 1, FP_DEV_NAME); + } else { + status = alloc_chrdev_region(&fp_dev->devt, fp_dev->ref++, 1, FP_DEV_NAME); + } + if (status < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] request devt fail, ret=%d.\n", __func__, status); + goto err_devt; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s], major=%d, minor=%d\n", __func__, MAJOR(fp_dev->devt), MINOR(fp_dev->devt)); + } + + fp_dev->dev = device_create(silfp_class, &fp_dev->spi->dev, fp_dev->devt, fp_dev, FP_DEV_NAME); + if (IS_ERR(fp_dev->dev)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] device_create() fail.\n", __func__); + status = -ENODEV; + goto err_dev; + } else { + mutex_lock(&device_list_lock); + list_add(&fp_dev->device_entry, &device_list); + mutex_unlock(&device_list_lock); + LOG_MSG_DEBUG(INFO_LOG, "[%s] device create success.\n", __func__); + } + + cdev_init(&fp_dev->cdev, &silfp_dev_fops); + fp_dev->cdev.owner = THIS_MODULE; + status = cdev_add(&fp_dev->cdev, fp_dev->devt, 1); + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] cdev_add() fail, ret=%d.\n", __func__, status); + goto err_cdev; + } + + atomic_set(&fp_dev->init,0); + status = silfp_init(fp_dev); + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] silfp_init fail ret=%d.\n", __func__, status); + goto err_cdev; + } +#ifdef PROC_NODE + silfp_proc_init(fp_dev); +#endif /* PROC_NODE */ + platform_set_drvdata(spi, fp_dev); + optical_fp = fp_dev; + + status = sysfs_create_group(&spi->dev.kobj, + &sl_attribute_group); + if (status) + pr_err("%s:could not create sysfs\n", __func__); + return status; + +err_cdev: + list_del(&fp_dev->device_entry); + +err_dev: + unregister_chrdev_region(fp_dev->devt, 1); + +err_devt: + platform_set_drvdata(spi, NULL); + fp_dev->spi = NULL; + kfree(fp_dev); + fp_dev = NULL; + + return status; +} + +static int silfp_remove(struct platform_device *spi) +{ + struct silfp_data *fp_dev = platform_get_drvdata(spi); + + wake_lock_destroy(&fp_dev->wakelock); + wake_lock_destroy(&fp_dev->wakelock_hal); + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&fp_dev->spi_lock); + fp_dev->spi = NULL; + spin_unlock_irq(&fp_dev->spi_lock); + + silfp_exit(fp_dev); + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&fp_dev->device_entry); + device_destroy(silfp_class, fp_dev->devt); + if (fp_dev->users == 0) + kfree(fp_dev); + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct of_device_id sildev_dt_ids[] = { + { .compatible = "sil,silead_fp" }, + { .compatible = "sil,silead-fp" }, + { .compatible = "sil,fingerprint" }, + { .compatible = "sil,silead_fp-pins" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sildev_dt_ids); + +static struct platform_driver silfp_driver = { + .driver = { + .name = "silead_fp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sildev_dt_ids), + }, + .probe = silfp_probe, + .remove = silfp_remove, + + /* NOTE: suspend/resume methods are not necessary here. + * We don't do anything except pass the requests to/from + * the underlying controller. The refrigerator handles + * most issues; the controller driver handles the rest. + */ +}; + +/*-------------------------------------------------------------------------*/ + +static int __init silfp_dev_init(void) +{ + int status = 0; + + LOG_MSG_DEBUG(ERR_LOG, "SILEAD_FP Driver, Version: %s.\n", FP_DEV_VERSION); + /* 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 ((fp_version != 0x05)) + return 0; + status = register_chrdev(FP_DEV_MAJOR, "sil", &silfp_dev_fops); + if (status < 0) { + return status; + } + + silfp_class = class_create(THIS_MODULE, "silead_fp"); + if (IS_ERR(silfp_class)) { + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + return PTR_ERR(silfp_class); + } + + status = platform_driver_register(&silfp_driver); + if (status < 0) { + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + LOG_MSG_DEBUG(ERR_LOG, "[%s] spi_register_driver fail ret=%d.\n", __func__, status); + return status; + } + silfp_wq = create_singlethread_workqueue("silfp_wq"); + return status; +} +module_init(silfp_dev_init); + +static void __exit silfp_dev_exit(void) +{ + platform_driver_unregister(&silfp_driver); + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); +} + +module_exit(silfp_dev_exit); + +MODULE_AUTHOR("Bill Yu "); +MODULE_DESCRIPTION("Silead Fingerprint driver for GSL61XX/GSL62XX series."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("sil:silead_fp"); diff --git a/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.c b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.c new file mode 100755 index 000000000000..78880057d8c9 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.c @@ -0,0 +1,606 @@ +/* + * @file silead_fp_qcom.c + * @brief Contains silead_fp device implements for Qualcomm platform. + * + * + * Copyright 2016-2018 Slead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/20 0.1.1 Default wait 3ms after reset + * Bill Yu 2018/6/5 0.1.2 Support chip enter power down + * + */ + +#ifdef BSP_SIL_PLAT_QCOM + +#ifndef __SILEAD_FP_QCOM__ +#define __SILEAD_FP_QCOM__ + +#include + +#ifndef INIT_COMPLETION +#define INIT_COMPLETION(x) ((x).done = 0) +#endif /* INIT_COMPLETION */ +/* Qualcomm MNC platform, need define this. */ +#define reinit_completion(x) INIT_COMPLETION(*(x)) + +#define SPI_PINCTRL_STATE_DEFAULT "spi_default" +#define SPI_PINCTRL_STATE_SLEEP "spi_sleep" + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id); +static void silfp_work_func(struct work_struct *work); +static int silfp_input_init(struct silfp_data *fp_dev); + +int sl_pinctrl_init(struct silfp_data *fp_dev) +{ + int ret = 0; + struct device *dev = &fp_dev->spi->dev; + + fp_dev->sl_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(fp_dev->sl_pinctrl)) { + dev_err(dev, "Target does not use pinctrl\n"); + ret = PTR_ERR(fp_dev->sl_pinctrl); + goto err; + } + + fp_dev->gpio_state_enable = + pinctrl_lookup_state(fp_dev->sl_pinctrl, "fp_en_init"); + if (IS_ERR_OR_NULL(fp_dev->gpio_state_enable)) { + dev_err(dev, "Cannot get active pinstate\n"); + ret = PTR_ERR(fp_dev->gpio_state_enable); + goto err; + } + + ret = pinctrl_select_state(fp_dev->sl_pinctrl, + fp_dev->gpio_state_enable); + if (ret) { + pr_err("can not set %s pins\n", "fp_en_init"); + goto err; + } + + return 0; +err: + fp_dev->sl_pinctrl = NULL; + fp_dev->gpio_state_enable = NULL; + return ret; +} + +/* -------------------------------------------------------------------- */ +/* power supply */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_poweron(struct silfp_data *fp_dev) +{ + int err = 0; + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo ) { + err = regulator_set_voltage(fp_dev->avdd_ldo, + AVDD_MIN, AVDD_MAX); /*set 2.8v*/ + err = regulator_set_load(fp_dev->avdd_ldo, CURRENT); + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "poweron: vreg mode(err:%d)\n", err); + err = regulator_enable(fp_dev->avdd_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } + if (fp_dev->vddio_ldo) { + err = regulator_set_voltage(fp_dev->vddio_ldo, + VDDIO_MIN, VDDIO_MAX); /*set 1.8v*/ + err = regulator_set_load(fp_dev->vddio_ldo, 200000);//100ma + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "poweron: vreg mode(err:%d)\n", err); + err = regulator_enable(fp_dev->vddio_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + if ( fp_dev->pin.pins_avdd_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_avdd_h); + } + if ( fp_dev->pin.pins_vddio_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_vddio_h); + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port > 0 ) { + err = gpio_direction_output(fp_dev->avdd_port, 1); + } + if ( fp_dev->vddio_port > 0 ) { + err = gpio_direction_output(fp_dev->vddio_port, 1); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 0; + LOG_MSG_DEBUG(INFO_LOG, "%s: power supply ret:%d \n", __func__, err); +} + +static void silfp_hw_poweroff(struct silfp_data *fp_dev) +{ + int err = 0; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ((fp_dev->power_is_off == 0) && + fp_dev->avdd_ldo && + (regulator_is_enabled(fp_dev->avdd_ldo) > 0)) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + err = regulator_set_load(fp_dev->avdd_ldo, 0); + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "poweroff: vreg mode(err:%d)\n", err); + } + if ((fp_dev->power_is_off == 0) && + fp_dev->vddio_ldo && + (regulator_is_enabled(fp_dev->vddio_ldo) > 0)) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + err = regulator_set_load(fp_dev->vddio_ldo, 0); + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "poweroff: vreg mode(err:%d)\n", err); + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + //fp_dev->pin.pins_avdd_h = NULL; + //fp_dev->pin.pins_vddio_h = NULL; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port > 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + } + if ( fp_dev->vddio_port > 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 1; +} + +static void silfp_power_deinit(struct silfp_data *fp_dev) +{ + int err = 0; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if (fp_dev->avdd_ldo) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + err = regulator_set_load(fp_dev->avdd_ldo, 0); + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "deinit: vreg mode(err:%d)\n", err); + regulator_put(fp_dev->avdd_ldo); + fp_dev->avdd_ldo = NULL; + } + if (fp_dev->vddio_ldo) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + err = regulator_set_load(fp_dev->vddio_ldo, 0); + if (err < 0) + LOG_MSG_DEBUG(INFO_LOG, + "deinit: Failed vreg mode(err:%d)\n", err); + regulator_put(fp_dev->vddio_ldo); + fp_dev->vddio_ldo = NULL; + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + fp_dev->pin.pins_avdd_h = NULL; + fp_dev->pin.pins_vddio_h = NULL; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port > 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + gpio_free(fp_dev->avdd_port); + fp_dev->avdd_port = 0; + } + if ( fp_dev->vddio_port > 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + gpio_free(fp_dev->vddio_port); + fp_dev->vddio_port = 0; + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +} + +/* -------------------------------------------------------------------- */ +/* hardware reset */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay) +{ + LOG_MSG_DEBUG(INFO_LOG, + "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if (fp_dev->rst_port > 0) { + gpio_direction_output(fp_dev->rst_port, 0); + mdelay((delay?delay:1)*RESET_TIME_MULTIPLE); + gpio_direction_output(fp_dev->rst_port, 1); + + mdelay((delay?delay:1)*RESET_TIME_MULTIPLE); + } +} + +/* -------------------------------------------------------------------- */ +/* power down */ +/* -------------------------------------------------------------------- */ +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if (SIFP_PWDN_FLASH == flag_avdd) { + silfp_hw_poweroff(fp_dev); + msleep(200*RESET_TIME_MULTIPLE); + silfp_hw_poweron(fp_dev); + } + if ( fp_dev->rst_port > 0 ) { + gpio_direction_output(fp_dev->rst_port, 0); + } + + if (SIFP_PWDN_POWEROFF == flag_avdd) { + silfp_hw_poweroff(fp_dev); + } +} + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_parse_dts(struct silfp_data* fp_dev) +{ + int ret; +#ifndef QSEE_V4 + /* Get the pinctrl node */ + fp_dev->pin.pinctrl = devm_pinctrl_get(&fp_dev->spi->dev); + if (IS_ERR_OR_NULL(fp_dev->pin.pinctrl)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl\n", __func__); + return PTR_ERR(fp_dev->pin.pinctrl); + } + + /* Get the active setting */ + fp_dev->pin.active = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(fp_dev->pin.active)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state active\n",__func__); + return PTR_ERR(fp_dev->pin.active); + } + + /* Get power settings */ +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + fp_dev->pin.pins_avdd_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "avdd-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_avdd_h)) { + fp_dev->pin.pins_avdd_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp avdd-enable\n", __func__); + // Ignore error + } + + fp_dev->pin.pins_vddio_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "vddio-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_vddio_h)) { + fp_dev->pin.pins_vddio_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp vddio-enable\n", __func__); + // Ignore error + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ +#endif /* QSEE_V4 */ + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + // Todo: use correct settings. + fp_dev->avdd_ldo = regulator_get(&fp_dev->spi->dev, "avdd"); + // fp_dev->vddio_ldo= regulator_get(&fp_dev->spi->dev, "vddio"); +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifndef QSEE_V4 + /* Get sleep settings */ + fp_dev->pin.sleep = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(fp_dev->pin.sleep)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state sleep\n",__func__); + return PTR_ERR(fp_dev->pin.sleep); + } + + /* Get iface_clk info */ + fp_dev->pin.iface_clk = clk_get(&fp_dev->spi->dev, "iface_clk"); + if (IS_ERR(fp_dev->pin.iface_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get iface_clk %ld\n", __func__, PTR_ERR(fp_dev->pin.iface_clk)); + return PTR_ERR(fp_dev->pin.iface_clk); + } + + /* Get core_clk info */ + fp_dev->pin.core_clk = clk_get(&fp_dev->spi->dev, "core_clk"); + if (IS_ERR(fp_dev->pin.core_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get core_clk %p\n", __func__, fp_dev->pin.core_clk); + return PTR_ERR(fp_dev->pin.core_clk); + } +#else + /* Get the SPI Max speed */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"spi-max-frequency", &fp_dev->pin.max_speed_hz); + if (ret) { + fp_dev->pin.max_speed_hz = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting spi max speed\n"); + } +#endif /* QSEE_V4 */ + + /* Get the QUP ID (#1-12) */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"qcom,qup-id", &fp_dev->pin.qup_id); + if (ret) { + fp_dev->pin.qup_id = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting qup_id\n"); + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] done (%d).\n",__func__,ret); + /* + Grab SPI master lock for exclusive access + call spi_bus_unlock to unlock the lock. + */ + //spi_bus_lock(fp_dev->spi->master); + return ret; +} + +#ifndef QSEE_V4 +static int spi_set_pinctrl(struct silfp_data* fp_dev, bool active) +{ + int ret = -1; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support\n", __func__); + return ret; + } + if (active) { /* Change to active settings */ + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.active); + } else { + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.sleep); + } + + LOG_MSG_DEBUG(INFO_LOG, "%s: pinctrl_select_state ret:%d Setting:%d\n", __func__, ret, active); + return ret; +} + +static int spi_set_clks(struct silfp_data* fp_dev, bool enable) +{ + int ret = -1; + int clk = 19200000; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(INFO_LOG, "%s: not support\n", __func__); + return ret; + } + if (enable) { + /* Enable the spi clocks */ + ret = clk_set_rate(fp_dev->pin.core_clk, fp_dev->spi->max_speed_hz); + if (ret) { + while ( clk > fp_dev->spi->max_speed_hz ) { + clk >>= 1; + } + if ( clk < 4800000 ) { + clk = 4800000; + } + ret = clk_set_rate(fp_dev->pin.core_clk, clk); + if ( ret ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error setting clk_rate:%d, ret=%d\n",__func__, clk,ret); + } else { + fp_dev->spi->max_speed_hz = clk; + } + } + ret = clk_prepare_enable(fp_dev->pin.core_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling core clk, ret=%d\n",__func__,ret); + } + ret = clk_prepare_enable(fp_dev->pin.iface_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling iface clk, ret=%d\n",__func__,ret); + } + } else { + /* Disable the clocks */ + clk_disable_unprepare(fp_dev->pin.iface_clk); + clk_disable_unprepare(fp_dev->pin.core_clk); + ret = 0; + } + + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d), speed = %d.\n",__func__,ret,fp_dev->spi->max_speed_hz ); + return ret; +} + +static int spi_set_fabric(struct silfp_data* fp_dev, bool active) +{ + int ret; + struct spi_master *master = fp_dev->spi->master; + + if (active) { + ret = master->prepare_transfer_hardware(master); + } else { + ret = master->unprepare_transfer_hardware(master); + } + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d).\n",__func__,ret); + return ret; +} +#endif /* !QSEE_V4 */ + +static int silfp_set_spi(struct silfp_data *fp_dev, bool enable) +{ + int ret = -ENOENT; +#ifndef QSEE_V4 + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active) || !fp_dev->pin.qup_id ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support!\n", __func__); + return ret; + } + + if ( enable && !atomic_read(&fp_dev->spionoff_count) ) { + atomic_inc(&fp_dev->spionoff_count); + ret = spi_set_pinctrl(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_clks(fp_dev, enable); + } else if (atomic_read(&fp_dev->spionoff_count)) { + atomic_dec(&fp_dev->spionoff_count); + //spi_change_pipe_owner(false); + ret = spi_set_clks(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_pinctrl(fp_dev, enable); + } +#else + LOG_MSG_DEBUG(ERR_LOG, "%s: qsee4 no needed!\n", __func__); +#endif /* !QSEE_V4 */ + return ret; +} + +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info) +{ + int status = 0; + int ret; + + if (atomic_read(&fp_dev->init)) { + atomic_inc(&fp_dev->init); + LOG_MSG_DEBUG(DBG_LOG, "[%s] dev already init(%d).\n",__func__,atomic_read(&fp_dev->init)); + return status; + } + + silfp_parse_dts(fp_dev); +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + fp_dev->avdd_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "avdd-gpios", 0); + fp_dev->vddio_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "vddio-gpios", 0); + if (fp_dev->avdd_port > 0 ) { + gpio_free(fp_dev->avdd_port); + ret = gpio_request(fp_dev->avdd_port, "SILFP_AVDD_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->avdd_port, ret); + status = -ENODEV; + goto err_avdd; + } + } + + if (fp_dev->vddio_port > 0 ) { + gpio_free(fp_dev->vddio_port); + ret = gpio_request(fp_dev->vddio_port, "SILFP_VDDIO_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->vddio_port, ret); + status = -ENODEV; + goto err_vddio; + } + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + silfp_hw_poweron(fp_dev); + fp_dev->int_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "irq-gpios", 0); + fp_dev->rst_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "rst-gpios", 0); + LOG_MSG_DEBUG(INFO_LOG, "[%s] int_port %d, rst_port %d.\n",__func__,fp_dev->int_port,fp_dev->rst_port); + if (fp_dev->int_port > 0 ) { + gpio_free(fp_dev->int_port); + } + + if (fp_dev->rst_port > 0 ) { + gpio_free(fp_dev->rst_port); + } + + ret = gpio_request(fp_dev->int_port, "SILFP_INT_IRQ"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret); + status = -ENODEV; + goto err; + } else { + gpio_direction_input(fp_dev->int_port); + fp_dev->irq = gpio_to_irq(fp_dev->int_port); + fp_dev->irq_is_disable = 0; + + ret = request_irq(fp_dev->irq, + silfp_irq_handler, + IRQ_TYPE_EDGE_RISING, //IRQ_TYPE_LEVEL_HIGH, //irq_table[ts->int_trigger_type], + "silfp", + fp_dev); + if ( ret < 0 ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Filed to request_irq (%d), ert=%d",__func__,fp_dev->irq, ret); + status = -ENODEV; + goto err_irq; + } else { + LOG_MSG_DEBUG(INFO_LOG,"[%s] Enable_irq_wake.\n",__func__); + enable_irq_wake(fp_dev->irq); + silfp_irq_disable(fp_dev); + } + } + + if (fp_dev->rst_port > 0 ) { + ret = gpio_request(fp_dev->rst_port, "SILFP_RST_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->rst_port, ret); + status = -ENODEV; + goto err_rst; + } else { + gpio_direction_output(fp_dev->rst_port, 0); + } + } + + if (!ret) { + if (silfp_input_init(fp_dev)) { + goto err_input; + } + atomic_set(&fp_dev->init,1); + } + dev_info->reserve = PKG_SIZE; + dev_info->reserve <<= 12; +#ifdef QSEE_V4 + if (fp_dev->pin.max_speed_hz) { + dev_info->speed = fp_dev->pin.max_speed_hz; + } +#endif /* QSEE_V4 */ + + if (dev_info && fp_dev->pin.qup_id && (fp_dev->pin.qup_id < 16)) { + dev_info->dev_id = fp_dev->pin.qup_id; + strncpy(dev_info->ta,TANAME,sizeof(dev_info->ta)); + } + status = sl_pinctrl_init(fp_dev); + if (status < 0) + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed init gpio %d", + __func__, status); + return status; + +err_input: + if (fp_dev->rst_port > 0 ) { + gpio_free(fp_dev->rst_port); + } + +err_rst: + free_irq(fp_dev->irq, fp_dev); + gpio_direction_input(fp_dev->int_port); + +err_irq: + gpio_free(fp_dev->int_port); + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + gpio_free(fp_dev->vddio_port); +err_vddio: + gpio_free(fp_dev->avdd_port); + +err_avdd: + fp_dev->avdd_port = 0; + fp_dev->vddio_port = 0; +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + +err: + fp_dev->int_port = 0; + fp_dev->rst_port = 0; + + return status; +} + +#endif /* __SILEAD_FP_QCOM__ */ + +#endif /* BSP_SIL_PLAT_QCOM */ + +/* End of file spilead_fp_qcom.c */ diff --git a/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.h b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.h new file mode 100755 index 000000000000..c1e02f0a1bf3 --- /dev/null +++ b/drivers/oneplus/drivers/input/fingerprint/silead/silead_fp_qcom.h @@ -0,0 +1,48 @@ +/* + * @file silead_fp_qcom.h + * @brief Contains silead_fp Qualcomm platform specific head file. + * + * + * Copyright 2016-2018 Slead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * + */ + +#ifndef __SILEAD_FP_QCOM_H__ +#define __SILEAD_FP_QCOM_H__ + +#include +#include +#include + +struct fp_plat_t { + u32 qup_id; +#ifdef QSEE_V4 + u32 max_speed_hz; +#else + /* pinctrl info */ + struct pinctrl *pinctrl; + struct pinctrl_state *active; + struct pinctrl_state *sleep; +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + struct pinctrl_state *pins_avdd_h, *pins_vddio_h; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + /* clock info */ + struct clk *core_clk; + struct clk *iface_clk; +#endif /* QSEE_V4 */ +}; + +#endif /* __SILEAD_FP_QCOM_H__ */ + +/* End of file silead_fp_qcom.h */ diff --git a/drivers/oneplus/drivers/oem_debug/Makefile b/drivers/oneplus/drivers/oem_debug/Makefile new file mode 100755 index 000000000000..0a8b0fff6d59 --- /dev/null +++ b/drivers/oneplus/drivers/oem_debug/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OEM_FORCE_DUMP) += oem_force_dump.o diff --git a/drivers/oneplus/drivers/oem_debug/oem_force_dump.c b/drivers/oneplus/drivers/oem_debug/oem_force_dump.c new file mode 100644 index 000000000000..4f2e8beb09e8 --- /dev/null +++ b/drivers/oneplus/drivers/oem_debug/oem_force_dump.c @@ -0,0 +1,215 @@ +/* + * oem_force_dump.c + * + * drivers supporting debug functions for Oneplus device. + * + * hefaxi@filesystems, 2015/07/03. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sock *nl_sk; +static int fd = -1; +static struct workqueue_struct *smg_workwq; +static struct work_struct smg_work; + +#define MAX_MSGSIZE 1024 +static int message_state = -1; + + +/* + * the way goto force dump: + * 1. press the voluemup key and then relase it. + * 2. press the volumedown key and then relase it. + * 3. long press volumeup key, without release it. + * 4. press twice power key, and release it. + * 5. release the volumeup key. + * 6. presss the volumeup key, without release it. + * 7. press the power key. + * after those step, the device will goto the force dump. + */ +void oem_check_force_dump_key(unsigned int code, int value) +{ + static enum { NONE, STEP1, STEP2, STEP3, STEP4, STEP5, + STEP6, STEP7, STEP8, STEP9, STEP10, STEP11, STEP_DEBUG1} state = NONE; + + switch (state) { + case NONE: + if (code == KEY_VOLUMEUP && value) + state = STEP1; + else + state = NONE; + break; + case STEP1: + if (code == KEY_VOLUMEUP && !value) + state = STEP2; + else + state = NONE; + break; + case STEP2: + if (code == KEY_VOLUMEDOWN && value) + state = STEP3; + else + state = NONE; + break; + case STEP3: + if (code == KEY_VOLUMEDOWN && !value) + state = STEP4; + else + state = NONE; + break; + case STEP4: + if (code == KEY_VOLUMEUP && value) + state = STEP5; + else + state = NONE; + break; + case STEP5: + if (code == KEY_POWER && value) + state = STEP6; + else + state = NONE; + break; + case STEP6: + if (code == KEY_POWER && !value) + state = STEP7; + else + state = NONE; + break; + case STEP7: + if (code == KEY_POWER && value) + state = STEP8; + else + state = NONE; + break; + case STEP8: + if (code == KEY_POWER && !value) + state = STEP9; + else + state = NONE; + break; + case STEP9: + if (code == KEY_VOLUMEUP && !value) + state = STEP10; + else + state = NONE; + break; + case STEP10: + if (code == KEY_VOLUMEUP && value) + state = STEP11; + else if (code == KEY_VOLUMEDOWN && value) + state = STEP_DEBUG1; + else + state = NONE; + break; + case STEP11: + if (code == KEY_POWER && value) { + if (oem_get_download_mode()) + panic("Force Dump"); + } else + state = NONE; + break; + + case STEP_DEBUG1: + if (code == KEY_POWER && value) { + message_state = 1; + state = NONE; + } else if (code == KEY_VOLUMEDOWN && !value) { + message_state = 2; + queue_work(smg_workwq, &smg_work); + state = NONE; + } else + state = NONE; + break; + } +} + +static void send_msg_worker(struct work_struct *work) +{ + if (message_state == 1) + send_msg("Enable DEBUG!"); + else if (message_state == 2) { + pr_info("force oem serial\n"); + msm_serial_oem_init(); + send_msg("ENABLE_OEM_FORCE_SERIAL"); + } + message_state = 0; +} + +void send_msg(char *message) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int len = NLMSG_SPACE(MAX_MSGSIZE); + + if (!message || !nl_sk) + return; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return; + nlh = nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0); + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + strlcpy(NLMSG_DATA(nlh), message, MAX_MSGSIZE); + netlink_unicast(nl_sk, skb, fd, MSG_DONTWAIT); +} + +void recv_nlmsg(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = nlmsg_hdr(skb); + + if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) + return; + fd = nlh->nlmsg_pid; + pr_err("received:%s %d\n", (char *)NLMSG_DATA(nlh), fd); +} + +struct netlink_kernel_cfg nl_kernel_cfg = { + .groups = 0, + .flags = 0, + .input = recv_nlmsg, + .cb_mutex = NULL, + .bind = NULL, + .compare = NULL, +}; + +int op_netlink_init(void) +{ + nl_sk = netlink_kernel_create(&init_net, NETLINK_ADB, &nl_kernel_cfg); + if (!nl_sk) { + pr_err("%s: Create netlink socket error.\n", __func__); + return 1; + } + smg_workwq = create_singlethread_workqueue("oem_key_dump"); + if (!smg_workwq) { + pr_err("%s: Create oem_key_dump error.\n", __func__); + return 1; + } + INIT_WORK(&smg_work, send_msg_worker); + pr_err("%s\n", __func__); + return 0; +} + +static void op_netlink_exit(void) +{ + if (nl_sk != NULL) + sock_release(nl_sk->sk_socket); + if (smg_workwq != NULL) + destroy_workqueue(smg_workwq); + pr_err("%s\n", __func__); +} + +module_init(op_netlink_init); +module_exit(op_netlink_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/oneplus/drivers/oem_trace/Makefile b/drivers/oneplus/drivers/oem_trace/Makefile new file mode 100644 index 000000000000..74399025931a --- /dev/null +++ b/drivers/oneplus/drivers/oem_trace/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OEM_TRACE_SUPPORT) += oem_trace.o diff --git a/drivers/oneplus/drivers/oem_trace/oem_trace.c b/drivers/oneplus/drivers/oem_trace/oem_trace.c new file mode 100755 index 000000000000..beeeec92ba9d --- /dev/null +++ b/drivers/oneplus/drivers/oem_trace/oem_trace.c @@ -0,0 +1,544 @@ +/* + * 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_node_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_node_page_state(NR_FILE_DIRTY)), + K(global_node_page_state(NR_WRITEBACK)), +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + K(global_page_state(NR_PAGETABLE) + + global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR), +#else + K(global_page_state(NR_PAGETABLE)), +#endif + K(global_node_page_state(NR_FILE_MAPPED)), + K(global_node_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_KB) * THREAD_SIZE / 1024, + K(global_page_state(NR_PAGETABLE)), +#ifdef CONFIG_QUICKLIST + K(quicklist_total_size()), +#endif + K(global_node_page_state(NR_UNSTABLE_NFS)), + K(global_page_state(NR_BOUNCE)), + K(global_node_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) { + kfree(lbuf); + 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/drivers/oem_trace/oem_trace.h b/drivers/oneplus/drivers/oem_trace/oem_trace.h new file mode 100755 index 000000000000..d7105943d746 --- /dev/null +++ b/drivers/oneplus/drivers/oem_trace/oem_trace.h @@ -0,0 +1,10 @@ +/*add by jiachenghui for support oem trace,2015/05/09*/ +#ifndef _OEM_TRACE_H +#define _OEM_TRACE_H + +#include + +#define IOCTL_TRACE_UPDATE_REBOOTFLAG _IO('t', 3) + + +#endif diff --git a/drivers/oneplus/drivers/param_read_write/Makefile b/drivers/oneplus/drivers/param_read_write/Makefile new file mode 100644 index 000000000000..0c099c5b06e8 --- /dev/null +++ b/drivers/oneplus/drivers/param_read_write/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PARAM_READ_WRITE) += param_read_write.o diff --git a/drivers/oneplus/drivers/param_read_write/param_read_write.c b/drivers/oneplus/drivers/param_read_write/param_read_write.c new file mode 100644 index 000000000000..a61faadcb6da --- /dev/null +++ b/drivers/oneplus/drivers/param_read_write/param_read_write.c @@ -0,0 +1,354 @@ +/* + * 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 DISABLE_PARAM_DEBUG_LOG + +#define PARAM_PARTITION "/dev/block/bootdevice/by-name/param" +#define READ_CHUNK_MAX_SIZE (1024) +#define WRITE_CHUNK_MAX_SIZE (1024) +#ifdef CONFIG_DEBUG_PARAM_DUMP +static uint default_param_data_dump_size= DEFAULT_PARAM_DUMP_SIZE; +#endif + +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; +int restart_08_count; +int restart_other_count; + +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); + +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); + + #ifndef DISABLE_PARAM_DEBUG_LOG + pr_info("%s[%d] sid_index = %d offset = %d buf = %p length = %d\n", + __func__, __LINE__, sid_index, offset, buf, length); + #endif + + 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 int param_get_restart_08_count(char *val, const struct kernel_param *kp) +{ + int cnt; + + 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, 0664); + +static int param_get_restart_other_count(char *val, + const struct kernel_param *kp) +{ + int cnt; + + 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, 0664); + +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_init(void) +{ +#ifdef CONFIG_DEBUG_PARAM_DUMP + int i; +#endif + int ret = 0; + + 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); + +#ifdef CONFIG_DEBUG_PARAM_DUMP + for (i = 0; i < NUM_PARAM_PLAINTEXT_SEGMENT; i++) { + 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); + } +#endif + + param_init_done= 1; + + if (misc_register(¶m_misc)) + pr_err("misc_register failure %d\n", ret); + return ret; +} +device_initcall(param_init); diff --git a/drivers/oneplus/drivers/sysrq/Makefile b/drivers/oneplus/drivers/sysrq/Makefile new file mode 100644 index 000000000000..84ab8e66944a --- /dev/null +++ b/drivers/oneplus/drivers/sysrq/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OEM_DEBUG_SUPPORT) += sysrq_x.o diff --git a/drivers/oneplus/drivers/sysrq/sysrq_x.c b/drivers/oneplus/drivers/sysrq/sysrq_x.c new file mode 100755 index 000000000000..77b5858a44f4 --- /dev/null +++ b/drivers/oneplus/drivers/sysrq/sysrq_x.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + + +static void sysrq_handle_init_state(int key) +{ + struct task_struct *p = NULL; + + rcu_read_lock(); + for_each_process(p) { + if (p->pid == 1) { + sched_show_task(p); + break; + } + } + rcu_read_unlock(); +} + +static struct sysrq_key_op sysrq_init_state_op = { + .handler = sysrq_handle_init_state, + .help_msg = "show-init-state(x)", + .action_msg = "Show init process state", + .enable_mask = SYSRQ_ENABLE_DUMP, +}; + +static int __init op_sysrq_init(void) +{ + return register_sysrq_key('x', &sysrq_init_state_op); +} + +module_init(op_sysrq_init); + diff --git a/drivers/oneplus/fs/Kconfig b/drivers/oneplus/fs/Kconfig new file mode 100644 index 000000000000..d422a8e412bf --- /dev/null +++ b/drivers/oneplus/fs/Kconfig @@ -0,0 +1,2 @@ +source "drivers/oneplus/fs/exfat/Kconfig" +source "drivers/oneplus/fs/proc/Kconfig" diff --git a/drivers/oneplus/fs/Makefile b/drivers/oneplus/fs/Makefile new file mode 100644 index 000000000000..294d62be4122 --- /dev/null +++ b/drivers/oneplus/fs/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_EXFAT_FS) += exfat/ +obj-$(CONFIG_PROC_FS) += proc/ diff --git a/drivers/oneplus/fs/exfat/Kconfig b/drivers/oneplus/fs/exfat/Kconfig new file mode 100644 index 000000000000..78b32aa2ca19 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/LICENSE b/drivers/oneplus/fs/exfat/LICENSE new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/Makefile b/drivers/oneplus/fs/exfat/Makefile new file mode 100644 index 000000000000..711ed87f991b --- /dev/null +++ b/drivers/oneplus/fs/exfat/Makefile @@ -0,0 +1,54 @@ +# +# 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) +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/drivers/oneplus/fs/exfat/README.md b/drivers/oneplus/fs/exfat/README.md new file mode 100644 index 000000000000..feab40038352 --- /dev/null +++ b/drivers/oneplus/fs/exfat/README.md @@ -0,0 +1,98 @@ +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. + + +Installing as a DKMS module: +================================= + +You can have even more fun with exfat-nofuse by installing it as a DKMS module has the main advantage of being auto-compiled (and thus, possibly surviving) between kernel upgrades. + +First, get dkms. On Ubuntu this should be: + + sudo apt install dkms + +Then copy the root of this repository to /usr/share: + + sudo cp -R . /usr/src/exfat-1.2.8 (or whatever version number declared on dkms.conf is) + sudo dkms add -m exfat -v 1.2.8 + +Build and load the module: + + sudo dkms build -m exfat -v 1.2.8 + sudo dkms install -m exfat -v 1.2.8 + +Now you have a proper dkms module that will work for a long time... hopefully. + + + +Free Software for the Free Minds! +================================= diff --git a/drivers/oneplus/fs/exfat/dkms.conf b/drivers/oneplus/fs/exfat/dkms.conf new file mode 100644 index 000000000000..d873c0aef4fa --- /dev/null +++ b/drivers/oneplus/fs/exfat/dkms.conf @@ -0,0 +1,7 @@ +PACKAGE_NAME="exfat" +PACKAGE_VERSION="1.2.8" +MAKE="KDIR=/lib/modules/$kernelver/build MDIR=/lib/modules/$kernelver make" +CLEAN="make clean" +BUILT_MODULE_NAME[0]="exfat" +AUTOINSTALL="yes" +DEST_MODULE_LOCATION="/extra" diff --git a/drivers/oneplus/fs/exfat/exfat-km.mk b/drivers/oneplus/fs/exfat/exfat-km.mk new file mode 100644 index 000000000000..4e3ef07b36ec --- /dev/null +++ b/drivers/oneplus/fs/exfat/exfat-km.mk @@ -0,0 +1,11 @@ +EXFAT_FOLDER ?= external/exfat-nofuse + +EXFAT_MODULE: + make clean -C $(EXFAT_FOLDER) KDIR=$(KERNEL_OUT) + make -j8 -C $(EXFAT_FOLDER) ARCH=arm KDIR=$(KERNEL_OUT) \ + $(if $(ARM_CROSS_COMPILE),$(ARM_CROSS_COMPILE),$(KERNEL_CROSS_COMPILE)) + mv $(EXFAT_FOLDER)/exfat.ko $(KERNEL_MODULES_OUT) + $(if $(ARM_EABI_TOOLCHAIN),$(ARM_EABI_TOOLCHAIN)/arm-eabi-strip, \ + $(KERNEL_TOOLCHAIN_PATH)strip) --strip-unneeded $(KERNEL_MODULES_OUT)/exfat.ko + +TARGET_KERNEL_MODULES += EXFAT_MODULE diff --git a/drivers/oneplus/fs/exfat/exfat_api.c b/drivers/oneplus/fs/exfat/exfat_api.c new file mode 100644 index 000000000000..32b29f0dd8d9 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_api.h b/drivers/oneplus/fs/exfat/exfat_api.h new file mode 100644 index 000000000000..84bdf612a1e6 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_bitmap.c b/drivers/oneplus/fs/exfat/exfat_bitmap.c new file mode 100644 index 000000000000..b0672dd073fc --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_bitmap.h b/drivers/oneplus/fs/exfat/exfat_bitmap.h new file mode 100644 index 000000000000..4f482c7b28cc --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_blkdev.c b/drivers/oneplus/fs/exfat/exfat_blkdev.c new file mode 100644 index 000000000000..eaccfd84e9f9 --- /dev/null +++ b/drivers/oneplus/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, sector_t 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, sector_t 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/drivers/oneplus/fs/exfat/exfat_blkdev.h b/drivers/oneplus/fs/exfat/exfat_blkdev.h new file mode 100644 index 000000000000..3363b591caeb --- /dev/null +++ b/drivers/oneplus/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, sector_t secno, struct buffer_head **bh, u32 num_secs, s32 read); +s32 bdev_write(struct super_block *sb, sector_t secno, struct buffer_head *bh, u32 num_secs, s32 sync); +s32 bdev_sync(struct super_block *sb); + +#endif /* _EXFAT_BLKDEV_H */ diff --git a/drivers/oneplus/fs/exfat/exfat_cache.c b/drivers/oneplus/fs/exfat/exfat_cache.c new file mode 100644 index 000000000000..4130102e3739 --- /dev/null +++ b/drivers/oneplus/fs/exfat/exfat_cache.c @@ -0,0 +1,784 @@ +/* + * 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, sector_t sec); +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, sector_t 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, sector_t sec); + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, sector_t sec); +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, sector_t 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 _content; + sector_t 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) { + 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; + sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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, sector_t 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:%llu).\n", + (unsigned long long)sec); + + sm_V(&b_sem); +} /* end of buf_modify */ + +void buf_lock(struct super_block *sb, sector_t 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:%llu).\n", + (unsigned long long)sec); + + sm_V(&b_sem); +} /* end of buf_lock */ + +void buf_unlock(struct super_block *sb, sector_t 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:%llu).\n", + (unsigned long long)sec); + + sm_V(&b_sem); +} /* end of buf_unlock */ + +void buf_release(struct super_block *sb, sector_t 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, sector_t 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, sector_t 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/drivers/oneplus/fs/exfat/exfat_cache.h b/drivers/oneplus/fs/exfat/exfat_cache.h new file mode 100644 index 000000000000..540e31681d04 --- /dev/null +++ b/drivers/oneplus/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; + sector_t 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, sector_t sec); +void FAT_modify(struct super_block *sb, sector_t sec); +void FAT_release_all(struct super_block *sb); +void FAT_sync(struct super_block *sb); +u8 *buf_getblk(struct super_block *sb, sector_t sec); +void buf_modify(struct super_block *sb, sector_t sec); +void buf_lock(struct super_block *sb, sector_t sec); +void buf_unlock(struct super_block *sb, sector_t sec); +void buf_release(struct super_block *sb, sector_t sec); +void buf_release_all(struct super_block *sb); +void buf_sync(struct super_block *sb); + +#endif /* _EXFAT_CACHE_H */ diff --git a/drivers/oneplus/fs/exfat/exfat_config.h b/drivers/oneplus/fs/exfat/exfat_config.h new file mode 100644 index 000000000000..33c6525e449b --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_core.c b/drivers/oneplus/fs/exfat/exfat_core.c new file mode 100644 index 000000000000..143b72155ef9 --- /dev/null +++ b/drivers/oneplus/fs/exfat/exfat_core.c @@ -0,0 +1,5138 @@ +/* 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 +#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; + sector_t 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; + sector_t 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_t 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_t 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) +{ + sector_t 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) +{ + sector_t 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_t 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_t 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) +{ + sector_t 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; + sector_t 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; + sector_t 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_t 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; + sector_t 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; + sector_t 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, sector_t 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; + sector_t end_sector = num_sectors + sector; + + 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 *)); + + while (sector < end_sector) { + ret = sector_read(sb, sector, &tmp_bh, 1); + if (ret != FFS_SUCCESS) { + DPRINTK("sector read (0x%llX)fail\n", (unsigned long long)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; + sector_t sector; + u32 type, 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) +{ + sector_t 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) +{ + sector_t 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; + sector_t 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; + sector_t 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; + sector_t 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; + sector_t 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; + sector_t 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, sector_t *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, sector_t 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, sector_t *sector) +{ + s32 off; + sector_t 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; + sector_t sec; + u32 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 %llu offset %d flags %d, num_entries %u buf ptr %p\n", + (unsigned long long)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, sector_t 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 %llu off %d count %d\n", es, (unsigned long long)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 %llu\n", copy_entries, (esbuf + buf_off), (unsigned long long)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; + sector_t 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_t 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; + sector_t 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; + sector_t 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; + sector_t 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, sector_t 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 = %llu)\n", (unsigned long long)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, sector_t 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 = %llu)\n", (unsigned long long)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, sector_t 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 = %llu, num_secs = %d)\n", + (unsigned long long)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, sector_t 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 = %llu, num_secs = %d)\n", + (unsigned long long)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/drivers/oneplus/fs/exfat/exfat_core.h b/drivers/oneplus/fs/exfat/exfat_core.h new file mode 100644 index 000000000000..52d05c7007d3 --- /dev/null +++ b/drivers/oneplus/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) \ + ((((sector_t)((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) \ + ((u32)((((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 */ + + u64 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 { + sector_t 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, sector_t *sector, s32 *offset); +DENTRY_T *get_entry_with_sector(struct super_block *sb, sector_t sector, s32 offset); +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, sector_t *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, sector_t sec, struct buffer_head **bh, s32 read); +s32 sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 sync); +s32 multi_sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh, s32 num_secs, s32 read); +s32 multi_sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 num_secs, s32 sync); + +#endif /* _EXFAT_H */ diff --git a/drivers/oneplus/fs/exfat/exfat_data.c b/drivers/oneplus/fs/exfat/exfat_data.c new file mode 100644 index 000000000000..65da07aff547 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_data.h b/drivers/oneplus/fs/exfat/exfat_data.h new file mode 100644 index 000000000000..53b0e39397fa --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_nls.c b/drivers/oneplus/fs/exfat/exfat_nls.c new file mode 100644 index 000000000000..a48b3d05a7c4 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_nls.h b/drivers/oneplus/fs/exfat/exfat_nls.h new file mode 100644 index 000000000000..bc516d762e90 --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_oal.c b/drivers/oneplus/fs/exfat/exfat_oal.c new file mode 100644 index 000000000000..743544244ca8 --- /dev/null +++ b/drivers/oneplus/fs/exfat/exfat_oal.c @@ -0,0 +1,196 @@ +/* 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; + time_t second, day, leap_day, month, year; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) + ts = CURRENT_TIME_SEC; +#else + ktime_get_real_ts(&ts); +#endif + + second = ts.tv_sec; + 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/drivers/oneplus/fs/exfat/exfat_oal.h b/drivers/oneplus/fs/exfat/exfat_oal.h new file mode 100644 index 000000000000..b6dd7897ab6e --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_super.c b/drivers/oneplus/fs/exfat/exfat_super.c new file mode 100644 index 000000000000..ab828c37468e --- /dev/null +++ b/drivers/oneplus/fs/exfat/exfat_super.c @@ -0,0 +1,2697 @@ +/* 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; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) +#define current_time(x) (CURRENT_TIME_SEC) +#endif + +#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; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_create entered\n"); + + 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 = current_time(dir); + 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 = current_time(inode); + /* 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) { + 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; + int err; + + __lock_super(sb); + + DPRINTK("exfat_unlink entered\n"); + + 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 = current_time(dir); + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + 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; + 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"); + + 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 = current_time(dir); + 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 = current_time(inode); + /* 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; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_mkdir entered\n"); + + 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 = current_time(dir); + 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 = current_time(inode); + /* 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; + int err; + + __lock_super(sb); + + DPRINTK("exfat_rmdir entered\n"); + + 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 = current_time(dir); + 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 = current_time(inode); + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_rmdir exited\n"); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +#else +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +#endif +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + loff_t i_pos; + int err; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (flags) + return -EINVAL; +#endif + + __lock_super(sb); + + DPRINTK("exfat_rename entered\n"); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + 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 = current_time(new_dir); + 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 = current_time(old_dir); + 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 = current_time(new_inode); + } + +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(inode); + 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); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + error = setattr_prepare(dentry, attr); +#else + error = inode_change_ok(inode, attr); +#endif + 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; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +static int exfat_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + struct inode *inode = path->dentry->d_inode; +#else +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; +#endif + + 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 = { + #if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0) + .readlink = generic_readlink, + #endif + #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(inode); + 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(inode); + 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, +}; + +/*======================================================================*/ +/* Export Operations */ +/*======================================================================*/ + +static struct inode *exfat_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) +{ + struct inode *inode = NULL; + if (ino < EXFAT_ROOT_INO) + return inode; + inode = ilookup(sb, ino); + + if (inode && generation && (inode->i_generation != generation)) { + iput(inode); + inode = NULL; + } + + return inode; +} + +static struct dentry *exfat_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + exfat_nfs_get_inode); +} + +static struct dentry *exfat_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + exfat_nfs_get_inode); +} + +const struct export_operations exfat_export_ops = { + .fh_to_dentry = exfat_fh_to_dentry, + .fh_to_parent = exfat_fh_to_parent, +}; + +/*======================================================================*/ +/* 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); + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + 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 = current_time(inode); +#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; + sb->s_export_op = &exfat_export_ops; + + 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) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); +#endif + 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/drivers/oneplus/fs/exfat/exfat_super.h b/drivers/oneplus/fs/exfat/exfat_super.h new file mode 100644 index 000000000000..916811e3d31e --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_upcase.c b/drivers/oneplus/fs/exfat/exfat_upcase.c new file mode 100644 index 000000000000..3807f37caacb --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/exfat/exfat_version.h b/drivers/oneplus/fs/exfat/exfat_version.h new file mode 100644 index 000000000000..a93fa46be04e --- /dev/null +++ b/drivers/oneplus/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/drivers/oneplus/fs/proc/Kconfig b/drivers/oneplus/fs/proc/Kconfig new file mode 100644 index 000000000000..7e691d1ce02e --- /dev/null +++ b/drivers/oneplus/fs/proc/Kconfig @@ -0,0 +1,5 @@ +config ONEPLUS_FG_OPT + bool "Enable /proc/fg_info/ if EXPERT" + default y + help + Add foreground process optimize support to promote performance . diff --git a/drivers/oneplus/fs/proc/Makefile b/drivers/oneplus/fs/proc/Makefile new file mode 100644 index 000000000000..c4b7aed55832 --- /dev/null +++ b/drivers/oneplus/fs/proc/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ADJ_CHAIN) += adj_chain_stat.o +obj-$(CONFIG_ONEPLUS_FG_OPT) += fg_uid.o diff --git a/drivers/oneplus/fs/proc/adj_chain_stat.c b/drivers/oneplus/fs/proc/adj_chain_stat.c new file mode 100644 index 000000000000..18916313a5e5 --- /dev/null +++ b/drivers/oneplus/fs/proc/adj_chain_stat.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +static int adj_chain_proc_show(struct seq_file *m, void *v) +{ + struct task_struct *tsk, *p; + int i = 0; + int tasksize = 0; + rcu_read_lock(); + read_lock_irq(&tasklist_lock); + while (i <= adj_chain_hist_high) { + if (!list_empty(&adj_chain[i])) { + list_for_each_entry_rcu(tsk, &adj_chain[i], adj_chain_tasks) { + tasksize = 0; + p = find_lock_task_mm(tsk); + if (!p) + goto just_print; + tasksize = get_mm_rss(p->mm); + task_unlock(p); +just_print: + seq_printf(m, "%d tsk: %s, %d, adj %d, size %d\n", + __adjr(i), tsk->comm, tsk->pid, tsk->signal->oom_score_adj, tasksize); + } + } + ++i; + } + read_unlock_irq(&tasklist_lock); + rcu_read_unlock(); + seq_printf(m, "adj chain hist high: %d\n", __adjr(adj_chain_hist_high)); + return 0; +} + +static int adj_chain_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, adj_chain_proc_show, NULL); +} + +static const struct file_operations adj_chain_proc_fops = { + .open= adj_chain_proc_open, + .read= seq_read, + .llseek= seq_lseek, + .release= single_release, +}; + +static int adj_chain_verify_proc_show(struct seq_file *m, void *v) +{ + struct task_struct *tsk, *p; + int tasksize = 0; + rcu_read_lock(); + for_each_process(tsk) { + if (tsk) { + tasksize = 0; + p = find_lock_task_mm(tsk); + if (!p) + goto just_print; + tasksize = get_mm_rss(p->mm); + task_unlock(p); +just_print: + seq_printf(m, "tsk: %s, %d, adj %d, size %d\n", + tsk->comm, tsk->pid, tsk->signal->oom_score_adj, tasksize); + } + } + rcu_read_unlock(); + return 0; +} + +static int adj_chain_verify_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, adj_chain_verify_proc_show, NULL); +} + +static const struct file_operations adj_chain_verify_proc_fops = { + .open= adj_chain_verify_proc_open, + .read= seq_read, + .llseek= seq_lseek, + .release= single_release, +}; + +static int __init proc_adj_chain_init(void) +{ + proc_create("adj_chain_stat", S_IFREG | 0400, NULL, &adj_chain_proc_fops); + proc_create("adj_chain_verify", S_IFREG| 0400, NULL, &adj_chain_verify_proc_fops); + return 0; +} +fs_initcall(proc_adj_chain_init); diff --git a/drivers/oneplus/fs/proc/fg_uid.c b/drivers/oneplus/fs/proc/fg_uid.c new file mode 100644 index 000000000000..975ed0d7c9e1 --- /dev/null +++ b/drivers/oneplus/fs/proc/fg_uid.c @@ -0,0 +1,125 @@ +/* + *Copyright (c) 2018 OnePlus Mobile Comm Corp., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include "../fs/proc/internal.h" +#include "fg_uid.h" + +#define FG_RW 0666 + +struct fg_info fginfo = { + .fg_num = 0, + .fg_uids = -555, +}; + +static struct proc_dir_entry *fg_dir; + +bool is_fg(int uid) +{ + bool ret = false; + + if (uid == fginfo.fg_uids) + ret = true; + return ret; +} + +static int fg_uids_show(struct seq_file *m, void *v) +{ + seq_printf(m, "fg_uids: %d\n", fginfo.fg_uids); + return 0; +} + +static int fg_uids_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, fg_uids_show, inode); +} + +static ssize_t fg_uids_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char buffer[MAX_ARRAY_LENGTH]; + int err = 0; + long r; + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user((void *)buffer, buf, count)) { + err = -EFAULT; + goto out; + } + err = kstrtol(buffer, 0, &r); + fginfo.fg_uids = r; + fginfo.fg_num = 1; +out: + return err < 0 ? err : count; +} + +static const struct file_operations proc_fg_uids_operations = { + .open = fg_uids_open, + .read = seq_read, + .write = fg_uids_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void uids_proc_fs_init(struct proc_dir_entry *p_parent) +{ + struct proc_dir_entry *p_temp; + + if (!p_parent) + goto out_p_temp; + + p_temp = proc_create(FS_FG_UIDS, FG_RW, + p_parent, &proc_fg_uids_operations); + if (!p_temp) + goto out_p_temp; + +out_p_temp: + return; +} + +static int __init fg_uids_init(void) +{ + struct proc_dir_entry *p_parent; + + p_parent = proc_mkdir(FS_FG_INFO_PATH, fg_dir); + if (!p_parent) + return -ENOMEM; + uids_proc_fs_init(p_parent); + return 0; +} + +static void __exit fg_uids_exit(void) +{ + if (!fg_dir) + return; + + remove_proc_entry(FS_FG_UIDS, fg_dir); +} + +module_init(fg_uids_init); +module_exit(fg_uids_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jared Wu "); +MODULE_DESCRIPTION("optimize foreground process to promote performance"); diff --git a/drivers/oneplus/fs/proc/fg_uid.h b/drivers/oneplus/fs/proc/fg_uid.h new file mode 100644 index 000000000000..73f993609e63 --- /dev/null +++ b/drivers/oneplus/fs/proc/fg_uid.h @@ -0,0 +1,30 @@ +/* + *Copyright (c) 2018 OnePlus Mobile Comm Corp., Ltd + * + * 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 _FG_UID_H +#define _FG_UID_H + +#include + +#define MAX_ARRAY_LENGTH 256 +#define FS_FG_INFO_PATH "fg_info" +#define FS_FG_UIDS "fg_uids" + +struct fg_info { + int fg_num; + int fg_uids; +}; + +#endif /*_FG_UID_H*/ diff --git a/drivers/oneplus/include/linux/oem/cpufreq_bouncing.h b/drivers/oneplus/include/linux/oem/cpufreq_bouncing.h new file mode 100644 index 000000000000..b334c8450d80 --- /dev/null +++ b/drivers/oneplus/include/linux/oem/cpufreq_bouncing.h @@ -0,0 +1,19 @@ +#ifndef __CPUFREQ_BOUNCING_H__ +#define __CPUFREQ_BOUNCING_H__ + +#include + +#ifdef CONFIG_CPUFREQ_BOUNCING +void cb_update(struct cpufreq_policy *pol, u64 time); +void cb_reset(int cpu, u64 time); +unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq); +unsigned int cb_get_cap(int cpu); +#else +static inline void cb_reset(int cpu, u64 time) {} +static inline void cb_update(struct cpufreq_policy *pol, u64 time) {} +static inline unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) { return freq; } +static inline unsigned int cb_get_cap(int cpu) { return UINT_MAX; } +#endif + +#endif + diff --git a/drivers/oneplus/include/linux/oem/tpd.h b/drivers/oneplus/include/linux/oem/tpd.h new file mode 100755 index 000000000000..ea0743a6a8f9 --- /dev/null +++ b/drivers/oneplus/include/linux/oem/tpd.h @@ -0,0 +1,74 @@ +#ifndef __TPD_H__ +#define __TPD_H__ + +#include +#include +#include + +#define TPD_CLUSTER_0 (1 << 0) +#define TPD_CLUSTER_1 (1 << 1) +#define TPD_CLUSTER_2 (1 << 2) + +#define TPD_TYPE_S TPD_CLUSTER_0 /* Silver only */ +#define TPD_TYPE_G TPD_CLUSTER_1 /* gold only */ +#define TPD_TYPE_GS (TPD_CLUSTER_1 | TPD_CLUSTER_0) /* sliver + gold */ +#define TPD_TYPE_P TPD_CLUSTER_2 /* gold+ only */ +#define TPD_TYPE_PS (TPD_CLUSTER_2 | TPD_CLUSTER_0) /* gold+ + silver */ +#define TPD_TYPE_PG (TPD_CLUSTER_2 | TPD_CLUSTER_1) /* gold+ + gold */ +#define TPD_TYPE_PGS (TPD_CLUSTER_2 | TPD_CLUSTER_1 | TPD_CLUSTER_0) /* all */ + +#define MAX_THREAD_INPUT 6 +#define MAX_MISS_LIST 20 + +#define TPD_TAG "TPD_DEBUG: " + +#define tpd_logv(fmt...) \ + do { \ + if (tpd_log_lv < 1) \ + pr_info(TPD_TAG fmt); \ + } while (0) + +#define tpd_logi(fmt...) \ + do { \ + if (tpd_log_lv < 2) \ + pr_info(TPD_TAG fmt); \ + } while (0) + +#define tpd_logw(fmt...) \ + do { \ + if (tpd_log_lv < 3) \ + pr_warn(TPD_TAG fmt); \ + } while (0) + +#define tpd_loge(fmt...) pr_err(TPD_TAG fmt) +#define tpd_logd(fmt...) pr_debug(TPD_TAG fmt) + +enum dynamic_tpd_type { + TPD_GROUP_MEDIAPROVIDER = 0, + + TPD_GROUP_MAX +}; + +#ifdef CONFIG_TPD +extern bool is_tpd_enable(void); +extern int tpd_suggested(struct task_struct* tsk, int request_cluster); +extern int tpd_suggested_cpu(struct task_struct* tsk, int request_cpu); +extern void tpd_mask(struct task_struct* tsk, cpumask_t *request); +extern bool tpd_check(struct task_struct *tsk, int dest_cpu); +extern bool is_dynamic_tpd_task(struct task_struct *tsk); +static inline bool is_tpd_task(struct task_struct *tsk) { return tsk ? (tsk->tpd > 0) : false; } +extern void tpd_tglist_del(struct task_struct *tsk); +extern void tpd_init_policy(struct cpufreq_policy *policy); +#else +static inline bool is_tpd_enable(void) { return false; } +static inline int tpd_suggested(struct task_struct* tsk, int request_cluster) { return request_cluster; } +static inline int tpd_suggested_cpu(struct task_struct* tsk, int request_cpu) { return request_cpu; } +static inline void tpd_mask(struct task_struct* tsk, cpumask_t *request) {} +static inline bool tpd_check(struct task_struct *tsk, int dest_cpu) { return false; } +static inline bool is_dynamic_tpd_task(struct task_struct *tsk) { return false; } +static inline bool is_tpd_task(struct task_struct *tsk) { return false; } +static inline void tpd_tglist_del(struct task_struct *tsk) {}; +static inline void tpd_init_policy(struct cpufreq_policy *policy) {} +#endif + +#endif diff --git a/drivers/oneplus/op_freezer/Kconfig b/drivers/oneplus/op_freezer/Kconfig new file mode 100755 index 000000000000..00dfd0a00566 --- /dev/null +++ b/drivers/oneplus/op_freezer/Kconfig @@ -0,0 +1,5 @@ +config OP_FREEZER + bool "op_freezer kernel and freezer native communication channel" + default n + help + Key events (signal/network package/binder) report to freezer native. \ No newline at end of file diff --git a/drivers/oneplus/op_freezer/Makefile b/drivers/oneplus/op_freezer/Makefile new file mode 100755 index 000000000000..488674528145 --- /dev/null +++ b/drivers/oneplus/op_freezer/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_OP_FREEZER) += op_freezer.o +obj-$(CONFIG_OP_FREEZER) += op_freezer_netfilter.o diff --git a/drivers/oneplus/op_freezer/op_freezer.c b/drivers/oneplus/op_freezer/op_freezer.c new file mode 100755 index 000000000000..4d147487ad47 --- /dev/null +++ b/drivers/oneplus/op_freezer/op_freezer.c @@ -0,0 +1,187 @@ +/* op_freezer.c + * + * add for oneplus freeze manager + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 + +#define NETLINK_PORT_OP_FREEZER (0x15356) + +static struct sock *sock_handle = NULL; +static atomic_t op_freezer_deamon_port; + +/* + * netlink report function to tell freezer native deamon unfreeze process info + * if the parameters is empty, fill it with (pid/uid with -1) + */ +int op_freezer_report(enum message_type type, int caller_pid, int target_uid, const char *rpc_name, int code) +{ + int len = 0; + int ret = 0; + struct op_freezer_message *data = NULL; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + + if (atomic_read(&op_freezer_deamon_port) == -1) { + pr_err("%s: op_freezer_deamon_port invalid!\n", __func__); + return OP_FREEZER_ERROR; + } + + if (sock_handle == NULL) { + pr_err("%s: sock_handle invalid!\n", __func__); + return OP_FREEZER_ERROR; + } + + if (type >= TYPE_MAX) { + pr_err("%s: type = %d invalid!\n", __func__, type); + return OP_FREEZER_ERROR; + } + + len = sizeof(struct op_freezer_message); + skb = nlmsg_new(len, GFP_ATOMIC); + if (skb == NULL) { + pr_err("%s: type =%d, nlmsg_new failed!\n", __func__, type); + return OP_FREEZER_ERROR; + } + + nlh = nlmsg_put(skb, 0, 0, 0, len, 0); + if (nlh == NULL) { + pr_err("%s: type =%d, nlmsg_put failed!\n", __func__, type); + kfree_skb(skb); + return OP_FREEZER_ERROR; + } + + data = nlmsg_data(nlh); + if(data == NULL) { + pr_err("%s: type =%d, nlmsg_data failed!\n", __func__, type); + return OP_FREEZER_ERROR; + } + data->type = type; + data->port = NETLINK_PORT_OP_FREEZER; + data->caller_pid = caller_pid; + data->target_uid = target_uid; + data->pkg_cmd = -1; //invalid package cmd + data->code = code; + strlcpy(data->rpc_name, rpc_name, INTERFACETOKEN_BUFF_SIZE); + nlmsg_end(skb, nlh); + + if ((ret = nlmsg_unicast(sock_handle, skb, (u32)atomic_read(&op_freezer_deamon_port))) < 0) { + pr_err("%s: nlmsg_unicast failed! err = %d\n", __func__ , ret); + return OP_FREEZER_ERROR; + } + + return OP_FREEZER_NOERROR; +} + +// op_freezer kernel module handle the message from freezer native deamon +static void op_freezer_handler(struct sk_buff *skb) +{ + struct op_freezer_message *data = NULL; + struct nlmsghdr *nlh = NULL; + unsigned int len = 0; + + if (!skb) { + pr_err("%s: recv skb NULL!\n", __func__); + return; + } + + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + len = NLMSG_PAYLOAD(nlh, 0); + data = (struct op_freezer_message *)NLMSG_DATA(nlh); + + if (len < sizeof (struct op_freezer_message)) { + pr_err("%s: op_freezer_message len check faied! len = %d min_expected_len = %lu!\n", __func__, len, sizeof(struct op_freezer_message)); + return; + } + + if (data->port < 0) { + pr_err("%s: portid = %d invalid!\n", __func__, data->port); + return; + } + if (data->type >= TYPE_MAX) { + pr_err("%s: type = %d invalid!\n", __func__, data->type); + return; + } + if (atomic_read(&op_freezer_deamon_port) == -1 && data->type != LOOP_BACK) { + pr_err("%s: handshake not setup, type = %d!\n", __func__, data->type); + return; + } + + switch (data->type) { + case LOOP_BACK: /*Loop back message, only for native deamon and kernel handshake*/ + atomic_set(&op_freezer_deamon_port, data->port); + op_freezer_report(LOOP_BACK, -1, -1, "loop back", -1); + printk(KERN_ERR "%s: --> LOOP_BACK, port = %d\n", __func__, data->port); + break; + case PKG: + printk(KERN_ERR "%s: --> PKG, uid = %d, pkg_cmd = %d\n", __func__, data->target_uid, data->pkg_cmd); + op_freezer_network_cmd_parse(data->target_uid, data->pkg_cmd); + break; + case FROZEN_TRANS: + printk(KERN_ERR "%s: --> FROZEN_TRANS, uid = %d\n", __func__, data->target_uid); + op_freezer_check_frozen_transcation(data->target_uid); + break; + + default: + pr_err("%s: op_freezer_messag type invalid %d\n", __func__, data->type); + break; + } + } +} + +static int __init op_freezer_init(void) +{ + struct netlink_kernel_cfg cfg = { + .input = op_freezer_handler, + }; + + atomic_set(&op_freezer_deamon_port, -1); + + sock_handle = netlink_kernel_create(&init_net, NETLINK_OP_FREEZER, &cfg); + if (sock_handle == NULL) { + pr_err("%s: create netlink socket failed!\n", __func__); + return OP_FREEZER_ERROR; + } + + if (op_freezer_netfilter_init() == OP_FREEZER_ERROR) { + pr_err("%s: netfilter init failed!\n", __func__); + netlink_kernel_release(sock_handle); //release socket + return OP_FREEZER_ERROR; + } + + printk(KERN_INFO "%s: -\n", __func__); + return OP_FREEZER_NOERROR; +} + +static void __exit op_freezer_exit(void) +{ + if (sock_handle) + netlink_kernel_release(sock_handle); + + op_freezer_netfilter_deinit(); + printk(KERN_INFO "%s: -\n", __func__); +} + +module_init(op_freezer_init); +module_exit(op_freezer_exit); + +MODULE_LICENSE("GPL"); + diff --git a/drivers/oneplus/op_freezer/op_freezer_netfilter.c b/drivers/oneplus/op_freezer/op_freezer_netfilter.c new file mode 100755 index 000000000000..3bdde1198324 --- /dev/null +++ b/drivers/oneplus/op_freezer/op_freezer_netfilter.c @@ -0,0 +1,232 @@ +/* op_freezer_netfilter.c + * + * add for oneplus freeze manager + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 + +#define MAX_SLOT (100) +static uid_t monitored_uids[MAX_SLOT]; +spinlock_t uids_lock; + +static inline uid_t sock2uid(struct sock *sk) +{ + if(sk && sk->sk_socket) + return SOCK_INODE(sk->sk_socket)->i_uid.val; + else + return 0; +} + +// Add netlink monitor uid. When the monitored UID has incoming network package, tell op freezer native deamon +static void op_freezer_add_monitored_uid(uid_t target_uid) +{ + int i = 0; + int fisrt_empty_slot = MAX_SLOT; + unsigned long flags; + + spin_lock_irqsave(&uids_lock, flags); + for (i = 0; i < MAX_SLOT; i++) { + if (monitored_uids[i] == target_uid) { //already in the monitored array + spin_unlock_irqrestore(&uids_lock, flags); + //printk(KERN_WARNING "%s: uid = %d already in array\n", __func__, target_uid); + return; + } else if (monitored_uids[i] == 0 && fisrt_empty_slot == MAX_SLOT) { // first empty slot for monitoring uid + fisrt_empty_slot = i; + } + } + + if (fisrt_empty_slot >= MAX_SLOT) { + spin_unlock_irqrestore(&uids_lock, flags); + pr_err("%s: monitored uid = %d add failed!\n", __func__, target_uid); + return; + } + monitored_uids[fisrt_empty_slot] = target_uid; + spin_unlock_irqrestore(&uids_lock, flags); +} + +static void op_freezer_remove_monitored_uid(uid_t target_uid) +{ + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&uids_lock, flags); + for (i = 0; i < MAX_SLOT; i++) { + if (monitored_uids[i] == target_uid) { + monitored_uids[i] = 0; + spin_unlock_irqrestore(&uids_lock, flags); + return; + } + } + spin_unlock_irqrestore(&uids_lock, flags); + printk(KERN_WARNING "%s: uid = %d remove uid not found\n", __func__, target_uid); +} + +static void op_freezer_remove_all_monitored_uid(void) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&uids_lock, flags); + + for (i = 0; i < MAX_SLOT; i++) + monitored_uids[i] = 0; + + spin_unlock_irqrestore(&uids_lock, flags); +} + +static bool op_freezer_find_remove_monitored_uid(uid_t target_uid) +{ + bool found = false; + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&uids_lock, flags); + for (i = 0; i < MAX_SLOT; i++) { + if (unlikely (monitored_uids[i] == target_uid)) { + found = true; + monitored_uids[i] = 0; + break; + } + } + spin_unlock_irqrestore(&uids_lock, flags); + + if (found) + printk(KERN_WARNING "%s: uid = %d found and removed\n", __func__, target_uid); + return found; +} + +void op_freezer_network_cmd_parse(uid_t uid, enum pkg_cmd cmd) +{ + switch (cmd) { + case ADD_ONE_UID: + op_freezer_add_monitored_uid(uid); + break; + case DEL_ONE_UID: + op_freezer_remove_monitored_uid(uid); + break; + case DEL_ALL_UID: + op_freezer_remove_all_monitored_uid(); + break; + default: + pr_err("%s: pkg_cmd type invalid %d\n", __func__, cmd); + break; + } +} + +// Moniter the uid by netlink filter hook function. +static unsigned int op_freezer_nf_ipv4v6_in(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct sock *sk; + uid_t uid; + unsigned int thoff = 0; + unsigned short frag_off = 0; + bool found = false; + + if (ip_hdr(skb)->version == 4) { + if (ip_hdr(skb)->protocol != IPPROTO_TCP) + return NF_ACCEPT; +#if IS_ENABLED(CONFIG_IPV6) + } else if (ip_hdr(skb)->version == 6) { + if (ipv6_find_hdr(skb, &thoff, -1, &frag_off, NULL) != IPPROTO_TCP) + return NF_ACCEPT; +#endif + } else { + return NF_ACCEPT; + } + + sk = skb_to_full_sk(skb); + if (sk == NULL) + return NF_ACCEPT; + + if (!sk_fullsock(sk)) + return NF_ACCEPT; + + uid = sock2uid(sk); + if (uid < MIN_USERAPP_UID) + return NF_ACCEPT; + + // Find the monitored UID and clear it from the monitor array + found = op_freezer_find_remove_monitored_uid(uid); + if (!found) + return NF_ACCEPT; + if (op_freezer_report(PKG, -1, uid, "PKG", -1) != OP_FREEZER_NOERROR) + pr_err("%s: op_freezer_report PKG failed!, uid = %d\n", __func__, uid); + + return NF_ACCEPT; +} + +//Only monitor input network packages +static struct nf_hook_ops op_freezer_nf_ops[] = { + + { + .hook = op_freezer_nf_ipv4v6_in, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_SELINUX_LAST + 1, + }, +#if IS_ENABLED(CONFIG_IPV6) + { + .hook = op_freezer_nf_ipv4v6_in, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP6_PRI_SELINUX_LAST + 1, + }, +#endif +}; + +void op_freezer_netfilter_deinit(void) +{ + struct net *net; + + rtnl_lock(); + for_each_net(net) { + nf_unregister_net_hooks(net, op_freezer_nf_ops, ARRAY_SIZE(op_freezer_nf_ops)); + } + rtnl_unlock(); +} + +int op_freezer_netfilter_init(void) +{ + struct net *net = NULL; + int err = 0; + + spin_lock_init(&uids_lock); + op_freezer_remove_all_monitored_uid(); + + rtnl_lock(); + for_each_net(net) { + err = nf_register_net_hooks(net, op_freezer_nf_ops, ARRAY_SIZE(op_freezer_nf_ops)); + if (err != 0) { + pr_err("%s: register netfilter hooks failed!\n", __func__); + break; + } + } + rtnl_unlock(); + + if (err != 0) { + op_freezer_netfilter_deinit(); + return OP_FREEZER_ERROR; + } + return OP_FREEZER_NOERROR; +} + diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index d5ea857844ba..a5e137c7753e 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o -obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o +obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o pinctrl-sdm845-v2.o obj-$(CONFIG_PINCTRL_KONA) += pinctrl-kona.o obj-$(CONFIG_PINCTRL_LITO) += pinctrl-lito.o obj-$(CONFIG_PINCTRL_BENGAL) += pinctrl-bengal.o diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c new file mode 100644 index 000000000000..955579a9ed7a --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c @@ -0,0 +1,1819 @@ +/* + * Copyright (c) 2017-2018, 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 +#include +#include +#include + +#include "pinctrl-msm.h" + +#define FUNCTION(fname) \ + [msm_mux_##fname] = { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +#define NORTH 0x00500000 +#define SOUTH 0x00900000 +#define EAST 0x00100000 +#define REG_SIZE 0x1000 +#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ + { \ + .name = "gpio" #id, \ + .pins = gpio##id##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10 \ + }, \ + .nfuncs = 11, \ + .ctl_reg = base + REG_SIZE * id, \ + .io_reg = base + 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = base + 0x8 + REG_SIZE * id, \ + .intr_status_reg = base + 0xc + REG_SIZE * id, \ + .intr_target_reg = base + 0x8 + REG_SIZE * id, \ + .dir_conn_reg = (base == NORTH) ? base + 0xa4000 : \ + ((base == SOUTH) ? base + 0xa8000 : base + 0x9e000), \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + .dir_conn_en_bit = 8, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } +static const struct pinctrl_pin_desc sdm845_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "SDC2_CLK"), + PINCTRL_PIN(151, "SDC2_CMD"), + PINCTRL_PIN(152, "SDC2_DATA"), + PINCTRL_PIN(153, "UFS_RESET"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); + +static const unsigned int sdc2_clk_pins[] = { 150 }; +static const unsigned int sdc2_cmd_pins[] = { 151 }; +static const unsigned int sdc2_data_pins[] = { 152 }; +static const unsigned int ufs_reset_pins[] = { 153 }; + +enum sdm845_functions { + msm_mux_ddr_pxi3, + msm_mux_cam_mclk, + msm_mux_pll_bypassnl, + msm_mux_qdss_gpio0, + msm_mux_pll_reset, + msm_mux_qdss_gpio1, + msm_mux_qdss_gpio2, + msm_mux_qdss_gpio3, + msm_mux_cci_i2c, + msm_mux_qup1, + msm_mux_qdss_gpio4, + msm_mux_qdss_gpio5, + msm_mux_qdss_gpio6, + msm_mux_qdss_gpio7, + msm_mux_cci_timer0, + msm_mux_gcc_gp2, + msm_mux_qdss_gpio8, + msm_mux_cci_timer1, + msm_mux_gcc_gp3, + msm_mux_qdss_gpio, + msm_mux_cci_timer2, + msm_mux_qdss_gpio9, + msm_mux_cci_timer3, + msm_mux_cci_async, + msm_mux_qdss_gpio10, + msm_mux_cci_timer4, + msm_mux_qdss_gpio11, + msm_mux_qdss_gpio12, + msm_mux_qup2, + msm_mux_qdss_gpio13, + msm_mux_qdss_gpio14, + msm_mux_phase_flag1, + msm_mux_qdss_gpio15, + msm_mux_phase_flag2, + msm_mux_qup11, + msm_mux_qup14, + msm_mux_pci_e0, + msm_mux_jitter_bist, + msm_mux_pll_bist, + msm_mux_atest_tsens, + msm_mux_agera_pll, + msm_mux_usb_phy, + msm_mux_lpass_slimbus, + msm_mux_sd_write, + msm_mux_tsif1_error, + msm_mux_qup3, + msm_mux_qup6, + msm_mux_qup12, + msm_mux_phase_flag16, + msm_mux_qup10, + msm_mux_phase_flag11, + msm_mux_phase_flag12, + msm_mux_phase_flag13, + msm_mux_phase_flag17, + msm_mux_qua_mi2s, + msm_mux_gcc_gp1, + msm_mux_phase_flag18, + msm_mux_phase_flag19, + msm_mux_phase_flag20, + msm_mux_cri_trng0, + msm_mux_phase_flag21, + msm_mux_cri_trng1, + msm_mux_phase_flag22, + msm_mux_cri_trng, + msm_mux_phase_flag23, + msm_mux_phase_flag24, + msm_mux_pri_mi2s, + msm_mux_sp_cmu, + msm_mux_phase_flag25, + msm_mux_qup8, + msm_mux_pri_mi2s_ws, + msm_mux_spkr_i2s, + msm_mux_audio_ref, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_btfm_slimbus, + msm_mux_atest_usb2, + msm_mux_ter_mi2s, + msm_mux_phase_flag7, + msm_mux_atest_usb23, + msm_mux_phase_flag8, + msm_mux_atest_usb22, + msm_mux_phase_flag9, + msm_mux_atest_usb21, + msm_mux_phase_flag4, + msm_mux_atest_usb20, + msm_mux_sec_mi2s, + msm_mux_qup15, + msm_mux_qup5, + msm_mux_tsif1_clk, + msm_mux_qup4, + msm_mux_qspi_cs, + msm_mux_tgu_ch3, + msm_mux_phase_flag10, + msm_mux_tsif1_en, + msm_mux_mdp_vsync0, + msm_mux_mdp_vsync1, + msm_mux_mdp_vsync2, + msm_mux_mdp_vsync3, + msm_mux_tgu_ch0, + msm_mux_phase_flag0, + msm_mux_tsif1_data, + msm_mux_sdc4_cmd, + msm_mux_qspi0, + msm_mux_tgu_ch1, + msm_mux_tsif2_error, + msm_mux_sdc43, + msm_mux_qspi1, + msm_mux_vfr_1, + msm_mux_tgu_ch2, + msm_mux_tsif2_clk, + msm_mux_sdc4_clk, + msm_mux_qup7, + msm_mux_qspi2, + msm_mux_tsif2_en, + msm_mux_sdc42, + msm_mux_qspi3, + msm_mux_tsif2_data, + msm_mux_sdc41, + msm_mux_qspi_clk, + msm_mux_tsif2_sync, + msm_mux_sdc40, + msm_mux_phase_flag3, + msm_mux_ldo_en, + msm_mux_ldo_update, + msm_mux_phase_flag14, + msm_mux_phase_flag15, + msm_mux_pci_e1, + msm_mux_prng_rosc, + msm_mux_phase_flag5, + msm_mux_uim2_data, + msm_mux_qup13, + msm_mux_uim2_clk, + msm_mux_uim2_reset, + msm_mux_uim2_present, + msm_mux_uim1_data, + msm_mux_uim1_clk, + msm_mux_uim1_reset, + msm_mux_uim1_present, + msm_mux_uim_batt, + msm_mux_edp_hot, + msm_mux_nav_pps, + msm_mux_atest_char, + msm_mux_adsp_ext, + msm_mux_atest_char3, + msm_mux_atest_char2, + msm_mux_atest_char1, + msm_mux_atest_char0, + msm_mux_qlink_request, + msm_mux_qlink_enable, + msm_mux_pa_indicator, + msm_mux_phase_flag26, + msm_mux_phase_flag27, + msm_mux_phase_flag28, + msm_mux_phase_flag6, + msm_mux_phase_flag29, + msm_mux_phase_flag30, + msm_mux_phase_flag31, + msm_mux_mss_lte, + msm_mux_qup0, + msm_mux_gpio, + msm_mux_qup9, + msm_mux_qdss_cti, + msm_mux_ddr_pxi0, + msm_mux_ddr_bist, + msm_mux_atest_tsens2, + msm_mux_vsense_trigger, + msm_mux_atest_usb1, + msm_mux_qup_l4, + msm_mux_wlan1_adc1, + msm_mux_atest_usb13, + msm_mux_ddr_pxi1, + msm_mux_qup_l5, + msm_mux_wlan1_adc0, + msm_mux_atest_usb12, + msm_mux_mdp_vsync, + msm_mux_qup_l6, + msm_mux_wlan2_adc1, + msm_mux_atest_usb11, + msm_mux_ddr_pxi2, + msm_mux_edp_lcd, + msm_mux_dbg_out, + msm_mux_wlan2_adc0, + msm_mux_atest_usb10, + msm_mux_m_voc, + msm_mux_tsif1_sync, + msm_mux_NA, +}; + +static const char * const ddr_pxi3_groups[] = { + "gpio12", "gpio13", +}; +static const char * const cam_mclk_groups[] = { + "gpio13", "gpio14", "gpio15", "gpio16", +}; +static const char * const pll_bypassnl_groups[] = { + "gpio13", +}; +static const char * const qdss_gpio0_groups[] = { + "gpio13", "gpio117", +}; +static const char * const pll_reset_groups[] = { + "gpio14", +}; +static const char * const qdss_gpio1_groups[] = { + "gpio14", "gpio118", +}; +static const char * const qdss_gpio2_groups[] = { + "gpio15", "gpio119", +}; +static const char * const qdss_gpio3_groups[] = { + "gpio16", "gpio120", +}; +static const char * const cci_i2c_groups[] = { + "gpio17", "gpio18", "gpio19", "gpio20", +}; +static const char * const qup1_groups[] = { + "gpio17", "gpio18", "gpio19", "gpio20", +}; +static const char * const qdss_gpio4_groups[] = { + "gpio17", "gpio121", +}; +static const char * const qdss_gpio5_groups[] = { + "gpio18", "gpio122", +}; +static const char * const qdss_gpio6_groups[] = { + "gpio19", "gpio41", +}; +static const char * const qdss_gpio7_groups[] = { + "gpio20", "gpio42", +}; +static const char * const cci_timer0_groups[] = { + "gpio21", +}; +static const char * const gcc_gp2_groups[] = { + "gpio21", "gpio58", +}; +static const char * const qdss_gpio8_groups[] = { + "gpio21", "gpio75", +}; +static const char * const cci_timer1_groups[] = { + "gpio22", +}; +static const char * const gcc_gp3_groups[] = { + "gpio22", "gpio59", +}; +static const char * const qdss_gpio_groups[] = { + "gpio22", "gpio30", "gpio123", "gpio124", +}; +static const char * const cci_timer2_groups[] = { + "gpio23", +}; +static const char * const qdss_gpio9_groups[] = { + "gpio23", "gpio76", +}; +static const char * const cci_timer3_groups[] = { + "gpio24", +}; +static const char * const cci_async_groups[] = { + "gpio24", "gpio25", "gpio26", +}; +static const char * const qdss_gpio10_groups[] = { + "gpio24", "gpio77", +}; +static const char * const cci_timer4_groups[] = { + "gpio25", +}; +static const char * const qdss_gpio11_groups[] = { + "gpio25", "gpio79", +}; +static const char * const qdss_gpio12_groups[] = { + "gpio26", "gpio80", +}; +static const char * const qup2_groups[] = { + "gpio27", "gpio28", "gpio29", "gpio30", +}; +static const char * const qdss_gpio13_groups[] = { + "gpio27", "gpio93", +}; +static const char * const qdss_gpio14_groups[] = { + "gpio28", "gpio43", +}; +static const char * const phase_flag1_groups[] = { + "gpio29", +}; +static const char * const qdss_gpio15_groups[] = { + "gpio29", "gpio44", +}; +static const char * const phase_flag2_groups[] = { + "gpio30", +}; +static const char * const qup11_groups[] = { + "gpio31", "gpio32", "gpio33", "gpio34", +}; +static const char * const qup14_groups[] = { + "gpio31", "gpio32", "gpio33", "gpio34", +}; +static const char * const pci_e0_groups[] = { + "gpio35", "gpio36", +}; +static const char * const jitter_bist_groups[] = { + "gpio35", +}; +static const char * const pll_bist_groups[] = { + "gpio36", +}; +static const char * const atest_tsens_groups[] = { + "gpio36", +}; +static const char * const agera_pll_groups[] = { + "gpio37", +}; +static const char * const usb_phy_groups[] = { + "gpio38", +}; +static const char * const lpass_slimbus_groups[] = { + "gpio39", "gpio70", "gpio71", "gpio72", +}; +static const char * const sd_write_groups[] = { + "gpio40", +}; +static const char * const tsif1_error_groups[] = { + "gpio40", +}; +static const char * const qup3_groups[] = { + "gpio41", "gpio42", "gpio43", "gpio44", +}; +static const char * const qup6_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48", +}; +static const char * const qup12_groups[] = { + "gpio49", "gpio50", "gpio51", "gpio52", +}; +static const char * const phase_flag16_groups[] = { + "gpio52", +}; +static const char * const qup10_groups[] = { + "gpio53", "gpio54", "gpio55", "gpio56", +}; +static const char * const phase_flag11_groups[] = { + "gpio53", +}; +static const char * const phase_flag12_groups[] = { + "gpio54", +}; +static const char * const phase_flag13_groups[] = { + "gpio55", +}; +static const char * const phase_flag17_groups[] = { + "gpio56", +}; +static const char * const qua_mi2s_groups[] = { + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", +}; +static const char * const gcc_gp1_groups[] = { + "gpio57", "gpio78", +}; +static const char * const phase_flag18_groups[] = { + "gpio57", +}; +static const char * const phase_flag19_groups[] = { + "gpio58", +}; +static const char * const phase_flag20_groups[] = { + "gpio59", +}; +static const char * const cri_trng0_groups[] = { + "gpio60", +}; +static const char * const phase_flag21_groups[] = { + "gpio60", +}; +static const char * const cri_trng1_groups[] = { + "gpio61", +}; +static const char * const phase_flag22_groups[] = { + "gpio61", +}; +static const char * const cri_trng_groups[] = { + "gpio62", +}; +static const char * const phase_flag23_groups[] = { + "gpio62", +}; +static const char * const phase_flag24_groups[] = { + "gpio63", +}; +static const char * const pri_mi2s_groups[] = { + "gpio64", "gpio65", "gpio67", "gpio68", +}; +static const char * const sp_cmu_groups[] = { + "gpio64", +}; +static const char * const phase_flag25_groups[] = { + "gpio64", +}; +static const char * const qup8_groups[] = { + "gpio65", "gpio66", "gpio67", "gpio68", +}; +static const char * const pri_mi2s_ws_groups[] = { + "gpio66", +}; +static const char * const spkr_i2s_groups[] = { + "gpio69", "gpio70", "gpio71", "gpio72", +}; +static const char * const audio_ref_groups[] = { + "gpio69", +}; +static const char * const tsense_pwm1_groups[] = { + "gpio71", +}; +static const char * const tsense_pwm2_groups[] = { + "gpio71", +}; +static const char * const btfm_slimbus_groups[] = { + "gpio73", "gpio74", +}; +static const char * const atest_usb2_groups[] = { + "gpio73", +}; +static const char * const ter_mi2s_groups[] = { + "gpio74", "gpio75", "gpio76", "gpio77", "gpio78", +}; +static const char * const phase_flag7_groups[] = { + "gpio74", +}; +static const char * const atest_usb23_groups[] = { + "gpio74", +}; +static const char * const phase_flag8_groups[] = { + "gpio75", +}; +static const char * const atest_usb22_groups[] = { + "gpio75", +}; +static const char * const phase_flag9_groups[] = { + "gpio76", +}; +static const char * const atest_usb21_groups[] = { + "gpio76", +}; +static const char * const phase_flag4_groups[] = { + "gpio77", +}; +static const char * const atest_usb20_groups[] = { + "gpio77", +}; +static const char * const sec_mi2s_groups[] = { + "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", +}; +static const char * const qup15_groups[] = { + "gpio81", "gpio82", "gpio83", "gpio84", +}; +static const char * const qup5_groups[] = { + "gpio85", "gpio86", "gpio87", "gpio88", +}; +static const char * const tsif1_clk_groups[] = { + "gpio89", +}; +static const char * const qup4_groups[] = { + "gpio89", "gpio90", "gpio91", "gpio92", +}; +static const char * const qspi_cs_groups[] = { + "gpio89", "gpio90", +}; +static const char * const tgu_ch3_groups[] = { + "gpio89", +}; +static const char * const phase_flag10_groups[] = { + "gpio89", +}; +static const char * const tsif1_en_groups[] = { + "gpio90", +}; +static const char * const mdp_vsync0_groups[] = { + "gpio90", +}; +static const char * const mdp_vsync1_groups[] = { + "gpio90", +}; +static const char * const mdp_vsync2_groups[] = { + "gpio90", +}; +static const char * const mdp_vsync3_groups[] = { + "gpio90", +}; +static const char * const tgu_ch0_groups[] = { + "gpio90", +}; +static const char * const phase_flag0_groups[] = { + "gpio90", +}; +static const char * const tsif1_data_groups[] = { + "gpio91", +}; +static const char * const sdc4_cmd_groups[] = { + "gpio91", +}; +static const char * const qspi0_groups[] = { + "gpio91", +}; +static const char * const tgu_ch1_groups[] = { + "gpio91", +}; +static const char * const tsif2_error_groups[] = { + "gpio92", +}; +static const char * const sdc43_groups[] = { + "gpio92", +}; +static const char * const qspi1_groups[] = { + "gpio92", +}; +static const char * const vfr_1_groups[] = { + "gpio92", +}; +static const char * const tgu_ch2_groups[] = { + "gpio92", +}; +static const char * const tsif2_clk_groups[] = { + "gpio93", +}; +static const char * const sdc4_clk_groups[] = { + "gpio93", +}; +static const char * const qup7_groups[] = { + "gpio93", "gpio94", "gpio95", "gpio96", +}; +static const char * const qspi2_groups[] = { + "gpio93", +}; +static const char * const tsif2_en_groups[] = { + "gpio94", +}; +static const char * const sdc42_groups[] = { + "gpio94", +}; +static const char * const qspi3_groups[] = { + "gpio94", +}; +static const char * const tsif2_data_groups[] = { + "gpio95", +}; +static const char * const sdc41_groups[] = { + "gpio95", +}; +static const char * const qspi_clk_groups[] = { + "gpio95", +}; +static const char * const tsif2_sync_groups[] = { + "gpio96", +}; +static const char * const sdc40_groups[] = { + "gpio96", +}; +static const char * const phase_flag3_groups[] = { + "gpio96", +}; +static const char * const ldo_en_groups[] = { + "gpio97", +}; +static const char * const ldo_update_groups[] = { + "gpio98", +}; +static const char * const phase_flag14_groups[] = { + "gpio99", +}; +static const char * const phase_flag15_groups[] = { + "gpio100", +}; +static const char * const pci_e1_groups[] = { + "gpio102", "gpio103", +}; +static const char * const prng_rosc_groups[] = { + "gpio102", +}; +static const char * const phase_flag5_groups[] = { + "gpio103", +}; +static const char * const uim2_data_groups[] = { + "gpio105", +}; +static const char * const qup13_groups[] = { + "gpio105", "gpio106", "gpio107", "gpio108", +}; +static const char * const uim2_clk_groups[] = { + "gpio106", +}; +static const char * const uim2_reset_groups[] = { + "gpio107", +}; +static const char * const uim2_present_groups[] = { + "gpio108", +}; +static const char * const uim1_data_groups[] = { + "gpio109", +}; +static const char * const uim1_clk_groups[] = { + "gpio110", +}; +static const char * const uim1_reset_groups[] = { + "gpio111", +}; +static const char * const uim1_present_groups[] = { + "gpio112", +}; +static const char * const uim_batt_groups[] = { + "gpio113", +}; +static const char * const edp_hot_groups[] = { + "gpio113", +}; +static const char * const nav_pps_groups[] = { + "gpio114", "gpio114", "gpio115", "gpio115", "gpio128", "gpio128", + "gpio129", "gpio129", "gpio143", "gpio143", +}; +static const char * const atest_char_groups[] = { + "gpio117", +}; +static const char * const adsp_ext_groups[] = { + "gpio118", +}; +static const char * const atest_char3_groups[] = { + "gpio118", +}; +static const char * const atest_char2_groups[] = { + "gpio119", +}; +static const char * const atest_char1_groups[] = { + "gpio120", +}; +static const char * const atest_char0_groups[] = { + "gpio121", +}; +static const char * const qlink_request_groups[] = { + "gpio130", +}; +static const char * const qlink_enable_groups[] = { + "gpio131", +}; +static const char * const pa_indicator_groups[] = { + "gpio135", +}; +static const char * const phase_flag26_groups[] = { + "gpio137", +}; +static const char * const phase_flag27_groups[] = { + "gpio138", +}; +static const char * const phase_flag28_groups[] = { + "gpio139", +}; +static const char * const phase_flag6_groups[] = { + "gpio140", +}; +static const char * const phase_flag29_groups[] = { + "gpio141", +}; +static const char * const phase_flag30_groups[] = { + "gpio142", +}; +static const char * const phase_flag31_groups[] = { + "gpio143", +}; +static const char * const mss_lte_groups[] = { + "gpio144", "gpio145", +}; +static const char * const qup0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", + "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", + "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128", + "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134", + "gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140", + "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", +}; +static const char * const qup9_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; +static const char * const qdss_cti_groups[] = { + "gpio4", "gpio5", "gpio51", "gpio52", "gpio62", "gpio63", "gpio90", + "gpio91", +}; +static const char * const ddr_pxi0_groups[] = { + "gpio6", "gpio7", +}; +static const char * const ddr_bist_groups[] = { + "gpio7", "gpio8", "gpio9", "gpio10", +}; +static const char * const atest_tsens2_groups[] = { + "gpio7", +}; +static const char * const vsense_trigger_groups[] = { + "gpio7", +}; +static const char * const atest_usb1_groups[] = { + "gpio7", +}; +static const char * const qup_l4_groups[] = { + "gpio8", "gpio35", "gpio105", "gpio123", +}; +static const char * const wlan1_adc1_groups[] = { + "gpio8", +}; +static const char * const atest_usb13_groups[] = { + "gpio8", +}; +static const char * const ddr_pxi1_groups[] = { + "gpio8", "gpio9", +}; +static const char * const qup_l5_groups[] = { + "gpio9", "gpio36", "gpio106", "gpio124", +}; +static const char * const wlan1_adc0_groups[] = { + "gpio9", +}; +static const char * const atest_usb12_groups[] = { + "gpio9", +}; +static const char * const mdp_vsync_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio97", "gpio98", +}; +static const char * const qup_l6_groups[] = { + "gpio10", "gpio37", "gpio107", "gpio125", +}; +static const char * const wlan2_adc1_groups[] = { + "gpio10", +}; +static const char * const atest_usb11_groups[] = { + "gpio10", +}; +static const char * const ddr_pxi2_groups[] = { + "gpio10", "gpio11", +}; +static const char * const edp_lcd_groups[] = { + "gpio11", +}; +static const char * const dbg_out_groups[] = { + "gpio11", +}; +static const char * const wlan2_adc0_groups[] = { + "gpio11", +}; +static const char * const atest_usb10_groups[] = { + "gpio11", +}; +static const char * const m_voc_groups[] = { + "gpio12", +}; +static const char * const tsif1_sync_groups[] = { + "gpio12", +}; + +static const struct msm_function sdm845_functions[] = { + FUNCTION(ddr_pxi3), + FUNCTION(cam_mclk), + FUNCTION(pll_bypassnl), + FUNCTION(qdss_gpio0), + FUNCTION(pll_reset), + FUNCTION(qdss_gpio1), + FUNCTION(qdss_gpio2), + FUNCTION(qdss_gpio3), + FUNCTION(cci_i2c), + FUNCTION(qup1), + FUNCTION(qdss_gpio4), + FUNCTION(qdss_gpio5), + FUNCTION(qdss_gpio6), + FUNCTION(qdss_gpio7), + FUNCTION(cci_timer0), + FUNCTION(gcc_gp2), + FUNCTION(qdss_gpio8), + FUNCTION(cci_timer1), + FUNCTION(gcc_gp3), + FUNCTION(qdss_gpio), + FUNCTION(cci_timer2), + FUNCTION(qdss_gpio9), + FUNCTION(cci_timer3), + FUNCTION(cci_async), + FUNCTION(qdss_gpio10), + FUNCTION(cci_timer4), + FUNCTION(qdss_gpio11), + FUNCTION(qdss_gpio12), + FUNCTION(qup2), + FUNCTION(qdss_gpio13), + FUNCTION(qdss_gpio14), + FUNCTION(phase_flag1), + FUNCTION(qdss_gpio15), + FUNCTION(phase_flag2), + FUNCTION(qup11), + FUNCTION(qup14), + FUNCTION(pci_e0), + FUNCTION(jitter_bist), + FUNCTION(pll_bist), + FUNCTION(atest_tsens), + FUNCTION(agera_pll), + FUNCTION(usb_phy), + FUNCTION(lpass_slimbus), + FUNCTION(sd_write), + FUNCTION(tsif1_error), + FUNCTION(qup3), + FUNCTION(qup6), + FUNCTION(qup12), + FUNCTION(phase_flag16), + FUNCTION(qup10), + FUNCTION(phase_flag11), + FUNCTION(phase_flag12), + FUNCTION(phase_flag13), + FUNCTION(phase_flag17), + FUNCTION(qua_mi2s), + FUNCTION(gcc_gp1), + FUNCTION(phase_flag18), + FUNCTION(phase_flag19), + FUNCTION(phase_flag20), + FUNCTION(cri_trng0), + FUNCTION(phase_flag21), + FUNCTION(cri_trng1), + FUNCTION(phase_flag22), + FUNCTION(cri_trng), + FUNCTION(phase_flag23), + FUNCTION(phase_flag24), + FUNCTION(pri_mi2s), + FUNCTION(sp_cmu), + FUNCTION(phase_flag25), + FUNCTION(qup8), + FUNCTION(pri_mi2s_ws), + FUNCTION(spkr_i2s), + FUNCTION(audio_ref), + FUNCTION(tsense_pwm1), + FUNCTION(tsense_pwm2), + FUNCTION(btfm_slimbus), + FUNCTION(atest_usb2), + FUNCTION(ter_mi2s), + FUNCTION(phase_flag7), + FUNCTION(atest_usb23), + FUNCTION(phase_flag8), + FUNCTION(atest_usb22), + FUNCTION(phase_flag9), + FUNCTION(atest_usb21), + FUNCTION(phase_flag4), + FUNCTION(atest_usb20), + FUNCTION(sec_mi2s), + FUNCTION(qup15), + FUNCTION(qup5), + FUNCTION(tsif1_clk), + FUNCTION(qup4), + FUNCTION(qspi_cs), + FUNCTION(tgu_ch3), + FUNCTION(phase_flag10), + FUNCTION(tsif1_en), + FUNCTION(mdp_vsync0), + FUNCTION(mdp_vsync1), + FUNCTION(mdp_vsync2), + FUNCTION(mdp_vsync3), + FUNCTION(tgu_ch0), + FUNCTION(phase_flag0), + FUNCTION(tsif1_data), + FUNCTION(sdc4_cmd), + FUNCTION(qspi0), + FUNCTION(tgu_ch1), + FUNCTION(tsif2_error), + FUNCTION(sdc43), + FUNCTION(qspi1), + FUNCTION(vfr_1), + FUNCTION(tgu_ch2), + FUNCTION(tsif2_clk), + FUNCTION(sdc4_clk), + FUNCTION(qup7), + FUNCTION(qspi2), + FUNCTION(tsif2_en), + FUNCTION(sdc42), + FUNCTION(qspi3), + FUNCTION(tsif2_data), + FUNCTION(sdc41), + FUNCTION(qspi_clk), + FUNCTION(tsif2_sync), + FUNCTION(sdc40), + FUNCTION(phase_flag3), + FUNCTION(ldo_en), + FUNCTION(ldo_update), + FUNCTION(phase_flag14), + FUNCTION(phase_flag15), + FUNCTION(pci_e1), + FUNCTION(prng_rosc), + FUNCTION(phase_flag5), + FUNCTION(uim2_data), + FUNCTION(qup13), + FUNCTION(uim2_clk), + FUNCTION(uim2_reset), + FUNCTION(uim2_present), + FUNCTION(uim1_data), + FUNCTION(uim1_clk), + FUNCTION(uim1_reset), + FUNCTION(uim1_present), + FUNCTION(uim_batt), + FUNCTION(edp_hot), + FUNCTION(nav_pps), + FUNCTION(atest_char), + FUNCTION(adsp_ext), + FUNCTION(atest_char3), + FUNCTION(atest_char2), + FUNCTION(atest_char1), + FUNCTION(atest_char0), + FUNCTION(qlink_request), + FUNCTION(qlink_enable), + FUNCTION(pa_indicator), + FUNCTION(phase_flag26), + FUNCTION(phase_flag27), + FUNCTION(phase_flag28), + FUNCTION(phase_flag6), + FUNCTION(phase_flag29), + FUNCTION(phase_flag30), + FUNCTION(phase_flag31), + FUNCTION(mss_lte), + FUNCTION(qup0), + FUNCTION(gpio), + FUNCTION(qup9), + FUNCTION(qdss_cti), + FUNCTION(ddr_pxi0), + FUNCTION(ddr_bist), + FUNCTION(atest_tsens2), + FUNCTION(vsense_trigger), + FUNCTION(atest_usb1), + FUNCTION(qup_l4), + FUNCTION(wlan1_adc1), + FUNCTION(atest_usb13), + FUNCTION(ddr_pxi1), + FUNCTION(qup_l5), + FUNCTION(wlan1_adc0), + FUNCTION(atest_usb12), + FUNCTION(mdp_vsync), + FUNCTION(qup_l6), + FUNCTION(wlan2_adc1), + FUNCTION(atest_usb11), + FUNCTION(ddr_pxi2), + FUNCTION(edp_lcd), + FUNCTION(dbg_out), + FUNCTION(wlan2_adc0), + FUNCTION(atest_usb10), + FUNCTION(m_voc), + FUNCTION(tsif1_sync), +}; + +/* Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sdm845_groups[] = { + [0] = PINGROUP(0, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [1] = PINGROUP(1, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [2] = PINGROUP(2, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [3] = PINGROUP(3, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA, + NA), + [7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2, + vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA), + [8] = PINGROUP(8, EAST, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1, + atest_usb13, ddr_pxi1, NA, NA), + [9] = PINGROUP(9, EAST, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12, + ddr_pxi1, NA, NA, NA, NA), + [10] = PINGROUP(10, EAST, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1, + atest_usb11, ddr_pxi2, NA, NA, NA, NA), + [11] = PINGROUP(11, EAST, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0, + atest_usb10, ddr_pxi2, NA, NA, NA, NA), + [12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA, + NA, NA, NA, NA, NA), + [13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0, + ddr_pxi3, NA, NA, NA, NA, NA, NA), + [14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA, + NA, NA, NA, NA), + [15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, + NA, NA, NA), + [16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, + NA, NA, NA), + [17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA, + NA, NA, NA), + [18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA, + NA, NA, NA), + [19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA, + NA, NA, NA), + [20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA, + NA, NA, NA), + [21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA, + NA, NA, NA, NA), + [22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA, + NA, NA, NA, NA), + [23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA, + NA, NA, NA), + [24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA, + NA, NA, NA, NA, NA), + [25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA, + NA, NA, NA, NA, NA), + [26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA, + NA, NA, NA), + [27] = PINGROUP(27, EAST, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA, + NA, NA), + [28] = PINGROUP(28, EAST, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA, + NA, NA), + [29] = PINGROUP(29, EAST, qup2, NA, phase_flag1, qdss_gpio15, NA, NA, + NA, NA, NA, NA), + [30] = PINGROUP(30, EAST, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA, + NA, NA, NA), + [31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA, + NA, NA, NA), + [36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens, + NA, NA, NA, NA, NA), + [37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA, + NA, NA), + [38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [39] = PINGROUP(39, EAST, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA, + NA, NA, NA), + [41] = PINGROUP(41, EAST, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA, + NA), + [42] = PINGROUP(42, EAST, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA, + NA), + [43] = PINGROUP(43, EAST, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA, + NA, NA), + [44] = PINGROUP(44, EAST, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA, + NA, NA), + [45] = PINGROUP(45, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [46] = PINGROUP(46, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [47] = PINGROUP(47, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [48] = PINGROUP(48, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA, + NA, NA, NA, NA), + [53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA, + NA, NA), + [54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA, + NA, NA), + [55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA, + NA, NA), + [56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA, + NA, NA), + [57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA, + NA, NA, NA, NA), + [58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA, + NA, NA, NA, NA), + [59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA, + NA, NA, NA, NA), + [60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA, + NA, NA, NA, NA, NA), + [61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA, + NA, NA, NA, NA, NA), + [62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti, + NA, NA, NA, NA, NA, NA), + [63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA, + NA, NA, NA, NA, NA), + [64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA, + NA, NA, NA, NA), + [65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA, + NA, NA), + [67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [69] = PINGROUP(69, EAST, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA, + NA, NA), + [70] = PINGROUP(70, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA, + NA, NA, NA), + [71] = PINGROUP(71, EAST, lpass_slimbus, spkr_i2s, tsense_pwm1, + tsense_pwm2, NA, NA, NA, NA, NA, NA), + [72] = PINGROUP(72, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA, + NA, NA, NA), + [73] = PINGROUP(73, EAST, btfm_slimbus, atest_usb2, NA, NA, NA, NA, NA, + NA, NA, NA), + [74] = PINGROUP(74, EAST, btfm_slimbus, ter_mi2s, phase_flag7, + atest_usb23, NA, NA, NA, NA, NA, NA), + [75] = PINGROUP(75, EAST, ter_mi2s, phase_flag8, qdss_gpio8, + atest_usb22, NA, NA, NA, NA, NA, NA), + [76] = PINGROUP(76, EAST, ter_mi2s, phase_flag9, qdss_gpio9, + atest_usb21, NA, NA, NA, NA, NA, NA), + [77] = PINGROUP(77, EAST, ter_mi2s, phase_flag4, qdss_gpio10, + atest_usb20, NA, NA, NA, NA, NA, NA), + [78] = PINGROUP(78, EAST, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA, + NA, NA), + [79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA, + NA, NA, NA), + [80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA, + NA, NA, NA), + [81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [85] = PINGROUP(85, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [86] = PINGROUP(86, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [87] = PINGROUP(87, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [88] = PINGROUP(88, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, qspi_cs, tgu_ch3, + phase_flag10, NA, NA, NA, NA, NA), + [90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, qspi_cs, + mdp_vsync1, mdp_vsync2, mdp_vsync3, tgu_ch0, + phase_flag0, qdss_cti), + [91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, qspi0, tgu_ch1, + NA, qdss_cti, NA, NA, NA), + [92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, qspi1, vfr_1, + tgu_ch2, NA, NA, NA, NA), + [93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, qspi2, NA, + qdss_gpio13, NA, NA, NA, NA), + [94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, qspi3, NA, NA, NA, + NA, NA, NA), + [95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, qspi_clk, NA, NA, + NA, NA, NA, NA), + [96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA, + NA, NA, NA, NA, NA), + [97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA, + NA, NA), + [98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA, + NA, NA, NA), + [99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA, + NA, NA), + [103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA, + NA, NA, NA), + [104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA, + NA, NA, NA), + [106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA, + NA, NA, NA), + [107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA, + NA, NA, NA), + [108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA, + NA, NA, NA), + [109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA, + NA, NA), + [114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA, + NA, NA), + [115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA, + NA, NA), + [116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA, + NA, NA, NA, NA), + [118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA, + NA, NA, NA, NA, NA), + [119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA, + NA, NA, NA, NA), + [120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA, + NA, NA, NA, NA), + [121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA, + NA, NA, NA, NA), + [122] = PINGROUP(122, EAST, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, NA, + NA), + [123] = PINGROUP(123, EAST, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA, + NA, NA), + [124] = PINGROUP(124, EAST, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA, + NA, NA), + [125] = PINGROUP(125, EAST, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [126] = PINGROUP(126, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA, + NA, NA), + [129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA, + NA, NA), + [130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA, + NA, NA), + [136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA, + NA, NA), + [138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA, + NA, NA), + [139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA, + NA, NA), + [140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA, + NA, NA), + [141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA, + NA, NA), + [142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA, + NA, NA), + [143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31, + NA, NA, NA, NA, NA), + [144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6), + [151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3), + [152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0), + [153] = UFS_RESET(ufs_reset, 0x99f000), +}; + +static struct msm_dir_conn sdm845_dir_conn[] = { + {1, 510}, + {3, 511}, + {5, 512}, + {10, 513}, + {11, 514}, + {20, 515}, + {22, 516}, + {24, 517}, + {26, 518}, + {30, 519}, + {31, 632}, + {32, 521}, + {34, 522}, + {36, 523}, + {37, 524}, + {38, 525}, + {39, 526}, + {40, 527}, + {41, 630}, + {43, 529}, + {44, 530}, + {46, 531}, + {48, 532}, + {49, 633 }, + {52, 534}, + {53, 535}, + {54, 536}, + {56, 537}, + {57, 538}, + {58, 539}, + {59, 540}, + {60, 541}, + {61, 542}, + {62, 543}, + {63, 544}, + {64, 545}, + {66, 546}, + {68, 547}, + {71, 548}, + {73, 549}, + {77, 550}, + {78, 551}, + {79, 552}, + {80, 553}, + {84, 554}, + {85, 555}, + {86, 556}, + {88, 557}, + {89, 631}, + {91, 559}, + {92, 560}, + {95, 561}, + {96, 562}, + {97, 563}, + {101, 564}, + {103, 565}, + {108, 567}, + {112, 568}, + {113, 569}, + {104, 566}, + {115, 570}, + {116, 571}, + {117, 572}, + {118, 573}, + {119, 609}, + {120, 610}, + {121, 611}, + {122, 612}, + {123, 613}, + {124, 614}, + {125, 615}, + {126, 616}, + {127, 617}, + {128, 618}, + {129, 619}, + {130, 620}, + {132, 621}, + {133, 622}, + {145, 623}, + {0, 216}, + {0, 215}, + {0, 214}, + {0, 213}, + {0, 212}, + {0, 211}, + {0, 210}, + {0, 209}, +}; + +static const struct msm_pinctrl_soc_data sdm845_pinctrl = { + .pins = sdm845_pins, + .npins = ARRAY_SIZE(sdm845_pins), + .functions = sdm845_functions, + .nfunctions = ARRAY_SIZE(sdm845_functions), + .groups = sdm845_groups, + .ngroups = ARRAY_SIZE(sdm845_groups), + .ngpios = 150, + .dir_conn = sdm845_dir_conn, +}; + +static int sdm845_pinctrl_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sdm845_pinctrl); +} + +static const struct of_device_id sdm845_pinctrl_of_match[] = { + { .compatible = "qcom,sdm845-pinctrl-v2", }, + { }, +}; + +static struct platform_driver sdm845_pinctrl_driver = { + .driver = { + .name = "sdm845-v2-pinctrl", + .owner = THIS_MODULE, + .of_match_table = sdm845_pinctrl_of_match, + }, + .probe = sdm845_pinctrl_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init sdm845_pinctrl_init(void) +{ + return platform_driver_register(&sdm845_pinctrl_driver); +} +arch_initcall(sdm845_pinctrl_init); + +static void __exit sdm845_pinctrl_exit(void) +{ + platform_driver_unregister(&sdm845_pinctrl_driver); +} +module_exit(sdm845_pinctrl_exit); + +MODULE_DESCRIPTION("QTI sdm845-v2 pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index 2ab7a8885757..d846ea88f87c 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -1,6 +1,14 @@ -// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 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 @@ -19,13 +27,12 @@ #define NORTH 0x00500000 #define SOUTH 0x00900000 -#define EAST 0x00100000 #define REG_SIZE 0x1000 #define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ { \ .name = "gpio" #id, \ .pins = gpio##id##_pins, \ - .npins = ARRAY_SIZE(gpio##id##_pins), \ + .npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins), \ .funcs = (int[]){ \ msm_mux_gpio, /* gpio mode */ \ msm_mux_##f1, \ @@ -45,6 +52,8 @@ .intr_cfg_reg = base + 0x8 + REG_SIZE * id, \ .intr_status_reg = base + 0xc + REG_SIZE * id, \ .intr_target_reg = base + 0x8 + REG_SIZE * id, \ + .dir_conn_reg = (base == NORTH) ? base + 0xa5000 :\ + base + 0xa8000, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,13 +68,14 @@ .intr_polarity_bit = 1, \ .intr_detection_bit = 2, \ .intr_detection_width = 2, \ + .dir_conn_en_bit = 8, \ } #define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ { \ .name = #pg_name, \ .pins = pg_name##_pins, \ - .npins = ARRAY_SIZE(pg_name##_pins), \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ .ctl_reg = ctl, \ .io_reg = 0, \ .intr_cfg_reg = 0, \ @@ -90,7 +100,7 @@ { \ .name = #pg_name, \ .pins = pg_name##_pins, \ - .npins = ARRAY_SIZE(pg_name##_pins), \ + .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ .ctl_reg = offset, \ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ @@ -110,6 +120,7 @@ .intr_detection_bit = -1, \ .intr_detection_width = -1, \ } + static const struct pinctrl_pin_desc sdm845_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), @@ -426,136 +437,187 @@ static const unsigned int sdc2_data_pins[] = { 152 }; static const unsigned int ufs_reset_pins[] = { 153 }; enum sdm845_functions { - msm_mux_gpio, - msm_mux_adsp_ext, - msm_mux_agera_pll, - msm_mux_atest_char, - msm_mux_atest_tsens, - msm_mux_atest_tsens2, - msm_mux_atest_usb1, - msm_mux_atest_usb10, - msm_mux_atest_usb11, - msm_mux_atest_usb12, - msm_mux_atest_usb13, - msm_mux_atest_usb2, - msm_mux_atest_usb20, - msm_mux_atest_usb21, - msm_mux_atest_usb22, - msm_mux_atest_usb23, - msm_mux_audio_ref, - msm_mux_btfm_slimbus, + msm_mux_ddr_pxi3, msm_mux_cam_mclk, - msm_mux_cci_async, + msm_mux_pll_bypassnl, + msm_mux_qdss_gpio0, + msm_mux_pll_reset, + msm_mux_qdss_gpio1, + msm_mux_qdss_gpio2, + msm_mux_qdss_gpio3, msm_mux_cci_i2c, + msm_mux_qup1, + msm_mux_qdss_gpio4, + msm_mux_qdss_gpio5, + msm_mux_qdss_gpio6, + msm_mux_qdss_gpio7, msm_mux_cci_timer0, + msm_mux_gcc_gp2, + msm_mux_qdss_gpio8, msm_mux_cci_timer1, + msm_mux_gcc_gp3, + msm_mux_qdss_gpio, msm_mux_cci_timer2, + msm_mux_qdss_gpio9, msm_mux_cci_timer3, + msm_mux_cci_async, + msm_mux_qdss_gpio10, msm_mux_cci_timer4, - msm_mux_cri_trng, - msm_mux_cri_trng0, - msm_mux_cri_trng1, - msm_mux_dbg_out, - msm_mux_ddr_bist, - msm_mux_ddr_pxi0, - msm_mux_ddr_pxi1, - msm_mux_ddr_pxi2, - msm_mux_ddr_pxi3, - msm_mux_edp_hot, - msm_mux_edp_lcd, - msm_mux_gcc_gp1, - msm_mux_gcc_gp2, - msm_mux_gcc_gp3, - msm_mux_jitter_bist, - msm_mux_ldo_en, - msm_mux_ldo_update, - msm_mux_lpass_slimbus, - msm_mux_m_voc, - msm_mux_mdp_vsync, - msm_mux_mdp_vsync0, - msm_mux_mdp_vsync1, - msm_mux_mdp_vsync2, - msm_mux_mdp_vsync3, - msm_mux_mss_lte, - msm_mux_nav_pps, - msm_mux_pa_indicator, - msm_mux_pci_e0, - msm_mux_pci_e1, - msm_mux_phase_flag, - msm_mux_pll_bist, - msm_mux_pll_bypassnl, - msm_mux_pll_reset, - msm_mux_pri_mi2s, - msm_mux_pri_mi2s_ws, - msm_mux_prng_rosc, - msm_mux_qdss_cti, - msm_mux_qdss, - msm_mux_qlink_enable, - msm_mux_qlink_request, - msm_mux_qspi_clk, - msm_mux_qspi_cs, - msm_mux_qspi_data, - msm_mux_qua_mi2s, - msm_mux_qup0, - msm_mux_qup1, - msm_mux_qup10, + msm_mux_qdss_gpio11, + msm_mux_qdss_gpio12, + msm_mux_qup2, + msm_mux_qdss_gpio13, + msm_mux_qdss_gpio14, + msm_mux_phase_flag1, + msm_mux_qdss_gpio15, + msm_mux_phase_flag2, msm_mux_qup11, - msm_mux_qup12, - msm_mux_qup13, msm_mux_qup14, - msm_mux_qup15, - msm_mux_qup2, + msm_mux_pci_e0, + msm_mux_jitter_bist, + msm_mux_pll_bist, + msm_mux_atest_tsens, + msm_mux_agera_pll, + msm_mux_usb_phy, + msm_mux_lpass_slimbus, + msm_mux_sd_write, + msm_mux_tsif1_error, msm_mux_qup3, - msm_mux_qup4, - msm_mux_qup5, msm_mux_qup6, - msm_mux_qup7, - msm_mux_qup8, - msm_mux_qup9, - msm_mux_qup_l4, - msm_mux_qup_l5, - msm_mux_qup_l6, - msm_mux_sd_write, - msm_mux_sdc4_clk, - msm_mux_sdc4_cmd, - msm_mux_sdc4_data, - msm_mux_sec_mi2s, + msm_mux_qup12, + msm_mux_phase_flag16, + msm_mux_qup10, + msm_mux_phase_flag11, + msm_mux_phase_flag12, + msm_mux_phase_flag13, + msm_mux_phase_flag17, + msm_mux_qua_mi2s, + msm_mux_gcc_gp1, + msm_mux_phase_flag18, + msm_mux_phase_flag19, + msm_mux_phase_flag20, + msm_mux_cri_trng0, + msm_mux_phase_flag21, + msm_mux_cri_trng1, + msm_mux_phase_flag22, + msm_mux_cri_trng, + msm_mux_phase_flag23, + msm_mux_phase_flag24, + msm_mux_pri_mi2s, msm_mux_sp_cmu, + msm_mux_phase_flag25, + msm_mux_qup8, + msm_mux_pri_mi2s_ws, msm_mux_spkr_i2s, - msm_mux_ter_mi2s, - msm_mux_tgu_ch0, - msm_mux_tgu_ch1, - msm_mux_tgu_ch2, - msm_mux_tgu_ch3, + msm_mux_audio_ref, msm_mux_tsense_pwm1, msm_mux_tsense_pwm2, + msm_mux_btfm_slimbus, + msm_mux_atest_usb2, + msm_mux_ter_mi2s, + msm_mux_phase_flag7, + msm_mux_atest_usb23, + msm_mux_phase_flag8, + msm_mux_atest_usb22, + msm_mux_phase_flag9, + msm_mux_atest_usb21, + msm_mux_phase_flag4, + msm_mux_atest_usb20, + msm_mux_sec_mi2s, + msm_mux_qup15, + msm_mux_qup5, msm_mux_tsif1_clk, - msm_mux_tsif1_data, + msm_mux_qup4, + msm_mux_tgu_ch3, + msm_mux_phase_flag10, msm_mux_tsif1_en, - msm_mux_tsif1_error, - msm_mux_tsif1_sync, + msm_mux_mdp_vsync0, + msm_mux_mdp_vsync1, + msm_mux_mdp_vsync2, + msm_mux_mdp_vsync3, + msm_mux_tgu_ch0, + msm_mux_phase_flag0, + msm_mux_tsif1_data, + msm_mux_sdc4_cmd, + msm_mux_tgu_ch1, + msm_mux_tsif2_error, + msm_mux_sdc43, + msm_mux_vfr_1, + msm_mux_tgu_ch2, msm_mux_tsif2_clk, - msm_mux_tsif2_data, + msm_mux_sdc4_clk, + msm_mux_qup7, msm_mux_tsif2_en, - msm_mux_tsif2_error, + msm_mux_sdc42, + msm_mux_tsif2_data, + msm_mux_sdc41, msm_mux_tsif2_sync, - msm_mux_uim1_clk, - msm_mux_uim1_data, - msm_mux_uim1_present, - msm_mux_uim1_reset, - msm_mux_uim2_clk, + msm_mux_sdc40, + msm_mux_phase_flag3, + msm_mux_ldo_en, + msm_mux_ldo_update, + msm_mux_phase_flag14, + msm_mux_phase_flag15, + msm_mux_pci_e1, + msm_mux_prng_rosc, + msm_mux_phase_flag5, msm_mux_uim2_data, - msm_mux_uim2_present, + msm_mux_qup13, + msm_mux_uim2_clk, msm_mux_uim2_reset, + msm_mux_uim2_present, + msm_mux_uim1_data, + msm_mux_uim1_clk, + msm_mux_uim1_reset, + msm_mux_uim1_present, msm_mux_uim_batt, - msm_mux_usb_phy, - msm_mux_vfr_1, + msm_mux_edp_hot, + msm_mux_nav_pps, + msm_mux_atest_char, + msm_mux_adsp_ext, + msm_mux_atest_char3, + msm_mux_atest_char2, + msm_mux_atest_char1, + msm_mux_atest_char0, + msm_mux_qlink_request, + msm_mux_qlink_enable, + msm_mux_pa_indicator, + msm_mux_phase_flag26, + msm_mux_phase_flag27, + msm_mux_phase_flag28, + msm_mux_phase_flag6, + msm_mux_phase_flag29, + msm_mux_phase_flag30, + msm_mux_phase_flag31, + msm_mux_mss_lte, + msm_mux_qup0, + msm_mux_gpio, + msm_mux_qup9, + msm_mux_qdss_cti, + msm_mux_ddr_pxi0, + msm_mux_ddr_bist, + msm_mux_atest_tsens2, msm_mux_vsense_trigger, - msm_mux_wlan1_adc0, + msm_mux_atest_usb1, + msm_mux_qup_l4, msm_mux_wlan1_adc1, - msm_mux_wlan2_adc0, + msm_mux_atest_usb13, + msm_mux_ddr_pxi1, + msm_mux_qup_l5, + msm_mux_wlan1_adc0, + msm_mux_atest_usb12, + msm_mux_mdp_vsync, + msm_mux_qup_l6, msm_mux_wlan2_adc1, - msm_mux__, + msm_mux_atest_usb11, + msm_mux_ddr_pxi2, + msm_mux_edp_lcd, + msm_mux_dbg_out, + msm_mux_wlan2_adc0, + msm_mux_atest_usb10, + msm_mux_m_voc, + msm_mux_tsif1_sync, + msm_mux_NA, }; static const char * const ddr_pxi3_groups[] = { @@ -567,56 +629,98 @@ static const char * const cam_mclk_groups[] = { static const char * const pll_bypassnl_groups[] = { "gpio13", }; -static const char * const qdss_groups[] = { - "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", - "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", - "gpio27", "gpio28", "gpio29", "gpio30", "gpio41", "gpio42", "gpio43", - "gpio44", "gpio75", "gpio76", "gpio77", "gpio79", "gpio80", "gpio93", - "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", - "gpio123", "gpio124", +static const char * const qdss_gpio0_groups[] = { + "gpio13", "gpio117", }; static const char * const pll_reset_groups[] = { "gpio14", }; +static const char * const qdss_gpio1_groups[] = { + "gpio14", "gpio118", +}; +static const char * const qdss_gpio2_groups[] = { + "gpio15", "gpio119", +}; +static const char * const qdss_gpio3_groups[] = { + "gpio16", "gpio120", +}; static const char * const cci_i2c_groups[] = { "gpio17", "gpio18", "gpio19", "gpio20", }; static const char * const qup1_groups[] = { "gpio17", "gpio18", "gpio19", "gpio20", }; +static const char * const qdss_gpio4_groups[] = { + "gpio17", "gpio121", +}; +static const char * const qdss_gpio5_groups[] = { + "gpio18", "gpio122", +}; +static const char * const qdss_gpio6_groups[] = { + "gpio19", "gpio41", +}; +static const char * const qdss_gpio7_groups[] = { + "gpio20", "gpio42", +}; static const char * const cci_timer0_groups[] = { "gpio21", }; static const char * const gcc_gp2_groups[] = { "gpio21", "gpio58", }; +static const char * const qdss_gpio8_groups[] = { + "gpio21", "gpio75", +}; static const char * const cci_timer1_groups[] = { "gpio22", }; static const char * const gcc_gp3_groups[] = { "gpio22", "gpio59", }; +static const char * const qdss_gpio_groups[] = { + "gpio22", "gpio30", "gpio123", "gpio124", +}; static const char * const cci_timer2_groups[] = { "gpio23", }; +static const char * const qdss_gpio9_groups[] = { + "gpio23", "gpio76", +}; static const char * const cci_timer3_groups[] = { "gpio24", }; static const char * const cci_async_groups[] = { "gpio24", "gpio25", "gpio26", }; +static const char * const qdss_gpio10_groups[] = { + "gpio24", "gpio77", +}; static const char * const cci_timer4_groups[] = { "gpio25", }; +static const char * const qdss_gpio11_groups[] = { + "gpio25", "gpio79", +}; +static const char * const qdss_gpio12_groups[] = { + "gpio26", "gpio80", +}; static const char * const qup2_groups[] = { "gpio27", "gpio28", "gpio29", "gpio30", }; -static const char * const phase_flag_groups[] = { - "gpio29", "gpio30", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", - "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", - "gpio64", "gpio74", "gpio75", "gpio76", "gpio77", "gpio89", "gpio90", - "gpio96", "gpio99", "gpio100", "gpio103", "gpio137", "gpio138", - "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", +static const char * const qdss_gpio13_groups[] = { + "gpio27", "gpio93", +}; +static const char * const qdss_gpio14_groups[] = { + "gpio28", "gpio43", +}; +static const char * const phase_flag1_groups[] = { + "gpio29", +}; +static const char * const qdss_gpio15_groups[] = { + "gpio29", "gpio44", +}; +static const char * const phase_flag2_groups[] = { + "gpio30", }; static const char * const qup11_groups[] = { "gpio31", "gpio32", "gpio33", "gpio34", @@ -660,30 +764,69 @@ static const char * const qup6_groups[] = { static const char * const qup12_groups[] = { "gpio49", "gpio50", "gpio51", "gpio52", }; +static const char * const phase_flag16_groups[] = { + "gpio52", +}; static const char * const qup10_groups[] = { "gpio53", "gpio54", "gpio55", "gpio56", }; +static const char * const phase_flag11_groups[] = { + "gpio53", +}; +static const char * const phase_flag12_groups[] = { + "gpio54", +}; +static const char * const phase_flag13_groups[] = { + "gpio55", +}; +static const char * const phase_flag17_groups[] = { + "gpio56", +}; static const char * const qua_mi2s_groups[] = { "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", }; static const char * const gcc_gp1_groups[] = { "gpio57", "gpio78", }; +static const char * const phase_flag18_groups[] = { + "gpio57", +}; +static const char * const phase_flag19_groups[] = { + "gpio58", +}; +static const char * const phase_flag20_groups[] = { + "gpio59", +}; static const char * const cri_trng0_groups[] = { "gpio60", }; +static const char * const phase_flag21_groups[] = { + "gpio60", +}; static const char * const cri_trng1_groups[] = { "gpio61", }; +static const char * const phase_flag22_groups[] = { + "gpio61", +}; static const char * const cri_trng_groups[] = { "gpio62", }; +static const char * const phase_flag23_groups[] = { + "gpio62", +}; +static const char * const phase_flag24_groups[] = { + "gpio63", +}; static const char * const pri_mi2s_groups[] = { "gpio64", "gpio65", "gpio67", "gpio68", }; static const char * const sp_cmu_groups[] = { "gpio64", }; +static const char * const phase_flag25_groups[] = { + "gpio64", +}; static const char * const qup8_groups[] = { "gpio65", "gpio66", "gpio67", "gpio68", }; @@ -711,15 +854,27 @@ static const char * const atest_usb2_groups[] = { static const char * const ter_mi2s_groups[] = { "gpio74", "gpio75", "gpio76", "gpio77", "gpio78", }; +static const char * const phase_flag7_groups[] = { + "gpio74", +}; static const char * const atest_usb23_groups[] = { "gpio74", }; +static const char * const phase_flag8_groups[] = { + "gpio75", +}; static const char * const atest_usb22_groups[] = { "gpio75", }; +static const char * const phase_flag9_groups[] = { + "gpio76", +}; static const char * const atest_usb21_groups[] = { "gpio76", }; +static const char * const phase_flag4_groups[] = { + "gpio77", +}; static const char * const atest_usb20_groups[] = { "gpio77", }; @@ -738,12 +893,12 @@ static const char * const tsif1_clk_groups[] = { static const char * const qup4_groups[] = { "gpio89", "gpio90", "gpio91", "gpio92", }; -static const char * const qspi_cs_groups[] = { - "gpio89", "gpio90", -}; static const char * const tgu_ch3_groups[] = { "gpio89", }; +static const char * const phase_flag10_groups[] = { + "gpio89", +}; static const char * const tsif1_en_groups[] = { "gpio90", }; @@ -762,26 +917,23 @@ static const char * const mdp_vsync3_groups[] = { static const char * const tgu_ch0_groups[] = { "gpio90", }; +static const char * const phase_flag0_groups[] = { + "gpio90", +}; static const char * const tsif1_data_groups[] = { "gpio91", }; static const char * const sdc4_cmd_groups[] = { "gpio91", }; -static const char * const qspi_data_groups[] = { - "gpio91", "gpio92", "gpio93", "gpio94", -}; static const char * const tgu_ch1_groups[] = { "gpio91", }; static const char * const tsif2_error_groups[] = { "gpio92", }; -static const char * const sdc4_data_groups[] = { +static const char * const sdc43_groups[] = { "gpio92", - "gpio94", - "gpio95", - "gpio96", }; static const char * const vfr_1_groups[] = { "gpio92", @@ -801,27 +953,45 @@ static const char * const qup7_groups[] = { static const char * const tsif2_en_groups[] = { "gpio94", }; +static const char * const sdc42_groups[] = { + "gpio94", +}; static const char * const tsif2_data_groups[] = { "gpio95", }; -static const char * const qspi_clk_groups[] = { +static const char * const sdc41_groups[] = { "gpio95", }; static const char * const tsif2_sync_groups[] = { "gpio96", }; +static const char * const sdc40_groups[] = { + "gpio96", +}; +static const char * const phase_flag3_groups[] = { + "gpio96", +}; static const char * const ldo_en_groups[] = { "gpio97", }; static const char * const ldo_update_groups[] = { "gpio98", }; +static const char * const phase_flag14_groups[] = { + "gpio99", +}; +static const char * const phase_flag15_groups[] = { + "gpio100", +}; static const char * const pci_e1_groups[] = { "gpio102", "gpio103", }; static const char * const prng_rosc_groups[] = { "gpio102", }; +static const char * const phase_flag5_groups[] = { + "gpio103", +}; static const char * const uim2_data_groups[] = { "gpio105", }; @@ -860,11 +1030,23 @@ static const char * const nav_pps_groups[] = { "gpio129", "gpio129", "gpio143", "gpio143", }; static const char * const atest_char_groups[] = { - "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", + "gpio117", }; static const char * const adsp_ext_groups[] = { "gpio118", }; +static const char * const atest_char3_groups[] = { + "gpio118", +}; +static const char * const atest_char2_groups[] = { + "gpio119", +}; +static const char * const atest_char1_groups[] = { + "gpio120", +}; +static const char * const atest_char0_groups[] = { + "gpio121", +}; static const char * const qlink_request_groups[] = { "gpio130", }; @@ -874,6 +1056,27 @@ static const char * const qlink_enable_groups[] = { static const char * const pa_indicator_groups[] = { "gpio135", }; +static const char * const phase_flag26_groups[] = { + "gpio137", +}; +static const char * const phase_flag27_groups[] = { + "gpio138", +}; +static const char * const phase_flag28_groups[] = { + "gpio139", +}; +static const char * const phase_flag6_groups[] = { + "gpio140", +}; +static const char * const phase_flag29_groups[] = { + "gpio141", +}; +static const char * const phase_flag30_groups[] = { + "gpio142", +}; +static const char * const phase_flag31_groups[] = { + "gpio143", +}; static const char * const mss_lte_groups[] = { "gpio144", "gpio145", }; @@ -983,135 +1186,186 @@ static const char * const tsif1_sync_groups[] = { }; static const struct msm_function sdm845_functions[] = { - FUNCTION(gpio), - FUNCTION(adsp_ext), - FUNCTION(agera_pll), - FUNCTION(atest_char), - FUNCTION(atest_tsens), - FUNCTION(atest_tsens2), - FUNCTION(atest_usb1), - FUNCTION(atest_usb10), - FUNCTION(atest_usb11), - FUNCTION(atest_usb12), - FUNCTION(atest_usb13), - FUNCTION(atest_usb2), - FUNCTION(atest_usb20), - FUNCTION(atest_usb21), - FUNCTION(atest_usb22), - FUNCTION(atest_usb23), - FUNCTION(audio_ref), - FUNCTION(btfm_slimbus), + FUNCTION(ddr_pxi3), FUNCTION(cam_mclk), - FUNCTION(cci_async), + FUNCTION(pll_bypassnl), + FUNCTION(qdss_gpio0), + FUNCTION(pll_reset), + FUNCTION(qdss_gpio1), + FUNCTION(qdss_gpio2), + FUNCTION(qdss_gpio3), FUNCTION(cci_i2c), + FUNCTION(qup1), + FUNCTION(qdss_gpio4), + FUNCTION(qdss_gpio5), + FUNCTION(qdss_gpio6), + FUNCTION(qdss_gpio7), FUNCTION(cci_timer0), + FUNCTION(gcc_gp2), + FUNCTION(qdss_gpio8), FUNCTION(cci_timer1), + FUNCTION(gcc_gp3), + FUNCTION(qdss_gpio), FUNCTION(cci_timer2), + FUNCTION(qdss_gpio9), FUNCTION(cci_timer3), + FUNCTION(cci_async), + FUNCTION(qdss_gpio10), FUNCTION(cci_timer4), - FUNCTION(cri_trng), - FUNCTION(cri_trng0), - FUNCTION(cri_trng1), - FUNCTION(dbg_out), - FUNCTION(ddr_bist), - FUNCTION(ddr_pxi0), - FUNCTION(ddr_pxi1), - FUNCTION(ddr_pxi2), - FUNCTION(ddr_pxi3), - FUNCTION(edp_hot), - FUNCTION(edp_lcd), - FUNCTION(gcc_gp1), - FUNCTION(gcc_gp2), - FUNCTION(gcc_gp3), - FUNCTION(jitter_bist), - FUNCTION(ldo_en), - FUNCTION(ldo_update), - FUNCTION(lpass_slimbus), - FUNCTION(m_voc), - FUNCTION(mdp_vsync), - FUNCTION(mdp_vsync0), - FUNCTION(mdp_vsync1), - FUNCTION(mdp_vsync2), - FUNCTION(mdp_vsync3), - FUNCTION(mss_lte), - FUNCTION(nav_pps), - FUNCTION(pa_indicator), - FUNCTION(pci_e0), - FUNCTION(pci_e1), - FUNCTION(phase_flag), - FUNCTION(pll_bist), - FUNCTION(pll_bypassnl), - FUNCTION(pll_reset), - FUNCTION(pri_mi2s), - FUNCTION(pri_mi2s_ws), - FUNCTION(prng_rosc), - FUNCTION(qdss_cti), - FUNCTION(qdss), - FUNCTION(qlink_enable), - FUNCTION(qlink_request), - FUNCTION(qspi_clk), - FUNCTION(qspi_cs), - FUNCTION(qspi_data), - FUNCTION(qua_mi2s), - FUNCTION(qup0), - FUNCTION(qup1), - FUNCTION(qup10), + FUNCTION(qdss_gpio11), + FUNCTION(qdss_gpio12), + FUNCTION(qup2), + FUNCTION(qdss_gpio13), + FUNCTION(qdss_gpio14), + FUNCTION(phase_flag1), + FUNCTION(qdss_gpio15), + FUNCTION(phase_flag2), FUNCTION(qup11), - FUNCTION(qup12), - FUNCTION(qup13), FUNCTION(qup14), - FUNCTION(qup15), - FUNCTION(qup2), + FUNCTION(pci_e0), + FUNCTION(jitter_bist), + FUNCTION(pll_bist), + FUNCTION(atest_tsens), + FUNCTION(agera_pll), + FUNCTION(usb_phy), + FUNCTION(lpass_slimbus), + FUNCTION(sd_write), + FUNCTION(tsif1_error), FUNCTION(qup3), - FUNCTION(qup4), - FUNCTION(qup5), FUNCTION(qup6), - FUNCTION(qup7), - FUNCTION(qup8), - FUNCTION(qup9), - FUNCTION(qup_l4), - FUNCTION(qup_l5), - FUNCTION(qup_l6), - FUNCTION(sd_write), - FUNCTION(sdc4_clk), - FUNCTION(sdc4_cmd), - FUNCTION(sdc4_data), - FUNCTION(sec_mi2s), + FUNCTION(qup12), + FUNCTION(phase_flag16), + FUNCTION(qup10), + FUNCTION(phase_flag11), + FUNCTION(phase_flag12), + FUNCTION(phase_flag13), + FUNCTION(phase_flag17), + FUNCTION(qua_mi2s), + FUNCTION(gcc_gp1), + FUNCTION(phase_flag18), + FUNCTION(phase_flag19), + FUNCTION(phase_flag20), + FUNCTION(cri_trng0), + FUNCTION(phase_flag21), + FUNCTION(cri_trng1), + FUNCTION(phase_flag22), + FUNCTION(cri_trng), + FUNCTION(phase_flag23), + FUNCTION(phase_flag24), + FUNCTION(pri_mi2s), FUNCTION(sp_cmu), + FUNCTION(phase_flag25), + FUNCTION(qup8), + FUNCTION(pri_mi2s_ws), FUNCTION(spkr_i2s), - FUNCTION(ter_mi2s), - FUNCTION(tgu_ch0), - FUNCTION(tgu_ch1), - FUNCTION(tgu_ch2), - FUNCTION(tgu_ch3), + FUNCTION(audio_ref), FUNCTION(tsense_pwm1), FUNCTION(tsense_pwm2), + FUNCTION(btfm_slimbus), + FUNCTION(atest_usb2), + FUNCTION(ter_mi2s), + FUNCTION(phase_flag7), + FUNCTION(atest_usb23), + FUNCTION(phase_flag8), + FUNCTION(atest_usb22), + FUNCTION(phase_flag9), + FUNCTION(atest_usb21), + FUNCTION(phase_flag4), + FUNCTION(atest_usb20), + FUNCTION(sec_mi2s), + FUNCTION(qup15), + FUNCTION(qup5), FUNCTION(tsif1_clk), - FUNCTION(tsif1_data), + FUNCTION(qup4), + FUNCTION(tgu_ch3), + FUNCTION(phase_flag10), FUNCTION(tsif1_en), - FUNCTION(tsif1_error), - FUNCTION(tsif1_sync), + FUNCTION(mdp_vsync0), + FUNCTION(mdp_vsync1), + FUNCTION(mdp_vsync2), + FUNCTION(mdp_vsync3), + FUNCTION(tgu_ch0), + FUNCTION(phase_flag0), + FUNCTION(tsif1_data), + FUNCTION(sdc4_cmd), + FUNCTION(tgu_ch1), + FUNCTION(tsif2_error), + FUNCTION(sdc43), + FUNCTION(vfr_1), + FUNCTION(tgu_ch2), FUNCTION(tsif2_clk), - FUNCTION(tsif2_data), + FUNCTION(sdc4_clk), + FUNCTION(qup7), FUNCTION(tsif2_en), - FUNCTION(tsif2_error), + FUNCTION(sdc42), + FUNCTION(tsif2_data), + FUNCTION(sdc41), FUNCTION(tsif2_sync), - FUNCTION(uim1_clk), - FUNCTION(uim1_data), - FUNCTION(uim1_present), - FUNCTION(uim1_reset), - FUNCTION(uim2_clk), + FUNCTION(sdc40), + FUNCTION(phase_flag3), + FUNCTION(ldo_en), + FUNCTION(ldo_update), + FUNCTION(phase_flag14), + FUNCTION(phase_flag15), + FUNCTION(pci_e1), + FUNCTION(prng_rosc), + FUNCTION(phase_flag5), FUNCTION(uim2_data), - FUNCTION(uim2_present), + FUNCTION(qup13), + FUNCTION(uim2_clk), FUNCTION(uim2_reset), + FUNCTION(uim2_present), + FUNCTION(uim1_data), + FUNCTION(uim1_clk), + FUNCTION(uim1_reset), + FUNCTION(uim1_present), FUNCTION(uim_batt), - FUNCTION(usb_phy), - FUNCTION(vfr_1), + FUNCTION(edp_hot), + FUNCTION(nav_pps), + FUNCTION(atest_char), + FUNCTION(adsp_ext), + FUNCTION(atest_char3), + FUNCTION(atest_char2), + FUNCTION(atest_char1), + FUNCTION(atest_char0), + FUNCTION(qlink_request), + FUNCTION(qlink_enable), + FUNCTION(pa_indicator), + FUNCTION(phase_flag26), + FUNCTION(phase_flag27), + FUNCTION(phase_flag28), + FUNCTION(phase_flag6), + FUNCTION(phase_flag29), + FUNCTION(phase_flag30), + FUNCTION(phase_flag31), + FUNCTION(mss_lte), + FUNCTION(qup0), + FUNCTION(gpio), + FUNCTION(qup9), + FUNCTION(qdss_cti), + FUNCTION(ddr_pxi0), + FUNCTION(ddr_bist), + FUNCTION(atest_tsens2), FUNCTION(vsense_trigger), - FUNCTION(wlan1_adc0), + FUNCTION(atest_usb1), + FUNCTION(qup_l4), FUNCTION(wlan1_adc1), - FUNCTION(wlan2_adc0), + FUNCTION(atest_usb13), + FUNCTION(ddr_pxi1), + FUNCTION(qup_l5), + FUNCTION(wlan1_adc0), + FUNCTION(atest_usb12), + FUNCTION(mdp_vsync), + FUNCTION(qup_l6), FUNCTION(wlan2_adc1), + FUNCTION(atest_usb11), + FUNCTION(ddr_pxi2), + FUNCTION(edp_lcd), + FUNCTION(dbg_out), + FUNCTION(wlan2_adc0), + FUNCTION(atest_usb10), + FUNCTION(m_voc), + FUNCTION(tsif1_sync), }; /* Every pin is maintained as a single group, and missing or non-existing pin @@ -1120,161 +1374,369 @@ static const struct msm_function sdm845_functions[] = { * Clients would not be able to request these dummy pin groups. */ static const struct msm_pingroup sdm845_groups[] = { - PINGROUP(0, EAST, qup0, _, _, _, _, _, _, _, _, _), - PINGROUP(1, EAST, qup0, _, _, _, _, _, _, _, _, _), - PINGROUP(2, EAST, qup0, _, _, _, _, _, _, _, _, _), - PINGROUP(3, EAST, qup0, _, _, _, _, _, _, _, _, _), - PINGROUP(4, NORTH, qup9, qdss_cti, _, _, _, _, _, _, _, _), - PINGROUP(5, NORTH, qup9, qdss_cti, _, _, _, _, _, _, _, _), - PINGROUP(6, NORTH, qup9, _, ddr_pxi0, _, _, _, _, _, _, _), - PINGROUP(7, NORTH, qup9, ddr_bist, _, atest_tsens2, vsense_trigger, atest_usb1, ddr_pxi0, _, _, _), - PINGROUP(8, EAST, qup_l4, _, ddr_bist, _, _, wlan1_adc1, atest_usb13, ddr_pxi1, _, _), - PINGROUP(9, EAST, qup_l5, ddr_bist, _, wlan1_adc0, atest_usb12, ddr_pxi1, _, _, _, _), - PINGROUP(10, EAST, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1, atest_usb11, ddr_pxi2, _, _, _, _), - PINGROUP(11, EAST, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0, atest_usb10, ddr_pxi2, _, _, _, _), - PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, _, _, _, _, _, _), - PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss, ddr_pxi3, _, _, _, _, _, _), - PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss, _, _, _, _, _, _, _), - PINGROUP(15, SOUTH, cam_mclk, qdss, _, _, _, _, _, _, _, _), - PINGROUP(16, SOUTH, cam_mclk, qdss, _, _, _, _, _, _, _, _), - PINGROUP(17, SOUTH, cci_i2c, qup1, qdss, _, _, _, _, _, _, _), - PINGROUP(18, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _), - PINGROUP(19, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _), - PINGROUP(20, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _), - PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss, _, _, _, _, _, _, _), - PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss, _, _, _, _, _, _, _), - PINGROUP(23, SOUTH, cci_timer2, qdss, _, _, _, _, _, _, _, _), - PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss, _, _, _, _, _, _, _), - PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss, _, _, _, _, _, _, _), - PINGROUP(26, SOUTH, cci_async, qdss, _, _, _, _, _, _, _, _), - PINGROUP(27, EAST, qup2, qdss, _, _, _, _, _, _, _, _), - PINGROUP(28, EAST, qup2, qdss, _, _, _, _, _, _, _, _), - PINGROUP(29, EAST, qup2, _, phase_flag, qdss, _, _, _, _, _, _), - PINGROUP(30, EAST, qup2, phase_flag, qdss, _, _, _, _, _, _, _), - PINGROUP(31, NORTH, qup11, qup14, _, _, _, _, _, _, _, _), - PINGROUP(32, NORTH, qup11, qup14, _, _, _, _, _, _, _, _), - PINGROUP(33, NORTH, qup11, qup14, _, _, _, _, _, _, _, _), - PINGROUP(34, NORTH, qup11, qup14, _, _, _, _, _, _, _, _), - PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, _, _, _, _, _, _, _), - PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, _, atest_tsens, _, _, _, _, _), - PINGROUP(37, SOUTH, qup_l6, agera_pll, _, _, _, _, _, _, _, _), - PINGROUP(38, NORTH, usb_phy, _, _, _, _, _, _, _, _, _), - PINGROUP(39, EAST, lpass_slimbus, _, _, _, _, _, _, _, _, _), - PINGROUP(40, SOUTH, sd_write, tsif1_error, _, _, _, _, _, _, _, _), - PINGROUP(41, EAST, qup3, _, qdss, _, _, _, _, _, _, _), - PINGROUP(42, EAST, qup3, _, qdss, _, _, _, _, _, _, _), - PINGROUP(43, EAST, qup3, _, qdss, _, _, _, _, _, _, _), - PINGROUP(44, EAST, qup3, _, qdss, _, _, _, _, _, _, _), - PINGROUP(45, EAST, qup6, _, _, _, _, _, _, _, _, _), - PINGROUP(46, EAST, qup6, _, _, _, _, _, _, _, _, _), - PINGROUP(47, EAST, qup6, _, _, _, _, _, _, _, _, _), - PINGROUP(48, EAST, qup6, _, _, _, _, _, _, _, _, _), - PINGROUP(49, NORTH, qup12, _, _, _, _, _, _, _, _, _), - PINGROUP(50, NORTH, qup12, _, _, _, _, _, _, _, _, _), - PINGROUP(51, NORTH, qup12, qdss_cti, _, _, _, _, _, _, _, _), - PINGROUP(52, NORTH, qup12, phase_flag, qdss_cti, _, _, _, _, _, _, _), - PINGROUP(53, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(54, NORTH, qup10, _, phase_flag, _, _, _, _, _, _, _), - PINGROUP(55, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(56, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag, _, _, _, _, _, _, _), - PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag, _, _, _, _, _, _, _), - PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag, _, _, _, _, _, _, _), - PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag, _, _, _, _, _, _, _), - PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag, _, _, _, _, _, _, _), - PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag, qdss_cti, _, _, _, _, _, _), - PINGROUP(63, NORTH, qua_mi2s, _, phase_flag, qdss_cti, _, _, _, _, _, _), - PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag, _, _, _, _, _, _, _), - PINGROUP(65, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _), - PINGROUP(66, NORTH, pri_mi2s_ws, qup8, _, _, _, _, _, _, _, _), - PINGROUP(67, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _), - PINGROUP(68, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _), - PINGROUP(69, EAST, spkr_i2s, audio_ref, _, _, _, _, _, _, _, _), - PINGROUP(70, EAST, lpass_slimbus, spkr_i2s, _, _, _, _, _, _, _, _), - PINGROUP(71, EAST, lpass_slimbus, spkr_i2s, tsense_pwm1, tsense_pwm2, _, _, _, _, _, _), - PINGROUP(72, EAST, lpass_slimbus, spkr_i2s, _, _, _, _, _, _, _, _), - PINGROUP(73, EAST, btfm_slimbus, atest_usb2, _, _, _, _, _, _, _, _), - PINGROUP(74, EAST, btfm_slimbus, ter_mi2s, phase_flag, atest_usb23, _, _, _, _, _, _), - PINGROUP(75, EAST, ter_mi2s, phase_flag, qdss, atest_usb22, _, _, _, _, _, _), - PINGROUP(76, EAST, ter_mi2s, phase_flag, qdss, atest_usb21, _, _, _, _, _, _), - PINGROUP(77, EAST, ter_mi2s, phase_flag, qdss, atest_usb20, _, _, _, _, _, _), - PINGROUP(78, EAST, ter_mi2s, gcc_gp1, _, _, _, _, _, _, _, _), - PINGROUP(79, NORTH, sec_mi2s, _, _, qdss, _, _, _, _, _, _), - PINGROUP(80, NORTH, sec_mi2s, _, qdss, _, _, _, _, _, _, _), - PINGROUP(81, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _), - PINGROUP(82, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _), - PINGROUP(83, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _), - PINGROUP(84, NORTH, qup15, _, _, _, _, _, _, _, _, _), - PINGROUP(85, EAST, qup5, _, _, _, _, _, _, _, _, _), - PINGROUP(86, EAST, qup5, _, _, _, _, _, _, _, _, _), - PINGROUP(87, EAST, qup5, _, _, _, _, _, _, _, _, _), - PINGROUP(88, EAST, qup5, _, _, _, _, _, _, _, _, _), - PINGROUP(89, SOUTH, tsif1_clk, qup4, qspi_cs, tgu_ch3, phase_flag, _, _, _, _, _), - PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, qspi_cs, mdp_vsync1, - mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag, qdss_cti), - PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, qspi_data, tgu_ch1, _, qdss_cti, _, _, _), - PINGROUP(92, SOUTH, tsif2_error, sdc4_data, qup4, qspi_data, vfr_1, tgu_ch2, _, _, _, _), - PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, qspi_data, _, qdss, _, _, _, _), - PINGROUP(94, SOUTH, tsif2_en, sdc4_data, qup7, qspi_data, _, _, _, _, _, _), - PINGROUP(95, SOUTH, tsif2_data, sdc4_data, qup7, qspi_clk, _, _, _, _, _, _), - PINGROUP(96, SOUTH, tsif2_sync, sdc4_data, qup7, phase_flag, _, _, _, _, _, _), - PINGROUP(97, NORTH, _, _, mdp_vsync, ldo_en, _, _, _, _, _, _), - PINGROUP(98, NORTH, _, mdp_vsync, ldo_update, _, _, _, _, _, _, _), - PINGROUP(99, NORTH, phase_flag, _, _, _, _, _, _, _, _, _), - PINGROUP(100, NORTH, phase_flag, _, _, _, _, _, _, _, _, _), - PINGROUP(101, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(102, NORTH, pci_e1, prng_rosc, _, _, _, _, _, _, _, _), - PINGROUP(103, NORTH, pci_e1, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(104, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, _, _, _, _, _, _, _), - PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, _, _, _, _, _, _, _), - PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, _, _, _, _, _, _, _), - PINGROUP(108, NORTH, uim2_present, qup13, _, _, _, _, _, _, _, _), - PINGROUP(109, NORTH, uim1_data, _, _, _, _, _, _, _, _, _), - PINGROUP(110, NORTH, uim1_clk, _, _, _, _, _, _, _, _, _), - PINGROUP(111, NORTH, uim1_reset, _, _, _, _, _, _, _, _, _), - PINGROUP(112, NORTH, uim1_present, _, _, _, _, _, _, _, _, _), - PINGROUP(113, NORTH, uim_batt, edp_hot, _, _, _, _, _, _, _, _), - PINGROUP(114, NORTH, _, nav_pps, nav_pps, _, _, _, _, _, _, _), - PINGROUP(115, NORTH, _, nav_pps, nav_pps, _, _, _, _, _, _, _), - PINGROUP(116, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(117, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _), - PINGROUP(118, NORTH, adsp_ext, _, qdss, atest_char, _, _, _, _, _, _), - PINGROUP(119, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _), - PINGROUP(120, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _), - PINGROUP(121, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _), - PINGROUP(122, EAST, _, qdss, _, _, _, _, _, _, _, _), - PINGROUP(123, EAST, qup_l4, _, qdss, _, _, _, _, _, _, _), - PINGROUP(124, EAST, qup_l5, _, qdss, _, _, _, _, _, _, _), - PINGROUP(125, EAST, qup_l6, _, _, _, _, _, _, _, _, _), - PINGROUP(126, EAST, _, _, _, _, _, _, _, _, _, _), - PINGROUP(127, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(128, NORTH, nav_pps, nav_pps, _, _, _, _, _, _, _, _), - PINGROUP(129, NORTH, nav_pps, nav_pps, _, _, _, _, _, _, _, _), - PINGROUP(130, NORTH, qlink_request, _, _, _, _, _, _, _, _, _), - PINGROUP(131, NORTH, qlink_enable, _, _, _, _, _, _, _, _, _), - PINGROUP(132, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(133, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(134, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(135, NORTH, _, pa_indicator, _, _, _, _, _, _, _, _), - PINGROUP(136, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(137, NORTH, _, _, phase_flag, _, _, _, _, _, _, _), - PINGROUP(138, NORTH, _, _, phase_flag, _, _, _, _, _, _, _), - PINGROUP(139, NORTH, _, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(140, NORTH, _, _, phase_flag, _, _, _, _, _, _, _), - PINGROUP(141, NORTH, _, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(142, NORTH, _, phase_flag, _, _, _, _, _, _, _, _), - PINGROUP(143, NORTH, _, nav_pps, nav_pps, _, phase_flag, _, _, _, _, _), - PINGROUP(144, NORTH, mss_lte, _, _, _, _, _, _, _, _, _), - PINGROUP(145, NORTH, mss_lte, _, _, _, _, _, _, _, _, _), - PINGROUP(146, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(147, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(148, NORTH, _, _, _, _, _, _, _, _, _, _), - PINGROUP(149, NORTH, _, _, _, _, _, _, _, _, _, _), - SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6), - SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3), - SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0), - UFS_RESET(ufs_reset, 0x99f000), + [0] = PINGROUP(0, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [1] = PINGROUP(1, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [2] = PINGROUP(2, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [3] = PINGROUP(3, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA, + NA), + [7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2, + vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA), + [8] = PINGROUP(8, NORTH, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1, + atest_usb13, ddr_pxi1, NA, NA), + [9] = PINGROUP(9, NORTH, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12, + ddr_pxi1, NA, NA, NA, NA), + [10] = PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1, + atest_usb11, ddr_pxi2, NA, NA, NA, NA), + [11] = PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0, + atest_usb10, ddr_pxi2, NA, NA, NA, NA), + [12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA, + NA, NA, NA, NA, NA), + [13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0, + ddr_pxi3, NA, NA, NA, NA, NA, NA), + [14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA, + NA, NA, NA, NA), + [15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, + NA, NA, NA), + [16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, + NA, NA, NA), + [17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA, + NA, NA, NA), + [18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA, + NA, NA, NA), + [19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA, + NA, NA, NA), + [20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA, + NA, NA, NA), + [21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA, + NA, NA, NA, NA), + [22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA, + NA, NA, NA, NA), + [23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA, + NA, NA, NA), + [24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA, + NA, NA, NA, NA, NA), + [25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA, + NA, NA, NA, NA, NA), + [26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA, + NA, NA, NA), + [27] = PINGROUP(27, NORTH, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA, + NA, NA), + [28] = PINGROUP(28, NORTH, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA, + NA, NA), + [29] = PINGROUP(29, NORTH, qup2, NA, phase_flag1, qdss_gpio15, NA, NA, + NA, NA, NA, NA), + [30] = PINGROUP(30, NORTH, qup2, phase_flag2, qdss_gpio, NA, NA, NA, + NA, NA, NA, NA), + [31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA, + NA), + [35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA, + NA, NA, NA), + [36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens, + NA, NA, NA, NA, NA), + [37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA, + NA, NA), + [38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [39] = PINGROUP(39, NORTH, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA, + NA, NA, NA), + [41] = PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, + NA, NA), + [42] = PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, + NA, NA), + [43] = PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA, + NA, NA), + [44] = PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA, + NA, NA), + [45] = PINGROUP(45, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [46] = PINGROUP(46, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [47] = PINGROUP(47, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [48] = PINGROUP(48, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA, + NA), + [52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA, + NA, NA, NA, NA), + [53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA, + NA, NA), + [54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA, + NA, NA), + [55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA, + NA, NA), + [56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA, + NA, NA), + [57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA, + NA, NA, NA, NA), + [58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA, + NA, NA, NA, NA), + [59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA, + NA, NA, NA, NA), + [60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA, + NA, NA, NA, NA, NA), + [61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA, + NA, NA, NA, NA, NA), + [62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti, + NA, NA, NA, NA, NA, NA), + [63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA, + NA, NA, NA, NA, NA), + [64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA, + NA, NA, NA, NA), + [65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA, + NA, NA), + [67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA, + NA), + [69] = PINGROUP(69, NORTH, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA, + NA, NA), + [70] = PINGROUP(70, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA, + NA, NA, NA), + [71] = PINGROUP(71, NORTH, lpass_slimbus, spkr_i2s, tsense_pwm1, + tsense_pwm2, NA, NA, NA, NA, NA, NA), + [72] = PINGROUP(72, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA, + NA, NA, NA), + [73] = PINGROUP(73, NORTH, btfm_slimbus, atest_usb2, NA, NA, NA, NA, + NA, NA, NA, NA), + [74] = PINGROUP(74, NORTH, btfm_slimbus, ter_mi2s, phase_flag7, + atest_usb23, NA, NA, NA, NA, NA, NA), + [75] = PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8, + atest_usb22, NA, NA, NA, NA, NA, NA), + [76] = PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9, + atest_usb21, NA, NA, NA, NA, NA, NA), + [77] = PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10, + atest_usb20, NA, NA, NA, NA, NA, NA), + [78] = PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA, + NA, NA), + [79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA, + NA, NA, NA), + [80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA, + NA, NA, NA), + [81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA, + NA), + [84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [85] = PINGROUP(85, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [86] = PINGROUP(86, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [87] = PINGROUP(87, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [88] = PINGROUP(88, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA, + NA, NA, NA, NA, NA), + [90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1, + mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti, + NA), + [91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA, + qdss_cti, NA, NA, NA, NA), + [92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2, + NA, NA, NA, NA, NA), + [93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13, + NA, NA, NA, NA, NA), + [94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA, + NA, NA), + [95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, NA, NA, NA, NA, NA, + NA, NA), + [96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA, + NA, NA, NA, NA, NA), + [97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA, + NA, NA), + [98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA, + NA, NA, NA), + [99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA, + NA, NA), + [103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA, + NA, NA, NA), + [104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA, + NA, NA, NA), + [106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA, + NA, NA, NA), + [107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA, + NA, NA, NA), + [108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA, + NA, NA, NA), + [109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA, + NA, NA), + [114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA, + NA, NA), + [115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA, + NA, NA), + [116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA, + NA, NA, NA, NA), + [118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA, + NA, NA, NA, NA, NA), + [119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA, + NA, NA, NA, NA), + [120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA, + NA, NA, NA, NA), + [121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA, + NA, NA, NA, NA), + [122] = PINGROUP(122, NORTH, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, + NA, NA), + [123] = PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA, + NA, NA), + [124] = PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA, + NA, NA), + [125] = PINGROUP(125, NORTH, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [126] = PINGROUP(126, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA, + NA, NA), + [129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA, + NA, NA), + [130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA, + NA, NA), + [132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA, + NA, NA), + [136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA, + NA, NA), + [138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA, + NA, NA), + [139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA, + NA, NA), + [140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA, + NA, NA), + [141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA, + NA, NA), + [142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA, + NA, NA), + [143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31, + NA, NA, NA, NA, NA), + [144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA, + NA), + [146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), + [150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6), + [151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3), + [152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0), + [153] = UFS_RESET(ufs_reset, 0x99f000), +}; + +static struct msm_dir_conn sdm845_dir_conn[] = { + {1, 510}, + {3, 511}, + {5, 512}, + {10, 513}, + {11, 514}, + {20, 515}, + {22, 516}, + {24, 517}, + {26, 518}, + {30, 519}, + {31, 632}, + {32, 521}, + {34, 522}, + {36, 523}, + {37, 524}, + {38, 525}, + {39, 526}, + {40, 527}, + {41, 630}, + {43, 529}, + {44, 530}, + {46, 531}, + {48, 532}, + {49, 633}, + {52, 534}, + {53, 535}, + {54, 536}, + {56, 537}, + {57, 538}, + {58, 539}, + {59, 540}, + {60, 541}, + {61, 542}, + {62, 543}, + {63, 544}, + {64, 545}, + {66, 546}, + {68, 547}, + {71, 548}, + {73, 549}, + {77, 550}, + {78, 551}, + {79, 552}, + {80, 553}, + {84, 554}, + {85, 555}, + {86, 556}, + {88, 557}, + {89, 631}, + {91, 559}, + {92, 560}, + {95, 561}, + {96, 562}, + {97, 563}, + {101, 564}, + {103, 565}, + {104, 566}, + {115, 570}, + {116, 571}, + {117, 572}, + {118, 573}, + {119, 609}, + {120, 610}, + {121, 611}, + {122, 612}, + {123, 613}, + {124, 614}, + {125, 615}, + {126, 616}, + {127, 617}, + {128, 618}, + {129, 619}, + {130, 620}, + {132, 621}, + {133, 622}, + {145, 623}, + {0, 216}, + {0, 215}, + {0, 214}, + {0, 213}, + {0, 212}, + {0, 211}, + {0, 210}, + {0, 209}, }; static const struct msm_pinctrl_soc_data sdm845_pinctrl = { @@ -1285,6 +1747,7 @@ static const struct msm_pinctrl_soc_data sdm845_pinctrl = { .groups = sdm845_groups, .ngroups = ARRAY_SIZE(sdm845_groups), .ngpios = 150, + .dir_conn = sdm845_dir_conn, }; static int sdm845_pinctrl_probe(struct platform_device *pdev) @@ -1300,6 +1763,7 @@ static const struct of_device_id sdm845_pinctrl_of_match[] = { static struct platform_driver sdm845_pinctrl_driver = { .driver = { .name = "sdm845-pinctrl", + .owner = THIS_MODULE, .of_match_table = sdm845_pinctrl_of_match, }, .probe = sdm845_pinctrl_probe, diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 273ed10ecd3b..4e08effdaf0d 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -13,6 +13,16 @@ config MSM_EXT_DISPLAY This should be enabled to support audio & video over HDMI or DP for hot pluggable sink devices. +config QPNP_COINCELL + tristate "QPNP coincell charger support" + depends on SPMI + help + This driver supports the QPNP coincell peripheral found inside of + Qualcomm Technologies, Inc. QPNP PMIC devices. The coincell charger + provides a means to charge a coincell battery or backup capacitor + which is used to maintain PMIC register state when the main battery is + removed from the mobile device. + config QPNP_REVID tristate "QPNP Revision ID Peripheral" depends on SPMI diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index c16fd2aae52c..e2baa428984a 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_IPA) += ipa/ obj-$(CONFIG_IPA3) += ipa/ obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o +obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o obj-$(CONFIG_MSM_11AD) += msm_11ad/ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 84023764bb28..c5020d846b32 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -3884,7 +3884,10 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in, IPA_GENERIC_RX_BUFF_BASE_SZ); sys->get_skb = ipa3_get_skb_ipa_rx; sys->free_skb = ipa3_free_skb_rx; - in->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR; + if (in->bypass_agg) + in->ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR; + else + in->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR; if (in->client == IPA_CLIENT_APPS_WAN_COAL_CONS) in->ipa_ep_cfg.aggr.aggr = IPA_COALESCE; else diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index fa6fa393fc23..dfb17dda7a1b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -2883,6 +2883,79 @@ static const struct ipa_ep_configuration ipa3_ep_mapping }; +static struct ipa3_mem_partition ipa_3_5_1_mem_part = { + .ofst_start = 0x280, + .v4_flt_hash_ofst = 0x288, + .v4_flt_hash_size = 0x78, + .v4_flt_hash_size_ddr = 0x4000, + .v4_flt_nhash_ofst = 0x308, + .v4_flt_nhash_size = 0x78, + .v4_flt_nhash_size_ddr = 0x4000, + .v6_flt_hash_ofst = 0x388, + .v6_flt_hash_size = 0x78, + .v6_flt_hash_size_ddr = 0x4000, + .v6_flt_nhash_ofst = 0x408, + .v6_flt_nhash_size = 0x78, + .v6_flt_nhash_size_ddr = 0x4000, + .v4_rt_num_index = 0xf, + .v4_modem_rt_index_lo = 0x0, + .v4_modem_rt_index_hi = 0x7, + .v4_apps_rt_index_lo = 0x8, + .v4_apps_rt_index_hi = 0xe, + .v4_rt_hash_ofst = 0x488, + .v4_rt_hash_size = 0x78, + .v4_rt_hash_size_ddr = 0x4000, + .v4_rt_nhash_ofst = 0x508, + .v4_rt_nhash_size = 0x78, + .v4_rt_nhash_size_ddr = 0x4000, + .v6_rt_num_index = 0xf, + .v6_modem_rt_index_lo = 0x0, + .v6_modem_rt_index_hi = 0x7, + .v6_apps_rt_index_lo = 0x8, + .v6_apps_rt_index_hi = 0xe, + .v6_rt_hash_ofst = 0x588, + .v6_rt_hash_size = 0x78, + .v6_rt_hash_size_ddr = 0x4000, + .v6_rt_nhash_ofst = 0x608, + .v6_rt_nhash_size = 0x78, + .v6_rt_nhash_size_ddr = 0x4000, + .modem_hdr_ofst = 0x688, + .modem_hdr_size = 0x140, + .apps_hdr_ofst = 0x7c8, + .apps_hdr_size = 0x0, + .apps_hdr_size_ddr = 0x800, + .modem_hdr_proc_ctx_ofst = 0x7d0, + .modem_hdr_proc_ctx_size = 0x200, + .apps_hdr_proc_ctx_ofst = 0x9d0, + .apps_hdr_proc_ctx_size = 0x200, + .apps_hdr_proc_ctx_size_ddr = 0x0, + .modem_comp_decomp_ofst = 0x0, + .modem_comp_decomp_size = 0x0, + .modem_ofst = 0xbd8, + .modem_size = 0x1024, + .apps_v4_flt_hash_ofst = 0x2000, + .apps_v4_flt_hash_size = 0x0, + .apps_v4_flt_nhash_ofst = 0x2000, + .apps_v4_flt_nhash_size = 0x0, + .apps_v6_flt_hash_ofst = 0x2000, + .apps_v6_flt_hash_size = 0x0, + .apps_v6_flt_nhash_ofst = 0x2000, + .apps_v6_flt_nhash_size = 0x0, + .uc_info_ofst = 0x80, + .uc_info_size = 0x200, + .end_ofst = 0x2000, + .apps_v4_rt_hash_ofst = 0x2000, + .apps_v4_rt_hash_size = 0x0, + .apps_v4_rt_nhash_ofst = 0x2000, + .apps_v4_rt_nhash_size = 0x0, + .apps_v6_rt_hash_ofst = 0x2000, + .apps_v6_rt_hash_size = 0x0, + .apps_v6_rt_nhash_ofst = 0x2000, + .apps_v6_rt_nhash_size = 0x0, + .uc_descriptor_ram_ofst = 0x1c00, + .uc_descriptor_ram_size = 0x400, +}; + static struct ipa3_mem_partition ipa_4_1_mem_part = { .ofst_start = 0x280, .v4_flt_hash_ofst = 0x288, @@ -5623,6 +5696,9 @@ int ipa3_straddle_boundary(u32 start, u32 end, u32 boundary) int ipa3_init_mem_partition(enum ipa_hw_type type) { switch (type) { + case IPA_HW_v3_5_1: + ipa3_ctx->ctrl->mem_partition = &ipa_3_5_1_mem_part; + break; case IPA_HW_v4_1: ipa3_ctx->ctrl->mem_partition = &ipa_4_1_mem_part; break; @@ -5645,7 +5721,6 @@ int ipa3_init_mem_partition(enum ipa_hw_type type) case IPA_HW_v3_0: case IPA_HW_v3_1: case IPA_HW_v3_5: - case IPA_HW_v3_5_1: case IPA_HW_v4_0: IPAERR("unsupported version %d\n", type); return -EPERM; @@ -7770,11 +7845,6 @@ static int _ipa_suspend_resume_pipe(enum ipa_client_type client, bool suspend) struct ipa3_ep_context *ep; int res; - if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) { - IPAERR("not supported\n"); - return -EPERM; - } - ipa_ep_idx = ipa3_get_ep_mapping(client); if (ipa_ep_idx < 0) { IPADBG("client %d not configued\n", client); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 8a52d823ca63..d4858d77c64e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -160,6 +160,7 @@ struct rmnet_ipa3_context { atomic_t ap_suspend; bool ipa_config_is_apq; bool ipa_mhi_aggr_formet_set; + bool no_qmap_config; }; static struct rmnet_ipa3_context *rmnet_ipa3_ctx; @@ -1236,7 +1237,8 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - if (skb->protocol != htons(ETH_P_MAP)) { + if (skb->protocol != htons(ETH_P_MAP) && + (!rmnet_ipa3_ctx->no_qmap_config)) { IPAWANDBG_LOW ("SW filtering out none QMAP packet received from %s", current->comm); @@ -1257,17 +1259,22 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } if (netif_queue_stopped(dev)) { - if (qmap_check && - atomic_read(&wwan_ptr->outstanding_pkts) < - rmnet_ipa3_ctx->outstanding_high_ctl) { - IPAWANERR("[%s]Queue stop, send ctrl pkts\n", - dev->name); - goto send; - } else { - IPAWANERR("[%s]fatal: %s stopped\n", dev->name, - __func__); + if (rmnet_ipa3_ctx->no_qmap_config) { spin_unlock_irqrestore(&wwan_ptr->lock, flags); return NETDEV_TX_BUSY; + } else { + if (qmap_check && + atomic_read(&wwan_ptr->outstanding_pkts) < + rmnet_ipa3_ctx->outstanding_high_ctl) { + IPAWANERR("[%s]Queue stop, send ctrl pkts\n", + dev->name); + goto send; + } else { + IPAWANERR("[%s]fatal: %s stopped\n", dev->name, + __func__); + spin_unlock_irqrestore(&wwan_ptr->lock, flags); + return NETDEV_TX_BUSY; + } } } /* checking High WM hit */ @@ -1428,7 +1435,8 @@ static void apps_ipa_packet_receive_notify(void *priv, IPAWANDBG_LOW("Rx packet was received"); skb->dev = IPA_NETDEV(); - skb->protocol = htons(ETH_P_MAP); + if (!rmnet_ipa3_ctx->no_qmap_config) + skb->protocol = htons(ETH_P_MAP); skb_set_mac_header(skb, 0); if (ipa3_rmnet_res.ipa_napi_enable) { @@ -1503,6 +1511,12 @@ static int handle3_ingress_format(struct net_device *dev, IPAWANDBG("DL chksum set\n"); } + + if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_IP_ROUTE) { + rmnet_ipa3_ctx->no_qmap_config = true; + ipa_wan_ep_cfg->bypass_agg = true; + } + if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) { IPAWANDBG("get AGG size %d count %d\n", in->u.ingress_format.agg_size, @@ -1525,8 +1539,11 @@ static int handle3_ingress_format(struct net_device *dev, ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 8; rmnet_ipa3_ctx->dl_csum_offload_enabled = true; } else { - ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4; rmnet_ipa3_ctx->dl_csum_offload_enabled = false; + if (rmnet_ipa3_ctx->no_qmap_config) + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 0; + else + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4; } ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1; @@ -1629,6 +1646,9 @@ static int handle3_egress_format(struct net_device *dev, return -EFAULT; } + if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_IP_ROUTE) + rmnet_ipa3_ctx->no_qmap_config = true; + ipa_wan_ep_cfg = &rmnet_ipa3_ctx->apps_to_ipa_ep_cfg; if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM) { IPAWANDBG("UL chksum set\n"); @@ -1637,7 +1657,10 @@ static int handle3_egress_format(struct net_device *dev, IPA_ENABLE_CS_OFFLOAD_UL; ipa_wan_ep_cfg->ipa_ep_cfg.cfg.cs_metadata_hdr_offset = 1; } else { - ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4; + if (rmnet_ipa3_ctx->no_qmap_config) + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 0; + else + ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4; } if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION) { @@ -2767,7 +2790,8 @@ static int ipa3_lcl_mdm_ssr_notifier_cb(struct notifier_block *this, if (atomic_read(&rmnet_ipa3_ctx->is_ssr) && ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) ipa3_q6_post_shutdown_cleanup(); - ipa3_odl_pipe_cleanup(true); + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_1) + ipa3_odl_pipe_cleanup(true); IPAWANINFO("IPA BEFORE_SHUTDOWN handling is complete\n"); break; case SUBSYS_AFTER_SHUTDOWN: @@ -2807,7 +2831,8 @@ static int ipa3_lcl_mdm_ssr_notifier_cb(struct notifier_block *this, if (!atomic_read(&rmnet_ipa3_ctx->is_initialized) && atomic_read(&rmnet_ipa3_ctx->is_ssr)) platform_driver_register(&rmnet_ipa_driver); - ipa3_odl_pipe_open(); + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_1) + ipa3_odl_pipe_open(); IPAWANINFO("IPA AFTER_POWERUP handling is complete\n"); break; default: diff --git a/drivers/platform/msm/qpnp-coincell.c b/drivers/platform/msm/qpnp-coincell.c new file mode 100644 index 000000000000..d8b570d2915b --- /dev/null +++ b/drivers/platform/msm/qpnp-coincell.c @@ -0,0 +1,272 @@ +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QPNP_COINCELL_DRIVER_NAME "qcom,qpnp-coincell" + +struct qpnp_coincell { + struct platform_device *pdev; + struct regmap *regmap; + u16 base_addr; +}; + +#define QPNP_COINCELL_REG_TYPE 0x04 +#define QPNP_COINCELL_REG_SUBTYPE 0x05 +#define QPNP_COINCELL_REG_RSET 0x44 +#define QPNP_COINCELL_REG_VSET 0x45 +#define QPNP_COINCELL_REG_ENABLE 0x46 + +#define QPNP_COINCELL_TYPE 0x02 +#define QPNP_COINCELL_SUBTYPE 0x20 +#define QPNP_COINCELL_ENABLE 0x80 +#define QPNP_COINCELL_DISABLE 0x00 + +static const int qpnp_rset_map[] = {2100, 1700, 1200, 800}; +static const int qpnp_vset_map[] = {2500, 3200, 3100, 3000}; + +static int qpnp_coincell_set_resistance(struct qpnp_coincell *chip, int rset) +{ + int i, rc; + u8 reg; + + for (i = 0; i < ARRAY_SIZE(qpnp_rset_map); i++) + if (rset == qpnp_rset_map[i]) + break; + + if (i >= ARRAY_SIZE(qpnp_rset_map)) { + pr_err("invalid rset=%d value\n", rset); + return -EINVAL; + } + + reg = i; + rc = regmap_write(chip->regmap, + chip->base_addr + QPNP_COINCELL_REG_RSET, reg); + if (rc) + dev_err(&chip->pdev->dev, + "%s: could not write to RSET register, rc=%d\n", + __func__, rc); + + return rc; +} + +static int qpnp_coincell_set_voltage(struct qpnp_coincell *chip, int vset) +{ + int i, rc; + u8 reg; + + for (i = 0; i < ARRAY_SIZE(qpnp_vset_map); i++) + if (vset == qpnp_vset_map[i]) + break; + + if (i >= ARRAY_SIZE(qpnp_vset_map)) { + pr_err("invalid vset=%d value\n", vset); + return -EINVAL; + } + + reg = i; + rc = regmap_write(chip->regmap, + chip->base_addr + QPNP_COINCELL_REG_VSET, reg); + if (rc) + dev_err(&chip->pdev->dev, + "%s: could not write to VSET register, rc=%d\n", + __func__, rc); + + return rc; +} + +static int qpnp_coincell_set_charge(struct qpnp_coincell *chip, bool enabled) +{ + int rc; + u8 reg; + + reg = enabled ? QPNP_COINCELL_ENABLE : QPNP_COINCELL_DISABLE; + rc = regmap_write(chip->regmap, + chip->base_addr + QPNP_COINCELL_REG_ENABLE, reg); + if (rc) + dev_err(&chip->pdev->dev, + "%s: could not write to ENABLE register, rc=%d\n", + __func__, rc); + + return rc; +} + +static void qpnp_coincell_charger_show_state(struct qpnp_coincell *chip) +{ + int rc, rset, vset, temp; + bool enabled; + u8 reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET + 1]; + + rc = regmap_bulk_read(chip->regmap, + chip->base_addr + QPNP_COINCELL_REG_RSET, reg, + ARRAY_SIZE(reg)); + if (rc) { + dev_err(&chip->pdev->dev, + "%s: could not read RSET register, rc=%d\n", + __func__, rc); + return; + } + + temp = reg[QPNP_COINCELL_REG_RSET - QPNP_COINCELL_REG_RSET]; + if (temp >= ARRAY_SIZE(qpnp_rset_map)) { + dev_err(&chip->pdev->dev, + "unknown RSET=0x%02X register value\n", + temp); + return; + } + rset = qpnp_rset_map[temp]; + + temp = reg[QPNP_COINCELL_REG_VSET - QPNP_COINCELL_REG_RSET]; + if (temp >= ARRAY_SIZE(qpnp_vset_map)) { + dev_err(&chip->pdev->dev, + "unknown VSET=0x%02X register value\n", + temp); + return; + } + vset = qpnp_vset_map[temp]; + + temp = reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET]; + enabled = temp & QPNP_COINCELL_ENABLE; + + pr_info("enabled=%c, voltage=%d mV, resistance=%d ohm\n", + (enabled ? 'Y' : 'N'), vset, rset); +} + +static int qpnp_coincell_check_type(struct qpnp_coincell *chip) +{ + int rc; + u8 type[2]; + + rc = regmap_bulk_read(chip->regmap, + chip->base_addr + QPNP_COINCELL_REG_TYPE, type, + 2); + if (rc) { + dev_err(&chip->pdev->dev, + "%s: could not read type register, rc=%d\n", + __func__, rc); + return rc; + } + + if (type[0] != QPNP_COINCELL_TYPE || type[1] != QPNP_COINCELL_SUBTYPE) { + dev_err(&chip->pdev->dev, + "%s: invalid type=0x%02X or subtype=0x%02X register value\n", + __func__, type[0], type[1]); + return -ENODEV; + } + + return rc; +} + +static int qpnp_coincell_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct qpnp_coincell *chip; + unsigned int base; + u32 temp; + int rc = 0; + + if (!node) { + dev_err(&pdev->dev, "%s: device node missing\n", __func__); + return -ENODEV; + } + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + chip->pdev = pdev; + + rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", + pdev->dev.of_node->full_name, rc); + return rc; + } + chip->base_addr = base; + + rc = qpnp_coincell_check_type(chip); + if (rc) + return rc; + + rc = of_property_read_u32(node, "qcom,rset-ohms", &temp); + if (!rc) { + rc = qpnp_coincell_set_resistance(chip, temp); + if (rc) + return rc; + } + + rc = of_property_read_u32(node, "qcom,vset-millivolts", &temp); + if (!rc) { + rc = qpnp_coincell_set_voltage(chip, temp); + if (rc) + return rc; + } + + rc = of_property_read_u32(node, "qcom,charge-enable", &temp); + if (!rc) { + rc = qpnp_coincell_set_charge(chip, temp); + if (rc) + return rc; + } + + qpnp_coincell_charger_show_state(chip); + + return 0; +} + +static int qpnp_coincell_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id qpnp_coincell_match_table[] = { + { .compatible = QPNP_COINCELL_DRIVER_NAME, }, + {} +}; + +static const struct platform_device_id qpnp_coincell_id[] = { + { QPNP_COINCELL_DRIVER_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(spmi, qpnp_coincell_id); + +static struct platform_driver qpnp_coincell_driver = { + .driver = { + .name = QPNP_COINCELL_DRIVER_NAME, + .of_match_table = qpnp_coincell_match_table, + .owner = THIS_MODULE, + }, + .probe = qpnp_coincell_probe, + .remove = qpnp_coincell_remove, + .id_table = qpnp_coincell_id, +}; + +module_platform_driver(qpnp_coincell_driver); + +MODULE_DESCRIPTION("QPNP PMIC coincell charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index 2f0dd8f282df..7ff5fcfd3915 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -2927,6 +2927,9 @@ static struct msm_usb_bam_data *usb_bam_dt_to_data( else usb_bam_data->usb_bam_fifo_baseaddr = addr; + usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node, + "qcom,ignore-core-reset-ack"); + usb_bam_data->disable_clk_gating = of_property_read_bool(node, "qcom,disable-clk-gating"); diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index c1b0799963e6..de98700c63bd 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -62,7 +62,7 @@ static void scm_disable_sdi(void); * There is no API from TZ to re-enable the registers. * So the SDI cannot be re-enabled when it already by-passed. */ -static int download_mode = 1; +static int download_mode = 0; static struct kobject dload_kobj; static int in_panic; @@ -489,12 +489,23 @@ static void msm_restart_prepare(const char *cmd) if (force_warm_reboot) pr_info("Forcing a warm reset of the system\n"); + if (in_panic) + need_warm_reset = true; + /* Hard reset the PMIC unless memory contents must be maintained. */ if (force_warm_reboot || need_warm_reset) qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); else qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); + if (in_panic) { + // Reboot to recovery + qpnp_pon_set_restart_reason( + PON_RESTART_REASON_RECOVERY); + __raw_writel(0x77665502, restart_reason); + goto finish_set_restart_reason; + } + if (cmd != NULL) { if (!strncmp(cmd, "bootloader", 10)) { qpnp_pon_set_restart_reason( @@ -535,6 +546,7 @@ static void msm_restart_prepare(const char *cmd) } } +finish_set_restart_reason: flush_cache_all(); /*outer_flush_all is not supported by 64bit kernel*/ diff --git a/drivers/power/supply/power_supply.h b/drivers/power/supply/power_supply.h index cc439fd89d8d..4f7be680fc26 100644 --- a/drivers/power/supply/power_supply.h +++ b/drivers/power/supply/power_supply.h @@ -26,6 +26,12 @@ static inline void power_supply_init_attrs(struct device_type *dev_type) {} #endif /* CONFIG_SYSFS */ +#if defined(CONFIG_HOUSTON) +extern void ht_register_power_supply(struct power_supply *psy); +#else +static inline void ht_register_power_supply(struct power_supply *psy) {} +#endif /* CONFIG_HOUSTON */ + #ifdef CONFIG_LEDS_TRIGGERS extern void power_supply_update_leds(struct power_supply *psy); diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 2963570da602..bed25384c07d 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -949,6 +949,8 @@ __power_supply_register(struct device *parent, &psy->deferred_register_work, POWER_SUPPLY_DEFERRED_REGISTER_TIME); + ht_register_power_supply(psy); + return psy; create_triggers_failed: diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 7a5a8177723a..0ecf00468df8 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -46,7 +46,13 @@ static const char * const power_supply_type_text[] = { "USB_PD", "USB_PD_DRP", "BrickID", "USB_HVDCP", "USB_HVDCP_3", "USB_HVDCP_3P5", "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "USB_C_UFP", "USB_C_DFP", - "Charge_Pump", +/* david.liu@bsp, 20171023 Battery & Charging porting */ + "Charge_Pump", "Wipower", "DASH" +}; + +/* david.liu@bsp, 20171023 Battery & Charging porting */ +static const char * const cc_orientation_text[] = { + "Unknown", "cc1", "cc2" }; static const char * const power_supply_usb_type_text[] = { @@ -202,6 +208,11 @@ static ssize_t power_supply_show_property(struct device *dev, ret = sprintf(buf, "%s\n", power_supply_usbc_pr_text[value.intval]); break; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + case POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION: + ret = snprintf(buf, sizeof(buf), "%s\n", + cc_orientation_text[value.intval]); + break; case POWER_SUPPLY_PROP_TYPEC_SRC_RP: ret = sprintf(buf, "%s\n", power_supply_typec_src_rp_text[value.intval]); @@ -283,6 +294,27 @@ 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), +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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(hw_detect), + 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(connect_disable), + POWER_SUPPLY_ATTR(connecter_temp), + POWER_SUPPLY_ATTR(bq_soc), + POWER_SUPPLY_ATTR(oem_cc_orientation), POWER_SUPPLY_ATTR(charge_type), POWER_SUPPLY_ATTR(health), POWER_SUPPLY_ATTR(present), @@ -375,6 +407,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(temp_cold), POWER_SUPPLY_ATTR(temp_hot), POWER_SUPPLY_ATTR(system_temp_level), + POWER_SUPPLY_ATTR(battery_health), + POWER_SUPPLY_ATTR(op_disable_charge), + POWER_SUPPLY_ATTR(remaining_capacity), POWER_SUPPLY_ATTR(resistance), POWER_SUPPLY_ATTR(resistance_capacitive), POWER_SUPPLY_ATTR(resistance_id), diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index 9581455fc4d2..42495697f357 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -113,6 +113,15 @@ config SMB1355_SLAVE_CHARGER The driver reports the charger status via the power supply framework. A charger status change triggers an IRQ via the device STAT pin. +config QPNP_QNOVO + bool "QPNP QNOVO driver" + depends on MFD_SPMI_PMIC + help + Say Y here to enable the Qnovo pulse charging engine. Qnovo driver + accepts pulse parameters via sysfs entries and programs the hardware + module. It also allows userspace code to read diagnostics of voltage + and current measured during certain phases of the pulses. + config QPNP_QNOVO5 bool "QPNP QNOVO5 driver" depends on MFD_SPMI_PMIC @@ -175,4 +184,31 @@ config SMB1398_CHARGER wireless charger receiver and provide the input for downstream chargers. +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 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 ONEPLUS_FASTCHG + tristate "ONEPLUS FAST CHARGE" + depends on I2C + help + Say Y here to enable the ONEPLUS FAST CHARGE driver.This adds support + for VOOC charge and state of charge of battery connected to + the battery. The state of charge is reported through a battery power + supply property + endmenu diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index b9cc5e6b00f2..432c6e19228a 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.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) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o +obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o pmic-voter.o obj-$(CONFIG_QPNP_QNOVO5) += qpnp-qnovo5.o battery.o pmic-voter.o obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o pmic-voter.o obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o @@ -15,3 +16,6 @@ obj-$(CONFIG_SMB1398_CHARGER) += smb1398-charger.o pmic-voter.o obj-$(CONFIG_QPNP_SMBLITE) += step-chg-jeita.o battery.o qpnp-smblite.o smblite-lib.o pmic-voter.o storm-watch.o schgm-flashlite.o obj-$(CONFIG_QPNP_VM_BMS) += qpnp-vm-bms.o batterydata-lib.o batterydata-interface.o obj-$(CONFIG_QPNP_LINEAR_CHARGER) += qpnp-linear-charger.o +obj-$(CONFIG_FG_BQ27541) += bq27541_fuelgauger.o +obj-$(CONFIG_ONEPLUS_FASTCHG) += oneplus_fastchg.o op_dash_adapter.o +obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index ec45b7d1c502..efd5e0123b32 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -952,6 +952,8 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, if (!chip->main_psy) return 0; + pr_info("total_fcc_ua=%d\n", total_fcc_ua); + if (!chip->cp_disable_votable) chip->cp_disable_votable = find_votable("CP_DISABLE"); @@ -1221,6 +1223,7 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, return 0; pval.intval = fv_uv; + pr_info("fv_uv=%d\n", fv_uv); rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); @@ -1326,6 +1329,7 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); } + pr_info("total_icl_ua=%d\n", icl_ua); /* set the effective ICL */ pval.intval = icl_ua; @@ -1400,6 +1404,7 @@ static int pl_disable_vote_callback(struct votable *votable, chip->fcc_stepper_enable = pval.intval; pr_debug("FCC Stepper %s\n", pval.intval ? "enabled" : "disabled"); +#if !defined(CONFIG_QPNP_SMB2) rc = power_supply_get_property(chip->main_psy, POWER_SUPPLY_PROP_MAIN_FCC_MAX, &pval); if (rc < 0) { @@ -1409,6 +1414,7 @@ static int pl_disable_vote_callback(struct votable *votable, } else if (pval.intval > 0) { chip->main_fcc_max = pval.intval; } +#endif if (chip->fcc_stepper_enable) { cancel_delayed_work_sync(&chip->fcc_stepper_work); diff --git a/drivers/power/supply/qcom/bq27541_fuelgauger.c b/drivers/power/supply/qcom/bq27541_fuelgauger.c new file mode 100755 index 000000000000..22e665578e71 --- /dev/null +++ b/drivers/power/supply/qcom/bq27541_fuelgauger.c @@ -0,0 +1,2229 @@ +/* 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 +#include + +#ifdef CONFIG_OF +#include +#include +#endif +#include + +#include + +/* david.liu@bsp, 20161004 Add BQ27411 support */ +#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_REG_DCAP 0x3c + +#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 +/* david.liu@bsp, 20161004 Add BQ27411 support */ +/* 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 BQ27411_REG_FCC 0xE +#define BQ27411_REG_OVER_TEMP 0x40 +#define BQ27411_REG_GET_OVER_TEMP_EN 0x0002 + +#define CONTROL_CMD 0x00 +#define CONTROL_STATUS 0x00 +#define SEAL_POLLING_RETRY_LIMIT SEAL_POLLING_RETRY_LIMIT_2 +#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 0x3e +#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 600 + + +/* 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 BQ27411_OVER_TEMP 0x6c02 +#define BQ27411_MCL_DF 0xBD + + +#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 +/* david.liu@bsp, 20161004 Add BQ27411 support */ +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 wakeup_source *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 dcap_pre; + int fcc_pre; + int health_pre; + int get_over_temp; + unsigned long rtc_resume_time; + unsigned long rtc_suspend_time; + atomic_t suspended; + int temp_pre; + int lcd_off_delt_soc; + int t_count; + int temp_thr_update_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; + /* david.liu@oneplus.tw, 2016/05/16 Fix capacity won't udate */ + unsigned long soc_store_time; +#ifdef CONFIG_GAUGE_BQ27411 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + int device_type; + struct cmd_address cmd_addr; + bool modify_soc_smooth; + bool already_modify_smooth; + bool is_mcl_verion; + bool df_ver_match; +#endif +}; + +/*add by yangrujin@bsp 2016/3/16, reduce bq resume time*/ +#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 int __debug_temp_mask; +module_param_named( + debug_temp_mask, __debug_temp_mask, int, 0600 +); + +int SEAL_POLLING_RETRY_LIMIT_2; + +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 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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 bool check_df_version(struct bq27541_device_info *di) +{ + int df; + bool ret; + + if (di->device_type == DEVICE_BQ27541) + return true; + bq27541_cntl_cmd(di, BQ27541_SUBCMD_DF_CSUM); + udelay(66); + bq27541_read(BQ27541_REG_CNTL, &df, 0, di); + pr_info("DEVICE DF:%d\n", df); + if (df == BQ27411_MCL_DF) + ret = true; + else + ret = false; + return ret; +} + +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 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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_design_capacity(struct bq27541_device_info *di) +{ + int ret; + int cap = 0; + + /* Add for get right soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) + return di->dcap_pre; + + if (di->allow_reading) { + ret = bq27541_read(BQ27541_REG_DCAP, &cap, 0, di); + if (ret) { + pr_err("error reading design capacity.\n"); + return ret; + } + di->dcap_pre = cap * 1000; + } + + return di->dcap_pre; +} + +static int bq27541_remaining_capacity(struct bq27541_device_info *di) +{ + int ret; + int cap = 0; + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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_full_chg_capacity(struct bq27541_device_info *di) +{ + int ret; + int cap = 0; + + /* Add for get right soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) + return di->fcc_pre; + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + ret = bq27541_read(BQ27411_REG_FCC, + &cap, 0, di); +#else + ret = bq27541_read(BQ27541_REG_FCC, &cap, 0, di); +#endif + if (ret) { + pr_err("error reading full chg capacity.\n"); + return ret; + } + di->fcc_pre = cap * 1000; + } + + return di->fcc_pre; +} + +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_design_capacity(void) +{ + return bq27541_design_capacity(bq27541_di); +} + +static int bq27541_get_batt_remaining_capacity(void) +{ + return bq27541_remaining_capacity(bq27541_di); +} + +static int bq27541_get_batt_full_chg_capacity(void) +{ + return bq27541_full_chg_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; +} + +static bool battery_is_match(void) +{ + if (bq27541_di->device_type == DEVICE_BQ27541) + return true; + if (bq27541_di->df_ver_match) + return true; + if (bq27541_di->get_over_temp == BQ27411_OVER_TEMP + && bq27541_di->is_mcl_verion) + return true; + else if (bq27541_di->get_over_temp != BQ27411_OVER_TEMP + && !bq27541_di->is_mcl_verion) + return true; + else + return false; +} +static int bq_get_over_temp(struct bq27541_device_info *di) +{ + int flags; + int ret; + + ret = bq27541_i2c_txsubcmd(BQ27411_DATA_CLASS_ACCESS, + BQ27411_REG_GET_OVER_TEMP_EN, bq27541_di); + if (ret < 0) + pr_err("error w register %02x ret = %d\n", + BQ27411_DATA_CLASS_ACCESS, ret); + ret = bq27541_read(BQ27411_REG_OVER_TEMP, + &flags, 0, bq27541_di); + if (ret < 0) { + pr_err("error reading register %02x ret = %d\n", + BQ27411_REG_OVER_TEMP, ret); + } + pr_info("BQ27411 OVER_TEMP:0x%x\n",flags); + return flags; +} + + +#define SHUTDOWN_TBAT 680 +static int bq27541_get_battery_temperature(void) +{ + int ret; + static unsigned long pre_time; + unsigned long current_time, time_last; + + if (__debug_temp_mask) + return __debug_temp_mask; + if (bq27541_di->df_ver_match + || bq27541_di->already_modify_smooth) { + if (!battery_is_match()) + return SHUTDOWN_TBAT+10; + } + 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 +/* david.liu@bsp, 20161025 Add BQ27411 dash charging */ +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_design_capacity + = bq27541_get_batt_design_capacity, + .get_batt_remaining_capacity + = bq27541_get_batt_remaining_capacity, + .get_batt_full_chg_capacity + = bq27541_get_batt_full_chg_capacity, + .get_batt_health = bq27541_get_batt_health, + .get_batt_bq_soc = bq27541_get_batt_bq_soc, +#ifdef CONFIG_GAUGE_BQ27411 + /* david.liu@bsp, 20161025 Add BQ27411 dash charging */ + .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 usb_present, rc; /* david@bsp modified */ + + if (!psy) { + psy = power_supply_get_by_name("usb"); + if (!psy) { + pr_err("failed to get ps usb\n"); + return -EINVAL; + } + } + + /* david@bsp modified */ + rc = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &ret); + if (rc) + return -EINVAL; + + if (ret.intval < 0) + return -EINVAL; + + usb_present = ret.intval; + return usb_present; +} + +static bool get_dash_started(void) +{ + if (bq27541_di && bq27541_di->fastchg_started) + return bq27541_di->fastchg_started; + else + return false; +} +#define TEMP_UPDATE_COUNT 5 +#define TEMP_UPDATE_THRESHOLD 450 + + +static int bq27541_temperature_thrshold_update(int temp) +{ + int ret; + + if (!bq27541_di->batt_psy) + return 0; + if (temp >= TEMP_UPDATE_THRESHOLD) { + bq27541_di->temp_thr_update_count++; + if (bq27541_di->temp_thr_update_count > TEMP_UPDATE_COUNT) { + bq27541_di->temp_thr_update_count = 0; + power_supply_changed(bq27541_di->batt_psy); + } + } else { + bq27541_di->temp_thr_update_count = 0; + } + + return ret; +} +static void update_battery_soc_work(struct work_struct *work) +{ + int schedule_time, vbat, temp; + + 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(); + temp = 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_get_batt_full_chg_capacity(); + bq27541_set_allow_reading(false); + bq27541_temperature_thrshold_update(temp); + 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 +/* david.liu@bsp, 20161004 Add BQ27411 support */ +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 ssize_t battery_exist_read(struct file *p_file, + char __user *puser_buf, size_t count, loff_t *p_offset) +{ + return 0; +} + +static ssize_t battery_exist_write(struct file *p_file, + const char __user *puser_buf, + size_t count, loff_t *p_offset) +{ + return 0; +} + +static const struct file_operations battery_exist_operations = { + .read = battery_exist_read, + .write = battery_exist_write, +}; + +static void init_battery_exist_node(void) +{ + if (!proc_create("battery_exist", 0644, NULL, + &battery_exist_operations)){ + pr_err("%s : Failed to register proc interface\n", __func__); + } +} + + +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 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + if (type == DEVICE_TYPE_BQ27411) { + di->device_type = DEVICE_BQ27411; + pr_info("DEVICE_BQ27411\n"); + di->df_ver_match = check_df_version(di); + } 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_get_batt_remaining_capacity(); + bq27541_get_batt_full_chg_capacity(); + bq27541_set_allow_reading(false); + __pm_relax(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"); + di->is_mcl_verion = of_property_read_bool(node, + "op,mcl_verion"); + pr_info("di->is_mcl_verion=%d\n", di->is_mcl_verion); +} +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); + + if (get_boot_mode() == MSM_BOOT_MODE__RECOVERY || get_boot_mode() == MSM_BOOT_MODE__CHARGE) + SEAL_POLLING_RETRY_LIMIT_2 = 10; + else + SEAL_POLLING_RETRY_LIMIT_2 = 100; + + 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); + } + if (di->is_mcl_verion && !di->df_ver_match) + di->get_over_temp = bq_get_over_temp(di); +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; + + di->update_soc_wake_lock = wakeup_source_register(NULL, "bq_delt_soc_wake_lock"); + di->soc_pre = DEFAULT_INVALID_SOC_PRE; + di->temp_pre = 0; +#ifndef CONFIG_GAUGE_BQ27411 + /* david.liu@bsp, 20161004 Add BQ27411 support */ + 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); + retval = check_bat_present(di); + if( retval ) { + init_battery_exist_node(); + pr_info("probe success battery exist \n"); + } + else { + pr_info("probe success battery not exist \n"); + } + 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); + __pm_stay_awake(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 7e3de38af9b5..9ed74859820f 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -477,6 +477,8 @@ struct fg_dev { bool profile_available; enum prof_load_status profile_load_status; bool battery_missing; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + bool use_external_fg; bool fg_restarting; bool charge_full; bool recharge_soc_adjusted; @@ -528,6 +530,7 @@ struct fg_dbgfs { u32 addr; }; +static struct external_battery_gauge *external_fg; extern int fg_decode_voltage_24b(struct fg_sram_param *sp, enum fg_sram_param_id id, int val); extern int fg_decode_voltage_15b(struct fg_sram_param *sp, diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index 0d152d57258e..d37807b06280 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -378,6 +378,7 @@ bool is_input_present(struct fg_dev *fg) return is_usb_present(fg) || is_dc_present(fg); } +/* Yangfb@bsp, 20170109 set Ibat 500mA by default */ void fg_notify_charger(struct fg_dev *fg) { union power_supply_propval prop = {0, }; @@ -389,27 +390,21 @@ void fg_notify_charger(struct fg_dev *fg) if (!fg->profile_available) return; - if (fg->bp.float_volt_uv > 0) { - prop.intval = fg->bp.float_volt_uv; - rc = power_supply_set_property(fg->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 = fg->bp.fastchg_curr_ma * 1000; + rc = power_supply_set_property(fg->batt_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop); + if (rc < 0) { + pr_err("Error in setting constant_charge_current_max property on batt_psy, rc=%d\n", + rc); + return; } - if (fg->bp.fastchg_curr_ma > 0) { - prop.intval = fg->bp.fastchg_curr_ma * 1000; - rc = power_supply_set_property(fg->batt_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - &prop); - if (rc < 0) { - pr_err("Error in setting constant_charge_current_max property on batt_psy, rc=%d\n", - rc); - return; - } + rc = power_supply_set_property(fg->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; } } @@ -1466,7 +1461,7 @@ static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf, { int bytes_read; int data; - int pos = 0; + u32 pos = 0; int cnt = 0; u8 *values; char *kbuf; diff --git a/drivers/power/supply/qcom/oneplus_fastchg.c b/drivers/power/supply/qcom/oneplus_fastchg.c new file mode 100755 index 000000000000..1e4e1aeb2d52 --- /dev/null +++ b/drivers/power/supply/qcom/oneplus_fastchg.c @@ -0,0 +1,1669 @@ +/**************************************************** + **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 + +#define BYTE_OFFSET 2 +#define BYTES_TO_WRITE 16 +#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; + bool n76e_present; + bool is_mcl_verion; + int mcu_reset_ahead; + int erase_count; + int addr_low; + int addr_high; + 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 dash_enhance; + 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 wakeup_source *fastchg_wake_lock; + struct wakeup_source *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[255]; + char manu_name[255]; +}; + +struct fastchg_device_info *fastchg_di; + +static unsigned char *dashchg_firmware_data; +static struct i2c_client *mcu_client; + + +static ssize_t n76e_exist_read(struct file *p_file, + char __user *puser_buf, size_t count, loff_t *p_offset) +{ + return 0; +} + +static ssize_t n76e_exist_write(struct file *p_file, + const char __user *puser_buf, + size_t count, loff_t *p_offset) +{ + return 0; +} + +static const struct file_operations n76e_exist_operations = { + .read = n76e_exist_read, + .write = n76e_exist_write, +}; + +static void init_n76e_exist_node(void) +{ + if (!proc_create("n76e_exit", 0644, NULL, + &n76e_exist_operations)){ + pr_info("Failed to register n76e node\n"); + } +} +#define PAGESIZE 512 + +static ssize_t enhance_exist_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct fastchg_device_info *di = fastchg_di; + + if (!di) + return ret; + ret = sprintf(page, "%d", di->dash_enhance); + ret = simple_read_from_buffer(user_buf, + count, ppos, page, strlen(page)); + return ret; +} + +static ssize_t enhance_exist_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct fastchg_device_info *di = fastchg_di; + int ret = 0; + char buf[4] = {0}; + + if (count > 2) + return count; + + if (copy_from_user(buf, buffer, count)) { + pr_err("%s: write proc dash error.\n", __func__); + return count; + } + + if (-1 == sscanf(buf, "%d", &ret)) { + pr_err("%s sscanf error\n", __func__); + return count; + } + if (!di) + return count; + if ((ret == 0) || (ret == 1)) + di->dash_enhance = ret; + pr_info("%s:the dash enhance is = %d\n", + __func__, di->dash_enhance); + return count; +} + +static const struct file_operations enhance_exist_operations = { + .read = enhance_exist_read, + .write = enhance_exist_write, +}; + +static void init_enhance_dash_exist_node(void) +{ + if (!proc_create("enhance_dash", 0644, NULL, + &enhance_exist_operations)) + pr_err("Failed to register enhance dash node\n"); +} + +//for mcu_data irq delay issue 2017.10.14@Infi +extern void msm_cpuidle_set_sleep_disable(bool disable); + +static int is_usb_pluged(void) +{ + static struct power_supply *psy; + union power_supply_propval ret = {0,}; + int usb_present, rc; + + if (!psy) { + psy = power_supply_get_by_name("usb"); + if (!psy) { + pr_err("fastchg failed to get ps usb\n"); + return -EINVAL; + } + } + + rc = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &ret); + if (rc) { + pr_err("fastchg failed to get POWER_SUPPLY_PROP_PRESENT\n"); + return -EINVAL; + } + + if (ret.intval < 0) { + pr_err("fastchg get POWER_SUPPLY_PROP_PRESENT EINVAL \n"); + return -EINVAL; + } + + pr_debug("%s usb_present [%d]\n", __func__, usb_present); + usb_present = ret.intval; + return usb_present; +} + +void opchg_set_data_active(struct fastchg_device_info *chip) +{ + gpio_direction_input(chip->ap_data); + if (chip->pinctrl && + !IS_ERR_OR_NULL(chip->pinctrl_mcu_data_state_active)) + 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); + /* @bsp 2018/09/05 FAT-4556 fix the audio heaset pop + * issue when shutdown + */ + if (audio_adapter_flag) { + usleep_range(10000, 10001); + gpio_direction_output(fastchg_di->mcu_en_gpio, 0); + pr_info("mcu reset ahead when audio adaptor present!\n"); + } + } +} + +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; + /* david@bsp add log */ + 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); + /* david@bsp modified */ + 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 unsigned char addr_buf[2]; +static bool n76e_fw_check(struct fastchg_device_info *chip) +{ + unsigned char data_buf[16] = {0x0}; + int rc = 0; + int j = 0, i; + int fw_line = 0; + int total_line = 0; + + total_line = chip->dashchg_fw_ver_count / 18; + + for (fw_line = 0; fw_line < total_line; fw_line++) { + addr_buf[0] = dashchg_firmware_data[fw_line * 18 + 1]; + addr_buf[1] = dashchg_firmware_data[fw_line * 18]; + rc = oneplus_dash_i2c_write(chip->client, + 0x01, 2, &addr_buf[0]); + if (rc < 0) { + pr_err("i2c_write 0x01 error\n"); + return FW_CHECK_FAIL; + } + + data_buf[0] = 0; + oneplus_dash_i2c_write(chip->client, 0x03, 1, &data_buf[0]); + usleep_range(2000, 2100); + oneplus_dash_i2c_read(chip->client, 0x03, 16, &data_buf[0]); + + for (j = 0; j < 16; j++) { + if (data_buf[j] != dashchg_firmware_data[fw_line * 18 + 2 + j]) { + pr_err("fail, data_buf[%d]:0x%x != n76e_firmware_data[%d]:0x%x\n", + j, data_buf[j], (fw_line * 18 + 2 + j), + dashchg_firmware_data[fw_line * 18 + 2 + j]); + for (i = 0; i < 16; i++) + pr_info("data_buf[%d]:0x%x\n", i, data_buf[i]); + pr_info("fail line=%d\n", fw_line); + return FW_CHECK_FAIL; + } + } + } + return FW_CHECK_SUCCESS; +} + +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; + + addr_buf[0] = fastchg_di->addr_low; + addr_buf[1] = fastchg_di->addr_high; + 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; + + addr_buf[0] = fastchg_di->addr_low; + addr_buf[1] = fastchg_di->addr_high; + 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 */ + if (!fastchg_di->n76e_present) { + 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; + } else + count = count + BYTE_OFFSET + 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; + irq_set_status_flags(di->irq, IRQ_DISABLE_UNLAZY); +} + + +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; + int download_again = 0; + struct fastchg_device_info *di = container_of(work, + struct fastchg_device_info, + update_firmware.work); + + addr_buf[0] = fastchg_di->addr_low; + addr_buf[1] = fastchg_di->addr_high; + addr = (addr_buf[0] << 8) + (addr_buf[1] & 0xFF); + __pm_stay_awake(di->fastchg_update_fireware_lock); + if (di->n76e_present) + rc = n76e_fw_check(di); + else + rc = dashchg_fw_check(); + if (rc == FW_CHECK_SUCCESS) { + di->firmware_already_updated = true; + reset_mcu_and_request_irq(di); + __pm_relax(di->fastchg_update_fireware_lock); + set_property_on_smbcharger(POWER_SUPPLY_PROP_SWITCH_DASH, true); + pr_info("FW check success\n"); /* david@bsp add log */ + return; + } + pr_info("start erasing data.......\n"); + +update_fw: + /* erase address 0x200-0x7FF */ + for (i = 0; i < di->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 */ + if (!di->n76e_present) + 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]); + if (di->n76e_present) + usleep_range(7000, 7100); + /* 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[] */ + if (di->n76e_present) + rc = dashchg_fw_check(); + else + rc = n76e_fw_check(di); + 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); + __pm_relax(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); + __pm_relax(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 void clean_enhache_status(void) +{ + if (fastchg_di) + fastchg_di->dash_enhance = 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; +} + +static bool enhance_dash_on(void) +{ + if (fastchg_di) + return fastchg_di->dash_enhance; + + 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, + .is_enhance_dash = enhance_dash_on, + .clean_enhache = clean_enhache_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); + msm_cpuidle_set_sleep_disable(false); +} + +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; + irq_set_status_flags(di->irq, IRQ_DISABLE_UNLAZY); + } + } 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(struct timer_list *t) +{ + struct fastchg_device_info *di = fastchg_di; + + 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"); + + __pm_relax(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); + if (di->pinctrl && + !IS_ERR_OR_NULL(di->pinctrl_mcu_data_state_suspended)) + 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); + /*pm_qos_update_request(&big_cpu_update_freq, MAX_CPUFREQ);*/ + op_bus_vote(false); + 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);*/ + + pr_info("%s end update_result:%d\n", + __func__, update_result); + __pm_relax(chip->fastchg_wake_lock); + op_bus_vote(true); + +} + +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) +#define DASH_NOTIFY_UPDATE_ADAPTER_INFO _IOW(DASH_IOC_MAGIC, 15, 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) { + __pm_stay_awake(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; + msm_cpuidle_set_sleep_disable(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; + di->dash_enhance = 0; + 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(); + __pm_relax(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_UPDATE_ADAPTER_INFO: + if (is_usb_pluged()) + di->dash_enhance = arg; + if (!di->batt_psy) + di->batt_psy = + power_supply_get_by_name("battery"); + if (di->batt_psy) + power_supply_changed(di->batt_psy); + 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"); + di->fast_switch_to_normal = true; + switch_mode_to_normal(); + del_timer(&di->watchdog); + } else if (arg == DASH_NOTIFY_NORMAL_TEMP_FULL + 2) { + 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(); + __pm_relax(di->fastchg_wake_lock); + } else if (arg == DASH_NOTIFY_NORMAL_TEMP_FULL + 3) { + op_switch_normal_set(); + } + 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); + __pm_relax(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); + __pm_stay_awake(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; + __pm_relax(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); + __pm_relax(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; + } + schedule_delayed_work(&di->update_fireware_version_work, + msecs_to_jiffies(SHOW_FW_VERSION_DELAY_MS)); + 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; + int rc; + 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); + di->n76e_present = of_property_read_bool(dev_node, + "op,n76e_support"); + di->is_mcl_verion = of_property_read_bool(dev_node, + "op,mcl_verion"); + rc = of_property_read_u32(dev_node, + "op,fw-erase-count", &di->erase_count); + if (rc < 0) + di->erase_count = 384; + rc = of_property_read_u32(dev_node, + "op,fw-addr-low", &di->addr_low); + if (rc < 0) + di->addr_low = 0x88; + rc = of_property_read_u32(dev_node, + "op,fw-addr-high", &di->addr_high); + if (rc < 0) + di->addr_high = 0; + 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); + return -EINVAL; + } + 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); + return -EINVAL; + } + gpio_direction_output(di->usb_sw_2_gpio, 0); + + } else + return -EINVAL; + + 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; + } else { + 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 void check_n76e_support(struct fastchg_device_info *di) +{ + if (di->n76e_present) { + init_n76e_exist_node(); + pr_info("n76e exist\n"); + } else { + pr_info("n76e not exist\n"); + } + +} + +static void check_enhance_support(struct fastchg_device_info *di) +{ + if (di->is_mcl_verion) { + init_enhance_dash_exist_node(); + pr_info("enhance dash exist\n"); + } else { + pr_info("enhance dash not exist\n"); + } + +} + + +/* @bsp 2018/09/05 FAT-4556 fix the audio heaset pop issue when shutdown*/ +static int set_mcu_reset_ahead(const char *val, const struct kernel_param *kp) +{ + unsigned long reset_value = 0; + int ret = 0; + + if (!audio_adapter_flag) { + pr_info("audio_adapter_flag = %d,return!\n", + audio_adapter_flag); + return 0; + } + + ret = kstrtoul(val, 10, &reset_value); + if (ret) + return ret; + + if (!reset_value) { + fastchg_di->mcu_reset_ahead = 0; + return 0; + } + + if (reset_value) { + pr_info("reset_value:%lu\n", + reset_value); + mcu_en_reset(); + fastchg_di->mcu_reset_ahead = reset_value; + } + + return 0; +} + +static int get_mcu_reset_status(char *buffer, const struct kernel_param *kp) +{ + int cnt = 0; + + cnt = snprintf(buffer, sizeof(char), "%d", + fastchg_di->mcu_reset_ahead); + pr_debug("mcu reset ahead status:%d\n", + fastchg_di->mcu_reset_ahead); + + return cnt; +} + +module_param_call(mcu_reset, set_mcu_reset_ahead, + get_mcu_reset_status, NULL, 0644); + +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; + + ret = request_dash_gpios(di); + if (ret < 0) + goto err_read_dt; + dash_pinctrl_init(di); + mutex_init(&di->read_mutex); + + init_waitqueue_head(&di->read_wq); + di->fastchg_wake_lock = wakeup_source_register(NULL, "fastcg_wake_lock"); + di->fastchg_update_fireware_lock = wakeup_source_register(NULL, "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, TIMER_IRQSAFE); + //di->watchdog.data = (unsigned long)di;//20190707 + //di->watchdog.function = di_watchdog;//20190707 + + 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); + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register_failed; + } + + mcu_init(di); + check_n76e_support(di); + check_enhance_support(di); + fastcharge_information_register(&fastcharge_information); + 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); + + return 0; +} + +static void dash_shutdown(struct i2c_client *client) +{ + usb_sw_gpio_set(0); + /* @bsp 2018/09/05 FAT-4556 fix the audio heaset pop + * issue when shutdown + */ + if (audio_adapter_flag && fastchg_di->mcu_reset_ahead) + return; + mcu_en_reset(); +} + +static const struct of_device_id dash_match[] = { + { .compatible = "microchip,oneplus_fastchg" }, + { }, +}; + +static const struct i2c_device_id dash_id[] = { + { "dash_fastchg", 1 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, dash_id); + +static struct i2c_driver dash_fastcg_driver = { + .driver = { + .name = "dash_fastchg", + .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_charge.h b/drivers/power/supply/qcom/op_charge.h new file mode 100644 index 000000000000..c0501968b97a --- /dev/null +++ b/drivers/power/supply/qcom/op_charge.h @@ -0,0 +1,691 @@ +/* Copyright (c) 2016-2018 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 __OP_CHARGE_H__ +#define __OP_CHARGE_H__ + +int con_temp_30k[] = { + -40, + -39, + -38, + -37, + -36, + -35, + -34, + -33, + -32, + -31, + -30, + -29, + -28, + -27, + -26, + -25, + -24, + -23, + -22, + -21, + -20, + -19, + -18, + -17, + -16, + -15, + -14, + -13, + -12, + -11, + -10, + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, +}; + +int con_volt_30k[] = { + 1774, + 1773, + 1771, + 1769, + 1767, + 1764, + 1762, + 1759, + 1757, + 1754, + 1751, + 1747, + 1744, + 1740, + 1737, + 1733, + 1728, + 1724, + 1719, + 1714, + 1709, + 1704, + 1698, + 1692, + 1686, + 1679, + 1672, + 1665, + 1658, + 1650, + 1642, + 1633, + 1624, + 1615, + 1605, + 1595, + 1585, + 1574, + 1563, + 1552, + 1540, + 1527, + 1515, + 1501, + 1488, + 1474, + 1460, + 1445, + 1430, + 1414, + 1399, + 1382, + 1366, + 1349, + 1332, + 1314, + 1296, + 1278, + 1260, + 1241, + 1222, + 1203, + 1184, + 1164, + 1144, + 1125, + 1105, + 1084, + 1064, + 1044, + 1024, + 1004, + 983, + 963, + 943, + 923, + 903, + 883, + 863, + 843, + 824, + 804, + 785, + 766, + 748, + 729, + 711, + 693, + 675, + 658, + 641, + 624, + 607, + 591, + 575, + 559, + 544, + 529, + 514, + 500, + 486, + 472, + 459, + 446, + 433, + 421, + 408, + 397, + 385, + 374, + 363, + 352, + 342, + 332, + 322, + 313, + 303, + 294, + 286, + 278, + 269, + 261, + 254, + 246, + 239, + 232, + 225, + 218, + 212, + 206, + 200, + 194, + 188, + 182, + 177, + 172, + 167, + 162, + 157, + 153, + 148, + 144, + 140, + 136, + 132, + 128, + 125, + 121, + 118, + 114, + 111, + 108, + 105, + 102, + 99, + 96, + 94, + 91, + 89, + 86, + 84, + 82, + 79, + 77, + 75, + 73, +}; + +int con_volt[] = { + 1721, + 1716, + 1710, + 1704, + 1697, + 1690, + 1683, + 1675, + 1667, + 1658, + 1649, + 1640, + 1630, + 1620, + 1609, + 1597, + 1586, + 1573, + 1560, + 1547, + 1533, + 1519, + 1504, + 1488, + 1472, + 1456, + 1438, + 1421, + 1403, + 1384, + 1365, + 1346, + 1326, + 1305, + 1285, + 1263, + 1242, + 1220, + 1198, + 1176, + 1153, + 1130, + 1107, + 1084, + 1061, + 1038, + 1014, + 991, + 967, + 944, + 921, + 898, + 875, + 852, + 829, + 807, + 785, + 763, + 741, + 720, + 699, + 678, + 658, + 638, + 619, + 600, + 581, + 563, + 545, + 527, + 510, + 494, + 477, + 462, + 446, + 432, + 417, + 403, + 389, + 376, + 363, + 351, + 339, + 327, + 316, + 305, + 295, + 284, + 274, + 265, + 256, + 247, + 238, + 230, + 222, + 214, + 207, + 200, + 193, + 186, + 180, + 173, + 167, + 162, + 156, + 151, + 145, + 140, + 136, + 131, + 127, + 122, + 118, + 114, + 110, + 106, + 103, + 99, + 96, + 93, + 90, + 87, + 84, + 81, + 79, + 76, + 73, + 71, + 69, + 67, + 64, + 62, + 60, + 58, + 57, + 55, + 53, + 51, + 50, + 48, + 47, + 45, + 44, + 42, + 41, + 40, + 39, + 38, + 36, + 35, + 34, + 33, + 32, + 31, + 30, + 29, + 29, + 28, + 27, + 26, + 25, + 25, + 24, + 23, + 23, + 22, +}; + +int con_temp[] = { + -40, + -39, + -38, + -37, + -36, + -35, + -34, + -33, + -32, + -31, + -30, + -29, + -28, + -27, + -26, + -25, + -24, + -23, + -22, + -21, + -20, + -19, + -18, + -17, + -16, + -15, + -14, + -13, + -12, + -11, + -10, + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, +}; +#endif /* __OP_CHARGE_H__ */ 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..deecd3b91d77 --- /dev/null +++ b/drivers/power/supply/qcom/op_dash_adapter.c @@ -0,0 +1,601 @@ +#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 0 + if (enable) { + preempt_enable(); + local_fiq_enable(); + local_irq_enable(); + } else { + local_irq_disable(); + local_fiq_disable(); + preempt_disable(); + } +#endif +} + +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 int 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 int 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/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h index f6ee0807022c..6ce4f381a865 100644 --- a/drivers/power/supply/qcom/qg-reg.h +++ b/drivers/power/supply/qcom/qg-reg.h @@ -6,132 +6,132 @@ #ifndef __QG_REG_H__ #define __QG_REG_H__ -#define PERPH_TYPE_REG 0x04 +#define PERPH_TYPE_REG 0x04 -#define PERPH_SUBTYPE_REG 0x05 -#define QG_ADC_IBAT_5A 0x3 -#define QG_ADC_IBAT_10A 0x4 +#define PERPH_SUBTYPE_REG 0x05 +#define QG_ADC_IBAT_5A 0x3 +#define QG_ADC_IBAT_10A 0x4 -#define QG_TYPE 0x0D +#define QG_TYPE 0x0D -#define QG_STATUS1_REG 0x08 -#define QG_OK_BIT BIT(7) -#define BATTERY_PRESENT_BIT BIT(0) -#define ESR_MEAS_DONE_BIT BIT(4) +#define QG_STATUS1_REG 0x08 +#define QG_OK_BIT BIT(7) +#define BATTERY_PRESENT_BIT BIT(0) +#define ESR_MEAS_DONE_BIT BIT(4) -#define QG_STATUS2_REG 0x09 -#define BATTERY_MISSING_BIT BIT(3) -#define GOOD_OCV_BIT BIT(1) +#define QG_STATUS2_REG 0x09 +#define BATTERY_MISSING_BIT BIT(3) +#define GOOD_OCV_BIT BIT(1) -#define QG_STATUS3_REG 0x0A -#define COUNT_FIFO_RT_MASK GENMASK(3, 0) +#define QG_STATUS3_REG 0x0A +#define COUNT_FIFO_RT_MASK GENMASK(3, 0) -#define QG_STATUS4_REG 0x0B -#define ESR_MEAS_IN_PROGRESS_BIT BIT(4) +#define QG_STATUS4_REG 0x0B +#define ESR_MEAS_IN_PROGRESS_BIT BIT(4) -#define QG_INT_RT_STS_REG 0x10 -#define FIFO_UPDATE_DONE_RT_STS_BIT BIT(3) -#define VBAT_LOW_INT_RT_STS_BIT BIT(1) -#define BATTERY_MISSING_INT_RT_STS_BIT BIT(0) +#define QG_INT_RT_STS_REG 0x10 +#define FIFO_UPDATE_DONE_RT_STS_BIT BIT(3) +#define VBAT_LOW_INT_RT_STS_BIT BIT(1) +#define BATTERY_MISSING_INT_RT_STS_BIT BIT(0) -#define QG_INT_LATCHED_STS_REG 0x18 -#define FIFO_UPDATE_DONE_INT_LAT_STS_BIT BIT(3) +#define QG_INT_LATCHED_STS_REG 0x18 +#define FIFO_UPDATE_DONE_INT_LAT_STS_BIT BIT(3) -#define QG_STATE_TRIG_CMD_REG 0x40 -#define S7_PON_OCV_START BIT(3) +#define QG_STATE_TRIG_CMD_REG 0x40 +#define S7_PON_OCV_START BIT(3) -#define QG_DATA_CTL1_REG 0x41 -#define MASTER_HOLD_OR_CLR_BIT BIT(0) +#define QG_DATA_CTL1_REG 0x41 +#define MASTER_HOLD_OR_CLR_BIT BIT(0) -#define QG_DATA_CTL2_REG 0x42 -#define BURST_AVG_HOLD_FOR_READ_BIT BIT(0) +#define QG_DATA_CTL2_REG 0x42 +#define BURST_AVG_HOLD_FOR_READ_BIT BIT(0) -#define QG_MODE_CTL1_REG 0x43 -#define PARALLEL_IBAT_SENSE_EN_BIT BIT(7) +#define QG_MODE_CTL1_REG 0x43 +#define PARALLEL_IBAT_SENSE_EN_BIT BIT(7) -#define QG_MODE_CTL2_REG 0x44 -#define VI_MODE_BIT BIT(0) +#define QG_MODE_CTL2_REG 0x44 +#define VI_MODE_BIT BIT(0) -#define QG_VBAT_EMPTY_THRESHOLD_REG 0x4B -#define QG_VBAT_LOW_THRESHOLD_REG 0x4C +#define QG_VBAT_EMPTY_THRESHOLD_REG 0x4B +#define QG_VBAT_LOW_THRESHOLD_REG 0x4C -#define QG_S2_NORMAL_MEAS_CTL2_REG 0x51 -#define FIFO_LENGTH_MASK GENMASK(5, 3) -#define FIFO_LENGTH_SHIFT 3 -#define NUM_OF_ACCUM_MASK GENMASK(2, 0) +#define QG_S2_NORMAL_MEAS_CTL2_REG 0x51 +#define FIFO_LENGTH_MASK GENMASK(5, 3) +#define FIFO_LENGTH_SHIFT 3 +#define NUM_OF_ACCUM_MASK GENMASK(2, 0) -#define QG_S2_NORMAL_MEAS_CTL3_REG 0x52 +#define QG_S2_NORMAL_MEAS_CTL3_REG 0x52 -#define QG_S3_SLEEP_OCV_MEAS_CTL4_REG 0x59 -#define S3_SLEEP_OCV_TIMER_MASK GENMASK(2, 0) +#define QG_S3_SLEEP_OCV_MEAS_CTL4_REG 0x59 +#define S3_SLEEP_OCV_TIMER_MASK GENMASK(2, 0) -#define QG_S3_SLEEP_OCV_TREND_CTL2_REG 0x5C -#define TREND_TOL_MASK GENMASK(5, 0) +#define QG_S3_SLEEP_OCV_TREND_CTL2_REG 0x5C +#define TREND_TOL_MASK GENMASK(5, 0) -#define QG_S3_SLEEP_OCV_IBAT_CTL1_REG 0x5D -#define SLEEP_IBAT_QUALIFIED_LENGTH_MASK GENMASK(2, 0) +#define QG_S3_SLEEP_OCV_IBAT_CTL1_REG 0x5D +#define SLEEP_IBAT_QUALIFIED_LENGTH_MASK GENMASK(2, 0) -#define QG_S3_ENTRY_IBAT_THRESHOLD_REG 0x5E -#define QG_S3_EXIT_IBAT_THRESHOLD_REG 0x5F +#define QG_S3_ENTRY_IBAT_THRESHOLD_REG 0x5E +#define QG_S3_EXIT_IBAT_THRESHOLD_REG 0x5F -#define QG_S5_OCV_VALIDATE_MEAS_CTL1_REG 0x60 -#define ALLOW_S5_BIT BIT(7) +#define QG_S5_OCV_VALIDATE_MEAS_CTL1_REG 0x60 +#define ALLOW_S5_BIT BIT(7) -#define QG_S7_PON_OCV_MEAS_CTL1_REG 0x64 -#define ADC_CONV_DLY_MASK GENMASK(3, 0) +#define QG_S7_PON_OCV_MEAS_CTL1_REG 0x64 +#define ADC_CONV_DLY_MASK GENMASK(3, 0) -#define QG_ESR_MEAS_TRIG_REG 0x68 -#define HW_ESR_MEAS_START_BIT BIT(0) +#define QG_ESR_MEAS_TRIG_REG 0x68 +#define HW_ESR_MEAS_START_BIT BIT(0) -#define QG_S7_PON_OCV_V_DATA0_REG 0x70 -#define QG_S7_PON_OCV_I_DATA0_REG 0x72 -#define QG_S3_GOOD_OCV_V_DATA0_REG 0x74 -#define QG_S3_GOOD_OCV_I_DATA0_REG 0x76 +#define QG_S7_PON_OCV_V_DATA0_REG 0x70 +#define QG_S7_PON_OCV_I_DATA0_REG 0x72 +#define QG_S3_GOOD_OCV_V_DATA0_REG 0x74 +#define QG_S3_GOOD_OCV_I_DATA0_REG 0x76 -#define QG_PRE_ESR_V_DATA0_REG 0x78 -#define QG_PRE_ESR_I_DATA0_REG 0x7A -#define QG_POST_ESR_V_DATA0_REG 0x7C -#define QG_POST_ESR_I_DATA0_REG 0x7E +#define QG_PRE_ESR_V_DATA0_REG 0x78 +#define QG_PRE_ESR_I_DATA0_REG 0x7A +#define QG_POST_ESR_V_DATA0_REG 0x7C +#define QG_POST_ESR_I_DATA0_REG 0x7E -#define QG_S2_NORMAL_AVG_V_DATA0_REG 0x80 -#define QG_S2_NORMAL_AVG_I_DATA0_REG 0x82 +#define QG_S2_NORMAL_AVG_V_DATA0_REG 0x80 +#define QG_S2_NORMAL_AVG_I_DATA0_REG 0x82 -#define QG_V_ACCUM_DATA0_RT_REG 0x88 -#define QG_I_ACCUM_DATA0_RT_REG 0x8B -#define QG_ACCUM_CNT_RT_REG 0x8E +#define QG_V_ACCUM_DATA0_RT_REG 0x88 +#define QG_I_ACCUM_DATA0_RT_REG 0x8B +#define QG_ACCUM_CNT_RT_REG 0x8E -#define QG_V_FIFO0_DATA0_REG 0x90 -#define QG_I_FIFO0_DATA0_REG 0xA0 +#define QG_V_FIFO0_DATA0_REG 0x90 +#define QG_I_FIFO0_DATA0_REG 0xA0 -#define QG_SOC_MONOTONIC_REG 0xBF +#define QG_SOC_MONOTONIC_REG 0xBF -#define QG_LAST_ADC_V_DATA0_REG 0xC0 -#define QG_LAST_ADC_I_DATA0_REG 0xC2 +#define QG_LAST_ADC_V_DATA0_REG 0xC0 +#define QG_LAST_ADC_I_DATA0_REG 0xC2 -#define QG_LAST_BURST_AVG_I_DATA0_REG 0xC6 +#define QG_LAST_BURST_AVG_I_DATA0_REG 0xC6 -#define QG_LAST_S3_SLEEP_V_DATA0_REG 0xCC +#define QG_LAST_S3_SLEEP_V_DATA0_REG 0xCC /* SDAM offsets */ -#define QG_SDAM_VALID_OFFSET 0x46 /* 1-byte 0x46 */ -#define QG_SDAM_SOC_OFFSET 0x47 /* 1-byte 0x47 */ -#define QG_SDAM_TEMP_OFFSET 0x48 /* 2-byte 0x48-0x49 */ -#define QG_SDAM_RBAT_OFFSET 0x4A /* 2-byte 0x4A-0x4B */ -#define QG_SDAM_OCV_OFFSET 0x4C /* 4-byte 0x4C-0x4F */ -#define QG_SDAM_IBAT_OFFSET 0x50 /* 4-byte 0x50-0x53 */ -#define QG_SDAM_TIME_OFFSET 0x54 /* 4-byte 0x54-0x57 */ -#define QG_SDAM_CYCLE_COUNT_OFFSET 0x58 /* 16-byte 0x58-0x67 */ -#define QG_SDAM_LEARNED_CAPACITY_OFFSET 0x68 /* 2-byte 0x68-0x69 */ -#define QG_SDAM_ESR_CHARGE_DELTA_OFFSET 0x6A /* 4-byte 0x6A-0x6D */ -#define QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET 0x6E /* 4-byte 0x6E-0x71 */ -#define QG_SDAM_ESR_CHARGE_SF_OFFSET 0x72 /* 2-byte 0x72-0x73 */ -#define QG_SDAM_ESR_DISCHARGE_SF_OFFSET 0x74 /* 2-byte 0x74-0x75 */ -#define QG_SDAM_BATT_AGE_LEVEL_OFFSET 0x76 /* 1-byte 0x76 */ -#define QG_SDAM_MAGIC_OFFSET 0x80 /* 4-byte 0x80-0x83 */ -#define QG_SDAM_FLASH_OCV_OFFSET 0x84 /* 1-byte 0x84 */ -#define QG_SDAM_MAX_OFFSET 0xA4 +#define QG_SDAM_VALID_OFFSET 0x46 /* 1-byte 0x46 */ +#define QG_SDAM_SOC_OFFSET 0x47 /* 1-byte 0x47 */ +#define QG_SDAM_TEMP_OFFSET 0x48 /* 2-byte 0x48-0x49 */ +#define QG_SDAM_RBAT_OFFSET 0x4A /* 2-byte 0x4A-0x4B */ +#define QG_SDAM_OCV_OFFSET 0x4C /* 4-byte 0x4C-0x4F */ +#define QG_SDAM_IBAT_OFFSET 0x50 /* 4-byte 0x50-0x53 */ +#define QG_SDAM_TIME_OFFSET 0x54 /* 4-byte 0x54-0x57 */ +#define QG_SDAM_CYCLE_COUNT_OFFSET 0x58 /* 16-byte 0x58-0x67 */ +#define QG_SDAM_LEARNED_CAPACITY_OFFSET 0x68 /* 2-byte 0x68-0x69 */ +#define QG_SDAM_ESR_CHARGE_DELTA_OFFSET 0x6A /* 4-byte 0x6A-0x6D */ +#define QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET 0x6E /* 4-byte 0x6E-0x71 */ +#define QG_SDAM_ESR_CHARGE_SF_OFFSET 0x72 /* 2-byte 0x72-0x73 */ +#define QG_SDAM_ESR_DISCHARGE_SF_OFFSET 0x74 /* 2-byte 0x74-0x75 */ +#define QG_SDAM_BATT_AGE_LEVEL_OFFSET 0x76 /* 1-byte 0x76 */ +#define QG_SDAM_MAGIC_OFFSET 0x80 /* 4-byte 0x80-0x83 */ +#define QG_SDAM_FLASH_OCV_OFFSET 0x84 /* 1-byte 0x84 */ +#define QG_SDAM_MAX_OFFSET 0xA4 /* Below offset is used by PBS */ -#define QG_SDAM_PON_OCV_OFFSET 0xBC /* 2-byte 0xBC-0xBD */ +#define QG_SDAM_PON_OCV_OFFSET 0xBC /* 2-byte 0xBC-0xBD */ #endif diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index f23f36d1b749..71196f86f8d4 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -17,6 +17,8 @@ #include #include "fg-core.h" #include "fg-reg.h" +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#include #define FG_GEN3_DEV_NAME "qcom,fg-gen3" @@ -156,6 +158,14 @@ #define KI_COEFF_SOC_LEVELS 3 #define BATT_THERM_NUM_COEFFS 3 +/* david.liu@bsp, 20171023 Battery & Charging porting */ +static void oem_update_cc_cv_setpoint(struct fg_dev *fg, + int cv_float_point); +static void oneplus_set_allow_read_iic(struct fg_dev *fg, + bool status); +static void oneplus_set_lcd_off_status(struct fg_dev *fg, + bool status); + static struct fg_irq_info fg_irqs[FG_GEN3_IRQ_MAX]; /* DT parameters for FG device */ @@ -486,6 +496,25 @@ static ssize_t profile_dump_store(struct device *dev, } static DEVICE_ATTR_RW(profile_dump); +/* david.liu@bsp, 20171023 Battery & Charging porting */ +void external_battery_gauge_register( + struct external_battery_gauge *batt_gauge) +{ + if (external_fg) { + external_fg = batt_gauge; + pr_err("multiple battery gauge called\n"); + } 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; static ssize_t sram_dump_period_ms_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -767,6 +796,8 @@ static int fg_batt_missing_config(struct fg_dev *fg, bool enable) return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#define OP_SW_DEFAULT_ID 200000 static int fg_get_batt_id(struct fg_dev *fg) { struct fg_gen3_chip *chip = container_of(fg, struct fg_gen3_chip, fg); @@ -791,7 +822,8 @@ static int fg_get_batt_id(struct fg_dev *fg) msleep(chip->dt.bmd_en_delay_ms); fg_dbg(fg, FG_STATUS, "batt_id: %d\n", batt_id); - fg->batt_id_ohms = batt_id; +/* Yangfb@bsp, 20170110 Add OP battery profile */ + fg->batt_id_ohms = OP_SW_DEFAULT_ID; out: ret = fg_batt_missing_config(fg, true); if (ret < 0) { @@ -947,9 +979,11 @@ static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, if (enable) { enable_irq(fg->irqs[BSOC_DELTA_IRQ].irq); - enable_irq_wake(fg->irqs[BSOC_DELTA_IRQ].irq); + if (fg->irqs[BSOC_DELTA_IRQ].wakeable) + enable_irq_wake(fg->irqs[BSOC_DELTA_IRQ].irq); } else { - disable_irq_wake(fg->irqs[BSOC_DELTA_IRQ].irq); + if (fg->irqs[BSOC_DELTA_IRQ].wakeable) + disable_irq_wake(fg->irqs[BSOC_DELTA_IRQ].irq); disable_irq_nosync(fg->irqs[BSOC_DELTA_IRQ].irq); } @@ -3433,6 +3467,8 @@ static int fg_update_maint_soc(struct fg_dev *fg) return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#define DEFALUT_BATT_TEMP 250 static int fg_esr_validate(struct fg_dev *fg) { struct fg_gen3_chip *chip = container_of(fg, struct fg_gen3_chip, fg); @@ -3647,25 +3683,54 @@ static int fg_psy_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CAPACITY: - rc = fg_get_prop_capacity(fg, &pval->intval); - break; - case POWER_SUPPLY_PROP_REAL_CAPACITY: - rc = fg_get_prop_real_capacity(fg, &pval->intval); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (!get_extern_fg_regist_done()) + pval->intval = get_prop_pre_shutdown_soc(); + else if (fg->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(fg, &pval->intval); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - if (fg->battery_missing) - pval->intval = 3700000; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (fg->use_external_fg && external_fg + && external_fg->get_battery_mvolts) + pval->intval = external_fg->get_battery_mvolts(); else - rc = fg_get_battery_voltage(fg, &pval->intval); + pval->intval = 4000000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: - rc = fg_get_battery_current(fg, &pval->intval); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (fg->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(fg, &pval->intval); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (!get_extern_fg_regist_done() + && get_extern_bq_present()) + pval->intval = DEFALUT_BATT_TEMP; + else if (fg->use_external_fg && external_fg + && external_fg->get_average_current) { + pval->intval = external_fg->get_battery_temperature(); + } else + pval->intval = -400; + break; + /*yangfb@bsp,add for battery health*/ + case POWER_SUPPLY_PROP_BATTERY_HEALTH: + if (fg->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_COLD_TEMP: rc = fg_get_jeita_threshold(fg, JEITA_COLD, &pval->intval); @@ -3702,7 +3767,12 @@ static int fg_psy_get_property(struct power_supply *psy, rc = fg_get_sram_prop(fg, FG_SRAM_OCV, &pval->intval); break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - pval->intval = chip->cl.nom_cap_uah; + if (!get_extern_fg_regist_done() && get_extern_bq_present()) + pval->intval = -EINVAL; + else if (fg->use_external_fg && external_fg && external_fg->get_batt_design_capacity) + pval->intval = external_fg->get_batt_design_capacity(); + else + pval->intval = chip->cl.nom_cap_uah; break; case POWER_SUPPLY_PROP_RESISTANCE_ID: pval->intval = fg->batt_id_ohms; @@ -3726,7 +3796,20 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = chip->cl.init_cc_uah; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - pval->intval = chip->cl.learned_cc_uah; + if (!get_extern_fg_regist_done() && get_extern_bq_present()) + pval->intval = -EINVAL; + else if (fg->use_external_fg && external_fg && external_fg->get_batt_full_chg_capacity) + pval->intval = external_fg->get_batt_full_chg_capacity(); + else + pval->intval = chip->cl.learned_cc_uah; + break; + case POWER_SUPPLY_PROP_REMAINING_CAPACITY: + if (!get_extern_fg_regist_done() && get_extern_bq_present()) + pval->intval = DEFALUT_BATT_TEMP; + else if (fg->use_external_fg && external_fg && external_fg->get_batt_remaining_capacity) + pval->intval = external_fg->get_batt_remaining_capacity(); + else + pval->intval = -EINVAL; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: rc = fg_get_charge_counter(fg, &pval->intval); @@ -3737,9 +3820,6 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: rc = fg_get_time_to_full(fg, &pval->intval); break; - case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: - rc = fg_get_time_to_full(fg, &pval->intval); - break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: rc = fg_get_time_to_empty(fg, &pval->intval); break; @@ -3749,6 +3829,26 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_DEBUG_BATTERY: pval->intval = is_debug_batt_id(fg); break; +/* david.liu@bsp, 20170303 Add internal FG info */ + case POWER_SUPPLY_PROP_FG_CAPACITY: + rc = fg_get_prop_capacity(fg, &pval->intval); + break; + case POWER_SUPPLY_PROP_FG_VOLTAGE_NOW: + if (fg->battery_missing) + pval->intval = 3700000; + else + rc = fg_get_battery_voltage(fg, &pval->intval); + break; + case POWER_SUPPLY_PROP_FG_CURRENT_NOW: + rc = fg_get_battery_current(fg, &pval->intval); + break; + case POWER_SUPPLY_PROP_BQ_SOC: + if (fg->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(fg, FG_SRAM_VBATT_FULL, &pval->intval); break; @@ -3766,6 +3866,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CC_STEP_SEL: pval->intval = chip->ttf.cc_step.sel; break; + case POWER_SUPPLY_PROP_REAL_CAPACITY: + rc = fg_get_prop_real_capacity(fg, &pval->intval); + break; case POWER_SUPPLY_PROP_FG_RESET_CLOCK: pval->intval = 0; break; @@ -3884,6 +3987,16 @@ static int fg_psy_set_property(struct power_supply *psy, int rc = 0; switch (psp) { +/* david.liu@bsp, 20171023 Battery & Charging porting */ + case POWER_SUPPLY_PROP_CC_TO_CV_POINT: + oem_update_cc_cv_setpoint(fg, pval->intval); + break; + case POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC: + oneplus_set_allow_read_iic(fg, pval->intval); + break; + case POWER_SUPPLY_PROP_UPDATE_LCD_IS_OFF: + oneplus_set_lcd_off_status(fg, pval->intval); + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: rc = fg_set_constant_chg_voltage(fg, pval->intval); break; @@ -3983,6 +4096,8 @@ 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: +/* david.liu@bsp, 20171023 Battery & Charging porting */ + case POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC: return 1; default: break; @@ -4048,7 +4163,6 @@ static int twm_notifier_cb(struct notifier_block *nb, static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_REAL_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_RAW, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_COLD_TEMP, @@ -4072,12 +4186,17 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, + POWER_SUPPLY_PROP_REAL_CAPACITY, + POWER_SUPPLY_PROP_BATTERY_HEALTH, +/* david.liu@bsp, 20171023 Battery & Charging porting */ + POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC, + POWER_SUPPLY_PROP_BQ_SOC, + POWER_SUPPLY_PROP_REMAINING_CAPACITY, POWER_SUPPLY_PROP_FG_RESET_CLOCK, }; @@ -4348,6 +4467,14 @@ static int fg_hw_init(struct fg_dev *fg) return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ + rc = fg_masked_write(fg, + BATT_INFO_ESR_PULL_DN_CFG(fg), 0xFF, 0); + if (rc < 0) { + pr_err("Error in writing ESR PULL DN, rc=%d\n", rc); + return rc; + } + fg_encode(fg->sp, FG_SRAM_ESR_PULSE_THRESH, chip->dt.esr_pulse_thresh_ma, buf); rc = fg_sram_write(fg, fg->sp[FG_SRAM_ESR_PULSE_THRESH].addr_word, @@ -4366,6 +4493,14 @@ static int fg_hw_init(struct fg_dev *fg) return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ + rc = fg_masked_write(fg, + BATT_INFO_ESR_PULL_DN_CFG(fg), 0xFF, 0); + if (rc < 0) { + pr_err("Error in writing ESR PULL DN, rc=%d\n", rc); + return rc; + } + if (is_debug_batt_id(fg) || chip->dt.disable_esr_pull_dn) { val = ESR_NO_PULL_DOWN; rc = fg_masked_write(fg, BATT_INFO_ESR_PULL_DN_CFG(fg), @@ -4703,7 +4838,6 @@ static struct fg_irq_info fg_irqs[FG_GEN3_IRQ_MAX] = { [MSOC_DELTA_IRQ] = { .name = "msoc-delta", .handler = fg_delta_msoc_irq_handler, - .wakeable = true, }, [BSOC_DELTA_IRQ] = { .name = "bsoc-delta", @@ -4723,7 +4857,6 @@ static struct fg_irq_info fg_irqs[FG_GEN3_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", @@ -5117,6 +5250,11 @@ static int fg_parse_dt(struct fg_gen3_chip *chip) else chip->dt.rsense_sel = (u8)temp & SOURCE_SELECT_MASK; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + fg->use_external_fg = + of_property_read_bool(node, "oem,use_external_fg"); + pr_info("use_external_fg=%d\n", fg->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; @@ -5367,6 +5505,9 @@ static void fg_cleanup(struct fg_gen3_chip *chip) cancel_work_sync(&fg->status_change_work); cancel_work_sync(&fg->esr_filter_work); cancel_delayed_work_sync(&chip->pl_enable_work); +/* david.liu@bsp, 20170314 Fix system crash */ + if (fg->fg_psy) + power_supply_unregister(fg->fg_psy); fg_unregister_interrupts(fg, chip, FG_GEN3_IRQ_MAX); alarm_try_to_cancel(&fg->esr_filter_alarm); @@ -5387,6 +5528,33 @@ static void fg_cleanup(struct fg_gen3_chip *chip) dev_set_drvdata(fg->dev, NULL); } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +static void oem_update_cc_cv_setpoint( + struct fg_dev *fg, int cv_float_point) +{ + /* TODO: write CC_CV_SETPOINT_REG */ +} + +static void oneplus_set_allow_read_iic(struct fg_dev *fg, + bool status) +{ + if (fg->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_dev *fg, + bool status) +{ + if (fg->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_gen3_chip *chip; @@ -5601,7 +5769,7 @@ static int fg_gen3_probe(struct platform_device *pdev) device_init_wakeup(fg->dev, true); schedule_delayed_work(&fg->profile_load_work, 0); - pr_debug("FG GEN3 driver probed successfully\n"); + pr_info("FG GEN3 driver probed successfully\n"); return 0; exit: fg_cleanup(chip); @@ -5727,18 +5895,7 @@ static struct platform_driver fg_gen3_driver = { .shutdown = fg_gen3_shutdown, }; -static int __init fg_gen3_init(void) -{ - return platform_driver_register(&fg_gen3_driver); -} - -static void __exit fg_gen3_exit(void) -{ - return platform_driver_unregister(&fg_gen3_driver); -} - -module_init(fg_gen3_init); -module_exit(fg_gen3_exit); +module_platform_driver(fg_gen3_driver); MODULE_DESCRIPTION("QPNP Fuel gauge GEN3 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c new file mode 100644 index 000000000000..10875c2b7016 --- /dev/null +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -0,0 +1,1817 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QNOVO_REVISION1 0x00 +#define QNOVO_REVISION2 0x01 +#define QNOVO_PERPH_TYPE 0x04 +#define QNOVO_PERPH_SUBTYPE 0x05 +#define QNOVO_PTTIME_STS 0x07 +#define QNOVO_PTRAIN_STS 0x08 +#define QNOVO_ERROR_STS 0x09 +#define QNOVO_ERROR_BIT BIT(0) +#define QNOVO_ERROR_STS2 0x0A +#define QNOVO_ERROR_CHARGING_DISABLED BIT(1) +#define QNOVO_INT_RT_STS 0x10 +#define QNOVO_INT_SET_TYPE 0x11 +#define QNOVO_INT_POLARITY_HIGH 0x12 +#define QNOVO_INT_POLARITY_LOW 0x13 +#define QNOVO_INT_LATCHED_CLR 0x14 +#define QNOVO_INT_EN_SET 0x15 +#define QNOVO_INT_EN_CLR 0x16 +#define QNOVO_INT_LATCHED_STS 0x18 +#define QNOVO_INT_PENDING_STS 0x19 +#define QNOVO_INT_MID_SEL 0x1A +#define QNOVO_INT_PRIORITY 0x1B +#define QNOVO_PE_CTRL 0x40 +#define QNOVO_PREST1_CTRL 0x41 +#define QNOVO_PPULS1_LSB_CTRL 0x42 +#define QNOVO_PPULS1_MSB_CTRL 0x43 +#define QNOVO_NREST1_CTRL 0x44 +#define QNOVO_NPULS1_CTRL 0x45 +#define QNOVO_PPCNT_CTRL 0x46 +#define QNOVO_VLIM1_LSB_CTRL 0x47 +#define QNOVO_VLIM1_MSB_CTRL 0x48 +#define QNOVO_PTRAIN_EN 0x49 +#define QNOVO_PTRAIN_EN_BIT BIT(0) +#define QNOVO_PE_CTRL2 0x4A +#define QNOVO_PREST2_LSB_CTRL 0x50 +#define QNOVO_PREST2_MSB_CTRL 0x51 +#define QNOVO_PPULS2_LSB_CTRL 0x52 +#define QNOVO_PPULS2_MSB_CTRL 0x53 +#define QNOVO_NREST2_CTRL 0x54 +#define QNOVO_NPULS2_CTRL 0x55 +#define QNOVO_VLIM2_LSB_CTRL 0x56 +#define QNOVO_VLIM2_MSB_CTRL 0x57 +#define QNOVO_PVOLT1_LSB 0x60 +#define QNOVO_PVOLT1_MSB 0x61 +#define QNOVO_PCUR1_LSB 0x62 +#define QNOVO_PCUR1_MSB 0x63 +#define QNOVO_PVOLT2_LSB 0x70 +#define QNOVO_PVOLT2_MSB 0x71 +#define QNOVO_RVOLT2_LSB 0x72 +#define QNOVO_RVOLT2_MSB 0x73 +#define QNOVO_PCUR2_LSB 0x74 +#define QNOVO_PCUR2_MSB 0x75 +#define QNOVO_SCNT 0x80 +#define QNOVO_VMAX_LSB 0x90 +#define QNOVO_VMAX_MSB 0x91 +#define QNOVO_SNUM 0x92 + +/* Registers ending in 0 imply external rsense */ +#define QNOVO_IADC_OFFSET_0 0xA0 +#define QNOVO_IADC_OFFSET_1 0xA1 +#define QNOVO_IADC_GAIN_0 0xA2 +#define QNOVO_IADC_GAIN_1 0xA3 +#define QNOVO_VADC_OFFSET 0xA4 +#define QNOVO_VADC_GAIN 0xA5 +#define QNOVO_IADC_GAIN_2 0xA6 +#define QNOVO_SPARE 0xA7 +#define QNOVO_STRM_CTRL 0xA8 +#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9 +#define QNOVO_IADC_OFFSET_OVR 0xAA + +#define QNOVO_DISABLE_CHARGING 0xAB +#define ERR_SWITCHER_DISABLED BIT(7) +#define ERR_JEITA_SOFT_CONDITION BIT(6) +#define ERR_BAT_OV BIT(5) +#define ERR_CV_MODE BIT(4) +#define ERR_BATTERY_MISSING BIT(3) +#define ERR_SAFETY_TIMER_EXPIRED BIT(2) +#define ERR_CHARGING_DISABLED BIT(1) +#define ERR_JEITA_HARD_CONDITION BIT(0) + +#define QNOVO_TR_IADC_OFFSET_0 0xF1 +#define QNOVO_TR_IADC_OFFSET_1 0xF2 + +#define DRV_MAJOR_VERSION 1 +#define DRV_MINOR_VERSION 0 + +#define IADC_LSB_NA 2441400 +#define VADC_LSB_NA 1220700 +#define GAIN_LSB_FACTOR 976560 + +#define USER_VOTER "user_voter" +#define SHUTDOWN_VOTER "user_voter" +#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter" + +#define QNOVO_VOTER "qnovo_voter" +#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER" +#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER" +#define QNI_PT_VOTER "QNI_PT_VOTER" +#define ESR_VOTER "ESR_VOTER" + +#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER" +#define CHG_READY_VOTER "CHG_READY_VOTER" +#define USB_READY_VOTER "USB_READY_VOTER" +#define DC_READY_VOTER "DC_READY_VOTER" + +#define PT_RESTART_VOTER "PT_RESTART_VOTER" +#define REG_WRITE_VOTER "REG_WRITE_VOTER" + +#define CLASS_ATTR_IDX_RO(_name, _func) \ +static ssize_t _name##_show(struct class *c, struct class_attribute *attr, \ + char *ubuf) \ +{ \ + return _func##_show(c, attr, ubuf); \ +}; \ +static CLASS_ATTR_RO(_name) + +#define CLASS_ATTR_IDX_RW(_name, _func) \ +static ssize_t _name##_show(struct class *c, struct class_attribute *attr, \ + char *ubuf) \ +{ \ + return _func##_show(c, attr, ubuf); \ +}; \ +static ssize_t _name##_store(struct class *c, struct class_attribute *attr, \ + const char *ubuf, size_t count) \ +{ \ + return _func##_store(c, attr, ubuf, count); \ +}; \ +static CLASS_ATTR_RW(_name) + +struct qnovo_dt_props { + bool external_rsense; + struct device_node *revid_dev_node; + bool enable_for_dc; +}; + +struct qnovo { + int base; + struct mutex write_lock; + struct regmap *regmap; + struct qnovo_dt_props dt; + struct device *dev; + struct votable *disable_votable; + struct votable *pt_dis_votable; + struct votable *not_ok_to_qnovo_votable; + struct votable *chg_ready_votable; + struct votable *awake_votable; + struct votable *auto_esr_votable; + struct class qnovo_class; + struct pmic_revid_data *pmic_rev_id; + u32 wa_flags; + s64 external_offset_nA; + s64 internal_offset_nA; + s64 offset_nV; + s64 external_i_gain_mega; + s64 internal_i_gain_mega; + s64 v_gain_mega; + struct notifier_block nb; + struct power_supply *batt_psy; + struct power_supply *bms_psy; + struct power_supply *usb_psy; + struct power_supply *dc_psy; + struct work_struct status_change_work; + int fv_uV_request; + int fcc_uA_request; + int usb_present; + int dc_present; + struct delayed_work usb_debounce_work; + struct delayed_work dc_debounce_work; + + struct delayed_work ptrain_restart_work; +}; + +static int debug_mask; +module_param_named(debug_mask, debug_mask, int, 0600); + +#define qnovo_dbg(chip, reason, fmt, ...) \ + do { \ + if (debug_mask & (reason)) \ + dev_info(chip->dev, fmt, ##__VA_ARGS__); \ + else \ + dev_dbg(chip->dev, fmt, ##__VA_ARGS__); \ + } while (0) + +static bool is_secure(struct qnovo *chip, int addr) +{ + /* assume everything above 0x40 is secure */ + return (bool)(addr >= 0x40); +} + +static int qnovo_read(struct qnovo *chip, u16 addr, u8 *buf, int len) +{ + return regmap_bulk_read(chip->regmap, chip->base + addr, buf, len); +} + +static int qnovo_masked_write(struct qnovo *chip, u16 addr, u8 mask, u8 val) +{ + int rc = 0; + + mutex_lock(&chip->write_lock); + if (is_secure(chip, addr)) { + rc = regmap_write(chip->regmap, + ((chip->base + addr) & ~(0xFF)) | 0xD0, 0xA5); + if (rc < 0) + goto unlock; + } + + rc = regmap_update_bits(chip->regmap, chip->base + addr, mask, val); + +unlock: + mutex_unlock(&chip->write_lock); + return rc; +} + +static int qnovo_write(struct qnovo *chip, u16 addr, u8 *buf, int len) +{ + int i, rc = 0; + bool is_start_secure, is_end_secure; + + is_start_secure = is_secure(chip, addr); + is_end_secure = is_secure(chip, addr + len); + + if (!is_start_secure && !is_end_secure) { + mutex_lock(&chip->write_lock); + rc = regmap_bulk_write(chip->regmap, chip->base + addr, + buf, len); + goto unlock; + } + + mutex_lock(&chip->write_lock); + for (i = addr; i < addr + len; i++) { + if (is_secure(chip, i)) { + rc = regmap_write(chip->regmap, + ((chip->base + i) & ~(0xFF)) | 0xD0, 0xA5); + if (rc < 0) + goto unlock; + } + rc = regmap_write(chip->regmap, chip->base + i, buf[i - addr]); + if (rc < 0) + goto unlock; + } + +unlock: + mutex_unlock(&chip->write_lock); + return rc; +} + +static bool is_batt_available(struct qnovo *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +static bool is_fg_available(struct qnovo *chip) +{ + if (!chip->bms_psy) + chip->bms_psy = power_supply_get_by_name("bms"); + + if (!chip->bms_psy) + return false; + + return true; +} + +static bool is_usb_available(struct qnovo *chip) +{ + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + + if (!chip->usb_psy) + return false; + + return true; +} + +static bool is_dc_available(struct qnovo *chip) +{ + if (!chip->dc_psy) + chip->dc_psy = power_supply_get_by_name("dc"); + + if (!chip->dc_psy) + return false; + + return true; +} + +static int qnovo_batt_psy_update(struct qnovo *chip, bool disable) +{ + union power_supply_propval pval = {0}; + int rc = 0; + + if (!is_batt_available(chip)) + return -EINVAL; + + if (chip->fv_uV_request != -EINVAL) { + pval.intval = disable ? -EINVAL : chip->fv_uV_request; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + &pval); + if (rc < 0) { + pr_err("Couldn't set prop qnovo_fv rc = %d\n", rc); + return -EINVAL; + } + } + + if (chip->fcc_uA_request != -EINVAL) { + pval.intval = disable ? -EINVAL : chip->fcc_uA_request; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + &pval); + if (rc < 0) { + pr_err("Couldn't set prop qnovo_fcc rc = %d\n", rc); + return -EINVAL; + } + } + + return rc; +} + +static int qnovo_disable_cb(struct votable *votable, void *data, int disable, + const char *client) +{ + struct qnovo *chip = data; + union power_supply_propval pval = {0}; + int rc; + + if (!is_batt_available(chip)) + return -EINVAL; + + pval.intval = !disable; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, + &pval); + if (rc < 0) { + pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc); + return -EINVAL; + } + + vote(chip->auto_esr_votable, QNOVO_OVERALL_VOTER, disable, 0); + + vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0); + rc = qnovo_batt_psy_update(chip, disable); + return rc; +} + +static int pt_dis_votable_cb(struct votable *votable, void *data, int disable, + const char *client) +{ + struct qnovo *chip = data; + int rc; + + if (disable) { + cancel_delayed_work_sync(&chip->ptrain_restart_work); + vote(chip->awake_votable, PT_RESTART_VOTER, false, 0); + } + + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", + (bool)disable ? "disable" : "enable", rc); + return rc; + } + + if (!disable) { + vote(chip->awake_votable, PT_RESTART_VOTER, true, 0); + schedule_delayed_work(&chip->ptrain_restart_work, + msecs_to_jiffies(20)); + } + + return 0; +} + +static int not_ok_to_qnovo_cb(struct votable *votable, void *data, + int not_ok_to_qnovo, + const char *client) +{ + struct qnovo *chip = data; + + vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0); + if (not_ok_to_qnovo) + vote(chip->disable_votable, USER_VOTER, true, 0); + + kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + return 0; +} + +static int chg_ready_cb(struct votable *votable, void *data, int ready, + const char *client) +{ + struct qnovo *chip = data; + + vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0); + + return 0; +} + +static int awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct qnovo *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + return 0; +} + +static int auto_esr_cb(struct votable *votable, void *data, int auto_esr, + const char *client) +{ + struct qnovo *chip = data; + union power_supply_propval pval = {0}; + + pval.intval = !auto_esr; + if (is_fg_available(chip)) + power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, + &pval); + + return 0; +} + +static void pe_ctrl2_write_cb(struct qnovo *chip, u8 *val) +{ + if (get_effective_result(chip->disable_votable) == 0) + vote(chip->auto_esr_votable, REG_WRITE_VOTER, (*val == 0), 0); +} + +static int qnovo_parse_dt(struct qnovo *chip) +{ + struct device_node *node = chip->dev->of_node; + int rc; + + if (!node) { + pr_err("device tree node missing\n"); + return -EINVAL; + } + + rc = of_property_read_u32(node, "reg", &chip->base); + if (rc < 0) { + pr_err("Couldn't read base rc = %d\n", rc); + return rc; + } + + chip->dt.external_rsense = of_property_read_bool(node, + "qcom,external-rsense"); + + chip->dt.revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0); + if (!chip->dt.revid_dev_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + chip->dt.enable_for_dc = of_property_read_bool(node, + "qcom,enable-for-dc"); + + return 0; +} + +enum { + VER = 0, + OK_TO_QNOVO, + QNOVO_ENABLE, + PT_ENABLE, + FV_REQUEST, + FCC_REQUEST, + PE_CTRL_REG, + PE_CTRL2_REG, + PTRAIN_STS_REG, + INT_RT_STS_REG, + ERR_STS2_REG, + PREST1, + PPULS1, + NREST1, + NPULS1, + PPCNT, + VLIM1, + PVOLT1, + PCUR1, + PTTIME, + PREST2, + PPULS2, + NREST2, + NPULS2, + VLIM2, + PVOLT2, + RVOLT2, + PCUR2, + SCNT, + VMAX, + SNUM, + VBATT, + IBATT, + BATTTEMP, + BATTSOC, +}; + +struct param_info { + char *name; + int start_addr; + int num_regs; + int reg_to_unit_multiplier; + int reg_to_unit_divider; + int reg_to_unit_offset; + int min_val; + int max_val; + void (*callback)(struct qnovo *chip, u8 *val); + char *units_str; +}; + +static struct param_info params[] = { + [PT_ENABLE] = { + .name = "PT_ENABLE", + .start_addr = QNOVO_PTRAIN_EN, + .num_regs = 1, + .units_str = "", + }, + [FV_REQUEST] = { + .units_str = "uV", + }, + [FCC_REQUEST] = { + .units_str = "uA", + }, + [PE_CTRL_REG] = { + .name = "CTRL_REG", + .start_addr = QNOVO_PE_CTRL, + .num_regs = 1, + .units_str = "", + }, + [PE_CTRL2_REG] = { + .name = "PE_CTRL2_REG", + .start_addr = QNOVO_PE_CTRL2, + .num_regs = 1, + .callback = pe_ctrl2_write_cb, + .units_str = "", + }, + [PTRAIN_STS_REG] = { + .name = "PTRAIN_STS", + .start_addr = QNOVO_PTRAIN_STS, + .num_regs = 1, + .units_str = "", + }, + [INT_RT_STS_REG] = { + .name = "INT_RT_STS", + .start_addr = QNOVO_INT_RT_STS, + .num_regs = 1, + .units_str = "", + }, + [ERR_STS2_REG] = { + .name = "RAW_CHGR_ERR", + .start_addr = QNOVO_ERROR_STS2, + .num_regs = 1, + .units_str = "", + }, + [PREST1] = { + .name = "PREST1", + .start_addr = QNOVO_PREST1_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .min_val = 5, + .max_val = 255, + .units_str = "mS", + }, + [PPULS1] = { + .name = "PPULS1", + .start_addr = QNOVO_PPULS1_LSB_CTRL, + .num_regs = 2, + .reg_to_unit_multiplier = 1600, /* converts to uC */ + .reg_to_unit_divider = 1, + .min_val = 30000, + .max_val = 65535000, + .units_str = "uC", + }, + [NREST1] = { + .name = "NREST1", + .start_addr = QNOVO_NREST1_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .min_val = 5, + .max_val = 255, + .units_str = "mS", + }, + [NPULS1] = { + .name = "NPULS1", + .start_addr = QNOVO_NPULS1_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .min_val = 0, + .max_val = 255, + .units_str = "mS", + }, + [PPCNT] = { + .name = "PPCNT", + .start_addr = QNOVO_PPCNT_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 1, + .reg_to_unit_divider = 1, + .min_val = 1, + .max_val = 255, + .units_str = "pulses", + }, + [VLIM1] = { + .name = "VLIM1", + .start_addr = QNOVO_VLIM1_LSB_CTRL, + .num_regs = 2, + .reg_to_unit_multiplier = 610350, /* converts to nV */ + .reg_to_unit_divider = 1, + .min_val = 2200000, + .max_val = 4500000, + .units_str = "uV", + }, + [PVOLT1] = { + .name = "PVOLT1", + .start_addr = QNOVO_PVOLT1_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 610350, /* converts to nV */ + .reg_to_unit_divider = 1, + .units_str = "uV", + }, + [PCUR1] = { + .name = "PCUR1", + .start_addr = QNOVO_PCUR1_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 1220700, /* converts to nA */ + .reg_to_unit_divider = 1, + .units_str = "uA", + }, + [PTTIME] = { + .name = "PTTIME", + .start_addr = QNOVO_PTTIME_STS, + .num_regs = 1, + .reg_to_unit_multiplier = 2, + .reg_to_unit_divider = 1, + .units_str = "S", + }, + [PREST2] = { + .name = "PREST2", + .start_addr = QNOVO_PREST2_LSB_CTRL, + .num_regs = 2, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .min_val = 5, + .max_val = 65535, + .units_str = "mS", + }, + [PPULS2] = { + .name = "PPULS2", + .start_addr = QNOVO_PPULS2_LSB_CTRL, + .num_regs = 2, + .reg_to_unit_multiplier = 1600, /* converts to uC */ + .reg_to_unit_divider = 1, + .min_val = 30000, + .max_val = 65535000, + .units_str = "uC", + }, + [NREST2] = { + .name = "NREST2", + .start_addr = QNOVO_NREST2_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .reg_to_unit_offset = -5, + .min_val = 5, + .max_val = 255, + .units_str = "mS", + }, + [NPULS2] = { + .name = "NPULS2", + .start_addr = QNOVO_NPULS2_CTRL, + .num_regs = 1, + .reg_to_unit_multiplier = 5, + .reg_to_unit_divider = 1, + .min_val = 0, + .max_val = 255, + .units_str = "mS", + }, + [VLIM2] = { + .name = "VLIM2", + .start_addr = QNOVO_VLIM2_LSB_CTRL, + .num_regs = 2, + .reg_to_unit_multiplier = 610350, /* converts to nV */ + .reg_to_unit_divider = 1, + .min_val = 2200000, + .max_val = 4500000, + .units_str = "uV", + }, + [PVOLT2] = { + .name = "PVOLT2", + .start_addr = QNOVO_PVOLT2_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 610350, /* converts to nV */ + .reg_to_unit_divider = 1, + .units_str = "uV", + }, + [RVOLT2] = { + .name = "RVOLT2", + .start_addr = QNOVO_RVOLT2_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 610350, + .reg_to_unit_divider = 1, + .units_str = "uV", + }, + [PCUR2] = { + .name = "PCUR2", + .start_addr = QNOVO_PCUR2_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 1220700, /* converts to nA */ + .reg_to_unit_divider = 1, + .units_str = "uA", + }, + [SCNT] = { + .name = "SCNT", + .start_addr = QNOVO_SCNT, + .num_regs = 1, + .reg_to_unit_multiplier = 1, + .reg_to_unit_divider = 1, + .min_val = 0, + .max_val = 255, + .units_str = "pulses", + }, + [VMAX] = { + .name = "VMAX", + .start_addr = QNOVO_VMAX_LSB, + .num_regs = 2, + .reg_to_unit_multiplier = 814000, /* converts to nV */ + .reg_to_unit_divider = 1, + .units_str = "uV", + }, + [SNUM] = { + .name = "SNUM", + .start_addr = QNOVO_SNUM, + .num_regs = 1, + .reg_to_unit_multiplier = 1, + .reg_to_unit_divider = 1, + .units_str = "pulses", + }, + [VBATT] = { + .name = "POWER_SUPPLY_PROP_VOLTAGE_NOW", + .start_addr = POWER_SUPPLY_PROP_VOLTAGE_NOW, + .units_str = "uV", + }, + [IBATT] = { + .name = "POWER_SUPPLY_PROP_CURRENT_NOW", + .start_addr = POWER_SUPPLY_PROP_CURRENT_NOW, + .units_str = "uA", + }, + [BATTTEMP] = { + .name = "POWER_SUPPLY_PROP_TEMP", + .start_addr = POWER_SUPPLY_PROP_TEMP, + .units_str = "uV", + }, + [BATTSOC] = { + .name = "POWER_SUPPLY_PROP_CAPACITY", + .start_addr = POWER_SUPPLY_PROP_CAPACITY, + .units_str = "%", + }, +}; + +static struct attribute *qnovo_class_attrs[]; + +static ssize_t version_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d.%d\n", + DRV_MAJOR_VERSION, DRV_MINOR_VERSION); +} +static CLASS_ATTR_RO(version); + +static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int val = get_effective_result(chip->not_ok_to_qnovo_votable); + + return snprintf(buf, PAGE_SIZE, "%d\n", !val); +} +static CLASS_ATTR_RO(ok_to_qnovo); + +static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int val = get_effective_result(chip->disable_votable); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", !val); +} + +static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + unsigned long val; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + vote(chip->disable_votable, USER_VOTER, !val, 0); + + return count; +} +static CLASS_ATTR_RW(qnovo_enable); + +static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int val = get_effective_result(chip->pt_dis_votable); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", !val); +} + +static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + unsigned long val; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + /* val being 0, userspace wishes to disable pt so vote true */ + vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0); + + return count; +} +static CLASS_ATTR_RW(pt_enable); + + +static ssize_t val_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int i = &attr->attr - *qnovo_class_attrs; + int val = 0; + + if (i == FV_REQUEST) + val = chip->fv_uV_request; + + if (i == FCC_REQUEST) + val = chip->fcc_uA_request; + + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t val_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int i = &attr->attr - *qnovo_class_attrs; + unsigned long val; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + if (i == FV_REQUEST) + chip->fv_uV_request = val; + + if (i == FCC_REQUEST) + chip->fcc_uA_request = val; + + if (!get_effective_result(chip->disable_votable)) + qnovo_batt_psy_update(chip, false); + + return count; +} + +static ssize_t reg_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + u16 regval; + int rc; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval = buf[1] << 8 | buf[0]; + + return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval); +} + +static ssize_t reg_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + unsigned long val; + int rc; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + + rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't write %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + + if (params[i].callback) + params[i].callback(chip, buf); + + return count; +} + +static ssize_t time_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + u16 regval; + int val; + int rc; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval = buf[1] << 8 | buf[0]; + + val = ((regval * params[i].reg_to_unit_multiplier) + / params[i].reg_to_unit_divider) + - params[i].reg_to_unit_offset; + + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t time_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + u16 regval; + unsigned long val; + int rc; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + if (val < params[i].min_val || val > params[i].max_val) { + pr_err("Out of Range %d%s for %s\n", (int)val, + params[i].units_str, + params[i].name); + return -ERANGE; + } + + regval = (((int)val + params[i].reg_to_unit_offset) + * params[i].reg_to_unit_divider) + / params[i].reg_to_unit_multiplier; + buf[0] = regval & 0xFF; + buf[1] = (regval >> 8) & 0xFF; + + rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't write %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + + return count; +} + +static ssize_t current_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + int rc; + int comp_val_uA; + s64 regval_nA; + s64 gain, offset_nA, comp_val_nA; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + + if (buf[1] & BIT(5)) + buf[1] |= GENMASK(7, 6); + + regval_nA = (s16)(buf[1] << 8 | buf[0]); + regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier, + params[i].reg_to_unit_divider) + - params[i].reg_to_unit_offset; + + if (chip->dt.external_rsense) { + offset_nA = chip->external_offset_nA; + gain = chip->external_i_gain_mega; + } else { + offset_nA = chip->internal_offset_nA; + gain = chip->internal_i_gain_mega; + } + + comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA; + comp_val_uA = div_s64(comp_val_nA, 1000); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA); +} + +static ssize_t voltage_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + int rc; + int comp_val_uV; + s64 regval_nV; + s64 gain, offset_nV, comp_val_nV; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval_nV = buf[1] << 8 | buf[0]; + regval_nV = div_s64(regval_nV * params[i].reg_to_unit_multiplier, + params[i].reg_to_unit_divider) + - params[i].reg_to_unit_offset; + + offset_nV = chip->offset_nV; + gain = chip->v_gain_mega; + + comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV; + comp_val_uV = div_s64(comp_val_nV, 1000); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV); +} + +static ssize_t voltage_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + int rc; + unsigned long val_uV; + s64 regval_nV; + s64 gain, offset_nV; + + if (kstrtoul(ubuf, 0, &val_uV)) + return -EINVAL; + + if (val_uV < params[i].min_val || val_uV > params[i].max_val) { + pr_err("Out of Range %d%s for %s\n", (int)val_uV, + params[i].units_str, + params[i].name); + return -ERANGE; + } + + offset_nV = chip->offset_nV; + gain = chip->v_gain_mega; + + regval_nV = (s64)val_uV * 1000 - offset_nV; + regval_nV = div_s64(regval_nV * 1000000, gain); + + regval_nV = div_s64((regval_nV + params[i].reg_to_unit_offset) + * params[i].reg_to_unit_divider, + params[i].reg_to_unit_multiplier); + buf[0] = regval_nV & 0xFF; + buf[1] = ((u64)regval_nV >> 8) & 0xFF; + + rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't write %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + + return count; +} + +static ssize_t coulomb_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + int rc; + int comp_val_uC; + s64 regval_uC, gain; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval_uC = buf[1] << 8 | buf[0]; + regval_uC = div_s64(regval_uC * params[i].reg_to_unit_multiplier, + params[i].reg_to_unit_divider) + - params[i].reg_to_unit_offset; + + if (chip->dt.external_rsense) + gain = chip->external_i_gain_mega; + else + gain = chip->internal_i_gain_mega; + + comp_val_uC = div_s64(regval_uC * gain, 1000000); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC); +} + +static ssize_t coulomb_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + int rc; + unsigned long val_uC; + s64 regval; + s64 gain; + + if (kstrtoul(ubuf, 0, &val_uC)) + return -EINVAL; + + if (val_uC < params[i].min_val || val_uC > params[i].max_val) { + pr_err("Out of Range %d%s for %s\n", (int)val_uC, + params[i].units_str, + params[i].name); + return -ERANGE; + } + + if (chip->dt.external_rsense) + gain = chip->external_i_gain_mega; + else + gain = chip->internal_i_gain_mega; + + regval = div_s64((s64)val_uC * 1000000, gain); + + regval = div_s64((regval + params[i].reg_to_unit_offset) + * params[i].reg_to_unit_divider, + params[i].reg_to_unit_multiplier); + + buf[0] = regval & 0xFF; + buf[1] = ((u64)regval >> 8) & 0xFF; + + rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't write %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + + return count; +} + +static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = &attr->attr - *qnovo_class_attrs; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + int rc = -EINVAL; + int prop = params[i].start_addr; + union power_supply_propval pval = {0}; + + if (!is_batt_available(chip)) + return -EINVAL; + + rc = power_supply_get_property(chip->batt_psy, prop, &pval); + if (rc < 0) { + pr_err("Couldn't read battery prop %s rc = %d\n", + params[i].name, rc); + return -EINVAL; + } + + return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval); +} + +CLASS_ATTR_IDX_RW(fv_uV_request, val); +CLASS_ATTR_IDX_RW(fcc_uA_request, val); +CLASS_ATTR_IDX_RW(PE_CTRL_REG, reg); +CLASS_ATTR_IDX_RW(PE_CTRL2_REG, reg); +CLASS_ATTR_IDX_RO(PTRAIN_STS_REG, reg); +CLASS_ATTR_IDX_RO(INT_RT_STS_REG, reg); +CLASS_ATTR_IDX_RO(ERR_STS2_REG, reg); +CLASS_ATTR_IDX_RW(PREST1_mS, time); +CLASS_ATTR_IDX_RW(PPULS1_uC, coulomb); +CLASS_ATTR_IDX_RW(NREST1_mS, time); +CLASS_ATTR_IDX_RW(NPULS1_mS, time); +CLASS_ATTR_IDX_RW(PPCNT, time); +CLASS_ATTR_IDX_RW(VLIM1_uV, voltage); +CLASS_ATTR_IDX_RO(PVOLT1_uV, voltage); +CLASS_ATTR_IDX_RO(PCUR1_uA, current); +CLASS_ATTR_IDX_RO(PTTIME_S, time); +CLASS_ATTR_IDX_RW(PREST2_mS, time); +CLASS_ATTR_IDX_RW(PPULS2_uC, coulomb); +CLASS_ATTR_IDX_RW(NREST2_mS, time); +CLASS_ATTR_IDX_RW(NPULS2_mS, time); +CLASS_ATTR_IDX_RW(VLIM2_uV, voltage); +CLASS_ATTR_IDX_RO(PVOLT2_uV, voltage); +CLASS_ATTR_IDX_RO(RVOLT2_uV, voltage); +CLASS_ATTR_IDX_RO(PCUR2_uA, current); +CLASS_ATTR_IDX_RW(SCNT, time); +CLASS_ATTR_IDX_RO(VMAX_uV, voltage); +CLASS_ATTR_IDX_RO(SNUM, time); +CLASS_ATTR_IDX_RO(VBATT_uV, batt_prop); +CLASS_ATTR_IDX_RO(IBATT_uA, batt_prop); +CLASS_ATTR_IDX_RO(BATTTEMP_deciDegC, batt_prop); +CLASS_ATTR_IDX_RO(BATTSOC, batt_prop); + +static struct attribute *qnovo_class_attrs[] = { + [VER] = &class_attr_version.attr, + [OK_TO_QNOVO] = &class_attr_ok_to_qnovo.attr, + [QNOVO_ENABLE] = &class_attr_qnovo_enable.attr, + [PT_ENABLE] = &class_attr_pt_enable.attr, + [FV_REQUEST] = &class_attr_fv_uV_request.attr, + [FCC_REQUEST] = &class_attr_fcc_uA_request.attr, + [PE_CTRL_REG] = &class_attr_PE_CTRL_REG.attr, + [PE_CTRL2_REG] = &class_attr_PE_CTRL2_REG.attr, + [PTRAIN_STS_REG] = &class_attr_PTRAIN_STS_REG.attr, + [INT_RT_STS_REG] = &class_attr_INT_RT_STS_REG.attr, + [ERR_STS2_REG] = &class_attr_ERR_STS2_REG.attr, + [PREST1] = &class_attr_PREST1_mS.attr, + [PPULS1] = &class_attr_PPULS1_uC.attr, + [NREST1] = &class_attr_NREST1_mS.attr, + [NPULS1] = &class_attr_NPULS1_mS.attr, + [PPCNT] = &class_attr_PPCNT.attr, + [VLIM1] = &class_attr_VLIM1_uV.attr, + [PVOLT1] = &class_attr_PVOLT1_uV.attr, + [PCUR1] = &class_attr_PCUR1_uA.attr, + [PTTIME] = &class_attr_PTTIME_S.attr, + [PREST2] = &class_attr_PREST2_mS.attr, + [PPULS2] = &class_attr_PPULS2_uC.attr, + [NREST2] = &class_attr_NREST2_mS.attr, + [NPULS2] = &class_attr_NPULS2_mS.attr, + [VLIM2] = &class_attr_VLIM2_uV.attr, + [PVOLT2] = &class_attr_PVOLT2_uV.attr, + [RVOLT2] = &class_attr_RVOLT2_uV.attr, + [PCUR2] = &class_attr_PCUR2_uA.attr, + [SCNT] = &class_attr_SCNT.attr, + [VMAX] = &class_attr_VMAX_uV.attr, + [SNUM] = &class_attr_SNUM.attr, + [VBATT] = &class_attr_VBATT_uV.attr, + [IBATT] = &class_attr_IBATT_uA.attr, + [BATTTEMP] = &class_attr_BATTTEMP_deciDegC.attr, + [BATTSOC] = &class_attr_BATTSOC.attr, + NULL, +}; +ATTRIBUTE_GROUPS(qnovo_class); + +static int qnovo_update_status(struct qnovo *chip) +{ + u8 val = 0; + int rc; + bool hw_ok_to_qnovo; + + rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1); + if (rc < 0) { + pr_err("Couldn't read error sts rc = %d\n", rc); + hw_ok_to_qnovo = false; + } else { + /* + * For CV mode keep qnovo enabled, userspace is expected to + * disable it after few runs + */ + hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? + true : false; + } + + vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER, + !hw_ok_to_qnovo, 0); + return 0; +} + +static void usb_debounce_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, usb_debounce_work.work); + + vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0); + vote(chip->awake_votable, USB_READY_VOTER, false, 0); +} + +static void dc_debounce_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, dc_debounce_work.work); + + vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0); + vote(chip->awake_votable, DC_READY_VOTER, false, 0); +} + +#define DEBOUNCE_MS 15000 /* 15 seconds */ +static void status_change_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, status_change_work); + union power_supply_propval pval; + bool usb_present = false, dc_present = false; + int rc; + + if (is_fg_available(chip)) + vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0); + + if (is_usb_available(chip)) { + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + usb_present = (rc < 0) ? 0 : pval.intval; + } + + if (chip->usb_present && !usb_present) { + /* removal */ + chip->usb_present = 0; + cancel_delayed_work_sync(&chip->usb_debounce_work); + vote(chip->awake_votable, USB_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0); + } else if (!chip->usb_present && usb_present) { + /* insertion */ + chip->usb_present = 1; + vote(chip->awake_votable, USB_READY_VOTER, true, 0); + schedule_delayed_work(&chip->usb_debounce_work, + msecs_to_jiffies(DEBOUNCE_MS)); + } + + if (is_dc_available(chip)) { + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_PRESENT, + &pval); + dc_present = (rc < 0) ? 0 : pval.intval; + } + + if (usb_present) + dc_present = 0; + + /* disable qnovo for dc path by forcing dc_present = 0 always */ + if (!chip->dt.enable_for_dc) + dc_present = 0; + + if (chip->dc_present && !dc_present) { + /* removal */ + chip->dc_present = 0; + cancel_delayed_work_sync(&chip->dc_debounce_work); + vote(chip->awake_votable, DC_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0); + } else if (!chip->dc_present && dc_present) { + /* insertion */ + chip->dc_present = 1; + vote(chip->awake_votable, DC_READY_VOTER, true, 0); + schedule_delayed_work(&chip->dc_debounce_work, + msecs_to_jiffies(DEBOUNCE_MS)); + } + + qnovo_update_status(chip); +} + +static void ptrain_restart_work(struct work_struct *work) +{ + struct qnovo *chip = container_of(work, + struct qnovo, ptrain_restart_work.work); + u8 pt_t1, pt_t2; + int rc; + u8 pt_en; + + rc = qnovo_read(chip, QNOVO_PTRAIN_EN, &pt_en, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTRAIN_EN rc = %d\n", + rc); + goto clean_up; + } + + if (!pt_en) { + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, + QNOVO_PTRAIN_EN_BIT, QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", + rc); + goto clean_up; + } + /* sleep 20ms for the pulse trains to restart and settle */ + msleep(20); + } + + rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t1, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n", + rc); + goto clean_up; + } + + /* pttime increments every 2 seconds */ + msleep(2100); + + rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t2, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n", + rc); + goto clean_up; + } + + if (pt_t1 != pt_t2) + goto clean_up; + + /* Toggle pt enable to restart pulse train */ + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, 0); + if (rc < 0) { + dev_err(chip->dev, "Couldn't disable pulse train rc=%d\n", rc); + goto clean_up; + } + msleep(1000); + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + QNOVO_PTRAIN_EN_BIT); + if (rc < 0) { + dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", rc); + goto clean_up; + } + +clean_up: + vote(chip->awake_votable, PT_RESTART_VOTER, false, 0); +} + +static int qnovo_notifier_call(struct notifier_block *nb, + unsigned long ev, void *v) +{ + struct power_supply *psy = v; + struct qnovo *chip = container_of(nb, struct qnovo, nb); + + if (ev != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (strcmp(psy->desc->name, "battery") == 0 + || strcmp(psy->desc->name, "bms") == 0 + || strcmp(psy->desc->name, "usb") == 0 + || strcmp(psy->desc->name, "dc") == 0) + schedule_work(&chip->status_change_work); + + return NOTIFY_OK; +} + +static irqreturn_t handle_ptrain_done(int irq, void *data) +{ + struct qnovo *chip = data; + union power_supply_propval pval = {0}; + + qnovo_update_status(chip); + + /* + * hw resets pt_en bit once ptrain_done triggers. + * vote on behalf of QNI to disable it such that + * once QNI enables it, the votable state changes + * and the callback that sets it is indeed invoked + */ + vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0); + + vote(chip->pt_dis_votable, ESR_VOTER, true, 0); + if (is_fg_available(chip) + && !get_client_vote(chip->disable_votable, USER_VOTER) + && !get_effective_result(chip->not_ok_to_qnovo_votable)) + power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE, + &pval); + + vote(chip->pt_dis_votable, ESR_VOTER, false, 0); + kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + return IRQ_HANDLED; +} + +static int qnovo_hw_init(struct qnovo *chip) +{ + int rc; + u8 iadc_offset_external, iadc_offset_internal; + u8 iadc_gain_external, iadc_gain_internal; + u8 vadc_offset, vadc_gain; + u8 val; + + vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0); + vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0); + + vote(chip->disable_votable, USER_VOTER, true, 0); + vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0); + + vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0); + vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0); + vote(chip->pt_dis_votable, ESR_VOTER, false, 0); + + vote(chip->auto_esr_votable, QNOVO_OVERALL_VOTER, true, 0); + + val = 0; + rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc bitstream control rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1); + if (rc < 0) { + pr_err("Couldn't read iadc exernal offset rc = %d\n", rc); + return rc; + } + + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_external; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1); + if (rc < 0) { + pr_err("Couldn't read iadc internal offset rc = %d\n", rc); + return rc; + } + + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_internal; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1); + if (rc < 0) { + pr_err("Couldn't read iadc external gain rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_IADC_GAIN_1, &iadc_gain_internal, 1); + if (rc < 0) { + pr_err("Couldn't read iadc internal gain rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_VADC_OFFSET, &vadc_offset, 1); + if (rc < 0) { + pr_err("Couldn't read vadc offset rc = %d\n", rc); + return rc; + } + + rc = qnovo_read(chip, QNOVO_VADC_GAIN, &vadc_gain, 1); + if (rc < 0) { + pr_err("Couldn't read vadc external gain rc = %d\n", rc); + return rc; + } + + chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA; + chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA; + chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA; + chip->external_i_gain_mega + = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR; + chip->external_i_gain_mega + = div_s64(chip->external_i_gain_mega, 1000); + chip->internal_i_gain_mega + = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR; + chip->internal_i_gain_mega + = div_s64(chip->internal_i_gain_mega, 1000); + chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR; + chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000); + + /* allow charger error conditions to disable qnovo, CV mode excluded */ + val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV | + ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED | + ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION; + rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1); + if (rc < 0) { + pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc); + return rc; + } + + return 0; +} + +static int qnovo_register_notifier(struct qnovo *chip) +{ + int rc; + + chip->nb.notifier_call = qnovo_notifier_call; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + return rc; + } + + return 0; +} + +static int qnovo_determine_initial_status(struct qnovo *chip) +{ + status_change_work(&chip->status_change_work); + return 0; +} + +static int qnovo_request_interrupts(struct qnovo *chip) +{ + int rc = 0; + int irq_ptrain_done = of_irq_get_byname(chip->dev->of_node, + "ptrain-done"); + + rc = devm_request_threaded_irq(chip->dev, irq_ptrain_done, NULL, + handle_ptrain_done, + IRQF_ONESHOT, "ptrain-done", chip); + if (rc < 0) { + pr_err("Couldn't request irq %d rc = %d\n", + irq_ptrain_done, rc); + return rc; + } + + enable_irq_wake(irq_ptrain_done); + + return rc; +} + +static int qnovo_probe(struct platform_device *pdev) +{ + struct qnovo *chip; + int rc = 0; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->fv_uV_request = -EINVAL; + chip->fcc_uA_request = -EINVAL; + chip->dev = &pdev->dev; + mutex_init(&chip->write_lock); + + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + pr_err("parent regmap is missing\n"); + return -EINVAL; + } + + rc = qnovo_parse_dt(chip); + if (rc < 0) { + pr_err("Couldn't parse device tree rc=%d\n", rc); + return rc; + } + + /* set driver data before resources request it */ + platform_set_drvdata(pdev, chip); + + chip->disable_votable = create_votable("QNOVO_DISABLE", VOTE_SET_ANY, + qnovo_disable_cb, chip); + if (IS_ERR(chip->disable_votable)) { + rc = PTR_ERR(chip->disable_votable); + goto cleanup; + } + + chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY, + pt_dis_votable_cb, chip); + if (IS_ERR(chip->pt_dis_votable)) { + rc = PTR_ERR(chip->pt_dis_votable); + goto destroy_disable_votable; + } + + chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK", + VOTE_SET_ANY, + not_ok_to_qnovo_cb, chip); + if (IS_ERR(chip->not_ok_to_qnovo_votable)) { + rc = PTR_ERR(chip->not_ok_to_qnovo_votable); + goto destroy_pt_dis_votable; + } + + chip->chg_ready_votable = create_votable("QNOVO_CHG_READY", + VOTE_SET_ANY, + chg_ready_cb, chip); + if (IS_ERR(chip->chg_ready_votable)) { + rc = PTR_ERR(chip->chg_ready_votable); + goto destroy_not_ok_to_qnovo_votable; + } + + chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY, + awake_cb, chip); + if (IS_ERR(chip->awake_votable)) { + rc = PTR_ERR(chip->awake_votable); + goto destroy_chg_ready_votable; + } + + chip->auto_esr_votable = create_votable("AUTO_ESR", VOTE_SET_ANY, + auto_esr_cb, chip); + if (IS_ERR(chip->auto_esr_votable)) { + rc = PTR_ERR(chip->auto_esr_votable); + goto destroy_auto_esr_votable; + } + + INIT_WORK(&chip->status_change_work, status_change_work); + INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work); + INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work); + INIT_DELAYED_WORK(&chip->ptrain_restart_work, ptrain_restart_work); + + rc = qnovo_hw_init(chip); + if (rc < 0) { + pr_err("Couldn't initialize hardware rc=%d\n", rc); + goto destroy_awake_votable; + } + + rc = qnovo_register_notifier(chip); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + goto unreg_notifier; + } + + rc = qnovo_determine_initial_status(chip); + if (rc < 0) { + pr_err("Couldn't determine initial status rc=%d\n", rc); + goto unreg_notifier; + } + + rc = qnovo_request_interrupts(chip); + if (rc < 0) { + pr_err("Couldn't request interrupts rc=%d\n", rc); + goto unreg_notifier; + } + chip->qnovo_class.name = "qnovo", + chip->qnovo_class.owner = THIS_MODULE, + chip->qnovo_class.class_groups = qnovo_class_groups; + + rc = class_register(&chip->qnovo_class); + if (rc < 0) { + pr_err("couldn't register qnovo sysfs class rc = %d\n", rc); + goto unreg_notifier; + } + + device_init_wakeup(chip->dev, true); + + return rc; + +unreg_notifier: + power_supply_unreg_notifier(&chip->nb); +destroy_auto_esr_votable: + destroy_votable(chip->auto_esr_votable); +destroy_awake_votable: + destroy_votable(chip->awake_votable); +destroy_chg_ready_votable: + destroy_votable(chip->chg_ready_votable); +destroy_not_ok_to_qnovo_votable: + destroy_votable(chip->not_ok_to_qnovo_votable); +destroy_pt_dis_votable: + destroy_votable(chip->pt_dis_votable); +destroy_disable_votable: + destroy_votable(chip->disable_votable); +cleanup: + platform_set_drvdata(pdev, NULL); + return rc; +} + +static int qnovo_remove(struct platform_device *pdev) +{ + struct qnovo *chip = platform_get_drvdata(pdev); + + class_unregister(&chip->qnovo_class); + power_supply_unreg_notifier(&chip->nb); + destroy_votable(chip->chg_ready_votable); + destroy_votable(chip->not_ok_to_qnovo_votable); + destroy_votable(chip->pt_dis_votable); + destroy_votable(chip->disable_votable); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static void qnovo_shutdown(struct platform_device *pdev) +{ + struct qnovo *chip = platform_get_drvdata(pdev); + + vote(chip->not_ok_to_qnovo_votable, SHUTDOWN_VOTER, true, 0); +} + +static const struct of_device_id match_table[] = { + { .compatible = "qcom,qpnp-qnovo", }, + { }, +}; + +static struct platform_driver qnovo_driver = { + .driver = { + .name = "qcom,qnovo-driver", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, + .probe = qnovo_probe, + .remove = qnovo_remove, + .shutdown = qnovo_shutdown, +}; +module_platform_driver(qnovo_driver); + +MODULE_DESCRIPTION("QPNP Qnovo Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 24e424b5d6b8..018ebd210e31 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -2,6 +2,9 @@ /* Copyright (c) 2016-2017,2019-2020, The Linux Foundation. All rights reserved. */ +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#define pr_fmt(fmt) "SMB2: %s: " fmt, __func__ + #include #include #include @@ -16,11 +19,20 @@ #include #include #include +#include +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#include +#include +#include #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" #include +/*yangfb@bsp, 20180302,enable stm6620 sheepmode */ +#include +#include + #define SMB2_DEFAULT_WPWR_UW 8000000 static struct smb_params v1_params = { @@ -172,7 +184,37 @@ struct smb2 { bool bad_part; }; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +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, 0600); + +#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 static int __weak_chg_icl_ua = 500000; static ssize_t weak_chg_icl_ua_show(struct device *dev, struct device_attribute @@ -258,7 +300,9 @@ static int smb2_parse_dt(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; struct device_node *node = chg->dev->of_node; - int rc, byte_len; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + int byte_len, rc = 0; + enum of_gpio_flags flags; if (!node) { pr_err("device tree node missing\n"); @@ -268,9 +312,177 @@ static int smb2_parse_dt(struct smb2 *chip) chg->reddragon_ipc_wa = of_property_read_bool(node, "qcom,qcs605-ipc-wa"); - chg->step_chg_enabled = of_property_read_bool(node, - "qcom,step-charging-enable"); - +/* david.liu@bsp, 20171023 Battery & Charging porting */ + /* 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-little-cool-low_ma", + chg->temp_littel_cool_low_current, rc, 1); + if (rc < 0) + chg->temp_littel_cool_low_current = 450; + pr_info("temp_littel_cool_low_current:%d\n", + chg->temp_littel_cool_low_current); + 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); + OF_PROP_READ(node, "little-cool-vbat-thr-mv", + chg->temp_littel_cool_voltage, rc, 1); + if (rc < 0) + chg->temp_littel_cool_voltage = 4180; + /* 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 ffc param*/ + OF_PROP_READ(node, "ffc-pre-normal-decidegc", + chg->FFC_TEMP_T1, rc, 1); + OF_PROP_READ(node, "ffc-normal-decidegc", + chg->FFC_TEMP_T2, rc, 1); + OF_PROP_READ(node, "ffc-warm-decidegc", + chg->FFC_TEMP_T3, rc, 1); + OF_PROP_READ(node, "ffc-normal-fcc-ma", + chg->FFC_NOR_FCC, rc, 1); + OF_PROP_READ(node, "ffc-warm-fcc-ma", + chg->FFC_WARM_FCC, rc, 1); + OF_PROP_READ(node, "ffc-normal-cutoff-ma", + chg->FFC_NORMAL_CUTOFF, rc, 1); + OF_PROP_READ(node, "ffc-warm-cutoff-ma", + chg->FFC_WARM_CUTOFF, rc, 1); + OF_PROP_READ(node, "ffc-full-vbat-mv", + chg->FFC_VBAT_FULL, rc, 1); + pr_info("T1:%d, T2:%d, T3:%d, fcc1:%d, fcc1:%d, cut1:%d, cut2:%d,full:%d\n", + chg->FFC_TEMP_T1, chg->FFC_TEMP_T2, chg->FFC_TEMP_T3, + chg->FFC_NOR_FCC, chg->FFC_WARM_FCC, chg->FFC_NORMAL_CUTOFF, + chg->FFC_WARM_CUTOFF, chg->FFC_VBAT_FULL); +/*yangfb@bsp, 20181023 icl set 1A if battery lower than 15%*/ + chg->OTG_ICL_CTRL = of_property_read_bool(node, + "op,otg-icl-ctrl-enable"); + OF_PROP_READ(node, "otg-low-battery-thr", + chg->OTG_LOW_BAT, rc, 1); + if (rc < 0) + chg->OTG_LOW_BAT = -EINVAL; + OF_PROP_READ(node, "otg-low-bat-icl-thr", + chg->OTG_LOW_BAT_ICL, rc, 1); + if (rc < 0) + chg->OTG_LOW_BAT_ICL = -EINVAL; + OF_PROP_READ(node, "otg-normal-bat-icl-thr", + chg->OTG_NORMAL_BAT_ICL, rc, 1); + if (rc < 0) + chg->OTG_NORMAL_BAT_ICL = -EINVAL; + pr_info("OTG_ICL:enable:%d,CapThr:%d,LowThr:%d,NorThr:%d\n", + chg->OTG_ICL_CTRL, + chg->OTG_LOW_BAT, + chg->OTG_LOW_BAT_ICL, + chg->OTG_NORMAL_BAT_ICL); + pr_info("q1:%d,q2:%d,q3:%d,NORMAL_CUTOFF:%d,warm:%d,FULL=%d\n", + chg->FFC_TEMP_T1, + chg->FFC_TEMP_T2, + chg->FFC_TEMP_T3, + chg->FFC_NORMAL_CUTOFF, + chg->FFC_WARM_CUTOFF, + chg->FFC_VBAT_FULL); + + chg->plug_irq = of_get_named_gpio_flags(node, + "op,usb-check", 0, &flags); + chg->vbus_ctrl = of_get_named_gpio_flags(node, + "op,vbus-ctrl-gpio", 0, &flags); + /* 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); + + chg->check_batt_full_by_sw = of_property_read_bool(node, + "op,sw-check-full-enable"); + rc = of_property_read_u32(node, + "op,sw-iterm-ma", + &chg->sw_iterm_ma); + if (rc < 0) + chg->sw_iterm_ma = 150; + pr_info("sw_iterm_ma=%d,check_batt_full_by_sw=%d", + chg->sw_iterm_ma, chg->check_batt_full_by_sw); + /* disable step_chg */ + chg->step_chg_enabled = false; chg->sw_jeita_enabled = of_property_read_bool(node, "qcom,sw-jeita-enable"); @@ -394,6 +606,22 @@ static int smb2_parse_dt(struct smb2 *chip) chg->ufp_only_mode = of_property_read_bool(node, "qcom,ufp-only-mode"); + rc = of_property_match_string(node, "io-channel-names", + "gpio12_voltage"); + if (rc >= 0) { + chg->iio.op_connector_temp_chan = iio_channel_get(chg->dev, + "gpio12_voltage"); + if (IS_ERR(chg->iio.op_connector_temp_chan)) { + rc = PTR_ERR(chg->iio.op_connector_temp_chan); + if (rc != -EPROBE_DEFER) + dev_err(chg->dev, + "op_connector_temp_chan channel unavailable,%ld\n", + rc); + chg->iio.op_connector_temp_chan = NULL; + return rc; + } + } + return 0; } @@ -413,6 +641,10 @@ 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, +/* david.liu@bsp, 20171023 Battery & Charging porting */ + POWER_SUPPLY_PROP_OTG_SWITCH, + POWER_SUPPLY_PROP_HW_DETECT, + POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_TYPEC_SRC_RP, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, @@ -492,6 +724,13 @@ static int smb2_usb_get_prop(struct power_supply *psy, else val->intval = chg->typec_mode; break; +/* david.liu@bsp, 20170414 Add otg switch */ + case POWER_SUPPLY_PROP_OTG_SWITCH: + val->intval = chg->otg_switch; + break; + case POWER_SUPPLY_PROP_HW_DETECT: + val->intval = chg->hw_detect; + break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_PR_NONE; @@ -499,6 +738,8 @@ 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: +/* david.liu@bsp, 20171023 Battery & Charging porting */ + case POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION: if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = 0; else @@ -578,7 +819,8 @@ static int smb2_usb_set_prop(struct power_supply *psy, int rc = 0; mutex_lock(&chg->lock); - if (!chg->typec_present) { +/* david.liu@bsp, 20170417 Fix otg switch */ + if (!chg->typec_present && psp != POWER_SUPPLY_PROP_OTG_SWITCH) { switch (psp) { case POWER_SUPPLY_PROP_MOISTURE_DETECTED: vote(chg->disable_power_role_switch, MOISTURE_VOTER, @@ -596,6 +838,11 @@ 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; +/* david.liu@bsp, 20170414 Add otg switch */ + case POWER_SUPPLY_PROP_OTG_SWITCH: + rc = vote(chg->otg_toggle_votable, USER_VOTER, + val->intval, 0); + break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; @@ -645,6 +892,8 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { +/* david.liu@bsp, 20170414 Add otg switch */ + case POWER_SUPPLY_PROP_OTG_SWITCH: case POWER_SUPPLY_PROP_CTM_CURRENT_MAX: return 1; default: @@ -690,6 +939,7 @@ static enum power_supply_property smb2_usb_port_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, }; + static int smb2_usb_port_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -853,10 +1103,12 @@ static int smb2_usb_main_set_prop(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MAX: - rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval); + rc = smblib_set_charge_param(chg, + &chg->param.fv, val->intval); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval); + rc = smblib_set_charge_param(chg, + &chg->param.fcc, val->intval); break; case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_set_icl_current(chg, val->intval); @@ -978,6 +1230,8 @@ static int smb2_dc_set_prop(struct power_supply *psy, (bool)val->intval, 0); break; case POWER_SUPPLY_PROP_CURRENT_MAX: +/* Yangfb@bsp support ec4016 wipower */ + pr_info("dc set icl:%d\n", val->intval); rc = smblib_set_prop_dc_current_max(chg, val); break; default: @@ -1043,6 +1297,17 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CAPACITY, +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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_CONNECT_DISABLE, + POWER_SUPPLY_PROP_CONNECTER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, @@ -1069,8 +1334,8 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, - POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_OP_DISABLE_CHARGE, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -1083,7 +1348,8 @@ 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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + val->intval = get_prop_batt_status(chg); break; case POWER_SUPPLY_PROP_HEALTH: rc = smblib_get_prop_batt_health(chg, val); @@ -1100,6 +1366,40 @@ 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_OP_DISABLE_CHARGE: + val->intval = chg->chg_disabled; + break; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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_CONNECT_DISABLE: + val->intval = chg->disconnect_vbus; + break; + case POWER_SUPPLY_PROP_CONNECTER_TEMP: + val->intval = chg->connecter_temp; + break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: rc = smblib_get_prop_system_temp_level(chg, val); break; @@ -1180,7 +1480,6 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_COUNTER: case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - case POWER_SUPPLY_PROP_CYCLE_COUNT: case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: @@ -1188,8 +1487,6 @@ static int smb2_batt_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_from_bms(chg, psp, val); - if (!rc) - val->intval *= (-1); break; case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: val->intval = chg->fcc_stepper_enable; @@ -1224,6 +1521,63 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: rc = smblib_set_prop_system_temp_level(chg, val); break; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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 uA\n", val->intval); + if (__debug_mask == PR_OP_DEBUG + || val->intval == 900000) + op_usb_icl_set(chg, val->intval); + break; + case POWER_SUPPLY_PROP_OP_DISABLE_CHARGE: + vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, + (bool)val->intval, 0); + if (val->intval) { + switch_mode_to_normal(); + op_set_fast_chg_allow(chg, false); + } + chg->chg_disabled = (bool)val->intval; + pr_info("user set disable chg %d\n", 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_CONNECT_DISABLE: + op_disconnect_vbus(chg, (bool)val->intval); + break; case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_set_prop_batt_capacity(chg, val); break; @@ -1310,6 +1664,14 @@ 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: +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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_CONNECT_DISABLE: case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: @@ -1317,6 +1679,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_OP_DISABLE_CHARGE: return 1; default: break; @@ -1514,6 +1877,33 @@ static int smb2_configure_typec(struct smb_charger *chg) "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } +/* david.liu@bsp, 20170414 Add otg switch */ + 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 @@ -1643,7 +2033,10 @@ 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; - +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); @@ -1665,7 +2058,19 @@ static int smb2_init_hw(struct smb2 *chip) pr_err("Couldn't set otg soft start rc=%d\n", rc); return rc; } - +/* Yangfb@bsp support ec4016 wipower */ + /* set a AICL THR for DCIN */ + rc = smblib_write(chg, DCIN_AICL_REF_SEL_CFG_REG, 0x3); + if (rc < 0) { + pr_err("Couldn't write DCIN_AICL_REF_SEL_CFG_REG\n"); + return rc; + } + /* disable qcom wipower*/ + rc = smblib_write(chg, WI_PWR_OPTIONS_REG, 0); + if (rc < 0) { + pr_err("Couldn't disable qcom wipower\n"); + return rc; + } /* set OTG current limit */ rc = smblib_set_charge_param(chg, &chg->param.otg_cl, (chg->wa_flags & OTG_WA) ? @@ -1683,7 +2088,9 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } - smblib_rerun_apsd_if_required(chg); + /* Disable APSD rerun for CDP case */ + if (!(stat & (CDP_CHARGER_BIT | SDP_CHARGER_BIT))) + smblib_rerun_apsd_if_required(chg); /* clear the ICL override if it is set */ if (smblib_icl_override(chg, false) < 0) { @@ -1692,17 +2099,21 @@ 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 */ +/* david.liu@bsp, 20171023 Battery & Charging porting */ 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, + true, 0); + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + true, 0); vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, chip->dt.hvdcp_disable, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, @@ -1711,6 +2122,25 @@ static int smb2_init_hw(struct smb2 *chip) true, 0); vote(chg->pd_disallowed_votable_indirect, PD_NOT_SUPPORTED_VOTER, chip->dt.no_pd, 0); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + /* 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: * start from min and AICL ADC disable @@ -1786,6 +2216,15 @@ static int smb2_init_hw(struct smb2 *chip) "Couldn't configure VBUS for SW control rc=%d\n", rc); 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; @@ -1996,7 +2435,6 @@ static int smb2_post_init(struct smb2 *chip) return rc; } } - rerun_election(chg->usb_irq_enable_votable); return 0; @@ -2062,6 +2500,8 @@ static int smb2_chg_config_init(struct smb2 *chip) return -EINVAL; } +/* david.liu@bsp, 20170317 Improve coldboot time */ + pr_info("PMI8998 Revision=0x%x\n", pmic_rev_id->rev4); return 0; } @@ -2238,9 +2678,10 @@ static struct smb_irq_info smb2_irqs[] = { .name = "aicl-fail", .handler = smblib_handle_debug, }, +/* david.liu@bsp, 20171023 Battery & Charging porting */ [AICL_DONE_IRQ] = { .name = "aicl-done", - .handler = smblib_handle_debug, + .handler = smblib_handle_aicl_done, }, [HIGH_DUTY_CYCLE_IRQ] = { .name = "high-duty-cycle", @@ -2441,6 +2882,108 @@ static void smb2_create_debugfs(struct smb2 *chip) #endif +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#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 op_config_usb_temperature_adc(struct smb_charger *chip) +{ + if (IS_ERR_OR_NULL(chip->pinctrl)) { + chip->pinctrl = devm_pinctrl_get(chip->dev); + if (IS_ERR_OR_NULL(chip->pinctrl)) { + dev_err(chip->dev, + "Unable to acquire pinctrl\n"); + chip->pinctrl = NULL; + return -EINVAL; + } + } + + chip->usb_temperature_default = + pinctrl_lookup_state(chip->pinctrl, "op_usb_temp_adc_default"); + if (IS_ERR_OR_NULL(chip->usb_temperature_default)) { + dev_err(chip->dev, + "Can not lookup op_usb_temp_adc_default\n"); + devm_pinctrl_put(chip->pinctrl); + chip->pinctrl = NULL; + return PTR_ERR(chip->usb_temperature_default); + } + + if (pinctrl_select_state(chip->pinctrl, + chip->usb_temperature_default) < 0) + dev_err(chip->dev, "pinctrl set op_usb_temp_adc_default fail\n"); + + return 0; +} + +static irqreturn_t op_usb_plugin_irq_handler(int irq, void *dev_id) +{ + schedule_work(&g_chip->otg_switch_work); + return IRQ_HANDLED; +} + +static void request_plug_irq(struct smb_charger *chip) +{ + int ret; + + if (!gpio_is_valid(chip->plug_irq)) + return; + ret = gpio_request(chip->plug_irq, "op_usb_plug"); + if (ret) { + pr_err("request failed,gpio:%d ret=%d\n", chip->plug_irq, ret); + return; + } + gpio_direction_input(chip->plug_irq); + ret = request_irq(gpio_to_irq(chip->plug_irq), + op_usb_plugin_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "op_usb_plug", chip); + if (ret < 0) { + pr_err("request usb_plug irq failed.\n"); + return; + } + enable_irq_wake(gpio_to_irq(chip->plug_irq)); + pr_info("request usb_plug irq success\n"); + /*connect with usb cable when reboot, give a vote 1*/ + if (!gpio_get_value(chip->plug_irq)) { + pr_info("%s:reboot time hw detect gpio low, vote 1\n", + __func__); + vote(chip->otg_toggle_votable, HW_DETECT_VOTER, 1, 0); + chip->hw_detect = 1; + chip->pre_cable_pluged = 1; + + ret = plugin_update(chip); + pr_info("%s:hw_detect=%d and report rc: %d\n", + __func__, chip->hw_detect, ret); + } else + chip->pre_cable_pluged = 0; +} + +void request_vbus_ctrl_gpio(struct smb_charger *chg) +{ + int ret; + + if (!gpio_is_valid(chg->vbus_ctrl)) + return; + ret = gpio_request(chg->vbus_ctrl, "VbusCtrl"); + if (ret) + pr_err("request failed,gpio:%d ret=%d\n", chg->vbus_ctrl, ret); +} + static int smb2_probe(struct platform_device *pdev) { struct smb2 *chip; @@ -2448,12 +2991,15 @@ static int smb2_probe(struct platform_device *pdev) int rc = 0; union power_supply_propval val; int usb_present, batt_present, batt_health, batt_charge_type; + struct msm_bus_scale_pdata *pdata; chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chg = &chip->chg; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + g_chip = chg; chg->dev = &pdev->dev; chg->param = v1_params; chg->debug_mask = &__debug_mask; @@ -2481,7 +3027,7 @@ static int smb2_probe(struct platform_device *pdev) rc = smb2_parse_dt(chip); if (rc < 0) { pr_err("Couldn't parse device tree rc=%d\n", rc); - goto cleanup; + return rc; } rc = smblib_init(chg); @@ -2493,6 +3039,14 @@ static int smb2_probe(struct platform_device *pdev) /* set driver data before resources request it */ platform_set_drvdata(pdev, chip); +/* david.liu@bsp, 20171228 Fix abnormal animation */ + op_charge_info_init(chg); + pdata = msm_bus_cl_get_pdata(pdev); + if (!pdata) + pr_err("Fail to get bus data\n"); + else + chg->bus_client = msm_bus_scale_register_client(pdata); + rc = smb2_init_vbus_regulator(chip); if (rc < 0) { pr_err("Couldn't initialize vbus regulator rc=%d\n", @@ -2613,8 +3167,24 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } batt_charge_type = val.intval; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#ifdef CONFIG_PROC_FS + if (!proc_create("ship_mode", 0644, NULL, + &proc_ship_mode_operations)) + pr_err("Failed to register proc interface\n"); +#endif + + if (usb_present) { + chg->boot_usb_present = true; + } + if (!usb_present && chg->vbus_present) + op_handle_usb_plugin(chg); device_init_wakeup(chg->dev, true); + chg->probe_done = true; + request_vbus_ctrl_gpio(chg); + op_config_usb_temperature_adc(chg); + request_plug_irq(chg); pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n", usb_present, chg->real_charger_type, @@ -2649,11 +3219,15 @@ 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); +/* david.liu@bsp, 20170330 Fix system crash */ + if (chg->usb_psy) + power_supply_unregister(chg->usb_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); sysfs_remove_groups(&chg->dev->kobj, smb2_groups); debugfs_remove_recursive(chip->dfs_root); platform_set_drvdata(pdev, NULL); @@ -2664,6 +3238,19 @@ static void smb2_shutdown(struct platform_device *pdev) { struct smb2 *chip = platform_get_drvdata(pdev); struct smb_charger *chg = &chip->chg; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#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 101f21fcbeb3..d14f37d249d0 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -2,6 +2,9 @@ /* Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. */ +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#define pr_fmt(fmt) "SMBLIB: %s: " fmt, __func__ + #include #include #include @@ -17,6 +20,60 @@ #include "step-chg-jeita.h" #include "storm-watch.h" +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "op_charge.h" + + +#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); +static int op_check_battery_temp(struct smb_charger *chg); +static void op_clean_enhache_status(void); + #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ __func__, ##__VA_ARGS__) \ @@ -31,6 +88,14 @@ __func__, ##__VA_ARGS__); \ } while (0) +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +static BLOCKING_NOTIFIER_HEAD(typec_cc_chain); +static int cc_notifier_call_chain(unsigned long val); + +/* infi@bsp, 2018/08/08 add for support audio_adaptor trigger when reboot */ +bool audio_adapter_flag; +EXPORT_SYMBOL(audio_adapter_flag); + static bool is_secure(struct smb_charger *chg, int addr) { if (addr == SHIP_MODE_REG || addr == FREQ_CLK_DIV_REG) @@ -241,7 +306,8 @@ static const struct apsd_result const smblib_apsd_results[] = { [FLOAT] = { .name = "FLOAT", .bit = FLOAT_CHARGER_BIT, - .pst = POWER_SUPPLY_TYPE_USB_FLOAT +/* yangfb@bsp, 20160926 Add dash charging */ + .pst = POWER_SUPPLY_TYPE_USB_DCP }, [HVDCP2] = { .name = "HVDCP2", @@ -267,10 +333,11 @@ 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)) +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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", @@ -386,6 +453,8 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; int irq = chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_info("suspend=%d\n", suspend); if (suspend && irq) { if (chg->usb_icl_change_irq_enabled) { @@ -413,7 +482,8 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; - +/* Yangfb@bsp support ec4016 wipower */ + pr_info("%d\n", suspend); rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0); if (rc < 0) @@ -615,6 +685,14 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) chg->real_charger_type = apsd_result->pst; } +/* Yangfb@bsp add to fix fastcharge test not pass */ + if (chg->dash_on) { + chg->real_charger_type = POWER_SUPPLY_TYPE_DASH; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_DASH; + } else { + chg->usb_psy_desc.type = apsd_result->pst; + chg->real_charger_type = apsd_result->pst; + } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); return apsd_result; @@ -917,6 +995,62 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +void op_bus_vote(int disable) +{ + int ret; + + if (!g_chg) + return; + + ret = msm_bus_scale_client_update_request(g_chg->bus_client, disable); + + if (ret) { + pr_err("%s: failed: bus_client_handle=0x%x, vote=%d, err=%d\n", + __func__, g_chg->bus_client, disable, ret); + } + pr_info("enable =%d\n", disable); +} + +int op_usb_icl_set(struct smb_charger *chg, int icl_ua) +{ + int rc = 0; + bool override; + + pr_info("icl_ua=%d\n", 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) { int rc; @@ -943,6 +1077,8 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; bool override; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_info("icl_ua=%d\n", icl_ua); /* suspend and return if 25mA or less is requested */ if (icl_ua <= USBIN_25MA) @@ -954,13 +1090,19 @@ 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)) { +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); @@ -1152,6 +1294,9 @@ static int __smblib_set_prop_typec_power_role(struct smb_charger *chg, smblib_err(chg, "power role %d not supported\n", val->intval); return -EINVAL; } + /* david.liu@bsp, 20170414 Add otg switch */ + if (!chg->otg_switch) + power_role = UFP_EN_CMD_BIT; if (power_role != TYPEC_DISABLE_CMD_BIT) { if (chg->ufp_only_mode) @@ -1241,13 +1386,34 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, return smblib_set_dc_suspend(chg, (bool)suspend); } +/*infi@bsp, 2018/07/10 Add otg toggle vote optimize otg_switch set flow*/ +static int smblib_otg_toggle_vote_callback(struct votable *votable, + void *data, int value, const char *client) +{ + struct smb_charger *chg = data; + int rc = 0; + + if (value < 0) + value = 0; + + rc = op_set_prop_otg_switch(chg, (bool)value); + if (rc < 0) { + smblib_err(chg, "Can not set otg switch,value=%d, rc=%d\n", + value, rc); + return rc; + } + + return rc; +} + static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, int icl_ua, const char *client) { struct smb_charger *chg = data; int rc = 0; bool suspend; - +/* Yangfb@bsp support ec4016 wipower */ + pr_info("dc icl set %d\n", icl_ua); if (icl_ua < 0) { smblib_dbg(chg, PR_MISC, "No Voter hence suspending\n"); icl_ua = 0; @@ -1291,6 +1457,8 @@ static int smblib_awake_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_info("set awake=%d\n", awake); if (awake) pm_stay_awake(chg->dev); else @@ -1305,6 +1473,8 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, struct smb_charger *chg = data; int rc; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_err("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); @@ -1968,9 +2138,8 @@ 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); @@ -1983,21 +2152,8 @@ 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; - } - } +/* david.liu@bsp, 20171023 Battery & Charging porting */ + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) @@ -2010,8 +2166,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; } @@ -2049,6 +2203,51 @@ int smblib_get_prop_input_current_limited(struct smb_charger *chg, return 0; } +int smblib_get_prop_batt_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +int smblib_get_prop_batt_current_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + return rc; +} + +int smblib_get_prop_batt_temp(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + +/* david.liu@bsp, 20171122 Fix fake battery temperature */ + if (chg->use_fake_temp) { + val->intval = chg->fake_temp; + return 0; + } + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_TEMP, val); + return rc; +} + int smblib_get_prop_batt_charge_done(struct smb_charger *chg, union power_supply_propval *val) { @@ -2126,6 +2325,143 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#define POWER_ROLE_BIT (DFP_EN_CMD_BIT | UFP_EN_CMD_BIT) +int op_set_prop_otg_switch(struct smb_charger *chg, + bool enable) +{ + 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 = enable; + + 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 op_set_otg_switch(struct smb_charger *chg, bool enable) +{ + 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 = enable; + + 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) { @@ -2217,6 +2553,10 @@ int smblib_rerun_aicl(struct smb_charger *chg) return rc; smblib_dbg(chg, PR_MISC, "re-running AICL\n"); + /*yangfb@bsp enable aicl_rerun before rerurn aicl*/ + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + USBIN_AICL_RERUN_EN_BIT, + USBIN_AICL_RERUN_EN_BIT); rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_icl_ua); if (rc < 0) { @@ -2434,7 +2774,8 @@ int smblib_set_prop_dc_current_max(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - +/* Yangfb@bsp support ec4016 wipower */ + pr_info("%s,%d\n", __func__, val->intval); rc = vote(chg->dc_icl_votable, USER_VOTER, true, val->intval); return rc; } @@ -2470,6 +2811,12 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, return rc; } +/* david.liu@bsp, 20161122 Fix power off charging loop */ + 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", @@ -2529,14 +2876,18 @@ int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg, int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { +/* yangfb@bsp, 20161229 Vbus switch uV to mV */ + 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); +/* yangfb@bsp, 20161229 Vbus switch uV to mV */ + 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, @@ -2702,7 +3053,11 @@ 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); +/* david.liu@bsp, 201710503 Fix slow SRC & SNK */ + if (chg->pd_disabled) + val->intval = 0; + else + val->intval = get_effective_result(chg->pd_allowed_votable); return 0; } @@ -2878,6 +3233,8 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, { int rc = 0; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_err("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) { @@ -2998,6 +3355,12 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) bool orientation, sink_attached, hvdcp; u8 stat; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + rc = 0; + if (chg->pd_disabled) + return rc; + + pr_info("set pd_active=%d\n", pd_active); chg->pd_active = pd_active; if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; @@ -3251,6 +3614,9 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, { int rc = 0; +/* david.liu@bsp, 20170202 Add pd_disabled */ + if (chg->pd_disabled) + return rc; if (chg->pd_hard_reset == val->intval) return rc; @@ -3501,6 +3867,20 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) } stat = stat & BATTERY_CHARGER_STATUS_MASK; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (stat == TERMINATE_CHARGE) { + /* charge done, disable charge in software also */ + chg->chg_done = true; + pr_info("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; } @@ -3552,6 +3932,12 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) int rc; u8 stat = 0, max_pulses = 0; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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; @@ -3606,9 +3992,44 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) smblib_rerun_apsd(chg); } +/* david.liu@bsp, 20171023 Battery & Charging porting */ + smblib_err(chg, "DEBUG: RESET STORM COUNT FOR POWER_OK\n"); return IRQ_HANDLED; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +char dump_val[2048]; +static inline void op_dump_reg(struct smb_charger *chip, + u16 addr_start, u16 addr_end) +{ + u8 reg = 0; + u16 addr; + char reg_val[19]; + int rc; + + memset(dump_val, 0, sizeof(dump_val)); + for (addr = addr_start; addr <= addr_end; addr++) { + memset(reg_val, 0, sizeof(reg_val)); + rc = smblib_read(chip, addr, ®); + if (rc < 0) + smblib_err(chip, "op_dump_reg read error addr=%d rc=%d\n", + addr, rc); + 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) { if (vbus_rising) { @@ -3630,6 +4051,16 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) struct smb_irq_data *data; struct storm_watch *wdata; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); @@ -3679,6 +4110,17 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) struct smb_irq_data *data; struct storm_watch *wdata; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); @@ -3689,6 +4131,17 @@ 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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + chg->vbus_present = vbus_rising; + if (last_vbus_present != chg->vbus_present) { + if (chg->vbus_present) { + pr_info("acquire chg_wake_lock\n"); + __pm_stay_awake(chg->chg_wake_lock); + } else { + pr_info("release chg_wake_lock\n"); + __pm_relax(chg->chg_wake_lock); + } + } if (vbus_rising) { if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE) { chg->fake_usb_insertion = true; @@ -3703,6 +4156,16 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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 (gpio_is_valid(chg->vbus_ctrl)) + schedule_delayed_work(&chg->connecter_check_work, + msecs_to_jiffies(200)); /* Schedule work to enable parallel charger */ vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); schedule_delayed_work(&chg->pl_enable_work, @@ -3738,6 +4201,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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if (last_vbus_present != chg->vbus_present) + op_handle_usb_removal(chg); } if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) @@ -3746,6 +4212,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"); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); + } + } + } + + pr_err("IRQ: %s\n", + vbus_rising ? "attached" : "detached"); } irqreturn_t smblib_handle_usb_plugin(int irq, void *data) @@ -3762,6 +4243,15 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) return IRQ_HANDLED; } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +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 irqreturn_t smblib_handle_icl_change(int irq, void *data) @@ -4003,7 +4493,8 @@ 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, USBIN_500MA + USBIN_25MA); vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); break; case POWER_SUPPLY_TYPE_USB_CDP: @@ -4012,6 +4503,7 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) case POWER_SUPPLY_TYPE_USB_DCP: typec_mode = smblib_get_prop_typec_mode(chg); rp_ua = get_rp_based_dcp_current(chg, typec_mode); +/* david.liu@bsp, 20171023 Battery & Charging porting */ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); break; case POWER_SUPPLY_TYPE_USB_FLOAT: @@ -4019,7 +4511,9 @@ 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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + vote(chg->usb_icl_votable, + LEGACY_UNKNOWN_VOTER, true, DCP_CURRENT_UA); break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: @@ -4063,9 +4557,38 @@ static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable); } +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#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; +} + #define HVDCP_DET_MS 2500 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { +/* david.liu@bsp, 20171023 Battery & Charging porting */ + int temp_region = 0, current_limit_ua = 0; const struct apsd_result *apsd_result; if (!rising) @@ -4101,7 +4624,65 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } - smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", +/* david.liu@bsp, 20171023 Battery & Charging porting */ + if ((apsd_result->bit) == SDP_CHARGER_BIT) + current_limit_ua = SDP_CURRENT_UA; + else if ((apsd_result->bit) == CDP_CHARGER_BIT) + current_limit_ua = CDP_CURRENT_UA; + else if ((apsd_result->bit) == DCP_CHARGER_BIT) + current_limit_ua = DCP_CURRENT_UA; + else if ((apsd_result->bit) == FLOAT_CHARGER_BIT) { + if (chg->usb_type_redet_done) + current_limit_ua = DCP_CURRENT_UA; + else + current_limit_ua = TYPEC_DEFAULT_CURRENT_UA; + } else if ((apsd_result->bit) == OCP_CHARGER_BIT) + current_limit_ua = DCP_CURRENT_UA; + + 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); + smblib_set_prop_charge_parameter_set(chg); + } + + 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) { + 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 { + if (chg->probe_done) { + if (apsd_result->bit == FLOAT_CHARGER_BIT) + schedule_delayed_work( + &chg->check_switch_dash_work, + msecs_to_jiffies(500)); + 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); } @@ -4114,6 +4695,12 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) if (chg->fake_usb_insertion) return IRQ_HANDLED; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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) { @@ -4121,6 +4708,8 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + pr_info("APSD_STATUS=0x%02x\n", stat); if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (stat & APSD_DTC_STATUS_DONE_BIT) @@ -4335,8 +4924,15 @@ static void typec_sink_insertion(struct smb_charger *chg) } typec_mode = smblib_get_prop_typec_mode(chg); - if (typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) + +/*2018/06/14 @bsp add for support notify audio adapter switch*/ + if (typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { chg->is_audio_adapter = true; + audio_adapter_flag = true; + pr_info("Type-C %s detected,notify!\n", + smblib_typec_mode_name[chg->typec_mode]); + cc_notifier_call_chain(1); + } /* when a sink is inserted we should not wait on hvdcp timeout to * enable pd @@ -4388,7 +4984,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->hvdcp_detect_work); /* reset input current limit voters */ - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); +/*Yangfb@bsp ,20180202,set icl 500mA after charger removed*/ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000); vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); @@ -4487,14 +5084,20 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", rc); - if (chg->is_audio_adapter) +/*2018/06/14 @bsp add for support notify audio adapter switch*/ + if (chg->is_audio_adapter) { /* wait for the audio driver to lower its en gpio */ msleep(*chg->audio_headset_drp_wait_ms); - - chg->is_audio_adapter = false; + chg->is_audio_adapter = false; + audio_adapter_flag = false; + pr_info("Type-C removal, audio_adapter_present=(%d),notify!\n", + chg->is_audio_adapter); + cc_notifier_call_chain(0); + } /* enable DRP */ - val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; + val.intval = chg->otg_switch ? + POWER_SUPPLY_TYPEC_PR_DUAL : POWER_SUPPLY_TYPEC_PR_SINK; rc = smblib_set_prop_typec_power_role(chg, &val); if (rc < 0) smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc); @@ -4604,6 +5207,37 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); } +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +int register_cc_notifier_client(struct notifier_block *nb) +{ + int ret; + + ret = blocking_notifier_chain_register(&typec_cc_chain, nb); + if (ret) + pr_err("Cann't register cc notifier, ret = %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(register_cc_notifier_client); + +int unregister_cc_notifier_client(struct notifier_block *nb) +{ + int ret; + + ret = blocking_notifier_chain_unregister(&typec_cc_chain, nb); + if (ret) + pr_err("Cann't unregister cc notifier, ret = %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(unregister_cc_notifier_client); + +static int cc_notifier_call_chain(unsigned long val) +{ + return (blocking_notifier_call_chain(&typec_cc_chain, val, NULL) + == NOTIFY_BAD) ? -EINVAL : 0; +} + static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) { int typec_mode; @@ -4628,7 +5262,12 @@ static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) smblib_dbg(chg, PR_MISC, "TypeC removal\n"); smblib_handle_typec_removal(chg); } - + /* if audio adapter connectted,do not set ICL 0*/ + if (audio_adapter_flag) { + smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n", + smblib_typec_mode_name[chg->typec_mode]); + return; + } /* suspend usb if sink */ if ((chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) && chg->typec_present) @@ -4666,6 +5305,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; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) + return IRQ_HANDLED; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { cancel_delayed_work_sync(&chg->uusb_otg_work); @@ -4740,9 +5383,17 @@ 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; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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; @@ -4762,32 +5413,18 @@ 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)); - } +/* david.liu@bsp, 20171023 Battery & Charging porting */ + /*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; @@ -4811,196 +5448,2775 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) return IRQ_HANDLED; } -/************** - * Additional USB PSY getters/setters - * that call interrupt functions - ***************/ - -int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg, - union power_supply_propval *val) +/* david.liu@bsp, 20171023 Battery & Charging porting */ +static void op_get_aicl_work(struct work_struct *work) { - val->intval = chg->pr_swap_in_progress; - return 0; -} + struct smb_charger *chg = container_of(work, struct smb_charger, + get_aicl_work); + int rc, settled_ua; -int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, - const union power_supply_propval *val) -{ - int rc; + rc = smblib_get_charge_param(chg, &chg->param.icl_stat, + &settled_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); + return; + } - chg->pr_swap_in_progress = val->intval; - /* - * call the cc changed irq to handle real removals while - * PR_SWAP was in progress - */ - smblib_usb_typec_change(chg); - rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, - val->intval ? TCC_DEBOUNCE_20MS_BIT : 0); - if (rc < 0) - smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc); - return 0; + pr_info("AICL result=%dmA\n", settled_ua / 1000); } +static bool is_usb_present(struct smb_charger *chg); -/*************** - * Work Queues * - ***************/ -static void smblib_uusb_otg_work(struct work_struct *work) +static void op_dash_check_work(struct work_struct *work) { - struct smb_charger *chg = container_of(work, struct smb_charger, - uusb_otg_work.work); - int rc; - u8 stat; - bool otg; + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, dash_check_work); + int soc, temp; - rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc); - goto out; + chg->dash_check_count++; + if (!is_usb_present(chg)) { + chg->dash_check_count = 0; + return; + } + if (!chg->dash_present) { + chg->dash_check_count = 0; + return; + } + if (op_get_fastchg_ing(chg)) { + chg->dash_check_count = 0; + return; + } + if (chg->non_stand_chg_count < 300) { + schedule_delayed_work(&chg->dash_check_work, + msecs_to_jiffies(TIME_1000MS)); + return; } - otg = !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)); - extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, otg); - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n", - stat, otg); - power_supply_changed(chg->usb_psy); - -out: - vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0); + chg->dash_check_count = 0; + soc = get_prop_batt_capacity(chg); + temp = get_prop_batt_temp(chg); + if (soc >= 1 && temp < 410) { + chg->re_trigr_dash_done = true; + set_usb_switch(chg, false); + set_usb_switch(chg, true); + } } +static int get_usb_temp(struct smb_charger *chg); -static void smblib_hvdcp_detect_work(struct work_struct *work) +static void op_connect_temp_check_work(struct work_struct *work) { - struct smb_charger *chg = container_of(work, struct smb_charger, - hvdcp_detect_work.work); + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, connecter_check_work); - vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, - false, 0); - power_supply_changed(chg->usb_psy); + if (!chg->vbus_present) + return; + chg->connecter_temp = get_usb_temp(chg); + if (chg->connecter_temp < 68) + schedule_delayed_work(&chg->connecter_check_work, + msecs_to_jiffies(200)); + else + op_disconnect_vbus(chg, true); } -static void bms_update_work(struct work_struct *work) +irqreturn_t smblib_handle_aicl_done(int irq, void *data) { - struct smb_charger *chg = container_of(work, struct smb_charger, - bms_update_work); + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; - smblib_suspend_on_debug_battery(chg); + cancel_work_sync(&chg->get_aicl_work); + schedule_work(&chg->get_aicl_work); - if (chg->batt_psy) - power_supply_changed(chg->batt_psy); + return IRQ_HANDLED; } -static void pl_update_work(struct work_struct *work) +int op_get_aicl_result(struct smb_charger *chg) { - struct smb_charger *chg = container_of(work, struct smb_charger, - pl_update_work); + int icl_ma, rc; - smblib_stat_sw_override_cfg(chg, false); + 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 void clear_hdc_work(struct work_struct *work) +static int get_property_from_fg(struct smb_charger *chg, + enum power_supply_property prop, int *val) { - struct smb_charger *chg = container_of(work, struct smb_charger, - clear_hdc_work.work); + int rc; + union power_supply_propval ret = {0, }; - chg->is_hdc = false; - if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) - enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + 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 void rdstd_cc2_detach_work(struct work_struct *work) +static int set_property_on_fg(struct smb_charger *chg, + enum power_supply_property prop, int val) { int rc; - u8 stat4, stat5; - struct smb_charger *chg = container_of(work, struct smb_charger, - rdstd_cc2_detach_work); - - if (!chg->cc2_detach_wa_active) - return; + union power_supply_propval ret = {0, }; - /* - * WA steps - - * 1. Enable both UFP and DFP, wait for 10ms. - * 2. Disable DFP, wait for 30ms. - * 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS - * and TIMER_STAGE bits are gone, otherwise repeat all by - * work rescheduling. - * Note, work will be cancelled when USB_PLUGIN rises. - */ + if (!chg->bms_psy) + chg->bms_psy = power_supply_get_by_name("bms"); - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - UFP_EN_CMD_BIT | DFP_EN_CMD_BIT, - UFP_EN_CMD_BIT | DFP_EN_CMD_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc); - return; + 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; } - usleep_range(10000, 11000); - - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - UFP_EN_CMD_BIT | DFP_EN_CMD_BIT, - UFP_EN_CMD_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc); - return; - } + return rc; +} - usleep_range(30000, 31000); +static int op_charging_en(struct smb_charger *chg, bool en) +{ + int rc; - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return; + pr_info("enable=%d\n", en); + if (chg->chg_disabled && en) { + pr_info("chg_disabled just return\n"); + return 0; } - rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + en ? CHARGING_ENABLE_CMD_BIT : 0); if (rc < 0) { - smblib_err(chg, - "Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc); - return; + pr_err("Couldn't %s charging rc=%d\n", + en ? "enable" : "disable", rc); + return rc; } - if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT) - || (stat5 & TIMER_STAGE_2_BIT)) { - smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n", - (int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT), - (int)(stat5 & TIMER_STAGE_2_BIT)); - goto rerun; - } + return 0; +} - smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n"); - chg->cc2_detach_wa_active = false; - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - EXIT_SNK_BASED_ON_CC_BIT, 0); - smblib_reg_block_restore(chg, cc2_detach_settings); +static bool is_usb_present(struct smb_charger *chg) +{ + int rc = 0; + u8 stat; - /* - * Mutex acquisition deadlock can happen while cancelling this work - * during pd_hard_reset from the function smblib_cc2_sink_removal_exit - * which is called in the same lock context that we try to acquire in - * this work routine. - * Check if this work is running during pd_hard_reset and skip holding - * mutex if lock is already held. - */ - if (!chg->in_chg_lock) - mutex_lock(&chg->lock); - smblib_usb_typec_change(chg); - if (!chg->in_chg_lock) - mutex_unlock(&chg->lock); + 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); +} - return; +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; +} -rerun: - schedule_work(&chg->rdstd_cc2_detach_work); +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 void smblib_otg_oc_exit(struct smb_charger *chg, bool success) +static bool get_prop_fast_switch_to_normal(struct smb_charger *chg) { - int rc; + 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; +} - chg->otg_attempts = 0; - if (!success) { +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 (chg->chg_disabled) + return false; + 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; +} + +void op_switch_normal_set(void) +{ + if (!g_chg) + return; + pr_info("op_switch_normal_set\n"); + vote(g_chg->usb_icl_votable, + DCP_VOTER, true, 2000 * 1000); + vote(g_chg->fv_votable, + DEFAULT_VOTER, true, 4500 * 1000); + vote(g_chg->fcc_votable, + DEFAULT_VOTER, true, 1000 * 1000); + g_chg->ffc_status = FFC_FAST; +} +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); + op_clean_enhache_status(); + + 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->ffc_status = FFC_DEFAULT; + 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; + chg->dash_check_count = 0; + chg->ffc_count = 0; + chg->chg_disabled = 0; + vote(chg->fcc_votable, + DEFAULT_VOTER, true, SDP_CURRENT_UA); + set_sdp_current(chg, USBIN_500MA); + vote(chg->chg_disable_votable, + FORCE_RECHARGE_VOTER, false, 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) { + op_dump_regs(g_chg); + 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("en=%d\n", 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; +} + +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; +} + +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; +} + +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 void op_clean_enhache_status(void) +{ + if (fast_charger && fast_charger->clean_enhache) + return fast_charger->clean_enhache(); + pr_err("no fast_charger register found\n"); +} + +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, DCP_CURRENT_UA); + } + 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->chg_disabled) { + 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 + || (!is_usb_present(chg))) { + 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)); + smblib_rerun_aicl(chg); + } + } + 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, soc; + 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_DASH; + vote(g_chg->usb_icl_votable, PD_VOTER, true, + DEFAULT_WALL_CHG_MA * 1000); + soc = get_prop_batt_capacity(g_chg); + if (g_chg->boot_usb_present && !soc) + schedule_delayed_work(&g_chg->dash_check_work, + msecs_to_jiffies(2000)); + } + if (g_chg->dash_present) { + g_chg->real_charger_type = POWER_SUPPLY_TYPE_DASH; + g_chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_DASH; + } + 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; +} + +/*yangfb@bsp, 20181023 icl set 1A if battery lower than 15%*/ +static void op_otg_icl_contrl(struct smb_charger *chg) +{ + int cap, rc; + static int icl_pre, icl; + + if (!chg->OTG_ICL_CTRL) + return; + cap = get_prop_batt_capacity(chg); + if (cap <= chg->OTG_LOW_BAT) { + icl = chg->OTG_LOW_BAT_ICL; + } else if (cap > chg->OTG_LOW_BAT + && icl_pre == chg->OTG_LOW_BAT_ICL) { + icl = chg->OTG_NORMAL_BAT_ICL; + } + + if (icl_pre == icl) + return; + pr_info("cap=%d,icl=%d,icl_pre=%d\n", cap, icl, icl_pre); + rc = smblib_set_charge_param(chg, &chg->param.otg_cl, + icl); + if (rc < 0) + smblib_err(chg, "Couldn't set otg icl rc=%d\n", rc); + icl_pre = icl; +} + +#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 = 0, 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 = 0, 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 = 0, 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 = 0, 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 = 0, 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 = 0, 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 = 0, 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 == 100)) { + 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; + if (batt_status == POWER_SUPPLY_STATUS_FULL + && !(chg->chg_done || chg->recharge_status)) + return POWER_SUPPLY_STATUS_CHARGING; + 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); + + if (chg->ffc_status != FFC_DEFAULT) + return; + 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); +} + +static void op_temp_region_charging_en(struct smb_charger *chg, int vbatmax) +{ + int vbat_mv = 0; + union power_supply_propval pval; + + smblib_get_prop_batt_voltage_now(chg, &pval); + vbat_mv = pval.intval/1000; + pr_info("%s vbat_mv =%d\n",__func__, vbat_mv); + if (vbat_mv < vbatmax) + return; + op_charging_en(chg, false); + chg->chg_done = true; +} + +/* 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); + op_temp_region_charging_en(chg, + chg->vbatmax[BATT_TEMP_LITTLE_COLD]); + 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 > chg->temp_littel_cool_voltage) { + set_chg_ibat_vbat_max(chg, chg->temp_littel_cool_low_current, + chg->vbatmax[BATT_TEMP_LITTLE_COOL]); + chg->temp_littel_cool_set_current = 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 = 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); + op_temp_region_charging_en(chg, + chg->vbatmax[BATT_TEMP_WARM]); + 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 = 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 = 0, chger_stat = 0, pwer_source_stats = 0; + + 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; +} + +static int msm_drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + struct smb_charger *chip = + container_of(self, struct smb_charger, msm_drm_notifier); + int *blank; + + if (!evdata) + return 0; + + if (evdata->id != MSM_DRM_PRIMARY_DISPLAY) + return 0; + if (event != MSM_DRM_EARLY_EVENT_BLANK) + return 0; + + if (evdata && evdata->data && chip) { + blank = evdata->data; + if (*blank == MSM_DRM_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 == MSM_DRM_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; +} + +static void ffc_exit(struct smb_charger *chg) { + int icharging, batt_volt, temp; + + if (chg->ffc_status == FFC_DEFAULT) { + chg->ffc_count = 0; + return; + } + batt_volt = get_prop_batt_voltage_now(chg) / 1000; + icharging = get_prop_batt_current_now(chg) / 1000; + temp = get_prop_batt_temp(chg); + + if (chg->ffc_status == FFC_NOR_TAPER + || chg->ffc_status == FFC_WARM_TAPER) { + if (temp > chg->FFC_TEMP_T1 + && temp < chg->FFC_TEMP_T2) { + chg->ffc_status = FFC_NOR_TAPER; + vote(g_chg->fcc_votable, + DEFAULT_VOTER, true, chg->FFC_NOR_FCC * 1000); + } else if (temp >= chg->FFC_TEMP_T2 + && temp < chg->FFC_TEMP_T3) { + chg->ffc_status = FFC_WARM_TAPER; + vote(g_chg->fcc_votable, + DEFAULT_VOTER, true, chg->FFC_WARM_FCC * 1000); + } else { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + } + } + + if (chg->ffc_status == FFC_FAST) { + if (batt_volt >= chg->FFC_VBAT_FULL) + chg->ffc_count++; + else + chg->ffc_count = 0; + if (chg->ffc_count >= 2) { + chg->ffc_count = 0; + chg->ffc_status = FFC_TAPER; + pr_info("ffc one done\n"); + } + } else if (chg->ffc_status == FFC_TAPER) { + if (temp > chg->FFC_TEMP_T1 + && temp < chg->FFC_TEMP_T2) { + chg->ffc_status = FFC_NOR_TAPER; + vote(g_chg->fcc_votable, + DEFAULT_VOTER, true, chg->FFC_NOR_FCC * 1000); + } else if (temp >= chg->FFC_TEMP_T2 + && temp < chg->FFC_TEMP_T3) { + chg->ffc_status = FFC_WARM_TAPER; + vote(g_chg->fcc_votable, + DEFAULT_VOTER, true, chg->FFC_WARM_FCC * 1000); + } else { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + } + } else if (chg->ffc_status == FFC_NOR_TAPER) { + if (icharging <= (-1)*chg->FFC_NORMAL_CUTOFF + && (batt_volt >= chg->FFC_VBAT_FULL)) { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + } else if (icharging > (-1)*chg->FFC_NORMAL_CUTOFF) + chg->ffc_count++; + else + chg->ffc_count = 0; + if (chg->ffc_count >= 2) { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + pr_info("ffc nor taper done\n"); + } + } else if (chg->ffc_status == FFC_WARM_TAPER) { + if (icharging <= (-1)*chg->FFC_WARM_CUTOFF + && (batt_volt >= chg->FFC_VBAT_FULL)) { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + } else if (icharging > (-1)*chg->FFC_WARM_CUTOFF) + chg->ffc_count++; + else + chg->ffc_count = 0; + if (chg->ffc_count >= 2) { + chg->ffc_count = 0; + chg->ffc_status = FFC_IDLE; + pr_info("ffc normal taper done\n"); + } + } else if (chg->ffc_status == FFC_IDLE) { + chg->ffc_count++; + op_charging_en(chg, false); + if (chg->ffc_count > 2) { + chg->ffc_status = FFC_DEFAULT; + smblib_set_prop_charge_parameter_set(chg); + op_charging_en(chg, true); + } + } else { + chg->ffc_count = 0; + chg->ffc_status = FFC_DEFAULT; + } +} + +#define FULL_COUNTS_SW 5 +#define FULL_COUNTS_HW 3 +static bool op_check_vbat_is_full_by_sw(struct smb_charger *chg) +{ + static bool ret_sw = false; + static bool ret_hw = false; + static int vbat_counts_sw = 0; + static int vbat_counts_hw = 0; + int vbatt_full_vol_sw; + int vbatt_full_vol_hw; + int tbatt_status, icharging, batt_volt; + + if (!chg->check_batt_full_by_sw) + return false; + if (chg->ffc_status != FFC_DEFAULT) + return false; + if (!chg->vbus_present) { + vbat_counts_sw = 0; + vbat_counts_hw = 0; + ret_sw = false; + ret_hw = false; + return false; + } + + tbatt_status = op_battery_temp_region_get(chg); + vbatt_full_vol_hw = chg->vbatmax[tbatt_status]; + if (tbatt_status == BATT_TEMP_LITTLE_COLD) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else if (tbatt_status == BATT_TEMP_COOL) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else if (tbatt_status == BATT_TEMP_LITTLE_COOL) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else if (tbatt_status == BATT_TEMP_PRE_NORMAL) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else if (tbatt_status == BATT_TEMP_NORMAL) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else if (tbatt_status == BATT_TEMP_WARM) + vbatt_full_vol_sw = chg->vbatmax[tbatt_status] - 70; + else { + vbat_counts_sw = 0; + vbat_counts_hw = 0; + ret_sw = 0; + ret_hw = 0; + return false; + } + + /* use SW Vfloat to check */ + batt_volt = get_prop_batt_voltage_now(chg) / 1000; + icharging = get_prop_batt_current_now(chg) / 1000; + if (batt_volt > vbatt_full_vol_sw) { + if (icharging < 0 && (icharging * -1) <= chg->sw_iterm_ma) { + vbat_counts_sw++; + if (vbat_counts_sw > FULL_COUNTS_SW) { + vbat_counts_sw = 0; + ret_sw = true; + } + } else if (icharging >= 0) { + vbat_counts_sw++; + if (vbat_counts_sw > FULL_COUNTS_SW * 2) { + vbat_counts_sw = 0; + ret_sw = true; + pr_info("[BATTERY] Battery full by sw when icharging>=0!!\n"); + } + } else { + vbat_counts_sw = 0; + ret_sw = false; + } + } else { + vbat_counts_sw = 0; + ret_sw = false; + } + + /* use HW Vfloat to check */ + if (batt_volt >= vbatt_full_vol_hw + 18) { + vbat_counts_hw++; + if (vbat_counts_hw >= FULL_COUNTS_HW) { + vbat_counts_hw = 0; + ret_hw = true; + } + } else { + vbat_counts_hw = 0; + ret_hw = false; + } + + if (ret_sw == true || ret_hw == true) { + pr_info("[BATTERY] Battery full by sw[%s] !!\n", (ret_sw == true) ? "S" : "H"); + ret_sw = ret_hw = false; + return true; + } else { + return false; + } +} + +void checkout_term_current(struct smb_charger *chg) +{ + bool chg_full; + + if (chg->chg_done) + return; + chg_full = op_check_vbat_is_full_by_sw(chg); + if (chg_full) { + chg->chg_done = true; + op_charging_en(chg, false); + pr_info("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)); + } +} + +static void trigger_check_charger(struct smb_charger *chg) +{ + chg->non_stand_chg_count = 0; + schedule_delayed_work( + &chg->non_standard_charger_check_work, + msecs_to_jiffies(TIME_1000MS)); +} + + +static int usb_enum_check(const char *val, const struct kernel_param *kp) +{ + const struct apsd_result *apsd_result; + struct smb_charger *chg = g_chg; + unsigned long usb_sw_reset = 0; + int ret = 0; + + if (!g_chg) + return 0; + if (chg->usb_enum_status) + return 0; + + if (!is_usb_present(chg)) + return 0; + + trigger_check_charger(chg); + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->bit != SDP_CHARGER_BIT + && apsd_result->bit != CDP_CHARGER_BIT) + return 0; + + ret = kstrtoul(val, 10, &usb_sw_reset); + if (ret) + return ret; + + if (!usb_sw_reset) + return 0; + + pr_info("usb don't enum for longtime in boot\n"); + op_handle_usb_removal(chg); + + 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 if (aicl_result >= 700*1000) + op_usb_icl_set(chg, aicl_result - 200*1000); + else + op_usb_icl_set(chg, 1200*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 = 0; + + 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 = 0, vbat = 0; + + 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); + } + } +} + +int plugin_update(struct smb_charger *chg) +{ + int rc = 0; + char *connected_str[2] = { "USBCable=CONNECTED", NULL}; + char *disconnected_str[2] = { "USBCable=DISCONNECTED", NULL}; + + rc = kobject_uevent_env(&chg->dev->kobj, KOBJ_CHANGE, + chg->hw_detect ? connected_str : disconnected_str); + + return rc; +} + +static void op_otg_switch(struct work_struct *work) +{ + int usb_pluged; + int rc = 0; + + if (!g_chg) + return; + usb_pluged = gpio_get_value(g_chg->plug_irq) ? 0 : 1; + if (usb_pluged == g_chg->pre_cable_pluged) { + pr_info("same status,return;usb_present:%d\n", usb_pluged); + return; + } + pr_info("%s,usb_present:%d\n", __func__, usb_pluged); + + if (usb_pluged) { + vote(g_chg->otg_toggle_votable, HW_DETECT_VOTER, 1, 0); + g_chg->hw_detect = 1; + } else { + vote(g_chg->otg_toggle_votable, HW_DETECT_VOTER, 0, 0); + g_chg->hw_detect = 0; + } + rc = plugin_update(g_chg); + pr_info("%s:hw_detect=%d and report rc: %d\n", + __func__, g_chg->hw_detect, rc); + g_chg->pre_cable_pluged = usb_pluged; +} + +#define USB_CONNECTOR_DEFAULT_TEMP 25 +static int get_usb_temp(struct smb_charger *chg) +{ + int ret, i, result; + + if (chg->iio.op_connector_temp_chan) { + ret = iio_read_channel_processed( + chg->iio.op_connector_temp_chan, + &result); + if (ret < 0) { + smblib_err(chg, "Error in reading IIO channel data, rc=%d\n", + ret); + return USB_CONNECTOR_DEFAULT_TEMP; + } + chg->connecter_voltage = result/1000; + } else { + pr_err("op_connector_temp_chan no found!\n"); + return USB_CONNECTOR_DEFAULT_TEMP; + } + for (i = ARRAY_SIZE(con_volt_30k) - 1; i >= 0; i--) { + if (con_volt_30k[i] >= chg->connecter_voltage) + break; + else if (i == 0) + break; + } + + pr_debug("connectter vol:%d,temp:%d\n", + chg->connecter_voltage, con_volt_30k[i]); + + return con_temp_30k[i]; +} + +void op_disconnect_vbus(struct smb_charger *chg, bool enable) +{ + +#ifdef CONFIG_OP_DEBUG_CHG + /* *#806# aging test not need Vbus disconnect feature*/ + return; +#else + if (chg->is_aging_test) + return; +#endif + if (!gpio_is_valid(chg->vbus_ctrl)) + return; + if (!enable) { + gpio_set_value(chg->vbus_ctrl, 0); + chg->disconnect_vbus = false; + pr_info("usb connecter connectd!"); + return; + } + pr_info("usb connecter hot,Vbus disconnected!"); + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + switch_mode_to_normal(); + op_set_fast_chg_allow(chg, false); + } + gpio_set_value(chg->vbus_ctrl, 1); + chg->disconnect_vbus = true; +} + +void op_recovery_revert_boost(struct smb_charger *chg) +{ + int soc = 0, rc = 0; + union power_supply_propval vbus_val; + const struct apsd_result *apsd_result; + + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_info("return directly because dash is online\n"); + return; + } + if (get_prop_batt_current_now(chg) / 1000 < 0) + return; + apsd_result = smblib_update_usb_type(chg); + if (apsd_result->bit == OCP_CHARGER_BIT) + return; + soc = get_prop_batt_capacity(chg); + if (soc < 7) + return; + 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; + } + if (vbus_val.intval < 2000) + return; + pr_info("suspend usb about 1s\n"); + chg->revert_boost_trigger = true; + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); + msleep(1000); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + chg->revert_boost_trigger = false; +} + +static void op_revert_boost_recovery_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, revertboost_recovery_work); + + op_recovery_revert_boost(chg); +} + +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 = 0; + bool fast_charging = 0; + static int vbat_mv; + union power_supply_propval vbus_val; + int rc; + +/*yangfb@bsp, 20181023 icl set 1A if battery lower than 15%*/ + op_otg_icl_contrl(chg); +#ifndef CONFIG_OP_DEBUG_CHG + op_check_charge_timeout(chg); +#endif + +#ifdef CONFIG_OP_DEBUG_CHG + 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; + } +#endif + 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) { + if (chg->chg_disabled) { + set_usb_switch(chg, false); + goto out; + } + 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)); + } + 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; + } + + 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 > chg->temp_littel_cool_voltage + 40 + && chg->temp_littel_cool_set_current) { + chg->is_power_changed = true; + } else if (vbat_mv < chg->temp_littel_cool_voltage - 40 + && !chg->temp_littel_cool_set_current) { + chg->is_power_changed = true; + } + } + ffc_exit(chg); + checkout_term_current(chg); + 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 = 0; + 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 = 0; + 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; + } + + shutdown_soc = (stored_soc >> 1); + 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 = 0, rc = 0; + bool batt_present = 0; + 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 = 0; + + 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 + ***************/ + +int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->pr_swap_in_progress; + return 0; +} + +int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + chg->pr_swap_in_progress = val->intval; + /* + * call the cc changed irq to handle real removals while + * PR_SWAP was in progress + */ + smblib_usb_typec_change(chg); + rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, + val->intval ? TCC_DEBOUNCE_20MS_BIT : 0); + if (rc < 0) + smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc); + return 0; +} + +/*************** + * Work Queues * + ***************/ +static void smblib_uusb_otg_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + uusb_otg_work.work); + int rc; + u8 stat; + bool otg; + + rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc); + goto out; + } + + otg = !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)); + extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, otg); + smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n", + stat, otg); + power_supply_changed(chg->usb_psy); + +out: + vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0); +} + + +static void smblib_hvdcp_detect_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + hvdcp_detect_work.work); + + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + false, 0); + power_supply_changed(chg->usb_psy); +} + +static void bms_update_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + bms_update_work); + + smblib_suspend_on_debug_battery(chg); + + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); +} + +static void pl_update_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + pl_update_work); + + smblib_stat_sw_override_cfg(chg, false); +} + +static void clear_hdc_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + clear_hdc_work.work); + + chg->is_hdc = false; + if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) + enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); +} + +static void rdstd_cc2_detach_work(struct work_struct *work) +{ + int rc; + u8 stat4, stat5; + struct smb_charger *chg = container_of(work, struct smb_charger, + rdstd_cc2_detach_work); + + if (!chg->cc2_detach_wa_active) + return; + + /* + * WA steps - + * 1. Enable both UFP and DFP, wait for 10ms. + * 2. Disable DFP, wait for 30ms. + * 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS + * and TIMER_STAGE bits are gone, otherwise repeat all by + * work rescheduling. + * Note, work will be cancelled when USB_PLUGIN rises. + */ + + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + UFP_EN_CMD_BIT | DFP_EN_CMD_BIT, + UFP_EN_CMD_BIT | DFP_EN_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc); + return; + } + + usleep_range(10000, 11000); + + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + UFP_EN_CMD_BIT | DFP_EN_CMD_BIT, + UFP_EN_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc); + return; + } + + usleep_range(30000, 31000); + + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + return; + } + + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); + if (rc < 0) { + smblib_err(chg, + "Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc); + return; + } + + if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT) + || (stat5 & TIMER_STAGE_2_BIT)) { + smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n", + (int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT), + (int)(stat5 & TIMER_STAGE_2_BIT)); + goto rerun; + } + + smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n"); + chg->cc2_detach_wa_active = false; + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + EXIT_SNK_BASED_ON_CC_BIT, 0); + smblib_reg_block_restore(chg, cc2_detach_settings); + + /* + * Mutex acquisition deadlock can happen while cancelling this work + * during pd_hard_reset from the function smblib_cc2_sink_removal_exit + * which is called in the same lock context that we try to acquire in + * this work routine. + * Check if this work is running during pd_hard_reset and skip holding + * mutex if lock is already held. + */ + if (!chg->in_chg_lock) + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + if (!chg->in_chg_lock) + mutex_unlock(&chg->lock); + + return; + +rerun: + schedule_work(&chg->rdstd_cc2_detach_work); +} + +static void smblib_otg_oc_exit(struct smb_charger *chg, bool success) +{ + int rc; + + chg->otg_attempts = 0; + if (!success) { smblib_err(chg, "OTG soft start failed\n"); chg->otg_en = false; } @@ -5294,6 +8510,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } +/*infi@bsp, 2018/07/10 Add otg toggle vote optimize otg_switch set flow*/ + chg->otg_toggle_votable = create_votable("OTG_TOGGLE", VOTE_SET_ANY, + smblib_otg_toggle_vote_callback, + chg); + if (IS_ERR(chg->otg_toggle_votable)) { + rc = PTR_ERR(chg->otg_toggle_votable); + return rc; + } + chg->dc_icl_votable = create_votable("DC_ICL", VOTE_MIN, smblib_dc_icl_vote_callback, chg); @@ -5408,6 +8633,9 @@ static void smblib_destroy_votables(struct smb_charger *chg) { if (chg->dc_suspend_votable) destroy_votable(chg->dc_suspend_votable); +/*infi@bsp, 2018/07/10 Add otg toggle vote optimize otg_switch set flow*/ + if (chg->otg_toggle_votable) + destroy_votable(chg->otg_toggle_votable); if (chg->usb_icl_votable) destroy_votable(chg->usb_icl_votable); if (chg->dc_icl_votable) @@ -5442,6 +8670,8 @@ static void smblib_iio_deinit(struct smb_charger *chg) iio_channel_release(chg->iio.usbin_v_chan); if (!IS_ERR_OR_NULL(chg->iio.batt_i_chan)) iio_channel_release(chg->iio.batt_i_chan); + if (!IS_ERR_OR_NULL(chg->iio.op_connector_temp_chan)) + iio_channel_release(chg->iio.op_connector_temp_chan); } int smblib_init(struct smb_charger *chg) @@ -5451,11 +8681,50 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->lock); mutex_init(&chg->write_lock); mutex_init(&chg->otg_oc_lock); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + mutex_init(&chg->sw_dash_lock); mutex_init(&chg->vconn_oc_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->pl_update_work, pl_update_work); INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work); INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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); + INIT_WORK(&chg->get_aicl_work, op_get_aicl_work); + INIT_WORK(&chg->otg_switch_work, op_otg_switch); + INIT_DELAYED_WORK(&chg->dash_check_work, op_dash_check_work); + INIT_DELAYED_WORK(&chg->connecter_check_work, + op_connect_temp_check_work); + INIT_DELAYED_WORK(&chg->revertboost_recovery_work, + op_revert_boost_recovery_work); + schedule_delayed_work(&chg->revertboost_recovery_work, + msecs_to_jiffies(16000)); + schedule_delayed_work(&chg->heartbeat_work, + msecs_to_jiffies(HEARTBEAT_INTERVAL_MS)); + notify_dash_unplug_register(¬ify_unplug_event); + chg->chg_wake_lock = wakeup_source_register(NULL, "chg_wake_lock"); + g_chg = chg; + + regsister_notify_usb_enumeration_status(&usb_enumeration); + + chg->msm_drm_notifier.notifier_call = msm_drm_notifier_callback; + + rc = msm_drm_register_client(&chg->msm_drm_notifier); + if (rc) + pr_err("Smb unable to register notifier: %d\n", rc); + 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); @@ -5463,6 +8732,8 @@ 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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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; @@ -5538,7 +8809,9 @@ int smblib_deinit(struct smb_charger *chg) 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); +/* david.liu@bsp, 20170330 Fix system crash */ + if (chg->nb.notifier_call) + power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); qcom_batt_deinit(); @@ -5552,5 +8825,7 @@ int smblib_deinit(struct smb_charger *chg) smblib_iio_deinit(chg); +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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 046e6218d4ea..4b1a4f28f268 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -9,6 +9,9 @@ #include #include #include + +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#include #include #include "storm-watch.h" #include "battery.h" @@ -19,10 +22,28 @@ enum print_reason { PR_MISC = BIT(2), PR_PARALLEL = BIT(3), PR_OTG = BIT(4), +/* david.liu@bsp, 20171023 Battery & Charging porting */ + PR_OP_DEBUG = BIT(5), }; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +#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 100 +#define TIME_1000MS 1000 +#define REDET_COUTNT 1 +#define APSD_CHECK_COUTNT 15 +#define DASH_CHECK_COUNT 40 +#define BOOST_BACK_COUNT 2 +#define TIME_200MS 200 +#define TIME_100MS 100 + +#define TIME_3S 3000 + #define DEFAULT_VOTER "DEFAULT_VOTER" #define USER_VOTER "USER_VOTER" +#define HW_DETECT_VOTER "HW_DETECT_VOTER" #define PD_VOTER "PD_VOTER" #define DCP_VOTER "DCP_VOTER" #define QC_VOTER "QC_VOTER" @@ -61,6 +82,7 @@ enum print_reason { #define OTG_VOTER "OTG_VOTER" #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define WBC_VOTER "WBC_VOTER" +#define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER" #define MOISTURE_VOTER "MOISTURE_VOTER" #define HVDCP2_ICL_VOTER "HVDCP2_ICL_VOTER" #define OV_VOTER "OV_VOTER" @@ -221,6 +243,7 @@ struct smb_iio { struct iio_channel *connector_temp_thr1_chan; struct iio_channel *connector_temp_thr2_chan; struct iio_channel *connector_temp_thr3_chan; + struct iio_channel *op_connector_temp_chan; }; struct reg_info { @@ -246,6 +269,7 @@ struct smb_charger { struct charger_param chg_param; int otg_delay_ms; int *weak_chg_icl_ua; + int ffc_count; /* locks */ struct mutex lock; @@ -253,6 +277,8 @@ struct smb_charger { struct mutex ps_change_lock; struct mutex otg_oc_lock; struct mutex vconn_oc_lock; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + struct mutex sw_dash_lock; /* power supplies */ struct power_supply *batt_psy; @@ -266,6 +292,8 @@ struct smb_charger { /* notifiers */ struct notifier_block nb; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + struct notifier_block msm_drm_notifier; /* parallel charging */ struct parallel_params pl; @@ -277,6 +305,8 @@ struct smb_charger { /* votables */ struct votable *dc_suspend_votable; +/*infi@bsp, 2018/07/10 Add otg toggle vote optimize otg_switch set flow*/ + struct votable *otg_toggle_votable; struct votable *fcc_votable; struct votable *fv_votable; struct votable *usb_icl_votable; @@ -301,6 +331,22 @@ struct smb_charger { struct work_struct rdstd_cc2_detach_work; struct delayed_work hvdcp_detect_work; struct delayed_work ps_change_timeout_work; +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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 work_struct get_aicl_work; + struct work_struct otg_switch_work; + struct delayed_work dash_check_work; + struct delayed_work revertboost_recovery_work; + struct delayed_work connecter_check_work; + struct wakeup_source *chg_wake_lock; struct delayed_work clear_hdc_work; struct work_struct otg_oc_work; struct work_struct vconn_oc_work; @@ -312,6 +358,87 @@ struct smb_charger { struct delayed_work bb_removal_work; /* cached status */ +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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 FFC_TEMP_T1; + int FFC_TEMP_T2; + int FFC_TEMP_T3; + int FFC_NOR_FCC; + int FFC_WARM_FCC; + int FFC_NORMAL_CUTOFF; + int FFC_WARM_CUTOFF; + int FFC_VBAT_FULL; + int batt_health; + int ibatmax[TEMP_REGION_MAX]; + int vbatmax[TEMP_REGION_MAX]; + int vbatdet[TEMP_REGION_MAX]; + int temp_littel_cool_voltage; + int temp_littel_cool_low_current; + int fake_chgvol; + int fake_temp; + int fake_protect_sts; + int non_stand_chg_current; + int non_stand_chg_count; + int dash_check_count; + int redet_count; + int reset_count; + int dump_count; + int ck_apsd_count; + int ck_dash_count; + int recovery_boost_count; + int plug_irq; + int pre_cable_pluged; + int hw_detect; + int op_icl_val; + int sw_iterm_ma; + bool otg_switch; + bool use_fake_chgvol; + bool use_fake_temp; + bool use_fake_protect_sts; + bool vbus_present; + bool probe_done; + 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_disabled; + bool chg_ovp; + bool is_power_changed; + bool recharge_pending; + bool recharge_status; + bool temp_littel_cool_set_current; + 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; + bool check_batt_full_by_sw; + enum ffc_step ffc_status; + 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; + uint32_t bus_client; int voltage_min_uv; int voltage_max_uv; int pd_active; @@ -331,6 +458,15 @@ struct smb_charger { bool otg_en; bool vconn_en; bool suspend_input_on_debug_batt; + /*yangfb@bsp, 20181023 icl set 1A if battery lower than 15%*/ + bool OTG_ICL_CTRL; + int OTG_LOW_BAT; + int OTG_LOW_BAT_ICL; + int OTG_NORMAL_BAT_ICL; + int connecter_temp; + int connecter_voltage; + int disconnect_vbus; + int vbus_ctrl; int otg_attempts; int vconn_attempts; int default_icl_ua; @@ -353,6 +489,8 @@ struct smb_charger { bool in_chg_lock; bool fcc_stepper_enable; bool ufp_only_mode; + struct pinctrl_state *usb_temperature_default; + struct pinctrl *pinctrl; /* workaround flag */ u32 wa_flags; @@ -378,8 +516,17 @@ struct smb_charger { int pulse_cnt; int die_health; + int filter_count; }; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +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); int smblib_write(struct smb_charger *chg, u16 addr, u8 val); @@ -455,6 +602,32 @@ int smblib_set_prop_batt_status(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); +/* david.liu@bsp, 20171023 Battery & Charging porting */ +void op_bus_vote(int disable); +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, + bool enalbe); +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 plugin_update(struct smb_charger *chg); +void op_disconnect_vbus(struct smb_charger *chg, bool enable); int smblib_set_prop_input_current_limited(struct smb_charger *chg, const union power_supply_propval *val); @@ -551,6 +724,8 @@ int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override); void smblib_usb_typec_change(struct smb_charger *chg); int smblib_toggle_stat(struct smb_charger *chg, int reset); int smblib_force_ufp(struct smb_charger *chg); +int smblib_get_prop_batt_temp(struct smb_charger *chg, + union power_supply_propval *val); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a1c7b1d05a6d..c7ac560c1cde 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5026,9 +5026,9 @@ int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, } while (ret && peer && --retries); if (ret) - dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x failed %d retries\n", + dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x failed %d retries, err %d\n", set, UIC_GET_ATTR_ID(attr_sel), mib_val, - UFS_UIC_COMMAND_RETRIES - retries); + UFS_UIC_COMMAND_RETRIES - retries, ret); return ret; } @@ -5601,7 +5601,7 @@ int ufshcd_change_power_mode(struct ufs_hba *hba, /* INITIAL ADAPT */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), PA_INITIAL_ADAPT); - } else { + } else if (hba->ufs_version >= UFSHCI_VERSION_30) { /* NO ADAPT */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), PA_NO_ADAPT); } diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index eb6517668c70..7e82e9f0e748 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -666,6 +666,19 @@ config MSM_SPCOM spcom provides clients/server API, although currently only one client or server is allowed per logical channel. +config MSM_SPCOM_LEGACY + depends on QCOM_GLINK + bool "Secure Processor Communication over GLINK" + help + spcom driver allows loading Secure Processor Applications and + sending messages to Secure Processor Applications. + spcom provides interface to both user space app and kernel driver. + It is using glink as the transport layer, which provides multiple + logical channels over single physical channel. + The physical layer is based on shared memory and interrupts. + spcom provides clients/server API, although currently only one client + or server is allowed per logical channel. + config QSEE_IPC_IRQ bool "QSEE interrupt manager" help @@ -981,4 +994,12 @@ config ICNSS_QMI and configurations. It also send WLAN on/off control message to FW over QMI channel. +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. + source "drivers/soc/qcom/wcnss/Kconfig" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 3fe90e5ab8e5..fb630ad88791 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/ obj-$(CONFIG_MSM_PIL) += peripheral-loader.o obj-$(CONFIG_MSM_SPCOM) += spcom.o +obj-$(CONFIG_MSM_SPCOM_LEGACY) += spcom-legacy.o obj-$(CONFIG_MSM_CDSP_LOADER) += qdsp6v2/ obj-$(CONFIG_MSM_JTAGV8) += jtagv8.o jtagv8-etm.o obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o @@ -110,3 +111,5 @@ obj-$(CONFIG_QTI_CRYPTO_TZ) += crypto-qti-tz.o obj-$(CONFIG_QTI_HW_KEY_MANAGER) += hwkm_qti.o hwkm_qti-y += hwkm.o obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o +obj-y += project_info.o +obj-$(CONFIG_RF_CABLE_DETECT) += op_rf_cable_monitor.o diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c index 3a48c03a8be7..aefca337be75 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c @@ -1000,6 +1000,7 @@ static int msm_bus_dev_init_qos(struct device *dev, void *data) bus_node_info->fabdev->noc_ops.qos_init( node_dev, + bus_node_info, bus_node_info->fabdev->qos_base, bus_node_info->fabdev->base_offset, bus_node_info->fabdev->qos_off, diff --git a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c index b3bc943008f8..37b91a0348c4 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c @@ -11,10 +11,14 @@ #include #include #include +#include +#include #include "msm_bus_core.h" #include "msm_bus_noc.h" #include "msm_bus_rpmh.h" +static DEFINE_SPINLOCK(noc_lock); + /* NOC_QOS generic */ #define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x)) #define SAT_SCALE 16 /* 16 bytes minimum for saturation */ @@ -27,6 +31,8 @@ #define MIN_BW_FIELD 1 #define READ_TIMEOUT_MS msecs_to_jiffies(1) #define READ_DELAY_US 10 +#define QM_BASE 0x010B8000 +#define MSM_BUS_FAB_MEM_NOC 6152 #define NOC_QOS_REG_BASE(b, o) ((b) + (o)) @@ -112,6 +118,18 @@ enum noc_qos_id_saturationn { NOC_QOS_SATn_SAT_SHFT = 0x0, }; +#define QM_CLn_TH_LVL_MUX_ADDR(b, o, n, d) \ + (NOC_QOS_REG_BASE(b, o) + 0x8C0 + (d) * (n)) +enum qm_cl_id_th_lvl_mux_cfg { + QM_CLn_TH_LVL_SW_OVERRD_BMSK = 0x80000000, + QM_CLn_TH_LVL_SW_OVERRD_SHFT = 0x1F, + QM_CLn_TH_LVL_SW_BMSK = 0x00000007, + QM_CLn_TH_LVL_SW_SHFT = 0x0, +}; + +static void __iomem *qm_base; +static void __iomem *memnoc_qos_base; + static int noc_div(uint64_t *a, uint32_t b) { if ((*a > 0) && (*a < b)) { @@ -162,6 +180,18 @@ static uint32_t noc_ws(uint64_t bw, uint32_t sat, uint32_t qos_freq) } #define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase)) +static void noc_set_qm_th_lvl_cfg(void __iomem *base, uint32_t off, + uint32_t n, uint32_t delta, + uint32_t override_val, uint32_t override) +{ + writel_relaxed(((override << QM_CLn_TH_LVL_SW_OVERRD_SHFT) | + (override_val & QM_CLn_TH_LVL_SW_BMSK)), + QM_CLn_TH_LVL_MUX_ADDR(base, off, n, delta)); + + /* Ensure QM CFG is set before exiting */ + wmb(); +} + static void noc_set_qos_dflt_prio(void __iomem *base, uint32_t qos_off, uint32_t mport, uint32_t qos_delta, uint32_t prio) @@ -179,48 +209,59 @@ static void noc_set_qos_dflt_prio(void __iomem *base, uint32_t qos_off, wmb(); } -static void noc_set_qos_limiter(void __iomem *base, uint32_t qos_off, - uint32_t mport, uint32_t qos_delta, - struct msm_bus_noc_limiter *lim, uint32_t lim_en) +static void noc_enable_qos_limiter(void __iomem *base, uint32_t qos_off, + uint32_t mport, uint32_t qos_delta, uint32_t lim_en) { uint32_t reg_val, val; - reg_val = readl_relaxed(NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, qos_delta)); - - writel_relaxed((reg_val & (~(NOC_QOS_MCTL_LIMIT_ENn_BMSK))), + val = lim_en << NOC_QOS_MCTL_LIMIT_ENn_SHFT; + writel_relaxed(((reg_val & (~(NOC_QOS_MCTL_LIMIT_ENn_BMSK))) | + (val & NOC_QOS_MCTL_LIMIT_ENn_BMSK)), NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, qos_delta)); - /* Ensure we disable limiter before config*/ + /* Ensure we disable/enable limiter before exiting*/ wmb(); +} +static void noc_set_qos_limit_bw(void __iomem *base, uint32_t qos_off, + uint32_t mport, uint32_t qos_delta, uint32_t bw) +{ + uint32_t reg_val, val; reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta)); - val = lim->bw << NOC_QOS_LIMITBW_BWn_SHFT; + val = bw << NOC_QOS_LIMITBW_BWn_SHFT; writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_BWn_BMSK))) | (val & NOC_QOS_LIMITBW_BWn_BMSK)), NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta)); + /* Ensure we set limiter bw before exiting*/ + wmb(); +} + +static void noc_set_qos_limit_sat(void __iomem *base, uint32_t qos_off, + uint32_t mport, uint32_t qos_delta, uint32_t sat) +{ + uint32_t reg_val, val; reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta)); - val = lim->sat << NOC_QOS_LIMITBW_SATn_SHFT; + val = sat << NOC_QOS_LIMITBW_SATn_SHFT; writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_SATn_BMSK))) | (val & NOC_QOS_LIMITBW_SATn_BMSK)), NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta)); - /* Ensure qos limiter settings in place before possibly enabling */ - wmb(); - - reg_val = readl_relaxed(NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, - qos_delta)); - val = lim_en << NOC_QOS_MCTL_LIMIT_ENn_SHFT; - writel_relaxed(((reg_val & (~(NOC_QOS_MCTL_LIMIT_ENn_BMSK))) | - (val & NOC_QOS_MCTL_LIMIT_ENn_BMSK)), - NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, qos_delta)); - - /* Ensure qos limiter writes take place before exiting*/ + /* Ensure we set limiter sat before exiting*/ wmb(); } +static void noc_set_qos_limiter(void __iomem *base, uint32_t qos_off, + uint32_t mport, uint32_t qos_delta, + struct msm_bus_noc_limiter *lim, uint32_t lim_en) +{ + noc_enable_qos_limiter(base, qos_off, mport, qos_delta, 0); + noc_set_qos_limit_bw(base, qos_off, mport, qos_delta, lim->bw); + noc_set_qos_limit_sat(base, qos_off, mport, qos_delta, lim->sat); + noc_enable_qos_limiter(base, qos_off, mport, qos_delta, lim_en); +} static void noc_set_qos_regulator(void __iomem *base, uint32_t qos_off, uint32_t mport, uint32_t qos_delta, @@ -328,6 +369,7 @@ void msm_bus_noc_get_qos_bw(void __iomem *base, uint32_t qos_off, } static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info, + struct msm_bus_node_device_type *fabdev, void __iomem *qos_base, uint32_t qos_off, uint32_t qos_delta, uint32_t qos_freq) @@ -335,6 +377,7 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info, struct msm_bus_noc_qos_params *qos_params; int ret = 0; int i; + unsigned long flags; qos_params = &info->node_info->qos_params; @@ -344,6 +387,21 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info, goto err_qos_init; } + if (!qm_base) { + qm_base = ioremap_nocache(QM_BASE, 0x4000); + if (!qm_base) { + MSM_BUS_ERR("%s: Error remapping address 0x%zx", + __func__, (size_t)QM_BASE); + ret = -ENOMEM; + goto err_qos_init; + } + } + + spin_lock_irqsave(&noc_lock, flags); + + if (fabdev->node_info->id == MSM_BUS_FAB_MEM_NOC) + memnoc_qos_base = qos_base; + for (i = 0; i < info->node_info->num_qports; i++) { noc_set_qos_dflt_prio(qos_base, qos_off, info->node_info->qport[i], @@ -367,6 +425,8 @@ static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info, qos_delta, qos_params->urg_fwd_en); } + spin_unlock_irqrestore(&noc_lock, flags); + err_qos_init: return ret; } @@ -456,3 +516,50 @@ int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev) return 0; } EXPORT_SYMBOL(msm_bus_noc_set_ops); + +int msm_bus_noc_throttle_wa(bool enable) +{ + unsigned long flags; + + spin_lock_irqsave(&noc_lock, flags); + + if (!qm_base) { + MSM_BUS_ERR("QM CFG base address not found!"); + goto noc_throttle_exit; + } + + if (enable) { + noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0x3, 0x1); + noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0x3, 0x1); + } else { + noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0, 0); + noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0, 0); + } + +noc_throttle_exit: + spin_unlock_irqrestore(&noc_lock, flags); + return 0; +} +EXPORT_SYMBOL(msm_bus_noc_throttle_wa); + +int msm_bus_noc_priority_wa(bool enable) +{ + unsigned long flags; + + spin_lock_irqsave(&noc_lock, flags); + if (!memnoc_qos_base) { + MSM_BUS_ERR("Memnoc QoS Base address not found!"); + goto noc_priority_exit; + } + + if (enable) + noc_set_qos_dflt_prio(memnoc_qos_base, 0x10000, 0, + 0x1000, 7); + else + noc_set_qos_dflt_prio(memnoc_qos_base, 0x10000, 0, + 0x1000, 6); +noc_priority_exit: + spin_unlock_irqrestore(&noc_lock, flags); + return 0; +} +EXPORT_SYMBOL(msm_bus_noc_priority_wa); diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h index 351902b0fabc..dbc8b1296563 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h +++ b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h @@ -35,6 +35,7 @@ struct link_node { /* New types introduced for adhoc topology */ struct msm_bus_noc_ops { int (*qos_init)(struct msm_bus_node_device_type *dev, + struct msm_bus_node_device_type *fabdev, void __iomem *qos_base, uint32_t qos_off, uint32_t qos_delta, uint32_t qos_freq); int (*set_bw)(struct msm_bus_node_device_type *dev, 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..3715c395ee70 --- /dev/null +++ b/drivers/soc/qcom/op_rf_cable_monitor.c @@ -0,0 +1,343 @@ +/*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 + +static struct project_info *project_info_desc; + +struct cable_data { + int irq_0; + int irq_1; + int cable_gpio_0;//gpio32 + int cable_gpio_1;//gpio86 + struct delayed_work work; + struct workqueue_struct *wqueue; + struct device *dev; + struct wakeup_source *wl; + atomic_t running; + int rf_v3; + int rf_v3_pre; + 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) +{ + size_t size; + + project_info_desc = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_PROJECT_INFO, + &size); + + 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_0); + disable_irq_nosync(_cdata->irq_1); + spin_unlock_irqrestore(&_cdata->lock, flags); + pr_err("modem :kevin debug:%s, %d:_cdata->rf_v3_pre=%d, _cdata->rf_v3=%d,\n", + __func__, __LINE__, _cdata->rf_v3_pre, _cdata->rf_v3); + + _cdata->rf_v3 = + gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1); + + modify_rf_cable_smem_info(_cdata->rf_v3); + if (_cdata->rf_v3 != _cdata->rf_v3_pre) + op_restart_modem(); + + _cdata->rf_v3_pre = + gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1); + + + spin_lock_irqsave(&_cdata->lock, flags); + enable_irq(_cdata->irq_0); + enable_irq(_cdata->irq_1); + spin_unlock_irqrestore(&_cdata->lock, flags); +} + +irqreturn_t cable_interrupt(int irq, void *_dev) +{ + __pm_wakeup_event(_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) +{ + char page[PAGESIZE]; + int len; + + len = scnprintf(page, sizeof(page), "%d\n", _cdata->enable); + + return simple_read_from_buffer(user_buf, + count, ppos, page, len); +} + +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; + char buf[10]; + int ret; + + if (copy_from_user(buf, buffer, count)) { + pr_err("%s: read proc input error.\n", __func__); + return count; + } + + ret = kstrtoint(buf, 0, &enable); + if (ret < 0) + return ret; + + + if (enable != _cdata->enable) { + _cdata->enable = enable; + if (!_cdata->enable) { + spin_lock_irqsave(&_cdata->lock, flags); + disable_irq_nosync(_cdata->irq_0); + disable_irq_nosync(_cdata->irq_1); + spin_unlock_irqrestore(&_cdata->lock, flags); + _cdata->rf_v3 = 1; + + modify_rf_cable_smem_info(1); + if (!_cdata->rf_v3_pre) + op_restart_modem(); + _cdata->rf_v3_pre = 1; + } else { + spin_lock_irqsave(&_cdata->lock, flags); + enable_irq(_cdata->irq_0); + enable_irq(_cdata->irq_1); + spin_unlock_irqrestore(&_cdata->lock, flags); + + _cdata->rf_v3 = gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1); + + modify_rf_cable_smem_info(_cdata->rf_v3); + if (_cdata->rf_v3 != _cdata->rf_v3_pre) + op_restart_modem(); + _cdata->rf_v3_pre = + gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1); + } + } + 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", + 0644, 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); +//request gpio 0 .gpio 1. + rc = op_rf_request_named_gpio("rf,cable-gpio-0", + &_cdata->cable_gpio_0); + if (rc) { + pr_err("%s: op_rf_request_named_gpio gpio-0 fail\n", + __func__); + goto exit_gpio; + } + rc = op_rf_request_named_gpio("rf,cable-gpio-1", + &_cdata->cable_gpio_1); + if (rc) { + pr_err("%s: op_rf_request_named_gpio gpio-1 fail\n", + __func__); + goto exit_gpio; + } +//set input and gpio to irq. + gpio_direction_input(_cdata->cable_gpio_0); + _cdata->irq_0 = gpio_to_irq(_cdata->cable_gpio_0); + if (_cdata->irq_0 < 0) { + pr_err("Unable to get irq number for GPIO %d, error %d\n", + _cdata->cable_gpio_0, _cdata->irq_0); + rc = _cdata->irq_0; + goto exit_gpio; + } + + gpio_direction_input(_cdata->cable_gpio_1); + _cdata->irq_1 = gpio_to_irq(_cdata->cable_gpio_1); + if (_cdata->irq_1 < 0) { + pr_err("Unable to get irq number for GPIO %d, error %d\n", + _cdata->cable_gpio_1, _cdata->irq_1); + rc = _cdata->irq_1; + goto exit_gpio; + } +//creat workqueue. + _cdata->wqueue = create_singlethread_workqueue( + "op_rf_cable_wqueue"); + INIT_DELAYED_WORK(&_cdata->work, rf_cable_work); + +//request _irq0 + rc = request_irq(_cdata->irq_0, cable_interrupt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "op_rf_cable", _cdata); + if (rc) { + pr_err("could not request irq %d\n", _cdata->irq_0); + goto exit_gpio; + } + pr_err("requested irq %d\n", _cdata->irq_0); + enable_irq_wake(_cdata->irq_0); + +//request _irq1 + rc = request_irq(_cdata->irq_1, cable_interrupt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "op_rf_cable", _cdata); + if (rc) { + pr_err("could not request irq %d\n", _cdata->irq_1); + goto exit_gpio; + } + + pr_err("requested irq %d\n", _cdata->irq_1); + enable_irq_wake(_cdata->irq_1); + + _cdata->wl = wakeup_source_register(NULL, "rf_cable_wake_lock"); + spin_lock_init(&_cdata->lock); + atomic_set(&_cdata->running, + gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1)); + + modify_rf_cable_smem_info( + gpio_get_value(_cdata->cable_gpio_0) || + gpio_get_value(_cdata->cable_gpio_1)); + 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..43cd6394ab9a --- /dev/null +++ b/drivers/soc/qcom/project_info.c @@ -0,0 +1,908 @@ +/* For OEM project information + *such as project name, hardware ID + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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_v1 = NULL; +static struct project_info_v2 *project_info_desc_v2 = NULL; +static struct dump_info *dp_info; + +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 int op_aboard_read_gpio(void); + +static DEVICE_ATTR(project_name, 0444, project_info_get, NULL); +static DEVICE_ATTR(hw_id, 0444, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v1, 0444, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v2, 0444, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v3, 0444, project_info_get, NULL); +static DEVICE_ATTR(modem, 0444, project_info_get, NULL); +static DEVICE_ATTR(operator_no, 0444, project_info_get, NULL); +static DEVICE_ATTR(ddr_manufacture_info, 0444, project_info_get, NULL); +static DEVICE_ATTR(ddr_row, 0444, project_info_get, NULL); +static DEVICE_ATTR(ddr_column, 0444, project_info_get, NULL); +static DEVICE_ATTR(ddr_fw_version, 0444, project_info_get, NULL); +static DEVICE_ATTR(ddr_reserve_info, 0444, project_info_get, NULL); +static DEVICE_ATTR(secboot_status, 0444, project_info_get, NULL); +static DEVICE_ATTR(platform_id, 0444, project_info_get, NULL); +static DEVICE_ATTR(serialno, 0444, project_info_get, NULL); +static DEVICE_ATTR(feature_id, 0444, project_info_get, NULL); +static DEVICE_ATTR(aboard_id, 0444, project_info_get, NULL); + +#define GET_PROJECT_INFO(member) (project_info_desc_v2 ? project_info_desc_v2->member : project_info_desc_v1->member) +#define SET_PROJECT_INFO(member, value) \ + if (project_info_desc_v2) \ + project_info_desc_v2->member = value; \ + else \ + project_info_desc_v1->member = value; + +void save_dump_reason_to_smem(char *info, char *function_name) +{ + int strl = 0, strl1 = 0; + static int flag = 0; + size_t size; + + /* Make sure save_dump_reason_to_smem() is not called + infinite times by panic() during DDR bit flip crash etc */ + if (flag > 1) + return; + + dp_info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_DUMP_INFO, &size); + + if (IS_ERR_OR_NULL(dp_info)) + pr_err("%s: get dp_info failure\n", __func__); + else { + pr_err("%s: info : %s\n",__func__, info); + + strl = strlen(info)+1; + strl1 = strlen(function_name)+1; + strl = strl < DUMP_REASON_SIZE ? strl: DUMP_REASON_SIZE; + strl1 = strl1 < DUMP_REASON_SIZE ? strl1: DUMP_REASON_SIZE ; + if ((strlen(dp_info->dump_reason) + strl) < DUMP_REASON_SIZE) + strncat(dp_info->dump_reason,info,strl); + + if (function_name != NULL && ((strlen(dp_info->dump_reason) + strl1) < DUMP_REASON_SIZE)) { + strncat(dp_info->dump_reason,function_name,strl1); + strncat(dp_info->dump_reason,"\n",1); + } + } + pr_err("\r%s: dump_reason : %s strl=%d function caused panic :%s strl1=%d \n", __func__, + dp_info->dump_reason, strl, function_name, strl1); + //save_dump_reason_to_device_info(dp_info->dump_reason); + flag++; +} + +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_v1 || project_info_desc_v2) { + if (attr == &dev_attr_project_name) + return snprintf(buf, BUF_SIZE, "%s\n", + GET_PROJECT_INFO(project_name)); + if (attr == &dev_attr_hw_id) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(hw_version)); + if (attr == &dev_attr_rf_id_v1) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(rf_v1)); + if (attr == &dev_attr_rf_id_v2) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(rf_v2)); + if (attr == &dev_attr_rf_id_v3) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(rf_v3)); + if (attr == &dev_attr_modem) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(modem)); + if (attr == &dev_attr_operator_no) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(operator)); + if (attr == &dev_attr_ddr_manufacture_info) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(ddr_manufacture_info)); + if (attr == &dev_attr_ddr_row) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(ddr_row)); + if (attr == &dev_attr_ddr_column) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(ddr_column)); + if (attr == &dev_attr_ddr_fw_version) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(ddr_fw_version)); + if (attr == &dev_attr_ddr_reserve_info) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(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", + GET_PROJECT_INFO(platform_id)); + + if (attr == &dev_attr_serialno) + return snprintf(buf, BUF_SIZE, "0x%x\n", + socinfo_get_serial_number()); + + if (attr == &dev_attr_feature_id) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(feature_id)); + + if (attr == &dev_attr_aboard_id) + return snprintf(buf, BUF_SIZE, "%d\n", + GET_PROJECT_INFO(a_board_version)); + } + + 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, + &dev_attr_aboard_id.attr, + NULL, +}; + +static struct attribute_group project_info_attr_group = { + .attrs = project_info_sysfs_entries, +}; + +static DEVICE_ATTR(ddr, 0444, component_info_get, NULL); +static DEVICE_ATTR(emmc, 0444, component_info_get, NULL); +static DEVICE_ATTR(f_camera, 0444, component_info_get, NULL); +static DEVICE_ATTR(r_camera, 0444, component_info_get, NULL); +static DEVICE_ATTR(second_r_camera, 0444, component_info_get, NULL); +static DEVICE_ATTR(ois, 0444, component_info_get, NULL); +static DEVICE_ATTR(tp, 0444, component_info_get, NULL); +static DEVICE_ATTR(lcd, 0444, component_info_get, NULL); +static DEVICE_ATTR(wcn, 0444, component_info_get, NULL); +static DEVICE_ATTR(l_sensor, 0444, component_info_get, NULL); +static DEVICE_ATTR(g_sensor, 0444, component_info_get, NULL); +static DEVICE_ATTR(m_sensor, 0444, component_info_get, NULL); +static DEVICE_ATTR(gyro, 0444, component_info_get, NULL); +static DEVICE_ATTR(backlight, 0444, component_info_get, NULL); +static DEVICE_ATTR(mainboard, 0444, component_info_get, NULL); +static DEVICE_ATTR(fingerprints, 0444, component_info_get, NULL); +static DEVICE_ATTR(touch_key, 0444, component_info_get, NULL); +static DEVICE_ATTR(ufs, 0444, component_info_get, NULL); +static DEVICE_ATTR(Aboard, 0444, component_info_get, NULL); +static DEVICE_ATTR(nfc, 0444, component_info_get, NULL); +static DEVICE_ATTR(fast_charge, 0444, component_info_get, NULL); +static DEVICE_ATTR(cpu, 0444, component_info_get, NULL); +static DEVICE_ATTR(rf_version, 0444, 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_ois.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, + &dev_attr_rf_version.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_ois) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(OIS), + get_component_manufacture(OIS)); + 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) { + //op_aboard_read_gpio(); + 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)); + if (attr == &dev_attr_rf_version) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(RF_VERSION), + get_component_manufacture(RF_VERSION)); + 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]; +}; +//ddr id and ddr name +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 "}, + {321, "SDM845 "}, + {0, "Unknown"}, +}; + +void get_ddr_manufacture_name(void) +{ + uint32 i, length; + + length = ARRAY_SIZE(ddr_manufacture_list); + if (project_info_desc_v1 || project_info_desc_v2) { + for (i = 0; i < length; i++) { + if (ddr_manufacture_list[i].id == + GET_PROJECT_INFO(ddr_manufacture_info)) { + snprintf(ddr_manufacture, sizeof(ddr_manufacture), "%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_v1 || project_info_desc_v2) { + for (i = 0; i < length; i++) { + if (cpu_list_msm[i].id == + GET_PROJECT_INFO(platform_id)) { + snprintf(cpu_type, sizeof(cpu_type), + "%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[16] = {0}; +static char rf_version[16] = {0}; + +struct a_borad_version{ + + int version; + char name[16]; +}; + +struct a_borad_version a_borad_version_string_arry[]={ + + {1, "TMO"}, + {2, "US/EU" }, + {3, "CN/IN"}, + {4, "Unknown"}, + {5, "Unknown" }, +}; + + +struct a_borad_version a_borad_version_string_arry_gpio[]={ + + {0, "TMO"}, + {1, "US/EU/TMO" }, + {2, "CN/IN"}, + {3, "Unknown"}, + {4, "Unknown" }, + {5, "Unknown"}, + +}; + +uint32 get_hw_version(void) +{ + size_t size; + + if (strnstr(saved_command_line, "androidboot.platform_name=", strlen(saved_command_line))) + project_info_desc_v2 = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_PROJECT_INFO, + &size); + else + project_info_desc_v1 = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_PROJECT_INFO, + &size); + + if (IS_ERR_OR_NULL(project_info_desc_v1) && IS_ERR_OR_NULL(project_info_desc_v2)) + pr_err("%s: get project_info failure\n", __func__); + else { + pr_err("%s: hw version: %d\n", __func__, + GET_PROJECT_INFO(hw_version)); + return GET_PROJECT_INFO(hw_version); + } + return 0; +} + +int __init init_project_info(void) +{ + static bool project_info_init_done; + int ddr_size = 0; + size_t size; + + if (project_info_init_done) + return 0; + + if (strnstr(saved_command_line, "androidboot.platform_name=", strlen(saved_command_line))) + project_info_desc_v2 = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_PROJECT_INFO, + &size); + else + project_info_desc_v1 = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_PROJECT_INFO, + &size); + + if (IS_ERR_OR_NULL(project_info_desc_v1) && IS_ERR_OR_NULL(project_info_desc_v2)) { + 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__, GET_PROJECT_INFO(project_name), + GET_PROJECT_INFO(hw_version), + GET_PROJECT_INFO(rf_v1), + GET_PROJECT_INFO(rf_v2), + GET_PROJECT_INFO(rf_v3), + GET_PROJECT_INFO(platform_id)); + + switch (GET_PROJECT_INFO(hw_version)) { + case 11: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "EVB"); + break; + case 12: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "T0"); + break; + case 13: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "T1"); + break; + case 14: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "EVT1"); + break; + case 15: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "EVT2"); + break; + case 21: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "DVT"); + break; + case 22: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT"); + break; + case 23: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT"); + break; + case 24: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT_MCU"); + break; + case 25: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "DVTBACKUP"); + break; + + case 31: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "EVB"); + break; + case 32: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "T0"); + break; + case 33: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "EVT1"); + break; + case 34: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "DVT"); + break; + case 35: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "2ND"); + break; + case 41: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT"); + break; + case 42: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT2nd"); + break; + case 43: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVT-1"); + break; + case 44: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "PVTSpec"); + break; + case 45: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "MPSpec"); + break; + case 55: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + GET_PROJECT_INFO(project_name), "DVTUSB30"); + break; + + default: + snprintf(mainboard_version, sizeof(mainboard_version), "%d", + GET_PROJECT_INFO(hw_version)); + break; + } + push_component_info(MAINBOARD, + mainboard_version, + mainboard_manufacture); + + if( GET_PROJECT_INFO(hw_version) <= 32 ) { + snprintf(Aboard_version, sizeof(Aboard_version), "%d %s", + GET_PROJECT_INFO(a_board_version),GET_PROJECT_INFO(a_board_version) <=5 ? + a_borad_version_string_arry[GET_PROJECT_INFO(a_board_version) -1].name:"Unknown"); + + push_component_info(ABOARD, Aboard_version, mainboard_manufacture); + pr_err("%s: Aboard_gpio(%s)\n", __func__, Aboard_version); + + } + + snprintf(rf_version, sizeof(rf_version), " %d",GET_PROJECT_INFO(rf_v1)); + push_component_info(RF_VERSION, rf_version, mainboard_manufacture); + + get_ddr_manufacture_name(); + if (totalram_pages > 9*(1<<18)) + ddr_size = 10; + else 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, GET_PROJECT_INFO(ddr_row), + GET_PROJECT_INFO(ddr_column)); + snprintf(ddr_manufacture_and_fw_verion, + sizeof(ddr_manufacture_and_fw_verion), + "%s%s %u.%u", ddr_manufacture, + GET_PROJECT_INFO(ddr_reserve_info) == 0x05 ? "20nm" : + (GET_PROJECT_INFO(ddr_reserve_info) == 0x06 ? "18nm" : " "), + GET_PROJECT_INFO(ddr_fw_version) >> 16, + GET_PROJECT_INFO(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; +} +struct aborad_data { + int aboard_gpio_0;//gpio33 + int aboard_gpio_1;//gpio34 + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + + struct device *dev; +}; +static struct aborad_data *data = NULL ; + +static int op_aboard_request_named_gpio(const char *label, int *gpio) +{ + struct device *dev = data->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_aboard_read_gpio(void) +{ + int gpio0 = 0; + int gpio1 = 0; + if ( data == NULL ) + { + return 0 ; + } + gpio0 = gpio_get_value(data->aboard_gpio_0); + gpio1 = gpio_get_value(data->aboard_gpio_1); + + pr_err("%s: gpio0=%d gpio1=%d\n", __func__, gpio0,gpio1); + + if( gpio0 == 0 && gpio1 == 0 ) + { + SET_PROJECT_INFO(a_board_version, 0); + } + else if( gpio0 == 0 && gpio1 == 1 ) + { + SET_PROJECT_INFO(a_board_version, 1); + } + else if( gpio0 == 1 && gpio1 == 0 ) + { + SET_PROJECT_INFO(a_board_version, 2); + } + else + { + SET_PROJECT_INFO(a_board_version, -1); + } + snprintf(Aboard_version, sizeof(Aboard_version), "%d %s", + GET_PROJECT_INFO(a_board_version),GET_PROJECT_INFO(a_board_version) <3 ? + a_borad_version_string_arry_gpio[GET_PROJECT_INFO(a_board_version)].name:"Unknown"); + + push_component_info(ABOARD, Aboard_version, mainboard_manufacture); + pr_err("%s: Aboard_gpio(%s)\n", __func__, Aboard_version); + return 0 ; + +} + +static int oem_aboard_probe(struct platform_device *pdev) +{ + int rc = 0; + struct device *dev = &pdev->dev; + + data = kzalloc(sizeof(struct aborad_data), GFP_KERNEL); + if (!data) { + pr_err("%s: failed to allocate memory\n", __func__); + rc = -ENOMEM; + goto exit; + } + + data->dev = dev; + + rc = op_aboard_request_named_gpio("oem,aboard-gpio-0",&data->aboard_gpio_0); + if (rc) { + pr_err("%s: op_rf_request_named_gpio gpio-0 fail\n", + __func__); + goto exit_gpio; + } + rc = op_aboard_request_named_gpio("oem,aboard-gpio-1",&data->aboard_gpio_1); + if (rc) { + pr_err("%s: op_rf_request_named_gpio gpio-1 fail\n", + __func__); + goto exit_gpio; + } + + data->pinctrl = devm_pinctrl_get((data->dev)); + if (IS_ERR_OR_NULL(data->pinctrl)) { + rc = PTR_ERR(data->pinctrl); + pr_err("%s pinctrl error!\n",__func__); + goto err_pinctrl_get; + } + + data->pinctrl_state_active = pinctrl_lookup_state(data->pinctrl, "oem_aboard_active"); + + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) { + rc = PTR_ERR(data->pinctrl_state_active); + pr_err("%s pinctrl state active error!\n",__func__); + goto err_pinctrl_lookup; + } + + if (data->pinctrl) { + rc = pinctrl_select_state(data->pinctrl,data->pinctrl_state_active); + } + + gpio_direction_input(data->aboard_gpio_0); + gpio_direction_input(data->aboard_gpio_1); + op_aboard_read_gpio(); + pr_err("%s: probe ok!\n", __func__); + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(data->pinctrl); +err_pinctrl_get: + data->pinctrl = NULL; +exit_gpio: + kfree(data); +exit: + pr_err("%s: probe Fail!\n", __func__); + + return rc; +} + +static const struct of_device_id aboard_of_match[] = { + { .compatible = "oem,aboard", }, + {} +}; +MODULE_DEVICE_TABLE(of, aboard_of_match); + +static struct platform_driver aboard_driver = { + .driver = { + .name = "op_aboard", + .owner = THIS_MODULE, + .of_match_table = aboard_of_match, + }, + .probe = oem_aboard_probe, +}; + +static int __init init_project(void) +{ + int ret; + + init_project_info(); + + if( GET_PROJECT_INFO(hw_version) > 32 ){ + ret = platform_driver_register(&aboard_driver); + if (ret) + pr_err("aboard_driver register failed: %d\n", ret); + }else { + ret = 0; + } + return ret; +} + + +subsys_initcall(init_project); diff --git a/drivers/soc/qcom/qsee_ipc_irq.c b/drivers/soc/qcom/qsee_ipc_irq.c index 4db221999139..79b3dcbe5403 100644 --- a/drivers/soc/qcom/qsee_ipc_irq.c +++ b/drivers/soc/qcom/qsee_ipc_irq.c @@ -331,6 +331,7 @@ static const struct qsee_irq_data qsee_irq_data_init[] = { }; static const struct of_device_id qsee_irq_of_match[] = { + { .compatible = "qcom,sdm845-qsee-irq", .data = &qsee_irq_data_init}, { .compatible = "qcom,sm8150-qsee-irq", .data = &qsee_irq_data_init}, { .compatible = "qcom,kona-qsee-irq", .data = &qsee_irq_data_init}, {}, diff --git a/drivers/soc/qcom/qtee_shmbridge.c b/drivers/soc/qcom/qtee_shmbridge.c index d064860c11a8..c34a1cec1086 100644 --- a/drivers/soc/qcom/qtee_shmbridge.c +++ b/drivers/soc/qcom/qtee_shmbridge.c @@ -123,6 +123,10 @@ static int32_t qtee_shmbridge_enable(bool enable) return ret; } +#ifdef CONFIG_ARCH_SDM845 + return ret; +#endif + desc.arginfo = TZ_SHM_BRIDGE_ENABLE_PARAM_ID; ret = scm_call2(TZ_SHM_BRIDGE_ENABLE, &desc); if (ret || desc.ret[0]) { diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index d1b52f7e548a..df794df11d29 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -341,6 +341,9 @@ static struct msm_soc_info cpu_of_id[] = { /* SDM660 ID */ [317] = {MSM_CPU_SDM660, "SDM660"}, + /* SDM845 ID */ + [321] = {MSM_CPU_SDM845, "SDM845"}, + /* sm8150 ID */ [339] = {MSM_CPU_SM8150, "SM8150"}, @@ -1546,6 +1549,10 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 317; strlcpy(dummy_socinfo.build_id, "sdm660 - ", sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_sdm845()) { + dummy_socinfo.id = 321; + strlcpy(dummy_socinfo.build_id, "sdm845 - ", + sizeof(dummy_socinfo.build_id)); } else if (early_machine_is_sm8150()) { dummy_socinfo.id = 339; strlcpy(dummy_socinfo.build_id, "sm8150 - ", diff --git a/drivers/soc/qcom/spcom-legacy.c b/drivers/soc/qcom/spcom-legacy.c new file mode 100644 index 000000000000..64fb72f8a393 --- /dev/null +++ b/drivers/soc/qcom/spcom-legacy.c @@ -0,0 +1,2215 @@ +/* + * Copyright (c) 2015-2019, 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. + */ + +/* + * Secure-Processor-Communication (SPCOM). + * + * This driver provides communication to Secure Processor (SP) + * over RPMSG framework. + * + * It provides interface to userspace spcomlib. + * + * Userspace application shall use spcomlib for communication with SP. Userspace + * application can be either client or server. spcomlib shall use write() file + * operation to send data, and read() file operation to read data. + * + * This driver uses RPMSG with glink-spss as a transport layer. + * This driver exposes "/dev/" file node for each rpmsg logical + * channel. + * This driver exposes "/dev/spcom" file node for some debug/control command. + * The predefined channel "/dev/sp_kernel" is used for loading SP application + * from HLOS. + * This driver exposes "/dev/sp_ssr" file node to allow user space poll for SSR. + * After the remote SP App is loaded, this driver exposes a new file node + * "/dev/" for the matching HLOS App to use. + * The access to predefined file nodes and dynamically allocated file nodes is + * restricted by using unix group and SELinux. + * + * No message routing is used, but using the rpmsg/G-Link "multiplexing" feature + * to use a dedicated logical channel for HLOS and SP Application-Pair. + * + * Each HLOS/SP Application can be either Client or Server or both, + * Messaging is allways point-to-point between 2 HLOS<=>SP applications. + * Each channel exclusevly used by single Client or Server. + * + * User Space Request & Response are synchronous. + * read() & write() operations are blocking until completed or terminated. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include /* min() */ +#include /* MODULE_LICENSE */ +#include /* class_create() */ +#include /* kzalloc() */ +#include /* file_operations */ +#include /* cdev_add() */ +#include /* EINVAL, ETIMEDOUT */ +#include /* pr_err() */ +#include /* BIT(x) */ +#include /* wait_for_completion_timeout() */ +#include /* POLLOUT */ +#include +#include /* of_property_count_strings() */ +#include +#include /* msleep() */ +#include +#include +#include +#include +#include +#include +#include + +/** + * Request buffer size. + * Any large data (multiply of 4KB) is provided by temp buffer in DDR. + * Request shall provide the temp buffer physical address (align to 4KB). + * Maximum request/response size of 268 is used to accommodate APDU size. + * From kernel spcom driver perspective a PAGE_SIZE of 4K + * is the actual maximum size for a single read/write file operation. + */ +#define SPCOM_MAX_RESPONSE_SIZE 268 + +/* SPCOM driver name */ +#define DEVICE_NAME "spcom" + +/* maximum ION buffers should be >= SPCOM_MAX_CHANNELS */ +#define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4) + +/* maximum ION buffer per send request/response command */ +#define SPCOM_MAX_ION_BUF_PER_CMD SPCOM_MAX_ION_BUF + +/* Maximum command size */ +#define SPCOM_MAX_COMMAND_SIZE (PAGE_SIZE) + +/* Maximum input size */ +#define SPCOM_MAX_READ_SIZE (PAGE_SIZE) + +/* Current Process ID */ +#define current_pid() ((u32)(current->pid)) + +/* + * After both sides get CONNECTED, + * there is a race between one side queueing rx buffer and the other side + * trying to call glink_tx() , this race is only on the 1st tx. + * Do tx retry with some delay to allow the other side to queue rx buffer. + */ +#define TX_RETRY_DELAY_MSEC 100 + +/* SPCOM_MAX_REQUEST_SIZE-or-SPCOM_MAX_RESPONSE_SIZE + header */ +#define SPCOM_RX_BUF_SIZE 300 + +/* + * Initial transaction id, use non-zero nonce for debug. + * Incremented by client on request, and copied back by server on response. + */ +#define INITIAL_TXN_ID 0x12345678 + +/** + * struct spcom_msg_hdr - Request/Response message header between HLOS and SP. + * + * This header is proceeding any request specific parameters. + * The transaction id is used to match request with response. + * Note: rpmsg API provides the rx/tx data size, so user payload size is + * calculated by reducing the header size. + */ +struct spcom_msg_hdr { + uint32_t reserved; /* for future use */ + uint32_t txn_id; /* transaction id */ + char buf[0]; /* Variable buffer size, must be last field */ +} __packed; + +/** + * struct spcom_client - Client handle + */ +struct spcom_client { + struct spcom_channel *ch; +}; + +/** + * struct spcom_server - Server handle + */ +struct spcom_server { + struct spcom_channel *ch; +}; + +/** + * struct spcom_channel - channel context + */ +struct spcom_channel { + char name[SPCOM_CHANNEL_NAME_SIZE]; + struct mutex lock; + uint32_t txn_id; /* incrementing nonce per client request */ + bool is_server; /* for txn_id and response_timeout_msec */ + bool comm_role_undefined; /* is true on channel creation, before */ + /* first tx/rx on channel */ + uint32_t response_timeout_msec; /* for client only */ + + /* char dev */ + struct cdev *cdev; + struct device *dev; + struct device_attribute attr; + + /* rpmsg */ + struct rpmsg_driver *rpdrv; + struct rpmsg_device *rpdev; + + /* Events notification */ + struct completion rx_done; + struct completion connect; + + /* + * Only one client or server per channel. + * Only one rx/tx transaction at a time (request + response). + */ + bool is_busy; + + u32 pid; /* debug only to find user space application */ + + /* abort flags */ + bool rpmsg_abort; + + /* rx data info */ + size_t actual_rx_size; /* actual data size received */ + void *rpmsg_rx_buf; + + /* shared buffer lock/unlock support */ + int dmabuf_fd_table[SPCOM_MAX_ION_BUF_PER_CH]; + struct dma_buf *dmabuf_handle_table[SPCOM_MAX_ION_BUF_PER_CH]; +}; + +/** + * struct rx_buff_list - holds rx rpmsg data, before it will be consumed + * by spcom_signal_rx_done worker, item per rx packet + */ +struct rx_buff_list { + struct list_head list; + + void *rpmsg_rx_buf; + int rx_buf_size; + struct spcom_channel *ch; +}; + +/** + * struct spcom_device - device state structure. + */ +struct spcom_device { + char predefined_ch_name[SPCOM_MAX_CHANNELS][SPCOM_CHANNEL_NAME_SIZE]; + + /* char device info */ + struct cdev cdev; + dev_t device_no; + struct class *driver_class; + struct device *class_dev; + struct platform_device *pdev; + + /* rpmsg channels */ + struct spcom_channel channels[SPCOM_MAX_CHANNELS]; + atomic_t chdev_count; + + struct completion rpmsg_state_change; + atomic_t rpmsg_dev_count; + + /* rx data path */ + struct list_head rx_list_head; + spinlock_t rx_lock; +}; + +/* Device Driver State */ +static struct spcom_device *spcom_dev; + +/* static functions declaration */ +static int spcom_create_channel_chardev(const char *name); +static struct spcom_channel *spcom_find_channel_by_name(const char *name); +static int spcom_register_rpmsg_drv(struct spcom_channel *ch); +static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch); + +/** + * spcom_is_channel_open() - channel is open on this side. + * + * Channel is fully connected, when rpmsg driver is registered and + * rpmsg device probed + */ +static inline bool spcom_is_channel_open(struct spcom_channel *ch) +{ + return ch->rpdrv != NULL; +} + +/** + * spcom_is_channel_connected() - channel is fully connected by both sides. + */ +static inline bool spcom_is_channel_connected(struct spcom_channel *ch) +{ + /* Channel must be open before it gets connected */ + if (!spcom_is_channel_open(ch)) + return false; + + return ch->rpdev != NULL; +} + +/** + * spcom_create_predefined_channels_chardev() - expose predefined channels to + * user space. + * + * Predefined channels list is provided by device tree. Typically, it is for + * known servers on remote side that are not loaded by the HLOS + */ +static int spcom_create_predefined_channels_chardev(void) +{ + int i; + int ret; + static bool is_predefined_created; + + if (is_predefined_created) + return 0; + + for (i = 0; i < SPCOM_MAX_CHANNELS; i++) { + const char *name = spcom_dev->predefined_ch_name[i]; + + if (name[0] == 0) + break; + ret = spcom_create_channel_chardev(name); + if (ret) { + pr_err("failed to create chardev [%s], ret [%d].\n", + name, ret); + return -EFAULT; + } + } + + is_predefined_created = true; + + return 0; +} + +/*======================================================================*/ +/* UTILITIES */ +/*======================================================================*/ + +/** + * spcom_init_channel() - initialize channel state. + * + * @ch: channel state struct pointer + * @name: channel name + */ +static int spcom_init_channel(struct spcom_channel *ch, const char *name) +{ + if (!ch || !name || !name[0]) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + + strlcpy(ch->name, name, SPCOM_CHANNEL_NAME_SIZE); + + init_completion(&ch->rx_done); + init_completion(&ch->connect); + + mutex_init(&ch->lock); + ch->rpdrv = NULL; + ch->rpdev = NULL; + ch->actual_rx_size = 0; + ch->is_busy = false; + ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */ + ch->pid = 0; + ch->rpmsg_abort = false; + ch->rpmsg_rx_buf = NULL; + ch->comm_role_undefined = true; + + return 0; +} + +/** + * spcom_find_channel_by_name() - find a channel by name. + * + * @name: channel name + * + * Return: a channel state struct. + */ +static struct spcom_channel *spcom_find_channel_by_name(const char *name) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(spcom_dev->channels); i++) { + struct spcom_channel *ch = &spcom_dev->channels[i]; + + if (strcmp(ch->name, name) == 0) + return ch; + } + + return NULL; +} + +/** + * spcom_rx() - wait for received data until timeout, unless pending rx data is + * already ready + * + * @ch: channel state struct pointer + * @buf: buffer pointer + * @size: buffer size + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_rx(struct spcom_channel *ch, + void *buf, + uint32_t size, + uint32_t timeout_msec) +{ + unsigned long jiffies = msecs_to_jiffies(timeout_msec); + long timeleft = 1; + int ret = 0; + + mutex_lock(&ch->lock); + + /* check for already pending data */ + if (!ch->actual_rx_size) { + reinit_completion(&ch->rx_done); + + mutex_unlock(&ch->lock); /* unlock while waiting */ + /* wait for rx response */ + pr_debug("wait for rx done, timeout_msec=%d\n", timeout_msec); + if (timeout_msec) + timeleft = wait_for_completion_interruptible_timeout( + &ch->rx_done, jiffies); + else + ret = wait_for_completion_interruptible(&ch->rx_done); + + mutex_lock(&ch->lock); + if (timeout_msec && timeleft == 0) { + ch->txn_id++; /* to drop expired rx packet later */ + pr_err("rx_done timeout expired %d ms, set txn_id=%d\n", + timeout_msec, ch->txn_id); + ret = -ETIMEDOUT; + goto exit_err; + } else if (ch->rpmsg_abort) { + pr_warn("rpmsg channel is closing\n"); + ret = -ERESTART; + goto exit_err; + } else if (ret < 0 || timeleft == -ERESTARTSYS) { + pr_debug("wait interrupted: ret=%d, timeleft=%ld\n", + ret, timeleft); + if (timeleft == -ERESTARTSYS) + ret = -ERESTARTSYS; + goto exit_err; + } else if (ch->actual_rx_size) { + pr_debug("actual_rx_size is [%zu], txn_id %d\n", + ch->actual_rx_size, ch->txn_id); + } else { + pr_err("actual_rx_size is zero.\n"); + ret = -EFAULT; + goto exit_err; + } + } else { + pr_debug("pending data size [%zu], requested size [%u], ch->txn_id %d\n", + ch->actual_rx_size, size, ch->txn_id); + } + if (!ch->rpmsg_rx_buf) { + pr_err("invalid rpmsg_rx_buf.\n"); + ret = -ENOMEM; + goto exit_err; + } + + size = min_t(size_t, ch->actual_rx_size, size); + memcpy(buf, ch->rpmsg_rx_buf, size); + + pr_debug("copy size [%d].\n", (int) size); + + memset(ch->rpmsg_rx_buf, 0, ch->actual_rx_size); + kfree((void *)ch->rpmsg_rx_buf); + ch->rpmsg_rx_buf = NULL; + ch->actual_rx_size = 0; + + mutex_unlock(&ch->lock); + + return size; +exit_err: + mutex_unlock(&ch->lock); + return ret; +} + +/** + * spcom_get_next_request_size() - get request size. + * already ready + * + * @ch: channel state struct pointer + * + * Server needs the size of the next request to allocate a request buffer. + * Initially used intent-request, however this complicated the remote side, + * so both sides are not using glink_tx() with INTENT_REQ anymore. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_get_next_request_size(struct spcom_channel *ch) +{ + int size = -1; + int ret = 0; + + /* NOTE: Remote clients might not be connected yet.*/ + mutex_lock(&ch->lock); + reinit_completion(&ch->rx_done); + + /* check if already got it via callback */ + if (ch->actual_rx_size) { + pr_debug("next-req-size already ready ch [%s] size [%zu]\n", + ch->name, ch->actual_rx_size); + ret = -EFAULT; + goto exit_ready; + } + mutex_unlock(&ch->lock); /* unlock while waiting */ + + pr_debug("Wait for Rx Done, ch [%s].\n", ch->name); + ret = wait_for_completion_interruptible(&ch->rx_done); + if (ret < 0) { + pr_debug("ch [%s]:interrupted wait ret=%d\n", + ch->name, ret); + goto exit_error; + } + + mutex_lock(&ch->lock); /* re-lock after waiting */ + + if (ch->actual_rx_size == 0) { + pr_err("invalid rx size [%zu] ch [%s]\n", + ch->actual_rx_size, ch->name); + mutex_unlock(&ch->lock); + ret = -EFAULT; + goto exit_error; + } + +exit_ready: + /* actual_rx_size not exeeds SPCOM_RX_BUF_SIZE*/ + size = (int)ch->actual_rx_size; + if (size > sizeof(struct spcom_msg_hdr)) { + size -= sizeof(struct spcom_msg_hdr); + } else { + pr_err("rx size [%d] too small.\n", size); + ret = -EFAULT; + mutex_unlock(&ch->lock); + goto exit_error; + } + + mutex_unlock(&ch->lock); + return size; + +exit_error: + return ret; +} + +/*======================================================================*/ +/* USER SPACE commands handling */ +/*======================================================================*/ + +/** + * spcom_handle_create_channel_command() - Handle Create Channel command from + * user space. + * + * @cmd_buf: command buffer. + * @cmd_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size) +{ + int ret = 0; + struct spcom_user_create_channel_command *cmd = cmd_buf; + const char *ch_name; + const size_t maxlen = sizeof(cmd->ch_name); + + if (cmd_size != sizeof(*cmd)) { + pr_err("cmd_size [%d] , expected [%d].\n", + (int) cmd_size, (int) sizeof(*cmd)); + return -EINVAL; + } + + ch_name = cmd->ch_name; + if (strnlen(cmd->ch_name, maxlen) == maxlen) { + pr_err("channel name is not NULL terminated\n"); + return -EINVAL; + } + + pr_debug("ch_name [%s].\n", ch_name); + + ret = spcom_create_channel_chardev(ch_name); + + return ret; +} + +/** + * spcom_handle_restart_sp_command() - Handle Restart SP command from + * user space. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_restart_sp_command(void) +{ + void *subsystem_get_retval = NULL; + + pr_debug("restart - PIL FW loading process initiated\n"); + + subsystem_get_retval = subsystem_get("spss"); + if (!subsystem_get_retval) { + pr_err("restart - unable to trigger PIL process for FW loading\n"); + return -EINVAL; + } + + pr_debug("restart - PIL FW loading process is complete\n"); + return 0; +} + +/** + * spcom_handle_send_command() - Handle send request/response from user space. + * + * @buf: command buffer. + * @buf_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_send_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int ret = 0; + struct spcom_send_command *cmd = cmd_buf; + uint32_t buf_size; + void *buf; + struct spcom_msg_hdr *hdr; + void *tx_buf; + int tx_buf_size; + uint32_t timeout_msec; + int time_msec = 0; + + pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size); + + /* + * check that cmd buf size is at least struct size, + * to allow access to struct fields. + */ + if (size < sizeof(*cmd)) { + pr_err("ch [%s] invalid cmd buf.\n", + ch->name); + return -EINVAL; + } + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* parse command buffer */ + buf = &cmd->buf; + buf_size = cmd->buf_size; + timeout_msec = cmd->timeout_msec; + + /* Check param validity */ + if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid buf size [%d].\n", + ch->name, buf_size); + return -EINVAL; + } + if (size != sizeof(*cmd) + buf_size) { + pr_err("ch [%s] invalid cmd size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + tx_buf_size = sizeof(*hdr) + buf_size; + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + /* Prepare Tx Buf */ + hdr = tx_buf; + + mutex_lock(&ch->lock); + if (ch->comm_role_undefined) { + pr_debug("ch [%s] send first -> it is client\n", ch->name); + ch->comm_role_undefined = false; + ch->is_server = false; + } + + if (!ch->is_server) { + ch->txn_id++; /* client sets the request txn_id */ + ch->response_timeout_msec = timeout_msec; + } + hdr->txn_id = ch->txn_id; + + /* user buf */ + memcpy(hdr->buf, buf, buf_size); + + time_msec = 0; + do { + if (ch->rpmsg_abort) { + pr_err("ch [%s] aborted\n", ch->name); + ret = -ECANCELED; + break; + } + /* may fail when RX intent not queued by SP */ + ret = rpmsg_trysend(ch->rpdev->ept, tx_buf, tx_buf_size); + if (ret == 0) + break; + time_msec += TX_RETRY_DELAY_MSEC; + mutex_unlock(&ch->lock); + msleep(TX_RETRY_DELAY_MSEC); + mutex_lock(&ch->lock); + } while ((ret == -EBUSY || ret == -EAGAIN) && time_msec < timeout_msec); + if (ret) + pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", + ch->name, ret, timeout_msec); + mutex_unlock(&ch->lock); + + kfree(tx_buf); + return ret; +} + +/** + * modify_ion_addr() - replace the ION buffer virtual address with physical + * address in a request or response buffer. + * + * @buf: buffer to modify + * @buf_size: buffer size + * @ion_info: ION buffer info such as FD and offset in buffer. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int modify_ion_addr(void *buf, + uint32_t buf_size, + struct spcom_ion_info ion_info) +{ + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sg = NULL; + dma_addr_t phy_addr = 0; + int fd, ret = 0; + uint32_t buf_offset; + char *ptr = (char *)buf; + + fd = ion_info.fd; + buf_offset = ion_info.buf_offset; + ptr += buf_offset; + + if (fd < 0) { + pr_err("invalid fd [%d].\n", fd); + return -ENODEV; + } + + if (buf_size < sizeof(uint64_t)) { + pr_err("buf size too small [%d].\n", buf_size); + return -ENODEV; + } + + if (buf_offset % sizeof(uint64_t)) + pr_debug("offset [%d] is NOT 64-bit aligned.\n", buf_offset); + else + pr_debug("offset [%d] is 64-bit aligned.\n", buf_offset); + + if (buf_offset > buf_size - sizeof(uint64_t)) { + pr_err("invalid buf_offset [%d].\n", buf_offset); + return -ENODEV; + } + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + pr_err("fail to get dma buf handle.\n"); + return -EINVAL; + } + pr_debug("dma_buf handle ok.\n"); + attach = dma_buf_attach(dma_buf, &spcom_dev->pdev->dev); + if (IS_ERR_OR_NULL(attach)) { + ret = PTR_ERR(attach); + pr_err("fail to attach dma buf %d\n", ret); + dma_buf_put(dma_buf); + goto mem_map_table_failed; + } + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sg)) { + ret = PTR_ERR(sg); + pr_err("fail to get sg table of dma buf %d\n", ret); + goto mem_map_table_failed; + } + if (sg->sgl) { + phy_addr = sg->sgl->dma_address; + } else { + pr_err("sgl is NULL\n"); + ret = -ENOMEM; + goto mem_map_sg_failed; + } + + /* Set the physical address at the buffer offset */ + pr_debug("ion phys addr = [0x%lx].\n", (long int) phy_addr); + memcpy(ptr, &phy_addr, sizeof(phy_addr)); + +mem_map_sg_failed: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +mem_map_table_failed: + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + + return ret; +} + +/** + * spcom_handle_send_modified_command() - send a request/response with ION + * buffer address. Modify the request/response by replacing the ION buffer + * virtual address with the physical address. + * + * @ch: channel pointer + * @cmd_buf: User space command buffer + * @size: size of user command buffer + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_send_modified_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int ret = 0; + struct spcom_user_send_modified_command *cmd = cmd_buf; + uint32_t buf_size; + void *buf; + struct spcom_msg_hdr *hdr; + void *tx_buf; + int tx_buf_size; + struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF_PER_CMD]; + int i; + uint32_t timeout_msec; + int time_msec = 0; + + pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size); + + /* + * check that cmd buf size is at least struct size, + * to allow access to struct fields. + */ + if (size < sizeof(*cmd)) { + pr_err("ch [%s] invalid cmd buf.\n", + ch->name); + return -EINVAL; + } + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* parse command buffer */ + buf = &cmd->buf; + buf_size = cmd->buf_size; + timeout_msec = cmd->timeout_msec; + memcpy(ion_info, cmd->ion_info, sizeof(ion_info)); + + /* Check param validity */ + if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid buf size [%d].\n", + ch->name, buf_size); + return -EINVAL; + } + if (size != sizeof(*cmd) + buf_size) { + pr_err("ch [%s] invalid cmd size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + tx_buf_size = sizeof(*hdr) + buf_size; + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + /* Prepare Tx Buf */ + hdr = tx_buf; + + mutex_lock(&ch->lock); + if (ch->comm_role_undefined) { + pr_debug("ch [%s] send first -> it is client\n", ch->name); + ch->comm_role_undefined = false; + ch->is_server = false; + } + if (!ch->is_server) { + ch->txn_id++; /* client sets the request txn_id */ + ch->response_timeout_msec = timeout_msec; + } + hdr->txn_id = ch->txn_id; + + /* user buf */ + memcpy(hdr->buf, buf, buf_size); + + for (i = 0 ; i < ARRAY_SIZE(ion_info) ; i++) { + if (ion_info[i].fd >= 0) { + ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]); + if (ret < 0) { + mutex_unlock(&ch->lock); + pr_err("modify_ion_addr() error [%d].\n", ret); + memset(tx_buf, 0, tx_buf_size); + kfree(tx_buf); + return -EFAULT; + } + } + } + + time_msec = 0; + do { + if (ch->rpmsg_abort) { + pr_err("ch [%s] aborted\n", ch->name); + ret = -ECANCELED; + break; + } + /* may fail when RX intent not queued by SP */ + ret = rpmsg_trysend(ch->rpdev->ept, tx_buf, tx_buf_size); + if (ret == 0) + break; + time_msec += TX_RETRY_DELAY_MSEC; + mutex_unlock(&ch->lock); + msleep(TX_RETRY_DELAY_MSEC); + mutex_lock(&ch->lock); + } while ((ret == -EBUSY || ret == -EAGAIN) && time_msec < timeout_msec); + if (ret) + pr_err("ch [%s] rpmsg_trysend() error (%d), timeout_msec=%d\n", + ch->name, ret, timeout_msec); + + mutex_unlock(&ch->lock); + memset(tx_buf, 0, tx_buf_size); + kfree(tx_buf); + return ret; +} + + +/** + * spcom_handle_lock_ion_buf_command() - Lock an shared buffer. + * + * Lock an shared buffer, prevent it from being free if the userspace App crash, + * while it is used by the remote subsystem. + */ +static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + struct spcom_user_command *cmd = cmd_buf; + int fd; + int i; + struct dma_buf *dma_buf; + + if (size != sizeof(*cmd)) { + pr_err("cmd size [%d] , expected [%d].\n", + (int) size, (int) sizeof(*cmd)); + return -EINVAL; + } + + if (cmd->arg > (unsigned int)INT_MAX) { + pr_err("int overflow [%u]\n", cmd->arg); + return -EINVAL; + } + fd = cmd->arg; + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + pr_err("fail to get dma buf handle.\n"); + return -EINVAL; + } + pr_debug("dma_buf referenced ok.\n"); + + /* shared buf lock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + + /* Check if this shared buffer is already locked */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (ch->dmabuf_handle_table[i] == dma_buf) { + pr_debug("fd [%d] shared buf is already locked.\n", fd); + /* decrement back the ref count */ + mutex_unlock(&ch->lock); + dma_buf_put(dma_buf); + return -EINVAL; + } + } + + /* Store the dma_buf handle */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (ch->dmabuf_handle_table[i] == NULL) { + ch->dmabuf_handle_table[i] = dma_buf; + ch->dmabuf_fd_table[i] = fd; + pr_debug("ch [%s] locked ion buf #%d fd [%d] dma_buf=%pK\n", + ch->name, i, + ch->dmabuf_fd_table[i], + ch->dmabuf_handle_table[i]); + mutex_unlock(&ch->lock); + return 0; + } + } + + mutex_unlock(&ch->lock); + /* decrement back the ref count */ + dma_buf_put(dma_buf); + pr_err("no free entry to store ion handle of fd [%d].\n", fd); + + return -EFAULT; +} + +/** + * spcom_handle_unlock_ion_buf_command() - Unlock an ION buffer. + * + * Unlock an ION buffer, let it be free, when it is no longer being used by + * the remote subsystem. + */ +static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int i; + struct spcom_user_command *cmd = cmd_buf; + int fd; + bool found = false; + struct dma_buf *dma_buf; + + if (size != sizeof(*cmd)) { + pr_err("cmd size [%d], expected [%d]\n", + (int)size, (int)sizeof(*cmd)); + return -EINVAL; + } + if (cmd->arg > (unsigned int)INT_MAX) { + pr_err("int overflow [%u]\n", cmd->arg); + return -EINVAL; + } + fd = cmd->arg; + + pr_debug("Unlock ion buf ch [%s] fd [%d].\n", ch->name, fd); + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + pr_err("fail to get dma buf handle\n"); + return -EINVAL; + } + dma_buf_put(dma_buf); + pr_debug("dma_buf referenced ok\n"); + + /* shared buf unlock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) { + pr_debug("unlocked ALL ion buf ch [%s].\n", ch->name); + found = true; + /* unlock all buf */ + for (i = 0; i < ARRAY_SIZE(ch->dmabuf_handle_table); i++) { + if (ch->dmabuf_handle_table[i] != NULL) { + pr_debug("unlocked ion buf #%d fd [%d]\n", + i, ch->dmabuf_fd_table[i]); + dma_buf_put(ch->dmabuf_handle_table[i]); + ch->dmabuf_handle_table[i] = NULL; + ch->dmabuf_fd_table[i] = -1; + } + } + } else { + /* unlock specific buf */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (!ch->dmabuf_handle_table[i]) + continue; + if (ch->dmabuf_handle_table[i] == dma_buf) { + pr_debug("ch [%s] unlocked ion buf #%d fd [%d] dma_buf=%pK\n", + ch->name, i, + ch->dmabuf_fd_table[i], + ch->dmabuf_handle_table[i]); + dma_buf_put(ch->dmabuf_handle_table[i]); + ch->dmabuf_handle_table[i] = NULL; + ch->dmabuf_fd_table[i] = -1; + found = true; + break; + } + } + } + mutex_unlock(&ch->lock); + + if (!found) { + pr_err("ch [%s] fd [%d] was not found.\n", ch->name, fd); + return -ENODEV; + } + + return 0; +} + +/** + * spcom_handle_write() - Handle user space write commands. + * + * @buf: command buffer. + * @buf_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_write(struct spcom_channel *ch, + void *buf, + int buf_size) +{ + int ret = 0; + struct spcom_user_command *cmd = NULL; + int cmd_id = 0; + + /* Minimal command should have command-id and argument */ + if (buf_size < sizeof(struct spcom_user_command)) { + pr_err("Command buffer size [%d] too small\n", buf_size); + return -EINVAL; + } + + cmd = (struct spcom_user_command *)buf; + cmd_id = (int) cmd->cmd_id; + + pr_debug("cmd_id [0x%x]\n", cmd_id); + + if (!ch && cmd_id != SPCOM_CMD_CREATE_CHANNEL + && cmd_id != SPCOM_CMD_RESTART_SP) { + pr_err("channel context is null\n"); + return -EINVAL; + } + + switch (cmd_id) { + case SPCOM_CMD_SEND: + ret = spcom_handle_send_command(ch, buf, buf_size); + break; + case SPCOM_CMD_SEND_MODIFIED: + ret = spcom_handle_send_modified_command(ch, buf, buf_size); + break; + case SPCOM_CMD_LOCK_ION_BUF: + ret = spcom_handle_lock_ion_buf_command(ch, buf, buf_size); + break; + case SPCOM_CMD_UNLOCK_ION_BUF: + ret = spcom_handle_unlock_ion_buf_command(ch, buf, buf_size); + break; + case SPCOM_CMD_CREATE_CHANNEL: + ret = spcom_handle_create_channel_command(buf, buf_size); + break; + case SPCOM_CMD_RESTART_SP: + ret = spcom_handle_restart_sp_command(); + break; + default: + pr_err("Invalid Command Id [0x%x].\n", (int) cmd->cmd_id); + ret = -EINVAL; + } + + return ret; +} + +/** + * spcom_handle_get_req_size() - Handle user space get request size command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_get_req_size(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret = -1; + uint32_t next_req_size = 0; + + if (size < sizeof(next_req_size)) { + pr_err("buf size [%d] too small.\n", (int) size); + return -EINVAL; + } + + ret = spcom_get_next_request_size(ch); + if (ret < 0) + return ret; + next_req_size = (uint32_t) ret; + + memcpy(buf, &next_req_size, sizeof(next_req_size)); + pr_debug("next_req_size [%d].\n", next_req_size); + + return sizeof(next_req_size); /* can't exceed user buffer size */ +} + +/** + * spcom_handle_read_req_resp() - Handle user space get request/response command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_read_req_resp(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret; + struct spcom_msg_hdr *hdr; + void *rx_buf; + int rx_buf_size; + uint32_t timeout_msec = 0; /* client only */ + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* Check param validity */ + if (size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + rx_buf_size = sizeof(*hdr) + size; + rx_buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!rx_buf) + return -ENOMEM; + + /* + * client response timeout depends on the request + * handling time on the remote side . + */ + if (!ch->is_server) { + timeout_msec = ch->response_timeout_msec; + pr_debug("response_timeout_msec = %d.\n", (int) timeout_msec); + } + + ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec); + if (ret < 0) { + pr_err("rx error %d.\n", ret); + goto exit_err; + } else { + size = ret; /* actual_rx_size */ + } + + hdr = rx_buf; + + if (ch->is_server) { + ch->txn_id = hdr->txn_id; + pr_debug("request txn_id [0x%x].\n", ch->txn_id); + } + + /* copy data to user without the header */ + if (size > sizeof(*hdr)) { + size -= sizeof(*hdr); + memcpy(buf, hdr->buf, size); + } else { + pr_err("rx size [%d] too small.\n", size); + ret = -EFAULT; + goto exit_err; + } + + kfree(rx_buf); + return size; +exit_err: + kfree(rx_buf); + return ret; +} + +/** + * spcom_handle_read() - Handle user space read request/response or + * request-size command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * A special size SPCOM_GET_NEXT_REQUEST_SIZE, which is bigger than the max + * response/request tells the kernel that user space only need the size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_read(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret = -1; + + if (size == SPCOM_GET_NEXT_REQUEST_SIZE) { + pr_debug("get next request size, ch [%s].\n", ch->name); + ch->is_server = true; + ret = spcom_handle_get_req_size(ch, buf, size); + } else { + pr_debug("get request/response, ch [%s].\n", ch->name); + ret = spcom_handle_read_req_resp(ch, buf, size); + } + + pr_debug("ch [%s] , size = %d.\n", ch->name, size); + + return ret; +} + +/*======================================================================*/ +/* CHAR DEVICE USER SPACE INTERFACE */ +/*======================================================================*/ + +/** + * file_to_filename() - get the filename from file pointer. + * + * @filp: file pointer + * + * it is used for debug prints. + * + * Return: filename string or "unknown". + */ +static char *file_to_filename(struct file *filp) +{ + struct dentry *dentry = NULL; + char *filename = NULL; + + if (!filp || !filp->f_path.dentry) + return "unknown"; + + dentry = filp->f_path.dentry; + filename = dentry->d_iname; + + return filename; +} + +/** + * spcom_device_open() - handle channel file open() from user space. + * + * @filp: file pointer + * + * The file name (without path) is the channel name. + * Register rpmsg driver matching with channel name. + * Store the channel context in the file private date pointer for future + * read/write/close operations. + */ +static int spcom_device_open(struct inode *inode, struct file *filp) +{ + struct spcom_channel *ch; + int ret; + const char *name = file_to_filename(filp); + u32 pid = current_pid(); + + pr_debug("open file [%s].\n", name); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (strcmp(name, DEVICE_NAME) == 0) { + pr_debug("root dir skipped.\n"); + return 0; + } + + if (strcmp(name, "sp_ssr") == 0) { + pr_debug("sp_ssr dev node skipped.\n"); + return 0; + } + + ch = spcom_find_channel_by_name(name); + if (!ch) { + pr_err("channel %s doesn't exist, load App first.\n", name); + return -ENODEV; + } + + mutex_lock(&ch->lock); + if (!spcom_is_channel_open(ch)) { + reinit_completion(&ch->connect); + /* channel was closed need to register drv again */ + ret = spcom_register_rpmsg_drv(ch); + if (ret < 0) { + pr_err("register rpmsg driver failed %d\n", ret); + mutex_unlock(&ch->lock); + return ret; + } + } + /* only one client/server may use the channel */ + if (ch->is_busy) { + pr_err("channel [%s] is BUSY, already in use by pid [%d].\n", + name, ch->pid); + mutex_unlock(&ch->lock); + return -EBUSY; + } + + ch->is_busy = true; + ch->pid = pid; + mutex_unlock(&ch->lock); + + filp->private_data = ch; + return 0; +} + +/** + * spcom_device_release() - handle channel file close() from user space. + * + * @filp: file pointer + * + * The file name (without path) is the channel name. + * Open the relevant glink channel. + * Store the channel context in the file private + * date pointer for future read/write/close + * operations. + */ +static int spcom_device_release(struct inode *inode, struct file *filp) +{ + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + int ret = 0; + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (strcmp(name, DEVICE_NAME) == 0) { + pr_debug("root dir skipped.\n"); + return 0; + } + + if (strcmp(name, "sp_ssr") == 0) { + pr_debug("sp_ssr dev node skipped.\n"); + return 0; + } + + ch = filp->private_data; + if (!ch) { + pr_debug("ch is NULL, file name %s.\n", file_to_filename(filp)); + return -ENODEV; + } + + mutex_lock(&ch->lock); + /* channel might be already closed or disconnected */ + if (!spcom_is_channel_open(ch)) { + pr_debug("ch [%s] already closed.\n", name); + mutex_unlock(&ch->lock); + return 0; + } + + ch->is_busy = false; + ch->pid = 0; + if (ch->rpmsg_rx_buf) { + pr_debug("ch [%s] discarting unconsumed rx packet actual_rx_size=%lu\n", + name, ch->actual_rx_size); + kfree(ch->rpmsg_rx_buf); + ch->rpmsg_rx_buf = NULL; + } + ch->actual_rx_size = 0; + mutex_unlock(&ch->lock); + filp->private_data = NULL; + + return ret; +} + +/** + * spcom_device_write() - handle channel file write() from user space. + * + * @filp: file pointer + * + * Return: On Success - same size as number of bytes to write. + * On Failure - negative value. + */ +static ssize_t spcom_device_write(struct file *filp, + const char __user *user_buff, + size_t size, loff_t *f_pos) +{ + int ret; + char *buf; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + int buf_size = 0; + + if (!user_buff || !f_pos || !filp) { + pr_err("invalid null parameters.\n"); + return -EINVAL; + } + + if (*f_pos != 0) { + pr_err("offset should be zero, no sparse buffer.\n"); + return -EINVAL; + } + + if (!name) { + pr_err("name is NULL\n"); + return -EINVAL; + } + pr_debug("write file [%s] size [%d] pos [%d].\n", + name, (int) size, (int) *f_pos); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + ch = filp->private_data; + if (!ch) { + if (strcmp(name, DEVICE_NAME) != 0) { + pr_err("invalid ch pointer, command not allowed.\n"); + return -EINVAL; + } + pr_debug("control device - no channel context.\n"); + } else { + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + } + + if (size > SPCOM_MAX_COMMAND_SIZE) { + pr_err("size [%d] > max size [%d].\n", + (int) size, (int) SPCOM_MAX_COMMAND_SIZE); + return -EINVAL; + } + buf_size = size; /* explicit casting size_t to int */ + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + ret = copy_from_user(buf, user_buff, size); + if (ret) { + pr_err("Unable to copy from user (err %d).\n", ret); + kfree(buf); + return -EFAULT; + } + + ret = spcom_handle_write(ch, buf, buf_size); + if (ret) { + pr_err("handle command error [%d].\n", ret); + kfree(buf); + return ret; + } + + kfree(buf); + + return size; +} + +/** + * spcom_device_read() - handle channel file read() from user space. + * + * @filp: file pointer + * + * Return: number of bytes to read on success, negative value on + * failure. + */ +static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, + size_t size, loff_t *f_pos) +{ + int ret = 0; + int actual_size = 0; + char *buf; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + uint32_t buf_size = 0; + + pr_debug("read file [%s], size = %d bytes.\n", name, (int) size); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (!user_buff || !f_pos || + (size == 0) || (size > SPCOM_MAX_READ_SIZE)) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + buf_size = size; /* explicit casting size_t to uint32_t */ + + ch = filp->private_data; + + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } + + if (!spcom_is_channel_open(ch)) { + pr_err("ch is not open, file [%s].\n", name); + return -EINVAL; + } + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + ret = spcom_handle_read(ch, buf, buf_size); + if (ret < 0) { + if (ret != -ERESTARTSYS) + pr_err("read error [%d].\n", ret); + kfree(buf); + return ret; + } + actual_size = ret; + if ((actual_size == 0) || (actual_size > size)) { + pr_err("invalid actual_size [%d].\n", actual_size); + kfree(buf); + return -EFAULT; + } + + ret = copy_to_user(user_buff, buf, actual_size); + if (ret) { + pr_err("Unable to copy to user, err = %d.\n", ret); + kfree(buf); + return -EFAULT; + } + + kfree(buf); + pr_debug("ch [%s] ret [%d].\n", name, (int) actual_size); + + return actual_size; +} + +/** + * spcom_device_poll() - handle channel file poll() from user space. + * + * @filp: file pointer + * + * This allows user space to wait/check for channel connection, + * or wait for SSR event. + * + * Return: event bitmask on success, set POLLERR on failure. + */ +static unsigned int spcom_device_poll(struct file *filp, + struct poll_table_struct *poll_table) +{ + /* + * when user call with timeout -1 for blocking mode, + * any bit must be set in response + */ + unsigned int ret = SPCOM_POLL_READY_FLAG; + unsigned long mask; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + bool wait = false; + bool done = false; + /* Event types always implicitly polled for */ + unsigned long reserved = POLLERR | POLLHUP | POLLNVAL; + int ready = 0; + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (!poll_table) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + + ch = filp->private_data; + mask = poll_requested_events(poll_table); + + pr_debug("== ch [%s] mask [0x%x] ==.\n", name, (int) mask); + + /* user space API has poll use "short" and not "long" */ + mask &= 0x0000FFFF; + + wait = mask & SPCOM_POLL_WAIT_FLAG; + if (wait) + pr_debug("ch [%s] wait for event flag is ON.\n", name); + + // mask will be used in output, clean input bits + mask &= (unsigned long)~SPCOM_POLL_WAIT_FLAG; + mask &= (unsigned long)~SPCOM_POLL_READY_FLAG; + mask &= (unsigned long)~reserved; + + switch (mask) { + case SPCOM_POLL_LINK_STATE: + pr_debug("ch [%s] SPCOM_POLL_LINK_STATE.\n", name); + if (wait) { + reinit_completion(&spcom_dev->rpmsg_state_change); + ready = wait_for_completion_interruptible( + &spcom_dev->rpmsg_state_change); + pr_debug("ch [%s] poll LINK_STATE signaled.\n", name); + } + done = atomic_read(&spcom_dev->rpmsg_dev_count) > 0; + break; + case SPCOM_POLL_CH_CONNECT: + /* + * ch is not expected to be NULL since user must call open() + * to get FD before it can call poll(). + * open() will fail if no ch related to the char-device. + */ + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return POLLERR; + } + pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); + if (wait) { + reinit_completion(&ch->connect); + ready = wait_for_completion_interruptible(&ch->connect); + pr_debug("ch [%s] poll CH_CONNECT signaled.\n", name); + } + mutex_lock(&ch->lock); + done = (ch->rpdev != NULL); + pr_debug("ch [%s] reported done=%d\n", name, done); + mutex_unlock(&ch->lock); + break; + default: + pr_err("ch [%s] poll, invalid mask [0x%x].\n", + name, (int) mask); + ret = POLLERR; + break; + } + + if (ready < 0) { /* wait was interrupted */ + pr_debug("ch [%s] poll interrupted, ret [%d].\n", name, ready); + ret = POLLERR | SPCOM_POLL_READY_FLAG | mask; + } + if (done) + ret |= mask; + + pr_debug("ch [%s] poll, mask = 0x%x, ret=0x%x.\n", + name, (int) mask, ret); + + return ret; +} + +/* file operation supported from user space */ +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = spcom_device_read, + .poll = spcom_device_poll, + .write = spcom_device_write, + .open = spcom_device_open, + .release = spcom_device_release, +}; + +/** + * spcom_create_channel_chardev() - Create a channel char-dev node file + * for user space interface + */ +static int spcom_create_channel_chardev(const char *name) +{ + int ret; + struct device *dev; + struct spcom_channel *ch; + dev_t devt; + struct class *cls = spcom_dev->driver_class; + struct device *parent = spcom_dev->class_dev; + void *priv; + struct cdev *cdev; + + pr_debug("Add channel [%s].\n", name); + + ch = spcom_find_channel_by_name(name); + if (ch) { + pr_err("channel [%s] already exist.\n", name); + return -EBUSY; + } + + ch = spcom_find_channel_by_name(""); /* find reserved channel */ + if (!ch) { + pr_err("no free channel.\n"); + return -ENODEV; + } + + ret = spcom_init_channel(ch, name); + if (ret < 0) { + pr_err("can't init channel %d\n", ret); + return ret; + } + + ret = spcom_register_rpmsg_drv(ch); + if (ret < 0) { + pr_err("register rpmsg driver failed %d\n", ret); + goto exit_destroy_channel; + } + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) { + ret = -ENOMEM; + goto exit_unregister_drv; + } + + devt = spcom_dev->device_no + atomic_read(&spcom_dev->chdev_count); + priv = ch; + dev = device_create(cls, parent, devt, priv, name); + if (IS_ERR(dev)) { + pr_err("device_create failed.\n"); + ret = -ENODEV; + goto exit_free_cdev; + } + + cdev_init(cdev, &fops); + cdev->owner = THIS_MODULE; + + ret = cdev_add(cdev, devt, 1); + if (ret < 0) { + pr_err("cdev_add failed %d\n", ret); + ret = -ENODEV; + goto exit_destroy_device; + } + atomic_inc(&spcom_dev->chdev_count); + mutex_lock(&ch->lock); + ch->cdev = cdev; + ch->dev = dev; + mutex_unlock(&ch->lock); + + return 0; + +exit_destroy_device: + device_destroy(spcom_dev->driver_class, devt); +exit_free_cdev: + kfree(cdev); +exit_unregister_drv: + ret = spcom_unregister_rpmsg_drv(ch); + if (ret != 0) + pr_err("can't unregister rpmsg drv %d\n", ret); +exit_destroy_channel: + // empty channel leaves free slot for next time + mutex_lock(&ch->lock); + memset(ch->name, 0, SPCOM_CHANNEL_NAME_SIZE); + mutex_unlock(&ch->lock); + return -EFAULT; +} + +static int spcom_register_chardev(void) +{ + int ret; + unsigned int baseminor = 0; + unsigned int count = 1; + void *priv = spcom_dev; + + ret = alloc_chrdev_region(&spcom_dev->device_no, baseminor, count, + DEVICE_NAME); + if (ret < 0) { + pr_err("alloc_chrdev_region failed %d\n", ret); + return ret; + } + + spcom_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(spcom_dev->driver_class)) { + ret = -ENOMEM; + pr_err("class_create failed %d\n", ret); + goto exit_unreg_chrdev_region; + } + + spcom_dev->class_dev = device_create(spcom_dev->driver_class, NULL, + spcom_dev->device_no, priv, + DEVICE_NAME); + + if (IS_ERR(spcom_dev->class_dev)) { + pr_err("class_device_create failed %d\n", ret); + ret = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&spcom_dev->cdev, &fops); + spcom_dev->cdev.owner = THIS_MODULE; + + ret = cdev_add(&spcom_dev->cdev, + MKDEV(MAJOR(spcom_dev->device_no), 0), + SPCOM_MAX_CHANNELS); + if (ret < 0) { + pr_err("cdev_add failed %d\n", ret); + goto exit_destroy_device; + } + + pr_debug("char device created.\n"); + + return 0; + +exit_destroy_device: + device_destroy(spcom_dev->driver_class, spcom_dev->device_no); +exit_destroy_class: + class_destroy(spcom_dev->driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(spcom_dev->device_no, 1); + return ret; +} + +static void spcom_unregister_chrdev(void) +{ + cdev_del(&spcom_dev->cdev); + device_destroy(spcom_dev->driver_class, spcom_dev->device_no); + class_destroy(spcom_dev->driver_class); + unregister_chrdev_region(spcom_dev->device_no, + atomic_read(&spcom_dev->chdev_count)); + +} + +static int spcom_parse_dt(struct device_node *np) +{ + int ret; + const char *propname = "qcom,spcom-ch-names"; + int num_ch; + int i; + const char *name; + + num_ch = of_property_count_strings(np, propname); + if (num_ch < 0) { + pr_err("wrong format of predefined channels definition [%d].\n", + num_ch); + return num_ch; + } + if (num_ch > ARRAY_SIZE(spcom_dev->predefined_ch_name)) { + pr_err("too many predefined channels [%d].\n", num_ch); + return -EINVAL; + } + + pr_debug("num of predefined channels [%d].\n", num_ch); + for (i = 0; i < num_ch; i++) { + ret = of_property_read_string_index(np, propname, i, &name); + if (ret) { + pr_err("failed to read DT channel [%d] name .\n", i); + return -EFAULT; + } + strlcpy(spcom_dev->predefined_ch_name[i], + name, + sizeof(spcom_dev->predefined_ch_name[i])); + + pr_debug("found ch [%s].\n", name); + } + + return num_ch; +} + +/* + * the function is running on system workqueue context, + * processes delayed (by rpmsg rx callback) packets: + * each paket belong to its destination spcom channel ch + */ +static void spcom_signal_rx_done(struct work_struct *ignored) +{ + struct spcom_channel *ch; + struct rx_buff_list *rx_item; + struct spcom_msg_hdr *hdr; + unsigned long flags; + + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + while (!list_empty(&spcom_dev->rx_list_head)) { + /* detach last entry */ + rx_item = list_last_entry(&spcom_dev->rx_list_head, + struct rx_buff_list, list); + list_del(&rx_item->list); + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + + if (!rx_item) { + pr_err("empty entry in pending rx list\n"); + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + continue; + } + ch = rx_item->ch; + hdr = (struct spcom_msg_hdr *)rx_item->rpmsg_rx_buf; + mutex_lock(&ch->lock); + + if (ch->comm_role_undefined) { + ch->comm_role_undefined = false; + ch->is_server = true; + ch->txn_id = hdr->txn_id; + pr_debug("ch [%s] first packet txn_id=%d, it is server\n", + ch->name, ch->txn_id); + } + + if (ch->rpmsg_abort) { + if (ch->rpmsg_rx_buf) { + pr_debug("ch [%s] rx aborted free %lu bytes\n", + ch->name, ch->actual_rx_size); + kfree(ch->rpmsg_rx_buf); + ch->actual_rx_size = 0; + } + goto rx_aborted; + } + if (ch->rpmsg_rx_buf) { + pr_err("ch [%s] previous buffer not consumed %lu bytes\n", + ch->name, ch->actual_rx_size); + kfree(ch->rpmsg_rx_buf); + ch->rpmsg_rx_buf = NULL; + ch->actual_rx_size = 0; + } + if (!ch->is_server && (hdr->txn_id != ch->txn_id)) { + pr_err("ch [%s] rx dropped txn_id %d, ch->txn_id %d\n", + ch->name, hdr->txn_id, ch->txn_id); + goto rx_aborted; + } + ch->rpmsg_rx_buf = rx_item->rpmsg_rx_buf; + ch->actual_rx_size = rx_item->rx_buf_size; + complete_all(&ch->rx_done); + mutex_unlock(&ch->lock); + + kfree(rx_item); + + /* lock for the next list entry */ + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + } + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + return; +rx_aborted: + mutex_unlock(&ch->lock); + kfree(rx_item->rpmsg_rx_buf); + kfree(rx_item); +} + +static int spcom_rpdev_cb(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 src) +{ + struct spcom_channel *ch; + static DECLARE_WORK(rpmsg_rx_consumer, spcom_signal_rx_done); + struct rx_buff_list *rx_item; + unsigned long flags; + + if (!rpdev || !data) { + pr_err("rpdev or data is NULL\n"); + return -EINVAL; + } + pr_debug("incoming msg from %s\n", rpdev->id.name); + ch = dev_get_drvdata(&rpdev->dev); + if (!ch) { + pr_err("%s: invalid ch\n", __func__); + return -EINVAL; + } + if (len > SPCOM_RX_BUF_SIZE || len <= 0) { + pr_err("got msg size %d, max allowed %d\n", + len, SPCOM_RX_BUF_SIZE); + return -EINVAL; + } + + rx_item = kzalloc(sizeof(*rx_item), GFP_ATOMIC); + if (!rx_item) + return -ENOMEM; + + rx_item->rpmsg_rx_buf = kzalloc(len, GFP_ATOMIC); + if (!rx_item->rpmsg_rx_buf) { + kfree(rx_item); + return -ENOMEM; + } + memcpy(rx_item->rpmsg_rx_buf, data, len); + rx_item->rx_buf_size = len; + rx_item->ch = ch; + + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + list_add(&rx_item->list, &spcom_dev->rx_list_head); + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + pr_debug("signaling rx item for %s, received %d bytes\n", + rpdev->id.name, len); + + schedule_work(&rpmsg_rx_consumer); + return 0; +} + +static int spcom_rpdev_probe(struct rpmsg_device *rpdev) +{ + const char *name; + struct spcom_channel *ch; + + if (!rpdev) { + pr_err("rpdev is NULL\n"); + return -EINVAL; + } + name = rpdev->id.name; + pr_debug("new channel %s rpmsg_device arrived\n", name); + ch = spcom_find_channel_by_name(name); + if (!ch) { + pr_err("channel %s not found\n", name); + return -ENODEV; + } + mutex_lock(&ch->lock); + ch->rpdev = rpdev; + ch->rpmsg_abort = false; + ch->txn_id = INITIAL_TXN_ID; + complete_all(&ch->connect); + mutex_unlock(&ch->lock); + + dev_set_drvdata(&rpdev->dev, ch); + + /* used to evaluate underlying transport link up/down */ + atomic_inc(&spcom_dev->rpmsg_dev_count); + if (atomic_read(&spcom_dev->rpmsg_dev_count) == 1) + complete_all(&spcom_dev->rpmsg_state_change); + + return 0; +} + +static void spcom_rpdev_remove(struct rpmsg_device *rpdev) +{ + struct spcom_channel *ch; + int i; + + if (!rpdev) { + pr_err("rpdev is NULL\n"); + return; + } + + dev_info(&rpdev->dev, "rpmsg device %s removed\n", rpdev->id.name); + ch = dev_get_drvdata(&rpdev->dev); + if (!ch) { + pr_err("channel %s not found\n", rpdev->id.name); + return; + } + + mutex_lock(&ch->lock); + // unlock all ion buffers of sp_kernel channel + if (strcmp(ch->name, "sp_kernel") == 0) { + for (i = 0; i < ARRAY_SIZE(ch->dmabuf_handle_table); i++) { + if (ch->dmabuf_handle_table[i] != NULL) { + pr_debug("unlocked ion buf #%d fd [%d].\n", + i, ch->dmabuf_fd_table[i]); + dma_buf_put(ch->dmabuf_handle_table[i]); + ch->dmabuf_handle_table[i] = NULL; + ch->dmabuf_fd_table[i] = -1; + } + } + } + + ch->rpdev = NULL; + ch->rpmsg_abort = true; + ch->txn_id = 0; + complete_all(&ch->rx_done); + mutex_unlock(&ch->lock); + + /* used to evaluate underlying transport link up/down */ + if (atomic_dec_and_test(&spcom_dev->rpmsg_dev_count)) + complete_all(&spcom_dev->rpmsg_state_change); + +} + +/* register rpmsg driver to match with channel ch_name */ +static int spcom_register_rpmsg_drv(struct spcom_channel *ch) +{ + struct rpmsg_driver *rpdrv; + struct rpmsg_device_id *match; + char *drv_name; + int ret; + + if (ch->rpdrv) { + pr_err("ch:%s, rpmsg driver %s already registered\n", ch->name, + ch->rpdrv->id_table->name); + return -ENODEV; + } + + rpdrv = kzalloc(sizeof(*rpdrv), GFP_KERNEL); + if (!rpdrv) + return -ENOMEM; + + /* zalloc array of two to NULL terminate the match list */ + match = kzalloc(2 * sizeof(*match), GFP_KERNEL); + if (!match) { + kfree(rpdrv); + return -ENOMEM; + } + snprintf(match->name, RPMSG_NAME_SIZE, "%s", ch->name); + + drv_name = kasprintf(GFP_KERNEL, "%s_%s", "spcom_rpmsg_drv", ch->name); + if (!drv_name) { + pr_err("can't allocate drv_name for %s\n", ch->name); + kfree(rpdrv); + kfree(match); + return -ENOMEM; + } + + rpdrv->probe = spcom_rpdev_probe; + rpdrv->remove = spcom_rpdev_remove; + rpdrv->callback = spcom_rpdev_cb; + rpdrv->id_table = match; + rpdrv->drv.name = drv_name; + ret = register_rpmsg_driver(rpdrv); + if (ret) { + pr_err("can't register rpmsg_driver for %s\n", ch->name); + kfree(rpdrv); + kfree(match); + kfree(drv_name); + return ret; + } + mutex_lock(&ch->lock); + ch->rpdrv = rpdrv; + ch->rpmsg_abort = false; + mutex_unlock(&ch->lock); + + return 0; +} + +static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch) +{ + if (!ch->rpdrv) + return -ENODEV; + unregister_rpmsg_driver(ch->rpdrv); + + mutex_lock(&ch->lock); + kfree(ch->rpdrv->drv.name); + kfree((void *)ch->rpdrv->id_table); + kfree(ch->rpdrv); + ch->rpdrv = NULL; + ch->rpmsg_abort = true; /* will unblock spcom_rx() */ + mutex_unlock(&ch->lock); + return 0; +} + +static int spcom_probe(struct platform_device *pdev) +{ + int ret; + struct spcom_device *dev = NULL; + struct device_node *np; + + if (!pdev) { + pr_err("invalid pdev.\n"); + return -ENODEV; + } + + np = pdev->dev.of_node; + if (!np) { + pr_err("invalid DT node.\n"); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + + spcom_dev = dev; + spcom_dev->pdev = pdev; + /* start counting exposed channel char devices from 1 */ + atomic_set(&spcom_dev->chdev_count, 1); + init_completion(&spcom_dev->rpmsg_state_change); + atomic_set(&spcom_dev->rpmsg_dev_count, 0); + + INIT_LIST_HEAD(&spcom_dev->rx_list_head); + spin_lock_init(&spcom_dev->rx_lock); + + ret = spcom_register_chardev(); + if (ret) { + pr_err("create character device failed.\n"); + goto fail_while_chardev_reg; + } + + ret = spcom_parse_dt(np); + if (ret < 0) + goto fail_reg_chardev; + + ret = spcom_create_predefined_channels_chardev(); + if (ret < 0) { + pr_err("create character device failed.\n"); + goto fail_reg_chardev; + } + pr_debug("Driver Initialization ok.\n"); + return 0; + +fail_reg_chardev: + pr_err("failed to init driver\n"); + spcom_unregister_chrdev(); +fail_while_chardev_reg: + kfree(dev); + spcom_dev = NULL; + + return -ENODEV; +} + +static const struct of_device_id spcom_match_table[] = { + { .compatible = "qcom,spcom", }, + { }, +}; + +static struct platform_driver spcom_driver = { + .probe = spcom_probe, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spcom_match_table), + }, +}; + +static int __init spcom_init(void) +{ + int ret; + + pr_info("spcom driver version 2.1 23-April-2018.\n"); + + ret = platform_driver_register(&spcom_driver); + if (ret) + pr_err("spcom_driver register failed %d\n", ret); + + return ret; +} +module_init(spcom_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Secure Processor Communication"); diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c index e3ab1c4b28e5..47e8df7c1294 100644 --- a/drivers/soc/qcom/spss_utils.c +++ b/drivers/soc/qcom/spss_utils.c @@ -64,6 +64,8 @@ static u32 spss_emul_type_reg_addr; /* TCSR_SOC_EMULATION_TYPE */ static void *iar_notif_handle; static struct notifier_block *iar_nb; static bool is_iar_active; +/* To differentiate legacy and new generation of hardware supported features*/ +static bool is_cmac_and_iar_feature_supported = true; #define CMAC_SIZE_IN_BYTES (128/8) /* 128 bit = 16 bytes */ #define CMAC_SIZE_IN_DWORDS (CMAC_SIZE_IN_BYTES/sizeof(u32)) /* 4 dwords */ @@ -330,6 +332,9 @@ static int spss_create_sysfs(struct device *dev) goto remove_test_fuse_state; } + if (!is_cmac_and_iar_feature_supported) + goto out; + ret = device_create_file(dev, &dev_attr_cmac_buf); if (ret < 0) { pr_err("failed to create sysfs file for cmac_buf.\n"); @@ -360,7 +365,7 @@ static int spss_create_sysfs(struct device *dev) goto remove_pbl_cmac; } - +out: return 0; remove_pbl_cmac: @@ -525,6 +530,11 @@ static long spss_utils_ioctl(struct file *file, return -EINVAL; } + if (!is_cmac_and_iar_feature_supported) { + pr_err("legacy SPSS not support cmac,iar feature.\n"); + return -EINVAL; + } + /* spdaemon uses this ioctl only when IAR is active */ is_iar_active = true; @@ -805,8 +815,8 @@ static int spss_parse_dt(struct device_node *node) ret = of_property_read_u32(node, "qcom,spss-emul-type-reg-addr", &spss_emul_type_reg_addr); if (ret < 0) { - pr_err("can't get spss-emulation-type-reg addr\n"); - return -EINVAL; + pr_warn("can't get spss-emulation-type-reg addr\n"); + goto end; } spss_emul_type_reg = ioremap_nocache(spss_emul_type_reg_addr, @@ -825,6 +835,12 @@ static int spss_parse_dt(struct device_node *node) firmware_type = SPSS_FW_TYPE_NONE; } iounmap(spss_emul_type_reg); +end: + + if (!is_cmac_and_iar_feature_supported) { + pr_info("legacy SPSS not support cmac & iar feature.\n"); + goto out; + } /* PIL-SPSS area */ np = of_parse_phandle(node, "pil-mem", 0); @@ -921,7 +937,7 @@ static int spss_parse_dt(struct device_node *node) iar_state = val1; pr_debug("iar_state [%d]\n", iar_state); - +out: return 0; } @@ -1113,6 +1129,10 @@ static int spss_utils_pil_callback(struct notifier_block *nb, pr_debug("[SUBSYS_PROXY_UNVOTE] event.\n"); break; case SUBSYS_BEFORE_AUTH_AND_RESET: + if (!is_cmac_and_iar_feature_supported) { + pr_info("legacy SPSS not support cmac,iar feature.\n"); + break; + } /* do nothing if IAR is not active */ if (!is_iar_active) return NOTIFY_OK; @@ -1164,6 +1184,14 @@ static int spss_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, dev); + /* Based on flag will differentiate legacy spss + * supported features + */ + if (of_property_read_bool(pdev->dev.of_node, + "qcom,no-cmac-and-iar-feature-support")) { + pr_info("legacy SPSS not support cmac & iar feature.\n"); + is_cmac_and_iar_feature_supported = false; + } ret = spss_parse_dt(np); if (ret < 0) diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index 2479632e103b..b25637ac1f6a 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -831,6 +831,22 @@ struct subsys_device *find_subsys_device(const char *str) } EXPORT_SYMBOL(find_subsys_device); +static int restart_level;/*system original val*/ +int op_restart_modem(void) +{ + struct subsys_device *subsys = find_subsys_device("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; diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c index 198f0e9f146e..4ad97d7ca6e7 100644 --- a/drivers/soc/qcom/system_pm.c +++ b/drivers/soc/qcom/system_pm.c @@ -16,6 +16,13 @@ #define PDC_TIME_VALID_SHIFT 31 #define PDC_TIME_UPPER_MASK 0xFFFFFF +#if defined(CONFIG_ARM_GIC_V3) && defined(CONFIG_ARCH_SDM845) +#include +#else +static inline void gic_v3_dist_restore(void) {} +static inline void gic_v3_dist_save(void) {} +#endif + static struct device *dev; static int setup_wakeup(uint32_t lo, uint32_t hi) @@ -55,6 +62,7 @@ static bool system_sleep_allowed(void) */ static int system_sleep_enter(struct cpumask *mask) { + gic_v3_dist_save(); return rpmh_flush(dev); } @@ -65,6 +73,7 @@ static void system_sleep_exit(bool success) { if (success) msm_rpmh_master_stats_update(); + gic_v3_dist_restore(); } static struct system_pm_ops pm_ops = { diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig index 813407f45741..d7d8a38c3765 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -11,6 +11,12 @@ menuconfig ION If you're not using Android its probably safe to say N here. +config ION_LEGACY + bool "Legacy ion" + depends on ION + help + Choose this option to enable the legacy ion. If in doubt, say N. + config ION_SYSTEM_HEAP bool "Ion system heap" depends on ION diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile index dc638f080d7f..2b6d1525cea3 100644 --- a/drivers/staging/android/ion/Makefile +++ b/drivers/staging/android/ion/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_ION) += ion.o ion-ioctl.o ion_heap.o \ ion_system_secure_heap.o ion_cma_heap.o \ ion_secure_util.o ion_cma_secure_heap.o msm/ +ifdef CONFIG_COMPAT +obj-$(CONFIG_ION_LEGACY) += compat_ion.o +endif diff --git a/drivers/staging/android/ion/compat_ion.c b/drivers/staging/android/ion/compat_ion.c new file mode 100644 index 000000000000..057810447f42 --- /dev/null +++ b/drivers/staging/android/ion/compat_ion.c @@ -0,0 +1,159 @@ +/* + * drivers/staging/android/ion/compat_ion.c + * + * Copyright (C) 2013 Google, Inc. + * + * 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 "ion.h" +#include "compat_ion.h" +#include "ion_legacy.h" + +/* See drivers/staging/android/uapi/ion.h for the definition of these structs */ +struct compat_ion_old_allocation_data { + compat_size_t len; + compat_size_t align; + compat_uint_t heap_id_mask; + compat_uint_t flags; + compat_int_t handle; +}; + +struct compat_ion_handle_data { + compat_int_t handle; +}; + +#define COMPAT_ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct compat_ion_old_allocation_data) +#define COMPAT_ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, \ + struct compat_ion_handle_data) + +static int compat_get_ion_allocation_data( + struct compat_ion_old_allocation_data __user *data32, + struct ion_old_allocation_data __user *data) +{ + compat_size_t s; + compat_uint_t u; + compat_int_t i; + int err; + + err = get_user(s, &data32->len); + err |= put_user(s, &data->len); + err |= get_user(s, &data32->align); + err |= put_user(s, &data->align); + err |= get_user(u, &data32->heap_id_mask); + err |= put_user(u, &data->heap_id_mask); + err |= get_user(u, &data32->flags); + err |= put_user(u, &data->flags); + err |= get_user(i, &data32->handle); + err |= put_user(i, &data->handle); + + return err; +} + +static int compat_get_ion_handle_data( + struct compat_ion_handle_data __user *data32, + struct ion_handle_data __user *data) +{ + compat_int_t i; + int err; + + err = get_user(i, &data32->handle); + err |= put_user(i, &data->handle); + + return err; +} + +static int compat_put_ion_allocation_data( + struct compat_ion_old_allocation_data __user *data32, + struct ion_old_allocation_data __user *data) +{ + compat_size_t s; + compat_uint_t u; + compat_int_t i; + int err; + + err = get_user(s, &data->len); + err |= put_user(s, &data32->len); + err |= get_user(s, &data->align); + err |= put_user(s, &data32->align); + err |= get_user(u, &data->heap_id_mask); + err |= put_user(u, &data32->heap_id_mask); + err |= get_user(u, &data->flags); + err |= put_user(u, &data32->flags); + err |= get_user(i, &data->handle); + err |= put_user(i, &data32->handle); + + return err; +} + +long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret; + + if (!filp->f_op->unlocked_ioctl) + return -ENOTTY; + + switch (cmd) { + case COMPAT_ION_IOC_ALLOC: + { + struct compat_ion_old_allocation_data __user *data32; + struct ion_old_allocation_data __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (!data) + return -EFAULT; + + err = compat_get_ion_allocation_data(data32, data); + if (err) + return err; + ret = filp->f_op->unlocked_ioctl(filp, ION_OLD_IOC_ALLOC, + (unsigned long)data); + err = compat_put_ion_allocation_data(data32, data); + return ret ? ret : err; + } + case COMPAT_ION_IOC_FREE: + { + struct compat_ion_handle_data __user *data32; + struct ion_handle_data __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (!data) + return -EFAULT; + + err = compat_get_ion_handle_data(data32, data); + if (err) + return err; + + return filp->f_op->unlocked_ioctl(filp, ION_IOC_FREE, + (unsigned long)data); + } + case ION_IOC_SHARE: + case ION_IOC_MAP: + case ION_IOC_IMPORT: + case ION_IOC_ALLOC: + case ION_IOC_HEAP_QUERY: + case ION_IOC_PREFETCH: + case ION_IOC_DRAIN: + return filp->f_op->unlocked_ioctl(filp, cmd, + (unsigned long)compat_ptr(arg)); + default: + return -ENOIOCTLCMD; + } +} diff --git a/drivers/staging/android/ion/compat_ion.h b/drivers/staging/android/ion/compat_ion.h new file mode 100644 index 000000000000..9da8f917670b --- /dev/null +++ b/drivers/staging/android/ion/compat_ion.h @@ -0,0 +1,29 @@ +/* + * drivers/staging/android/ion/compat_ion.h + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_COMPAT_ION_H +#define _LINUX_COMPAT_ION_H + +#if IS_ENABLED(CONFIG_COMPAT) + +long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#else + +#define compat_ion_ioctl NULL + +#endif /* CONFIG_COMPAT */ +#endif /* _LINUX_COMPAT_ION_H */ diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c index cba8f04df9f3..acf483fe3154 100644 --- a/drivers/staging/android/ion/ion-ioctl.c +++ b/drivers/staging/android/ion/ion-ioctl.c @@ -11,10 +11,19 @@ #include "ion.h" #include "ion_system_secure_heap.h" +#ifdef CONFIG_ION_LEGACY +#include "ion_legacy.h" +#endif + union ion_ioctl_arg { struct ion_allocation_data allocation; struct ion_heap_query query; struct ion_prefetch_data prefetch_data; +#ifdef CONFIG_ION_LEGACY + struct ion_fd_data fd; + struct ion_old_allocation_data old_allocation; + struct ion_handle_data handle; +#endif }; static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg) @@ -37,6 +46,10 @@ static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg) static unsigned int ion_ioctl_dir(unsigned int cmd) { switch (cmd) { +#ifdef CONFIG_ION_LEGACY + case ION_IOC_FREE: + return _IOC_WRITE; +#endif default: return _IOC_DIR(cmd); } @@ -115,6 +128,41 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return ret; break; } +#ifdef CONFIG_ION_LEGACY + case ION_OLD_IOC_ALLOC: + { + int fd; + + fd = ion_alloc_fd(data.old_allocation.len, + data.old_allocation.heap_id_mask, + data.old_allocation.flags); + if (fd < 0) + return fd; + + data.old_allocation.handle = fd; + + break; + } + case ION_IOC_FREE: + /* + * libion passes 0 as the handle to check for this ioctl's + * existence and expects -ENOTTY on kernel 4.12+ as an indicator + * of having a new ION ABI. We want to use new ION as much as + * possible, so pretend that this ioctl doesn't exist when + * libion checks for it. + */ + if (!data.handle.handle) + ret = -ENOTTY; + + break; + case ION_IOC_SHARE: + case ION_IOC_MAP: + data.fd.fd = data.fd.handle; + break; + case ION_IOC_IMPORT: + data.fd.handle = data.fd.fd; + break; +#endif default: return -ENOTTY; } diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 729f3d1ed2e3..cd653bfa238c 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -37,6 +37,7 @@ #include "ion.h" #include "ion_secure_util.h" +#include "compat_ion.h" static struct ion_device *internal_dev; static atomic_long_t total_heap_bytes; @@ -1194,7 +1195,11 @@ int ion_query_heaps(struct ion_heap_query *query) static const struct file_operations ion_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ion_ioctl, +#ifdef CONFIG_ION_LEGACY + .compat_ioctl = compat_ion_ioctl, +#else .compat_ioctl = compat_ptr_ioctl, +#endif }; static int ion_debug_heap_show(struct seq_file *s, void *unused) diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index 2591a451e20a..46a8b5a152c5 100644 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "ion.h" #include "ion_secure_util.h" @@ -23,6 +24,12 @@ struct ion_cma_heap { struct cma *cma; }; +struct ion_cma_buffer_info { + void *cpu_addr; + dma_addr_t handle; + struct page *pages; +}; + #define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap) static bool ion_heap_is_cma_heap_type(enum ion_heap_type type) @@ -30,90 +37,137 @@ static bool ion_heap_is_cma_heap_type(enum ion_heap_type type) return type == ION_HEAP_TYPE_DMA; } +static bool ion_cma_has_kernel_mapping(struct ion_heap *heap) +{ + struct device *dev = heap->priv; + struct device_node *mem_region; + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!mem_region) + return false; + + return !of_property_read_bool(mem_region, "no-map"); +} + /* ION CMA heap operations functions */ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long len, unsigned long flags) { struct ion_cma_heap *cma_heap = to_cma_heap(heap); + struct ion_cma_buffer_info *info; struct sg_table *table; - struct page *pages; + struct page *pages = NULL; unsigned long size = PAGE_ALIGN(len); unsigned long nr_pages = size >> PAGE_SHIFT; unsigned long align = get_order(size); int ret; struct device *dev = heap->priv; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + if (ion_heap_is_cma_heap_type(buffer->heap->type) && is_secure_allocation(buffer->flags)) { pr_err("%s: CMA heap doesn't support secure allocations\n", __func__); - return -EINVAL; + goto free_info; } if (align > CONFIG_CMA_ALIGNMENT) align = CONFIG_CMA_ALIGNMENT; - pages = cma_alloc(cma_heap->cma, nr_pages, align, false); - if (!pages) - return -ENOMEM; - - if (hlos_accessible_buffer(buffer)) { - if (PageHighMem(pages)) { - unsigned long nr_clear_pages = nr_pages; - struct page *page = pages; - - while (nr_clear_pages > 0) { - void *vaddr = kmap_atomic(page); + if (!ion_cma_has_kernel_mapping(heap)) { + flags &= ~((unsigned long)ION_FLAG_CACHED); + buffer->flags = flags; - memset(vaddr, 0, PAGE_SIZE); - kunmap_atomic(vaddr); - page++; - nr_clear_pages--; + info->cpu_addr = dma_alloc_wc(dev, size, &info->handle, + GFP_KERNEL); + if (!info->cpu_addr) { + dev_err(dev, "failed to allocate buffer\n"); + goto free_info; + } + pages = pfn_to_page(PFN_DOWN(info->handle)); + } else { + pages = cma_alloc(cma_heap->cma, nr_pages, align, false); + if (!pages) + goto free_info; + + if (hlos_accessible_buffer(buffer)) { + if (PageHighMem(pages)) { + unsigned long nr_clear_pages = nr_pages; + struct page *page = pages; + + while (nr_clear_pages > 0) { + void *vaddr = kmap_atomic(page); + + memset(vaddr, 0, PAGE_SIZE); + kunmap_atomic(vaddr); + page++; + nr_clear_pages--; + } + } else { + memset(page_address(pages), 0, size); } - } else { - memset(page_address(pages), 0, size); } - } - if (MAKE_ION_ALLOC_DMA_READY || - (!hlos_accessible_buffer(buffer)) || - (!ion_buffer_cached(buffer))) - ion_pages_sync_for_device(dev, pages, size, - DMA_BIDIRECTIONAL); + if (MAKE_ION_ALLOC_DMA_READY || + (!hlos_accessible_buffer(buffer)) || + (!ion_buffer_cached(buffer))) + ion_pages_sync_for_device(dev, pages, size, + DMA_BIDIRECTIONAL); + } table = kmalloc(sizeof(*table), GFP_KERNEL); if (!table) - goto err; + goto err_alloc; ret = sg_alloc_table(table, 1, GFP_KERNEL); if (ret) - goto free_mem; + goto free_table; sg_set_page(table->sgl, pages, size, 0); buffer->priv_virt = pages; + info->pages = pages; + buffer->priv_virt = info; buffer->sg_table = table; return 0; -free_mem: +free_table: kfree(table); -err: - cma_release(cma_heap->cma, pages, nr_pages); +err_alloc: + if (info->cpu_addr) + dma_free_attrs(dev, size, info->cpu_addr, info->handle, 0); + else + cma_release(cma_heap->cma, pages, nr_pages); +free_info: + kfree(info); return -ENOMEM; } static void ion_cma_free(struct ion_buffer *buffer) { struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); - struct page *pages = buffer->priv_virt; - unsigned long nr_pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + if (info->cpu_addr) { + struct device *dev = buffer->heap->priv; + + dma_free_attrs(dev, PAGE_ALIGN(buffer->size), info->cpu_addr, + info->handle, 0); + } else { + struct page *pages = sg_page(buffer->sg_table->sgl); + unsigned long nr_pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT; - /* release memory */ - cma_release(cma_heap->cma, pages, nr_pages); + /* release memory */ + cma_release(cma_heap->cma, pages, nr_pages); + } /* release sg table */ sg_free_table(buffer->sg_table); kfree(buffer->sg_table); + kfree(info); } static struct ion_heap_ops ion_cma_ops = { @@ -129,9 +183,6 @@ struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) struct ion_cma_heap *cma_heap; struct device *dev = (struct device *)data->priv; - if (!dev->cma_area) - return ERR_PTR(-EINVAL); - cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL); if (!cma_heap) @@ -225,9 +276,6 @@ struct ion_heap *ion_cma_secure_heap_create(struct ion_platform_heap *data) struct ion_cma_heap *cma_heap; struct device *dev = (struct device *)data->priv; - if (!dev->cma_area) - return ERR_PTR(-EINVAL); - cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL); if (!cma_heap) diff --git a/drivers/staging/android/ion/ion_legacy.h b/drivers/staging/android/ion/ion_legacy.h new file mode 100644 index 000000000000..a4b78963e83b --- /dev/null +++ b/drivers/staging/android/ion/ion_legacy.h @@ -0,0 +1,101 @@ +#ifndef _ION_LEGACY_H +#define _ION_LEGACY_H + +#ifdef CONFIG_ION_LEGACY + +#include "../uapi/ion.h" + +typedef int ion_user_handle_t; + +/** + * struct ion_old_allocation_data - metadata passed from legacy (pre-4.12 kernel) + * userspace for allocations + * @len: size of the allocation + * @align: required alignment of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_old_allocation_data { + size_t len; + size_t align; + unsigned int heap_id_mask; + unsigned int flags; + ion_user_handle_t handle; +}; + +/** + * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair + * @handle: a handle + * @fd: a file descriptor representing that handle + * + * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with + * the handle returned from ion alloc, and the kernel returns the file + * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace + * provides the file descriptor and the kernel returns the handle. + */ +struct ion_fd_data { + ion_user_handle_t handle; + int fd; +}; + +/** + * struct ion_handle_data - a handle passed to/from the kernel + * @handle: a handle + */ +struct ion_handle_data { + ion_user_handle_t handle; +}; + +/** + * DOC: ION_OLD_IOC_ALLOC - allocate memory (pre-4.12 version) + * + * Takes an ion_old_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_OLD_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_old_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + */ +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +/** + * DOC: ION_IOC_MAP - get a file descriptor to mmap + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be used as an argument to mmap. + */ +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + */ +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +/** + * DOC: ION_IOC_IMPORT - imports a shared file descriptor + * + * Takes an ion_fd_data struct with the fd field populated with a valid file + * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle + * filed set to the corresponding opaque handle. + */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#endif /* CONFIG_ION_LEGACY */ + +#endif /* _ION_LEGACY_H */ diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index d79e32fd291f..ea136b008f41 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -212,6 +212,7 @@ static int tsens_thermal_zone_register(struct tsens_device *tmdev) for (i = 0; i < TSENS_MAX_SENSORS; i++) { tmdev->sensor[i].tmdev = tmdev; tmdev->sensor[i].hw_id = i; + tmdev->sensor[i].cached_temp = INT_MIN; if (tmdev->ops->sensor_en(tmdev, i)) { tmdev->sensor[i].tzd = devm_thermal_zone_of_sensor_register( @@ -264,6 +265,7 @@ static void tsens_therm_fwk_notify(struct work_struct *work) TSENS_DBG(tmdev, "Controller %pK\n", &tmdev->phys_addr_tm); for (i = 0; i < TSENS_MAX_SENSORS; i++) { + tmdev->sensor[i].cached_temp = INT_MIN; if (tmdev->ops->sensor_en(tmdev, i)) { rc = tsens_get_temp(&tmdev->sensor[i], &temp); if (rc) { diff --git a/drivers/thermal/qcom/adc-tm5.c b/drivers/thermal/qcom/adc-tm5.c index c66f6c087db7..a0caa98e4653 100644 --- a/drivers/thermal/qcom/adc-tm5.c +++ b/drivers/thermal/qcom/adc-tm5.c @@ -63,6 +63,10 @@ #define ADC_TM_Mn_DATA1(n) ((n * 2) + 0xa1) #define ADC_TM_DATA_SHIFT 8 +#if defined(CONFIG_ARCH_SDM845) +#define ADC_TM_NUM_CHANNELS 8 +#endif + static struct adc_tm_trip_reg_type adc_tm_ch_data[] = { [ADC_TM_CHAN0] = {ADC_TM_M0_ADC_CH_SEL_CTL}, [ADC_TM_CHAN1] = {ADC_TM_M1_ADC_CH_SEL_CTL}, @@ -1054,7 +1058,11 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) return ret; } +#if !defined(CONFIG_ARCH_SDM845) if (dt_chans > channels_available) { +#else + if (dt_chans > ADC_TM_NUM_CHANNELS) { +#endif pr_err("More nodes than channels supported:%d\n", channels_available); return -EINVAL; diff --git a/drivers/thermal/qcom/bcl_peripheral.c b/drivers/thermal/qcom/bcl_peripheral.c index a9cf4f5e1155..432c1abd58dd 100644 --- a/drivers/thermal/qcom/bcl_peripheral.c +++ b/drivers/thermal/qcom/bcl_peripheral.c @@ -514,7 +514,7 @@ static int bcl_read_soc(void *data, int *val) *val = 100; if (!batt_psy) - batt_psy = power_supply_get_by_name("battery"); + batt_psy = power_supply_get_by_name("bms"); if (batt_psy) { err = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); @@ -559,7 +559,7 @@ static int battery_supply_callback(struct notifier_block *nb, { struct power_supply *psy = data; - if (strcmp(psy->desc->name, "battery")) + if (strcmp(psy->desc->name, "bms")) return NOTIFY_OK; schedule_work(&bcl_perph->soc_eval_work); diff --git a/drivers/thermal/qcom/msm_lmh_dcvs.c b/drivers/thermal/qcom/msm_lmh_dcvs.c index efdde7742cd4..9d5f2c7e74c7 100644 --- a/drivers/thermal/qcom/msm_lmh_dcvs.c +++ b/drivers/thermal/qcom/msm_lmh_dcvs.c @@ -80,6 +80,8 @@ struct __limits_cdev_data { u32 min_freq; }; +static bool lmh_enabled = false; + struct limits_dcvs_hw { char sensor_name[THERMAL_NAME_LENGTH]; uint32_t affinity; @@ -344,11 +346,14 @@ static struct limits_dcvs_hw *get_dcvsh_hw_from_cpu(int cpu) return NULL; } -static int enable_lmh(void) +static int enable_lmh(struct device_node *dn) { int ret = 0; struct scm_desc desc_arg; + if (lmh_enabled) + return 0; + desc_arg.args[0] = 1; desc_arg.arginfo = SCM_ARGS(1, SCM_VAL); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LIMITS_PROFILE_CHANGE), @@ -358,6 +363,9 @@ static int enable_lmh(void) return ret; } + if (of_property_read_bool(dn, "qcom,legacy-lmh-enable")) + lmh_enabled = true; + return ret; } @@ -649,7 +657,7 @@ static int limits_dcvs_probe(struct platform_device *pdev) affinity); return ret; } - ret = enable_lmh(); + ret = enable_lmh(dn); if (ret) return ret; } diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h index 4398615857d0..f023620297b7 100644 --- a/drivers/thermal/tsens.h +++ b/drivers/thermal/tsens.h @@ -137,6 +137,7 @@ struct tsens_sensor { struct tsens_context thr_state; int offset; int slope; + int cached_temp; }; /** diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c index ea78f9048e1f..b6e1cd33795b 100644 --- a/drivers/thermal/tsens2xxx.c +++ b/drivers/thermal/tsens2xxx.c @@ -155,6 +155,11 @@ static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp) sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr); trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr); + if (sensor->cached_temp != INT_MIN) { + *temp = sensor->cached_temp; + goto dbg; + } + code = readl_relaxed_no_log(trdy); if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >> @@ -762,10 +767,12 @@ static irqreturn_t tsens_tm_irq_thread(int irq, void *data) if (upper_thr || lower_thr) { /* Use id for multiple controllers */ + tm->sensor[i].cached_temp = temp; pr_debug("sensor:%d trigger temp (%d degC)\n", tm->sensor[i].hw_id, temp); of_thermal_handle_trip_temp(tm->sensor[i].tzd, temp); } + tm->sensor[i].cached_temp = INT_MIN; } /* Disable monitoring sensor trip threshold for triggered sensor */ diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 5d745def2354..8ee13e679130 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -105,9 +105,9 @@ #define STALE_TIMEOUT (16) #define STALE_COUNT (DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT) #define SEC_TO_USEC (1000000) -#define STALE_DELAY (1000) //10msec +#define STALE_DELAY (10000) //10msec #define DEFAULT_BITS_PER_CHAR (10) -#define GENI_UART_NR_PORTS (15) +#define GENI_UART_NR_PORTS (6) #define GENI_UART_CONS_PORTS (1) #define DEF_FIFO_DEPTH_WORDS (16) #define DEF_TX_WM (2) @@ -272,6 +272,7 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport); static void msm_geni_serial_set_manual_flow(bool enable, struct msm_geni_serial_port *port); static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport); +static void msm_geni_serial_allow_rx(struct msm_geni_serial_port *port); static int uart_line_id; static bool is_earlycon; @@ -521,16 +522,17 @@ static bool check_transfers_inflight(struct uart_port *uport) return xfer_on; } -static void wait_for_transfers_inflight(struct uart_port *uport) +static int wait_for_transfers_inflight(struct uart_port *uport) { int iter = 0; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); unsigned int geni_status; + u32 rx_len_in = 0; geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called before this. */ if (!(geni_status & S_GENI_CMD_ACTIVE)) - return; + return 0; while (iter < WAIT_XFER_MAX_ITER) { if (check_transfers_inflight(uport)) { @@ -542,9 +544,18 @@ static void wait_for_transfers_inflight(struct uart_port *uport) } } if (check_transfers_inflight(uport)) { + rx_len_in = + geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN); + if (rx_len_in) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Bailout rx_len_in is set\n", + __func__); + return -EBUSY; + } geni_se_dump_dbg_regs(&port->serial_rsc, uport->membase, port->ipc_log_misc); } + return 0; } static int vote_clock_on(struct uart_port *uport) @@ -593,7 +604,7 @@ static int vote_clock_off(struct uart_port *uport) __func__, port->ioctl_count); return -EPERM; } - wait_for_transfers_inflight(uport); + ret = wait_for_transfers_inflight(uport); if (ret) { IPC_LOG_MSG(port->ipc_log_pwr, "%s wait_for_transfer_inflight return ret: %d", @@ -663,6 +674,7 @@ static int msm_geni_serial_ioctl(struct uart_port *uport, unsigned int cmd, static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl) { struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + int ret = 0; if (!uart_console(uport) && device_pending_suspend(uport)) { IPC_LOG_MSG(port->ipc_log_misc, @@ -672,7 +684,11 @@ static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl) } if (ctl) { - wait_for_transfers_inflight(uport); + ret = wait_for_transfers_inflight(uport); + if (ret) + IPC_LOG_MSG(port->ipc_log_misc, + "%s: wait_for_transfer_inflight return ret:%d\n", + __func__, ret); geni_setup_m_cmd(uport->membase, UART_START_BREAK, 0); } else { geni_setup_m_cmd(uport->membase, UART_STOP_BREAK, 0); @@ -1539,7 +1555,6 @@ static int stop_rx_sequencer(struct uart_port *uport) bool is_rx_active; u32 dma_rx_status, s_irq_status; int usage_count; - int iter = 0; IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__); @@ -1553,19 +1568,13 @@ static int stop_rx_sequencer(struct uart_port *uport) } if (!uart_console(uport)) { - if (!port->bypass_flow_control) - msm_geni_serial_set_manual_flow(false, port); /* * Wait for the stale timeout around 10msec to happen * if there is any data pending in the rx fifo. - * This will help to handle incoming rx data in - * stop_rx_sequencer for interrupt latency or - * system delay cases. + * This will help to handle incoming rx data in stop_rx_sequencer + * for interrupt latency or system delay cases. */ - while (iter < STALE_DELAY) { - iter++; - udelay(10); - } + udelay(STALE_DELAY); dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT); @@ -1584,8 +1593,7 @@ static int stop_rx_sequencer(struct uart_port *uport) IPC_LOG_MSG(port->ipc_log_misc, "%s: Interrupt delay\n", __func__); handle_rx_dma_xfer(s_irq_status, uport); - if (pm_runtime_enabled(uport->dev) && - !port->ioctl_count) { + if (!port->ioctl_count) { usage_count = atomic_read( &uport->dev->power.usage_count); IPC_LOG_MSG(port->ipc_log_misc, @@ -1629,10 +1637,10 @@ static int stop_rx_sequencer(struct uart_port *uport) IPC_LOG_MSG(port->console_log, "%s cancel failed timeout:%d is_rx_active:%d 0x%x\n", __func__, timeout, is_rx_active, geni_status); - geni_se_dump_dbg_regs(&port->serial_rsc, - uport->membase, port->ipc_log_misc); msm_geni_update_uart_error_code(port, UART_ERROR_RX_CANCEL_FAIL); + geni_se_dump_dbg_regs(&port->serial_rsc, + uport->membase, port->ipc_log_misc); /* * Possible that stop_rx is called from system resume context @@ -1646,22 +1654,6 @@ static int stop_rx_sequencer(struct uart_port *uport) } port->s_cmd_done = false; - /* Check if Cancel Interrupt arrived but irq is delayed */ - s_irq_status = geni_read_reg(uport->membase, - SE_GENI_S_IRQ_STATUS); - if (s_irq_status & S_CMD_CANCEL_EN) { - /* Clear delayed Cancel IRQ */ - geni_write_reg(S_CMD_CANCEL_EN, uport->membase, - SE_GENI_S_IRQ_CLEAR); - IPC_LOG_MSG(port->ipc_log_misc, - "%s Cancel Command succeeded 0x%x\n", - __func__, s_irq_status); - /* Reset the error code and skip abort operation */ - msm_geni_update_uart_error_code(port, - UART_ERROR_DEFAULT); - goto exit_enable_irq; - } - reinit_completion(&port->s_cmd_timeout); geni_abort_s_cmd(uport->membase); /* Ensure this goes through before polling. */ @@ -1685,6 +1677,9 @@ static int stop_rx_sequencer(struct uart_port *uport) geni_se_dump_dbg_regs(&port->serial_rsc, uport->membase, port->ipc_log_misc); } + msm_geni_serial_allow_rx(port); + geni_write_reg(FORCE_DEFAULT, uport->membase, + GENI_FORCE_DEFAULT_REG); if (port->xfer_mode == SE_DMA) { port->s_cmd_done = false; @@ -1702,14 +1697,12 @@ static int stop_rx_sequencer(struct uart_port *uport) } } } -exit_enable_irq: + /* Enable the interrupts once the cancel operation is done. */ msm_geni_serial_enable_interrupts(uport); port->s_cmd = false; exit_rx_seq: - if (!uart_console(uport) && !port->bypass_flow_control) - msm_geni_serial_set_manual_flow(true, port); geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); IPC_LOG_MSG(port->ipc_log_misc, "%s: End 0x%x dma_dbg:0x%x\n", @@ -2066,8 +2059,9 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) bool drop_rx = false; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); u32 dma_rx_status; + unsigned long lock_flags; - spin_lock(&msm_port->rx_lock); + spin_lock_irqsave(&msm_port->rx_lock, lock_flags); dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT); @@ -2079,6 +2073,7 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.Reset done. 0x%x.\n", __func__, dma_rx_status); ret = true; + goto exit; } if (dma_rx_status & UART_DMA_RX_ERRS) { @@ -2133,7 +2128,8 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) ret = true; - spin_unlock(&msm_port->rx_lock); +exit: + spin_unlock_irqrestore(&msm_port->rx_lock, lock_flags); return ret; } @@ -2441,7 +2437,11 @@ static void msm_geni_serial_shutdown(struct uart_port *uport) disable_irq(uport->irq); } else { msm_geni_serial_power_on(uport); - wait_for_transfers_inflight(uport); + ret = wait_for_transfers_inflight(uport); + if (ret) + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: wait_for_transfer_inflight return ret:%d\n", + __func__, ret); msm_geni_serial_stop_tx(uport); } @@ -3685,13 +3685,18 @@ static int msm_geni_serial_probe(struct platform_device *pdev) INIT_WORK(&dev_port->work, msm_geni_serial_worker); } + /* + * In abrupt kill scenarios, previous state of the uart causing runtime + * resume, lead to spinlock bug in stop_rx_sequencer, so initializing it + * before + */ + if (!is_console) + spin_lock_init(&dev_port->rx_lock); + ret = uart_add_one_port(drv, uport); if (ret) goto exit_workqueue_destroy; - if (!uart_console(uport)) - spin_lock_init(&dev_port->rx_lock); - /* * Earlyconsole to kernel console will switch happen after * uart_add_one_port. Hence marking is_earlycon to false here. @@ -3766,7 +3771,15 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) SE_GENI_STATUS); IPC_LOG_MSG(port->ipc_log_pwr, "%s: Start\n", __func__); - wait_for_transfers_inflight(&port->uport); + ret = wait_for_transfers_inflight(&port->uport); + if (ret) { + IPC_LOG_MSG(port->ipc_log_pwr, + "%s: wait_for_transfer_inflight return ret:%d\n", + __func__, ret); + /* Flow on from UART */ + msm_geni_serial_allow_rx(port); + return -EBUSY; + } /* * Manual RFR On. * Stop Rx. @@ -3940,6 +3953,7 @@ static struct uart_driver msm_geni_serial_hs_driver = { .nr = GENI_UART_NR_PORTS, }; +static int msm_serial_oem_pinctrl_init(void); static int __init msm_geni_serial_init(void) { int ret = 0; @@ -3959,9 +3973,11 @@ static int __init msm_geni_serial_init(void) msm_geni_console_port.uport.line = i; } - ret = console_register(&msm_geni_console_driver); - if (ret) - return ret; + if (false) { + ret = console_register(&msm_geni_console_driver); + if (ret) + return ret; + } ret = uart_register_driver(&msm_geni_serial_hs_driver); if (ret) { @@ -3976,6 +3992,8 @@ static int __init msm_geni_serial_init(void) return ret; } + msm_serial_oem_pinctrl_init(); + pr_info("%s: Driver initialized\n", __func__); return ret; @@ -3990,6 +4008,58 @@ static void __exit msm_geni_serial_exit(void) } module_exit(msm_geni_serial_exit); +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_oem_pinctrl_init(void) +{ + int ret = 0; + + pr_err("%s\n", __func__); + + ret = platform_driver_register(&msm_platform_serial_pinctrl_driver); + + return ret; +} +EXPORT_SYMBOL(msm_serial_oem_pinctrl_init); + MODULE_DESCRIPTION("Serial driver for GENI based QTI serial cores"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("tty:msm_geni_geni_serial"); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d58bc668ee7f..183a95540817 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1357,6 +1357,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) &dwc->hsphy_interface); device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + dwc->enable_super_speed = device_property_read_bool(dev, + "op,enable_super_speed"); dwc->enable_bus_suspend = device_property_read_bool(dev, "snps,bus-suspend-enable"); dwc->usb3_u1u2_disable = device_property_read_bool(dev, @@ -1383,6 +1385,12 @@ static void dwc3_get_properties(struct dwc3 *dwc) device_property_read_u32(dev, "snps,gen2-tx-de-emph3", &dwc->gen2_tx_de_emph3); + if (!dwc->enable_super_speed) { + pr_info("Force USB running as High speed"); + dwc->max_hw_supp_speed = USB_SPEED_HIGH; + 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/core.h b/drivers/usb/dwc3/core.h index 573e935cf9d1..75a045b7a673 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1327,6 +1327,7 @@ struct dwc3 { unsigned disable_clk_gating:1; unsigned enable_bus_suspend:1; unsigned usb3_u1u2_disable:1; + unsigned enable_super_speed:1; atomic_t in_lpm; bool b_suspend; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 8f125e73de25..e83e61ca5e82 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -76,6 +76,7 @@ MODULE_PARM_DESC(bc12_compliance, "Disable sending dp pulse for CDP"); #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) @@ -2308,6 +2309,7 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc) static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc) { + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); unsigned long timeout; u32 reg = 0; @@ -2335,8 +2337,19 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc) if (reg & PWR_EVNT_LPM_IN_L2_MASK) break; } - 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->drd_state == DRD_STATE_PERIPHERAL_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 9fc071514b30..5a566ea4c5a0 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -27,6 +27,22 @@ #include "debug.h" #include "gadget.h" #include "io.h" +#include + +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 bool enable_dwc3_u1u2; module_param(enable_dwc3_u1u2, bool, 0644); @@ -847,6 +863,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); + } ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 711ee1005618..9ecb28489644 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3692,8 +3692,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc->b_suspend = false; dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0); - usb_gadget_vbus_draw(&dwc->gadget, 100); - dwc3_reset_gadget(dwc); reg = dwc3_readl(dwc->regs, DWC3_DCTL); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7219ff7df74b..1b72fc37e23a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -1191,6 +1192,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 test_mode = 0; struct xhci_hub *rhub; struct xhci_port **ports; + enum usb_device_speed s = hcd->self.root_hub->speed; rhub = xhci_get_rhub(hcd); ports = rhub->ports; @@ -1443,6 +1445,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, writel(temp, ports[wIndex]->addr); temp = readl(ports[wIndex]->addr); + + if (s == USB_SPEED_HIGH) + usb_phy_start_port_reset(hcd->usb_phy); + xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bf2a25633551..1a2574f03886 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -17,6 +17,11 @@ #include "xhci.h" #include "xhci-trace.h" #include "xhci-debugfs.h" +#include + +static bool usb2_lpm_disable = 1; +module_param(usb2_lpm_disable, bool, 0644); +MODULE_PARM_DESC(usb2_lpm_disable, "DISABLE USB2 LPM"); /* * Allocates a generic ring segment from the ring pool, sets the dma address, @@ -2337,7 +2342,7 @@ 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->hw_lpm_support = 1; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 493aff432a56..c51c5fdb12d1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1062,6 +1062,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 */ @@ -2963,6 +2976,11 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) } } + 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 6dca69d619b4..2d47d5b3d377 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -3870,6 +3870,23 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) if (pd->typec_mode == typec_mode) return 0; + ret = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + if (ret) { + usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret); + return ret; + } + + if ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || + (typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM) || + (typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH)) { + if (val.intval == POWER_SUPPLY_TYPE_UNKNOWN) { + usbpd_err(&pd->dev, "typec_mode:%d, psy_type:%d\n", + typec_mode, val.intval); + return 0; + } + } + pd->typec_mode = typec_mode; usbpd_dbg(&pd->dev, "typec mode:%d present:%d orientation:%d\n", diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index 47d49c92c671..a8b42d2eda75 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include /* QUSB2PHY_PWR_CTRL1 register related bits */ #define PWR_CTRL1_POWR_DOWN BIT(0) @@ -46,6 +48,14 @@ #define CORE_RESET BIT(5) #define CORE_RESET_MUX BIT(6) +#define QUSB2PHY_PORT_TUNE1 0x240 +#define QUSB2PHY_PORT_TUNE2 0x244 +#define QUSB2PHY_PORT_TUNE3 0x248 +#define QUSB2PHY_PORT_TUNE4 0x24c +#define QUSB2PHY_PORT_TUNE5 0x250 +#define QUSB2PHY_PORT_BIAS1 0x194 +#define QUSB2PHY_PORT_BIAS2 0x198 + #define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ #define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ #define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */ @@ -73,6 +83,27 @@ /* STAT5 register bits */ #define VSTATUS_PLL_LOCK_STATUS_MASK BIT(0) +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 phy_tune5; +module_param(phy_tune5, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune5, "QUSB PHY v2 TUNE5"); +unsigned int phy_bias1; +module_param(phy_bias1, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_bias1, "QUSB PHY v2 BIAS1"); +unsigned int phy_bias2; +module_param(phy_bias2, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_bias2, "QUSB PHY v2 BIAS2"); /* DEBUG_CTRL4 register bits */ #define FORCED_UTMI_DPPULLDOWN BIT(2) @@ -114,6 +145,9 @@ struct qusb_phy { int vdd_levels[3]; /* none, low, high */ int init_seq_len; int *qusb_phy_init_seq; +/*2018/03/31 @BSP add host mode phy init parameters*/ + int ophost_init_seq_len; + int *qusb_phy_ophost_init_seq; int host_init_seq_len; int *qusb_phy_host_init_seq; @@ -121,6 +155,8 @@ struct qusb_phy { int qusb_phy_reg_offset_cnt; u32 tune_val; +/*2018/02/21 BSP@Infi do not need override the bias2 value*/ + bool overwrite_bias2_disable; int efuse_bit_pos; int efuse_num_of_bits; @@ -131,6 +167,15 @@ struct qusb_phy { struct regulator_desc dpdm_rdesc; struct regulator_dev *dpdm_rdev; + struct pinctrl *pinctrl; + struct pinctrl_state *atest_usb_suspend; + struct pinctrl_state *atest_usb_active; + +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ + struct pinctrl_state *usb_oe_active; + struct pinctrl_state *usb_oe_suspend; + bool usb_oe_exist; + /* emulation targets specific */ void __iomem *emu_phy_base; bool emulation; @@ -147,6 +192,8 @@ struct qusb_phy { u8 bias_ctrl2; bool override_bias_ctrl2; + + struct hrtimer timer; }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) @@ -440,6 +487,7 @@ static void qusb_phy_get_tune1_param(struct qusb_phy *qphy) qphy->tune_val = TUNE_VAL_MASK(qphy->tune_val, qphy->efuse_bit_pos, bit_mask); reg = readb_relaxed(qphy->base + qphy->phy_reg[PORT_TUNE1]); + pr_debug("%s(): tune1 value:0x%x before change\n",__func__, reg); reg = reg & 0x0f; reg |= (qphy->tune_val << 4); @@ -545,11 +593,6 @@ static void qusb_phy_host_init(struct usb_phy *phy) (4 * p_index)); } - if (qphy->refgen_north_bg_reg && qphy->override_bias_ctrl2) - if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS) - writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL, - qphy->base + qphy->phy_reg[BIAS_CTRL_2]); - if (qphy->bias_ctrl2) writel_relaxed(qphy->bias_ctrl2, qphy->base + qphy->phy_reg[BIAS_CTRL_2]); @@ -616,9 +659,18 @@ static int qusb_phy_init(struct usb_phy *phy) PWR_CTRL1_POWR_DOWN, qphy->base + qphy->phy_reg[PWR_CTRL1]); - if (qphy->qusb_phy_init_seq) - qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, - qphy->init_seq_len, 0); +/*2018/03/31 @BSP add host mode phy init parameters*/ + if (qphy->qusb_phy_init_seq || qphy->qusb_phy_ophost_init_seq){ + if ((qphy->phy.flags & PHY_HOST_MODE) && qphy->qusb_phy_ophost_init_seq){ + dev_info(phy->dev, "%s PHY_HOST_MODE!\n", __func__); + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_ophost_init_seq, + qphy->init_seq_len, 0); + } + else + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, + qphy->init_seq_len, 0); + } + if (qphy->efuse_reg) { qusb_phy_get_tune1_param(qphy); @@ -630,20 +682,74 @@ static int qusb_phy_init(struct usb_phy *phy) /* if debugfs based tunex params are set, use that value. */ for (p_index = 0; p_index < 5; p_index++) { - if (qphy->tune[p_index]) + if (qphy->tune[p_index]){ + pr_debug("%s(): Programming TUNE%d parameter as:%x\n", __func__,p_index+1, + qphy->tune_val); writel_relaxed(qphy->tune[p_index], qphy->base + qphy->phy_reg[PORT_TUNE1] + (4 * p_index)); + } } - if (qphy->refgen_north_bg_reg && qphy->override_bias_ctrl2) - if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS) - writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL, - qphy->base + qphy->phy_reg[BIAS_CTRL_2]); - if (qphy->bias_ctrl2) writel_relaxed(qphy->bias_ctrl2, qphy->base + qphy->phy_reg[BIAS_CTRL_2]); +/*2018/02/21 BSP@Infi do not need override the bias2 value*/ + if (qphy->refgen_north_bg_reg && !qphy->overwrite_bias2_disable) + if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS){ + pr_debug("%s(): overwrite bias2\n", __func__); + writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL, + qphy->base + qphy->phy_reg[BIAS_CTRL_2]); + } + /* If phy_tune1 modparam set, override tune1 value */ + if (phy_tune1) { + pr_err("%s(): (modparam) TUNE1 val:0x%02x\n", + __func__, phy_tune1); + writel_relaxed(phy_tune1, + qphy->base + qphy->phy_reg[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 + qphy->phy_reg[PORT_TUNE1]+4); + } + /* 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 + qphy->phy_reg[PORT_TUNE1]+8); + } + /* 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 + qphy->phy_reg[PORT_TUNE1]+0xc); + } + /* If phy_tune5 modparam set, override tune5 value */ + if (phy_tune5) { + pr_err("%s(): (modparam) TUNE5 val:0x%02x\n", + __func__, phy_tune5); + writel_relaxed(phy_tune5, + qphy->base + qphy->phy_reg[PORT_TUNE1]+0x10); + } + /* If phy_BIAS1 modparam set, override bias1 value */ + if (phy_bias1) { + pr_err("%s(): (modparam) bias1 val:0x%02x\n", + __func__, phy_bias1); + writel_relaxed(phy_bias1, + qphy->base + qphy->phy_reg[BIAS_CTRL_2]-4); + } + /* If phy_BIAS2 modparam set, override bias2 value */ + if (phy_bias2) { + pr_err("%s(): (modparam) bias2 val:0x%02x\n", + __func__, phy_bias2); + writel_relaxed(phy_bias2, + qphy->base + qphy->phy_reg[BIAS_CTRL_2]); + } /* ensure above writes are completed before re-enabling PHY */ wmb(); @@ -668,6 +774,74 @@ static int qusb_phy_init(struct usb_phy *phy) return 0; } +static enum hrtimer_restart qusb_dis_ext_pulldown_timer(struct hrtimer *timer) +{ + struct qusb_phy *qphy = container_of(timer, struct qusb_phy, timer); + int ret = 0; + + if (qphy->pinctrl && qphy->atest_usb_suspend) { + ret = pinctrl_select_state(qphy->pinctrl, + qphy->atest_usb_suspend); + if (ret < 0) + dev_err(qphy->phy.dev, + "pinctrl state suspend select failed\n"); +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ + if (qphy->usb_oe_exist && qphy->usb_oe_active) { + ret = pinctrl_select_state(qphy->pinctrl, + qphy->usb_oe_active); + if (ret < 0) + dev_err(qphy->phy.dev, + "pinctrl state usb_oe_active select failed\n"); + } + } + + return HRTIMER_NORESTART; +} + +static void qusb_phy_enable_ext_pulldown(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret = 0; + + dev_dbg(phy->dev, "%s\n", __func__); + + if (qphy->pinctrl && qphy->atest_usb_active) { + ret = pinctrl_select_state(qphy->pinctrl, + qphy->atest_usb_active); + if (ret < 0) { + dev_err(phy->dev, + "pinctrl state active select failed\n"); + return; + } +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ + if (qphy->usb_oe_exist && qphy->usb_oe_suspend) { + ret = pinctrl_select_state(qphy->pinctrl, + qphy->usb_oe_suspend); + if (ret < 0) + dev_err(phy->dev, + "pinctrl state usb_oe_suspend select failed\n"); + } + hrtimer_start(&qphy->timer, ms_to_ktime(10), HRTIMER_MODE_REL); + } +} + +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ +static void qusb_phy_enable_usb_oe(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret = 0; + + dev_dbg(phy->dev, "%s\n", __func__); + + if (qphy->pinctrl && qphy->usb_oe_active) { + ret = pinctrl_select_state(qphy->pinctrl, + qphy->usb_oe_active); + if (ret < 0) + dev_err(phy->dev, + "pinctrl state usb_oe_active select failed\n"); + } +} + static void qusb_phy_shutdown(struct usb_phy *phy) { struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); @@ -1082,6 +1256,9 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->efuse_reg = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (!IS_ERR_OR_NULL(qphy->efuse_reg)) { +/*2018/02/21 BSP@Infi do not need override the bias2 value*/ + qphy->overwrite_bias2_disable = of_property_read_bool(dev->of_node, + "qcom,overwrite-bias2-disable"); ret = of_property_read_u32(dev->of_node, "qcom,efuse-bit-pos", &qphy->efuse_bit_pos); @@ -1273,6 +1450,31 @@ static int qusb_phy_probe(struct platform_device *pdev) } } +/*2018/03/31 @BSP add host mode phy init parameters*/ + size = 0; + of_get_property(dev->of_node, "qcom,qusb-phy-ophost-init-seq", &size); + if (size) { + dev_info(dev,"%s:qusb-phy-ophost-init-seq got!",__func__); + qphy->qusb_phy_ophost_init_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->qusb_phy_ophost_init_seq) { + qphy->ophost_init_seq_len = + (size / sizeof(*qphy->qusb_phy_ophost_init_seq)); + if (qphy->ophost_init_seq_len % 2) { + dev_err(dev, "invalid ophost_init_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,qusb-phy-ophost-init-seq", + qphy->qusb_phy_ophost_init_seq, + qphy->ophost_init_seq_len); + } else { + dev_err(dev, + "error allocating memory for phy_ophost_init_seq\n"); + } + } + qphy->host_init_seq_len = of_property_count_elems_of_size(dev->of_node, "qcom,qusb-phy-host-init-seq", sizeof(*qphy->qusb_phy_host_init_seq)); @@ -1305,6 +1507,57 @@ static int qusb_phy_probe(struct platform_device *pdev) if (ret) return ret; + qphy->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(qphy->pinctrl)) { + ret = PTR_ERR(qphy->pinctrl); + if (ret == -EPROBE_DEFER) + return ret; + dev_err(dev, "pinctrl not available\n"); + goto skip_pinctrl_config; + } + qphy->atest_usb_suspend = pinctrl_lookup_state(qphy->pinctrl, + "atest_usb13_suspend"); + + if (IS_ERR(qphy->atest_usb_suspend) && + PTR_ERR(qphy->atest_usb_suspend) == -ENODEV) { + qphy->atest_usb_suspend = pinctrl_lookup_state(qphy->pinctrl, + "suspend"); + if (IS_ERR(qphy->atest_usb_suspend)) { + dev_err(dev, "pinctrl lookup suspend failed\n"); + goto skip_pinctrl_config; + } + } + + qphy->atest_usb_active = pinctrl_lookup_state(qphy->pinctrl, + "atest_usb13_active"); + if (IS_ERR(qphy->atest_usb_active) && + PTR_ERR(qphy->atest_usb_active) == -ENODEV) { + qphy->atest_usb_active = pinctrl_lookup_state(qphy->pinctrl, + "active"); + if (IS_ERR(qphy->atest_usb_active)) + dev_err(dev, "pinctrl lookup active failed\n"); + } + +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ + qphy->usb_oe_exist = of_property_read_bool(dev->of_node, + "qcom,usb-oe-exist"); + dev_info(dev, "usb_oe_exist=%d\n", qphy->usb_oe_exist); + if (qphy->usb_oe_exist) { + qphy->usb_oe_suspend = pinctrl_lookup_state(qphy->pinctrl, + "usb_oe_suspend"); + if (IS_ERR(qphy->usb_oe_suspend)) + dev_err(dev, "pinctrl lookup usb_oe_suspend failed\n"); + + qphy->usb_oe_active = pinctrl_lookup_state(qphy->pinctrl, + "usb_oe_active"); + if (IS_ERR(qphy->usb_oe_active)) + dev_err(dev, "pinctrl lookup usb_oe_active failed\n"); + } + + hrtimer_init(&qphy->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + qphy->timer.function = qusb_dis_ext_pulldown_timer; + +skip_pinctrl_config: mutex_init(&qphy->lock); platform_set_drvdata(pdev, qphy); @@ -1316,6 +1569,7 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.notify_connect = qusb_phy_notify_connect; qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; qphy->phy.drive_dp_pulse = msm_qusb_phy_drive_dp_pulse; + qphy->phy.start_port_reset = qusb_phy_enable_ext_pulldown; ret = usb_add_phy_dev(&qphy->phy); if (ret) @@ -1326,6 +1580,10 @@ static int qusb_phy_probe(struct platform_device *pdev) usb_remove_phy(&qphy->phy); qphy->suspended = true; + +/*2018/06/28 @BSP Add HW-SW WR to optimize the usb diagram*/ + if (qphy->usb_oe_exist) + qusb_phy_enable_usb_oe(&qphy->phy); qusb_phy_create_debugfs(qphy); /* diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index af22f7b261ac..3b78af4c5028 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -103,3 +103,14 @@ config PROC_UID depends on PROC_FS && RT_MUTEXES help Provides aggregated per-uid information under /proc/uid. + +config PROC_CMDLINE_APPEND_ANDROID_FORCE_NORMAL_BOOT + bool "Append androidboot.force_normal_boot=1 to cmdline if skip_initramfs is present" + default n + help + If `skip_initramfs` is present in the kernel cmdline, then append + `androidboot.force_normal_boot=1` to the cmdline string that we present + in /proc/cmdline. + + This is a workaround to allow recovery-as-boot A/B devices that + launched with Android 9 to use two-stage init. diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c index fa762c5fbcb2..edfd0b97ee12 100644 --- a/fs/proc/cmdline.c +++ b/fs/proc/cmdline.c @@ -3,16 +3,58 @@ #include #include #include +#include + +#if defined(CONFIG_INITRAMFS_IGNORE_SKIP_FLAG) || \ + defined(CONFIG_PROC_CMDLINE_APPEND_ANDROID_FORCE_NORMAL_BOOT) +#define INITRAMFS_STR_FIND "skip_initramf" +#endif + +#ifdef CONFIG_INITRAMFS_IGNORE_SKIP_FLAG +#define INITRAMFS_STR_REPLACE "want_initramf" +#define INITRAMFS_STR_LEN (sizeof(INITRAMFS_STR_FIND) - 1) +#endif + +#ifdef CONFIG_PROC_CMDLINE_APPEND_ANDROID_FORCE_NORMAL_BOOT +#define ANDROID_FORCE_NORMAL_BOOT_STR "androidboot.force_normal_boot=1" +#endif + +static char proc_command_line[COMMAND_LINE_SIZE]; + +static void proc_command_line_init(void) { + char *offset_addr; + char *proc_command_line_tail; + + strcpy(proc_command_line, saved_command_line); + +#ifdef CONFIG_INITRAMFS_IGNORE_SKIP_FLAG + offset_addr = strstr(proc_command_line, INITRAMFS_STR_FIND); + if (offset_addr) + memcpy(offset_addr, INITRAMFS_STR_REPLACE, INITRAMFS_STR_LEN); +#endif + +#ifdef CONFIG_PROC_CMDLINE_APPEND_ANDROID_FORCE_NORMAL_BOOT + if (strstr(saved_command_line, INITRAMFS_STR_FIND)) { + // point proc_command_line_tail to the null terminator of the cmdline + proc_command_line_tail = proc_command_line + strlen(proc_command_line); + memcpy(proc_command_line_tail, " ", 1); + memcpy(proc_command_line_tail + 1, ANDROID_FORCE_NORMAL_BOOT_STR, + sizeof(ANDROID_FORCE_NORMAL_BOOT_STR)); + } +#endif +} static int cmdline_proc_show(struct seq_file *m, void *v) { - seq_puts(m, saved_command_line); + seq_puts(m, proc_command_line); seq_putc(m, '\n'); return 0; } static int __init proc_cmdline_init(void) { + proc_command_line_init(); + proc_create_single("cmdline", 0, NULL, cmdline_proc_show); return 0; } diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index d80635d66bef..102061d6edec 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -292,6 +292,8 @@ int mipi_dsi_dcs_set_display_brightness_large(struct mipi_dsi_device *dsi, u16 brightness); int mipi_dsi_dcs_get_display_brightness_large(struct mipi_dsi_device *dsi, u16 *brightness); +int mipi_dsi_dcs_set_display_brightness_samsung(struct mipi_dsi_device *dsi, + u16 brightness); /** * struct mipi_dsi_driver - DSI driver diff --git a/include/dt-bindings/clock/qcom,camcc-sdm845.h b/include/dt-bindings/clock/qcom,camcc-sdm845.h new file mode 100644 index 000000000000..35850ef51fda --- /dev/null +++ b/include/dt-bindings/clock/qcom,camcc-sdm845.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_CAM_CC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_CAM_CC_SDM845_H + +#define CAM_CC_BPS_AHB_CLK 0 +#define CAM_CC_BPS_AREG_CLK 1 +#define CAM_CC_BPS_AXI_CLK 2 +#define CAM_CC_BPS_CLK 3 +#define CAM_CC_BPS_CLK_SRC 4 +#define CAM_CC_CAMNOC_ATB_CLK 5 +#define CAM_CC_CAMNOC_AXI_CLK 6 +#define CAM_CC_CCI_CLK 7 +#define CAM_CC_CCI_CLK_SRC 8 +#define CAM_CC_CPAS_AHB_CLK 9 +#define CAM_CC_CPHY_RX_CLK_SRC 10 +#define CAM_CC_CSI0PHYTIMER_CLK 11 +#define CAM_CC_CSI0PHYTIMER_CLK_SRC 12 +#define CAM_CC_CSI1PHYTIMER_CLK 13 +#define CAM_CC_CSI1PHYTIMER_CLK_SRC 14 +#define CAM_CC_CSI2PHYTIMER_CLK 15 +#define CAM_CC_CSI2PHYTIMER_CLK_SRC 16 +#define CAM_CC_CSI3PHYTIMER_CLK 17 +#define CAM_CC_CSI3PHYTIMER_CLK_SRC 18 +#define CAM_CC_CSIPHY0_CLK 19 +#define CAM_CC_CSIPHY1_CLK 20 +#define CAM_CC_CSIPHY2_CLK 21 +#define CAM_CC_CSIPHY3_CLK 22 +#define CAM_CC_FAST_AHB_CLK_SRC 23 +#define CAM_CC_FD_CORE_CLK 24 +#define CAM_CC_FD_CORE_CLK_SRC 25 +#define CAM_CC_FD_CORE_UAR_CLK 26 +#define CAM_CC_ICP_APB_CLK 27 +#define CAM_CC_ICP_ATB_CLK 28 +#define CAM_CC_ICP_CLK 29 +#define CAM_CC_ICP_CLK_SRC 30 +#define CAM_CC_ICP_CTI_CLK 31 +#define CAM_CC_ICP_TS_CLK 32 +#define CAM_CC_IFE_0_AXI_CLK 33 +#define CAM_CC_IFE_0_CLK 34 +#define CAM_CC_IFE_0_CLK_SRC 35 +#define CAM_CC_IFE_0_CPHY_RX_CLK 36 +#define CAM_CC_IFE_0_CSID_CLK 37 +#define CAM_CC_IFE_0_CSID_CLK_SRC 38 +#define CAM_CC_IFE_0_DSP_CLK 39 +#define CAM_CC_IFE_1_AXI_CLK 40 +#define CAM_CC_IFE_1_CLK 41 +#define CAM_CC_IFE_1_CLK_SRC 42 +#define CAM_CC_IFE_1_CPHY_RX_CLK 43 +#define CAM_CC_IFE_1_CSID_CLK 44 +#define CAM_CC_IFE_1_CSID_CLK_SRC 45 +#define CAM_CC_IFE_1_DSP_CLK 46 +#define CAM_CC_IFE_LITE_CLK 47 +#define CAM_CC_IFE_LITE_CLK_SRC 48 +#define CAM_CC_IFE_LITE_CPHY_RX_CLK 49 +#define CAM_CC_IFE_LITE_CSID_CLK 50 +#define CAM_CC_IFE_LITE_CSID_CLK_SRC 51 +#define CAM_CC_IPE_0_AHB_CLK 52 +#define CAM_CC_IPE_0_AREG_CLK 53 +#define CAM_CC_IPE_0_AXI_CLK 54 +#define CAM_CC_IPE_0_CLK 55 +#define CAM_CC_IPE_0_CLK_SRC 56 +#define CAM_CC_IPE_1_AHB_CLK 57 +#define CAM_CC_IPE_1_AREG_CLK 58 +#define CAM_CC_IPE_1_AXI_CLK 59 +#define CAM_CC_IPE_1_CLK 60 +#define CAM_CC_IPE_1_CLK_SRC 61 +#define CAM_CC_JPEG_CLK 62 +#define CAM_CC_JPEG_CLK_SRC 63 +#define CAM_CC_LRME_CLK 64 +#define CAM_CC_LRME_CLK_SRC 65 +#define CAM_CC_MCLK0_CLK 66 +#define CAM_CC_MCLK0_CLK_SRC 67 +#define CAM_CC_MCLK1_CLK 68 +#define CAM_CC_MCLK1_CLK_SRC 69 +#define CAM_CC_MCLK2_CLK 70 +#define CAM_CC_MCLK2_CLK_SRC 71 +#define CAM_CC_MCLK3_CLK 72 +#define CAM_CC_MCLK3_CLK_SRC 73 +#define CAM_CC_PLL0 74 +#define CAM_CC_PLL0_OUT_EVEN 75 +#define CAM_CC_PLL1 76 +#define CAM_CC_PLL1_OUT_EVEN 77 +#define CAM_CC_PLL2 78 +#define CAM_CC_PLL2_OUT_EVEN 79 +#define CAM_CC_PLL2_OUT_ODD 80 +#define CAM_CC_PLL3 81 +#define CAM_CC_PLL3_OUT_EVEN 82 +#define CAM_CC_PLL_TEST_CLK 83 +#define CAM_CC_SLOW_AHB_CLK_SRC 84 +#define CAM_CC_SOC_AHB_CLK 85 +#define CAM_CC_SYS_TMR_CLK 86 + +#define TITAN_CAM_CC_CCI_BCR 0 +#define TITAN_CAM_CC_CPAS_BCR 1 +#define TITAN_CAM_CC_CSI0PHY_BCR 2 +#define TITAN_CAM_CC_CSI1PHY_BCR 3 +#define TITAN_CAM_CC_CSI2PHY_BCR 4 +#define TITAN_CAM_CC_MCLK0_BCR 5 +#define TITAN_CAM_CC_MCLK1_BCR 6 +#define TITAN_CAM_CC_MCLK2_BCR 7 +#define TITAN_CAM_CC_MCLK3_BCR 8 +#define TITAN_CAM_CC_TITAN_TOP_BCR 9 + +#endif diff --git a/include/dt-bindings/clock/qcom,cpucc-sdm845.h b/include/dt-bindings/clock/qcom,cpucc-sdm845.h new file mode 100644 index 000000000000..f694c222c2b9 --- /dev/null +++ b/include/dt-bindings/clock/qcom,cpucc-sdm845.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_CPU_CC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_CPU_CC_SDM845_H + +#define L3_CLK 0 +#define PWRCL_CLK 1 +#define PERFCL_CLK 2 +#define L3_CLUSTER0_VOTE_CLK 3 +#define L3_CLUSTER1_VOTE_CLK 4 +#define CPU0_PWRCL_CLK 5 +#define CPU1_PWRCL_CLK 6 +#define CPU2_PWRCL_CLK 7 +#define CPU3_PWRCL_CLK 8 +#define CPU4_PERFCL_CLK 9 +#define CPU5_PERFCL_CLK 10 +#define CPU6_PERFCL_CLK 11 +#define CPU7_PERFCL_CLK 12 +#define L3_MISC_VOTE_CLK 13 +#define CPU4_PWRCL_CLK 14 +#define CPU5_PWRCL_CLK 15 +#define L3_GPU_VOTE_CLK 16 + +#endif diff --git a/include/dt-bindings/clock/qcom,dispcc-sdm845.h b/include/dt-bindings/clock/qcom,dispcc-sdm845.h index 11eed4bc9646..18db9ba5d022 100644 --- a/include/dt-bindings/clock/qcom,dispcc-sdm845.h +++ b/include/dt-bindings/clock/qcom,dispcc-sdm845.h @@ -1,12 +1,11 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. */ -#ifndef _DT_BINDINGS_CLK_SDM_DISP_CC_SDM845_H -#define _DT_BINDINGS_CLK_SDM_DISP_CC_SDM845_H +#ifndef _DT_BINDINGS_CLK_MSM_DISP_CC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_DISP_CC_SDM845_H -/* DISP_CC clock registers */ #define DISP_CC_MDSS_AHB_CLK 0 #define DISP_CC_MDSS_AXI_CLK 1 #define DISP_CC_MDSS_BYTE0_CLK 2 @@ -15,31 +14,40 @@ #define DISP_CC_MDSS_BYTE1_CLK 5 #define DISP_CC_MDSS_BYTE1_CLK_SRC 6 #define DISP_CC_MDSS_BYTE1_INTF_CLK 7 -#define DISP_CC_MDSS_ESC0_CLK 8 -#define DISP_CC_MDSS_ESC0_CLK_SRC 9 -#define DISP_CC_MDSS_ESC1_CLK 10 -#define DISP_CC_MDSS_ESC1_CLK_SRC 11 -#define DISP_CC_MDSS_MDP_CLK 12 -#define DISP_CC_MDSS_MDP_CLK_SRC 13 -#define DISP_CC_MDSS_MDP_LUT_CLK 14 -#define DISP_CC_MDSS_PCLK0_CLK 15 -#define DISP_CC_MDSS_PCLK0_CLK_SRC 16 -#define DISP_CC_MDSS_PCLK1_CLK 17 -#define DISP_CC_MDSS_PCLK1_CLK_SRC 18 -#define DISP_CC_MDSS_ROT_CLK 19 -#define DISP_CC_MDSS_ROT_CLK_SRC 20 -#define DISP_CC_MDSS_RSCC_AHB_CLK 21 -#define DISP_CC_MDSS_RSCC_VSYNC_CLK 22 -#define DISP_CC_MDSS_VSYNC_CLK 23 -#define DISP_CC_MDSS_VSYNC_CLK_SRC 24 -#define DISP_CC_PLL0 25 -#define DISP_CC_MDSS_BYTE0_DIV_CLK_SRC 26 -#define DISP_CC_MDSS_BYTE1_DIV_CLK_SRC 27 +#define DISP_CC_MDSS_DP_AUX_CLK 8 +#define DISP_CC_MDSS_DP_AUX_CLK_SRC 9 +#define DISP_CC_MDSS_DP_CRYPTO_CLK 10 +#define DISP_CC_MDSS_DP_CRYPTO_CLK_SRC 11 +#define DISP_CC_MDSS_DP_LINK_CLK 12 +#define DISP_CC_MDSS_DP_LINK_CLK_SRC 13 +#define DISP_CC_MDSS_DP_LINK_INTF_CLK 14 +#define DISP_CC_MDSS_DP_PIXEL1_CLK 15 +#define DISP_CC_MDSS_DP_PIXEL1_CLK_SRC 16 +#define DISP_CC_MDSS_DP_PIXEL_CLK 17 +#define DISP_CC_MDSS_DP_PIXEL_CLK_SRC 18 +#define DISP_CC_MDSS_ESC0_CLK 19 +#define DISP_CC_MDSS_ESC0_CLK_SRC 20 +#define DISP_CC_MDSS_ESC1_CLK 21 +#define DISP_CC_MDSS_ESC1_CLK_SRC 22 +#define DISP_CC_MDSS_MDP_CLK 23 +#define DISP_CC_MDSS_MDP_CLK_SRC 24 +#define DISP_CC_MDSS_MDP_LUT_CLK 25 +#define DISP_CC_MDSS_PCLK0_CLK 26 +#define DISP_CC_MDSS_PCLK0_CLK_SRC 27 +#define DISP_CC_MDSS_PCLK1_CLK 28 +#define DISP_CC_MDSS_PCLK1_CLK_SRC 29 +#define DISP_CC_MDSS_QDSS_AT_CLK 30 +#define DISP_CC_MDSS_QDSS_TSCTR_DIV8_CLK 31 +#define DISP_CC_MDSS_ROT_CLK 32 +#define DISP_CC_MDSS_ROT_CLK_SRC 33 +#define DISP_CC_MDSS_RSCC_AHB_CLK 34 +#define DISP_CC_MDSS_RSCC_VSYNC_CLK 35 +#define DISP_CC_MDSS_VSYNC_CLK 36 +#define DISP_CC_MDSS_VSYNC_CLK_SRC 37 +#define DISP_CC_PLL0 38 +#define DISP_CC_MDSS_BYTE0_DIV_CLK_SRC 39 +#define DISP_CC_MDSS_BYTE1_DIV_CLK_SRC 40 -/* DISP_CC Reset */ #define DISP_CC_MDSS_RSCC_BCR 0 -/* DISP_CC GDSCR */ -#define MDSS_GDSC 0 - -#endif +#endif \ No newline at end of file diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h index f96fc2dbf60e..711176bfc2e9 100644 --- a/include/dt-bindings/clock/qcom,gcc-sdm845.h +++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h @@ -1,201 +1,220 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ -#ifndef _DT_BINDINGS_CLK_SDM_GCC_SDM845_H -#define _DT_BINDINGS_CLK_SDM_GCC_SDM845_H +#ifndef _DT_BINDINGS_CLK_MSM_GCC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_GCC_SDM845_H + +/* Dummy clocks for rate measurement */ +#define MEASURE_ONLY_SNOC_CLK 0 +#define MEASURE_ONLY_CNOC_CLK 1 +#define MEASURE_ONLY_BIMC_CLK 2 +#define MEASURE_ONLY_IPA_2X_CLK 3 +#define UFS_PHY_AXI_EMMC_VOTE_CLK 4 +#define UFS_PHY_AXI_UFS_VOTE_CLK 5 /* GCC clock registers */ -#define GCC_AGGRE_NOC_PCIE_TBU_CLK 0 -#define GCC_AGGRE_UFS_CARD_AXI_CLK 1 -#define GCC_AGGRE_UFS_PHY_AXI_CLK 2 -#define GCC_AGGRE_USB3_PRIM_AXI_CLK 3 -#define GCC_AGGRE_USB3_SEC_AXI_CLK 4 -#define GCC_BOOT_ROM_AHB_CLK 5 -#define GCC_CAMERA_AHB_CLK 6 -#define GCC_CAMERA_AXI_CLK 7 -#define GCC_CAMERA_XO_CLK 8 -#define GCC_CE1_AHB_CLK 9 -#define GCC_CE1_AXI_CLK 10 -#define GCC_CE1_CLK 11 -#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 12 -#define GCC_CFG_NOC_USB3_SEC_AXI_CLK 13 -#define GCC_CPUSS_AHB_CLK 14 -#define GCC_CPUSS_AHB_CLK_SRC 15 -#define GCC_CPUSS_RBCPR_CLK 16 -#define GCC_CPUSS_RBCPR_CLK_SRC 17 -#define GCC_DDRSS_GPU_AXI_CLK 18 -#define GCC_DISP_AHB_CLK 19 -#define GCC_DISP_AXI_CLK 20 -#define GCC_DISP_GPLL0_CLK_SRC 21 -#define GCC_DISP_GPLL0_DIV_CLK_SRC 22 -#define GCC_DISP_XO_CLK 23 -#define GCC_GP1_CLK 24 -#define GCC_GP1_CLK_SRC 25 -#define GCC_GP2_CLK 26 -#define GCC_GP2_CLK_SRC 27 -#define GCC_GP3_CLK 28 -#define GCC_GP3_CLK_SRC 29 -#define GCC_GPU_CFG_AHB_CLK 30 -#define GCC_GPU_GPLL0_CLK_SRC 31 -#define GCC_GPU_GPLL0_DIV_CLK_SRC 32 -#define GCC_GPU_MEMNOC_GFX_CLK 33 -#define GCC_GPU_SNOC_DVM_GFX_CLK 34 -#define GCC_MSS_AXIS2_CLK 35 -#define GCC_MSS_CFG_AHB_CLK 36 -#define GCC_MSS_GPLL0_DIV_CLK_SRC 37 -#define GCC_MSS_MFAB_AXIS_CLK 38 -#define GCC_MSS_Q6_MEMNOC_AXI_CLK 39 -#define GCC_MSS_SNOC_AXI_CLK 40 -#define GCC_PCIE_0_AUX_CLK 41 -#define GCC_PCIE_0_AUX_CLK_SRC 42 -#define GCC_PCIE_0_CFG_AHB_CLK 43 -#define GCC_PCIE_0_CLKREF_CLK 44 -#define GCC_PCIE_0_MSTR_AXI_CLK 45 -#define GCC_PCIE_0_PIPE_CLK 46 -#define GCC_PCIE_0_SLV_AXI_CLK 47 -#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 48 -#define GCC_PCIE_1_AUX_CLK 49 -#define GCC_PCIE_1_AUX_CLK_SRC 50 -#define GCC_PCIE_1_CFG_AHB_CLK 51 -#define GCC_PCIE_1_CLKREF_CLK 52 -#define GCC_PCIE_1_MSTR_AXI_CLK 53 -#define GCC_PCIE_1_PIPE_CLK 54 -#define GCC_PCIE_1_SLV_AXI_CLK 55 -#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 56 -#define GCC_PCIE_PHY_AUX_CLK 57 -#define GCC_PCIE_PHY_REFGEN_CLK 58 -#define GCC_PCIE_PHY_REFGEN_CLK_SRC 59 -#define GCC_PDM2_CLK 60 -#define GCC_PDM2_CLK_SRC 61 -#define GCC_PDM_AHB_CLK 62 -#define GCC_PDM_XO4_CLK 63 -#define GCC_PRNG_AHB_CLK 64 -#define GCC_QMIP_CAMERA_AHB_CLK 65 -#define GCC_QMIP_DISP_AHB_CLK 66 -#define GCC_QMIP_VIDEO_AHB_CLK 67 -#define GCC_QUPV3_WRAP0_S0_CLK 68 -#define GCC_QUPV3_WRAP0_S0_CLK_SRC 69 -#define GCC_QUPV3_WRAP0_S1_CLK 70 -#define GCC_QUPV3_WRAP0_S1_CLK_SRC 71 -#define GCC_QUPV3_WRAP0_S2_CLK 72 -#define GCC_QUPV3_WRAP0_S2_CLK_SRC 73 -#define GCC_QUPV3_WRAP0_S3_CLK 74 -#define GCC_QUPV3_WRAP0_S3_CLK_SRC 75 -#define GCC_QUPV3_WRAP0_S4_CLK 76 -#define GCC_QUPV3_WRAP0_S4_CLK_SRC 77 -#define GCC_QUPV3_WRAP0_S5_CLK 78 -#define GCC_QUPV3_WRAP0_S5_CLK_SRC 79 -#define GCC_QUPV3_WRAP0_S6_CLK 80 -#define GCC_QUPV3_WRAP0_S6_CLK_SRC 81 -#define GCC_QUPV3_WRAP0_S7_CLK 82 -#define GCC_QUPV3_WRAP0_S7_CLK_SRC 83 -#define GCC_QUPV3_WRAP1_S0_CLK 84 -#define GCC_QUPV3_WRAP1_S0_CLK_SRC 85 -#define GCC_QUPV3_WRAP1_S1_CLK 86 -#define GCC_QUPV3_WRAP1_S1_CLK_SRC 87 -#define GCC_QUPV3_WRAP1_S2_CLK 88 -#define GCC_QUPV3_WRAP1_S2_CLK_SRC 89 -#define GCC_QUPV3_WRAP1_S3_CLK 90 -#define GCC_QUPV3_WRAP1_S3_CLK_SRC 91 -#define GCC_QUPV3_WRAP1_S4_CLK 92 -#define GCC_QUPV3_WRAP1_S4_CLK_SRC 93 -#define GCC_QUPV3_WRAP1_S5_CLK 94 -#define GCC_QUPV3_WRAP1_S5_CLK_SRC 95 -#define GCC_QUPV3_WRAP1_S6_CLK 96 -#define GCC_QUPV3_WRAP1_S6_CLK_SRC 97 -#define GCC_QUPV3_WRAP1_S7_CLK 98 -#define GCC_QUPV3_WRAP1_S7_CLK_SRC 99 -#define GCC_QUPV3_WRAP_0_M_AHB_CLK 100 -#define GCC_QUPV3_WRAP_0_S_AHB_CLK 101 -#define GCC_QUPV3_WRAP_1_M_AHB_CLK 102 -#define GCC_QUPV3_WRAP_1_S_AHB_CLK 103 -#define GCC_SDCC2_AHB_CLK 104 -#define GCC_SDCC2_APPS_CLK 105 -#define GCC_SDCC2_APPS_CLK_SRC 106 -#define GCC_SDCC4_AHB_CLK 107 -#define GCC_SDCC4_APPS_CLK 108 -#define GCC_SDCC4_APPS_CLK_SRC 109 -#define GCC_SYS_NOC_CPUSS_AHB_CLK 110 -#define GCC_TSIF_AHB_CLK 111 -#define GCC_TSIF_INACTIVITY_TIMERS_CLK 112 -#define GCC_TSIF_REF_CLK 113 -#define GCC_TSIF_REF_CLK_SRC 114 -#define GCC_UFS_CARD_AHB_CLK 115 -#define GCC_UFS_CARD_AXI_CLK 116 -#define GCC_UFS_CARD_AXI_CLK_SRC 117 -#define GCC_UFS_CARD_CLKREF_CLK 118 -#define GCC_UFS_CARD_ICE_CORE_CLK 119 -#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 120 -#define GCC_UFS_CARD_PHY_AUX_CLK 121 -#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 122 -#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 123 -#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 124 -#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 125 -#define GCC_UFS_CARD_UNIPRO_CORE_CLK 126 -#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 127 -#define GCC_UFS_MEM_CLKREF_CLK 128 -#define GCC_UFS_PHY_AHB_CLK 129 -#define GCC_UFS_PHY_AXI_CLK 130 -#define GCC_UFS_PHY_AXI_CLK_SRC 131 -#define GCC_UFS_PHY_ICE_CORE_CLK 132 -#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 133 -#define GCC_UFS_PHY_PHY_AUX_CLK 134 -#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 135 -#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 136 -#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 137 -#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 138 -#define GCC_UFS_PHY_UNIPRO_CORE_CLK 139 -#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 140 -#define GCC_USB30_PRIM_MASTER_CLK 141 -#define GCC_USB30_PRIM_MASTER_CLK_SRC 142 -#define GCC_USB30_PRIM_MOCK_UTMI_CLK 143 -#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 144 -#define GCC_USB30_PRIM_SLEEP_CLK 145 -#define GCC_USB30_SEC_MASTER_CLK 146 -#define GCC_USB30_SEC_MASTER_CLK_SRC 147 -#define GCC_USB30_SEC_MOCK_UTMI_CLK 148 -#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 149 -#define GCC_USB30_SEC_SLEEP_CLK 150 -#define GCC_USB3_PRIM_CLKREF_CLK 151 -#define GCC_USB3_PRIM_PHY_AUX_CLK 152 -#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 153 -#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 154 -#define GCC_USB3_PRIM_PHY_PIPE_CLK 155 -#define GCC_USB3_SEC_CLKREF_CLK 156 -#define GCC_USB3_SEC_PHY_AUX_CLK 157 -#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 158 -#define GCC_USB3_SEC_PHY_PIPE_CLK 159 -#define GCC_USB3_SEC_PHY_COM_AUX_CLK 160 -#define GCC_USB_PHY_CFG_AHB2PHY_CLK 161 -#define GCC_VIDEO_AHB_CLK 162 -#define GCC_VIDEO_AXI_CLK 163 -#define GCC_VIDEO_XO_CLK 164 -#define GPLL0 165 -#define GPLL0_OUT_EVEN 166 -#define GPLL0_OUT_MAIN 167 -#define GCC_GPU_IREF_CLK 168 -#define GCC_SDCC1_AHB_CLK 169 -#define GCC_SDCC1_APPS_CLK 170 -#define GCC_SDCC1_ICE_CORE_CLK 171 -#define GCC_SDCC1_APPS_CLK_SRC 172 -#define GCC_SDCC1_ICE_CORE_CLK_SRC 173 -#define GCC_APC_VS_CLK 174 -#define GCC_GPU_VS_CLK 175 -#define GCC_MSS_VS_CLK 176 -#define GCC_VDDA_VS_CLK 177 -#define GCC_VDDCX_VS_CLK 178 -#define GCC_VDDMX_VS_CLK 179 -#define GCC_VS_CTRL_AHB_CLK 180 -#define GCC_VS_CTRL_CLK 181 -#define GCC_VS_CTRL_CLK_SRC 182 -#define GCC_VSENSOR_CLK_SRC 183 -#define GPLL4 184 -#define GCC_CPUSS_DVM_BUS_CLK 185 -#define GCC_CPUSS_GNOC_CLK 186 +#define GCC_AGGRE_NOC_PCIE_TBU_CLK 6 +#define GCC_AGGRE_UFS_CARD_AXI_CLK 7 +#define GCC_AGGRE_UFS_PHY_AXI_CLK 8 +#define GCC_AGGRE_USB3_PRIM_AXI_CLK 9 +#define GCC_AGGRE_USB3_SEC_AXI_CLK 10 +#define GCC_BOOT_ROM_AHB_CLK 11 +#define GCC_CAMERA_AHB_CLK 12 +#define GCC_CAMERA_AXI_CLK 13 +#define GCC_CAMERA_XO_CLK 14 +#define GCC_CE1_AHB_CLK 15 +#define GCC_CE1_AXI_CLK 16 +#define GCC_CE1_CLK 17 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 18 +#define GCC_CFG_NOC_USB3_SEC_AXI_CLK 19 +#define GCC_CPUSS_AHB_CLK 20 +#define GCC_CPUSS_AHB_CLK_SRC 21 +#define GCC_CPUSS_DVM_BUS_CLK 22 +#define GCC_CPUSS_GNOC_CLK 23 +#define GCC_CPUSS_RBCPR_CLK 24 +#define GCC_CPUSS_RBCPR_CLK_SRC 25 +#define GCC_DDRSS_GPU_AXI_CLK 26 +#define GCC_DISP_AHB_CLK 27 +#define GCC_DISP_AXI_CLK 28 +#define GCC_DISP_GPLL0_CLK_SRC 29 +#define GCC_DISP_GPLL0_DIV_CLK_SRC 30 +#define GCC_DISP_XO_CLK 31 +#define GCC_GP1_CLK 32 +#define GCC_GP1_CLK_SRC 33 +#define GCC_GP2_CLK 34 +#define GCC_GP2_CLK_SRC 35 +#define GCC_GP3_CLK 36 +#define GCC_GP3_CLK_SRC 37 +#define GCC_GPU_CFG_AHB_CLK 38 +#define GCC_GPU_GPLL0_CLK_SRC 39 +#define GCC_GPU_GPLL0_DIV_CLK_SRC 40 +#define GCC_GPU_MEMNOC_GFX_CLK 41 +#define GCC_GPU_SNOC_DVM_GFX_CLK 42 +#define GCC_MSS_AXIS2_CLK 43 +#define GCC_MSS_CFG_AHB_CLK 44 +#define GCC_MSS_GPLL0_DIV_CLK_SRC 45 +#define GCC_MSS_MFAB_AXIS_CLK 46 +#define GCC_MSS_Q6_MEMNOC_AXI_CLK 47 +#define GCC_MSS_SNOC_AXI_CLK 48 +#define GCC_PCIE_0_AUX_CLK 49 +#define GCC_PCIE_0_AUX_CLK_SRC 50 +#define GCC_PCIE_0_CFG_AHB_CLK 51 +#define GCC_PCIE_0_CLKREF_CLK 52 +#define GCC_PCIE_0_MSTR_AXI_CLK 53 +#define GCC_PCIE_0_PIPE_CLK 54 +#define GCC_PCIE_0_SLV_AXI_CLK 55 +#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 56 +#define GCC_PCIE_1_AUX_CLK 57 +#define GCC_PCIE_1_AUX_CLK_SRC 58 +#define GCC_PCIE_1_CFG_AHB_CLK 59 +#define GCC_PCIE_1_CLKREF_CLK 60 +#define GCC_PCIE_1_MSTR_AXI_CLK 61 +#define GCC_PCIE_1_PIPE_CLK 62 +#define GCC_PCIE_1_SLV_AXI_CLK 63 +#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 64 +#define GCC_PCIE_PHY_AUX_CLK 65 +#define GCC_PCIE_PHY_REFGEN_CLK 66 +#define GCC_PCIE_PHY_REFGEN_CLK_SRC 67 +#define GCC_PDM2_CLK 68 +#define GCC_PDM2_CLK_SRC 69 +#define GCC_PDM_AHB_CLK 70 +#define GCC_PDM_XO4_CLK 71 +#define GCC_PRNG_AHB_CLK 72 +#define GCC_QMIP_CAMERA_AHB_CLK 73 +#define GCC_QMIP_DISP_AHB_CLK 74 +#define GCC_QMIP_VIDEO_AHB_CLK 75 +#define GCC_QUPV3_WRAP0_S0_CLK 76 +#define GCC_QUPV3_WRAP0_S0_CLK_SRC 77 +#define GCC_QUPV3_WRAP0_S1_CLK 78 +#define GCC_QUPV3_WRAP0_S1_CLK_SRC 79 +#define GCC_QUPV3_WRAP0_S2_CLK 80 +#define GCC_QUPV3_WRAP0_S2_CLK_SRC 81 +#define GCC_QUPV3_WRAP0_S3_CLK 82 +#define GCC_QUPV3_WRAP0_S3_CLK_SRC 83 +#define GCC_QUPV3_WRAP0_S4_CLK 84 +#define GCC_QUPV3_WRAP0_S4_CLK_SRC 85 +#define GCC_QUPV3_WRAP0_S5_CLK 86 +#define GCC_QUPV3_WRAP0_S5_CLK_SRC 87 +#define GCC_QUPV3_WRAP0_S6_CLK 88 +#define GCC_QUPV3_WRAP0_S6_CLK_SRC 89 +#define GCC_QUPV3_WRAP0_S7_CLK 90 +#define GCC_QUPV3_WRAP0_S7_CLK_SRC 91 +#define GCC_QUPV3_WRAP1_S0_CLK 92 +#define GCC_QUPV3_WRAP1_S0_CLK_SRC 93 +#define GCC_QUPV3_WRAP1_S1_CLK 94 +#define GCC_QUPV3_WRAP1_S1_CLK_SRC 95 +#define GCC_QUPV3_WRAP1_S2_CLK 96 +#define GCC_QUPV3_WRAP1_S2_CLK_SRC 97 +#define GCC_QUPV3_WRAP1_S3_CLK 98 +#define GCC_QUPV3_WRAP1_S3_CLK_SRC 99 +#define GCC_QUPV3_WRAP1_S4_CLK 100 +#define GCC_QUPV3_WRAP1_S4_CLK_SRC 101 +#define GCC_QUPV3_WRAP1_S5_CLK 102 +#define GCC_QUPV3_WRAP1_S5_CLK_SRC 103 +#define GCC_QUPV3_WRAP1_S6_CLK 104 +#define GCC_QUPV3_WRAP1_S6_CLK_SRC 105 +#define GCC_QUPV3_WRAP1_S7_CLK 106 +#define GCC_QUPV3_WRAP1_S7_CLK_SRC 107 +#define GCC_QUPV3_WRAP_0_M_AHB_CLK 108 +#define GCC_QUPV3_WRAP_0_S_AHB_CLK 109 +#define GCC_QUPV3_WRAP_1_M_AHB_CLK 110 +#define GCC_QUPV3_WRAP_1_S_AHB_CLK 111 +#define GCC_SDCC2_AHB_CLK 112 +#define GCC_SDCC2_APPS_CLK 113 +#define GCC_SDCC2_APPS_CLK_SRC 114 +#define GCC_SDCC4_AHB_CLK 115 +#define GCC_SDCC4_APPS_CLK 116 +#define GCC_SDCC4_APPS_CLK_SRC 117 +#define GCC_SYS_NOC_CPUSS_AHB_CLK 118 +#define GCC_TSIF_AHB_CLK 119 +#define GCC_TSIF_INACTIVITY_TIMERS_CLK 120 +#define GCC_TSIF_REF_CLK 121 +#define GCC_TSIF_REF_CLK_SRC 122 +#define GCC_UFS_CARD_AHB_CLK 123 +#define GCC_UFS_CARD_AXI_CLK 124 +#define GCC_UFS_CARD_AXI_CLK_SRC 125 +#define GCC_UFS_CARD_CLKREF_CLK 126 +#define GCC_UFS_CARD_ICE_CORE_CLK 127 +#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 128 +#define GCC_UFS_CARD_PHY_AUX_CLK 129 +#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 130 +#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 131 +#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 132 +#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 133 +#define GCC_UFS_CARD_UNIPRO_CORE_CLK 134 +#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 135 +#define GCC_UFS_MEM_CLKREF_CLK 136 +#define GCC_UFS_PHY_AHB_CLK 137 +#define GCC_UFS_PHY_AXI_CLK 138 +#define GCC_UFS_PHY_AXI_CLK_SRC 139 +#define GCC_UFS_PHY_ICE_CORE_CLK 140 +#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 141 +#define GCC_UFS_PHY_PHY_AUX_CLK 142 +#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 143 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 144 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 145 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 146 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK 147 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 148 +#define GCC_USB30_PRIM_MASTER_CLK 149 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 150 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 151 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 152 +#define GCC_USB30_PRIM_SLEEP_CLK 153 +#define GCC_USB30_SEC_MASTER_CLK 154 +#define GCC_USB30_SEC_MASTER_CLK_SRC 155 +#define GCC_USB30_SEC_MOCK_UTMI_CLK 156 +#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 157 +#define GCC_USB30_SEC_SLEEP_CLK 158 +#define GCC_USB3_PRIM_CLKREF_CLK 159 +#define GCC_USB3_PRIM_PHY_AUX_CLK 160 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 161 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 162 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 163 +#define GCC_USB3_SEC_CLKREF_CLK 164 +#define GCC_USB3_SEC_PHY_AUX_CLK 165 +#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 166 +#define GCC_USB3_SEC_PHY_COM_AUX_CLK 167 +#define GCC_USB3_SEC_PHY_PIPE_CLK 168 +#define GCC_USB_PHY_CFG_AHB2PHY_CLK 169 +#define GCC_VIDEO_AHB_CLK 170 +#define GCC_VIDEO_AXI_CLK 171 +#define GCC_VIDEO_XO_CLK 172 +#define GPLL0 173 +#define GPLL0_OUT_EVEN 174 +#define GPLL0_OUT_MAIN 175 +#define GCC_UFS_CARD_AXI_HW_CTL_CLK 176 +#define GCC_UFS_PHY_AXI_HW_CTL_CLK 177 +#define GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK 178 +#define GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK 179 +#define GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK 180 +#define GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK 181 +#define GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK 182 +#define GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK 183 +#define GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK 184 +#define GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK 185 +#define GCC_GPU_IREF_CLK 186 +#define GCC_SDCC1_AHB_CLK 187 +#define GCC_SDCC1_APPS_CLK 188 +#define GCC_SDCC1_ICE_CORE_CLK 189 +#define GCC_SDCC1_APPS_CLK_SRC 190 +#define GCC_SDCC1_ICE_CORE_CLK_SRC 191 +#define GCC_APC_VS_CLK 192 +#define GCC_GPU_VS_CLK 193 +#define GCC_MSS_VS_CLK 194 +#define GCC_VDDA_VS_CLK 195 +#define GCC_VDDCX_VS_CLK 196 +#define GCC_VDDMX_VS_CLK 197 +#define GCC_VS_CTRL_AHB_CLK 198 +#define GCC_VS_CTRL_CLK 199 +#define GCC_VS_CTRL_CLK_SRC 200 +#define GCC_VSENSOR_CLK_SRC 201 +#define GPLL4 202 +#define GPLL6 203 -/* GCC Resets */ +/* GCC reset clocks */ #define GCC_MMSS_BCR 0 #define GCC_PCIE_0_BCR 1 #define GCC_PCIE_1_BCR 2 @@ -222,20 +241,6 @@ #define GCC_USB_PHY_CFG_AHB2PHY_BCR 23 #define GCC_PCIE_0_PHY_BCR 24 #define GCC_PCIE_1_PHY_BCR 25 - -/* GCC GDSCRs */ -#define PCIE_0_GDSC 0 -#define PCIE_1_GDSC 1 -#define UFS_CARD_GDSC 2 -#define UFS_PHY_GDSC 3 -#define USB30_PRIM_GDSC 4 -#define USB30_SEC_GDSC 5 -#define HLOS1_VOTE_AGGRE_NOC_MMU_AUDIO_TBU_GDSC 6 -#define HLOS1_VOTE_AGGRE_NOC_MMU_PCIE_TBU_GDSC 7 -#define HLOS1_VOTE_AGGRE_NOC_MMU_TBU1_GDSC 8 -#define HLOS1_VOTE_AGGRE_NOC_MMU_TBU2_GDSC 9 -#define HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC 10 -#define HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC 11 -#define HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC 12 +#define GCC_SDCC1_BCR 26 #endif diff --git a/include/dt-bindings/clock/qcom,gpucc-sdm845.h b/include/dt-bindings/clock/qcom,gpucc-sdm845.h new file mode 100644 index 000000000000..a82a630734f2 --- /dev/null +++ b/include/dt-bindings/clock/qcom,gpucc-sdm845.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef _DT_BINDINGS_CLK_MSM_GPU_CC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_GPU_CC_SDM845_H + +/* GPUCC clock registers */ +#define GPU_CC_ACD_AHB_CLK 0 +#define GPU_CC_ACD_CXO_CLK 1 +#define GPU_CC_CRC_AHB_CLK 2 +#define GPU_CC_CX_APB_CLK 3 +#define GPU_CC_CX_GMU_CLK 4 +#define GPU_CC_CX_QDSS_AT_CLK 5 +#define GPU_CC_CX_QDSS_TRIG_CLK 6 +#define GPU_CC_CX_QDSS_TSCTR_CLK 7 +#define GPU_CC_CX_SNOC_DVM_CLK 8 +#define GPU_CC_CXO_AON_CLK 9 +#define GPU_CC_CXO_CLK 10 +#define GPU_CC_GX_GMU_CLK 11 +#define GPU_CC_GX_QDSS_TSCTR_CLK 12 +#define GPU_CC_GX_VSENSE_CLK 13 +#define GPU_CC_PLL0_OUT_MAIN 14 +#define GPU_CC_PLL0_OUT_ODD 15 +#define GPU_CC_PLL0_OUT_TEST 16 +#define GPU_CC_PLL1 17 +#define GPU_CC_PLL1_OUT_EVEN 18 +#define GPU_CC_PLL1_OUT_MAIN 19 +#define GPU_CC_PLL1_OUT_ODD 20 +#define GPU_CC_PLL1_OUT_TEST 21 +#define GPU_CC_PLL_TEST_CLK 22 +#define GPU_CC_SLEEP_CLK 23 +#define GPU_CC_GMU_CLK_SRC 24 +#define GPU_CC_CX_GFX3D_CLK 25 +#define GPU_CC_CX_GFX3D_SLV_CLK 26 +#define GPU_CC_PLL0 27 + +/* GPUCC reset clock registers */ +#define GPUCC_GPU_CC_ACD_BCR 0 +#define GPUCC_GPU_CC_CX_BCR 1 +#define GPUCC_GPU_CC_GFX3D_AON_BCR 2 +#define GPUCC_GPU_CC_GMU_BCR 3 +#define GPUCC_GPU_CC_GX_BCR 4 +#define GPUCC_GPU_CC_SPDM_BCR 5 +#define GPUCC_GPU_CC_XO_BCR 6 + +/* GFX3D clock registers */ +#define GPU_CC_PLL0_OUT_EVEN 1 +#define GPU_CC_GX_GFX3D_CLK_SRC 2 +#define GPU_CC_GX_GFX3D_CLK 3 +#endif diff --git a/include/dt-bindings/clock/qcom,videocc-sdm845.h b/include/dt-bindings/clock/qcom,videocc-sdm845.h index 1b868165e8ce..5d25292ad0f7 100644 --- a/include/dt-bindings/clock/qcom,videocc-sdm845.h +++ b/include/dt-bindings/clock/qcom,videocc-sdm845.h @@ -1,35 +1,23 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. */ -#ifndef _DT_BINDINGS_CLK_SDM_VIDEO_CC_SDM845_H -#define _DT_BINDINGS_CLK_SDM_VIDEO_CC_SDM845_H +#ifndef _DT_BINDINGS_CLK_MSM_VIDEO_CC_SDM845_H +#define _DT_BINDINGS_CLK_MSM_VIDEO_CC_SDM845_H -/* VIDEO_CC clock registers */ -#define VIDEO_CC_APB_CLK 0 -#define VIDEO_CC_AT_CLK 1 -#define VIDEO_CC_QDSS_TRIG_CLK 2 -#define VIDEO_CC_QDSS_TSCTR_DIV8_CLK 3 -#define VIDEO_CC_VCODEC0_AXI_CLK 4 -#define VIDEO_CC_VCODEC0_CORE_CLK 5 -#define VIDEO_CC_VCODEC1_AXI_CLK 6 -#define VIDEO_CC_VCODEC1_CORE_CLK 7 -#define VIDEO_CC_VENUS_AHB_CLK 8 -#define VIDEO_CC_VENUS_CLK_SRC 9 -#define VIDEO_CC_VENUS_CTL_AXI_CLK 10 -#define VIDEO_CC_VENUS_CTL_CORE_CLK 11 -#define VIDEO_PLL0 12 - -/* VIDEO_CC Resets */ -#define VIDEO_CC_VENUS_BCR 0 -#define VIDEO_CC_VCODEC0_BCR 1 -#define VIDEO_CC_VCODEC1_BCR 2 -#define VIDEO_CC_INTERFACE_BCR 3 - -/* VIDEO_CC GDSCRs */ -#define VENUS_GDSC 0 -#define VCODEC0_GDSC 1 -#define VCODEC1_GDSC 2 +#define VIDEO_CC_APB_CLK 0 +#define VIDEO_CC_AT_CLK 1 +#define VIDEO_CC_QDSS_TRIG_CLK 2 +#define VIDEO_CC_QDSS_TSCTR_DIV8_CLK 3 +#define VIDEO_CC_VCODEC0_AXI_CLK 4 +#define VIDEO_CC_VCODEC0_CORE_CLK 5 +#define VIDEO_CC_VCODEC1_AXI_CLK 6 +#define VIDEO_CC_VCODEC1_CORE_CLK 7 +#define VIDEO_CC_VENUS_AHB_CLK 8 +#define VIDEO_CC_VENUS_CLK_SRC 9 +#define VIDEO_CC_VENUS_CTL_AXI_CLK 10 +#define VIDEO_CC_VENUS_CTL_CORE_CLK 11 +#define VIDEO_PLL0 12 #endif diff --git a/include/linux/adj_chain.h b/include/linux/adj_chain.h new file mode 100644 index 000000000000..f0579c0cdc11 --- /dev/null +++ b/include/linux/adj_chain.h @@ -0,0 +1,46 @@ +#ifndef _LINUX_ADJ_CHAIN_H +#define _LINUX_ADJ_CHAIN_H + +#include +#include +#include + +#define ADJ_CHAIN_MAX ((OOM_SCORE_ADJ_MAX) - (OOM_SCORE_ADJ_MIN)) + +// real adj to adj chain index +#define __adjc(adj) (adj + OOM_SCORE_ADJ_MAX) + +// adj chain index to real adj +#define __adjr(adj) (adj - OOM_SCORE_ADJ_MAX) + +// adj chain helper +#define __adj_tasks(adj) (adj_chain[adj].adj_chain_tasks) +#define adj_tasks(adj) (adj_chain[__adjc(adj)].adj_chain_tasks) +#define get_oom_score_adj(p) (p->signal->oom_score_adj) +#define get_task_struct_adj_chain_rcu(h) \ + list_entry_rcu(h, struct task_struct, adj_chain_tasks) +enum { + AC_ATTACH, + AC_DETACH, + AC_UPDATE_ADJ, + AC_PROM_LEADER +}; + +#ifdef CONFIG_ADJ_CHAIN +#define adj_chain_init_list(p) INIT_LIST_HEAD(&(p)->adj_chain_tasks) +#define adj_chain_empty(adj) list_empty(&__adj_tasks(adj)) +extern struct list_head adj_chain[ADJ_CHAIN_MAX + 1]; +extern int adj_chain_ready; +extern int adj_chain_hist_high; +extern void adj_chain_update_oom_score_adj(struct task_struct *p); +extern void adj_chain_attach(struct task_struct *p); +extern void adj_chain_detach(struct task_struct *p); +#else +#define adj_chain_init_list(p) +#define adj_chain_empty(adj) (1) +static inline void adj_chain_update_oom_score_adj(struct task_struct *p) { } +static inline void adj_chain_attach(struct task_struct *p) { } +static inline void adj_chain_detach(struct task_struct *p) { } +#endif // CONFIG_ADJ_CHAIN + +#endif // _LINUX_ADJ_CHAIN_H diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7fd4a16426a8..a55bf65e8500 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -48,6 +48,8 @@ * to first consumer that enables clk */ #define CLK_IS_MEASURE BIT(16) /* measure clock */ +/* do not call clk_change_rate on the clock's children */ +#define CLK_CHILD_NO_RATE_PROP BIT(17) struct clk; struct clk_hw; diff --git a/include/linux/input/qpnp-power-on.h b/include/linux/input/qpnp-power-on.h index d80ab2b31d43..d29b29fabb78 100644 --- a/include/linux/input/qpnp-power-on.h +++ b/include/linux/input/qpnp-power-on.h @@ -46,6 +46,45 @@ enum pon_power_off_type { PON_POWER_OFF_MAX_TYPE = 0x10, }; +/* david.liu@bsp, 20171023 Battery & Charging porting */ +struct qpnp_pon { + struct device *dev; + 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; + u16 base; + u8 subtype; + u8 pon_ver; + u8 warm_reset_reason1; + u8 warm_reset_reason2; + int num_pon_config; + int num_pon_reg; + int pon_trigger_reason; + int pon_power_off_reason; + u32 dbc_time_us; + u32 uvlo; + int warm_reset_poff_type; + int hard_reset_poff_type; + int shutdown_poff_type; + int resin_warm_reset_type; + int resin_hard_reset_type; + int resin_shutdown_type; + bool is_spon; + bool store_hard_reset_reason; + bool resin_hard_reset_disable; + bool resin_shutdown_disable; + bool ps_hold_hard_reset_disable; + bool ps_hold_shutdown_disable; + bool kpdpwr_dbc_enable; + bool resin_pon_reset; + ktime_t kpdpwr_last_release_time; + bool log_kpd_event; +}; + enum pon_restart_reason { PON_RESTART_REASON_UNKNOWN = 0x00, PON_RESTART_REASON_RECOVERY = 0x01, @@ -54,8 +93,29 @@ 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, }; +/* 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/ipa.h b/include/linux/ipa.h index afc380d35e07..2903ac2de57e 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -684,6 +684,7 @@ struct ipa_ext_intf { * by IPA driver * @keep_ipa_awake: when true, IPA will not be clock gated * @napi_enabled: when true, IPA call client callback to start polling + * @bypass_agg: when true, IPA bypasses the aggregation */ struct ipa_sys_connect_params { struct ipa_ep_cfg ipa_ep_cfg; @@ -696,6 +697,7 @@ struct ipa_sys_connect_params { struct napi_struct *napi_obj; bool napi_enabled; bool recycle_enabled; + bool bypass_agg; }; /** diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index db71f25ad793..304d9fa9657a 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -616,6 +616,8 @@ static inline bool gic_enable_sre(void) return !!(val & ICC_SRE_EL1_SRE); } +void gic_v3_dist_save(void); +void gic_v3_dist_restore(void); #endif #endif diff --git a/include/linux/msm-bus.h b/include/linux/msm-bus.h index 9d376a4e89f5..07754c26f9eb 100644 --- a/include/linux/msm-bus.h +++ b/include/linux/msm-bus.h @@ -206,6 +206,21 @@ static inline int msm_bus_scale_query_tcs_cmd_all(struct msm_bus_tcs_handle #endif +#if defined(CONFIG_QCOM_BUS_SCALING) && defined(CONFIG_QCOM_BUS_CONFIG_RPMH) +int msm_bus_noc_throttle_wa(bool enable); +int msm_bus_noc_priority_wa(bool enable); +#else +static inline int msm_bus_noc_throttle_wa(bool enable) +{ + return 0; +} + +static inline int msm_bus_noc_priority_wa(bool enable) +{ + return 0; +} +#endif + #if defined(CONFIG_OF) && defined(CONFIG_QCOM_BUS_SCALING) struct msm_bus_scale_pdata *msm_bus_pdata_from_node( struct platform_device *pdev, struct device_node *of_node); diff --git a/include/linux/msm_drm_notify.h b/include/linux/msm_drm_notify.h new file mode 100644 index 000000000000..866327be492a --- /dev/null +++ b/include/linux/msm_drm_notify.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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. + */ +#ifndef _MSM_DRM_NOTIFY_H_ +#define _MSM_DRM_NOTIFY_H_ + +#include + +/* A hardware display blank change occurred */ +#define MSM_DRM_EVENT_BLANK 0x01 +/* A hardware display blank early change occurred */ +#define MSM_DRM_EARLY_EVENT_BLANK 0x02 + +enum { + /* panel: power on */ + MSM_DRM_BLANK_UNBLANK, + /* panel: power off */ + MSM_DRM_BLANK_POWERDOWN, + /* panel: power on for tp*/ + MSM_DRM_BLANK_UNBLANK_CUST, + /* panel: lcd doze mode */ + MSM_DRM_BLANK_NORMAL, + /* panel: power off */ + MSM_DRM_BLANK_POWERDOWN_CUST, + /* panel: fingerprit on display */ + MSM_DRM_ONSCREENFINGERPRINT_EVENT, +}; + +enum msm_drm_display_id { + /* primary display */ + MSM_DRM_PRIMARY_DISPLAY, + /* external display */ + MSM_DRM_EXTERNAL_DISPLAY, + MSM_DRM_DISPLAY_MAX +}; + +struct msm_drm_notifier { + enum msm_drm_display_id id; + void *data; +}; + +extern int msm_drm_register_client(struct notifier_block *nb); +extern int msm_drm_unregister_client(struct notifier_block *nb); +#endif diff --git a/include/linux/oem_force_dump.h b/include/linux/oem_force_dump.h new file mode 100644 index 000000000000..4dee0ebd619b --- /dev/null +++ b/include/linux/oem_force_dump.h @@ -0,0 +1,16 @@ +/* + * oem_force_dump.h + * + * header file supporting debug functions for Oneplus device. + * + * hefaxi@filesystems, 2015/07/03. + */ + +#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); +#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/pm_qos.h b/include/linux/pm_qos.h index 1d6f8439d67b..fece2c4804bb 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -157,6 +157,8 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); +extern void msm_cpuidle_set_sleep_disable(bool disable); + #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/include/linux/power/oem_external_fg.h b/include/linux/power/oem_external_fg.h new file mode 100644 index 000000000000..284d134aa15e --- /dev/null +++ b/include/linux/power/oem_external_fg.h @@ -0,0 +1,140 @@ +#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_design_capacity)(void); + int (*get_batt_remaining_capacity)(void); + int (*get_batt_full_chg_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); + /*2015/01/06 Modify for sync with KK charge standard */ + 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); + void (*clean_enhache)(void); + 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 (*is_enhance_dash)(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); + /* david.liu@bsp, 20161025 Add BQ27411 dash charging */ +}; + +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 ffc_step { + FFC_DEFAULT = 0, + FFC_FAST, + FFC_TAPER, + FFC_NOR_TAPER, + FFC_WARM_TAPER, + FFC_IDLE, +}; + +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 void op_bus_vote(int disable); +extern bool dash_adapter_update_is_tx_gpio(unsigned int gpio_num); +extern bool dash_adapter_update_is_rx_gpio(unsigned int gpio_num); +extern int is_hw_support_n76e(void); +/* @bsp 2018/09/05 FAT-4556 fix the audio heaset pop issue when shutdown*/ +extern bool audio_adapter_flag; + +void op_switch_normal_set(void); +#endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 5e95332448d5..c9dfa2a7080a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -164,6 +164,27 @@ enum { enum power_supply_property { /* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS = 0, +/* david.liu@bsp, 20171023 Battery & Charging porting */ + 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_HW_DETECT, + 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_CONNECT_DISABLE, + POWER_SUPPLY_PROP_CONNECTER_TEMP, + POWER_SUPPLY_PROP_BQ_SOC, + POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, @@ -256,6 +277,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_COLD_TEMP, POWER_SUPPLY_PROP_HOT_TEMP, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_BATTERY_HEALTH, + POWER_SUPPLY_PROP_OP_DISABLE_CHARGE, + POWER_SUPPLY_PROP_REMAINING_CAPACITY, POWER_SUPPLY_PROP_RESISTANCE, POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, POWER_SUPPLY_PROP_RESISTANCE_ID, /* in Ohms */ @@ -405,6 +429,8 @@ enum power_supply_type { POWER_SUPPLY_TYPE_UFP, /* Type-C UFP */ POWER_SUPPLY_TYPE_DFP, /* Type-C DFP */ POWER_SUPPLY_TYPE_CHARGE_PUMP, /* Charge Pump */ +/* david.liu@bsp, 20171023 Battery & Charging porting */ + POWER_SUPPLY_TYPE_DASH, }; enum power_supply_usb_type { diff --git a/include/linux/project_info.h b/include/linux/project_info.h new file mode 100644 index 000000000000..e0b1de6dfc1f --- /dev/null +++ b/include/linux/project_info.h @@ -0,0 +1,122 @@ +#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 + +#include +#include +//extern uint32_t chip_serial_num; + +struct project_info { + char project_name[8]; //eg, 16859 + uint32 hw_version; //PCB number, T0, EVT + uint32 rf_v1; //v1 for mainboard_rf_version + uint32 rf_v2; //v2 for aboard_rf_version + 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; + uint32 a_board_version; +}; + +struct project_info_v2 { + char project_name[8]; //eg, 15801 + char project_codename[20]; + char reservename[12]; + uint32 prj_version; + uint32 hw_version; //PCB number, T0, EVT + uint32 rf_v1; + uint32 rf_v2; + uint32 rf_v3; + uint32 uart_boot_mode; + uint32 platform_id; + uint32 ddr_manufacture_info; + uint32 ddr_row; + uint32 ddr_column; + uint32 ddr_fw_version; + uint32 ddr_reserve_info; + uint32 ddr_type; + uint32 reserve02; /*reserve for feture use*/ + uint32 reserve03; + uint32 reserve04; + uint32 reserve05; + uint32 a_board_version; + uint32 feature_id; + uint32 ftm_uart_boot_mode; + uint32 operator; + uint32 modem; +}; + +#define DUMP_REASON_SIZE 256 + +struct dump_info{ + char dump_reason[DUMP_REASON_SIZE]; //dump reason +}; + +struct component_info { + char *version; + char *manufacture; +}; + +enum{ + HW_VERSION__UNKNOWN, + HW_VERSION__11 = 11,//all EVB + HW_VERSION__12, //T0 +}; + +enum COMPONENT_TYPE { + DDR, + EMMC, + F_CAMERA, + R_CAMERA, + SECOND_R_CAMERA, + OIS, + 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, + RF_VERSION, + COMPONENT_MAX, +}; + +enum { + SMEM_ID_VENDOR1, + SMEM_DUMP_INFO = SMEM_ID_VENDOR1, + /*For more details, could check boot_iamges/core/api/mproc/smem_type.h*/ + SMEM_PROJECT_INFO = 136, +}; + +char *parse_function_builtin_return_address(unsigned long function_address); +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); +void save_dump_reason_to_smem(char *info, char *function_name); + + +#endif diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 7b2a7c313391..dfcfa2fe93b4 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -146,6 +146,8 @@ struct usb_phy { /* enable/disable VBUS */ int (*set_vbus)(struct usb_phy *x, int on); + /* callback to indicate port is being reset or reset the port */ + void (*start_port_reset)(struct usb_phy *x); /* effective for B devices, ignored for A-peripheral */ int (*set_power)(struct usb_phy *x, @@ -240,6 +242,15 @@ usb_phy_vbus_off(struct usb_phy *x) return x->set_vbus(x, false); } +static inline void +usb_phy_start_port_reset(struct usb_phy *x) +{ + if (!x || !x->start_port_reset) + return; + + x->start_port_reset(x); +} + static inline int usb_phy_reset(struct usb_phy *x) { diff --git a/include/net/rmnet_config.h b/include/net/rmnet_config.h index 051ddc62d735..2d7d02d5c62b 100644 --- a/include/net/rmnet_config.h +++ b/include/net/rmnet_config.h @@ -7,6 +7,13 @@ #ifndef _RMNET_CONFIG_H_ #define _RMNET_CONFIG_H_ +#include + +struct rmnet_phys_ep_conf_s { + void (*recycle)(struct sk_buff *); /* Destruct function */ + void *config; +}; + struct rmnet_map_header_s { #ifndef RMNET_USE_BIG_ENDIAN_STRUCTS uint8_t pad_len:6; diff --git a/include/oneplus/memplus/memplus_helper.h b/include/oneplus/memplus/memplus_helper.h new file mode 100644 index 000000000000..7fdd83fd363b --- /dev/null +++ b/include/oneplus/memplus/memplus_helper.h @@ -0,0 +1,92 @@ +#ifndef _MEMORY_PLUS_HELPER_H +#define _MEMORY_PLUS_HELPER_H + +#ifdef CONFIG_MEMPLUS +#include + +struct memplus_cb_set { + bool (*memplus_enabled_cb)(void); + bool (*__memplus_enabled_cb)(void); + bool (*current_is_swapind_cb)(void); + void (*memplus_move_swapcache_to_anon_lru_cb)(struct page *); + void (*memplus_move_anon_to_swapcache_lru_cb)(struct page *); + void (*memplus_state_check_cb)(bool, int, + struct task_struct *, int, int); + bool (*memplus_check_isolate_page_cb)(struct page *); +}; +void register_cb_set(struct memplus_cb_set *set); +extern bool memplus_enabled(void); +extern bool __memplus_enabled(void); +extern bool current_is_swapind(void); +extern void memplus_move_swapcache_to_anon_lru(struct page *page); +extern void memplus_move_anon_to_swapcache_lru(struct page *page); +extern void memplus_state_check(bool legacy, int oom_adj, + struct task_struct *task, int type, int update); +extern bool memplus_check_isolate_page(struct page *page); + +#define memplus_init_task_reclaim_stat(sig) \ +do { \ + sig->swapin_should_readahead_m = 0; \ + sig->reclaim_state_lock = __SPIN_LOCK_UNLOCKED(reclaim_state_lock); \ + sig->memplus_type = 0; \ +} while (0) +#define __memplus_clear_entry(entry) ((entry).val = 0) +#define __memplus_entry(bdev_flag) ((bdev_flag) & SWP_SYNCHRONOUS_IO) +#define __get_memplus_swp_flag(entry) ((entry).val & SWP_SYNCHRONOUS_IO) +#define __set_memplus_entry(entry, flag) (entry.val = flag) +#define memplus_set_private(page, type) \ + set_page_private((page), __memplus_entry((type)?SWP_SYNCHRONOUS_IO:0)) + +#define memplus_set_willneed(page) \ +do { \ + if (current_is_swapind()) \ + SetPageWillneed(page); \ +} while (0) + +#define MEMPLUS_PAGE_LRU \ + (__memplus_enabled()?LRU_INACTIVE_ANON_SWPCACHE:LRU_INACTIVE_ANON) + +#define INIT_RECLAIM_STATE .reclaim_timeout = 0, \ + .swapin_should_readahead_m = 0, \ + .reclaim_state_lock = \ + __SPIN_LOCK_UNLOCKED(reclaim_state_lock), \ + .memplus_type = 0, + +#else +static __always_inline +void memplus_move_swapcache_to_anon_lru(struct page *page) +{ + clear_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} +static __always_inline +void memplus_move_anon_to_swapcache_lru(struct page *page) +{ + set_bit(PG_swapcache, &(PF_NO_TAIL(page, 1))->flags); +} +#define memplus_init_task_reclaim_stat(sig) +#define __memplus_clear_entry(entry) +#define __memplus_entry(bdev_flag) +#define __get_memplus_swp_flag(entry) +#define __set_memplus_entry(entry, flag) +#define memplus_set_private(page, type) +#define memplus_state_check(legacy, oom_adj, task, type, update) +static __always_inline bool memplus_enabled(void) +{ + return false; +} +static __always_inline bool __memplus_enabled(void) +{ + return false; +} +static __always_inline bool memplus_check_isolate_page(struct page *page) +{ + return false; +} + +#define MEMPLUS_PAGE_LRU LRU_INACTIVE_ANON + +#define INIT_RECLAIM_STATE + + +#endif +#endif /* _MEMORY_PLUS_H */ diff --git a/include/oneplus/smartboost/smartboost_helper.h b/include/oneplus/smartboost/smartboost_helper.h new file mode 100644 index 000000000000..81716f1973e5 --- /dev/null +++ b/include/oneplus/smartboost/smartboost_helper.h @@ -0,0 +1,81 @@ +#ifndef __SMART_BOOST_HELPER_H__ +#define __SMART_BOOST_HELPER_H__ + +#include + +#ifdef CONFIG_SMART_BOOST +struct smb_cb_set { + bool (*smb_uid_lru_add_cb)(struct page *page); + + unsigned long (*smb_isolate_list_or_putbcak_cb) + (struct list_head *page_list, + struct lruvec *lruvec, + struct pglist_data *pgdat, int priority, + bool enough_list_reclaimed); + + bool (*smb_update_uid_lru_size_cb)(struct page *page, + struct lruvec *lruvec, enum lru_list lru); +}; + +extern unsigned long get_max_minfree(void); +extern unsigned long coretech_reclaim_pagelist(struct list_head *page_list, + struct vm_area_struct *vma, void *sc); +void smb_register_cb_set(struct smb_cb_set *set); +extern const struct file_operations proc_page_hot_count_operations; +extern unsigned long smb_invalidate_mapping_pages(struct address_space *mapping, + pgoff_t start, pgoff_t end); +extern bool smb_uid_lru_add(struct page *page); +extern unsigned long smb_isolate_list_or_putbcak(struct list_head *page_list, + struct lruvec *lruvec, struct pglist_data *pgdat, int priority, + bool enough_list_reclaimed); + +extern bool smb_update_uid_lru_size(struct page *page, + struct lruvec *lruvec, enum lru_list lru); + +#define UID_LRU_SIZE global_page_state(NR_ZONE_UID_LRU) +#define PG_UIDLRU(page) PageUIDLRU(page) +#define ZONE_UID_LRU_SIZE(z) zone_page_state((z), NR_ZONE_UID_LRU) + +#define SMB_HOT_COUNT_INIT(condition, p) \ + do { \ + if (!condition) \ + p->hot_count = 0; \ + } while (0) + + +#else /* !CONFIG_SMART_BOOST */ +#define SMB_HOT_COUNT_INIT(condition, p) \ + do { \ + } while (0) + +#define UID_LRU_SIZE 0L +#define ZONE_UID_LRU_SIZE(z) 0L +#define PG_UIDLRU(page) 0 + +static __always_inline +unsigned long smb_invalidate_mapping_pages(struct address_space *mapping, + pgoff_t start, pgoff_t end) +{ + return invalidate_mapping_pages(mapping, start, end); +} +static __always_inline +bool smb_update_uid_lru_size(struct page *page, + struct lruvec *lruvec, enum lru_list lru) +{ + return false; +} +static __always_inline bool smb_uid_lru_add(struct page *page) +{ + return false; +} + +static __always_inline unsigned long +smb_isolate_list_or_putbcak(struct list_head *page_list, + struct lruvec *lruvec, struct pglist_data *pgdat, int priority, + bool enough_list_reclaimed) +{ + return 0; +} + +#endif +#endif diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index 70aa770f6da7..b858ec606b78 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -151,6 +151,8 @@ enum socinfo_parttype { of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdmmagpie") #define early_machine_is_sdm660() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm660") +#define early_machine_is_sdm845() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm845") #define early_machine_is_bengal_iot() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,bengal-iot") #define early_machine_is_bengalp_iot() \ @@ -210,6 +212,7 @@ enum socinfo_parttype { #define early_machine_is_sdxprairie() 0 #define early_machine_is_sdmmagpie() 0 #define early_machine_is_sdm660() 0 +#define early_machine_is_sdm845() 0 #define early_machine_is_bengal_iot() 0 #define early_machine_is_bengalp_iot() 0 #define early_machine_is_msm8937() 0 @@ -241,6 +244,7 @@ enum msm_cpu { MSM_CPU_8084, MSM_CPU_8996, MSM_CPU_SDM660, + MSM_CPU_SDM845, MSM_CPU_SM8150, MSM_CPU_SA8150, MSM_CPU_KONA, diff --git a/include/trace/events/iommu.h b/include/trace/events/iommu.h index 0db6f7f86270..dea920328a70 100644 --- a/include/trace/events/iommu.h +++ b/include/trace/events/iommu.h @@ -243,6 +243,20 @@ DEFINE_EVENT(iommu_tlbi, tlbsync_timeout, TP_ARGS(dev, time) ); +DEFINE_EVENT(iommu_tlbi, tlbi_throttle_start, + + TP_PROTO(struct device *dev, u64 time), + + TP_ARGS(dev, time) +); + +DEFINE_EVENT(iommu_tlbi, tlbi_throttle_end, + + TP_PROTO(struct device *dev, u64 time), + + TP_ARGS(dev, time) +); + TRACE_EVENT(smmu_init, TP_PROTO(u64 time), diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h index 029393041408..5aae512709aa 100644 --- a/include/uapi/drm/sde_drm.h +++ b/include/uapi/drm/sde_drm.h @@ -501,4 +501,10 @@ struct sde_drm_roi_v1 { * Implementation may be platform and base-format specific. */ #define DRM_FORMAT_MOD_QCOM_FSC_TILE fourcc_mod_code(QCOM, 0x10) + +/** + * sde fod dim layer + */ +#define FOD_PRESSED_LAYER_ZORDER 0x20000000u + #endif /* _SDE_DRM_H_ */ diff --git a/include/uapi/linux/msm_rmnet.h b/include/uapi/linux/msm_rmnet.h index 5c3ec42599e2..e5d70a7e3969 100644 --- a/include/uapi/linux/msm_rmnet.h +++ b/include/uapi/linux/msm_rmnet.h @@ -82,6 +82,7 @@ #define RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION (1<<2) #define RMNET_IOCTL_EGRESS_FORMAT_MUXING (1<<3) #define RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM (1<<4) +#define RMNET_IOCTL_EGRESS_FORMAT_IP_ROUTE (1<<5) /* Input values for the RMNET_IOCTL_SET_INGRESS_DATA_FORMAT IOCTL */ #define RMNET_IOCTL_INGRESS_FORMAT_MAP (1<<1) @@ -89,6 +90,7 @@ #define RMNET_IOCTL_INGRESS_FORMAT_DEMUXING (1<<3) #define RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM (1<<4) #define RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA (1<<5) +#define RMNET_IOCTL_INGRESS_FORMAT_IP_ROUTE (1<<6) /* Input values for the RMNET_IOCTL_SET_OFFLOAD */ #define RMNET_IOCTL_OFFLOAD_FORMAT_NONE (0) diff --git a/include/uapi/linux/net_map.h b/include/uapi/linux/net_map.h new file mode 100644 index 000000000000..a5d6d589db19 --- /dev/null +++ b/include/uapi/linux/net_map.h @@ -0,0 +1,8 @@ +#ifndef _NET_MAP_H_ +#define _NET_MAP_H_ + +#define RMNET_IP_VER_MASK 0xF0 +#define RMNET_IPV4 0x40 +#define RMNET_IPV6 0x60 + +#endif /* _NET_MAP_H_ */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index f39603798acb..b6e2fac1bb18 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -369,10 +369,16 @@ enum v4l2_mpeg_video_bitrate_mode { V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1, V4L2_MPEG_VIDEO_BITRATE_MODE_MBR = 2, + /* RC_OFF */ + V4L2_MPEG_VIDEO_BITRATE_MODE_RC_OFF = 3, /* VFR Modes */ - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR = 3, - V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR = 4, - V4L2_MPEG_VIDEO_BITRATE_MODE_CQ = 5, +#define V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR \ + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR_VFR = 4, +#define V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR \ + V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR + V4L2_MPEG_VIDEO_BITRATE_MODE_MBR_VFR = 5, + V4L2_MPEG_VIDEO_BITRATE_MODE_CQ = 6, }; #define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207) #define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208) @@ -448,9 +454,13 @@ enum v4l2_mpeg_video_h264_level { V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15, V4L2_MPEG_VIDEO_H264_LEVEL_5_2 = 16, V4L2_MPEG_VIDEO_H264_LEVEL_6_0 = 17, - V4L2_MPEG_VIDEO_H264_LEVEL_6_1 = 18, - V4L2_MPEG_VIDEO_H264_LEVEL_6_2 = 19, - V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN = 20, + V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN = 18, +#define V4L2_MPEG_VIDEO_H264_LEVEL_6_1 \ + V4L2_MPEG_VIDEO_H264_LEVEL_6_1 + V4L2_MPEG_VIDEO_H264_LEVEL_6_1 = 19, +#define V4L2_MPEG_VIDEO_H264_LEVEL_6_2 \ + V4L2_MPEG_VIDEO_H264_LEVEL_6_2 + V4L2_MPEG_VIDEO_H264_LEVEL_6_2 = 20, }; #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360) #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361) @@ -602,8 +612,9 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) #define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) +#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) -#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_MPEG_BASE+511) +#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_MPEG_BASE+512) enum v4l2_mpeg_video_vp8_profile { V4L2_MPEG_VIDEO_VP8_PROFILE_0 = 0, V4L2_MPEG_VIDEO_VP8_PROFILE_1 = 1, @@ -611,8 +622,7 @@ enum v4l2_mpeg_video_vp8_profile { V4L2_MPEG_VIDEO_VP8_PROFILE_3 = 3, }; /* Deprecated alias for compatibility reasons. */ -#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE V4L2_CID_MPEG_VIDEO_VP8_PROFILE -#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_MPEG_BASE+512) +#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_MPEG_BASE+513) enum v4l2_mpeg_video_vp9_profile { V4L2_MPEG_VIDEO_VP9_PROFILE_0 = 0, V4L2_MPEG_VIDEO_VP9_PROFILE_1 = 1, @@ -642,30 +652,8 @@ enum v4l2_mpeg_video_hevc_hier_coding_type { #define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (V4L2_CID_MPEG_BASE + 613) #define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (V4L2_CID_MPEG_BASE + 614) #define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE (V4L2_CID_MPEG_BASE + 615) -enum v4l2_mpeg_video_hevc_profile { - V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0, - V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1, - V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2, -}; #define V4L2_CID_MPEG_VIDEO_HEVC_LEVEL (V4L2_CID_MPEG_BASE + 616) -enum v4l2_mpeg_video_hevc_level { - V4L2_MPEG_VIDEO_HEVC_LEVEL_1 = 0, - V4L2_MPEG_VIDEO_HEVC_LEVEL_2 = 1, - V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 = 2, - V4L2_MPEG_VIDEO_HEVC_LEVEL_3 = 3, - V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 = 4, - V4L2_MPEG_VIDEO_HEVC_LEVEL_4 = 5, - V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 = 6, - V4L2_MPEG_VIDEO_HEVC_LEVEL_5 = 7, - V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 = 8, - V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 = 9, - V4L2_MPEG_VIDEO_HEVC_LEVEL_6 = 10, - V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 = 11, - V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 = 12, - V4L2_MPEG_VIDEO_HEVC_LEVEL_UNKNOWN = 13, -}; - #define V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (V4L2_CID_MPEG_BASE + 617) #define V4L2_CID_MPEG_VIDEO_HEVC_TIER (V4L2_CID_MPEG_BASE + 618) enum v4l2_mpeg_video_hevc_tier { @@ -814,7 +802,14 @@ enum v4l2_mpeg_vidc_video_stream_format { V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH = 4, }; -#define V4L2_CID_MPEG_VIDC_VIDEO_DECODE_ORDER (V4L2_CID_MPEG_MSM_VIDC_BASE+3) +#define V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER (V4L2_CID_MPEG_MSM_VIDC_BASE+3) +enum v4l2_mpeg_vidc_video_output_order { + V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY = 0, + V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE (V4L2_CID_MPEG_MSM_VIDC_BASE+4) +#define V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD (V4L2_CID_MPEG_MSM_VIDC_BASE+5) #define V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+6) #define V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+7) #define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME (V4L2_CID_MPEG_MSM_VIDC_BASE+8) @@ -875,20 +870,10 @@ enum v4l2_mpeg_vidc_video_sync_frame_decode { #define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 17) enum v4l2_mpeg_vidc_extradata { -#define EXTRADATA_NONE \ - EXTRADATA_NONE - EXTRADATA_NONE = 0, - EXTRADATA_DEFAULT = 1, - EXTRADATA_ADVANCED = 2, - EXTRADATA_ENC_INPUT_ROI = 4, - EXTRADATA_ENC_INPUT_HDR10PLUS = 8, - EXTRADATA_ENC_INPUT_CVP = 16, -}; -enum v4l2_mpeg_vidc3x_extradata { - V4L2_MPEG_VIDC_EXTRADATA_NONE = 0, + V4L2_MPEG_VIDC_EXTRADATA_NONE = 0, V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1, V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2, - #define V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS \ +#define V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS \ V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS = 3, V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP = 4, @@ -998,6 +983,13 @@ enum v4l2_mpeg_vidc_video_preserve_text_quality { V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_ENABLED = 1 }; +#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 22) +enum v4l2_mpeg_vidc_video_decoder_multi_stream { + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY = 0, + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY = 1, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE+23) enum v4l2_mpeg_vidc_video_mpeg2_level { V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0 = 0, @@ -1083,7 +1075,7 @@ enum vl42_mpeg_vidc_video_vpx_error_resilience { #define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 40) -enum v4l2_cid_mpeg_video_hevc_profile { +enum v4l2_mpeg_video_hevc_profile { V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN = 0, V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10 = 1, V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC = 2, @@ -1091,7 +1083,7 @@ enum v4l2_cid_mpeg_video_hevc_profile { #define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 41) -enum v4l2_cid_mpeg_video_hevc_level { +enum v4l2_mpeg_video_hevc_level { V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1 = 0, V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1 = 1, V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2 = 2, @@ -1203,14 +1195,18 @@ enum v4l2_mpeg_vidc_video_lowlatency_mode { V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE = 1, }; -#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_DIMENSIONS \ +#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 57) -#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH \ +#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 58) -#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 59) +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 59) +enum v4l2_mpeg_vidc_video_h264_transform_8x8 { + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1, +}; #define V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 60) @@ -1257,7 +1253,11 @@ enum v4l2_mpeg_vidc_video_vp9_level { V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_41 = 8, V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_5 = 9, V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51 = 10, +#define V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6 \ + V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6 V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6 = 11, +#define V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61 \ + V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61 V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61 = 12, }; @@ -1295,23 +1295,6 @@ enum v4l2_mpeg_vidc_video_h263_profile { V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY = 8, }; -#define V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER \ - (V4L2_CID_MPEG_MSM_VIDC_BASE+75) -enum v4l2_mpeg_vidc_video_output_order { - V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY = 0, - V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE = 1, -}; - -#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 76) -enum v4l2_mpeg_vidc_video_h264_transform_8x8 { - V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0, - V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1, -}; - -#define V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 77) - #define V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 96) #define V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED \ @@ -1336,8 +1319,7 @@ enum v4l2_mpeg_vidc_video_h264_transform_8x8 { (V4L2_CID_MPEG_MSM_VIDC_BASE + 106) #define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 107) - -#define V4L2_CID_MPEG_VIDC_VIDEO_DYN_QP \ +#define V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 108) enum v4l2_mpeg_vidc_video_venc_iframesize_type { @@ -1389,50 +1371,38 @@ enum v4l2_mpeg_vidc_video_flip { #define V4L2_CID_MPEG_VIDC_VENC_HDR_INFO \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 116) - -#define V4L2_CID_MPEG_VIDC_IMG_GRID_SIZE \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 117) - -#define V4L2_CID_MPEG_VIDC_COMPRESSION_QUALITY \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 118) - -#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 119) - -#define V4L2_CID_MPEG_VIDC_VENC_BITRATE_SAVINGS \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 131) - -#define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 120) -enum v4l2_mpeg_vidc_video_hevc_max_hier_coding_layer { - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_0 = 0, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_1 = 1, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_2 = 2, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_3 = 3, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_4 = 4, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_5 = 5, - V4L2_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER_6 = 6, -}; - -#define V4L2_CID_MPEG_VIDC_VENC_CVP_DISABLE \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 121) -#define V4L2_CID_MPEG_VIDC_VENC_NATIVE_RECORDER \ +#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21 \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 122) - -#define V4L2_CID_MPEG_VIDC_VENC_RC_TIMESTAMP_DISABLE \ +#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 123) - -#define V4L2_CID_MPEG_VIDC_SUPERFRAME \ +#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 124) - -#define V4L2_CID_MPEG_VIDC_CAPTURE_FRAME_RATE \ +#define V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 125) - -#define V4L2_CID_MPEG_VIDC_CVP_FRAME_RATE \ +#define V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 126) - -#define V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE \ +#define V4L2_CID_MPEG_VIDC_VENC_MAX_CLL \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 127) +#define V4L2_CID_MPEG_VIDC_VENC_MAX_FLL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 128) +#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 129) +#define V4L2_CID_MPEG_VIDC_IMG_GRID_ENABLE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 130) +#define V4L2_CID_MPEG_VIDC_VENC_BITRATE_SAVINGS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 131) +#define V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 132) enum v4l2_mpeg_vidc_video_roi_type { V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_NONE = 0, V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BIT = 1, @@ -1440,16 +1410,13 @@ enum v4l2_mpeg_vidc_video_roi_type { }; #define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 128) + (V4L2_CID_MPEG_MSM_VIDC_BASE + 133) enum v4l2_mpeg_vidc_perf_level { V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL = 0, V4L2_CID_MPEG_VIDC_PERF_LEVEL_PERFORMANCE = 1, V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO = 2, }; -#define V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_HINT \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 133) - #define V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE + 134) #define V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF (V4L2_CID_MPEG_MSM_VIDC_BASE + 135) #define V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE + 136) @@ -1471,7 +1438,7 @@ enum v4l2_mpeg_vidc_video_h264_vui_bitstream_restrict { (V4L2_CID_MPEG_MSM_VIDC_BASE + 140) enum v4l2_mpeg_vidc_video_deinterlace { V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED = 0, - V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED = 1 }; #define V4L2_CID_MPEG_VIDC_VIDEO_MPEG4_TIME_RESOLUTION \ @@ -1582,9 +1549,6 @@ enum v4l2_mpeg_vidc_video_intra_refresh_mode { #define V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE+166) -#define V4L2_CID_MPEG_VIDC_ENABLE_ONLY_BASE_LAYER_IR \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 167) - enum v4l2_mpeg_vidc_video_mbi_statistics_mode { V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT = 0, V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1 = 1, @@ -1601,7 +1565,8 @@ enum v4l2_mpeg_vidc_video_h264_vui_timing_info { V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED = 0, V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED = 1 }; - +#define V4L2_CID_MPEG_VIDC_VENC_COMPLEXITY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 167) /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) diff --git a/include/uapi/media/cam_cpas.h b/include/uapi/media/cam_cpas.h new file mode 100644 index 000000000000..371b74c74500 --- /dev/null +++ b/include/uapi/media/cam_cpas.h @@ -0,0 +1,84 @@ +#ifndef __UAPI_CAM_CPAS_H__ +#define __UAPI_CAM_CPAS_H__ + +#include "cam_defs.h" + +#define CAM_FAMILY_CAMERA_SS 1 +#define CAM_FAMILY_CPAS_SS 2 + +/* AXI BW Voting Version */ +#define CAM_AXI_BW_VOTING_V2 2 + +/* AXI BW Voting Transaction Type */ +#define CAM_AXI_TRANSACTION_READ 0 +#define CAM_AXI_TRANSACTION_WRITE 1 + +/* AXI BW Voting Path Data Type */ +#define CAM_AXI_PATH_DATA_IFE_START_OFFSET 0 +#define CAM_AXI_PATH_DATA_IFE_LINEAR (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 0) +#define CAM_AXI_PATH_DATA_IFE_VID (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 1) +#define CAM_AXI_PATH_DATA_IFE_DISP (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 2) +#define CAM_AXI_PATH_DATA_IFE_STATS (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 3) +#define CAM_AXI_PATH_DATA_IFE_RDI0 (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 4) +#define CAM_AXI_PATH_DATA_IFE_RDI1 (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 5) +#define CAM_AXI_PATH_DATA_IFE_RDI2 (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 6) +#define CAM_AXI_PATH_DATA_IFE_RDI3 (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 7) +#define CAM_AXI_PATH_DATA_IFE_PDAF (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 8) +#define CAM_AXI_PATH_DATA_IFE_PIXEL_RAW \ + (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 9) +#define CAM_AXI_PATH_DATA_IFE_MAX_OFFSET \ + (CAM_AXI_PATH_DATA_IFE_START_OFFSET + 31) + +#define CAM_AXI_PATH_DATA_IPE_START_OFFSET 32 +#define CAM_AXI_PATH_DATA_IPE_RD_IN (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 0) +#define CAM_AXI_PATH_DATA_IPE_RD_REF (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 1) +#define CAM_AXI_PATH_DATA_IPE_WR_VID (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 2) +#define CAM_AXI_PATH_DATA_IPE_WR_DISP (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 3) +#define CAM_AXI_PATH_DATA_IPE_WR_REF (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 4) +#define CAM_AXI_PATH_DATA_IPE_MAX_OFFSET \ + (CAM_AXI_PATH_DATA_IPE_START_OFFSET + 31) + +#define CAM_AXI_PATH_DATA_ALL 256 + +/** + * struct cam_cpas_query_cap - CPAS query device capability payload + * + * @camera_family : Camera family type + * @reserved : Reserved field for alignment + * @camera_version : Camera platform version + * @cpas_version : Camera CPAS version within camera platform + * + */ +struct cam_cpas_query_cap { + uint32_t camera_family; + uint32_t reserved; + struct cam_hw_version camera_version; + struct cam_hw_version cpas_version; +}; + +/** + * struct cam_axi_per_path_bw_vote - Per path bandwidth vote information + * + * @usage_data client usage data (left/right/rdi) + * @transac_type Transaction type on the path (read/write) + * @path_data_type Path for which vote is given (video, display, rdi) + * @reserved Reserved for alignment + * @camnoc_bw CAMNOC bw for this path + * @mnoc_ab_bw MNOC AB bw for this path + * @mnoc_ib_bw MNOC IB bw for this path + * @ddr_ab_bw DDR AB bw for this path + * @ddr_ib_bw DDR IB bw for this path + */ +struct cam_axi_per_path_bw_vote { + uint32_t usage_data; + uint32_t transac_type; + uint32_t path_data_type; + uint32_t reserved; + uint64_t camnoc_bw; + uint64_t mnoc_ab_bw; + uint64_t mnoc_ib_bw; + uint64_t ddr_ab_bw; + uint64_t ddr_ib_bw; +}; + +#endif /* __UAPI_CAM_CPAS_H__ */ diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h new file mode 100644 index 000000000000..6ec286c33834 --- /dev/null +++ b/include/uapi/media/cam_defs.h @@ -0,0 +1,633 @@ +#ifndef __UAPI_CAM_DEFS_H__ +#define __UAPI_CAM_DEFS_H__ + +#include +#include +#include + + +/* camera op codes */ +#define CAM_COMMON_OPCODE_BASE 0x100 +#define CAM_QUERY_CAP (CAM_COMMON_OPCODE_BASE + 0x1) +#define CAM_ACQUIRE_DEV (CAM_COMMON_OPCODE_BASE + 0x2) +#define CAM_START_DEV (CAM_COMMON_OPCODE_BASE + 0x3) +#define CAM_STOP_DEV (CAM_COMMON_OPCODE_BASE + 0x4) +#define CAM_CONFIG_DEV (CAM_COMMON_OPCODE_BASE + 0x5) +#define CAM_RELEASE_DEV (CAM_COMMON_OPCODE_BASE + 0x6) +#define CAM_SD_SHUTDOWN (CAM_COMMON_OPCODE_BASE + 0x7) +#define CAM_FLUSH_REQ (CAM_COMMON_OPCODE_BASE + 0x8) +#define CAM_GET_FUSE_ID (CAM_COMMON_OPCODE_BASE + 0x9) +#define CAM_COMMON_OPCODE_MAX (CAM_COMMON_OPCODE_BASE + 0xA) + +#define CAM_COMMON_OPCODE_BASE_v2 0x150 +#define CAM_ACQUIRE_HW (CAM_COMMON_OPCODE_BASE_v2 + 0x1) +#define CAM_RELEASE_HW (CAM_COMMON_OPCODE_BASE_v2 + 0x2) + +#define CAM_EXT_OPCODE_BASE 0x200 +#define CAM_CONFIG_DEV_EXTERNAL (CAM_EXT_OPCODE_BASE + 0x1) + +/* camera handle type */ +#define CAM_HANDLE_USER_POINTER 1 +#define CAM_HANDLE_MEM_HANDLE 2 + +/* Generic Blob CmdBuffer header properties */ +#define CAM_GENERIC_BLOB_CMDBUFFER_SIZE_MASK 0xFFFFFF00 +#define CAM_GENERIC_BLOB_CMDBUFFER_SIZE_SHIFT 8 +#define CAM_GENERIC_BLOB_CMDBUFFER_TYPE_MASK 0xFF +#define CAM_GENERIC_BLOB_CMDBUFFER_TYPE_SHIFT 0 + +/* Command Buffer Types */ +#define CAM_CMD_BUF_DMI 0x1 +#define CAM_CMD_BUF_DMI16 0x2 +#define CAM_CMD_BUF_DMI32 0x3 +#define CAM_CMD_BUF_DMI64 0x4 +#define CAM_CMD_BUF_DIRECT 0x5 +#define CAM_CMD_BUF_INDIRECT 0x6 +#define CAM_CMD_BUF_I2C 0x7 +#define CAM_CMD_BUF_FW 0x8 +#define CAM_CMD_BUF_GENERIC 0x9 +#define CAM_CMD_BUF_LEGACY 0xA + +/* UBWC API Version */ +#define CAM_UBWC_CFG_VERSION_1 1 + +/** + * enum flush_type_t - Identifies the various flush types + * + * @CAM_FLUSH_TYPE_REQ: Flush specific request + * @CAM_FLUSH_TYPE_ALL: Flush all requests belonging to a context + * @CAM_FLUSH_TYPE_MAX: Max enum to validate flush type + * + */ +enum flush_type_t { + CAM_FLUSH_TYPE_REQ, + CAM_FLUSH_TYPE_ALL, + CAM_FLUSH_TYPE_MAX +}; + +/** + * struct cam_control - Structure used by ioctl control for camera + * + * @op_code: This is the op code for camera control + * @size: Control command size + * @handle_type: User pointer or shared memory handle + * @reserved: Reserved field for 64 bit alignment + * @handle: Control command payload + */ +struct cam_control { + uint32_t op_code; + uint32_t size; + uint32_t handle_type; + uint32_t reserved; + uint64_t handle; +}; + +/* camera IOCTL */ +#define VIDIOC_CAM_CONTROL \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct cam_control) + +#define VIDIOC_CAM_FTM_POWNER_UP 0 +#define VIDIOC_CAM_FTM_POWNER_DOWN 1 + +/** + * struct cam_hw_version - Structure for HW version of camera devices + * + * @major : Hardware version major + * @minor : Hardware version minor + * @incr : Hardware version increment + * @reserved : Reserved for 64 bit aligngment + */ +struct cam_hw_version { + uint32_t major; + uint32_t minor; + uint32_t incr; + uint32_t reserved; +}; + +/** + * struct cam_iommu_handle - Structure for IOMMU handles of camera hw devices + * + * @non_secure: Device Non Secure IOMMU handle + * @secure: Device Secure IOMMU handle + * + */ +struct cam_iommu_handle { + int32_t non_secure; + int32_t secure; +}; + +/* camera secure mode */ +#define CAM_SECURE_MODE_NON_SECURE 0 +#define CAM_SECURE_MODE_SECURE 1 + +/* Camera Format Type */ +#define CAM_FORMAT_BASE 0 +#define CAM_FORMAT_MIPI_RAW_6 1 +#define CAM_FORMAT_MIPI_RAW_8 2 +#define CAM_FORMAT_MIPI_RAW_10 3 +#define CAM_FORMAT_MIPI_RAW_12 4 +#define CAM_FORMAT_MIPI_RAW_14 5 +#define CAM_FORMAT_MIPI_RAW_16 6 +#define CAM_FORMAT_MIPI_RAW_20 7 +#define CAM_FORMAT_QTI_RAW_8 8 +#define CAM_FORMAT_QTI_RAW_10 9 +#define CAM_FORMAT_QTI_RAW_12 10 +#define CAM_FORMAT_QTI_RAW_14 11 +#define CAM_FORMAT_PLAIN8 12 +#define CAM_FORMAT_PLAIN16_8 13 +#define CAM_FORMAT_PLAIN16_10 14 +#define CAM_FORMAT_PLAIN16_12 15 +#define CAM_FORMAT_PLAIN16_14 16 +#define CAM_FORMAT_PLAIN16_16 17 +#define CAM_FORMAT_PLAIN32_20 18 +#define CAM_FORMAT_PLAIN64 19 +#define CAM_FORMAT_PLAIN128 20 +#define CAM_FORMAT_ARGB 21 +#define CAM_FORMAT_ARGB_10 22 +#define CAM_FORMAT_ARGB_12 23 +#define CAM_FORMAT_ARGB_14 24 +#define CAM_FORMAT_DPCM_10_6_10 25 +#define CAM_FORMAT_DPCM_10_8_10 26 +#define CAM_FORMAT_DPCM_12_6_12 27 +#define CAM_FORMAT_DPCM_12_8_12 28 +#define CAM_FORMAT_DPCM_14_8_14 29 +#define CAM_FORMAT_DPCM_14_10_14 30 +#define CAM_FORMAT_NV21 31 +#define CAM_FORMAT_NV12 32 +#define CAM_FORMAT_TP10 33 +#define CAM_FORMAT_YUV422 34 +#define CAM_FORMAT_PD8 35 +#define CAM_FORMAT_PD10 36 +#define CAM_FORMAT_UBWC_NV12 37 +#define CAM_FORMAT_UBWC_NV12_4R 38 +#define CAM_FORMAT_UBWC_TP10 39 +#define CAM_FORMAT_UBWC_P010 40 +#define CAM_FORMAT_PLAIN8_SWAP 41 +#define CAM_FORMAT_PLAIN8_10 42 +#define CAM_FORMAT_PLAIN8_10_SWAP 43 +#define CAM_FORMAT_YV12 44 +#define CAM_FORMAT_Y_ONLY 45 +#define CAM_FORMAT_DPCM_12_10_12 46 +#define CAM_FORMAT_MAX 47 + +/* camera rotaion */ +#define CAM_ROTATE_CW_0_DEGREE 0 +#define CAM_ROTATE_CW_90_DEGREE 1 +#define CAM_RORATE_CW_180_DEGREE 2 +#define CAM_ROTATE_CW_270_DEGREE 3 + +/* camera Color Space */ +#define CAM_COLOR_SPACE_BASE 0 +#define CAM_COLOR_SPACE_BT601_FULL 1 +#define CAM_COLOR_SPACE_BT601625 2 +#define CAM_COLOR_SPACE_BT601525 3 +#define CAM_COLOR_SPACE_BT709 4 +#define CAM_COLOR_SPACE_DEPTH 5 +#define CAM_COLOR_SPACE_MAX 6 + +/* camera buffer direction */ +#define CAM_BUF_INPUT 1 +#define CAM_BUF_OUTPUT 2 +#define CAM_BUF_IN_OUT 3 + +/* camera packet device Type */ +#define CAM_PACKET_DEV_BASE 0 +#define CAM_PACKET_DEV_IMG_SENSOR 1 +#define CAM_PACKET_DEV_ACTUATOR 2 +#define CAM_PACKET_DEV_COMPANION 3 +#define CAM_PACKET_DEV_EEPOM 4 +#define CAM_PACKET_DEV_CSIPHY 5 +#define CAM_PACKET_DEV_OIS 6 +#define CAM_PACKET_DEV_FLASH 7 +#define CAM_PACKET_DEV_FD 8 +#define CAM_PACKET_DEV_JPEG_ENC 9 +#define CAM_PACKET_DEV_JPEG_DEC 10 +#define CAM_PACKET_DEV_VFE 11 +#define CAM_PACKET_DEV_CPP 12 +#define CAM_PACKET_DEV_CSID 13 +#define CAM_PACKET_DEV_ISPIF 14 +#define CAM_PACKET_DEV_IFE 15 +#define CAM_PACKET_DEV_ICP 16 +#define CAM_PACKET_DEV_LRME 17 +#define CAM_PACKET_DEV_MAX 18 + + +/* constants */ +#define CAM_PACKET_MAX_PLANES 3 + +/** + * struct cam_plane_cfg - Plane configuration info + * + * @width: Plane width in pixels + * @height: Plane height in lines + * @plane_stride: Plane stride in pixel + * @slice_height: Slice height in line (not used by ISP) + * @meta_stride: UBWC metadata stride + * @meta_size: UBWC metadata plane size + * @meta_offset: UBWC metadata offset + * @packer_config: UBWC packer config + * @mode_config: UBWC mode config + * @tile_config: UBWC tile config + * @h_init: UBWC horizontal initial coordinate in pixels + * @v_init: UBWC vertical initial coordinate in lines + * + */ +struct cam_plane_cfg { + uint32_t width; + uint32_t height; + uint32_t plane_stride; + uint32_t slice_height; + uint32_t meta_stride; + uint32_t meta_size; + uint32_t meta_offset; + uint32_t packer_config; + uint32_t mode_config; + uint32_t tile_config; + uint32_t h_init; + uint32_t v_init; +}; + +/** + * struct cam_ubwc_plane_cfg_v1 - UBWC Plane configuration info + * + * @port_type: Port Type + * @meta_stride: UBWC metadata stride + * @meta_size: UBWC metadata plane size + * @meta_offset: UBWC metadata offset + * @packer_config: UBWC packer config + * @mode_config_0: UBWC mode config 0 + * @mode_config_1: UBWC 3 mode config 1 + * @tile_config: UBWC tile config + * @h_init: UBWC horizontal initial coordinate in pixels + * @v_init: UBWC vertical initial coordinate in lines + * + */ +struct cam_ubwc_plane_cfg_v1 { + uint32_t port_type; + uint32_t meta_stride; + uint32_t meta_size; + uint32_t meta_offset; + uint32_t packer_config; + uint32_t mode_config_0; + uint32_t mode_config_1; + uint32_t tile_config; + uint32_t h_init; + uint32_t v_init; +}; + +/** + * struct cam_cmd_buf_desc - Command buffer descriptor + * + * @mem_handle: Command buffer handle + * @offset: Command start offset + * @size: Size of the command buffer in bytes + * @length: Used memory in command buffer in bytes + * @type: Type of the command buffer + * @meta_data: Data type for private command buffer + * Between UMD and KMD + * + */ +struct cam_cmd_buf_desc { + int32_t mem_handle; + uint32_t offset; + uint32_t size; + uint32_t length; + uint32_t type; + uint32_t meta_data; +}; + +/** + * struct cam_buf_io_cfg - Buffer io configuration for buffers + * + * @mem_handle: Mem_handle array for the buffers. + * @offsets: Offsets for each planes in the buffer + * @planes: Per plane information + * @width: Main plane width in pixel + * @height: Main plane height in lines + * @format: Format of the buffer + * @color_space: Color space for the buffer + * @color_pattern: Color pattern in the buffer + * @bpp: Bit per pixel + * @rotation: Rotation information for the buffer + * @resource_type: Resource type associated with the buffer + * @fence: Fence handle + * @early_fence: Fence handle for early signal + * @aux_cmd_buf: An auxiliary command buffer that may be + * used for programming the IO + * @direction: Direction of the config + * @batch_size: Batch size in HFR mode + * @subsample_pattern: Subsample pattern. Used in HFR mode. It + * should be consistent with batchSize and + * CAMIF programming. + * @subsample_period: Subsample period. Used in HFR mode. It + * should be consistent with batchSize and + * CAMIF programming. + * @framedrop_pattern: Framedrop pattern + * @framedrop_period: Framedrop period + * @flag: Flags for extra information + * @direction: Buffer direction: input or output + * @padding: Padding for the structure + * + */ +struct cam_buf_io_cfg { + int32_t mem_handle[CAM_PACKET_MAX_PLANES]; + uint32_t offsets[CAM_PACKET_MAX_PLANES]; + struct cam_plane_cfg planes[CAM_PACKET_MAX_PLANES]; + uint32_t format; + uint32_t color_space; + uint32_t color_pattern; + uint32_t bpp; + uint32_t rotation; + uint32_t resource_type; + int32_t fence; + int32_t early_fence; + struct cam_cmd_buf_desc aux_cmd_buf; + uint32_t direction; + uint32_t batch_size; + uint32_t subsample_pattern; + uint32_t subsample_period; + uint32_t framedrop_pattern; + uint32_t framedrop_period; + uint32_t flag; + uint32_t padding; +}; + +/** + * struct cam_packet_header - Camera packet header + * + * @op_code: Camera packet opcode + * @size: Size of the camera packet in bytes + * @request_id: Request id for this camera packet + * @flags: Flags for the camera packet + * @padding: Padding + * + */ +struct cam_packet_header { + uint32_t op_code; + uint32_t size; + uint64_t request_id; + uint32_t flags; + uint32_t padding; +}; + +/** + * struct cam_patch_desc - Patch structure + * + * @dst_buf_hdl: Memory handle for the dest buffer + * @dst_offset: Offset byte in the dest buffer + * @src_buf_hdl: Memory handle for the source buffer + * @src_offset: Offset byte in the source buffer + * + */ +struct cam_patch_desc { + int32_t dst_buf_hdl; + uint32_t dst_offset; + int32_t src_buf_hdl; + uint32_t src_offset; +}; + +/** + * struct cam_packet - Camera packet structure + * + * @header: Camera packet header + * @cmd_buf_offset: Command buffer start offset + * @num_cmd_buf: Number of the command buffer in the packet + * @io_config_offset: Buffer io configuration start offset + * @num_io_configs: Number of the buffer io configurations + * @patch_offset: Patch offset for the patch structure + * @num_patches: Number of the patch structure + * @kmd_cmd_buf_index: Command buffer index which contains extra + * space for the KMD buffer + * @kmd_cmd_buf_offset: Offset from the beginning of the command + * buffer for KMD usage. + * @payload: Camera packet payload + * + */ +struct cam_packet { + struct cam_packet_header header; + uint32_t cmd_buf_offset; + uint32_t num_cmd_buf; + uint32_t io_configs_offset; + uint32_t num_io_configs; + uint32_t patch_offset; + uint32_t num_patches; + uint32_t kmd_cmd_buf_index; + uint32_t kmd_cmd_buf_offset; + uint64_t payload[1]; + +}; + +/** + * struct cam_release_dev_cmd - Control payload for release devices + * + * @session_handle: Session handle for the release + * @dev_handle: Device handle for the release + */ +struct cam_release_dev_cmd { + int32_t session_handle; + int32_t dev_handle; +}; + +/** + * struct cam_start_stop_dev_cmd - Control payload for start/stop device + * + * @session_handle: Session handle for the start/stop command + * @dev_handle: Device handle for the start/stop command + * + */ +struct cam_start_stop_dev_cmd { + int32_t session_handle; + int32_t dev_handle; +}; + +/** + * struct cam_config_dev_cmd - Command payload for configure device + * + * @session_handle: Session handle for the command + * @dev_handle: Device handle for the command + * @offset: Offset byte in the packet handle. + * @packet_handle: Packet memory handle for the actual packet: + * struct cam_packet. + * + */ +struct cam_config_dev_cmd { + int32_t session_handle; + int32_t dev_handle; + uint64_t offset; + uint64_t packet_handle; +}; + +/** + * struct cam_query_cap_cmd - Payload for query device capability + * + * @size: Handle size + * @handle_type: User pointer or shared memory handle + * @caps_handle: Device specific query command payload + * + */ +struct cam_query_cap_cmd { + uint32_t size; + uint32_t handle_type; + uint64_t caps_handle; +}; + +/** + * struct cam_acquire_dev_cmd - Control payload for acquire devices + * + * @session_handle: Session handle for the acquire command + * @dev_handle: Device handle to be returned + * @handle_type: Resource handle type: + * 1 = user pointer, 2 = mem handle + * @num_resources: Number of the resources to be acquired + * @resources_hdl: Resource handle that refers to the actual + * resource array. Each item in this + * array is device specific resource structure + * + */ +struct cam_acquire_dev_cmd { + int32_t session_handle; + int32_t dev_handle; + uint32_t handle_type; + uint32_t num_resources; + uint64_t resource_hdl; +}; + +/* + * In old version, while acquiring device the num_resources in + * struct cam_acquire_dev_cmd will be a valid value. During ACQUIRE_DEV + * KMD driver will return dev_handle as well as associate HW to handle. + * If num_resources is set to the constant below, we are using + * the new version and we do not acquire HW in ACQUIRE_DEV IOCTL. + * ACQUIRE_DEV will only return handle and we should receive + * ACQUIRE_HW IOCTL after ACQUIRE_DEV and that is when the HW + * is associated with the dev_handle. + * + * (Data type): uint32_t + */ +#define CAM_API_COMPAT_CONSTANT 0xFEFEFEFE + +#define CAM_ACQUIRE_HW_STRUCT_VERSION_1 1 + +/** + * struct cam_acquire_hw_cmd_v1 - Control payload for acquire HW IOCTL (Ver 1) + * + * @struct_version: = CAM_ACQUIRE_HW_STRUCT_VERSION_1 for this struct + * This value should be the first 32-bits in any structure + * related to this IOCTL. So that if the struct needs to + * change, we can first read the starting 32-bits, get the + * version number and then typecast the data to struct + * accordingly. + * @reserved: Reserved field for 64-bit alignment + * @session_handle: Session handle for the acquire command + * @dev_handle: Device handle to be returned + * @handle_type: Tells you how to interpret the variable resource_hdl- + * 1 = user pointer, 2 = mem handle + * @data_size: Total size of data contained in memory pointed + * to by resource_hdl + * @resource_hdl: Resource handle that refers to the actual + * resource data. + */ +struct cam_acquire_hw_cmd_v1 { + uint32_t struct_version; + uint32_t reserved; + int32_t session_handle; + int32_t dev_handle; + uint32_t handle_type; + uint32_t data_size; + uint64_t resource_hdl; +}; + +#define CAM_RELEASE_HW_STRUCT_VERSION_1 1 + +/** + * struct cam_release_hw_cmd_v1 - Control payload for release HW IOCTL (Ver 1) + * + * @struct_version: = CAM_RELEASE_HW_STRUCT_VERSION_1 for this struct + * This value should be the first 32-bits in any structure + * related to this IOCTL. So that if the struct needs to + * change, we can first read the starting 32-bits, get the + * version number and then typecast the data to struct + * accordingly. + * @reserved: Reserved field for 64-bit alignment + * @session_handle: Session handle for the release + * @dev_handle: Device handle for the release + */ +struct cam_release_hw_cmd_v1 { + uint32_t struct_version; + uint32_t reserved; + int32_t session_handle; + int32_t dev_handle; +}; + +/** + * struct cam_flush_dev_cmd - Control payload for flush devices + * + * @version: Version + * @session_handle: Session handle for the acquire command + * @dev_handle: Device handle to be returned + * @flush_type: Flush type: + * 0 = flush specific request + * 1 = flush all + * @reserved: Reserved for 64 bit aligngment + * @req_id: Request id that needs to cancel + * + */ +struct cam_flush_dev_cmd { + uint64_t version; + int32_t session_handle; + int32_t dev_handle; + uint32_t flush_type; + uint32_t reserved; + int64_t req_id; +}; + +/** + * struct cam_ubwc_config - UBWC Configuration Payload + * + * @api_version: UBWC config api version + * @num_ports: Number of ports to be configured + * @ubwc_plane_config: Array of UBWC configurations per port + * Size [CAM_PACKET_MAX_PLANES - 1] per port + * as UBWC is supported on Y & C planes + * and therefore a max size of 2 planes + * + */ +struct cam_ubwc_config { + uint32_t api_version; + uint32_t num_ports; + struct cam_ubwc_plane_cfg_v1 + ubwc_plane_cfg[1][CAM_PACKET_MAX_PLANES - 1]; +}; + +/** + * struct cam_cmd_mem_region_info - + * Cmd buffer region info + * + * @mem_handle : Memory handle of the region + * @offset : Offset if any + * @size : Size of the region + * @flags : Flags if any + */ +struct cam_cmd_mem_region_info { + int32_t mem_handle; + uint32_t offset; + uint32_t size; + uint32_t flags; +}; + +/** + * struct cam_cmd_mem_regions - + * List of multiple memory descriptors of + * of different regions + * + * @version : Version number + * @num_regions : Number of regions + * @map_info_array : Array of all the regions + */ +struct cam_cmd_mem_regions { + uint32_t version; + uint32_t num_regions; + struct cam_cmd_mem_region_info map_info_array[1]; +}; + +#endif /* __UAPI_CAM_DEFS_H__ */ diff --git a/include/uapi/media/cam_fd.h b/include/uapi/media/cam_fd.h new file mode 100644 index 000000000000..8feb6e4da89a --- /dev/null +++ b/include/uapi/media/cam_fd.h @@ -0,0 +1,127 @@ +#ifndef __UAPI_CAM_FD_H__ +#define __UAPI_CAM_FD_H__ + +#include "cam_defs.h" + +#define CAM_FD_MAX_FACES 35 +#define CAM_FD_RAW_RESULT_ENTRIES 512 + +/* FD Op Codes */ +#define CAM_PACKET_OPCODES_FD_FRAME_UPDATE 0x0 + +/* FD Command Buffer identifiers */ +#define CAM_FD_CMD_BUFFER_ID_GENERIC 0x0 +#define CAM_FD_CMD_BUFFER_ID_CDM 0x1 +#define CAM_FD_CMD_BUFFER_ID_MAX 0x2 + +/* FD Blob types */ +#define CAM_FD_BLOB_TYPE_SOC_CLOCK_BW_REQUEST 0x0 +#define CAM_FD_BLOB_TYPE_RAW_RESULTS_REQUIRED 0x1 + +/* FD Resource IDs */ +#define CAM_FD_INPUT_PORT_ID_IMAGE 0x0 +#define CAM_FD_INPUT_PORT_ID_MAX 0x1 + +#define CAM_FD_OUTPUT_PORT_ID_RESULTS 0x0 +#define CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS 0x1 +#define CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER 0x2 +#define CAM_FD_OUTPUT_PORT_ID_MAX 0x3 + +/** + * struct cam_fd_soc_clock_bw_request - SOC clock, bandwidth request info + * + * @clock_rate : Clock rate required while processing frame + * @bandwidth : Bandwidth required while processing frame + * @reserved : Reserved for future use + */ +struct cam_fd_soc_clock_bw_request { + uint64_t clock_rate; + uint64_t bandwidth; + uint64_t reserved[4]; +}; + +/** + * struct cam_fd_face - Face properties + * + * @prop1 : Property 1 of face + * @prop2 : Property 2 of face + * @prop3 : Property 3 of face + * @prop4 : Property 4 of face + * + * Do not change this layout, this is inline with how HW writes + * these values directly when the buffer is programmed to HW + */ +struct cam_fd_face { + uint32_t prop1; + uint32_t prop2; + uint32_t prop3; + uint32_t prop4; +}; + +/** + * struct cam_fd_results - FD results layout + * + * @faces : Array of faces with face properties + * @face_count : Number of faces detected + * @reserved : Reserved for alignment + * + * Do not change this layout, this is inline with how HW writes + * these values directly when the buffer is programmed to HW + */ +struct cam_fd_results { + struct cam_fd_face faces[CAM_FD_MAX_FACES]; + uint32_t face_count; + uint32_t reserved[3]; +}; + +/** + * struct cam_fd_hw_caps - Face properties + * + * @core_version : FD core version + * @wrapper_version : FD wrapper version + * @raw_results_available : Whether raw results are available on this HW + * @supported_modes : Modes supported by this HW. + * @reserved : Reserved for future use + */ +struct cam_fd_hw_caps { + struct cam_hw_version core_version; + struct cam_hw_version wrapper_version; + uint32_t raw_results_available; + uint32_t supported_modes; + uint64_t reserved; +}; + +/** + * struct cam_fd_query_cap_cmd - FD Query capabilities information + * + * @device_iommu : FD IOMMU handles + * @cdm_iommu : CDM iommu handles + * @hw_caps : FD HW capabilities + * @reserved : Reserved for alignment + */ +struct cam_fd_query_cap_cmd { + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + struct cam_fd_hw_caps hw_caps; + uint64_t reserved; +}; + +/** + * struct cam_fd_acquire_dev_info - FD acquire device information + * + * @clk_bw_request : SOC clock, bandwidth request + * @priority : Priority for this acquire + * @mode : Mode in which to run FD HW. + * @get_raw_results : Whether this acquire needs face raw results + * while frame processing + * @reserved : Reserved field for 64 bit alignment + */ +struct cam_fd_acquire_dev_info { + struct cam_fd_soc_clock_bw_request clk_bw_request; + uint32_t priority; + uint32_t mode; + uint32_t get_raw_results; + uint32_t reserved[13]; +}; + +#endif /* __UAPI_CAM_FD_H__ */ diff --git a/include/uapi/media/cam_icp.h b/include/uapi/media/cam_icp.h new file mode 100644 index 000000000000..5b4a6045d9ce --- /dev/null +++ b/include/uapi/media/cam_icp.h @@ -0,0 +1,204 @@ +#ifndef __UAPI_CAM_ICP_H__ +#define __UAPI_CAM_ICP_H__ + +#include "cam_defs.h" +#include "cam_cpas.h" + +/* icp, ipe, bps, cdm(ipe/bps) are used in querycap */ +#define CAM_ICP_DEV_TYPE_A5 1 +#define CAM_ICP_DEV_TYPE_IPE 2 +#define CAM_ICP_DEV_TYPE_BPS 3 +#define CAM_ICP_DEV_TYPE_IPE_CDM 4 +#define CAM_ICP_DEV_TYPE_BPS_CDM 5 +#define CAM_ICP_DEV_TYPE_MAX 5 + +/* definitions needed for icp aquire device */ +#define CAM_ICP_RES_TYPE_BPS 1 +#define CAM_ICP_RES_TYPE_IPE_RT 2 +#define CAM_ICP_RES_TYPE_IPE 3 +#define CAM_ICP_RES_TYPE_MAX 4 + +/* packet opcode types */ +#define CAM_ICP_OPCODE_IPE_UPDATE 0 +#define CAM_ICP_OPCODE_BPS_UPDATE 1 +#define CAM_ICP_OPCODE_IPE_SETTINGS 2 +#define CAM_ICP_OPCODE_BPS_SETTINGS 3 + +/* IPE input port resource type */ +#define CAM_ICP_IPE_INPUT_IMAGE_FULL 0x0 +#define CAM_ICP_IPE_INPUT_IMAGE_DS4 0x1 +#define CAM_ICP_IPE_INPUT_IMAGE_DS16 0x2 +#define CAM_ICP_IPE_INPUT_IMAGE_DS64 0x3 +#define CAM_ICP_IPE_INPUT_IMAGE_FULL_REF 0x4 +#define CAM_ICP_IPE_INPUT_IMAGE_DS4_REF 0x5 +#define CAM_ICP_IPE_INPUT_IMAGE_DS16_REF 0x6 +#define CAM_ICP_IPE_INPUT_IMAGE_DS64_REF 0x7 + +/* IPE output port resource type */ +#define CAM_ICP_IPE_OUTPUT_IMAGE_DISPLAY 0x8 +#define CAM_ICP_IPE_OUTPUT_IMAGE_VIDEO 0x9 +#define CAM_ICP_IPE_OUTPUT_IMAGE_FULL_REF 0xA +#define CAM_ICP_IPE_OUTPUT_IMAGE_DS4_REF 0xB +#define CAM_ICP_IPE_OUTPUT_IMAGE_DS16_REF 0xC +#define CAM_ICP_IPE_OUTPUT_IMAGE_DS64_REF 0xD + +#define CAM_ICP_IPE_IMAGE_MAX 0xE + +/* BPS input port resource type */ +#define CAM_ICP_BPS_INPUT_IMAGE 0x0 + +/* BPS output port resource type */ +#define CAM_ICP_BPS_OUTPUT_IMAGE_FULL 0x1 +#define CAM_ICP_BPS_OUTPUT_IMAGE_DS4 0x2 +#define CAM_ICP_BPS_OUTPUT_IMAGE_DS16 0x3 +#define CAM_ICP_BPS_OUTPUT_IMAGE_DS64 0x4 +#define CAM_ICP_BPS_OUTPUT_IMAGE_STATS_BG 0x5 +#define CAM_ICP_BPS_OUTPUT_IMAGE_STATS_BHIST 0x6 +#define CAM_ICP_BPS_OUTPUT_IMAGE_REG1 0x7 +#define CAM_ICP_BPS_OUTPUT_IMAGE_REG2 0x8 + +#define CAM_ICP_BPS_IO_IMAGES_MAX 0x9 + +/* Command meta types */ +#define CAM_ICP_CMD_META_GENERIC_BLOB 0x1 + +/* Generic blob types */ +#define CAM_ICP_CMD_GENERIC_BLOB_CLK 0x1 +#define CAM_ICP_CMD_GENERIC_BLOB_CFG_IO 0x2 +#define CAM_ICP_CMD_GENERIC_BLOB_FW_MEM_MAP 0x3 +#define CAM_ICP_CMD_GENERIC_BLOB_FW_MEM_UNMAP 0x4 +#define CAM_ICP_CMD_GENERIC_BLOB_CLK_V2 0x5 + +/** + * struct cam_icp_clk_bw_request_v2 + * + * @budget_ns: Time required to process frame + * @frame_cycles: Frame cycles needed to process the frame + * @rt_flag: Flag to indicate real time stream + * @reserved: For memory alignment + * @num_paths: Number of axi paths in bw request + * @axi_path: Per path vote info for IPE/BPS + */ +struct cam_icp_clk_bw_request_v2 { + uint64_t budget_ns; + uint32_t frame_cycles; + uint32_t rt_flag; + uint32_t reserved; + uint32_t num_paths; + struct cam_axi_per_path_bw_vote axi_path[1]; +}; + +/** + * struct cam_icp_clk_bw_request + * + * @budget_ns: Time required to process frame + * @frame_cycles: Frame cycles needed to process the frame + * @rt_flag: Flag to indicate real time stream + * @uncompressed_bw: Bandwidth required to process frame + * @compressed_bw: Compressed bandwidth to process frame + */ +struct cam_icp_clk_bw_request { + uint64_t budget_ns; + uint32_t frame_cycles; + uint32_t rt_flag; + uint64_t uncompressed_bw; + uint64_t compressed_bw; +}; + +/** + * struct cam_icp_dev_ver - Device information for particular hw type + * + * This is used to get device version info of + * ICP, IPE, BPS and CDM related IPE and BPS from firmware + * and use this info in CAM_QUERY_CAP IOCTL + * + * @dev_type: hardware type for the cap info(icp, ipe, bps, cdm(ipe/bps)) + * @reserved: reserved field + * @hw_ver: major, minor and incr values of a device version + */ +struct cam_icp_dev_ver { + uint32_t dev_type; + uint32_t reserved; + struct cam_hw_version hw_ver; +}; + +/** + * struct cam_icp_ver - ICP version info + * + * This strcuture is used for fw and api version + * this is used to get firmware version and api version from firmware + * and use this info in CAM_QUERY_CAP IOCTL + * + * @major: FW version major + * @minor: FW version minor + * @revision: FW version increment + */ +struct cam_icp_ver { + uint32_t major; + uint32_t minor; + uint32_t revision; + uint32_t reserved; +}; + +/** + * struct cam_icp_query_cap_cmd - ICP query device capability payload + * + * @dev_iommu_handle: icp iommu handles for secure/non secure modes + * @cdm_iommu_handle: iommu handles for secure/non secure modes + * @fw_version: firmware version info + * @api_version: api version info + * @num_ipe: number of ipes + * @num_bps: number of bps + * @dev_ver: returned device capability array + */ +struct cam_icp_query_cap_cmd { + struct cam_iommu_handle dev_iommu_handle; + struct cam_iommu_handle cdm_iommu_handle; + struct cam_icp_ver fw_version; + struct cam_icp_ver api_version; + uint32_t num_ipe; + uint32_t num_bps; + struct cam_icp_dev_ver dev_ver[CAM_ICP_DEV_TYPE_MAX]; +}; + +/** + * struct cam_icp_res_info - ICP output resource info + * + * @format: format of the resource + * @width: width in pixels + * @height: height in lines + * @fps: fps + */ +struct cam_icp_res_info { + uint32_t format; + uint32_t width; + uint32_t height; + uint32_t fps; +}; + +/** + * struct cam_icp_acquire_dev_info - An ICP device info + * + * @scratch_mem_size: Output param - size of scratch memory + * @dev_type: device type (IPE_RT/IPE_NON_RT/BPS) + * @io_config_cmd_size: size of IO config command + * @io_config_cmd_handle: IO config command for each acquire + * @secure_mode: camera mode (secure/non secure) + * @chain_info: chaining info of FW device handles + * @in_res: resource info used for clock and bandwidth calculation + * @num_out_res: number of output resources + * @out_res: output resource + */ +struct cam_icp_acquire_dev_info { + uint32_t scratch_mem_size; + uint32_t dev_type; + uint32_t io_config_cmd_size; + int32_t io_config_cmd_handle; + uint32_t secure_mode; + int32_t chain_info; + struct cam_icp_res_info in_res; + uint32_t num_out_res; + struct cam_icp_res_info out_res[1]; +} __attribute__((__packed__)); + +#endif /* __UAPI_CAM_ICP_H__ */ diff --git a/include/uapi/media/cam_isp.h b/include/uapi/media/cam_isp.h new file mode 100644 index 000000000000..7489b72031b7 --- /dev/null +++ b/include/uapi/media/cam_isp.h @@ -0,0 +1,510 @@ +#ifndef __UAPI_CAM_ISP_H__ +#define __UAPI_CAM_ISP_H__ + +#include "cam_defs.h" +#include "cam_isp_vfe.h" +#include "cam_isp_ife.h" +#include "cam_cpas.h" + +/* ISP driver name */ +#define CAM_ISP_DEV_NAME "cam-isp" + +/* HW type */ +#define CAM_ISP_HW_BASE 0 +#define CAM_ISP_HW_CSID 1 +#define CAM_ISP_HW_VFE 2 +#define CAM_ISP_HW_IFE 3 +#define CAM_ISP_HW_ISPIF 4 +#define CAM_ISP_HW_MAX 5 + +/* Color Pattern */ +#define CAM_ISP_PATTERN_BAYER_RGRGRG 0 +#define CAM_ISP_PATTERN_BAYER_GRGRGR 1 +#define CAM_ISP_PATTERN_BAYER_BGBGBG 2 +#define CAM_ISP_PATTERN_BAYER_GBGBGB 3 +#define CAM_ISP_PATTERN_YUV_YCBYCR 4 +#define CAM_ISP_PATTERN_YUV_YCRYCB 5 +#define CAM_ISP_PATTERN_YUV_CBYCRY 6 +#define CAM_ISP_PATTERN_YUV_CRYCBY 7 +#define CAM_ISP_PATTERN_MAX 8 + +/* Usage Type */ +#define CAM_ISP_RES_USAGE_SINGLE 0 +#define CAM_ISP_RES_USAGE_DUAL 1 +#define CAM_ISP_RES_USAGE_MAX 2 + +/* Resource ID */ +#define CAM_ISP_RES_ID_PORT 0 +#define CAM_ISP_RES_ID_CLK 1 +#define CAM_ISP_RES_ID_MAX 2 + +/* Resource Type - Type of resource for the resource id + * defined in cam_isp_vfe.h, cam_isp_ife.h + */ + +/* Lane Type in input resource for Port */ +#define CAM_ISP_LANE_TYPE_DPHY 0 +#define CAM_ISP_LANE_TYPE_CPHY 1 +#define CAM_ISP_LANE_TYPE_MAX 2 + +/* ISP Resurce Composite Group ID */ +#define CAM_ISP_RES_COMP_GROUP_NONE 0 +#define CAM_ISP_RES_COMP_GROUP_ID_0 1 +#define CAM_ISP_RES_COMP_GROUP_ID_1 2 +#define CAM_ISP_RES_COMP_GROUP_ID_2 3 +#define CAM_ISP_RES_COMP_GROUP_ID_3 4 +#define CAM_ISP_RES_COMP_GROUP_ID_4 5 +#define CAM_ISP_RES_COMP_GROUP_ID_5 6 +#define CAM_ISP_RES_COMP_GROUP_ID_MAX 6 + +/* ISP packet opcode for ISP */ +#define CAM_ISP_PACKET_OP_BASE 0 +#define CAM_ISP_PACKET_INIT_DEV 1 +#define CAM_ISP_PACKET_UPDATE_DEV 2 +#define CAM_ISP_PACKET_OP_MAX 3 + +/* ISP packet meta_data type for command buffer */ +#define CAM_ISP_PACKET_META_BASE 0 +#define CAM_ISP_PACKET_META_LEFT 1 +#define CAM_ISP_PACKET_META_RIGHT 2 +#define CAM_ISP_PACKET_META_COMMON 3 +#define CAM_ISP_PACKET_META_DMI_LEFT 4 +#define CAM_ISP_PACKET_META_DMI_RIGHT 5 +#define CAM_ISP_PACKET_META_DMI_COMMON 6 +#define CAM_ISP_PACKET_META_CLOCK 7 +#define CAM_ISP_PACKET_META_CSID 8 +#define CAM_ISP_PACKET_META_DUAL_CONFIG 9 +#define CAM_ISP_PACKET_META_GENERIC_BLOB_LEFT 10 +#define CAM_ISP_PACKET_META_GENERIC_BLOB_RIGHT 11 +#define CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON 12 + +/* DSP mode */ +#define CAM_ISP_DSP_MODE_NONE 0 +#define CAM_ISP_DSP_MODE_ONE_WAY 1 +#define CAM_ISP_DSP_MODE_ROUND 2 + +/* ISP Generic Cmd Buffer Blob types */ +#define CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG 0 +#define CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG 1 +#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG 2 +#define CAM_ISP_GENERIC_BLOB_TYPE_UBWC_CONFIG 3 +#define CAM_ISP_GENERIC_BLOB_TYPE_CSID_CLOCK_CONFIG 4 +#define CAM_ISP_GENERIC_BLOB_TYPE_FE_CONFIG 5 +#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG_V2 6 +#define CAM_ISP_GENERIC_BLOB_TYPE_INIT_FRAME_DROP 10 + +/* Per Path Usage Data */ +#define CAM_ISP_USAGE_INVALID 0 +#define CAM_ISP_USAGE_LEFT_PX 1 +#define CAM_ISP_USAGE_RIGHT_PX 2 +#define CAM_ISP_USAGE_RDI 3 + +/* Query devices */ +/** + * struct cam_isp_dev_cap_info - A cap info for particular hw type + * + * @hw_type: Hardware type for the cap info + * @reserved: reserved field for alignment + * @hw_version: Hardware version + * + */ +struct cam_isp_dev_cap_info { + uint32_t hw_type; + uint32_t reserved; + struct cam_hw_version hw_version; +}; + +/** + * struct cam_isp_query_cap_cmd - ISP query device capability payload + * + * @device_iommu: returned iommu handles for device + * @cdm_iommu: returned iommu handles for cdm + * @num_dev: returned number of device capabilities + * @reserved: reserved field for alignment + * @dev_caps: returned device capability array + * + */ +struct cam_isp_query_cap_cmd { + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + int32_t num_dev; + uint32_t reserved; + struct cam_isp_dev_cap_info dev_caps[CAM_ISP_HW_MAX]; +}; + +/* Acquire Device */ +/** + * struct cam_isp_out_port_info - An output port resource info + * + * @res_type: output resource type defined in file + * cam_isp_vfe.h or cam_isp_ife.h + * @format: output format of the resource + * @wdith: output width in pixels + * @height: output height in lines + * @comp_grp_id: composite group id for the resource. + * @split_point: split point in pixels for the dual VFE. + * @secure_mode: flag to tell if output should be run in secure + * mode or not. See cam_defs.h for definition + * @reserved: reserved field for alignment + * + */ +struct cam_isp_out_port_info { + uint32_t res_type; + uint32_t format; + uint32_t width; + uint32_t height; + uint32_t comp_grp_id; + uint32_t split_point; + uint32_t secure_mode; + uint32_t reserved; +}; + +/** + * struct cam_isp_in_port_info - An input port resource info + * + * @res_type: input resource type define in file + * cam_isp_vfe.h or cam_isp_ife.h + * @lane_type: lane type: c-phy or d-phy. + * @lane_num: active lane number + * @lane_cfg: lane configurations: 4 bits per lane + * @vc: input virtual channel number + * @dt: input data type number + * @format: input format + * @test_pattern: test pattern for the testgen + * @usage_type: whether dual vfe is required + * @left_start: left input start offset in pixels + * @left_stop: left input stop offset in pixels + * @left_width: left input width in pixels + * @right_start: right input start offset in pixels. + * Only for Dual VFE + * @right_stop: right input stop offset in pixels. + * Only for Dual VFE + * @right_width: right input width in pixels. + * Only for dual VFE + * @line_start: top of the line number + * @line_stop: bottome of the line number + * @height: input height in lines + * @pixel_clk; sensor output clock + * @batch_size: batch size for HFR mode + * @dsp_mode: DSP stream mode (Defines as CAM_ISP_DSP_MODE_*) + * @hbi_cnt: HBI count for the camif input + * @reserved: Reserved field for alignment + * @num_out_res: number of the output resource associated + * @data: payload that contains the output resources + * + */ +struct cam_isp_in_port_info { + uint32_t res_type; + uint32_t lane_type; + uint32_t lane_num; + uint32_t lane_cfg; + uint32_t vc; + uint32_t dt; + uint32_t format; + uint32_t test_pattern; + uint32_t usage_type; + uint32_t left_start; + uint32_t left_stop; + uint32_t left_width; + uint32_t right_start; + uint32_t right_stop; + uint32_t right_width; + uint32_t line_start; + uint32_t line_stop; + uint32_t height; + uint32_t pixel_clk; + uint32_t batch_size; + uint32_t dsp_mode; + uint32_t hbi_cnt; + uint32_t reserved; + uint32_t num_out_res; + struct cam_isp_out_port_info data[1]; +}; + +/** + * struct cam_isp_resource - A resource bundle + * + * @resoruce_id: resource id for the resource bundle + * @length: length of the while resource blob + * @handle_type: type of the resource handle + * @reserved: reserved field for alignment + * @res_hdl: resource handle that points to the + * resource array; + * + */ +struct cam_isp_resource { + uint32_t resource_id; + uint32_t length; + uint32_t handle_type; + uint32_t reserved; + uint64_t res_hdl; +}; + +/** + * struct cam_isp_port_hfr_config - HFR configuration for this port + * + * @resource_type: Resource type + * @subsample_pattern: Subsample pattern. Used in HFR mode. It + * should be consistent with batchSize and + * CAMIF programming. + * @subsample_period: Subsample period. Used in HFR mode. It + * should be consistent with batchSize and + * CAMIF programming. + * @framedrop_pattern: Framedrop pattern + * @framedrop_period: Framedrop period + * @reserved: Reserved for alignment + */ +struct cam_isp_port_hfr_config { + uint32_t resource_type; + uint32_t subsample_pattern; + uint32_t subsample_period; + uint32_t framedrop_pattern; + uint32_t framedrop_period; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct cam_isp_resource_hfr_config - Resource HFR configuration + * + * @num_ports: Number of ports + * @reserved: Reserved for alignment + * @port_hfr_config: HFR configuration for each IO port + */ +struct cam_isp_resource_hfr_config { + uint32_t num_ports; + uint32_t reserved; + struct cam_isp_port_hfr_config port_hfr_config[1]; +} __attribute__((packed)); + +/** + * struct cam_isp_dual_split_params - dual isp spilt parameters + * + * @split_point: Split point information x, where (0 < x < width) + * left ISP's input ends at x + righ padding and + * Right ISP's input starts at x - left padding + * @right_padding: Padding added past the split point for left + * ISP's input + * @left_padding: Padding added before split point for right + * ISP's input + * @reserved: Reserved filed for alignment + * + */ +struct cam_isp_dual_split_params { + uint32_t split_point; + uint32_t right_padding; + uint32_t left_padding; + uint32_t reserved; +}; + +/** + * struct cam_isp_dual_stripe_config - stripe config per bus client + * + * @offset: Start horizontal offset relative to + * output buffer + * In UBWC mode, this value indicates the H_INIT + * value in pixel + * @width: Width of the stripe in bytes + * @tileconfig Ubwc meta tile config. Contain the partial + * tile info + * @port_id: port id of ISP output + * + */ +struct cam_isp_dual_stripe_config { + uint32_t offset; + uint32_t width; + uint32_t tileconfig; + uint32_t port_id; +}; + +/** + * struct cam_isp_dual_config - dual isp configuration + * + * @num_ports Number of isp output ports + * @reserved Reserved field for alignment + * @split_params: Inpput split parameters + * @stripes: Stripe information + * + */ +struct cam_isp_dual_config { + uint32_t num_ports; + uint32_t reserved; + struct cam_isp_dual_split_params split_params; + struct cam_isp_dual_stripe_config stripes[1]; +} __attribute__((packed)); + +/** + * struct cam_isp_clock_config - Clock configuration + * + * @usage_type: Usage type (Single/Dual) + * @num_rdi: Number of RDI votes + * @left_pix_hz: Pixel Clock for Left ISP + * @right_pix_hz: Pixel Clock for Right ISP, valid only if Dual + * @rdi_hz: RDI Clock. ISP clock will be max of RDI and + * PIX clocks. For a particular context which ISP + * HW the RDI is allocated to is not known to UMD. + * Hence pass the clock and let KMD decide. + */ +struct cam_isp_clock_config { + uint32_t usage_type; + uint32_t num_rdi; + uint64_t left_pix_hz; + uint64_t right_pix_hz; + uint64_t rdi_hz[1]; +} __attribute__((packed)); + +/** + * struct cam_isp_csid_clock_config - CSID clock configuration + * + * @csid_clock CSID clock + */ +struct cam_isp_csid_clock_config { + uint64_t csid_clock; +} __attribute__((packed)); + +/** + * struct cam_isp_bw_vote - Bandwidth vote information + * + * @resource_id: Resource ID + * @reserved: Reserved field for alignment + * @cam_bw_bps: Bandwidth vote for CAMNOC + * @ext_bw_bps: Bandwidth vote for path-to-DDR after CAMNOC + */ +struct cam_isp_bw_vote { + uint32_t resource_id; + uint32_t reserved; + uint64_t cam_bw_bps; + uint64_t ext_bw_bps; +} __attribute__((packed)); + +/** + * struct cam_isp_bw_config - Bandwidth configuration + * + * @usage_type: Usage type (Single/Dual) + * @num_rdi: Number of RDI votes + * @left_pix_vote: Bandwidth vote for left ISP + * @right_pix_vote: Bandwidth vote for right ISP + * @rdi_vote: RDI bandwidth requirements + */ +struct cam_isp_bw_config { + uint32_t usage_type; + uint32_t num_rdi; + struct cam_isp_bw_vote left_pix_vote; + struct cam_isp_bw_vote right_pix_vote; + struct cam_isp_bw_vote rdi_vote[1]; +} __attribute__((packed)); + + +/** + * struct cam_isp_bw_config_ab - Bandwidth configuration + * + * @usage_type: Usage type (Single/Dual) + * @num_rdi: Number of RDI votes + * @left_pix_vote_ab: AB Bandwidth vote for left ISP + * @right_pix_vote_ab: AB Bandwidth vote for right ISP + * @rdi_vote_ab: AB RDI bandwidth requirements + */ + +struct cam_isp_bw_config_ab { + uint32_t usage_type; + uint32_t num_rdi; + uint64_t left_pix_vote_ab; + uint64_t right_pix_vote_ab; + uint64_t rdi_vote_ab[1]; +} __attribute__((packed)); + +/** + * struct cam_isp_bw_config_v2 - Bandwidth configuration + * + * @usage_type: Usage type (Single/Dual) + * @num_paths: Number of axi data paths + * @axi_path Per path vote info + */ +struct cam_isp_bw_config_v2 { + uint32_t usage_type; + uint32_t num_paths; + struct cam_axi_per_path_bw_vote axi_path[1]; +} __attribute__((packed)); + +/** + * struct cam_fe_config - Fetch Engine configuration + * + * @version: fetch engine veriosn + * @min_vbi: require min vbi + * @fs_mode: indicates if fs mode enabled + * @fs_line_sync_en: frame level sync or line level + * sync for fetch engine + * @hbi_count: hbi count + * @fs_sync_enable: indicates if fetch engine working + * wokring in sync with write engine + * @go_cmd_sel: softwrae go_cmd or hw go_cmd + * @client_enable: enable read engine + * @source_addr: adrress of buffer to read from + * @width: buffer width + * @height: buffer height + * @stride: buffer stride (here equal to width) + * @format: format of image in buffer + * @unpacker_cfg: unpacker config type + * @latency_buf_size: latency buffer for read engine + */ +struct cam_fe_config { + uint64_t version; + uint32_t min_vbi; + uint32_t fs_mode; + uint32_t fs_line_sync_en; + uint32_t hbi_count; + uint32_t fs_sync_enable; + uint32_t go_cmd_sel; + uint32_t client_enable; + uint32_t source_addr; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; + uint32_t unpacker_cfg; + uint32_t latency_buf_size; +} __attribute__((packed)); + +/* Acquire Device/HW v2 */ + +/** + * struct cam_isp_acquire_hw_info - ISP acquire HW params + * + * @common_info_version : Version of common info struct used + * @common_info_size : Size of common info struct used + * @common_info_offset : Offset of common info from start of data + * @num_inputs : Number of inputs + * @input_info_version : Version of input info struct used + * @input_info_size : Size of input info struct used + * @input_info_offset : Offset of input info from start of data + * @data : Start of data region + */ +struct cam_isp_acquire_hw_info { + uint16_t common_info_version; + uint16_t common_info_size; + uint32_t common_info_offset; + uint32_t num_inputs; + uint32_t input_info_version; + uint32_t input_info_size; + uint32_t input_info_offset; + uint64_t data; +}; + +#define CAM_ISP_ACQUIRE_COMMON_VER0 0x1000 + +#define CAM_ISP_ACQUIRE_COMMON_SIZE_VER0 0x0 + +#define CAM_ISP_ACQUIRE_INPUT_VER0 0x2000 + +#define CAM_ISP_ACQUIRE_OUT_VER0 0x3000 + +/** + * struct cam_isp_init_frame_drop_config - init frame drop configuration + * + * @init_frame_drop: Initial number of frames needs to drop + */ + +struct cam_isp_init_frame_drop_config { + uint32_t init_frame_drop; +} __attribute__((packed)); + +#endif /* __UAPI_CAM_ISP_H__ */ diff --git a/include/uapi/media/cam_isp_ife.h b/include/uapi/media/cam_isp_ife.h new file mode 100644 index 000000000000..7e9ba62c507f --- /dev/null +++ b/include/uapi/media/cam_isp_ife.h @@ -0,0 +1,44 @@ +#ifndef __UAPI_CAM_ISP_IFE_H__ +#define __UAPI_CAM_ISP_IFE_H__ + +/* IFE output port resource type (global unique)*/ +#define CAM_ISP_IFE_OUT_RES_BASE 0x3000 + +#define CAM_ISP_IFE_OUT_RES_FULL (CAM_ISP_IFE_OUT_RES_BASE + 0) +#define CAM_ISP_IFE_OUT_RES_DS4 (CAM_ISP_IFE_OUT_RES_BASE + 1) +#define CAM_ISP_IFE_OUT_RES_DS16 (CAM_ISP_IFE_OUT_RES_BASE + 2) +#define CAM_ISP_IFE_OUT_RES_RAW_DUMP (CAM_ISP_IFE_OUT_RES_BASE + 3) +#define CAM_ISP_IFE_OUT_RES_FD (CAM_ISP_IFE_OUT_RES_BASE + 4) +#define CAM_ISP_IFE_OUT_RES_PDAF (CAM_ISP_IFE_OUT_RES_BASE + 5) +#define CAM_ISP_IFE_OUT_RES_RDI_0 (CAM_ISP_IFE_OUT_RES_BASE + 6) +#define CAM_ISP_IFE_OUT_RES_RDI_1 (CAM_ISP_IFE_OUT_RES_BASE + 7) +#define CAM_ISP_IFE_OUT_RES_RDI_2 (CAM_ISP_IFE_OUT_RES_BASE + 8) +#define CAM_ISP_IFE_OUT_RES_RDI_3 (CAM_ISP_IFE_OUT_RES_BASE + 9) +#define CAM_ISP_IFE_OUT_RES_STATS_HDR_BE (CAM_ISP_IFE_OUT_RES_BASE + 10) +#define CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST (CAM_ISP_IFE_OUT_RES_BASE + 11) +#define CAM_ISP_IFE_OUT_RES_STATS_TL_BG (CAM_ISP_IFE_OUT_RES_BASE + 12) +#define CAM_ISP_IFE_OUT_RES_STATS_BF (CAM_ISP_IFE_OUT_RES_BASE + 13) +#define CAM_ISP_IFE_OUT_RES_STATS_AWB_BG (CAM_ISP_IFE_OUT_RES_BASE + 14) +#define CAM_ISP_IFE_OUT_RES_STATS_BHIST (CAM_ISP_IFE_OUT_RES_BASE + 15) +#define CAM_ISP_IFE_OUT_RES_STATS_RS (CAM_ISP_IFE_OUT_RES_BASE + 16) +#define CAM_ISP_IFE_OUT_RES_STATS_CS (CAM_ISP_IFE_OUT_RES_BASE + 17) +#define CAM_ISP_IFE_OUT_RES_STATS_IHIST (CAM_ISP_IFE_OUT_RES_BASE + 18) +#define CAM_ISP_IFE_OUT_RES_FULL_DISP (CAM_ISP_IFE_OUT_RES_BASE + 19) +#define CAM_ISP_IFE_OUT_RES_DS4_DISP (CAM_ISP_IFE_OUT_RES_BASE + 20) +#define CAM_ISP_IFE_OUT_RES_DS16_DISP (CAM_ISP_IFE_OUT_RES_BASE + 21) +#define CAM_ISP_IFE_OUT_RES_2PD (CAM_ISP_IFE_OUT_RES_BASE + 22) +#define CAM_ISP_IFE_OUT_RES_RDI_RD (CAM_ISP_IFE_OUT_RES_BASE + 23) +#define CAM_ISP_IFE_OUT_RES_MAX (CAM_ISP_IFE_OUT_RES_BASE + 24) + +/*IFE input port resource type (global unique) */ +#define CAM_ISP_IFE_IN_RES_BASE 0x4000 + +#define CAM_ISP_IFE_IN_RES_TPG (CAM_ISP_IFE_IN_RES_BASE + 0) +#define CAM_ISP_IFE_IN_RES_PHY_0 (CAM_ISP_IFE_IN_RES_BASE + 1) +#define CAM_ISP_IFE_IN_RES_PHY_1 (CAM_ISP_IFE_IN_RES_BASE + 2) +#define CAM_ISP_IFE_IN_RES_PHY_2 (CAM_ISP_IFE_IN_RES_BASE + 3) +#define CAM_ISP_IFE_IN_RES_PHY_3 (CAM_ISP_IFE_IN_RES_BASE + 4) +#define CAM_ISP_IFE_IN_RES_RD (CAM_ISP_IFE_IN_RES_BASE + 5) +#define CAM_ISP_IFE_IN_RES_MAX (CAM_ISP_IFE_IN_RES_BASE + 6) + +#endif /* __UAPI_CAM_ISP_IFE_H__ */ diff --git a/include/uapi/media/cam_isp_vfe.h b/include/uapi/media/cam_isp_vfe.h new file mode 100644 index 000000000000..e48db2f98d91 --- /dev/null +++ b/include/uapi/media/cam_isp_vfe.h @@ -0,0 +1,44 @@ +#ifndef __UAPI_CAM_ISP_VFE_H__ +#define __UAPI_CAM_ISP_VFE_H__ + +/* VFE output port resource type (global unique) */ +#define CAM_ISP_VFE_OUT_RES_BASE 0x1000 + +#define CAM_ISP_VFE_OUT_RES_ENC (CAM_ISP_VFE_OUT_RES_BASE + 0) +#define CAM_ISP_VFE_OUT_RES_VIEW (CAM_ISP_VFE_OUT_RES_BASE + 1) +#define CAM_ISP_VFE_OUT_RES_VID (CAM_ISP_VFE_OUT_RES_BASE + 2) +#define CAM_ISP_VFE_OUT_RES_RDI_0 (CAM_ISP_VFE_OUT_RES_BASE + 3) +#define CAM_ISP_VFE_OUT_RES_RDI_1 (CAM_ISP_VFE_OUT_RES_BASE + 4) +#define CAM_ISP_VFE_OUT_RES_RDI_2 (CAM_ISP_VFE_OUT_RES_BASE + 5) +#define CAM_ISP_VFE_OUT_RES_RDI_3 (CAM_ISP_VFE_OUT_RES_BASE + 6) +#define CAM_ISP_VFE_OUT_RES_STATS_AEC (CAM_ISP_VFE_OUT_RES_BASE + 7) +#define CAM_ISP_VFE_OUT_RES_STATS_AF (CAM_ISP_VFE_OUT_RES_BASE + 8) +#define CAM_ISP_VFE_OUT_RES_STATS_AWB (CAM_ISP_VFE_OUT_RES_BASE + 9) +#define CAM_ISP_VFE_OUT_RES_STATS_RS (CAM_ISP_VFE_OUT_RES_BASE + 10) +#define CAM_ISP_VFE_OUT_RES_STATS_CS (CAM_ISP_VFE_OUT_RES_BASE + 11) +#define CAM_ISP_VFE_OUT_RES_STATS_IHIST (CAM_ISP_VFE_OUT_RES_BASE + 12) +#define CAM_ISP_VFE_OUT_RES_STATS_SKIN (CAM_ISP_VFE_OUT_RES_BASE + 13) +#define CAM_ISP_VFE_OUT_RES_STATS_BG (CAM_ISP_VFE_OUT_RES_BASE + 14) +#define CAM_ISP_VFE_OUT_RES_STATS_BF (CAM_ISP_VFE_OUT_RES_BASE + 15) +#define CAM_ISP_VFE_OUT_RES_STATS_BE (CAM_ISP_VFE_OUT_RES_BASE + 16) +#define CAM_ISP_VFE_OUT_RES_STATS_BHIST (CAM_ISP_VFE_OUT_RES_BASE + 17) +#define CAM_ISP_VFE_OUT_RES_STATS_BF_SCALE (CAM_ISP_VFE_OUT_RES_BASE + 18) +#define CAM_ISP_VFE_OUT_RES_STATS_HDR_BE (CAM_ISP_VFE_OUT_RES_BASE + 19) +#define CAM_ISP_VFE_OUT_RES_STATS_HDR_BHIST (CAM_ISP_VFE_OUT_RES_BASE + 20) +#define CAM_ISP_VFE_OUT_RES_STATS_AEC_BG (CAM_ISP_VFE_OUT_RES_BASE + 21) +#define CAM_ISP_VFE_OUT_RES_CAMIF_RAW (CAM_ISP_VFE_OUT_RES_BASE + 22) +#define CAM_ISP_VFE_OUT_RES_IDEAL_RAW (CAM_ISP_VFE_OUT_RES_BASE + 23) +#define CAM_ISP_VFE_OUT_RES_MAX (CAM_ISP_VFE_OUT_RES_BASE + 24) + +/* VFE input port_ resource type (global unique) */ +#define CAM_ISP_VFE_IN_RES_BASE 0x2000 + +#define CAM_ISP_VFE_IN_RES_TPG (CAM_ISP_VFE_IN_RES_BASE + 0) +#define CAM_ISP_VFE_IN_RES_PHY_0 (CAM_ISP_VFE_IN_RES_BASE + 1) +#define CAM_ISP_VFE_IN_RES_PHY_1 (CAM_ISP_VFE_IN_RES_BASE + 2) +#define CAM_ISP_VFE_IN_RES_PHY_2 (CAM_ISP_VFE_IN_RES_BASE + 3) +#define CAM_ISP_VFE_IN_RES_PHY_3 (CAM_ISP_VFE_IN_RES_BASE + 4) +#define CAM_ISP_VFE_IN_RES_FE (CAM_ISP_VFE_IN_RES_BASE + 5) +#define CAM_ISP_VFE_IN_RES_MAX (CAM_ISP_VFE_IN_RES_BASE + 6) + +#endif /* __UAPI_CAM_ISP_VFE_H__ */ diff --git a/include/uapi/media/cam_jpeg.h b/include/uapi/media/cam_jpeg.h new file mode 100644 index 000000000000..f3082f3bfe29 --- /dev/null +++ b/include/uapi/media/cam_jpeg.h @@ -0,0 +1,117 @@ +#ifndef __UAPI_CAM_JPEG_H__ +#define __UAPI_CAM_JPEG_H__ + +#include "cam_defs.h" + +/* enc, dma, cdm(enc/dma) are used in querycap */ +#define CAM_JPEG_DEV_TYPE_ENC 0 +#define CAM_JPEG_DEV_TYPE_DMA 1 +#define CAM_JPEG_DEV_TYPE_MAX 2 + +#define CAM_JPEG_NUM_DEV_PER_RES_MAX 1 + +/* definitions needed for jpeg aquire device */ +#define CAM_JPEG_RES_TYPE_ENC 0 +#define CAM_JPEG_RES_TYPE_DMA 1 +#define CAM_JPEG_RES_TYPE_MAX 2 + +/* packet opcode types */ +#define CAM_JPEG_OPCODE_ENC_UPDATE 0 +#define CAM_JPEG_OPCODE_DMA_UPDATE 1 + +/* ENC input port resource type */ +#define CAM_JPEG_ENC_INPUT_IMAGE 0x0 + +/* ENC output port resource type */ +#define CAM_JPEG_ENC_OUTPUT_IMAGE 0x1 + +#define CAM_JPEG_ENC_IO_IMAGES_MAX 0x2 + +/* DMA input port resource type */ +#define CAM_JPEG_DMA_INPUT_IMAGE 0x0 + +/* DMA output port resource type */ +#define CAM_JPEG_DMA_OUTPUT_IMAGE 0x1 + +#define CAM_JPEG_DMA_IO_IMAGES_MAX 0x2 + +#define CAM_JPEG_IMAGE_MAX 0x2 + +/** + * struct cam_jpeg_dev_ver - Device information for particular hw type + * + * This is used to get device version info of JPEG ENC, JPEG DMA + * from hardware and use this info in CAM_QUERY_CAP IOCTL + * + * @size : Size of struct passed + * @dev_type: Hardware type for the cap info(jpeg enc, jpeg dma) + * @hw_ver: Major, minor and incr values of a device version + */ +struct cam_jpeg_dev_ver { + uint32_t size; + uint32_t dev_type; + struct cam_hw_version hw_ver; +}; + +/** + * struct cam_jpeg_query_cap_cmd - JPEG query device capability payload + * + * @dev_iommu_handle: Jpeg iommu handles for secure/non secure + * modes + * @cdm_iommu_handle: Iommu handles for secure/non secure modes + * @num_enc: Number of encoder + * @num_dma: Number of dma + * @dev_ver: Returned device capability array + */ +struct cam_jpeg_query_cap_cmd { + struct cam_iommu_handle dev_iommu_handle; + struct cam_iommu_handle cdm_iommu_handle; + uint32_t num_enc; + uint32_t num_dma; + struct cam_jpeg_dev_ver dev_ver[CAM_JPEG_DEV_TYPE_MAX]; +}; + +/** + * struct cam_jpeg_res_info - JPEG output resource info + * + * @format: Format of the resource + * @width: Width in pixels + * @height: Height in lines + * @fps: Fps + */ +struct cam_jpeg_res_info { + uint32_t format; + uint32_t width; + uint32_t height; + uint32_t fps; +}; + +/** + * struct cam_jpeg_acquire_dev_info - An JPEG device info + * + * @dev_type: Device type (ENC/DMA) + * @reserved: Reserved Bytes + * @in_res: In resource info + * @in_res: Iut resource info + */ +struct cam_jpeg_acquire_dev_info { + uint32_t dev_type; + uint32_t reserved; + struct cam_jpeg_res_info in_res; + struct cam_jpeg_res_info out_res; +}; + +/** + * struct cam_jpeg_config_inout_param_info - JPEG Config time + * input output params + * + * @clk_index: Input Param- clock selection index.(-1 default) + * @output_size: Output Param - jpeg encode/dma output size in + * bytes + */ +struct cam_jpeg_config_inout_param_info { + int32_t clk_index; + int32_t output_size; +}; + +#endif /* __UAPI_CAM_JPEG_H__ */ diff --git a/include/uapi/media/cam_lrme.h b/include/uapi/media/cam_lrme.h new file mode 100644 index 000000000000..97d957835ee3 --- /dev/null +++ b/include/uapi/media/cam_lrme.h @@ -0,0 +1,65 @@ +#ifndef __UAPI_CAM_LRME_H__ +#define __UAPI_CAM_LRME_H__ + +#include "cam_defs.h" + +/* LRME Resource Types */ + +enum CAM_LRME_IO_TYPE { + CAM_LRME_IO_TYPE_TAR, + CAM_LRME_IO_TYPE_REF, + CAM_LRME_IO_TYPE_RES, + CAM_LRME_IO_TYPE_DS2, +}; + +#define CAM_LRME_INPUT_PORT_TYPE_TAR (1 << 0) +#define CAM_LRME_INPUT_PORT_TYPE_REF (1 << 1) + +#define CAM_LRME_OUTPUT_PORT_TYPE_DS2 (1 << 0) +#define CAM_LRME_OUTPUT_PORT_TYPE_RES (1 << 1) + +#define CAM_LRME_DEV_MAX 1 + + +struct cam_lrme_hw_version { + uint32_t gen; + uint32_t rev; + uint32_t step; +}; + +struct cam_lrme_dev_cap { + struct cam_lrme_hw_version clc_hw_version; + struct cam_lrme_hw_version bus_rd_hw_version; + struct cam_lrme_hw_version bus_wr_hw_version; + struct cam_lrme_hw_version top_hw_version; + struct cam_lrme_hw_version top_titan_version; +}; + +/** + * struct cam_lrme_query_cap_cmd - LRME query device capability payload + * + * @dev_iommu_handle: LRME iommu handles for secure/non secure + * modes + * @cdm_iommu_handle: Iommu handles for secure/non secure modes + * @num_devices: number of hardware devices + * @dev_caps: Returned device capability array + */ +struct cam_lrme_query_cap_cmd { + struct cam_iommu_handle device_iommu; + struct cam_iommu_handle cdm_iommu; + uint32_t num_devices; + struct cam_lrme_dev_cap dev_caps[CAM_LRME_DEV_MAX]; +}; + +struct cam_lrme_soc_info { + uint64_t clock_rate; + uint64_t bandwidth; + uint64_t reserved[4]; +}; + +struct cam_lrme_acquire_args { + struct cam_lrme_soc_info lrme_soc_info; +}; + +#endif /* __UAPI_CAM_LRME_H__ */ + diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h new file mode 100644 index 000000000000..eb1dcc6e5452 --- /dev/null +++ b/include/uapi/media/cam_req_mgr.h @@ -0,0 +1,457 @@ +#ifndef __UAPI_LINUX_CAM_REQ_MGR_H +#define __UAPI_LINUX_CAM_REQ_MGR_H + +#include +#include +#include +#include +#include + +#define CAM_REQ_MGR_VNODE_NAME "cam-req-mgr-devnode" + +#define CAM_DEVICE_TYPE_BASE (MEDIA_ENT_F_OLD_BASE) +#define CAM_VNODE_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE) +#define CAM_SENSOR_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 1) +#define CAM_IFE_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 2) +#define CAM_ICP_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 3) +#define CAM_LRME_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 4) +#define CAM_JPEG_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 5) +#define CAM_FD_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 6) +#define CAM_CPAS_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 7) +#define CAM_CSIPHY_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 8) +#define CAM_ACTUATOR_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 9) +#define CAM_CCI_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 10) +#define CAM_FLASH_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 11) +#define CAM_EEPROM_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 12) +#define CAM_OIS_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 13) +#define CAM_IRLED_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 14) + +/* cam_req_mgr hdl info */ +#define CAM_REQ_MGR_HDL_IDX_POS 8 +#define CAM_REQ_MGR_HDL_IDX_MASK ((1 << CAM_REQ_MGR_HDL_IDX_POS) - 1) +#define CAM_REQ_MGR_GET_HDL_IDX(hdl) (hdl & CAM_REQ_MGR_HDL_IDX_MASK) + +/** + * Max handles supported by cam_req_mgr + * It includes both session and device handles + */ +#define CAM_REQ_MGR_MAX_HANDLES 64 +#define CAM_REQ_MGR_MAX_HANDLES_V2 128 +#define MAX_LINKS_PER_SESSION 2 + +/* V4L event type which user space will subscribe to */ +#define V4L_EVENT_CAM_REQ_MGR_EVENT (V4L2_EVENT_PRIVATE_START + 0) + +/* Specific event ids to get notified in user space */ +#define V4L_EVENT_CAM_REQ_MGR_SOF 0 +#define V4L_EVENT_CAM_REQ_MGR_ERROR 1 +#define V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS 2 + +/* SOF Event status */ +#define CAM_REQ_MGR_SOF_EVENT_SUCCESS 0 +#define CAM_REQ_MGR_SOF_EVENT_ERROR 1 + +/* Link control operations */ +#define CAM_REQ_MGR_LINK_ACTIVATE 0 +#define CAM_REQ_MGR_LINK_DEACTIVATE 1 + +/** + * Request Manager : flush_type + * @CAM_REQ_MGR_FLUSH_TYPE_ALL: Req mgr will remove all the pending + * requests from input/processing queue. + * @CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ: Req mgr will remove only particular + * request id from input/processing queue. + * @CAM_REQ_MGR_FLUSH_TYPE_MAX: Max number of the flush type + * @opcode: CAM_REQ_MGR_FLUSH_REQ + */ +#define CAM_REQ_MGR_FLUSH_TYPE_ALL 0 +#define CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ 1 +#define CAM_REQ_MGR_FLUSH_TYPE_MAX 2 + +/** + * Request Manager : Sync Mode type + * @CAM_REQ_MGR_SYNC_MODE_NO_SYNC: Req mgr will apply non-sync mode for this + * request. + * @CAM_REQ_MGR_SYNC_MODE_SYNC: Req mgr will apply sync mode for this request. + */ +#define CAM_REQ_MGR_SYNC_MODE_NO_SYNC 0 +#define CAM_REQ_MGR_SYNC_MODE_SYNC 1 + +/** + * struct cam_req_mgr_event_data + * @session_hdl: session handle + * @link_hdl: link handle + * @frame_id: frame id + * @reserved: reserved for 64 bit aligngment + * @req_id: request id + * @tv_sec: timestamp in seconds + * @tv_usec: timestamp in micro seconds + */ +struct cam_req_mgr_event_data { + int32_t session_hdl; + int32_t link_hdl; + int32_t frame_id; + int32_t reserved; + int64_t req_id; + uint64_t tv_sec; + uint64_t tv_usec; +}; + +/** + * struct cam_req_mgr_session_info + * @session_hdl: In/Output param - session_handle + * @opcode1: CAM_REQ_MGR_CREATE_SESSION + * @opcode2: CAM_REQ_MGR_DESTROY_SESSION + */ +struct cam_req_mgr_session_info { + int32_t session_hdl; + int32_t reserved; +}; + +/** + * struct cam_req_mgr_link_info + * @session_hdl: Input param - Identifier for CSL session + * @num_devices: Input Param - Num of devices to be linked + * @dev_hdls: Input param - List of device handles to be linked + * @link_hdl: Output Param -Identifier for link + * @opcode: CAM_REQ_MGR_LINK + */ +struct cam_req_mgr_link_info { + int32_t session_hdl; + uint32_t num_devices; + int32_t dev_hdls[CAM_REQ_MGR_MAX_HANDLES]; + int32_t link_hdl; +}; + +struct cam_req_mgr_link_info_v2 { + int32_t session_hdl; + uint32_t num_devices; + int32_t dev_hdls[CAM_REQ_MGR_MAX_HANDLES_V2]; + int32_t link_hdl; +}; + +struct cam_req_mgr_ver_info { + uint32_t version; + union { + struct cam_req_mgr_link_info link_info_v1; + struct cam_req_mgr_link_info_v2 link_info_v2; + } u; +}; +/** + * struct cam_req_mgr_unlink_info + * @session_hdl: input param - session handle + * @link_hdl: input param - link handle + * @opcode: CAM_REQ_MGR_UNLINK + */ +struct cam_req_mgr_unlink_info { + int32_t session_hdl; + int32_t link_hdl; +}; + +/** + * struct cam_req_mgr_flush_info + * @brief: User can tell drivers to flush a particular request id or + * flush all requests from its pending processing queue. Flush is a + * blocking call and driver shall ensure all requests are flushed + * before returning. + * @session_hdl: Input param - Identifier for CSL session + * @link_hdl: Input Param -Identifier for link + * @flush_type: User can cancel a particular req id or can flush + * all requests in queue + * @reserved: reserved for 64 bit aligngment + * @req_id: field is valid only if flush type is cancel request + * for flush all this field value is not considered. + * @opcode: CAM_REQ_MGR_FLUSH_REQ + */ +struct cam_req_mgr_flush_info { + int32_t session_hdl; + int32_t link_hdl; + uint32_t flush_type; + uint32_t reserved; + int64_t req_id; +}; + +/** struct cam_req_mgr_sched_info + * @session_hdl: Input param - Identifier for CSL session + * @link_hdl: Input Param -Identifier for link + * inluding itself. + * @bubble_enable: Input Param - Cam req mgr will do bubble recovery if this + * flag is set. + * @sync_mode: Type of Sync mode for this request + * @req_id: Input Param - Request Id from which all requests will be flushed + */ +struct cam_req_mgr_sched_request { + int32_t session_hdl; + int32_t link_hdl; + int32_t bubble_enable; + int32_t sync_mode; + int64_t req_id; +}; + +/** + * struct cam_req_mgr_sync_mode + * @session_hdl: Input param - Identifier for CSL session + * @sync_mode: Input Param - Type of sync mode + * @num_links: Input Param - Num of links in sync mode (Valid only + * when sync_mode is one of SYNC enabled modes) + * @link_hdls: Input Param - Array of link handles to be in sync mode + * (Valid only when sync_mode is one of SYNC + * enabled modes) + * @master_link_hdl: Input Param - To dictate which link's SOF drives system + * (Valid only when sync_mode is one of SYNC + * enabled modes) + * + * @opcode: CAM_REQ_MGR_SYNC_MODE + */ +struct cam_req_mgr_sync_mode { + int32_t session_hdl; + int32_t sync_mode; + int32_t num_links; + int32_t link_hdls[MAX_LINKS_PER_SESSION]; + int32_t master_link_hdl; + int32_t reserved; +}; + +/** + * struct cam_req_mgr_link_control + * @ops: Link operations: activate/deactive + * @session_hdl: Input param - Identifier for CSL session + * @num_links: Input Param - Num of links + * @reserved: reserved field + * @link_hdls: Input Param - Links to be activated/deactivated + * + * @opcode: CAM_REQ_MGR_LINK_CONTROL + */ +struct cam_req_mgr_link_control { + int32_t ops; + int32_t session_hdl; + int32_t num_links; + int32_t reserved; + int32_t link_hdls[MAX_LINKS_PER_SESSION]; +}; + +/** + * cam_req_mgr specific opcode ids + */ +#define CAM_REQ_MGR_CREATE_DEV_NODES (CAM_COMMON_OPCODE_MAX + 1) +#define CAM_REQ_MGR_CREATE_SESSION (CAM_COMMON_OPCODE_MAX + 2) +#define CAM_REQ_MGR_DESTROY_SESSION (CAM_COMMON_OPCODE_MAX + 3) +#define CAM_REQ_MGR_LINK (CAM_COMMON_OPCODE_MAX + 4) +#define CAM_REQ_MGR_UNLINK (CAM_COMMON_OPCODE_MAX + 5) +#define CAM_REQ_MGR_SCHED_REQ (CAM_COMMON_OPCODE_MAX + 6) +#define CAM_REQ_MGR_FLUSH_REQ (CAM_COMMON_OPCODE_MAX + 7) +#define CAM_REQ_MGR_SYNC_MODE (CAM_COMMON_OPCODE_MAX + 8) +#define CAM_REQ_MGR_ALLOC_BUF (CAM_COMMON_OPCODE_MAX + 9) +#define CAM_REQ_MGR_MAP_BUF (CAM_COMMON_OPCODE_MAX + 10) +#define CAM_REQ_MGR_RELEASE_BUF (CAM_COMMON_OPCODE_MAX + 11) +#define CAM_REQ_MGR_CACHE_OPS (CAM_COMMON_OPCODE_MAX + 12) +#define CAM_REQ_MGR_LINK_CONTROL (CAM_COMMON_OPCODE_MAX + 13) +#define CAM_REQ_MGR_LINK_V2 (CAM_COMMON_OPCODE_MAX + 14) +/* end of cam_req_mgr opcodes */ + +#define CAM_MEM_FLAG_HW_READ_WRITE (1<<0) +#define CAM_MEM_FLAG_HW_READ_ONLY (1<<1) +#define CAM_MEM_FLAG_HW_WRITE_ONLY (1<<2) +#define CAM_MEM_FLAG_KMD_ACCESS (1<<3) +#define CAM_MEM_FLAG_UMD_ACCESS (1<<4) +#define CAM_MEM_FLAG_PROTECTED_MODE (1<<5) +#define CAM_MEM_FLAG_CMD_BUF_TYPE (1<<6) +#define CAM_MEM_FLAG_PIXEL_BUF_TYPE (1<<7) +#define CAM_MEM_FLAG_STATS_BUF_TYPE (1<<8) +#define CAM_MEM_FLAG_PACKET_BUF_TYPE (1<<9) +#define CAM_MEM_FLAG_CACHE (1<<10) +#define CAM_MEM_FLAG_HW_SHARED_ACCESS (1<<11) +#define CAM_MEM_FLAG_CDSP_OUTPUT (1<<12) +#define CAM_MEM_FLAG_DISABLE_DELAYED_UNMAP (1<<13) + +#define CAM_MEM_MMU_MAX_HANDLE 16 + +/* Maximum allowed buffers in existence */ +#define CAM_MEM_BUFQ_MAX 1024 + +#define CAM_MEM_MGR_SECURE_BIT_POS 15 +#define CAM_MEM_MGR_HDL_IDX_SIZE 15 +#define CAM_MEM_MGR_HDL_FD_SIZE 16 +#define CAM_MEM_MGR_HDL_IDX_END_POS 16 +#define CAM_MEM_MGR_HDL_FD_END_POS 32 + +#define CAM_MEM_MGR_HDL_IDX_MASK ((1 << CAM_MEM_MGR_HDL_IDX_SIZE) - 1) + +#define GET_MEM_HANDLE(idx, fd) \ + ((idx & CAM_MEM_MGR_HDL_IDX_MASK) | \ + (fd << (CAM_MEM_MGR_HDL_FD_END_POS - CAM_MEM_MGR_HDL_FD_SIZE))) \ + +#define GET_FD_FROM_HANDLE(hdl) \ + (hdl >> (CAM_MEM_MGR_HDL_FD_END_POS - CAM_MEM_MGR_HDL_FD_SIZE)) \ + +#define CAM_MEM_MGR_GET_HDL_IDX(hdl) (hdl & CAM_MEM_MGR_HDL_IDX_MASK) + +#define CAM_MEM_MGR_SET_SECURE_HDL(hdl, flag) \ + ((flag) ? (hdl |= (1 << CAM_MEM_MGR_SECURE_BIT_POS)) : \ + ((hdl) &= ~(1 << CAM_MEM_MGR_SECURE_BIT_POS))) + +#define CAM_MEM_MGR_IS_SECURE_HDL(hdl) \ + (((hdl) & \ + (1<> CAM_MEM_MGR_SECURE_BIT_POS) + +/** + * memory allocation type + */ +#define CAM_MEM_DMA_NONE 0 +#define CAM_MEM_DMA_BIDIRECTIONAL 1 +#define CAM_MEM_DMA_TO_DEVICE 2 +#define CAM_MEM_DMA_FROM_DEVICE 3 + + +/** + * memory cache operation + */ +#define CAM_MEM_CLEAN_CACHE 1 +#define CAM_MEM_INV_CACHE 2 +#define CAM_MEM_CLEAN_INV_CACHE 3 + + +/** + * struct cam_mem_alloc_out_params + * @buf_handle: buffer handle + * @fd: output buffer file descriptor + * @vaddr: virtual address pointer + */ +struct cam_mem_alloc_out_params { + uint32_t buf_handle; + int32_t fd; + uint64_t vaddr; +}; + +/** + * struct cam_mem_map_out_params + * @buf_handle: buffer handle + * @reserved: reserved for future + * @vaddr: virtual address pointer + */ +struct cam_mem_map_out_params { + uint32_t buf_handle; + uint32_t reserved; + uint64_t vaddr; +}; + +/** + * struct cam_mem_mgr_alloc_cmd + * @len: size of buffer to allocate + * @align: alignment of the buffer + * @mmu_hdls: array of mmu handles + * @num_hdl: number of handles + * @flags: flags of the buffer + * @out: out params + */ +/* CAM_REQ_MGR_ALLOC_BUF */ +struct cam_mem_mgr_alloc_cmd { + uint64_t len; + uint64_t align; + int32_t mmu_hdls[CAM_MEM_MMU_MAX_HANDLE]; + uint32_t num_hdl; + uint32_t flags; + struct cam_mem_alloc_out_params out; +}; + +/** + * struct cam_mem_mgr_map_cmd + * @mmu_hdls: array of mmu handles + * @num_hdl: number of handles + * @flags: flags of the buffer + * @fd: output buffer file descriptor + * @reserved: reserved field + * @out: out params + */ + +/* CAM_REQ_MGR_MAP_BUF */ +struct cam_mem_mgr_map_cmd { + int32_t mmu_hdls[CAM_MEM_MMU_MAX_HANDLE]; + uint32_t num_hdl; + uint32_t flags; + int32_t fd; + uint32_t reserved; + struct cam_mem_map_out_params out; +}; + +/** + * struct cam_mem_mgr_map_cmd + * @buf_handle: buffer handle + * @reserved: reserved field + */ +/* CAM_REQ_MGR_RELEASE_BUF */ +struct cam_mem_mgr_release_cmd { + int32_t buf_handle; + uint32_t reserved; +}; + +/** + * struct cam_mem_mgr_map_cmd + * @buf_handle: buffer handle + * @ops: cache operations + */ +/* CAM_REQ_MGR_CACHE_OPS */ +struct cam_mem_cache_ops_cmd { + int32_t buf_handle; + uint32_t mem_cache_ops; +}; + +/** + * Request Manager : error message type + * @CAM_REQ_MGR_ERROR_TYPE_DEVICE: Device error message, fatal to session + * @CAM_REQ_MGR_ERROR_TYPE_REQUEST: Error on a single request, not fatal + * @CAM_REQ_MGR_ERROR_TYPE_BUFFER: Buffer was not filled, not fatal + * @CAM_REQ_MGR_ERROR_TYPE_RECOVERY: Fatal error, can be recovered + */ +#define CAM_REQ_MGR_ERROR_TYPE_DEVICE 0 +#define CAM_REQ_MGR_ERROR_TYPE_REQUEST 1 +#define CAM_REQ_MGR_ERROR_TYPE_BUFFER 2 +#define CAM_REQ_MGR_ERROR_TYPE_RECOVERY 3 + +/** + * struct cam_req_mgr_error_msg + * @error_type: type of error + * @request_id: request id of frame + * @device_hdl: device handle + * @linke_hdl: link_hdl + * @resource_size: size of the resource + */ +struct cam_req_mgr_error_msg { + uint32_t error_type; + uint32_t request_id; + int32_t device_hdl; + int32_t link_hdl; + uint64_t resource_size; +}; + +/** + * struct cam_req_mgr_frame_msg + * @request_id: request id of the frame + * @frame_id: frame id of the frame + * @timestamp: timestamp of the frame + * @link_hdl: link handle associated with this message + * @sof_status: sof status success or fail + */ +struct cam_req_mgr_frame_msg { + uint64_t request_id; + uint64_t frame_id; + uint64_t timestamp; + int32_t link_hdl; + uint32_t sof_status; +}; + +/** + * struct cam_req_mgr_message + * @session_hdl: session to which the frame belongs to + * @reserved: reserved field + * @u: union which can either be error or frame message + */ +struct cam_req_mgr_message { + int32_t session_hdl; + int32_t reserved; + union { + struct cam_req_mgr_error_msg err_msg; + struct cam_req_mgr_frame_msg frame_msg; + } u; +}; +#endif /* __UAPI_LINUX_CAM_REQ_MGR_H */ diff --git a/include/uapi/media/cam_sensor.h b/include/uapi/media/cam_sensor.h new file mode 100644 index 000000000000..e5c7605616c5 --- /dev/null +++ b/include/uapi/media/cam_sensor.h @@ -0,0 +1,505 @@ +#ifndef __UAPI_CAM_SENSOR_H__ +#define __UAPI_CAM_SENSOR_H__ + +#include +#include +#include + +#define CAM_SENSOR_PROBE_CMD (CAM_COMMON_OPCODE_MAX + 1) +#define CAM_FLASH_MAX_LED_TRIGGERS 3 +#define MAX_OIS_NAME_SIZE 32 +#define CAM_CSIPHY_SECURE_MODE_ENABLED 1 +#define CAM_IR_LED_SUPPORTED +/** + * struct cam_sensor_query_cap - capabilities info for sensor + * + * @slot_info : Indicates about the slotId or cell Index + * @secure_camera : Camera is in secure/Non-secure mode + * @pos_pitch : Sensor position pitch + * @pos_roll : Sensor position roll + * @pos_yaw : Sensor position yaw + * @actuator_slot_id : Actuator slot id which connected to sensor + * @eeprom_slot_id : EEPROM slot id which connected to sensor + * @ois_slot_id : OIS slot id which connected to sensor + * @flash_slot_id : Flash slot id which connected to sensor + * @csiphy_slot_id : CSIphy slot id which connected to sensor + * @irled_slot_id : IRLED slot id which connected to sensor + * + */ +struct cam_sensor_query_cap { + uint32_t slot_info; + uint32_t secure_camera; + uint32_t pos_pitch; + uint32_t pos_roll; + uint32_t pos_yaw; + uint32_t actuator_slot_id; + uint32_t eeprom_slot_id; + uint32_t ois_slot_id; + uint32_t flash_slot_id; + uint32_t csiphy_slot_id; + uint32_t ir_led_slot_id; +} __attribute__((packed)); + +/** + * struct cam_csiphy_query_cap - capabilities info for csiphy + * + * @slot_info : Indicates about the slotId or cell Index + * @version : CSIphy version + * @clk lane : Of the 5 lanes, informs lane configured + * as clock lane + * @reserved + */ +struct cam_csiphy_query_cap { + uint32_t slot_info; + uint32_t version; + uint32_t clk_lane; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct cam_actuator_query_cap - capabilities info for actuator + * + * @slot_info : Indicates about the slotId or cell Index + * @reserved + */ +struct cam_actuator_query_cap { + uint32_t slot_info; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct cam_eeprom_query_cap_t - capabilities info for eeprom + * + * @slot_info : Indicates about the slotId or cell Index + * @eeprom_kernel_probe : Indicates about the kernel or userspace probe + */ +struct cam_eeprom_query_cap_t { + uint32_t slot_info; + uint16_t eeprom_kernel_probe; + uint16_t reserved; +} __attribute__((packed)); + +/** + * struct cam_ois_query_cap_t - capabilities info for ois + * + * @slot_info : Indicates about the slotId or cell Index + */ +struct cam_ois_query_cap_t { + uint32_t slot_info; + uint16_t reserved; +} __attribute__((packed)); + +/** + * struct cam_cmd_i2c_info - Contains slave I2C related info + * + * @slave_addr : Slave address + * @i2c_freq_mode : 4 bits are used for I2c freq mode + * @cmd_type : Explains type of command + */ +struct cam_cmd_i2c_info { + uint16_t slave_addr; + uint8_t i2c_freq_mode; + uint8_t cmd_type; +} __attribute__((packed)); + +/** + * struct cam_ois_opcode - Contains OIS opcode + * + * @prog : OIS FW prog register address + * @coeff : OIS FW coeff register address + * @pheripheral : OIS pheripheral + * @memory : OIS memory + */ +struct cam_ois_opcode { + uint32_t prog; + uint32_t coeff; + uint32_t pheripheral; + uint32_t memory; +} __attribute__((packed)); + +/** + * struct cam_cmd_ois_info - Contains OIS slave info + * + * @slave_addr : OIS i2c slave address + * @i2c_freq_mode : i2c frequency mode + * @cmd_type : Explains type of command + * @ois_fw_flag : indicates if fw is present or not + * @is_ois_calib : indicates the calibration data is available + * @ois_name : OIS name + * @opcode : opcode + */ +struct cam_cmd_ois_info { + uint16_t slave_addr; + uint8_t i2c_freq_mode; + uint8_t cmd_type; + uint8_t ois_fw_flag; + uint8_t is_ois_calib; + char ois_name[MAX_OIS_NAME_SIZE]; + struct cam_ois_opcode opcode; +} __attribute__((packed)); + +/** + * struct cam_cmd_probe - Contains sensor slave info + * + * @data_type : Slave register data type + * @addr_type : Slave register address type + * @op_code : Don't Care + * @cmd_type : Explains type of command + * @reg_addr : Slave register address + * @expected_data : Data expected at slave register address + * @data_mask : Data mask if only few bits are valid + * @camera_id : Indicates the slot to which camera + * needs to be probed + * @reserved + */ +struct cam_cmd_probe { + uint8_t data_type; + uint8_t addr_type; + uint8_t op_code; + uint8_t cmd_type; + uint32_t reg_addr; + uint32_t expected_data; + uint32_t data_mask; + uint16_t camera_id; + uint16_t reserved; +} __attribute__((packed)); + +/** + * struct cam_power_settings - Contains sensor power setting info + * + * @power_seq_type : Type of power sequence + * @reserved + * @config_val_low : Lower 32 bit value configuration value + * @config_val_high : Higher 32 bit value configuration value + * + */ +struct cam_power_settings { + uint16_t power_seq_type; + uint16_t reserved; + uint32_t config_val_low; + uint32_t config_val_high; +} __attribute__((packed)); + +/** + * struct cam_cmd_power - Explains about the power settings + * + * @count : Number of power settings follows + * @reserved + * @cmd_type : Explains type of command + * @power_settings : Contains power setting info + */ +struct cam_cmd_power { + uint16_t count; + uint8_t reserved; + uint8_t cmd_type; + struct cam_power_settings power_settings[1]; +} __attribute__((packed)); + +/** + * struct i2c_rdwr_header - header of READ/WRITE I2C command + * + * @ count : Number of registers / data / reg-data pairs + * @ op_code : Operation code + * @ cmd_type : Command buffer type + * @ data_type : I2C data type + * @ addr_type : I2C address type + * @ reserved + */ +struct i2c_rdwr_header { + uint16_t count; + uint8_t op_code; + uint8_t cmd_type; + uint8_t data_type; + uint8_t addr_type; + uint16_t reserved; +} __attribute__((packed)); + +/** + * struct i2c_random_wr_payload - payload for I2C random write + * + * @ reg_addr : Register address + * @ reg_data : Register data + * + */ +struct i2c_random_wr_payload { + uint32_t reg_addr; + uint32_t reg_data; +} __attribute__((packed)); + +/** + * struct cam_cmd_i2c_random_wr - I2C random write command + * @ header : header of READ/WRITE I2C command + * @ random_wr_payload : payload for I2C random write + */ +struct cam_cmd_i2c_random_wr { + struct i2c_rdwr_header header; + struct i2c_random_wr_payload random_wr_payload[1]; +} __attribute__((packed)); + +/** + * struct cam_cmd_read - I2C read command + * @ reg_data : Register data + * @ reserved + */ +struct cam_cmd_read { + uint32_t reg_data; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct cam_cmd_i2c_continuous_wr - I2C continuous write command + * @ header : header of READ/WRITE I2C command + * @ reg_addr : Register address + * @ data_read : I2C read command + */ +struct cam_cmd_i2c_continuous_wr { + struct i2c_rdwr_header header; + uint32_t reg_addr; + struct cam_cmd_read data_read[1]; +} __attribute__((packed)); + +/** + * struct cam_cmd_i2c_random_rd - I2C random read command + * @ header : header of READ/WRITE I2C command + * @ data_read : I2C read command + */ +struct cam_cmd_i2c_random_rd { + struct i2c_rdwr_header header; + struct cam_cmd_read data_read[1]; +} __attribute__((packed)); + +/** + * struct cam_cmd_i2c_continuous_rd - I2C continuous continuous read command + * @ header : header of READ/WRITE I2C command + * @ reg_addr : Register address + * + */ +struct cam_cmd_i2c_continuous_rd { + struct i2c_rdwr_header header; + uint32_t reg_addr; +} __attribute__((packed)); + +/** + * struct cam_cmd_conditional_wait - Conditional wait command + * @data_type : Data type + * @addr_type : Address type + * @op_code : Opcode + * @cmd_type : Explains type of command + * @timeout : Timeout for retries + * @reserved + * @reg_addr : Register Address + * @reg_data : Register data + * @data_mask : Data mask if only few bits are valid + * @camera_id : Indicates the slot to which camera + * needs to be probed + * + */ +struct cam_cmd_conditional_wait { + uint8_t data_type; + uint8_t addr_type; + uint8_t op_code; + uint8_t cmd_type; + uint16_t timeout; + uint16_t reserved; + uint32_t reg_addr; + uint32_t reg_data; + uint32_t data_mask; +} __attribute__((packed)); + +/** + * struct cam_cmd_unconditional_wait - Un-conditional wait command + * @delay : Delay + * @op_code : Opcode + * @cmd_type : Explains type of command + */ +struct cam_cmd_unconditional_wait { + int16_t delay; + uint8_t op_code; + uint8_t cmd_type; +} __attribute__((packed)); + +/** + * cam_csiphy_info: Provides cmdbuffer structre + * @lane_mask : Lane mask details + * @lane_assign : Lane sensor will be using + * @csiphy_3phase : Total number of lanes + * @combo_mode : Info regarding combo_mode is enable / disable + * @lane_cnt : Total number of lanes + * @secure_mode : Secure mode flag to enable / disable + * @3phase : Details whether 3Phase / 2Phase operation + * @settle_time : Settling time in ms + * @data_rate : Data rate + * + */ +struct cam_csiphy_info { + uint16_t lane_mask; + uint16_t lane_assign; + uint8_t csiphy_3phase; + uint8_t combo_mode; + uint8_t lane_cnt; + uint8_t secure_mode; + uint64_t settle_time; + uint64_t data_rate; +} __attribute__((packed)); + +/** + * cam_csiphy_acquire_dev_info : Information needed for + * csiphy at the time of acquire + * @combo_mode : Indicates the device mode of operation + * @reserved + * + */ +struct cam_csiphy_acquire_dev_info { + uint32_t combo_mode; + uint32_t reserved; +} __attribute__((packed)); + +/** + * cam_sensor_acquire_dev : Updates sensor acuire cmd + * @device_handle : Updates device handle + * @session_handle : Session handle for acquiring device + * @handle_type : Resource handle type + * @reserved + * @info_handle : Handle to additional info + * needed for sensor sub modules + * + */ +struct cam_sensor_acquire_dev { + uint32_t session_handle; + uint32_t device_handle; + uint32_t handle_type; + uint32_t reserved; + uint64_t info_handle; +} __attribute__((packed)); + +/** + * cam_sensor_streamon_dev : StreamOn command for the sensor + * @session_handle : Session handle for acquiring device + * @device_handle : Updates device handle + * @handle_type : Resource handle type + * @reserved + * @info_handle : Information Needed at the time of streamOn + * + */ +struct cam_sensor_streamon_dev { + uint32_t session_handle; + uint32_t device_handle; + uint32_t handle_type; + uint32_t reserved; + uint64_t info_handle; +} __attribute__((packed)); + +/** + * struct cam_flash_init : Init command for the flash + * @flash_type : flash hw type + * @reserved + * @cmd_type : command buffer type + */ +struct cam_flash_init { + uint8_t flash_type; + uint16_t reserved; + uint8_t cmd_type; +} __attribute__((packed)); + +/** + * struct cam_flash_set_rer : RedEyeReduction command buffer + * + * @count : Number of flash leds + * @opcode : Command buffer opcode + * CAM_FLASH_FIRE_RER + * @cmd_type : command buffer operation type + * @num_iteration : Number of led turn on/off sequence + * @reserved + * @led_on_delay_ms : flash led turn on time in ms + * @led_off_delay_ms : flash led turn off time in ms + * @led_current_ma : flash led current in ma + * + */ +struct cam_flash_set_rer { + uint16_t count; + uint8_t opcode; + uint8_t cmd_type; + uint16_t num_iteration; + uint16_t reserved; + uint32_t led_on_delay_ms; + uint32_t led_off_delay_ms; + uint32_t led_current_ma[CAM_FLASH_MAX_LED_TRIGGERS]; +} __attribute__((packed)); + +/** + * struct cam_flash_set_on_off : led turn on/off command buffer + * + * @count : Number of Flash leds + * @opcode : command buffer opcodes + * CAM_FLASH_FIRE_LOW + * CAM_FLASH_FIRE_HIGH + * CAM_FLASH_OFF + * @cmd_type : command buffer operation type + * @led_current_ma : flash led current in ma + * + */ +struct cam_flash_set_on_off { + uint16_t count; + uint8_t opcode; + uint8_t cmd_type; + uint32_t led_current_ma[CAM_FLASH_MAX_LED_TRIGGERS]; +} __attribute__((packed)); + +/** + * struct cam_flash_query_curr : query current command buffer + * + * @reserved + * @opcode : command buffer opcode + * @cmd_type : command buffer operation type + * @query_current_ma : battery current in ma + * + */ +struct cam_flash_query_curr { + uint16_t reserved; + uint8_t opcode; + uint8_t cmd_type; + uint32_t query_current_ma; +} __attribute__ ((packed)); + +/** + * struct cam_flash_query_cap : capabilities info for flash + * + * @slot_info : Indicates about the slotId or cell Index + * @max_current_flash : max supported current for flash + * @max_duration_flash : max flash turn on duration + * @max_current_torch : max supported current for torch + * + */ +struct cam_flash_query_cap_info { + uint32_t slot_info; + uint32_t max_current_flash[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t max_duration_flash[CAM_FLASH_MAX_LED_TRIGGERS]; + uint32_t max_current_torch[CAM_FLASH_MAX_LED_TRIGGERS]; +} __attribute__ ((packed)); + +/** + * struct cam_ir_led_query_cap : capabilities info for ir_led + * + * @slot_info : Indicates about the slotId or cell Index + * + */ +struct cam_ir_led_query_cap_info { + uint32_t slot_info; +} __attribute__ ((packed)); + +/** + * struct cam_ir_ledset_on_off : led turn on/off command buffer + * + * @opcode : command buffer opcodes + * @cmd_type : command buffer operation type + * @ir_led_intensity : ir led intensity level + * + */ +struct cam_ir_led_set_on_off { + uint16_t reserved; + uint8_t opcode; + uint8_t cmd_type; + uint32_t ir_led_intensity; +} __attribute__((packed)); + +#endif diff --git a/include/uapi/media/cam_sync.h b/include/uapi/media/cam_sync.h new file mode 100644 index 000000000000..4a8781fc823d --- /dev/null +++ b/include/uapi/media/cam_sync.h @@ -0,0 +1,134 @@ +#ifndef __UAPI_CAM_SYNC_H__ +#define __UAPI_CAM_SYNC_H__ + +#include +#include +#include +#include + +#define CAM_SYNC_DEVICE_NAME "cam_sync_device" + +/* V4L event which user space will subscribe to */ +#define CAM_SYNC_V4L_EVENT (V4L2_EVENT_PRIVATE_START + 0) + +/* Specific event ids to get notified in user space */ +#define CAM_SYNC_V4L_EVENT_ID_CB_TRIG 0 + +/* Size of opaque payload sent to kernel for safekeeping until signal time */ +#define CAM_SYNC_USER_PAYLOAD_SIZE 2 + +/* Device type for sync device needed for device discovery */ +#define CAM_SYNC_DEVICE_TYPE (MEDIA_ENT_F_OLD_BASE) + +#define CAM_SYNC_GET_PAYLOAD_PTR(ev, type) \ + (type *)((char *)ev.u.data + sizeof(struct cam_sync_ev_header)) + +#define CAM_SYNC_GET_HEADER_PTR(ev) \ + ((struct cam_sync_ev_header *)ev.u.data) + +#define CAM_SYNC_STATE_INVALID 0 +#define CAM_SYNC_STATE_ACTIVE 1 +#define CAM_SYNC_STATE_SIGNALED_SUCCESS 2 +#define CAM_SYNC_STATE_SIGNALED_ERROR 3 + +/** + * struct cam_sync_ev_header - Event header for sync event notification + * + * @sync_obj: Sync object + * @status: Status of the object + */ +struct cam_sync_ev_header { + int32_t sync_obj; + int32_t status; +}; + +/** + * struct cam_sync_info - Sync object creation information + * + * @name: Optional string representation of the sync object + * @sync_obj: Sync object returned after creation in kernel + */ +struct cam_sync_info { + char name[64]; + int32_t sync_obj; +}; + +/** + * struct cam_sync_signal - Sync object signaling struct + * + * @sync_obj: Sync object to be signaled + * @sync_state: State of the sync object to which it should be signaled + */ +struct cam_sync_signal { + int32_t sync_obj; + uint32_t sync_state; +}; + +/** + * struct cam_sync_merge - Merge information for sync objects + * + * @sync_objs: Pointer to sync objects + * @num_objs: Number of objects in the array + * @merged: Merged sync object + */ +struct cam_sync_merge { + __u64 sync_objs; + uint32_t num_objs; + int32_t merged; +}; + +/** + * struct cam_sync_userpayload_info - Payload info from user space + * + * @sync_obj: Sync object for which payload has to be registered for + * @reserved: Reserved + * @payload: Pointer to user payload + */ +struct cam_sync_userpayload_info { + int32_t sync_obj; + uint32_t reserved; + __u64 payload[CAM_SYNC_USER_PAYLOAD_SIZE]; +}; + +/** + * struct cam_sync_wait - Sync object wait information + * + * @sync_obj: Sync object to wait on + * @reserved: Reserved + * @timeout_ms: Timeout in milliseconds + */ +struct cam_sync_wait { + int32_t sync_obj; + uint32_t reserved; + uint64_t timeout_ms; +}; + +/** + * struct cam_private_ioctl_arg - Sync driver ioctl argument + * + * @id: IOCTL command id + * @size: Size of command payload + * @result: Result of command execution + * @reserved: Reserved + * @ioctl_ptr: Pointer to user data + */ +struct cam_private_ioctl_arg { + __u32 id; + __u32 size; + __u32 result; + __u32 reserved; + __u64 ioctl_ptr; +}; + +#define CAM_PRIVATE_IOCTL_CMD \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct cam_private_ioctl_arg) + +#define CAM_SYNC_CREATE 0 +#define CAM_SYNC_DESTROY 1 +#define CAM_SYNC_SIGNAL 2 +#define CAM_SYNC_MERGE 3 +#define CAM_SYNC_REGISTER_PAYLOAD 4 +#define CAM_SYNC_DEREGISTER_PAYLOAD 5 +#define CAM_SYNC_WAIT 6 + +#endif /* __UAPI_CAM_SYNC_H__ */ diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h index 734fee1b75f4..24e045b5f06b 100644 --- a/include/uapi/media/msm_media_info.h +++ b/include/uapi/media/msm_media_info.h @@ -2,23 +2,6 @@ #ifndef __MSM_MEDIA_INFO_H__ #define __MSM_MEDIA_INFO_H__ -#include - -#if __BITS_PER_LONG == 64 -#define NV12_STRIDE_ALIGNMENT 512 -#define NV12_SCANLINE_ALIGNMENT 512 -#else -#define NV12_STRIDE_ALIGNMENT 128 -#define NV12_SCANLINE_ALIGNMENT 32 -#endif - -#ifdef VENUS_USE_64BIT_ALIGNMENT -#undef NV12_STRIDE_ALIGNMENT -#undef NV12_SCANLINE_ALIGNMENT -#define NV12_STRIDE_ALIGNMENT 512 -#define NV12_SCANLINE_ALIGNMENT 512 -#endif - /* Width and Height should be multiple of 16 */ #define INTERLACE_WIDTH_MAX 1920 #define INTERLACE_HEIGHT_MAX 1920 @@ -34,6 +17,10 @@ #define MSM_MEDIA_ROUNDUP(__sz, __r) (((__sz) + ((__r) - 1)) / (__r)) #endif +#ifndef MSM_MEDIA_MAX +#define MSM_MEDIA_MAX(__a, __b) ((__a) > (__b)?(__a):(__b)) +#endif + enum color_fmts { /* Venus NV12: * YUV 4:2:0 image with a plane of 8 bit Y samples followed @@ -62,12 +49,14 @@ enum color_fmts { * . . . . . . . . . . . . . . . . V * . . . . . . . . . . . . . . . . --> Buffer size alignment * - * Y_Stride : Width aligned to 512 or 128 - * UV_Stride : Width aligned to 512 or 128 - * Y_Scanlines: Height aligned to 512 or 32 - * UV_Scanlines: Height/2 aligned to 256 or 16 - * Total size = align(Y_Stride * Y_Scanlines - * + UV_Stride * UV_Scanlines, 4096) + * Y_Stride : Width aligned to 128 + * UV_Stride : Width aligned to 128 + * Y_Scanlines: Height aligned to 32 + * UV_Scanlines: Height/2 aligned to 16 + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((Y_Stride * Y_Scanlines + * + UV_Stride * UV_Scanlines + * + max(Extradata, Y_Stride * 8), 4096) */ COLOR_FMT_NV12, /* Venus NV12: @@ -132,12 +121,15 @@ enum color_fmts { * . . . . . . . . . . . . . . . . V * . . . . . . . . . . . . . . . . --> Padding & Buffer size alignment * - * Y_Stride : Width aligned to 512 or 128 - * UV_Stride : Width aligned to 512 or 128 - * Y_Scanlines: Height aligned to 512 or 32 - * UV_Scanlines: Height/2 aligned to 256 or 16 - * Total size = align(Y_Stride * Y_Scanlines - * + UV_Stride * UV_Scanlines, 4096) + * Y_Stride : Width aligned to 128 + * UV_Stride : Width aligned to 128 + * Y_Scanlines: Height aligned to 32 + * UV_Scanlines: Height/2 aligned to 16 + * View_1 begin at: 0 (zero) + * View_2 begin at: Y_Stride * Y_Scanlines + UV_Stride * UV_Scanlines + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((2*(Y_Stride * Y_Scanlines) + * + 2*(UV_Stride * UV_Scanlines) + Extradata), 4096) */ COLOR_FMT_NV21, /* @@ -217,7 +209,7 @@ enum color_fmts { * Y_Stride = align(Width, 128) * UV_Stride = align(Width, 128) * Y_Scanlines = align(Height, 32) - * UV_Scanlines = align(Height/2, 32) + * UV_Scanlines = align(Height/2, 16) * Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096) * UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096) * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) @@ -226,9 +218,11 @@ enum color_fmts { * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) + * Extradata = 8k * * Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size + - * Y_Meta_Plane_size + UV_Meta_Plane_size, 4096) + * Y_Meta_Plane_size + UV_Meta_Plane_size + * + max(Extradata, Y_Stride * 48), 4096) * * * (2) Venus NV12 UBWC Interlaced Buffer Format: @@ -402,11 +396,13 @@ enum color_fmts { * UV_BF_Meta_Scanlines = align(roundup(Half_height, UV_TileHeight), 16) * UV_BF_Meta_Plane_size = * align(UV_BF_Meta_Stride * UV_BF_Meta_Scanlines, 4096) + * Extradata = 8k * * Total size = align( Y_UBWC_TF_Plane_size + UV_UBWC_TF_Plane_size + * Y_TF_Meta_Plane_size + UV_TF_Meta_Plane_size + * Y_UBWC_BF_Plane_size + UV_UBWC_BF_Plane_size + - * Y_BF_Meta_Plane_size + UV_BF_Meta_Plane_size +, 4096) + * Y_BF_Meta_Plane_size + UV_BF_Meta_Plane_size + + * + max(Extradata, Y_TF_Stride * 48), 4096) */ COLOR_FMT_NV12_UBWC, /* Venus NV12 10-bit UBWC: @@ -479,8 +475,8 @@ enum color_fmts { * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k * * - * Y_Stride = align(Width * 4/3, 256) - * UV_Stride = align(Width * 4/3, 256) + * Y_Stride = align(Width * 4/3, 128) + * UV_Stride = align(Width * 4/3, 128) * Y_Scanlines = align(Height, 32) * UV_Scanlines = align(Height/2, 16) * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096) @@ -491,9 +487,11 @@ enum color_fmts { * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) + * Extradata = 8k * * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size + - * Y_Meta_Plane_size + UV_Meta_Plane_size, 4096) + * Y_Meta_Plane_size + UV_Meta_Plane_size + * + max(Extradata, Y_Stride * 48), 4096) */ COLOR_FMT_NV12_BPP10_UBWC, /* Venus RGBA8888 format: @@ -518,8 +516,9 @@ enum color_fmts { * RGB_Stride = align(Width * 4, 128) * RGB_Scanlines = align(Height, 32) * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096) + * Extradata = 8k * - * Total size = align(RGB_Plane_size , 4096) + * Total size = align(RGB_Plane_size + Extradata, 4096) */ COLOR_FMT_RGBA8888, /* Venus RGBA8888 UBWC format: @@ -556,15 +555,17 @@ enum color_fmts { * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k * . . . . . . . . . . . . . . . . V * - * RGB_Stride = align(Width * 4, 256) - * RGB_Scanlines = align(Height, 16) + * RGB_Stride = align(Width * 4, 128) + * RGB_Scanlines = align(Height, 32) * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096) * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64) * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16) * RGB_Meta_Plane_size = align(RGB_Meta_Stride * * RGB_Meta_Scanlines, 4096) + * Extradata = 8k * - * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096) + * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size + + * Extradata, 4096) */ COLOR_FMT_RGBA8888_UBWC, /* Venus RGBA1010102 UBWC format: @@ -608,8 +609,10 @@ enum color_fmts { * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16) * RGB_Meta_Plane_size = align(RGB_Meta_Stride * * RGB_Meta_Scanlines, 4096) + * Extradata = 8k * - * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096) + * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size + + * Extradata, 4096) */ COLOR_FMT_RGBA1010102_UBWC, /* Venus RGB565 UBWC format: @@ -646,15 +649,17 @@ enum color_fmts { * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k * . . . . . . . . . . . . . . . . V * - * RGB_Stride = align(Width * 2, 256) + * RGB_Stride = align(Width * 2, 128) * RGB_Scanlines = align(Height, 16) * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096) * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64) * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16) * RGB_Meta_Plane_size = align(RGB_Meta_Stride * * RGB_Meta_Scanlines, 4096) + * Extradata = 8k * - * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096) + * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size + + * Extradata, 4096) */ COLOR_FMT_RGB565_UBWC, /* P010 UBWC: @@ -739,9 +744,11 @@ enum color_fmts { * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) + * Extradata = 8k * * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size + - * Y_Meta_Plane_size + UV_Meta_Plane_size, 4096) + * Y_Meta_Plane_size + UV_Meta_Plane_size + * + max(Extradata, Y_Stride * 48), 4096) */ COLOR_FMT_P010_UBWC, /* Venus P010: @@ -771,12 +778,14 @@ enum color_fmts { * . . . . . . . . . . . . . . . . V * . . . . . . . . . . . . . . . . --> Buffer size alignment * - * Y_Stride : Width * 2 aligned to 256 - * UV_Stride : Width * 2 aligned to 256 + * Y_Stride : Width * 2 aligned to 128 + * UV_Stride : Width * 2 aligned to 128 * Y_Scanlines: Height aligned to 32 * UV_Scanlines: Height/2 aligned to 16 - * Total size = align(Y_Stride * Y_Scanlines - * + UV_Stride * UV_Scanlines, 4096) + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((Y_Stride * Y_Scanlines + * + UV_Stride * UV_Scanlines + * + max(Extradata, Y_Stride * 8), 4096) */ COLOR_FMT_P010, /* Venus NV12_512: @@ -816,6 +825,18 @@ enum color_fmts { COLOR_FMT_NV12_512, }; +static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height) +{ + (void)height; + (void)width; + + /* + * In the future, calculate the size based on the w/h but just + * hardcode it for now since 16K satisfies all current usecases. + */ + return 16 * 1024; +} + /* * Function arguments: * @color_fmt @@ -832,15 +853,12 @@ static inline unsigned int VENUS_Y_STRIDE(unsigned int color_fmt, goto invalid_input; switch (color_fmt) { - case COLOR_FMT_NV12: - case COLOR_FMT_NV21: - alignment = NV12_STRIDE_ALIGNMENT; - stride = MSM_MEDIA_ALIGN(width, alignment); - break; case COLOR_FMT_NV12_512: alignment = 512; stride = MSM_MEDIA_ALIGN(width, alignment); break; + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: case COLOR_FMT_NV12_128: case COLOR_FMT_NV12_UBWC: alignment = 128; @@ -852,10 +870,13 @@ static inline unsigned int VENUS_Y_STRIDE(unsigned int color_fmt, stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment); break; case COLOR_FMT_P010_UBWC: - case COLOR_FMT_P010: alignment = 256; stride = MSM_MEDIA_ALIGN(width * 2, alignment); break; + case COLOR_FMT_P010: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width*2, alignment); + break; default: break; } @@ -879,15 +900,12 @@ static inline unsigned int VENUS_UV_STRIDE(unsigned int color_fmt, goto invalid_input; switch (color_fmt) { - case COLOR_FMT_NV21: - case COLOR_FMT_NV12: - alignment = NV12_STRIDE_ALIGNMENT; - stride = MSM_MEDIA_ALIGN(width, alignment); - break; case COLOR_FMT_NV12_512: alignment = 512; stride = MSM_MEDIA_ALIGN(width, alignment); break; + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: case COLOR_FMT_NV12_128: case COLOR_FMT_NV12_UBWC: alignment = 128; @@ -899,10 +917,13 @@ static inline unsigned int VENUS_UV_STRIDE(unsigned int color_fmt, stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment); break; case COLOR_FMT_P010_UBWC: - case COLOR_FMT_P010: alignment = 256; stride = MSM_MEDIA_ALIGN(width * 2, alignment); break; + case COLOR_FMT_P010: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width*2, alignment); + break; default: break; } @@ -926,13 +947,11 @@ static inline unsigned int VENUS_Y_SCANLINES(unsigned int color_fmt, goto invalid_input; switch (color_fmt) { - case COLOR_FMT_NV12: - case COLOR_FMT_NV21: - alignment = NV12_SCANLINE_ALIGNMENT; - break; case COLOR_FMT_NV12_512: alignment = 512; break; + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: case COLOR_FMT_NV12_128: case COLOR_FMT_NV12_UBWC: case COLOR_FMT_P010: @@ -966,13 +985,11 @@ static inline unsigned int VENUS_UV_SCANLINES(unsigned int color_fmt, goto invalid_input; switch (color_fmt) { - case COLOR_FMT_NV21: - case COLOR_FMT_NV12: - alignment = NV12_SCANLINE_ALIGNMENT/2; - break; case COLOR_FMT_NV12_512: alignment = 256; break; + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: case COLOR_FMT_NV12_128: case COLOR_FMT_NV12_BPP10_UBWC: case COLOR_FMT_P010_UBWC: @@ -1248,7 +1265,9 @@ static inline unsigned int VENUS_RGB_META_SCANLINES(unsigned int color_fmt, static inline unsigned int VENUS_BUFFER_SIZE(unsigned int color_fmt, unsigned int width, unsigned int height) { - unsigned int size = 0; + const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height); + unsigned int uv_alignment = 0, size = 0; + unsigned int w_alignment = 512; unsigned int y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0; @@ -1272,54 +1291,62 @@ static inline unsigned int VENUS_BUFFER_SIZE(unsigned int color_fmt, switch (color_fmt) { case COLOR_FMT_NV21: case COLOR_FMT_NV12: + uv_alignment = 4096; + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + uv_alignment; + size = y_plane + uv_plane + + MSM_MEDIA_MAX(extra_size, 8 * y_stride); + size = MSM_MEDIA_ALIGN(size, 4096); + + /* Additional size to cover last row of non-aligned frame */ + if (width >= 2400 && height >= 2400) { + size += MSM_MEDIA_ALIGN(width, w_alignment) * + w_alignment; + size = MSM_MEDIA_ALIGN(size, 4096); + } + break; case COLOR_FMT_P010: case COLOR_FMT_NV12_512: + uv_alignment = 4096; + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + uv_alignment; + size = y_plane + uv_plane + + MSM_MEDIA_MAX(extra_size, 8 * y_stride); + size = MSM_MEDIA_ALIGN(size, 4096); + break; case COLOR_FMT_NV12_128: y_plane = y_stride * y_sclines; uv_plane = uv_stride * uv_sclines; size = y_plane + uv_plane; + size = 2 * size + extra_size; + size = MSM_MEDIA_ALIGN(size, 4096); break; case COLOR_FMT_NV12_UBWC: + y_sclines = VENUS_Y_SCANLINES(color_fmt, (height+1)>>1); + y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); + uv_sclines = VENUS_UV_SCANLINES(color_fmt, (height+1)>>1); + uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width); - uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width); - if (width <= INTERLACE_WIDTH_MAX && - height <= INTERLACE_HEIGHT_MAX && - (height * width) / 256 <= INTERLACE_MB_PER_FRAME_MAX) { - y_sclines = - VENUS_Y_SCANLINES(color_fmt, (height+1)>>1); - y_ubwc_plane = - MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); - uv_sclines = - VENUS_UV_SCANLINES(color_fmt, (height+1)>>1); - uv_ubwc_plane = - MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); - y_meta_scanlines = + y_meta_scanlines = VENUS_Y_META_SCANLINES(color_fmt, (height+1)>>1); - y_meta_plane = MSM_MEDIA_ALIGN( - y_meta_stride * y_meta_scanlines, 4096); - uv_meta_scanlines = + y_meta_plane = MSM_MEDIA_ALIGN( + y_meta_stride * y_meta_scanlines, 4096); + uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width); + uv_meta_scanlines = VENUS_UV_META_SCANLINES(color_fmt, (height+1)>>1); - uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride * - uv_meta_scanlines, 4096); - size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane + - uv_meta_plane)*2; - } else { - y_sclines = VENUS_Y_SCANLINES(color_fmt, height); - y_ubwc_plane = - MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); - uv_sclines = VENUS_UV_SCANLINES(color_fmt, height); - uv_ubwc_plane = - MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); - y_meta_scanlines = - VENUS_Y_META_SCANLINES(color_fmt, height); - y_meta_plane = MSM_MEDIA_ALIGN( - y_meta_stride * y_meta_scanlines, 4096); - uv_meta_scanlines = - VENUS_UV_META_SCANLINES(color_fmt, height); - uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride * - uv_meta_scanlines, 4096); - size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane + - uv_meta_plane); + uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride * + uv_meta_scanlines, 4096); + + size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane + + uv_meta_plane)*2 + + MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride); + size = MSM_MEDIA_ALIGN(size, 4096); + + /* Additional size to cover last row of non-aligned frame */ + if (width >= 2400 && height >= 2400) { + size += MSM_MEDIA_ALIGN(width, w_alignment) * + w_alignment; + size = MSM_MEDIA_ALIGN(size, 4096); } break; case COLOR_FMT_NV12_BPP10_UBWC: @@ -1335,7 +1362,9 @@ static inline unsigned int VENUS_BUFFER_SIZE(unsigned int color_fmt, uv_meta_scanlines, 4096); size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + - uv_meta_plane; + uv_meta_plane + + MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride); + size = MSM_MEDIA_ALIGN(size, 4096); break; case COLOR_FMT_P010_UBWC: y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); @@ -1351,10 +1380,12 @@ static inline unsigned int VENUS_BUFFER_SIZE(unsigned int color_fmt, size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + size = MSM_MEDIA_ALIGN(size, 4096); break; case COLOR_FMT_RGBA8888: rgb_plane = MSM_MEDIA_ALIGN(rgb_stride * rgb_scanlines, 4096); size = rgb_plane; + size = MSM_MEDIA_ALIGN(size, 4096); break; case COLOR_FMT_RGBA8888_UBWC: case COLOR_FMT_RGBA1010102_UBWC: @@ -1367,12 +1398,13 @@ static inline unsigned int VENUS_BUFFER_SIZE(unsigned int color_fmt, rgb_meta_plane = MSM_MEDIA_ALIGN(rgb_meta_stride * rgb_meta_scanlines, 4096); size = rgb_ubwc_plane + rgb_meta_plane; + size = MSM_MEDIA_ALIGN(size, 4096); break; default: break; } invalid_input: - return MSM_MEDIA_ALIGN(size, 4096); + return size; } static inline unsigned int VENUS_BUFFER_SIZE_USED(unsigned int color_fmt, diff --git a/include/uapi/media/msm_vidc_utils.h b/include/uapi/media/msm_vidc_utils.h index c121c36c1692..c7a304add779 100644 --- a/include/uapi/media/msm_vidc_utils.h +++ b/include/uapi/media/msm_vidc_utils.h @@ -4,211 +4,305 @@ #include -#define MSM_VIDC_EXTRADATA_NONE 0x00000000 -struct msm_vidc_extradata_header { - __u32 size; - __u32 version; /** Keeping binary compatibility */ - __u32 port_index; /* with firmware and OpenMAX IL **/ - __u32 type; /* msm_vidc_extradata_type */ - __u32 data_size; - __u32 data[1]; -}; +#if defined(CONFIG_ARCH_SDM845) +#define VENUS_USES_LEGACY_MISR_INFO +#endif -/* msm_vidc_interlace_type */ -#define MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE 0x01 -#define MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02 -#define MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04 -#define MSM_VIDC_INTERLACE_FRAME_TOPFIELDFIRST 0x08 -#define MSM_VIDC_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10 -#define MSM_VIDC_INTERLACE_FRAME_MBAFF 0x20 -/* Color formats */ #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12 0x2 #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC 0x8002 -#define MSM_VIDC_EXTRADATA_INTERLACE_VIDEO 0x00000002 +#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1 + +struct msm_vidc_extradata_header { + unsigned int size; + unsigned int:32; /** Keeping binary compatibility */ + unsigned int:32; /* with firmware and OpenMAX IL **/ + unsigned int type; /* msm_vidc_extradata_type */ + unsigned int data_size; + unsigned char data[1]; +}; + struct msm_vidc_interlace_payload { - __u32 format; /* Interlace format */ - __u32 color_format; + unsigned int format; + unsigned int color_format; }; -#define MSM_VIDC_EXTRADATA_FRAME_RATE 0x00000007 struct msm_vidc_framerate_payload { - __u32 frame_rate; /*In Q16 format */ + unsigned int frame_rate; }; -#define MSM_VIDC_EXTRADATA_TIMESTAMP 0x00000005 struct msm_vidc_ts_payload { - __u32 timestamp_lo; - __u32 timestamp_hi; + unsigned int timestamp_lo; + unsigned int timestamp_hi; }; -#define MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB 0x7F100001 struct msm_vidc_concealmb_payload { - __u32 num_mbs; + unsigned int num_mbs; }; - -#define MSM_VIDC_FRAME_RECONSTRUCTION_INCORRECT 0x0 -#define MSM_VIDC_FRAME_RECONSTRUCTION_CORRECT 0x01 -#define MSM_VIDC_FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT 0x02 -#define MSM_VIDC_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 struct msm_vidc_recoverysei_payload { - __u32 flags; + unsigned int flags; }; -#define MSM_VIDC_EXTRADATA_ASPECT_RATIO 0x7F100003 struct msm_vidc_aspect_ratio_payload { - __u32 size; - __u32 version; - __u32 port_index; - __u32 aspect_width; - __u32 aspect_height; + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int aspect_width; + unsigned int aspect_height; +}; + +struct msm_vidc_mpeg2_seqdisp_payload { + unsigned int video_format; + unsigned int color_descp; + unsigned int color_primaries; + unsigned int transfer_char; + unsigned int matrix_coeffs; + unsigned int disp_width; + unsigned int disp_height; +}; + +struct msm_vidc_input_crop_payload { + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int left; + unsigned int top; + unsigned int width; + unsigned int height; }; +#ifdef VENUS_USES_LEGACY_MISR_INFO struct msm_vidc_misr_info { - __u32 misr_set; - __u32 misr_dpb_luma[8]; - __u32 misr_dpb_chroma[8]; - __u32 misr_opb_luma[8]; - __u32 misr_opb_chroma[8]; + unsigned int misr_dpb_luma; + unsigned int misr_dpb_chroma; + unsigned int misr_opb_luma; + unsigned int misr_opb_chroma; }; -#define MSM_VIDC_EXTRADATA_OUTPUT_CROP 0x0700000F +#else +struct msm_vidc_misr_info { + unsigned int misr_set; + unsigned int misr_dpb_luma[8]; + unsigned int misr_dpb_chroma[8]; + unsigned int misr_opb_luma[8]; + unsigned int misr_opb_chroma[8]; +}; +#endif + struct msm_vidc_output_crop_payload { - __u32 size; - __u32 version; - __u32 port_index; - __u32 left; - __u32 top; - __u32 display_width; - __u32 display_height; - __u32 width; - __u32 height; - __u32 frame_num; - __u32 bit_depth_y; - __u32 bit_depth_c; + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int left; + unsigned int top; + unsigned int display_width; + unsigned int display_height; + unsigned int width; + unsigned int height; + unsigned int frame_num; + unsigned int bit_depth_y; + unsigned int bit_depth_c; struct msm_vidc_misr_info misr_info[2]; }; -#define MSM_VIDC_EXTRADATA_INDEX 0x7F100002 struct msm_vidc_extradata_index { - __u32 type; + unsigned int type; union { + struct msm_vidc_input_crop_payload input_crop; struct msm_vidc_aspect_ratio_payload aspect_ratio; }; }; -#define MSM_VIDC_EXTRADATA_PANSCAN_WINDOW 0x00000008 struct msm_vidc_panscan_window { - __u32 panscan_height_offset; - __u32 panscan_width_offset; - __u32 panscan_window_width; - __u32 panscan_window_height; + unsigned int panscan_height_offset; + unsigned int panscan_width_offset; + unsigned int panscan_window_width; + unsigned int panscan_window_height; }; + struct msm_vidc_panscan_window_payload { - __u32 num_panscan_windows; + unsigned int num_panscan_windows; struct msm_vidc_panscan_window wnd[1]; }; - -#define MSM_VIDC_USERDATA_TYPE_FRAME 0x1 -#define MSM_VIDC_USERDATA_TYPE_TOP_FIELD 0x2 -#define MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD 0x3 -#define MSM_VIDC_EXTRADATA_STREAM_USERDATA 0x0000000E struct msm_vidc_stream_userdata_payload { - __u32 type; - __u32 data[1]; + unsigned int type; + unsigned int data[1]; }; -#define MSM_VIDC_EXTRADATA_FRAME_QP 0x0000000F struct msm_vidc_frame_qp_payload { - __u32 frame_qp; - __u32 qp_sum; - __u32 skip_qp_sum; - __u32 skip_num_blocks; - __u32 total_num_blocks; + unsigned int frame_qp; + unsigned int qp_sum; + unsigned int skip_qp_sum; + unsigned int skip_num_blocks; + unsigned int total_num_blocks; +}; + +struct msm_vidc_dts_payload { + unsigned int timestamp_hi; + unsigned int timestamp_lo; }; -#define MSM_VIDC_EXTRADATA_FRAME_BITS_INFO 0x00000010 struct msm_vidc_frame_bits_info_payload { - __u32 frame_bits; - __u32 header_bits; + unsigned int frame_bits; + unsigned int header_bits; }; -#define MSM_VIDC_EXTRADATA_S3D_FRAME_PACKING 0x00000006 struct msm_vidc_s3d_frame_packing_payload { - __u32 fpa_id; - __u32 cancel_flag; - __u32 fpa_type; - __u32 quin_cunx_flag; - __u32 content_interprtation_type; - __u32 spatial_flipping_flag; - __u32 frame0_flipped_flag; - __u32 field_views_flag; - __u32 current_frame_is_frame0_flag; - __u32 frame0_self_contained_flag; - __u32 frame1_self_contained_flag; - __u32 frame0_graid_pos_x; - __u32 frame0_graid_pos_y; - __u32 frame1_graid_pos_x; - __u32 frame1_graid_pos_y; - __u32 fpa_reserved_byte; - __u32 fpa_repetition_period; - __u32 fpa_extension_flag; + unsigned int fpa_id; + unsigned int cancel_flag; + unsigned int fpa_type; + unsigned int quin_cunx_flag; + unsigned int content_interprtation_type; + unsigned int spatial_flipping_flag; + unsigned int frame0_flipped_flag; + unsigned int field_views_flag; + unsigned int current_frame_is_frame0_flag; + unsigned int frame0_self_contained_flag; + unsigned int frame1_self_contained_flag; + unsigned int frame0_graid_pos_x; + unsigned int frame0_graid_pos_y; + unsigned int frame1_graid_pos_x; + unsigned int frame1_graid_pos_y; + unsigned int fpa_reserved_byte; + unsigned int fpa_repetition_period; + unsigned int fpa_extension_flag; +}; + +struct msm_vidc_ubwc_cr_stats_info { + unsigned int stats_tile_32; + unsigned int stats_tile_64; + unsigned int stats_tile_96; + unsigned int stats_tile_128; + unsigned int stats_tile_160; + unsigned int stats_tile_192; + unsigned int stats_tile_256; +}; + +struct msm_vidc_yuv_stats_payload { + unsigned int frame_qp; + unsigned int texture; + unsigned int luma_in_q16; + unsigned int frame_difference; +}; + +struct msm_vidc_vpx_colorspace_payload { + unsigned int color_space; + unsigned int yuv_range_flag; + unsigned int sumsampling_x; + unsigned int sumsampling_y; }; struct msm_vidc_roi_qp_payload { - __s32 upper_qp_offset; - __s32 lower_qp_offset; - __u32 b_roi_info; - __u32 mbi_info_size; - __u32 data[1]; + int upper_qp_offset; + int lower_qp_offset; + unsigned int b_roi_info; + int mbi_info_size; + unsigned int data[1]; }; -#define MSM_VIDC_EXTRADATA_ROI_QP 0x00000013 +#define MSM_VIDC_EXTRADATA_ROI_DELTAQP 0x1 struct msm_vidc_roi_deltaqp_payload { - __u32 b_roi_info; /*Enable/Disable*/ - __u32 mbi_info_size; /*Size of QP data*/ - __u32 data[1]; + unsigned int b_roi_info; /*Enable/Disable*/ + int mbi_info_size; /*Size of QP data*/ + unsigned int data[1]; +}; + +struct msm_vidc_hdr10plus_metadata_payload { + unsigned int size; + unsigned int data[1]; }; -#define MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI 0x00000015 struct msm_vidc_mastering_display_colour_sei_payload { - __u32 nDisplayPrimariesX[3]; - __u32 nDisplayPrimariesY[3]; - __u32 nWhitePointX; - __u32 nWhitePointY; - __u32 nMaxDisplayMasteringLuminance; - __u32 nMinDisplayMasteringLuminance; + unsigned int nDisplayPrimariesX[3]; + unsigned int nDisplayPrimariesY[3]; + unsigned int nWhitePointX; + unsigned int nWhitePointY; + unsigned int nMaxDisplayMasteringLuminance; + unsigned int nMinDisplayMasteringLuminance; }; -#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI 0x00000016 struct msm_vidc_content_light_level_sei_payload { - __u32 nMaxContentLight; - __u32 nMaxPicAverageLight; + unsigned int nMaxContentLight; + unsigned int nMaxPicAverageLight; }; -#define MSM_VIDC_EXTRADATA_HDR10PLUS_METADATA 0x0000001A -struct msm_vidc_hdr10plus_metadata_payload { - __u32 size; - __u32 data[1]; +struct msm_vidc_vui_display_info_payload { + unsigned int video_signal_present_flag; + unsigned int video_format; + unsigned int bit_depth_y; + unsigned int bit_depth_c; + unsigned int video_full_range_flag; + unsigned int color_description_present_flag; + unsigned int color_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + unsigned int chroma_location_info_present_flag; + unsigned int chroma_format_idc; + unsigned int separate_color_plane_flag; + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; }; -#define MSM_VIDC_EXTRADATA_CVP_METADATA 0x0000001B -struct msm_vidc_enc_cvp_metadata_payload { - __u32 data[256]; -}; +/* msm_vidc_extradata_type */ +#define MSM_VIDC_EXTRADATA_NONE 0x00000000 +#define MSM_VIDC_EXTRADATA_MB_QUANTIZATION 0x00000001 +#define MSM_VIDC_EXTRADATA_INTERLACE_VIDEO 0x00000002 +#define MSM_VIDC_EXTRADATA_TIMESTAMP 0x00000005 +#define MSM_VIDC_EXTRADATA_S3D_FRAME_PACKING 0x00000006 +#define MSM_VIDC_EXTRADATA_FRAME_RATE 0x00000007 +#define MSM_VIDC_EXTRADATA_PANSCAN_WINDOW 0x00000008 +#define MSM_VIDC_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 +#define MSM_VIDC_EXTRADATA_MPEG2_SEQDISP 0x0000000D +#define MSM_VIDC_EXTRADATA_STREAM_USERDATA 0x0000000E +#define MSM_VIDC_EXTRADATA_FRAME_QP 0x0000000F +#define MSM_VIDC_EXTRADATA_FRAME_BITS_INFO 0x00000010 +#define MSM_VIDC_EXTRADATA_ROI_QP 0x00000013 +#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO 0x00000014 +#define MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI 0x00000015 +#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI 0x00000016 +#define MSM_VIDC_EXTRADATA_PQ_INFO 0x00000017 +#define MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI 0x00000018 +#define MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO 0x00000019 +#define MSM_VIDC_EXTRADATA_HDR10PLUS_METADATA 0x0000001A +#define MSM_VIDC_EXTRADATA_INPUT_CROP 0x0700000E +#define MSM_VIDC_EXTRADATA_OUTPUT_CROP 0x0700000F +#define MSM_VIDC_EXTRADATA_MULTISLICE_INFO 0x7F100000 +#define MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB 0x7F100001 +#define MSM_VIDC_EXTRADATA_INDEX 0x7F100002 +#define MSM_VIDC_EXTRADATA_ASPECT_RATIO 0x7F100003 +#define MSM_VIDC_EXTRADATA_METADATA_LTR 0x7F100004 +#define MSM_VIDC_EXTRADATA_METADATA_MBI 0x7F100005 +#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO 0x7F100006 +#define MSM_VIDC_EXTRADATA_ENC_DTS_INFO 0x7F100008 -/* video_format */ -#define MSM_VIDC_COMPONENT 0 -#define MSM_VIDC_PAL 1 -#define MSM_VIDC_NTSC 2 -#define MSM_VIDC_SECAM 3 -#define MSM_VIDC_MAC 4 -#define MSM_VIDC_UNSPECIFIED_FORMAT 5 -#define MSM_VIDC_RESERVED_1_FORMAT 6 -#define MSM_VIDC_RESERVED_2_FORMAT 7 +/* msm_vidc_interlace_type */ +#define MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE 0x01 +#define MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02 +#define MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04 +#define MSM_VIDC_INTERLACE_FRAME_TOPFIELDFIRST 0x08 +#define MSM_VIDC_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10 +#define MSM_VIDC_INTERLACE_FRAME_MBAFF 0x20 + +/* msm_vidc_framepack_type */ +#define MSM_VIDC_FRAMEPACK_CHECKERBOARD 0x00 +#define MSM_VIDC_FRAMEPACK_COLUMN_INTERLEAVE 0x01 +#define MSM_VIDC_FRAMEPACK_ROW_INTERLEAVE 0x02 +#define MSM_VIDC_FRAMEPACK_SIDE_BY_SIDE 0x03 +#define MSM_VIDC_FRAMEPACK_TOP_BOTTOM 0x04 +#define MSM_VIDC_FRAMEPACK_TEMPORAL_INTERLEAVE 0x05 + +/* msm_vidc_recovery_sei */ +#define MSM_VIDC_FRAME_RECONSTRUCTION_INCORRECT 0x0 +#define MSM_VIDC_FRAME_RECONSTRUCTION_CORRECT 0x01 +#define MSM_VIDC_FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT 0x02 + +/* msm_vidc_userdata_type */ +#define MSM_VIDC_USERDATA_TYPE_FRAME 0x1 +#define MSM_VIDC_USERDATA_TYPE_TOP_FIELD 0x2 +#define MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD 0x3 /* See colour_primaries of ISO/IEC 14496 for significance */ -/* color_primaries values */ +/* msm_vidc_h264_color_primaries_values */ #define MSM_VIDC_RESERVED_1 0 #define MSM_VIDC_BT709_5 1 #define MSM_VIDC_UNSPECIFIED 2 @@ -221,7 +315,17 @@ struct msm_vidc_enc_cvp_metadata_payload { #define MSM_VIDC_GENERIC_FILM 8 #define MSM_VIDC_BT2020 9 -/* matrix_coeffs values */ +/* msm_vidc_vp9_color_primaries_values */ +#define MSM_VIDC_CS_UNKNOWN 0 +#define MSM_VIDC_CS_BT_601 1 +#define MSM_VIDC_CS_BT_709 2 +#define MSM_VIDC_CS_SMPTE_170 3 +#define MSM_VIDC_CS_SMPTE_240 4 +#define MSM_VIDC_CS_BT_2020 5 +#define MSM_VIDC_CS_RESERVED 6 +#define MSM_VIDC_CS_RGB 7 + +/* msm_vidc_h264_matrix_coeff_values */ #define MSM_VIDC_MATRIX_RGB 0 #define MSM_VIDC_MATRIX_BT_709_5 1 #define MSM_VIDC_MATRIX_UNSPECIFIED 2 @@ -236,7 +340,7 @@ struct msm_vidc_enc_cvp_metadata_payload { #define MSM_VIDC_MATRIX_BT_2020 9 #define MSM_VIDC_MATRIX_BT_2020_CONST 10 -/* transfer_char values */ +/* msm_vidc_h264_transfer_chars_values */ #define MSM_VIDC_TRANSFER_RESERVED_1 0 #define MSM_VIDC_TRANSFER_BT709_5 1 #define MSM_VIDC_TRANSFER_UNSPECIFIED 2 @@ -258,118 +362,30 @@ struct msm_vidc_enc_cvp_metadata_payload { #define MSM_VIDC_TRANSFER_SMPTE_ST428_1 17 #define MSM_VIDC_TRANSFER_HLG 18 -#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO 0x7F100006 -struct msm_vidc_vui_display_info_payload { - __u32 video_signal_present_flag; - __u32 video_format; - __u32 bit_depth_y; - __u32 bit_depth_c; - __u32 video_full_range_flag; - __u32 color_description_present_flag; - __u32 color_primaries; - __u32 transfer_char; - __u32 matrix_coeffs; - __u32 chroma_location_info_present_flag; - __u32 chroma_format_idc; - __u32 separate_color_plane_flag; - __u32 chroma_sample_loc_type_top_field; - __u32 chroma_sample_loc_type_bottom_field; -}; - -#define MSM_VIDC_EXTRADATA_HDR_HIST 0x7F100008 -struct msm_vidc_extradata_hdr_hist_payload { - __u32 value_count[1024]; -}; - -#define MSM_VIDC_EXTRADATA_MPEG2_SEQDISP 0x0000000D -struct msm_vidc_mpeg2_seqdisp_payload { - __u32 video_format; - __u32 color_descp; - __u32 color_primaries; - __u32 transfer_char; - __u32 matrix_coeffs; - __u32 disp_width; - __u32 disp_height; -}; - -/* VPx color_space values */ -#define MSM_VIDC_CS_UNKNOWN 0 -#define MSM_VIDC_CS_BT_601 1 -#define MSM_VIDC_CS_BT_709 2 -#define MSM_VIDC_CS_SMPTE_170 3 -#define MSM_VIDC_CS_SMPTE_240 4 -#define MSM_VIDC_CS_BT_2020 5 -#define MSM_VIDC_CS_RESERVED 6 -#define MSM_VIDC_CS_RGB 7 -#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO 0x00000014 -struct msm_vidc_vpx_colorspace_payload { - __u32 color_space; - __u32 yuv_range_flag; - __u32 sumsampling_x; - __u32 sumsampling_y; -}; - -#define MSM_VIDC_EXTRADATA_METADATA_LTRINFO 0x7F100004 -/* Don't use the #define below. It is to bypass checkpatch */ -#define LTRINFO MSM_VIDC_EXTRADATA_METADATA_LTRINFO -struct msm_vidc_metadata_ltr_payload { - __u32 ltr_use_mark; -}; - -/* ptr[2]: event_notify: pixel_depth */ +/* msm_vidc_pixel_depth */ #define MSM_VIDC_BIT_DEPTH_8 0 #define MSM_VIDC_BIT_DEPTH_10 1 #define MSM_VIDC_BIT_DEPTH_UNSUPPORTED 0XFFFFFFFF -/* ptr[3]: event_notify: pic_struct */ +/* msm_vidc_video_format */ +#define MSM_VIDC_COMPONENT 0 +#define MSM_VIDC_PAL 1 +#define MSM_VIDC_NTSC 2 +#define MSM_VIDC_SECAM 3 +#define MSM_VIDC_MAC 4 +#define MSM_VIDC_UNSPECIFIED_FORMAT 5 +#define MSM_VIDC_RESERVED_1_FORMAT 6 +#define MSM_VIDC_RESERVED_2_FORMAT 7 + +/* msm_vidc_color_desc_flag */ +#define MSM_VIDC_COLOR_DESC_NOT_PRESENT 0 +#define MSM_VIDC_COLOR_DESC_PRESENT 1 + +/* msm_vidc_pic_struct */ #define MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED 0x0 #define MSM_VIDC_PIC_STRUCT_PROGRESSIVE 0x1 /*default when layer ID isn't specified*/ #define MSM_VIDC_ALL_LAYER_ID 0xFF -static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height) -{ - (void)height; - (void)width; - - /* - * In the future, calculate the size based on the w/h but just - * hardcode it for now since 16K satisfies all current usecases. - */ - return 16 * 1024; -} - -/* V4L2_CID_MPEG_VIDC_VENC_HDR_INFO payload index */ -enum msm_vidc_hdr_info_types { - MSM_VIDC_RGB_PRIMARY_00, - MSM_VIDC_RGB_PRIMARY_01, - MSM_VIDC_RGB_PRIMARY_10, - MSM_VIDC_RGB_PRIMARY_11, - MSM_VIDC_RGB_PRIMARY_20, - MSM_VIDC_RGB_PRIMARY_21, - MSM_VIDC_WHITEPOINT_X, - MSM_VIDC_WHITEPOINT_Y, - MSM_VIDC_MAX_DISP_LUM, - MSM_VIDC_MIN_DISP_LUM, - MSM_VIDC_RGB_MAX_CLL, - MSM_VIDC_RGB_MAX_FLL, -}; - -enum msm_vidc_plane_reserved_field_types { - MSM_VIDC_BUFFER_FD, - MSM_VIDC_DATA_OFFSET, - MSM_VIDC_COMP_RATIO, - MSM_VIDC_INPUT_TAG_1, - MSM_VIDC_INPUT_TAG_2, -}; - -enum msm_vidc_cb_event_types { - MSM_VIDC_HEIGHT, - MSM_VIDC_WIDTH, - MSM_VIDC_BIT_DEPTH, - MSM_VIDC_PIC_STRUCT, - MSM_VIDC_COLOR_SPACE, - MSM_VIDC_FW_MIN_COUNT, -}; #endif diff --git a/init/initramfs.c b/init/initramfs.c index e6a800a7c12a..c2ef3486c9b8 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -624,7 +624,7 @@ static int __init skip_initramfs_param(char *str) { if (*str) return 0; - do_skip_initramfs = 1; + do_skip_initramfs = !IS_ENABLED(CONFIG_INITRAMFS_IGNORE_SKIP_FLAG); return 1; } __setup("skip_initramfs", skip_initramfs_param); diff --git a/net/Kconfig b/net/Kconfig index 2ba11ce2bf12..256934bec52b 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -234,6 +234,7 @@ source "net/switchdev/Kconfig" source "net/l3mdev/Kconfig" source "net/qrtr/Kconfig" source "net/ncsi/Kconfig" +source "net/rmnet_data/Kconfig" config RPS bool diff --git a/net/Makefile b/net/Makefile index 177b6fbac29c..b2a2f3b72a67 100644 --- a/net/Makefile +++ b/net/Makefile @@ -86,3 +86,4 @@ endif obj-$(CONFIG_QRTR) += qrtr/ obj-$(CONFIG_NET_NCSI) += ncsi/ obj-$(CONFIG_XDP_SOCKETS) += xdp/ +obj-$(CONFIG_RMNET_DATA) += rmnet_data/ diff --git a/net/rmnet_data/Kconfig b/net/rmnet_data/Kconfig new file mode 100644 index 000000000000..36d581771cb9 --- /dev/null +++ b/net/rmnet_data/Kconfig @@ -0,0 +1,29 @@ +# +# RMNET Data and MAP driver +# + +menuconfig RMNET_DATA + depends on NETDEVICES + bool "RmNet Data and MAP driver" + ---help--- + If you say Y here, then the rmnet_data module will be statically + compiled into the kernel. The rmnet data module provides MAP + functionality for embedded and bridged traffic. +if RMNET_DATA + +config RMNET_DATA_FC + bool "RmNet Data Flow Control" + depends on NET_SCHED && NET_SCH_PRIO + ---help--- + Say Y here if you want RmNet data to handle in-band flow control and + ioctl based flow control. This depends on net scheduler and prio queue + capability being present in the kernel. In-band flow control requires + MAP protocol be used. +config RMNET_DATA_DEBUG_PKT + bool "Packet Debug Logging" + ---help--- + Say Y here if you want RmNet data to be able to log packets in main + system log. This should not be enabled on production builds as it can + impact system performance. Note that simply enabling it here will not + enable the logging; it must be enabled at run-time as well. +endif # RMNET_DATA diff --git a/net/rmnet_data/Makefile b/net/rmnet_data/Makefile new file mode 100644 index 000000000000..ccb8b5b76d6c --- /dev/null +++ b/net/rmnet_data/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the RMNET Data module +# + +rmnet_data-y := rmnet_data_main.o +rmnet_data-y += rmnet_data_config.o +rmnet_data-y += rmnet_data_vnd.o +rmnet_data-y += rmnet_data_handlers.o +rmnet_data-y += rmnet_map_data.o +rmnet_data-y += rmnet_map_command.o +rmnet_data-y += rmnet_data_stats.o +obj-$(CONFIG_RMNET_DATA) += rmnet_data.o + +CFLAGS_rmnet_data_main.o := -I$(src) diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c new file mode 100644 index 000000000000..c117e506d36e --- /dev/null +++ b/net/rmnet_data/rmnet_data_config.c @@ -0,0 +1,1301 @@ +/* Copyright (c) 2013-2018, 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. + * + * RMNET Data configuration engine + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_config.h" +#include "rmnet_data_handlers.h" +#include "rmnet_data_vnd.h" +#include "rmnet_data_private.h" +#include "rmnet_data_trace.h" +#include "rmnet_map.h" + +RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_CONFIG); + +/* Local Definitions and Declarations */ +static struct sock *nl_socket_handle; + +#ifndef RMNET_KERNEL_PRE_3_8 +static struct netlink_kernel_cfg rmnet_netlink_cfg = { + .input = rmnet_config_netlink_msg_handler +}; +#endif + +static struct notifier_block rmnet_dev_notifier = { + .notifier_call = rmnet_config_notify_cb, + .next = 0, + .priority = 0 +}; + +struct rmnet_free_vnd_work { + struct work_struct work; + int vnd_id[RMNET_DATA_MAX_VND]; + int count; +}; + +/* Init and Cleanup */ + +#ifdef RMNET_KERNEL_PRE_3_8 +static struct sock *_rmnet_config_start_netlink(void) +{ + return netlink_kernel_create(&init_net, + RMNET_NETLINK_PROTO, + 0, + rmnet_config_netlink_msg_handler, + NULL, + THIS_MODULE); +} +#else +static struct sock *_rmnet_config_start_netlink(void) +{ + return netlink_kernel_create(&init_net, + RMNET_NETLINK_PROTO, + &rmnet_netlink_cfg); +} +#endif /* RMNET_KERNEL_PRE_3_8 */ + +/* rmnet_config_init() - Startup init + * + * Registers netlink protocol with kernel and opens socket. Netlink handler is + * registered with kernel. + */ +int rmnet_config_init(void) +{ + int rc; + + nl_socket_handle = _rmnet_config_start_netlink(); + if (!nl_socket_handle) { + LOGE("%s", "Failed to init netlink socket"); + return RMNET_INIT_ERROR; + } + + rc = register_netdevice_notifier(&rmnet_dev_notifier); + if (rc != 0) { + LOGE("Failed to register device notifier; rc=%d", rc); + /* TODO: Cleanup the nl socket */ + return RMNET_INIT_ERROR; + } + + return 0; +} + +/* rmnet_config_exit() - Cleans up all netlink related resources */ +void rmnet_config_exit(void) +{ + int rc; + + netlink_kernel_release(nl_socket_handle); + rc = unregister_netdevice_notifier(&rmnet_dev_notifier); + if (rc != 0) + LOGE("Failed to unregister device notifier; rc=%d", rc); +} + +/* Helper Functions */ + +/* _rmnet_is_physical_endpoint_associated() - Determines if device is associated + * @dev: Device to get check + * + * Compares device rx_handler callback pointer against known function + * + * Return: + * - 1 if associated + * - 0 if NOT associated + */ +static inline int _rmnet_is_physical_endpoint_associated(struct net_device *dev) +{ + rx_handler_func_t *rx_handler; + + rx_handler = rcu_dereference(dev->rx_handler); + + if (rx_handler == rmnet_data_rx_handler) + return 1; + else + return 0; +} + +/* _rmnet_get_phys_ep_config() - Get physical ep config for an associated device + * @dev: Device to get endpoint configuration from + * + * Return: + * - pointer to configuration if successful + * - 0 (null) if device is not associated + */ +struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config + (struct net_device *dev) +{ + struct rmnet_phys_ep_conf_s *_rmnet_phys_ep_config; + + if (_rmnet_is_physical_endpoint_associated(dev)) { + _rmnet_phys_ep_config = (struct rmnet_phys_ep_conf_s *) + rcu_dereference(dev->rx_handler_data); + if (_rmnet_phys_ep_config && _rmnet_phys_ep_config->config) + return (struct rmnet_phys_ep_config *) + _rmnet_phys_ep_config->config; + else + return 0; + } else { + return 0; + } +} + +/* _rmnet_get_logical_ep() - Gets the logical end point configuration + * structure for a network device + * @dev: Device to get endpoint configuration from + * @config_id: Logical endpoint id on device + * Retrieves the logical_endpoint_config structure. + * + * Return: + * - End point configuration structure + * - NULL in case of an error + */ +struct rmnet_logical_ep_conf_s *_rmnet_get_logical_ep(struct net_device *dev, + int config_id) +{ + struct rmnet_phys_ep_config *config; + struct rmnet_logical_ep_conf_s *epconfig_l; + + if (rmnet_vnd_is_vnd(dev)) { + epconfig_l = rmnet_vnd_get_le_config(dev); + } else { + config = _rmnet_get_phys_ep_config(dev); + + if (!config) + return NULL; + + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) + epconfig_l = &config->local_ep; + else + epconfig_l = &config->muxed_ep[config_id]; + } + + return epconfig_l; +} + +/* Netlink Handler */ +static void _rmnet_netlink_set_link_egress_data_format + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev); + + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + resp_rmnet->return_code = + rmnet_set_egress_data_format(dev, + rmnet_header->data_format.flags, + rmnet_header->data_format.agg_size, + rmnet_header->data_format.agg_count + ); + dev_put(dev); +} + +static void _rmnet_netlink_set_link_ingress_data_format + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + resp_rmnet->return_code = rmnet_set_ingress_data_format( + dev, + rmnet_header->data_format.flags, + rmnet_header->data_format.tail_spacing); + dev_put(dev); +} + +static void _rmnet_netlink_set_logical_ep_config + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev, *dev2; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + if (rmnet_header->local_ep_config.ep_id < -1 || + rmnet_header->local_ep_config.ep_id > 254) { + resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS; + return; + } + + dev = dev_get_by_name(&init_net, + rmnet_header->local_ep_config.dev); + + dev2 = dev_get_by_name(&init_net, + rmnet_header->local_ep_config.next_dev); + + if (dev && dev2) + resp_rmnet->return_code = + rmnet_set_logical_endpoint_config( + dev, + rmnet_header->local_ep_config.ep_id, + rmnet_header->local_ep_config.operating_mode, + dev2); + else + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + + if (dev) + dev_put(dev); + if (dev2) + dev_put(dev2); +} + +static void _rmnet_netlink_unset_logical_ep_config + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + if (rmnet_header->local_ep_config.ep_id < -1 || + rmnet_header->local_ep_config.ep_id > 254) { + resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS; + return; + } + + dev = dev_get_by_name(&init_net, + rmnet_header->local_ep_config.dev); + + if (dev) { + resp_rmnet->return_code = + rmnet_unset_logical_endpoint_config( + dev, + rmnet_header->local_ep_config.ep_id); + dev_put(dev); + } else { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + } +} + +static void _rmnet_netlink_get_logical_ep_config + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + if (rmnet_header->local_ep_config.ep_id < -1 || + rmnet_header->local_ep_config.ep_id > 254) { + resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS; + return; + } + + dev = dev_get_by_name(&init_net, + rmnet_header->local_ep_config.dev); + + if (dev) { + resp_rmnet->return_code = + rmnet_get_logical_endpoint_config( + dev, + rmnet_header->local_ep_config.ep_id, + &resp_rmnet->local_ep_config.operating_mode, + resp_rmnet->local_ep_config.next_dev, + sizeof(resp_rmnet->local_ep_config.next_dev)); + } else { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + if (resp_rmnet->return_code == RMNET_CONFIG_OK) { + /* Begin Data */ + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; + resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0) + ->local_ep_config); + } + dev_put(dev); +} + +static void _rmnet_netlink_associate_network_device + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + resp_rmnet->return_code = rmnet_associate_network_device(dev); + dev_put(dev); +} + +static void _rmnet_netlink_unassociate_network_device + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + resp_rmnet->return_code = rmnet_unassociate_network_device(dev); + dev_put(dev); +} + +static void _rmnet_netlink_get_network_device_associated + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + resp_rmnet->return_code = _rmnet_is_physical_endpoint_associated(dev); + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; + dev_put(dev); +} + +static void _rmnet_netlink_get_link_egress_data_format + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + struct rmnet_phys_ep_config *config; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + config = _rmnet_get_phys_ep_config(dev); + if (!config) { + resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST; + dev_put(dev); + return; + } + + /* Begin Data */ + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; + resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0) + ->data_format); + resp_rmnet->data_format.flags = config->egress_data_format; + resp_rmnet->data_format.agg_count = config->egress_agg_count; + resp_rmnet->data_format.agg_size = config->egress_agg_size; + dev_put(dev); +} + +static void _rmnet_netlink_get_link_ingress_data_format + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + struct net_device *dev; + struct rmnet_phys_ep_config *config; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev); + if (!dev) { + resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; + return; + } + + config = _rmnet_get_phys_ep_config(dev); + if (!config) { + resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST; + dev_put(dev); + return; + } + + /* Begin Data */ + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; + resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0) + ->data_format); + resp_rmnet->data_format.flags = config->ingress_data_format; + resp_rmnet->data_format.tail_spacing = config->tail_spacing; + dev_put(dev); +} + +static void _rmnet_netlink_get_vnd_name + (struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + int r; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + r = rmnet_vnd_get_name(rmnet_header->vnd.id, resp_rmnet->vnd.vnd_name, + RMNET_MAX_STR_LEN); + + if (r != 0) { + resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST; + return; + } + + /* Begin Data */ + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; + resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0)->vnd); +} + +static void _rmnet_netlink_add_del_vnd_tc_flow + (u32 command, + struct rmnet_nl_msg_s *rmnet_header, + struct rmnet_nl_msg_s *resp_rmnet) +{ + u32 id; + u32 map_flow_id; + u32 tc_flow_id; + + if (!rmnet_header || !resp_rmnet) + return; + + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + + id = rmnet_header->flow_control.id; + map_flow_id = rmnet_header->flow_control.map_flow_id; + tc_flow_id = rmnet_header->flow_control.tc_flow_id; + + switch (command) { + case RMNET_NETLINK_ADD_VND_TC_FLOW: + resp_rmnet->return_code = rmnet_vnd_add_tc_flow(id, + map_flow_id, + tc_flow_id); + break; + case RMNET_NETLINK_DEL_VND_TC_FLOW: + resp_rmnet->return_code = rmnet_vnd_del_tc_flow(id, + map_flow_id, + tc_flow_id); + break; + default: + LOGM("Called with unhandled command %d", command); + resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST; + break; + } +} + +/* rmnet_config_netlink_msg_handler() - Netlink message handler callback + * @skb: Packet containing netlink messages + * + * Standard kernel-expected format for a netlink message handler. Processes SKBs + * which contain RmNet data specific netlink messages. + */ +void rmnet_config_netlink_msg_handler(struct sk_buff *skb) +{ + struct nlmsghdr *nlmsg_header, *resp_nlmsg; + struct rmnet_nl_msg_s *rmnet_header, *resp_rmnet; + int return_pid, response_data_length; + struct sk_buff *skb_response; + + response_data_length = 0; + nlmsg_header = (struct nlmsghdr *)skb->data; + rmnet_header = (struct rmnet_nl_msg_s *)nlmsg_data(nlmsg_header); + + if (!nlmsg_header->nlmsg_pid || + (nlmsg_header->nlmsg_len < sizeof(struct nlmsghdr) + + sizeof(struct rmnet_nl_msg_s))) + return; + + LOGL("Netlink message pid=%d, seq=%d, length=%d, rmnet_type=%d", + nlmsg_header->nlmsg_pid, + nlmsg_header->nlmsg_seq, + nlmsg_header->nlmsg_len, + rmnet_header->message_type); + + return_pid = nlmsg_header->nlmsg_pid; + + skb_response = nlmsg_new(sizeof(struct nlmsghdr) + + sizeof(struct rmnet_nl_msg_s), + GFP_KERNEL); + + if (!skb_response) { + LOGH("%s", "Failed to allocate response buffer"); + return; + } + + resp_nlmsg = nlmsg_put(skb_response, + 0, + nlmsg_header->nlmsg_seq, + NLMSG_DONE, + sizeof(struct rmnet_nl_msg_s), + 0); + + resp_rmnet = nlmsg_data(resp_nlmsg); + + if (!resp_rmnet) + return; + + resp_rmnet->message_type = rmnet_header->message_type; + rtnl_lock(); + switch (rmnet_header->message_type) { + case RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE: + _rmnet_netlink_associate_network_device + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE: + _rmnet_netlink_unassociate_network_device + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED: + _rmnet_netlink_get_network_device_associated + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT: + _rmnet_netlink_set_link_egress_data_format + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT: + _rmnet_netlink_get_link_egress_data_format + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT: + _rmnet_netlink_set_link_ingress_data_format + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT: + _rmnet_netlink_get_link_ingress_data_format + (rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_SET_LOGICAL_EP_CONFIG: + _rmnet_netlink_set_logical_ep_config(rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG: + _rmnet_netlink_unset_logical_ep_config(rmnet_header, + resp_rmnet); + break; + + case RMNET_NETLINK_GET_LOGICAL_EP_CONFIG: + _rmnet_netlink_get_logical_ep_config(rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_NEW_VND: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + resp_rmnet->return_code = + rmnet_create_vnd(rmnet_header->vnd.id); + break; + + case RMNET_NETLINK_NEW_VND_WITH_PREFIX: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + resp_rmnet->return_code = rmnet_create_vnd_prefix( + rmnet_header->vnd.id, + rmnet_header->vnd.vnd_name); + break; + + case RMNET_NETLINK_NEW_VND_WITH_NAME: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + resp_rmnet->return_code = rmnet_create_vnd_name( + rmnet_header->vnd.id, + rmnet_header->vnd.vnd_name); + break; + + case RMNET_NETLINK_FREE_VND: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + /* Please check rmnet_vnd_free_dev documentation regarding + * the below locking sequence + */ + rtnl_unlock(); + resp_rmnet->return_code = rmnet_free_vnd(rmnet_header->vnd.id); + rtnl_lock(); + break; + + case RMNET_NETLINK_GET_VND_NAME: + _rmnet_netlink_get_vnd_name(rmnet_header, resp_rmnet); + break; + + case RMNET_NETLINK_DEL_VND_TC_FLOW: + case RMNET_NETLINK_ADD_VND_TC_FLOW: + _rmnet_netlink_add_del_vnd_tc_flow(rmnet_header->message_type, + rmnet_header, + resp_rmnet); + break; + + default: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + resp_rmnet->return_code = RMNET_CONFIG_UNKNOWN_MESSAGE; + break; + } + rtnl_unlock(); + nlmsg_unicast(nl_socket_handle, skb_response, return_pid); + LOGD("%s", "Done processing command"); +} + +/* Configuration API */ + +/* rmnet_unassociate_network_device() - Unassociate network device + * @dev: Device to unassociate + * + * Frees all structures generate for device. Unregisters rx_handler + * todo: needs to do some sanity verification first (is device in use, etc...) + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null + * - RMNET_CONFIG_INVALID_REQUEST if device is not already associated + * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + */ +int rmnet_unassociate_network_device(struct net_device *dev) +{ + struct rmnet_phys_ep_conf_s *config; + int config_id = RMNET_LOCAL_LOGICAL_ENDPOINT; + struct rmnet_logical_ep_conf_s *epconfig_l; + + ASSERT_RTNL(); + + LOGL("(%s);", dev->name); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + if (!_rmnet_is_physical_endpoint_associated(dev)) + return RMNET_CONFIG_INVALID_REQUEST; + + for (; config_id < RMNET_DATA_MAX_LOGICAL_EP; config_id++) { + epconfig_l = _rmnet_get_logical_ep(dev, config_id); + if (epconfig_l && epconfig_l->refcount) + return RMNET_CONFIG_DEVICE_IN_USE; + } + + config = (struct rmnet_phys_ep_conf_s *) + rcu_dereference(dev->rx_handler_data); + + if (!config) + return RMNET_CONFIG_UNKNOWN_ERROR; + + kfree(config); + + netdev_rx_handler_unregister(dev); + + /* Explicitly release the reference from the device */ + dev_put(dev); + trace_rmnet_unassociate(dev); + return RMNET_CONFIG_OK; +} + +/* rmnet_set_ingress_data_format() - Set ingress data format on network device + * @dev: Device to ingress data format on + * @egress_data_format: 32-bit unsigned bitmask of ingress format + * + * Network device must already have association with RmNet Data driver + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + */ +int rmnet_set_ingress_data_format(struct net_device *dev, + u32 ingress_data_format, + uint8_t tail_spacing) +{ + struct rmnet_phys_ep_config *config; + + ASSERT_RTNL(); + + LOGL("(%s,0x%08X);", dev->name, ingress_data_format); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + config = _rmnet_get_phys_ep_config(dev); + + if (!config) + return RMNET_CONFIG_INVALID_REQUEST; + + config->ingress_data_format = ingress_data_format; + config->tail_spacing = tail_spacing; + + return RMNET_CONFIG_OK; +} + +/* rmnet_set_egress_data_format() - Set egress data format on network device + * @dev: Device to egress data format on + * @egress_data_format: 32-bit unsigned bitmask of egress format + * + * Network device must already have association with RmNet Data driver + * todo: Bounds check on agg_* + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + */ +int rmnet_set_egress_data_format(struct net_device *dev, + u32 egress_data_format, + u16 agg_size, + u16 agg_count) +{ + struct rmnet_phys_ep_config *config; + + ASSERT_RTNL(); + + LOGL("(%s,0x%08X, %d, %d);", + dev->name, egress_data_format, agg_size, agg_count); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + config = _rmnet_get_phys_ep_config(dev); + + if (!config) + return RMNET_CONFIG_UNKNOWN_ERROR; + + config->egress_data_format = egress_data_format; + config->egress_agg_size = agg_size; + config->egress_agg_count = agg_count; + + return RMNET_CONFIG_OK; +} + +/* rmnet_associate_network_device() - Associate network device + * @dev: Device to register with RmNet data + * + * Typically used on physical network devices. Registers RX handler and private + * metadata structures. + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null + * - RMNET_CONFIG_INVALID_REQUEST if the device to be associated is a vnd + * - RMNET_CONFIG_DEVICE_IN_USE if dev rx_handler is already filled + * - RMNET_CONFIG_DEVICE_IN_USE if netdev_rx_handler_register() fails + */ +int rmnet_associate_network_device(struct net_device *dev) +{ + struct rmnet_phys_ep_conf_s *config; + struct rmnet_phys_ep_config *conf; + int rc; + + ASSERT_RTNL(); + + LOGL("(%s);\n", dev->name); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + if (_rmnet_is_physical_endpoint_associated(dev)) { + LOGM("%s is already regestered", dev->name); + return RMNET_CONFIG_DEVICE_IN_USE; + } + + if (rmnet_vnd_is_vnd(dev)) { + LOGM("%s is a vnd", dev->name); + return RMNET_CONFIG_INVALID_REQUEST; + } + + config = kmalloc(sizeof(*config), GFP_ATOMIC); + conf = kmalloc(sizeof(*conf), GFP_ATOMIC); + + if (!config || !conf) + return RMNET_CONFIG_NOMEM; + + memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s)); + memset(conf, 0, sizeof(struct rmnet_phys_ep_config)); + + config->config = conf; + conf->dev = dev; + spin_lock_init(&conf->agg_lock); + config->recycle = kfree_skb; + hrtimer_init(&conf->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + conf->hrtimer.function = rmnet_map_flush_packet_queue; + rc = netdev_rx_handler_register(dev, rmnet_data_rx_handler, config); + + if (rc) { + LOGM("netdev_rx_handler_register returns %d", rc); + kfree(config); + kfree(conf); + return RMNET_CONFIG_DEVICE_IN_USE; + } + + /* Explicitly hold a reference to the device */ + dev_hold(dev); + trace_rmnet_associate(dev); + return RMNET_CONFIG_OK; +} + +/* _rmnet_set_logical_endpoint_config() - Set logical endpoing config on device + * @dev: Device to set endpoint configuration on + * @config_id: logical endpoint id on device + * @epconfig: endpoing configuration structure to set + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null + * - RMNET_CONFIG_DEVICE_IN_USE if device already has a logical ep + * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range + */ +int _rmnet_set_logical_endpoint_config(struct net_device *dev, + int config_id, + struct rmnet_logical_ep_conf_s *epconfig) +{ + struct rmnet_logical_ep_conf_s *epconfig_l; + + ASSERT_RTNL(); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || + config_id >= RMNET_DATA_MAX_LOGICAL_EP) + return RMNET_CONFIG_BAD_ARGUMENTS; + + epconfig_l = _rmnet_get_logical_ep(dev, config_id); + + if (!epconfig_l) + return RMNET_CONFIG_UNKNOWN_ERROR; + + if (epconfig_l->refcount) + return RMNET_CONFIG_DEVICE_IN_USE; + + memcpy(epconfig_l, epconfig, sizeof(struct rmnet_logical_ep_conf_s)); + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) + epconfig_l->mux_id = 0; + else + epconfig_l->mux_id = config_id; + + /* Explicitly hold a reference to the egress device */ + dev_hold(epconfig_l->egress_dev); + return RMNET_CONFIG_OK; +} + +/* _rmnet_unset_logical_endpoint_config() - Un-set the logical endpoing config + * on device + * @dev: Device to set endpoint configuration on + * @config_id: logical endpoint id on device + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null + * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range + */ +int _rmnet_unset_logical_endpoint_config(struct net_device *dev, + int config_id) +{ + struct rmnet_logical_ep_conf_s *epconfig_l = 0; + + ASSERT_RTNL(); + + if (!dev) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || + config_id >= RMNET_DATA_MAX_LOGICAL_EP) + return RMNET_CONFIG_BAD_ARGUMENTS; + + epconfig_l = _rmnet_get_logical_ep(dev, config_id); + + if (!epconfig_l || !epconfig_l->refcount) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + /* Explicitly release the reference from the egress device */ + dev_put(epconfig_l->egress_dev); + memset(epconfig_l, 0, sizeof(struct rmnet_logical_ep_conf_s)); + + return RMNET_CONFIG_OK; +} + +/* rmnet_set_logical_endpoint_config() - Set logical endpoint config on a device + * @dev: Device to set endpoint configuration on + * @config_id: logical endpoint id on device + * @rmnet_mode: endpoint mode. Values from: rmnet_config_endpoint_modes_e + * @egress_device: device node to forward packet to once done processing in + * ingress/egress handlers + * + * Creates a logical_endpoint_config structure and fills in the information from + * function arguments. Calls _rmnet_set_logical_endpoint_config() to finish + * configuration. Network device must already have association with RmNet Data + * driver + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is null + * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is not handled by + * RmNet data module + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null + * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range + */ +int rmnet_set_logical_endpoint_config(struct net_device *dev, + int config_id, + u8 rmnet_mode, + struct net_device *egress_dev) +{ + struct rmnet_logical_ep_conf_s epconfig; + + LOGL("(%s, %d, %d, %s);", + dev->name, config_id, rmnet_mode, egress_dev->name); + + if (!egress_dev || + ((!_rmnet_is_physical_endpoint_associated(egress_dev)) && + (!rmnet_vnd_is_vnd(egress_dev)))) { + return RMNET_CONFIG_BAD_EGRESS_DEVICE; + } + + memset(&epconfig, 0, sizeof(struct rmnet_logical_ep_conf_s)); + epconfig.refcount = 1; + epconfig.rmnet_mode = rmnet_mode; + epconfig.egress_dev = egress_dev; + + return _rmnet_set_logical_endpoint_config(dev, config_id, &epconfig); +} + +/* rmnet_unset_logical_endpoint_config() - Un-set logical endpoing configuration + * on a device + * @dev: Device to set endpoint configuration on + * @config_id: logical endpoint id on device + * + * Retrieves the logical_endpoint_config structure and frees the egress device. + * Network device must already have association with RmNet Data driver + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated + * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range + */ +int rmnet_unset_logical_endpoint_config(struct net_device *dev, + int config_id) +{ + LOGL("(%s, %d);", dev->name, config_id); + + if (!dev || ((!_rmnet_is_physical_endpoint_associated(dev)) && + (!rmnet_vnd_is_vnd(dev)))) { + return RMNET_CONFIG_NO_SUCH_DEVICE; + } + + return _rmnet_unset_logical_endpoint_config(dev, config_id); +} + +/* rmnet_get_logical_endpoint_config() - Gets logical endpoing configuration + * for a device + * @dev: Device to get endpoint configuration on + * @config_id: logical endpoint id on device + * @rmnet_mode: (I/O) logical endpoint mode + * @egress_dev_name: (I/O) logical endpoint egress device name + * @egress_dev_name_size: The maximal size of the I/O egress_dev_name + * + * Retrieves the logical_endpoint_config structure. + * Network device must already have association with RmNet Data driver + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null + * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated + * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range or + * if the provided buffer size for egress dev name is too short + */ +int rmnet_get_logical_endpoint_config(struct net_device *dev, + int config_id, + u8 *rmnet_mode, + u8 *egress_dev_name, + size_t egress_dev_name_size) +{ + struct rmnet_logical_ep_conf_s *epconfig_l = 0; + size_t strlcpy_res = 0; + + LOGL("(%s, %d);", dev->name, config_id); + + if (!egress_dev_name || !rmnet_mode) + return RMNET_CONFIG_BAD_ARGUMENTS; + if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || + config_id >= RMNET_DATA_MAX_LOGICAL_EP) + return RMNET_CONFIG_BAD_ARGUMENTS; + + epconfig_l = _rmnet_get_logical_ep(dev, config_id); + + if (!epconfig_l || !epconfig_l->refcount) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + *rmnet_mode = epconfig_l->rmnet_mode; + + strlcpy_res = strlcpy(egress_dev_name, epconfig_l->egress_dev->name, + egress_dev_name_size); + + if (strlcpy_res >= egress_dev_name_size) + return RMNET_CONFIG_BAD_ARGUMENTS; + + return RMNET_CONFIG_OK; +} + +/* rmnet_create_vnd() - Create virtual network device node + * @id: RmNet virtual device node id + * + * Return: + * - result of rmnet_vnd_create_dev() + */ +int rmnet_create_vnd(int id) +{ + struct net_device *dev; + + ASSERT_RTNL(); + LOGL("(%d);", id); + return rmnet_vnd_create_dev(id, &dev, NULL, 0); +} + +/* rmnet_create_vnd_prefix() - Create virtual network device node + * @id: RmNet virtual device node id + * @prefix: String prefix for device name + * + * Return: + * - result of rmnet_vnd_create_dev() + */ +int rmnet_create_vnd_prefix(int id, const char *prefix) +{ + struct net_device *dev; + + ASSERT_RTNL(); + LOGL("(%d, \"%s\");", id, prefix); + return rmnet_vnd_create_dev(id, &dev, prefix, 0); +} + +/** + * rmnet_create_vnd_name() - Create virtual network device node + * @id: RmNet virtual device node id + * @prefix: String prefix for device name + * + * Return: + * - result of rmnet_vnd_create_dev() + */ +int rmnet_create_vnd_name(int id, const char *name) +{ + struct net_device *dev; + + ASSERT_RTNL(); + LOGL("(%d, \"%s\");", id, name); + return rmnet_vnd_create_dev(id, &dev, name, 1); +} + +/* rmnet_free_vnd() - Free virtual network device node + * @id: RmNet virtual device node id + * + * Return: + * - result of rmnet_vnd_free_dev() + */ +int rmnet_free_vnd(int id) +{ + LOGL("(%d);", id); + return rmnet_vnd_free_dev(id); +} + +static void _rmnet_free_vnd_later(struct work_struct *work) +{ + int i; + struct rmnet_free_vnd_work *fwork; + + fwork = container_of(work, struct rmnet_free_vnd_work, work); + + for (i = 0; i < fwork->count; i++) + rmnet_free_vnd(fwork->vnd_id[i]); + kfree(fwork); +} + +/* rmnet_force_unassociate_device() - Force a device to unassociate + * @dev: Device to unassociate + * + * Return: + * - void + */ +static void rmnet_force_unassociate_device(struct net_device *dev) +{ + int i, j; + struct net_device *vndev; + struct rmnet_phys_ep_config *config; + struct rmnet_logical_ep_conf_s *cfg; + struct rmnet_free_vnd_work *vnd_work; + + ASSERT_RTNL(); + + if (!dev) + return; + + if (!_rmnet_is_physical_endpoint_associated(dev)) { + LOGM("%s", "Called on unassociated device, skipping"); + return; + } + + trace_rmnet_unregister_cb_clear_vnds(dev); + vnd_work = kmalloc(sizeof(*vnd_work), GFP_KERNEL); + if (!vnd_work) { + LOGH("%s", "Out of Memory"); + return; + } + INIT_WORK(&vnd_work->work, _rmnet_free_vnd_later); + vnd_work->count = 0; + + /* Check the VNDs for offending mappings */ + for (i = 0, j = 0; i < RMNET_DATA_MAX_VND && + j < RMNET_DATA_MAX_VND; i++) { + vndev = rmnet_vnd_get_by_id(i); + if (!vndev) { + LOGL("VND %d not in use; skipping", i); + continue; + } + cfg = rmnet_vnd_get_le_config(vndev); + if (!cfg) { + LOGH("Got NULL config from VND %d", i); + continue; + } + if (cfg->refcount && (cfg->egress_dev == dev)) { + /* Make sure the device is down before clearing any of + * the mappings. Otherwise we could see a potential + * race condition if packets are actively being + * transmitted. + */ + dev_close(vndev); + rmnet_unset_logical_endpoint_config + (vndev, RMNET_LOCAL_LOGICAL_ENDPOINT); + vnd_work->vnd_id[j] = i; + j++; + } + } + if (j > 0) { + vnd_work->count = j; + schedule_work(&vnd_work->work); + } else { + kfree(vnd_work); + } + + config = _rmnet_get_phys_ep_config(dev); + + if (config) { + unsigned long flags; + + hrtimer_cancel(&config->hrtimer); + spin_lock_irqsave(&config->agg_lock, flags); + if (config->agg_state == RMNET_MAP_TXFER_SCHEDULED) { + if (config->agg_skb) { + kfree_skb(config->agg_skb); + config->agg_skb = NULL; + config->agg_count = 0; + memset(&config->agg_time, 0, + sizeof(struct timespec)); + } + config->agg_state = RMNET_MAP_AGG_IDLE; + } + spin_unlock_irqrestore(&config->agg_lock, flags); + + cfg = &config->local_ep; + + if (cfg && cfg->refcount) + rmnet_unset_logical_endpoint_config + (cfg->egress_dev, RMNET_LOCAL_LOGICAL_ENDPOINT); + } + + /* Clear the mappings on the phys ep */ + trace_rmnet_unregister_cb_clear_lepcs(dev); + rmnet_unset_logical_endpoint_config(dev, RMNET_LOCAL_LOGICAL_ENDPOINT); + for (i = 0; i < RMNET_DATA_MAX_LOGICAL_EP; i++) + rmnet_unset_logical_endpoint_config(dev, i); + rmnet_unassociate_network_device(dev); +} + +/* rmnet_config_notify_cb() - Callback for netdevice notifier chain + * @nb: Notifier block data + * @event: Netdevice notifier event ID + * @data: Contains a net device for which we are getting notified + * + * Return: + * - result of NOTIFY_DONE() + */ +int rmnet_config_notify_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct net_device *dev = netdev_notifier_info_to_dev(data); + + if (!dev) + return NOTIFY_DONE; + + LOGL("(..., %lu, %s)", event, dev->name); + + switch (event) { + case NETDEV_UNREGISTER: + trace_rmnet_unregister_cb_entry(dev); + LOGH("Kernel is trying to unregister %s", dev->name); + rmnet_force_unassociate_device(dev); + trace_rmnet_unregister_cb_exit(dev); + break; + + default: + trace_rmnet_unregister_cb_unhandled(dev); + LOGD("Unhandeled event [%lu]", event); + break; + } + + return NOTIFY_DONE; +} diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h new file mode 100644 index 000000000000..5e3a6cd1867f --- /dev/null +++ b/net/rmnet_data/rmnet_data_config.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2013-2014, 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. + * + * RMNET Data configuration engine + */ + +#include +#include +#include +#include +#include + +#ifndef _RMNET_DATA_CONFIG_H_ +#define _RMNET_DATA_CONFIG_H_ + +#define RMNET_DATA_MAX_LOGICAL_EP 256 + +/** + * struct rmnet_logical_ep_conf_s - Logical end-point configuration + * + * @refcount: Reference count for this endpoint. 0 signifies the endpoint is not + * configured for use + * @rmnet_mode: Specifies how the traffic should be finally delivered. Possible + * options are available in enum rmnet_config_endpoint_modes_e + * @mux_id: Virtual channel ID used by MAP protocol + * @egress_dev: Next device to deliver the packet to. Exact usage of this + * parmeter depends on the rmnet_mode + */ +struct rmnet_logical_ep_conf_s { + u8 refcount; + u8 rmnet_mode; + u8 mux_id; + struct timespec flush_time; + unsigned int flush_byte_count; + struct net_device *egress_dev; +}; + +/** + * struct rmnet_phys_ep_conf_s - Physical endpoint configuration + * One instance of this structure is instantiated for each net_device associated + * with rmnet_data. + * + * @dev: The device which is associated with rmnet_data. Corresponds to this + * specific instance of rmnet_phys_ep_conf_s + * @local_ep: Default non-muxed endpoint. Used for non-MAP protocols/formats + * @muxed_ep: All multiplexed logical endpoints associated with this device + * @ingress_data_format: RMNET_INGRESS_FORMAT_* flags from rmnet_data.h + * @egress_data_format: RMNET_EGRESS_FORMAT_* flags from rmnet_data.h + * + * @egress_agg_size: Maximum size (bytes) of data which should be aggregated + * @egress_agg_count: Maximum count (packets) of data which should be aggregated + * Smaller of the two parameters above are chosen for + * aggregation + * @tail_spacing: Guaranteed padding (bytes) when de-aggregating ingress frames + * @agg_time: Wall clock time when aggregated frame was created + * @agg_last: Last time the aggregation routing was invoked + */ +struct rmnet_phys_ep_config { + struct net_device *dev; + struct rmnet_logical_ep_conf_s local_ep; + struct rmnet_logical_ep_conf_s muxed_ep[RMNET_DATA_MAX_LOGICAL_EP]; + u32 ingress_data_format; + u32 egress_data_format; + + /* MAP specific */ + u16 egress_agg_size; + u16 egress_agg_count; + u8 tail_spacing; + /* MAP aggregation state machine + * - This is not sctrictly configuration and is updated at runtime + * Make sure all of these are protected by the agg_lock + */ + spinlock_t agg_lock; + struct sk_buff *agg_skb; + u8 agg_state; + u8 agg_count; + struct timespec agg_time; + struct timespec agg_last; + struct hrtimer hrtimer; +}; + +int rmnet_config_init(void); +void rmnet_config_exit(void); + +int rmnet_unassociate_network_device(struct net_device *dev); +int rmnet_set_ingress_data_format(struct net_device *dev, + u32 ingress_data_format, + u8 tail_spacing); +int rmnet_set_egress_data_format(struct net_device *dev, + u32 egress_data_format, + u16 agg_size, + u16 agg_count); +int rmnet_associate_network_device(struct net_device *dev); +int _rmnet_set_logical_endpoint_config + (struct net_device *dev, int config_id, + struct rmnet_logical_ep_conf_s *epconfig); +int rmnet_set_logical_endpoint_config(struct net_device *dev, + int config_id, + u8 rmnet_mode, + struct net_device *egress_dev); +int _rmnet_unset_logical_endpoint_config(struct net_device *dev, + int config_id); +int rmnet_unset_logical_endpoint_config(struct net_device *dev, + int config_id); +int _rmnet_get_logical_endpoint_config + (struct net_device *dev, int config_id, + struct rmnet_logical_ep_conf_s *epconfig); +int rmnet_get_logical_endpoint_config(struct net_device *dev, + int config_id, + u8 *rmnet_mode, + u8 *egress_dev_name, + size_t egress_dev_name_size); +void rmnet_config_netlink_msg_handler (struct sk_buff *skb); +int rmnet_config_notify_cb(struct notifier_block *nb, + unsigned long event, void *data); +int rmnet_create_vnd(int id); +int rmnet_create_vnd_prefix(int id, const char *name); +int rmnet_create_vnd_name(int id, const char *name); +int rmnet_free_vnd(int id); + +struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config + (struct net_device *dev); + +#endif /* _RMNET_DATA_CONFIG_H_ */ diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c new file mode 100644 index 000000000000..c26be78375f8 --- /dev/null +++ b/net/rmnet_data/rmnet_data_handlers.c @@ -0,0 +1,771 @@ +/* Copyright (c) 2013-2018, 2020 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. + * + * RMNET Data ingress/egress handler + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_private.h" +#include "rmnet_data_config.h" +#include "rmnet_data_vnd.h" +#include "rmnet_map.h" +#include "rmnet_data_stats.h" +#include "rmnet_data_trace.h" +#include "rmnet_data_handlers.h" + +RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_HANDLER); + +#ifdef CONFIG_RMNET_DATA_DEBUG_PKT +unsigned int dump_pkt_rx; +module_param(dump_pkt_rx, uint, 0644); +MODULE_PARM_DESC(dump_pkt_rx, "Dump packets entering ingress handler"); + +unsigned int dump_pkt_tx; +module_param(dump_pkt_tx, uint, 0644); +MODULE_PARM_DESC(dump_pkt_tx, "Dump packets exiting egress handler"); +#endif /* CONFIG_RMNET_DATA_DEBUG_PKT */ + +/* Time in nano seconds. This number must be less that a second. */ +long gro_flush_time __read_mostly = 10000L; +module_param(gro_flush_time, long, 0644); +MODULE_PARM_DESC(gro_flush_time, "Flush GRO when spaced more than this"); + +unsigned int gro_min_byte_thresh __read_mostly = 7500; +module_param(gro_min_byte_thresh, uint, 0644); +MODULE_PARM_DESC(gro_min_byte_thresh, "Min byte thresh to change flush time"); + +unsigned int dynamic_gro_on __read_mostly = 1; +module_param(dynamic_gro_on, uint, 0644); +MODULE_PARM_DESC(dynamic_gro_on, "Toggle to turn on dynamic gro logic"); + +unsigned int upper_flush_time __read_mostly = 15000; +module_param(upper_flush_time, uint, 0644); +MODULE_PARM_DESC(upper_flush_time, "Upper limit on flush time"); + +unsigned int upper_byte_limit __read_mostly = 10500; +module_param(upper_byte_limit, uint, 0644); +MODULE_PARM_DESC(upper_byte_limit, "Upper byte limit"); + +#define RMNET_DATA_IP_VERSION_4 0x40 +#define RMNET_DATA_IP_VERSION_6 0x60 + +#define RMNET_DATA_GRO_RCV_FAIL 0 +#define RMNET_DATA_GRO_RCV_PASS 1 + +/* Helper Functions */ + +/* __rmnet_data_set_skb_proto() - Set skb->protocol field + * @skb: packet being modified + * + * Peek at the first byte of the packet and set the protocol. There is not + * good way to determine if a packet has a MAP header. As of writing this, + * the reserved bit in the MAP frame will prevent it from overlapping with + * IPv4/IPv6 frames. This could change in the future! + */ +static inline void __rmnet_data_set_skb_proto(struct sk_buff *skb) +{ + switch (skb->data[0] & 0xF0) { + case RMNET_DATA_IP_VERSION_4: + skb->protocol = htons(ETH_P_IP); + break; + case RMNET_DATA_IP_VERSION_6: + skb->protocol = htons(ETH_P_IPV6); + break; + default: + skb->protocol = htons(ETH_P_MAP); + break; + } +} + +#ifdef CONFIG_RMNET_DATA_DEBUG_PKT +/* rmnet_print_packet() - Print packet / diagnostics + * @skb: Packet to print + * @printlen: Number of bytes to print + * @dev: Name of interface + * @dir: Character representing direction (e.g.. 'r' for receive) + * + * This function prints out raw bytes in an SKB. Use of this will have major + * performance impacts and may even trigger watchdog resets if too much is being + * printed. Hence, this should always be compiled out unless absolutely needed. + */ +void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir) +{ + char buffer[200]; + unsigned int len, printlen; + int i, buffloc = 0; + + switch (dir) { + case 'r': + printlen = dump_pkt_rx; + break; + + case 't': + printlen = dump_pkt_tx; + break; + + default: + printlen = 0; + break; + } + + if (!printlen) + return; + + pr_err("[%s][%c] - PKT skb->len=%d skb->head=%pK skb->data=%pK\n", + dev, dir, skb->len, (void *)skb->head, (void *)skb->data); + pr_err("[%s][%c] - PKT skb->tail=%pK skb->end=%pK\n", + dev, dir, skb_tail_pointer(skb), skb_end_pointer(skb)); + + if (skb->len > 0) + len = skb->len; + else + len = ((unsigned int)(uintptr_t)skb->end) - + ((unsigned int)(uintptr_t)skb->data); + + pr_err("[%s][%c] - PKT len: %d, printing first %d bytes\n", + dev, dir, len, printlen); + + memset(buffer, 0, sizeof(buffer)); + for (i = 0; (i < printlen) && (i < len); i++) { + if ((i % 16) == 0) { + pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer); + memset(buffer, 0, sizeof(buffer)); + buffloc = 0; + buffloc += snprintf(&buffer[buffloc], + sizeof(buffer) - buffloc, "%04X:", + i); + } + + buffloc += snprintf(&buffer[buffloc], sizeof(buffer) - buffloc, + " %02x", skb->data[i]); + } + pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer); +} +#else +void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir) +{ +} +#endif /* CONFIG_RMNET_DATA_DEBUG_PKT */ + +/* Generic handler */ + +/* rmnet_bridge_handler() - Bridge related functionality + * + * Return: + * - RX_HANDLER_CONSUMED in all cases + */ +static rx_handler_result_t rmnet_bridge_handler + (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep) +{ + if (!ep->egress_dev) { + LOGD("Missing egress device for packet arriving on %s", + skb->dev->name); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_BRDG_NO_EGRESS); + } else { + rmnet_data_egress_handler(skb, ep); + } + + return RX_HANDLER_CONSUMED; +} + +/* RX/TX Fixup */ + +/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook + * @skb: Socket buffer ("packet") to modify + * @dev: Virtual network device + * + * Additional VND specific packet processing for ingress packets + * + * Return: void + */ +static void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; +} + +/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook + * @skb: Socket buffer ("packet") to modify + * @dev: Virtual network device + * + * Additional VND specific packet processing for egress packets + * + * Return: void + */ +static void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; +} + +/* rmnet_check_skb_can_gro() - Check is skb can be passed through GRO handler + * + * Determines whether to pass the skb to the GRO handler napi_gro_receive() or + * handle normally by passing to netif_receive_skb(). + * + * Warning: + * This assumes that only TCP packets can be coalesced by the GRO handler which + * is not true in general. We lose the ability to use GRO for cases like UDP + * encapsulation protocols. + * + * Return: + * - RMNET_DATA_GRO_RCV_FAIL if packet is sent to netif_receive_skb() + * - RMNET_DATA_GRO_RCV_PASS if packet is sent to napi_gro_receive() + */ +static int rmnet_check_skb_can_gro(struct sk_buff *skb) +{ + switch (skb->data[0] & 0xF0) { + case RMNET_DATA_IP_VERSION_4: + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + return RMNET_DATA_GRO_RCV_PASS; + break; + case RMNET_DATA_IP_VERSION_6: + if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) + return RMNET_DATA_GRO_RCV_PASS; + /* Fall through */ + } + + return RMNET_DATA_GRO_RCV_FAIL; +} + +/* rmnet_optional_gro_flush() - Check if GRO handler needs to flush now + * + * Determines whether GRO handler needs to flush packets which it has + * coalesced so far. + * + * Tuning this parameter will trade TCP slow start performance for GRO coalesce + * ratio. + */ +static void rmnet_optional_gro_flush(struct napi_struct *napi, + struct rmnet_logical_ep_conf_s *ep, + unsigned int skb_size) +{ + struct timespec curr_time, diff; + + if (!gro_flush_time) + return; + + if (unlikely(ep->flush_time.tv_sec == 0)) { + getnstimeofday(&ep->flush_time); + ep->flush_byte_count = 0; + } else { + getnstimeofday(&(curr_time)); + diff = timespec_sub(curr_time, ep->flush_time); + ep->flush_byte_count += skb_size; + + if (dynamic_gro_on) { + if ((!(diff.tv_sec > 0) || diff.tv_nsec <= + gro_flush_time) && + ep->flush_byte_count >= + gro_min_byte_thresh) { + /* Processed many bytes in a small time window. + * No longer need to flush so often and we can + * increase our byte limit + */ + gro_flush_time = upper_flush_time; + gro_min_byte_thresh = upper_byte_limit; + } else if ((diff.tv_sec > 0 || + diff.tv_nsec > gro_flush_time) && + ep->flush_byte_count < + gro_min_byte_thresh) { + /* We have not hit our time limit and we are not + * receive many bytes. Demote ourselves to the + * lowest limits and flush + */ + napi_gro_flush(napi, false); + getnstimeofday(&ep->flush_time); + ep->flush_byte_count = 0; + gro_flush_time = 10000L; + gro_min_byte_thresh = 7500L; + } else if ((diff.tv_sec > 0 || + diff.tv_nsec > gro_flush_time) && + ep->flush_byte_count >= + gro_min_byte_thresh) { + /* Above byte and time limt, therefore we can + * move/maintain our limits to be the max + * and flush + */ + napi_gro_flush(napi, false); + getnstimeofday(&ep->flush_time); + ep->flush_byte_count = 0; + gro_flush_time = upper_flush_time; + gro_min_byte_thresh = upper_byte_limit; + } + /* else, below time limit and below + * byte thresh, so change nothing + */ + } else if (diff.tv_sec > 0 || + diff.tv_nsec >= gro_flush_time) { + napi_gro_flush(napi, false); + getnstimeofday(&ep->flush_time); + ep->flush_byte_count = 0; + } + } +} + +/* __rmnet_deliver_skb() - Deliver skb + * + * Determines where to deliver skb. Options are: consume by network stack, + * pass to bridge handler, or pass to virtual network device + * + * Return: + * - RX_HANDLER_CONSUMED if packet forwarded or dropped + * - RX_HANDLER_PASS if packet is to be consumed by network stack as-is + */ +static rx_handler_result_t __rmnet_deliver_skb + (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep) +{ + struct napi_struct *napi = NULL; + gro_result_t gro_res; + unsigned int skb_size; + + trace___rmnet_deliver_skb(skb); + switch (ep->rmnet_mode) { + case RMNET_EPMODE_VND: + skb_reset_transport_header(skb); + skb_reset_network_header(skb); + rmnet_vnd_rx_fixup(skb, skb->dev); + + skb->pkt_type = PACKET_HOST; + skb_set_mac_header(skb, 0); + + if (rmnet_check_skb_can_gro(skb) && + (skb->dev->features & NETIF_F_GRO)) { + napi = get_current_napi_context(); + + skb_size = skb->len; + gro_res = napi_gro_receive(napi, skb); + trace_rmnet_gro_downlink(gro_res); + rmnet_optional_gro_flush(napi, ep, skb_size); + } else{ + netif_receive_skb(skb); + } + return RX_HANDLER_CONSUMED; + + case RMNET_EPMODE_NONE: + return RX_HANDLER_PASS; + + case RMNET_EPMODE_BRIDGE: + return rmnet_bridge_handler(skb, ep); + + default: + LOGD("Unknown ep mode %d", ep->rmnet_mode); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP); + return RX_HANDLER_CONSUMED; + } +} + +/* rmnet_ingress_deliver_packet() - Ingress handler for raw IP and bridged + * MAP packets. + * @skb: Packet needing a destination. + * @config: Physical end point configuration that the packet arrived on. + * + * Return: + * - RX_HANDLER_CONSUMED if packet forwarded/dropped + * - RX_HANDLER_PASS if packet should be passed up the stack by caller + */ +static rx_handler_result_t rmnet_ingress_deliver_packet + (struct sk_buff *skb, struct rmnet_phys_ep_config *config) +{ + if (!config) { + LOGD("%s", "NULL physical EP provided"); + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + if (!(config->local_ep.refcount)) { + LOGD("Packet on %s has no local endpoint configuration", + skb->dev->name); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_IPINGRESS_NO_EP); + return RX_HANDLER_CONSUMED; + } + + skb->dev = config->local_ep.egress_dev; + + return __rmnet_deliver_skb(skb, &config->local_ep); +} + +/* MAP handler */ + +/* _rmnet_map_ingress_handler() - Actual MAP ingress handler + * @skb: Packet being received + * @config: Physical endpoint configuration for the ingress device + * + * Most MAP ingress functions are processed here. Packets are processed + * individually; aggregated packets should use rmnet_map_ingress_handler() + * + * Return: + * - RX_HANDLER_CONSUMED if packet is dropped + * - result of __rmnet_deliver_skb() for all other cases + */ +static rx_handler_result_t _rmnet_map_ingress_handler + (struct sk_buff *skb, struct rmnet_phys_ep_config *config) +{ + struct rmnet_logical_ep_conf_s *ep; + u8 mux_id; + u16 len; + int ckresult; + + if (RMNET_MAP_GET_CD_BIT(skb)) { + if (config->ingress_data_format + & RMNET_INGRESS_FORMAT_MAP_COMMANDS) + return rmnet_data_map_command(skb, config); + + LOGM("MAP command packet on %s; %s", skb->dev->name, + "Not configured for MAP commands"); + rmnet_kfree_skb(skb, + RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC); + return RX_HANDLER_CONSUMED; + } + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + len = RMNET_MAP_GET_LENGTH(skb) + - RMNET_MAP_GET_PAD(skb) + - config->tail_spacing; + + if (mux_id >= RMNET_DATA_MAX_LOGICAL_EP) { + LOGD("Got packet on %s with bad mux id %d", + skb->dev->name, mux_id); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX); + return RX_HANDLER_CONSUMED; + } + + ep = &config->muxed_ep[mux_id]; + + if (!ep->refcount) { + LOGE("Packet on %s:%d; has no logical endpoint config", + skb->dev->name, mux_id); + + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP); + return RX_HANDLER_CONSUMED; + } + + skb->dev = ep->egress_dev; + + if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) || + (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) { + ckresult = rmnet_map_data_checksum_downlink_packet(skb); + trace_rmnet_map_checksum_downlink_packet(skb, ckresult); + rmnet_stats_dl_checksum(ckresult); + if (likely((ckresult == RMNET_MAP_CHECKSUM_OK) || + (ckresult == RMNET_MAP_CHECKSUM_SKIPPED))) + skb->ip_summed |= CHECKSUM_UNNECESSARY; + else if (ckresult != + RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION && + ckresult != RMNET_MAP_CHECKSUM_VALIDATION_FAILED && + ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT && + ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET && + ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) { + rmnet_kfree_skb + (skb, RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM); + return RX_HANDLER_CONSUMED; + } + } + + /* Subtract MAP header */ + skb_pull(skb, sizeof(struct rmnet_map_header_s)); + skb_trim(skb, len); + __rmnet_data_set_skb_proto(skb); + return __rmnet_deliver_skb(skb, ep); +} + +/* rmnet_map_ingress_handler() - MAP ingress handler + * @skb: Packet being received + * @config: Physical endpoint configuration for the ingress device + * + * Called if and only if MAP is configured in the ingress device's ingress data + * format. Deaggregation is done here, actual MAP processing is done in + * _rmnet_map_ingress_handler(). + * + * Return: + * - RX_HANDLER_CONSUMED for aggregated packets + * - RX_HANDLER_CONSUMED for dropped packets + * - result of _rmnet_map_ingress_handler() for all other cases + */ +static rx_handler_result_t rmnet_map_ingress_handler + (struct sk_buff *skb, struct rmnet_phys_ep_config *config) +{ + struct sk_buff *skbn; + int rc; + + if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { + trace_rmnet_start_deaggregation(skb); + while ((skbn = rmnet_data_map_deaggregate(skb, config)) != 0) + _rmnet_map_ingress_handler(skbn, config); + + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF); + rc = RX_HANDLER_CONSUMED; + } else { + rc = _rmnet_map_ingress_handler(skb, config); + } + + return rc; +} + +/* rmnet_map_egress_handler() - MAP egress handler + * @skb: Packet being sent + * @config: Physical endpoint configuration for the egress device + * @ep: logical endpoint configuration of the packet originator + * (e.g.. RmNet virtual network device) + * @orig_dev: The originator vnd device + * + * Called if and only if MAP is configured in the egress device's egress data + * format. Will expand skb if there is insufficient headroom for MAP protocol. + * Note: headroomexpansion will incur a performance penalty. + * + * Return: + * - 0 on success + * - 1 on failure + */ +static int rmnet_map_egress_handler(struct sk_buff *skb, + struct rmnet_phys_ep_config *config, + struct rmnet_logical_ep_conf_s *ep, + struct net_device *orig_dev) +{ + int required_headroom, additional_header_length, ckresult; + struct rmnet_map_header_s *map_header; + int non_linear_skb; + int csum_required = (config->egress_data_format & + RMNET_EGRESS_FORMAT_MAP_CKSUMV3) || + (config->egress_data_format & + RMNET_EGRESS_FORMAT_MAP_CKSUMV4); + + additional_header_length = 0; + + required_headroom = sizeof(struct rmnet_map_header_s); + if (csum_required) { + required_headroom += + sizeof(struct rmnet_map_ul_checksum_header_s); + additional_header_length += + sizeof(struct rmnet_map_ul_checksum_header_s); + } + + LOGD("headroom of %d bytes", required_headroom); + + if (skb_headroom(skb) < required_headroom) { + LOGE("Not enough headroom for %d bytes", required_headroom); + kfree_skb(skb); + return 1; + } + + if (csum_required) { + ckresult = rmnet_map_data_checksum_uplink_packet + (skb, orig_dev, config->egress_data_format); + trace_rmnet_map_checksum_uplink_packet(orig_dev, ckresult); + rmnet_stats_ul_checksum(ckresult); + } + + non_linear_skb = (orig_dev->features & NETIF_F_GSO) && + skb_is_nonlinear(skb); + + if ((!(config->egress_data_format & + RMNET_EGRESS_FORMAT_AGGREGATION)) || csum_required || + non_linear_skb) + map_header = rmnet_data_map_add_map_header + (skb, additional_header_length, RMNET_MAP_NO_PAD_BYTES); + else + map_header = rmnet_data_map_add_map_header + (skb, additional_header_length, RMNET_MAP_ADD_PAD_BYTES); + + if (!map_header) { + LOGD("%s", "Failed to add MAP header to egress packet"); + kfree_skb(skb); + return 1; + } + + if (config->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { + if (ep->mux_id == 0xff) + map_header->mux_id = 0; + else + map_header->mux_id = ep->mux_id; + } + + skb->protocol = htons(ETH_P_MAP); + + if (config->egress_data_format & RMNET_EGRESS_FORMAT_AGGREGATION) { + if (rmnet_ul_aggregation_skip(skb, required_headroom)) + return RMNET_MAP_SUCCESS; + + if (non_linear_skb) + if (unlikely(__skb_linearize(skb))) + return RMNET_MAP_SUCCESS; + + rmnet_map_aggregate(skb, config); + return RMNET_MAP_CONSUMED; + } + + return RMNET_MAP_SUCCESS; +} + +/* Ingress / Egress Entry Points */ + +/* rmnet_ingress_handler() - Ingress handler entry point + * @skb: Packet being received + * + * Processes packet as per ingress data format for receiving device. Logical + * endpoint is determined from packet inspection. Packet is then sent to the + * egress device listed in the logical endpoint configuration. + * + * Return: + * - RX_HANDLER_PASS if packet is not processed by handler (caller must + * deal with the packet) + * - RX_HANDLER_CONSUMED if packet is forwarded or processed by MAP + */ +rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb) +{ + struct rmnet_phys_ep_config *config; + struct net_device *dev; + int rc; + + if (!skb) + return RX_HANDLER_CONSUMED; + + dev = skb->dev; + trace_rmnet_ingress_handler(skb); + rmnet_print_packet(skb, dev->name, 'r'); + + config = _rmnet_get_phys_ep_config(skb->dev); + + if (!config) { + LOGD("%s is not associated with rmnet_data", skb->dev->name); + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + /* Sometimes devices operate in ethernet mode even thouth there is no + * ethernet header. This causes the skb->protocol to contain a bogus + * value and the skb->data pointer to be off by 14 bytes. Fix it if + * configured to do so + */ + if (config->ingress_data_format & RMNET_INGRESS_FIX_ETHERNET) { + skb_push(skb, RMNET_ETHERNET_HEADER_LENGTH); + __rmnet_data_set_skb_proto(skb); + } + + if (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { + rc = rmnet_map_ingress_handler(skb, config); + } else { + switch (ntohs(skb->protocol)) { + case ETH_P_MAP: + if (config->local_ep.rmnet_mode == + RMNET_EPMODE_BRIDGE) { + rc = rmnet_ingress_deliver_packet(skb, config); + } else { + LOGD("MAP packet on %s; MAP not set", + dev->name); + rmnet_kfree_skb + (skb, + RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD); + rc = RX_HANDLER_CONSUMED; + } + break; + + case ETH_P_ARP: + case ETH_P_IP: + case ETH_P_IPV6: + rc = rmnet_ingress_deliver_packet(skb, config); + break; + + default: + LOGD("Unknown skb->proto 0x%04X\n", + ntohs(skb->protocol) & 0xFFFF); + rc = RX_HANDLER_PASS; + } + } + + return rc; +} + +/* rmnet_data_rx_handler() - Rx handler callback registered with kernel + * @pskb: Packet to be processed by rx handler + * + * Standard kernel-expected footprint for rx handlers. Calls + * rmnet_ingress_handler with correctly formatted arguments + * + * Return: + * - Whatever rmnet_ingress_handler() returns + */ +rx_handler_result_t rmnet_data_rx_handler(struct sk_buff **pskb) +{ + return rmnet_ingress_handler(*pskb); +} + +/* rmnet_data_egress_handler() - Egress handler entry point + * @skb: packet to transmit + * @ep: logical endpoint configuration of the packet originator + * (e.g.. RmNet virtual network device) + * + * Modifies packet as per logical endpoint configuration and egress data format + * for egress device configured in logical endpoint. Packet is then transmitted + * on the egress device. + */ +void rmnet_data_egress_handler(struct sk_buff *skb, + struct rmnet_logical_ep_conf_s *ep) +{ + struct rmnet_phys_ep_config *config; + struct net_device *orig_dev; + int rc; + + orig_dev = skb->dev; + skb->dev = ep->egress_dev; + + config = _rmnet_get_phys_ep_config(skb->dev); + + if (!config) { + LOGD("%s is not associated with rmnet_data", skb->dev->name); + kfree_skb(skb); + return; + } + + LOGD("Packet going out on %s with egress format 0x%08X", + skb->dev->name, config->egress_data_format); + + if (ep->rmnet_mode == RMNET_EPMODE_VND) + rmnet_vnd_tx_fixup(skb, orig_dev); + + if (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { + switch (rmnet_map_egress_handler(skb, config, ep, orig_dev)) { + case RMNET_MAP_CONSUMED: + LOGD("%s", "MAP process consumed packet"); + return; + + case RMNET_MAP_SUCCESS: + break; + + default: + LOGD("MAP egress failed on packet on %s", + skb->dev->name); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_EGR_MAPFAIL); + return; + } + } + + rmnet_print_packet(skb, skb->dev->name, 't'); + trace_rmnet_egress_handler(skb); + + if (config->egress_data_format & RMNET_EGRESS_FORMAT__RESERVED__) + skb_push(skb, RMNET_ETHERNET_HEADER_LENGTH); + + rc = dev_queue_xmit(skb); + if (rc != 0) { + LOGD("Failed to queue packet for transmission on [%s]", + skb->dev->name); + } + rmnet_stats_queue_xmit(rc, RMNET_STATS_QUEUE_XMIT_EGRESS); +} diff --git a/net/rmnet_data/rmnet_data_handlers.h b/net/rmnet_data/rmnet_data_handlers.h new file mode 100644 index 000000000000..a61266703e05 --- /dev/null +++ b/net/rmnet_data/rmnet_data_handlers.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2013, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data ingress/egress handler + */ + +#ifndef _RMNET_DATA_HANDLERS_H_ +#define _RMNET_DATA_HANDLERS_H_ + +void rmnet_data_egress_handler(struct sk_buff *skb, + struct rmnet_logical_ep_conf_s *ep); + +rx_handler_result_t rmnet_data_rx_handler(struct sk_buff **pskb); + +#endif /* _RMNET_DATA_HANDLERS_H_ */ diff --git a/net/rmnet_data/rmnet_data_main.c b/net/rmnet_data/rmnet_data_main.c new file mode 100644 index 000000000000..b5baa6f9410f --- /dev/null +++ b/net/rmnet_data/rmnet_data_main.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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. + * + * + * RMNET Data generic framework + */ + +#include +#include +#include +#include "rmnet_data_private.h" +#include "rmnet_data_config.h" +#include "rmnet_data_vnd.h" + +/* Trace Points */ +#define CREATE_TRACE_POINTS +#include "rmnet_data_trace.h" + +/* Module Parameters */ +unsigned int rmnet_data_log_level = RMNET_LOG_LVL_ERR | RMNET_LOG_LVL_HI; +module_param(rmnet_data_log_level, uint, 0644); +MODULE_PARM_DESC(log_level, "Logging level"); + +unsigned int rmnet_data_log_module_mask; +module_param(rmnet_data_log_module_mask, uint, 0644); +MODULE_PARM_DESC(rmnet_data_log_module_mask, "Logging module mask"); + +/* Startup/Shutdown */ + +/* rmnet_init() - Module initialization + * + * todo: check for (and init) startup errors + */ +static int __init rmnet_init(void) +{ + rmnet_config_init(); + rmnet_vnd_init(); + + LOGL("%s", "RMNET Data driver loaded successfully"); + return 0; +} + +static void __exit rmnet_exit(void) +{ + rmnet_config_exit(); + rmnet_vnd_exit(); +} + +module_init(rmnet_init) +module_exit(rmnet_exit) +MODULE_LICENSE("GPL v2"); diff --git a/net/rmnet_data/rmnet_data_private.h b/net/rmnet_data/rmnet_data_private.h new file mode 100644 index 000000000000..0ac68cbd8ffa --- /dev/null +++ b/net/rmnet_data/rmnet_data_private.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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 _RMNET_DATA_PRIVATE_H_ +#define _RMNET_DATA_PRIVATE_H_ + +#define RMNET_DATA_MAX_VND 32 +#define RMNET_DATA_MAX_PACKET_SIZE 16384 +#define RMNET_DATA_DFLT_PACKET_SIZE 1500 +#define RMNET_DATA_DEV_NAME_STR "rmnet_data" +#define RMNET_DATA_NEEDED_HEADROOM 16 +#define RMNET_DATA_TX_QUEUE_LEN 1000 +#define RMNET_ETHERNET_HEADER_LENGTH 14 + +extern unsigned int rmnet_data_log_level; +extern unsigned int rmnet_data_log_module_mask; + +#define RMNET_INIT_OK 0 +#define RMNET_INIT_ERROR 1 + +#define RMNET_LOG_LVL_DBG BIT(4) +#define RMNET_LOG_LVL_LOW BIT(3) +#define RMNET_LOG_LVL_MED BIT(2) +#define RMNET_LOG_LVL_HI BIT(1) +#define RMNET_LOG_LVL_ERR BIT(0) + +#define RMNET_LOG_MODULE(X) \ + static u32 rmnet_mod_mask = X + +#define RMNET_DATA_LOGMASK_CONFIG BIT(0) +#define RMNET_DATA_LOGMASK_HANDLER BIT(1) +#define RMNET_DATA_LOGMASK_VND BIT(2) +#define RMNET_DATA_LOGMASK_MAPD BIT(3) +#define RMNET_DATA_LOGMASK_MAPC BIT(4) + +#define LOGE(fmt, ...) do { if (rmnet_data_log_level & RMNET_LOG_LVL_ERR) \ + pr_err("[RMNET:ERR] %s(): " fmt "\n", __func__, \ + ##__VA_ARGS__); \ + } while (0) + +#define LOGH(fmt, ...) do { if (rmnet_data_log_level & RMNET_LOG_LVL_HI) \ + pr_err("[RMNET:HI] %s(): " fmt "\n", __func__, \ + ##__VA_ARGS__); \ + } while (0) + +#define LOGM(fmt, ...) do { if (rmnet_data_log_level & RMNET_LOG_LVL_MED) \ + pr_warn("[RMNET:MED] %s(): " fmt "\n", __func__, \ + ##__VA_ARGS__); \ + } while (0) + +#define LOGL(fmt, ...) do { if (unlikely \ + (rmnet_data_log_level & RMNET_LOG_LVL_LOW)) \ + pr_notice("[RMNET:LOW] %s(): " fmt "\n", __func__, \ + ##__VA_ARGS__); \ + } while (0) + +/* Don't use pr_debug as it is compiled out of the kernel. We can be sure of + * minimal impact as LOGD is not enabled by default. + */ +#define LOGD(fmt, ...) do { if (unlikely( \ + (rmnet_data_log_level & RMNET_LOG_LVL_DBG) && \ + (rmnet_data_log_module_mask & rmnet_mod_mask))) \ + pr_notice("[RMNET:DBG] %s(): " fmt "\n", __func__, \ + ##__VA_ARGS__); \ + } while (0) + +#endif /* _RMNET_DATA_PRIVATE_H_ */ diff --git a/net/rmnet_data/rmnet_data_stats.c b/net/rmnet_data/rmnet_data_stats.c new file mode 100644 index 000000000000..db74c4fd03c5 --- /dev/null +++ b/net/rmnet_data/rmnet_data_stats.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * RMNET Data statistics + */ + +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_private.h" +#include "rmnet_data_stats.h" +#include "rmnet_data_config.h" +#include "rmnet_map.h" + +enum rmnet_deagg_e { + RMNET_STATS_AGG_BUFF, + RMNET_STATS_AGG_PKT, + RMNET_STATS_AGG_MAX +}; + +static DEFINE_SPINLOCK(rmnet_skb_free_lock); +unsigned long int skb_free[RMNET_STATS_SKBFREE_MAX]; +module_param_array(skb_free, ulong, 0, 0444); +MODULE_PARM_DESC(skb_free, "SKBs dropped or freed"); + +static DEFINE_SPINLOCK(rmnet_queue_xmit_lock); +unsigned long int queue_xmit[RMNET_STATS_QUEUE_XMIT_MAX * 2]; +module_param_array(queue_xmit, ulong, 0, 0444); +MODULE_PARM_DESC(queue_xmit, "SKBs queued for transmit"); + +static DEFINE_SPINLOCK(rmnet_agg_count); +unsigned long int agg_count[RMNET_STATS_AGG_MAX]; +module_param_array(agg_count, ulong, 0, 0444); +MODULE_PARM_DESC(agg_count, "SKBs Aggregated"); + +static DEFINE_SPINLOCK(rmnet_checksum_dl_stats); +unsigned long int checksum_dl_stats[RMNET_MAP_CHECKSUM_ENUM_LENGTH]; +module_param_array(checksum_dl_stats, ulong, 0, 0444); +MODULE_PARM_DESC(checksum_dl_stats, "Downlink Checksum Statistics"); + +static DEFINE_SPINLOCK(rmnet_checksum_ul_stats); +unsigned long int checksum_ul_stats[RMNET_MAP_CHECKSUM_ENUM_LENGTH]; +module_param_array(checksum_ul_stats, ulong, 0, 0444); +MODULE_PARM_DESC(checksum_ul_stats, "Uplink Checksum Statistics"); + +void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason) +{ + unsigned long flags; + + if (reason >= RMNET_STATS_SKBFREE_MAX) + reason = RMNET_STATS_SKBFREE_UNKNOWN; + + spin_lock_irqsave(&rmnet_skb_free_lock, flags); + skb_free[reason]++; + spin_unlock_irqrestore(&rmnet_skb_free_lock, flags); + + kfree_skb(skb); +} + +void rmnet_stats_queue_xmit(int rc, unsigned int reason) +{ + unsigned long flags; + + if (rc != 0) + reason += RMNET_STATS_QUEUE_XMIT_MAX; + if (reason >= RMNET_STATS_QUEUE_XMIT_MAX * 2) + reason = RMNET_STATS_SKBFREE_UNKNOWN; + + spin_lock_irqsave(&rmnet_queue_xmit_lock, flags); + queue_xmit[reason]++; + spin_unlock_irqrestore(&rmnet_queue_xmit_lock, flags); +} + +void rmnet_stats_agg_pkts(int aggcount) +{ + unsigned long flags; + + spin_lock_irqsave(&rmnet_agg_count, flags); + agg_count[RMNET_STATS_AGG_BUFF]++; + agg_count[RMNET_STATS_AGG_PKT] += aggcount; + spin_unlock_irqrestore(&rmnet_agg_count, flags); +} + +void rmnet_stats_dl_checksum(unsigned int rc) +{ + unsigned long flags; + + if (rc >= RMNET_MAP_CHECKSUM_ENUM_LENGTH) + rc = RMNET_MAP_CHECKSUM_ERR_UNKNOWN; + + spin_lock_irqsave(&rmnet_checksum_dl_stats, flags); + checksum_dl_stats[rc]++; + spin_unlock_irqrestore(&rmnet_checksum_dl_stats, flags); +} + +void rmnet_stats_ul_checksum(unsigned int rc) +{ + unsigned long flags; + + if (rc >= RMNET_MAP_CHECKSUM_ENUM_LENGTH) + rc = RMNET_MAP_CHECKSUM_ERR_UNKNOWN; + + spin_lock_irqsave(&rmnet_checksum_ul_stats, flags); + checksum_ul_stats[rc]++; + spin_unlock_irqrestore(&rmnet_checksum_ul_stats, flags); +} diff --git a/net/rmnet_data/rmnet_data_stats.h b/net/rmnet_data/rmnet_data_stats.h new file mode 100644 index 000000000000..e3350ef566ca --- /dev/null +++ b/net/rmnet_data/rmnet_data_stats.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * RMNET Data statistics + * + */ + +#ifndef _RMNET_DATA_STATS_H_ +#define _RMNET_DATA_STATS_H_ + +enum rmnet_skb_free_e { + RMNET_STATS_SKBFREE_UNKNOWN, + RMNET_STATS_SKBFREE_BRDG_NO_EGRESS, + RMNET_STATS_SKBFREE_DELIVER_NO_EP, + RMNET_STATS_SKBFREE_IPINGRESS_NO_EP, + RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX, + RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP, + RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF, + RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD, + RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC, + RMNET_STATS_SKBFREE_EGR_MAPFAIL, + RMNET_STATS_SKBFREE_VND_NO_EGRESS, + RMNET_STATS_SKBFREE_MAPC_BAD_MUX, + RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP, + RMNET_STATS_SKBFREE_AGG_CPY_EXPAND, + RMNET_STATS_SKBFREE_AGG_INTO_BUFF, + RMNET_STATS_SKBFREE_DEAGG_MALFORMED, + RMNET_STATS_SKBFREE_DEAGG_CLONE_FAIL, + RMNET_STATS_SKBFREE_DEAGG_UNKNOWN_IP_TYPE, + RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0, + RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM, + RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED, + RMNET_STATS_SKBFREE_MAX +}; + +enum rmnet_queue_xmit_e { + RMNET_STATS_QUEUE_XMIT_UNKNOWN, + RMNET_STATS_QUEUE_XMIT_EGRESS, + RMNET_STATS_QUEUE_XMIT_AGG_FILL_BUFFER, + RMNET_STATS_QUEUE_XMIT_AGG_TIMEOUT, + RMNET_STATS_QUEUE_XMIT_AGG_CPY_EXP_FAIL, + RMNET_STATS_QUEUE_XMIT_AGG_SKIP, + RMNET_STATS_QUEUE_XMIT_MAX +}; + +void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason); +void rmnet_stats_queue_xmit(int rc, unsigned int reason); +void rmnet_stats_deagg_pkts(int aggcount); +void rmnet_stats_agg_pkts(int aggcount); +void rmnet_stats_dl_checksum(unsigned int rc); +void rmnet_stats_ul_checksum(unsigned int rc); +#endif /* _RMNET_DATA_STATS_H_ */ diff --git a/net/rmnet_data/rmnet_data_trace.h b/net/rmnet_data/rmnet_data_trace.h new file mode 100644 index 000000000000..7c3aa9a5a01f --- /dev/null +++ b/net/rmnet_data/rmnet_data_trace.h @@ -0,0 +1,358 @@ +/* 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 + * 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. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rmnet_data +#define TRACE_INCLUDE_FILE rmnet_data_trace + +#if !defined(_TRACE_MSM_LOW_POWER_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _RMNET_DATA_TRACE_H_ + +#include +#include +#include + +DECLARE_EVENT_CLASS + (rmnet_handler_template, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb), + + TP_STRUCT__entry( + __field(void *, skbaddr) + __field(unsigned int, len) + __string(name, skb->dev->name) + ), + + TP_fast_assign( + __entry->skbaddr = skb; + __entry->len = skb->len; + __assign_str(name, skb->dev->name); + ), + + TP_printk("dev=%s skbaddr=%pK len=%u", + __get_str(name), __entry->skbaddr, __entry->len) +) + +DEFINE_EVENT + (rmnet_handler_template, rmnet_egress_handler, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb) +); + +DEFINE_EVENT + (rmnet_handler_template, rmnet_ingress_handler, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb) +); + +DEFINE_EVENT + (rmnet_handler_template, rmnet_vnd_start_xmit, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb) +); + +DEFINE_EVENT + (rmnet_handler_template, __rmnet_deliver_skb, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb) +); + +DECLARE_EVENT_CLASS + (rmnet_tc_fc_template, + + TP_PROTO(u32 tcm_handle, int qdisc_len, int is_enable), + + TP_ARGS(tcm_handle, qdisc_len, is_enable), + + TP_STRUCT__entry( + __field(u32, handle) + __field(int, qlen) + __field(int, enable) + ), + + TP_fast_assign( + __entry->handle = tcm_handle; + __entry->qlen = qdisc_len; + __entry->enable = is_enable; + ), + + TP_printk("tcm_handle=%d qdisc length=%d flow %s", + __entry->handle, __entry->qlen, + __entry->enable ? "enable" : "disable") +) + +DEFINE_EVENT + (rmnet_tc_fc_template, rmnet_fc_qmi, + + TP_PROTO(u32 tcm_handle, int qdisc_len, int is_enable), + + TP_ARGS(tcm_handle, qdisc_len, is_enable) +); + +DEFINE_EVENT + (rmnet_tc_fc_template, rmnet_fc_map, + + TP_PROTO(u32 tcm_handle, int qdisc_len, int is_enable), + + TP_ARGS(tcm_handle, qdisc_len, is_enable) +); + +DECLARE_EVENT_CLASS + (rmnet_aggregation_template, + + TP_PROTO(struct sk_buff *skb, int num_agg_pakcets), + + TP_ARGS(skb, num_agg_pakcets), + + TP_STRUCT__entry( + __field(void *, skbaddr) + __field(unsigned int, len) + __string(name, skb->dev->name) + __field(int, num) + ), + + TP_fast_assign( + __entry->skbaddr = skb; + __entry->len = skb->len; + __assign_str(name, skb->dev->name); + __entry->num = num_agg_pakcets; + ), + + TP_printk("dev=%s skbaddr=%pK len=%u agg_count: %d", + __get_str(name), __entry->skbaddr, __entry->len, + __entry->num) +) + +DEFINE_EVENT + (rmnet_aggregation_template, rmnet_map_aggregate, + + TP_PROTO(struct sk_buff *skb, int num_agg_pakcets), + + TP_ARGS(skb, num_agg_pakcets) +); + +DEFINE_EVENT + (rmnet_aggregation_template, rmnet_map_flush_packet_queue, + + TP_PROTO(struct sk_buff *skb, int num_agg_pakcets), + + TP_ARGS(skb, num_agg_pakcets) +); + +TRACE_EVENT + (rmnet_start_aggregation, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb), + + TP_STRUCT__entry( + __string(name, skb->dev->name) + ), + + TP_fast_assign( + __assign_str(name, skb->dev->name); + ), + + TP_printk("dev: %s, aggregated first packet", __get_str(name)) +) + +TRACE_EVENT + (rmnet_start_deaggregation, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb), + + TP_STRUCT__entry( + __string(name, skb->dev->name) + ), + + TP_fast_assign( + __assign_str(name, skb->dev->name); + ), + + TP_printk("dev: %s, deaggregated first packet", __get_str(name)) +) + +TRACE_EVENT + (rmnet_end_deaggregation, + + TP_PROTO(struct sk_buff *skb, int num_deagg_packets), + + TP_ARGS(skb, num_deagg_packets), + + TP_STRUCT__entry( + __string(name, skb->dev->name) + __field(int, num) + ), + + TP_fast_assign( + __assign_str(name, skb->dev->name); + __entry->num = num_deagg_packets; + ), + + TP_printk("dev: %s, deaggregate end count: %d", + __get_str(name), __entry->num) +) + +TRACE_EVENT + (rmnet_map_checksum_downlink_packet, + + TP_PROTO(struct sk_buff *skb, int ckresult), + + TP_ARGS(skb, ckresult), + + TP_STRUCT__entry( + __string(name, skb->dev->name) + __field(int, res) + ), + + TP_fast_assign( + __assign_str(name, skb->dev->name); + __entry->res = ckresult; + ), + + TP_printk("DL checksum on dev=%s, res: %d", + __get_str(name), __entry->res) +) + +TRACE_EVENT + (rmnet_map_checksum_uplink_packet, + + TP_PROTO(struct net_device *dev, int ckresult), + + TP_ARGS(dev, ckresult), + + TP_STRUCT__entry( + __string(name, dev->name) + __field(int, res) + ), + + TP_fast_assign( + __assign_str(name, dev->name); + __entry->res = ckresult; + ), + + TP_printk("UL checksum on dev=%s, res: %d", + __get_str(name), __entry->res) +) + +DECLARE_EVENT_CLASS + (rmnet_physdev_action_template, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev), + + TP_STRUCT__entry( + __string(name, dev->name) + ), + + TP_fast_assign( + __assign_str(name, dev->name); + ), + + TP_printk("Physical dev=%s", __get_str(name)) +) + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unregister_cb_unhandled, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unregister_cb_entry, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unregister_cb_exit, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unregister_cb_clear_vnds, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unregister_cb_clear_lepcs, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_associate, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +DEFINE_EVENT + (rmnet_physdev_action_template, rmnet_unassociate, + + TP_PROTO(struct net_device *dev), + + TP_ARGS(dev) +); + +TRACE_EVENT + (rmnet_gro_downlink, + + TP_PROTO(gro_result_t gro_res), + + TP_ARGS(gro_res), + + TP_STRUCT__entry( + __field(gro_result_t, res) + ), + + TP_fast_assign( + __entry->res = gro_res; + ), + + TP_printk("GRO res: %d", __entry->res) +) + +#endif /* _RMNET_DATA_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include + diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c new file mode 100644 index 000000000000..1d7d4f6d7bd7 --- /dev/null +++ b/net/rmnet_data/rmnet_data_vnd.c @@ -0,0 +1,1061 @@ +/* Copyright (c) 2013-2018, 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. + * + * + * RMNET Data virtual network driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_config.h" +#include "rmnet_data_handlers.h" +#include "rmnet_data_private.h" +#include "rmnet_map.h" +#include "rmnet_data_vnd.h" +#include "rmnet_data_stats.h" +#include "rmnet_data_trace.h" + +RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_VND); + +#define RMNET_MAP_FLOW_NUM_TC_HANDLE 3 +#define RMNET_VND_UF_ACTION_ADD 0 +#define RMNET_VND_UF_ACTION_DEL 1 +enum { + RMNET_VND_UPDATE_FLOW_OK, + RMNET_VND_UPDATE_FLOW_NO_ACTION, + RMNET_VND_UPDATE_FLOW_NO_MORE_ROOM, + RMNET_VND_UPDATE_FLOW_NO_VALID_LEFT +}; + +struct net_device *rmnet_devices[RMNET_DATA_MAX_VND]; + +struct rmnet_map_flow_mapping_s { + struct list_head list; + u32 map_flow_id; + u32 tc_flow_valid[RMNET_MAP_FLOW_NUM_TC_HANDLE]; + u32 tc_flow_id[RMNET_MAP_FLOW_NUM_TC_HANDLE]; + atomic_t v4_seq; + atomic_t v6_seq; +}; + +struct rmnet_vnd_private_s { + u32 qos_version; + struct rmnet_logical_ep_conf_s local_ep; + + rwlock_t flow_map_lock; + struct list_head flow_head; + struct rmnet_map_flow_mapping_s root_flow; +}; + +#define RMNET_VND_FC_QUEUED 0 +#define RMNET_VND_FC_NOT_ENABLED 1 +#define RMNET_VND_FC_KMALLOC_ERR 2 + +/* Helper Functions */ + +/* rmnet_vnd_add_qos_header() - Adds QoS header to front of skb->data + * @skb: Socket buffer ("packet") to modify + * @dev: Egress interface + * + * Does not check for sufficient headroom! Caller must make sure there is enough + * headroom. + */ +static void rmnet_vnd_add_qos_header(struct sk_buff *skb, + struct net_device *dev, + uint32_t qos_version) +{ + struct QMI_QOS_HDR_S *qmih; + struct qmi_qos_hdr8_s *qmi8h; + + if (qos_version & RMNET_IOCTL_QOS_MODE_6) { + qmih = (struct QMI_QOS_HDR_S *) + skb_push(skb, sizeof(struct QMI_QOS_HDR_S)); + qmih->version = 1; + qmih->flags = 0; + qmih->flow_id = skb->mark; + } else if (qos_version & RMNET_IOCTL_QOS_MODE_8) { + qmi8h = (struct qmi_qos_hdr8_s *) + skb_push(skb, sizeof(struct qmi_qos_hdr8_s)); + /* Flags are 0 always */ + qmi8h->hdr.version = 0; + qmi8h->hdr.flags = 0; + memset(qmi8h->reserved, 0, sizeof(qmi8h->reserved)); + qmi8h->hdr.flow_id = skb->mark; + } else { + LOGD("%s(): Bad QoS version configured\n", __func__); + } +} + +/* Network Device Operations */ + +/* rmnet_vnd_start_xmit() - Transmit NDO callback + * @skb: Socket buffer ("packet") being sent from network stack + * @dev: Virtual Network Device + * + * Standard network driver operations hook to transmit packets on virtual + * network device. Called by network stack. Packet is not transmitted directly + * from here; instead it is given to the rmnet egress handler. + * + * Return: + * - NETDEV_TX_OK under all cirumstances (cannot block/fail) + */ +static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct rmnet_vnd_private_s *dev_conf; + + trace_rmnet_vnd_start_xmit(skb); + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + if (dev_conf->local_ep.egress_dev) { + /* QoS header should come after MAP header */ + if (dev_conf->qos_version) + rmnet_vnd_add_qos_header(skb, + dev, + dev_conf->qos_version); + skb_orphan(skb); + rmnet_data_egress_handler(skb, &dev_conf->local_ep); + } else { + dev->stats.tx_dropped++; + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_VND_NO_EGRESS); + } + return NETDEV_TX_OK; +} + +/* rmnet_vnd_change_mtu() - Change MTU NDO callback + * @dev: Virtual network device + * @new_mtu: New MTU value to set (in bytes) + * + * Standard network driver operations hook to set the MTU. Called by kernel to + * set the device MTU. Checks if desired MTU is less than zero or greater than + * RMNET_DATA_MAX_PACKET_SIZE; + * + * Return: + * - 0 if successful + * - -EINVAL if new_mtu is out of range + */ +static int rmnet_vnd_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < 0 || new_mtu > RMNET_DATA_MAX_PACKET_SIZE) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +#ifdef CONFIG_RMNET_DATA_FC +static int _rmnet_vnd_do_qos_ioctl(struct net_device *dev, + struct ifreq *ifr, + int cmd) +{ + struct rmnet_vnd_private_s *dev_conf; + int rc, qdisc_len = 0; + struct rmnet_ioctl_data_s ioctl_data; + + rc = 0; + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + switch (cmd) { + case RMNET_IOCTL_SET_QOS_ENABLE: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + LOGM("RMNET_IOCTL_SET_QOS_ENABLE on %s", dev->name); + if (!dev_conf->qos_version) + dev_conf->qos_version = RMNET_IOCTL_QOS_MODE_6; + break; + + case RMNET_IOCTL_SET_QOS_DISABLE: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + LOGM("RMNET_IOCTL_SET_QOS_DISABLE on %s", dev->name); + dev_conf->qos_version = 0; + break; + + case RMNET_IOCTL_GET_QOS: /* Get QoS header state */ + LOGM("RMNET_IOCTL_GET_QOS on %s", dev->name); + ioctl_data.u.operation_mode = (dev_conf->qos_version == + RMNET_IOCTL_QOS_MODE_6); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data, + sizeof(struct rmnet_ioctl_data_s))) + rc = -EFAULT; + break; + + case RMNET_IOCTL_FLOW_ENABLE: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + LOGL("RMNET_IOCTL_FLOW_ENABLE on %s", dev->name); + if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data, + sizeof(struct rmnet_ioctl_data_s))) { + rc = -EFAULT; + break; + } + qdisc_len = tc_qdisc_flow_control(dev, + ioctl_data.u.tcm_handle, 1); + trace_rmnet_fc_qmi(ioctl_data.u.tcm_handle, qdisc_len, 1); + break; + + case RMNET_IOCTL_FLOW_DISABLE: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + LOGL("RMNET_IOCTL_FLOW_DISABLE on %s", dev->name); + if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data, + sizeof(struct rmnet_ioctl_data_s))) { + rc = -EFAULT; + break; + } + qdisc_len = tc_qdisc_flow_control(dev, + ioctl_data.u.tcm_handle, 0); + trace_rmnet_fc_qmi(ioctl_data.u.tcm_handle, qdisc_len, 0); + break; + + default: + rc = -EINVAL; + } + + return rc; +} + +struct rmnet_vnd_fc_work { + struct work_struct work; + struct net_device *dev; + u32 tc_handle; + int enable; +}; + +static void _rmnet_vnd_wq_flow_control(struct work_struct *work) +{ + struct rmnet_vnd_fc_work *fcwork; + int qdisc_len = 0; + + fcwork = (struct rmnet_vnd_fc_work *)work; + + rtnl_lock(); + qdisc_len = tc_qdisc_flow_control(fcwork->dev, fcwork->tc_handle, + fcwork->enable); + trace_rmnet_fc_map(fcwork->tc_handle, qdisc_len, fcwork->enable); + rtnl_unlock(); + + LOGL("[%s] handle:%08X enable:%d", + fcwork->dev->name, fcwork->tc_handle, fcwork->enable); + + kfree(work); +} + +static int _rmnet_vnd_do_flow_control(struct net_device *dev, + u32 tc_handle, + int enable) +{ + struct rmnet_vnd_fc_work *fcwork; + + fcwork = kmalloc(sizeof(*fcwork), GFP_ATOMIC); + if (!fcwork) + return RMNET_VND_FC_KMALLOC_ERR; + memset(fcwork, 0, sizeof(struct rmnet_vnd_fc_work)); + + INIT_WORK((struct work_struct *)fcwork, _rmnet_vnd_wq_flow_control); + fcwork->dev = dev; + fcwork->tc_handle = tc_handle; + fcwork->enable = enable; + + schedule_work((struct work_struct *)fcwork); + return RMNET_VND_FC_QUEUED; +} +#else +static int _rmnet_vnd_do_qos_ioctl(struct net_device *dev, + struct ifreq *ifr, + int cmd) +{ + return -EINVAL; +} + +static inline int _rmnet_vnd_do_flow_control(struct net_device *dev, + u32 tc_handle, + int enable) +{ + LOGD("[%s] called with no QoS support", dev->name); + return RMNET_VND_FC_NOT_ENABLED; +} +#endif /* CONFIG_RMNET_DATA_FC */ + +static int rmnet_vnd_ioctl_extended(struct net_device *dev, struct ifreq *ifr) +{ + struct rmnet_vnd_private_s *dev_conf; + struct rmnet_ioctl_extended_s ext_cmd; + int rc = 0; + + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + rc = copy_from_user(&ext_cmd, ifr->ifr_ifru.ifru_data, + sizeof(struct rmnet_ioctl_extended_s)); + if (rc) { + LOGM("%s(): copy_from_user() failed\n", __func__); + return rc; + } + + switch (ext_cmd.extended_ioctl) { + case RMNET_IOCTL_GET_SUPPORTED_FEATURES: + ext_cmd.u.data = 0; + break; + + case RMNET_IOCTL_GET_DRIVER_NAME: + strlcpy(ext_cmd.u.if_name, "rmnet_data", + sizeof(ext_cmd.u.if_name)); + break; + + case RMNET_IOCTL_GET_SUPPORTED_QOS_MODES: + ext_cmd.u.data = RMNET_IOCTL_QOS_MODE_6 + | RMNET_IOCTL_QOS_MODE_8; + break; + + case RMNET_IOCTL_GET_QOS_VERSION: + ext_cmd.u.data = dev_conf->qos_version; + break; + + case RMNET_IOCTL_SET_QOS_VERSION: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_6 || + ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_8 || + ext_cmd.u.data == 0) { + dev_conf->qos_version = ext_cmd.u.data; + } else { + rc = -EINVAL; + goto done; + } + break; + + default: + rc = -EINVAL; + goto done; + } + + rc = copy_to_user(ifr->ifr_ifru.ifru_data, &ext_cmd, + sizeof(struct rmnet_ioctl_extended_s)); + if (rc) + LOGM("%s(): copy_to_user() failed\n", __func__); + +done: + return rc; +} + +/* rmnet_vnd_ioctl() - IOCTL NDO callback + * @dev: Virtual network device + * @ifreq: User data + * @cmd: IOCTL command value + * + * Standard network driver operations hook to process IOCTLs. Called by kernel + * to process non-stanard IOCTLs for device + * + * Return: + * - 0 if successful + * - -EINVAL if unknown IOCTL + */ +static int rmnet_vnd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct rmnet_vnd_private_s *dev_conf; + int rc; + struct rmnet_ioctl_data_s ioctl_data; + + rc = 0; + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + rc = _rmnet_vnd_do_qos_ioctl(dev, ifr, cmd); + if (rc != -EINVAL) + return rc; + rc = 0; /* Reset rc as it may contain -EINVAL from above */ + + switch (cmd) { + case RMNET_IOCTL_OPEN: /* Do nothing. Support legacy behavior */ + LOGM("RMNET_IOCTL_OPEN on %s (ignored)", dev->name); + break; + + case RMNET_IOCTL_CLOSE: /* Do nothing. Support legacy behavior */ + LOGM("RMNET_IOCTL_CLOSE on %s (ignored)", dev->name); + break; + + case RMNET_IOCTL_SET_LLP_ETHERNET: + LOGM("RMNET_IOCTL_SET_LLP_ETHERNET on %s (no support)", + dev->name); + rc = -EINVAL; + break; + + case RMNET_IOCTL_SET_LLP_IP: /* Do nothing. Support legacy behavior */ + LOGM("RMNET_IOCTL_SET_LLP_IP on %s (ignored)", dev->name); + break; + + case RMNET_IOCTL_GET_LLP: /* Always return IP mode */ + LOGM("RMNET_IOCTL_GET_LLP on %s", dev->name); + ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP; + if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data, + sizeof(struct rmnet_ioctl_data_s))) + rc = -EFAULT; + break; + + case RMNET_IOCTL_EXTENDED: + rc = rmnet_vnd_ioctl_extended(dev, ifr); + break; + + default: + LOGM("Unknown IOCTL 0x%08X", cmd); + rc = -EINVAL; + } + + return rc; +} + +static const struct net_device_ops rmnet_data_vnd_ops = { + .ndo_init = 0, + .ndo_start_xmit = rmnet_vnd_start_xmit, + .ndo_do_ioctl = rmnet_vnd_ioctl, + .ndo_change_mtu = rmnet_vnd_change_mtu, + .ndo_set_mac_address = 0, + .ndo_validate_addr = 0, +}; + +/* rmnet_vnd_setup() - net_device initialization callback + * @dev: Virtual network device + * + * Called by kernel whenever a new rmnet_data device is created. Sets MTU, + * flags, ARP type, needed headroom, etc... + */ +static void rmnet_vnd_setup(struct net_device *dev) +{ + struct rmnet_vnd_private_s *dev_conf; + + LOGM("Setting up device %s", dev->name); + + /* Clear out private data */ + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + memset(dev_conf, 0, sizeof(struct rmnet_vnd_private_s)); + + dev->netdev_ops = &rmnet_data_vnd_ops; + dev->mtu = RMNET_DATA_DFLT_PACKET_SIZE; + dev->needed_headroom = RMNET_DATA_NEEDED_HEADROOM; + random_ether_addr(dev->dev_addr); + dev->tx_queue_len = RMNET_DATA_TX_QUEUE_LEN; + + /* Raw IP mode */ + dev->header_ops = 0; /* No header */ + dev->type = ARPHRD_RAWIP; + dev->hard_header_len = 0; + dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + + /* Flow control */ + rwlock_init(&dev_conf->flow_map_lock); + INIT_LIST_HEAD(&dev_conf->flow_head); +} + +/* rmnet_vnd_setup() - net_device initialization helper function + * @dev: Virtual network device + * + * Called during device initialization. Disables GRO. + */ +static void rmnet_vnd_disable_offload(struct net_device *dev) +{ + dev->wanted_features &= ~NETIF_F_GRO; + __netdev_update_features(dev); +} + +/* Exposed API */ + +/* rmnet_vnd_exit() - Shutdown cleanup hook + * + * Called by RmNet main on module unload. Cleans up data structures and + * unregisters/frees net_devices. + */ +void rmnet_vnd_exit(void) +{ + int i; + + for (i = 0; i < RMNET_DATA_MAX_VND; i++) + if (rmnet_devices[i]) { + unregister_netdev(rmnet_devices[i]); + free_netdev(rmnet_devices[i]); + } +} + +/* rmnet_vnd_init() - Init hook + * + * Called by RmNet main on module load. Initializes data structures + */ +int rmnet_vnd_init(void) +{ + memset(rmnet_devices, 0, + sizeof(struct net_device *) * RMNET_DATA_MAX_VND); + return 0; +} + +/* rmnet_vnd_create_dev() - Create a new virtual network device node. + * @id: Virtual device node id + * @new_device: Pointer to newly created device node + * @prefix: Device name prefix + * + * Allocates structures for new virtual network devices. Sets the name of the + * new device and registers it with the network stack. Device will appear in + * ifconfig list after this is called. If the prefix is null, then + * RMNET_DATA_DEV_NAME_STR will be assumed. + * + * Return: + * - 0 if successful + * - RMNET_CONFIG_BAD_ARGUMENTS if id is out of range or prefix is too long + * - RMNET_CONFIG_DEVICE_IN_USE if id already in use + * - RMNET_CONFIG_NOMEM if net_device allocation failed + * - RMNET_CONFIG_UNKNOWN_ERROR if register_netdevice() fails + */ +int rmnet_vnd_create_dev(int id, struct net_device **new_device, + const char *prefix, int use_name) +{ + struct net_device *dev; + char dev_prefix[IFNAMSIZ]; + int p, rc = 0; + + if (id < 0 || id >= RMNET_DATA_MAX_VND) { + *new_device = 0; + return RMNET_CONFIG_BAD_ARGUMENTS; + } + + if (rmnet_devices[id] != 0) { + *new_device = 0; + return RMNET_CONFIG_DEVICE_IN_USE; + } + + if (!prefix && !use_name) + p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", + RMNET_DATA_DEV_NAME_STR); + else if (prefix && use_name) + p = scnprintf(dev_prefix, IFNAMSIZ, "%s", prefix); + else if (prefix && !use_name) + p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", prefix); + else + return RMNET_CONFIG_BAD_ARGUMENTS; + + if (p >= (IFNAMSIZ - 1)) { + LOGE("Specified prefix longer than IFNAMSIZ"); + return RMNET_CONFIG_BAD_ARGUMENTS; + } + + dev = alloc_netdev(sizeof(struct rmnet_vnd_private_s), + dev_prefix, + use_name ? NET_NAME_UNKNOWN : NET_NAME_ENUM, + rmnet_vnd_setup); + if (!dev) { + LOGE("Failed to to allocate netdev for id %d", id); + *new_device = 0; + return RMNET_CONFIG_NOMEM; + } + + if (!prefix) { + /* Configuring DL checksum offload on rmnet_data interfaces */ + dev->hw_features = NETIF_F_RXCSUM; + /* Configuring UL checksum offload on rmnet_data interfaces */ + dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + /* Configuring GRO on rmnet_data interfaces */ + dev->hw_features |= NETIF_F_GRO; + /* Configuring Scatter-Gather on rmnet_data interfaces */ + dev->hw_features |= NETIF_F_SG; + /* Configuring GSO on rmnet_data interfaces */ + dev->hw_features |= NETIF_F_GSO; + dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; + dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + + rc = register_netdevice(dev); + if (rc != 0) { + LOGE("Failed to to register netdev [%s]", dev->name); + free_netdev(dev); + *new_device = 0; + rc = RMNET_CONFIG_UNKNOWN_ERROR; + } else { + rmnet_devices[id] = dev; + *new_device = dev; + LOGM("Registered device %s", dev->name); + } + + rmnet_vnd_disable_offload(dev); + + return rc; +} + +/* rmnet_vnd_free_dev() - free a virtual network device node. + * @id: Virtual device node id + * + * Unregisters the virtual network device node and frees it. + * unregister_netdev locks the rtnl mutex, so the mutex must not be locked + * by the caller of the function. unregister_netdev enqueues the request to + * unregister the device into a TODO queue. The requests in the TODO queue + * are only done after rtnl mutex is unlocked, therefore free_netdev has to + * called after unlocking rtnl mutex. + * + * Return: + * - 0 if successful + * - RMNET_CONFIG_NO_SUCH_DEVICE if id is invalid or not in range + * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset + */ +int rmnet_vnd_free_dev(int id) +{ + struct rmnet_logical_ep_conf_s *epconfig_l; + struct net_device *dev; + + rtnl_lock(); + if ((id < 0) || (id >= RMNET_DATA_MAX_VND) || !rmnet_devices[id]) { + rtnl_unlock(); + LOGM("Invalid id [%d]", id); + return RMNET_CONFIG_NO_SUCH_DEVICE; + } + + epconfig_l = rmnet_vnd_get_le_config(rmnet_devices[id]); + if (epconfig_l && epconfig_l->refcount) { + rtnl_unlock(); + return RMNET_CONFIG_DEVICE_IN_USE; + } + + dev = rmnet_devices[id]; + rmnet_devices[id] = 0; + rtnl_unlock(); + + if (dev) { + unregister_netdev(dev); + free_netdev(dev); + return 0; + } else { + return RMNET_CONFIG_NO_SUCH_DEVICE; + } +} + +/* rmnet_vnd_get_name() - Gets the string name of a VND based on ID + * @id: Virtual device node id + * @name: Buffer to store name of virtual device node + * @name_len: Length of name buffer + * + * Copies the name of the virtual device node into the users buffer. Will throw + * an error if the buffer is null, or too small to hold the device name. + * + * Return: + * - 0 if successful + * - -EINVAL if name is null + * - -EINVAL if id is invalid or not in range + * - -EINVAL if name is too small to hold things + */ +int rmnet_vnd_get_name(int id, char *name, int name_len) +{ + int p; + + if (!name) { + LOGM("%s", "Bad arguments; name buffer null"); + return -EINVAL; + } + + if ((id < 0) || (id >= RMNET_DATA_MAX_VND) || !rmnet_devices[id]) { + LOGM("Invalid id [%d]", id); + return -EINVAL; + } + + p = strlcpy(name, rmnet_devices[id]->name, name_len); + if (p >= name_len) { + LOGM("Buffer to small (%d) to fit device name", name_len); + return -EINVAL; + } + LOGL("Found mapping [%d]->\"%s\"", id, name); + + return 0; +} + +/* rmnet_vnd_is_vnd() - Determine if net_device is RmNet owned virtual devices + * @dev: Network device to test + * + * Searches through list of known RmNet virtual devices. This function is O(n) + * and should not be used in the data path. + * + * Return: + * - 0 if device is not RmNet virtual device + * - 1 if device is RmNet virtual device + */ +int rmnet_vnd_is_vnd(struct net_device *dev) +{ + /* This is not an efficient search, but, this will only be called in + * a configuration context, and the list is small. + */ + int i; + + if (!dev) + return 0; + + for (i = 0; i < RMNET_DATA_MAX_VND; i++) + if (dev == rmnet_devices[i]) + return i + 1; + + return 0; +} + +/* rmnet_vnd_get_le_config() - Get the logical endpoint configuration + * @dev: Virtual device node + * + * Gets the logical endpoint configuration for a RmNet virtual network device + * node. Caller should confirm that devices is a RmNet VND before calling. + * + * Return: + * - Pointer to logical endpoint configuration structure + * - 0 (null) if dev is null + */ +struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev) +{ + struct rmnet_vnd_private_s *dev_conf; + + if (!dev) + return 0; + + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + if (!dev_conf) + return 0; + + return &dev_conf->local_ep; +} + +/* _rmnet_vnd_get_flow_map() - Gets object representing a MAP flow handle + * @dev_conf: Private configuration structure for virtual network device + * @map_flow: MAP flow handle IF + * + * Loops through available flow mappings and compares the MAP flow handle. + * Returns when mapping is found. + * + * Return: + * - Null if no mapping was found + * - Pointer to mapping otherwise + */ +static struct rmnet_map_flow_mapping_s *_rmnet_vnd_get_flow_map + (struct rmnet_vnd_private_s *dev_conf, + u32 map_flow) +{ + struct list_head *p; + struct rmnet_map_flow_mapping_s *itm; + + list_for_each(p, &dev_conf->flow_head) { + itm = list_entry(p, struct rmnet_map_flow_mapping_s, list); + + if (unlikely(!itm)) + return 0; + + if (itm->map_flow_id == map_flow) + return itm; + } + return 0; +} + +/* _rmnet_vnd_update_flow_map() - Add or remove individual TC flow handles + * @action: One of RMNET_VND_UF_ACTION_ADD / RMNET_VND_UF_ACTION_DEL + * @itm: Flow mapping object + * @map_flow: TC flow handle + * + * RMNET_VND_UF_ACTION_ADD: + * Will check for a free mapping slot in the mapping object. If one is found, + * valid for that slot will be set to 1 and the value will be set. + * + * RMNET_VND_UF_ACTION_DEL: + * Will check for matching tc handle. If found, valid for that slot will be + * set to 0 and the value will also be zeroed. + * + * Return: + * - RMNET_VND_UPDATE_FLOW_OK tc flow handle is added/removed ok + * - RMNET_VND_UPDATE_FLOW_NO_MORE_ROOM if there are no more tc handles + * - RMNET_VND_UPDATE_FLOW_NO_VALID_LEFT if flow mapping is now empty + * - RMNET_VND_UPDATE_FLOW_NO_ACTION if no action was taken + */ +static int _rmnet_vnd_update_flow_map(u8 action, + struct rmnet_map_flow_mapping_s *itm, + u32 tc_flow) +{ + int rc, i, j; + + rc = RMNET_VND_UPDATE_FLOW_OK; + + switch (action) { + case RMNET_VND_UF_ACTION_ADD: + rc = RMNET_VND_UPDATE_FLOW_NO_MORE_ROOM; + for (i = 0; i < RMNET_MAP_FLOW_NUM_TC_HANDLE; i++) { + if (itm->tc_flow_valid[i] == 0) { + itm->tc_flow_valid[i] = 1; + itm->tc_flow_id[i] = tc_flow; + rc = RMNET_VND_UPDATE_FLOW_OK; + LOGD("{%pK}->tc_flow_id[%d]=%08X", + itm, i, tc_flow); + break; + } + } + break; + + case RMNET_VND_UF_ACTION_DEL: + j = 0; + rc = RMNET_VND_UPDATE_FLOW_OK; + for (i = 0; i < RMNET_MAP_FLOW_NUM_TC_HANDLE; i++) { + if (itm->tc_flow_valid[i] == 1) { + if (itm->tc_flow_id[i] == tc_flow) { + itm->tc_flow_valid[i] = 0; + itm->tc_flow_id[i] = 0; + j++; + LOGD("{%pK}->tc_flow_id[%d]=0", itm, i); + } + } else { + j++; + } + } + if (j == RMNET_MAP_FLOW_NUM_TC_HANDLE) + rc = RMNET_VND_UPDATE_FLOW_NO_VALID_LEFT; + break; + + default: + rc = RMNET_VND_UPDATE_FLOW_NO_ACTION; + break; + } + return rc; +} + +/* rmnet_vnd_add_tc_flow() - Add a MAP/TC flow handle mapping + * @id: Virtual network device ID + * @map_flow: MAP flow handle + * @tc_flow: TC flow handle + * + * Checkes for an existing flow mapping object corresponding to map_flow. If one + * is found, then it will try to add to the existing mapping object. Otherwise, + * a new mapping object is created. + * + * Return: + * - RMNET_CONFIG_OK if successful + * - RMNET_CONFIG_TC_HANDLE_FULL if there is no more room in the map object + * - RMNET_CONFIG_NOMEM failed to allocate a new map object + */ +int rmnet_vnd_add_tc_flow(u32 id, u32 map_flow, u32 tc_flow) +{ + struct rmnet_map_flow_mapping_s *itm; + struct net_device *dev; + struct rmnet_vnd_private_s *dev_conf; + int r; + unsigned long flags; + + if ((id < 0) || (id >= RMNET_DATA_MAX_VND) || !rmnet_devices[id]) { + LOGM("Invalid VND id [%d]", id); + return RMNET_CONFIG_NO_SUCH_DEVICE; + } + + dev = rmnet_devices[id]; + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + if (!dev_conf) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + write_lock_irqsave(&dev_conf->flow_map_lock, flags); + itm = _rmnet_vnd_get_flow_map(dev_conf, map_flow); + if (itm) { + r = _rmnet_vnd_update_flow_map(RMNET_VND_UF_ACTION_ADD, + itm, tc_flow); + if (r != RMNET_VND_UPDATE_FLOW_OK) { + write_unlock_irqrestore(&dev_conf->flow_map_lock, + flags); + return RMNET_CONFIG_TC_HANDLE_FULL; + } + write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); + return RMNET_CONFIG_OK; + } + write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); + + itm = kmalloc(sizeof(*itm), GFP_KERNEL); + + if (!itm) { + LOGM("%s", "Failure allocating flow mapping"); + return RMNET_CONFIG_NOMEM; + } + memset(itm, 0, sizeof(struct rmnet_map_flow_mapping_s)); + + itm->map_flow_id = map_flow; + itm->tc_flow_valid[0] = 1; + itm->tc_flow_id[0] = tc_flow; + + /* How can we dynamically init these safely? Kernel only provides static + * initializers for atomic_t + */ + itm->v4_seq.counter = 0; /* Init is broken: ATOMIC_INIT(0); */ + itm->v6_seq.counter = 0; /* Init is broken: ATOMIC_INIT(0); */ + + write_lock_irqsave(&dev_conf->flow_map_lock, flags); + list_add(&itm->list, &dev_conf->flow_head); + write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); + + LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%pK", + dev->name, itm->map_flow_id, itm->tc_flow_id[0], itm); + + return RMNET_CONFIG_OK; +} + +/* rmnet_vnd_del_tc_flow() - Delete a MAP/TC flow handle mapping + * @id: Virtual network device ID + * @map_flow: MAP flow handle + * @tc_flow: TC flow handle + * + * Checkes for an existing flow mapping object corresponding to map_flow. If one + * is found, then it will try to remove the existing tc_flow mapping. If the + * mapping object no longer contains any mappings, then it is freed. Otherwise + * the mapping object is left in the list + * + * Return: + * - RMNET_CONFIG_OK if successful or if there was no such tc_flow + * - RMNET_CONFIG_INVALID_REQUEST if there is no such map_flow + */ +int rmnet_vnd_del_tc_flow(u32 id, u32 map_flow, u32 tc_flow) +{ + struct rmnet_vnd_private_s *dev_conf; + struct net_device *dev; + struct rmnet_map_flow_mapping_s *itm; + int r; + unsigned long flags; + int rc = RMNET_CONFIG_OK; + + if ((id < 0) || (id >= RMNET_DATA_MAX_VND) || !rmnet_devices[id]) { + LOGM("Invalid VND id [%d]", id); + return RMNET_CONFIG_NO_SUCH_DEVICE; + } + + dev = rmnet_devices[id]; + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + if (!dev_conf) + return RMNET_CONFIG_NO_SUCH_DEVICE; + + r = RMNET_VND_UPDATE_FLOW_NO_ACTION; + write_lock_irqsave(&dev_conf->flow_map_lock, flags); + itm = _rmnet_vnd_get_flow_map(dev_conf, map_flow); + if (!itm) { + rc = RMNET_CONFIG_INVALID_REQUEST; + } else { + r = _rmnet_vnd_update_flow_map(RMNET_VND_UF_ACTION_DEL, + itm, tc_flow); + if (r == RMNET_VND_UPDATE_FLOW_NO_VALID_LEFT) + list_del(&itm->list); + } + write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); + + if (r == RMNET_VND_UPDATE_FLOW_NO_VALID_LEFT) { + if (itm) + LOGD("Removed flow mapping [%s][0x%08X]@%pK", + dev->name, itm->map_flow_id, itm); + kfree(itm); + } + + return rc; +} + +/* rmnet_data_vnd_do_flow_control() - Process flow control request + * @dev: Virtual network device node to do lookup on + * @map_flow_id: Flow ID from MAP message + * @v4_seq: pointer to IPv4 indication sequence number + * @v6_seq: pointer to IPv6 indication sequence number + * @enable: boolean to enable/disable flow. + * + * Return: + * - 0 if successful + * - 1 if no mapping is found + * - 2 if dev is not RmNet virtual network device node + */ +int rmnet_data_vnd_do_flow_control(struct net_device *dev, + u32 map_flow_id, + u16 v4_seq, + u16 v6_seq, + int enable) +{ + struct rmnet_vnd_private_s *dev_conf; + struct rmnet_map_flow_mapping_s *itm; + int do_fc, error, i; + + error = 0; + do_fc = 0; + + if (unlikely(!dev)) + return 2; + + if (!rmnet_vnd_is_vnd(dev)) + return 2; + + dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev); + + if (unlikely(!dev_conf)) + return 2; + + read_lock(&dev_conf->flow_map_lock); + if (map_flow_id == 0xFFFFFFFF) { + itm = &dev_conf->root_flow; + goto nolookup; + } + + itm = _rmnet_vnd_get_flow_map(dev_conf, map_flow_id); + + if (!itm) { + LOGL("Got flow control request for unknown flow %08X", + map_flow_id); + goto fcdone; + } + +nolookup: + if (v4_seq == 0 || v4_seq >= atomic_read(&itm->v4_seq)) { + atomic_set(&itm->v4_seq, v4_seq); + if (map_flow_id == 0xFFFFFFFF) { + LOGD("Setting VND TX queue state to %d", enable); + /* Although we expect similar number of enable/disable + * commands, optimize for the disable. That is more + * latency sensitive than enable + */ + if (unlikely(enable)) + netif_wake_queue(dev); + else + netif_stop_queue(dev); + trace_rmnet_fc_map(0xFFFFFFFF, 0, enable); + goto fcdone; + } + for (i = 0; i < RMNET_MAP_FLOW_NUM_TC_HANDLE; i++) { + if (itm->tc_flow_valid[i] == 1) { + LOGD("Found [%s][0x%08X][%d:0x%08X]", + dev->name, itm->map_flow_id, i, + itm->tc_flow_id[i]); + + _rmnet_vnd_do_flow_control(dev, + itm->tc_flow_id[i], + enable); + } + } + } else { + LOGD("Internal seq(%hd) higher than called(%hd)", + atomic_read(&itm->v4_seq), v4_seq); + } + +fcdone: + read_unlock(&dev_conf->flow_map_lock); + + return error; +} + +/* rmnet_vnd_get_by_id() - Get VND by array index ID + * @id: Virtual network deice id [0:RMNET_DATA_MAX_VND] + * + * Return: + * - 0 if no device or ID out of range + * - otherwise return pointer to VND net_device struct + */ +struct net_device *rmnet_vnd_get_by_id(int id) +{ + if (id < 0 || id >= RMNET_DATA_MAX_VND) { + LOGE("Bug; VND ID out of bounds"); + return 0; + } + return rmnet_devices[id]; +} diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h new file mode 100644 index 000000000000..a121d5472bb2 --- /dev/null +++ b/net/rmnet_data/rmnet_data_vnd.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2013-2018, 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. + * + * RMNET Data Virtual Network Device APIs + */ + +#include + +#ifndef _RMNET_DATA_VND_H_ +#define _RMNET_DATA_VND_H_ + +int rmnet_data_vnd_do_flow_control(struct net_device *dev, + u32 map_flow_id, + u16 v4_seq, + u16 v6_seq, + int enable); +struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev); +int rmnet_vnd_get_name(int id, char *name, int name_len); +int rmnet_vnd_create_dev(int id, struct net_device **new_device, + const char *prefix, int use_name); +int rmnet_vnd_free_dev(int id); +int rmnet_vnd_is_vnd(struct net_device *dev); +int rmnet_vnd_add_tc_flow(u32 id, u32 map_flow, u32 tc_flow); +int rmnet_vnd_del_tc_flow(u32 id, u32 map_flow, u32 tc_flow); +int rmnet_vnd_init(void); +void rmnet_vnd_exit(void); +struct net_device *rmnet_vnd_get_by_id(int id); + +#endif /* _RMNET_DATA_VND_H_ */ diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h new file mode 100644 index 000000000000..4f074415e7c1 --- /dev/null +++ b/net/rmnet_data/rmnet_map.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2013-2018, 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 +#include +#include + +#ifndef _RMNET_MAP_H_ +#define _RMNET_MAP_H_ + +struct rmnet_map_control_command_s { + u8 command_name; +#ifndef RMNET_USE_BIG_ENDIAN_STRUCTS + u8 cmd_type:2; + u8 reserved:6; +#else + u8 reserved:6; + u8 cmd_type:2; +#endif /* RMNET_USE_BIG_ENDIAN_STRUCTS */ + u16 reserved2; + u32 transaction_id; + union { + u8 data[65528]; + struct { +#ifndef RMNET_USE_BIG_ENDIAN_STRUCTS + u16 ip_family:2; + u16 reserved:14; +#else + u16 reserved:14; + u16 ip_family:2; +#endif /* RMNET_USE_BIG_ENDIAN_STRUCTS */ + u16 flow_control_seq_num; + u32 qos_id; + } flow_control; + }; +} __aligned(1); + +struct rmnet_map_dl_checksum_trailer_s { + unsigned char reserved_h; +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned char valid:1; + unsigned char reserved_l:7; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned char reserved_l:7; + unsigned char valid:1; +#else +#error "Please fix " +#endif + unsigned short checksum_start_offset; + unsigned short checksum_length; + unsigned short checksum_value; +} __aligned(1); + +struct rmnet_map_ul_checksum_header_s { + unsigned short checksum_start_offset; +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned short checksum_insert_offset:14; + unsigned short udp_ip4_ind:1; + unsigned short cks_en:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned short cks_en:1; + unsigned short udp_ip4_ind:1; + unsigned short checksum_insert_offset:14; +#else +#error "Please fix " +#endif +} __aligned(1); + +enum rmnet_map_results_e { + RMNET_MAP_SUCCESS, + RMNET_MAP_CONSUMED, + RMNET_MAP_GENERAL_FAILURE, + RMNET_MAP_NOT_ENABLED, + RMNET_MAP_FAILED_AGGREGATION, + RMNET_MAP_FAILED_MUX +}; + +enum rmnet_map_mux_errors_e { + RMNET_MAP_MUX_SUCCESS, + RMNET_MAP_MUX_INVALID_MUX_ID, + RMNET_MAP_MUX_INVALID_PAD_LENGTH, + RMNET_MAP_MUX_INVALID_PKT_LENGTH, + /* This should always be the last element */ + RMNET_MAP_MUX_ENUM_LENGTH +}; + +enum rmnet_map_checksum_errors_e { + RMNET_MAP_CHECKSUM_OK, + RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET, + RMNET_MAP_CHECKSUM_VALIDATION_FAILED, + RMNET_MAP_CHECKSUM_ERR_UNKNOWN, + RMNET_MAP_CHECKSUM_ERR_NOT_DATA_PACKET, + RMNET_MAP_CHECKSUM_ERR_BAD_BUFFER, + RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION, + RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT, + RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET, + RMNET_MAP_CHECKSUM_SKIPPED, + RMNET_MAP_CHECKSUM_SW, + /* This should always be the last element */ + RMNET_MAP_CHECKSUM_ENUM_LENGTH +}; + +enum rmnet_map_commands_e { + RMNET_MAP_COMMAND_NONE, + RMNET_MAP_COMMAND_FLOW_DISABLE, + RMNET_MAP_COMMAND_FLOW_ENABLE, + /* These should always be the last 2 elements */ + RMNET_MAP_COMMAND_UNKNOWN, + RMNET_MAP_COMMAND_ENUM_LENGTH +}; + +enum rmnet_map_agg_state_e { + RMNET_MAP_AGG_IDLE, + RMNET_MAP_TXFER_SCHEDULED +}; + +#define RMNET_MAP_COMMAND_REQUEST 0 +#define RMNET_MAP_COMMAND_ACK 1 +#define RMNET_MAP_COMMAND_UNSUPPORTED 2 +#define RMNET_MAP_COMMAND_INVALID 3 + +#define RMNET_MAP_NO_PAD_BYTES 0 +#define RMNET_MAP_ADD_PAD_BYTES 1 + +uint8_t rmnet_map_demultiplex(struct sk_buff *skb); +struct sk_buff* +rmnet_data_map_deaggregate(struct sk_buff *skb, + struct rmnet_phys_ep_config *config); + +struct rmnet_map_header_s *rmnet_data_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad); +rx_handler_result_t +rmnet_data_map_command(struct sk_buff *skb, + struct rmnet_phys_ep_config *config); +void rmnet_map_aggregate(struct sk_buff *skb, + struct rmnet_phys_ep_config *config); + +int rmnet_map_data_checksum_downlink_packet(struct sk_buff *skb); +int rmnet_map_data_checksum_uplink_packet(struct sk_buff *skb, + struct net_device *orig_dev, + u32 egress_data_format); +int rmnet_ul_aggregation_skip(struct sk_buff *skb, int offset); +enum hrtimer_restart rmnet_map_flush_packet_queue(struct hrtimer *t); +#endif /* _RMNET_MAP_H_ */ diff --git a/net/rmnet_data/rmnet_map_command.c b/net/rmnet_data/rmnet_map_command.c new file mode 100644 index 000000000000..e6c7811d3478 --- /dev/null +++ b/net/rmnet_data/rmnet_map_command.c @@ -0,0 +1,205 @@ +/* Copyright (c) 2013-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_config.h" +#include "rmnet_map.h" +#include "rmnet_data_private.h" +#include "rmnet_data_vnd.h" +#include "rmnet_data_stats.h" + +RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_MAPC); + +unsigned long int rmnet_map_command_stats[RMNET_MAP_COMMAND_ENUM_LENGTH]; +module_param_array(rmnet_map_command_stats, ulong, 0, 0444); +MODULE_PARM_DESC(rmnet_map_command_stats, "MAP command statistics"); + +/* rmnet_map_do_flow_control() - Process MAP flow control command + * @skb: Socket buffer containing the MAP flow control message + * @config: Physical end-point configuration of ingress device + * @enable: boolean for enable/disable + * + * Process in-band MAP flow control messages. Assumes mux ID is mapped to a + * RmNet Data vitrual network device. + * + * Return: + * - RMNET_MAP_COMMAND_UNSUPPORTED on any error + * - RMNET_MAP_COMMAND_ACK on success + */ +static uint8_t rmnet_map_do_flow_control(struct sk_buff *skb, + struct rmnet_phys_ep_config *config, + int enable) +{ + struct rmnet_map_control_command_s *cmd; + struct net_device *vnd; + struct rmnet_logical_ep_conf_s *ep; + u8 mux_id; + u16 ip_family; + u16 fc_seq; + u32 qos_id; + int r; + + if (unlikely(!skb || !config)) + return RX_HANDLER_CONSUMED; + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + cmd = RMNET_MAP_GET_CMD_START(skb); + + if (mux_id >= RMNET_DATA_MAX_LOGICAL_EP) { + LOGD("Got packet on %s with bad mux id %d", + skb->dev->name, mux_id); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_BAD_MUX); + return RX_HANDLER_CONSUMED; + } + + ep = &config->muxed_ep[mux_id]; + + if (!ep->refcount) { + LOGD("Packet on %s:%d; has no logical endpoint config", + skb->dev->name, mux_id); + + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP); + return RX_HANDLER_CONSUMED; + } + + vnd = ep->egress_dev; + + ip_family = cmd->flow_control.ip_family; + fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); + qos_id = ntohl(cmd->flow_control.qos_id); + + /* Ignore the ip family and pass the sequence number for both v4 and v6 + * sequence. User space does not support creating dedicated flows for + * the 2 protocols + */ + r = rmnet_data_vnd_do_flow_control(vnd, qos_id, fc_seq, fc_seq, enable); + LOGD("dev:%s, qos_id:0x%08X, ip_family:%hd, fc_seq %hd, en:%d", + skb->dev->name, qos_id, ip_family & 3, fc_seq, enable); + + if (r) { + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED); + return RMNET_MAP_COMMAND_UNSUPPORTED; + } else { + return RMNET_MAP_COMMAND_ACK; + } +} + +/* rmnet_map_send_ack() - Send N/ACK message for MAP commands + * @skb: Socket buffer containing the MAP command message + * @type: N/ACK message selector + * @config: Physical end-point configuration of ingress device + * + * skb is modified to contain the message type selector. The message is then + * transmitted on skb->dev. Note that this function grabs global Tx lock on + * skb->dev for latency reasons. + * + * Return: + * - void + */ +static void rmnet_map_send_ack(struct sk_buff *skb, + unsigned char type, + struct rmnet_phys_ep_config *config) +{ + struct rmnet_map_control_command_s *cmd; + int xmit_status; + int rc; + + if (unlikely(!skb)) + return; + + skb->protocol = htons(ETH_P_MAP); + + if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) || + (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) { + if (unlikely(skb->len < (sizeof(struct rmnet_map_header_s) + + + RMNET_MAP_GET_LENGTH(skb) + + sizeof(struct rmnet_map_dl_checksum_trailer_s)))) { + rmnet_stats_dl_checksum( + RMNET_MAP_CHECKSUM_ERR_BAD_BUFFER); + return; + } + + skb_trim(skb, skb->len - + sizeof(struct rmnet_map_dl_checksum_trailer_s)); + } + + cmd = RMNET_MAP_GET_CMD_START(skb); + cmd->cmd_type = type & 0x03; + + netif_tx_lock(skb->dev); + xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); + netif_tx_unlock(skb->dev); + + LOGD("MAP command ACK=%hhu sent with rc: %d", type & 0x03, xmit_status); + + if (xmit_status != NETDEV_TX_OK) { + rc = dev_queue_xmit(skb); + if (rc != 0) { + LOGD("Failed to queue packet for transmission on [%s]", + skb->dev->name); + } + } +} + +/* rmnet_data_map_command() - Entry point for handling MAP commands + * @skb: Socket buffer containing the MAP command message + * @config: Physical end-point configuration of ingress device + * + * Process MAP command frame and send N/ACK message as appropriate. Message cmd + * name is decoded here and appropriate handler is called. + * + * Return: + * - RX_HANDLER_CONSUMED. Command frames are always consumed. + */ +rx_handler_result_t rmnet_data_map_command(struct sk_buff *skb, + struct rmnet_phys_ep_config *config) +{ + struct rmnet_map_control_command_s *cmd; + unsigned char command_name; + unsigned char rc = 0; + + if (unlikely(!skb)) + return RX_HANDLER_CONSUMED; + + cmd = RMNET_MAP_GET_CMD_START(skb); + command_name = cmd->command_name; + + if (command_name < RMNET_MAP_COMMAND_ENUM_LENGTH) + rmnet_map_command_stats[command_name]++; + + switch (command_name) { + case RMNET_MAP_COMMAND_FLOW_ENABLE: + rc = rmnet_map_do_flow_control(skb, config, 1); + break; + + case RMNET_MAP_COMMAND_FLOW_DISABLE: + rc = rmnet_map_do_flow_control(skb, config, 0); + break; + + default: + rmnet_map_command_stats[RMNET_MAP_COMMAND_UNKNOWN]++; + LOGM("Uknown MAP command: %d", command_name); + rc = RMNET_MAP_COMMAND_UNSUPPORTED; + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED); + break; + } + if (rc == RMNET_MAP_COMMAND_ACK) + rmnet_map_send_ack(skb, rc, config); + return RX_HANDLER_CONSUMED; +} diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c new file mode 100644 index 000000000000..4a8dae6969eb --- /dev/null +++ b/net/rmnet_data/rmnet_map_data.c @@ -0,0 +1,781 @@ +/* Copyright (c) 2013-2018, 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. + * + * RMNET Data MAP protocol + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data_config.h" +#include "rmnet_map.h" +#include "rmnet_data_private.h" +#include "rmnet_data_stats.h" +#include "rmnet_data_trace.h" + +RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_MAPD); + +/* Local Definitions */ + +long agg_time_limit __read_mostly = 1000000L; +module_param(agg_time_limit, long, 0644); +MODULE_PARM_DESC(agg_time_limit, "Maximum time packets sit in the agg buf"); + +long agg_bypass_time __read_mostly = 10000000L; +module_param(agg_bypass_time, long, 0644); +MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this"); + +struct agg_work { + struct work_struct work; + struct rmnet_phys_ep_config *config; +}; + +#define RMNET_MAP_DEAGGR_SPACING 64 +#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) + +/* rmnet_data_map_add_map_header() - Adds MAP header to front of skb->data + * @skb: Socket buffer ("packet") to modify + * @hdrlen: Number of bytes of header data which should not be included in + * MAP length field + * @pad: Specify if padding the MAP packet to make it 4 byte aligned is + * necessary + * + * Padding is calculated and set appropriately in MAP header. Mux ID is + * initialized to 0. + * + * Return: + * - Pointer to MAP structure + * - 0 (null) if insufficient headroom + * - 0 (null) if insufficient tailroom for padding bytes + */ +struct rmnet_map_header_s *rmnet_data_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad) +{ + u32 padding, map_datalen; + u8 *padbytes; + struct rmnet_map_header_s *map_header; + + if (skb_headroom(skb) < sizeof(struct rmnet_map_header_s)) + return 0; + + map_datalen = skb->len - hdrlen; + map_header = (struct rmnet_map_header_s *) + skb_push(skb, sizeof(struct rmnet_map_header_s)); + memset(map_header, 0, sizeof(struct rmnet_map_header_s)); + + if (pad == RMNET_MAP_NO_PAD_BYTES) { + map_header->pkt_len = htons(map_datalen); + return map_header; + } + + padding = ALIGN(map_datalen, 4) - map_datalen; + + if (padding == 0) + goto done; + + if (skb_tailroom(skb) < padding) + return 0; + + padbytes = (u8 *)skb_put(skb, padding); + LOGD("pad: %d", padding); + memset(padbytes, 0, padding); + +done: + map_header->pkt_len = htons(map_datalen + padding); + map_header->pad_len = padding & 0x3F; + + return map_header; +} + +/* rmnet_data_map_deaggregate() - Deaggregates a single packet + * @skb: Source socket buffer containing multiple MAP frames + * @config: Physical endpoint configuration of the ingress device + * + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + * + * Return: + * - Pointer to new skb + * - 0 (null) if no more aggregated packets + */ +struct sk_buff *rmnet_data_map_deaggregate(struct sk_buff *skb, + struct rmnet_phys_ep_config *config) +{ + struct sk_buff *skbn; + struct rmnet_map_header_s *maph; + u32 packet_len; + + if (skb->len == 0) + return 0; + + maph = (struct rmnet_map_header_s *)skb->data; + packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header_s); + + if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) || + (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) + packet_len += sizeof(struct rmnet_map_dl_checksum_trailer_s); + + if ((((int)skb->len) - ((int)packet_len)) < 0) { + LOGM("%s", "Got malformed packet. Dropping"); + return 0; + } + + skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); + if (!skbn) + return 0; + + skbn->dev = skb->dev; + skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM); + skb_put(skbn, packet_len); + memcpy(skbn->data, skb->data, packet_len); + skb_pull(skb, packet_len); + + /* Some hardware can send us empty frames. Catch them */ + if (ntohs(maph->pkt_len) == 0) { + LOGD("Dropping empty MAP frame"); + rmnet_kfree_skb(skbn, RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0); + return 0; + } + + return skbn; +} + +static void rmnet_map_flush_packet_work(struct work_struct *work) +{ + struct rmnet_phys_ep_config *config; + struct agg_work *real_work; + int rc, agg_count = 0; + unsigned long flags; + struct sk_buff *skb; + + real_work = (struct agg_work *)work; + config = real_work->config; + skb = NULL; + + LOGD("%s", "Entering flush thread"); + spin_lock_irqsave(&config->agg_lock, flags); + if (likely(config->agg_state == RMNET_MAP_TXFER_SCHEDULED)) { + /* Buffer may have already been shipped out */ + if (likely(config->agg_skb)) { + rmnet_stats_agg_pkts(config->agg_count); + if (config->agg_count > 1) + LOGL("Agg count: %d", config->agg_count); + skb = config->agg_skb; + agg_count = config->agg_count; + config->agg_skb = NULL; + config->agg_count = 0; + memset(&config->agg_time, 0, sizeof(struct timespec)); + } + config->agg_state = RMNET_MAP_AGG_IDLE; + } + + spin_unlock_irqrestore(&config->agg_lock, flags); + if (skb) { + trace_rmnet_map_flush_packet_queue(skb, agg_count); + rc = dev_queue_xmit(skb); + rmnet_stats_queue_xmit(rc, RMNET_STATS_QUEUE_XMIT_AGG_TIMEOUT); + } + + kfree(work); +} + +/* rmnet_map_flush_packet_queue() - Transmits aggregeted frame on timeout + * + * This function is scheduled to run in a specified number of ns after + * the last frame transmitted by the network stack. When run, the buffer + * containing aggregated packets is finally transmitted on the underlying link. + * + */ +enum hrtimer_restart rmnet_map_flush_packet_queue(struct hrtimer *t) +{ + struct rmnet_phys_ep_config *config; + struct agg_work *work; + + config = container_of(t, struct rmnet_phys_ep_config, hrtimer); + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + config->agg_state = RMNET_MAP_AGG_IDLE; + + return HRTIMER_NORESTART; + } + + INIT_WORK(&work->work, rmnet_map_flush_packet_work); + work->config = config; + schedule_work((struct work_struct *)work); + return HRTIMER_NORESTART; +} + +/* rmnet_map_aggregate() - Software aggregates multiple packets. + * @skb: current packet being transmitted + * @config: Physical endpoint configuration of the ingress device + * + * Aggregates multiple SKBs into a single large SKB for transmission. MAP + * protocol is used to separate the packets in the buffer. This function + * consumes the argument SKB and should not be further processed by any other + * function. + */ +void rmnet_map_aggregate(struct sk_buff *skb, + struct rmnet_phys_ep_config *config) +{ + u8 *dest_buff; + unsigned long flags; + struct sk_buff *agg_skb; + struct timespec diff, last; + int size, rc, agg_count = 0; + + if (!skb || !config) + return; + +new_packet: + spin_lock_irqsave(&config->agg_lock, flags); + memcpy(&last, &config->agg_last, sizeof(struct timespec)); + getnstimeofday(&config->agg_last); + + if (!config->agg_skb) { + /* Check to see if we should agg first. If the traffic is very + * sparse, don't aggregate. We will need to tune this later + */ + diff = timespec_sub(config->agg_last, last); + + if ((diff.tv_sec > 0) || (diff.tv_nsec > agg_bypass_time)) { + spin_unlock_irqrestore(&config->agg_lock, flags); + LOGL("delta t: %ld.%09lu\tcount: bypass", diff.tv_sec, + diff.tv_nsec); + rmnet_stats_agg_pkts(1); + trace_rmnet_map_aggregate(skb, 0); + rc = dev_queue_xmit(skb); + rmnet_stats_queue_xmit(rc, + RMNET_STATS_QUEUE_XMIT_AGG_SKIP); + return; + } + + size = config->egress_agg_size - skb->len; + config->agg_skb = skb_copy_expand(skb, 0, size, GFP_ATOMIC); + if (!config->agg_skb) { + config->agg_skb = 0; + config->agg_count = 0; + memset(&config->agg_time, 0, sizeof(struct timespec)); + spin_unlock_irqrestore(&config->agg_lock, flags); + rmnet_stats_agg_pkts(1); + trace_rmnet_map_aggregate(skb, 0); + rc = dev_queue_xmit(skb); + rmnet_stats_queue_xmit + (rc, + RMNET_STATS_QUEUE_XMIT_AGG_CPY_EXP_FAIL); + return; + } + config->agg_count = 1; + getnstimeofday(&config->agg_time); + trace_rmnet_start_aggregation(skb); + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_AGG_CPY_EXPAND); + goto schedule; + } + diff = timespec_sub(config->agg_last, config->agg_time); + + if (skb->len > (config->egress_agg_size - config->agg_skb->len) || + (config->agg_count >= config->egress_agg_count) || + (diff.tv_sec > 0) || (diff.tv_nsec > agg_time_limit)) { + rmnet_stats_agg_pkts(config->agg_count); + agg_skb = config->agg_skb; + agg_count = config->agg_count; + config->agg_skb = 0; + config->agg_count = 0; + memset(&config->agg_time, 0, sizeof(struct timespec)); + config->agg_state = RMNET_MAP_AGG_IDLE; + spin_unlock_irqrestore(&config->agg_lock, flags); + hrtimer_cancel(&config->hrtimer); + LOGL("delta t: %ld.%09lu\tcount: %d", diff.tv_sec, + diff.tv_nsec, agg_count); + trace_rmnet_map_aggregate(skb, agg_count); + rc = dev_queue_xmit(agg_skb); + rmnet_stats_queue_xmit(rc, + RMNET_STATS_QUEUE_XMIT_AGG_FILL_BUFFER); + goto new_packet; + } + + dest_buff = skb_put(config->agg_skb, skb->len); + memcpy(dest_buff, skb->data, skb->len); + config->agg_count++; + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_AGG_INTO_BUFF); + +schedule: + if (config->agg_state != RMNET_MAP_TXFER_SCHEDULED) { + config->agg_state = RMNET_MAP_TXFER_SCHEDULED; + hrtimer_start(&config->hrtimer, ns_to_ktime(3000000), + HRTIMER_MODE_REL); + } + spin_unlock_irqrestore(&config->agg_lock, flags); +} + +/* Checksum Offload */ + +static inline u16 *rmnet_map_get_checksum_field(unsigned char protocol, + const void *txporthdr) +{ + u16 *check = 0; + + switch (protocol) { + case IPPROTO_TCP: + check = &(((struct tcphdr *)txporthdr)->check); + break; + + case IPPROTO_UDP: + check = &(((struct udphdr *)txporthdr)->check); + break; + + default: + check = 0; + break; + } + + return check; +} + +static inline u16 rmnet_map_add_checksums(u16 val1, u16 val2) +{ + int sum = val1 + val2; + + sum = (((sum & 0xFFFF0000) >> 16) + sum) & 0x0000FFFF; + return (u16)(sum & 0x0000FFFF); +} + +static inline u16 rmnet_map_subtract_checksums(u16 val1, u16 val2) +{ + return rmnet_map_add_checksums(val1, ~val2); +} + +/* rmnet_map_validate_ipv4_packet_checksum() - Validates TCP/UDP checksum + * value for IPv4 packet + * @map_payload: Pointer to the beginning of the map payload + * @cksum_trailer: Pointer to the checksum trailer + * + * Validates the TCP/UDP checksum for the packet using the checksum value + * from the checksum trailer added to the packet. + * The validation formula is the following: + * 1. Performs 1's complement over the checksum value from the trailer + * 2. Computes 1's complement checksum over IPv4 header and subtracts it from + * the value from step 1 + * 3. Computes 1's complement checksum over IPv4 pseudo header and adds it to + * the value from step 2 + * 4. Subtracts the checksum value from the TCP/UDP header from the value from + * step 3 + * 5. Compares the value from step 4 to the checksum value from the TCP/UDP + * header + * + * Fragmentation and tunneling are not supported. + * + * Return: 0 is validation succeeded. + */ +static int rmnet_map_validate_ipv4_packet_checksum + (unsigned char *map_payload, + struct rmnet_map_dl_checksum_trailer_s *cksum_trailer) +{ + struct iphdr *ip4h; + u16 *checksum_field; + void *txporthdr; + u16 pseudo_checksum; + u16 ip_hdr_checksum; + u16 checksum_value; + u16 ip_payload_checksum; + u16 ip_pseudo_payload_checksum; + u16 checksum_value_final; + + ip4h = (struct iphdr *)map_payload; + if ((ntohs(ip4h->frag_off) & IP_MF) || + ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) + return RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET; + + txporthdr = map_payload + ip4h->ihl * 4; + + checksum_field = rmnet_map_get_checksum_field(ip4h->protocol, + txporthdr); + + if (unlikely(!checksum_field)) + return RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT; + + /* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */ + if ((*checksum_field == 0) && (ip4h->protocol == IPPROTO_UDP)) + return RMNET_MAP_CHECKSUM_SKIPPED; + + checksum_value = ~ntohs(cksum_trailer->checksum_value); + ip_hdr_checksum = ~ip_fast_csum(ip4h, (int)ip4h->ihl); + ip_payload_checksum = rmnet_map_subtract_checksums(checksum_value, + ip_hdr_checksum); + + pseudo_checksum = ~ntohs(csum_tcpudp_magic(ip4h->saddr, ip4h->daddr, + (u16)(ntohs(ip4h->tot_len) - ip4h->ihl * 4), + (u16)ip4h->protocol, 0)); + ip_pseudo_payload_checksum = rmnet_map_add_checksums( + ip_payload_checksum, pseudo_checksum); + + checksum_value_final = ~rmnet_map_subtract_checksums( + ip_pseudo_payload_checksum, ntohs(*checksum_field)); + + if (unlikely(checksum_value_final == 0)) { + switch (ip4h->protocol) { + case IPPROTO_UDP: + /* RFC 768 */ + LOGD("DL4 1's complement rule for UDP checksum 0"); + checksum_value_final = ~checksum_value_final; + break; + + case IPPROTO_TCP: + if (*checksum_field == 0xFFFF) { + LOGD( + "DL4 Non-RFC compliant TCP checksum found"); + checksum_value_final = ~checksum_value_final; + } + break; + } + } + + LOGD( + "DL4 cksum: ~HW: %04X, field: %04X, pseudo header: %04X, final: %04X", + ~ntohs(cksum_trailer->checksum_value), ntohs(*checksum_field), + pseudo_checksum, checksum_value_final); + + if (checksum_value_final == ntohs(*checksum_field)) + return RMNET_MAP_CHECKSUM_OK; + else + return RMNET_MAP_CHECKSUM_VALIDATION_FAILED; +} + +/* rmnet_map_validate_ipv6_packet_checksum() - Validates TCP/UDP checksum + * value for IPv6 packet + * @map_payload: Pointer to the beginning of the map payload + * @cksum_trailer: Pointer to the checksum trailer + * + * Validates the TCP/UDP checksum for the packet using the checksum value + * from the checksum trailer added to the packet. + * The validation formula is the following: + * 1. Performs 1's complement over the checksum value from the trailer + * 2. Computes 1's complement checksum over IPv6 header and subtracts it from + * the value from step 1 + * 3. Computes 1's complement checksum over IPv6 pseudo header and adds it to + * the value from step 2 + * 4. Subtracts the checksum value from the TCP/UDP header from the value from + * step 3 + * 5. Compares the value from step 4 to the checksum value from the TCP/UDP + * header + * + * Fragmentation, extension headers and tunneling are not supported. + * + * Return: 0 is validation succeeded. + */ +static int rmnet_map_validate_ipv6_packet_checksum + (unsigned char *map_payload, + struct rmnet_map_dl_checksum_trailer_s *cksum_trailer) +{ + struct ipv6hdr *ip6h; + u16 *checksum_field; + void *txporthdr; + u16 pseudo_checksum; + u16 ip_hdr_checksum; + u16 checksum_value; + u16 ip_payload_checksum; + u16 ip_pseudo_payload_checksum; + u16 checksum_value_final; + u32 length; + + ip6h = (struct ipv6hdr *)map_payload; + + txporthdr = map_payload + sizeof(struct ipv6hdr); + checksum_field = rmnet_map_get_checksum_field(ip6h->nexthdr, + txporthdr); + + if (unlikely(!checksum_field)) + return RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT; + + checksum_value = ~ntohs(cksum_trailer->checksum_value); + ip_hdr_checksum = ~ntohs(ip_compute_csum(ip6h, + (int)(txporthdr - (void *)map_payload))); + ip_payload_checksum = rmnet_map_subtract_checksums + (checksum_value, ip_hdr_checksum); + + length = (ip6h->nexthdr == IPPROTO_UDP) ? + ntohs(((struct udphdr *)txporthdr)->len) : + ntohs(ip6h->payload_len); + pseudo_checksum = ~ntohs(csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + length, ip6h->nexthdr, 0)); + ip_pseudo_payload_checksum = rmnet_map_add_checksums( + ip_payload_checksum, pseudo_checksum); + + checksum_value_final = ~rmnet_map_subtract_checksums( + ip_pseudo_payload_checksum, ntohs(*checksum_field)); + + if (unlikely(checksum_value_final == 0)) { + switch (ip6h->nexthdr) { + case IPPROTO_UDP: + /* RFC 2460 section 8.1 */ + LOGD("DL6 One's complement rule for UDP checksum 0"); + checksum_value_final = ~checksum_value_final; + break; + + case IPPROTO_TCP: + if (*checksum_field == 0xFFFF) { + LOGD( + "DL6 Non-RFC compliant TCP checksum found"); + checksum_value_final = ~checksum_value_final; + } + break; + } + } + + LOGD( + "DL6 cksum: ~HW: %04X, field: %04X, pseudo header: %04X, final: %04X", + ~ntohs(cksum_trailer->checksum_value), ntohs(*checksum_field), + pseudo_checksum, checksum_value_final); + + if (checksum_value_final == ntohs(*checksum_field)) + return RMNET_MAP_CHECKSUM_OK; + else + return RMNET_MAP_CHECKSUM_VALIDATION_FAILED; + } + +/* rmnet_map_data_checksum_downlink_packet() - Validates checksum on + * a downlink packet + * @skb: Pointer to the packet's skb. + * + * Validates packet checksums. Function takes a pointer to + * the beginning of a buffer which contains the entire MAP + * frame: MAP header + IP payload + padding + checksum trailer. + * Currently, only IPv4 and IPv6 are supported along with + * TCP & UDP. Fragmented or tunneled packets are not supported. + * + * Return: + * - RMNET_MAP_CHECKSUM_OK: Validation of checksum succeeded. + * - RMNET_MAP_CHECKSUM_ERR_BAD_BUFFER: Skb buffer given is corrupted. + * - RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET: Valid flag is not set in the + * checksum trailer. + * - RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET: The packet is a fragment. + * - RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT: The transport header is + * not TCP/UDP. + * - RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION: Unrecognized IP header. + * - RMNET_MAP_CHECKSUM_VALIDATION_FAILED: In case the validation failed. + */ +int rmnet_map_data_checksum_downlink_packet(struct sk_buff *skb) +{ + struct rmnet_map_dl_checksum_trailer_s *cksum_trailer; + unsigned int data_len; + unsigned char *map_payload; + unsigned char ip_version; + + data_len = RMNET_MAP_GET_LENGTH(skb); + + if (unlikely(skb->len < (sizeof(struct rmnet_map_header_s) + data_len + + sizeof(struct rmnet_map_dl_checksum_trailer_s)))) + return RMNET_MAP_CHECKSUM_ERR_BAD_BUFFER; + + cksum_trailer = (struct rmnet_map_dl_checksum_trailer_s *) + (skb->data + data_len + + sizeof(struct rmnet_map_header_s)); + + if (unlikely(!ntohs(cksum_trailer->valid))) + return RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET; + + map_payload = (unsigned char *)(skb->data + + sizeof(struct rmnet_map_header_s)); + + ip_version = (*map_payload & 0xF0) >> 4; + if (ip_version == 0x04) + return rmnet_map_validate_ipv4_packet_checksum(map_payload, + cksum_trailer); + else if (ip_version == 0x06) + return rmnet_map_validate_ipv6_packet_checksum(map_payload, + cksum_trailer); + + return RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION; +} + +static void rmnet_map_fill_ipv4_packet_ul_checksum_header + (void *iphdr, struct rmnet_map_ul_checksum_header_s *ul_header, + struct sk_buff *skb) +{ + struct iphdr *ip4h = (struct iphdr *)iphdr; + unsigned short *hdr = (unsigned short *)ul_header; + + ul_header->checksum_start_offset = htons((unsigned short) + (skb_transport_header(skb) - (unsigned char *)iphdr)); + ul_header->checksum_insert_offset = skb->csum_offset; + ul_header->cks_en = 1; + if (ip4h->protocol == IPPROTO_UDP) + ul_header->udp_ip4_ind = 1; + else + ul_header->udp_ip4_ind = 0; + /* Changing checksum_insert_offset to network order */ + hdr++; + *hdr = htons(*hdr); + skb->ip_summed = CHECKSUM_NONE; +} + +static void rmnet_map_fill_ipv6_packet_ul_checksum_header + (void *iphdr, struct rmnet_map_ul_checksum_header_s *ul_header, + struct sk_buff *skb) +{ + unsigned short *hdr = (unsigned short *)ul_header; + + ul_header->checksum_start_offset = htons((unsigned short) + (skb_transport_header(skb) - (unsigned char *)iphdr)); + ul_header->checksum_insert_offset = skb->csum_offset; + ul_header->cks_en = 1; + ul_header->udp_ip4_ind = 0; + /* Changing checksum_insert_offset to network order */ + hdr++; + *hdr = htons(*hdr); + skb->ip_summed = CHECKSUM_NONE; +} + +static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr) +{ + struct iphdr *ip4h = (struct iphdr *)iphdr; + void *txporthdr; + u16 *csum; + + txporthdr = iphdr + ip4h->ihl * 4; + + if ((ip4h->protocol == IPPROTO_TCP) || + (ip4h->protocol == IPPROTO_UDP)) { + csum = (u16 *)rmnet_map_get_checksum_field(ip4h->protocol, + txporthdr); + *csum = ~(*csum); + } +} + +static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr) +{ + struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr; + void *txporthdr; + u16 *csum; + + txporthdr = ip6hdr + sizeof(struct ipv6hdr); + + if ((ip6h->nexthdr == IPPROTO_TCP) || (ip6h->nexthdr == IPPROTO_UDP)) { + csum = (u16 *)rmnet_map_get_checksum_field(ip6h->nexthdr, + txporthdr); + *csum = ~(*csum); + } +} + +/* rmnet_map_checksum_uplink_packet() - Generates UL checksum + * meta info header + * @skb: Pointer to the packet's skb. + * + * Generates UL checksum meta info header for IPv4 and IPv6 over TCP and UDP + * packets that are supported for UL checksum offload. + * + * Return: + * - RMNET_MAP_CHECKSUM_OK: Validation of checksum succeeded. + * - RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION: Unrecognized IP header. + * - RMNET_MAP_CHECKSUM_SW: Unsupported packet for UL checksum offload. + */ +int rmnet_map_data_checksum_uplink_packet(struct sk_buff *skb, + struct net_device *orig_dev, + u32 egress_data_format) +{ + unsigned char ip_version; + struct rmnet_map_ul_checksum_header_s *ul_header; + void *iphdr; + int ret; + + ul_header = (struct rmnet_map_ul_checksum_header_s *) + skb_push(skb, sizeof(struct rmnet_map_ul_checksum_header_s)); + + if (unlikely(!(orig_dev->features & + (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))) { + ret = RMNET_MAP_CHECKSUM_SW; + goto sw_checksum; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + iphdr = (char *)ul_header + + sizeof(struct rmnet_map_ul_checksum_header_s); + ip_version = (*(char *)iphdr & 0xF0) >> 4; + if (ip_version == 0x04) { + rmnet_map_fill_ipv4_packet_ul_checksum_header + (iphdr, ul_header, skb); + if (egress_data_format & + RMNET_EGRESS_FORMAT_MAP_CKSUMV4) + rmnet_map_complement_ipv4_txporthdr_csum_field( + iphdr); + ret = RMNET_MAP_CHECKSUM_OK; + goto done; + } else if (ip_version == 0x06) { + rmnet_map_fill_ipv6_packet_ul_checksum_header + (iphdr, ul_header, skb); + if (egress_data_format & + RMNET_EGRESS_FORMAT_MAP_CKSUMV4) + rmnet_map_complement_ipv6_txporthdr_csum_field( + iphdr); + ret = RMNET_MAP_CHECKSUM_OK; + goto done; + } else { + ret = RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION; + goto sw_checksum; + } + } else { + ret = RMNET_MAP_CHECKSUM_SW; + goto sw_checksum; + } + +sw_checksum: + ul_header->checksum_start_offset = 0; + ul_header->checksum_insert_offset = 0; + ul_header->cks_en = 0; + ul_header->udp_ip4_ind = 0; +done: + return ret; +} + +int rmnet_ul_aggregation_skip(struct sk_buff *skb, int offset) +{ + unsigned char *packet_start = skb->data + offset; + int is_icmp = 0; + + if (skb->data[offset] >> 4 == 0x04) { + struct iphdr *ip4h = (struct iphdr *)(packet_start); + + if (ip4h->protocol == IPPROTO_ICMP) + is_icmp = 1; + } else if ((skb->data[offset]) >> 4 == 0x06) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); + + if (ip6h->nexthdr == IPPROTO_ICMPV6) { + is_icmp = 1; + } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { + struct frag_hdr *frag; + + frag = (struct frag_hdr *)(packet_start + + sizeof(struct ipv6hdr)); + if (frag->nexthdr == IPPROTO_ICMPV6) + is_icmp = 1; + } + } + + return is_icmp; +} diff --git a/techpack/audio/Android.mk b/techpack/audio/Android.mk index ab2e37355bea..9bd1aa27015f 100644 --- a/techpack/audio/Android.mk +++ b/techpack/audio/Android.mk @@ -3,7 +3,7 @@ MY_LOCAL_PATH := $(call my-dir) UAPI_OUT := $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/include -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) $(shell mkdir -p $(UAPI_OUT)/linux;) $(shell mkdir -p $(UAPI_OUT)/sound;) $(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers) diff --git a/techpack/audio/Makefile b/techpack/audio/Makefile index 357525074003..245c746b8e03 100644 --- a/techpack/audio/Makefile +++ b/techpack/audio/Makefile @@ -27,6 +27,9 @@ ifeq ($(CONFIG_ARCH_SDM660), y) include $(srctree)/techpack/audio/config/sdm660auto.conf export $(shell sed 's/=.*//' $(srctree)/techpack/audio/config/sdm660auto.conf) endif +ifeq ($(CONFIG_ARCH_SDM845), y) +include $(srctree)/techpack/audio/config/sdm845auto.conf +endif # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE += \ @@ -67,6 +70,10 @@ ifeq ($(CONFIG_ARCH_SDM660), y) LINUXINCLUDE += \ -include $(srctree)/techpack/audio/config/sdm660autoconf.h endif +ifeq ($(CONFIG_ARCH_SDM845), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdm845autoconf.h +endif obj-y += soc/ obj-y += dsp/ obj-y += ipc/ diff --git a/techpack/audio/Makefile.am b/techpack/audio/Makefile.am index b10cd8720b23..32dec894e26c 100644 --- a/techpack/audio/Makefile.am +++ b/techpack/audio/Makefile.am @@ -23,6 +23,9 @@ endif ifeq ($(TARGET_SUPPORT),apq8053 msm8953 msm8937) KBUILD_OPTIONS += CONFIG_ARCH_SDM450=y endif +ifeq ($(TARGET_SUPPORT),sdm845) +KBUILD_OPTIONS += CONFIG_ARCH_SDM845=y +endif obj-m := ipc/ obj-m += dsp/ @@ -30,7 +33,7 @@ obj-m += dsp/codecs/ obj-m += soc/ obj-m += asoc/ obj-m += asoc/codecs/ -ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), sdmsteppe, sdm660)) +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), sdmsteppe, sdm660 sdm845)) obj-m += asoc/codecs/wcd934x/ endif ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), qcs40x)) diff --git a/techpack/audio/asoc/Android.mk b/techpack/audio/asoc/Android.mk index b2cac8fe7e05..de4f8db2f38e 100644 --- a/techpack/audio/asoc/Android.mk +++ b/techpack/audio/asoc/Android.mk @@ -52,9 +52,14 @@ AUDIO_SELECT += CONFIG_SND_SOC_SDM450=m AUDIO_SELECT += CONFIG_SND_SOC_EXT_CODEC_SDM450=m endif +ifeq ($(call is-board-platform,sdm845),true) +TARGET := sdm845 +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/asoc/Kbuild b/techpack/audio/asoc/Kbuild index dddfd1440a83..97344fc9e2ba 100644 --- a/techpack/audio/asoc/Kbuild +++ b/techpack/audio/asoc/Kbuild @@ -14,6 +14,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM8150), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf @@ -124,6 +128,11 @@ COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) ############ ASoC Drivers ############ +# for SDM845 sound card driver +ifdef CONFIG_SND_SOC_SDM845 + MACHINE_OBJS += sdm845.o +endif + # for SM8150 sound card driver ifdef CONFIG_SND_SOC_SM8150 MACHINE_OBJS += sm8150.o @@ -286,6 +295,9 @@ machine_dlkm-y := $(MACHINE_OBJS) obj-$(CONFIG_SND_SOC_EXT_CODEC_SDM450) += machine_ext_dlkm.o machine_ext_dlkm-y := $(MACHINE_EXT_OBJS) +obj-$(CONFIG_SND_SOC_SDM845) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + obj-$(CONFIG_SND_SOC_SM8150) += machine_dlkm.o machine_dlkm-y := $(MACHINE_OBJS) diff --git a/techpack/audio/asoc/codecs/Android.mk b/techpack/audio/asoc/codecs/Android.mk index 08c555b53a13..996fb69465f3 100644 --- a/techpack/audio/asoc/codecs/Android.mk +++ b/techpack/audio/asoc/codecs/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform-in-list,msmnile sdmshrike),true) ifeq ($(TARGET_BOARD_AUTO),true) AUDIO_SELECT := CONFIG_SND_SOC_SA8155=m @@ -46,7 +50,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/asoc/codecs/Kbuild b/techpack/audio/asoc/codecs/Kbuild index 19e374e43045..b2c317437e76 100644 --- a/techpack/audio/asoc/codecs/Kbuild +++ b/techpack/audio/asoc/codecs/Kbuild @@ -14,6 +14,11 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM8150), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf @@ -252,6 +257,9 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += rouleur/ obj-y += wsa883x/ obj-y += sdm660_cdc/ + obj-y += fsa4840/ + obj-y += max98927/ + obj-y += tfa9874/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/techpack/audio/asoc/codecs/fsa4840/Kbuild b/techpack/audio/asoc/codecs/fsa4840/Kbuild new file mode 100644 index 000000000000..478479d21a92 --- /dev/null +++ b/techpack/audio/asoc/codecs/fsa4840/Kbuild @@ -0,0 +1,123 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.19 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_KONA), y) + include $(AUDIO_ROOT)/config/konaauto.conf + INCS += -include $(AUDIO_ROOT)/config/konaautoconf.h + endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ fsa4840 ############ +ifdef CONFIG_SND_SOC_FSA4840 + FSA4840_OBJS += fsa4840.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_FSA4840) += fsa4840_dlkm.o +fsa4840_dlkm-y := $(FSA4840_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/techpack/audio/asoc/codecs/fsa4840/fsa4480.h b/techpack/audio/asoc/codecs/fsa4840/fsa4480.h new file mode 100644 index 000000000000..634eb0391c6d --- /dev/null +++ b/techpack/audio/asoc/codecs/fsa4840/fsa4480.h @@ -0,0 +1,62 @@ +#ifndef __FSA4480_INC__ +#define __FSA4480_INC__ + +#include +#include +#include +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +#include + +#define FSA4480_DEVID 0x00 +#define FSA4480_OVPMSK 0x01 +#define FSA4480_OVPFLG 0x02 +#define FSA4480_OVPST 0x03 +#define FSA4480_SWEN 0x04 +#define FSA4480_SWSEL 0x05 +#define FSA4480_SWST0 0x06 +#define FSA4480_SWST1 0x07 +#define FSA4480_CNT_L 0x08 +#define FSA4480_CNT_R 0x09 +#define FSA4480_CNT_MIC 0x0A +#define FSA4480_CNT_SEN 0x0B +#define FSA4480_CNT_GND 0x0C +#define FSA4480_TIM_R 0x0D +#define FSA4480_TIM_MIC 0x0E +#define FSA4480_TIM_SEN 0x0F +#define FSA4480_TIM_GND 0x10 +#define FSA4480_ACC_DET 0x11 +#define FSA4480_FUN_EN 0x12 +#define FSA4480_RES_SET 0x13 +#define FSA4480_RES_VAL 0x14 +#define FSA4480_RES_THR 0x15 +#define FSA4480_RES_INV 0x16 +#define FSA4480_JACTYP 0x17 +#define FSA4480_DECINT 0x18 +#define FSA4480_DECMSK 0x19 +#define FSA4480_AUDREG1 0x1A +#define FSA4480_AUDREG2 0x1B +#define FSA4480_AUDDATA0 0x1C +#define FSA4480_AUDDATA1 0x1D +#define FSA4480_RESET 0x1E +#define FSA4480_CURSET 0x1F + +#define FSA4480_ALLSW 0 +#define FSA4480_USB 1 +#define FSA4480_Audio 2 +#define FSA4480_MIC 3 +#define FSA4480_SBU 4 + +struct fsa4480 { + struct i2c_client *i2c; + struct mutex fsa_lock; + wait_queue_head_t wq; + struct device *dev; + int mbhc_en; + struct delayed_work call_wcd_dwork; +}; + +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +extern int register_cc_notifier_client(struct notifier_block *nb); +extern int unregister_cc_notifier_client(struct notifier_block *nb); + +#endif diff --git a/techpack/audio/asoc/codecs/fsa4840/fsa4840.c b/techpack/audio/asoc/codecs/fsa4840/fsa4840.c new file mode 100644 index 000000000000..145cc64ae941 --- /dev/null +++ b/techpack/audio/asoc/codecs/fsa4840/fsa4840.c @@ -0,0 +1,547 @@ +/* + * + * Copyright + * Aurthor:suzhiguang@oneplus.com + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsa4480.h" + +#define I2C_RETRIES 2 +#define I2C_RETRY_DELAY 5 /* ms */ + +struct fsa4480 *gFsa4480; +bool fsa4480_enable = false; +EXPORT_SYMBOL_GPL(fsa4480_enable); +extern bool audio_adapter_flag; + +/* +*suzhiguang:read register value +*register address +*read value +*/ +int fsa4480_read_data(struct fsa4480 *fsa, + unsigned char reg, + int len, unsigned char value[]) +{ + struct i2c_client *fsa_client; + int err; + int tries = 0; + int error = 0; + struct i2c_msg msgs[] = { + { + .flags = 0, + .len = 1, + .buf = ®, + }, { + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + if (fsa == NULL) { + pr_err("fsa read data:No device available\n"); + return -1; + } + + if (fsa->i2c) { + fsa_client = fsa->i2c; + msgs[0].addr = fsa_client->addr; + msgs[1].addr = fsa_client->addr; + + do { + err = i2c_transfer(fsa_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(&fsa_client->dev, "read transfer error %d\n", + err); + error = -1; + } + + } else { + pr_err("No device available\n"); + error = -1; + } + return error; +} + +/* +*suzhiguang:write register value +*/ +int fsa4480_write_data(struct fsa4480 *fsa, char *writebuf, int writelen) +{ + int ret = 0; + struct i2c_client *client; + + client = fsa->i2c; + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + pr_err("fsa4480_write_data [IIC]: i2c_write error, ret=%d", ret); + } + + return ret; +} + +/* +*suzhiguang:write register value +*register address +*write value +*/ +int fsa4480_i2c_write_reg(struct fsa4480 *fsa, unsigned char regaddr, unsigned char regvalue) +{ + unsigned char buf[2] = {0}; + + if (fsa == NULL) { + pr_err("fsa write reg: No device available\n"); + return -1; + } + + buf[0] = regaddr; + buf[1] = regvalue; + return fsa4480_write_data(fsa, buf, sizeof(buf)); +} + +#if 0 +static void parseString(const char *buf) +{ + int i,j; + char commandStr[10]; + char regStr[10]; + char valueStr[10]; + int reg, value; + + pr_err("fsa input buffer is %s\n", buf); + for(i=0,j=0; buf[i]!=' ' && buf[i]!='\0'; i++,j++){ + commandStr[j] = buf[i]; + } + commandStr[j] = '\0'; + pr_err("commandStr is %s\n", commandStr); + if (buf[i] =='\0') + return; + + for(; buf[i]==' ' && buf[i]!='\0'; i++){} + if (buf[i] == '\0') + return; + + + for(j=0;buf[i]!=' ' && buf[i]!='\0';i++,j++) { + regStr[j] = buf[i]; + } + regStr[j] = '\0'; + pr_err("regStr is %s\n", regStr); + if (buf[i] =='\0') + return; + + for(; buf[i]==' ' && buf[i]!='\0'; i++){} + if (buf[i] == '\0') + return; + + + for(j=0;buf[i]!=' ' && buf[i]!='\0';i++,j++) { + valueStr[j] = buf[i]; + } + valueStr[j] = '\0'; + pr_err("regStr is %s\n", valueStr); + + reg = atoi(regStr); + value = atoi(valueStr); + + pr_err("parse comand is %s reg =0x%x value = 0x%x\n", commandStr, reg, value); +} +#endif + +static ssize_t fsa4480_state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg04 = 0 , reg05 = 0, reg17 = 0, val = 0; + unsigned char regValue = 0; + + pr_err("%s",__func__); + + if (gFsa4480 == NULL) { + pr_err("%s:No fsa device found.\n",__func__); + return -EINVAL; + } + +// parseString(buf); + + if (sysfs_streq(buf, "lr")) { + + pr_err("%s lr\n",__func__); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x9f); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWSEL, 0x00); + fsa4480_read_data(gFsa4480, FSA4480_SWEN, 1, ®Value); + pr_err("%s FSA4480_SWEN = %x\n", __func__, regValue); + fsa4480_read_data(gFsa4480, FSA4480_SWSEL, 1, ®Value); + pr_err("%s FSA4480_SWSEL = %x\n", __func__, regValue); + + } else if(sysfs_streq(buf, "mic")) { + + } else if(sysfs_streq(buf, "all")) { + + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x98); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWSEL, 0x00); + + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x9b); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_FUN_EN, 0x0d); + msleep_interruptible(30); + + fsa4480_read_data(gFsa4480, FSA4480_JACTYP, 1, ®17); + fsa4480_read_data(gFsa4480, FSA4480_SWEN, 1, ®04); + fsa4480_read_data(gFsa4480, FSA4480_SWSEL, 1, ®05); + msleep_interruptible(10); + val = reg17; + switch(val) + { + case 0x08: // 4 pole, + case 0x04: // 4 pole, + // automatically configure if it is 4 pole audio jack + break; + case 0x02: // 3 pole + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, (reg04 & 0x7f)); + msleep_interruptible(50); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, (reg04 | 0x80)); + break; + default: + pr_err("No audio accessory was recognized\n"); + break; + } + } else if(sysfs_streq(buf, "off")) { + fsa4480_read_data(gFsa4480, FSA4480_SWEN, 1, ®04); + fsa4480_read_data(gFsa4480, FSA4480_SWSEL, 1, ®05); + + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, (reg04 & 0x98)); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWSEL, (reg05 | 0x18)); + msleep_interruptible(30); + } else { + count = -EINVAL; + } + return count; +} + +static ssize_t fsa4480_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int err = 0; + u8 reg = 0x0; + u8 statu = 0x0; + char *temp = buf; + ssize_t buf_size = 0; + + if (gFsa4480 == NULL) { + pr_err("%s:No fsa device found.\n",__func__); + return -EINVAL; + } + + for (reg = 0x0; reg <= FSA4480_RESET; reg++) { + err = fsa4480_read_data(gFsa4480, reg, 1, &statu); + if (err < 0) { + pr_err("%s: read reg %#04x is failed\n", __func__, reg); + } + sprintf(temp,"%#04x: %#04x\n", reg, statu); + memset(&statu, 0, sizeof(statu)); + while (*temp != '\0') { + buf_size++; + temp++; + } + } + + *temp = '\0'; + + pr_err("%s:%s\n", __func__, buf); + + return buf_size; +} + +static struct device_attribute fsa4480_state_attr = + __ATTR(test, 0444, fsa4480_state_show, fsa4480_state_store); + + +/* +*suzhiguang:fsa init. +*/ +#if 0 +static void fsa4480_init(struct fsa4480 *fsa4480) +{ + fsa4480_i2c_write_reg(fsa4480, FSA4480_OVPMSK, 0xff); // OVP Interrupt Mask: ffh= disable all OVP interrupt +// fsa4480_i2c_write_reg(FSA4480_DECMSK, 0x00); // Audio Jack and Moisture Detection Interrupt Mask: + + fsa4480_i2c_write_reg(fsa4480, FSA4480_AUDDATA0, 0x20); // set mic threshold data0 + fsa4480_i2c_write_reg(fsa4480, FSA4480_AUDDATA1, 0xff); // set mic threshold data1 + + +/* + //Configure audio turn on timing to suppress pop noise + fsa4480_i2c_write_reg(FSA4480_CNT_L,0x01); // Set Audio L slow turn on timing, + fsa4480_i2c_write_reg(FSA4480_CNT_R,0x01); // Set Audio R slow turn on timing, + fsa4480_i2c_write_reg(FSA4480_CNT_MIC,0x01); // Set Audio MIC slow turn on timing, + fsa4480_i2c_write_reg(FSA4480_CNT_SEN,0x01); // Set Audio Sense slow turn on timing, + fsa4480_i2c_write_reg(FSA4480_CNT_GND,0x01); // Set Audio Ground slow turn on timing, + fsa4480_i2c_write_reg(FSA4480_TIM_R,0x00); // Set Delay between R and L + fsa4480_i2c_write_reg(FSA4480_TIM_MIC,0x00); // Set Delay between Mic and L + fsa4480_i2c_write_reg(FSA4480_TIM_SEN,0x00); // Set Delay between Sense and L + fsa4480_i2c_write_reg(FSA4480_TIM_GND,0x00); // Set Delay between Analog Ground and L +*/ + +/* + //configure moisture detection + fsa4480_i2c_write_reg(FSA4480_RES_SET, 0x00); // select which pin is enabled for moisture detection + fsa4480_i2c_write_reg(FSA4480_RES_THR, 0x16); // set detection threshold: 16h = 22 or 220 Kohm + fsa4480_i2c_write_reg(FSA4480_RES_INV, 0x00); // set detection interval: 00h =single + +*/ + + fsa4480_i2c_write_reg(fsa4480, FSA4480_FUN_EN, 0x0d); // Function Setting: h4b= DEC open drain output, + fsa4480_i2c_write_reg(fsa4480, FSA4480_SWEN, 0x80); // Switch Enable: 80h= enable FSA4480 device +// fsa4480_i2c_write_reg(FSA4480_SWSEL, 0x18); // set default switch: 18h= to DP/DN +} +#endif + +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +static int cc_audio_adapter_detect_callback(struct notifier_block *nb, + unsigned long value, void *data) +{ + unsigned char reg04 =0 , reg05 =0, reg17 =0, val = 0; + + if (gFsa4480 == NULL) { + pr_err("%s:No fsa device found.\n",__func__); + return NOTIFY_OK; + } + + if (value == 1) { + pr_err("%s:audio_adapter attached!\n", __func__); + pr_err("%s:open the audio switch!\n", __func__); + + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x9f); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWSEL, 0x00); + msleep_interruptible(1); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_FUN_EN, 0x0d); + msleep_interruptible(30); + + fsa4480_read_data(gFsa4480, FSA4480_JACTYP, 1, ®17); + fsa4480_read_data(gFsa4480, FSA4480_SWEN, 1, ®04); + fsa4480_read_data(gFsa4480, FSA4480_SWSEL, 1, ®05); + pr_err("%s:FSA4480_JACTYP =%x FSA4480_SWEN =%x FSA4480_SWSEL =%x\n", + __func__, reg17, reg04, reg05); + + msleep_interruptible(10); + val = reg17; + switch(val) + { + case 0x08: // 4 pole, + case 0x04: // 4 pole, + // automatically configure if it is 4 pole audio jack + break; + case 0x02: // 3 pole + reg04 |= 0x7; + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, (reg04 & 0x7f)); + msleep_interruptible(50); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, (reg04 | 0x80)); + fsa4480_read_data(gFsa4480, FSA4480_SWEN, 1, ®04); + pr_err("%s: reg04 is %#x\n", __func__, reg04); + break; + default: + pr_err("fsa4480 No audio accessory was recognized"); + + } + + if (gpio_is_valid(gFsa4480->mbhc_en)) { + gpio_set_value_cansleep(gFsa4480->mbhc_en, 1); + pr_err("gFsa4480->mbhc_en set to 1\n"); + } else { + pr_err("gpio_is_valid failed\n"); + } + + } else if (value == 0) { + pr_err("%s:audio_adapter removal!\n", __func__); + pr_err("%s:close the audio switch!\n", __func__); + + if (gpio_is_valid(gFsa4480->mbhc_en)) { + gpio_set_value_cansleep(gFsa4480->mbhc_en, 0); + pr_err("gpio set value to 0\n"); + } else { + pr_err("gpio_is_valid failed\n"); + } + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x80); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWSEL, 0x18); + usleep_range(50, 55); + fsa4480_i2c_write_reg(gFsa4480, FSA4480_SWEN, 0x98); + } else + pr_err("%s:audio adapter value = %lu\n", __func__, value); + + return NOTIFY_OK; +} + +static struct notifier_block typec_cc_notifier = { + .notifier_call = cc_audio_adapter_detect_callback, +}; + +/* liuhaituo@MM.Audio 2018/8/8 Solve not detected the headset after restarting the phone + * after plugging in the headset + */ +static void call_wcd_detect_headset(struct work_struct *work) +{ + if (audio_adapter_flag) + cc_audio_adapter_detect_callback(NULL, 1, NULL); + + pr_err("%s: enter\n", __func__); + register_cc_notifier_client(&typec_cc_notifier); + pr_err("%s: exit\n", __func__); +} + +/* +*suzhiguang:fsa probe. +*/ +static int fsa4480_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct fsa4480 *fsa4480; + struct device_node *np = i2c->dev.of_node; + unsigned char regValue = 0; + int ret = 0; + + pr_err("fsa4480_i2c_probe addr=0x%x\n", i2c->addr); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + fsa4480 = devm_kzalloc(&i2c->dev, sizeof(struct fsa4480), GFP_KERNEL); + if (fsa4480 == NULL) + return -ENOMEM; + + fsa4480->dev = &i2c->dev; + fsa4480->i2c = i2c; + i2c_set_clientdata(i2c, fsa4480); + + if (fsa4480_read_data(fsa4480, FSA4480_DEVID, 1, ®Value) < 0) { + pr_err("fsa4480 read device id error\n"); + return -EINVAL; + } else { + pr_err("fsa4480 device id = %x\n", regValue); + } + + if (regValue == 0x9) { + pr_err("fsa:fsa4480 found\n"); + gFsa4480 = fsa4480; + fsa4480_enable = true; + } else { + pr_err("fsa:no fsa4480 found.\n"); + return 0; + } + + fsa4480->mbhc_en = of_get_named_gpio(np, "mbhc_en", 0); + if (fsa4480->mbhc_en < 0){ + pr_err("fsa4480 of get gpio failed\n"); + return ret; + } else { + ret = devm_gpio_request_one(&i2c->dev, fsa4480->mbhc_en, + GPIOF_OUT_INIT_LOW, "FSA4480_MBHC"); + if (ret) { + pr_err("%s devm_gpio_request_one fsa4480->mbhc_en failed\n",__func__); + return ret; + } + } + //fsa4480_init(fsa4480); + + ret = sysfs_create_file(&i2c->dev.kobj, &fsa4480_state_attr.attr); + if(ret < 0) + { + pr_err("%s sysfs_create_file fsa4480_state_attr error.",__func__); + } + + /* liuhaituo@MM.Audio 2018/8/8 Solve not detected the headset after restarting the phone + * after plugging in the headset*/ + INIT_DELAYED_WORK(&fsa4480->call_wcd_dwork, call_wcd_detect_headset); + schedule_delayed_work(&fsa4480->call_wcd_dwork, msecs_to_jiffies(8000)); + return 0; +} + +static int fsa4480_i2c_remove(struct i2c_client *i2c) +{ +/*2018/06/14 @bsp add for support notify audio adapter switch*/ + unregister_cc_notifier_client(&typec_cc_notifier); + return 0; +} + +static const struct i2c_device_id fsa4480_i2c_id[] = { + { "fsa4480", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fsa4480_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id fsa4480_dt_match[] = { + { .compatible = "fsa,fsa4480" }, + { }, +}; +#endif + +static struct i2c_driver fsa4480_i2c_driver = { + .driver = { + .name = "fsa4480", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fsa4480_dt_match), + }, + .probe = fsa4480_i2c_probe, + .remove = fsa4480_i2c_remove, + .id_table = fsa4480_i2c_id, +}; + +static int __init fsa4480_i2c_init(void) +{ + int ret = 0; + + pr_err("fsa4480 driver fsa4480_i2c_init\n"); + + ret = i2c_add_driver(&fsa4480_i2c_driver); + + return ret; +} +module_init(fsa4480_i2c_init); + +static void __exit fsa4480_i2c_exit(void) +{ + i2c_del_driver(&fsa4480_i2c_driver); +} +module_exit(fsa4480_i2c_exit); + +MODULE_DESCRIPTION("ASoC FSA4480 driver"); +MODULE_LICENSE("GPL"); diff --git a/techpack/audio/asoc/codecs/max98927/Kbuild b/techpack/audio/asoc/codecs/max98927/Kbuild new file mode 100644 index 000000000000..0feef7cd8ca5 --- /dev/null +++ b/techpack/audio/asoc/codecs/max98927/Kbuild @@ -0,0 +1,123 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.19 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_KONA), y) + include $(AUDIO_ROOT)/config/konaauto.conf + INCS += -include $(AUDIO_ROOT)/config/konaautoconf.h + endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ max98927 ############ +ifdef CONFIG_SND_SOC_MAX98927 + MAX98927_OBJS += max98927.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_MAX98927) += max98927_dlkm.o +max98927_dlkm-y := $(MAX98927_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/techpack/audio/asoc/codecs/max98927/max98927.c b/techpack/audio/asoc/codecs/max98927/max98927.c new file mode 100644 index 000000000000..f622741eb81c --- /dev/null +++ b/techpack/audio/asoc/codecs/max98927/max98927.c @@ -0,0 +1,2223 @@ +/* + * max98927.c -- ALSA SoC Stereo MAX98927 driver + * Copyright 2013-15 Maxim Integrated Products + * 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 "max98927.h" + +//#undef pr_info +//#define pr_info pr_err + +//#undef pr_debug +//#define pr_debug pr_err + +//suzhiguang +int smartpa_present = 0; +EXPORT_SYMBOL_GPL(smartpa_present); + +struct max98927_priv *g_max98927 = NULL; +EXPORT_SYMBOL_GPL(g_max98927); + +#define USE_DSM_MISC_DEV 1 + +#ifdef USE_DSM_MISC_DEV +extern int afe_dsm_rx_set_params(uint8_t *payload, int size); +extern int afe_dsm_rx_get_params(uint8_t *payload, int size); +extern int afe_dsm_set_calib(uint8_t* payload); +extern int afe_dsm_pre_calib(uint8_t* payload); +extern int afe_dsm_post_calib(uint8_t* payload); +extern int afe_dsm_get_calib(uint8_t* payload); +extern int afe_dsm_get_average_calib(uint8_t* payload); +extern int afe_dsm_ramp_dn_cfg(uint8_t *payload, int delay_in_ms); +extern int afe_dsm_get_libary_info(uint32_t* payload, int size); +static DEFINE_MUTEX(dsm_lock); +#endif + +static int max98927_info_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +static int max98927_get_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int max98927_set_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +#define Q_DSM_ADAPTIVE_FC 9 +#define Q_DSM_ADAPTIVE_DC_RES 27 + +static unsigned int i2c_states = 0; +static int delay_array_msec[] = {10, 20, 30, 40, 50}; + +int reg_common_map[][2] = { + {MAX98927_Brownout_level_infinite_hold, 0x00}, + {MAX98927_Brownout_level_hold, 0x00}, + {MAX98927_Brownout__level_1_current_limit, 0x14}, + {MAX98927_Brownout__level_1_amp_1_control_1, 0x00}, + {MAX98927_Brownout__level_1_amp_1_control_2, 0x0c}, + {MAX98927_Brownout__level_1_amp_1_control_3, 0x00}, + {MAX98927_Brownout__level_2_current_limit, 0x10}, + {MAX98927_Brownout__level_2_amp_1_control_1, 0x00}, + {MAX98927_Brownout__level_2_amp_1_control_2, 0x0c}, + {MAX98927_Brownout__level_2_amp_1_control_3, 0x00}, + {MAX98927_Brownout__level_3_current_limit, 0x0c}, + {MAX98927_Brownout__level_3_amp_1_control_1, 0x06}, + {MAX98927_Brownout__level_3_amp_1_control_2, 0x18}, + {MAX98927_Brownout__level_3_amp_1_control_3, 0x0c}, + {MAX98927_Brownout__level_4_current_limit, 0x08}, + {MAX98927_Brownout__level_4_amp_1_control_1, 0x0e}, + {MAX98927_Brownout__level_4_amp_1_control_2, 0x80}, + {MAX98927_Brownout__level_4_amp_1_control_3, 0x00}, + {MAX98927_Brownout_threshold_hysterysis, 0x00}, + {MAX98927_Brownout_AMP_limiter_attack_release, 0x00}, + {MAX98927_Brownout_AMP_gain_attack_release, 0x00}, + {MAX98927_Brownout_AMP1_clip_mode, 0x00}, + {MAX98927_Meas_ADC_Config, 0x07}, + {MAX98927_Meas_ADC_Thermal_Warning_Threshhold, 0x78}, + {MAX98927_Meas_ADC_Thermal_Shutdown_Threshhold, 0xFF}, + {MAX98927_Pin_Config, 0x55}, + {MAX98927_Measurement_DSP_Config, 0xF7}, + {MAX98927_PCM_Tx_Enables_B, 0x00}, + {MAX98927_PCM_Rx_Enables_B, 0x00}, + {MAX98927_PCM_Tx_Channel_Sources_B, 0x00}, + {MAX98927_PCM_Tx_HiZ_Control_B, 0xFF}, + {MAX98927_Measurement_enables, 0x03}, + {MAX98927_PDM_Rx_Enable, 0x00}, + {MAX98927_AMP_volume_control, 0x38}, + {MAX98927_AMP_DSP_Config, 0x33}, + {MAX98927_DRE_Control, 0x01}, + {MAX98927_Speaker_Gain, 0x05}, + {MAX98927_SSM_Configuration, 0x85}, + {MAX98927_Boost_Control_0, 0x0c}, + {MAX98927_Boost_Control_1, 0x3e}, + {MAX98927_Meas_ADC_Base_Divide_MSByte, 0x00}, + {MAX98927_Meas_ADC_Base_Divide_LSByte, 0x00}, + {MAX98927_Meas_ADC_Thermal_Hysteresis, 0x00}, + {MAX98927_Env_Tracker_Vout_Headroom, 0x08}, + {MAX98927_Env_Tracker_Control, 0x01}, + {MAX98927_Brownout_enables, 0x00}, +}; + +int reg_channel_map[][7][2] = { + { //mono + {MAX98927_Boost_Control_3, 0x01}, + {MAX98927_PCM_Tx_Channel_Sources_A, 0x01}, + {MAX98927_PCM_Rx_Enables_A, 0x03}, + {MAX98927_PCM_Tx_Enables_A, 0x03}, + {MAX98927_PCM_Tx_HiZ_Control_A, 0xFC}, + {MAX98927_PCM_to_speaker_monomix_A, 0x80}, + {MAX98927_PCM_to_speaker_monomix_B, 0x00}, + }, + { //left channel + {MAX98927_Boost_Control_3, 0x01}, + {MAX98927_PCM_Tx_Channel_Sources_A, 0x00}, + {MAX98927_PCM_Rx_Enables_A, 0x01}, + {MAX98927_PCM_Tx_Enables_A, 0x01}, + {MAX98927_PCM_Tx_HiZ_Control_A, 0xFE}, + {MAX98927_PCM_to_speaker_monomix_A, 0x80}, + {MAX98927_PCM_to_speaker_monomix_B, 0x00}, + }, + { // right channel + {MAX98927_Boost_Control_3, 0x09}, + {MAX98927_PCM_Tx_Channel_Sources_A, 0x11}, + {MAX98927_PCM_Rx_Enables_A, 0x02}, + {MAX98927_PCM_Tx_Enables_A, 0x02}, + {MAX98927_PCM_Tx_HiZ_Control_A, 0xFD}, + {MAX98927_PCM_to_speaker_monomix_A, 0x81}, + {MAX98927_PCM_to_speaker_monomix_B, 0x01}, + }, + +}; + +static bool max98927_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_Interrupt_Raw_1: + case MAX98927_Interrupt_Raw_2: + case MAX98927_Interrupt_Raw_3: + case MAX98927_Interrupt_State_1: + case MAX98927_Interrupt_State_2: + case MAX98927_Interrupt_State_3: + case MAX98927_Interrupt_Flag_1: + case MAX98927_Interrupt_Flag_2: + case MAX98927_Interrupt_Flag_3: + case MAX98927_Interrupt_Enable_1: + case MAX98927_Interrupt_Enable_2: + case MAX98927_Interrupt_Enable_3: + case MAX98927_IRQ_Control: + case MAX98927_Clock_monitor_enable: + case MAX98927_Watchdog_Control: + case MAX98927_Meas_ADC_Thermal_Warning_Threshhold: + case MAX98927_Meas_ADC_Thermal_Shutdown_Threshhold: + case MAX98927_Meas_ADC_Thermal_Hysteresis: + case MAX98927_Pin_Config: + case MAX98927_PCM_Rx_Enables_A: + case MAX98927_PCM_Rx_Enables_B: + case MAX98927_PCM_Tx_Enables_A: + case MAX98927_PCM_Tx_Enables_B: + case MAX98927_PCM_Tx_HiZ_Control_A: + case MAX98927_PCM_Tx_HiZ_Control_B: + case MAX98927_PCM_Tx_Channel_Sources_A: + case MAX98927_PCM_Tx_Channel_Sources_B: + case MAX98927_PCM_Mode_Config: + case MAX98927_PCM_Master_Mode: + case MAX98927_PCM_Clock_setup: + case MAX98927_PCM_Sample_rate_setup_1: + case MAX98927_PCM_Sample_rate_setup_2: + case MAX98927_PCM_to_speaker_monomix_A: + case MAX98927_PCM_to_speaker_monomix_B: + case MAX98927_ICC_RX_Enables_A: + case MAX98927_ICC_RX_Enables_B: + case MAX98927_ICC_TX_Enables_A: + case MAX98927_ICC_TX_Enables_B: + case MAX98927_ICC_Data_Order_Select: + case MAX98927_ICC_HiZ_Manual_Mode: + case MAX98927_ICC_TX_HiZ_Enables_A: + case MAX98927_ICC_TX_HiZ_Enables_B: + case MAX98927_ICC_Link_Enables: + case MAX98927_PDM_Tx_Enables: + case MAX98927_PDM_Tx_HiZ_Control: + case MAX98927_PDM_Tx_Control: + case MAX98927_PDM_Rx_Enable: + case MAX98927_AMP_volume_control: + case MAX98927_AMP_DSP_Config: + case MAX98927_Tone_Generator_and_DC_Config: + case MAX98927_DRE_Control: + case MAX98927_AMP_enables: + case MAX98927_Speaker_source_select: + case MAX98927_Speaker_Gain: + case MAX98927_SSM_Configuration: + case MAX98927_Measurement_enables: + case MAX98927_Measurement_DSP_Config: + case MAX98927_Boost_Control_0: + case MAX98927_Boost_Control_3: + case MAX98927_Boost_Control_1: + case MAX98927_Meas_ADC_Config: + case MAX98927_Meas_ADC_Base_Divide_MSByte: + case MAX98927_Meas_ADC_Base_Divide_LSByte: + case MAX98927_Meas_ADC_Chan_0_Divide: + case MAX98927_Meas_ADC_Chan_1_Divide: + case MAX98927_Meas_ADC_Chan_2_Divide: + case MAX98927_Meas_ADC_Chan_0_Filt_Config: + case MAX98927_Meas_ADC_Chan_1_Filt_Config: + case MAX98927_Meas_ADC_Chan_2_Filt_Config: + case MAX98927_Meas_ADC_Chan_0_Readback: + case MAX98927_Meas_ADC_Chan_1_Readback: + case MAX98927_Meas_ADC_Chan_2_Readback: + case MAX98927_Brownout_status: + case MAX98927_Brownout_enables: + case MAX98927_Brownout_level_infinite_hold: + case MAX98927_Brownout_level_hold: + case MAX98927_Brownout__level_1_threshold: + case MAX98927_Brownout__level_2_threshold: + case MAX98927_Brownout__level_3_threshold: + case MAX98927_Brownout__level_4_threshold: + case MAX98927_Brownout_threshold_hysterysis: + case MAX98927_Brownout_AMP_limiter_attack_release: + case MAX98927_Brownout_AMP_gain_attack_release: + case MAX98927_Brownout_AMP1_clip_mode: + case MAX98927_Brownout__level_1_current_limit: + case MAX98927_Brownout__level_1_amp_1_control_1: + case MAX98927_Brownout__level_1_amp_1_control_2: + case MAX98927_Brownout__level_1_amp_1_control_3: + case MAX98927_Brownout__level_2_current_limit: + case MAX98927_Brownout__level_2_amp_1_control_1: + case MAX98927_Brownout__level_2_amp_1_control_2: + case MAX98927_Brownout__level_2_amp_1_control_3: + case MAX98927_Brownout__level_3_current_limit: + case MAX98927_Brownout__level_3_amp_1_control_1: + case MAX98927_Brownout__level_3_amp_1_control_2: + case MAX98927_Brownout__level_3_amp_1_control_3: + case MAX98927_Brownout__level_4_current_limit: + case MAX98927_Brownout__level_4_amp_1_control_1: + case MAX98927_Brownout__level_4_amp_1_control_2: + case MAX98927_Brownout__level_4_amp_1_control_3: + case MAX98927_Env_Tracker_Vout_Headroom: + case MAX98927_Env_Tracker_Boost_Vout_Delay: + case MAX98927_Env_Tracker_Release_Rate: + case MAX98927_Env_Tracker_Hold_Rate: + case MAX98927_Env_Tracker_Control: + case MAX98927_Env_Tracker__Boost_Vout_ReadBack: + case MAX98927_Global_Enable: + case MAX98927_REV_ID: + return true; + default: + return false; + } +} + +static bool max98927_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_Interrupt_Raw_1: + case MAX98927_Interrupt_Raw_2: + case MAX98927_Interrupt_Raw_3: + case MAX98927_Interrupt_State_1: + case MAX98927_Interrupt_State_2: + case MAX98927_Interrupt_State_3: + case MAX98927_Interrupt_Flag_1: + case MAX98927_Interrupt_Flag_2: + case MAX98927_Interrupt_Flag_3: + case MAX98927_Meas_ADC_Chan_0_Readback: + case MAX98927_Meas_ADC_Chan_1_Readback: + case MAX98927_Meas_ADC_Chan_2_Readback: + case MAX98927_Brownout_status: + case MAX98927_Env_Tracker__Boost_Vout_ReadBack: + return true; + default: + return false; + } +} + +#ifdef USE_DSM_MISC_DEV +#define PKG_HEADER (48) +#define PAYLOAD_COUNT (110) + +#ifdef CONFIG_DEBUG_FS +typedef enum { + DSM_API_MONO_SPKER = 0x00000000,//the mono speaker + DSM_API_STEREO_SPKER = 0x03000000,//the stereo speakers + + DSM_API_L_CHAN = 0x01000000,//the left channel speaker Id + DSM_API_R_CHAN = 0x02000000,//the left channel speaker Id + + DSM_API_CHANNEL_1 = 0x01000000, + DSM_API_CHANNEL_2 = 0x02000000, + DSM_API_CHANNEL_3 = 0x04000000, + DSM_API_CHANNEL_4 = 0x08000000, + DSM_API_CHANNEL_5 = 0x10000000, + DSM_API_CHANNEL_6 = 0x20000000, + DSM_API_CHANNEL_7 = 0x40000000, + DSM_API_CHANNEL_8 = 0x80000000, + + DSM_MAX_SUPPORTED_CHANNELS = 8 +} DSM_API_CHANNEL_ID; + +#define DSM_SET_MONO_PARAM(cmdId) ((cmdId&0x00FFFFFF)|DSM_API_MONO_SPKER) +#define DSM_SET_STEREO_PARAM(cmdId) ((cmdId&0x00FFFFFF)|DSM_API_STEREO_SPKER) +#define DSM_SET_LEFT_PARAM(cmdId) ((cmdId&0x00FFFFFF)|DSM_API_L_CHAN) +#define DSM_SET_RIGHT_PARAM(cmdId) ((cmdId&0x00FFFFFF)|DSM_API_R_CHAN) + +enum working_mode { + DSM_MODE_NONE = 0, + DSM_MODE_LEFT_ONLY, + DSM_MODE_RIGHT_ONLY, + DSM_MODE_LEFT_RIGHT, + DSM_MODE_RIGHT_LEFT, + DSM_MODE_CALIB_START, + DSM_MODE_CALIB_ING, + DSM_MODE_RDC, + DSM_MODE_CALIB_DONE, +}; + +typedef struct dsm_params { + uint32_t mode; + uint32_t pcount; + uint32_t pdata[PAYLOAD_COUNT]; +} dsm_param_t; + +struct param_info { + int pid; + char name[80]; + int q_val; +}; +#endif + +//MULTIPLE = 3.33, rdc/(1<<27) * MULTIPLE = [min, max] ohm +#define RDC_MIN (241833636) //1.801801 * (1<<27), 1.801801 * MULTIPLE = 6 ohm +#define RDC_MAX (403056239) //3.003003 * (1<<27), 3.003003 * MULTIPLE = 10 ohm + +static uint32_t gParam[PKG_HEADER+PAYLOAD_COUNT]; + +static int maxdsm_open(struct inode *inode, struct file *filep) +{ + return 0; +} + +#define ADAPTIVE_FC (16) +#define ADAPTIVE_DC_RES (18) + +static ssize_t maxdsm_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + uint8_t *payload = (uint8_t *)&gParam[PKG_HEADER]; + + if (count > sizeof(uint32_t)*PAYLOAD_COUNT) + count = sizeof(uint32_t)*PAYLOAD_COUNT; + + mutex_lock(&dsm_lock); + rc = afe_dsm_rx_get_params(payload, sizeof(uint32_t)*PAYLOAD_COUNT); + + if (rc != 0) { + pr_err("%s: afe_dsm_rx_get_params failed - %d\n", __func__, rc); + } + rc = copy_to_user(buf, payload, count); + if (rc != 0) { + pr_err("%s: copy_to_user failed - %d\n", __func__, rc); + } + mutex_unlock(&dsm_lock); + + return rc; +} + +static ssize_t maxdsm_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + uint8_t *payload = (uint8_t *)&gParam[PKG_HEADER]; + + if (count > sizeof(uint32_t)*PAYLOAD_COUNT) + count = sizeof(uint32_t)*PAYLOAD_COUNT; + + mutex_lock(&dsm_lock); + rc = copy_from_user(payload, buf, count); + if (rc != 0) { + pr_err("%s: copy_from_user failed - %d\n", __func__, rc); + goto exit; + } + + afe_dsm_rx_set_params(payload, count); +exit: + mutex_unlock(&dsm_lock); + + return rc; +} + +static const struct file_operations dsm_ctrl_fops = { + .owner = THIS_MODULE, + .open = maxdsm_open, + .release = NULL, + .read = maxdsm_read, + .write = maxdsm_write, + .mmap = NULL, + .poll = NULL, + .fasync = NULL, + .llseek = NULL, +}; + +static struct miscdevice dsm_ctrl_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsm_ctrl_dev", + .fops = &dsm_ctrl_fops +}; +#endif + +/* max. length of a alsa mixer control name */ +#define MAX_CONTROL_NAME 48 +#define CALIBRATE_FILE "/persist/spkr_calibration.bin" +#define CALIBRATE_FILE_R "/persist/spkr_calibration_r.bin" + +#if 0 +static int max989xx_create_calibfile(void) +{ + struct file *pfile = NULL; + mm_segment_t old_fs; + int ret = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + pfile = filp_open(CALIBRATE_FILE, O_RDWR | O_CREAT, 0666); + if (!IS_ERR(pfile)) { + pr_info("%s: %s create success! \n", __func__, CALIBRATE_FILE); + filp_close(pfile, NULL); + } else { + pr_info("%s: %s create failed! \n", __func__, CALIBRATE_FILE); + ret = -1; + } + + set_fs(old_fs); + + return ret; + +} +#endif + +static int max989xx_calib_save(uint32_t calib_value) +{ + struct file *pfile = NULL; + mm_segment_t old_fs; + int ret = 0; + loff_t pos = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + pfile = filp_open(CALIBRATE_FILE, O_RDWR | O_CREAT, 0666); + if (!IS_ERR(pfile)) { + pr_info("%s: save calib_value=%d \n", __func__, calib_value); + vfs_write(pfile, (char *)&calib_value, sizeof(uint32_t), &pos); + filp_close(pfile, NULL); + } else { + pr_info("%s: %s open failed! \n", __func__, CALIBRATE_FILE); + ret = -1; + } + + set_fs(old_fs); + + return ret; +} + +static int max989xx_calib_save_right(uint32_t calib_value) +{ + struct file *pfile = NULL; + mm_segment_t old_fs; + int ret = 0; + loff_t pos = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + pfile = filp_open(CALIBRATE_FILE_R, O_RDWR | O_CREAT, 0666); + if (!IS_ERR(pfile)) { + pr_info("%s: save calib_value=%d \n", __func__, calib_value); + vfs_write(pfile, (char *)&calib_value, sizeof(uint32_t), &pos); + filp_close(pfile, NULL); + } else { + pr_info("%s: %s open failed! \n", __func__, CALIBRATE_FILE_R); + ret = -1; + } + + set_fs(old_fs); + + return ret; +} + +static bool rdc_check_valid(uint32_t rdc) +{ + if (rdc > RDC_MIN && rdc < RDC_MAX) { + return true; + } + + pr_info("%s: rdc=%d invalid, [%d, %d] \n", __func__, rdc, RDC_MIN, RDC_MAX); + return false; +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t max989xx_dbgfs_calibrate_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct max98927_priv *max989xx = i2c_get_clientdata(i2c); + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + uint32_t impedance_l, impedance_r; + uint32_t calibrate_done = 2; + char *str; + //wait for playback stabilization + pr_info("%s: enter... \n", __func__); + mutex_lock(&dsm_lock); + ret = afe_dsm_pre_calib((uint8_t* )payload); + ret |= afe_dsm_get_calib((uint8_t* )payload); + if (ret == 0) { + impedance_l = *payload; + impedance_r = *(payload+1); + if (!rdc_check_valid(impedance_l)) { + impedance_l = 0xCACACACA; //calibration failed specail code + *payload = 0xCACACACA; + } + max989xx->ref_RDC[MAX98927L] = impedance_l; + max989xx_calib_save(impedance_l); + + if (max989xx->mono_stereo == 3) { + if (!rdc_check_valid(impedance_r)) { + impedance_r = 0xCACACACA; //calibration failed specail code + *(payload+1) = 0xCACACACA; + } + max989xx->ref_RDC[MAX98927R] = impedance_r; + max989xx_calib_save_right(impedance_r); + } + + afe_dsm_set_calib((uint8_t *)(payload)); + } else { + pr_info("%s failed to calibrate \n", __func__); + ret = -EIO; + goto exit; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + pr_info("%s: calibrate [impedance]=%d \n", __func__, impedance_l); + ret = snprintf(str, PAGE_SIZE, "%d:%d\n",calibrate_done, impedance_l); + if (max989xx->mono_stereo == 3) { + pr_info("%s: calibrate [impedance_r]=%d \n", __func__, impedance_r); + ret += snprintf(str+ret, PAGE_SIZE, "%d\n", impedance_r); + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + kfree(str); + +exit: + afe_dsm_post_calib((uint8_t* )payload); + mutex_unlock(&dsm_lock); + return ret; +} + +static ssize_t max989xx_dbgfs_impedance_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct max98927_priv *max989xx = i2c_get_clientdata(i2c); + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + uint32_t impedance = 0, impedance_r = 0; + char *str; + + afe_dsm_get_calib((uint8_t *)payload); + impedance = *payload; + if (!rdc_check_valid(impedance)) { + pr_info("%s failed to read impedance. \n", __func__); + ret = -EIO; + goto exit; + } + + if (max989xx->mono_stereo == 3) { + impedance_r = *(payload + 1); + if (!rdc_check_valid(impedance_r)) { + pr_info("%s failed to read impedance_r. \n", __func__); + ret = -EIO; + goto exit; + } + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + pr_info("%s: [impedance] = %d \n", __func__, impedance); + ret = snprintf(str, PAGE_SIZE, "%d\n", impedance); + if (max989xx->mono_stereo == 3) { + pr_info("%s: [impedance_r] = %d \n", __func__, impedance_r); + ret += snprintf(str+ret, PAGE_SIZE, "%d\n", impedance_r); + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + kfree(str); + +exit: + + return ret; +} +#endif + +static struct snd_kcontrol_new max98927_at_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SPK_PA rivision", + .info = max98927_info_rivision_ctl, + .get = max98927_get_rivision_ctl, + .put = max98927_set_rivision_ctl, + }, +}; + +static ssize_t max98927_state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_err("%s",__func__); + + return 0; +} +static ssize_t max98927_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + + struct max98927_priv *max989xx = g_max98927; + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + uint32_t impedance_l, impedance_r; + uint32_t calibrate_done = 2; + char *str; + + //wait for playback stabilization + pr_info("%s: enter... \n", __func__); + mutex_lock(&dsm_lock); + ret = afe_dsm_pre_calib((uint8_t* )payload); + ret |= afe_dsm_get_calib((uint8_t* )payload); + if (ret == 0) { + impedance_l = *payload; + impedance_r = *(payload+1); + if (!rdc_check_valid(impedance_l)) { + impedance_l = 0xCACACACA; //calibration failed specail code + *payload = 0xCACACACA; + } + max989xx->ref_RDC[MAX98927L] = impedance_l; +// impedance = impedance_l; + max989xx_calib_save(impedance_l); + + if (max989xx->mono_stereo == 3) { + if (!rdc_check_valid(impedance_r)) { + impedance_r = 0xCACACACA; //calibration failed specail code + *(payload+1) = 0xCACACACA; + } + max989xx->ref_RDC[MAX98927R] = impedance_r; + max989xx_calib_save_right(impedance_r); + } + + afe_dsm_set_calib((uint8_t *)(payload)); + } else { + pr_info("%s failed to calibrate \n", __func__); + ret = -EIO; + goto exit; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + pr_info("%s: calibrate [impedance]=%d \n", __func__, impedance_l); + ret = snprintf(str, PAGE_SIZE, "%d\n", impedance_l); + if (max989xx->mono_stereo == 3) { + pr_info("%s: calibrate [impedance_r]=%d \n", __func__, impedance_r); + ret += snprintf(str+ret, PAGE_SIZE, "%d\n", impedance_r); + } + + ret = sprintf(buf,"%d:%d\n",calibrate_done,impedance_l); + + kfree(str); + +exit: + + afe_dsm_post_calib((uint8_t* )payload); + mutex_unlock(&dsm_lock); + return ret; +} + +static struct device_attribute max98927_state_attr = + __ATTR(calibra, 0444, max98927_state_show, max98927_state_store); + +#ifdef CONFIG_DEBUG_FS +struct dsm_info_t { + int32_t averageTemp[2]; + int32_t bits_per_sample; + int32_t sampling_rate; + int32_t Q_factor; + int32_t working_mode; + int32_t enable_flag; + int32_t num_channels; + int32_t maxTempQ19[2]; //Q19, in Hz, valid floating-point range = [0, 4194304) + int32_t minTempQ19[2]; //Q19, in Hz, valid floating-point range = [0, 4194304) + int32_t maxFcQ9[2]; //Q9, in Hz, valid floating-point range = [0, 4194304) + int32_t minFcQ9[2]; //Q9, in Hz, valid floating-point range = [0, 4194304) + int32_t maxRdcQ27[2]; //Q27, valid floating-point range = [0, 16) + int32_t minRdcQ27[2]; //Q27 +}; +static ssize_t max989xx_dbgfs_info_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + //struct i2c_client *i2c = file->private_data; + //struct max98927_priv *max989xx = i2c_get_clientdata(i2c); + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + struct dsm_info_t* pInfo; + char *str; + + afe_dsm_get_libary_info(payload, 100); + pInfo = (struct dsm_info_t* ) payload; + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + pr_info("%s: [average temp] = %d \n", __func__, pInfo->averageTemp[0]); + ret = snprintf(str, PAGE_SIZE, "%d\n", pInfo->averageTemp[0]); + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + kfree(str); + +exit: + + return ret; +} + +static ssize_t max989xx_dbgfs_f0_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + //struct i2c_client *i2c = file->private_data; + //struct max98927_priv *max989xx = i2c_get_clientdata(i2c); + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + uint32_t f0 = 0; + char *str; + + afe_dsm_get_calib((uint8_t *)payload); + f0 = *(payload+2); + if (!rdc_check_valid(f0)) { + pr_info("%s failed to read f0. \n", __func__); + ret = -EIO; + goto exit; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + pr_info("%s: [f0] = %d \n", __func__, f0); + ret = snprintf(str, PAGE_SIZE, "%d\n", f0); + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + kfree(str); + +exit: + + return ret; +} + + +static ssize_t max989xx_dbgfs_temperature_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + //struct i2c_client *i2c = file->private_data; + //struct max98927_priv *max989xx = i2c_get_clientdata(i2c); + uint32_t *payload = (uint32_t *)&gParam[PKG_HEADER]; + int ret = 0; + uint32_t coiltemp = 0; + char *str; + + + afe_dsm_get_calib((uint8_t *)payload); + coiltemp = *(payload + 4); + pr_info("%s: [coiltemp] = %d \n", __func__, coiltemp); + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + pr_info("%s failed to kmalloc \n", __func__); + ret = -ENOMEM; + goto exit; + } + + ret = snprintf(str, PAGE_SIZE, "%d\n", coiltemp); + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + kfree(str); + +exit: + + return ret; +} + +static const struct file_operations max989xx_dbgfs_calibrate_fops = { + .open = simple_open, + .read = max989xx_dbgfs_calibrate_read, + .llseek = default_llseek, +}; + +static const struct file_operations max989xx_dbgfs_impedance_fops = { + .open = simple_open, + .read = max989xx_dbgfs_impedance_read, + .llseek = default_llseek, +}; + +static const struct file_operations max989xx_dbgfs_f0_fops = { + .open = simple_open, + .read = max989xx_dbgfs_f0_read, + .llseek = default_llseek, +}; +static const struct file_operations max989xx_dbgfs_temperature_fops = { + .open = simple_open, + .read = max989xx_dbgfs_temperature_read, + .llseek = default_llseek, +}; + +static const struct file_operations max989xx_dbgfs_info_fops = { + .open = simple_open, + .read = max989xx_dbgfs_info_read, + .llseek = default_llseek, +}; + +static void max989xx_debug_init(struct max98927_priv *max989xx, struct i2c_client *i2c) +{ + char name[60]; + + scnprintf(name, MAX_CONTROL_NAME, "%s", i2c->name); + max989xx->dbg_dir = debugfs_create_dir(name, NULL); + debugfs_create_file("calibrate", S_IRUGO|S_IWUGO, max989xx->dbg_dir, + i2c, &max989xx_dbgfs_calibrate_fops); + debugfs_create_file("impedance", S_IRUGO|S_IWUGO, max989xx->dbg_dir, + i2c, &max989xx_dbgfs_impedance_fops); + debugfs_create_file("f0detect", S_IRUGO|S_IWUGO, max989xx->dbg_dir, + i2c, &max989xx_dbgfs_f0_fops); + debugfs_create_file("temperature", S_IRUGO|S_IWUGO, max989xx->dbg_dir, + i2c, &max989xx_dbgfs_temperature_fops); + debugfs_create_file("infomation", S_IRUGO|S_IWUGO, max989xx->dbg_dir, + i2c, &max989xx_dbgfs_info_fops); + +} + +static void max989xx_debug_remove(struct max98927_priv *max989xx) +{ + if (max989xx->dbg_dir) + debugfs_remove_recursive(max989xx->dbg_dir); +} +#endif + +static int max989xx_calib_get(uint32_t* calib_value) +{ + struct file *pfile = NULL; + mm_segment_t old_fs; + int found = 0; + loff_t pos = 0; + + *calib_value = 0; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + pfile = filp_open(CALIBRATE_FILE, O_RDONLY, 0); + if (!IS_ERR_OR_NULL(pfile)) { + found = 1; + vfs_read(pfile, (char *)calib_value, sizeof(uint32_t), &pos); + pr_info("%s get calib_value %d \n", __func__, *calib_value); + filp_close(pfile, NULL); + } else { + pr_info("%s No found\n", __func__); + found = 0; + } + + set_fs(old_fs); + + return found; +} + +static int max989xx_calib_get_r(uint32_t* calib_value) +{ + struct file *pfile = NULL; + mm_segment_t old_fs; + int found = 0; + loff_t pos = 0; + + *calib_value = 0; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + pfile = filp_open(CALIBRATE_FILE_R, O_RDONLY, 0); + if (!IS_ERR_OR_NULL(pfile)) { + found = 1; + vfs_read(pfile, (char *)calib_value, sizeof(uint32_t), &pos); + pr_info("%s get calib_value %d \n", __func__, *calib_value); + filp_close(pfile, NULL); + } else { + pr_info("%s No found\n", __func__); + found = 0; + } + + set_fs(old_fs); + + return found; +} + + +int max98927_wrapper_read(struct max98927_priv *max98927, bool speaker, + unsigned int reg, unsigned int *val) +{ + int ret = -1; + if(i2c_states & (1 << speaker)){ + ret = regmap_read(max98927->regmap[speaker], reg, val); + } + return ret; +} + +void max98927_wrapper_write(struct max98927_priv *max98927, + unsigned int reg, unsigned int val) +{ + int i; + for(i = 0; i < MAX_CHANNEL_NUM; i++){ + if(i2c_states & (1 << i)){ + regmap_write(max98927->regmap[i], reg, val); + } + } +} + +void max98927_wrap_update_bits(struct max98927_priv *max98927, + unsigned int reg, unsigned int mask, unsigned int val) +{ + int i; + for(i = 0; i < MAX_CHANNEL_NUM; i++){ + if(i2c_states & (1 << i)){ + regmap_update_bits(max98927->regmap[i], reg, mask, val); + } + } +} + +static int max98927_reg_get_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + + max98927_wrapper_read(max98927, 0, reg, &val); + + val = (val >> shift) & mask; + + if (invert) + ucontrol->value.integer.value[0] = max - val; + else + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int max98927_reg_put_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + + unsigned int val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = max - val; + mask = mask << shift; + val = val << shift; + + max98927_wrap_update_bits(max98927, reg, mask, val); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, reg, val); + return 0; +} +static int max98927_reg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, unsigned int reg, + unsigned int mask, unsigned int shift) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data; + + max98927_wrapper_read(max98927, 0, reg, &data); + ucontrol->value.integer.value[0] = + (data & mask) >> shift; + return 0; +} + +static int max98927_reg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, unsigned int reg, + unsigned int mask, unsigned int shift) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + unsigned int sel = ucontrol->value.integer.value[0]; + max98927_wrap_update_bits(max98927, reg, mask, sel << shift); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, reg, sel); + return 0; +} + +static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + pr_info("%s: fmt 0x%08X\n", __func__, fmt); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE); + break; + case SND_SOC_DAIFMT_CBM_CFM: + max98927->master = true; + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFM: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID); + break; + default: + pr_info("DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE, + 0); + break; + case SND_SOC_DAIFMT_IB_NF: + max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE, + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE); + break; + default: + pr_info("DAI invert mode unsupported"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + max98927->iface |= SND_SOC_DAIFMT_I2S; + max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_FORMAT_Mask, + MAX98927_PCM_Mode_Config_PCM_FORMAT_I2S); + break; + case SND_SOC_DAIFMT_LEFT_J: + max98927->iface |= SND_SOC_DAIFMT_LEFT_J; + max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_FORMAT_Mask, + MAX98927_PCM_Mode_Config_PCM_FORMAT_LEFT); + break; + default: + return -EINVAL; + } + return 0; +} + +/* codec MCLK rate in master mode */ +static const int rate_table[] = { + 5644800, 6000000, 6144000, 6500000, + 9600000, 11289600, 12000000, 12288000, + 13000000, 19200000, +}; + +static int max98927_set_clock(struct max98927_priv *max98927, + struct snd_pcm_hw_params *params) +{ + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = 2 * max98927->ch_size; + int reg = MAX98927_PCM_Clock_setup; + int mask = MAX98927_PCM_Clock_setup_PCM_BSEL_Mask; + int value; + + if (max98927->master) { + int i; + /* match rate to closest value */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= max98927->sysclk) + break; + } + if (i == ARRAY_SIZE(rate_table)) { + pr_err("%s couldn't get the MCLK to match codec\n", __func__); + return -EINVAL; + } + max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode, + MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask, + i << MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT); + } + + switch (blr_clk_ratio) { + case 32: + value = 2; + break; + case 48: + value = 3; + break; + case 64: + value = 4; + break; + default: + return -EINVAL; + } + + pr_info("%s: BLCK fix to %d\n", __func__, blr_clk_ratio); + max98927_wrap_update_bits(max98927, + reg, mask, value); + return 0; +} + +static int max98927_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int sampling_rate = 0; + + switch (snd_pcm_format_width(params_format(params))) { + case 16: + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_16); + max98927->ch_size = 16; + break; + case 24: + //max98927_wrap_update_bits(max98927, + // MAX98927_PCM_Mode_Config, + // MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask, + // MAX98927_PCM_Mode_Config_PCM_CHANSZ_24); + //max98927->ch_size = 24; + //break; + case 32: + max98927_wrap_update_bits(max98927, + MAX98927_PCM_Mode_Config, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask, + MAX98927_PCM_Mode_Config_PCM_CHANSZ_32); + max98927->ch_size = 32; + break; + default: + pr_err("%s: format unsupported %d", + __func__, params_format(params)); + goto err; + } + pr_info("%s: format supported %d", + __func__, max98927->ch_size); + + switch (params_rate(params)) { + case 8000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000; + break; + case 11025: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025; + break; + case 12000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000; + break; + case 16000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000; + break; + case 22050: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050; + break; + case 24000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000; + break; + case 32000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000; + break; + case 44100: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100; + break; + case 48000: + sampling_rate |= + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000; + break; + default: + pr_err("%s rate %d not supported\n", __func__, params_rate(params)); + goto err; + } + /* set DAI_SR to correct LRCLK frequency */ + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_1, + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask, sampling_rate); + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2, + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask, sampling_rate<<4); + if (max98927->interleave_mode){ + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2, + MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask, (sampling_rate-3)); + } else { + max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2, + MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask, sampling_rate); + } + + return max98927_set_clock(max98927, params); +err: + return -EINVAL; +} + +#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int max98927_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + pr_info("%s: clk_id %d, freq %d, dir %d\n", __func__, clk_id, freq, dir); + + max98927->sysclk = freq; + return 0; +} + +static int max98927_stream_mute(struct snd_soc_dai *codec_dai, int mute, int stream) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + uint32_t* payload = (uint32_t *)&gParam[PKG_HEADER]; + int rc = 0; + uint32_t impedance = 0; + + pr_info("%s--- stream %d, mute %d \n", __func__, stream, mute); + + if (!max98927) { + pr_err("%s ------ priv data null pointer\n", __func__); + return 0; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mute) { + if (max98927->path_status) + afe_dsm_ramp_dn_cfg((uint8_t*)payload, 25); + max98927_wrap_update_bits(max98927, MAX98927_Global_Enable, MAX98927_Global_Enable_EN, 0); + max98927_wrap_update_bits(max98927, MAX98927_AMP_enables, MAX98927_AMP_enables, 0); + max98927->spk_mode = 0; + pr_info("%s ------ disable max98927 playback \n", __func__); + } else { + if (max98927->mono_stereo == 0x3) { // in stereo mode , we need put some changes into mixer ctl + if (max98927->spk_mode & (0x1 << MAX98927L)) { + regmap_update_bits(max98927->regmap[MAX98927L], MAX98927_AMP_enables, + MAX98927_AMP_enables_SPK_EN, 1); + regmap_update_bits(max98927->regmap[MAX98927L], MAX98927_Global_Enable, + MAX98927_Global_Enable_EN, 1); + } else if (max98927->spk_mode & (0x1 << MAX98927R)) { + regmap_update_bits(max98927->regmap[MAX98927R], MAX98927_AMP_enables, + MAX98927_AMP_enables_SPK_EN, 1); + regmap_update_bits(max98927->regmap[MAX98927R], MAX98927_Global_Enable, + MAX98927_Global_Enable_EN, 1); + } + } else { + max98927_wrap_update_bits(max98927, MAX98927_AMP_enables, MAX98927_AMP_enables_SPK_EN, 1); + max98927_wrap_update_bits(max98927, MAX98927_Global_Enable, MAX98927_Global_Enable_EN, 1); + } + + pr_info("%s ------ enable max98927 playback \n", __func__); + } + } else if(stream == SNDRV_PCM_STREAM_CAPTURE) { //feedback path disable and enable + if (mute) { + /* max98927_wrapper_write(max98927, MAX98927_Measurement_enables, 0x0); */ + max98927->path_status = 0; + pr_info("%s ------ disable max98927 capture\n", __func__); + } else { + max98927_wrapper_write(max98927, MAX98927_Measurement_enables, 0x3); + max98927->path_status |= i2c_states; // flag to status of feedback (0: means feedback is inactive, other: feedback is active, DSM should be started to protect speaker) + if (max98927->mono_stereo == 0x0 || (max98927->mono_stereo & 0x1)) { + if (!rdc_check_valid(max98927->ref_RDC[MAX98927L]) && max98927->ref_RDC[MAX98927L] != 0xCACACACA){ + rc = max989xx_calib_get(&impedance); + if (rdc_check_valid(impedance) || impedance == 0xCACACACA) { + max98927->ref_RDC[MAX98927L] = impedance; + pr_info("%s: ref_RDC left =%d \n", __func__, max98927->ref_RDC[MAX98927L]); + } + } + } + if (max98927->mono_stereo & 0x2) { + if (!rdc_check_valid(max98927->ref_RDC[MAX98927R]) && max98927->ref_RDC[MAX98927R] != 0xCACACACA){ + impedance = 0; + rc = max989xx_calib_get_r(&impedance); + if (rdc_check_valid(impedance) || impedance == 0xCACACACA) { + max98927->ref_RDC[MAX98927R] = impedance; + pr_info("%s: ref_RDC right=%d \n", __func__, max98927->ref_RDC[MAX98927R]); + } + } + } + mutex_lock(&dsm_lock); + *payload = max98927->ref_RDC[MAX98927L]; + *(payload+1) = max98927->ref_RDC[MAX98927R]; + afe_dsm_set_calib((uint8_t *)payload); + //load calibration to DSM + mutex_unlock(&dsm_lock); + pr_info("%s ------ enable max98927 capture\n", __func__); + } + } + + return 0; +} + +static const struct snd_soc_dai_ops max98927_dai_ops = { + .set_sysclk = max98927_dai_set_sysclk, + .set_fmt = max98927_dai_set_fmt, + .hw_params = max98927_dai_hw_params, + .mute_stream = max98927_stream_mute, +}; + +static int max98927_feedforward_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + u32 ret = 0; + //struct snd_soc_component *component = w->component; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + /* uint32_t* payload = (uint32_t *)&gParam[PKG_HEADER]; */ + + if(!max98927){ + pr_err("%s------priv data null pointer\n", __func__); + return ret; + } + pr_info("%s---feedforward event %d\n", __func__, event); + switch(event){ + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_POST_PMD: + break; + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + default: + break; + } + return ret; +} +static int max98927_feedback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + u32 ret = 0; + //struct snd_soc_component *component = w->component; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + if(!max98927){ + pr_err("%s------priv data null pointer\n", __func__); + return ret; + } + pr_info("%s---feedback event %d\n", __func__, event); + switch(event){ + case SND_SOC_DAPM_POST_PMU: + /* max98927_wrapper_write(max98927, MAX98927_Measurement_enables, 0x3); */ + break; + case SND_SOC_DAPM_POST_PMD: + /* max98927_wrapper_write(max98927, MAX98927_Measurement_enables, 0x0); */ + break; + default: + break; + } + return ret; +} + +static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DACs", "HiFi Playback", SND_SOC_NOPM, 0, 0, + max98927_feedforward_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADCs", "HiFi Capture", SND_SOC_NOPM, 0, 0, + max98927_feedback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("MAX98927_IN"), + SND_SOC_DAPM_OUTPUT("MAX98927_OUT"), +}; + +/*zhiguang.su@MultiMediaService,2017-04-26,add ftm spk pa rivision test*/ +static int max98927_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 max98927_get_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int ret; + unsigned int reg; + + pr_err("%s\n", __func__); + ret = regmap_read(max98927->regmap[0], MAX98927_REV_ID , ®); + if (ret < 0) { + pr_err("%s Failed to read Revision register: %d\n", + __func__, ret); + ucontrol->value.integer.value[0] = 0; + }else + ucontrol->value.integer.value[0] = 1; + + return 0; +} + +static int max98927_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 DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0); +static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0); + +static int max98927_spk_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = max98927->spk_gain; + pr_info("max98927_spk_gain_get: spk_gain setting returned %d\n", + (int) ucontrol->value.integer.value[0]); + + return 0; +} + +static int max98927_spk_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + pr_info("max98927_spk_gain_put: %d\n",sel); + + if (sel < ((1 << MAX98927_Speaker_Gain_Width) - 1)) { + max98927_wrap_update_bits(max98927, MAX98927_Speaker_Gain, + MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask, sel); + max98927->spk_gain = sel; + } + return 0; +} + +static int max98927_digital_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = max98927->digital_gain; + pr_info("%s: spk_gain setting returned %d\n", __func__, + (int) ucontrol->value.integer.value[0]); + return 0; +} + +static int max98927_digital_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + pr_info("max98927_digital_gain_put: %d\n",sel); + + if (sel <= ((1 << MAX98927_AMP_VOL_WIDTH) - 1)) { + max98927_wrap_update_bits(max98927, MAX98927_AMP_volume_control, + MAX98927_AMP_volume_control_AMP_VOL_Mask, sel); + max98927->digital_gain = sel; + } + return 0; +} + +static int max98927_boost_voltage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_0, + MAX98927_Boost_Control_0_BST_VOUT_Mask, 0); +} + +static int max98927_boost_voltage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_Boost_Control_0, + MAX98927_Boost_Control_0_BST_VOUT_Mask, 0); +} + +static int max98927_boost_input_limit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_1, + MAX98927_Boost_Control_1_BST_ILIM_Mask, MAX98927_BST_ILIM_SHIFT); +} + +static int max98927_boost_input_limit_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_Boost_Control_1, + MAX98927_Boost_Control_1_BST_ILIM_Mask, MAX98927_BST_ILIM_SHIFT); +} + +static int max98927_spk_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_Speaker_source_select, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0); +} + +static int max98927_spk_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_Speaker_source_select, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0); +} + +static int max98927_mono_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_get(kcontrol, ucontrol, MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask, 0); +} + +static int max98927_mono_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return max98927_reg_put(kcontrol, ucontrol, MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask, 0); +} + +static int max98927_mono_out_get_l(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data = 0; + if(i2c_states & MAX98927_CH0){ + regmap_read(max98927->regmap[MAX98927L], MAX98927_PCM_to_speaker_monomix_A, &data); + ucontrol->value.integer.value[0] = + (data & MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask); + pr_info("%s: value:%d", __func__, data); + } + + return 0; +} + +static int max98927_mono_out_put_l(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + if(i2c_states & MAX98927_CH0){ + regmap_update_bits(max98927->regmap[MAX98927L], MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask, sel); + regmap_update_bits(max98927->regmap[MAX98927L], MAX98927_PCM_Rx_Enables_A, + 0xf, sel+1); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_PCM_to_speaker_monomix_A, sel); + } + + return 0; +} + +static int max98927_mono_out_get_r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data = 0; + + if(i2c_states & MAX98927_CH1){ + regmap_read(max98927->regmap[MAX98927R], MAX98927_PCM_to_speaker_monomix_A, &data); + ucontrol->value.integer.value[0] = + (data & MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask); + } + pr_info("%s: value:%d", __func__, data); + return 0; +} + +static int max98927_mono_out_put_r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + if(i2c_states & MAX98927_CH1){ + regmap_update_bits(max98927->regmap[MAX98927R], MAX98927_PCM_to_speaker_monomix_A, + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask, sel); + regmap_update_bits(max98927->regmap[MAX98927R], MAX98927_PCM_Rx_Enables_A, 0xf, sel+1); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_PCM_to_speaker_monomix_A, sel); + } else { + pr_info("%s: mono mode not support!!\n", __func__); + } + return 0; +} + +static int max98927_feedback_en_get_l(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data = 0; + + if(i2c_states & MAX98927_CH0){ + regmap_read(max98927->regmap[MAX98927L], MAX98927_Measurement_enables, &data); + ucontrol->value.integer.value[0] = data; + pr_info("%s: value:%d", __func__, data); + } + + return 0; +} + +static int max98927_feedback_en_put_l(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + if(i2c_states & MAX98927_CH0){ + regmap_write(max98927->regmap[MAX98927L], MAX98927_Measurement_enables, sel); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_Measurement_enables, sel); + } + return 0; +} + +static int max98927_feedback_en_get_r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data = 0; + + if(i2c_states & MAX98927_CH1){ + regmap_read(max98927->regmap[MAX98927R], MAX98927_Measurement_enables, &data); + ucontrol->value.integer.value[0] = data; + } + pr_info("%s: value:%d", __func__, data); + return 0; +} + +static int max98927_feedback_en_put_r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + if(i2c_states & MAX98927_CH1){ + regmap_write(max98927->regmap[MAX98927R], MAX98927_Measurement_enables, sel); + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_Measurement_enables, sel); + } else { + pr_info("%s: mono mode not support!!\n", __func__); + } + return 0; +} + +static int max98927_left_channel_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data_global = 0; + int data_amp = 0; + //int data = 0; + + if(i2c_states & MAX98927_CH0){ + regmap_read(max98927->regmap[MAX98927L], MAX98927_Global_Enable, &data_global); + regmap_read(max98927->regmap[MAX98927L], MAX98927_AMP_enables, &data_amp); + ucontrol->value.integer.value[0] = (data_global & MAX98927_Global_Enable_EN) + & (data_amp & MAX98927_AMP_enables_SPK_EN); + } + + pr_info("%s: value:%d", __func__, (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int max98927_left_channel_enable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + max98927->spk_mode &= ~0x1; + max98927->spk_mode |= sel; + + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_Global_Enable, sel); + return 0; +} + +static int max98927_right_channel_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int data_global = 0; + int data_amp = 0; + + if(i2c_states & MAX98927_CH1){ + regmap_read(max98927->regmap[MAX98927R], MAX98927_Global_Enable, &data_global); + regmap_read(max98927->regmap[MAX98927R], MAX98927_AMP_enables, &data_amp); + ucontrol->value.integer.value[0] = (data_global & MAX98927_Global_Enable_EN) + & (data_amp & MAX98927_AMP_enables_SPK_EN); + } + + pr_info("%s: value:%d", __func__, (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int max98927_right_channel_enable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + max98927->spk_mode &= ~0x2; + max98927->spk_mode |= sel<<0x1; + pr_info("%s: register 0x%02X, value 0x%02X\n", + __func__, MAX98927_Global_Enable, sel); + return 0; +} + +static const char * const max98927_boost_voltage_text[] = { + "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V", + "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V", + "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V", + "9.5V", "9.625V", "9.75V", "9.875V", "10V" +}; + +static const char * const max98927_boost_current_limit_text[] = { + "1.0A", "1.1A", "1.2A", "1.3A", "1.4A", "1.5A", "1.6A", "1.7A", "1.8A", "1.9A", + "2.0A", "2.1A", "2.2A", "2.3A", "2.4A", "2.5A", "2.6A", "2.7A", "2.8A", "2.9A", + "3.0A", "3.1A", "3.2A", "3.3A", "3.4A", "3.5A", "3.6A", "3.7A", "3.8A", "3.9A", + "4.0A", "4.1A" +}; + +static const char * const max98927_speaker_source_text[] = { + "i2s", "reserved", "tone", "pdm" +}; + +static const char * const max98927_monomix_output_text[] = { + "ch_0", "ch_1", "ch_1_2_div" +}; +static const char * const max98927_feedback_switch_text[] = { + "OFF", "V_EN", "I_EN", "ON" +}; + +static const struct soc_enum max98927_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_monomix_output_text), max98927_monomix_output_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_speaker_source_text), max98927_speaker_source_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_boost_voltage_text), max98927_boost_voltage_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_feedback_switch_text), max98927_feedback_switch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_boost_current_limit_text), max98927_boost_current_limit_text), +}; + +static const struct snd_kcontrol_new max98927_snd_controls[] = { + SOC_SINGLE_EXT_TLV("Speaker Volume", MAX98927_Speaker_Gain, + 0, (1<dapm; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + pr_info("%s: enter\n", __func__); + + max98927->component = component; + snd_soc_dapm_ignore_suspend(dapm, "MAX98927_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAX98927_IN"); + snd_soc_dapm_ignore_suspend(dapm, "HiFi Playback"); + snd_soc_dapm_ignore_suspend(dapm, "HiFi Capture"); + + snd_soc_add_component_controls(max98927->component, max98927_at_controls, + ARRAY_SIZE(max98927_at_controls)); + + + snd_soc_dapm_sync(dapm); + + g_max98927 = max98927; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_max98927 = { + .probe = max98927_probe, + .dapm_routes = max98927_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98927_audio_map), + .dapm_widgets = max98927_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets), + .controls = max98927_snd_controls, + .num_controls = ARRAY_SIZE(max98927_snd_controls), +}; + +static const struct regmap_config max98927_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98927_REV_ID, + .readable_reg = max98927_readable_register, + .volatile_reg = max98927_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +int max98927_get_i2c_states(void) +{ + return i2c_states; +} +EXPORT_SYMBOL(max98927_get_i2c_states); + +static int max98927_reset(struct i2c_client *i2c, struct max98927_priv* max98927) +{ + int ret = 0; + max98927->reset_gpio_l= of_get_named_gpio(i2c->dev.of_node, "maxim,98927-reset-gpio", 0); + pr_info("max98927_reset:%d------\n", max98927->reset_gpio_l); + + if (max98927->reset_gpio_l > 0){ + ret = gpio_request(max98927->reset_gpio_l, "max_98927_reset"); + if (ret) { + pr_err("max98927_i2c_probe : failed to request rest gpio %d error:%d\n", + max98927->reset_gpio_l, ret); + gpio_free(max98927->reset_gpio_l); + return ret; + } + gpio_direction_output(max98927->reset_gpio_l, 0); + msleep(10); + gpio_direction_output(max98927->reset_gpio_l, 1); + msleep(5); + } + return ret; +} + +static bool check_max98927_presence(struct regmap* regmap) +{ + int rc = 0, reg = 0, i; + + rc = regmap_read(regmap, MAX98927_REV_ID, ®); + for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { + pr_err("Failed reading version=%u - retry(%d)\n", reg, i); + /* retry after delay of increasing order */ + msleep(delay_array_msec[i]); + rc = regmap_read(regmap, MAX98927_REV_ID, ®); + } + if (rc) { + pr_err("Failed reading version=%u rc=%d\n", reg, rc); + return false; + }else{ + pr_info("max98927 device version 0x%02X\n", reg); + return true; + } +} +static int max98927_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + static struct max98927_priv *max98927 = NULL; + int value, i, ret = 0; + unsigned int presence = 0; + + pr_err("****** %s id.name =%s id.driver_data= %d \n",__func__,id->name,(int)(id->driver_data)); + + if (!max98927) { + max98927 = devm_kzalloc(&i2c->dev, + sizeof(*max98927), GFP_KERNEL); + if (!max98927) { + pr_info("------%s devm_kzalloc error!!\n", __func__); + return -ENOMEM; + } + } + if (!of_property_read_u32(i2c->dev.of_node, "mono_stereo_mode", &value)) { + if (value > 3) { + pr_err("only support max to 2 channel!\n"); + value = 0; + } + max98927->mono_stereo = value; // 0: mono 1: left only 2: right only 3: stereo + } + + if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode", &value)) { + if (value > 1) { + pr_info("interleave number is wrong:\n"); + } + max98927->interleave_mode = value; + } +#if 0 + if (!max98927->i2c_pull) { + max98927->i2c_pull = devm_regulator_get(&i2c->dev, "i2c-pull"); + if (IS_ERR(max98927->i2c_pull)) { + pr_err("regulator i2c_pull get failed\n "); + devm_kfree(&i2c->dev, max98927); + return PTR_ERR(max98927->i2c_pull); + } + + ret = regulator_enable(max98927->i2c_pull); + if (ret) { + pr_err("regulator_enable i2c_pull failed! \n"); + devm_kfree(&i2c->dev, max98927); + return ret; + } + } + max98927->max989xx_vdd = regulator_get(&i2c->dev, "max989xx_vdd"); + if (IS_ERR(max98927->max989xx_vdd)) { + pr_err("regulator max989xx_vdd get failed\n "); + devm_kfree(&i2c->dev, max98927); + return PTR_ERR(max98927->max989xx_vdd); + } else { + if (regulator_count_voltages(max98927->max989xx_vdd) > 0) { + ret = regulator_set_voltage(max98927->max989xx_vdd, 1800000, 1800000); + if (ret) { + pr_err("%s Regulator set vdd failed ret=%d\n", __func__, ret); + return ret; + } + + ret = regulator_set_load(max98927->max989xx_vdd, 200000); + if (ret) { + pr_err("%s failed to set load, ret=%d\n", __func__, ret); + return ret; + } + } + } + + ret = regulator_enable(max98927->max989xx_vdd); + if (ret) { + pr_err("regulator_enable max989xx_vdd failed! \n"); + devm_kfree(&i2c->dev, max98927); + return ret; + } +#endif + + ret = max98927_reset(i2c, max98927); // reset pin to chip hardware reset. + + i2c_set_clientdata(i2c, max98927); + + max98927->regmap[id->driver_data] = + devm_regmap_init_i2c(i2c, &max98927_regmap); + if(IS_ERR(max98927->regmap[id->driver_data])){ + ret = PTR_ERR(max98927->regmap[id->driver_data]); + dev_err(&i2c->dev, + "Failed to allocate chennel %lu regmap : %d\n", id->driver_data, ret); + }else{ //below initialize the register by mode and chip status. + if(check_max98927_presence(max98927->regmap[id->driver_data])){ + presence = (1 << id->driver_data); + if(max98927->mono_stereo == 0){ + i2c_states |= presence; //mark this chip, then app can address it. + for (i = 0;i < sizeof(reg_channel_map[0])/sizeof(reg_channel_map[0][0]);i++) + regmap_write(max98927->regmap[id->driver_data], reg_channel_map[0][i][0], reg_channel_map[0][i][1]); + }else if(max98927->mono_stereo & presence){ + i2c_states |= presence; //mark this chip, then app can address it. + for (i = 0;i < sizeof(reg_channel_map[id->driver_data+1])/sizeof(reg_channel_map[id->driver_data+1][0]);i++) + regmap_write(max98927->regmap[id->driver_data], + reg_channel_map[id->driver_data+1][i][0], reg_channel_map[id->driver_data+1][i][1]); + } + for (i = 0;i < sizeof(reg_common_map)/sizeof(reg_common_map[0]);i++) + regmap_write(max98927->regmap[id->driver_data], reg_common_map[i][0], reg_common_map[i][1]); + + if (max98927->interleave_mode) + regmap_write(max98927->regmap[id->driver_data], MAX98927_PCM_Tx_Channel_Sources_B, 0x20); + + } + } + if(presence){ + smartpa_present = 1; + if(max98927->dev == NULL){ + dev_set_name(&i2c->dev, "%s", "max98927"); //rename the i2c clinet name for easy to use. + ret = snd_soc_register_component(&i2c->dev, &soc_codec_dev_max98927, + max98927_dai, ARRAY_SIZE(max98927_dai)); + pr_err("****** %s snd_soc_register_component id =%d\n",__func__,(int)(id->driver_data)); + if (ret < 0) { + pr_err("max98927 Failed to register codec: %d\n", ret); + i2c_states = 0; + return ret; + } + max98927->dev = &i2c->dev; + pr_info("max98927 register codec ok.\n"); +#ifdef USE_DSM_MISC_DEV + ret = misc_register(&dsm_ctrl_miscdev); + if (ret != 0) + pr_err("max98927 misc_register error:%d\n", ret); +#endif + + #ifdef CONFIG_DEBUG_FS + max989xx_debug_init(max98927, i2c); + #endif + } + ret = sysfs_create_file(&i2c->dev.kobj, &max98927_state_attr.attr); + if(ret < 0) + { + pr_err("%s sysfs_create_file max98927_state_attr err.",__func__); + } + }else + { + pr_err("max98927 detection failed at %s - %x. \n", i2c->name, i2c->addr); + //suzhiguang,should release when no smartpa detect. + gpio_free(max98927->reset_gpio_l); + } + return ret; +} + +static int max98927_i2c_remove(struct i2c_client *client) +{ + struct max98927_priv *max98927 = i2c_get_clientdata(client); + if(max98927) { + if(max98927->dev == &client->dev) { + snd_soc_unregister_component(&client->dev); + i2c_set_clientdata(client, NULL); +#ifdef CONFIG_DEBUG_FS + max989xx_debug_remove(max98927); +#endif + kfree(max98927); +#ifdef USE_DSM_MISC_DEV + misc_deregister(&dsm_ctrl_miscdev); +#endif + } + } + + return 0; +} + +static const struct i2c_device_id max98927_i2c_id[] = { + { "max98927L", MAX98927L }, + { "max98927R", MAX98927R }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98927_i2c_id); + +static const struct of_device_id max98927_of_match[] = { + { .compatible = "maxim,max98927L", }, + { .compatible = "maxim,max98927R", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98927_of_match); + +static struct i2c_driver max98927_i2c_driver = { + .driver = { + .name = "max98927", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max98927_of_match), + .pm = NULL, + }, + .probe = max98927_i2c_probe, + .remove = max98927_i2c_remove, + .id_table = max98927_i2c_id, +}; + +module_i2c_driver(max98927_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98927 driver"); +MODULE_AUTHOR("Maxim Integrated Inc."); +MODULE_LICENSE("GPL"); diff --git a/techpack/audio/asoc/codecs/max98927/max98927.h b/techpack/audio/asoc/codecs/max98927/max98927.h new file mode 100644 index 000000000000..061882ba458e --- /dev/null +++ b/techpack/audio/asoc/codecs/max98927/max98927.h @@ -0,0 +1,1256 @@ +/* + * AnishBestPingPongerAliveButNotAsGoodAsMax.h + * + * Auto generated by Maxim Integrated, supporting revision + * Search for #BYHAND for flags on special cases to fix by hand + * #BYHAND width doesn't match: (this is usually when a bit has shorthand or formulaic descriptions) + * #BYHAND width >= 5: + */ +#ifndef __MAX98927_REGISTERDEFS_H +#define __MAX98927_REGISTERDEFS_H +#ifdef CONFIG_SND_SOC_MAXIM_DSM +#include +#endif /* CONFIG_SND_SOC_MAXIM_DSM */ + +#define MAX98927L 0 +#define MAX98927R 1 +#define MAX_CHANNEL_NUM (2) +typedef enum +{ + // Interrupt Raw 1 (Address 0x0001) + MAX98927_Interrupt_Raw_1 = 0x0001, + MAX98927_Interrupt_Raw_1_BDE_ACTIVE_END_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_1_BDE_ACTIVE_BGN_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_1_BDE_LEVEL_CHANGE_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_1_BDE_L8_RAW = (0x1 << 3), + MAX98927_Interrupt_Raw_1_THERMWARN_END_RAW = (0x1 << 4), + MAX98927_Interrupt_Raw_1_THERMWARN_START_RAW = (0x1 << 5), + MAX98927_Interrupt_Raw_1_THERMSHDN_END_RAW = (0x1 << 6), + MAX98927_Interrupt_Raw_1_THERMSHDN_START_RAW = (0x1 << 7), + + // Interrupt Raw 2 (Address 0x0002) + MAX98927_Interrupt_Raw_2 = 0x0002, + MAX98927_Interrupt_Raw_2_WATCHDOGWARN_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_2_WATCHDOGFAIL_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_2_BOOSTCURRLIM_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_2_CLKSTOP_RAW = (0x1 << 3), + MAX98927_Interrupt_Raw_2_CLKSTART_RAW = (0x1 << 4), + MAX98927_Interrupt_Raw_2_MEASADC_END_RAW = (0x1 << 5), + MAX98927_Interrupt_Raw_2_PWRDN_DONE_RAW = (0x1 << 6), + MAX98927_Interrupt_Raw_2_PWRUP_DONE_RAW = (0x1 << 7), + + // Interrupt Raw 3 (Address 0x0003) + MAX98927_Interrupt_Raw_3 = 0x0003, + MAX98927_Interrupt_Raw_3_PWRUP_FAIL_RAW = (0x1 << 0), + MAX98927_Interrupt_Raw_3_AUTH_DONE_RAW = (0x1 << 1), + MAX98927_Interrupt_Raw_3_SPK_OVC_RAW = (0x1 << 2), + MAX98927_Interrupt_Raw_3_BST_UVLO_RAW = (0x1 << 3), + + // Interrupt State 1 (Address 0x0004) + MAX98927_Interrupt_State_1 = 0x0004, + MAX98927_Interrupt_State_1_BDE_ACTIVE_END_STATE = (0x1 << 0), + MAX98927_Interrupt_State_1_BDE_ACTIVE_BGN_STATE = (0x1 << 1), + MAX98927_Interrupt_State_1_BDE_LEVEL_CHANGE_STATE = (0x1 << 2), + MAX98927_Interrupt_State_1_BDE_L8_STATE = (0x1 << 3), + MAX98927_Interrupt_State_1_THERMWARN_END_STATE = (0x1 << 4), + MAX98927_Interrupt_State_1_THERMWARN_START_STATE = (0x1 << 5), + MAX98927_Interrupt_State_1_THERMSHDN_END_STATE = (0x1 << 6), + MAX98927_Interrupt_State_1_THERMSHDN_START_STATE = (0x1 << 7), + + // Interrupt State 2 (Address 0x0005) + MAX98927_Interrupt_State_2 = 0x0005, + MAX98927_Interrupt_State_2_WATCHDOGWARN_STATE = (0x1 << 0), + MAX98927_Interrupt_State_2_WATCHDOGFAIL_STATE = (0x1 << 1), + MAX98927_Interrupt_State_2_BOOSTCURRLIM_STATE = (0x1 << 2), + MAX98927_Interrupt_State_2_CLKSTOP_STATE = (0x1 << 3), + MAX98927_Interrupt_State_2_CLKSTART_STATE = (0x1 << 4), + MAX98927_Interrupt_State_2_MEASADC_END_STATE = (0x1 << 5), + MAX98927_Interrupt_State_2_PWRDN_DONE_STATE = (0x1 << 6), + MAX98927_Interrupt_State_2_PWRUP_DONE_STATE = (0x1 << 7), + + // Interrupt State 3 (Address 0x0006) + MAX98927_Interrupt_State_3 = 0x0006, + MAX98927_Interrupt_State_3_PWRUP_FAIL_STATE = (0x1 << 0), + MAX98927_Interrupt_State_3_AUTH_DONE_STATE = (0x1 << 1), + MAX98927_Interrupt_State_3_SPK_OVC_STATE = (0x1 << 2), + MAX98927_Interrupt_State_3_BST_UVLO_STATE = (0x1 << 3), + + // Interrupt Flag 1 (Address 0x0007) + MAX98927_Interrupt_Flag_1 = 0x0007, + MAX98927_Interrupt_Flag_1_BDE_ACTIVE_END_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_1_BDE_ACTIVE_BGN_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_1_BDE_LEVEL_CHANGE_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_1_BDE_L8_FLAG = (0x1 << 3), + MAX98927_Interrupt_Flag_1_THERMWARN_END_FLAG = (0x1 << 4), + MAX98927_Interrupt_Flag_1_THERMWARN_START_FLAG = (0x1 << 5), + MAX98927_Interrupt_Flag_1_THERMSHDN_END_FLAG = (0x1 << 6), + MAX98927_Interrupt_Flag_1_THERMSHDN_START_FLAG = (0x1 << 7), + + // Interrupt Flag 2 (Address 0x0008) + MAX98927_Interrupt_Flag_2 = 0x0008, + MAX98927_Interrupt_Flag_2_WATCHDOGWARN_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_2_WATCHDOGFAIL_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_2_BOOSTCURRLIM_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_2_CLKSTOP_FLAG = (0x1 << 3), + MAX98927_Interrupt_Flag_2_CLKSTART_FLAG = (0x1 << 4), + MAX98927_Interrupt_Flag_2_MEASADC_END_FLAG = (0x1 << 5), + MAX98927_Interrupt_Flag_2_PWRDN_DONE_FLAG = (0x1 << 6), + MAX98927_Interrupt_Flag_2_PWRUP_DONE_FLAG = (0x1 << 7), + + // Interrupt Flag 3 (Address 0x0009) + MAX98927_Interrupt_Flag_3 = 0x0009, + MAX98927_Interrupt_Flag_3_PWRUP_FAIL_FLAG = (0x1 << 0), + MAX98927_Interrupt_Flag_3_AUTH_DONE_FLAG = (0x1 << 1), + MAX98927_Interrupt_Flag_3_SPK_OVC_FLAG = (0x1 << 2), + MAX98927_Interrupt_Flag_3_BST_UVLO_FLAG = (0x1 << 3), + + // Interrupt Enable 1 (Address 0x000a) + MAX98927_Interrupt_Enable_1 = 0x000a, + MAX98927_Interrupt_Enable_1_BDE_ACTIVE_END_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_1_BDE_ACTIVE_BGN_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_1_BDE_LEVEL_CHANGE_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_1_BDE_L8_EN = (0x1 << 3), + MAX98927_Interrupt_Enable_1_THERMWARN_END_EN = (0x1 << 4), + MAX98927_Interrupt_Enable_1_THERMWARN_START_EN = (0x1 << 5), + MAX98927_Interrupt_Enable_1_THERMSHDN_END_EN = (0x1 << 6), + MAX98927_Interrupt_Enable_1_THERMSHDN_START_EN = (0x1 << 7), + + // Interrupt Enable 2 (Address 0x000b) + MAX98927_Interrupt_Enable_2 = 0x000b, + MAX98927_Interrupt_Enable_2_WATCHDOGWARN_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_2_WATCHDOGFAIL_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_2_BOOSTCURRLIM_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_2_CLKSTOP_EN = (0x1 << 3), + MAX98927_Interrupt_Enable_2_CLKSTART_EN = (0x1 << 4), + MAX98927_Interrupt_Enable_2_MEASADC_END_EN = (0x1 << 5), + MAX98927_Interrupt_Enable_2_PWRDN_DONE_EN = (0x1 << 6), + MAX98927_Interrupt_Enable_2_PWRUP_DONE_EN = (0x1 << 7), + + // Interrupt Enable 3 (Address 0x000c) + MAX98927_Interrupt_Enable_3 = 0x000c, + MAX98927_Interrupt_Enable_3_PWRUP_FAIL_EN = (0x1 << 0), + MAX98927_Interrupt_Enable_3_AUTH_DONE_EN = (0x1 << 1), + MAX98927_Interrupt_Enable_3_SPK_OVC_EN = (0x1 << 2), + MAX98927_Interrupt_Enable_3_BST_UVLO_EN = (0x1 << 3), + + // Interrupt Flag Clear 1 (Address 0x000d) + MAX98927_Interrupt_Flag_Clear_1 = 0x000d, + MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_END_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_BGN_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_1_BDE_LEVEL_CHANGE_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_1_BDE_L8_CLR = (0x1 << 3), + MAX98927_Interrupt_Flag_Clear_1_THERMWARN_END_CLR = (0x1 << 4), + MAX98927_Interrupt_Flag_Clear_1_THERMWARN_START_CLR = (0x1 << 5), + MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_END_CLR = (0x1 << 6), + MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_START_CLR = (0x1 << 7), + + // Interrupt Flag Clear 2 (Address 0x000e) + MAX98927_Interrupt_Flag_Clear_2 = 0x000e, + MAX98927_Interrupt_Flag_Clear_2_WATCHDOGWARN_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_2_WATCHDOGFAIL_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_2_BOOSTCURRLIM_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_2_CLKSTOP_CLR = (0x1 << 3), + MAX98927_Interrupt_Flag_Clear_2_CLKSTART_CLR = (0x1 << 4), + MAX98927_Interrupt_Flag_Clear_2_MEASADC_END_CLR = (0x1 << 5), + MAX98927_Interrupt_Flag_Clear_2_PWRDN_DONE_CLR = (0x1 << 6), + MAX98927_Interrupt_Flag_Clear_2_PWRUP_DONE_CLR = (0x1 << 7), + + // Interrupt Flag Clear 3 (Address 0x000f) + MAX98927_Interrupt_Flag_Clear_3 = 0x000f, + MAX98927_Interrupt_Flag_Clear_3_PWRUP_FAIL_CLR = (0x1 << 0), + MAX98927_Interrupt_Flag_Clear_3_AUTH_DONE_CLR = (0x1 << 1), + MAX98927_Interrupt_Flag_Clear_3_SPK_OVC_CLR = (0x1 << 2), + MAX98927_Interrupt_Flag_Clear_3_BST_UVLO_CLR = (0x1 << 3), + + // IRQ Control (Address 0x0010) + MAX98927_IRQ_Control = 0x0010, + MAX98927_IRQ_Control_IRQ_EN = (0x1 << 0), + MAX98927_IRQ_Control_IRQ_POL = (0x1 << 1), + MAX98927_IRQ_Control_IRQ_MODE = (0x1 << 2), + + // Clock monitor enable (Address 0x0011) + MAX98927_Clock_monitor_enable = 0x0011, + MAX98927_Clock_monitor_enable_CMON_ENA = (0x1 << 0), + MAX98927_Clock_monitor_enable_CMON_AUTORESTART_ENA = (0x1 << 1), + + // Watchdog Control (Address 0x0012) + MAX98927_Watchdog_Control = 0x0012, + MAX98927_Watchdog_Control_WDT_ENA = (0x1 << 0), + MAX98927_Watchdog_Control_WDT_MODE = (0x1 << 1), + MAX98927_Watchdog_Control_WDT_TO_SEL_Mask = (0x3 << 2), + //Specific_bit_values_goes_here + MAX98927_Watchdog_Control_WDT_TO_SEL_5 = (0x0 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_10 = (0x1 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_35 = (0x2 << 2), + MAX98927_Watchdog_Control_WDT_TO_SEL_50 = (0x3 << 2), + MAX98927_Watchdog_Control_WDT_HW_SOURCE = (0x1 << 4), + + // Watchdog SW Reset (Address 0x0013) + MAX98927_Watchdog_SW_Reset = 0x0013, + //#BYHAND width >= 5: + MAX98927_Watchdog_SW_Reset_WDT_SW_RST_Mask = (0xff << 0), + + // Meas ADC Thermal Warning Threshhold (Address 0x0014) + MAX98927_Meas_ADC_Thermal_Warning_Threshhold = 0x0014, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Thermal_Warning_Threshhold_MEAS_ADC_WARN_THRESH_Mask = (0xff << 0), + + // Meas ADC Thermal Shutdown Threshhold (Address 0x0015) + MAX98927_Meas_ADC_Thermal_Shutdown_Threshhold = 0x0015, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Thermal_Shutdown_Threshhold_MEAS_ADC_SHDN_THRESH_Mask = (0xff << 0), + + // Meas ADC Thermal Hysteresis (Address 0x0016) + MAX98927_Meas_ADC_Thermal_Hysteresis = 0x0016, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Thermal_Hysteresis_MEAS_ADC_THERM_HYST_Mask = (0x1f << 0), + + // Pin Config (Address 0x0017) + MAX98927_Pin_Config = 0x0017, + MAX98927_Pin_Config_DOUT_DRV_Mask = (0x3 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Pin_Config_DOUT_DRV_01 = (0x0 << 0), + MAX98927_Pin_Config_DOUT_DRV_11 = (0x2 << 0), + MAX98927_Pin_Config_BCLK_DRV_Mask = (0x3 << 2), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Pin_Config_BCLK_DRV_01 = (0x0 << 2), + MAX98927_Pin_Config_BCLK_DRV_11 = (0x2 << 2), + MAX98927_Pin_Config_LRCLK_DRV_Mask = (0x3 << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Pin_Config_LRCLK_DRV_01 = (0x0 << 4), + MAX98927_Pin_Config_LRCLK_DRV_11 = (0x2 << 4), + MAX98927_Pin_Config_ICC_DRV_Mask = (0x3 << 6), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Pin_Config_ICC_DRV_01 = (0x0 << 6), + MAX98927_Pin_Config_ICC_DRV_11 = (0x2 << 6), + + // PCM Rx Enables A (Address 0x0018) + MAX98927_PCM_Rx_Enables_A = 0x0018, + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH0_EN = (0x1 << 0), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH1_EN = (0x1 << 1), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH2_EN = (0x1 << 2), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH3_EN = (0x1 << 3), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH4_EN = (0x1 << 4), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH5_EN = (0x1 << 5), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH6_EN = (0x1 << 6), + MAX98927_PCM_Rx_Enables_A_PCM_RX_CH7_EN = (0x1 << 7), + + // PCM Rx Enables B (Address 0x0019) + MAX98927_PCM_Rx_Enables_B = 0x0019, + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH8_EN = (0x1 << 0), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH9_EN = (0x1 << 1), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH10_EN = (0x1 << 2), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH11_EN = (0x1 << 3), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH12_EN = (0x1 << 4), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH13_EN = (0x1 << 5), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH14_EN = (0x1 << 6), + MAX98927_PCM_Rx_Enables_B_PCM_RX_CH15_EN = (0x1 << 7), + + // PCM Tx Enables A (Address 0x001a) + MAX98927_PCM_Tx_Enables_A = 0x001a, + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH0_EN = (0x1 << 0), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH1_EN = (0x1 << 1), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH2_EN = (0x1 << 2), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH3_EN = (0x1 << 3), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH4_EN = (0x1 << 4), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH5_EN = (0x1 << 5), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH6_EN = (0x1 << 6), + MAX98927_PCM_Tx_Enables_A_PCM_TX_CH7_EN = (0x1 << 7), + + // PCM Tx Enables B (Address 0x001b) + MAX98927_PCM_Tx_Enables_B = 0x001b, + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH8_EN = (0x1 << 0), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH9_EN = (0x1 << 1), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH10_EN = (0x1 << 2), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH11_EN = (0x1 << 3), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH12_EN = (0x1 << 4), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH13_EN = (0x1 << 5), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH14_EN = (0x1 << 6), + MAX98927_PCM_Tx_Enables_B_PCM_TX_CH15_EN = (0x1 << 7), + + // PCM Tx HiZ Control A (Address 0x001c) + MAX98927_PCM_Tx_HiZ_Control_A = 0x001c, + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH0_HIZ = (0x1 << 0), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH1_HIZ = (0x1 << 1), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH2_HIZ = (0x1 << 2), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH3_HIZ = (0x1 << 3), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH4_HIZ = (0x1 << 4), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH5_HIZ = (0x1 << 5), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH6_HIZ = (0x1 << 6), + MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH7_HIZ = (0x1 << 7), + + // PCM Tx HiZ Control B (Address 0x001d) + MAX98927_PCM_Tx_HiZ_Control_B = 0x001d, + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH8_HIZ = (0x1 << 0), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH9_HIZ = (0x1 << 1), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH10_HIZ = (0x1 << 2), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH11_HIZ = (0x1 << 3), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH12_HIZ = (0x1 << 4), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH13_HIZ = (0x1 << 5), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH14_HIZ = (0x1 << 6), + MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH15_HIZ = (0x1 << 7), + + // PCM Tx Channel Sources A (Address 0x001e) + MAX98927_PCM_Tx_Channel_Sources_A = 0x001e, + MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_V_DEST_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_I_DEST_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // PCM Tx Channel Sources B (Address 0x001f) + MAX98927_PCM_Tx_Channel_Sources_B = 0x001f, + MAX98927_PCM_Tx_Channel_Sources_B_PCM_AMP_DSP_DEST_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // PCM Mode Config (Address 0x0020) + MAX98927_PCM_Mode_Config = 0x0020, + MAX98927_PCM_Mode_Config_PCM_TX_EXTRA_HIZ = (0x1 << 0), + MAX98927_PCM_Mode_Config_PCM_CHANSEL = (0x1 << 1), + MAX98927_PCM_Mode_Config_PCM_BCLKEDGE = (0x1 << 2), + MAX98927_PCM_Mode_Config_PCM_FORMAT_Mask = (0x7 << 3), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_PCM_Mode_Config_PCM_FORMAT_I2S = (0x0 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_LEFT = (0x1 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_0 = (0x3 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_1 = (0x4 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_2 = (0x5 << 3), + MAX98927_PCM_Mode_Config_PCM_FORMAT_ = (0x6 << 3), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask = (0x3 << 6), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_PCM_Mode_Config_PCM_CHANSZ_16 = (0x1 << 6), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_24 = (0x2 << 6), + MAX98927_PCM_Mode_Config_PCM_CHANSZ_32 = (0x3 << 6), + + // PCM Master Mode (Address 0x0021) + MAX98927_PCM_Master_Mode = 0x0021, + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask = (0x3 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE = (0x0 << 0), + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER = (0x3 << 0), + MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID = (0x1 << 0), + MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask = (0xf << 2), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 6 + MAX98927_PCM_Master_Mode_PCM_CLK_SOURCE = (0x1 << 6), + + // PCM Clock setup (Address 0x0022) + MAX98927_PCM_Clock_setup = 0x0022, + MAX98927_PCM_Clock_setup_PCM_BSEL_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 6 + MAX98927_PCM_Clock_setup_PCM_MSEL_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 6 + + // PCM Sample rate setup 1 (Address 0x0023) + MAX98927_PCM_Sample_rate_setup_1 = 0x0023, + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000 = (0x0 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025 = (0x1 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000 = (0x2 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000 = (0x3 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050 = (0x4 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000 = (0x5 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000 = (0x6 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100 = (0x7 << 0), + MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000 = (0x8 << 0), + + // PCM Sample rate setup 1 (Address 0x0024) + MAX98927_PCM_Sample_rate_setup_2 = 0x0024, + MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 6 + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0001 = (0x0 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0011 = (0x2 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0101 = (0x4 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0111 = (0x6 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1001 = (0x8 << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1011 = (0xa << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1101 = (0xc << 4), + MAX98927_PCM_Sample_rate_setup_2_SPK_SR_ = (0xf << 4), + + // PCM to speaker monomix A (Address 0x0025) + MAX98927_PCM_to_speaker_monomix_A = 0x0025, + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CH0_SOURCE_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CFG_Mask = (0x3 << 6), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CFG_1 = (0x0 << 6), + MAX98927_PCM_to_speaker_monomix_A_DMONOMIX_CFG_3 = (0x0 << 6), + + // PCM to speaker monomix B (Address 0x0026) + MAX98927_PCM_to_speaker_monomix_B = 0x0026, + MAX98927_PCM_to_speaker_monomix_B_DMONOMIX_CH1_SOURCE_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // ICC RX Enables A (Address 0x0027) + MAX98927_ICC_RX_Enables_A = 0x0027, + MAX98927_ICC_RX_Enables_A_ICC_RX_CH0_EN = (0x1 << 0), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH1_EN = (0x1 << 1), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH2_EN = (0x1 << 2), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH3_EN = (0x1 << 3), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH4_EN = (0x1 << 4), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH5_EN = (0x1 << 5), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH6_EN = (0x1 << 6), + MAX98927_ICC_RX_Enables_A_ICC_RX_CH7_EN = (0x1 << 7), + + // ICC RX Enables B (Address 0x0028) + MAX98927_ICC_RX_Enables_B = 0x0028, + MAX98927_ICC_RX_Enables_B_ICC_RX_CH8_EN = (0x1 << 0), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH9_EN = (0x1 << 1), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH10_EN = (0x1 << 2), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH11_EN = (0x1 << 3), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH12_EN = (0x1 << 4), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH13_EN = (0x1 << 5), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH14_EN = (0x1 << 6), + MAX98927_ICC_RX_Enables_B_ICC_RX_CH15_EN = (0x1 << 7), + + // ICC TX Enables A (Address 0x002b) + MAX98927_ICC_TX_Enables_A = 0x002b, + MAX98927_ICC_TX_Enables_A_ICC_TX_CH0_EN = (0x1 << 0), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH1_EN = (0x1 << 1), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH2_EN = (0x1 << 2), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH3_EN = (0x1 << 3), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH4_EN = (0x1 << 4), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH5_EN = (0x1 << 5), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH6_EN = (0x1 << 6), + MAX98927_ICC_TX_Enables_A_ICC_TX_CH7_EN = (0x1 << 7), + + // ICC TX Enables B (Address 0x002c) + MAX98927_ICC_TX_Enables_B = 0x002c, + MAX98927_ICC_TX_Enables_B_ICC_TX_CH8_EN = (0x1 << 0), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH9_EN = (0x1 << 1), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH10_EN = (0x1 << 2), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH11_EN = (0x1 << 3), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH12_EN = (0x1 << 4), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH13_EN = (0x1 << 5), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH14_EN = (0x1 << 6), + MAX98927_ICC_TX_Enables_B_ICC_TX_CH15_EN = (0x1 << 7), + + // ICC Data Order Select (Address 0x002d) + MAX98927_ICC_Data_Order_Select = 0x002d, + MAX98927_ICC_Data_Order_Select_ICC_DRIVE_MODE = (0x1 << 3), + + // ICC HiZ Manual Mode (Address 0x002e) + MAX98927_ICC_HiZ_Manual_Mode = 0x002e, + MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_HIZ_MANUAL = (0x1 << 0), + MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_EXTRA_HIZ = (0x1 << 1), + + // ICC TX HiZ Enables A (Address 0x002f) + MAX98927_ICC_TX_HiZ_Enables_A = 0x002f, + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH0_HIZ = (0x1 << 0), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH1_HIZ = (0x1 << 1), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH2_HIZ = (0x1 << 2), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH3_HIZ = (0x1 << 3), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH4_HIZ = (0x1 << 4), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH5_HIZ = (0x1 << 5), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH6_HIZ = (0x1 << 6), + MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH7_HIZ = (0x1 << 7), + + // ICC TX HiZ Enables B (Address 0x0030) + MAX98927_ICC_TX_HiZ_Enables_B = 0x0030, + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH8_HIZ = (0x1 << 0), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH9_HIZ = (0x1 << 1), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH10_HIZ = (0x1 << 2), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH11_HIZ = (0x1 << 3), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH12_HIZ = (0x1 << 4), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH13_HIZ = (0x1 << 5), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH14_HIZ = (0x1 << 6), + MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH15_HIZ = (0x1 << 7), + + // ICC Link Enables (Address 0x0031) + MAX98927_ICC_Link_Enables = 0x0031, + MAX98927_ICC_Link_Enables_ICC_LINK_EN = (0x1 << 1), + + // PDM Tx Enables (Address 0x0032) + MAX98927_PDM_Tx_Enables = 0x0032, + MAX98927_PDM_Tx_Enables_PDM_TX_EN = (0x1 << 0), + MAX98927_PDM_Tx_Enables_PDM_TX_CLK_DIV2 = (0x1 << 1), + + // PDM Tx HiZ Control (Address 0x0033) + MAX98927_PDM_Tx_HiZ_Control = 0x0033, + MAX98927_PDM_Tx_HiZ_Control_PDM_TX_HIZ = (0x1 << 0), + + // PDM Tx Control (Address 0x0034) + MAX98927_PDM_Tx_Control = 0x0034, + MAX98927_PDM_Tx_Control_PDM_TX_CH0_SOURCE = (0x1 << 0), + MAX98927_PDM_Tx_Control_PDM_TX_CH1_SOURCE = (0x1 << 1), + + // PDM Rx Enable (Address 0x0034) + MAX98927_PDM_Rx_Enable = 0x0035, + MAX98927_PDM_Rx_Enable_PDM_RX_EN = (0x1 << 0), + MAX98927_PDM_Rx_Enable_PDM_DSP_EN = (0x1 << 1), + MAX98927_PDM_Rx_Enable_PDM_DITH_EN = (0x1 << 2), + MAX98927_PDM_Rx_Enable_PDM_RX_CH_SEL = (0x1 << 3), + MAX98927_PDM_Rx_Enable_PDM_FIFO_RDY_LVL_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 5 + + // AMP volume control (Address 0x0036) + MAX98927_AMP_volume_control = 0x0036, + //#BYHAND width >= 5: + MAX98927_AMP_volume_control_AMP_VOL_Mask = (0x7f << 0), + MAX98927_AMP_volume_control_AMP_VOL_SEL = (0x1 << 7), + + // AMP DSP Config (Address 0x0037) + MAX98927_AMP_DSP_Config = 0x0037, + MAX98927_AMP_DSP_Config_AMP_DCBLK_EN = (0x1 << 0), + MAX98927_AMP_DSP_Config_AMP_DITH_EN = (0x1 << 1), + MAX98927_AMP_DSP_Config_DAC_HALF_REF_CURRENT = (0x1 << 2), + MAX98927_AMP_DSP_Config_DAC_DOUBLE_RFB = (0x1 << 3), + MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS = (0x1 << 4), + MAX98927_AMP_DSP_Config_DAC_INVERT = (0x1 << 5), + + // Tone Generator and DC Config (Address 0x0038) + MAX98927_Tone_Generator_and_DC_Config = 0x0038, + MAX98927_Tone_Generator_and_DC_Config_TONE_CONFIG_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 6 + + // DRE Control (Address 0x0039) + MAX98927_DRE_Control = 0x0039, + MAX98927_DRE_Control_DRE_EN = (0x1 << 0), + + // AMP enables (Address 0x003a) + MAX98927_AMP_enables = 0x003a, + MAX98927_AMP_enables_SPK_EN = (0x1 << 0), + + // Speaker source select (Address 0x003b) + MAX98927_Speaker_source_select = 0x003b, + MAX98927_Speaker_source_select_SPK_SOURCE_Mask = (0x3 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Speaker_source_select_SPK_SOURCE_01 = (0x0 << 0), + MAX98927_Speaker_source_select_SPK_SOURCE_11 = (0x2 << 0), + + // Speaker Gain (Address 0x003c) + MAX98927_Speaker_Gain = 0x003c, + MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_Speaker_Gain_SPK_PCM_GAIN_001 = (0x0 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_011 = (0x2 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_101 = (0x4 << 0), + MAX98927_Speaker_Gain_SPK_PCM_GAIN_111 = (0x6 << 0), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_Mask = (0x7 << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_Speaker_Gain_SPK_PDM_GAIN_001 = (0x0 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_011 = (0x2 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_101 = (0x4 << 4), + MAX98927_Speaker_Gain_SPK_PDM_GAIN_111 = (0x6 << 4), + + // SSM Configuration (Address 0x003d) + MAX98927_SSM_Configuration = 0x003d, + MAX98927_SSM_Configuration_SSM_MOD_INDEX_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_SSM_Configuration_SSM_MOD_INDEX_001 = (0x0 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_011 = (0x2 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_101 = (0x4 << 0), + MAX98927_SSM_Configuration_SSM_MOD_INDEX_ = (0x6 << 0), + MAX98927_SSM_Configuration_SPK_FSW_SEL = (0x1 << 3), + MAX98927_SSM_Configuration_SSM_ENA = (0x1 << 7), + + // Measurement enables (Address 0x003e) + MAX98927_Measurement_enables = 0x003e, + MAX98927_Measurement_enables_IVADC_V_EN = (0x1 << 0), + MAX98927_Measurement_enables_IVADC_I_EN = (0x1 << 1), + + // Measurement DSP Config (Address 0x003f) + MAX98927_Measurement_DSP_Config = 0x003f, + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_EN = (0x1 << 0), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_EN = (0x1 << 1), + MAX98927_Measurement_DSP_Config_MEAS_DITH_EN = (0x1 << 2), + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_Mask = (0x3 << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_01 = (0x0 << 4), + MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_11 = (0x2 << 4), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_Mask = (0x3 << 6), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_01 = (0x0 << 6), + MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_11 = (0x2 << 6), + + // Boost Control 0 (Address 0x0040) + MAX98927_Boost_Control_0 = 0x0040, + //#BYHAND width >= 5: + MAX98927_Boost_Control_0_BST_VOUT_Mask = (0x1f << 0), + MAX98927_Boost_Control_0_EXT_PVDD_EN = (0x1 << 7), + + // Boost Control 3 (Address 0x0041) + MAX98927_Boost_Control_3 = 0x0041, + MAX98927_Boost_Control_3_BST_SKIPLOAD_Mask = (0x3 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Boost_Control_3_BST_SKIPLOAD_01 = (0x0 << 0), + MAX98927_Boost_Control_3_BST_SKIPLOAD_11 = (0x2 << 0), + MAX98927_Boost_Control_3_BST_PHASE_Mask = (0x7 << 2), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 3 + MAX98927_Boost_Control_3_BST_PHASE_001 = (0x0 << 2), + MAX98927_Boost_Control_3_BST_PHASE_011 = (0x2 << 2), + MAX98927_Boost_Control_3_BST_PHASE_ = (0x1 << 2), + MAX98927_Boost_Control_3_BST_SLOWSTART = (0x1 << 5), + + // Boost Control 1 (Address 0x0042) + MAX98927_Boost_Control_1 = 0x0042, + //#BYHAND width >= 5: + MAX98927_Boost_Control_1_BST_ILIM_Mask = (0x1f << 1), + + // Meas ADC Config (Address 0x0043) + MAX98927_Meas_ADC_Config = 0x0043, + MAX98927_Meas_ADC_Config_MEAS_ADC_CH0_EN = (0x1 << 0), + MAX98927_Meas_ADC_Config_MEAS_ADC_CH1_EN = (0x1 << 1), + MAX98927_Meas_ADC_Config_MEAS_ADC_CH2_EN = (0x1 << 2), + + // Meas ADC Base Divide MSByte (Address 0x0044) + MAX98927_Meas_ADC_Base_Divide_MSByte = 0x0044, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Base_Divide_MSByte_MEAS_ADC_BASE_DIV_Mask = (0xff << 0), + + // Meas ADC Base Divide LSByte (Address 0x0045) + MAX98927_Meas_ADC_Base_Divide_LSByte = 0x0045, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Base_Divide_LSByte_MEAS_ADC_BASE_DIV_Mask = (0xff << 0), + + // Meas ADC Chan 0 Divide (Address 0x0046) + MAX98927_Meas_ADC_Chan_0_Divide = 0x0046, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_0_Divide_MEAS_ADC_CH0_DIV_Mask = (0xff << 0), + + // Meas ADC Chan 1 Divide (Address 0x0047) + MAX98927_Meas_ADC_Chan_1_Divide = 0x0047, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_1_Divide_MEAS_ADC_CH1_DIV_Mask = (0xff << 0), + + // Meas ADC Chan 2 Divide (Address 0x0048) + MAX98927_Meas_ADC_Chan_2_Divide = 0x0048, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_2_Divide_MEAS_ADC_CH2_DIV_Mask = (0xff << 0), + + // Meas ADC Chan 0 Filt Config (Address 0x0049) + MAX98927_Meas_ADC_Chan_0_Filt_Config = 0x0049, + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 3 + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_001 = (0x0 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_011 = (0x2 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_101 = (0x4 << 0), + MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_EN = (0x1 << 3), + + // Meas ADC Chan 1 Filt Config (Address 0x004a) + MAX98927_Meas_ADC_Chan_1_Filt_Config = 0x004a, + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 3 + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_001 = (0x0 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_011 = (0x2 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_101 = (0x4 << 0), + MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_EN = (0x1 << 3), + + // Meas ADC Chan 2 Filt Config (Address 0x004b) + MAX98927_Meas_ADC_Chan_2_Filt_Config = 0x004b, + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 3 + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_001 = (0x0 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_011 = (0x2 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_101 = (0x4 << 0), + MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_EN = (0x1 << 3), + + // Meas ADC Chan 0 Readback (Address 0x004c) + MAX98927_Meas_ADC_Chan_0_Readback = 0x004c, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_0_Readback_MEAS_ADC_CH0_DATA_Mask = (0xff << 0), + + // Meas ADC Chan 1 Readback (Address 0x004d) + MAX98927_Meas_ADC_Chan_1_Readback = 0x004d, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_1_Readback_MEAS_ADC_CH1_DATA_Mask = (0xff << 0), + + // Meas ADC Chan 2 Readback (Address 0x004e) + MAX98927_Meas_ADC_Chan_2_Readback = 0x004e, + //#BYHAND width >= 5: + MAX98927_Meas_ADC_Chan_2_Readback_MEAS_ADC_CH2_DATA_Mask = (0xff << 0), + + // Brownout status (Address 0x0051) + MAX98927_Brownout_status = 0x0051, + MAX98927_Brownout_status_BDE_STATE_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 4 + + // Brownout enables (Address 0x0052) + MAX98927_Brownout_enables = 0x0052, + MAX98927_Brownout_enables_BDE_EN = (0x1 << 0), + MAX98927_Brownout_enables_BDE_AMP_EN = (0x1 << 1), + MAX98927_Brownout_enables_AMP_DSP_EN = (0x1 << 2), + + // Brownout level infinite hold (Address 0x0053) + MAX98927_Brownout_level_infinite_hold = 0x0053, + MAX98927_Brownout_level_infinite_hold_BDE_L8_INF_HLD = (0x1 << 1), + + // Brownout level infinite hold clear (Address 0x0054) + MAX98927_Brownout_level_infinite_hold_clear = 0x0054, + MAX98927_Brownout_level_infinite_hold_clear_BDE_L8_HLD_RLS = (0x1 << 1), + + // Brownout level hold (Address 0x0055) + MAX98927_Brownout_level_hold = 0x0055, + //#BYHAND width >= 5: + MAX98927_Brownout_level_hold_BDE_HLD_Mask = (0xff << 0), + + // Brownout level 1 threshold (Address 0x0056) + MAX98927_Brownout__level_1_threshold = 0x005a, + //#BYHAND width >= 5: + MAX98927_Brownout__level_1_threshold_BDE_L1_VTHRESH_Mask = (0xff << 0), + + // Brownout level 2 threshold (Address 0x0057) + MAX98927_Brownout__level_2_threshold = 0x005b, + //#BYHAND width >= 5: + MAX98927_Brownout__level_2_threshold_BDE_L2_VTHRESH_Mask = (0xff << 0), + + // Brownout level 3 threshold (Address 0x0058) + MAX98927_Brownout__level_3_threshold = 0x005c, + //#BYHAND width >= 5: + MAX98927_Brownout__level_3_threshold_BDE_L3_VTHRESH_Mask = (0xff << 0), + + // Brownout level 4 threshold (Address 0x0059) + MAX98927_Brownout__level_4_threshold = 0x005d, + //#BYHAND width >= 5: + MAX98927_Brownout__level_4_threshold_BDE_L4_VTHRESH_Mask = (0xff << 0), + + // Brownout level 5 threshold (Address 0x005a) + MAX98927_Brownout__level_5_threshold = 0x005a, + //#BYHAND width >= 5: + MAX98927_Brownout__level_5_threshold_BDE_L5_VTHRESH_Mask = (0xff << 0), + + // Brownout level 6 threshold (Address 0x005b) + MAX98927_Brownout__level_6_threshold = 0x005b, + //#BYHAND width >= 5: + MAX98927_Brownout__level_6_threshold_BDE_L6_VTHRESH_Mask = (0xff << 0), + + // Brownout level 7 threshold (Address 0x005c) + MAX98927_Brownout__level_7_threshold = 0x005c, + //#BYHAND width >= 5: + MAX98927_Brownout__level_7_threshold_BDE_L7_VTHRESH_Mask = (0xff << 0), + + // Brownout level 8 threshold (Address 0x005d) + MAX98927_Brownout__level_8_threshold = 0x005d, + //#BYHAND width >= 5: + MAX98927_Brownout__level_8_threshold_BDE_L8_VTHRESH_Mask = (0xff << 0), + + // Brownout threshold hysterysis (Address 0x005e) + MAX98927_Brownout_threshold_hysterysis = 0x005e, + //#BYHAND width >= 5: + MAX98927_Brownout_threshold_hysterysis_BDE_VTHRESH_HYST_Mask = (0xff << 0), + + // Brownout AMP limiter attack/release (Address 0x005f) + MAX98927_Brownout_AMP_limiter_attack_release = 0x005f, + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0001 = (0x0 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0011 = (0x2 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0101 = (0x4 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0111 = (0x6 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1001 = (0x8 << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1011 = (0xa << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1101 = (0xc << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1111 = (0xe << 0), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0001 = (0x0 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0011 = (0x2 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0101 = (0x4 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0111 = (0x6 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1001 = (0x8 << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1011 = (0xa << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1101 = (0xc << 4), + MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1111 = (0xe << 4), + + // Brownout AMP gain attack/release (Address 0x0060) + MAX98927_Brownout_AMP_gain_attack_release = 0x0060, + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0001 = (0x0 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0011 = (0x2 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0101 = (0x4 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0111 = (0x6 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1001 = (0x8 << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1011 = (0xa << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1101 = (0xc << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1111 = (0xe << 0), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_Mask = (0xf << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 8 + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0001 = (0x0 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0011 = (0x2 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0101 = (0x4 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0111 = (0x6 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1001 = (0x8 << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1011 = (0xa << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1101 = (0xc << 4), + MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1111 = (0xe << 4), + + // Brownout AMP1 clip mode (Address 0x0061) + MAX98927_Brownout_AMP1_clip_mode = 0x0061, + MAX98927_Brownout_AMP1_clip_mode_AMP_CLIP_MODE = (0x1 << 0), + + // Brownout level 1 current limit (Address 0x0062) + MAX98927_Brownout__level_1_current_limit = 0x0072, + //#BYHAND width >= 5: + MAX98927_Brownout__level_1_current_limit_BDE_L1_ILIM_Mask = (0x3f << 0), + + // Brownout level 1 amp 1 control 1 (Address 0x0063) + MAX98927_Brownout__level_1_amp_1_control_1 = 0x0073, + MAX98927_Brownout__level_1_amp_1_control_1_BDE_L1_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 1 amp 1 control 2 (Address 0x0064) + MAX98927_Brownout__level_1_amp_1_control_2 = 0x0074, + //#BYHAND width >= 5: + MAX98927_Brownout__level_1_amp_1_control_2_BDE_L1_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 1 amp 1 control 3 (Address 0x0065) + MAX98927_Brownout__level_1_amp_1_control_3 = 0x0075, + //#BYHAND width >= 5: + MAX98927_Brownout__level_1_amp_1_control_3_BDE_L1_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 2 current limit (Address 0x0066) + MAX98927_Brownout__level_2_current_limit = 0x0076, + //#BYHAND width >= 5: + MAX98927_Brownout__level_2_current_limit_BDE_L2_ILIM_Mask = (0x3f << 0), + + // Brownout level 2 amp 1 control 1 (Address 0x0067) + MAX98927_Brownout__level_2_amp_1_control_1 = 0x0077, + MAX98927_Brownout__level_2_amp_1_control_1_BDE_L2_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 2 amp 1 control 2 (Address 0x0068) + MAX98927_Brownout__level_2_amp_1_control_2 = 0x0078, + //#BYHAND width >= 5: + MAX98927_Brownout__level_2_amp_1_control_2_BDE_L2_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 2 amp 1 control 3 (Address 0x0069) + MAX98927_Brownout__level_2_amp_1_control_3 = 0x0079, + //#BYHAND width >= 5: + MAX98927_Brownout__level_2_amp_1_control_3_BDE_L2_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 3 current limit (Address 0x006a) + MAX98927_Brownout__level_3_current_limit = 0x007a, + //#BYHAND width >= 5: + MAX98927_Brownout__level_3_current_limit_BDE_L3_ILIM_Mask = (0x3f << 0), + + // Brownout level 3 amp 1 control 1 (Address 0x006b) + MAX98927_Brownout__level_3_amp_1_control_1 = 0x007b, + MAX98927_Brownout__level_3_amp_1_control_1_BDE_L3_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 3 amp 1 control 2 (Address 0x006c) + MAX98927_Brownout__level_3_amp_1_control_2 = 0x007c, + //#BYHAND width >= 5: + MAX98927_Brownout__level_3_amp_1_control_2_BDE_L3_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 3 amp 1 control 3 (Address 0x006d) + MAX98927_Brownout__level_3_amp_1_control_3 = 0x007d, + //#BYHAND width >= 5: + MAX98927_Brownout__level_3_amp_1_control_3_BDE_L3_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 4 current limit (Address 0x006e) + MAX98927_Brownout__level_4_current_limit = 0x007e, + //#BYHAND width >= 5: + MAX98927_Brownout__level_4_current_limit_BDE_L4_ILIM_Mask = (0x3f << 0), + + // Brownout level 4 amp 1 control 1 (Address 0x006f) + MAX98927_Brownout__level_4_amp_1_control_1 = 0x007f, + MAX98927_Brownout__level_4_amp_1_control_1_BDE_L4_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 4 amp 1 control 2 (Address 0x0070) + MAX98927_Brownout__level_4_amp_1_control_2 = 0x0080, + //#BYHAND width >= 5: + MAX98927_Brownout__level_4_amp_1_control_2_BDE_L4_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 4 amp 1 control 3 (Address 0x0071) + MAX98927_Brownout__level_4_amp_1_control_3 = 0x0081, + //#BYHAND width >= 5: + MAX98927_Brownout__level_4_amp_1_control_3_BDE_L4_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 5 current limit (Address 0x0072) + MAX98927_Brownout__level_5_current_limit = 0x0072, + //#BYHAND width >= 5: + MAX98927_Brownout__level_5_current_limit_BDE_L5_ILIM_Mask = (0x3f << 0), + + // Brownout level 5 amp 1 control 1 (Address 0x0073) + MAX98927_Brownout__level_5_amp_1_control_1 = 0x0073, + MAX98927_Brownout__level_5_amp_1_control_1_BDE_L5_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 5 amp 1 control 2 (Address 0x0074) + MAX98927_Brownout__level_5_amp_1_control_2 = 0x0074, + //#BYHAND width >= 5: + MAX98927_Brownout__level_5_amp_1_control_2_BDE_L5_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 5 amp 1 control 3 (Address 0x0075) + MAX98927_Brownout__level_5_amp_1_control_3 = 0x0075, + //#BYHAND width >= 5: + MAX98927_Brownout__level_5_amp_1_control_3_BDE_L5_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 6 current limit (Address 0x0076) + MAX98927_Brownout__level_6_current_limit = 0x0076, + //#BYHAND width >= 5: + MAX98927_Brownout__level_6_current_limit_BDE_L6_ILIM_Mask = (0x3f << 0), + + // Brownout level 6 amp 1 control 1 (Address 0x0077) + MAX98927_Brownout__level_6_amp_1_control_1 = 0x0077, + MAX98927_Brownout__level_6_amp_1_control_1_BDE_L6_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 6 amp 1 control 2 (Address 0x0078) + MAX98927_Brownout__level_6_amp_1_control_2 = 0x0078, + //#BYHAND width >= 5: + MAX98927_Brownout__level_6_amp_1_control_2_BDE_L6_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 6 amp 1 control 3 (Address 0x0079) + MAX98927_Brownout__level_6_amp_1_control_3 = 0x0079, + //#BYHAND width >= 5: + MAX98927_Brownout__level_6_amp_1_control_3_BDE_L6_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 7 current limit (Address 0x007a) + MAX98927_Brownout__level_7_current_limit = 0x007a, + //#BYHAND width >= 5: + MAX98927_Brownout__level_7_current_limit_BDE_L7_ILIM_Mask = (0x3f << 0), + + // Brownout level 7 amp 1 control 1 (Address 0x007b) + MAX98927_Brownout__level_7_amp_1_control_1 = 0x007b, + MAX98927_Brownout__level_7_amp_1_control_1_BDE_L7_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 7 amp 1 control 2 (Address 0x007c) + MAX98927_Brownout__level_7_amp_1_control_2 = 0x007c, + //#BYHAND width >= 5: + MAX98927_Brownout__level_7_amp_1_control_2_BDE_L7_AMP1_CLIP_Mask = (0x3f << 0), + + // Brownout level 7 amp 1 control 3 (Address 0x007d) + MAX98927_Brownout__level_7_amp_1_control_3 = 0x007d, + //#BYHAND width >= 5: + MAX98927_Brownout__level_7_amp_1_control_3_BDE_L7_AMP1_GAIN_Mask = (0x3f << 0), + + // Brownout level 8 current limit (Address 0x007e) + MAX98927_Brownout__level_8_current_limit = 0x007e, + //#BYHAND width >= 5: + MAX98927_Brownout__level_8_current_limit_BDE_L8_ILIM_Mask = (0x3f << 0), + + // Brownout level 8 amp 1 control 1 (Address 0x007f) + MAX98927_Brownout__level_8_amp_1_control_1 = 0x007f, + MAX98927_Brownout__level_8_amp_1_control_1_BDE_L8_AMP1_LIM_Mask = (0xf << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 16 != 3 + + // Brownout level 8 amp 1 control 2 (Address 0x0080) + MAX98927_Brownout__level_8_amp_1_control_2 = 0x0080, + //#BYHAND width >= 5: + MAX98927_Brownout__level_8_amp_1_control_2_BDE_L8_AMP1_CLIP_Mask = (0x3f << 0), + MAX98927_Brownout__level_8_amp_1_control_2_BDE_L8_AMP1_MUTE = (0x1 << 7), + + // Brownout level 8 amp 1 control 3 (Address 0x0081) + MAX98927_Brownout__level_8_amp_1_control_3 = 0x0081, + //#BYHAND width >= 5: + MAX98927_Brownout__level_8_amp_1_control_3_BDE_L8_AMP1_GAIN_Mask = (0x3f << 0), + + // Env Tracker Vout Headroom (Address 0x0082) + MAX98927_Env_Tracker_Vout_Headroom = 0x0082, + //#BYHAND width >= 5: + MAX98927_Env_Tracker_Vout_Headroom_ENV_TRACKER_BST_VOUT_HEADROOM_Mask = (0x1f << 0), + + // Env Tracker Boost Vout Delay (Address 0x0083) + MAX98927_Env_Tracker_Boost_Vout_Delay = 0x0083, + //#BYHAND width >= 5: + MAX98927_Env_Tracker_Boost_Vout_Delay_ENV_TRACKER_BST_VOUT_DELAY_Mask = (0x1f << 0), + MAX98927_Env_Tracker_Boost_Vout_Delay_ENV_TRACKER_BDE_MODE = (0x1 << 7), + + // Env Tracker Release Rate (Address 0x0084) + MAX98927_Env_Tracker_Release_Rate = 0x0084, + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_001 = (0x0 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_011 = (0x2 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_101 = (0x4 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_111 = (0x6 << 0), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_PEAK_DET_LPF_BYP_EN = (0x1 << 3), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_Mask = (0x3 << 4), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 4 != 2 + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_01 = (0x0 << 4), + MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_11 = (0x2 << 4), + + // Env Tracker Hold Rate (Address 0x0085) + MAX98927_Env_Tracker_Hold_Rate = 0x0085, + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_Mask = (0x7 << 0), + //Specific_bit_values_goes_here + //#BYHAND width doesn't match: 8 != 4 + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_001 = (0x0 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_011 = (0x2 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_101 = (0x4 << 0), + MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_111 = (0x6 << 0), + + // Env Tracker Control (Address 0x0086) + MAX98927_Env_Tracker_Control = 0x0086, + MAX98927_Env_Tracker_Control_ENV_TRACKER_EN = (0x1 << 0), + + // Env Tracker Boost Vout ReadBack (Address 0x0087) + MAX98927_Env_Tracker__Boost_Vout_ReadBack = 0x0087, + //#BYHAND width >= 5: + MAX98927_Env_Tracker__Boost_Vout_ReadBack_ENV_TRACKER_BST_VOUT_RD_Mask = (0x1f << 0), + + // Advanced Settings (Address 0x0089) + MAX98927_Advanced_Settings = 0x0089, + MAX98927_Advanced_Settings_DAC_HALF_FIR = (0x1 << 0), + MAX98927_Advanced_Settings_PDM_MOD_SEL = (0x1 << 1), + MAX98927_Advanced_Settings_ISOCH_EN = (0x1 << 2), + + // DAC Test 1 (Address 0x009f) + MAX98927_DAC_Test_1 = 0x009f, + MAX98927_DAC_Test_1_DAC_PCM_TIMING = (0x1 << 0), + MAX98927_DAC_Test_1_DAC_HALFI_AMP = (0x1 << 1), + MAX98927_DAC_Test_1_DAC_LONG_HOLD = (0x1 << 3), + MAX98927_DAC_Test_1_DAC_DISABLE_CHOP = (0x1 << 4), + MAX98927_DAC_Test_1_DAC_TM = (0x1 << 5), + MAX98927_DAC_Test_1_DAC_INVERT_DACCLK = (0x1 << 6), + + // Authentication key 0 (Address 0x00ea) + MAX98927_Authentication_key_0 = 0x00ea, + //#BYHAND width >= 5: + MAX98927_Authentication_key_0_AUTH_KEY_Mask = (0xff << 0), + + // Authentication key 1 (Address 0x00eb) + MAX98927_Authentication_key_1 = 0x00eb, + //#BYHAND width >= 5: + MAX98927_Authentication_key_1_AUTH_KEY_Mask = (0xff << 0), + + // Authentication key 2 (Address 0x00ec) + MAX98927_Authentication_key_2 = 0x00ec, + //#BYHAND width >= 5: + MAX98927_Authentication_key_2_AUTH_KEY_Mask = (0xff << 0), + + // Authentication key 3 (Address 0x00ed) + MAX98927_Authentication_key_3 = 0x00ed, + //#BYHAND width >= 5: + MAX98927_Authentication_key_3_AUTH_KEY_Mask = (0xff << 0), + + // Authentication enable (Address 0x00ee) + MAX98927_Authentication_enable = 0x00ee, + MAX98927_Authentication_enable_AUTH_EN = (0x1 << 0), + + // Authentication result 0 (Address 0x00ef) + MAX98927_Authentication_result_0 = 0x00ef, + //#BYHAND width >= 5: + MAX98927_Authentication_result_0_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 1 (Address 0x00f0) + MAX98927_Authentication_result_1 = 0x00f0, + //#BYHAND width >= 5: + MAX98927_Authentication_result_1_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 2 (Address 0x00f1) + MAX98927_Authentication_result_2 = 0x00f1, + //#BYHAND width >= 5: + MAX98927_Authentication_result_2_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 3 (Address 0x00f2) + MAX98927_Authentication_result_3 = 0x00f2, + //#BYHAND width >= 5: + MAX98927_Authentication_result_3_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 4 (Address 0x00f3) + MAX98927_Authentication_result_4 = 0x00f3, + //#BYHAND width >= 5: + MAX98927_Authentication_result_4_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 5 (Address 0x00f4) + MAX98927_Authentication_result_5 = 0x00f4, + //#BYHAND width >= 5: + MAX98927_Authentication_result_5_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 6 (Address 0x00f5) + MAX98927_Authentication_result_6 = 0x00f5, + //#BYHAND width >= 5: + MAX98927_Authentication_result_6_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 7 (Address 0x00f6) + MAX98927_Authentication_result_7 = 0x00f6, + //#BYHAND width >= 5: + MAX98927_Authentication_result_7_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 8 (Address 0x00f7) + MAX98927_Authentication_result_8 = 0x00f7, + //#BYHAND width >= 5: + MAX98927_Authentication_result_8_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 9 (Address 0x00f8) + MAX98927_Authentication_result_9 = 0x00f8, + //#BYHAND width >= 5: + MAX98927_Authentication_result_9_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 10 (Address 0x00f9) + MAX98927_Authentication_result_10 = 0x00f9, + //#BYHAND width >= 5: + MAX98927_Authentication_result_10_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 11 (Address 0x00fa) + MAX98927_Authentication_result_11 = 0x00fa, + //#BYHAND width >= 5: + MAX98927_Authentication_result_11_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 12 (Address 0x00fb) + MAX98927_Authentication_result_12 = 0x00fb, + //#BYHAND width >= 5: + MAX98927_Authentication_result_12_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 13 (Address 0x00fc) + MAX98927_Authentication_result_13 = 0x00fc, + //#BYHAND width >= 5: + MAX98927_Authentication_result_13_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 14 (Address 0x00fd) + MAX98927_Authentication_result_14 = 0x00fd, + //#BYHAND width >= 5: + MAX98927_Authentication_result_14_AUTH_RESULT_Mask = (0xff << 0), + + // Authentication result 15 (Address 0x00fe) + MAX98927_Authentication_result_15 = 0x00fe, + //#BYHAND width >= 5: + MAX98927_Authentication_result_15_AUTH_RESULT_Mask = (0xff << 0), + + // Global Enable (Address 0x00ff) + MAX98927_Global_Enable = 0x00ff, + MAX98927_Global_Enable_EN = (0x1 << 0), + // Software Reset (Address 0x0100) + MAX98927_Software_Reset = 0x0100, + MAX98927_Software_Reset_RST = (0x1 << 0), + + // REV ID (Address 0x01ff) + MAX98927_REV_ID = 0x01ff, + //#BYHAND width >= 5: + MAX98927_REV_ID_REV_ID_Mask = (0xff << 0), + + +} MAX98927Registers; + +typedef enum{ + MAX98927_CH0 = (1 << 0), + MAX98927_CH1 = (1 << 1), + MAX98927_CH2 = (1 << 2), + MAX98927_CH3 = (1 << 3), +} MAX98927_CHANNEL_SHIT; + +struct max98927_priv { + struct device *dev; + struct regmap *regmap[MAX_CHANNEL_NUM]; + struct snd_soc_component *component; + struct max98927_pdata *pdata; + unsigned int spk_mode; + unsigned int spk_gain; + unsigned int sysclk; + unsigned int v_l_slot; + unsigned int i_l_slot; + unsigned int v_r_slot; + unsigned int i_r_slot; + unsigned int mono_stereo; + unsigned int i2c_states; + bool interleave_mode; + unsigned int ch_size; + unsigned int rate; + unsigned int iface; + unsigned int master; + unsigned int thres_hyste; + unsigned int level5_hold; + unsigned int level6_hold; + unsigned int level7_hold; + unsigned int level8_hold; + unsigned int amp_limit; + unsigned int amp_limit_rel; + unsigned int amp1_level; + unsigned int amp2_level; + unsigned int amp3_level; + unsigned int amp1_level8; + unsigned int amp2_level8; + unsigned int amp3_level8; + unsigned int amp1_level7; + unsigned int amp2_level7; + unsigned int amp3_level7; + unsigned int amp1_level6; + unsigned int amp2_level6; + unsigned int amp3_level6; + unsigned int amp1_level5; + unsigned int amp2_level5; + unsigned int amp3_level5; + unsigned int digital_gain; + unsigned int pdm_gain; + unsigned int level_hold; + unsigned int ref_RDC[2]; + unsigned int path_status; + int reset_gpio_l; + int reset_gpio_r; //dongchen to delete +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + struct regulator *max989xx_vdd; +}; + +#define MAX98927_GLOBAL_SHIFT 0 +#define M98927_DAI_MSEL_SHIFT 4 +#define M98927_DAI_BSEL_SHIFT 0 +#define M98927_DAI_BSEL_32 (2 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_BSEL_48 (3 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_BSEL_64 (4 << M98927_DAI_BSEL_SHIFT) +#define M98927_DAI_MSEL_32 (2 << M98927_DAI_MSEL_SHIFT) +#define M98927_DAI_MSEL_48 (3 << M98927_DAI_MSEL_SHIFT) +#define M98927_DAI_MSEL_64 (4 << M98927_DAI_MSEL_SHIFT) +#define MAX98927_Speaker_Gain_Width 3 +#define MAX98927_SPK_RMP_EN_SHIFT 4 +#define MAX98927_PDM_GAIN_SHIFT 4 +#define MAX98927_pdm_Gain_Width 3 +#define MAX98927_AMP_VOL_WIDTH 7 +#define MAX98927_AMP_VOL_LOCATION_SHIFT 7 +#define MAX98927_PDM_Rx_Enable_PDM_CH_SHIFT 3 +#define MAX98927_PCM_to_speaker_monomix_A_SHIFT 6 +#define MAX98927_PCM_Sample_rate_setup_2_DIG_IF_SR_48000 (0x8 << 4) +#define MAX98927_PCM_FORMAT_DSP_A (0x3 << 3) +#define MAX98927_DRE_Control_DRE_SHIFT 0 +#define MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT (2) +#define MAX98927_Brownout_AMP_limiter_attack_release_shift 4 +#define MAX98927_BDE_DSP_SHIFT 2 +#define MAX98927_Speaker_Gain_SPK_PDM_GAIN_SHIFT (4) +#define MAX98927_BDE_AMP_SHIFT 1 +#define MAX98927_BST_ILIM_SHIFT 1 +#endif // __MAX98927_REGISTERDEFS_H diff --git a/techpack/audio/asoc/codecs/tfa9874/Kbuild b/techpack/audio/asoc/codecs/tfa9874/Kbuild new file mode 100644 index 000000000000..4f61be5e63f7 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/Kbuild @@ -0,0 +1,126 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.19 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm710auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm710autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_KONA), y) + include $(AUDIO_ROOT)/config/konaauto.conf + INCS += -include $(AUDIO_ROOT)/config/konaautoconf.h + endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ tfa98xx ############ +ifdef CONFIG_SND_SOC_TFA98XX + TFA98XX_OBJS += tfa98xx.o + TFA98XX_OBJS += tfa_container.o + TFA98XX_OBJS += tfa_dsp.o + TFA98XX_OBJS += tfa_init.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_TFA98XX) += tfa98xx_dlkm.o +tfa98xx_dlkm-y := $(TFA98XX_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/techpack/audio/asoc/codecs/tfa9874/config.h b/techpack/audio/asoc/codecs/tfa9874/config.h new file mode 100644 index 000000000000..82b4d2265eee --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/config.h @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2017 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. + */ + +/* + Linux kernel specific definitions used by code shared with + Linux/Windows user space. +*/ + +#ifndef __CONFIG_LINUX_KERNEL_INC__ +#define __CONFIG_LINUX_KERNEL_INC__ + +#include +#include +#include +#include +#include + +#define _ASSERT(e) +#define PRINT_ASSERT(e)if ((e)) printk(KERN_ERR "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) + +#if defined(CONFIG_TRACING) && defined(DEBUG) + #define tfa98xx_trace_printk(...) +#else + #define tfa98xx_trace_printk(...) +#endif + +#endif /* __CONFIG_LINUX_KERNEL_INC__ */ + diff --git a/techpack/audio/asoc/codecs/tfa9874/dbgprint.h b/techpack/audio/asoc/codecs/tfa9874/dbgprint.h new file mode 100644 index 000000000000..c30157c84e73 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/dbgprint.h @@ -0,0 +1,165 @@ +/* + * Copyright 2014-2017 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 _DBGPRINT_H +# define _DBGPRINT_H + +/* Debugging macro's. */ +# ifndef DEBUG +# define DEBUG +# endif + +# ifndef ASSERT +//#define ASSERT +# endif + //TODO wwwim +# ifndef _ASSERT + #define _ASSERT(e) +# endif + +# ifndef PREFIX +# define PREFIX "tfa98xx: " +# define DRIVER_NAME "tfa98xx" +# endif + +#ifdef __KERNEL__ + +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) do {\ + if (unlikely(debug >= (level))) \ + printk(KERN_INFO PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); \ + } while (0) + +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,va...) printk(KERN_INFO PREFIX "%s:%d: "fmt,__func__,__LINE__,##va) +# define _ERRORMSG(fmt,va...) printk(KERN_ERR PREFIX "ERROR %s:%d: "fmt,__func__,__LINE__, ##va) + + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) printk(x) +# define PRINT_ERROR(x...) printk(KERN_INFO PREFIX " **ERROR** " x) +# define PRINT_ASSERT(e)if ((e)) printk(KERN_ERR "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) + +# define PRINT_ENTRY DEBUG2("+[%s]\n", __func__) +# define PRINT_EXIT DEBUG2("-[%s]\n", __func__) + +# ifdef ASSERT +# define assert(cond,action) do { if (unlikely(!(cond))) { DEBUG0("Assert: %s\n",#cond); action; }} while(0) +# else +# define assert(cond,action) do { } while (0) +# endif + +#else /* __KERNEL__ */ +#if defined(WIN32) || defined(_X64) +#include +/* user mode */ +# ifdef DEBUG +# define _DEBUGMSG(level,fmt,...) printf(PREFIX "%s:%d: "fmt,__FUNCTION__,__LINE__,__VA_ARGS__); +# else +# define _DEBUGMSG(level,fmt,...) do {} while(0) +# endif + +# define _ERRORMSG(fmt,...) printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__FUNCTION__,__LINE__,__VA_ARGS__) + +# define DEBUG0(...) MSG(__VA_ARGS__) +# define DEBUG1(...) _DEBUGMSG(1,__VA_ARGS__) +# define DEBUG2(...) _DEBUGMSG(2,__VA_ARGS__) +# define DEBUG3(...) _DEBUGMSG(3,__VA_ARGS__) +# define ERRORMSG(fmt,...) _ERRORMSG(fmt,__VA_ARGS__) +# define PRINT(...) printf(__VA_ARGS__) +/* +# define PRINT(...) { FILE *stream; \ + if((stream = freopen("nxp_tfa.txt", "ab+", stdout)) == NULL) exit(-1); \ + printf(__VA_ARGS__); \ + freopen( "CON", "ab+", stdout ); \ + } +*/ +# define PRINT_ERROR(...) fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,...) fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e)if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) +//# define PRINT_ASSERT(e) if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) + +#elif defined(__CODE_RED) +#include "app_global.h" +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,va)) +//printf(PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,__VA_ARGS__)) +//printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__func__,__LINE__,##va) +//TB_TRACE_INF(TbTracePfx2(APP_PFX,TB_FUNC,"path=%s, chan=%u, muted=%s, vol=%d\n", +// path->isRecording ? "recording" : "playback", +// i, +// channelVol.currentMuteValue ? "YES" : "NO", +// channelVol.currentVolumeValue +// )); +//# define _ERRORMSG(fmt,va...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,va)) +# define ERRORMSG(...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,__VA_ARGS__)) +//fprintf(stderr, PREFIX "ERROR %s:%s:%d: "fmt,__FILE__,__func__,__LINE__, ##va) + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +//# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//printf(x) +# define PRINT_ERROR(x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e) +//TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,Tfa98xx_GetErrorString(e))) +//if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) +#else +#include +/* user mode */ +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) printf(PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,va...) printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__func__,__LINE__,##va) +# define _ERRORMSG(fmt,va...) fprintf(stderr, PREFIX "ERROR %s:%s:%d: "fmt,__FILE__,__func__,__LINE__, ##va) + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) printf(x) +# define PRINT_ERROR(...) fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,...) fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e)if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) +//# define PRINT_ASSERT(e) if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) + + +#endif /* WIN32 */ + +#endif /* user */ + +#endif /* _DBGPRINT_H --------------- */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa.h b/techpack/audio/asoc/codecs/tfa9874/tfa.h new file mode 100644 index 000000000000..2cf59f107e89 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015-2017 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) + +extern struct tfa_device **devs; + +/** + * tfa error return codes + */ +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 */ +}; + +enum Tfa98xx_Error tfa_write_filters(struct tfa_device *tfa, int prof_idx); + +struct tfa_device ** tfa_devs_create(int count); +void tfa_devs_destroy(int count); + +struct tfa_device ** tfa_get_device_struct(void); + +int tfa_plop_noise_interrupt(struct tfa_device *tfa, int profile, int vstep); +void tfa_lp_mode_interrupt(struct tfa_device *tfa); + +#endif /* TFA_H_ */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa1_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa1_tfafieldnames.h new file mode 100644 index 000000000000..f83156ea9e11 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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 + */ +#define TFA9897_I2CVERSION 34 +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/techpack/audio/asoc/codecs/tfa9874/tfa2_tfafieldnames_N1C.h b/techpack/audio/asoc/codecs/tfa9874/tfa2_tfafieldnames_N1C.h new file mode 100644 index 000000000000..ebbb86390dcf --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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 + */ +#define TFA9888_I2CVERSION 18 +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/techpack/audio/asoc/codecs/tfa9874/tfa9872_device_genregs_POR.h b/techpack/audio/asoc/codecs/tfa9874/tfa9872_device_genregs_POR.h new file mode 100644 index 000000000000..df35f57996c1 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9872_device_genregs_POR.h @@ -0,0 +1,121 @@ +/** Filename: Tfa98xx_device_genregs.h + * This file was generated automatically on 08/11/16 at 07:44:41. + * Source file: TFA9872N1B2_DefaultI2CSettings - V1.xlsx + */ + +#ifndef _TFA9872_DEVICE_GENREGS_H +#define _TFA9872_DEVICE_GENREGS_H + + +#define TFA9872_SYS_CONTROL0 0x00 +#define TFA9872_SYS_CONTROL1 0x01 +#define TFA9872_SYS_CONTROL2 0x02 +#define TFA9872_DEVICE_REVISION 0x03 +#define TFA9872_CLOCK_CONTROL 0x04 +#define TFA9872_CLOCK_GATING_CONTROL 0x05 +#define TFA9872_SIDE_TONE_CONFIG 0x0d +#define TFA9872_STATUS_FLAGS0 0x10 +#define TFA9872_STATUS_FLAGS1 0x11 +#define TFA9872_STATUS_FLAGS3 0x13 +#define TFA9872_STATUS_FLAGS4 0x14 +#define TFA9872_BATTERY_VOLTAGE 0x15 +#define TFA9872_TEMPERATURE 0x16 +#define TFA9872_VDDP_VOLTAGE 0x17 +#define TFA9872_TDM_CONFIG0 0x20 +#define TFA9872_TDM_CONFIG1 0x21 +#define TFA9872_TDM_CONFIG2 0x22 +#define TFA9872_TDM_CONFIG3 0x23 +#define TFA9872_TDM_CONFIG6 0x26 +#define TFA9872_TDM_CONFIG7 0x27 +#define TFA9872_PDM_CONFIG0 0x31 +#define TFA9872_INTERRUPT_OUT_REG1 0x40 +#define TFA9872_INTERRUPT_OUT_REG2 0x41 +#define TFA9872_INTERRUPT_OUT_REG3 0x42 +#define TFA9872_INTERRUPT_IN_REG1 0x44 +#define TFA9872_INTERRUPT_IN_REG2 0x45 +#define TFA9872_INTERRUPT_IN_REG3 0x46 +#define TFA9872_INTERRUPT_ENABLE_REG1 0x48 +#define TFA9872_INTERRUPT_ENABLE_REG2 0x49 +#define TFA9872_INTERRUPT_ENABLE_REG3 0x4a +#define TFA9872_STATUS_POLARITY_REG1 0x4c +#define TFA9872_STATUS_POLARITY_REG2 0x4d +#define TFA9872_STATUS_POLARITY_REG3 0x4e +#define TFA9872_BAT_PROT_CONFIG 0x50 +#define TFA9872_AUDIO_CONTROL 0x51 +#define TFA9872_AMPLIFIER_CONFIG 0x52 +#define TFA9872_PGA_CONTROL0 0x60 +#define TFA9872_GAIN_ATT 0x61 +#define TFA9872_TDM_SOURCE_CTRL 0x68 +#define TFA9872_SAM_CTRL 0x69 +#define TFA9872_STATUS_FLAGS5 0x6e +#define TFA9872_CURSENSE_COMP 0x6f +#define TFA9872_DCDC_CONTROL0 0x70 +#define TFA9872_DCDC_CONTROL4 0x74 +#define TFA9872_DCDC_CONTROL5 0x75 +#define TFA9872_MTPKEY2_REG 0xa1 +#define TFA9872_MTP_STATUS 0xa2 +#define TFA9872_KEY_PROTECTED_MTP_CONTROL 0xa3 +#define TFA9872_MTP_DATA_OUT_MSB 0xa5 +#define TFA9872_MTP_DATA_OUT_LSB 0xa6 +#define TFA9872_TEMP_SENSOR_CONFIG 0xb1 +#define TFA9872_SOFTWARE_PROFILE 0xee +#define TFA9872_SOFTWARE_VSTEP 0xef +#define TFA9872_KEY2_PROTECTED_MTP0 0xf0 +#define TFA9872_KEY2_PROTECTED_MTP5 0xf5 +#define TFA9872_SYS_CONTROL0_POR 0x0001 +#define TFA9872_SYS_CONTROL1_POR 0x0000 +#define TFA9872_SYS_CONTROL2_POR 0x2828 +#define TFA9872_DEVICE_REVISION_POR 0x3b72 +#define TFA9872_CLOCK_CONTROL_POR 0x0000 +#define TFA9872_CLOCK_GATING_CONTROL_POR 0x1f6a +#define TFA9872_SIDE_TONE_CONFIG_POR 0x0ebe +#define TFA9872_STATUS_FLAGS0_POR 0x001d +#define TFA9872_STATUS_FLAGS1_POR 0x0004 +#define TFA9872_STATUS_FLAGS3_POR 0x000f +#define TFA9872_STATUS_FLAGS4_POR 0x0000 +#define TFA9872_BATTERY_VOLTAGE_POR 0x03ff +#define TFA9872_TEMPERATURE_POR 0x0100 +#define TFA9872_VDDP_VOLTAGE_POR 0x0000 +#define TFA9872_TDM_CONFIG0_POR 0x2890 +#define TFA9872_TDM_CONFIG1_POR 0xc1f1 +#define TFA9872_TDM_CONFIG2_POR 0x045c +#define TFA9872_TDM_CONFIG3_POR 0x0003 +#define TFA9872_TDM_CONFIG6_POR 0x0010 +#define TFA9872_TDM_CONFIG7_POR 0x0001 +#define TFA9872_PDM_CONFIG0_POR 0x0000 +#define TFA9872_INTERRUPT_OUT_REG1_POR 0x0081 +#define TFA9872_INTERRUPT_OUT_REG2_POR 0x0000 +#define TFA9872_INTERRUPT_OUT_REG3_POR 0x0000 +#define TFA9872_INTERRUPT_IN_REG1_POR 0x0000 +#define TFA9872_INTERRUPT_IN_REG2_POR 0x0000 +#define TFA9872_INTERRUPT_IN_REG3_POR 0x0000 +#define TFA9872_INTERRUPT_ENABLE_REG1_POR 0x0001 +#define TFA9872_INTERRUPT_ENABLE_REG2_POR 0x0000 +#define TFA9872_INTERRUPT_ENABLE_REG3_POR 0x0000 +#define TFA9872_STATUS_POLARITY_REG1_POR 0x74e3 +#define TFA9872_STATUS_POLARITY_REG2_POR 0x967b +#define TFA9872_STATUS_POLARITY_REG3_POR 0x0085 +#define TFA9872_BAT_PROT_CONFIG_POR 0x8091 +#define TFA9872_AUDIO_CONTROL_POR 0x0080 +#define TFA9872_AMPLIFIER_CONFIG_POR 0x7a08 +#define TFA9872_PGA_CONTROL0_POR 0x0000 +#define TFA9872_GAIN_ATT_POR 0x0000 +#define TFA9872_TDM_SOURCE_CTRL_POR 0x0400 +#define TFA9872_SAM_CTRL_POR 0x0000 +#define TFA9872_STATUS_FLAGS5_POR 0x0007 +#define TFA9872_CURSENSE_COMP_POR 0x02e4 +#define TFA9872_DCDC_CONTROL0_POR 0x06e6 +#define TFA9872_DCDC_CONTROL4_POR 0xd913 +#define TFA9872_DCDC_CONTROL5_POR 0x118a +#define TFA9872_MTPKEY2_REG_POR 0x0000 +#define TFA9872_MTP_STATUS_POR 0x0003 +#define TFA9872_KEY_PROTECTED_MTP_CONTROL_POR 0x0000 +#define TFA9872_MTP_DATA_OUT_MSB_POR 0x0000 +#define TFA9872_MTP_DATA_OUT_LSB_POR 0x0000 +#define TFA9872_TEMP_SENSOR_CONFIG_POR 0x0000 +#define TFA9872_SOFTWARE_PROFILE_POR 0x0000 +#define TFA9872_SOFTWARE_VSTEP_POR 0x0000 +#define TFA9872_KEY2_PROTECTED_MTP0_POR 0x0000 +#define TFA9872_KEY2_PROTECTED_MTP5_POR 0x0000 + +#endif /* _TFA9872_DEVICE_GENREGS_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9872_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9872_tfafieldnames.h new file mode 100644 index 000000000000..23669d4c0011 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9872_tfafieldnames.h @@ -0,0 +1,1219 @@ +/** Filename: Tfa9872_TfaFieldnames.h + * This file was generated automatically on 10/11/16 at 10:19:27. + * Source file: TFA9872N1B2_DefaultI2CSettings -v16.xlsx + */ + +#ifndef _TFA9872_TFAFIELDNAMES_H +#define _TFA9872_TFAFIELDNAMES_H + + +#define TFA9872_I2CVERSION_N1A 26 +#define TFA9872_I2CVERSION_N1B 29 +#define TFA9872_I2CVERSION_N1B2 21 + + +typedef enum nxpTfa9872BfEnumList { + TFA9872_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9872_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9872_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA9872_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9872_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9872_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9872_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9872_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9872_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9872_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9872_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA9872_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9872_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9872_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9872_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA9872_BF_REV = 0x030f, /*!< Revision info */ + TFA9872_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9872_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9872_BF_SSE = 0x0510, /*!< Enable speaker path */ + TFA9872_BF_VSE = 0x0530, /*!< Voltage sense */ + TFA9872_BF_CSE = 0x0550, /*!< Current sense */ + TFA9872_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA9872_BF_PGAE = 0x0580, /*!< Enable PGA chop clock */ + TFA9872_BF_SSTDME= 0x0590, /*!< Sub-system TDM */ + TFA9872_BF_SSPBSTE= 0x05a0, /*!< Sub-system boost */ + TFA9872_BF_SSADCE= 0x05b0, /*!< Sub-system ADC */ + TFA9872_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9872_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA9872_BF_STSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA9872_BF_ST1C = 0x0db0, /*!< side tone one s complement */ + TFA9872_BF_VDDS = 0x1000, /*!< POR */ + TFA9872_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA9872_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9872_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9872_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9872_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA9872_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA9872_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA9872_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA9872_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9872_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9872_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9872_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9872_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA9872_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA9872_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9872_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9872_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9872_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA9872_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA9872_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA9872_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA9872_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA9872_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9872_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9872_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9872_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9872_BF_CLIPAH= 0x1340, /*!< Clipping A to Vddp */ + TFA9872_BF_CLIPAL= 0x1350, /*!< Clipping A to gnd */ + TFA9872_BF_CLIPBH= 0x1360, /*!< Clipping B to Vddp */ + TFA9872_BF_CLIPBL= 0x1370, /*!< Clipping B to gnd */ + TFA9872_BF_OCDS = 0x1380, /*!< OCP amplifier */ + TFA9872_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9872_BF_OCPOKMC= 0x13a0, /*!< OCPOK MICVDD */ + TFA9872_BF_MANALARM= 0x13b0, /*!< Alarm state */ + TFA9872_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9872_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9872_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9872_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9872_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9872_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9872_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9872_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9872_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/9.5 V) */ + TFA9872_BF_TDME = 0x2040, /*!< Enable interface */ + TFA9872_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9872_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9872_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA9872_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9872_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9872_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9872_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9872_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9872_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9872_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9872_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9872_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9872_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA9872_BF_TDMTXUS0= 0x2291, /*!< Format unused slots DATAO */ + TFA9872_BF_TDMSPKE= 0x2300, /*!< Control audio tdm channel in 0 (spkr + dcdc) */ + TFA9872_BF_TDMDCE= 0x2310, /*!< Control audio tdm channel in 1 (dcdc) */ + TFA9872_BF_TDMCSE= 0x2330, /*!< current sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMVSE= 0x2340, /*!< Voltage sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMSPKS= 0x2603, /*!< tdm slot for sink 0 (speaker + dcdc) */ + TFA9872_BF_TDMDCS= 0x2643, /*!< tdm slot for sink 1 (dcdc) */ + TFA9872_BF_TDMCSS= 0x26c3, /*!< Slot Position of current sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense vbat temperature and vddp feedback */ + TFA9872_BF_PDMSTSEL= 0x3111, /*!< Side tone input */ + TFA9872_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9872_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA9872_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9872_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA9872_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9872_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA9872_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA9872_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9872_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA9872_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA9872_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA9872_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA9872_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA9872_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA9872_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA9872_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA9872_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA9872_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA9872_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA9872_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm */ + TFA9872_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA9872_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA9872_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA9872_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA9872_BF_ISTCLPR= 0x4220, /*!< Status clip */ + TFA9872_BF_ISTLP0= 0x4240, /*!< Status low power mode0 */ + TFA9872_BF_ISTLP1= 0x4250, /*!< Status low power mode1 */ + TFA9872_BF_ISTLA = 0x4260, /*!< Status low noise detection */ + TFA9872_BF_ISTVDDPH= 0x4270, /*!< Status VDDP greater than VBAT */ + TFA9872_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9872_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA9872_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9872_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA9872_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9872_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA9872_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA9872_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9872_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA9872_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA9872_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA9872_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA9872_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA9872_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA9872_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA9872_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA9872_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA9872_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA9872_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA9872_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm */ + TFA9872_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA9872_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA9872_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA9872_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA9872_BF_ICLCLPR= 0x4620, /*!< Clear clip */ + TFA9872_BF_ICLLP0= 0x4640, /*!< Clear low power mode0 */ + TFA9872_BF_ICLLP1= 0x4650, /*!< Clear low power mode1 */ + TFA9872_BF_ICLLA = 0x4660, /*!< Clear low noise detection */ + TFA9872_BF_ICLVDDPH= 0x4670, /*!< Clear VDDP greater then VBAT */ + TFA9872_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9872_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA9872_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9872_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA9872_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9872_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA9872_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA9872_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9872_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA9872_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA9872_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA9872_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA9872_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA9872_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA9872_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA9872_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA9872_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA9872_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA9872_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA9872_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm */ + TFA9872_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA9872_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA9872_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA9872_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA9872_BF_IECLPR= 0x4a20, /*!< Enable clip */ + TFA9872_BF_IELP0 = 0x4a40, /*!< Enable low power mode0 */ + TFA9872_BF_IELP1 = 0x4a50, /*!< Enable low power mode1 */ + TFA9872_BF_IELA = 0x4a60, /*!< Enable low noise detection */ + TFA9872_BF_IEVDDPH= 0x4a70, /*!< Enable VDDP greater tehn VBAT */ + TFA9872_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9872_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA9872_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9872_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA9872_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9872_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA9872_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA9872_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9872_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA9872_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA9872_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA9872_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA9872_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA9872_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA9872_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA9872_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA9872_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA9872_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA9872_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA9872_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm */ + TFA9872_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA9872_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA9872_BF_IPCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA9872_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA9872_BF_IPOCLPR= 0x4e20, /*!< Polarity clip right */ + TFA9872_BF_IPOLP0= 0x4e40, /*!< Polarity low power mode0 */ + TFA9872_BF_IPOLP1= 0x4e50, /*!< Polarity low power mode1 */ + TFA9872_BF_IPOLA = 0x4e60, /*!< Polarity low noise mode */ + TFA9872_BF_IPOVDDPH= 0x4e70, /*!< Polarity VDDP greater than VBAT */ + TFA9872_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9872_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9872_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9872_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9872_BF_BSSBY = 0x50f0, /*!< Bypass battery safeguard */ + TFA9872_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9872_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA9872_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9872_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9872_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9872_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9872_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9872_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9872_BF_PGAGAIN= 0x6081, /*!< PGA gain selection */ + TFA9872_BF_PGALPE= 0x60b0, /*!< Lowpass enable */ + TFA9872_BF_LPM0BYP= 0x6110, /*!< bypass low power idle mode */ + TFA9872_BF_TDMDCG= 0x6123, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9872_BF_TDMSPKG= 0x6163, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9872_BF_STIDLEEN= 0x61b0, /*!< enable idle feature for channel 1 */ + TFA9872_BF_LNMODE= 0x62e1, /*!< ctrl select mode */ + TFA9872_BF_LPM1MODE= 0x64e1, /*!< low power mode control */ + TFA9872_BF_LPM1DIS= 0x65c0, /*!< low power mode1 detector control */ + TFA9872_BF_TDMSRCMAP= 0x6801, /*!< tdm source mapping */ + TFA9872_BF_TDMSRCAS= 0x6821, /*!< Sensed value A */ + TFA9872_BF_TDMSRCBS= 0x6841, /*!< Sensed value B */ + TFA9872_BF_ANCSEL= 0x6881, /*!< anc input */ + TFA9872_BF_ANC1C = 0x68a0, /*!< ANC one s complement */ + TFA9872_BF_SAMMODE= 0x6901, /*!< sam enable */ + TFA9872_BF_SAMSEL= 0x6920, /*!< sam source */ + TFA9872_BF_PDMOSELH= 0x6931, /*!< pdm out value when pdm_clk is higth */ + TFA9872_BF_PDMOSELL= 0x6951, /*!< pdm out value when pdm_clk is low */ + TFA9872_BF_SAMOSEL= 0x6970, /*!< ram output on mode sam and audio */ + TFA9872_BF_LP0 = 0x6e00, /*!< low power mode 0 detection */ + TFA9872_BF_LP1 = 0x6e10, /*!< low power mode 1 detection */ + TFA9872_BF_LA = 0x6e20, /*!< low amplitude detection */ + TFA9872_BF_VDDPH = 0x6e30, /*!< vddp greater than vbat */ + TFA9872_BF_DELCURCOMP= 0x6f02, /*!< delay to allign compensation signal with current sense signal */ + TFA9872_BF_SIGCURCOMP= 0x6f40, /*!< polarity of compensation for current sense */ + TFA9872_BF_ENCURCOMP= 0x6f50, /*!< enable current sense compensation */ + TFA9872_BF_SELCLPPWM= 0x6f60, /*!< Select pwm clip flag */ + TFA9872_BF_LVLCLPPWM= 0x6f72, /*!< set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9872_BF_DCVOS = 0x7002, /*!< Second boost voltage level */ + TFA9872_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9872_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9872_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9872_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9872_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA9872_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9872_BF_DCVOF = 0x7402, /*!< 1st boost voltage level */ + TFA9872_BF_DCTRACK= 0x7430, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_DCTRIP= 0x7444, /*!< 1st Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9872_BF_DCHOLD= 0x7494, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_DCTRIP2= 0x7534, /*!< 2nd Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9872_BF_DCTRIPT= 0x7584, /*!< Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9872_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9872_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9872_BF_CMTPI = 0xa350, /*!< Start copying all the data from mtp to I2C mtp registers */ + TFA9872_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9872_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9872_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9872_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9872_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9872_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9872_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9872_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9872_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9872_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9872_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9872_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9872_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9872_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9872BfEnumList_t; +#define TFA9872_NAMETABLE static tfaBfName_t Tfa9872DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "RCVNS"}, /* Noise shaper selection , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x510, "SSE"}, /* Enable speaker path , */\ + { 0x530, "VSE"}, /* Voltage sense , */\ + { 0x550, "CSE"}, /* Current sense , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0x580, "PGAE"}, /* Enable PGA chop clock , */\ + { 0x590, "SSTDME"}, /* Sub-system TDM , */\ + { 0x5a0, "SSPBSTE"}, /* Sub-system boost , */\ + { 0x5b0, "SSADCE"}, /* Sub-system ADC , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "STSMUTE"}, /* Side tone soft mute , */\ + { 0xdb0, "ST1C"}, /* side tone one s complement , */\ + { 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 , */\ + { 0x10a0, "SWS"}, /* Amplifier engage , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 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 , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1340, "CLIPAH"}, /* Clipping A to Vddp , */\ + { 0x1350, "CLIPAL"}, /* Clipping A to gnd , */\ + { 0x1360, "CLIPBH"}, /* Clipping B to Vddp , */\ + { 0x1370, "CLIPBL"}, /* Clipping B to gnd , */\ + { 0x1380, "OCDS"}, /* OCP amplifier , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "OCPOKMC"}, /* OCPOK MICVDD , */\ + { 0x13b0, "MANALARM"}, /* Alarm state , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/9.5 V) , */\ + { 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 DATAO , */\ + { 0x2300, "TDMSPKE"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "TDMDCE"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "TDMCSE"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "TDMSPKS"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x3111, "PDMSTSEL"}, /* Side tone input , */\ + { 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 , */\ + { 0x40a0, "ISTSWS"}, /* Status amplifier engage , */\ + { 0x40c0, "ISTAMPS"}, /* Status amplifier enable , */\ + { 0x40d0, "ISTAREFS"}, /* Status Ref enable , */\ + { 0x40e0, "ISTADCCR"}, /* Status Control ADC , */\ + { 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 , */\ + { 0x4190, "ISTOCPR"}, /* Status ocp alarm , */\ + { 0x41a0, "ISTMWSRC"}, /* Status Waits HW I2C settings , */\ + { 0x41c0, "ISTMWSMU"}, /* Status Audio mute sequence , */\ + { 0x41f0, "ISTCLKOOR"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4220, "ISTCLPR"}, /* Status clip , */\ + { 0x4240, "ISTLP0"}, /* Status low power mode0 , */\ + { 0x4250, "ISTLP1"}, /* Status low power mode1 , */\ + { 0x4260, "ISTLA"}, /* Status low noise detection , */\ + { 0x4270, "ISTVDDPH"}, /* Status VDDP greater than VBAT , */\ + { 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 , */\ + { 0x44a0, "ICLSWS"}, /* Clear amplifier engage , */\ + { 0x44c0, "ICLAMPS"}, /* Clear enbl amp , */\ + { 0x44d0, "ICLAREFS"}, /* Clear ref enable , */\ + { 0x44e0, "ICLADCCR"}, /* Clear control ADC , */\ + { 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 , */\ + { 0x4590, "ICLOCPR"}, /* Clear ocp alarm , */\ + { 0x45a0, "ICLMWSRC"}, /* Clear wait HW I2C settings , */\ + { 0x45c0, "ICLMWSMU"}, /* Clear audio mute sequence , */\ + { 0x45f0, "ICLCLKOOR"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4620, "ICLCLPR"}, /* Clear clip , */\ + { 0x4640, "ICLLP0"}, /* Clear low power mode0 , */\ + { 0x4650, "ICLLP1"}, /* Clear low power mode1 , */\ + { 0x4660, "ICLLA"}, /* Clear low noise detection , */\ + { 0x4670, "ICLVDDPH"}, /* Clear VDDP greater then VBAT , */\ + { 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 , */\ + { 0x48a0, "IESWS"}, /* Enable amplifier engage , */\ + { 0x48c0, "IEAMPS"}, /* Enable enbl amp , */\ + { 0x48d0, "IEAREFS"}, /* Enable ref enable , */\ + { 0x48e0, "IEADCCR"}, /* Enable Control ADC , */\ + { 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 , */\ + { 0x4990, "IEOCPR"}, /* Enable ocp alarm , */\ + { 0x49a0, "IEMWSRC"}, /* Enable waits HW I2C settings , */\ + { 0x49c0, "IEMWSMU"}, /* Enable man Audio mute sequence , */\ + { 0x49f0, "IECLKOOR"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "IETDMER"}, /* Enable tdm error , */\ + { 0x4a20, "IECLPR"}, /* Enable clip , */\ + { 0x4a40, "IELP0"}, /* Enable low power mode0 , */\ + { 0x4a50, "IELP1"}, /* Enable low power mode1 , */\ + { 0x4a60, "IELA"}, /* Enable low noise detection , */\ + { 0x4a70, "IEVDDPH"}, /* Enable VDDP greater tehn VBAT , */\ + { 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 , */\ + { 0x4ca0, "IPOSWS"}, /* Polarity amplifier engage , */\ + { 0x4cc0, "IPOAMPS"}, /* Polarity enbl amp , */\ + { 0x4cd0, "IPOAREFS"}, /* Polarity ref enable , */\ + { 0x4ce0, "IPOADCCR"}, /* Polarity Control ADC , */\ + { 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 , */\ + { 0x4d90, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4da0, "IPOMWSRC"}, /* Polarity waits HW I2C settings , */\ + { 0x4dc0, "IPOMWSMU"}, /* Polarity man audio mute sequence , */\ + { 0x4df0, "IPCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip right , */\ + { 0x4e40, "IPOLP0"}, /* Polarity low power mode0 , */\ + { 0x4e50, "IPOLP1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "IPOLA"}, /* Polarity low noise mode , */\ + { 0x4e70, "IPOVDDPH"}, /* Polarity VDDP greater than VBAT , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass battery safeguard , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5110, "INTSMUTE"}, /* Soft mute HW , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x6081, "PGAGAIN"}, /* PGA gain selection , */\ + { 0x60b0, "PGALPE"}, /* Lowpass enable , */\ + { 0x6110, "LPM0BYP"}, /* bypass low power idle mode , */\ + { 0x6123, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x61b0, "STIDLEEN"}, /* enable idle feature for channel 1 , */\ + { 0x62e1, "LNMODE"}, /* ctrl select mode , */\ + { 0x64e1, "LPM1MODE"}, /* low power mode control , */\ + { 0x65c0, "LPM1DIS"}, /* low power mode1 detector control , */\ + { 0x6801, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6821, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x6841, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x6881, "ANCSEL"}, /* anc input , */\ + { 0x68a0, "ANC1C"}, /* ANC one s complement , */\ + { 0x6901, "SAMMODE"}, /* sam enable , */\ + { 0x6920, "SAMSEL"}, /* sam source , */\ + { 0x6931, "PDMOSELH"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "PDMOSELL"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "SAMOSEL"}, /* ram output on mode sam and audio , */\ + { 0x6e00, "LP0"}, /* low power mode 0 detection , */\ + { 0x6e10, "LP1"}, /* low power mode 1 detection , */\ + { 0x6e20, "LA"}, /* low amplitude detection , */\ + { 0x6e30, "VDDPH"}, /* vddp greater than vbat , */\ + { 0x6f02, "DELCURCOMP"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "SIGCURCOMP"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "ENCURCOMP"}, /* enable current sense compensation , */\ + { 0x6f60, "SELCLPPWM"}, /* Select pwm clip flag , */\ + { 0x6f72, "LVLCLPPWM"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7002, "DCVOS"}, /* Second boost voltage level , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7402, "DCVOF"}, /* 1st boost voltage level , */\ + { 0x7430, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "DCTRIP"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7534, "DCTRIP2"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "DCTRIPT"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa350, "CMTPI"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 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 , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 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 , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9872_BITNAMETABLE static tfaBfName_t Tfa9872BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 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 , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker path , */\ + { 0x530, "enbl_volsense"}, /* Voltage sense , */\ + { 0x550, "enbl_cursense"}, /* Current sense , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0x580, "enbl_pga_chop"}, /* Enable PGA chop clock , */\ + { 0x590, "enbl_tdm_ss"}, /* Sub-system TDM , */\ + { 0x5a0, "enbl_bst_ss"}, /* Sub-system boost , */\ + { 0x5b0, "enbl_adc_ss"}, /* Sub-system ADC , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0xd18, "side_tone_gain"}, /* Side tone gain , */\ + { 0xda0, "mute_side_tone"}, /* Side tone soft mute , */\ + { 0xdb0, "side_tone_1scomplement"}, /* side tone one s complement , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 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 , */\ + { 0x10a0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 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 , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1340, "flag_clipa_high"}, /* Clipping A to Vddp , */\ + { 0x1350, "flag_clipa_low"}, /* Clipping A to gnd , */\ + { 0x1360, "flag_clipb_high"}, /* Clipping B to Vddp , */\ + { 0x1370, "flag_clipb_low"}, /* Clipping B to gnd , */\ + { 0x1380, "flag_ocp_alarm"}, /* OCP amplifier , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13b0, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/9.5 V) , */\ + { 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 DATAO , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "tdm_source0_enable"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "tdm_sink0_slot"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x3111, "pdm_side_tone_sel"}, /* Side tone input , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 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 , */\ + { 0x40a0, "int_out_flag_engage"}, /* Status amplifier engage , */\ + { 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 , */\ + { 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 , */\ + { 0x4190, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 0x41a0, "int_out_flag_man_wait_src_settings"}, /* Status Waits HW I2C settings , */\ + { 0x41c0, "int_out_flag_man_start_mute_audio"}, /* Status Audio mute sequence , */\ + { 0x41f0, "int_out_flag_clk_out_of_range"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4220, "int_out_flag_clip"}, /* Status clip , */\ + { 0x4240, "int_out_flag_lp_detect_mode0"}, /* Status low power mode0 , */\ + { 0x4250, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 , */\ + { 0x4260, "int_out_flag_low_amplitude"}, /* Status low noise detection , */\ + { 0x4270, "int_out_flag_vddp_gt_vbat"}, /* Status VDDP greater than VBAT , */\ + { 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 , */\ + { 0x44a0, "int_in_flag_engage"}, /* Clear amplifier engage , */\ + { 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 , */\ + { 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 , */\ + { 0x4590, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 0x45a0, "int_in_flag_man_wait_src_settings"}, /* Clear wait HW I2C settings , */\ + { 0x45c0, "int_in_flag_man_start_mute_audio"}, /* Clear audio mute sequence , */\ + { 0x45f0, "int_in_flag_clk_out_of_range"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4620, "int_in_flag_clip"}, /* Clear clip , */\ + { 0x4640, "int_in_flag_lp_detect_mode0"}, /* Clear low power mode0 , */\ + { 0x4650, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 , */\ + { 0x4660, "int_in_flag_low_amplitude"}, /* Clear low noise detection , */\ + { 0x4670, "int_in_flag_vddp_gt_vbat"}, /* Clear VDDP greater then VBAT , */\ + { 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 , */\ + { 0x48a0, "int_enable_flag_engage"}, /* Enable amplifier engage , */\ + { 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 , */\ + { 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 , */\ + { 0x4990, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 0x49a0, "int_enable_flag_man_wait_src_settings"}, /* Enable waits HW I2C settings , */\ + { 0x49c0, "int_enable_flag_man_start_mute_audio"}, /* Enable man Audio mute sequence , */\ + { 0x49f0, "int_enable_flag_clk_out_of_range"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4a20, "int_enable_flag_clip"}, /* Enable clip , */\ + { 0x4a40, "int_enable_flag_lp_detect_mode0"}, /* Enable low power mode0 , */\ + { 0x4a50, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 , */\ + { 0x4a60, "int_enable_flag_low_amplitude"}, /* Enable low noise detection , */\ + { 0x4a70, "int_enable_flag_vddp_gt_vbat"}, /* Enable VDDP greater tehn VBAT , */\ + { 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 , */\ + { 0x4ca0, "int_polarity_flag_engage"}, /* Polarity amplifier engage , */\ + { 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 , */\ + { 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 , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4da0, "int_polarity_flag_man_wait_src_settings"}, /* Polarity waits HW I2C settings , */\ + { 0x4dc0, "int_polarity_flag_man_start_mute_audio"}, /* Polarity man audio mute sequence , */\ + { 0x4df0, "int_polarity_flag_clk_out_of_range"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4e20, "int_polarity_flag_clip"}, /* Polarity clip right , */\ + { 0x4e40, "int_polarity_flag_lp_detect_mode0"}, /* Polarity low power mode0 , */\ + { 0x4e50, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "int_polarity_flag_low_amplitude"}, /* Polarity low noise mode , */\ + { 0x4e70, "int_polarity_flag_vddp_gt_vbat"}, /* Polarity VDDP greater than VBAT , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass battery safeguard , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5110, "soft_mute"}, /* Soft mute HW , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 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 , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 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 , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x6081, "pga_gain_set"}, /* PGA gain selection , */\ + { 0x60b0, "pga_lowpass_enable"}, /* Lowpass enable , */\ + { 0x60c0, "pga_pwr_enable"}, /* Power enable, directcontrol mode only , */\ + { 0x60d0, "pga_switch_enable"}, /* Switch enable, directcontrol mode only , */\ + { 0x60e0, "pga_switch_aux_enable"}, /* Switch enable aux, directcontrol mode only , */\ + { 0x6100, "force_idle"}, /* force low power in idle mode , */\ + { 0x6110, "bypass_idle"}, /* bypass low power idle mode , */\ + { 0x6123, "ctrl_attl"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "ctrl_attr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x61a0, "idle_cnt"}, /* idle counter , */\ + { 0x61b0, "enbl_idle_ch1"}, /* enable idle feature for channel 1 , */\ + { 0x6265, "zero_lvl"}, /* low noise gain switch zero trigger level , */\ + { 0x62c1, "ctrl_fb_classd"}, /* class D gain ctrl_fb_50k ctrl_fb_100k , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* low noise gain switch trigger level , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 zero crossing detection level , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode control , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 amplitude trigger level , */\ + { 0x6565, "lpm1_hold_time"}, /* low power mode1 detector ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtred vs unfiltered compare , */\ + { 0x6801, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6821, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x6841, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x6881, "pdm_anc_sel"}, /* anc input , */\ + { 0x68a0, "anc_1scomplement"}, /* ANC one s complement , */\ + { 0x6901, "sam_mode"}, /* sam enable , */\ + { 0x6920, "sam_src"}, /* sam source , */\ + { 0x6931, "pdmdat_h_sel"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "pdmdat_l_sel"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "sam_spkr_sel"}, /* ram output on mode sam and audio , */\ + { 0x6a02, "rst_min_vbat_delay"}, /* rst_min_vbat delay (nb fs) , */\ + { 0x6b00, "disable_auto_engage"}, /* disable auto engange , */\ + { 0x6b10, "sel_tdm_data_valid"}, /* select tdm valid for speaker subsystem , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c69, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e00, "flag_lp_detect_mode0"}, /* low power mode 0 detection , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x6f02, "cursense_comp_delay"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "cursense_comp_sign"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "enbl_cursense_comp"}, /* enable current sense compensation , */\ + { 0x6f60, "sel_clip_pwms"}, /* Select pwm clip flag , */\ + { 0x6f72, "pwms_clip_lvl"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7002, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 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 , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7402, "frst_boost_voltage"}, /* 1st boost voltage level , */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "boost_trip_lvl_1st"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 , */\ + { 0x7502, "track_decay"}, /* DCDC Boost decay speed after a peak value, effective only when boost_track is set to 1, */\ + { 0x7534, "boost_trip_lvl_2nd"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "boost_trip_lvl_track"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7620, "pga_test_ldo_bypass"}, /* bypass internal PGA LDO , */\ + { 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, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8200, "enbl_cmfb"}, /* Current sense common mode feedback control , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8222, "cmfb_gain"}, /* Current sense common mode feedback control gain , */\ + { 0x8254, "cmfb_offset"}, /* Current sense common mode feedback control offset , */\ + { 0x82a0, "cs_sam_set"}, /* Enable SAM input for current sense , */\ + { 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), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8760, "enbl_cs_nofloating_n"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "vol_cur_sense_dc_offset"}, /* voltage and current sense decimator offset control, */\ + { 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 , */\ + { 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 , */\ + { 0xc5b0, "pga_test_enable"}, /* Enable PGA test mode , */\ + { 0xc5c0, "pga_test_offset_enable"}, /* Enable PGA test offset , */\ + { 0xc5d0, "pga_test_shortinput_enable"}, /* Enable PGA test shortinput , */\ + { 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, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to PDMDAT (see Digimux list for details), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT 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 , */\ + { 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 , */\ + { 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, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 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 , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd701, "pdmdat_ehs"}, /* Speed/load setting for PDMDAT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd721, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd740, "bck_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd750, "datai_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd760, "pdmclk_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd800, "source_in_testmode"}, /* tdm source in test mode (return only current and voltage sense), */\ + { 0xd810, "gainatt_feedback"}, /* gainatt feedback to tdm , */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost , */\ + { 0xd890, "pdm_loopback"}, /* pdm loop back to tdm , */\ + { 0xd8a1, "force_pga_clock"}, /* force pga clock , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 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, "spare_mpt1_9_6"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "spare_mpt1_15_10"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a3, "spare_mpt2_13_10"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "spare_mpt3_7_0"}, /* SPARE , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "spare_mtp4_15_0"}, /* SPARE , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of speaker coil , */\ + { 0xf606, "spare_mpt6_6_0"}, /* SPARE , */\ + { 0xf686, "spare_mpt6_14_8"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of 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, "disable_sam_mode"}, /* Disable same mode , */\ + { 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 , */\ + { 0xf980, "mtp_enbl_amp_in_state_alarm"}, /* Enbl_amp in alarm state , */\ + { 0xf990, "mtp_enbl_pwm_delay_clock_gating"}, /* pwm delay clock auto gating , */\ + { 0xf9a0, "mtp_enbl_ocp_clock_gating"}, /* ocpclock auto gating , */\ + { 0xf9b0, "mtp_gate_cgu_clock_for_test"}, /* cgu test clock control , */\ + { 0xf9c3, "spare_mtp9_15_12"}, /* 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) , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum TFA9872_irq { + TFA9872_irq_stvdds = 0, + TFA9872_irq_stplls = 1, + TFA9872_irq_stotds = 2, + TFA9872_irq_stovds = 3, + TFA9872_irq_stuvds = 4, + TFA9872_irq_stclks = 5, + TFA9872_irq_stmtpb = 6, + TFA9872_irq_stnoclk = 7, + TFA9872_irq_stsws = 10, + TFA9872_irq_stamps = 12, + TFA9872_irq_starefs = 13, + TFA9872_irq_stadccr = 14, + TFA9872_irq_stbstcu = 16, + TFA9872_irq_stbsthi = 17, + TFA9872_irq_stbstoc = 18, + TFA9872_irq_stbstpkcur = 19, + TFA9872_irq_stbstvc = 20, + TFA9872_irq_stbst86 = 21, + TFA9872_irq_stbst93 = 22, + TFA9872_irq_stocpr = 25, + TFA9872_irq_stmwsrc = 26, + TFA9872_irq_stmwsmu = 28, + TFA9872_irq_stclkoor = 31, + TFA9872_irq_sttdmer = 32, + TFA9872_irq_stclpr = 34, + TFA9872_irq_stlp0 = 36, + TFA9872_irq_stlp1 = 37, + TFA9872_irq_stla = 38, + TFA9872_irq_stvddph = 39, + TFA9872_irq_max = 40, + TFA9872_irq_all = -1 /* all irqs */}; + +#define TFA9872_IRQ_NAMETABLE static tfaIrqName_t Tfa9872IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STPLLS"},\ + { 2, "STOTDS"},\ + { 3, "STOVDS"},\ + { 4, "STUVDS"},\ + { 5, "STCLKS"},\ + { 6, "STMTPB"},\ + { 7, "STNOCLK"},\ + { 8, "8"},\ + { 9, "9"},\ + { 10, "STSWS"},\ + { 11, "11"},\ + { 12, "STAMPS"},\ + { 13, "STAREFS"},\ + { 14, "STADCCR"},\ + { 15, "15"},\ + { 16, "STBSTCU"},\ + { 17, "STBSTHI"},\ + { 18, "STBSTOC"},\ + { 19, "STBSTPKCUR"},\ + { 20, "STBSTVC"},\ + { 21, "STBST86"},\ + { 22, "STBST93"},\ + { 23, "23"},\ + { 24, "24"},\ + { 25, "STOCPR"},\ + { 26, "STMWSRC"},\ + { 27, "27"},\ + { 28, "STMWSMU"},\ + { 29, "29"},\ + { 30, "30"},\ + { 31, "STCLKOOR"},\ + { 32, "STTDMER"},\ + { 33, "33"},\ + { 34, "STCLPR"},\ + { 35, "35"},\ + { 36, "STLP0"},\ + { 37, "STLP1"},\ + { 38, "STLA"},\ + { 39, "STVDDPH"},\ + { 40, "40"},\ +}; +#endif /* _TFA9872_TFAFIELDNAMES_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9874_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9874_tfafieldnames.h new file mode 100644 index 000000000000..72c155418810 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9874_tfafieldnames.h @@ -0,0 +1,830 @@ +/** Filename: tfa9874_tfafieldnames.h + * This file was generated automatically on 08/22/17 at 13:43:43. + * Source file: TFA9874N1C0_DefaultI2CSettings.xlsx + */ + +#ifndef _TFA9874_TFAFIELDNAMES_H +#define _TFA9874_TFAFIELDNAMES_H + + +#define TFA9874_I2CVERSION 1 + +typedef enum nxpTfa9874BfEnumList { + TFA9874_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9874_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9874_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA9874_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9874_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9874_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9874_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9874_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9874_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9874_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9874_BF_OPENMTP= 0x01e0, /*!< Control for FAIM protection */ + TFA9874_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9874_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9874_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9874_BF_REV = 0x030f, /*!< Revision info */ + TFA9874_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9874_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9874_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9874_BF_AMPOCRT= 0x0802, /*!< Amplifier on-off criteria for shutdown */ + TFA9874_BF_VDDS = 0x1000, /*!< POR */ + TFA9874_BF_DCOCPOK= 0x1010, /*!< DCDC OCP nmos (sticky register , clear on read) */ + TFA9874_BF_OTDS = 0x1020, /*!< OTP alarm (sticky register , clear on read) */ + TFA9874_BF_OCDS = 0x1030, /*!< OCP amplifier (sticky register , clear on read) */ + TFA9874_BF_UVDS = 0x1040, /*!< UVP alarm (sticky register , clear on read) */ + TFA9874_BF_MANALARM= 0x1050, /*!< Alarm state */ + TFA9874_BF_TDMERR= 0x1060, /*!< TDM error */ + TFA9874_BF_NOCLK = 0x1070, /*!< Lost clock (sticky register , clear on read) */ + TFA9874_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9874_BF_DCDCA = 0x1110, /*!< DCDC active (sticky register , clear on read) */ + TFA9874_BF_DCHVBAT= 0x1130, /*!< DCDC level 1x */ + TFA9874_BF_DCH114= 0x1140, /*!< DCDC level 1.14x */ + TFA9874_BF_DCH107= 0x1150, /*!< DCDC level 1.07x */ + TFA9874_BF_PLLS = 0x1160, /*!< PLL lock */ + TFA9874_BF_CLKS = 0x1170, /*!< Clocks stable */ + TFA9874_BF_TDMLUTER= 0x1180, /*!< TDM LUT error */ + TFA9874_BF_TDMSTAT= 0x1192, /*!< TDM status bits */ + TFA9874_BF_MTPB = 0x11c0, /*!< MTP busy */ + TFA9874_BF_SWS = 0x11d0, /*!< Amplifier engage */ + TFA9874_BF_AMPS = 0x11e0, /*!< Amplifier enable */ + TFA9874_BF_AREFS = 0x11f0, /*!< References enable */ + TFA9874_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9874_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9874_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9874_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9874_BF_OVDS = 0x1380, /*!< OVP alarm */ + TFA9874_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9874_BF_ADCCR = 0x13a0, /*!< Control ADC */ + TFA9874_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9874_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9874_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9874_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9874_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9874_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9874_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9874_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9874_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/13 V) */ + TFA9874_BF_TDME = 0x2040, /*!< Enable interface */ + TFA9874_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9874_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9874_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA9874_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9874_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9874_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9874_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9874_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9874_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9874_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9874_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9874_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9874_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA9874_BF_TDMTXUS0= 0x2291, /*!< Format unused slots DATAO */ + TFA9874_BF_TDMSPKE= 0x2300, /*!< Control audio tdm channel in 0 (spkr + dcdc) */ + TFA9874_BF_TDMDCE= 0x2310, /*!< Control audio tdm channel in 1 (dcdc) */ + TFA9874_BF_TDMCSE= 0x2330, /*!< current sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMVSE= 0x2340, /*!< Voltage sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMSPKS= 0x2603, /*!< tdm slot for sink 0 (speaker + dcdc) */ + TFA9874_BF_TDMDCS= 0x2643, /*!< tdm slot for sink 1 (dcdc) */ + TFA9874_BF_TDMCSS= 0x26c3, /*!< Slot Position of current sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense vbat temperature and vddp feedback */ + TFA9874_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9874_BF_ISTBSTOC= 0x4010, /*!< Status DCDC OCP */ + TFA9874_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9874_BF_ISTOCPR= 0x4030, /*!< Status ocp alarm */ + TFA9874_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9874_BF_ISTMANALARM= 0x4050, /*!< Status nanager Alarm state */ + TFA9874_BF_ISTTDMER= 0x4060, /*!< Status tdm error */ + TFA9874_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9874_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9874_BF_ICLBSTOC= 0x4410, /*!< Clear DCDC OCP */ + TFA9874_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9874_BF_ICLOCPR= 0x4430, /*!< Clear ocp alarm */ + TFA9874_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9874_BF_ICLMANALARM= 0x4450, /*!< clear nanager Alarm state */ + TFA9874_BF_ICLTDMER= 0x4460, /*!< Clear tdm error */ + TFA9874_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9874_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9874_BF_IEBSTOC= 0x4810, /*!< Enable DCDC OCP */ + TFA9874_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9874_BF_IEOCPR= 0x4830, /*!< Enable ocp alarm */ + TFA9874_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9874_BF_IEMANALARM= 0x4850, /*!< Enable nanager Alarm state */ + TFA9874_BF_IETDMER= 0x4860, /*!< Enable tdm error */ + TFA9874_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9874_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9874_BF_IPOBSTOC= 0x4c10, /*!< Polarity DCDC OCP */ + TFA9874_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9874_BF_IPOOCPR= 0x4c30, /*!< Polarity ocp alarm */ + TFA9874_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9874_BF_IPOMANALARM= 0x4c50, /*!< Polarity nanager Alarm state */ + TFA9874_BF_IPOTDMER= 0x4c60, /*!< Polarity tdm error */ + TFA9874_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9874_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9874_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9874_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9874_BF_VBATFLTL= 0x5080, /*!< vbat filter limit */ + TFA9874_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9874_BF_BSSBY = 0x50f0, /*!< Bypass battery safeguard */ + TFA9874_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9874_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9874_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9874_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9874_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9874_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9874_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9874_BF_TDMDCG= 0x6123, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9874_BF_TDMSPKG= 0x6163, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9874_BF_LNMODE= 0x62e1, /*!< ctrl select mode */ + TFA9874_BF_LPM1MODE= 0x64e1, /*!< low power mode control */ + TFA9874_BF_TDMSRCMAP= 0x6802, /*!< tdm source mapping */ + TFA9874_BF_TDMSRCAS= 0x6831, /*!< Sensed value A */ + TFA9874_BF_TDMSRCBS= 0x6851, /*!< Sensed value B */ + TFA9874_BF_TDMSRCACLIP= 0x6871, /*!< clip information (analog /digital) for source0 */ + TFA9874_BF_TDMSRCBCLIP= 0x6891, /*!< clip information (analog /digital) for source1 */ + TFA9874_BF_LP1 = 0x6e10, /*!< low power mode 1 detection */ + TFA9874_BF_LA = 0x6e20, /*!< low amplitude detection */ + TFA9874_BF_VDDPH = 0x6e30, /*!< vddp greater than vbat */ + TFA9874_BF_DELCURCOMP= 0x6f02, /*!< delay to allign compensation signal with current sense signal */ + TFA9874_BF_SIGCURCOMP= 0x6f40, /*!< polarity of compensation for current sense */ + TFA9874_BF_ENCURCOMP= 0x6f50, /*!< enable current sense compensation */ + TFA9874_BF_LVLCLPPWM= 0x6f72, /*!< set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9874_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9874_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9874_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9874_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9874_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA9874_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9874_BF_DCTRACK= 0x7430, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIP= 0x7444, /*!< 1st Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9874_BF_DCHOLD= 0x7494, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIP2= 0x7534, /*!< 2nd Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9874_BF_DCTRIPT= 0x7584, /*!< Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIPHYSTE= 0x75f0, /*!< Enable hysteresis on booster trip levels */ + TFA9874_BF_DCVOF = 0x7635, /*!< First boost voltage level */ + TFA9874_BF_DCVOS = 0x7695, /*!< Second boost voltage level */ + TFA9874_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9874_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9874_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9874_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9874_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9874_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9874_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9874_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9874_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9874_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9874_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9874_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9874_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9874_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9874_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9874_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9874_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9874BfEnumList_t; +#define TFA9874_NAMETABLE static tfaBfName_t Tfa9874DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "OPENMTP"}, /* Control for FAIM protection , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0x802, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "DCOCPOK"}, /* DCDC OCP nmos (sticky register , clear on read) , */\ + { 0x1020, "OTDS"}, /* OTP alarm (sticky register , clear on read) , */\ + { 0x1030, "OCDS"}, /* OCP amplifier (sticky register , clear on read), */\ + { 0x1040, "UVDS"}, /* UVP alarm (sticky register , clear on read) , */\ + { 0x1050, "MANALARM"}, /* Alarm state , */\ + { 0x1060, "TDMERR"}, /* TDM error , */\ + { 0x1070, "NOCLK"}, /* Lost clock (sticky register , clear on read) , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active (sticky register , clear on read) , */\ + { 0x1130, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1140, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1150, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1160, "PLLS"}, /* PLL lock , */\ + { 0x1170, "CLKS"}, /* Clocks stable , */\ + { 0x1180, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x1192, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11c0, "MTPB"}, /* MTP busy , */\ + { 0x11d0, "SWS"}, /* Amplifier engage , */\ + { 0x11e0, "AMPS"}, /* Amplifier enable , */\ + { 0x11f0, "AREFS"}, /* References enable , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1380, "OVDS"}, /* OVP alarm , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "ADCCR"}, /* Control ADC , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 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 DATAO , */\ + { 0x2300, "TDMSPKE"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "TDMDCE"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "TDMCSE"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "TDMSPKS"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOCPR"}, /* Status ocp alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTMANALARM"}, /* Status nanager Alarm state , */\ + { 0x4060, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOCPR"}, /* Clear ocp alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLMANALARM"}, /* clear nanager Alarm state , */\ + { 0x4460, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOCPR"}, /* Enable ocp alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IEMANALARM"}, /* Enable nanager Alarm state , */\ + { 0x4860, "IETDMER"}, /* Enable tdm error , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOMANALARM"}, /* Polarity nanager Alarm state , */\ + { 0x4c60, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x5080, "VBATFLTL"}, /* vbat filter limit , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass battery safeguard , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x6123, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x62e1, "LNMODE"}, /* ctrl select mode , */\ + { 0x64e1, "LPM1MODE"}, /* low power mode control , */\ + { 0x6802, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6831, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x6851, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x6871, "TDMSRCACLIP"}, /* clip information (analog /digital) for source0 , */\ + { 0x6891, "TDMSRCBCLIP"}, /* clip information (analog /digital) for source1 , */\ + { 0x6e10, "LP1"}, /* low power mode 1 detection , */\ + { 0x6e20, "LA"}, /* low amplitude detection , */\ + { 0x6e30, "VDDPH"}, /* vddp greater than vbat , */\ + { 0x6f02, "DELCURCOMP"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "SIGCURCOMP"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "ENCURCOMP"}, /* enable current sense compensation , */\ + { 0x6f72, "LVLCLPPWM"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7430, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "DCTRIP"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7534, "DCTRIP2"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "DCTRIPT"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x75f0, "DCTRIPHYSTE"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7635, "DCVOF"}, /* First boost voltage level , */\ + { 0x7695, "DCVOS"}, /* Second boost voltage level , */\ + { 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 , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 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 , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9874_BITNAMETABLE static tfaBfName_t Tfa9874BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1e0, "unprotect_faim"}, /* Control for FAIM protection , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0x802, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_bst_ocpok"}, /* DCDC OCP nmos (sticky register , clear on read) , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm (sticky register , clear on read) , */\ + { 0x1030, "flag_ocp_alarm"}, /* OCP amplifier (sticky register , clear on read), */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm (sticky register , clear on read) , */\ + { 0x1050, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x1060, "flag_tdm_error"}, /* TDM error , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock (sticky register , clear on read) , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active (sticky register , clear on read) , */\ + { 0x1120, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1130, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1140, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1150, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1160, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1170, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1180, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x1192, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11c0, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x11d0, "flag_engage"}, /* Amplifier engage , */\ + { 0x11e0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x11f0, "flag_enbl_ref"}, /* References enable , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1380, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13a0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 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 DATAO , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "tdm_source0_enable"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "tdm_sink0_slot"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_man_alarm_state"}, /* Status nanager Alarm state , */\ + { 0x4060, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_man_alarm_state"}, /* clear nanager Alarm state , */\ + { 0x4460, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_man_alarm_state"}, /* Enable nanager Alarm state , */\ + { 0x4860, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_man_alarm_state"}, /* Polarity nanager Alarm state , */\ + { 0x4c60, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x5080, "vbat_flt_limit"}, /* vbat filter limit , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass battery safeguard , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 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 , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) , */\ + { 0x5690, "sel_pwm_delay_src"}, /* Control for selection for PWM delay line source , */\ + { 0x56a1, "enbl_odd_up_even_down"}, /* Control for PWM reference sawtooth generartion , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 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 , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x6123, "ctrl_attl"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "ctrl_attr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x6265, "zero_lvl"}, /* low noise gain switch zero trigger level , */\ + { 0x62c1, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* low noise gain switch trigger level , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 zero crossing detection level , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode control , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 amplitude trigger level , */\ + { 0x6565, "lpm1_hold_time"}, /* low power mode1 detector ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtred vs unfiltered compare , */\ + { 0x6761, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x6802, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6831, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x6851, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x6871, "tdm_source0_clip_sel"}, /* clip information (analog /digital) for source0 , */\ + { 0x6891, "tdm_source1_clip_sel"}, /* clip information (analog /digital) for source1 , */\ + { 0x6a02, "rst_min_vbat_delay"}, /* rst_min_vbat delay (nb fs) , */\ + { 0x6b00, "disable_auto_engage"}, /* disable auto engange , */\ + { 0x6b10, "disable_engage"}, /* disable engange , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c69, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x6f02, "cursense_comp_delay"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "cursense_comp_sign"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "enbl_cursense_comp"}, /* enable current sense compensation , */\ + { 0x6f72, "pwms_clip_lvl"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 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 , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7360, "bst_use_new_zercur_detect"}, /* Enable new zero current detection for boost control, */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "boost_trip_lvl_1st"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Determines the maximum PWM frequency be the most efficient in relation to the Booster inductor value, */\ + { 0x7534, "boost_trip_lvl_2nd"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "boost_trip_lvl_track"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x75f0, "enbl_trip_hyst"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7635, "frst_boost_voltage"}, /* First boost voltage level , */\ + { 0x7695, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 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, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8305, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP)*signal, */\ + { 0x8364, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 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), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8790, "enbl_dc_filter"}, /* Control for enabling the DC blocking filter for voltage and current sense, */\ + { 0x8801, "volsense_pwm_sel"}, /* Voltage sense source selection control , */\ + { 0x8850, "vs_gain_control"}, /* Voltage sense gain control , */\ + { 0x8860, "vs_bypass_gc"}, /* Bypasses the VS gain correction , */\ + { 0x8870, "vs_igen_supply"}, /* Switch internal supply of current generator , */\ + { 0x8887, "vs_gain"}, /* voltage sense gain , */\ + { 0x8c00, "vs_adc_bsoinv"}, /* Bitstream inversion for voltage sense ADC , */\ + { 0x8c40, "vs_adc_nortz"}, /* Return to zero for voltage sense ADC , */\ + { 0x8c90, "vs_adc_slowdel"}, /* Select delay for voltage sense ADC (internal decision circuitry), */\ + { 0x8d10, "vs_classd_tran_skip"}, /* Skip voltage sense connection during a classD amplifier transition, */\ + { 0x8d30, "vs_inn_short"}, /* Short voltage sense negative to common mode , */\ + { 0x8d40, "vs_inp_short"}, /* Short voltage sense positive to common mode , */\ + { 0x8d50, "vs_ldo_bypass"}, /* Bypass voltage sense LDO , */\ + { 0x8d60, "vs_ldo_pulldown"}, /* Pull down voltage sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8d74, "vs_ldo_voset"}, /* Voltage sense LDO voltage level setting (two's complement), */\ + { 0x8f00, "enbl_vs_adc"}, /* Enable voltage sense ADC (Direct Control only only others done by manager), */\ + { 0x8f10, "enbl_vs_inn1"}, /* Enable connection of voltage sense negative1 , */\ + { 0x8f20, "enbl_vs_inn2"}, /* Enable connection of voltage sense negative2 , */\ + { 0x8f30, "enbl_vs_inp1"}, /* Enable connection of voltage sense positive1 , */\ + { 0x8f40, "enbl_vs_inp2"}, /* Enable connection of voltage sense positive2 , */\ + { 0x8f50, "enbl_vs_ldo"}, /* Enable voltage sense LDO (Direct Control only only others done by manager), */\ + { 0x8f80, "enbl_vs_vbatldo"}, /* Enable of voltage sense LDO (Direct Control only others done by manager), */\ + { 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, */\ + { 0xc0c0, "use_direct_vs_ctrls"}, /* voltage sense Direct control to overrule several functions for testing, */\ + { 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 , */\ + { 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 , */\ + { 0xc580, "test_enbl_vs"}, /* Enable for digimux mode of voltage 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, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to PDMDAT (see Digimux list for details), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT 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 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb53, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba3, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 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, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 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 , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd721, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd740, "bck_ehs"}, /* High-speed and standard/fast mode selection for BCK IO cell (see IIC3V3 IO cell datasheet), */\ + { 0xd750, "datai_ehs"}, /* High-speed and standard/fast mode selection for DATAI IO cell (see IIC3V3 IO cell datasheet), */\ + { 0xd800, "source_in_testmode"}, /* tdm source in test mode (return only current and voltage sense), */\ + { 0xd810, "gainatt_feedback"}, /* gainatt feedback to tdm , */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 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), */\ + { 0xf169, "spare_mpt1_15_6"}, /* SPARE , */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a5, "spare_mtp2_15_10"}, /* SPARE , */\ + { 0xf307, "calibr_gain_vs"}, /* Voltage sense gain , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf407, "spare_mtp4_15_0"}, /* SPARE , */\ + { 0xf487, "vs_trim"}, /* VS Trimming , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of speaker coil , */\ + { 0xf60f, "spare_mpt6_6_0"}, /* SPARE , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf770, "spare_mtp7_07"}, /* SPARE , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of amplifier level shifter B , */\ + { 0xf7f0, "spare_mtp7_15"}, /* SPARE , */\ + { 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, "spare_mtp9_1"}, /* SPARE , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Force Boost in follower mode , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf980, "spare_mtp9_8"}, /* SPARE , */\ + { 0xf990, "mtp_enbl_pwm_delay_clock_gating"}, /* pwm delay clock auto gating , */\ + { 0xf9a0, "mtp_enbl_ocp_clock_gating"}, /* ocpclock auto gating , */\ + { 0xf9b0, "mtp_gate_cgu_clock_for_test"}, /* cgu test clock control , */\ + { 0xf9c0, "mtp_tdm_pad_sel"}, /* tdm pad selection , */\ + { 0xf9d2, "spare_mtp9_15_12"}, /* 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) , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xff87, "spare_mtp7_15_08"}, /* SPARE , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9874_irq { + tfa9874_irq_stvdds = 0, + tfa9874_irq_stbstoc = 1, + tfa9874_irq_stotds = 2, + tfa9874_irq_stocpr = 3, + tfa9874_irq_stuvds = 4, + tfa9874_irq_stmanalarm = 5, + tfa9874_irq_sttdmer = 6, + tfa9874_irq_stnoclk = 7, + tfa9874_irq_max = 8, + tfa9874_irq_all = -1 /* all irqs */}; + +#define TFA9874_IRQ_NAMETABLE static tfaIrqName_t Tfa9874IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STBSTOC"},\ + { 2, "STOTDS"},\ + { 3, "STOCPR"},\ + { 4, "STUVDS"},\ + { 5, "STMANALARM"},\ + { 6, "STTDMER"},\ + { 7, "STNOCLK"},\ + { 8, "8"},\ +}; +#endif /* _TFA9874_TFAFIELDNAMES_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9887_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9887_tfafieldnames.h new file mode 100644 index 000000000000..515b6bc5e0fe --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9887_tfafieldnames.h @@ -0,0 +1,61 @@ +/** 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_I2CVERSION 34 +#define TFA9895_I2CVERSION 34 +#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/techpack/audio/asoc/codecs/tfa9874/tfa9890_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9890_tfafieldnames.h new file mode 100644 index 000000000000..e6f0e0228bb6 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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_I2CVERSION 34 +#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/techpack/audio/asoc/codecs/tfa9874/tfa9891_genregs.h b/techpack/audio/asoc/codecs/tfa9874/tfa9891_genregs.h new file mode 100644 index 000000000000..14f75afc7852 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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/techpack/audio/asoc/codecs/tfa9874/tfa9891_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9891_tfafieldnames.h new file mode 100644 index 000000000000..563eee830344 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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 + + +#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/techpack/audio/asoc/codecs/tfa9874/tfa9894_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9894_tfafieldnames.h new file mode 100644 index 000000000000..25c55cd8ce74 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9894_tfafieldnames.h @@ -0,0 +1,1065 @@ +/** Filename: tfa9894_tfaFieldnames.h + * This file was generated automatically on 09/06/17 at 14:51:37. + * Source file: TFA9894_N1A1_I2C_RegisterMap.xlsx + */ + +#ifndef _TFA9894_TFAFIELDNAMES_H +#define _TFA9894_TFAFIELDNAMES_H + +#define TFA9894_I2CVERSION 7 + +typedef enum nxpTfa9894BfEnumList { + TFA9894_BF_PWDN = 0x0000, /*!< Powerdown control */ + TFA9894_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9894_BF_CFE = 0x0020, /*!< Enable CoolFlux DSP */ + TFA9894_BF_AMPE = 0x0030, /*!< Enable Amplifier */ + TFA9894_BF_DCA = 0x0040, /*!< Enable DCDC Boost converter */ + TFA9894_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA9894_BF_AMPC = 0x0060, /*!< CoolFlux control over amplifier */ + TFA9894_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9894_BF_FSSSEL= 0x0090, /*!< Audio sample reference */ + TFA9894_BF_BYPOCP= 0x00a0, /*!< Bypass OCP */ + TFA9894_BF_TSTOCP= 0x00b0, /*!< OCP testing control */ + TFA9894_BF_BSSS = 0x00c0, /*!< Vbat protection steepness */ + TFA9894_BF_HPFBYP= 0x00d0, /*!< Bypass High Pass Filter */ + TFA9894_BF_DPSA = 0x00e0, /*!< Enable DPSA */ + TFA9894_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA9894_BF_MANSCONF= 0x0120, /*!< Device I2C settings configured */ + TFA9894_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA9894_BF_MANROBOD= 0x0140, /*!< Reaction on BOD */ + TFA9894_BF_BODE = 0x0150, /*!< Enable BOD (only in direct control mode) */ + TFA9894_BF_BODHYS= 0x0160, /*!< Enable Hysteresis of BOD */ + TFA9894_BF_BODFILT= 0x0171, /*!< BOD filter */ + TFA9894_BF_BODTHLVL= 0x0191, /*!< BOD threshold */ + TFA9894_BF_MUTETO= 0x01b0, /*!< Time out SB mute sequence */ + TFA9894_BF_MANWDE= 0x01c0, /*!< Watchdog enable */ + TFA9894_BF_OPENMTP= 0x01e0, /*!< Control for FAIM protection */ + TFA9894_BF_FAIMVBGOVRRL= 0x01f0, /*!< Overrule the enabling of VBG for faim erase/write access */ + TFA9894_BF_AUDFS = 0x0203, /*!< Audio sample rate Fs */ + TFA9894_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9894_BF_FRACTDEL= 0x0255, /*!< Current sense fractional delay */ + TFA9894_BF_TDMPRES= 0x02b1, /*!< Control for HW manager */ + TFA9894_BF_AMPOCRT= 0x02d2, /*!< Amplifier on-off criteria for shutdown */ + TFA9894_BF_REV = 0x030f, /*!< Revision info */ + TFA9894_BF_REFCKEXT= 0x0401, /*!< PLL external reference clock */ + TFA9894_BF_REFCKSEL= 0x0420, /*!< PLL internal reference clock */ + TFA9894_BF_MCLKSEL= 0x0432, /*!< Master Clock Selection */ + TFA9894_BF_MANAOOSC= 0x0460, /*!< Internal OSC1M off at PWDN */ + TFA9894_BF_ACKCLDDIS= 0x0470, /*!< Automatic PLL reference clock selection for cold start */ + TFA9894_BF_SPKSSEN= 0x0510, /*!< Enable speaker sub-system */ + TFA9894_BF_MTPSSEN= 0x0520, /*!< Enable FAIM sub-system */ + TFA9894_BF_WDTCLKEN= 0x0530, /*!< Enable Coolflux watchdog clock */ + TFA9894_BF_VDDS = 0x1000, /*!< POR */ + TFA9894_BF_PLLS = 0x1010, /*!< PLL Lock */ + TFA9894_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9894_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9894_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9894_BF_OCDS = 0x1050, /*!< OCP amplifier (sticky register, clear on read) */ + TFA9894_BF_CLKS = 0x1060, /*!< Clocks stable */ + TFA9894_BF_MTPB = 0x1070, /*!< MTP busy */ + TFA9894_BF_NOCLK = 0x1080, /*!< Lost clock */ + TFA9894_BF_ACS = 0x1090, /*!< Cold Start */ + TFA9894_BF_WDS = 0x10a0, /*!< Watchdog */ + TFA9894_BF_SWS = 0x10b0, /*!< Amplifier engage */ + TFA9894_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9894_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9894_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9894_BF_BODNOK= 0x10f0, /*!< BOD Flag - VDD NOT OK */ + TFA9894_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9894_BF_DCDCA = 0x1110, /*!< DCDC active (sticky register, clear on read) */ + TFA9894_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos (sticky register, clear on read) */ + TFA9894_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9894_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9894_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9894_BF_SPKS = 0x1170, /*!< Speaker status */ + TFA9894_BF_CLKOOR= 0x1180, /*!< External clock status */ + TFA9894_BF_MANALARM= 0x1190, /*!< Alarm state */ + TFA9894_BF_TDMERR= 0x11a0, /*!< TDM error */ + TFA9894_BF_TDMLUTER= 0x11b0, /*!< TDM lookup table error */ + TFA9894_BF_OCPOAP= 0x1200, /*!< OCPOK pmos A */ + TFA9894_BF_OCPOAN= 0x1210, /*!< OCPOK nmos A */ + TFA9894_BF_OCPOBP= 0x1220, /*!< OCPOK pmos B */ + TFA9894_BF_OCPOBN= 0x1230, /*!< OCPOK nmos B */ + TFA9894_BF_CLIPS = 0x1240, /*!< Amplifier clipping */ + TFA9894_BF_MANMUTE= 0x1250, /*!< Audio mute sequence */ + TFA9894_BF_MANOPER= 0x1260, /*!< Device in Operating state */ + TFA9894_BF_LP1 = 0x1270, /*!< Low power MODE1 detection */ + TFA9894_BF_LA = 0x1280, /*!< Low amplitude detection */ + TFA9894_BF_VDDPH = 0x1290, /*!< VDDP greater than VBAT flag */ + TFA9894_BF_TDMSTAT= 0x1402, /*!< TDM Status bits */ + TFA9894_BF_MANSTATE= 0x1433, /*!< Device Manager status */ + TFA9894_BF_DCMODE= 0x14b1, /*!< DCDC mode status bits */ + TFA9894_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9894_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9894_BF_VDDPS = 0x1709, /*!< IC VDDP voltage (1023*VDDP/13V) */ + TFA9894_BF_TDME = 0x2000, /*!< Enable interface */ + TFA9894_BF_TDMSPKE= 0x2010, /*!< Control audio tdm channel in sink0 */ + TFA9894_BF_TDMDCE= 0x2020, /*!< Control audio tdm channel in sink1 */ + TFA9894_BF_TDMCSE= 0x2030, /*!< Source 0 enable */ + TFA9894_BF_TDMVSE= 0x2040, /*!< Source 1 enable */ + TFA9894_BF_TDMCFE= 0x2050, /*!< Source 2 enable */ + TFA9894_BF_TDMCF2E= 0x2060, /*!< Source 3 enable */ + TFA9894_BF_TDMCLINV= 0x2070, /*!< Reception data to BCK clock */ + TFA9894_BF_TDMFSPOL= 0x2080, /*!< FS polarity */ + TFA9894_BF_TDMDEL= 0x2090, /*!< Data delay to FS */ + TFA9894_BF_TDMADJ= 0x20a0, /*!< Data adjustment */ + TFA9894_BF_TDMOOMP= 0x20b1, /*!< Received audio compression */ + TFA9894_BF_TDMNBCK= 0x2103, /*!< TDM NBCK - Bit clock to FS ratio */ + TFA9894_BF_TDMFSLN= 0x2143, /*!< FS length (master mode only) */ + TFA9894_BF_TDMSLOTS= 0x2183, /*!< N-slots in Frame */ + TFA9894_BF_TDMTXDFO= 0x21c1, /*!< Format unused bits */ + TFA9894_BF_TDMTXUS0= 0x21e1, /*!< Format unused slots DATAO */ + TFA9894_BF_TDMSLLN= 0x2204, /*!< N-bits in slot */ + TFA9894_BF_TDMBRMG= 0x2254, /*!< N-bits remaining */ + TFA9894_BF_TDMSSIZE= 0x22a4, /*!< Sample size per slot */ + TFA9894_BF_TDMSPKS= 0x2303, /*!< TDM slot for sink 0 */ + TFA9894_BF_TDMDCS= 0x2343, /*!< TDM slot for sink 1 */ + TFA9894_BF_TDMCFSEL= 0x2381, /*!< TDM Source 2 data selection */ + TFA9894_BF_TDMCF2SEL= 0x23a1, /*!< TDM Source 3 data selection */ + TFA9894_BF_TDMCSS= 0x2403, /*!< Slot Position of source 0 data */ + TFA9894_BF_TDMVSS= 0x2443, /*!< Slot Position of source 1 data */ + TFA9894_BF_TDMCFS= 0x2483, /*!< Slot Position of source 2 data */ + TFA9894_BF_TDMCF2S= 0x24c3, /*!< Slot Position of source 3 data */ + TFA9894_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9894_BF_ISTBSTOC= 0x4010, /*!< Status DCDC OCP */ + TFA9894_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9894_BF_ISTOCPR= 0x4030, /*!< Status OCP alarm */ + TFA9894_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9894_BF_ISTMANALARM= 0x4050, /*!< Status manager alarm state */ + TFA9894_BF_ISTTDMER= 0x4060, /*!< Status TDM error */ + TFA9894_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9894_BF_ISTCFMER= 0x4080, /*!< Status cfma error */ + TFA9894_BF_ISTCFMAC= 0x4090, /*!< Status cfma ack */ + TFA9894_BF_ISTSPKS= 0x40a0, /*!< Status coolflux speaker error */ + TFA9894_BF_ISTACS= 0x40b0, /*!< Status cold started */ + TFA9894_BF_ISTWDS= 0x40c0, /*!< Status watchdog reset */ + TFA9894_BF_ISTBODNOK= 0x40d0, /*!< Status brown out detect */ + TFA9894_BF_ISTLP1= 0x40e0, /*!< Status low power mode1 detect */ + TFA9894_BF_ISTCLKOOR= 0x40f0, /*!< Status clock out of range */ + TFA9894_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9894_BF_ICLBSTOC= 0x4410, /*!< Clear DCDC OCP */ + TFA9894_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9894_BF_ICLOCPR= 0x4430, /*!< Clear OCP alarm */ + TFA9894_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9894_BF_ICLMANALARM= 0x4450, /*!< Clear manager alarm state */ + TFA9894_BF_ICLTDMER= 0x4460, /*!< Clear TDM error */ + TFA9894_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9894_BF_ICLCFMER= 0x4480, /*!< Clear cfma err */ + TFA9894_BF_ICLCFMAC= 0x4490, /*!< Clear cfma ack */ + TFA9894_BF_ICLSPKS= 0x44a0, /*!< Clear coolflux speaker error */ + TFA9894_BF_ICLACS= 0x44b0, /*!< Clear cold started */ + TFA9894_BF_ICLWDS= 0x44c0, /*!< Clear watchdog reset */ + TFA9894_BF_ICLBODNOK= 0x44d0, /*!< Clear brown out detect */ + TFA9894_BF_ICLLP1= 0x44e0, /*!< Clear low power mode1 detect */ + TFA9894_BF_ICLCLKOOR= 0x44f0, /*!< Clear clock out of range */ + TFA9894_BF_IEVDDS= 0x4800, /*!< Enable POR */ + TFA9894_BF_IEBSTOC= 0x4810, /*!< Enable DCDC OCP */ + TFA9894_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9894_BF_IEOCPR= 0x4830, /*!< Enable OCP alarm */ + TFA9894_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9894_BF_IEMANALARM= 0x4850, /*!< Enable Manager Alarm state */ + TFA9894_BF_IETDMER= 0x4860, /*!< Enable TDM error */ + TFA9894_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9894_BF_IECFMER= 0x4880, /*!< Enable cfma err */ + TFA9894_BF_IECFMAC= 0x4890, /*!< Enable cfma ack */ + TFA9894_BF_IESPKS= 0x48a0, /*!< Enable coolflux speaker error */ + TFA9894_BF_IEACS = 0x48b0, /*!< Enable cold started */ + TFA9894_BF_IEWDS = 0x48c0, /*!< Enable watchdog reset */ + TFA9894_BF_IEBODNOK= 0x48d0, /*!< Enable brown out detect */ + TFA9894_BF_IELP1 = 0x48e0, /*!< Enable low power mode1 detect */ + TFA9894_BF_IECLKOOR= 0x48f0, /*!< Enable clock out of range */ + TFA9894_BF_IPOVDDS= 0x4c00, /*!< Polarity POR */ + TFA9894_BF_IPOBSTOC= 0x4c10, /*!< Polarity DCDC OCP */ + TFA9894_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9894_BF_IPOOCPR= 0x4c30, /*!< Polarity ocp alarm */ + TFA9894_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9894_BF_IPOMANALARM= 0x4c50, /*!< Polarity manager alarm state */ + TFA9894_BF_IPOTDMER= 0x4c60, /*!< Polarity TDM error */ + TFA9894_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9894_BF_IPOCFMER= 0x4c80, /*!< Polarity cfma err */ + TFA9894_BF_IPOCFMAC= 0x4c90, /*!< Polarity cfma ack */ + TFA9894_BF_IPOSPKS= 0x4ca0, /*!< Polarity coolflux speaker error */ + TFA9894_BF_IPOACS= 0x4cb0, /*!< Polarity cold started */ + TFA9894_BF_IPOWDS= 0x4cc0, /*!< Polarity watchdog reset */ + TFA9894_BF_IPOBODNOK= 0x4cd0, /*!< Polarity brown out detect */ + TFA9894_BF_IPOLP1= 0x4ce0, /*!< Polarity low power mode1 detect */ + TFA9894_BF_IPOCLKOOR= 0x4cf0, /*!< Polarity clock out of range */ + TFA9894_BF_BSSCR = 0x5001, /*!< Battery safeguard attack time */ + TFA9894_BF_BSST = 0x5023, /*!< Battery safeguard threshold voltage level */ + TFA9894_BF_BSSRL = 0x5061, /*!< Battery safeguard maximum reduction */ + TFA9894_BF_BSSRR = 0x5082, /*!< Battery safeguard release time */ + TFA9894_BF_BSSHY = 0x50b1, /*!< Battery Safeguard hysteresis */ + TFA9894_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9894_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA9894_BF_CFSM = 0x5130, /*!< Coolflux firmware soft mute control */ + TFA9894_BF_VOL = 0x5187, /*!< CF firmware volume control */ + TFA9894_BF_CLIPCTRL= 0x5202, /*!< Clip control setting */ + TFA9894_BF_SLOPEE= 0x5230, /*!< Enables slope control */ + TFA9894_BF_SLOPESET= 0x5240, /*!< Slope speed setting (binary coded) */ + TFA9894_BF_AMPGAIN= 0x5287, /*!< Amplifier gain */ + TFA9894_BF_TDMDCG= 0x5703, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9894_BF_TDMSPKG= 0x5743, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9894_BF_DCINSEL= 0x5781, /*!< VAMP_OUT2 input selection */ + TFA9894_BF_LNMODE= 0x5881, /*!< Low noise gain mode control */ + TFA9894_BF_LPM1MODE= 0x5ac1, /*!< Low power mode control */ + TFA9894_BF_TDMSRCMAP= 0x5d02, /*!< TDM source mapping */ + TFA9894_BF_TDMSRCAS= 0x5d31, /*!< Sensed value A */ + TFA9894_BF_TDMSRCBS= 0x5d51, /*!< Sensed value B */ + TFA9894_BF_TDMSRCACLIP= 0x5d71, /*!< Clip information (analog /digital) for source0 */ + TFA9894_BF_TDMSRCBCLIP= 0x5d91, /*!< Clip information (analog /digital) for source1 */ + TFA9894_BF_DELCURCOMP= 0x6102, /*!< Delay to allign compensation signal with current sense signal */ + TFA9894_BF_SIGCURCOMP= 0x6130, /*!< Polarity of compensation for current sense */ + TFA9894_BF_ENCURCOMP= 0x6140, /*!< Enable current sense compensation */ + TFA9894_BF_LVLCLPPWM= 0x6152, /*!< Set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9894_BF_DCVOF = 0x7005, /*!< First Boost Voltage Level */ + TFA9894_BF_DCVOS = 0x7065, /*!< Second Boost Voltage Level */ + TFA9894_BF_DCMCC = 0x70c3, /*!< Max Coil Current */ + TFA9894_BF_DCCV = 0x7101, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9894_BF_DCIE = 0x7120, /*!< Adaptive boost mode */ + TFA9894_BF_DCSR = 0x7130, /*!< Soft ramp up/down */ + TFA9894_BF_DCDIS = 0x7140, /*!< DCDC on/off */ + TFA9894_BF_DCPWM = 0x7150, /*!< DCDC PWM only mode */ + TFA9894_BF_DCTRACK= 0x7160, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_DCTRIP= 0x7204, /*!< 1st adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9894_BF_DCTRIP2= 0x7254, /*!< 2nd adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9894_BF_DCTRIPT= 0x72a4, /*!< Track adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_DCTRIPHYSTE= 0x72f0, /*!< Enable hysteresis on booster trip levels */ + TFA9894_BF_DCHOLD= 0x7304, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_RST = 0x9000, /*!< Reset for Coolflux DSP */ + TFA9894_BF_DMEM = 0x9011, /*!< Target memory for CFMA using I2C interface */ + TFA9894_BF_AIF = 0x9030, /*!< Auto increment */ + TFA9894_BF_CFINT = 0x9040, /*!< Coolflux Interrupt - auto clear */ + TFA9894_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA9894_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA9894_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA9894_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA9894_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA9894_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA9894_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA9894_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA9894_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA9894_BF_MADD = 0x910f, /*!< CF memory address */ + TFA9894_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA9894_BF_ERR = 0x9307, /*!< CF error flags */ + TFA9894_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA9894_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA9894_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA9894_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA9894_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA9894_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA9894_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA9894_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA9894_BF_MTPK = 0xa107, /*!< KEY2 to access KEY2 protected registers, customer key */ + TFA9894_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9894_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9894_BF_CMTPI = 0xa350, /*!< Start copying all the data from mtp to I2C mtp registers - auto clear */ + TFA9894_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp - auto clear */ + TFA9894_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9894_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9894_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9894_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9894_BF_SWPROFIL= 0xe00f, /*!< Software profile data */ + TFA9894_BF_SWVSTEP= 0xe10f, /*!< Software vstep information */ + TFA9894_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9894_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9894_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9894_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9894_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9894_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9894_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9894BfEnumList_t; +#define TFA9894_NAMETABLE static tfaBfName_t Tfa9894DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown control , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux DSP , */\ + { 0x30, "AMPE"}, /* Enable Amplifier , */\ + { 0x40, "DCA"}, /* Enable DCDC Boost converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux control over amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x90, "FSSSEL"}, /* Audio sample reference , */\ + { 0xa0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xb0, "TSTOCP"}, /* OCP testing control , */\ + { 0xc0, "BSSS"}, /* Vbat protection steepness , */\ + { 0xd0, "HPFBYP"}, /* Bypass High Pass Filter , */\ + { 0xe0, "DPSA"}, /* Enable DPSA , */\ + { 0x101, "AMPINSEL"}, /* Amplifier input selection , */\ + { 0x120, "MANSCONF"}, /* Device I2C settings configured , */\ + { 0x130, "MANCOLD"}, /* Execute cold start , */\ + { 0x140, "MANROBOD"}, /* Reaction on BOD , */\ + { 0x150, "BODE"}, /* Enable BOD (only in direct control mode) , */\ + { 0x160, "BODHYS"}, /* Enable Hysteresis of BOD , */\ + { 0x171, "BODFILT"}, /* BOD filter , */\ + { 0x191, "BODTHLVL"}, /* BOD threshold , */\ + { 0x1b0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1c0, "MANWDE"}, /* Watchdog enable , */\ + { 0x1e0, "OPENMTP"}, /* Control for FAIM protection , */\ + { 0x1f0, "FAIMVBGOVRRL"}, /* Overrule the enabling of VBG for faim erase/write access, */\ + { 0x203, "AUDFS"}, /* Audio sample rate Fs , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* Current sense fractional delay , */\ + { 0x2b1, "TDMPRES"}, /* Control for HW manager , */\ + { 0x2d2, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external reference clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal reference clock , */\ + { 0x432, "MCLKSEL"}, /* Master Clock Selection , */\ + { 0x460, "MANAOOSC"}, /* Internal OSC1M off at PWDN , */\ + { 0x470, "ACKCLDDIS"}, /* Automatic PLL reference clock selection for cold start, */\ + { 0x510, "SPKSSEN"}, /* Enable speaker sub-system , */\ + { 0x520, "MTPSSEN"}, /* Enable FAIM sub-system , */\ + { 0x530, "WDTCLKEN"}, /* Enable Coolflux watchdog clock , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL Lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "OCDS"}, /* OCP amplifier (sticky register, clear on read) , */\ + { 0x1060, "CLKS"}, /* Clocks stable , */\ + { 0x1070, "MTPB"}, /* MTP busy , */\ + { 0x1080, "NOCLK"}, /* Lost clock , */\ + { 0x1090, "ACS"}, /* Cold Start , */\ + { 0x10a0, "WDS"}, /* Watchdog , */\ + { 0x10b0, "SWS"}, /* Amplifier engage , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x10f0, "BODNOK"}, /* BOD Flag - VDD NOT OK , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active (sticky register, clear on read) , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos (sticky register, clear on read) , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "SPKS"}, /* Speaker status , */\ + { 0x1180, "CLKOOR"}, /* External clock status , */\ + { 0x1190, "MANALARM"}, /* Alarm state , */\ + { 0x11a0, "TDMERR"}, /* TDM error , */\ + { 0x11b0, "TDMLUTER"}, /* TDM lookup table error , */\ + { 0x1200, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1210, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1220, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1230, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1240, "CLIPS"}, /* Amplifier clipping , */\ + { 0x1250, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x1260, "MANOPER"}, /* Device in Operating state , */\ + { 0x1270, "LP1"}, /* Low power MODE1 detection , */\ + { 0x1280, "LA"}, /* Low amplitude detection , */\ + { 0x1290, "VDDPH"}, /* VDDP greater than VBAT flag , */\ + { 0x1402, "TDMSTAT"}, /* TDM Status bits , */\ + { 0x1433, "MANSTATE"}, /* Device Manager status , */\ + { 0x14b1, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage (1023*VDDP/13V) , */\ + { 0x2000, "TDME"}, /* Enable interface , */\ + { 0x2010, "TDMSPKE"}, /* Control audio tdm channel in sink0 , */\ + { 0x2020, "TDMDCE"}, /* Control audio tdm channel in sink1 , */\ + { 0x2030, "TDMCSE"}, /* Source 0 enable , */\ + { 0x2040, "TDMVSE"}, /* Source 1 enable , */\ + { 0x2050, "TDMCFE"}, /* Source 2 enable , */\ + { 0x2060, "TDMCF2E"}, /* Source 3 enable , */\ + { 0x2070, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2080, "TDMFSPOL"}, /* FS polarity , */\ + { 0x2090, "TDMDEL"}, /* Data delay to FS , */\ + { 0x20a0, "TDMADJ"}, /* Data adjustment , */\ + { 0x20b1, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2103, "TDMNBCK"}, /* TDM NBCK - Bit clock to FS ratio , */\ + { 0x2143, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x2183, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x21c1, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x21e1, "TDMTXUS0"}, /* Format unused slots DATAO , */\ + { 0x2204, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2254, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x22a4, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2303, "TDMSPKS"}, /* TDM slot for sink 0 , */\ + { 0x2343, "TDMDCS"}, /* TDM slot for sink 1 , */\ + { 0x2381, "TDMCFSEL"}, /* TDM Source 2 data selection , */\ + { 0x23a1, "TDMCF2SEL"}, /* TDM Source 3 data selection , */\ + { 0x2403, "TDMCSS"}, /* Slot Position of source 0 data , */\ + { 0x2443, "TDMVSS"}, /* Slot Position of source 1 data , */\ + { 0x2483, "TDMCFS"}, /* Slot Position of source 2 data , */\ + { 0x24c3, "TDMCF2S"}, /* Slot Position of source 3 data , */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOCPR"}, /* Status OCP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTMANALARM"}, /* Status manager alarm state , */\ + { 0x4060, "ISTTDMER"}, /* Status TDM error , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4080, "ISTCFMER"}, /* Status cfma error , */\ + { 0x4090, "ISTCFMAC"}, /* Status cfma ack , */\ + { 0x40a0, "ISTSPKS"}, /* Status coolflux speaker error , */\ + { 0x40b0, "ISTACS"}, /* Status cold started , */\ + { 0x40c0, "ISTWDS"}, /* Status watchdog reset , */\ + { 0x40d0, "ISTBODNOK"}, /* Status brown out detect , */\ + { 0x40e0, "ISTLP1"}, /* Status low power mode1 detect , */\ + { 0x40f0, "ISTCLKOOR"}, /* Status clock out of range , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOCPR"}, /* Clear OCP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLMANALARM"}, /* Clear manager alarm state , */\ + { 0x4460, "ICLTDMER"}, /* Clear TDM error , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4480, "ICLCFMER"}, /* Clear cfma err , */\ + { 0x4490, "ICLCFMAC"}, /* Clear cfma ack , */\ + { 0x44a0, "ICLSPKS"}, /* Clear coolflux speaker error , */\ + { 0x44b0, "ICLACS"}, /* Clear cold started , */\ + { 0x44c0, "ICLWDS"}, /* Clear watchdog reset , */\ + { 0x44d0, "ICLBODNOK"}, /* Clear brown out detect , */\ + { 0x44e0, "ICLLP1"}, /* Clear low power mode1 detect , */\ + { 0x44f0, "ICLCLKOOR"}, /* Clear clock out of range , */\ + { 0x4800, "IEVDDS"}, /* Enable POR , */\ + { 0x4810, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOCPR"}, /* Enable OCP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IEMANALARM"}, /* Enable Manager Alarm state , */\ + { 0x4860, "IETDMER"}, /* Enable TDM error , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4880, "IECFMER"}, /* Enable cfma err , */\ + { 0x4890, "IECFMAC"}, /* Enable cfma ack , */\ + { 0x48a0, "IESPKS"}, /* Enable coolflux speaker error , */\ + { 0x48b0, "IEACS"}, /* Enable cold started , */\ + { 0x48c0, "IEWDS"}, /* Enable watchdog reset , */\ + { 0x48d0, "IEBODNOK"}, /* Enable brown out detect , */\ + { 0x48e0, "IELP1"}, /* Enable low power mode1 detect , */\ + { 0x48f0, "IECLKOOR"}, /* Enable clock out of range , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity POR , */\ + { 0x4c10, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOMANALARM"}, /* Polarity manager alarm state , */\ + { 0x4c60, "IPOTDMER"}, /* Polarity TDM error , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4c80, "IPOCFMER"}, /* Polarity cfma err , */\ + { 0x4c90, "IPOCFMAC"}, /* Polarity cfma ack , */\ + { 0x4ca0, "IPOSPKS"}, /* Polarity coolflux speaker error , */\ + { 0x4cb0, "IPOACS"}, /* Polarity cold started , */\ + { 0x4cc0, "IPOWDS"}, /* Polarity watchdog reset , */\ + { 0x4cd0, "IPOBODNOK"}, /* Polarity brown out detect , */\ + { 0x4ce0, "IPOLP1"}, /* Polarity low power mode1 detect , */\ + { 0x4cf0, "IPOCLKOOR"}, /* Polarity clock out of range , */\ + { 0x5001, "BSSCR"}, /* Battery safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery safeguard maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery safeguard release time , */\ + { 0x50b1, "BSSHY"}, /* Battery Safeguard hysteresis , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass HW clipper , */\ + { 0x5130, "CFSM"}, /* Coolflux firmware soft mute control , */\ + { 0x5187, "VOL"}, /* CF firmware volume control , */\ + { 0x5202, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5230, "SLOPEE"}, /* Enables slope control , */\ + { 0x5240, "SLOPESET"}, /* Slope speed setting (binary coded) , */\ + { 0x5287, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x5703, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x5743, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x5781, "DCINSEL"}, /* VAMP_OUT2 input selection , */\ + { 0x5881, "LNMODE"}, /* Low noise gain mode control , */\ + { 0x5ac1, "LPM1MODE"}, /* Low power mode control , */\ + { 0x5d02, "TDMSRCMAP"}, /* TDM source mapping , */\ + { 0x5d31, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x5d51, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x5d71, "TDMSRCACLIP"}, /* Clip information (analog /digital) for source0 , */\ + { 0x5d91, "TDMSRCBCLIP"}, /* Clip information (analog /digital) for source1 , */\ + { 0x6102, "DELCURCOMP"}, /* Delay to allign compensation signal with current sense signal, */\ + { 0x6130, "SIGCURCOMP"}, /* Polarity of compensation for current sense , */\ + { 0x6140, "ENCURCOMP"}, /* Enable current sense compensation , */\ + { 0x6152, "LVLCLPPWM"}, /* Set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7005, "DCVOF"}, /* First Boost Voltage Level , */\ + { 0x7065, "DCVOS"}, /* Second Boost Voltage Level , */\ + { 0x70c3, "DCMCC"}, /* Max Coil Current , */\ + { 0x7101, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7120, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7130, "DCSR"}, /* Soft ramp up/down , */\ + { 0x7140, "DCDIS"}, /* DCDC on/off , */\ + { 0x7150, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7160, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7204, "DCTRIP"}, /* 1st adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7254, "DCTRIP2"}, /* 2nd adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x72a4, "DCTRIPT"}, /* Track adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x72f0, "DCTRIPHYSTE"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7304, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x9000, "RST"}, /* Reset for Coolflux DSP , */\ + { 0x9011, "DMEM"}, /* Target memory for CFMA using I2C interface , */\ + { 0x9030, "AIF"}, /* Auto increment , */\ + { 0x9040, "CFINT"}, /* Coolflux 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"}, /* CF memory address , */\ + { 0x920f, "MEMA"}, /* Activate memory access , */\ + { 0x9307, "ERR"}, /* CF error flags , */\ + { 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"}, /* KEY2 to access KEY2 protected registers, customer key, */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa350, "CMTPI"}, /* Start copying all the data from mtp to I2C mtp registers - auto clear, */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp - auto clear, */\ + { 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 , */\ + { 0xe00f, "SWPROFIL"}, /* Software profile data , */\ + { 0xe10f, "SWVSTEP"}, /* Software vstep information , */\ + { 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 , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9894_BITNAMETABLE static tfaBfName_t Tfa9894BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown control , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux DSP , */\ + { 0x30, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x40, "enbl_boost"}, /* Enable DCDC Boost converter , */\ + { 0x50, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x60, "sel_enbl_amplifier"}, /* CoolFlux control over amplifier , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0x90, "fs_pulse_sel"}, /* Audio sample reference , */\ + { 0xa0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xb0, "test_ocp"}, /* OCP testing control , */\ + { 0xc0, "batsense_steepness"}, /* Vbat protection steepness , */\ + { 0xd0, "bypass_hp"}, /* Bypass High Pass Filter , */\ + { 0xe0, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0xf0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x101, "vamp_sel1"}, /* Amplifier input selection , */\ + { 0x120, "src_set_configured"}, /* Device I2C settings configured , */\ + { 0x130, "execute_cold_start"}, /* Execute cold start , */\ + { 0x140, "man_enbl_brown_out"}, /* Reaction on BOD , */\ + { 0x150, "bod_enbl"}, /* Enable BOD (only in direct control mode) , */\ + { 0x160, "bod_hyst_enbl"}, /* Enable Hysteresis of BOD , */\ + { 0x171, "bod_delay_set"}, /* BOD filter , */\ + { 0x191, "bod_lvl_set"}, /* BOD threshold , */\ + { 0x1b0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1c0, "man_enbl_watchdog"}, /* Watchdog enable , */\ + { 0x1d0, "disable_engage"}, /* Disable Engage , */\ + { 0x1e0, "unprotect_faim"}, /* Control for FAIM protection , */\ + { 0x1f0, "faim_enable_vbg"}, /* Overrule the enabling of VBG for faim erase/write access, */\ + { 0x203, "audio_fs"}, /* Audio sample rate Fs , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* Current sense fractional delay , */\ + { 0x2b1, "use_tdm_presence"}, /* Control for HW manager , */\ + { 0x2d2, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external reference clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal reference clock , */\ + { 0x432, "mclk_sel"}, /* Master Clock Selection , */\ + { 0x460, "enbl_osc1m_auto_off"}, /* Internal OSC1M off at PWDN , */\ + { 0x470, "disable_auto_sel_refclk"}, /* Automatic PLL reference clock selection for cold start, */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker sub-system , */\ + { 0x520, "enbl_faim_ss"}, /* Enable FAIM sub-system , */\ + { 0x530, "enbl_wdt_clk"}, /* Enable Coolflux watchdog clock , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* Hidden code to enable access to hidden register. (0x5A6B/23147 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_ocp_alarm"}, /* OCP amplifier (sticky register, clear on read) , */\ + { 0x1060, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1070, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1080, "flag_lost_clk"}, /* Lost clock , */\ + { 0x1090, "flag_cold_started"}, /* Cold Start , */\ + { 0x10a0, "flag_watchdog_reset"}, /* Watchdog , */\ + { 0x10b0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x10f0, "flag_bod_vddd_nok"}, /* BOD Flag - VDD NOT OK , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active (sticky register, clear on read) , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos (sticky register, clear on read) , */\ + { 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_cf_speakererror"}, /* Speaker status , */\ + { 0x1180, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1190, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x11a0, "flag_tdm_error"}, /* TDM error , */\ + { 0x11b0, "flag_tdm_lut_error"}, /* TDM lookup table error , */\ + { 0x1200, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1210, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1220, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1230, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1240, "flag_clip"}, /* Amplifier clipping , */\ + { 0x1250, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x1260, "flag_man_operating_state"}, /* Device in Operating state , */\ + { 0x1270, "flag_lp_detect_mode1"}, /* Low power MODE1 detection , */\ + { 0x1280, "flag_low_amplitude"}, /* Low amplitude detection , */\ + { 0x1290, "flag_vddp_gt_vbat"}, /* VDDP greater than VBAT flag , */\ + { 0x1402, "tdm_status"}, /* TDM Status bits , */\ + { 0x1433, "man_state"}, /* Device Manager status , */\ + { 0x1473, "amp_ctrl_state"}, /* Amplifier control status , */\ + { 0x14b1, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage (1023*VDDP/13V) , */\ + { 0x2000, "tdm_enable"}, /* Enable interface , */\ + { 0x2010, "tdm_sink0_enable"}, /* Control audio tdm channel in sink0 , */\ + { 0x2020, "tdm_sink1_enable"}, /* Control audio tdm channel in sink1 , */\ + { 0x2030, "tdm_source0_enable"}, /* Source 0 enable , */\ + { 0x2040, "tdm_source1_enable"}, /* Source 1 enable , */\ + { 0x2050, "tdm_source2_enable"}, /* Source 2 enable , */\ + { 0x2060, "tdm_source3_enable"}, /* Source 3 enable , */\ + { 0x2070, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2080, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x2090, "tdm_data_delay"}, /* Data delay to FS , */\ + { 0x20a0, "tdm_data_adjustment"}, /* Data adjustment , */\ + { 0x20b1, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2103, "tdm_nbck"}, /* TDM NBCK - Bit clock to FS ratio , */\ + { 0x2143, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x2183, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x21c1, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x21e1, "tdm_txdata_format_unused_slot"}, /* Format unused slots DATAO , */\ + { 0x2204, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2254, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x22a4, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2303, "tdm_sink0_slot"}, /* TDM slot for sink 0 , */\ + { 0x2343, "tdm_sink1_slot"}, /* TDM slot for sink 1 , */\ + { 0x2381, "tdm_source2_sel"}, /* TDM Source 2 data selection , */\ + { 0x23a1, "tdm_source3_sel"}, /* TDM Source 3 data selection , */\ + { 0x2403, "tdm_source0_slot"}, /* Slot Position of source 0 data , */\ + { 0x2443, "tdm_source1_slot"}, /* Slot Position of source 1 data , */\ + { 0x2483, "tdm_source2_slot"}, /* Slot Position of source 2 data , */\ + { 0x24c3, "tdm_source3_slot"}, /* Slot Position of source 3 data , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ocp_alarm"}, /* Status OCP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_man_alarm_state"}, /* Status manager alarm state , */\ + { 0x4060, "int_out_flag_tdm_error"}, /* Status TDM error , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4080, "int_out_flag_cfma_err"}, /* Status cfma error , */\ + { 0x4090, "int_out_flag_cfma_ack"}, /* Status cfma ack , */\ + { 0x40a0, "int_out_flag_cf_speakererror"}, /* Status coolflux speaker error , */\ + { 0x40b0, "int_out_flag_cold_started"}, /* Status cold started , */\ + { 0x40c0, "int_out_flag_watchdog_reset"}, /* Status watchdog reset , */\ + { 0x40d0, "int_out_flag_bod_vddd_nok"}, /* Status brown out detect , */\ + { 0x40e0, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 detect , */\ + { 0x40f0, "int_out_flag_clk_out_of_range"}, /* Status clock out of range , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ocp_alarm"}, /* Clear OCP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_man_alarm_state"}, /* Clear manager alarm state , */\ + { 0x4460, "int_in_flag_tdm_error"}, /* Clear TDM error , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4480, "int_in_flag_cfma_err"}, /* Clear cfma err , */\ + { 0x4490, "int_in_flag_cfma_ack"}, /* Clear cfma ack , */\ + { 0x44a0, "int_in_flag_cf_speakererror"}, /* Clear coolflux speaker error , */\ + { 0x44b0, "int_in_flag_cold_started"}, /* Clear cold started , */\ + { 0x44c0, "int_in_flag_watchdog_reset"}, /* Clear watchdog reset , */\ + { 0x44d0, "int_in_flag_bod_vddd_nok"}, /* Clear brown out detect , */\ + { 0x44e0, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 detect , */\ + { 0x44f0, "int_in_flag_clk_out_of_range"}, /* Clear clock out of range , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable POR , */\ + { 0x4810, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ocp_alarm"}, /* Enable OCP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_man_alarm_state"}, /* Enable Manager Alarm state , */\ + { 0x4860, "int_enable_flag_tdm_error"}, /* Enable TDM error , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4880, "int_enable_flag_cfma_err"}, /* Enable cfma err , */\ + { 0x4890, "int_enable_flag_cfma_ack"}, /* Enable cfma ack , */\ + { 0x48a0, "int_enable_flag_cf_speakererror"}, /* Enable coolflux speaker error , */\ + { 0x48b0, "int_enable_flag_cold_started"}, /* Enable cold started , */\ + { 0x48c0, "int_enable_flag_watchdog_reset"}, /* Enable watchdog reset , */\ + { 0x48d0, "int_enable_flag_bod_vddd_nok"}, /* Enable brown out detect , */\ + { 0x48e0, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 detect , */\ + { 0x48f0, "int_enable_flag_clk_out_of_range"}, /* Enable clock out of range , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity POR , */\ + { 0x4c10, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_man_alarm_state"}, /* Polarity manager alarm state , */\ + { 0x4c60, "int_polarity_flag_tdm_error"}, /* Polarity TDM error , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4c80, "int_polarity_flag_cfma_err"}, /* Polarity cfma err , */\ + { 0x4c90, "int_polarity_flag_cfma_ack"}, /* Polarity cfma ack , */\ + { 0x4ca0, "int_polarity_flag_cf_speakererror"}, /* Polarity coolflux speaker error , */\ + { 0x4cb0, "int_polarity_flag_cold_started"}, /* Polarity cold started , */\ + { 0x4cc0, "int_polarity_flag_watchdog_reset"}, /* Polarity watchdog reset , */\ + { 0x4cd0, "int_polarity_flag_bod_vddd_nok"}, /* Polarity brown out detect , */\ + { 0x4ce0, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 detect , */\ + { 0x4cf0, "int_polarity_flag_clk_out_of_range"}, /* Polarity clock out of range , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery safeguard maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery safeguard release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery Safeguard hysteresis , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass HW clipper , */\ + { 0x5130, "cf_mute"}, /* Coolflux firmware soft mute control , */\ + { 0x5187, "cf_volume"}, /* CF firmware volume control , */\ + { 0x5202, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5230, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x5240, "ctrl_slope"}, /* Slope speed setting (binary coded) , */\ + { 0x5287, "gain"}, /* Amplifier gain , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for battery safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5360, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5370, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5380, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5390, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x53a3, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5400, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5413, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5452, "dpsa_drive"}, /* Drive setting (binary coded) , */\ + { 0x550a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x55b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x55c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5600, "pwm_shape"}, /* PWM shape , */\ + { 0x5614, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5660, "reclock_pwm"}, /* Reclock the PWM signal inside analog , */\ + { 0x5670, "reclock_voltsense"}, /* Reclock the voltage sense PWM signal , */\ + { 0x5680, "enbl_pwm_phase_shift"}, /* Control for PWM phase shift , */\ + { 0x5690, "sel_pwm_delay_src"}, /* Control for selection for PWM delay line source , */\ + { 0x56a1, "enbl_odd_up_even_down"}, /* Control for PWM reference sawtooth generartion , */\ + { 0x5703, "ctrl_att_dcdc"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x5743, "ctrl_att_spkr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x5781, "vamp_sel2"}, /* VAMP_OUT2 input selection , */\ + { 0x5805, "zero_lvl"}, /* Low noise gain switch zero trigger level , */\ + { 0x5861, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x5881, "lownoisegain_mode"}, /* Low noise gain mode control , */\ + { 0x5905, "threshold_lvl"}, /* Low noise gain switch trigger level , */\ + { 0x5965, "hold_time"}, /* Low noise mode hold time before entering into low noise mode, */\ + { 0x5a05, "lpm1_cal_offset"}, /* Low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x5a65, "lpm1_zero_lvl"}, /* Low power mode1 zero crossing detection level , */\ + { 0x5ac1, "lpm1_mode"}, /* Low power mode control , */\ + { 0x5b05, "lpm1_threshold_lvl"}, /* Low power mode1 amplitude trigger level , */\ + { 0x5b65, "lpm1_hold_time"}, /* Low power mode hold time before entering into low power mode, */\ + { 0x5bc0, "disable_low_power_mode"}, /* Low power mode1 detector control , */\ + { 0x5c00, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x5c13, "vth_vddpvbat"}, /* Select vddp-vbat threshold signal , */\ + { 0x5c50, "lpen_vddpvbat"}, /* Select vddp-vbat filtred vs unfiltered compare , */\ + { 0x5c61, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x5d02, "tdm_source_mapping"}, /* TDM source mapping , */\ + { 0x5d31, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x5d51, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x5d71, "tdm_source0_clip_sel"}, /* Clip information (analog /digital) for source0 , */\ + { 0x5d91, "tdm_source1_clip_sel"}, /* Clip information (analog /digital) for source1 , */\ + { 0x5e02, "rst_min_vbat_delay"}, /* Delay for reseting the min_vbat value inside HW Clipper (number of Fs pulses), */\ + { 0x5e30, "rst_min_vbat_sel"}, /* Control for selecting reset signal for min_bat , */\ + { 0x5f00, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5f12, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x5f42, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x5f78, "spare_out"}, /* Spare out register , */\ + { 0x600f, "spare_in"}, /* Spare IN , */\ + { 0x6102, "cursense_comp_delay"}, /* Delay to allign compensation signal with current sense signal, */\ + { 0x6130, "cursense_comp_sign"}, /* Polarity of compensation for current sense , */\ + { 0x6140, "enbl_cursense_comp"}, /* Enable current sense compensation , */\ + { 0x6152, "pwms_clip_lvl"}, /* Set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7005, "frst_boost_voltage"}, /* First Boost Voltage Level , */\ + { 0x7065, "scnd_boost_voltage"}, /* Second Boost Voltage Level , */\ + { 0x70c3, "boost_cur"}, /* Max Coil Current , */\ + { 0x7101, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7120, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7130, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x7140, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x7150, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7160, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7170, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x7180, "ignore_flag_voutcomp86"}, /* Determines the maximum PWM frequency be the most efficient in relation to the Booster inductor value, */\ + { 0x7204, "boost_trip_lvl_1st"}, /* 1st adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7254, "boost_trip_lvl_2nd"}, /* 2nd adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x72a4, "boost_trip_lvl_track"}, /* Track adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x72f0, "enbl_trip_hyst"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7304, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7350, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x7361, "dcdc_ctrl_maxzercnt"}, /* Number of zero current flags to count before going to pfm mode, */\ + { 0x7386, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x73f0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x7404, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7451, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7474, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x74c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x74e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x74f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7500, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7510, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7520, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7530, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7540, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7550, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7560, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7570, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7580, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x7595, "bst_windac"}, /* For testing direct control windac , */\ + { 0x7600, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7611, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7631, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7650, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7660, "bst_use_new_zercur_detect"}, /* Enable new zero current detection for boost 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_gain_control"}, /* Current sense gain control , */\ + { 0x8050, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8060, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8105, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP) * signal, */\ + { 0x8164, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 0x81b0, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x81c0, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x81d0, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x81e0, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x81f0, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8200, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8210, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8220, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8231, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8250, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8263, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x82a0, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x82b4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8300, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8310, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8320, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8330, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8340, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8350, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if enbl_cs_ldo is high, */\ + { 0x8364, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8800, "ctrl_vs_igen_supply"}, /* Control for selecting supply for VS current generator, */\ + { 0x8810, "ctrl_vs_force_div2"}, /* Select input resistive divider gain , */\ + { 0x8820, "enbl_dc_filter"}, /* Control for enabling the DC blocking filter for voltage and current sense, */\ + { 0x8901, "volsense_pwm_sel"}, /* Voltage sense source selection control , */\ + { 0x8920, "vs_gain_control"}, /* Voltage sense gain control , */\ + { 0x8930, "vs_bypass_gc"}, /* Bypasses the VS gain correction , */\ + { 0x8940, "vs_adc_bsoinv"}, /* Bitstream inversion for voltage sense ADC , */\ + { 0x8950, "vs_adc_nortz"}, /* Return to zero for voltage sense ADC , */\ + { 0x8960, "vs_adc_slowdel"}, /* Select delay for voltage sense ADC (internal decision circuitry), */\ + { 0x8970, "vs_classd_tran_skip"}, /* Skip voltage sense connection during a classD amplifier transition, */\ + { 0x8987, "vs_gain"}, /* Voltage sense gain , */\ + { 0x8a00, "vs_inn_short"}, /* Short voltage sense negative to common mode , */\ + { 0x8a10, "vs_inp_short"}, /* Short voltage sense positive to common mode , */\ + { 0x8a20, "vs_ldo_bypass"}, /* Bypass voltage sense LDO , */\ + { 0x8a30, "vs_ldo_pulldown"}, /* Pull down voltage sense LDO, only valid if enbl_cs_ldo is high, */\ + { 0x8a44, "vs_ldo_voset"}, /* Voltage sense LDO voltage level setting (two's complement), */\ + { 0x8a90, "enbl_vs_adc"}, /* Enable voltage sense ADC , */\ + { 0x8aa0, "enbl_vs_inn1"}, /* Enable connection of voltage sense negative1 , */\ + { 0x8ab0, "enbl_vs_inn2"}, /* Enable connection of voltage sense negative2 , */\ + { 0x8ac0, "enbl_vs_inp1"}, /* Enable connection of voltage sense positive1 , */\ + { 0x8ad0, "enbl_vs_inp2"}, /* Enable connection of voltage sense positive2 , */\ + { 0x8ae0, "enbl_vs_ldo"}, /* Enable voltage sense LDO , */\ + { 0x8af0, "enbl_vs_vbatldo"}, /* Enable of voltage sense LDO , */\ + { 0x9000, "cf_rst_dsp"}, /* Reset for Coolflux DSP , */\ + { 0x9011, "cf_dmem"}, /* Target memory for CFMA using I2C interface , */\ + { 0x9030, "cf_aif"}, /* Auto increment , */\ + { 0x9040, "cf_int"}, /* Coolflux 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"}, /* CF memory address , */\ + { 0x920f, "cf_mema"}, /* Activate memory access , */\ + { 0x9307, "cf_err"}, /* CF error flags , */\ + { 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 , */\ + { 0xa007, "mtpkey1"}, /* KEY1 To access KEY1 protected registers 0x5A/90d (default for engineering), */\ + { 0xa107, "mtpkey2"}, /* KEY2 to access KEY2 protected registers, customer key, */\ + { 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 - auto clear, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp - auto clear, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers - auto clear, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp - auto clear, */\ + { 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 , */\ + { 0xb000, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb010, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb020, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb030, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb040, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb050, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb060, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb070, "disable_main_ctrl_change_prot"}, /* Disable main control change protection , */\ + { 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, */\ + { 0xc0c0, "use_direct_vs_ctrls"}, /* Voltage sense direct control to overrule several functions for testing, */\ + { 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), */\ + { 0xc3d0, "test_abistfft_enbl"}, /* Enable ABIST with FFT on Coolflux DSP , */\ + { 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., */\ + { 0xc530, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc540, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc550, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc560, "test_enbl_vs"}, /* Enable for digimux mode of voltage sense , */\ + { 0xc570, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc583, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc5c0, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc607, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO , */\ + { 0xc687, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT , */\ + { 0xc707, "digimuxc_sel"}, /* DigimuxC input selection control routed to ADS1 , */\ + { 0xc800, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xc810, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xc820, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xc830, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xc844, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xc894, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xc903, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xc943, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xca05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xca64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcab3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcaf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xcb09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcba0, "pll_mdec_msb"}, /* MSB of PLL_mdec - I2C direct PLL control mode only, */\ + { 0xcbb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcbc0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xcbd0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcbe0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcbf0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcc0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xcd06, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xce0f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xcf02, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xcf33, "tsig_gain"}, /* Test signal gain , */\ + { 0xd000, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd011, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd032, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd064, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd0b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd0c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd109, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd201, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd221, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd241, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd301, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO datasheet), */\ + { 0xd321, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO datasheet), */\ + { 0xd340, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xd407, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd480, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd491, "sel_wdt_clk"}, /* Watch dog clock divider settings , */\ + { 0xd4b0, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd500, "source_in_testmode"}, /* TDM source in test mode (return only current and voltage sense), */\ + { 0xd510, "gainatt_feedback"}, /* Gainatt feedback to tdm , */\ + { 0xd522, "test_parametric_io"}, /* Test IO parametric , */\ + { 0xd550, "ctrl_bst_clk_lp1"}, /* Boost clock control in low power mode1 , */\ + { 0xd561, "test_spare_out1"}, /* Test spare out 1 , */\ + { 0xd580, "bst_dcmbst"}, /* DCM boost , */\ + { 0xd593, "test_spare_out2"}, /* Test spare out 2 , */\ + { 0xe00f, "sw_profile"}, /* Software profile data , */\ + { 0xe10f, "sw_vstep"}, /* Software vstep information , */\ + { 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_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf307, "calibr_gain_vs"}, /* Voltage sense gain , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "mtpdata4"}, /* MTP4 data , */\ + { 0xf50f, "calibr_R25C"}, /* Ron resistance of speaker coil , */\ + { 0xf60f, "mtpdata6"}, /* MTP6 data , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of 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 functionality of dcdcoff_mode bit , */\ + { 0xf910, "mtp_lock_enbl_coolflux"}, /* Disable functionality of enbl_coolflux bit , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_enbl_pwm_delay_clock_gating"}, /* PWM delay clock auto gating , */\ + { 0xf940, "mtp_enbl_ocp_clock_gating"}, /* OCP clock auto gating , */\ + { 0xf950, "mtp_gate_cgu_clock_for_test"}, /* CGU test clock control , */\ + { 0xf987, "type_bits_fw"}, /* MTP control for firmware features - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9894_irq { + tfa9894_irq_max = -1, + tfa9894_irq_all = -1 /* all irqs */}; + +#define TFA9894_IRQ_NAMETABLE static tfaIrqName_t Tfa9894IrqNames[]= {\ +}; +#endif /* _TFA9894_TFAFIELDNAMES_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9896_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9896_tfafieldnames.h new file mode 100644 index 000000000000..9c2b15f02d42 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9896_tfafieldnames.h @@ -0,0 +1,919 @@ +/** Filename: tfa9896_tfafieldnames.h + * This file was generated automatically on 08/15/16 at 09:43:38. + * Source file: TFA9896_N1B1_I2C_regmap_V16.xlsx + */ + +#ifndef _TFA9896_TFAFIELDNAMES_H +#define _TFA9896_TFAFIELDNAMES_H + + +#define TFA9896_I2CVERSION 16 + +typedef enum nxpTFA9896BfEnumList { + TFA9896_BF_VDDS = 0x0000, /*!< Power-on-reset flag (auto clear by reading) */ + TFA9896_BF_PLLS = 0x0010, /*!< PLL lock to programmed frequency */ + TFA9896_BF_OTDS = 0x0020, /*!< Over Temperature Protection alarm */ + TFA9896_BF_OVDS = 0x0030, /*!< Over Voltage Protection alarm */ + TFA9896_BF_UVDS = 0x0040, /*!< Under Voltage Protection alarm */ + TFA9896_BF_OCDS = 0x0050, /*!< Over Current Protection alarm */ + TFA9896_BF_CLKS = 0x0060, /*!< Clocks stable flag */ + TFA9896_BF_CLIPS = 0x0070, /*!< Amplifier clipping */ + TFA9896_BF_MTPB = 0x0080, /*!< MTP busy copying data to/from I2C registers */ + TFA9896_BF_NOCLK = 0x0090, /*!< lost clock detection (reference input clock) */ + TFA9896_BF_SPKS = 0x00a0, /*!< Speaker error */ + TFA9896_BF_ACS = 0x00b0, /*!< Cold Start required */ + TFA9896_BF_SWS = 0x00c0, /*!< Amplifier engage (Amp Switching) */ + TFA9896_BF_WDS = 0x00d0, /*!< watchdog reset (activates reset) */ + TFA9896_BF_AMPS = 0x00e0, /*!< Amplifier is enabled by manager */ + TFA9896_BF_AREFS = 0x00f0, /*!< References are enabled by manager */ + TFA9896_BF_BATS = 0x0109, /*!< Battery voltage from ADC readout */ + TFA9896_BF_TEMPS = 0x0208, /*!< Temperature readout from the temperature sensor ( C) */ + TFA9896_BF_REV = 0x030f, /*!< Device revision information */ + TFA9896_BF_RCV = 0x0420, /*!< Enable receiver mode */ + TFA9896_BF_CHS12 = 0x0431, /*!< Channel Selection TDM input for Coolflux */ + TFA9896_BF_INPLVL= 0x0450, /*!< Input level selection attenuator ( */ + TFA9896_BF_CHSA = 0x0461, /*!< Input selection for amplifier */ + TFA9896_BF_AUDFS = 0x04c3, /*!< Audio sample rate setting */ + TFA9896_BF_BSSCR = 0x0501, /*!< Batteery protection attack time */ + TFA9896_BF_BSST = 0x0523, /*!< Battery protection threshold level */ + TFA9896_BF_BSSRL = 0x0561, /*!< Battery protection maximum reduction */ + TFA9896_BF_BSSRR = 0x0582, /*!< Battery protection release time */ + TFA9896_BF_BSSHY = 0x05b1, /*!< Battery Protection Hysteresis */ + TFA9896_BF_BSSR = 0x05e0, /*!< Battery voltage value for read out (only) */ + TFA9896_BF_BSSBY = 0x05f0, /*!< Bypass clipper battery protection */ + TFA9896_BF_DPSA = 0x0600, /*!< Enable dynamic powerstage activation (DPSA) */ + TFA9896_BF_ATTEN = 0x0613, /*!< Gain attenuation setting */ + TFA9896_BF_CFSM = 0x0650, /*!< Soft mute in CoolFlux */ + TFA9896_BF_BSSS = 0x0670, /*!< Battery sense steepness */ + TFA9896_BF_VOL = 0x0687, /*!< Coolflux volume control */ + TFA9896_BF_DCVO2 = 0x0702, /*!< Second Boost Voltage */ + TFA9896_BF_DCMCC = 0x0733, /*!< Max boost coil current - step of 175 mA */ + TFA9896_BF_DCVO1 = 0x0772, /*!< First Boost Voltage */ + TFA9896_BF_DCIE = 0x07a0, /*!< Adaptive boost mode */ + TFA9896_BF_DCSR = 0x07b0, /*!< Soft Rampup/down mode for DCDC controller */ + TFA9896_BF_DCPAVG= 0x07c0, /*!< ctrl_peak2avg for analog part of DCDC */ + TFA9896_BF_DCPWM = 0x07d0, /*!< DCDC PWM only mode */ + TFA9896_BF_TROS = 0x0800, /*!< Selection ambient temperature for speaker calibration */ + TFA9896_BF_EXTTS = 0x0818, /*!< External temperature for speaker calibration (C) */ + TFA9896_BF_PWDN = 0x0900, /*!< powerdown selection */ + TFA9896_BF_I2CR = 0x0910, /*!< All I2C registers reset to default */ + TFA9896_BF_CFE = 0x0920, /*!< Enable CoolFlux */ + TFA9896_BF_AMPE = 0x0930, /*!< Enable Amplifier */ + TFA9896_BF_DCA = 0x0940, /*!< Enable DCDC Boost converter */ + TFA9896_BF_SBSL = 0x0950, /*!< Coolflux configured */ + TFA9896_BF_AMPC = 0x0960, /*!< Selection if Coolflux enables amplifier */ + TFA9896_BF_DCDIS = 0x0970, /*!< DCDC boost converter not connected */ + TFA9896_BF_PSDR = 0x0980, /*!< IDDQ amplifier test selection */ + TFA9896_BF_INTPAD= 0x09c1, /*!< INT pad (interrupt bump output) configuration */ + TFA9896_BF_IPLL = 0x09e0, /*!< PLL input reference clock selection */ + TFA9896_BF_DCTRIP= 0x0a04, /*!< Adaptive boost trip levels (effective only when boost_intel is set to 1) */ + TFA9896_BF_DCHOLD= 0x0a54, /*!< Hold time for DCDC booster (effective only when boost_intel is set to 1) */ + TFA9896_BF_MTPK = 0x0b07, /*!< KEY2 to access key2 protected registers (default for engineering) */ + TFA9896_BF_CVFDLY= 0x0c25, /*!< Fractional delay adjustment between current and voltage sense */ + TFA9896_BF_OPENMTP= 0x0ec0, /*!< Enable programming of the MTP memory */ + TFA9896_BF_TDMPRF= 0x1011, /*!< TDM usecase selection control */ + TFA9896_BF_TDMEN = 0x1030, /*!< TDM interface enable */ + TFA9896_BF_TDMCKINV= 0x1040, /*!< TDM clock inversion, receive on */ + TFA9896_BF_TDMFSLN= 0x1053, /*!< TDM FS length */ + TFA9896_BF_TDMFSPOL= 0x1090, /*!< TDM FS polarity (start frame) */ + TFA9896_BF_TDMSAMSZ= 0x10a4, /*!< TDM sample size for all TDM sinks and sources */ + TFA9896_BF_TDMSLOTS= 0x1103, /*!< TDM number of slots */ + TFA9896_BF_TDMSLLN= 0x1144, /*!< TDM slot length */ + TFA9896_BF_TDMBRMG= 0x1194, /*!< TDM bits remaining after the last slot */ + TFA9896_BF_TDMDDEL= 0x11e0, /*!< TDM data delay */ + TFA9896_BF_TDMDADJ= 0x11f0, /*!< TDM data adjustment */ + TFA9896_BF_TDMTXFRM= 0x1201, /*!< TDM TXDATA format */ + TFA9896_BF_TDMUUS0= 0x1221, /*!< TDM TXDATA format unused slot SD0 */ + TFA9896_BF_TDMUUS1= 0x1241, /*!< TDM TXDATA format unused slot SD1 */ + TFA9896_BF_TDMSI0EN= 0x1270, /*!< TDM sink0 enable */ + TFA9896_BF_TDMSI1EN= 0x1280, /*!< TDM sink1 enable */ + TFA9896_BF_TDMSI2EN= 0x1290, /*!< TDM sink2 enable */ + TFA9896_BF_TDMSO0EN= 0x12a0, /*!< TDM source0 enable */ + TFA9896_BF_TDMSO1EN= 0x12b0, /*!< TDM source1 enable */ + TFA9896_BF_TDMSO2EN= 0x12c0, /*!< TDM source2 enable */ + TFA9896_BF_TDMSI0IO= 0x12d0, /*!< TDM sink0 IO selection */ + TFA9896_BF_TDMSI1IO= 0x12e0, /*!< TDM sink1 IO selection */ + TFA9896_BF_TDMSI2IO= 0x12f0, /*!< TDM sink2 IO selection */ + TFA9896_BF_TDMSO0IO= 0x1300, /*!< TDM source0 IO selection */ + TFA9896_BF_TDMSO1IO= 0x1310, /*!< TDM source1 IO selection */ + TFA9896_BF_TDMSO2IO= 0x1320, /*!< TDM source2 IO selection */ + TFA9896_BF_TDMSI0SL= 0x1333, /*!< TDM sink0 slot position [GAIN IN] */ + TFA9896_BF_TDMSI1SL= 0x1373, /*!< TDM sink1 slot position [CH1 IN] */ + TFA9896_BF_TDMSI2SL= 0x13b3, /*!< TDM sink2 slot position [CH2 IN] */ + TFA9896_BF_TDMSO0SL= 0x1403, /*!< TDM source0 slot position [GAIN OUT] */ + TFA9896_BF_TDMSO1SL= 0x1443, /*!< TDM source1 slot position [Voltage Sense] */ + TFA9896_BF_TDMSO2SL= 0x1483, /*!< TDM source2 slot position [Current Sense] */ + TFA9896_BF_NBCK = 0x14c3, /*!< TDM NBCK bit clock ratio */ + TFA9896_BF_INTOVDDS= 0x2000, /*!< flag_por_int_out */ + TFA9896_BF_INTOPLLS= 0x2010, /*!< flag_pll_lock_int_out */ + TFA9896_BF_INTOOTDS= 0x2020, /*!< flag_otpok_int_out */ + TFA9896_BF_INTOOVDS= 0x2030, /*!< flag_ovpok_int_out */ + TFA9896_BF_INTOUVDS= 0x2040, /*!< flag_uvpok_int_out */ + TFA9896_BF_INTOOCDS= 0x2050, /*!< flag_ocp_alarm_int_out */ + TFA9896_BF_INTOCLKS= 0x2060, /*!< flag_clocks_stable_int_out */ + TFA9896_BF_INTOCLIPS= 0x2070, /*!< flag_clip_int_out */ + TFA9896_BF_INTOMTPB= 0x2080, /*!< mtp_busy_int_out */ + TFA9896_BF_INTONOCLK= 0x2090, /*!< flag_lost_clk_int_out */ + TFA9896_BF_INTOSPKS= 0x20a0, /*!< flag_cf_speakererror_int_out */ + TFA9896_BF_INTOACS= 0x20b0, /*!< flag_cold_started_int_out */ + TFA9896_BF_INTOSWS= 0x20c0, /*!< flag_engage_int_out */ + TFA9896_BF_INTOWDS= 0x20d0, /*!< flag_watchdog_reset_int_out */ + TFA9896_BF_INTOAMPS= 0x20e0, /*!< flag_enbl_amp_int_out */ + TFA9896_BF_INTOAREFS= 0x20f0, /*!< flag_enbl_ref_int_out */ + TFA9896_BF_INTOERR= 0x2200, /*!< flag_cfma_err_int_out */ + TFA9896_BF_INTOACK= 0x2210, /*!< flag_cfma_ack_int_out */ + TFA9896_BF_INTIVDDS= 0x2300, /*!< flag_por_int_in */ + TFA9896_BF_INTIPLLS= 0x2310, /*!< flag_pll_lock_int_in */ + TFA9896_BF_INTIOTDS= 0x2320, /*!< flag_otpok_int_in */ + TFA9896_BF_INTIOVDS= 0x2330, /*!< flag_ovpok_int_in */ + TFA9896_BF_INTIUVDS= 0x2340, /*!< flag_uvpok_int_in */ + TFA9896_BF_INTIOCDS= 0x2350, /*!< flag_ocp_alarm_int_in */ + TFA9896_BF_INTICLKS= 0x2360, /*!< flag_clocks_stable_int_in */ + TFA9896_BF_INTICLIPS= 0x2370, /*!< flag_clip_int_in */ + TFA9896_BF_INTIMTPB= 0x2380, /*!< mtp_busy_int_in */ + TFA9896_BF_INTINOCLK= 0x2390, /*!< flag_lost_clk_int_in */ + TFA9896_BF_INTISPKS= 0x23a0, /*!< flag_cf_speakererror_int_in */ + TFA9896_BF_INTIACS= 0x23b0, /*!< flag_cold_started_int_in */ + TFA9896_BF_INTISWS= 0x23c0, /*!< flag_engage_int_in */ + TFA9896_BF_INTIWDS= 0x23d0, /*!< flag_watchdog_reset_int_in */ + TFA9896_BF_INTIAMPS= 0x23e0, /*!< flag_enbl_amp_int_in */ + TFA9896_BF_INTIAREFS= 0x23f0, /*!< flag_enbl_ref_int_in */ + TFA9896_BF_INTIERR= 0x2500, /*!< flag_cfma_err_int_in */ + TFA9896_BF_INTIACK= 0x2510, /*!< flag_cfma_ack_int_in */ + TFA9896_BF_INTENVDDS= 0x2600, /*!< flag_por_int_enable */ + TFA9896_BF_INTENPLLS= 0x2610, /*!< flag_pll_lock_int_enable */ + TFA9896_BF_INTENOTDS= 0x2620, /*!< flag_otpok_int_enable */ + TFA9896_BF_INTENOVDS= 0x2630, /*!< flag_ovpok_int_enable */ + TFA9896_BF_INTENUVDS= 0x2640, /*!< flag_uvpok_int_enable */ + TFA9896_BF_INTENOCDS= 0x2650, /*!< flag_ocp_alarm_int_enable */ + TFA9896_BF_INTENCLKS= 0x2660, /*!< flag_clocks_stable_int_enable */ + TFA9896_BF_INTENCLIPS= 0x2670, /*!< flag_clip_int_enable */ + TFA9896_BF_INTENMTPB= 0x2680, /*!< mtp_busy_int_enable */ + TFA9896_BF_INTENNOCLK= 0x2690, /*!< flag_lost_clk_int_enable */ + TFA9896_BF_INTENSPKS= 0x26a0, /*!< flag_cf_speakererror_int_enable */ + TFA9896_BF_INTENACS= 0x26b0, /*!< flag_cold_started_int_enable */ + TFA9896_BF_INTENSWS= 0x26c0, /*!< flag_engage_int_enable */ + TFA9896_BF_INTENWDS= 0x26d0, /*!< flag_watchdog_reset_int_enable */ + TFA9896_BF_INTENAMPS= 0x26e0, /*!< flag_enbl_amp_int_enable */ + TFA9896_BF_INTENAREFS= 0x26f0, /*!< flag_enbl_ref_int_enable */ + TFA9896_BF_INTENERR= 0x2800, /*!< flag_cfma_err_int_enable */ + TFA9896_BF_INTENACK= 0x2810, /*!< flag_cfma_ack_int_enable */ + TFA9896_BF_INTPOLVDDS= 0x2900, /*!< flag_por_int_pol */ + TFA9896_BF_INTPOLPLLS= 0x2910, /*!< flag_pll_lock_int_pol */ + TFA9896_BF_INTPOLOTDS= 0x2920, /*!< flag_otpok_int_pol */ + TFA9896_BF_INTPOLOVDS= 0x2930, /*!< flag_ovpok_int_pol */ + TFA9896_BF_INTPOLUVDS= 0x2940, /*!< flag_uvpok_int_pol */ + TFA9896_BF_INTPOLOCDS= 0x2950, /*!< flag_ocp_alarm_int_pol */ + TFA9896_BF_INTPOLCLKS= 0x2960, /*!< flag_clocks_stable_int_pol */ + TFA9896_BF_INTPOLCLIPS= 0x2970, /*!< flag_clip_int_pol */ + TFA9896_BF_INTPOLMTPB= 0x2980, /*!< mtp_busy_int_pol */ + TFA9896_BF_INTPOLNOCLK= 0x2990, /*!< flag_lost_clk_int_pol */ + TFA9896_BF_INTPOLSPKS= 0x29a0, /*!< flag_cf_speakererror_int_pol */ + TFA9896_BF_INTPOLACS= 0x29b0, /*!< flag_cold_started_int_pol */ + TFA9896_BF_INTPOLSWS= 0x29c0, /*!< flag_engage_int_pol */ + TFA9896_BF_INTPOLWDS= 0x29d0, /*!< flag_watchdog_reset_int_pol */ + TFA9896_BF_INTPOLAMPS= 0x29e0, /*!< flag_enbl_amp_int_pol */ + TFA9896_BF_INTPOLAREFS= 0x29f0, /*!< flag_enbl_ref_int_pol */ + TFA9896_BF_INTPOLERR= 0x2b00, /*!< flag_cfma_err_int_pol */ + TFA9896_BF_INTPOLACK= 0x2b10, /*!< flag_cfma_ack_int_pol */ + TFA9896_BF_CLIP = 0x4900, /*!< Bypass clip control */ + TFA9896_BF_CIMTP = 0x62b0, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9896_BF_RST = 0x7000, /*!< Reset CoolFlux DSP */ + TFA9896_BF_DMEM = 0x7011, /*!< Target memory for access */ + TFA9896_BF_AIF = 0x7030, /*!< Auto increment flag for memory-address */ + TFA9896_BF_CFINT = 0x7040, /*!< CF Interrupt - auto clear */ + TFA9896_BF_REQ = 0x7087, /*!< CF request for accessing the 8 channels */ + TFA9896_BF_MADD = 0x710f, /*!< Memory address */ + TFA9896_BF_MEMA = 0x720f, /*!< Activate memory access */ + TFA9896_BF_ERR = 0x7307, /*!< CF error flags */ + TFA9896_BF_ACK = 0x7387, /*!< CF acknowledgement of the requests channels */ + TFA9896_BF_MTPOTC= 0x8000, /*!< Calibration schedule selection */ + TFA9896_BF_MTPEX = 0x8010, /*!< Calibration of RON status bit */ +} nxpTFA9896BfEnumList_t; +#define TFA9896_NAMETABLE static tfaBfName_t Tfa9896DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* Power-on-reset flag (auto clear by reading) , */\ + { 0x10, "PLLS"}, /* PLL lock to programmed frequency , */\ + { 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 copying data to/from I2C registers , */\ + { 0x90, "NOCLK"}, /* lost clock detection (reference input clock) , */\ + { 0xa0, "SPKS"}, /* Speaker error , */\ + { 0xb0, "ACS"}, /* Cold Start required , */\ + { 0xc0, "SWS"}, /* Amplifier engage (Amp Switching) , */\ + { 0xd0, "WDS"}, /* watchdog reset (activates reset) , */\ + { 0xe0, "AMPS"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* References are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage from ADC readout , */\ + { 0x208, "TEMPS"}, /* Temperature readout from the temperature sensor ( C), */\ + { 0x30f, "REV"}, /* Device revision information , */\ + { 0x420, "RCV"}, /* Enable receiver mode , */\ + { 0x431, "CHS12"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "INPLVL"}, /* Input level selection attenuator ( , */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4c3, "AUDFS"}, /* Audio sample rate setting , */\ + { 0x501, "BSSCR"}, /* Batteery protection attack time , */\ + { 0x523, "BSST"}, /* Battery protection threshold level , */\ + { 0x561, "BSSRL"}, /* Battery protection maximum reduction , */\ + { 0x582, "BSSRR"}, /* Battery protection release time , */\ + { 0x5b1, "BSSHY"}, /* Battery Protection Hysteresis , */\ + { 0x5e0, "BSSR"}, /* Battery voltage value for read out (only) , */\ + { 0x5f0, "BSSBY"}, /* Bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation (DPSA) , */\ + { 0x613, "ATTEN"}, /* Gain attenuation setting , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "BSSS"}, /* Battery sense steepness , */\ + { 0x687, "VOL"}, /* Coolflux volume control , */\ + { 0x702, "DCVO2"}, /* Second Boost Voltage , */\ + { 0x733, "DCMCC"}, /* Max boost coil current - step of 175 mA , */\ + { 0x772, "DCVO1"}, /* First Boost Voltage , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft Rampup/down mode for DCDC controller , */\ + { 0x7c0, "DCPAVG"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x7d0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x800, "TROS"}, /* Selection ambient temperature for speaker calibration , */\ + { 0x818, "EXTTS"}, /* External temperature for speaker calibration (C) , */\ + { 0x900, "PWDN"}, /* powerdown selection , */\ + { 0x910, "I2CR"}, /* All I2C registers reset to default , */\ + { 0x920, "CFE"}, /* Enable CoolFlux , */\ + { 0x930, "AMPE"}, /* Enable Amplifier , */\ + { 0x940, "DCA"}, /* Enable DCDC Boost converter , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection if Coolflux enables amplifier , */\ + { 0x970, "DCDIS"}, /* DCDC boost converter not connected , */\ + { 0x980, "PSDR"}, /* IDDQ amplifier test selection , */\ + { 0x9c1, "INTPAD"}, /* INT pad (interrupt bump output) configuration , */\ + { 0x9e0, "IPLL"}, /* PLL input reference clock selection , */\ + { 0xa04, "DCTRIP"}, /* Adaptive boost trip levels (effective only when boost_intel is set to 1), */\ + { 0xa54, "DCHOLD"}, /* Hold time for DCDC booster (effective only when boost_intel is set to 1), */\ + { 0xb07, "MTPK"}, /* KEY2 to access key2 protected registers (default for engineering), */\ + { 0xc25, "CVFDLY"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xec0, "OPENMTP"}, /* Enable programming of the MTP memory , */\ + { 0x1011, "TDMPRF"}, /* TDM usecase selection control , */\ + { 0x1030, "TDMEN"}, /* TDM interface enable , */\ + { 0x1040, "TDMCKINV"}, /* TDM clock inversion, receive on , */\ + { 0x1053, "TDMFSLN"}, /* TDM FS length , */\ + { 0x1090, "TDMFSPOL"}, /* TDM FS polarity (start frame) , */\ + { 0x10a4, "TDMSAMSZ"}, /* TDM sample size for all TDM sinks and sources , */\ + { 0x1103, "TDMSLOTS"}, /* TDM number of slots , */\ + { 0x1144, "TDMSLLN"}, /* TDM slot length , */\ + { 0x1194, "TDMBRMG"}, /* TDM bits remaining after the last slot , */\ + { 0x11e0, "TDMDDEL"}, /* TDM data delay , */\ + { 0x11f0, "TDMDADJ"}, /* TDM data adjustment , */\ + { 0x1201, "TDMTXFRM"}, /* TDM TXDATA format , */\ + { 0x1221, "TDMUUS0"}, /* TDM TXDATA format unused slot SD0 , */\ + { 0x1241, "TDMUUS1"}, /* TDM 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 selection , */\ + { 0x12e0, "TDMSI1IO"}, /* TDM sink1 IO selection , */\ + { 0x12f0, "TDMSI2IO"}, /* TDM sink2 IO selection , */\ + { 0x1300, "TDMSO0IO"}, /* TDM source0 IO selection , */\ + { 0x1310, "TDMSO1IO"}, /* TDM source1 IO selection , */\ + { 0x1320, "TDMSO2IO"}, /* TDM source2 IO selection , */\ + { 0x1333, "TDMSI0SL"}, /* TDM sink0 slot position [GAIN IN] , */\ + { 0x1373, "TDMSI1SL"}, /* TDM sink1 slot position [CH1 IN] , */\ + { 0x13b3, "TDMSI2SL"}, /* TDM sink2 slot position [CH2 IN] , */\ + { 0x1403, "TDMSO0SL"}, /* TDM source0 slot position [GAIN OUT] , */\ + { 0x1443, "TDMSO1SL"}, /* TDM source1 slot position [Voltage Sense] , */\ + { 0x1483, "TDMSO2SL"}, /* TDM source2 slot position [Current Sense] , */\ + { 0x14c3, "NBCK"}, /* TDM NBCK bit clock ratio , */\ + { 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 , */\ + { 0x2200, "INTOERR"}, /* flag_cfma_err_int_out , */\ + { 0x2210, "INTOACK"}, /* flag_cfma_ack_int_out , */\ + { 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 , */\ + { 0x2500, "INTIERR"}, /* flag_cfma_err_int_in , */\ + { 0x2510, "INTIACK"}, /* flag_cfma_ack_int_in , */\ + { 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 , */\ + { 0x2800, "INTENERR"}, /* flag_cfma_err_int_enable , */\ + { 0x2810, "INTENACK"}, /* flag_cfma_ack_int_enable , */\ + { 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 , */\ + { 0x2b00, "INTPOLERR"}, /* flag_cfma_err_int_pol , */\ + { 0x2b10, "INTPOLACK"}, /* flag_cfma_ack_int_pol , */\ + { 0x4900, "CLIP"}, /* Bypass clip control , */\ + { 0x62b0, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Auto increment flag for memory-address , */\ + { 0x7040, "CFINT"}, /* CF Interrupt - auto clear , */\ + { 0x7087, "REQ"}, /* CF request for accessing the 8 channels , */\ + { 0x710f, "MADD"}, /* Memory address , */\ + { 0x720f, "MEMA"}, /* Activate memory access , */\ + { 0x7307, "ERR"}, /* CF error flags , */\ + { 0x7387, "ACK"}, /* CF acknowledgement of the requests channels , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule selection , */\ + { 0x8010, "MTPEX"}, /* Calibration of RON status bit , */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9896_BITNAMETABLE static tfaBfName_t Tfa9896BitNames[]= {\ + { 0x0, "flag_por"}, /* Power-on-reset flag (auto clear by reading) , */\ + { 0x10, "flag_pll_lock"}, /* PLL lock to programmed frequency , */\ + { 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 copying data to/from I2C registers , */\ + { 0x90, "flag_lost_clk"}, /* lost clock detection (reference input clock) , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker error , */\ + { 0xb0, "flag_cold_started"}, /* Cold Start required , */\ + { 0xc0, "flag_engage"}, /* Amplifier engage (Amp Switching) , */\ + { 0xd0, "flag_watchdog_reset"}, /* watchdog reset (activates reset) , */\ + { 0xe0, "flag_enbl_amp"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* References are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage from ADC readout , */\ + { 0x208, "temp_adc"}, /* Temperature readout from the temperature sensor ( C), */\ + { 0x30f, "device_rev"}, /* Device revision information , */\ + { 0x420, "ctrl_rcv"}, /* Enable receiver mode , */\ + { 0x431, "chan_sel"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "input_level"}, /* Input level selection attenuator ( , */\ + { 0x461, "vamp_sel"}, /* Input selection for amplifier , */\ + { 0x4c3, "audio_fs"}, /* Audio sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* Batteery protection attack time , */\ + { 0x523, "vbat_prot_thlevel"}, /* Battery protection threshold level , */\ + { 0x561, "vbat_prot_max_reduct"}, /* Battery protection maximum reduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Battery protection release time , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* Battery Protection Hysteresis , */\ + { 0x5d0, "reset_min_vbat"}, /* Battery supply safeguard clipper reset ( if CF_DSP is bypassed), */\ + { 0x5e0, "sel_vbat"}, /* Battery voltage value for read out (only) , */\ + { 0x5f0, "bypass_clipper"}, /* Bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation (DPSA) , */\ + { 0x613, "ctrl_att"}, /* Gain attenuation setting , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x670, "batsense_steepness"}, /* Battery sense steepness , */\ + { 0x687, "vol"}, /* Coolflux volume control , */\ + { 0x702, "scnd_boost_voltage"}, /* Second Boost Voltage , */\ + { 0x733, "boost_cur"}, /* Max boost coil current - step of 175 mA , */\ + { 0x772, "frst_boost_voltage"}, /* First Boost Voltage , */\ + { 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 , */\ + { 0x7d0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7e0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 (flag from analog) , */\ + { 0x800, "ext_temp_sel"}, /* Selection ambient temperature for speaker calibration , */\ + { 0x818, "ext_temp"}, /* External temperature for speaker calibration (C) , */\ + { 0x900, "powerdown"}, /* powerdown selection , */\ + { 0x910, "reset"}, /* All I2C registers reset to default , */\ + { 0x920, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x940, "enbl_boost"}, /* Enable DCDC Boost converter , */\ + { 0x950, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection if Coolflux enables amplifier , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC boost converter not connected , */\ + { 0x980, "iddqtest"}, /* IDDQ amplifier test selection , */\ + { 0x9c1, "int_pad_io"}, /* INT pad (interrupt bump output) configuration , */\ + { 0x9e0, "sel_fs_bck"}, /* PLL input reference clock selection , */\ + { 0x9f0, "sel_scl_cf_clock"}, /* Coolflux sub-system clock selection , */\ + { 0xa04, "boost_trip_lvl"}, /* Adaptive boost trip levels (effective only when boost_intel is set to 1), */\ + { 0xa54, "boost_hold_time"}, /* Hold time for DCDC booster (effective only when boost_intel is set to 1), */\ + { 0xaa1, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0xb07, "mtpkey2"}, /* KEY2 to access key2 protected registers (default for engineering), */\ + { 0xc00, "enbl_volt_sense"}, /* Voltage sense enabling control bit , */\ + { 0xc10, "vsense_pwm_sel"}, /* Voltage sense source selection , */\ + { 0xc25, "vi_frac_delay"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xc80, "sel_voltsense_out"}, /* TDM output data selection for AEC , */\ + { 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, */\ + { 0xe10, "bypass_ocp"}, /* Bypass OCP (digital IP block) , */\ + { 0xe20, "ocptest"}, /* ocptest (analog IP block) enable , */\ + { 0xe80, "disable_clock_sh_prot"}, /* Disable clock_sh protection , */\ + { 0xe92, "reserve_reg_15_09"}, /* Spare control bits for future usage , */\ + { 0xec0, "unprotect_mtp"}, /* Enable programming of the MTP memory , */\ + { 0xed2, "reserve_reg_15_13"}, /* Spare control bits for future usage , */\ + { 0xf00, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode forcing each 50us a pwm pulse , */\ + { 0xf11, "dcdc_ctrl_maxzercnt"}, /* DCDC number of zero current flags required to go to pfm mode, */\ + { 0xf36, "dcdc_vbat_delta_detect"}, /* DCDC threshold required on a delta Vbat (in PFM mode) switching to PWM mode, */\ + { 0xfa0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x1011, "tdm_usecase"}, /* TDM usecase selection control , */\ + { 0x1030, "tdm_enable"}, /* TDM interface enable , */\ + { 0x1040, "tdm_clk_inversion"}, /* TDM clock inversion, receive on , */\ + { 0x1053, "tdm_fs_ws_length"}, /* TDM FS length , */\ + { 0x1090, "tdm_fs_ws_polarity"}, /* TDM FS polarity (start frame) , */\ + { 0x10a4, "tdm_sample_size"}, /* TDM sample size for all TDM sinks and sources , */\ + { 0x1103, "tdm_nb_of_slots"}, /* TDM number of slots , */\ + { 0x1144, "tdm_slot_length"}, /* TDM slot length , */\ + { 0x1194, "tdm_bits_remaining"}, /* TDM bits remaining after the last slot , */\ + { 0x11e0, "tdm_data_delay"}, /* TDM data delay , */\ + { 0x11f0, "tdm_data_adjustment"}, /* TDM data adjustment , */\ + { 0x1201, "tdm_txdata_format"}, /* TDM TXDATA format , */\ + { 0x1221, "tdm_txdata_format_unused_slot_sd0"}, /* TDM TXDATA format unused slot SD0 , */\ + { 0x1241, "tdm_txdata_format_unused_slot_sd1"}, /* TDM 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 selection , */\ + { 0x12e0, "tdm_sink1_io"}, /* TDM sink1 IO selection , */\ + { 0x12f0, "tdm_sink2_io"}, /* TDM sink2 IO selection , */\ + { 0x1300, "tdm_source0_io"}, /* TDM source0 IO selection , */\ + { 0x1310, "tdm_source1_io"}, /* TDM source1 IO selection , */\ + { 0x1320, "tdm_source2_io"}, /* TDM source2 IO selection , */\ + { 0x1333, "tdm_sink0_slot"}, /* TDM sink0 slot position [GAIN IN] , */\ + { 0x1373, "tdm_sink1_slot"}, /* TDM sink1 slot position [CH1 IN] , */\ + { 0x13b3, "tdm_sink2_slot"}, /* TDM sink2 slot position [CH2 IN] , */\ + { 0x1403, "tdm_source0_slot"}, /* TDM source0 slot position [GAIN OUT] , */\ + { 0x1443, "tdm_source1_slot"}, /* TDM source1 slot position [Voltage Sense] , */\ + { 0x1483, "tdm_source2_slot"}, /* TDM source2 slot position [Current Sense] , */\ + { 0x14c3, "tdm_nbck"}, /* TDM NBCK bit clock ratio , */\ + { 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 , */\ + { 0x1551, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 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 , */\ + { 0x2200, "flag_cfma_err_int_out"}, /* flag_cfma_err_int_out , */\ + { 0x2210, "flag_cfma_ack_int_out"}, /* flag_cfma_ack_int_out , */\ + { 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 , */\ + { 0x2500, "flag_cfma_err_int_in"}, /* flag_cfma_err_int_in , */\ + { 0x2510, "flag_cfma_ack_int_in"}, /* flag_cfma_ack_int_in , */\ + { 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 , */\ + { 0x2800, "flag_cfma_err_int_enable"}, /* flag_cfma_err_int_enable , */\ + { 0x2810, "flag_cfma_ack_int_enable"}, /* flag_cfma_ack_int_enable , */\ + { 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 , */\ + { 0x2b00, "flag_cfma_err_int_pol"}, /* flag_cfma_err_int_pol , */\ + { 0x2b10, "flag_cfma_ack_int_pol"}, /* flag_cfma_ack_int_pol , */\ + { 0x3000, "flag_voutcomp"}, /* Status flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3010, "flag_voutcomp93"}, /* Status flag_voutcomp93, indication Vset is larger than 1.07 x Vbat, */\ + { 0x3020, "flag_voutcomp86"}, /* Status flag voutcomp86, indication Vset is larger than 1.14 x Vbat, */\ + { 0x3030, "flag_hiz"}, /* Status flag_hiz, indication Vbst is larger than Vbat, */\ + { 0x3040, "flag_ocpokbst"}, /* Status flag_ocpokbst, indication no over current in boost converter PMOS switch, */\ + { 0x3050, "flag_peakcur"}, /* Status flag_peakcur, indication current is max in dcdc converter, */\ + { 0x3060, "flag_ocpokap"}, /* Status flag_ocpokap, indication no over current in amplifier A PMOS output stage, */\ + { 0x3070, "flag_ocpokan"}, /* Status flag_ocpokan, indication no over current in amplifier A NMOS output stage, */\ + { 0x3080, "flag_ocpokbp"}, /* Status flag_ocpokbp, indication no over current in amplifier B PMOS output stage, */\ + { 0x3090, "flag_ocpokbn"}, /* Status flag_ocpokbn, indication no over current in amplifier B NMOS output stage, */\ + { 0x30a0, "flag_adc10_ready"}, /* Status flag_adc10_ready, indication adc10 is ready, */\ + { 0x30b0, "flag_clipa_high"}, /* Status flag_clipa_high, indication pmos amplifier A is clipping, */\ + { 0x30c0, "flag_clipa_low"}, /* Status flag_clipa_low, indication nmos amplifier A is clipping, */\ + { 0x30d0, "flag_clipb_high"}, /* Status flag_clipb_high, indication pmos amplifier B is clipping, */\ + { 0x30e0, "flag_clipb_low"}, /* Status flag_clipb_low, indication nmos amplifier B is clipping, */\ + { 0x310f, "mtp_man_data_out"}, /* MTP manual read out data , */\ + { 0x3200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0x3210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* MTP error correction test data out , */\ + { 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"}, /* Flag 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 for MTP , */\ + { 0x3309, "data_adc10_tempbat"}, /* ADC10 data output for testing battery voltage and temperature, */\ + { 0x400f, "hid_code"}, /* 5A6Bh, 23147d to access hidden 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 delay setting , */\ + { 0x4180, "pwm_shape"}, /* PWM Shape , */\ + { 0x4190, "pwm_bitlength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4203, "drive"}, /* Drive bits to select number of amplifier power stages, */\ + { 0x4240, "reclock_pwm"}, /* Control for enabling reclocking of PWM signal , */\ + { 0x4250, "reclock_voltsense"}, /* Control for enabling reclocking of voltage sense signal, */\ + { 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 power transistor sections boost converter, */\ + { 0x4370, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x4381, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x43a0, "ocptestbst"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0); For new ocp (ctrl_reversebst is 1), */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT Coolflux , */\ + { 0x43e0, "bst_dcmbst"}, /* DCM mode control for DCDC during I2C direct control mode, */\ + { 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 of 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 threshold level , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* Bypass OCP counter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (UVP/OUP handling) , */\ + { 0x4600, "bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x4627, "cs_gain"}, /* Current sense gain , */\ + { 0x46a0, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x46b0, "bypass_pwmcounter"}, /* Bypass PWM Counter , */\ + { 0x46c0, "cs_negfixed"}, /* Current sense does not switch to neg , */\ + { 0x46d2, "cs_neghyst"}, /* Current sense switches to neg depending on hyseteris level, */\ + { 0x4700, "switch_fb"}, /* Current sense control switch_fb , */\ + { 0x4713, "se_hyst"}, /* Current sense control se_hyst , */\ + { 0x4754, "se_level"}, /* Current sense control se_level , */\ + { 0x47a5, "ktemp"}, /* Current sense control temperature compensation trimming, */\ + { 0x4800, "cs_negin"}, /* Current sense control negin , */\ + { 0x4810, "cs_sein"}, /* Current sense control cs_sein , */\ + { 0x4820, "cs_coincidence"}, /* Coincidence current sense , */\ + { 0x4830, "iddqtestbst"}, /* IDDQ testing in powerstage of DCDC boost converter, */\ + { 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 and hold track time , */\ + { 0x4900, "bypass_clip"}, /* Bypass clip control , */\ + { 0x4920, "cf_cgate_off"}, /* Disable clock gating in the coolflux , */\ + { 0x4940, "clipfast"}, /* Clock selection for HW clipper for battery safeguard, */\ + { 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 Ended and differential mode; 1 is single ended, */\ + { 0x4a12, "adc10_sel"}, /* Select the input to convert the 10b ADC , */\ + { 0x4a60, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0x4a81, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0x4aa0, "bypass_lp_vbat"}, /* LP filter in batt sensor , */\ + { 0x4ae0, "dc_offset"}, /* Current sense 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 (two's complement format) , */\ + { 0x4b61, "adc13_slowdel"}, /* MICADC delay setting for internal clock (debug use only), */\ + { 0x4b83, "adc13_offset"}, /* MICADC 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"}, /* Dynamic element matching control, rest of codes are optional, */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* Enable direct control of pwm duty cycle , */\ + { 0x4f00, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x4f10, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x4f20, "bst_ctrl_azbst"}, /* Control of auto-zeroing of zero current comparator, */\ + { 0x5007, "gain"}, /* Gain setting of the gain multiplier , */\ + { 0x5081, "sourceb"}, /* PWM OUTB selection control , */\ + { 0x50a1, "sourcea"}, /* PWM OUTA selection control , */\ + { 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"}, /* I2C high speed mode selection control , */\ + { 0x5412, "test_parametric_io"}, /* Control for parametric tests of IO cells , */\ + { 0x5440, "enbl_ringo"}, /* Enable ring oscillator control, 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 controls to overrule several functions for testing - I2C direct control mode, */\ + { 0x5610, "rst_datapath"}, /* Reset datapath during direct control mode , */\ + { 0x5620, "rst_cgu"}, /* Reset CGU during durect control mode , */\ + { 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, */\ + { 0x5707, "anamux"}, /* Anamux control , */\ + { 0x57e0, "otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "reverse"}, /* 1b = Normal mode, slope is controlled , */\ + { 0x5813, "pll_selr"}, /* PLL pll_selr , */\ + { 0x5854, "pll_selp"}, /* PLL pll_selp , */\ + { 0x58a5, "pll_seli"}, /* PLL 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 pll_frm , */\ + { 0x5980, "pll_directi"}, /* PLL pll_directi , */\ + { 0x5990, "pll_directo"}, /* PLL pll_directo , */\ + { 0x59a0, "enbl_pll"}, /* PLL enbl_pll , */\ + { 0x59f0, "pll_bypass"}, /* PLL bypass , */\ + { 0x5a0f, "tsig_freq"}, /* Internal sinus test generator frequency control LSB bits, */\ + { 0x5b02, "tsig_freq_msb"}, /* Select internal sine wave generator, frequency control MSB bits, */\ + { 0x5b30, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0x5b44, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0x5c0f, "pll_mdec"}, /* PLL MDEC - I2C direct PLL control mode only , */\ + { 0x5d06, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0x5d78, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0x6007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0x6185, "mtp_ecc_tcin"}, /* MTP ECC TCIN data , */\ + { 0x6203, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 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 I2C mtp register, */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0x62d2, "mtp_speed_mode"}, /* MTP speed mode , */\ + { 0x6340, "mtp_direct_enable"}, /* mtp_direct_enable , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr , */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd , */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst , */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers , */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg , */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp , */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test , */\ + { 0x640f, "mtp_man_data_in"}, /* Write data for MTP manual write , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Auto increment flag for memory-address , */\ + { 0x7040, "cf_int"}, /* CF Interrupt - auto clear , */\ + { 0x7087, "cf_req"}, /* CF request for accessing the 8 channels , */\ + { 0x710f, "cf_madd"}, /* Memory address , */\ + { 0x720f, "cf_mema"}, /* Activate memory access , */\ + { 0x7307, "cf_err"}, /* CF error flags , */\ + { 0x7387, "cf_ack"}, /* CF acknowledgement of the requests channels , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule selection , */\ + { 0x8010, "calibr_ron_done"}, /* Calibration of RON status bit , */\ + { 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"}, /* calibration value of the RON resistance of the coil, */\ + { 0x8505, "type_bits_hw"}, /* bit0 = disable function dcdcoff_mode ($09[7]) , */\ + { 0x8601, "type_bits_1_0_sw"}, /* MTP control SW , */\ + { 0x8681, "type_bits_9_8_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"}, /* production_data1 , */\ + { 0x8b0f, "production_data2"}, /* production_data2 , */\ + { 0x8c0f, "production_data3"}, /* production_data3 , */\ + { 0x8d0f, "production_data4"}, /* production_data4 , */\ + { 0x8e0f, "production_data5"}, /* production_data5 , */\ + { 0x8f0f, "production_data6"}, /* production_data6 , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum TFA9896_irq { + TFA9896_irq_vdds = 0, + TFA9896_irq_plls = 1, + TFA9896_irq_ds = 2, + TFA9896_irq_vds = 3, + TFA9896_irq_uvds = 4, + TFA9896_irq_cds = 5, + TFA9896_irq_clks = 6, + TFA9896_irq_clips = 7, + TFA9896_irq_mtpb = 8, + TFA9896_irq_clk = 9, + TFA9896_irq_spks = 10, + TFA9896_irq_acs = 11, + TFA9896_irq_sws = 12, + TFA9896_irq_wds = 13, + TFA9896_irq_amps = 14, + TFA9896_irq_arefs = 15, + TFA9896_irq_err = 32, + TFA9896_irq_ack = 33, + TFA9896_irq_max = 34, + TFA9896_irq_all = -1 /* all irqs */}; + +#define TFA9896_IRQ_NAMETABLE static tfaIrqName_t TFA9896IrqNames[]= {\ + { 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, "ERR"},\ + { 33, "ACK"},\ + { 34, "34"},\ +}; +#endif /* _TFA9896_TFAFIELDNAMES_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa98xx.c b/techpack/audio/asoc/codecs/tfa9874/tfa98xx.c new file mode 100644 index 000000000000..163e38f4a9e3 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa98xx.c @@ -0,0 +1,3305 @@ +/* + * tfa98xx.c tfa98xx codec module + * + * Copyright 2014-2017 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 pr_fmt(fmt) "%s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "tfa98xx.h" +#include "tfa.h" + +/* required for enum tfa9912_irq */ +#include "tfa98xx_tfafieldnames.h" + +#define TFA98XX_VERSION TFA98XX_API_REV_STR + +#define I2C_RETRIES 50 +#define I2C_RETRY_DELAY 5 /* ms */ + +#undef pr_info +#undef pr_debug +#define pr_info pr_err +#define pr_debug pr_err +#undef dev_dbg +#define dev_dbg dev_info + +struct tfa98xx *g_tfa98xx = NULL; +EXPORT_SYMBOL_GPL(g_tfa98xx); + +/* 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 TF98XX_MAX_DSP_START_TRY_COUNT 10 + +extern int smartpa_present; + +/* data accessible by all instances */ +static struct kmem_cache *tfa98xx_cache = NULL; /* Memory pool used for DSP messages */ +/* Mutex protected data */ +static DEFINE_MUTEX(tfa98xx_mutex); +static LIST_HEAD(tfa98xx_device_list); +static int tfa98xx_device_count = 0; +static int tfa98xx_sync_count = 0; +static LIST_HEAD(profile_list); /* list of user selectable profiles */ +static int tfa98xx_mixer_profiles = 0; /* number of user selectable profiles */ +static int tfa98xx_mixer_profile = 0; /* current mixer profile */ +static struct snd_kcontrol_new *tfa98xx_controls; +static nxpTfaContainer_t *tfa98xx_container = NULL; + +static int tfa98xx_kmsg_regs = 0; +static int tfa98xx_ftrace_regs = 0; + +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 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,4=timing)."); + +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 int no_reset = 0; +module_param(no_reset, int, S_IRUGO); +MODULE_PARM_DESC(no_reset, "do not use the reset line; for debugging via user\n"); + +static int pcm_sample_format = 0; +module_param(pcm_sample_format, int, S_IRUGO); +MODULE_PARM_DESC(pcm_sample_format, "PCM sample format: 0=S16_LE, 1=S24_LE, 2=S32_LE\n"); + +static int pcm_no_constraint = 0; +module_param(pcm_no_constraint, int, S_IRUGO); +MODULE_PARM_DESC(pcm_no_constraint, "do not use constraints for PCM parameters\n"); + +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx); +static int tfa98xx_get_fssel(unsigned int rate); +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable); + +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 const struct tfa98xx_rate rate_to_fssel[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, +}; + +static inline char *tfa_cont_profile_name(struct tfa98xx *tfa98xx, int prof_idx) +{ + if (tfa98xx->tfa->cnt == NULL) + return NULL; + return tfaContProfileName(tfa98xx->tfa->cnt, tfa98xx->tfa->dev_idx, prof_idx); +} + +static enum Tfa98xx_Error tfa98xx_write_re25(struct tfa_device *tfa, int value) +{ + enum Tfa98xx_Error err; + + /* clear MTPEX */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 0); + if (err == Tfa98xx_Error_Ok) { + /* set RE25 in shadow regiser */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_RE25_PRIM, value); + } + if (err == Tfa98xx_Error_Ok) { + /* set MTPEX to copy RE25 into MTP */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 2); + } + + return err; +} + +/* Wrapper for tfa start */ +static enum Tfa98xx_Error tfa98xx_tfa_start(struct tfa98xx *tfa98xx, int next_profile, int vstep) +{ + enum Tfa98xx_Error err; + ktime_t start_time, stop_time; + u64 delta_time; + + if (trace_level & 8) { + start_time = ktime_get_boottime(); + } + + dev_info(&tfa98xx->i2c->dev, "tfa98xx_tfa_start enter\n"); + + err = tfa_dev_start(tfa98xx->tfa, next_profile, vstep); + + if (trace_level & 8) { + stop_time = ktime_get_boottime(); + delta_time = ktime_to_ns(ktime_sub(stop_time, start_time)); + do_div(delta_time, 1000); + dev_dbg(&tfa98xx->i2c->dev, "tfa_dev_start(%d,%d) time = %lld us\n", + next_profile, vstep, delta_time); + } + + if ((err == Tfa98xx_Error_Ok) && (tfa98xx->set_mtp_cal)) { + enum Tfa98xx_Error err_cal; + err_cal = tfa98xx_write_re25(tfa98xx->tfa, tfa98xx->cal_data); + if (err_cal != Tfa98xx_Error_Ok) { + pr_err("Error, setting calibration value in mtp, err=%d\n", err_cal); + } else { + tfa98xx->set_mtp_cal = false; + pr_info("Calibration value (%d) set in mtp\n", + tfa98xx->cal_data); + } + } + + /* Check and update tap-detection state (in case of profile change) */ + tfa98xx_tapdet_check_update(tfa98xx); + + /* Remove sticky bit by reading it once */ + tfa_get_noclk(tfa98xx->tfa); + + /* A cold start erases the configuration, including interrupts setting. + * Restore it if required + */ + tfa98xx_interrupt_enable(tfa98xx, true); + + return err; +} + +static int tfa98xx_input_open(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + dev_info(tfa98xx->component->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_info(&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_info(tfa98xx->component->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->component->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->component->dev, "Unable to register input device\n"); + goto err_free_dev; + } + + dev_dbg(tfa98xx->component->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 < tfa_cnt_get_dev_nprof(tfa98xx->tfa); i++) { + if (strstr(tfa_cont_profile_name(tfa98xx, i), ".tap")) { + tap_profile = true; + tfa98xx->tapdet_profiles |= 1 << i; + dev_info(tfa98xx->component->dev, + "found a tap-detection profile (%d - %s)\n", + i, tfa_cont_profile_name(tfa98xx, 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->component->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); + int value; + + mutex_lock(&tfa98xx->dsp_lock); + value = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_OTC); + mutex_unlock(&tfa98xx->dsp_lock); + + if (value < 0) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, value); + return -EIO; + } + + *val = value; + pr_debug("[0x%x] OTC : %d\n", tfa98xx->i2c->addr, value); + + 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); + enum Tfa98xx_Error err; + + if (val != 0 && val != 1) { + pr_err("[0x%x] Unexpected value %llu\n", tfa98xx->i2c->addr, val); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa_dev_mtp_set(tfa98xx->tfa, TFA_MTP_OTC, val); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != Tfa98xx_Error_Ok) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, err); + return -EIO; + } + + pr_debug("[0x%x] OTC < %llu\n", tfa98xx->i2c->addr, 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); + int value; + + mutex_lock(&tfa98xx->dsp_lock); + value = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_EX); + mutex_unlock(&tfa98xx->dsp_lock); + + if (value < 0) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, value); + return -EIO; + } + + + *val = value; + pr_debug("[0x%x] MTPEX : %d\n", tfa98xx->i2c->addr, value); + + 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); + enum Tfa98xx_Error err; + + if (val != 0) { + pr_err("[0x%x] Can only clear MTPEX (0 value expected)\n", tfa98xx->i2c->addr); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa_dev_mtp_set(tfa98xx->tfa, TFA_MTP_EX, val); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != Tfa98xx_Error_Ok) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, err); + return -EIO; + } + + pr_debug("[0x%x] MTPEX < 0\n", tfa98xx->i2c->addr); + + 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); + + mutex_lock(&tfa98xx->dsp_lock); + *val = tfa98xx_get_exttemp(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + + pr_debug("[0x%x] TEMP : %llu\n", tfa98xx->i2c->addr, *val); + + 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); + + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_set_exttemp(tfa98xx->tfa, (short)val); + mutex_unlock(&tfa98xx->dsp_lock); + + pr_debug("[0x%x] TEMP < %llu\n", tfa98xx->i2c->addr, val); + + return 0; +} + +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); + 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; + + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_calibrate(tfa98xx->tfa); + if (ret == Tfa98xx_Error_Ok) + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (ret == Tfa98xx_Error_Ok) + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + mutex_unlock(&tfa98xx->dsp_lock); + + if (ret) { + pr_info("[0x%x] Calibration start failed (%d)\n", tfa98xx->i2c->addr, ret); + return -EIO; + } else { + pr_info("[0x%x] Calibration started\n", tfa98xx->i2c->addr); + } + + 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; + + mutex_lock(&tfa98xx->dsp_lock); + + /* Need to ensure DSP is access-able, use mtp read access for this + * purpose + */ + ret = tfa98xx_get_mtp(tfa98xx->tfa, &status); + if (ret) { + ret = -EIO; + pr_err("[0x%x] MTP read failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + ret = tfaRunSpeakerCalibration(tfa98xx->tfa); + if (ret) { + ret = -EIO; + pr_err("[0x%x] calibration failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + pr_err("[0x%x] memory allocation failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + if (tfa98xx->tfa->spkr_count > 1) { + ret = snprintf(str, PAGE_SIZE, + "Prim:%d mOhms, Sec:%d mOhms\n", + tfa98xx->tfa->mohm[0], + tfa98xx->tfa->mohm[1]); + } else { + ret = snprintf(str, PAGE_SIZE, + "Prim:%d mOhms\n", + tfa98xx->tfa->mohm[0]); + } + + pr_info("[0x%x] calib_done: %s", tfa98xx->i2c->addr, 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: + 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"; + } + + pr_info("[0x%x] dsp_state : %s\n", tfa98xx->i2c->addr, str); + + 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 Tfa98xx_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("[0x%x] Manual triggering of dsp start...\n", tfa98xx->i2c->addr); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + mutex_unlock(&tfa98xx->dsp_lock); + pr_debug("[0x%x] tfa_dev_start complete: %d\n", tfa98xx->i2c->addr, ret); + } else if (!strncmp(buf, stop_cmd, sizeof(stop_cmd) - 1)) { + pr_info("[0x%x] Manual triggering of dsp stop...\n", tfa98xx->i2c->addr); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_dev_stop(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + pr_debug("[0x%x] tfa_dev_stop complete: %d\n", tfa98xx->i2c->addr, ret); + } else if (!strncmp(buf, mon_start_cmd, sizeof(mon_start_cmd) - 1)) { + pr_info("[0x%x] Manual start of monitor thread...\n", tfa98xx->i2c->addr); + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, HZ); + } else if (!strncmp(buf, mon_stop_cmd, sizeof(mon_stop_cmd) - 1)) { + pr_info("[0x%x] Manual stop of monitor thread...\n", tfa98xx->i2c->addr); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t tfa98xx_dbgfs_fw_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); + char *str; + + switch (tfa98xx->dsp_fw_state) { + case TFA98XX_DSP_FW_NONE: + str = "None\n"; + break; + case TFA98XX_DSP_FW_PENDING: + str = "Pending\n"; + break; + case TFA98XX_DSP_FW_FAIL: + str = "Fail\n"; + break; + case TFA98XX_DSP_FW_OK: + str = "Ok\n"; + break; + default: + str = "Invalid\n"; + } + + pr_info("[0x%x] fw_state : %s", tfa98xx->i2c->addr, str); + + return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); +} + +static ssize_t tfa98xx_dbgfs_rpc_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); + int ret = 0; + uint8_t *buffer; + enum Tfa98xx_Error error; + + if (tfa98xx->tfa == NULL) { + pr_debug("[0x%x] dsp is not available\n", tfa98xx->i2c->addr); + return -ENODEV; + } + + if (count == 0) + return 0; + + buffer = kmalloc(count, GFP_KERNEL); + if (buffer == NULL) { + pr_debug("[0x%x] can not allocate memory\n", tfa98xx->i2c->addr); + return -ENOMEM; + } + + mutex_lock(&tfa98xx->dsp_lock); + error = dsp_msg_read(tfa98xx->tfa, count, buffer); + mutex_unlock(&tfa98xx->dsp_lock); + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] dsp_msg_read error: %d\n", tfa98xx->i2c->addr, error); + kfree(buffer); + return -EFAULT; + } + + ret = copy_to_user(user_buf, buffer, count); + kfree(buffer); + if (ret) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t tfa98xx_dbgfs_rpc_send(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); + nxpTfaFileDsc_t *msg_file; + enum Tfa98xx_Error error; + int err = 0; + + if (tfa98xx->tfa == NULL) { + pr_debug("[0x%x] dsp is not available\n", tfa98xx->i2c->addr); + return -ENODEV; + } + + if (count == 0) + return 0; + + /* msg_file.name is not used */ + msg_file = kmalloc(count + sizeof(nxpTfaFileDsc_t), GFP_KERNEL); + if ( msg_file == NULL ) { + pr_debug("[0x%x] can not allocate memory\n", tfa98xx->i2c->addr); + return -ENOMEM; + } + msg_file->size = count; + + if (copy_from_user(msg_file->data, user_buf, count)) + return -EFAULT; + + mutex_lock(&tfa98xx->dsp_lock); + if ((msg_file->data[0] == 'M') && (msg_file->data[1] == 'G')) { + error = tfaContWriteFile(tfa98xx->tfa, msg_file, 0, 0); /* int vstep_idx, int vstep_msg_idx both 0 */ + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] tfaContWriteFile error: %d\n", tfa98xx->i2c->addr, error); + err = -EIO; + } + } else { + error = dsp_msg(tfa98xx->tfa, msg_file->size, msg_file->data); + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] dsp_msg error: %d\n", tfa98xx->i2c->addr, error); + err = -EIO; + } + } + mutex_unlock(&tfa98xx->dsp_lock); + + kfree(msg_file); + + if (err) + return err; + return count; +} +/* -- RPC */ + +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); + unsigned int value; + + value = tfa_get_pga_gain(tfa98xx->tfa); + if (value < 0) + return -EINVAL; + + *val = value; + 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); + uint16_t value; + int err; + + value = val & 0xffff; + if (value > 7) + return -EINVAL; + + err = tfa_set_pga_gain(tfa98xx->tfa, value); + if (err < 0) + return -EINVAL; + + return 0; +} + +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, + .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_fw_state_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_fw_state_get, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_rpc_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_rpc_read, + .write = tfa98xx_dbgfs_rpc_send, + .llseek = default_llseek, +}; + +static void tfa98xx_debug_init(struct tfa98xx *tfa98xx, struct i2c_client *i2c) +{ + char name[50]; + + 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("fw-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_fw_state_fops); + debugfs_create_file("rpc", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_rpc_fops); + + 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 + + +/* 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 ((len == bprof->len) && (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 ((len == bprof->len) && (0 == strncmp(bprof->basename, basename, len))) { + /* add supported samplerate for this profile */ + sr = tfa98xx_get_profile_sr(tfa98xx->tfa, 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_info("added profile:samplerate = [%d:%d] for mixer profile: %s\n", profile, sr, bprof->basename); + } + } + + return 0; +} + +static int tfa98xx_get_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + int mixer_profile = kcontrol->private_value; + int ret = 0; + int profile; + + profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_get_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int vstep = tfa98xx->prof_vsteps[profile]; + + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = + tfacont_get_max_vstep(tfa98xx->tfa, profile) + - vstep - 1; + } + mutex_unlock(&tfa98xx_mutex); + + return ret; +} + +static int tfa98xx_set_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + int mixer_profile = kcontrol->private_value; + int profile; + int err = 0; + int change = 0; + + if (no_start != 0) + return 0; + + profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_set_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int vstep, vsteps; + int ready = 0; + int new_vstep; + int value = ucontrol->value.integer.value[tfa98xx->tfa->dev_idx]; + + vstep = tfa98xx->prof_vsteps[profile]; + vsteps = tfacont_get_max_vstep(tfa98xx->tfa, profile); + + if (vstep == vsteps - value - 1) + continue; + + 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->vstep = new_vstep; + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_dsp_system_stable(tfa98xx->tfa, &ready); + + if (ready) { + err = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (err) { + pr_err("Write vstep error: %d\n", err); + } else { + pr_info("Succesfully changed vstep index!\n"); + change = 1; + } + } + + mutex_unlock(&tfa98xx->dsp_lock); +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + } +#endif + pr_info("%d: vstep:%d, (control value: %d) - profile %d\n", + tfa98xx->tfa->dev_idx, new_vstep, value, profile); + } + + if (change) { + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + mutex_unlock(&tfa98xx->dsp_lock); + } + } + + mutex_unlock(&tfa98xx_mutex); + + return change; +} + +static int tfa98xx_info_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + + int mixer_profile = tfa98xx_mixer_profile; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_info_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max(0, tfacont_get_max_vstep(tfa98xx->tfa, profile) - 1); + pr_info("vsteps count: %d [prof=%d]\n", tfacont_get_max_vstep(tfa98xx->tfa, profile), + profile); + return 0; +} + +static int tfa98xx_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&tfa98xx_mutex); + ucontrol->value.integer.value[0] = tfa98xx_mixer_profile; + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + int change = 0; + int new_profile; + int prof_idx; + int profile_count = tfa98xx_mixer_profiles; + int profile = tfa98xx_mixer_profile; + + if (no_start != 0) + return 0; + + new_profile = ucontrol->value.integer.value[0]; + if (new_profile == profile) + return 0; + + if ((new_profile < 0) || (new_profile >= profile_count)) { + pr_err("not existing profile (%d)\n", new_profile); + return -EINVAL; + } + + /* 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_debug("selected container profile [%d]\n", prof_idx); + + /* update mixer profile */ + tfa98xx_mixer_profile = new_profile; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + + /* update 'real' profile (container profile) */ + tfa98xx->profile = prof_idx; + tfa98xx->vstep = tfa98xx->prof_vsteps[prof_idx]; + + /* Flag DSP as invalidated as the profile change may invalidate the + * current DSP configuration. That way, further stream start can + * trigger a tfa_dev_start. + */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_INVALIDATED; + } + + + mutex_unlock(&tfa98xx_mutex); + + return change; +} + +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_info_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int tfa98xx_get_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = 0; + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_set_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int ready = 0; + int i = tfa98xx->tfa->dev_idx; + + pr_info("%d: %ld\n", i, ucontrol->value.integer.value[i]); + + tfa98xx_dsp_system_stable(tfa98xx->tfa, &ready); + + if ((ucontrol->value.integer.value[i] != 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) + continue; + + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_stop(tfa98xx->tfa); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } + + ucontrol->value.integer.value[i] = 0; + } + mutex_unlock(&tfa98xx_mutex); + + return 1; +} + +static int tfa98xx_info_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; /* 16 bit value */ + + return 0; +} + +static int tfa98xx_set_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + enum Tfa98xx_Error err; + int i = tfa98xx->tfa->dev_idx; + + tfa98xx->cal_data = (uint16_t)ucontrol->value.integer.value[i]; + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa98xx_write_re25(tfa98xx->tfa, tfa98xx->cal_data); + tfa98xx->set_mtp_cal = (err != Tfa98xx_Error_Ok); + if (tfa98xx->set_mtp_cal == false) { + pr_info("Calibration value (%d) set in mtp\n", + tfa98xx->cal_data); + } + mutex_unlock(&tfa98xx->dsp_lock); + } + mutex_unlock(&tfa98xx_mutex); + + return 1; +} + +static int tfa98xx_get_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_RE25_PRIM); + mutex_unlock(&tfa98xx->dsp_lock); + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +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 = 2; /* Profile and stop control */ + + if (tfa98xx->flags & TFA98XX_FLAG_CALIBRATION_CTL) + nr_controls += 1; /* calibration */ + + /* allocate the tfa98xx_controls base on the nr of profiles */ + nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + for (prof = 0; prof < nprof; prof++) { + if (tfacont_get_max_vstep(tfa98xx->tfa, prof)) + nr_controls++; /* Playback Volume control */ + } + + tfa98xx_controls = devm_kzalloc(tfa98xx->component->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->component->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->component->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, tfa_cont_profile_name(tfa98xx, 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(tfa_cont_profile_name(tfa98xx, prof)) == 0) { + /* the profile is not present, add it to the list */ + list_add(&bprofile->list, &profile_list); + bprofile->item_id = id++; + + pr_info("profile added [%d]: %s\n", bprofile->item_id, bprofile->basename); + + if (tfacont_get_max_vstep(tfa98xx->tfa, prof)) { + name = devm_kzalloc(tfa98xx->component->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 = bprofile->item_id; /* 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); + } + + /* set the number of user selectable profiles in the mixer */ + tfa98xx_mixer_profiles = id; + + /* Create a mixer item for stop control on TFA1 */ + name = devm_kzalloc(tfa98xx->component->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 = tfa98xx_info_stop_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_stop_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_stop_ctl; + mix_index++; + + if (tfa98xx->flags & TFA98XX_FLAG_CALIBRATION_CTL) { + name = devm_kzalloc(tfa98xx->component->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s Calibration", 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_cal_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_cal_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_cal_ctl; + mix_index++; + } + + return snd_soc_add_component_controls(tfa98xx->component, + 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_component_get_dapm(tfa98xx->component); + 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)); + } +} + +/* I2C wrapper functions */ +enum Tfa98xx_Error tfa98xx_write_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + 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!!" : ""); + return error; +} + +enum Tfa98xx_Error tfa98xx_read_register16(struct tfa_device *tfa, + 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 (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + 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!!" : ""); + + return error; +} + + +/* + * init external dsp + */ +enum Tfa98xx_Error +tfa98xx_init_dsp(struct tfa_device *tfa) +{ + return Tfa98xx_Error_Not_Supported; +} + +int tfa98xx_get_dsp_status(struct tfa_device *tfa) +{ + return 0; +} + +/* + * write external dsp message + */ +enum Tfa98xx_Error +tfa98xx_write_dsp(struct tfa_device *tfa, int num_bytes, const char *command_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} + +/* + * read external dsp message + */ +enum Tfa98xx_Error +tfa98xx_read_dsp(struct tfa_device *tfa, int num_bytes, unsigned char *result_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} +/* + * write/read external dsp message + */ +enum Tfa98xx_Error +tfa98xx_writeread_dsp(struct tfa_device *tfa, int command_length, void *command_buffer, + int result_length, void *result_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} + +enum Tfa98xx_Error tfa98xx_read_data(struct tfa_device *tfa, + 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 (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + if (tfa98xx->i2c) { + 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(struct tfa_device *tfa, + int len, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + +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; + + return error; +} + +/* Interrupts management */ + +static void tfa98xx_interrupt_enable_tfa2(struct tfa98xx *tfa98xx, bool enable) +{ + /* Only for 0x72 we need to enable NOCLK interrupts */ + if (tfa98xx->flags & TFA98XX_FLAG_REMOVE_PLOP_NOISE) + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_stnoclk, enable); + + if (tfa98xx->flags & TFA98XX_FLAG_LP_MODES) { + tfa_irq_ena(tfa98xx->tfa, 36, enable); /* FIXME: IELP0 does not excist for 9912 */ + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_stclpr, enable); + } +} + +/* 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 enable = false; + + /* Support tap-detection on TFA1 family of devices */ + if ((tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) == 0) + return; + + if (tfa98xx->tapdet_open && + (tfa98xx->tapdet_profiles & (1 << tfa98xx->profile))) + enable = true; + + 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->component->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->component->dev, + "Interrupt for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx->profile); + /* enabled interrupt */ + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_sttapdet , enable); + } + + /* check disabled => enabled transition to clear pending events */ + if (!tfa98xx->tapdet_enabled && enable) { + /* clear pending event if any */ + tfa_irq_clear(tfa98xx->tfa, tfa9912_irq_sttapdet); + } + + if (!tfa98xx->tapdet_poll) + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_sttapdet, 1); /* enable again */ +} + +/* global enable / disable interrupts */ +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx->tfa->tfa_family == 2) + tfa98xx_interrupt_enable_tfa2(tfa98xx, enable); +} + +/* Firmware management */ +static void tfa98xx_container_loaded(const struct firmware *cont, void *context) +{ + nxpTfaContainer_t *container; + struct tfa98xx *tfa98xx = context; + enum Tfa98xx_Error tfa_err; + int container_size; + int ret; + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + if (!cont) { + pr_err("Failed to read %s\n", fw_name); + return; + } + + pr_info("loaded %s - size: %zu\n", fw_name, cont->size); + + mutex_lock(&tfa98xx_mutex); + if (tfa98xx_container == NULL) { + container = kzalloc(cont->size, GFP_KERNEL); + if (container == NULL) { + mutex_unlock(&tfa98xx_mutex); + release_firmware(cont); + pr_err("Error allocating memory\n"); + return; + } + + container_size = cont->size; + memcpy(container, cont->data, container_size); + release_firmware(cont); + + pr_info("%.2s%.2s\n", container->version, container->subversion); + pr_info("%.8s\n", container->customer); + pr_info("%.8s\n", container->application); + pr_info("%.8s\n", container->type); + pr_info("%d ndev\n", container->ndev); + pr_info("%d nprof\n", container->nprof); + + tfa_err = (enum Tfa98xx_Error)(tfa_load_cnt(container, container_size)); + if (tfa_err != Tfa98xx_Error_Ok) { + mutex_unlock(&tfa98xx_mutex); + kfree(container); + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return; + } + + tfa98xx_container = container; + } else { + pr_info("container file already loaded...\n"); + container = tfa98xx_container; + release_firmware(cont); + } + mutex_unlock(&tfa98xx_mutex); + + tfa98xx->tfa->cnt = container; + + /* + i2c transaction limited to 64k + (Documentation/i2c/writing-clients) + */ + tfa98xx->tfa->buffer_size = 65536; + + /* DSP messages via i2c */ + tfa98xx->tfa->has_msg = 0; + + if (tfa_dev_probe(tfa98xx->i2c->addr, tfa98xx->tfa) != 0) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return; + } + + tfa98xx->tfa->dev_idx = tfa_cont_get_idx(tfa98xx->tfa); + if (tfa98xx->tfa->dev_idx < 0) { + dev_err(tfa98xx->dev, "Failed to find TFA98xx @ 0x%.2x in container file\n", tfa98xx->i2c->addr); + return; + } + + /* Enable debug traces */ + tfa98xx->tfa->verbose = trace_level & 1; + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->tfa, tfa98xx->fw.name); + + /* set default profile/vstep */ + tfa98xx->profile = 0; + tfa98xx->vstep = 0; + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + int nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + for (i = 0; i < nprof; i++) { + if (strcmp(tfa_cont_profile_name(tfa98xx, 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 >= nprof) + 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_info("Firmware init complete\n"); + + if (no_start != 0) + return; + + /* Only controls for master device */ + if (tfa98xx->tfa->dev_idx == 0) + tfa98xx_create_controls(tfa98xx); + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa_is_cold(tfa98xx->tfa) == 0) { + pr_debug("Warning: device 0x%.2x is still warm\n", tfa98xx->i2c->addr); + tfa_reset(tfa98xx->tfa); + } + + /* Preload settings using internal clock on TFA2 */ + if (tfa98xx->tfa->tfa_family == 2) { + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (ret == Tfa98xx_Error_Not_Supported) + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + mutex_unlock(&tfa98xx->dsp_lock); + } + + tfa98xx_interrupt_enable(tfa98xx, true); +} + +static int tfa98xx_load_container(struct tfa98xx *tfa98xx) +{ + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_PENDING; + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + fw_name, tfa98xx->dev, GFP_KERNEL, + tfa98xx, tfa98xx_container_loaded); +} + + +static void tfa98xx_tapdet(struct tfa98xx *tfa98xx) +{ + unsigned int tap_pattern; + int btn; + + /* check tap pattern (BTN_0 is "error" wrong tap indication */ + tap_pattern = tfa_get_tap_pattern(tfa98xx->tfa); + switch (tap_pattern) { + case 0xffffffff: + pr_info("More than 4 taps detected! (flagTapPattern = -1)\n"); + btn = BTN_0; + break; + case 0xfffffffe: + case 0xfe: + pr_info("Illegal tap detected!\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", tap_pattern); + btn = BTN_0 + tap_pattern; + break; + } + + input_report_key(tfa98xx->input, btn, 1); + input_report_key(tfa98xx->input, btn, 0); + input_sync(tfa98xx->input); + + /* acknowledge event done by clearing interrupt */ + +} + +static void tfa98xx_tapdet_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + + //TODO check is this is still needed for tap polling + tfa98xx = container_of(work, struct tfa98xx, tapdet_work.work); + + if ( tfa_irq_get(tfa98xx->tfa, tfa9912_irq_sttapdet)) + 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; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + tfa98xx = container_of(work, struct tfa98xx, monitor_work.work); + + /* Check for tap-detection - bypass monitor if it is active */ + if (!tfa98xx->input) { + mutex_lock(&tfa98xx->dsp_lock); + error = tfa_status(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + if (error == Tfa98xx_Error_DSP_not_running) { + if (tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_RECOVER; + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->init_work, 0); + } + } + } + + /* 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; + bool sync= false; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + pr_debug("Skipping tfa_dev_start (no FW: %d)\n", tfa98xx->dsp_fw_state); + return; + } + + if(tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + pr_debug("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->vstep); + if (ret == Tfa98xx_Error_Not_Supported) { + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + dev_err(&tfa98xx->i2c->dev, "Failed starting device\n"); + failed = true; + } else 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_dev_start failed! (err %d) - %d\n", + ret, tfa98xx->init_count); + reschedule = true; + } else { + sync = true; + + /* Subsystem ready, tfa init complete */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + dev_info(&tfa98xx->i2c->dev, + "tfa_dev_start success (%d)\n", + tfa98xx->init_count); + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + } + } 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); + + if (sync) { + /* check if all devices have started */ + bool do_sync; + mutex_lock(&tfa98xx_mutex); + + if (tfa98xx_sync_count < tfa98xx_device_count) + tfa98xx_sync_count++; + + do_sync = (tfa98xx_sync_count >= tfa98xx_device_count); + mutex_unlock(&tfa98xx_mutex); + + /* when all devices have started then unmute */ + if (do_sync) { + tfa98xx_sync_count = 0; + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + + /* + * start monitor thread to check IC status bit + * periodically, and re-init IC to recover if + * needed. + */ + if (tfa98xx->tfa->tfa_family == 1) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, + 1*HZ); + 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); + + tfa98xx_dsp_init(tfa98xx); +} + +static void tfa98xx_interrupt(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, interrupt_work.work); + + pr_info("\n"); + + if (tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) { + /* check for tap interrupt */ + if (tfa_irq_get(tfa98xx->tfa, tfa9912_irq_sttapdet)) { + tfa98xx_tapdet(tfa98xx); + + /* clear interrupt */ + tfa_irq_clear(tfa98xx->tfa, tfa9912_irq_sttapdet); + } + } /* TFA98XX_FLAG_TAPDET_AVAILABLE */ + + if (tfa98xx->flags & TFA98XX_FLAG_REMOVE_PLOP_NOISE) { + int start_triggered; + + mutex_lock(&tfa98xx->dsp_lock); + start_triggered = tfa_plop_noise_interrupt(tfa98xx->tfa, tfa98xx->profile, tfa98xx->vstep); + /* Only enable when the return value is 1, otherwise the interrupt is triggered twice */ + if(start_triggered) + tfa98xx_interrupt_enable(tfa98xx, true); + mutex_unlock(&tfa98xx->dsp_lock); + } /* TFA98XX_FLAG_REMOVE_PLOP_NOISE */ + + if (tfa98xx->flags & TFA98XX_FLAG_LP_MODES) { + tfa_lp_mode_interrupt(tfa98xx->tfa); + } /* TFA98XX_FLAG_LP_MODES */ + + /* unmask interrupts masked in IRQ handler */ + tfa_irq_unmask(tfa98xx->tfa); +} + +static int tfa98xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + unsigned int sr; + int len, prof, nprof, idx = 0; + char *basename; + u64 formats; + int err; + + /* + * Support CODEC to CODEC links, + * these are called with a NULL runtime pointer. + */ + if (!substream->runtime) + return 0; + + if (pcm_no_constraint != 0) + return 0; + + switch (pcm_sample_format) { + case 1: + formats = SNDRV_PCM_FMTBIT_S24_LE; + break; + case 2: + formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } + + err = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, formats); + if (err < 0) + return err; + + if (no_start != 0) + return 0; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + dev_info(component->dev, "Container file not loaded\n"); + return -EINVAL; + } + + basename = kzalloc(MAX_CONTROL_NAME, GFP_KERNEL); + if (!basename) + return -ENOMEM; + + /* copy profile name into basename until the . */ + get_profile_basename(basename, tfa_cont_profile_name(tfa98xx, tfa98xx->profile)); + len = strlen(basename); + + /* loop over all profiles and get the supported samples rate(s) from + * the profiles with the same basename + */ + nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + tfa98xx->rate_constraint.list = &tfa98xx->rate_constraint_list[0]; + tfa98xx->rate_constraint.count = 0; + for (prof = 0; prof < nprof; prof++) { + if (0 == strncmp(basename, tfa_cont_profile_name(tfa98xx, prof), len)) { + /* Check which sample rate is supported with current profile, + * and enforce this. + */ + sr = tfa98xx_get_profile_sr(tfa98xx->tfa, prof); + if (!sr) + dev_info(component->dev, "Unable to identify supported sample rate\n"); + + if (tfa98xx->rate_constraint.count >= TFA98XX_NUM_RATES) { + dev_err(component->dev, "too many sample rates\n"); + } else { + tfa98xx->rate_constraint_list[idx++] = sr; + tfa98xx->rate_constraint.count += 1; + } + } + } + + kfree(basename); + +return 0; + +// 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 snd_soc_component *component = codec_dai->component; + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + + tfa98xx->sysclk = freq; + return 0; +} + +static int tfa98xx_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + pr_debug("\n"); + return 0; +} + +static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + + pr_info("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(component->dev, "Invalid Codec master mode\n"); + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_PDM: + break; + default: + dev_err(component->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_component *component = dai->component; + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + unsigned int rate; + int prof_idx; + + /* Supported */ + rate = params_rate(params); + pr_info("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 (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_info("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_component *component = dai->component; + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + + dev_info(&tfa98xx->i2c->dev, "%s: state: %d\n", __func__, mute); + + if (no_start) { + pr_info("no_start parameter set no tfa_dev_start or tfa_dev_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; + + mutex_lock(&tfa98xx_mutex); + tfa98xx_sync_count = 0; + mutex_unlock(&tfa98xx_mutex); + + 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_dev_stop(tfa98xx->tfa); + 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; + +#if 1 + if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING) + tfa98xx_dsp_init(tfa98xx); +#else + /* Start DSP */ + if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, 0); +#endif + } + + 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, + .set_tdm_slot = tfa98xx_set_tdm_slot, + .hw_params = tfa98xx_hw_params, + .mute_stream = tfa98xx_mute, +}; + +static struct snd_soc_dai_driver tfa98xx_dai[] = { + { + .name = "tfa98xx-aif", + .id = 1, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + .capture = { + .stream_name = "AIF Capture", + .channels_min = 1, + .channels_max = 4, + .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_component *component) +{ + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + int ret; + + pr_debug("\n"); + + /* 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->component = component; + + ret = tfa98xx_load_container(tfa98xx); + pr_info("Container loading requested: %d\n", ret); + + tfa98xx_add_widgets(tfa98xx); + + dev_info(component->dev, "tfa98xx codec registered (%s)", + tfa98xx->fw.name); + +//suzhiguang,store for calibration + g_tfa98xx = tfa98xx; + return ret; +} + +static void tfa98xx_remove(struct snd_soc_component *component) +{ + struct tfa98xx *tfa98xx = snd_soc_component_get_drvdata(component); + pr_debug("\n"); + + tfa98xx_interrupt_enable(tfa98xx, false); + + 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; +} + +static struct snd_soc_component_driver soc_codec_dev_tfa98xx = { + .name = "tfa98xx_codec", + .probe = tfa98xx_probe, + .remove = tfa98xx_remove, +}; + + +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) +{ + pr_info("\n"); + + /* + * mask interrupts + * will be unmasked after handling interrupts in workqueue + */ + tfa_irq_mask(tfa98xx->tfa); + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->interrupt_work, 0); +} + + +static irqreturn_t tfa98xx_irq(int irq, void *data) +{ + struct tfa98xx *tfa98xx = data; + + if (tfa98xx->tfa->tfa_family == 2) + tfa98xx_irq_tfa2(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); + mdelay(1); + gpio_set_value_cansleep(tfa98xx->reset_gpio, 0); + mdelay(1); + } + 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_debug("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_debug("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); + + /* the number of data bytes written without the register address */ + return ((ret > 1) ? count : -EIO); +} + +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 transaction */ + /* return the number of bytes read */ + return ((ret > 1) ? count : -EIO); +} + +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 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 tfa98xx *tfa98xx; + int ret; + int cal_profile = -1, mtpex_state = -1, spkr_count = 0; + int state ,err; + + if(g_tfa98xx == NULL) + { + pr_err("%s g_tfa98xx = NULL\n",__func__); + return 0; + } + + tfa98xx = g_tfa98xx; + + pr_err("tfa98xx_state_show\n"); + + cal_profile = tfaContGetCalProfile(tfa98xx->tfa); + + if (cal_profile >= 0) { + tfa98xx->profile = cal_profile; + } else { + cal_profile = 0; + pr_err("tfa get cal profile error\n"); + } + + mutex_lock(&tfa98xx->dsp_lock); + + state = tfa_dev_get_state(tfa98xx->tfa); + if (state != TFA_STATE_OPERATING) { + pr_err("tfa Calibration must be started in operating state!\n"); + goto err_exit_mutex; + } + + err = tfa_supported_speakers(tfa98xx->tfa, &spkr_count); + if (err) { + pr_err("tfa Getting the number of supported speakers failed\n"); + goto err_exit_mutex; + } + + err = tfaRunColdStartup(tfa98xx->tfa, cal_profile); + if (err) { + pr_err("tfa It is not possible to set device into cold-boot state!\n"); + goto err_exit_mutex; + } + + /* Startup the device, ending in initCF state */ + err = tfaRunSpeakerBoost(tfa98xx->tfa, 0, cal_profile); + if (err) { + pr_err("tfa It is not possible to start device up!\n"); + goto err_exit_mutex; + } + + /* Check if CF is not in bypass */ + if (!tfa_cf_enabled(tfa98xx->tfa)) { + pr_err("tfa It is not possible to calibrate with CF in bypass! \n"); + goto err_exit_mutex; + } + mtpex_state = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_EX); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_OPERATING); + if (tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_EX) ) { + if (mtpex_state == 1) { + pr_err("tfa DSP already calibrated.\n Calibration skipped, previous results loaded.\n"); + //mtpex_state= tfa_dev_mtp_get(devs[dev_idx], TFA_MTP_EX); + } + /* Go to the Operating state */ + err = tfa_dsp_get_calibration_impedance(tfa98xx->tfa); + } + else + { + /* Go to the Operating state */ + //tfa_dev_set_state(devs[dev_idx], TFA_STATE_OPERATING); + /* calibrate */ + err = tfaRunSpeakerCalibration(tfa98xx->tfa); + if (err) + goto err_exit_mutex; + } + +// if (tfa98xx->tfa->tfa_family == 2 && !tfa98xx->tfa->is_probus_device) { +// scaling_factor = 1024; +// } + +#if 0 + imp[0] = (float)tfa98xx->tfa->mohm[0] / scaling_factor; + + if (spkr_count == 1) { + pr_err("tfa Calibration value is: %2.2f ohm\n", imp[0]); + } + else { + imp[1] = (float)tfa98xx->tfa->mohm[1] / scaling_factor; + pr_err("tfa Calibration value is: %2.2f %2.2f ohm\n", imp[0], imp[1]); + } +#else + pr_err("tfa Calibration value is: %d ohm\n", tfa98xx->tfa->mohm[0]); +#endif + + mutex_unlock(&tfa98xx->dsp_lock); + ret = sprintf(buf,"%d:%d",2,tfa98xx->tfa->mohm[0]); + return ret; + +err_exit_mutex: + mutex_unlock(&tfa98xx->dsp_lock); + ret = sprintf(buf,"%d:%d",2,0); + return ret; + +} + +static struct device_attribute tfa98xx_state_attr = + __ATTR(calibra, 0444, tfa98xx_state_show, tfa98xx_state_store); + + +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("addr=0x%x\n", i2c->addr); + + 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->tfa = NULL; + + 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); + return ret; + } + + 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"); + return ret; + } + if (no_start) + tfa98xx->irq_gpio = -1; + if (no_reset) + tfa98xx->reset_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) { + pr_err("%s devm_gpio_request_one tfa98xx->reset_gpio failed\n",__func__); + return ret; + } + } + + if (gpio_is_valid(tfa98xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->irq_gpio, + GPIOF_DIR_IN, "TFA98XX_INT"); + if (ret) { + pr_err("%s devm_gpio_request_one tfa98xx->irq_gpio failed\n",__func__); + return ret; + } + } + + /* Power up! */ + tfa98xx_ext_reset(tfa98xx); + + if ((no_start == 0) && (no_reset == 0)) { + ret = regmap_read(tfa98xx->regmap, 0x03, ®); + if (ret < 0) { + pr_err("tfa failed to read Revision register: %d\n", + ret); + //suzhiguang + if (gpio_is_valid(tfa98xx->reset_gpio)) { + gpio_free(tfa98xx->reset_gpio); + } + if (gpio_is_valid(tfa98xx->irq_gpio)) { + gpio_free(tfa98xx->irq_gpio); + } + return -EIO; + } + switch (reg & 0xff) { + case 0x72: /* tfa9872 */ + pr_info("TFA9872 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_CALIBRATION_CTL; + tfa98xx->flags |= TFA98XX_FLAG_REMOVE_PLOP_NOISE; + /* tfa98xx->flags |= TFA98XX_FLAG_LP_MODES; */ + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + case 0x74: /* tfa9874 */ + pr_info("TFA9874 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_CALIBRATION_CTL; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + case 0x88: /* tfa9888 */ + pr_info("TFA9888 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_STEREO_DEVICE; + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + case 0x13: /* tfa9912 */ + pr_info("TFA9912 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + /* tfa98xx->flags |= TFA98XX_FLAG_TAPDET_AVAILABLE; */ + smartpa_present = 1; + break; + case 0x94: /* tfa9894 */ + pr_info("TFA9894 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + case 0x80: /* tfa9890 */ + case 0x81: /* tfa9890 */ + pr_info("TFA9890 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + smartpa_present = 1; + break; + case 0x92: /* tfa9891 */ + pr_info("TFA9891 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SAAM_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + smartpa_present = 1; + break; + case 0x12: /* tfa9895 */ + pr_info("TFA9895 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + break; + case 0x97: + pr_info("TFA9897 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + case 0x96: + pr_info("TFA9896 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + smartpa_present = 1; + break; + default: + pr_info("Unsupported device revision (0x%x)\n", reg & 0xff); + return -EINVAL; + } + } + + tfa98xx->tfa = devm_kzalloc(&i2c->dev, sizeof(struct tfa_device), GFP_KERNEL); + if (tfa98xx->tfa == NULL) + return -ENOMEM; + + tfa98xx->tfa->data = (void *)tfa98xx; + tfa98xx->tfa->cachep = tfa98xx_cache; + + /* 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_component(&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); + return ret; + } + + 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); + return ret; + } + } 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 + if (no_start == 0) + tfa98xx_debug_init(tfa98xx, i2c); +#endif + + + 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__); + } + + + /* 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__); + + INIT_LIST_HEAD(&tfa98xx->list); + + mutex_lock(&tfa98xx_mutex); + tfa98xx_device_count++; + list_add(&tfa98xx->list, &tfa98xx_device_list); + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_i2c_remove(struct i2c_client *i2c) +{ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + pr_debug("addr=0x%x\n", i2c->addr); + + tfa98xx_interrupt_enable(tfa98xx, false); + + 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 + + snd_soc_unregister_component(&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); + + mutex_lock(&tfa98xx_mutex); + list_del(&tfa98xx->list); + tfa98xx_device_count--; + if (tfa98xx_device_count == 0) { + kfree(tfa98xx_container); + tfa98xx_container = NULL; + } + mutex_unlock(&tfa98xx_mutex); + + 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,tfa9872" }, + { .compatible = "nxp,tfa9874" }, + { .compatible = "nxp,tfa9888" }, + { .compatible = "nxp,tfa9890" }, + { .compatible = "nxp,tfa9891" }, + { .compatible = "nxp,tfa9894" }, + { .compatible = "nxp,tfa9895" }, + { .compatible = "nxp,tfa9896" }, + { .compatible = "nxp,tfa9897" }, + { .compatible = "nxp,tfa9912" }, + { }, +}; +#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 __init tfa98xx_i2c_init(void) +{ + int ret = 0; + + pr_info("TFA98XX driver version %s\n", TFA98XX_VERSION); + + /* Enable debug traces */ + tfa98xx_kmsg_regs = trace_level & 2; + tfa98xx_ftrace_regs = trace_level & 4; + + /* Initialize kmem_cache */ + tfa98xx_cache = kmem_cache_create("tfa98xx_cache", /* Cache name /proc/slabinfo */ + PAGE_SIZE, /* Structure size, we should fit in single page */ + 0, /* Structure alignment */ + (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD), /* Cache property */ + NULL); /* Object constructor */ + if (!tfa98xx_cache) { + pr_err("tfa98xx can't create memory pool\n"); + ret = -ENOMEM; + } + + 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); + kmem_cache_destroy(tfa98xx_cache); +} +module_exit(tfa98xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC TFA98XX driver"); +MODULE_LICENSE("GPL"); + diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa98xx.h b/techpack/audio/asoc/codecs/tfa9874/tfa98xx.h new file mode 100644 index 000000000000..82285c6c6fac --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa98xx.h @@ -0,0 +1,131 @@ +/* + * Copyright 2014-2017 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 __TFA98XX_INC__ +#define __TFA98XX_INC__ + +#include +#include +#include + +#include "tfa_device.h" +#include "tfa_container.h" +#include "config.h" + +/* max. length of a alsa mixer control name */ +#define MAX_CONTROL_NAME 48 + +#define TFA98XX_MAX_REGISTER 0xff + +#define TFA98XX_FLAG_SKIP_INTERRUPTS (1 << 0) +#define TFA98XX_FLAG_SAAM_AVAILABLE (1 << 1) +#define TFA98XX_FLAG_STEREO_DEVICE (1 << 2) +#define TFA98XX_FLAG_MULTI_MIC_INPUTS (1 << 3) +#define TFA98XX_FLAG_TAPDET_AVAILABLE (1 << 4) +#define TFA98XX_FLAG_CALIBRATION_CTL (1 << 5) +#define TFA98XX_FLAG_REMOVE_PLOP_NOISE (1 << 6) +#define TFA98XX_FLAG_LP_MODES (1 << 7) +#define TFA98XX_FLAG_TDM_DEVICE (1 << 8) + +#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_component *component; + 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 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; + + struct list_head list; + struct tfa_device *tfa; + int vstep; + int profile; + int prof_vsteps[TFACONT_MAXPROFS]; /* store vstep per profile (single device) */ + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + u8 reg; + unsigned int flags; + bool set_mtp_cal; + uint16_t cal_data; +}; + + +#endif /* __TFA98XX_INC__ */ + diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa98xx_genregs_N1C.h b/techpack/audio/asoc/codecs/tfa9874/tfa98xx_genregs_N1C.h new file mode 100644 index 000000000000..7a124b82b332 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/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<<6) +#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/techpack/audio/asoc/codecs/tfa9874/tfa98xx_parameters.h b/techpack/audio/asoc/codecs/tfa9874/tfa98xx_parameters.h new file mode 100644 index 000000000000..50f9a8c82914 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa98xx_parameters.h @@ -0,0 +1,741 @@ +/* + * Copyright 2013-2017 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. + */ + +/* + * 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 + fs_count // Should always be last item. +} nxpTfaSamplerate_t; + +// Keep in sync with nxpTfaSamplerate_t ! +static const int nxpTfaSamplerateHz[fs_count] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 96000 }; + + +/* + * 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 +* Note: This is not in line with smartstudio (JV: 12/12/2016) +*/ +typedef enum nxpTfaFilterType { + fCustom, //User defined biquad coefficients + 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]; +} 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; + +/* + * 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 ; + +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[]; +}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; + +#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 nxpTfaFWMsg { + struct nxpTfaFWVer fwVersion; + struct nxpTfaMsg payload; +}; + +typedef struct nxpTfaLiveData { + char name[25]; + char addrs[25]; + int tracker; + int scalefactor; +} 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 +}; + +/* + * parameter container file + */ +/* + * descriptors + * Note 1: append new DescriptorType at the end + * Note 2: add new descriptors to dsc_name[] in tfaContUtil.c + */ +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, + dscSetInputSelect, + dscSetOutputSelect, + dscSetProgramConfig, + dscSetLagW, + dscSetGains, + dscSetvBatFactors, + dscSetSensesCal, + dscSetSensesDelay, + dscBitfield, + dscDefault, // used to reset bitfields to there default values + dscLiveData, + dscLiveDataString, + dscGroup, + dscCmd, + dscSetMBDrc, + dscFilter, + dscNoInit, + dscFeatures, + dscCfMem, // coolflux memory x,y,io + dscSetFwkUseCase, + dscSetVddpConfig, + 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; + + +/* + * 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) + +#endif /* TFA98XXPARAMETERS_H_ */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa98xx_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa98xx_tfafieldnames.h new file mode 100644 index 000000000000..3f9a437df8b9 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa98xx_tfafieldnames.h @@ -0,0 +1,147 @@ +/* + * Copyright 2014-2017 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. + */ + +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" +#include "tfa9872_tfafieldnames.h" +#include "tfa9912_tfafieldnames.h" +#include "tfa9896_tfafieldnames.h" +#include "tfa9874_tfafieldnames.h" +#include "tfa9894_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_R25CL -1 +#define TFA1_BF_R25CR -1 +#define TFA1_BF_SWPROFIL 0x8045 /*!< profile save */ +#define TFA1_BF_SWVSTEP 0x80a5 /*!< vstep save */ + +/* missing 'common' defs break the build */ +#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 + + +/* TFA9891 specific bit field names */ +#define TFA1_BF_SAAMGAIN 0x2202 +#define TFA2_BF_SAAMGAIN -1 + +/* TFA9872 specific bit field names */ +#define TFA2_BF_IELP0 TFA9872_BF_IELP0 +#define TFA2_BF_ISTLP0 TFA9872_BF_ISTLP0 +#define TFA2_BF_IPOLP0 TFA9872_BF_IPOLP0 +#define TFA2_BF_IELP1 TFA9872_BF_IELP1 +#define TFA2_BF_ISTLP1 TFA9872_BF_ISTLP1 +#define TFA2_BF_IPOLP1 TFA9872_BF_IPOLP1 +#define TFA2_BF_LP0 TFA9872_BF_LP0 +#define TFA2_BF_LP1 TFA9872_BF_LP1 +#define TFA2_BF_R25C TFA9872_BF_R25C +#define TFA2_BF_SAMMODE TFA9872_BF_SAMMODE + +/* 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 +#define TFA1_BF_IECLKS -1 +#define TFA1_BF_ISTCLKS -1 +#define TFA1_BF_IPOCLKS -1 +#define TFA1_BF_IEAMPS -1 +#define TFA1_BF_ISTAMPS -1 +#define TFA1_BF_IPOAMPS -1 +#define TFA1_BF_IELP0 -1 +#define TFA1_BF_ISTLP0 -1 +#define TFA1_BF_IPOLP0 -1 +#define TFA1_BF_IELP1 -1 +#define TFA1_BF_ISTLP1 -1 +#define TFA1_BF_IPOLP1 -1 +#define TFA1_BF_LP0 -1 +#define TFA1_BF_LP1 -1 +#define TFA1_BF_R25C -1 +#define TFA1_BF_SAMMODE -1 + +/* TDM STATUS fields not available on TFA1 */ +#define TFA1_BF_TDMLUTER -1 +#define TFA1_BF_TDMERR -1 diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9912_device_genregs.h b/techpack/audio/asoc/codecs/tfa9874/tfa9912_device_genregs.h new file mode 100644 index 000000000000..6a4b7303e64e --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9912_device_genregs.h @@ -0,0 +1,255 @@ +/** Filename: tfa9912_device_genregs.h + * This file was generated automatically on 04/19/17 at 12:26:46. + * Source file: TFA9912_N1A_I2C_regmap_V1.39.xlsx + */ + +#ifndef _TFA9912_DEVICE_GENREGS_H +#define _TFA9912_DEVICE_GENREGS_H + + +#define TFA99XX_SYS_CONTROL0 0x00 +#define TFA99XX_SYS_CONTROL1 0x01 +#define TFA99XX_SYS_CONTROL2 0x02 +#define TFA99XX_DEVICE_REVISION 0x03 +#define TFA99XX_CLOCK_CONTROL 0x04 +#define TFA99XX_CLOCK_GATING_CONTROL 0x05 +#define TFA99XX_HW_PATH_CFG 0x06 +#define TFA99XX_CLKCHK_TH 0x07 +#define TFA99XX_AMP_CTRL 0x08 +#define TFA99XX_SIDE_TONE_CONFIG 0x0d +#define TFA99XX_CTRL_DIGTOANA_REG 0x0e +#define TFA99XX_STATUS_FLAGS0 0x10 +#define TFA99XX_STATUS_FLAGS1 0x11 +#define TFA99XX_STATUS_FLAGS3 0x13 +#define TFA99XX_STATUS_FLAGS4 0x14 +#define TFA99XX_BATTERY_VOLTAGE 0x15 +#define TFA99XX_TEMPERATURE 0x16 +#define TFA99XX_VDDP_VOLTAGE 0x17 +#define TFA99XX_TDM_CONFIG0 0x20 +#define TFA99XX_TDM_CONFIG1 0x21 +#define TFA99XX_TDM_CONFIG2 0x22 +#define TFA99XX_TDM_CONFIG3 0x23 +#define TFA99XX_TDM_CONFIG4 0x24 +#define TFA99XX_TDM_CONFIG5 0x25 +#define TFA99XX_TDM_CONFIG6 0x26 +#define TFA99XX_TDM_CONFIG7 0x27 +#define TFA99XX_TDM_CONFIG8 0x28 +#define TFA99XX_TDM_CONFIG9 0x29 +#define TFA99XX_PDM_CONFIG0 0x31 +#define TFA99XX_PDM_CONFIG1 0x32 +#define TFA99XX_INTERRUPT_OUT_REG1 0x40 +#define TFA99XX_INTERRUPT_OUT_REG2 0x41 +#define TFA99XX_INTERRUPT_OUT_REG3 0x42 +#define TFA99XX_INTERRUPT_IN_REG1 0x44 +#define TFA99XX_INTERRUPT_IN_REG2 0x45 +#define TFA99XX_INTERRUPT_IN_REG3 0x46 +#define TFA99XX_INTERRUPT_ENABLE_REG1 0x48 +#define TFA99XX_INTERRUPT_ENABLE_REG2 0x49 +#define TFA99XX_INTERRUPT_ENABLE_REG3 0x4a +#define TFA99XX_STATUS_POLARITY_REG1 0x4c +#define TFA99XX_STATUS_POLARITY_REG2 0x4d +#define TFA99XX_STATUS_POLARITY_REG3 0x4e +#define TFA99XX_BAT_PROT_CONFIG 0x50 +#define TFA99XX_AUDIO_CONTROL 0x51 +#define TFA99XX_AMPLIFIER_CONFIG 0x52 +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL0 0x53 +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL1 0x54 +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL2 0x55 +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL4 0x57 +#define TFA99XX_KEY1_PROTECTED_PWM_CONFIG 0x58 +#define TFA99XX_CF_TAP_STATUS_0 0x5c +#define TFA99XX_CF_TAP_STATUS_1 0x5d +#define TFA99XX_TAP_CONTROL 0x5f +#define TFA99XX_PGA_CONTROL0 0x60 +#define TFA99XX_GAIN_ATT 0x61 +#define TFA99XX_LOW_NOISE_GAIN1 0x62 +#define TFA99XX_LOW_NOISE_GAIN2 0x63 +#define TFA99XX_MODE1_DETECTOR1 0x64 +#define TFA99XX_MODE1_DETECTOR2 0x65 +#define TFA99XX_BST_PFM_CTRL 0x66 +#define TFA99XX_LOW_POWER_CTRL 0x67 +#define TFA99XX_TDM_SOURCE_CTRL 0x68 +#define TFA99XX_SAM_CTRL 0x69 +#define TFA99XX_RST_MIN_VBAT_CTRL 0x6a +#define TFA99XX_SYS_CONTROL3 0x6b +#define TFA99XX_STATUS_FLAGS5 0x6e +#define TFA99XX_DCDC_CONTROL0 0x70 +#define TFA99XX_KEY1_PROTECTED_DCDC_CONTROL3 0x73 +#define TFA99XX_DCDC_CONTROL4 0x74 +#define TFA99XX_DCDC_CONTROL5 0x75 +#define TFA99XX_DCDC_CONTROL6 0x76 +#define TFA99XX_KEY2_PROTECTED_DCDC_CONTROL7 0x77 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG0 0x80 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG2 0x82 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG3 0x83 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG4 0x84 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG5 0x85 +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG7 0x87 +#define TFA99XX_KEY2_PROTECTED_VOLSENSE_CONFIG 0x88 +#define TFA99XX_CURSENSE_CONFIG 0x89 +#define TFA99XX_CF_CONTROLS 0x90 +#define TFA99XX_CF_MAD 0x91 +#define TFA99XX_CF_MEM 0x92 +#define TFA99XX_CF_STATUS 0x93 +#define TFA99XX_MTPKEY1_REG 0xa0 +#define TFA99XX_MTPKEY2_REG 0xa1 +#define TFA99XX_MTP_STATUS 0xa2 +#define TFA99XX_KEY_PROTECTED_MTP_CONTROL 0xa3 +#define TFA99XX_KEY1_PROTECTED_FAIM_CONTROL 0xa4 +#define TFA99XX_MTP_DATA_OUT_MSB 0xa5 +#define TFA99XX_MTP_DATA_OUT_LSB 0xa6 +#define TFA99XX_KEY1_PROTECTED_PROTECTION_CONFIG 0xb0 +#define TFA99XX_TEMP_SENSOR_CONFIG 0xb1 +#define TFA99XX_KEY1_PROTECTED_DIRECT_CONTROL0 0xc0 +#define TFA99XX_KEY1_PROTECTED_DIRECT_CONTROL1 0xc1 +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG0 0xc3 +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG1 0xc4 +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG2 0xc5 +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG3 0xc6 +#define TFA99XX_KEY1_PROTECTED_DIGIMUX_CONTROL1 0xc8 +#define TFA99XX_KEY1_PROTECTED_ANAMUX_CONTROL0 0xca +#define TFA99XX_KEY1_PROTECTED_ANAMUX_CONTROL1 0xcb +#define TFA99XX_KEY1_PROTECTED_PLL_TEST0 0xcd +#define TFA99XX_KEY1_PROTECTED_PLL_TEST3 0xd0 +#define TFA99XX_KEY1_PROTECTED_TSIG_CONTROL1 0xd2 +#define TFA99XX_KEY1_PROTECTED_ADC10_CONTROL 0xd3 +#define TFA99XX_KEY1_PROTECTED_ADC10_DATA 0xd4 +#define TFA99XX_KEY2_PROTECTED_CTRL_DIGTOANA 0xd5 +#define TFA99XX_KEY1_PROTECTED_CLKDIV_CONTROL 0xd6 +#define TFA99XX_KEY1_PROTECTED_IO_CONFIG2 0xd7 +#define TFA99XX_KEY1_PROTECTED_TEST_CTRL1 0xd8 +#define TFA99XX_KEY1_PROTECTED_MODE_OVERRULE 0xd9 +#define TFA99XX_KEY1_PROTECTED_FRO8_CALIB_CTRL 0xed +#define TFA99XX_SOFTWARE_PROFILE 0xee +#define TFA99XX_SOFTWARE_VSTEP 0xef +#define TFA99XX_KEY2_PROTECTED_MTP0 0xf0 +#define TFA99XX_KEY1_PROTECTED_MTP2 0xf2 +#define TFA99XX_KEY2_PROTECTED_MTP4 0xf4 +#define TFA99XX_KEY1_PROTECTED_MTP6 0xf6 +#define TFA99XX_KEY1_PROTECTED_MTP7 0xf7 +#define TFA99XX_KEY1_PROTECTED_MTP9 0xf9 +#define TFA99XX_KEY1_PROTECTED_MTPF 0xff +#define TFA99XX_SYS_CONTROL0_POR +#define TFA99XX_SYS_CONTROL1_POR +#define TFA99XX_SYS_CONTROL2_POR +#define TFA99XX_DEVICE_REVISION_POR +#define TFA99XX_CLOCK_CONTROL_POR +#define TFA99XX_CLOCK_GATING_CONTROL_POR +#define TFA99XX_HW_PATH_CFG_POR +#define TFA99XX_CLKCHK_TH_POR +#define TFA99XX_AMP_CTRL_POR +#define TFA99XX_SIDE_TONE_CONFIG_POR +#define TFA99XX_CTRL_DIGTOANA_REG_POR +#define TFA99XX_STATUS_FLAGS0_POR +#define TFA99XX_STATUS_FLAGS1_POR +#define TFA99XX_STATUS_FLAGS3_POR +#define TFA99XX_STATUS_FLAGS4_POR +#define TFA99XX_BATTERY_VOLTAGE_POR +#define TFA99XX_TEMPERATURE_POR +#define TFA99XX_VDDP_VOLTAGE_POR +#define TFA99XX_TDM_CONFIG0_POR +#define TFA99XX_TDM_CONFIG1_POR +#define TFA99XX_TDM_CONFIG2_POR +#define TFA99XX_TDM_CONFIG3_POR +#define TFA99XX_TDM_CONFIG4_POR +#define TFA99XX_TDM_CONFIG5_POR +#define TFA99XX_TDM_CONFIG6_POR +#define TFA99XX_TDM_CONFIG7_POR +#define TFA99XX_TDM_CONFIG8_POR +#define TFA99XX_TDM_CONFIG9_POR +#define TFA99XX_PDM_CONFIG0_POR +#define TFA99XX_PDM_CONFIG1_POR +#define TFA99XX_INTERRUPT_OUT_REG1_POR +#define TFA99XX_INTERRUPT_OUT_REG2_POR +#define TFA99XX_INTERRUPT_OUT_REG3_POR +#define TFA99XX_INTERRUPT_IN_REG1_POR +#define TFA99XX_INTERRUPT_IN_REG2_POR +#define TFA99XX_INTERRUPT_IN_REG3_POR +#define TFA99XX_INTERRUPT_ENABLE_REG1_POR +#define TFA99XX_INTERRUPT_ENABLE_REG2_POR +#define TFA99XX_INTERRUPT_ENABLE_REG3_POR +#define TFA99XX_STATUS_POLARITY_REG1_POR +#define TFA99XX_STATUS_POLARITY_REG2_POR +#define TFA99XX_STATUS_POLARITY_REG3_POR +#define TFA99XX_BAT_PROT_CONFIG_POR +#define TFA99XX_AUDIO_CONTROL_POR +#define TFA99XX_AMPLIFIER_CONFIG_POR +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL0_POR +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL1_POR +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL2_POR +#define TFA99XX_KEY1_PROTECTED_AMPLIFIER_CONTROL4_POR +#define TFA99XX_KEY1_PROTECTED_PWM_CONFIG_POR +#define TFA99XX_CF_TAP_STATUS_0_POR +#define TFA99XX_CF_TAP_STATUS_1_POR +#define TFA99XX_TAP_CONTROL_POR +#define TFA99XX_PGA_CONTROL0_POR +#define TFA99XX_GAIN_ATT_POR +#define TFA99XX_LOW_NOISE_GAIN1_POR +#define TFA99XX_LOW_NOISE_GAIN2_POR +#define TFA99XX_MODE1_DETECTOR1_POR +#define TFA99XX_MODE1_DETECTOR2_POR +#define TFA99XX_BST_PFM_CTRL_POR +#define TFA99XX_LOW_POWER_CTRL_POR +#define TFA99XX_TDM_SOURCE_CTRL_POR +#define TFA99XX_SAM_CTRL_POR +#define TFA99XX_RST_MIN_VBAT_CTRL_POR +#define TFA99XX_SYS_CONTROL3_POR +#define TFA99XX_STATUS_FLAGS5_POR +#define TFA99XX_DCDC_CONTROL0_POR +#define TFA99XX_KEY1_PROTECTED_DCDC_CONTROL3_POR +#define TFA99XX_DCDC_CONTROL4_POR +#define TFA99XX_DCDC_CONTROL5_POR +#define TFA99XX_DCDC_CONTROL6_POR +#define TFA99XX_KEY2_PROTECTED_DCDC_CONTROL7_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG0_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG2_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG3_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG4_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG5_POR +#define TFA99XX_KEY2_PROTECTED_CURSENSE_CONFIG7_POR +#define TFA99XX_KEY2_PROTECTED_VOLSENSE_CONFIG_POR +#define TFA99XX_CURSENSE_CONFIG_POR +#define TFA99XX_CF_CONTROLS_POR +#define TFA99XX_CF_MAD_POR +#define TFA99XX_CF_MEM_POR +#define TFA99XX_CF_STATUS_POR +#define TFA99XX_MTPKEY1_REG_POR +#define TFA99XX_MTPKEY2_REG_POR +#define TFA99XX_MTP_STATUS_POR +#define TFA99XX_KEY_PROTECTED_MTP_CONTROL_POR +#define TFA99XX_KEY1_PROTECTED_FAIM_CONTROL_POR +#define TFA99XX_MTP_DATA_OUT_MSB_POR +#define TFA99XX_MTP_DATA_OUT_LSB_POR +#define TFA99XX_KEY1_PROTECTED_PROTECTION_CONFIG_POR +#define TFA99XX_TEMP_SENSOR_CONFIG_POR +#define TFA99XX_KEY1_PROTECTED_DIRECT_CONTROL0_POR +#define TFA99XX_KEY1_PROTECTED_DIRECT_CONTROL1_POR +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG0_POR +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG1_POR +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG2_POR +#define TFA99XX_KEY1_PROTECTED_TEST_CONFIG3_POR +#define TFA99XX_KEY1_PROTECTED_DIGIMUX_CONTROL1_POR +#define TFA99XX_KEY1_PROTECTED_ANAMUX_CONTROL0_POR +#define TFA99XX_KEY1_PROTECTED_ANAMUX_CONTROL1_POR +#define TFA99XX_KEY1_PROTECTED_PLL_TEST0_POR +#define TFA99XX_KEY1_PROTECTED_PLL_TEST3_POR +#define TFA99XX_KEY1_PROTECTED_TSIG_CONTROL1_POR +#define TFA99XX_KEY1_PROTECTED_ADC10_CONTROL_POR +#define TFA99XX_KEY1_PROTECTED_ADC10_DATA_POR +#define TFA99XX_KEY2_PROTECTED_CTRL_DIGTOANA_POR +#define TFA99XX_KEY1_PROTECTED_CLKDIV_CONTROL_POR +#define TFA99XX_KEY1_PROTECTED_IO_CONFIG2_POR +#define TFA99XX_KEY1_PROTECTED_TEST_CTRL1_POR +#define TFA99XX_KEY1_PROTECTED_MODE_OVERRULE_POR +#define TFA99XX_KEY1_PROTECTED_FRO8_CALIB_CTRL_POR +#define TFA99XX_SOFTWARE_PROFILE_POR +#define TFA99XX_SOFTWARE_VSTEP_POR +#define TFA99XX_KEY2_PROTECTED_MTP0_POR +#define TFA99XX_KEY1_PROTECTED_MTP2_POR +#define TFA99XX_KEY2_PROTECTED_MTP4_POR +#define TFA99XX_KEY1_PROTECTED_MTP6_POR +#define TFA99XX_KEY1_PROTECTED_MTP7_POR +#define TFA99XX_KEY1_PROTECTED_MTP9_POR +#define TFA99XX_KEY1_PROTECTED_MTPF_POR + +#endif /* _TFA9912_DEVICE_GENREGS_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa9912_tfafieldnames.h b/techpack/audio/asoc/codecs/tfa9874/tfa9912_tfafieldnames.h new file mode 100644 index 000000000000..bc4da8ab048b --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa9912_tfafieldnames.h @@ -0,0 +1,1764 @@ +/** Filename: tfa9912_tfafieldnames.h + * This file was generated automatically on 06/14/17 at 18:04:05. + * Source file: TFA9912_N1A_I2C_regmap_V1.41.xlsx + */ + +#ifndef _TFA9912_TFAFIELDNAMES_H +#define _TFA9912_TFAFIELDNAMES_H + + +#define TFA9912_I2CVERSION 1.41 + +typedef enum nxpTfa9912BfEnumList { + TFA9912_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9912_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9912_BF_CFE = 0x0020, /*!< Enable CoolFlux */ + TFA9912_BF_AMPE = 0x0030, /*!< Enables the Amplifier */ + TFA9912_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9912_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA9912_BF_AMPC = 0x0060, /*!< CoolFlux controls amplifier */ + TFA9912_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9912_BF_FSSSEL= 0x0090, /*!< Audio sample reference */ + TFA9912_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9912_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9912_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA9912_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9912_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA9912_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9912_BF_MANROBOD= 0x0150, /*!< Reaction on BOD */ + TFA9912_BF_BODE = 0x0160, /*!< BOD Enable */ + TFA9912_BF_BODHYS= 0x0170, /*!< BOD Hysteresis */ + TFA9912_BF_BODFILT= 0x0181, /*!< BOD filter */ + TFA9912_BF_BODTHLVL= 0x01a1, /*!< BOD threshold */ + TFA9912_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9912_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA9912_BF_MANWDE= 0x01f0, /*!< Watchdog enable */ + TFA9912_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9912_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9912_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9912_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA9912_BF_TDMC = 0x02c0, /*!< TDM Compatibility with TFA9872 */ + TFA9912_BF_ENBLADC10= 0x02e0, /*!< ADC10 Enable - I2C direct mode */ + TFA9912_BF_REV = 0x030f, /*!< Revision info */ + TFA9912_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9912_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9912_BF_ENCFCKSEL= 0x0430, /*!< Coolflux DSP clock scaling, low power mode */ + TFA9912_BF_CFCKSEL= 0x0441, /*!< Coolflux DSP clock scaler selection for low power mode */ + TFA9912_BF_TDMINFSEL= 0x0460, /*!< TDM clock selection */ + TFA9912_BF_DISBLAUTOCLKSEL= 0x0470, /*!< Disable Automatic dsp clock source selection */ + TFA9912_BF_SELCLKSRC= 0x0480, /*!< I2C selection of DSP clock when auto select is disabled */ + TFA9912_BF_SELTIMSRC= 0x0490, /*!< I2C selection of Watchdog and Timer clock */ + TFA9912_BF_SSLEFTE= 0x0500, /*!< */ + TFA9912_BF_SPKSSEN= 0x0510, /*!< Enable speaker path */ + TFA9912_BF_VSLEFTE= 0x0520, /*!< */ + TFA9912_BF_VSRIGHTE= 0x0530, /*!< Voltage sense */ + TFA9912_BF_CSLEFTE= 0x0540, /*!< */ + TFA9912_BF_CSRIGHTE= 0x0550, /*!< Current sense */ + TFA9912_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA9912_BF_PGALE = 0x0570, /*!< Enable PGA chop clock for left channel */ + TFA9912_BF_PGARE = 0x0580, /*!< Enable PGA chop clock */ + TFA9912_BF_SSTDME= 0x0590, /*!< Sub-system TDM */ + TFA9912_BF_SSPBSTE= 0x05a0, /*!< Sub-system boost */ + TFA9912_BF_SSADCE= 0x05b0, /*!< Sub-system ADC */ + TFA9912_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9912_BF_SSCFTIME= 0x05d0, /*!< CF Sub-system timer */ + TFA9912_BF_SSCFWDTE= 0x05e0, /*!< CF Sub-system WDT */ + TFA9912_BF_FAIMVBGOVRRL= 0x05f0, /*!< Over rule of vbg for FaIM access */ + TFA9912_BF_SAMSPKSEL= 0x0600, /*!< Input selection for TAP/SAM */ + TFA9912_BF_PDM2IISEN= 0x0610, /*!< PDM2IIS Bridge enable */ + TFA9912_BF_TAPRSTBYPASS= 0x0620, /*!< Tap decimator reset bypass - Bypass the decimator reset from tapdec */ + TFA9912_BF_CARDECISEL0= 0x0631, /*!< Cardec input 0 sel */ + TFA9912_BF_CARDECISEL1= 0x0651, /*!< Cardec input sel */ + TFA9912_BF_TAPDECSEL= 0x0670, /*!< Select TAP/Cardec for TAP */ + TFA9912_BF_COMPCOUNT= 0x0680, /*!< Comparator o/p filter selection */ + TFA9912_BF_STARTUPMODE= 0x0691, /*!< Startup Mode Selection */ + TFA9912_BF_AUTOTAP= 0x06b0, /*!< Enable auto tap switching */ + TFA9912_BF_COMPINITIME= 0x06c1, /*!< Comparator initialization time to be used in Tap Machine */ + TFA9912_BF_ANAPINITIME= 0x06e1, /*!< Analog initialization time to be used in Tap Machine */ + TFA9912_BF_CCHKTH= 0x0707, /*!< Clock check Higher Threshold */ + TFA9912_BF_CCHKTL= 0x0787, /*!< Clock check Higher Threshold */ + TFA9912_BF_AMPOCRT= 0x0802, /*!< Amplifier on-off criteria for shutdown */ + TFA9912_BF_AMPTCRR= 0x0832, /*!< Amplifier on-off criteria for tap mode entry */ + TFA9912_BF_STGS = 0x0d00, /*!< PDM side tone gain selector */ + TFA9912_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA9912_BF_STSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA9912_BF_ST1C = 0x0db0, /*!< side tone one s complement */ + TFA9912_BF_CMFBEL= 0x0e80, /*!< CMFB enable left */ + TFA9912_BF_VDDS = 0x1000, /*!< POR */ + TFA9912_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA9912_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9912_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9912_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9912_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA9912_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA9912_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA9912_BF_ACS = 0x1090, /*!< Cold Start */ + TFA9912_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA9912_BF_WDS = 0x10b0, /*!< Watchdog */ + TFA9912_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9912_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9912_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9912_BF_BODNOK= 0x10f0, /*!< BOD */ + TFA9912_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9912_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA9912_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA9912_BF_DCPEAKCUR= 0x1130, /*!< Indicates current is max in DC-to-DC converter */ + TFA9912_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9912_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9912_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9912_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA9912_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA9912_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA9912_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA9912_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA9912_BF_HAPTIC= 0x11e0, /*!< Status haptic driver */ + TFA9912_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9912_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9912_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9912_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9912_BF_CLIPAH= 0x1340, /*!< Clipping A to Vddp */ + TFA9912_BF_CLIPAL= 0x1350, /*!< Clipping A to gnd */ + TFA9912_BF_CLIPBH= 0x1360, /*!< Clipping B to Vddp */ + TFA9912_BF_CLIPBL= 0x1370, /*!< Clipping B to gnd */ + TFA9912_BF_OCDS = 0x1380, /*!< OCP amplifier */ + TFA9912_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9912_BF_TCMPTRG= 0x13a0, /*!< Status Tap comparator triggered */ + TFA9912_BF_TAPDET= 0x13b0, /*!< Status Tap detected */ + TFA9912_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9912_BF_MANWAIT2= 0x13d0, /*!< Wait CF config */ + TFA9912_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9912_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9912_BF_SPKSL = 0x1400, /*!< Left speaker status */ + TFA9912_BF_SPKS = 0x1410, /*!< Speaker status */ + TFA9912_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9912_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9912_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9912_BF_DSPCLKSRC= 0x1490, /*!< DSP clock source selected by manager */ + TFA9912_BF_STARTUPMODSTAT= 0x14a1, /*!< Startup Mode Selected by Manager(Read Only) */ + TFA9912_BF_TSPMSTATE= 0x14c3, /*!< Tap Machine State */ + TFA9912_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9912_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9912_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/13 V) */ + TFA9912_BF_DCILCF= 0x17a0, /*!< DCDC current limiting for DSP */ + TFA9912_BF_TDMUC = 0x2000, /*!< Mode setting */ + TFA9912_BF_DIO4SEL= 0x2011, /*!< DIO4 Input selection */ + TFA9912_BF_TDME = 0x2040, /*!< Enable TDM interface */ + TFA9912_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9912_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9912_BF_TDMFSLN= 0x2073, /*!< FS length */ + TFA9912_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9912_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9912_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9912_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9912_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9912_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9912_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9912_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9912_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9912_BF_TDMTXDFO= 0x2271, /*!< Format unused bits in a slot */ + TFA9912_BF_TDMTXUS0= 0x2291, /*!< Format unused slots GAINIO */ + TFA9912_BF_TDMTXUS1= 0x22b1, /*!< Format unused slots DIO1 */ + TFA9912_BF_TDMTXUS2= 0x22d1, /*!< Format unused slots DIO2 */ + TFA9912_BF_TDMGIE= 0x2300, /*!< Control gain (channel in 0) */ + TFA9912_BF_TDMDCE= 0x2310, /*!< Control audio left (channel in 1 ) */ + TFA9912_BF_TDMSPKE= 0x2320, /*!< Control audio right (channel in 2 ) */ + TFA9912_BF_TDMCSE= 0x2330, /*!< Current sense */ + TFA9912_BF_TDMVSE= 0x2340, /*!< Voltage sense */ + TFA9912_BF_TDMGOE= 0x2350, /*!< DSP Gainout */ + TFA9912_BF_TDMCF2E= 0x2360, /*!< DSP 2 */ + TFA9912_BF_TDMCF3E= 0x2370, /*!< DSP 3 */ + TFA9912_BF_TDMCFE= 0x2380, /*!< DSP */ + TFA9912_BF_TDMES6= 0x2390, /*!< Loopback of Audio left (channel 1) */ + TFA9912_BF_TDMES7= 0x23a0, /*!< Loopback of Audio right (channel 2) */ + TFA9912_BF_TDMCF4E= 0x23b0, /*!< AEC ref right control */ + TFA9912_BF_TDMPD1E= 0x23c0, /*!< PDM 1 control */ + TFA9912_BF_TDMPD2E= 0x23d0, /*!< PDM 2 control */ + TFA9912_BF_TDMGIN= 0x2401, /*!< IO gainin */ + TFA9912_BF_TDMLIO= 0x2421, /*!< IO audio left */ + TFA9912_BF_TDMRIO= 0x2441, /*!< IO audio right */ + TFA9912_BF_TDMCSIO= 0x2461, /*!< IO Current Sense */ + TFA9912_BF_TDMVSIO= 0x2481, /*!< IO voltage sense */ + TFA9912_BF_TDMGOIO= 0x24a1, /*!< IO gain out */ + TFA9912_BF_TDMCFIO2= 0x24c1, /*!< IO DSP 2 */ + TFA9912_BF_TDMCFIO3= 0x24e1, /*!< IO DSP 3 */ + TFA9912_BF_TDMCFIO= 0x2501, /*!< IO DSP */ + TFA9912_BF_TDMLPB6= 0x2521, /*!< IO Source 6 */ + TFA9912_BF_TDMLPB7= 0x2541, /*!< IO Source 7 */ + TFA9912_BF_TDMGS = 0x2603, /*!< Control gainin */ + TFA9912_BF_TDMDCS= 0x2643, /*!< tdm slot for audio left (channel 1) */ + TFA9912_BF_TDMSPKS= 0x2683, /*!< tdm slot for audio right (channel 2) */ + TFA9912_BF_TDMCSS= 0x26c3, /*!< Slot Position of Current Sense Out */ + TFA9912_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense */ + TFA9912_BF_TDMCGOS= 0x2743, /*!< Slot Position of GAIN out */ + TFA9912_BF_TDMCF2S= 0x2783, /*!< Slot Position DSPout2 */ + TFA9912_BF_TDMCF3S= 0x27c3, /*!< Slot Position DSPout3 */ + TFA9912_BF_TDMCFS= 0x2803, /*!< Slot Position of DSPout */ + TFA9912_BF_TDMEDAT6S= 0x2843, /*!< Slot Position of loopback channel left */ + TFA9912_BF_TDMEDAT7S= 0x2883, /*!< Slot Position of loopback channel right */ + TFA9912_BF_TDMTXUS3= 0x2901, /*!< Format unused slots D3 */ + TFA9912_BF_PDMSM = 0x3100, /*!< PDM control */ + TFA9912_BF_PDMSTSEL= 0x3110, /*!< PDM Decimator input selection */ + TFA9912_BF_PDMSTENBL= 0x3120, /*!< Side tone input enable */ + TFA9912_BF_PDMLSEL= 0x3130, /*!< PDM data selection for left channel during PDM direct mode */ + TFA9912_BF_PDMRSEL= 0x3140, /*!< PDM data selection for right channel during PDM direct mode */ + TFA9912_BF_MICVDDE= 0x3150, /*!< Enable MICVDD */ + TFA9912_BF_PDMCLRAT= 0x3201, /*!< PDM BCK/Fs ratio */ + TFA9912_BF_PDMGAIN= 0x3223, /*!< PDM gain */ + TFA9912_BF_PDMOSEL= 0x3263, /*!< PDM output selection - RE/FE data combination */ + TFA9912_BF_SELCFHAPD= 0x32a0, /*!< Select the source for haptic data output (not for customer) */ + TFA9912_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9912_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA9912_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9912_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA9912_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9912_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA9912_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA9912_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9912_BF_ISTSPKS= 0x4080, /*!< Status speaker error */ + TFA9912_BF_ISTACS= 0x4090, /*!< Status cold start */ + TFA9912_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA9912_BF_ISTWDS= 0x40b0, /*!< Status watchdog */ + TFA9912_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA9912_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA9912_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA9912_BF_ISTBODNOK= 0x40f0, /*!< Status BOD */ + TFA9912_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA9912_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA9912_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA9912_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA9912_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA9912_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA9912_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA9912_BF_ISTRCVLD= 0x4170, /*!< Status rcvldop ready */ + TFA9912_BF_ISTOCPL= 0x4180, /*!< Status ocp alarm left */ + TFA9912_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm */ + TFA9912_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA9912_BF_ISTMWCFC= 0x41b0, /*!< Status waits CF config */ + TFA9912_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA9912_BF_ISTCFMER= 0x41d0, /*!< Status cfma error */ + TFA9912_BF_ISTCFMAC= 0x41e0, /*!< Status cfma ack */ + TFA9912_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA9912_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA9912_BF_ISTCLPL= 0x4210, /*!< Status clip left */ + TFA9912_BF_ISTCLPR= 0x4220, /*!< Status clip */ + TFA9912_BF_ISTOCPM= 0x4230, /*!< Status mic ocpok */ + TFA9912_BF_ISTLP1= 0x4250, /*!< Status low power mode1 */ + TFA9912_BF_ISTLA = 0x4260, /*!< Status low amplitude detection */ + TFA9912_BF_ISTVDDP= 0x4270, /*!< Status VDDP greater than VBAT */ + TFA9912_BF_ISTTAPDET= 0x4280, /*!< Status Tap detected */ + TFA9912_BF_ISTAUDMOD= 0x4290, /*!< Status Audio Mode activated */ + TFA9912_BF_ISTSAMMOD= 0x42a0, /*!< Status SAM Mode activated */ + TFA9912_BF_ISTTAPMOD= 0x42b0, /*!< Status Tap Mode Activated */ + TFA9912_BF_ISTTAPTRG= 0x42c0, /*!< Status Tap comparator triggered */ + TFA9912_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9912_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA9912_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9912_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA9912_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9912_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA9912_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA9912_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9912_BF_ICLSPKS= 0x4480, /*!< Clear speaker error */ + TFA9912_BF_ICLACS= 0x4490, /*!< Clear cold started */ + TFA9912_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA9912_BF_ICLWDS= 0x44b0, /*!< Clear watchdog */ + TFA9912_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA9912_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA9912_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA9912_BF_ICLBODNOK= 0x44f0, /*!< Clear BOD */ + TFA9912_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA9912_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA9912_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA9912_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA9912_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA9912_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA9912_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA9912_BF_ICLRCVLD= 0x4570, /*!< Clear rcvldop ready */ + TFA9912_BF_ICLOCPL= 0x4580, /*!< Clear ocp alarm left */ + TFA9912_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm */ + TFA9912_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA9912_BF_ICLMWCFC= 0x45b0, /*!< Clear wait cf config */ + TFA9912_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA9912_BF_ICLCFMER= 0x45d0, /*!< Clear cfma err */ + TFA9912_BF_ICLCFMAC= 0x45e0, /*!< Clear cfma ack */ + TFA9912_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA9912_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA9912_BF_ICLCLPL= 0x4610, /*!< Clear clip left */ + TFA9912_BF_ICLCLP= 0x4620, /*!< Clear clip */ + TFA9912_BF_ICLOCPM= 0x4630, /*!< Clear mic ocpok */ + TFA9912_BF_ICLLP1= 0x4650, /*!< Clear low power mode1 */ + TFA9912_BF_ICLLA = 0x4660, /*!< Clear low amplitude detection */ + TFA9912_BF_ICLVDDP= 0x4670, /*!< Clear VDDP greater then VBAT */ + TFA9912_BF_ICLTAPDET= 0x4680, /*!< Clear Tap detected */ + TFA9912_BF_ICLAUDMOD= 0x4690, /*!< Clear Audio Mode activated */ + TFA9912_BF_ICLSAMMOD= 0x46a0, /*!< Clear SAM Mode activated */ + TFA9912_BF_ICLTAPMOD= 0x46b0, /*!< Clear Tap Mode Activated */ + TFA9912_BF_ICLTAPTRG= 0x46c0, /*!< Clear Comparator Interrupt */ + TFA9912_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9912_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA9912_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9912_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA9912_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9912_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA9912_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA9912_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9912_BF_IESPKS= 0x4880, /*!< Enable speaker error */ + TFA9912_BF_IEACS = 0x4890, /*!< Enable cold started */ + TFA9912_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA9912_BF_IEWDS = 0x48b0, /*!< Enable watchdog */ + TFA9912_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA9912_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA9912_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA9912_BF_IEBODNOK= 0x48f0, /*!< Enable BOD */ + TFA9912_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA9912_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA9912_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA9912_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA9912_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA9912_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA9912_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA9912_BF_IERCVLD= 0x4970, /*!< Enable rcvldop ready */ + TFA9912_BF_IEOCPL= 0x4980, /*!< Enable ocp alarm left */ + TFA9912_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm */ + TFA9912_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA9912_BF_IEMWCFC= 0x49b0, /*!< Enable man wait cf config */ + TFA9912_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA9912_BF_IECFMER= 0x49d0, /*!< Enable cfma err */ + TFA9912_BF_IECFMAC= 0x49e0, /*!< Enable cfma ack */ + TFA9912_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA9912_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA9912_BF_IECLPL= 0x4a10, /*!< Enable clip left */ + TFA9912_BF_IECLPR= 0x4a20, /*!< Enable clip */ + TFA9912_BF_IEOCPM1= 0x4a30, /*!< Enable mic ocpok */ + TFA9912_BF_IELP1 = 0x4a50, /*!< Enable low power mode1 */ + TFA9912_BF_IELA = 0x4a60, /*!< Enable low amplitude detection */ + TFA9912_BF_IEVDDP= 0x4a70, /*!< Enable VDDP greater than VBAT */ + TFA9912_BF_IETAPDET= 0x4a80, /*!< Enable Tap detected */ + TFA9912_BF_IEAUDMOD= 0x4a90, /*!< Enable Audio Mode activated */ + TFA9912_BF_IESAMMOD= 0x4aa0, /*!< Enable SAM Mode activated */ + TFA9912_BF_IETAPMOD= 0x4ab0, /*!< Enable Tap Mode Activated */ + TFA9912_BF_IETAPTRG= 0x4ac0, /*!< Enable comparator interrupt */ + TFA9912_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9912_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA9912_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9912_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA9912_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9912_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA9912_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA9912_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9912_BF_IPOSPKS= 0x4c80, /*!< Polarity speaker error */ + TFA9912_BF_IPOACS= 0x4c90, /*!< Polarity cold started */ + TFA9912_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA9912_BF_IPOWDS= 0x4cb0, /*!< Polarity watchdog */ + TFA9912_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA9912_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA9912_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA9912_BF_IPOBODNOK= 0x4cf0, /*!< Polarity BOD */ + TFA9912_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA9912_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA9912_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA9912_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA9912_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA9912_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA9912_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA9912_BF_IPORCVLD= 0x4d70, /*!< Polarity rcvldop ready */ + TFA9912_BF_IPOOCPL= 0x4d80, /*!< Polarity ocp alarm left */ + TFA9912_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm */ + TFA9912_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA9912_BF_IPOMWCFC= 0x4db0, /*!< Polarity man wait cf config */ + TFA9912_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA9912_BF_IPOCFMER= 0x4dd0, /*!< Polarity cfma err */ + TFA9912_BF_IPOCFMAC= 0x4de0, /*!< Polarity cfma ack */ + TFA9912_BF_IPOCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA9912_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA9912_BF_IPOCLPL= 0x4e10, /*!< Polarity clip left */ + TFA9912_BF_IPOCLPR= 0x4e20, /*!< Polarity clip */ + TFA9912_BF_IPOOCPM= 0x4e30, /*!< Polarity mic ocpok */ + TFA9912_BF_IPOLP1= 0x4e50, /*!< Polarity low power mode1 */ + TFA9912_BF_IPOLA = 0x4e60, /*!< Polarity low amplitude detection */ + TFA9912_BF_IPOVDDP= 0x4e70, /*!< Polarity VDDP greater than VBAT */ + TFA9912_BF_IPOLTAPDET= 0x4e80, /*!< PolarityTap detected */ + TFA9912_BF_IPOLAUDMOD= 0x4e90, /*!< PolarityAudio Mode activated */ + TFA9912_BF_IPOLSAMMOD= 0x4ea0, /*!< PolaritySAM Mode activated */ + TFA9912_BF_IPOLTAPMOD= 0x4eb0, /*!< Polarity Tap Mode Activated */ + TFA9912_BF_IPOLTAPTRG= 0x4ec0, /*!< PolarityTap Comparator Trigger */ + TFA9912_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9912_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9912_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9912_BF_BSSRR = 0x5082, /*!< Battery Safeguard release time */ + TFA9912_BF_BSSHY = 0x50b1, /*!< Battery Safeguard hysteresis */ + TFA9912_BF_BSSAC = 0x50d0, /*!< Reset clipper - Auto clear */ + TFA9912_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9912_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA9912_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9912_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA9912_BF_CFSML = 0x5120, /*!< Soft mute FW left */ + TFA9912_BF_CFSM = 0x5130, /*!< Soft mute FW */ + TFA9912_BF_HPFBYPL= 0x5140, /*!< Bypass HPF left */ + TFA9912_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9912_BF_DPSAL = 0x5160, /*!< Enable DPSA left */ + TFA9912_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9912_BF_VOL = 0x5187, /*!< FW volume control for primary audio channel */ + TFA9912_BF_HNDSFRCV= 0x5200, /*!< Selection receiver */ + TFA9912_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9912_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9912_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9912_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9912_BF_CFTAPPAT= 0x5c07, /*!< Coolflux tap pattern */ + TFA9912_BF_TAPDBGINFO= 0x5c83, /*!< Reserved */ + TFA9912_BF_TATPSTAT1= 0x5d0f, /*!< Tap Status 1 from CF FW */ + TFA9912_BF_TCOMPTHR= 0x5f03, /*!< Comparator threshold (in uV) */ + TFA9912_BF_PGAGAIN= 0x6081, /*!< PGA gain selection */ + TFA9912_BF_TDMSPKG= 0x6123, /*!< System gain (INPLEV 0) */ + TFA9912_BF_LPM1LVL= 0x6505, /*!< low power mode1 detector ctrl threshold for low_audio_lvl */ + TFA9912_BF_LPM1HLD= 0x6565, /*!< Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio */ + TFA9912_BF_LPM1DIS= 0x65c0, /*!< low power mode1 detector control */ + TFA9912_BF_DCDIS = 0x6630, /*!< DCDC */ + TFA9912_BF_TDMSRCMAP= 0x6801, /*!< tdm source mapping */ + TFA9912_BF_TDMSRCAS= 0x6821, /*!< frame a selection */ + TFA9912_BF_TDMSRCBS= 0x6841, /*!< frame b selection */ + TFA9912_BF_ANC1C = 0x68a0, /*!< ANC one s complement */ + TFA9912_BF_SAMMODE= 0x6901, /*!< Sam mode */ + TFA9912_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9912_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9912_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9912_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9912_BF_DCINSEL= 0x70c1, /*!< DCDC IIR input Selection */ + TFA9912_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9912_BF_DCTRIP= 0x7504, /*!< Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCTRIP2= 0x7554, /*!< Adaptive boost trip level 2, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCTRIPT= 0x75a4, /*!< Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCVOF = 0x7635, /*!< First boost voltage level */ + TFA9912_BF_DCVOS = 0x7695, /*!< Second boost voltage level */ + TFA9912_BF_RST = 0x9000, /*!< Reset */ + TFA9912_BF_DMEM = 0x9011, /*!< Target memory */ + TFA9912_BF_AIF = 0x9030, /*!< Auto increment */ + TFA9912_BF_CFINT = 0x9040, /*!< Interrupt - auto clear */ + TFA9912_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA9912_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA9912_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA9912_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA9912_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA9912_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA9912_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA9912_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA9912_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA9912_BF_MADD = 0x910f, /*!< Memory address */ + TFA9912_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA9912_BF_ERR = 0x9307, /*!< Error flags */ + TFA9912_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA9912_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA9912_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA9912_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA9912_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA9912_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA9912_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA9912_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA9912_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9912_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9912_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9912_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9912_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9912_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9912_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9912_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9912_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9912_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9912_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9912_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9912_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9912_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9912_BF_DCMCCCL= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9912_BF_USERDEF= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9912_BF_R25C = 0xf40f, /*!< Ron resistance of speaker coil */ +} nxpTfa9912BfEnumList_t; +#define TFA9912_NAMETABLE static tfaBfName_t Tfa9912DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux , */\ + { 0x30, "AMPE"}, /* Enables the Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux controls amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x90, "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 enable , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "TDMC"}, /* TDM Compatibility with TFA9872 , */\ + { 0x2e0, "ENBLADC10"}, /* ADC10 Enable - I2C direct mode , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x430, "ENCFCKSEL"}, /* Coolflux DSP clock scaling, low power mode , */\ + { 0x441, "CFCKSEL"}, /* Coolflux DSP clock scaler selection for low power mode, */\ + { 0x460, "TDMINFSEL"}, /* TDM clock selection , */\ + { 0x470, "DISBLAUTOCLKSEL"}, /* Disable Automatic dsp clock source selection , */\ + { 0x480, "SELCLKSRC"}, /* I2C selection of DSP clock when auto select is disabled, */\ + { 0x490, "SELTIMSRC"}, /* I2C selection of Watchdog and Timer clock , */\ + { 0x500, "SSLEFTE"}, /* , */\ + { 0x510, "SPKSSEN"}, /* Enable speaker path , */\ + { 0x520, "VSLEFTE"}, /* , */\ + { 0x530, "VSRIGHTE"}, /* Voltage sense , */\ + { 0x540, "CSLEFTE"}, /* , */\ + { 0x550, "CSRIGHTE"}, /* Current sense , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0x570, "PGALE"}, /* Enable PGA chop clock for left channel , */\ + { 0x580, "PGARE"}, /* Enable PGA chop clock , */\ + { 0x590, "SSTDME"}, /* Sub-system TDM , */\ + { 0x5a0, "SSPBSTE"}, /* Sub-system boost , */\ + { 0x5b0, "SSADCE"}, /* Sub-system ADC , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0x5d0, "SSCFTIME"}, /* CF Sub-system timer , */\ + { 0x5e0, "SSCFWDTE"}, /* CF Sub-system WDT , */\ + { 0x5f0, "FAIMVBGOVRRL"}, /* Over rule of vbg for FaIM access , */\ + { 0x600, "SAMSPKSEL"}, /* Input selection for TAP/SAM , */\ + { 0x610, "PDM2IISEN"}, /* PDM2IIS Bridge enable , */\ + { 0x620, "TAPRSTBYPASS"}, /* Tap decimator reset bypass - Bypass the decimator reset from tapdec, */\ + { 0x631, "CARDECISEL0"}, /* Cardec input 0 sel , */\ + { 0x651, "CARDECISEL1"}, /* Cardec input sel , */\ + { 0x670, "TAPDECSEL"}, /* Select TAP/Cardec for TAP , */\ + { 0x680, "COMPCOUNT"}, /* Comparator o/p filter selection , */\ + { 0x691, "STARTUPMODE"}, /* Startup Mode Selection , */\ + { 0x6b0, "AUTOTAP"}, /* Enable auto tap switching , */\ + { 0x6c1, "COMPINITIME"}, /* Comparator initialization time to be used in Tap Machine, */\ + { 0x6e1, "ANAPINITIME"}, /* Analog initialization time to be used in Tap Machine, */\ + { 0x707, "CCHKTH"}, /* Clock check Higher Threshold , */\ + { 0x787, "CCHKTL"}, /* Clock check Higher Threshold , */\ + { 0x802, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x832, "AMPTCRR"}, /* Amplifier on-off criteria for tap mode entry , */\ + { 0xd00, "STGS"}, /* PDM side tone gain selector , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "STSMUTE"}, /* Side tone soft mute , */\ + { 0xdb0, "ST1C"}, /* side tone one s complement , */\ + { 0xe80, "CMFBEL"}, /* CMFB enable left , */\ + { 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 , */\ + { 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 , */\ + { 0x1130, "DCPEAKCUR"}, /* Indicates current is max in DC-to-DC converter , */\ + { 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 , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1340, "CLIPAH"}, /* Clipping A to Vddp , */\ + { 0x1350, "CLIPAL"}, /* Clipping A to gnd , */\ + { 0x1360, "CLIPBH"}, /* Clipping B to Vddp , */\ + { 0x1370, "CLIPBL"}, /* Clipping B to gnd , */\ + { 0x1380, "OCDS"}, /* OCP amplifier , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "TCMPTRG"}, /* Status Tap comparator triggered , */\ + { 0x13b0, "TAPDET"}, /* Status Tap detected , */\ + { 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, "SPKS"}, /* Speaker status , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1490, "DSPCLKSRC"}, /* DSP clock source selected by manager , */\ + { 0x14a1, "STARTUPMODSTAT"}, /* Startup Mode Selected by Manager(Read Only) , */\ + { 0x14c3, "TSPMSTATE"}, /* Tap Machine State , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x17a0, "DCILCF"}, /* DCDC current limiting for DSP , */\ + { 0x2000, "TDMUC"}, /* Mode setting , */\ + { 0x2011, "DIO4SEL"}, /* DIO4 Input selection , */\ + { 0x2040, "TDME"}, /* Enable TDM interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length , */\ + { 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 in a slot , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "TDMTXUS1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "TDMTXUS2"}, /* Format unused slots DIO2 , */\ + { 0x2300, "TDMGIE"}, /* Control gain (channel in 0) , */\ + { 0x2310, "TDMDCE"}, /* Control audio left (channel in 1 ) , */\ + { 0x2320, "TDMSPKE"}, /* Control audio right (channel in 2 ) , */\ + { 0x2330, "TDMCSE"}, /* Current sense , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense , */\ + { 0x2350, "TDMGOE"}, /* DSP Gainout , */\ + { 0x2360, "TDMCF2E"}, /* DSP 2 , */\ + { 0x2370, "TDMCF3E"}, /* DSP 3 , */\ + { 0x2380, "TDMCFE"}, /* DSP , */\ + { 0x2390, "TDMES6"}, /* Loopback of Audio left (channel 1) , */\ + { 0x23a0, "TDMES7"}, /* Loopback of Audio right (channel 2) , */\ + { 0x23b0, "TDMCF4E"}, /* AEC ref right control , */\ + { 0x23c0, "TDMPD1E"}, /* PDM 1 control , */\ + { 0x23d0, "TDMPD2E"}, /* PDM 2 control , */\ + { 0x2401, "TDMGIN"}, /* IO gainin , */\ + { 0x2421, "TDMLIO"}, /* IO audio left , */\ + { 0x2441, "TDMRIO"}, /* IO audio right , */\ + { 0x2461, "TDMCSIO"}, /* IO Current Sense , */\ + { 0x2481, "TDMVSIO"}, /* IO voltage sense , */\ + { 0x24a1, "TDMGOIO"}, /* IO gain out , */\ + { 0x24c1, "TDMCFIO2"}, /* IO DSP 2 , */\ + { 0x24e1, "TDMCFIO3"}, /* IO DSP 3 , */\ + { 0x2501, "TDMCFIO"}, /* IO DSP , */\ + { 0x2521, "TDMLPB6"}, /* IO Source 6 , */\ + { 0x2541, "TDMLPB7"}, /* IO Source 7 , */\ + { 0x2603, "TDMGS"}, /* Control gainin , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for audio left (channel 1) , */\ + { 0x2683, "TDMSPKS"}, /* tdm slot for audio right (channel 2) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of Current Sense Out , */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense , */\ + { 0x2743, "TDMCGOS"}, /* Slot Position of GAIN out , */\ + { 0x2783, "TDMCF2S"}, /* Slot Position DSPout2 , */\ + { 0x27c3, "TDMCF3S"}, /* Slot Position DSPout3 , */\ + { 0x2803, "TDMCFS"}, /* Slot Position of DSPout , */\ + { 0x2843, "TDMEDAT6S"}, /* Slot Position of loopback channel left , */\ + { 0x2883, "TDMEDAT7S"}, /* Slot Position of loopback channel right , */\ + { 0x2901, "TDMTXUS3"}, /* Format unused slots D3 , */\ + { 0x3100, "PDMSM"}, /* PDM control , */\ + { 0x3110, "PDMSTSEL"}, /* PDM Decimator input selection , */\ + { 0x3120, "PDMSTENBL"}, /* Side tone input enable , */\ + { 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), */\ + { 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 , */\ + { 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 , */\ + { 0x4230, "ISTOCPM"}, /* Status mic ocpok , */\ + { 0x4250, "ISTLP1"}, /* Status low power mode1 , */\ + { 0x4260, "ISTLA"}, /* Status low amplitude detection , */\ + { 0x4270, "ISTVDDP"}, /* Status VDDP greater than VBAT , */\ + { 0x4280, "ISTTAPDET"}, /* Status Tap detected , */\ + { 0x4290, "ISTAUDMOD"}, /* Status Audio Mode activated , */\ + { 0x42a0, "ISTSAMMOD"}, /* Status SAM Mode activated , */\ + { 0x42b0, "ISTTAPMOD"}, /* Status Tap Mode Activated , */\ + { 0x42c0, "ISTTAPTRG"}, /* Status Tap comparator triggered , */\ + { 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 , */\ + { 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, "ICLCLP"}, /* Clear clip , */\ + { 0x4630, "ICLOCPM"}, /* Clear mic ocpok , */\ + { 0x4650, "ICLLP1"}, /* Clear low power mode1 , */\ + { 0x4660, "ICLLA"}, /* Clear low amplitude detection , */\ + { 0x4670, "ICLVDDP"}, /* Clear VDDP greater then VBAT , */\ + { 0x4680, "ICLTAPDET"}, /* Clear Tap detected , */\ + { 0x4690, "ICLAUDMOD"}, /* Clear Audio Mode activated , */\ + { 0x46a0, "ICLSAMMOD"}, /* Clear SAM Mode activated , */\ + { 0x46b0, "ICLTAPMOD"}, /* Clear Tap Mode Activated , */\ + { 0x46c0, "ICLTAPTRG"}, /* Clear Comparator Interrupt , */\ + { 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 , */\ + { 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 , */\ + { 0x4a30, "IEOCPM1"}, /* Enable mic ocpok , */\ + { 0x4a50, "IELP1"}, /* Enable low power mode1 , */\ + { 0x4a60, "IELA"}, /* Enable low amplitude detection , */\ + { 0x4a70, "IEVDDP"}, /* Enable VDDP greater than VBAT , */\ + { 0x4a80, "IETAPDET"}, /* Enable Tap detected , */\ + { 0x4a90, "IEAUDMOD"}, /* Enable Audio Mode activated , */\ + { 0x4aa0, "IESAMMOD"}, /* Enable SAM Mode activated , */\ + { 0x4ab0, "IETAPMOD"}, /* Enable Tap Mode Activated , */\ + { 0x4ac0, "IETAPTRG"}, /* Enable comparator interrupt , */\ + { 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 , */\ + { 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, "IPOCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e10, "IPOCLPL"}, /* Polarity clip left , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip , */\ + { 0x4e30, "IPOOCPM"}, /* Polarity mic ocpok , */\ + { 0x4e50, "IPOLP1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "IPOLA"}, /* Polarity low amplitude detection , */\ + { 0x4e70, "IPOVDDP"}, /* Polarity VDDP greater than VBAT , */\ + { 0x4e80, "IPOLTAPDET"}, /* PolarityTap detected , */\ + { 0x4e90, "IPOLAUDMOD"}, /* PolarityAudio Mode activated , */\ + { 0x4ea0, "IPOLSAMMOD"}, /* PolaritySAM Mode activated , */\ + { 0x4eb0, "IPOLTAPMOD"}, /* Polarity Tap Mode Activated , */\ + { 0x4ec0, "IPOLTAPTRG"}, /* PolarityTap Comparator Trigger , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery Safeguard release time , */\ + { 0x50b1, "BSSHY"}, /* Battery Safeguard hysteresis , */\ + { 0x50d0, "BSSAC"}, /* Reset clipper - Auto clear , */\ + { 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, "CFSM"}, /* Soft mute FW , */\ + { 0x5140, "HPFBYPL"}, /* Bypass HPF left , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5160, "DPSAL"}, /* Enable DPSA left , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 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 , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x5c07, "CFTAPPAT"}, /* Coolflux tap pattern , */\ + { 0x5c83, "TAPDBGINFO"}, /* Reserved , */\ + { 0x5d0f, "TATPSTAT1"}, /* Tap Status 1 from CF FW , */\ + { 0x5f03, "TCOMPTHR"}, /* Comparator threshold (in uV) , */\ + { 0x6081, "PGAGAIN"}, /* PGA gain selection , */\ + { 0x6123, "TDMSPKG"}, /* System gain (INPLEV 0) , */\ + { 0x6505, "LPM1LVL"}, /* low power mode1 detector ctrl threshold for low_audio_lvl , */\ + { 0x6565, "LPM1HLD"}, /* Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "LPM1DIS"}, /* low power mode1 detector control , */\ + { 0x6630, "DCDIS"}, /* DCDC , */\ + { 0x6801, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6821, "TDMSRCAS"}, /* frame a selection , */\ + { 0x6841, "TDMSRCBS"}, /* frame b selection , */\ + { 0x68a0, "ANC1C"}, /* ANC one s complement , */\ + { 0x6901, "SAMMODE"}, /* Sam mode , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70c1, "DCINSEL"}, /* DCDC IIR input Selection , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7504, "DCTRIP"}, /* Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1, */\ + { 0x7554, "DCTRIP2"}, /* Adaptive boost trip level 2, effective only when boost_intelligent is set to 1, */\ + { 0x75a4, "DCTRIPT"}, /* Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7635, "DCVOF"}, /* First boost voltage level , */\ + { 0x7695, "DCVOS"}, /* Second boost voltage level , */\ + { 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 , */\ + { 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 , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 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, "DCMCCCL"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "USERDEF"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf40f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9912_BITNAMETABLE static tfaBfName_t Tfa9912BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x30, "enbl_amplifier"}, /* Enables the 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 , */\ + { 0x90, "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_fro8m_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 enable , */\ + { 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, "tdm_tfa9872_compatible"}, /* TDM Compatibility with TFA9872 , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x2e0, "enbl_adc10"}, /* ADC10 Enable - I2C direct mode , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x430, "cf_clock_scaling"}, /* Coolflux DSP clock scaling, low power mode , */\ + { 0x441, "sel_cf_clock"}, /* Coolflux DSP clock scaler selection for low power mode, */\ + { 0x460, "tdm_intf_sel"}, /* TDM clock selection , */\ + { 0x470, "disable_auto_sel_refclk"}, /* Disable Automatic dsp clock source selection , */\ + { 0x480, "sel_clk_src"}, /* I2C selection of DSP clock when auto select is disabled, */\ + { 0x490, "wdt_tim_clk_src"}, /* I2C selection of Watchdog and Timer clock , */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker path , */\ + { 0x530, "enbl_volsense"}, /* Voltage sense , */\ + { 0x550, "enbl_cursense"}, /* Current sense , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0x580, "enbl_pga_chop"}, /* Enable PGA chop clock , */\ + { 0x590, "enbl_tdm_ss"}, /* Sub-system TDM , */\ + { 0x5a0, "enbl_bst_ss"}, /* Sub-system boost , */\ + { 0x5b0, "enbl_adc10_ss"}, /* Sub-system ADC , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0x5d0, "enbl_tim_clk"}, /* CF Sub-system timer , */\ + { 0x5e0, "enbl_wdt_clk"}, /* CF Sub-system WDT , */\ + { 0x5f0, "faim_enable_vbg"}, /* Over rule of vbg for FaIM access , */\ + { 0x600, "aux_spkr_sel"}, /* Input selection for TAP/SAM , */\ + { 0x620, "bypass_tapdec_reset"}, /* Tap decimator reset bypass - Bypass the decimator reset from tapdec, */\ + { 0x631, "car_dec_in_sel0"}, /* Cardec input 0 sel , */\ + { 0x651, "car_dec_in_sel1"}, /* Cardec input sel , */\ + { 0x670, "tapdec_sel"}, /* Select TAP/Cardec for TAP , */\ + { 0x680, "comp_count"}, /* Comparator o/p filter selection , */\ + { 0x691, "startup_mode"}, /* Startup Mode Selection , */\ + { 0x6b0, "enable_auto_tap_switching"}, /* Enable auto tap switching , */\ + { 0x6c1, "comp_init_time"}, /* Comparator initialization time to be used in Tap Machine, */\ + { 0x6e1, "ana_init_time"}, /* Analog initialization time to be used in Tap Machine, */\ + { 0x707, "clkchk_th_hi"}, /* Clock check Higher Threshold , */\ + { 0x787, "clkchk_th_lo"}, /* Clock check Higher Threshold , */\ + { 0x802, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x832, "ctrl_on2tap_criterion"}, /* Amplifier on-off criteria for tap mode entry , */\ + { 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 , */\ + { 0xdb0, "side_tone_1scomplement"}, /* side tone one s complement , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 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 , */\ + { 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 , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1340, "flag_clipa_high"}, /* Clipping A to Vddp , */\ + { 0x1350, "flag_clipa_low"}, /* Clipping A to gnd , */\ + { 0x1360, "flag_clipb_high"}, /* Clipping B to Vddp , */\ + { 0x1370, "flag_clipb_low"}, /* Clipping B to gnd , */\ + { 0x1380, "flag_ocp_alarm"}, /* OCP amplifier , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13a0, "flag_tap_comp_trig"}, /* Status Tap comparator triggered , */\ + { 0x13b0, "flag_cf_tapdetected"}, /* Status Tap detected , */\ + { 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 , */\ + { 0x1410, "flag_cf_speakererror"}, /* Speaker status , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1490, "man_dsp_clk_src"}, /* DSP clock source selected by manager , */\ + { 0x14a1, "man_startup_mode"}, /* Startup Mode Selected by Manager(Read Only) , */\ + { 0x14c3, "tap_machine_state"}, /* Tap Machine State , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x17a0, "flag_bst_bstcur_cf"}, /* DCDC current limiting for DSP , */\ + { 0x2000, "tdm_usecase"}, /* Mode setting , */\ + { 0x2011, "dio4_input_sel"}, /* DIO4 Input selection , */\ + { 0x2040, "tdm_enable"}, /* Enable TDM interface , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length , */\ + { 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 in a slot , */\ + { 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 gain (channel in 0) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio left (channel in 1 ) , */\ + { 0x2320, "tdm_sink2_enable"}, /* Control audio right (channel in 2 ) , */\ + { 0x2330, "tdm_source0_enable"}, /* Current sense , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense , */\ + { 0x2350, "tdm_source2_enable"}, /* DSP Gainout , */\ + { 0x2360, "tdm_source3_enable"}, /* DSP 2 , */\ + { 0x2370, "tdm_source4_enable"}, /* DSP 3 , */\ + { 0x2380, "tdm_source5_enable"}, /* DSP , */\ + { 0x2390, "tdm_source6_enable"}, /* Loopback of Audio left (channel 1) , */\ + { 0x23a0, "tdm_source7_enable"}, /* Loopback of Audio right (channel 2) , */\ + { 0x2401, "tdm_sink0_io"}, /* IO gainin , */\ + { 0x2421, "tdm_sink1_io"}, /* IO audio left , */\ + { 0x2441, "tdm_sink2_io"}, /* IO audio right , */\ + { 0x2461, "tdm_source0_io"}, /* IO Current Sense , */\ + { 0x2481, "tdm_source1_io"}, /* IO voltage sense , */\ + { 0x24a1, "tdm_source2_io"}, /* IO gain out , */\ + { 0x24c1, "tdm_source3_io"}, /* IO DSP 2 , */\ + { 0x24e1, "tdm_source4_io"}, /* IO DSP 3 , */\ + { 0x2501, "tdm_source5_io"}, /* IO DSP , */\ + { 0x2521, "tdm_source6_io"}, /* IO Source 6 , */\ + { 0x2541, "tdm_source7_io"}, /* IO Source 7 , */\ + { 0x2603, "tdm_sink0_slot"}, /* Control gainin , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for audio left (channel 1) , */\ + { 0x2683, "tdm_sink2_slot"}, /* tdm slot for audio right (channel 2) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of Current Sense Out , */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense , */\ + { 0x2743, "tdm_source2_slot"}, /* Slot Position of GAIN out , */\ + { 0x2783, "tdm_source3_slot"}, /* Slot Position DSPout2 , */\ + { 0x27c3, "tdm_source4_slot"}, /* Slot Position DSPout3 , */\ + { 0x2803, "tdm_source5_slot"}, /* Slot Position of DSPout , */\ + { 0x2843, "tdm_source6_slot"}, /* Slot Position of loopback channel left , */\ + { 0x2883, "tdm_source7_slot"}, /* Slot Position of loopback channel right , */\ + { 0x2901, "tdm_txdata_format_unused_slot_sd3"}, /* Format unused slots D3 , */\ + { 0x3100, "pdm_mode"}, /* PDM control , */\ + { 0x3110, "pdm_input_sel"}, /* PDM Decimator input selection , */\ + { 0x3120, "enbl_pdm_side_tone"}, /* Side tone input enable , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 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 , */\ + { 0x4190, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 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 , */\ + { 0x4220, "int_out_flag_clip"}, /* Status clip , */\ + { 0x4250, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 , */\ + { 0x4260, "int_out_flag_low_amplitude"}, /* Status low amplitude detection , */\ + { 0x4270, "int_out_flag_vddp_gt_vbat"}, /* Status VDDP greater than VBAT , */\ + { 0x4280, "int_out_newtap"}, /* Status Tap detected , */\ + { 0x4290, "int_out_audiomodeactive"}, /* Status Audio Mode activated , */\ + { 0x42a0, "int_out_sammodeactive"}, /* Status SAM Mode activated , */\ + { 0x42b0, "int_out_tapmodeactive"}, /* Status Tap Mode Activated , */\ + { 0x42c0, "int_out_flag_tap_comp_trig"}, /* Status Tap comparator triggered , */\ + { 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 , */\ + { 0x4590, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 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 , */\ + { 0x4620, "int_in_flag_clip"}, /* Clear clip , */\ + { 0x4650, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 , */\ + { 0x4660, "int_in_flag_low_amplitude"}, /* Clear low amplitude detection , */\ + { 0x4670, "int_in_flag_vddp_gt_vbat"}, /* Clear VDDP greater then VBAT , */\ + { 0x4680, "int_in_newtap"}, /* Clear Tap detected , */\ + { 0x4690, "int_in_audiomodeactive"}, /* Clear Audio Mode activated , */\ + { 0x46a0, "int_in_sammodeactive"}, /* Clear SAM Mode activated , */\ + { 0x46b0, "int_in_tapmodeactive"}, /* Clear Tap Mode Activated , */\ + { 0x46c0, "int_in_flag_tap_comp_trig"}, /* Clear Comparator Interrupt , */\ + { 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 , */\ + { 0x4990, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 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 , */\ + { 0x4a20, "int_enable_flag_clip"}, /* Enable clip , */\ + { 0x4a50, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 , */\ + { 0x4a60, "int_enable_flag_low_amplitude"}, /* Enable low amplitude detection , */\ + { 0x4a70, "int_enable_flag_vddp_gt_vbat"}, /* Enable VDDP greater than VBAT , */\ + { 0x4a80, "int_enable_newtap"}, /* Enable Tap detected , */\ + { 0x4a90, "int_enable_audiomodeactive"}, /* Enable Audio Mode activated , */\ + { 0x4aa0, "int_enable_sammodeactive"}, /* Enable SAM Mode activated , */\ + { 0x4ab0, "int_enable_tapmodeactive"}, /* Enable Tap Mode Activated , */\ + { 0x4ac0, "int_enable_flag_tap_comp_trig"}, /* Enable comparator interrupt , */\ + { 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 , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 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 , */\ + { 0x4e20, "int_polarity_flag_clip"}, /* Polarity clip , */\ + { 0x4e50, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "int_polarity_flag_low_amplitude"}, /* Polarity low amplitude detection , */\ + { 0x4e70, "int_polarity_flag_vddp_gt_vbat"}, /* Polarity VDDP greater than VBAT , */\ + { 0x4e80, "int_polarity_newtap"}, /* PolarityTap detected , */\ + { 0x4e90, "int_polarity_audiomodeactive"}, /* PolarityAudio Mode activated , */\ + { 0x4ea0, "int_polarity_sammodeactive"}, /* PolaritySAM Mode activated , */\ + { 0x4eb0, "int_polarity_tapmodeactive"}, /* Polarity Tap Mode Activated , */\ + { 0x4ec0, "int_polarity_flag_tap_comp_trig"}, /* PolarityTap Comparator Trigger , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery Safeguard release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery Safeguard 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 , */\ + { 0x5130, "cf_mute"}, /* Soft mute FW , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5187, "cf_volume"}, /* FW volume control for primary audio channel , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 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 , */\ + { 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 , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) - I2C direct mode , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually , */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop - I2C direct mode, */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clock is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x5c07, "flag_cf_tap_pattern"}, /* Coolflux tap pattern , */\ + { 0x5c83, "tap_debug_info"}, /* Reserved , */\ + { 0x5d0f, "tap_status_1"}, /* Tap Status 1 from CF FW , */\ + { 0x5f03, "tap_comp_threshold"}, /* Comparator threshold (in uV) , */\ + { 0x6081, "pga_gain_set"}, /* PGA gain selection , */\ + { 0x60b0, "pga_lowpass_enable"}, /* Lowpass enable , */\ + { 0x60c0, "pga_pwr_enable"}, /* PGA power enable , */\ + { 0x60d0, "pga_switch_enable"}, /* PGA switch enable , */\ + { 0x60e0, "pga_switch_aux_enable"}, /* Switch enable aux , */\ + { 0x6123, "ctrl_att"}, /* System gain (INPLEV 0) , */\ + { 0x6265, "zero_lvl"}, /* ctrl threshold for zero X-ing , */\ + { 0x62c1, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* ctrl threshold for low_audio_lvl , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 detector ctrl threshold for zero X-ing , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode1 detector ctrl select mode , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 detector ctrl threshold for low_audio_lvl , */\ + { 0x6565, "lpm1_hold_time"}, /* Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6630, "dcdcoff_mode"}, /* DCDC , */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage - direct ctrl , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtered vs unfiltered compare , */\ + { 0x6761, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x6801, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6821, "tdm_sourcea_frame_sel"}, /* frame a selection , */\ + { 0x6841, "tdm_sourceb_frame_sel"}, /* frame b selection , */\ + { 0x6901, "sam_mode"}, /* Sam mode , */\ + { 0x6931, "pdmdat_h_sel"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "pdmdat_l_sel"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "cs_sam_set"}, /* Enable SAM input for current sense - I2C Direct Mode, */\ + { 0x6980, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x6990, "sam_spkr_sel"}, /* SAM o/p sel during SAM and audio , */\ + { 0x6b00, "disable_engage"}, /* Disable auto engage , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c60, "sel_clip_pwms"}, /* To select clip-flags , */\ + { 0x6c72, "pwms_clip_lvl"}, /* To set the amount of pwm pulse that may be skipped before clip-flag is triggered. , */\ + { 0x6ca5, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70c1, "dcdc_sel"}, /* DCDC IIR input Selection , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 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 - I2C direct mode, */\ + { 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 - I2C direct mode, */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator - I2C direct mode , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current - I2C direct mode , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage - I2C direct mode , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac - I2C direct mode , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators - I2C direct mode , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators - I2C direct mode , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators - I2C direct mode , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac - I2C direct mode , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac - I2C direct mode, */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 , */\ + { 0x7504, "boost_trip_lvl_1st"}, /* Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1, */\ + { 0x7554, "boost_trip_lvl_2nd"}, /* Adaptive boost trip level 2, effective only when boost_intelligent is set to 1, */\ + { 0x75a4, "boost_trip_lvl_track"}, /* Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7602, "track_decay"}, /* DCDC Boost decay speed after a peak value, effective only when boost_track is set to 1, */\ + { 0x7635, "frst_boost_voltage"}, /* First boost voltage level , */\ + { 0x7695, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 0x7720, "pga_test_ldo_bypass"}, /* bypass internal PGA LDO , */\ + { 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, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8200, "enbl_cmfb"}, /* Current sense common mode feedback control , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8222, "cmfb_gain"}, /* Current sense common mode feedback control gain , */\ + { 0x8256, "cmfb_offset"}, /* Current sense common mode feedback control offset , */\ + { 0x8305, "cs_ktemp"}, /* First order temperature compensation coefficient , */\ + { 0x8364, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode 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), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC - I2C direct mode , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 - I2C direct mode, */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 - I2C direct mode, */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 - I2C direct mode, */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO - I2C direct mode , */\ + { 0x8760, "enbl_cs_nofloating_n"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO -- I2C direct mode , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "vol_cur_sense_dc_offset"}, /* voltage and current sense decimator offset control, */\ + { 0x8902, "cursense_comp_delay"}, /* To align compensation signal with current sense signal, */\ + { 0x8930, "cursense_comp_sign"}, /* To change polarity of compensation for current sense compensation, */\ + { 0x8940, "enbl_cursense_comp"}, /* To enable current sense compensation , */\ + { 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 , */\ + { 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 , */\ + { 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 - - I2C direct mode, */\ + { 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 - I2C direct mode, */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc130, "tap_comp_enable"}, /* Tap Comparator enable control - I2C direct mode , */\ + { 0xc140, "tap_comp_switch_enable"}, /* Tap Comparator Switch enable control - I2C direct mode, */\ + { 0xc150, "tap_comp_switch_aux_enable"}, /* Tap Comparator Switch enable control - I2C direct mode, */\ + { 0xc161, "tap_comp_test_enable"}, /* Comparator threshold - fine value , */\ + { 0xc180, "curdist_enable"}, /* Enable control - I2C direct mode , */\ + { 0xc190, "vbg2i_enbl"}, /* Enable control - I2C direct mode , */\ + { 0xc1a0, "bg_filt_bypass_enbl"}, /* Enable 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 , */\ + { 0xc560, "bypass_fro8"}, /* Bypass fro8 with pdm_clk , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc5b0, "pga_test_enable"}, /* Enable PGA test mode , */\ + { 0xc5c0, "pga_test_offset_enable"}, /* Enable PGA test offset , */\ + { 0xc5d0, "pga_test_shortinput_enable"}, /* Enable PGA test short input , */\ + { 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, "enbl_powerswitch"}, /* Vddd core power switch control - overrules the manager control, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DIO4 (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to DIO3 (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to TDO (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, "dio3_ehs"}, /* Speed/load setting for DIO3 cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc961, "dio4_ehs"}, /* Speed/load setting for DIO4 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc981, "spdmo_ehs"}, /* Speed/load setting for PDMO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9a1, "tdo_ehs"}, /* Speed/load setting for TDM IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "int_ehs"}, /* Slew Rate INT IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9d0, "pdmclk_ehs"}, /* Slew RateBCK2/PDMCLK IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9e0, "fs2_ehs"}, /* Slew Rate DS2 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9f0, "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 , */\ + { 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 , */\ + { 0xcc05, "pll_seli_lbw"}, /* PLL SELI - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xcc64, "pll_selp_lbw"}, /* PLL SELP - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xccb3, "pll_selr_lbw"}, /* PLL SELR - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xccf0, "sel_user_pll_bw"}, /* PLL Low Bandwidth Mode control , */\ + { 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_fro8"}, /* Enables FRO8M 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, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 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 , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_range_chk"}, /* Clock out of range , */\ + { 0xd601, "clkdiv_dsp_sel"}, /* DSP clock divider selection in direct clock control mode, */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd681, "dsp_tap_clk"}, /* Dsp clock frequency selection in TAP mode; , */\ + { 0xd6a1, "sel_wdt_clk"}, /* Watch dog clock post divider value , */\ + { 0xd6c1, "sel_tim_clk"}, /* Timer clock post divider value , */\ + { 0xd700, "ads1_ehs"}, /* Slew Rate ADS1 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xd710, "ads2_ehs"}, /* Slew Rate ADS2 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost - I2C direct mode , */\ + { 0xd8a1, "force_pga_clock"}, /* force pga clock , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xd900, "overrules_usercase"}, /* Overrule Mode control use , */\ + { 0xd910, "ovr_switch_ref"}, /* Overrule Value , */\ + { 0xd920, "ovr_enbl_pll"}, /* Overrule Value , */\ + { 0xd930, "ovr_switch_amp"}, /* Overrule Value , */\ + { 0xd940, "ovr_enbl_clk_cs"}, /* Overrule Value , */\ + { 0xd951, "ovr_sel_clk_cs"}, /* CS clock selection overrule , */\ + { 0xd970, "ovr_switch_cs"}, /* Overrule Value , */\ + { 0xd980, "ovr_enbl_csvs_ss"}, /* Overrule Value , */\ + { 0xd990, "ovr_enbl_comp"}, /* Overrule Value , */\ + { 0xed00, "enbl_fro8cal"}, /* Enable FRO calibration , */\ + { 0xed10, "start_fro8_calibration"}, /* Start FRO8 Calibration , */\ + { 0xed20, "fro8_calibration_done"}, /* FRO8 Calibration done - Read Only , */\ + { 0xed45, "fro8_auto_trim_val"}, /* Calibration value from Auto Calibration block, to be written into MTP - Read Only, */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 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, "spare_mtp1_9_6"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "spare_mtp1_15_10"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a3, "spare_mtp2_13_10"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "spare_mtp3_7_0"}, /* SPARE , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "calibr_R25C"}, /* Ron resistance of speaker coil , */\ + { 0xf50f, "spare_mtp5_15_0"}, /* SPARE , */\ + { 0xf600, "mtp_lock_enbl_coolflux"}, /* Disable function dcdcoff_mode , */\ + { 0xf610, "mtp_pwm_delay_enbl_clk_auto_gating"}, /* Auto clock gating on pwm_delay , */\ + { 0xf620, "mtp_ocp_enbl_clk_auto_gating"}, /* Auto clock gating on module ocp , */\ + { 0xf630, "mtp_disable_clk_a_gating"}, /* Disable clock_a gating , */\ + { 0xf642, "spare_mtp6_6_3"}, /* SPARE , */\ + { 0xf686, "spare_mtp6_14_8"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of 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) , */\ + { 0xf910, "disable_sam_mode"}, /* Disable sam mode , */\ + { 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, "fro8_trim"}, /* 8 MHz oscillator trim code , */\ + { 0xff61, "fro8_short_nwell_r"}, /* Short 4 or 6 n-well resistors , */\ + { 0xff81, "fro8_boost_i"}, /* Self bias current selection , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9912_irq { + tfa9912_irq_stvdds = 0, + tfa9912_irq_stplls = 1, + tfa9912_irq_stotds = 2, + tfa9912_irq_stovds = 3, + tfa9912_irq_stuvds = 4, + tfa9912_irq_stclks = 5, + tfa9912_irq_stmtpb = 6, + tfa9912_irq_stnoclk = 7, + tfa9912_irq_stspks = 8, + tfa9912_irq_stacs = 9, + tfa9912_irq_stsws = 10, + tfa9912_irq_stwds = 11, + tfa9912_irq_stamps = 12, + tfa9912_irq_starefs = 13, + tfa9912_irq_stadccr = 14, + tfa9912_irq_stbodnok = 15, + tfa9912_irq_stbstcu = 16, + tfa9912_irq_stbsthi = 17, + tfa9912_irq_stbstoc = 18, + tfa9912_irq_stbstpkcur = 19, + tfa9912_irq_stbstvc = 20, + tfa9912_irq_stbst86 = 21, + tfa9912_irq_stbst93 = 22, + tfa9912_irq_strcvld = 23, + tfa9912_irq_stocpl = 24, + tfa9912_irq_stocpr = 25, + tfa9912_irq_stmwsrc = 26, + tfa9912_irq_stmwcfc = 27, + tfa9912_irq_stmwsmu = 28, + tfa9912_irq_stcfmer = 29, + tfa9912_irq_stcfmac = 30, + tfa9912_irq_stclkoor = 31, + tfa9912_irq_sttdmer = 32, + tfa9912_irq_stclpl = 33, + tfa9912_irq_stclpr = 34, + tfa9912_irq_stocpm = 35, + tfa9912_irq_stlp1 = 37, + tfa9912_irq_stla = 38, + tfa9912_irq_stvddp = 39, + tfa9912_irq_sttapdet = 40, + tfa9912_irq_staudmod = 41, + tfa9912_irq_stsammod = 42, + tfa9912_irq_sttapmod = 43, + tfa9912_irq_sttaptrg = 44, + tfa9912_irq_max = 45, + tfa9912_irq_all = -1 /* all irqs */}; + +#define TFA9912_IRQ_NAMETABLE static tfaIrqName_t Tfa9912IrqNames[]= {\ + { 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"},\ + { 37, "STLP1"},\ + { 38, "STLA"},\ + { 39, "STVDDP"},\ + { 40, "STTAPDET"},\ + { 41, "STAUDMOD"},\ + { 42, "STSAMMOD"},\ + { 43, "STTAPMOD"},\ + { 44, "STTAPTRG"},\ +}; +#endif /* _TFA9912_TFAFIELDNAMES_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfaContUtil.h b/techpack/audio/asoc/codecs/tfa9874/tfaContUtil.h new file mode 100644 index 000000000000..434f50a192ab --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfaContUtil.h @@ -0,0 +1,110 @@ +/* + * Copyright 2012-2017 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. + */ + +/* + * tfaContUtil.h + * + * interface to the Tfa98xx API + * + * Created on: Apr 6, 2012 + * Author: wim + */ + +#ifndef TFACONTUTIL_H_ +#define TFACONTUTIL_H_ + +#ifdef __ANDROID__ +#include +#else +#define LOGV if (0/*tfa98xx_verbose*/) printf //TODO improve logging +#endif + +#include "tfa_dsp_fw.h" +#include "tfa98xx_parameters.h" + +#if defined(WIN32) || defined(_X64) +#include +#else +#include +#include +#endif + +#ifndef timer_INCLUDED +#define timer_INCLUDED +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define NXPTFA_MAXLINE (256) /* maximum string length */ +#define NXPTFA_MAXBUFFER (50*1024) /* maximum buffer size to hold the container */ + +/* + * buffer types for setting parameters + */ +typedef enum nxpTfa98xxParamsType { + tfa_patch_params, + tfa_speaker_params, + tfa_preset_params, + tfa_config_params, + tfa_equalizer_params, + tfa_drc_params, + tfa_vstep_params, + tfa_cnt_params, + tfa_msg_params, + tfa_no_params, + tfa_info_params, + tfa_algo_params +} nxpTfa98xxParamsType_t; + +enum Tfa98xx_Error tfaVersions(int dev_idx, char *strings, int maxlength); +int tfa98xxSaveFile(int dev_idx, char *filename, nxpTfa98xxParamsType_t params); +enum Tfa98xx_Error tfaGetDspFWAPIVersion(int dev_idx); +int tfaGetRegMapVersion(int dev_idx, char *buffer); +int compare_strings(char *buffer, char *name, int buffersize); +enum Tfa98xx_Error tfaVersion(char *buffer); + +/* + * save dedicated device files. Depends on the file extension + */ +int tfa98xxSaveFileWrapper(int dev_idx, char *filename); + +enum Tfa98xx_Error tfa98xx_verify_speaker_range(int idx, float imp[2], int spkr_count); + +/* hex dump of cnt */ +void tfa_cnt_hexdump(void); +void tfa_cnt_dump(void); + +/* Timer functions (currently only used by livedata) */ +int start_timer(int, void (*)(void)); +void stop_timer(void); + +void extern_msleep_interruptible(int msec); + +enum Tfa98xx_Error tfa_open(int maxdev); +void tfa_close(int maxdev); + +int is_ext_dsp(int dev_idx); +enum Tfa98xx_Error tfa_send_calibration_values(); +enum Tfa98xx_Error tfa_start(int next_profile, int vstep); +enum Tfa98xx_Error tfa_stop(); + +enum Tfa98xx_Error tfa_system_open(unsigned char *slave_address, int count); +#if defined(__cplusplus) +} /* extern "C" */ +#endif +#endif /* TFACONTUTIL_H_ */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfaOsal.h b/techpack/audio/asoc/codecs/tfa9874/tfaOsal.h new file mode 100644 index 000000000000..18bf3a6576bd --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfaOsal.h @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2017 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. + */ + +/* + * tfaOsal.c + * + * Operating specifics + */ + + +#ifndef TFAOSAL_H_ +#define TFAOSAL_H_ + +/* load hal plugin from dynamic lib */ +void * tfaosal_plugload(char *libarg); +/*generic function to load library and symbol from the library*/ +extern void * load_library(char *libname); +extern void * load_symbol(void *dlib_handle, char *dl_sym); + +int tfaosal_filewrite(const char *fullname, unsigned char *buffer, int filelenght ); + +#define isSpaceCharacter(C) (C==' '||C=='\t') + +#if defined (WIN32) || defined(_X64) +//suppress warnings for unsafe string routines. +#pragma warning(disable : 4996) +#pragma warning(disable : 4054) +char *basename(const char *fullpath); +#define bzero(ADDR,SZ) memset(ADDR,0,SZ) +#endif + +void tfaRun_Sleepus(int us); + +/* + * Read file + */ +int tfaReadFile(char *fname, void **buffer); + +#endif diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_container.c b/techpack/audio/asoc/codecs/tfa9874/tfa_container.c new file mode 100644 index 000000000000..cf01b1db8489 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_container.c @@ -0,0 +1,2312 @@ +/* + * Copyright 2014-2017 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 "dbgprint.h" +#include "tfa_container.h" +#include "tfa.h" +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" + +/* defines */ +#define MODULE_BIQUADFILTERBANK 2 +#define BIQUAD_COEFF_SIZE 6 + +/* module globals */ +static uint8_t gslave_address = 0; /* This is used to SET the slave with the --slave option */ + +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 +*/ +enum tfa_error tfa_load_cnt(void *cnt, int length) +{ + nxpTfaContainer_t *cntbuf = (nxpTfaContainer_t *)cnt; + + 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')) { + pr_err("container sub-version not supported: %c%c\n", + cntbuf->subversion[0], cntbuf->subversion[1]); + return tfa_error_container; + } + + return tfa_error_ok; +} + +/* + * 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 (cont == NULL) + return NULL; + + 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 number of profiles for the Nth device + */ +int tfa_cnt_get_dev_nprof(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev; + int idx, nprof = 0; + + if (tfa->cnt == NULL) + return 0; + + if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev)) + return 0; + + dev = tfaContGetDevList(tfa->cnt, tfa->dev_idx); + if (dev) { + for (idx = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscProfile) { + nprof++; + } + } + } + + return nprof; +} + +/* + * 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(struct tfa_device *tfa, int prof_idx) { + nxpTfaVolumeStep2File_t *vp; + struct nxpTfaVolumeStepMax2File *vp3; + int vstep_count = 0; + vp = (nxpTfaVolumeStep2File_t *) tfacont_getfiledata(tfa, prof_idx, volstepHdr); + if (vp == NULL) + return 0; + /* check the header type to load different NrOfVStep appropriately */ + if (tfa->tfa_family == 2) { + /* this is actually tfa2, so re-read the buffer*/ + vp3 = (struct nxpTfaVolumeStepMax2File *) + tfacont_getfiledata(tfa, 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(struct tfa_device *tfa, int prof_idx, enum nxpTfaHeaderType type) +{ + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + nxpTfaFileDsc_t *file; + nxpTfaHeader_t *hdr; + unsigned int i; + + if (tfa->cnt == NULL) { + pr_err("invalid pointer to container file\n"); + return NULL; + } + + dev = tfaContGetDevList(tfa->cnt, tfa->dev_idx); + if (dev == NULL) { + pr_err("invalid pointer to container file device list\n"); + 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 *)tfa->cnt); + if (file != NULL) { + hdr = (nxpTfaHeader_t *)file->data; + /* check for file type */ + if ( hdr->id == type) { + 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(tfa->cnt, tfa->dev_idx, prof_idx); + if (prof == NULL) { + pr_err("invalid pointer to container file profile list\n"); + return NULL; + } + + for (i=0;ilength;i++) { + if (prof->list[i].type == dscFile) { + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + if (file != NULL) { + hdr= (nxpTfaHeader_t *)file->data; + if (hdr != NULL) { + /* check for file type */ + if ( hdr->id == type) { + return (nxpTfaFileDsc_t *)&file->data; + } + } + } + } + } + + if (tfa->verbose) + pr_debug("%s: no file found of type %d\n", __FUNCTION__, type); + + return NULL; +} + +/* + * write a parameter file to the device + */ +static enum Tfa98xx_Error tfaContWriteVstep(struct tfa_device *tfa, 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(tfa, vol); + if (err != Tfa98xx_Error_Ok) + return err; + + err = tfa98xx_dsp_write_preset(tfa, sizeof(vp->vstep[0].preset), vp->vstep[vstep].preset); + if (err != Tfa98xx_Error_Ok) + return err; + err = tfa_cont_write_filterbank(tfa, vp->vstep[vstep].filter); + + } else { + pr_err("Incorrect volume given. The value vstep[%d] >= %d\n", vstep , vp->vsteps); + err = Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) pr_debug("vstep[%d][%d]\n", tfa->dev_idx, vstep); + + return err; +} + +static struct nxpTfaVolumeStepMessageInfo * +tfaContGetmsgInfoFromReg(struct nxpTfaVolumeStepRegisterInfo *regInfo) +{ + char *p = (char*) regInfo; + p += sizeof(regInfo->NrOfRegisters) + (regInfo->NrOfRegisters * sizeof(uint32_t)); + return (struct nxpTfaVolumeStepMessageInfo*) p; +} + +static int +tfaContGetmsgLen(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + return (msgInfo->MessageLength.b[0] << 16) + (msgInfo->MessageLength.b[1] << 8) + msgInfo->MessageLength.b[2]; +} + +static struct nxpTfaVolumeStepMessageInfo * +tfaContGetNextmsgInfo(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + char *p = (char*) msgInfo; + int msgLen = tfaContGetmsgLen(msgInfo); + int type = msgInfo->MessageType; + + p += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength); + if (type == 3) + p += msgLen; + else + p += msgLen * 3; + + return (struct nxpTfaVolumeStepMessageInfo*) p; +} + +static struct nxpTfaVolumeStepRegisterInfo* +tfaContGetNextRegFromEndInfo(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + char *p = (char*) msgInfo; + p += sizeof(msgInfo->NrOfMessages); + return (struct nxpTfaVolumeStepRegisterInfo*) p; + +} + +static struct nxpTfaVolumeStepRegisterInfo* +tfaContGetRegForVstep(nxpTfaVolumeStepMax2File_t *vp, int idx) +{ + int i, j, nrMessage; + + struct nxpTfaVolumeStepRegisterInfo *regInfo + = (struct nxpTfaVolumeStepRegisterInfo*) vp->vstepsBin; + struct nxpTfaVolumeStepMessageInfo *msgInfo = NULL; + + for (i = 0; i < idx; i++) { + msgInfo = tfaContGetmsgInfoFromReg(regInfo); + nrMessage = msgInfo->NrOfMessages; + + for (j = 0; j < nrMessage; j++) { + msgInfo = tfaContGetNextmsgInfo(msgInfo); + } + regInfo = tfaContGetNextRegFromEndInfo(msgInfo); + } + + return regInfo; +} + +#pragma pack (push, 1) +struct tfa_partial_msg_block { + uint8_t offset; + uint16_t change; + uint8_t update[16][3]; +}; +#pragma pack (pop) + +static enum Tfa98xx_Error tfaContWriteVstepMax2_One(struct tfa_device *tfa, struct nxpTfaVolumeStepMessageInfo *new_msg, + struct nxpTfaVolumeStepMessageInfo *old_msg, int enable_partial_update) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int len = (tfaContGetmsgLen(new_msg) - 1) * 3; + char *buf = (char*)new_msg->ParameterData; + uint8_t *partial = NULL; + uint8_t cmdid[3]; + int use_partial_coeff = 0; + + if (enable_partial_update) { + if (new_msg->MessageType != old_msg->MessageType) { + pr_debug("Message type differ - Disable Partial Update\n"); + enable_partial_update = 0; + } else if (tfaContGetmsgLen(new_msg) != tfaContGetmsgLen(old_msg)) { + pr_debug("Message Length differ - Disable Partial Update\n"); + enable_partial_update = 0; + } + } + + if ((enable_partial_update) && (new_msg->MessageType == 1)) { + /* No patial updates for message type 1 (Coefficients) */ + enable_partial_update = 0; + if ((tfa->rev & 0xff) == 0x88) { + use_partial_coeff = 1; + } else if ((tfa->rev & 0xff) == 0x13) { + use_partial_coeff = 1; + } + } + + /* Change Message Len to the actual buffer len */ + memcpy(cmdid, new_msg->CmdId, sizeof(cmdid)); + + /* 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_needs_reset(tfa) == 1) { + if (new_msg->MessageType == 0) { + cmdid[2] = SB_PARAM_SET_ALGO_PARAMS; + if (tfa->verbose) + pr_debug("P-ID for SetAlgoParams modified!\n"); + } else if (new_msg->MessageType == 2) { + cmdid[2] = SB_PARAM_SET_MBDRC; + if (tfa->verbose) + pr_debug("P-ID for SetMBDrc modified!\n"); + } + } + + /* + * +sizeof(struct tfa_partial_msg_block) will allow to fit one + * additonnal partial block If the partial update goes over the len of + * a regular message ,we can safely write our block and check afterward + * that we are over the size of a usual update + */ + if (enable_partial_update) { + partial = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (!partial) + pr_debug("Partial update memory error - Disabling\n"); + } + + if (partial) { + uint8_t offset = 0, i = 0; + uint16_t *change; + uint8_t *n = new_msg->ParameterData; + uint8_t *o = old_msg->ParameterData; + uint8_t *p = partial; + uint8_t* trim = partial; + + /* set dspFiltersReset */ + *p++ = 0x02; + *p++ = 0x00; + *p++ = 0x00; + + while ((o < (old_msg->ParameterData + len)) && + (p < (partial + len - 3))) { + if ((offset == 0xff) || + (memcmp(n, o, 3 * sizeof(uint8_t)))) { + *p++ = offset; + change = (uint16_t*) p; + *change = 0; + p += 2; + + for (i = 0; + (i < 16) && (o < (old_msg->ParameterData + len)); + i++, n += 3, o += 3) { + if (memcmp(n, o, 3 * sizeof(uint8_t))) { + *change |= BIT(i); + memcpy(p, n, 3); + p += 3; + trim = p; + } + } + + offset = 0; + *change = cpu_to_be16(*change); + } else { + n += 3; + o += 3; + offset++; + } + } + + if (trim == partial) { + pr_debug("No Change in message - discarding %d bytes\n", len); + len = 0; + + } else if (trim < (partial + len - 3)) { + pr_debug("Using partial update: %d -> %d bytes\n", len , (int)(trim-partial+3)); + + /* Add the termination marker */ + memset(trim, 0x00, 3); + trim += 3; + + /* Signal This will be a partial update */ + cmdid[2] |= BIT(6); + buf = (char*) partial; + len = (int)(trim - partial); + } else { + pr_debug("Partial too big - use regular update\n"); + } + } + + if (use_partial_coeff) { + err = dsp_partial_coefficients(tfa, old_msg->ParameterData, new_msg->ParameterData); + } else if (len) { + uint8_t *buffer; + + if (tfa->verbose) + pr_debug("Command-ID used: 0x%02x%02x%02x \n", cmdid[0], cmdid[1], cmdid[2]); + + buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (buffer == NULL) { + err = Tfa98xx_Error_Fail; + } else { + memcpy(&buffer[0], cmdid, 3); + memcpy(&buffer[3], buf, len); + err = dsp_msg(tfa, 3 + len, (char *)buffer); + kmem_cache_free(tfa->cachep, buffer); + } + } + + if (partial) + kmem_cache_free(tfa->cachep, partial); + + return err; +} + +static enum Tfa98xx_Error tfaContWriteVstepMax2(struct tfa_device *tfa, nxpTfaVolumeStepMax2File_t *vp, int vstep_idx, int vstep_msg_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + struct nxpTfaVolumeStepRegisterInfo *regInfo = NULL; + struct nxpTfaVolumeStepMessageInfo *msgInfo = NULL, *p_msgInfo = NULL; + nxpTfaBitfield_t bitF; + int i, nrMessages, enp = tfa->partial_enable; + + if (vstep_idx >= vp->NrOfVsteps) { + pr_debug("Volumestep %d is not available \n", vstep_idx); + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->p_regInfo == NULL) { + if (tfa->verbose) + pr_debug("Inital vstep write\n"); + enp = 0; + } + + regInfo = tfaContGetRegForVstep(vp, vstep_idx); + + msgInfo = tfaContGetmsgInfoFromReg(regInfo); + nrMessages = msgInfo->NrOfMessages; + + if (enp) { + p_msgInfo = tfaContGetmsgInfoFromReg(tfa->p_regInfo); + if (nrMessages != p_msgInfo->NrOfMessages) { + pr_debug("Message different - Disable partial update\n"); + enp = 0; + } + } + + for (i = 0; i < nrMessages; i++) { + /* Messagetype(3) is Smartstudio Info! Dont send this! */ + if(msgInfo->MessageType == 3) { + /* MessageLength is in bytes */ + msgInfo = tfaContGetNextmsgInfo(msgInfo); + if(enp) + p_msgInfo = tfaContGetNextmsgInfo(p_msgInfo); + continue; + } + + /* If no vstepMsgIndex is passed on, all message needs to be send */ + if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER) || (vstep_msg_idx == i)) { + err = tfaContWriteVstepMax2_One(tfa, msgInfo, p_msgInfo, enp); + if (err != Tfa98xx_Error_Ok) { + /* + * Force a full update for the next write + * As the current status of the DSP is unknown + */ + tfa->p_regInfo = NULL; + return err; + } + } + + msgInfo = tfaContGetNextmsgInfo(msgInfo); + if(enp) + p_msgInfo = tfaContGetNextmsgInfo(p_msgInfo); + } + + tfa->p_regInfo = regInfo; + + 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(tfa , bitF); + if (err != Tfa98xx_Error_Ok) + return err; + } + + /* Save the current vstep */ + tfa_dev_set_swvstep(tfa, (unsigned short)vstep_idx); + + return err; +} + +/* + * Write DRC message to the dsp + * If needed modify the cmd-id + */ + +enum Tfa98xx_Error tfaContWriteDrcFile(struct tfa_device *tfa, int size, uint8_t data[]) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + uint8_t *msg = NULL; + + msg = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + memcpy(msg, data, size); + + if (TFA_GET_BF(tfa, SBSL) == 0) { + /* Only do this when not set already */ + if (msg[2] != SB_PARAM_SET_MBDRC) { + msg[2] = SB_PARAM_SET_MBDRC; + + if (tfa->verbose) { + pr_debug("P-ID for SetMBDrc modified!: "); + pr_debug("Command-ID used: 0x%02x%02x%02x \n", + msg[0], msg[1], msg[2]); + } + } + } + + /* Send cmdId + payload to dsp */ + err = dsp_msg(tfa, size, (const char *)msg); + + kmem_cache_free(tfa->cachep, msg); + + 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(struct tfa_device *tfa, 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 (tfa->verbose) { + tfaContShowHeader(hdr); + } + + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case msgHdr: /* generic DSP message */ + size = hdr->size - sizeof(nxpTfaMsgFile_t); + err = dsp_msg(tfa, size, (const char *)((nxpTfaMsgFile_t *)hdr)->data); + break; + case volstepHdr: + if (tfa->tfa_family == 2) { + err = tfaContWriteVstepMax2(tfa, (nxpTfaVolumeStepMax2File_t *)hdr, vstep_idx, vstep_msg_idx); + } else { + err = tfaContWriteVstep(tfa, (nxpTfaVolumeStep2File_t *)hdr, vstep_idx); + } + break; + case speakerHdr: + if (tfa->tfa_family == 2) { + /* Remove header and xml_id */ + size = hdr->size - sizeof(struct nxpTfaSpkHeader) - sizeof(struct nxpTfaFWVer); + + err = dsp_msg(tfa, size, + (const char *)(((nxpTfaSpeakerFile_t *)hdr)->data + (sizeof(struct nxpTfaFWVer)))); + } else { + size = hdr->size - sizeof(nxpTfaSpeakerFile_t); + err = tfa98xx_dsp_write_speaker_parameters(tfa, size, + (const unsigned char *)((nxpTfaSpeakerFile_t *)hdr)->data); + } + break; + case presetHdr: + size = hdr->size - sizeof(nxpTfaPreset_t); + err = tfa98xx_dsp_write_preset(tfa, size, (const unsigned char *)((nxpTfaPreset_t *)hdr)->data); + break; + case equalizerHdr: + err = tfa_cont_write_filterbank(tfa, ((nxpTfaEqualizerFile_t *)hdr)->filter); + break; + case patchHdr: + size = hdr->size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) ((nxpTfaPatch_t *)hdr)->data); + break; + case configHdr: + size = hdr->size - sizeof(nxpTfaConfig_t); + err = tfa98xx_dsp_write_config(tfa, 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(tfa, 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(tfa, 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 + */ +static nxpTfaDescPtr_t *tfa_cnt_get_dsc(nxpTfaContainer_t *cnt, nxpTfaDescriptorType_t type, int dev_idx) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(cnt, 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); + if ( !patchdsc ) /* no patch for this device, assume non-i2c */ + return 0; + 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 firmware version from the patch in this devicelist + */ +int tfa_cnt_get_patch_version(struct tfa_device *tfa) +{ + nxpTfaPatch_t *patchfile; + nxpTfaDescPtr_t *patchdsc; + uint8_t *data; + int size, version; + + if (tfa->cnt == NULL) + return -1; + + patchdsc = tfa_cnt_get_dsc(tfa->cnt, dscPatch, tfa->dev_idx); + patchdsc += 2; /* first the filename dsc and filesize, so skip them */ + patchfile = (nxpTfaPatch_t *)patchdsc; + + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t); + data = patchfile->data; + + version = (data[size-3] << 16) + (data[size-2] << 8) + data[size-1]; + + return version; +} + + +/* + * get the slave for the device if it exists + */ +enum Tfa98xx_Error tfaContGetSlave(struct tfa_device *tfa, uint8_t *slave_addr) +{ + nxpTfaDeviceList_t *dev = NULL; + + /* Make sure the cnt file is loaded */ + if (tfa->cnt != NULL) { + dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + } + + if (dev == NULL) { + /* Check if slave argument is used! */ + if(gslave_address == 0) { + return Tfa98xx_Error_Bad_Parameter; + } else { + *slave_addr = gslave_address; + return Tfa98xx_Error_Ok; + } + } + + *slave_addr = dev->dev; + return Tfa98xx_Error_Ok; +} + +/* If no container file is given, we can always have used the slave argument */ +void tfaContSetSlave(uint8_t slave_addr) +{ + gslave_address = slave_addr; +} + +/* + * lookup slave and return device index + */ +int tfa_cont_get_idx(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = NULL; + int i; + + for (i=0; icnt->ndev; i++) { + dev = tfaContDevice(tfa->cnt, i); + if (dev->dev == tfa->slave_address) + break; + + } + if (i == tfa->cnt->ndev) + return -1; + + return i; +} + +/* + * write a bit field + */ +enum Tfa98xx_Error tfaRunWriteBitfield(struct tfa_device *tfa, 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; +#if 1//def TFA_DEBUG + if (tfa->verbose) + // pr_err ("tfa bitfield: %s=0x%x (0x%x[%d..%d]=0x%x)\n", tfaContBfName(bfUni.field, tfa->rev), value, + // bfUni.Enum.address, bfUni.Enum.pos, bfUni.Enum.pos+bfUni.Enum.len, value); + pr_err ("tfa bitfield: 0x%x value : 0x%x)\n", value, + bfUni.field); + +#endif + error = tfa_set_bf(tfa, bfUni.field, value); + + return error; +} + +/* + * read a bit field + */ +enum Tfa98xx_Error tfaRunReadBitfield(struct tfa_device *tfa, nxpTfaBitfield_t *bf) +{ + enum Tfa98xx_Error error; + union { + uint16_t field; + nxpTfaBfEnum_t Enum; + } bfUni; + uint16_t regvalue, msk; + + bfUni.field = bf->field; + + error = reg_read(tfa, (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 + */ +static enum Tfa98xx_Error tfaRunWriteDspMem(struct tfa_device *tfa, nxpTfaDspMem_t *cfmem) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int i; + + for(i=0;isize;i++) { + if (tfa->verbose) + pr_debug("dsp mem (%d): 0x%02x=0x%04x\n", cfmem->type, cfmem->address, cfmem->words[i]); + + error = mem_write(tfa, cfmem->address++, 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 + */ +static enum Tfa98xx_Error tfaRunWriteFilter(struct tfa_device *tfa, 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((tfa->rev & 0xff) == 0x88) { + runs=2; + } + + if (tfa->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 == Tfa98xx_DMEM_ERR) { + if (tfa->verbose) { + pr_debug("Warning: XFilter settings are applied via msg file (ini filter[x] format is skipped).\n"); + } + /* Dont exit with an error here, We could continue without problems */ + return Tfa98xx_Error_Ok; + } + + /* 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(tfa->tfa_family == 2) { + error = tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, FW_PAR_ID_SET_MEMORY, sizeof(data), data); + } else { + error = tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, 4 /* param */ , sizeof(data), data); + } + } + +#ifdef TFA_DEBUG + if (tfa->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 + */ +static enum Tfa98xx_Error tfaRunWriteRegister(struct tfa_device *tfa, nxpTfaRegpatch_t *reg) +{ + enum Tfa98xx_Error error; + uint16_t value,newvalue; + + if (tfa->verbose) + pr_debug("register: 0x%02x=0x%04x (msk=0x%04x)\n", reg->address, reg->value, reg->mask); + + error = reg_read(tfa, reg->address, &value); + if (error) return error; + + value &= ~reg->mask; + newvalue = reg->value & reg->mask; + + value |= newvalue; + error = reg_write(tfa, reg->address, value); + + return error; + +} + +// write reg and bitfield items in the devicelist to the target +enum Tfa98xx_Error tfaContWriteRegsDev(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->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 *)tfa->cnt); + err = tfaRunWriteBitfield(tfa , *bitF); + } + if ( dev->list[i].type == dscRegister ) { + err = tfaRunWriteRegister(tfa, (nxpTfaRegpatch_t *)( dev->list[i].offset+(char*)tfa->cnt)); + } + + if ( err ) break; + } + + return err; +} + +// write reg and bitfield items in the profilelist the target +enum Tfa98xx_Error tfaContWriteRegsProf(struct tfa_device *tfa, int prof_idx) +{ + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->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 (tfa->verbose) + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(tfa->cnt, &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 *)tfa->cnt); + err = tfaRunWriteBitfield(tfa , *bitf); + } + if ( prof->list[i].type == dscRegister ) { + err = tfaRunWriteRegister(tfa, (nxpTfaRegpatch_t *)( prof->list[i].offset+(char*)tfa->cnt)); + } + if ( err ) break; + } + return err; +} + +// write patchfile in the devicelist to the target +enum Tfa98xx_Error tfaContWritePatch(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->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 *)tfa->cnt); + patchfile =(nxpTfaPatch_t *)&file->data; + if (tfa->verbose) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) patchfile->data); + if ( err ) return err; + } + } + + return Tfa98xx_Error_Ok; +} + +/** + * Create a buffer which can be used to send to the dsp. + */ +static void create_dsp_buffer_msg(struct tfa_device *tfa, nxpTfaMsg_t *msg, char *buffer, int *size) +{ + int i, nr = 0; + + (void)tfa; + + /* Copy cmdId. Remember that the cmdId is reversed */ + buffer[nr++] = msg->cmdId[2]; + buffer[nr++] = msg->cmdId[1]; + buffer[nr++] = msg->cmdId[0]; + + /* Copy the data to the buffer */ + for(i=0; imsg_size; i++) { + buffer[nr++] = (uint8_t) ((msg->data[i] >> 16) & 0xffff); + buffer[nr++] = (uint8_t) ((msg->data[i] >> 8) & 0xff); + buffer[nr++] = (uint8_t) (msg->data[i] & 0xff); + } + + *size = nr; +} + +// write all param files in the devicelist to the target +enum Tfa98xx_Error tfaContWriteFiles(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + nxpTfaFileDsc_t *file; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 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 *)tfa->cnt); + if ( tfaContWriteFile(tfa, 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 || + dev->list[i].type == dscSetFwkUseCase || + dev->list[i].type == dscSetVddpConfig ) { + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + ( dev->list[i].offset+(char*)tfa->cnt), buffer, &size); + if (tfa->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]); + } + + err = dsp_msg(tfa, size, buffer); + } + + if ( dev->list[i].type == dscCmd ) { + size = *(uint16_t *)(dev->list[i].offset+(char*)tfa->cnt); + + err = dsp_msg(tfa, size, dev->list[i].offset+2+(char*)tfa->cnt); + if (tfa->verbose) { + const char *cmd_id = dev->list[i].offset+2+(char*)tfa->cnt; + pr_debug("Writing cmd=0x%02x%02x%02x \n", (uint8_t)cmd_id[0], (uint8_t)cmd_id[1], (uint8_t)cmd_id[2]); + } + } + if (err != Tfa98xx_Error_Ok) + break; + + if ( dev->list[i].type == dscCfMem ) { + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt)); + } + + 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(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 and 3 is the msg + 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 *)tfa->cnt); + err = tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + break; + case dscPatch: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + patchfile =(nxpTfaPatch_t *)&file->data; + if (tfa->verbose) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) patchfile->data); + break; + case dscCfMem: + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt)); + break; + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + case dscSetFwkUseCase: + case dscSetVddpConfig: + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + (prof->list[i].offset+(uint8_t *)tfa->cnt), buffer, &size); + if (tfa->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]); + } + + err = dsp_msg(tfa, size, buffer); + break; + default: + /* ignore any other type */ + break; + } + } + + return err; +} + +static enum Tfa98xx_Error tfaContWriteItem(struct tfa_device *tfa, nxpTfaDescPtr_t * dsc) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaRegpatch_t *reg; + nxpTfaMode_t *cas; + nxpTfaBitfield_t *bitf; + + // When no DSP should only write to HW registers. + if(tfa->ext_dsp == 0 && !(dsc->type == dscBitfield || dsc->type == dscRegister)) { + return Tfa98xx_Error_Ok; + } + + switch (dsc->type) { + case dscDefault: + case dscDevice: // ignore + case dscProfile: // profile list + break; + case dscRegister: // register patch + reg = (nxpTfaRegpatch_t *)(dsc->offset+(uint8_t *)tfa->cnt); + return tfaRunWriteRegister(tfa, 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(tfa->cnt, dsc)); + break; + case dscFile: // filename + file contents + case dscPatch: + break; + case dscMode: + cas = (nxpTfaMode_t *)(dsc->offset+(uint8_t *)tfa->cnt); + if(cas->value == Tfa98xx_Mode_RCV) + tfa98xx_select_mode(tfa, Tfa98xx_Mode_RCV); + else + tfa98xx_select_mode(tfa, Tfa98xx_Mode_Normal); + break; + case dscCfMem: + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(dsc->offset+(uint8_t *)tfa->cnt)); + break; + case dscBitfield: + bitf = (nxpTfaBitfield_t *)(dsc->offset+(uint8_t *)tfa->cnt); + return tfaRunWriteBitfield(tfa , *bitf); + break; + case dscFilter: + return tfaRunWriteFilter(tfa, (nxpTfaContBiquad_t *)(dsc->offset+(uint8_t *)tfa->cnt)); + 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(struct tfa_device *tfa, int prof_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + unsigned int i; + int status; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) { + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(tfa->cnt, &prof->name), prof_idx); + pr_debug("Waiting for CLKS... \n"); + } + + for(i=10; i>0; i--) { + err = tfa98xx_dsp_system_stable(tfa, &status); + if(status) + break; + else + msleep_interruptible(10); + } + + if(i==0) { + if (tfa->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(tfa, &prof->list[i]) != Tfa98xx_Error_Ok) + return Tfa98xx_Error_Bad_Parameter; + } + } + + return err; +} + +unsigned int tfa98xx_get_profile_sr(struct tfa_device *tfa, unsigned int prof_idx) +{ + nxpTfaBitfield_t *bitf; + unsigned int i; + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + int fs_profile = -1; + + dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + if (!dev) + return 0; + + prof = tfaContGetDevProfList(tfa->cnt, tfa->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 *)tfa->cnt); + if (bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + } + + if (tfa->verbose) + pr_debug("%s - profile fs: 0x%x = %dHz (%d - %d)\n", + __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + tfa->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 *)tfa->cnt); + if (bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + /* Ignore register case */ + } + + if (tfa->verbose) + pr_debug("%s - default fs: 0x%x = %dHz (%d - %d)\n", + __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + tfa->dev_idx, prof_idx); + + if (fs_profile != -1) + return tfa98xx_sr_from_field(fs_profile); + + return 48000; /* default of HW */ +} + +static enum Tfa98xx_Error get_sample_rate_info(struct tfa_device *tfa, 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 *)tfa->cnt); + if(bitf->field == TFA_FAM(tfa, 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 *)tfa->cnt); + if(bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_next_profile = bitf->value; + break; + } + } + } + + /* Enable if needed for debugging! + if (tfa->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 (tfa->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(tfa, fs_next_profile); + } else if(fs_default_profile != fs_previous_profile) { + if (tfa->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(tfa, 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(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + nxpTfaProfileList_t *previous_prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, tfa_dev_get_swprof(tfa)); + char buffer[(MEMTRACK_MAX_WORDS * 4) + 4] = {0}; //every word requires 3 or 4 bytes, and 3 or 4 is the msg + unsigned int i, k=0, j=0, tries=0; + nxpTfaFileDsc_t *file; + int size = 0, ready, fs_previous_profile = 8; /* default fs is 48kHz*/ + + if ( !prof || !previous_prof ) { + pr_err("Error trying to get the (previous) swprofile \n"); + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) { + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), + tfaContProfileName(tfa->cnt, tfa->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 (tfa->verbose) { + pr_debug("The new profile (%s) is in the same group as the current profile (%s) \n", + tfaContGetString(tfa->cnt, &prof->name), tfaContGetString(tfa->cnt, &previous_prof->name)); + } + } else { + /* mute */ + err = tfaRunMute(tfa); + if (err) return err; + + /* Get current sample rate before we start switching */ + fs_previous_profile = TFA_GET_BF(tfa, AUDFS); + + /* clear SBSL to make sure we stay in initCF state */ + if (tfa->tfa_family == 2) { + TFA_SET_BF_VOLATILE(tfa, SBSL, 0); + } + + /* When we switch profile we first power down the subsystem + * This should only be done when we are in operating mode + */ + if (((tfa->tfa_family == 2) && (TFA_GET_BF(tfa, MANSTATE) >= 6)) || (tfa->tfa_family != 2)) { + err = tfa98xx_powerdown(tfa, 1); + if (err) return err; + + /* Wait until we are in PLL powerdown */ + do { + err = tfa98xx_dsp_system_stable(tfa, &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 (tfa->verbose) { + pr_debug("---------- default settings profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &previous_prof->name), tfa_dev_get_swprof(tfa)); + } + + err = show_current_state(tfa); + + /* 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(tfa, &previous_prof->list[i]) != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + } + } + + if (tfa->verbose) + pr_debug("---------- new settings profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &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 dscSetFwkUseCase: + case dscSetVddpConfig: + case dscCmd: + case dscFilter: + case dscDefault: + /* When one of these files are found, we exit */ + i = prof->length; + break; + default: + err = tfaContWriteItem(tfa, &prof->list[i]); + if ( err != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + break; + } + } + + if (prof->group != previous_prof->group || prof->group == 0) { + if (tfa->tfa_family == 2) + TFA_SET_BF_VOLATILE(tfa, MANSCONF, 1); + + /* Leave powerdown state */ + err = tfa_cf_powerup(tfa); + if (err) return err; + + err = show_current_state(tfa); + + if (tfa->tfa_family == 2) { + /* Reset SBSL to 0 (workaround of enbl_powerswitch=0) */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 0); + /* Sending commands to DSP we need to make sure RST is 0 (otherwise we get no response)*/ + TFA_SET_BF(tfa, RST,0); + } + } + + /* Check if there are sample rate changes */ + err = get_sample_rate_info(tfa, 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(tfa->ext_dsp != 0) { + if (tfa->tfa_family == 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 ( tfa->verbose && k==0) { + pr_debug("---------- files default profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &previous_prof->name), prof_idx); + k++; + } + file = (nxpTfaFileDsc_t *)(previous_prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + } + } + } + } + + if ( tfa->verbose) { + pr_debug("---------- files new profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &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: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0){ + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaContWriteFile(tfa, 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: + case dscSetFwkUseCase: + case dscSetVddpConfig: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0) { + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + ( prof->list[i].offset+(char*)tfa->cnt), buffer, &size); + err = dsp_msg(tfa, size, buffer); + + if (tfa->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: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0) { + size = *(uint16_t *)(prof->list[i].offset+(char*)tfa->cnt); + err = dsp_msg(tfa, size, prof->list[i].offset+2+(char*)tfa->cnt); + if (tfa->verbose) { + const char *cmd_id = prof->list[i].offset+2+(char*)tfa->cnt; + pr_debug("Writing cmd=0x%02x%02x%02x \n", (uint8_t)cmd_id[0], (uint8_t)cmd_id[1], (uint8_t)cmd_id[2]); + } + } + break; + default: + /* This allows us to write bitfield, registers or xmem after files */ + if (tfaContWriteItem(tfa, &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) && (tfa->tfa_family == 2)) { + if (TFA_GET_BF(tfa, REFCKSEL) == 0) { + /* set SBSL to go to operation mode */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 1); + } + } + + return err; +} + +/* + * process only vstep in the profilelist + * + */ +enum Tfa98xx_Error tfaContWriteFilesVstep(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->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 (tfa->verbose) + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), + tfaContProfileName(tfa->cnt, tfa->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 *)tfa->cnt); + hdr = (nxpTfaHeader_t *)file->data; + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case volstepHdr: + if ( tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER) ) + return Tfa98xx_Error_Bad_Parameter; + break; + default: + break; + } + } + } + + return err; +} + +char *tfaContGetString(nxpTfaContainer_t *cnt, nxpTfaDescPtr_t *dsc) +{ + if ( dsc->type != dscString) + return "Undefined string"; + + return dsc->offset+(char*)cnt; +} + +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 == dscSetFwkUseCase) + return "SetFwkUseCase"; + else if(type == dscSetVddpConfig) + return "SetVddpConfig"; + else if(type == dscFilter) + return "filter"; + else + return "Undefined string"; +} + +/* + * Get the name of the device at a certain index in the container file + * return device name + */ +char *tfaContDeviceName(nxpTfaContainer_t *cnt, int dev_idx) +{ + nxpTfaDeviceList_t *dev; + + dev = tfaContDevice(cnt, dev_idx); + if (dev == NULL) + return "!ERROR!"; + + return tfaContGetString(cnt, &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(struct tfa_device *tfa, char *name) +{ + unsigned int i; + int len = 0; + + for(i=0; icnt->application); i++) { + if (isalnum(tfa->cnt->application[i])) /* copy char if valid */ + name[len++] = tfa->cnt->application[i]; + if (tfa->cnt->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(struct tfa_device *tfa) +{ + int prof, cal_idx = -2; + + if ( (tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev) ) + return -1; + + /* search for the calibration profile in the list of profiles */ + for (prof = 0; prof < tfa->cnt->nprof; prof++) { + if(strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, prof), ".cal") != NULL) { + cal_idx = prof; + pr_debug("Using calibration profile: '%s'\n", tfaContProfileName(tfa->cnt, tfa->dev_idx, prof)); + break; + } + } + + return cal_idx; +} + +/** + * Is the profile a tap profile + */ +int tfaContIsTapProfile(struct tfa_device *tfa, int prof_idx) +{ + if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev)) + return -1; + + /* Check if next profile is tap profile */ + if (strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, prof_idx), ".tap") != NULL) { + pr_debug("Using Tap profile: '%s'\n", tfaContProfileName(tfa->cnt, tfa->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(nxpTfaContainer_t *cnt, int dev_idx, int prof_idx) +{ + nxpTfaProfileList_t *prof = NULL; + + /* the Nth profiles for this device */ + prof = tfaContGetDevProfList(cnt, dev_idx, prof_idx); + + /* If the index is out of bound */ + if (prof == NULL) + return "NONE"; + + return tfaContGetString(cnt, &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; +} + +/* + * return the device list pointer + */ +nxpTfaDeviceList_t *tfaContDevice(nxpTfaContainer_t *cnt, int dev_idx) +{ + return tfaContGetDevList(cnt, 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; +} + +/* + * 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 + crc = ~crc32_le(~0u, base, size); + + return crc != cont->CRC; +} + +static void get_all_features_from_cnt(struct tfa_device *tfa, int *hw_feature_register, int sw_feature_register[2]) +{ + nxpTfaFeatures_t *features; + int i; + + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->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 *)tfa->cnt); + *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(struct tfa_device *tfa, int *hw_feature_register) +{ + int sw_feature_register[2]; + get_all_features_from_cnt(tfa, hw_feature_register, sw_feature_register); +} + +/* wrapper function */ +void get_sw_features_from_cnt(struct tfa_device *tfa, int sw_feature_register[2]) +{ + int hw_feature_register; + get_all_features_from_cnt(tfa, &hw_feature_register, sw_feature_register); +} + +enum Tfa98xx_Error tfa98xx_factory_trimmer(struct tfa_device *tfa) +{ + return (tfa->dev_ops.factory_trimmer)(tfa); +} + +enum Tfa98xx_Error tfa_set_filters(struct tfa_device *tfa, int prof_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->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(tfa, 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(tfa, &prof->list[i]) != Tfa98xx_Error_Ok) + return err; + } + } + + return err; +} + +int tfa_tib_dsp_msgmulti(struct tfa_device *tfa, int length, const char *buffer) +{ + uint8_t *buf = (uint8_t *)buffer; + static uint8_t *blob = NULL, *blobptr; /* TODO: not multi-thread safe */ + static int total = 0; /* TODO: not multi-thread safe */ + int post_len = 0; + + /* checks for 24b_BE or 32_LE */ + int len_word_in_bytes = (tfa->convert_dsp32) ? 4 : 3; + /* TODO: get rid of these magic constants max size should depend on the tfa device type */ + int tfadsp_max_msg_size = (tfa->convert_dsp32) ? 5336 : 4000; + + /* No data found*/ + if(length == -1 && blob == NULL) { + return -1; + } + + if (length == -1) { + int i; + /* set last length field to zero */ + for (i = total; i < (total + len_word_in_bytes); i++) { + blob[i] = 0; + } + total += len_word_in_bytes; + memcpy(buf, blob, total); + + kfree(blob); + blob = NULL; /* Set to NULL pointer, otherwise no new malloc is done! */ + return total; + } + + if (blob == NULL) { + if (tfa->verbose) + pr_debug("%s, Creating the multi-message \n\n", __FUNCTION__); + + blob = kmalloc(tfadsp_max_msg_size, GFP_KERNEL); + /* add command ID for multi-msg = 0x008015 */ + if (tfa->convert_dsp32) { + blob[0] = 0x15; + blob[1] = 0x80; + blob[2] = 0x0; + blob[3] = 0x0; + } + else { + blob[0] = 0x0; + blob[1] = 0x80; + blob[2] = 0x15; + } + blobptr = blob; + blobptr += len_word_in_bytes; + total = len_word_in_bytes; + } + + if (tfa->verbose) { + pr_debug("%s, id:0x%02x%02x%02x, length:%d \n", __FUNCTION__, buf[0], buf[1], buf[2], length); + } + + /* check total message size after concatination */ + post_len = total+length+(2*len_word_in_bytes); + if (post_len > tfadsp_max_msg_size) { + //pr_debug("New multi-message too large! (%d >= %d (max.)), current length: %d\n", post_len, tfadsp_max_msg_size, total); + return Tfa98xx_Error_Buffer_too_small; + } + + /* add length field (length in words) to the multi message */ + if (tfa->convert_dsp32) { + *blobptr++ = (uint8_t)((length/len_word_in_bytes) & 0xff); /* lsb */ + *blobptr++ = (uint8_t)(((length/len_word_in_bytes) & 0xff00) >> 8); /* msb */ + *blobptr++ = 0x0; + *blobptr++ = 0x0; + } + else { + *blobptr++ = 0x0; + *blobptr++ = (uint8_t)(((length/len_word_in_bytes) & 0xff00) >> 8); /* msb */ + *blobptr++ = (uint8_t)((length/len_word_in_bytes) & 0xff); /* lsb */ + } + memcpy(blobptr, buf, length); + blobptr += length; + total += (length + len_word_in_bytes); + + /* SetRe25 message is always the last message of the multi-msg */ + if (tfa->convert_dsp32) { + if (buf[1] == 0x81 && buf[0] == SB_PARAM_SET_RE25C) { + return 1; /* 1 means last message is done! */ + } + } else { + if (buf[1] == 0x81 && buf[2] == SB_PARAM_SET_RE25C) { + return 1; /* 1 means last message is done! */ + } + } + + return 0; +} diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_container.h b/techpack/audio/asoc/codecs/tfa9874/tfa_container.h new file mode 100644 index 000000000000..c35cba467ec7 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_container.h @@ -0,0 +1,364 @@ +/* + * Copyright 2013-2017 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. + */ + +/* + * 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" + +/** +* Pass the container buffer, initialize and allocate internal memory. +* +* @param cnt 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); + +/** + * Return the descriptor string + * @param cnt pointer to the container struct + * @param dsc pointer to nxpTfa descriptor + * @return descriptor string + */ +char *tfaContGetString(nxpTfaContainer_t *cnt, nxpTfaDescPtr_t *dsc); + +/** + * 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 slave for the device if it exists. + * @param tfa the device struct pointer + * @param slave_addr the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContGetSlave(struct tfa_device *tfa, uint8_t *slave_addr); + +void tfaContSetSlave(uint8_t slave_addr); + +/** + * Get the index for a skave address. + * @param tfa the device struct pointer + * @return the device index + */ +int tfa_cont_get_idx(struct tfa_device *tfa); + +/** + * Write reg and bitfield items in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsDev(struct tfa_device *tfa); + +/** + * Write reg and bitfield items in the profilelist to the target. + * @param tfa the device struct pointer + * @param prof_idx the profile index + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsProf(struct tfa_device *tfa, int prof_idx); + +/** + * Write a patchfile in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWritePatch(struct tfa_device *tfa); + +/** + * Write all param files in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFiles(struct tfa_device *tfa); + +/** + * Get sample rate from passed profile index + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @return sample rate value + */ +unsigned int tfa98xx_get_profile_sr(struct tfa_device *tfa, unsigned int prof_idx); + +/** + * Get the device name string + * @param cnt the pointer to the container struct + * @param dev_idx the index of the device + * @return device name string or error string if not found + */ +char *tfaContDeviceName(nxpTfaContainer_t *cnt, int dev_idx); + +/** + * Get the application name from the container file application field + * @param tfa the device struct pointer + * @param name the input stringbuffer with size: sizeof(application field)+1 + * @return actual string length + */ +int tfa_cnt_get_app_name(struct tfa_device *tfa, char *name); + +/** + * Get profile index of the calibration profile + * @param tfa the device struct pointer + * @return profile index, -2 if no calibration profile is found or -1 on error + */ +int tfaContGetCalProfile(struct tfa_device *tfa); + +/** + * Is the profile a tap profile ? + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @return 1 if the profile is a tap profile or 0 if not + */ +int tfaContIsTapProfile(struct tfa_device *tfa, int prof_idx); + +/** + * Get the name of the profile at certain index for a device in the container file + * @param cnt the pointer to the container struct + * @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(nxpTfaContainer_t *cnt, int dev_idx, int prof_idx); + +/** + * Process all items in the profilelist + * NOTE an error return during processing will leave the device muted + * @param tfa the device struct pointer + * @param prof_idx index of the profile + * @param vstep_idx index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteProfile(struct tfa_device *tfa, 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(int dev_idx, char *configuration); + +enum Tfa98xx_Error tfa_cont_write_filterbank(struct tfa_device *tfa, nxpTfaFilter_t *filter); + +/** + * Write all param files in the profilelist to the target + * this is used during startup when maybe ACS is set + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @param vstep_idx the index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFilesProf(struct tfa_device *tfa, int prof_idx, int vstep_idx); +enum Tfa98xx_Error tfaContWriteFilesVstep(struct tfa_device *tfa, int prof_idx, int vstep_idx); +enum Tfa98xx_Error tfaContWriteDrcFile(struct tfa_device *tfa, 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 number of profiles for device from contaienr + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @return device list pointer + */ +int tfa_cnt_get_dev_nprof(struct tfa_device *tfa); + + +/** + * 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 cnt pointer to the container struct + * @param dev_idx the index of the device + * @return pointer to device list + */ +nxpTfaDeviceList_t *tfaContDevice(nxpTfaContainer_t *cnt, int dev_idx); + +/** + * 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 tfa the device struct pointer + * @param bf bitfield to write + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunWriteBitfield(struct tfa_device *tfa, nxpTfaBitfield_t bf); + +/** + * Write a parameter file to the device + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx); + +/** + * Get the max volume step associated with Nth profile for the Nth device + * @param tfa the device struct pointer + * @param prof_idx profile index + * @return the number of vsteps + */ +int tfacont_get_max_vstep(struct tfa_device *tfa, 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 tfa the device struct pointer + * @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(struct tfa_device *tfa, 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 tfa the device struct pointer + * @param bf bitfield to read out + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunReadBitfield(struct tfa_device *tfa, nxpTfaBitfield_t *bf); + +/** + * Get hw feature bits from container file + * @param tfa the device struct pointer + * @param hw_feature_register pointer to where hw features are stored + */ +void get_hw_features_from_cnt(struct tfa_device *tfa, int *hw_feature_register); + +/** + * Get sw feature bits from container file + * @param tfa the device struct pointer + * @param sw_feature_register pointer to where sw features are stored + */ +void get_sw_features_from_cnt(struct tfa_device *tfa, int sw_feature_register[2]); + +/** + * Factory trimming for the Boost converter + * check if there is a correction needed + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa98xx_factory_trimmer(struct tfa_device *tfa); + +/** + * Search for filters settings and if found then write them to the device + * @param tfa the device struct pointer + * @param prof_idx profile to look in + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfa_set_filters(struct tfa_device *tfa, int prof_idx); + +/** + * Get the firmware version from the patch in the container file + * @param tfa the device struct pointer + * @return firmware version + */ +int tfa_cnt_get_patch_version(struct tfa_device *tfa); + +int tfa_tib_dsp_msgmulti(struct tfa_device *tfa, int length, const char *buffer); + +#endif /* TFACONTAINER_H_ */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_debug.c b/techpack/audio/asoc/codecs/tfa9874/tfa_debug.c new file mode 100644 index 000000000000..d6227262e77f --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_debug.c @@ -0,0 +1,416 @@ +/* + * Copyright 2014-2017 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 "dbgprint.h" +#include "tfa_service.h" +#include "tfa98xx_tfafieldnames.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_RpcInvalidCC: + pErrStr = "RpcInvalidCC"; + break; + case Tfa98xx_Error_RpcInvalidSeq: + pErrStr = "RpcInvalidSeq"; + break; + case Tfa98xx_Error_RpcInvalidParam: + pErrStr = "RpcInvalidParam"; + break; + case Tfa98xx_Error_RpcBufferOverflow: + pErrStr = "RpcBufferOverflow"; + break; + case Tfa98xx_Error_RpcCalibBusy: + pErrStr = "RpcCalibBusy"; + break; + case Tfa98xx_Error_RpcCalibFailed: + pErrStr = "RpcCalibFailed"; + 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 (strcasecmp(name, table[n].bfName)==0) + return table[n].bfEnum; + } while (table[n++].bfEnum != 0xffff); + + return 0xffff; +} + +/* + * tfa2 bitfield name table + */ +TFA2_NAMETABLE +TFA2_BITNAMETABLE + +/* + * tfa1 bitfield name tables + */ +TFA1_NAMETABLE +TFA9896_NAMETABLE +TFA9872_NAMETABLE +TFA9874_NAMETABLE +TFA9890_NAMETABLE +TFA9891_NAMETABLE +TFA9887_NAMETABLE +TFA1_BITNAMETABLE +TFA9912_NAMETABLE +TFA9894_NAMETABLE +TFA9896_BITNAMETABLE +TFA9872_BITNAMETABLE +TFA9874_BITNAMETABLE +TFA9912_BITNAMETABLE +TFA9890_BITNAMETABLE +TFA9891_BITNAMETABLE +TFA9887_BITNAMETABLE +TFA9894_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 0x96: + name = tfa_bf2name(Tfa9896BitNames, num); + break; + case 0x72: + name = tfa_bf2name(Tfa9872BitNames, num); + break; + case 0x74: + name = tfa_bf2name(Tfa9874BitNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891BitNames, num); + 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; + case 0x13: + name = tfa_bf2name(Tfa9912BitNames, num); + break; + case 0x94: + name = tfa_bf2name(Tfa9894BitNames, num); + 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 0x96: + name = tfa_bf2name(Tfa9896DatasheetNames, num); + break; + case 0x72: + name = tfa_bf2name(Tfa9872DatasheetNames, num); + break; + case 0x74: + name = tfa_bf2name(Tfa9874DatasheetNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891DatasheetNames, num); + 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; + case 0x13: + name = tfa_bf2name(Tfa9912DatasheetNames, num); + break; + case 0x94: + name = tfa_bf2name(Tfa9894DatasheetNames, num); + 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 0x96: + bfnum = tfa_name2bf(Tfa9896DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9896BitNames, name);/* try generic table */ + break; + case 0x72: + bfnum = tfa_name2bf(Tfa9872DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9872BitNames, name);/* try long bitname table */ + break; + case 0x74: + bfnum = tfa_name2bf(Tfa9874DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9874BitNames, name);/* try long bitname 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; + case 0x13: + bfnum = tfa_name2bf(Tfa9912DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9912BitNames, name);/* try long bitname table */ + break; + case 0x94: + bfnum = tfa_name2bf(Tfa9894DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9894BitNames, name);/* try long bitname 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; + bfnum = tfa_name2bf(Tfa9872DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9874DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9896DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9912DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9894DatasheetNames, 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); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9872BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9874BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9896BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9912BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9894BitNames, name); + + return bfnum; +} diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_device.h b/techpack/audio/asoc/codecs/tfa9874/tfa_device.h new file mode 100644 index 000000000000..5b7bc6708e84 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_device.h @@ -0,0 +1,296 @@ +/* + * Copyright 2014-2017 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. + */ + + +/**\file + * + * The tfa_device interface controls a single I2C device instance by + * referencing to the device specific context provided by means of the + * tfa_device structure pointer. + * Multiple instances of tfa_device structures will be created and maintained + * by the caller. + * + * The API is functionally grouped as: + * - tfa_dev basic codec interface to probe, start/stop and control the device state + * - access to internal MTP storage + * - abstraction for interrupt bits and handling + * - container reading support + */ +#ifndef __TFA_DEVICE_H__ +#define __TFA_DEVICE_H__ + +#include "config.h" + +struct tfa_device; + +/* + * hw/sw feature bit settings in MTP + */ +enum featureSupport { + supportNotSet, /**< default means not set yet */ + supportNo, /**< no support */ + supportYes /**< supported */ +}; +/* + * supported Digital Audio Interfaces bitmap + */ +enum Tfa98xx_DAI { + Tfa98xx_DAI_I2S = 0x01, /**< I2S only */ + Tfa98xx_DAI_TDM = 0x02, /**< TDM, I2S */ + Tfa98xx_DAI_PDM = 0x04, /**< PDM */ + }; + +/* + * device ops function structure + */ +struct tfa_device_ops { + enum Tfa98xx_Error(*dsp_msg)(struct tfa_device *tfa, int length, const char *buf); + enum Tfa98xx_Error(*dsp_msg_read)(struct tfa_device *tfa, int length, unsigned char *bytes); + enum Tfa98xx_Error(*reg_read)(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value); + enum Tfa98xx_Error(*reg_write)(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); + enum Tfa98xx_Error(*mem_read)(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues); + enum Tfa98xx_Error(*mem_write)(struct tfa_device *tfa, unsigned short address, int value, int memtype); + + enum Tfa98xx_Error (*tfa_init)(struct tfa_device *tfa); /**< init typically for loading optimal settings */ + enum Tfa98xx_Error (*dsp_reset)(struct tfa_device *tfa, int state); /**< reset the coolflux dsp */ + enum Tfa98xx_Error (*dsp_system_stable)(struct tfa_device *tfa, int *ready); /**< ready when clocks are stable to allow DSP subsystem access */ + enum Tfa98xx_Error (*dsp_write_tables)(struct tfa_device *tfa, int sample_rate); /**< write the device/type specific delaytables */ + enum Tfa98xx_Error (*auto_copy_mtp_to_iic)(struct tfa_device *tfa); /**< Set auto_copy_mtp_to_iic */ + enum Tfa98xx_Error (*factory_trimmer)(struct tfa_device *tfa); /**< Factory trimming for the Boost converter */ + int (*set_swprof)(struct tfa_device *tfa, unsigned short new_value); /**< Set the sw profile in the struct and the hw register */ + int (*get_swprof)(struct tfa_device *tfa); /**< Get the sw profile from the hw register */ + int(*set_swvstep)(struct tfa_device *tfa, unsigned short new_value); /**< Set the sw vstep in the struct and the hw register */ + int(*get_swvstep)(struct tfa_device *tfa); /**< Get the sw vstep from the hw register */ + int(*get_mtpb)(struct tfa_device *tfa); /**< get status of MTB busy bit*/ + enum Tfa98xx_Error (*set_mute)(struct tfa_device *tfa, int mute); /**< set mute */ + enum Tfa98xx_Error (*faim_protect)(struct tfa_device *tfa, int state); /**< Protect FAIM from being corrupted */ + enum Tfa98xx_Error(*set_osc_powerdown)(struct tfa_device *tfa, int state); /**< Allow to change internal osc. gating settings */ +}; + +/** + * Device states and modifier flags to allow a device/type independent fine + * grained control of the internal state.\n + * Values below 0x10 are referred to as base states which can be or-ed with + * state modifiers, from 0x10 and higher. + * + */ +enum tfa_state { + TFA_STATE_UNKNOWN, /**< unknown or invalid */ + TFA_STATE_POWERDOWN, /**< PLL in powerdown, Algo is up/warm */ + TFA_STATE_INIT_HW, /**< load I2C/PLL hardware setting (~wait2srcsettings) */ + TFA_STATE_INIT_CF, /**< coolflux HW access possible (~initcf) */ + TFA_STATE_INIT_FW, /**< DSP framework active (~patch loaded) */ + TFA_STATE_OPERATING, /**< Amp and Algo running */ + TFA_STATE_FAULT, /**< An alarm or error occurred */ + TFA_STATE_RESET, /**< I2C reset and ACS set */ + /* --sticky state modifiers-- */ + TFA_STATE_MUTE=0x10, /**< Algo & Amp mute */ + TFA_STATE_UNMUTE=0x20, /**< Algo & Amp unmute */ + TFA_STATE_CLOCK_ALWAYS=0x40, /**< PLL connect to internal oscillator */ + TFA_STATE_CLOCK_AUDIO=0x80, /**< PLL connect to audio clock (BCK/FS) */ + TFA_STATE_LOW_POWER=0x100, /**< lowest possible power state */ +}; + +/** + * This is the main tfa device context structure, it will carry all information + * that is needed to handle a single I2C device instance. + * All functions dealing with the device will need access to the fields herein. + */ +struct tfa_device { + int dev_idx; /**< device container index */ + int in_use; + int buffer_size; /**< lowest level max buffer size */ + int has_msg; /**< support direct dsp messaging */ + unsigned char slave_address; /**< I2C slave address (not shifted) */ + unsigned short rev; /**< full revid of this device */ + unsigned char tfa_family; /**< tfa1/tfa2 */ + enum featureSupport supportDrc; + enum featureSupport supportFramework; + enum featureSupport support_saam; + int sw_feature_bits[2]; /**< cached copy of sw feature bits */ + int hw_feature_bits; /**< cached copy of hw feature bits */ + int profile; /**< active profile */ + int vstep; /**< active vstep */ + unsigned char spkr_count; + unsigned char spkr_select; + unsigned char support_tcoef;/**< legacy tfa9887, will be removed */ + enum Tfa98xx_DAI daimap; /**< supported audio interface types */ + 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]; + int ext_dsp; /**< respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + int bus; /* TODO fix ext_dsp and bus handling */ + int tfadsp_event; /**< enum tfadsp_event_en is for external registry */ + int verbose; /**< verbosity level for debug print output */ + enum tfa_state state; /**< last known state or-ed with optional state_modifier */ + struct nxpTfaContainer *cnt;/**< the loaded container file */ + struct nxpTfaVolumeStepRegisterInfo *p_regInfo; /**< remember vstep for partial updates */ + int partial_enable; /**< enable partial updates */ + void *data; /**< typically pointing to Linux driver structure owning this device */ + int convert_dsp32; /**< convert 24 bit DSP messages to 32 bit */ + int sync_iv_delay; /**< synchronize I/V delay at cold start */ + int is_probus_device; /**< probus device: device without internal DSP */ + int needs_reset; /**< add the reset trigger for SetAlgoParams and SetMBDrc commands */ + struct kmem_cache *cachep; /**< Memory allocator handle */ +}; + +/** + * The tfa_dev_probe is called before accessing any device accessing functions. + * Access to the tfa device register 3 is attempted and will record the + * returned id for further use. If no device responds the function will abort. + * The recorded id will by used by the query functions to fill the remaining + * relevant data fields of the device structure. + * Data such as MTP features that requires device access will only be read when + * explicitly called and the result will be then cached in the struct. + * + * A structure pointer passed to this device needs to refer to existing memory + * space allocated by the caller. + * + * @param slave = I2C slave address of the target device (not shifted) + * @param tfa struct = points to memory that holds the context for this device + * instance + * + * @return + * - 0 if the I2C device responded to a read of register address 3\n + * when the device responds but with an unknown id a warning will be printed + * - -1 if no response from the I2C device + * + */ +int tfa_dev_probe(int slave, struct tfa_device *tfa); + +/** + * Start this instance at the profile and vstep as provided. + * The profile and vstep will be loaded first in case the current value differs + * from the requested values. + * Note that this call will not change the mute state of the tfa, which means + * that of this instance was called in muted state the caller will have to + * unmute in order to get audio. + * + * @param tfa struct = pointer to context of this device instance + * @param profile the selected profile to run + * @param vstep the selected vstep to use + * @return Tfa98xx_Error enum + */ +enum Tfa98xx_Error tfa_dev_start(struct tfa_device *tfa, int profile, int vstep); + + +/** + * Stop audio for this instance as gracefully as possible. + * Audio will be muted and the PLL will be shutdown together with any other + * device/type specific settings needed to prevent audio artifacts or + * workarounds. + * + * Note that this call will change state of the tfa to mute and powered down. + * + * @param tfa struct = pointer to context of this device instance + * @return Tfa98xx_Error enum + */ +enum Tfa98xx_Error tfa_dev_stop(struct tfa_device *tfa); + +/** + * This interface allows a device/type independent fine grained control of the + * internal state of the instance. + * Whenever a base state is requested an attempt is made to actively bring the device + * into this state. However this may depend on external conditions beyond control of + * this software layer. Therefore in case the state cannot be set an erro will + * be returned and the current state remains unchanged. + * The base states, lower values below 0x10, are all mutually exclusive, they higher ones + * can also function as a sticky modifier which means for example that operating + * state could be in either muted or unmuted state. Or in case of init_cf it can be + * internal clock (always) or external audio clock. + * This function is intended to be used for device mute/unmute synchronization + * when called from higher layers. Mostly internal calls will use this to control + * the startup and profile transitions in a device/type independent way. + * + * @param tfa struct = pointer to context of this device instance + * @param state struct = desired device state after function return + * @return Tfa98xx_Error enum + */ +enum Tfa98xx_Error tfa_dev_set_state(struct tfa_device *tfa, enum tfa_state state); + +/** + * Retrieve the current state of this instance in an active way. + * The state field in tfa structure will reflect the result unless an error is + * returned. + * Note that the hardware state may change on external events an as such this + * field should be treated as volatile. + * + * @param tfa struct = pointer to context of this device instance + * @return Tfa98xx_Error enum + * + */ +enum tfa_state tfa_dev_get_state(struct tfa_device *tfa); + + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * MTP support functions + */ +enum tfa_mtp { + TFA_MTP_OTC, /**< */ + TFA_MTP_EX, /**< */ + TFA_MTP_RE25, /**< */ + TFA_MTP_RE25_PRIM, /**< */ + TFA_MTP_RE25_SEC, /**< */ + TFA_MTP_LOCK, /**< */ +}; + +/** + * + */ +int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item); + +/** + * + */ +enum Tfa98xx_Error tfa_dev_mtp_set(struct tfa_device *tfa, enum tfa_mtp item, int value); + + +//irq +/* tfa2 interrupt support + * !!! enum tfa9912_irq !!!*/ +/* + * interrupt bit function to clear + */ +int tfa_irq_clear(struct tfa_device *tfa, int bit); +/* + * return state of irq or -1 if illegal bit + */ +int tfa_irq_get(struct tfa_device *tfa, int bit); +/* + * interrupt bit function that operates on the shadow regs in the handle + */ +int tfa_irq_ena(struct tfa_device *tfa, int bit, int state); +/* + * interrupt bit function that sets the polarity + */ +int tfa_irq_set_pol(struct tfa_device *tfa, int bit, int state); + +/* + * mask interrupts by disabling them + */ +int tfa_irq_mask(struct tfa_device *tfa); +/* + * unmask interrupts by enabling them again + */ +int tfa_irq_unmask(struct tfa_device *tfa); +//cnt read +//debug? + +#endif /* __TFA_DEVICE_H__ */ + diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_dsp.c b/techpack/audio/asoc/codecs/tfa9874/tfa_dsp.c new file mode 100644 index 000000000000..4406d3fdcc62 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_dsp.c @@ -0,0 +1,3910 @@ +/* + * Copyright 2014-2017 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 "dbgprint.h" +#include "tfa_container.h" +#include "tfa.h" +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.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(tfa,RST) >> 8) +#define FAM_TFA98XX_CF_MEM (TFA_FAM(tfa,MEMA)>> 8) +#define FAM_TFA98XX_MTP0 (TFA_FAM(tfa,MTPOTC) >> 8) +#define FAM_TFA98xx_INT_EN (TFA_FAM(tfa,INTENVDDS) >> 8) + +#define CF_STATUS_I2C_CMD_ACK 0x01 + +/* Defines below are used for irq function (this removed the genregs include) */ +#define TFA98XX_INTERRUPT_ENABLE_REG1 0x48 +#define TFA98XX_INTERRUPT_IN_REG1 0x44 +#define TFA98XX_INTERRUPT_OUT_REG1 0x40 +#define TFA98XX_STATUS_POLARITY_REG1 0x4c +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK 0x2 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK 0x1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS 0 + +void tfanone_ops(struct tfa_device_ops *ops); +void tfa9872_ops(struct tfa_device_ops *ops); +void tfa9874_ops(struct tfa_device_ops *ops); +void tfa9912_ops(struct tfa_device_ops *ops); +void tfa9888_ops(struct tfa_device_ops *ops); +void tfa9891_ops(struct tfa_device_ops *ops); +void tfa9897_ops(struct tfa_device_ops *ops); +void tfa9896_ops(struct tfa_device_ops *ops); +void tfa9890_ops(struct tfa_device_ops *ops); +void tfa9895_ops(struct tfa_device_ops *ops); +void tfa9894_ops(struct tfa_device_ops *ops); + +#ifndef MIN +#define MIN(A,B) (Amohm[channel]; +} + +/* return sign extended tap pattern */ +int tfa_get_tap_pattern(struct tfa_device *tfa) +{ + int value = tfa_get_bf(tfa, TFA9912_BF_CFTAPPAT ); + int bitshift; + uint8_t field_len = 1 + (TFA9912_BF_CFTAPPAT & 0x0f); /* length of bitfield */ + + bitshift = 8*sizeof(int)-field_len; + /* signextend */ + value = (value << bitshift) >> bitshift; + + return value; +} +/* + * interrupt bit function to clear + */ +int tfa_irq_clear(struct tfa_device *tfa, enum tfa9912_irq bit) +{ + unsigned char reg; + + /* make bitfield enum */ + if ( bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_INTERRUPT_IN_REG1; reg>4)); + reg_write(tfa, reg, 1<<(bit & 0x0f)); /* only this bit */ + } else + return -1; + + return 0; +} +/* + * return state of irq or -1 if illegal bit + */ +int tfa_irq_get(struct tfa_device *tfa, enum tfa9912_irq bit) +{ + uint16_t value; + int reg, mask; + + if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_INTERRUPT_OUT_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + } else + return -1; + + return (value & mask) !=0 ; +} +/* + * interrupt bit function that operates on the shadow regs in the handle + */ + +int tfa_irq_ena(struct tfa_device *tfa, enum tfa9912_irq bit, int state) +{ + uint16_t value, new_value; + int reg=0, mask; + /* */ + if ( bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) { + reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ + tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = state ? 0xffff : 0; /* all bits */ + } + } else if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_INTERRUPT_ENABLE_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + if (state) //set + new_value = (uint16_t)(value | mask); + else // clear + new_value = value & ~mask; + if ( new_value != value) { + reg_write(tfa, (unsigned char)reg, new_value); /* only this bit */ + tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = new_value; + } + } else + return -1; + + return 0; +} + +/* + * mask interrupts by disabling them + */ +int tfa_irq_mask(struct tfa_device *tfa) +{ + int reg; + + /* operate on all bits */ + for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) + reg_write(tfa, (unsigned char)reg, 0); + + return 0; +} + +/* + * unmask interrupts by enabling them again + */ +int tfa_irq_unmask(struct tfa_device *tfa) +{ + int reg; + + /* operate on all bits */ + for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) + reg_write(tfa, (unsigned char)reg, tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1]); + + return 0; +} + +/* + * interrupt bit function that sets the polarity + */ + +int tfa_irq_set_pol(struct tfa_device *tfa, enum tfa9912_irq bit, int state) +{ + uint16_t value, new_value; + int reg=0, mask; + + if (bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_STATUS_POLARITY_REG1; reg<=TFA98XX_STATUS_POLARITY_REG1+tfa9912_irq_max/16; reg++) { + reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ + } + } else if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_STATUS_POLARITY_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + if (state) /* Active High */ + new_value = (uint16_t)(value | mask); + else /* Active Low */ + new_value = value & ~mask; + if ( new_value != value) { + reg_write(tfa, (unsigned char)reg, new_value); /* only this bit */ + } + } else + return -1; + + return 0; +} + +/* + * set device info and register device ops + */ +void tfa_set_query_info(struct tfa_device *tfa) +{ + /* invalidate device struct cached values */ + tfa->hw_feature_bits = -1; + tfa->sw_feature_bits[0] = -1; + tfa->sw_feature_bits[1] = -1; + tfa->profile = -1; + tfa->vstep = -1; + /* defaults */ + tfa->is_probus_device = 0; + tfa->tfa_family = 1; + tfa->daimap = Tfa98xx_DAI_I2S; /* all others */ + tfa->spkr_count = 1; + tfa->spkr_select = 0; + tfa->support_tcoef = supportYes; + tfa->supportDrc = supportNotSet; + tfa->support_saam = supportNotSet; + tfa->ext_dsp = -1; /* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + tfa->bus=0; + tfa->partial_enable = 0; + tfa->convert_dsp32 = 0; + tfa->sync_iv_delay = 0; + + /* TODO use the getfeatures() for retrieving the features [artf103523] + tfa->supportDrc = supportNotSet;*/ + + switch (tfa->rev & 0xff) { + case 0: /* tfanone : non-i2c external DSP device */ + /* e.g. qc adsp */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 0; + tfa->spkr_count = 0; + tfa->daimap = 0; + tfanone_ops(&tfa->dev_ops); /* register device operations via tfa hal*/ + tfa->bus=1; + break; + case 0x72: + /* tfa9872 */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->is_probus_device = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9872_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x74: + /* tfa9874 */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->is_probus_device = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9874_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x88: + /* tfa9888 */ + tfa->tfa_family = 2; + tfa->spkr_count = 2; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9888_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x97: + /* tfa9897 */ + tfa->supportDrc = supportNo; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9897_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x96: + /* tfa9896 */ + tfa->supportDrc = supportNo; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9896_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x92: + /* tfa9891 */ + tfa->spkr_count = 1; + tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + tfa9891_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x91: + /* tfa9890B */ + tfa->spkr_count = 1; + tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + break; + case 0x80: + case 0x81: + /* tfa9890 */ + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_I2S; + tfa->supportDrc = supportNo; + tfa->supportFramework = supportNo; + tfa9890_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x12: + /* tfa9895 */ + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_I2S; + tfa9895_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x13: + /* tfa9912 */ + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9912_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x94: + /* tfa9894 */ + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9894_ops(&tfa->dev_ops); /* register device operations */ + break; + + default: + pr_err("unknown device type : 0x%02x\n", tfa->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: + case 0x96: + return 1; + case 0x88: + case 0x72: + case 0x13: + case 0x74: + case 0x94: + return 2; + case 0x50: + return 3; + default: + return 0; + } +} + +/* + * 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(struct tfa_device *tfa, 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 (tfa->rev & 0xff ) { // only compare lower byte + case 0x12: + *address = bq_table[2][idx]; + break; + case 0x97: + *address = bq_table[3][idx]; + break; + case 0x96: + *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; + case 0x72: + case 0x74: + case 0x13: + default: + /* unsupported case, possibly intermediate version */ + return -1; + _ASSERT(0); + } + } + return dmem; +} + +/************************ query functions ********************************************************/ +/** +* return revision +* Used by the LTT +*/ +void tfa98xx_rev(int *major, int *minor, int *revision) +{ + char version_str[] = TFA98XX_API_REV_STR; + sscanf(version_str, "v%d.%d.%d", major, minor, revision); +} + +/** + * tfa_supported_speakers + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa_supported_speakers(struct tfa_device *tfa, int* spkr_count) +{ + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + else + *spkr_count = tfa->spkr_count; + + return Tfa98xx_Error_Ok; +} + +/* + * tfa98xx_supported_saam + * returns the supportedspeaker as microphone feature + */ +enum Tfa98xx_Error tfa98xx_supported_saam(struct tfa_device *tfa, enum Tfa98xx_saam *saam) +{ + int features; + enum Tfa98xx_Error error; + + if (tfa->support_saam == supportNotSet) { + error = tfa98xx_dsp_get_hw_feature_bits(tfa, &features); + if (error!=Tfa98xx_Error_Ok) + return error; + tfa->support_saam = + (features & 0x8000)? supportYes : supportNo; /* SAAM is bit15 */ + } + *saam = tfa->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(struct tfa_device *tfa, 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 status; + + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) + return Tfa98xx_Error_NoClock; // Only test when we have a clock. + + /* Set proper MTP location per device: */ + if (tfa->tfa_family == 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(tfa, mtpbf) & 0xffff; + features_from_MTP[0] = tfa->hw_feature_bits = value; + + /* Read SW features: */ + error = tfa_dsp_cmd_id_write_read(tfa, 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(tfa, &features_from_cnt[0]); + get_sw_features_from_cnt(tfa, &features_from_cnt[1]); + + return error; +} + +/********************************* device specific ops ************************************************/ +/* the wrapper for DspReset, in case of full */ +enum Tfa98xx_Error tfa98xx_dsp_reset(struct tfa_device *tfa, int state) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = (tfa->dev_ops.dsp_reset)(tfa, state); + + return error; +} + +/* the ops wrapper for tfa98xx_dsp_SystemStable */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + return (tfa->dev_ops.dsp_system_stable)(tfa, ready); +} + +/* the ops wrapper for tfa98xx_dsp_system_stable */ +enum Tfa98xx_Error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + return (tfa->dev_ops.auto_copy_mtp_to_iic)(tfa); +} + +/* the ops wrapper for tfa98xx_faim_protect */ +enum Tfa98xx_Error tfa98xx_faim_protect(struct tfa_device *tfa, int state) +{ + return (tfa->dev_ops.faim_protect)(tfa, state); +} + +/* + * bring the device into a state similar to reset + */ +enum Tfa98xx_Error tfa98xx_init(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t value=0; + + /* 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(tfa, I2CR, 1, &value ); + TFA_WRITE_REG(tfa, I2CR, value); + + /* Put DSP in reset */ + tfa98xx_dsp_reset(tfa, 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 (tfa->dev_ops.tfa_init) + error = (tfa->dev_ops.tfa_init)(tfa); + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = (tfa->dev_ops.dsp_write_tables)(tfa, sample_rate); + + return error; +} + +/** Set internal oscillator into power down mode. +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +enum Tfa98xx_Error tfa98xx_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (tfa->dev_ops.set_osc_powerdown) { + return tfa->dev_ops.set_osc_powerdown(tfa, state); + } + + return Tfa98xx_Error_Not_Implemented; +} + +/** Check presence of powerswitch=1 in configuration and optimal setting. +* +* @param[in] tfa device description structure +* +* @return -1 when error, 0 or 1 depends on switch settings. +*/ +int tfa98xx_powerswitch_is_enabled(struct tfa_device *tfa) +{ + uint16_t value; + enum Tfa98xx_Error ret; + + if (((tfa->rev & 0xff) == 0x13) || ((tfa->rev & 0xff) == 0x88)) { + ret = reg_read(tfa, 0xc6, &value); + if (ret != Tfa98xx_Error_Ok) { + return -1; + } + /* PLMA5539: Check actual value of powerswitch. TODO: regmap v1.40 should make this bit public. */ + + return (int)(value & (1u << 6)); + } + + return 1; +} + +/********************* new tfa2 *********************************************************************/ +/* newly added messaging for tfa2 tfa1? */ +enum Tfa98xx_Error tfa98xx_dsp_get_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, unsigned char bytes[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + char msg[4*3]; + int nr = 0; + + msg[nr++] = 8; + msg[nr++] = MODULE_FRAMEWORK + 128; + msg[nr++] = FW_PAR_ID_GET_MEMORY; + + msg[nr++] = 0; + msg[nr++] = 0; + msg[nr++] = (char)memoryType; + + msg[nr++] = 0; + msg[nr++] = (offset>>8) & 0xff; + msg[nr++] = offset & 0xff; + + msg[nr++] = 0; + msg[nr++] = (length>>8) & 0xff; + msg[nr++] = length & 0xff; + + /* send msg */ + error = dsp_msg(tfa, nr, (char *)msg); + + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the device (length * 3) */ + error = dsp_msg_read(tfa, length * 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_set_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, int value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int nr = 0; + char msg[5*3]; + + msg[nr++] = 8; + msg[nr++] = MODULE_FRAMEWORK + 128; + msg[nr++] = FW_PAR_ID_SET_MEMORY; + + msg[nr++] = 0; + msg[nr++] = 0; + msg[nr++] = (char)memoryType; + + msg[nr++] = 0; + msg[nr++] = (offset>>8) & 0xff; + msg[nr++] = offset & 0xff; + + msg[nr++] = 0; + msg[nr++] = (length>>8) & 0xff; + msg[nr++] = length & 0xff; + + msg[nr++] = (value>>16) & 0xff; + msg[nr++] = (value>>8) & 0xff; + msg[nr++] = value & 0xff; + + /* send msg */ + error = dsp_msg(tfa, nr, (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(struct tfa_device *tfa, uint16_t *value) +{ + int status; + int result; + + /* not possible if PLL in powerdown */ + if ( TFA_GET_BF(tfa, PWDN) ) { + pr_debug("PLL in powerdown\n"); + return Tfa98xx_Error_NoClock; + } + + tfa98xx_dsp_system_stable(tfa, &status); + if (status==0) { + pr_debug("PLL not running\n"); + return Tfa98xx_Error_NoClock; + } + + result = TFA_READ_REG(tfa, 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(struct tfa_device *tfa, int lock) +{ + /* unhide lock registers */ + reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 :0x0F, 0x5A6B); + /* lock/unlock key2 MTPK */ + TFA_WRITE_REG(tfa, MTPKEY2, lock? 0 :0x5A ); + /* unhide lock registers */ + reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 :0x0F, 0); +} +void tfa2_manual_mtp_cpy(struct tfa_device *tfa, uint16_t reg_row_to_keep,uint16_t reg_row_to_set ,uint8_t row)///MCH_TO_TEST +{ + uint16_t value; + enum Tfa98xx_Error error; + /* Assure FAIM is enabled (enable it when neccesery) */ + error = tfa98xx_faim_protect(tfa, 1); + if (tfa->verbose) { + pr_debug("FAIM enabled (err:%d).\n", error); + } + reg_read(tfa, (unsigned char)reg_row_to_keep, &value); + if (!row) { + reg_write(tfa, 0xA7, value); + reg_write(tfa, 0xA8, reg_row_to_set); + } else { + reg_write(tfa, 0xA7, reg_row_to_set); + reg_write(tfa, 0xA8, value); + } + reg_write(tfa, 0xA3, 0x10|row); + + error = tfa98xx_faim_protect(tfa, 0); + if (tfa->verbose) { + pr_debug("FAIM disabled (err:%d).\n", error); + } +} + +enum Tfa98xx_Error tfa98xx_set_mtp(struct tfa_device *tfa, uint16_t value, uint16_t mask) +{ + unsigned short mtp_old, mtp_new; + int loop, status; + enum Tfa98xx_Error error; + + error = tfa98xx_get_mtp(tfa, &mtp_old); + + if (error != Tfa98xx_Error_Ok) + return error; + + mtp_new = (value & mask) | (mtp_old & ~mask); + + if ( mtp_old == mtp_new) /* no change */ { + if (tfa->verbose) + pr_info("No change in MTP. Value not written! \n"); + return Tfa98xx_Error_Ok; + } + + /* Assure FAIM is enabled (enable it when neccesery) */ + error = tfa98xx_faim_protect(tfa, 1); + if (error) { + return error; + } + if (tfa->verbose) { + pr_debug("MTP clock enabled.\n"); + } + + /* assure that the clock is up, else we can't write MTP */ + error = tfa98xx_dsp_system_stable(tfa, &status); + if (error){ + return error; + } + if (status==0){ + return Tfa98xx_Error_NoClock; + } + + tfa98xx_key2(tfa, 0); /* unlock */ + TFA_WRITE_REG(tfa, MTP0, mtp_new); /* write to i2c shadow reg */ + /* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/ + if (tfa->tfa_family == 2) + tfa2_manual_mtp_cpy(tfa, 0xF1, mtp_new, 0); + else + TFA_SET_BF(tfa, CIMTP, 1); + + /* no check for MTPBUSY here, i2c delay assumed to be enough */ + tfa98xx_key2(tfa, 1); /* lock */ + + /* wait until MTP write is done */ + error = Tfa98xx_Error_StateTimedOut; + for(loop=0; loop<100 /*x10ms*/ ;loop++) { + msleep_interruptible(10); /* wait 10ms to avoid busload */ + if (tfa_dev_get_mtpb(tfa) == 0) { + error = Tfa98xx_Error_Ok; + break; + } + } + /* MTP setting failed due to timeout ?*/ + if (error) { + return error; + } + + /* Disable the FAIM, if this is neccessary */ + error = tfa98xx_faim_protect(tfa, 0); + if (error) { + return error; + } + if (tfa->verbose) { + pr_debug("MTP clock disabled.\n"); + } + + return error; +} +/* + * clear mtpex + * set ACS + * start tfa + */ +int tfa_calibrate(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error; + + /* clear mtpex */ + error = tfa98xx_set_mtp(tfa, 0, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + if (error) + return error ; + + /* set RST=1 to put the DSP in Reset */ + TFA_SET_BF(tfa, RST, 1); + + /* set ACS/coldboot state */ + error = tfaRunColdboot(tfa, 1); + + /* start tfa by playing */ + return error; +} + +static short twos(short x) +{ + return (x<0)? x+512 : x; +} + +void tfa98xx_set_exttemp(struct tfa_device *tfa, 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(tfa, TROS, 1); + TFA_SET_BF(tfa, EXTTS, twos(ext_temp)); + } else { + pr_debug("Clearing ext temp settings\n"); + TFA_SET_BF(tfa, TROS, 0); + } +} +short tfa98xx_get_exttemp(struct tfa_device *tfa) +{ + short ext_temp = (short)TFA_GET_BF(tfa, EXTTS); + return (twos(ext_temp)); +} + +/************************** tfa simple bitfield interfacing ***************************************/ +/* convenience functions */ +enum Tfa98xx_Error tfa98xx_set_volume_level(struct tfa_device *tfa, unsigned short vol) +{ + if(tfa->in_use == 0) + 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(tfa, VOL, (uint16_t)vol); +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa2(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error; + + if (tfa->dev_ops.set_mute == NULL) + return Tfa98xx_Error_Not_Supported; + + switch (mute) { + case Tfa98xx_Mute_Off: + error = tfa->dev_ops.set_mute(tfa, 0); + TFA_SET_BF(tfa, AMPE, 1); + break; + case Tfa98xx_Mute_Amplifier: + case Tfa98xx_Mute_Digital: + error = tfa->dev_ops.set_mute(tfa, 1); + TFA_SET_BF(tfa, AMPE, 0); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + return error; +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa1(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error; + unsigned short audioctrl_value; + unsigned short sysctrl_value; + int value; + + value = TFA_READ_REG(tfa, CFSM); /* audio control register */ + if (value < 0) + return -value; + audioctrl_value = (unsigned short)value; + value = TFA_READ_REG(tfa, 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(tfa, CFSM, 0, &audioctrl_value); + TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value); + TFA_SET_BF_VALUE(tfa, DCA, 1, &sysctrl_value); + break; + case Tfa98xx_Mute_Digital: + /* expect the amplifier to run */ + /* set the cf_mute bit */ + TFA_SET_BF_VALUE(tfa, CFSM, 1, &audioctrl_value); + /* set the enbl_amplifier bit */ + TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value); + /* clear active mode */ + TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value); + break; + case Tfa98xx_Mute_Amplifier: + /* clear the cf_mute bit */ + TFA_SET_BF_VALUE(tfa, CFSM, 0, &audioctrl_value); + /* clear the enbl_amplifier bit and active mode */ + TFA_SET_BF_VALUE(tfa, AMPE, 0, &sysctrl_value); + TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + error = -TFA_WRITE_REG(tfa, CFSM, audioctrl_value); + if (error) + return error; + error = -TFA_WRITE_REG(tfa, AMPE, sysctrl_value); + return error; +} + +enum Tfa98xx_Error +tfa98xx_set_mute(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + if (tfa->in_use == 0) { + pr_err("device is not opened \n"); + return Tfa98xx_Error_NotOpen; + } + + if (tfa->tfa_family == 1) + return tfa98xx_set_mute_tfa1(tfa, mute); + else + return tfa98xx_set_mute_tfa2(tfa, mute); +} + +/****************** patching **********************************************************/ +static enum Tfa98xx_Error +tfa98xx_process_patch_file(struct tfa_device *tfa, 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 > tfa->buffer_size) { + /* too big, must fit buffer */ + return Tfa98xx_Error_Bad_Parameter; + } + + error = tfa98xx_write_raw(tfa, 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(struct tfa_device *tfa, 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 = tfa->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(tfa, &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 = mem_read(tfa, 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 != tfa->rev) { + pr_err("patch file device type check failed: expected 0x%02x, actual 0x%02x\n", + tfa->rev, revid); + return Tfa98xx_Error_Not_Supported; + } + } + } + + return error; +} + + +#define PATCH_HEADER_LENGTH 6 +enum Tfa98xx_Error +tfa_dsp_patch(struct tfa_device *tfa, int patchLength, + const unsigned char *patchBytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int status; + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (patchLength < PATCH_HEADER_LENGTH) + return Tfa98xx_Error_Bad_Parameter; + + error = tfa98xx_check_ic_rom_version(tfa, patchBytes); + if (Tfa98xx_Error_Ok != error) { + return error; + } + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) + return Tfa98xx_Error_NoClock; + if (error == Tfa98xx_Error_Ok) { + pr_info("tfa cold boot patch\n"); + error = tfaRunColdboot(tfa, 1); + if (error) { + pr_err("tfa_dsp_patch DSP_not_running\n"); + return Tfa98xx_Error_DSP_not_running; + } + } + + error = + tfa98xx_process_patch_file(tfa, patchLength - PATCH_HEADER_LENGTH, + patchBytes + PATCH_HEADER_LENGTH); + + return error; +} + +/****************** end patching **********************************************************/ + +TFA_INTERNAL enum Tfa98xx_Error +tfa98xx_wait_result(struct tfa_device *tfa, 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(tfa, 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 32 bit integers to 24 bit aligned bytes + input: data contains "num_data" int elements + output: bytes contains "3 * num_data" 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(struct tfa_device *tfa, int length, const char *buffer) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(tfa->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(tfa, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(tfa, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(tfa, 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(tfa, 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(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(tfa, CFINT, cfctl); + } + + return error; +} + +enum Tfa98xx_Error tfa_dsp_msg_write_id(struct tfa_device *tfa, int length, const char *buffer, uint8_t cmdid[3]) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(tfa->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(tfa, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(tfa, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(tfa, MADD, 1); + + /* write cmd-id */ + error = tfa98xx_write_data(tfa, 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(tfa, 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(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(tfa, 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(struct tfa_device *tfa, int *pRpcStatus) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = tfa98xx_wait_result(tfa, 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(tfa, 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, invalid parameter"; + 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; + case Tfa98xx_I2C_Req_Calib_Busy: + p_id_str = "Calibration not completed"; + break; + case Tfa98xx_I2C_Req_Calib_Failed: + p_id_str = "Calibration failed"; + break; + + default: + p_id_str = "Unspecified error"; + } + + return p_id_str; +} + +enum Tfa98xx_Error tfa_dsp_msg_read(struct tfa_device *tfa, 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(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM); + error = -TFA_WRITE_REG(tfa, MADD, start_offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes = length; /* input param */ + while (num_bytes > 0) { + burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + error = tfa98xx_read_data(tfa, FAM_TFA98XX_CF_MEM, burst_size, bytes + offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes -= burst_size; + offset += burst_size; + } + + return error; +} + +enum Tfa98xx_Error dsp_msg(struct tfa_device *tfa, int length24, const char *buf24) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int lastmessage=0; + uint8_t *blob; + int i; + int *intbuf = NULL; + char* buf = (char *)buf24; + int length = length24; + + if (tfa->convert_dsp32) { + int idx = 0; + + length = 4 * length24 / 3; + intbuf = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + buf = (char *)intbuf; + + /* convert 24 bit DSP messages to a 32 bit integer */ + for (i=0; i> 8; + } + } + + /* Only create multi-msg when the dsp is cold */ + if(tfa->ext_dsp == 1) { + /* Creating the multi-msg */ + error = tfa_tib_dsp_msgmulti(tfa, length, buf); + if(error == Tfa98xx_Error_Fail) + return Tfa98xx_Error_Fail; + + /* if the buffer is full we need to send the existing message and add the current message */ + if(error == Tfa98xx_Error_Buffer_too_small) { + int len; + + /* (a) send the existing (full) message */ + blob = kmalloc(64*1024, GFP_KERNEL); // max length is 64k + len = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob); + if (tfa->verbose) { + pr_debug("Multi-message buffer full. Sending multi-message, length=%d \n", len); + } + if (tfa->has_msg==0 ) /* via i2c */ { + /* Send tot the target selected */ + error = (tfa->dev_ops.dsp_msg)(tfa, len, (const char*)blob); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, len, (const char*)blob); + } + kfree(blob); + + /* (b) add the current DSP message to a new multi-message */ + error = tfa_tib_dsp_msgmulti(tfa, length, buf); + if(error == Tfa98xx_Error_Fail) { + return Tfa98xx_Error_Fail; + } + } + + lastmessage = error; + + /* When the lastmessage is done we can send the multi-msg to the target */ + if(lastmessage == 1) { + + /* Get the full multi-msg data */ + blob = kmalloc(64*1024, GFP_KERNEL); //max length is 64k + length = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob); + + if (tfa->verbose) + pr_debug("Last message for the multi-message received. Multi-message length=%d \n", length); + + if (tfa->has_msg==0 ) /* via i2c */ { + /* Send tot the target selected */ + error = (tfa->dev_ops.dsp_msg)(tfa, length, (const char*)blob); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, length, (const char*)blob); + } + + kfree(blob); /* Free the kmalloc blob */ + lastmessage = 0; /* reset to be able to re-start */ + } + } else { + if (tfa->has_msg==0 ) /* via i2c */ { + error = (tfa->dev_ops.dsp_msg)(tfa, length, buf); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, length, (const char*)buf); + } + } + + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + /* DSP verbose has argument 0x04 */ + if((tfa->verbose & 0x04)!=0) { + pr_debug("DSP w [%d]: ", length); + for(i=0; iconvert_dsp32) { + kmem_cache_free(tfa->cachep, intbuf); + } + + return error; +} + +enum Tfa98xx_Error dsp_msg_read(struct tfa_device *tfa, int length24, unsigned char *bytes24) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int i; + int length = length24; + unsigned char *bytes = bytes24; + + if (tfa->convert_dsp32) { + length = 4 * length24 / 3; + bytes = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + } + + if (tfa->has_msg==0) /* via i2c */ { + error = (tfa->dev_ops.dsp_msg_read)(tfa, length, bytes); + } else { /* via msg hal */ + error = tfa98xx_read_dsp(tfa, length, bytes); + } + + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + /* DSP verbose has argument 0x04 */ + if((tfa->verbose & 0x04)!=0) { + pr_debug("DSP R [%d]: ", length); + for(i=0; iconvert_dsp32) { + int idx = 0; + + /* convert 32 bit LE to 24 bit BE */ + for (i=0; icachep, bytes); + } + + return error; +} + +enum Tfa98xx_Error reg_read(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.reg_read)(tfa, subaddress, value); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.reg_write)(tfa, subaddress, value); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error mem_read(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.mem_read)(tfa, start_offset, num_words, pValues); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error mem_write(struct tfa_device *tfa, unsigned short address, int value, int memtype) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.mem_write)(tfa, address, value, memtype); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + 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. + */ +#define MAX_WORDS (300) +enum Tfa98xx_Error tfa_dsp_msg(struct tfa_device *tfa, 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(tfa, 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(tfa, &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 (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(struct tfa_device *tfa, 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(tfa, 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(tfa, &rpc_status); + if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) + break; + } + + 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(struct tfa_device *tfa, 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(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + if (pRpcStatus == NULL) + 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(tfa, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer); + } + if (error == Tfa98xx_Error_Ok) { + /* read 1 word (24 bit) from XMEM */ + error = tfa98xx_dsp_read_mem(tfa, 0, 1, pRpcStatus); + } + + return error; +} + +/***************************** xmem only **********************************/ +enum Tfa98xx_Error +tfa98xx_dsp_read_mem(struct tfa_device *tfa, + 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 *)kmem_cache_alloc(tfa->cachep, 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(tfa, DMEM, (uint16_t)dmem); + error = -TFA_WRITE_REG(tfa, MADD, (unsigned short)start_offset); + if (error != Tfa98xx_Error_Ok) + goto tfa98xx_dsp_read_mem_exit; + + for (; num_bytes > 0;) { + burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + + _ASSERT(burst_size <= sizeof(bytes)); + error = tfa98xx_read_data(tfa, 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: + kmem_cache_free(tfa->cachep, bytes); + + return error; +} + + +enum Tfa98xx_Error +tfa98xx_dsp_write_mem_word(struct tfa_device *tfa, unsigned short address, int value, int memtype) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3]; + + TFA_SET_BF(tfa, DMEM, (uint16_t)memtype); + + error = -TFA_WRITE_REG(tfa, MADD, address); + if (error != Tfa98xx_Error_Ok) + return error; + + tfa98xx_convert_data2bytes(1, &value, bytes); + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa_cont_write_filterbank(struct tfa_device *tfa, 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(tfa, MODULE_BIQUADFILTERBANK, + biquad_index+1, //start @1 + sizeof(filter[biquad_index].biquad.bytes), + filter[biquad_index].biquad.bytes); + } else { + error = Tfa98xx_DspBiquad_Disable(tfa, biquad_index+1); + } + if (error) return error; + + } + + return error; +} + +enum Tfa98xx_Error +Tfa98xx_DspBiquad_Disable(struct tfa_device *tfa, int biquad_index) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int coeff_buffer[BIQUAD_COEFF_SIZE]; + unsigned char bytes[3 + BIQUAD_COEFF_SIZE * 3]; + int nr = 0; + + if (biquad_index > TFA98XX_BIQUAD_NUM) + return Tfa98xx_Error_Bad_Parameter; + if (biquad_index < 1) + return Tfa98xx_Error_Bad_Parameter; + + /* make opcode */ + bytes[nr++] = 0; + bytes[nr++] = MODULE_BIQUADFILTERBANK+128; + bytes[nr++] = (unsigned char)biquad_index; + + + /* 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 */ + tfa98xx_convert_data2bytes(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[nr]); + nr += BIQUAD_COEFF_SIZE * 3; + + error = dsp_msg(tfa, nr, (char *)bytes); + + return error; +} + +/* wrapper for dsp_msg that adds opcode */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + const unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char *buffer; + int nr = 0; + + buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (buffer == NULL) + return Tfa98xx_Error_Fail; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + memcpy(&buffer[nr], data, num_bytes); + nr += num_bytes; + + error = dsp_msg(tfa, nr, (char *)buffer); + + kmem_cache_free(tfa->cachep, 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(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[3]; + int nr = 0; + + if (num_bytes <= 0) { + pr_debug("Error: The number of READ bytes is smaller or equal to 0! \n"); + return Tfa98xx_Error_Fail; + } + + if ((tfa->is_probus_device) && (tfa->cnt->ndev == 1) && + (param_id == SB_PARAM_GET_RE25C || + param_id == SB_PARAM_GET_LSMODEL || + param_id == SB_PARAM_GET_ALGO_PARAMS)) { + /* Modifying the ID for GetRe25C */ + buffer[nr++] = 4; + } else { + buffer[nr++] = tfa->spkr_select; + } + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + error = dsp_msg(tfa, nr, (char *)buffer); + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, 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(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[2*3]; + int nr = 0; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + buffer[nr++] = 0; + buffer[nr++] = 0; + buffer[nr++] = 0; + + error = dsp_msg(tfa, nr, (char *)buffer); + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, 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(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[2 * 3]; + int nr = 0; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + buffer[nr++] = 0; + buffer[nr++] = 0; + buffer[nr++] = (unsigned char)index_subband; + + error = dsp_msg(tfa, nr, (char *)buffer); + if(error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, num_bytes, data); + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_preset(struct tfa_device *tfa, int length, + const unsigned char *p_preset_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_preset_bytes != NULL) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + error = tfa_dsp_cmd_id_write(tfa, 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(struct tfa_device *tfa, int *features) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint32_t value; + uint16_t mtpbf; + + /* return the cache data if it's valid */ + if (tfa->hw_feature_bits != -1) { + *features = tfa->hw_feature_bits; + } else { + /* for tfa1 check if we have clock */ + if (tfa->tfa_family == 1) { + int status; + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) { + get_hw_features_from_cnt(tfa, 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(tfa, mtpbf) & 0xffff; + *features = tfa->hw_feature_bits = value; + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(struct tfa_device *tfa, int features[2]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + const int byte_size = 2 * 3; + unsigned char bytes[2 * 3]; + + /* return the cache data if it's valid */ + if (tfa->sw_feature_bits[0] != -1) { + features[0] = tfa->sw_feature_bits[0]; + features[1] = tfa->sw_feature_bits[1]; + } else { + /* for tfa1 check if we have clock */ + if (tfa->tfa_family == 1) { + int status; + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) { + get_sw_features_from_cnt(tfa, features); + /* skip reading MTP: */ + return (features[0] == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; + } + } + error = tfa_dsp_cmd_id_write_read(tfa, MODULE_FRAMEWORK, + FW_PAR_ID_GET_FEATURE_INFO, byte_size, bytes); + + if (error != Tfa98xx_Error_Ok) { + /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ + return error; + } + + tfa98xx_convert_bytes2data(byte_size, bytes, features); + } + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_get_state_info(struct tfa_device *tfa, 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(tfa, &bSupportFramework); + if (err == Tfa98xx_Error_Ok) { + if (bSupportFramework) { + err = tfa_dsp_cmd_id_write_read(tfa, 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(tfa, MODULE_SPEAKERBOOST, + SB_PARAM_GET_STATE, 3 * stateSize, bytes); + } + } + + *statesize = stateSize; + + return err; +} + +enum Tfa98xx_Error tfa98xx_dsp_support_drc(struct tfa_device *tfa, int *pbSupportDrc) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + *pbSupportDrc = 0; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + if (tfa->supportDrc != supportNotSet) { + *pbSupportDrc = (tfa->supportDrc == supportYes); + } else { + int featureBits[2]; + + error = tfa98xx_dsp_get_sw_feature_bits(tfa, 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) { + tfa->supportDrc = *pbSupportDrc ? supportYes : supportNo; + } + } + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(struct tfa_device *tfa, int *pbSupportFramework) +{ + int featureBits[2] = { 0, 0 }; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + _ASSERT(pbSupportFramework != 0); + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (tfa->supportFramework != supportNotSet) { + if(tfa->supportFramework == supportNo) + *pbSupportFramework = 0; + else + *pbSupportFramework = 1; + } else { + error = tfa98xx_dsp_get_sw_feature_bits(tfa, featureBits); + if (error == Tfa98xx_Error_Ok) { + *pbSupportFramework = 1; + tfa->supportFramework = supportYes; + } else { + *pbSupportFramework = 0; + tfa->supportFramework = supportNo; + error = Tfa98xx_Error_Ok; + } + } + + /* *pbSupportFramework only changed when error == Tfa98xx_Error_Ok */ + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_speaker_parameters(struct tfa_device *tfa, + int length, const unsigned char *p_speaker_bytes) +{ + enum Tfa98xx_Error error; + int bSupportDrc; + + if (p_speaker_bytes != NULL) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + /* Use long WaitResult retry count */ + error = tfa_dsp_cmd_id_write( + tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_LSMODEL, length, + p_speaker_bytes); + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(tfa, &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(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + 3, + bytes); + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_config(struct tfa_device *tfa, int length, + const unsigned char *p_config_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int bSupportDrc; + + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_CONFIG, length, + p_config_bytes); + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(tfa, &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(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + 3, + bytes); + } + + return error; +} + +/* load all the parameters for the DRC settings from a file */ +enum Tfa98xx_Error tfa98xx_dsp_write_drc(struct tfa_device *tfa, + int length, const unsigned char *p_drc_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_drc_bytes != NULL) { + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_DRC, length, + p_drc_bytes); + + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_powerdown(struct tfa_device *tfa, int powerdown) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + error = TFA_SET_BF(tfa, PWDN, (uint16_t)powerdown); + + if (powerdown) { + /* Workaround for ticket PLMA5337 */ + if (tfa->tfa_family == 2) { + TFA_SET_BF_VOLATILE(tfa, AMPE, 0); + } + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_select_mode(struct tfa_device *tfa, enum Tfa98xx_Mode mode) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (error == Tfa98xx_Error_Ok) { + switch (mode) { + + default: + error = Tfa98xx_Error_Bad_Parameter; + } + } + + return error; +} + +int tfa_set_bf(struct tfa_device *tfa, 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 = reg_read(tfa, 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 = reg_read(tfa, 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 = reg_read(tfa, 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(struct tfa_device *tfa, 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 = reg_write(tfa, address, reg_value); + if (err) + return -err; + + return 0; +} + +int tfa_read_reg(struct tfa_device *tfa, const uint16_t bf) +{ + enum Tfa98xx_Error err; + uint16_t regvalue; + + /* bitfield enum - 8..15 : address */ + uint8_t address = (bf >> 8) & 0xff; + + err = reg_read(tfa, address, ®value); + if (err) + return -err; + + return regvalue; +} + +/* + * powerup the coolflux subsystem and wait for it + */ +enum Tfa98xx_Error tfa_cf_powerup(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int tries, status; + + /* power on the sub system */ + TFA_SET_BF_VOLATILE(tfa, PWDN, 0); + + // wait until everything is stable, in case clock has been off + if (tfa->verbose) + pr_info("Waiting for DSP system stable...\n"); + for ( tries=CFSTABLE_TRIES; tries > 0; tries-- ) { + err = tfa98xx_dsp_system_stable(tfa, &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(struct tfa_device *tfa, int enable) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ((tfa->daimap & Tfa98xx_DAI_TDM) == Tfa98xx_DAI_TDM) + return err; + + if (tfa->tfa_family == 1) + err = -tfa_set_bf(tfa, 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(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int manstate = -1; + + if (tfa->tfa_family == 2 && tfa->verbose) { + manstate = TFA_GET_BF(tfa, 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(struct tfa_device *tfa, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int value; + + if (force) { + err= tfaRunColdStartup(tfa, profile); + if ( err ) return err; + } + + /* Returns 1 when device is "cold" and 0 when device is warm */ + value = tfa_is_cold(tfa); + + pr_debug("Startup of device [%s] is a %sstart\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), value ? "cold" : "warm"); + + /* cold start and not tap profile */ + if (value) { + /* Run startup and write all files */ + err = tfaRunSpeakerStartup(tfa, 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_dev_set_swprof(tfa, (unsigned short)profile); + tfa_dev_set_swvstep(tfa, 0); + + /* Synchonize I/V delay on 96/97 at cold start */ + if ((tfa->tfa_family == 1) && (tfa->daimap == Tfa98xx_DAI_TDM)) + tfa->sync_iv_delay = 1; + } + + return err; +} + +enum Tfa98xx_Error tfaRunSpeakerStartup(struct tfa_device *tfa, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !force ) { // in case of force CF already runnning + err = tfaRunStartup(tfa, profile); + PRINT_ASSERT(err); + if ( err ) + return err; + + /* Startup with CF in bypass then return here */ + if (tfa_cf_enabled(tfa) == 0) + return err; + + /* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + if(tfa->ext_dsp == -1) { + err = tfaRunStartDSP(tfa); + if ( err ) + return err; + } + } + + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1 */ + tfa98xx_auto_copy_mtp_to_iic(tfa); + + /* write all the files from the device list */ + err = tfaContWriteFiles(tfa); + if (err) { + pr_debug("[%s] tfaContWriteFiles error = %d \n", __FUNCTION__, err); + return err; + } + + /* write all the files from the profile list (use volumstep 0) */ + err = tfaContWriteFilesProf(tfa, profile, 0); + if (err) { + pr_debug("[%s] tfaContWriteFilesProf error = %d \n", __FUNCTION__, err); + return err; + } + + return err; +} + +/* + * Run calibration + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int calibrateDone; + + /* return if there is no audio running */ + if ((tfa->tfa_family == 2) && TFA_GET_BF(tfa, NOCLK)) + return Tfa98xx_Error_NoClock; + + /* When MTPOTC is set (cal=once) unlock key2 */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + tfa98xx_key2(tfa, 0); + } + + /* await calibration, this should return ok */ + err = tfaRunWaitCalibration(tfa, &calibrateDone); + if (err == Tfa98xx_Error_Ok) { + err = tfa_dsp_get_calibration_impedance(tfa); + PRINT_ASSERT(err); + } + + /* When MTPOTC is set (cal=once) re-lock key2 */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + tfa98xx_key2(tfa, 1); + } + + return err; +} + +enum Tfa98xx_Error tfaRunColdboot(struct tfa_device *tfa, 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(tfa, ACS)) { + /* set colstarted in CF_CONTROL to force ACS */ + err = mem_write(tfa, 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(struct tfa_device *tfa) +{ + return tfaContWritePatch(tfa); +} + +/* + * 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(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfa_run_load_patch(tfa); + 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 = mem_write(tfa, 512, 0, Tfa98xx_DMEM_XMEM); + PRINT_ASSERT(err); + + /* Reset DSP once for sure after initializing */ + if ( err == Tfa98xx_Error_Ok) { + err = tfa98xx_dsp_reset(tfa, 0); + PRINT_ASSERT(err); + } + + /* Sample rate is needed to set the correct tables */ + err = tfa98xx_dsp_write_tables(tfa, TFA_GET_BF(tfa, 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(struct tfa_device *tfa, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + int i, noinit=0; + + if(dev == NULL) + return Tfa98xx_Error_Fail; + + if(dev->bus) /* no i2c device, do nothing */ + return Tfa98xx_Error_Ok; + + /* 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(tfa); + 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(tfa); // 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(tfa, profile); + PRINT_ASSERT(err); + + /* Factory trimming for the Boost converter */ + tfa98xx_factory_trimmer(tfa); + + /* Go to the initCF state */ + tfa_dev_set_state(tfa, TFA_STATE_INIT_CF); + + err = show_current_state(tfa); + + return err; +} + +/* + * run the startup/init sequence and set ACS bit + */ +enum Tfa98xx_Error tfaRunColdStartup(struct tfa_device *tfa, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfaRunStartup(tfa, profile); + PRINT_ASSERT(err); + if (err) + return err; + + if(!tfa->is_probus_device) { + /* force cold boot */ + err = tfaRunColdboot(tfa, 1); // set ACS + PRINT_ASSERT(err); + if (err) + return err; + } + + /* start */ + err = tfaRunStartDSP(tfa); + PRINT_ASSERT(err); + + return err; +} + +/* + * + */ +enum Tfa98xx_Error tfaRunMute(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int status; + int tries = 0; + + /* signal the TFA98XX to mute */ + if (tfa->tfa_family == 1) { + err = tfa98xx_set_mute(tfa, Tfa98xx_Mute_Amplifier); + + if(err == Tfa98xx_Error_Ok) { + /* now wait for the amplifier to turn off */ + do { + status = TFA_GET_BF(tfa, SWS); + if (status != 0) + msleep_interruptible(10); /* wait 10ms to avoid busload */ + else + break; + tries++; + } while (tries < AMPOFFWAIT_TRIES); + + + if (tfa->verbose) + pr_debug("-------------------- muted --------------------\n"); + + /*The amplifier is always switching*/ + if (tries == AMPOFFWAIT_TRIES) + return Tfa98xx_Error_Other; + } + } + + return err; +} +/* + * + */ +enum Tfa98xx_Error tfaRunUnmute(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + /* signal the TFA98XX to mute */ + err = tfa98xx_set_mute(tfa, Tfa98xx_Mute_Off); + + if (tfa->verbose) + pr_debug("-------------------unmuted ------------------\n"); + + return err; +} + +static void individual_calibration_results(struct tfa_device *tfa) +{ + int value_P, value_S; + + /* Read the calibration result in xmem (529=primary channel) (530=secondary channel) */ + mem_read(tfa, 529, 1, &value_P); + mem_read(tfa, 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(tfa, 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(tfa, SSRIGHTE, 0); /* Disable the sound for the right speaker */ + } + + TFA_SET_BF_VOLATILE(tfa, AMPINSEL, 0); /* Set amplifier input to TDM */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 1); +} + +/* + * wait for calibrateDone + */ +enum Tfa98xx_Error tfaRunWaitCalibration(struct tfa_device *tfa, 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(tfa, MTPOTC)) { + // Check if MTP_busy is clear! + while (tries_mtp_busy < MTPBWAIT_TRIES) + { + mtp_busy = tfa_dev_get_mtpb(tfa); + 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(tfa, 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) && (triestfa_family == 2) && (TFA_GET_BF(tfa, REFCKSEL) == 1)) { + pr_err("Unable to calibrate the device with the internal clock! \n"); + } + } + + /* Check which speaker calibration failed. Only for 88C */ + if ((err != Tfa98xx_Error_Ok) && ((tfa->rev & 0x0FFF) == 0xc88)) { + individual_calibration_results(tfa); + } + + return err; +} + +/* + * tfa_dev_start will only do the basics: Going from powerdown to operating or a profile switch. + * for calibrating or akoustic shock handling use the tfa98xxCalibration function. + */ +enum Tfa98xx_Error tfa_dev_start(struct tfa_device *tfa, int next_profile, int vstep) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int active_profile = -1; + + pr_info("tfa_dev_start enter\n"); + + /* Get currentprofile */ + active_profile = tfa_dev_get_swprof(tfa); + if (active_profile == 0xff) + active_profile = -1; + + /* TfaRun_SpeakerBoost implies un-mute */ + pr_debug("Active_profile:%s, next_profile:%s\n", + tfaContProfileName(tfa->cnt, tfa->dev_idx, active_profile), + tfaContProfileName(tfa->cnt, tfa->dev_idx, next_profile)); + + err = show_current_state(tfa); + + if ( tfa->tfa_family == 1 ) { /* TODO move this to ini file */ + /* Enable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(tfa, 1); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + + if ( tfa->bus != 0 ) { /* non i2c */ +#ifndef __KERNEL__ + tfadsp_fw_start(tfa, next_profile, vstep); +#endif /* __KERNEL__ */ + } else { + /* Check if we need coldstart or ACS is set */ + err = tfaRunSpeakerBoost(tfa, 0, next_profile); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + + /* Make sure internal oscillator is running for DSP devices (non-dsp and max1 this is no-op) */ + tfa98xx_set_osc_powerdown(tfa, 0); + + /* Go to the Operating state */ + tfa_dev_set_state(tfa, TFA_STATE_OPERATING); + } + active_profile = tfa_dev_get_swprof(tfa); + + /* Profile switching */ + if ((next_profile != active_profile && active_profile >= 0)) { + err = tfaContWriteProfile(tfa, next_profile, vstep); + 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(tfa->cnt, tfa->dev_idx, next_profile), ".standby") != NULL) { + tfa_dev_set_swprof(tfa, (unsigned short)next_profile); + tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep); + goto error_exit; + } + + err = show_current_state(tfa); + + tfa->vstep = tfa_dev_get_swvstep(tfa); + if ((TFA_GET_BF(tfa, CFE) != 0) && (vstep != tfa->vstep) && (vstep != -1)) { + err = tfaContWriteFilesVstep(tfa, next_profile, vstep); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + + /* Always search and apply filters after a startup */ + err = tfa_set_filters(tfa, next_profile); + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + + tfa_dev_set_swprof(tfa, (unsigned short)next_profile); + tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep); + + /* PLMA5539: Gives information about current setting of powerswitch */ + if (tfa->verbose) { + if (!tfa98xx_powerswitch_is_enabled(tfa)) + pr_info("Device start without powerswitch enabled!\n"); + } + +error_exit: + show_current_state(tfa); + + return (enum Tfa98xx_Error)(err); +} + +enum Tfa98xx_Error tfa_dev_stop(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int times = 0, ready; + /* mute */ + tfaRunMute(tfa); + + /* Make sure internal oscillator is not running for DSP devices (non-dsp and max1 this is no-op) */ + tfa98xx_set_osc_powerdown(tfa, 1); + + /* powerdown CF */ + err = tfa98xx_powerdown(tfa, 1 ); + if ( err != Tfa98xx_Error_Ok) + return err; + + /* disable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(tfa, 0); + + while ((TFA_GET_BF(tfa, MANSTATE) != 0) && (times++ < 20)) { + pr_info("tfa stop wait state machine goto powerdown mode.\n"); + err = tfa98xx_dsp_system_stable(tfa, &ready); + if (err != Tfa98xx_Error_Ok || !ready) { + pr_err("tfa stop: No I2S CLK\n"); + break; + } + msleep_interruptible(5); + } + + if (times < 20) { + pr_debug("tfa stop: already in PowerDown\n"); + } else { + pr_debug("tfa stop: Not in PowerDown\n"); + } + + return (enum Tfa98xx_Error)err; //liuhaituo modify +} + +/* + * int registers and coldboot dsp + */ +int tfa_reset(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = -TFA_SET_BF_VOLATILE(tfa, I2CR, 1); + if(err) return err; + + if (tfa->tfa_family == 2) { + /* restore MANSCONF to POR state */ + TFA_SET_BF_VOLATILE(tfa, MANSCONF, 0); + } + + if( !tfa->is_probus_device ) { + if (tfa->tfa_family == 2) { + /* restore MANCOLD to POR state */ + TFA_SET_BF_VOLATILE(tfa, MANCOLD, 1); + } + + /* powerup CF to access CF io */ + tfa98xx_powerdown(tfa, 0 ); + /* for clock */ + err = tfa_cf_powerup(tfa); + PRINT_ASSERT(err); + + /* force cold boot */ + err = tfaRunColdboot(tfa, 1); // set ACS + PRINT_ASSERT(err); + + /* reset all i2C registers to default */ + err = -TFA_SET_BF(tfa, I2CR, 1); + PRINT_ASSERT(err); + } else { + if (tfa->ext_dsp > 0) + tfa98xx_init_dsp(tfa); + } + + return err; +} + +/* + * Write all the bytes specified by num_bytes and data + */ +enum Tfa98xx_Error +tfa98xx_write_data(struct tfa_device *tfa, + 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 *)kmem_cache_alloc(tfa->cachep, 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(tfa, bytes2write, write_data); + + kmem_cache_free(tfa->cachep, 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(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3*2] = {0}; + int nr_bytes, i, data[2], calibrateDone, spkr_count=0, cal_idx=0; + unsigned int scaled_data; + int tries=0; + + error = tfa_supported_speakers(tfa, &spkr_count); + + if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC)) { + pr_debug("Getting calibration values from MTP\n"); + + if((tfa->rev & 0xFF) == 0x88) { + for(i=0; imohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_PRIM); + else + tfa->mohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_SEC); + } + } else { + tfa->mohm[0] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25); + } + } else { + pr_debug("Getting calibration values from Speakerboost\n"); + + /* Make sure the calibrateDone bit is set before getting the values from speakerboost! + * This does not work for 72 (because the dsp cannot set this bit) + */ + if (!tfa->is_probus_device) { + /* poll xmem for calibrate always + * calibrateDone = 0 means "calibrating", + * calibrateDone = -1 (or 0xFFFFFF) means "fails" + * calibrateDone = 1 means calibration done + */ + calibrateDone = 0; + while ((calibrateDone != 1) && (tries < TFA98XX_API_WAITRESULT_NTRIES)) { + error = mem_read(tfa, TFA_FW_XMEM_CALIBRATION_DONE, 1, &calibrateDone); + if (calibrateDone == 1) + break; + tries++; + } + + if (calibrateDone != 1) { + pr_err("Calibration failed! \n"); + error = Tfa98xx_Error_Bad_Parameter; + } + else if (tries == TFA98XX_API_WAITRESULT_NTRIES) { + pr_debug("Calibration has timedout! \n"); + error = Tfa98xx_Error_StateTimedOut; + } + } + /* SoftDSP interface differs from hw-dsp interfaces */ + if(tfa->is_probus_device && tfa->cnt->ndev > 1) { + spkr_count = tfa->cnt->ndev; + } + + nr_bytes = spkr_count * 3; + error = tfa_dsp_cmd_id_write_read(tfa, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes); + if (error == Tfa98xx_Error_Ok) { + tfa98xx_convert_bytes2data(nr_bytes, bytes, data); + + for(i=0; iis_probus_device && tfa->dev_idx >= 1) ) { + cal_idx=0; + } + + /* signed data has a limit of 30 Ohm */ + scaled_data = data[i]; + + if(tfa->tfa_family == 2) + tfa->mohm[cal_idx] = (scaled_data*1000)/TFA2_FW_ReZ_SCALE; + else + tfa->mohm[cal_idx] = (scaled_data*1000)/TFA1_FW_ReZ_SCALE; + } + } + } + + return error; +} + +/* start count from 1, 0 is invalid */ +int tfa_dev_get_swprof(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_swprof)(tfa); +} + +int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value) +{ + return (tfa->dev_ops.set_swprof)(tfa, new_value+1); +} + +/* same value for all channels + * start count from 1, 0 is invalid */ +int tfa_dev_get_swvstep(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_swvstep)(tfa); +} + +int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + return (tfa->dev_ops.set_swvstep)(tfa, new_value + 1); +} + +/* + function overload for MTPB + */ +int tfa_dev_get_mtpb(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_mtpb)(tfa); +} + +int tfa_is_cold(struct tfa_device *tfa) +{ + int value; + + /* + * check for cold boot status + */ + if (tfa->is_probus_device) { + if (tfa->ext_dsp > 0) { + if (tfa->ext_dsp == 2) + value = 0; // warm + else /* no dsp or cold */ + value = 1; // cold + } else { + value = (TFA_GET_BF(tfa, MANSCONF) == 0); + } + } else { + value = TFA_GET_BF(tfa, ACS); + } + + return value; +} + +int tfa_needs_reset(struct tfa_device *tfa) +{ + int value; + + /* checks if the DSP commands SetAlgoParams and SetMBDrc + * need a DSP reset (now: at coldstart or during calibration) + */ + if (tfa_is_cold(tfa) == 1 || tfa->needs_reset == 1) + value = 1; + else + value = 0; + + return value; +} + +int tfa_cf_enabled(struct tfa_device *tfa) +{ + int value; + + /* For 72 there is no CF */ + if(tfa->is_probus_device) { + value = (tfa->ext_dsp != 0); + } else { + value = TFA_GET_BF(tfa, CFE); + } + + return value; +} + +#define NR_COEFFS 6 +#define NR_BIQUADS 28 +#define BQ_SIZE (3 * NR_COEFFS) +#define DSP_MSG_OVERHEAD 27 + +#pragma pack (push, 1) +struct dsp_msg_all_coeff { + uint8_t select_eq[3]; + uint8_t biquad[NR_BIQUADS][NR_COEFFS][3]; +}; +#pragma pack (pop) + +/* number of biquads for each equalizer */ +static const int eq_biquads[] = { + 10, 10, 2, 2, 2, 2 +}; + +#define NR_EQ (int)(sizeof(eq_biquads) / sizeof(int)) + +enum Tfa98xx_Error dsp_partial_coefficients(struct tfa_device *tfa, uint8_t *prev, uint8_t *next) +{ + uint8_t bq, eq; + int eq_offset; + int new_cost, old_cost; + uint32_t eq_biquad_mask[NR_EQ]; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + struct dsp_msg_all_coeff *data1 = (struct dsp_msg_all_coeff *)prev; + struct dsp_msg_all_coeff *data2 = (struct dsp_msg_all_coeff *)next; + + old_cost = DSP_MSG_OVERHEAD + 3 + sizeof(struct dsp_msg_all_coeff); + new_cost = 0; + + eq_offset = 0; + for (eq=0; eqbiquad[eq_offset][0][0]; + uint8_t *eq2 = &data2->biquad[eq_offset][0][0]; + + eq_biquad_mask[eq] = 0; + + if (memcmp(eq1, eq2, BQ_SIZE*eq_biquads[eq]) != 0) { + int nr_bq = 0; + int bq_sz, eq_sz; + + for (bq=0; bq < eq_biquads[eq]; bq++) { + uint8_t *bq1 = &eq1[bq*BQ_SIZE]; + uint8_t *bq2 = &eq2[bq*BQ_SIZE]; + + if (memcmp(bq1, bq2, BQ_SIZE) != 0) { + eq_biquad_mask[eq] |= (1<= eq_sz) { + eq_biquad_mask[eq] = 0xffffffff; + + new_cost += eq_sz; + + } else { + new_cost += bq_sz; + } + } + pr_debug("eq_biquad_mask[%d] = 0x%.8x\n", eq, eq_biquad_mask[eq]); + + eq_offset += eq_biquads[eq]; + } + + pr_debug("cost for writing all coefficients = %d\n", old_cost); + pr_debug("cost for writing changed coefficients = %d\n", new_cost); + + if (new_cost >= old_cost) { + const int buffer_sz = 3 + sizeof(struct dsp_msg_all_coeff); + uint8_t *buffer; + + buffer = kmalloc(buffer_sz, GFP_KERNEL); + if (buffer == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + buffer[0] = 0x00; + buffer[1] = 0x82; + buffer[2] = 0x00; + + /* parameters */ + memcpy(&buffer[3], data2, sizeof(struct dsp_msg_all_coeff)); + + err = dsp_msg(tfa, buffer_sz, (const char *)buffer); + + kfree(buffer); + if (err) + return err; + + } else { + eq_offset = 0; + for (eq=0; eqbiquad[eq_offset][0][0]; + + if (eq_biquad_mask[eq] == 0xffffffff) { + const int msg_sz = 6 + BQ_SIZE * eq_biquads[eq]; + uint8_t *msg; + + msg = kmalloc(msg_sz, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + msg[0] = 0x00; + msg[1] = 0x82; + msg[2] = 0x00; + + /* select eq and bq */ + msg[3] = 0x00; + msg[4] = eq+1; + msg[5] = 0x00; /* all biquads */ + + /* biquad parameters */ + memcpy(&msg[6], eq2, BQ_SIZE * eq_biquads[eq]); + + err = dsp_msg(tfa, msg_sz, (const char *)msg); + + kfree(msg); + if (err) + return err; + + } else if (eq_biquad_mask[eq] != 0) { + for(bq=0; bq < eq_biquads[eq]; bq++) { + + if (eq_biquad_mask[eq] & (1<cachep, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + msg[0] = 0x00; + msg[1] = 0x82; + msg[2] = 0x00; + + /* select eq and bq*/ + msg[3] = 0x00; + msg[4] = eq+1; + msg[5] = bq+1; + + /* biquad parameters */ + memcpy(&msg[6], bq2, BQ_SIZE); + + err = dsp_msg(tfa, msg_sz, (const char *)msg); + + kmem_cache_free(tfa->cachep, msg); + if (err) + return err; + } + } + } + eq_offset += eq_biquads[eq]; + } + } + + return err; +} + +/* fill context info */ +int tfa_dev_probe(int slave, struct tfa_device *tfa) +{ + uint16_t rev; + + tfa->slave_address = (unsigned char)slave; + + /* read revid via low level hal, register 3 */ + if (tfa98xx_read_register16(tfa, 3, &rev) != Tfa98xx_Error_Ok) { + PRINT("\nError: Unable to read revid from slave:0x%02x \n", slave); + return -1; + } + + tfa->rev = rev; + tfa->dev_idx = -1; + tfa->state = TFA_STATE_UNKNOWN; + tfa->p_regInfo = NULL; + + tfa_set_query_info(tfa); + + tfa->in_use = 1; + + return 0; +} + +enum Tfa98xx_Error tfa_dev_set_state(struct tfa_device *tfa, enum tfa_state state) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int loop = 50, ready = 0; + int count; + + /* Base states */ + /* Do not change the order of setting bits as this is important! */ + switch (state & 0x0f) { + case TFA_STATE_POWERDOWN: /* PLL in powerdown, Algo up */ + break; + case TFA_STATE_INIT_HW: /* load I2C/PLL hardware setting (~wait2srcsettings) */ + break; + case TFA_STATE_INIT_CF: /* coolflux HW access possible (~initcf) */ + /* Start with SBSL=0 to stay in initCF state */ + TFA_SET_BF(tfa, SBSL, 0); + + /* We want to leave Wait4SrcSettings state for max2 */ + if (tfa->tfa_family == 2) + TFA_SET_BF(tfa, MANSCONF, 1); + + /* And finally set PWDN to 0 to leave powerdown state */ + TFA_SET_BF(tfa, PWDN, 0); + + /* Make sure the DSP is running! */ + do { + err = tfa98xx_dsp_system_stable(tfa, &ready); + if (err != Tfa98xx_Error_Ok) + return err; + if (ready) + break; + } while (loop--); + /* Enable FAIM when clock is stable, to avoid MTP corruption */ + err = tfa98xx_faim_protect(tfa, 1); + if (tfa->verbose) { + pr_debug("FAIM enabled (err:%d).\n", err); + } + break; + case TFA_STATE_INIT_FW: /* DSP framework active (~patch loaded) */ + break; + case TFA_STATE_OPERATING: /* Amp and Algo running */ + /* Depending on our previous state we need to set 3 bits */ + TFA_SET_BF(tfa, PWDN, 0); /* Coming from state 0 */ + TFA_SET_BF(tfa, MANSCONF, 1); /* Coming from state 1 */ + TFA_SET_BF(tfa, SBSL, 1); /* Coming from state 6 */ + + /* + * Disable MTP clock to protect memory. + * However in case of calibration wait for DSP! (This should be case only during calibration). + */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + count = MTPEX_WAIT_NTRIES * 4; /* Calibration takes a lot of time */ + while ((TFA_GET_BF(tfa, MTPEX) != 1) && count) { + msleep_interruptible(10); + count--; + } + } + err = tfa98xx_faim_protect(tfa, 0); + if (tfa->verbose) { + pr_debug("FAIM disabled (err:%d).\n", err); + } + + /* Synchonize I/V delay on 96/97 at cold start */ + if (tfa->sync_iv_delay) { + if (tfa->verbose) + pr_debug("syncing I/V delay for %x\n", + (tfa->rev & 0xff)); + + /* wait for ACS to be cleared */ + count = 10; + while ((TFA_GET_BF(tfa, ACS) == 1) && + (count-- > 0)) { + msleep_interruptible(1); + } + + tfa98xx_dsp_reset(tfa, 1); + tfa98xx_dsp_reset(tfa, 0); + tfa->sync_iv_delay = 0; + } + break; + case TFA_STATE_FAULT: /* An alarm or error occurred */ + break; + case TFA_STATE_RESET: /* I2C reset and ACS set */ + tfa98xx_init(tfa); + break; + default: + if (state & 0x0f) + return Tfa98xx_Error_Bad_Parameter; + } + + /* state modifiers */ + + if (state & TFA_STATE_MUTE) + tfa98xx_set_mute(tfa, Tfa98xx_Mute_Amplifier); + + if (state & TFA_STATE_UNMUTE) + tfa98xx_set_mute(tfa, Tfa98xx_Mute_Off); + + tfa->state = state; + + return Tfa98xx_Error_Ok; +} + +enum tfa_state tfa_dev_get_state(struct tfa_device *tfa) +{ + int cold = TFA_GET_BF(tfa, ACS); + int manstate; + + /* different per family type */ + if ( tfa->tfa_family == 1 ) { + if ( cold && TFA_GET_BF(tfa, PWDN) ) + tfa->state = TFA_STATE_RESET; + else if ( !cold && TFA_GET_BF(tfa, SWS)) + tfa->state = TFA_STATE_OPERATING; + } else /* family 2 */ { + manstate = TFA_GET_BF(tfa, MANSTATE); + switch(manstate) { + case 0: + tfa->state = cold ? TFA_STATE_RESET : TFA_STATE_POWERDOWN; + break; + case 8: /* if dsp reset if off assume framework is running */ + tfa->state = TFA_GET_BF(tfa, RST) ? TFA_STATE_INIT_CF : TFA_STATE_INIT_FW; + break; + case 9: + tfa->state = TFA_STATE_OPERATING; + break; + default: + break; + } + } + + return tfa->state; +} + +int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item) +{ + int value = 0; + + switch (item) { + case TFA_MTP_OTC: + value = TFA_GET_BF(tfa, MTPOTC); + break; + case TFA_MTP_EX: + value = TFA_GET_BF(tfa, MTPEX); + break; + case TFA_MTP_RE25: + case TFA_MTP_RE25_PRIM: + if(tfa->tfa_family == 2) { + if((tfa->rev & 0xFF) == 0x88) + value = TFA_GET_BF(tfa, R25CL); + else if((tfa->rev & 0xFF) == 0x13) + value = tfa_get_bf(tfa, TFA9912_BF_R25C); + else + value = TFA_GET_BF(tfa, R25C); + } else { + reg_read(tfa, 0x83, (unsigned short*)&value); + } + break; + case TFA_MTP_RE25_SEC: + if((tfa->rev & 0xFF) == 0x88) { + value = TFA_GET_BF(tfa, R25CR); + } else { + pr_debug("Error: Current device has no secondary Re25 channel \n"); + } + break; + case TFA_MTP_LOCK: + break; + } + + return value; +} + +enum Tfa98xx_Error tfa_dev_mtp_set(struct tfa_device *tfa, enum tfa_mtp item, int value) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + switch (item) { + case TFA_MTP_OTC: + err = tfa98xx_set_mtp(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK); + break; + case TFA_MTP_EX: + err = tfa98xx_set_mtp(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + break; + case TFA_MTP_RE25: + case TFA_MTP_RE25_PRIM: + if(tfa->tfa_family == 2) { + if((tfa->rev & 0xFF) == 0x88) + TFA_SET_BF(tfa, R25CL, (uint16_t)value); + else + TFA_SET_BF(tfa, R25C, (uint16_t)value); + } + break; + case TFA_MTP_RE25_SEC: + if((tfa->rev & 0xFF) == 0x88) { + TFA_SET_BF(tfa, R25CR, (uint16_t)value); + } else { + pr_debug("Error: Current device has no secondary Re25 channel \n"); + err = Tfa98xx_Error_Bad_Parameter; + } + break; + case TFA_MTP_LOCK: + break; + } + + return err; +} + +int tfa_get_pga_gain(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, SAAMGAIN); +} + +int tfa_set_pga_gain(struct tfa_device *tfa, uint16_t value) +{ + + return TFA_SET_BF(tfa, SAAMGAIN, value); +} + +int tfa_get_noclk(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, NOCLK); +} + + +enum Tfa98xx_Error tfa_status(struct tfa_device *tfa) +{ + int value; + uint16_t val; + + /* + * check IC status bits: cold start + * and DSP watch dog bit to re init + */ + value = TFA_READ_REG(tfa, VDDS); /* STATUSREG */ + if (value < 0) + return -value; + val = (uint16_t)value; + + /* pr_debug("SYS_STATUS0: 0x%04x\n", val); */ + if (TFA_GET_BF_VALUE(tfa, ACS, val) || + TFA_GET_BF_VALUE(tfa, WDS, val)) { + + if (TFA_GET_BF_VALUE(tfa, ACS, val)) + pr_err("ERROR: ACS\n"); + if (TFA_GET_BF_VALUE(tfa, WDS, val)) + pr_err("ERROR: WDS\n"); + + return Tfa98xx_Error_DSP_not_running; + } + + if (TFA_GET_BF_VALUE(tfa, SPKS, val)) + pr_err("ERROR: SPKS\n"); + if (!TFA_GET_BF_VALUE(tfa, SWS, val)) + pr_err("ERROR: SWS\n"); + + /* Check secondary errors */ + if (!TFA_GET_BF_VALUE(tfa, CLKS, val) || + !TFA_GET_BF_VALUE(tfa, UVDS, val) || + !TFA_GET_BF_VALUE(tfa, OVDS, val) || + !TFA_GET_BF_VALUE(tfa, OTDS, val) || + !TFA_GET_BF_VALUE(tfa, PLLS, val) || + (!(tfa->daimap & Tfa98xx_DAI_TDM) && + !TFA_GET_BF_VALUE(tfa, VDDS, val)) ) + pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val); + + if ((tfa->daimap & Tfa98xx_DAI_TDM) && (tfa->tfa_family == 2)) { + value = TFA_READ_REG(tfa, TDMERR); /* STATUS_FLAGS1 */ + if (value < 0) + return -value; + val = (uint16_t)value; + if (TFA_GET_BF_VALUE(tfa, TDMERR, val) || + TFA_GET_BF_VALUE(tfa, TDMLUTER, val)) + pr_err("TDM related errors: STATUS_FLAG1 = 0x%x\n", val); + } + + return Tfa98xx_Error_Ok; +} + +int tfa_plop_noise_interrupt(struct tfa_device *tfa, int profile, int vstep) +{ + enum Tfa98xx_Error err; + int no_clk=0; + + /* Remove sticky bit by reading it once */ + TFA_GET_BF(tfa, NOCLK); + + /* No clock detected */ + if (tfa_irq_get(tfa, tfa9912_irq_stnoclk)) { + no_clk = TFA_GET_BF(tfa, NOCLK); + + /* Detect for clock is lost! (clock is not stable) */ + if (no_clk == 1) { + /* Clock is lost. Set I2CR to remove POP noise */ + pr_info("No clock detected. Resetting the I2CR to avoid pop on 72! \n"); + err = tfa_dev_start(tfa, profile, vstep); + if (err != Tfa98xx_Error_Ok) { + pr_err("Error loading i2c registers (tfa_dev_start), err=%d\n", err); + } else { + pr_info("Setting i2c registers after I2CR succesfull\n"); + tfa_dev_set_state(tfa, TFA_STATE_UNMUTE); + } + + /* Remove sticky bit by reading it once */ + tfa_get_noclk(tfa); + + /* This is only for SAAM on the 72. + Since the NOCLK interrupt is only enabled for 72 this is the place + However: Not tested yet! But also does not harm normal flow! + */ + if (strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, profile), ".saam")) { + pr_info("Powering down from a SAAM profile, workaround PLMA4766 used! \n"); + TFA_SET_BF(tfa, PWDN, 1); + TFA_SET_BF(tfa, AMPE, 0); + TFA_SET_BF(tfa, SAMMODE, 0); + } + } + + /* If clk is stable set polarity to check for LOW (no clock)*/ + tfa_irq_set_pol(tfa, tfa9912_irq_stnoclk, (no_clk == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, tfa9912_irq_stnoclk); + } + + /* return no_clk to know we called tfa_dev_start */ + return no_clk; +} + +void tfa_lp_mode_interrupt(struct tfa_device *tfa) +{ + const int irq_stclp0 = 36; /* FIXME: this 72 interrupt does not excist for 9912 */ + int lp0, lp1; + + if (tfa_irq_get(tfa, irq_stclp0)) { + lp0 = TFA_GET_BF(tfa, LP0); + if (lp0 > 0) { + pr_info("lowpower mode 0 detected\n"); + } else { + pr_info("lowpower mode 0 not detected\n"); + } + + tfa_irq_set_pol(tfa, irq_stclp0, (lp0 == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, irq_stclp0); + } + + if (tfa_irq_get(tfa, tfa9912_irq_stclpr)) { + lp1 = TFA_GET_BF(tfa, LP1); + if (lp1 > 0) { + pr_info("lowpower mode 1 detected\n"); + } else { + pr_info("lowpower mode 1 not detected\n"); + } + + tfa_irq_set_pol(tfa, tfa9912_irq_stclpr, (lp1 == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, tfa9912_irq_stclpr); + } +} diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_dsp_fw.h b/techpack/audio/asoc/codecs/tfa9874/tfa_dsp_fw.h new file mode 100644 index 000000000000..ce7340f70fee --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_dsp_fw.h @@ -0,0 +1,168 @@ +/* + * Copyright 2014-2017 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 TFA98XX_INTERNALS_H +#define TFA98XX_INTERNALS_H + +#include "config.h" + +#include "tfa_service.h" //TODO cleanup for enum Tfa98xx_Status_ID +/* + * tfadsp_fw_api.c + */ +/** + * Return a text version of the firmware status ID code + * @param status the given status ID code + * @return the firmware status ID string + */ +const char *tfadsp_fw_status_string(enum Tfa98xx_Status_ID status); +int tfadsp_fw_start(struct tfa_device *tfa, int prof_idx, int vstep_idx); +int tfadsp_fw_get_api_version(struct tfa_device *tfa, uint8_t *buffer); +#define FW_MAXTAG 150 +int tfadsp_fw_get_tag(struct tfa_device *tfa, uint8_t *buffer); +int tfadsp_fw_get_status_change(struct tfa_device *tfa, uint8_t *buffer); +int tfadsp_fw_set_re25(struct tfa_device *tfa, int prim, int sec ); +int tfadsp_fw_get_re25(struct tfa_device *tfa, uint8_t *buffer); + +/* + * 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_TAPTRIGGER 5 +#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 FW_PAR_ID_SET_FWKUSECASE 0x11 +#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 +#define FW_PAR_ID_GET_API_VERSION 0xFE +#define FW_PAR_ID_GET_STATUS_CHANGE 0x8D + +/* 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_RE25C 0x05 +#define SB_PARAM_SET_LSMODEL 0x06 +#define SB_PARAM_SET_MBDRC 0x07 +#define SB_PARAM_SET_MBDRC_WITHOUT_RESET 0x08 +#define SB_PARAM_SET_EXCURSION_FILTERS 0x0A +#define SB_PARAM_SET_DRC 0x0F +/* GET */ +#define SB_PARAM_GET_ALGO_PARAMS 0x80 +#define SB_PARAM_GET_LAGW 0x81 +#define SB_PARAM_GET_RE25C 0x85 +#define SB_PARAM_GET_LSMODEL 0x86 +#define SB_PARAM_GET_MBDRC 0x87 +#define SB_PARAM_GET_MBDRC_DYNAMICS 0x89 +#define SB_PARAM_GET_EXCURSION_FILTERS 0x8A +#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. */ +#define SB_PARAM_GET_XMODEL_COEFFS 0x8C /* Get coefficients for XModel */ +#define SB_PARAM_GET_EXCURSION_FILTERS 0x8A /* Get excursion filters */ +#define SB_PARAM_SET_EXCURSION_FILTERS 0x0A /* Set excursion filters */ + +/* SET: TAPTRIGGER */ +#define TAP_PARAM_SET_ALGO_PARAMS 0x01 +#define TAP_PARAM_SET_DECIMATION_PARAMS 0x02 + +/* GET: TAPTRIGGER*/ +#define TAP_PARAM_GET_ALGO_PARAMS 0x81 +#define TAP_PARAM_GET_TAP_RESULTS 0x84 + +/* 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(tfa,XMEM_CALIBRATION_DONE) +#define TFA_FW_XMEM_COUNT_BOOT TFA_FAM_FW(tfa,XMEM_COUNT_BOOT) +#define TFA_FW_XMEM_CMD_COUNT TFA_FAM_FW(tfa,XMEM_CMD_COUNT) + +#define TFA2_FW_ReZ_SCALE 65536 +#define TFA1_FW_ReZ_SCALE 16384 + +#endif /* TFA98XX_INTERNALS_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_ext.h b/techpack/audio/asoc/codecs/tfa9874/tfa_ext.h new file mode 100644 index 000000000000..da68f779bbe2 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_ext.h @@ -0,0 +1,57 @@ +/* + * Copyright 2016-2017 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. + */ +/* + * tfa_ext.h + * + * Created on: Jun 8, 2016 + * Author: wim + */ + +#ifndef TFA_SRC_TFA_EXT_H_ +#define TFA_SRC_TFA_EXT_H_ + +#include "tfa_device.h" + +/* + * events + */ +/** Maximum value for enumerator */ +#define LVM_MAXENUM (0xffff) +/** +This enum type specifies the different events that may trigger a callback. +*/ +enum tfadsp_event_en +{ + TFADSP_CMD_ACK = 1, /**< Command handling is completed */ + TFADSP_SOFT_MUTE_READY = 8, /**< Muting completed */ + TFADSP_VOLUME_READY = 16, /**< Volume change completed */ + TFADSP_DAMAGED_SPEAKER = 32, /**< Damaged speaker was detected */ + TFADSP_CALIBRATE_DONE = 64, /**< Calibration is completed */ + TFADSP_SPARSESIG_DETECTED = 128, /**< Sparse signal detected */ + TFADSP_CMD_READY = 256, /**< Ready to receive commands */ + TFADSP_EXT_PWRUP = 0x8000,/**< DSP API has started, powered up */ + TFADSP_EXT_PWRDOWN = 0x8001,/**< DSP API stopped, power down */ + TFADSP_EVENT_DUMMY = LVM_MAXENUM +} ; + +typedef int (*tfa_event_handler_t)(struct tfa_device *tfa, enum tfadsp_event_en tfadsp_event); +typedef int (*dsp_send_message_t)(struct tfa_device *tfa, int length, const char *buf); +typedef int (*dsp_read_message_t)(struct tfa_device *tfa, int length, char *buf); +typedef int (*dsp_write_reg_t)(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); + +int tfa_ext_register(dsp_write_reg_t tfa_write_reg, dsp_send_message_t tfa_send_message, dsp_read_message_t tfa_read_message, tfa_event_handler_t *tfa_event_handler); + +#endif /* TFA_SRC_TFA_EXT_H_ */ diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_init.c b/techpack/audio/asoc/codecs/tfa9874/tfa_init.c new file mode 100644 index 000000000000..54e462cac2f7 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_init.c @@ -0,0 +1,1674 @@ +/* + * Copyright 2014-2017 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 "dbgprint.h" +#include "tfa_service.h" +#include "tfa_internal.h" +#include "tfa_container.h" +#include "tfa98xx_tfafieldnames.h" + +/* The CurrentSense4 registers are not in the datasheet */ +#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1<<2) +#define TFA98XX_CURRENTSENSE4 0x49 + +/***********************************************************************************/ +/* GLOBAL (Defaults) */ +/***********************************************************************************/ +static enum Tfa98xx_Error no_overload_function_available(struct tfa_device *tfa, int not_used) +{ + (void)tfa; + (void)not_used; + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error no_overload_function_available2(struct tfa_device *tfa) +{ + (void)tfa; + + return Tfa98xx_Error_Ok; +} + +/* tfa98xx_dsp_system_stable +* return: *ready = 1 when clocks are stable to allow DSP subsystem access +*/ +static enum Tfa98xx_Error tfa_dsp_system_stable(struct tfa_device *tfa, 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(tfa, 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(tfa, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0)); + + return error; +} + +/* tfa98xx_toggle_mtp_clock + * Allows to stop clock for MTP/FAim needed for PLMA5505 */ +static enum Tfa98xx_Error tfa_faim_protect(struct tfa_device *tfa, int state) +{ + (void)tfa; + (void)state; + + return Tfa98xx_Error_Ok; +} + +/** Set internal oscillator into power down mode. + * + * This function is a worker for tfa98xx_set_osc_powerdown(). + * + * @param[in] tfa device description structure + * @param[in] state new state 0 - oscillator is on, 1 oscillator is off. + * + * @return Tfa98xx_Error_Ok when successfull, error otherwise. + */ +static enum Tfa98xx_Error tfa_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + /* This function has no effect in general case, only for tfa9912 */ + (void)tfa; + (void)state; + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa_dsp_reset(struct tfa_device *tfa, int state) +{ + /* generic function */ + TFA_SET_BF_VOLATILE(tfa, RST, (uint16_t)state); + + return Tfa98xx_Error_Ok; +} + +int tfa_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int mtpk, active_value = tfa->profile; + + /* Also set real profile index in the struct */ + tfa->profile = new_value - 1; + + /* for TFA1 devices */ + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(tfa, SWPROFIL, new_value); /* set current profile */ + TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */ + + return active_value; +} + +static int tfa_get_swprofile(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, SWPROFIL) - 1; +} + +static int tfa_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int mtpk, active_value = tfa->vstep; + + /* Also set the new value in the struct */ + tfa->vstep = new_value-1; + + /* for TFA1 devices */ + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(tfa, SWVSTEP, new_value); /* set current vstep */ + TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */ + + return active_value; +} + +static int tfa_get_swvstep(struct tfa_device *tfa) +{ + int value = 0; + /* Set the new value in the hw register */ + value = TFA_GET_BF(tfa, SWVSTEP); + + /* Also set the new value in the struct */ + tfa->vstep = value; + + return value - 1; /* invalid if 0 */ +} + +static int tfa_get_mtpb(struct tfa_device *tfa) { + + int value=0; + + /* Set the new value in the hw register */ + value = TFA_GET_BF(tfa, MTPB); + + return value; +} + +static enum Tfa98xx_Error +tfa_set_mute_nodsp(struct tfa_device *tfa, int mute) +{ + (void)tfa; + (void)mute; + + return Tfa98xx_Error_Ok; +} + +void set_ops_defaults(struct tfa_device_ops *ops) +{ + /* defaults */ + ops->reg_read = tfa98xx_read_register16; + ops->reg_write = tfa98xx_write_register16; + ops->mem_read = tfa98xx_dsp_read_mem; + ops->mem_write = tfa98xx_dsp_write_mem_word; + ops->dsp_msg = tfa_dsp_msg; + ops->dsp_msg_read = tfa_dsp_msg_read; + ops->dsp_write_tables = no_overload_function_available; + ops->dsp_reset = tfa_dsp_reset; + ops->dsp_system_stable = tfa_dsp_system_stable; + ops->auto_copy_mtp_to_iic = no_overload_function_available2; + ops->factory_trimmer = no_overload_function_available2; + ops->set_swprof = tfa_set_swprofile; + ops->get_swprof = tfa_get_swprofile; + ops->set_swvstep = tfa_set_swvstep; + ops->get_swvstep = tfa_get_swvstep; + ops->get_mtpb = tfa_get_mtpb; + ops->set_mute = tfa_set_mute_nodsp; + ops->faim_protect = tfa_faim_protect; + ops->set_osc_powerdown = tfa_set_osc_powerdown; +} + +/***********************************************************************************/ +/* no TFA + * external DSP SB instance */ +/***********************************************************************************/ +static short tfanone_swvstep, swprof; //TODO emulate in hal plugin +static enum Tfa98xx_Error tfanone_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + (void)tfa; /* suppress warning */ + *ready = 1; /* assume always ready */ + + return Tfa98xx_Error_Ok; +} + +static int tfanone_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value - 1; + + /* Set the new value in the hw register */ + swprof = new_value; + + return active_value; +} + +static int tfanone_get_swprofile(struct tfa_device *tfa) +{ + (void)tfa; /* suppress warning */ + return swprof; +} + +static int tfanone_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + /* Set the new value in the struct */ + tfa->vstep = new_value-1; + + /* Set the new value in the hw register */ + tfanone_swvstep = new_value; + + return new_value; +} + +static int tfanone_get_swvstep(struct tfa_device *tfa) +{ + (void)tfa; /* suppress warning */ + return tfanone_swvstep; +} + +void tfanone_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->dsp_system_stable = tfanone_dsp_system_stable; + ops->set_swprof = tfanone_set_swprofile; + ops->get_swprof = tfanone_get_swprofile; + ops->set_swvstep = tfanone_set_swvstep; + ops->get_swvstep = tfanone_get_swvstep; + +} + +/***********************************************************************************/ +/* TFA9912 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9912_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Fail; + + if (tfa) { + if (status == 0 || status == 1) { + ret = -(tfa_set_bf(tfa, TFA9912_BF_SSFAIME, (uint16_t)status)); + } + } + + return ret; +} + +static enum Tfa98xx_Error tfa9912_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* The optimal settings */ + if (tfa->rev == 0x1a13) { + + /* ----- generated code start ----- */ + /* ----- version 1.41 ----- */ + reg_write(tfa, 0x00, 0x0255); //POR=0x0245 + reg_write(tfa, 0x01, 0x838a); //POR=0x83ca + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2828 + reg_write(tfa, 0x05, 0x762a); //POR=0x766a + reg_write(tfa, 0x22, 0x543c); //POR=0x545c + reg_write(tfa, 0x26, 0x0100); //POR=0x0010 + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x551c); //POR=0x1afc + reg_write(tfa, 0x53, 0x003e); //POR=0x001e + reg_write(tfa, 0x61, 0x000c); //POR=0x0018 + reg_write(tfa, 0x63, 0x0a96); //POR=0x0a9a + reg_write(tfa, 0x65, 0x0a82); //POR=0x0a8b + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6c, 0x00d5); //POR=0x02d5 + reg_write(tfa, 0x70, 0x26f8); //POR=0x06e0 + reg_write(tfa, 0x71, 0x3074); //POR=0x2074 + reg_write(tfa, 0x75, 0x4484); //POR=0x4585 + reg_write(tfa, 0x76, 0x72ea); //POR=0x54a2 + reg_write(tfa, 0x83, 0x0716); //POR=0x0617 + reg_write(tfa, 0x89, 0x0013); //POR=0x0014 + reg_write(tfa, 0xb0, 0x4c08); //POR=0x4c00 + reg_write(tfa, 0xc6, 0x004e); //POR=0x000e /* PLMA5539: Please make sure bit 6 is always on! */ + /* ----- generated code end ----- */ + + /* PLMA5505: MTP key open makes vulanable for MTP corruption */ + tfa9912_faim_protect(tfa, 0); + } else { + pr_info("Warning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + } + + return error; +} + +static enum Tfa98xx_Error tfa9912_tfa_dsp_write_tables(struct tfa_device *tfa, 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(tfa, FRACTDEL, 40); + break; + case 1: /* 11.025KHz */ + TFA_SET_BF(tfa, FRACTDEL, 38); + break; + case 2: /* 12kHz */ + TFA_SET_BF(tfa, FRACTDEL, 37); + break; + case 3: /* 16kHz */ + TFA_SET_BF(tfa, FRACTDEL, 59); + break; + case 4: /* 22.05KHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 5: /* 24kHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 6: /* 32kHz */ + TFA_SET_BF(tfa, FRACTDEL, 52); + break; + case 7: /* 44.1kHz */ + TFA_SET_BF(tfa, FRACTDEL, 48); + break; + case 8: + default:/* 48kHz */ + TFA_SET_BF(tfa, 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 dsp_msg(tfa, size, (char *)buffer); +} + +static enum Tfa98xx_Error tfa9912_factory_trimmer(struct tfa_device *tfa) +{ + unsigned short currentValue, delta; + int result; + + /* Factory trimming for the Boost converter */ + /* check if there is a correction needed */ + result = TFA_GET_BF(tfa, DCMCCAPI); + if (result) { + /* Get currentvalue of DCMCC and the Delta value */ + currentValue = (unsigned short)TFA_GET_BF(tfa, DCMCC); + delta = (unsigned short)TFA_GET_BF(tfa, USERDEF); + + /* check the sign bit (+/-) */ + result = TFA_GET_BF(tfa, DCMCCSB); + if (result == 0) { + /* Do not exceed the maximum value of 15 */ + if (currentValue + delta < 15) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue + delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue + delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 15); + if (tfa->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(tfa, DCMCC, currentValue - delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue - delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 0); + if (tfa->verbose) + pr_debug("Max coil current is set to: 0 \n"); + } + } + } + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa9912_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72, 88 and 9912/9892(see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static int tfa9912_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value - 1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9912_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9912_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9912_BF_SWPROFIL) - 1; +} + +static int tfa9912_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + /* Set the new value in the struct */ + tfa->vstep = new_value-1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9912_BF_SWVSTEP, new_value); + + return new_value; +} + +static int tfa9912_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9912_BF_SWVSTEP) - 1; +} + +static enum Tfa98xx_Error +tfa9912_set_mute(struct tfa_device *tfa, int mute) +{ + tfa_set_bf(tfa, TFA9912_BF_CFSM, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +/* Maksimum value for combination of boost_voltage and vout calibration offset (see PLMA5322, PLMA5528). */ +#define TFA9912_VBOOST_MAX 57 +#define TFA9912_CALIBR_BOOST_MAX 63 +#define TFA9912_DCDCCNT6_REG (TFA9912_BF_DCVOF >> 8) +#define TFA9912_CALIBR_REG 0xf1 + +static uint16_t tfa9912_vboost_fixup(struct tfa_device *tfa, uint16_t dcdc_cnt6) +{ + unsigned short cal_offset; + unsigned short boost_v_1st, boost_v_2nd; + uint16_t new_dcdc_cnt6; + + /* Get current calibr_vout_offset, this register is not supported by bitfields */ + reg_read(tfa, TFA9912_CALIBR_REG, &cal_offset); + cal_offset = (cal_offset & 0x001f); + new_dcdc_cnt6 = dcdc_cnt6; + + /* Get current boost_volatage values */ + boost_v_1st = tfa_get_bf_value(TFA9912_BF_DCVOF, new_dcdc_cnt6); + boost_v_2nd = tfa_get_bf_value(TFA9912_BF_DCVOS, new_dcdc_cnt6); + + /* Check boost voltages */ + if (boost_v_1st > TFA9912_VBOOST_MAX) + boost_v_1st = TFA9912_VBOOST_MAX; + + if (boost_v_2nd > TFA9912_VBOOST_MAX) + boost_v_2nd = TFA9912_VBOOST_MAX; + + /* Recalculate values, max for the sum is TFA9912_CALIBR_BOOST_MAX */ + if (boost_v_1st + cal_offset > TFA9912_CALIBR_BOOST_MAX) + boost_v_1st = TFA9912_CALIBR_BOOST_MAX - cal_offset; + + if (boost_v_2nd + cal_offset > TFA9912_CALIBR_BOOST_MAX) + boost_v_2nd = TFA9912_CALIBR_BOOST_MAX - cal_offset; + + tfa_set_bf_value(TFA9912_BF_DCVOF, boost_v_1st, &new_dcdc_cnt6); + tfa_set_bf_value(TFA9912_BF_DCVOS, boost_v_2nd, &new_dcdc_cnt6); + + /* Change register value only when it's neccesary */ + if (new_dcdc_cnt6 != dcdc_cnt6) { + if (tfa->verbose) + pr_debug("tfa9912: V boost fixup applied. Old 0x%04x, new 0x%04x\n", + dcdc_cnt6, new_dcdc_cnt6); + dcdc_cnt6 = new_dcdc_cnt6; + } + + return dcdc_cnt6; +} + +/* PLMA5322, PLMA5528 - Limit values of DCVOS and DCVOF to range specified in datasheet. */ +enum Tfa98xx_Error tfa9912_reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value) +{ + if (subaddress == TFA9912_DCDCCNT6_REG) { + /* Correct V boost (first and secondary) to ensure 12V is not exceeded. */ + value = tfa9912_vboost_fixup(tfa, value); + } + + return tfa98xx_write_register16(tfa, subaddress, value); +} + +/** Set internal oscillator into power down mode for TFA9912. +* +* This function is a worker for tfa98xx_set_osc_powerdown(). +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +static enum Tfa98xx_Error tfa9912_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (state == 1 || state == 0) { + return -tfa_set_bf(tfa, TFA9912_BF_MANAOOSC, (uint16_t) state); + } + + return Tfa98xx_Error_Bad_Parameter; +} + +void tfa9912_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9912_specific; + /* PLMA5322, PLMA5528 - Limits values of DCVOS and DCVOF. */ + ops->reg_write = tfa9912_reg_write; + ops->dsp_write_tables = tfa9912_tfa_dsp_write_tables; + ops->factory_trimmer = tfa9912_factory_trimmer; + ops->auto_copy_mtp_to_iic = tfa9912_auto_copy_mtp_to_iic; + ops->set_swprof = tfa9912_set_swprofile; + ops->get_swprof = tfa9912_get_swprofile; + ops->set_swvstep = tfa9912_set_swvstep; + ops->get_swvstep = tfa9912_get_swvstep; + ops->set_mute = tfa9912_set_mute; + ops->faim_protect = tfa9912_faim_protect; + ops->set_osc_powerdown = tfa9912_set_osc_powerdown; +} + +/***********************************************************************************/ +/* TFA9872 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9872_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t MANAOOSC = 0x0140; /* version 17 */ + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock key 1 and 2 */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + tfa98xx_key2(tfa, 0); + + switch (tfa->rev) { + case 0x1a72: + case 0x2a72: + /* ----- generated code start ----- */ + /* ----- version 26 ----- */ + reg_write(tfa, 0x00, 0x1801); //POR=0x0001 + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2028 + reg_write(tfa, 0x20, 0x0890); //POR=0x2890 + reg_write(tfa, 0x22, 0x043c); //POR=0x045c + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x1a1c); //POR=0x7ae8 + reg_write(tfa, 0x58, 0x161c); //POR=0x101c + reg_write(tfa, 0x61, 0x0198); //POR=0x0000 + reg_write(tfa, 0x65, 0x0a8b); //POR=0x0a9a + reg_write(tfa, 0x70, 0x07f5); //POR=0x06e6 + reg_write(tfa, 0x74, 0xcc84); //POR=0xd823 + reg_write(tfa, 0x82, 0x01ed); //POR=0x000d + reg_write(tfa, 0x83, 0x0014); //POR=0x0013 + reg_write(tfa, 0x84, 0x0021); //POR=0x0020 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + /* ----- generated code end ----- */ + break; + case 0x1b72: + case 0x2b72: + case 0x3b72: + /* ----- generated code start ----- */ + /* ----- TFA9872 Probus Registers map N1B2 - Version 21 (10/19/2016) ----- */ + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2828 + reg_write(tfa, 0x20, 0x0890); //POR=0x2890 + reg_write(tfa, 0x22, 0x043c); //POR=0x045c + reg_write(tfa, 0x23, 0x0001); //POR=0x0003 + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x5a1c); //POR=0x7a08 + reg_write(tfa, 0x61, 0x0198); //POR=0x0000 + reg_write(tfa, 0x63, 0x0a9a); //POR=0x0a93 + reg_write(tfa, 0x65, 0x0a82); //POR=0x0a8d + reg_write(tfa, 0x6f, 0x01e3); //POR=0x02e4 + reg_write(tfa, 0x70, 0x06fd); //POR=0x06e6 + reg_write(tfa, 0x71, 0x307e); //POR=0x207e + reg_write(tfa, 0x74, 0xcc84); //POR=0xd913 + reg_write(tfa, 0x75, 0x1132); //POR=0x118a + reg_write(tfa, 0x82, 0x01ed); //POR=0x000d + reg_write(tfa, 0x83, 0x001a); //POR=0x0013 + /* ----- generated code end ----- */ + break; + default: + pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + break; + } + + /* Turn off the osc1m to save power: PLMA4928 */ + error = tfa_set_bf(tfa, MANAOOSC, 1); + + /* Bypass OVP by setting bit 3 from register 0xB0 (bypass_ovp=1): PLMA5258 */ + error = reg_read(tfa, 0xB0, &value); + value |= 1 << 3; + error = reg_write(tfa, 0xB0, value); + + return error; +} + +static enum Tfa98xx_Error tfa9872_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72 and 88 (see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static int tfa9872_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value - 1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9872_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9872_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9872_BF_SWPROFIL) - 1; +} + +static int tfa9872_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + + /* Set the new value in the struct */ + tfa->vstep = new_value-1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9872_BF_SWVSTEP, new_value); + + return new_value; +} + +static int tfa9872_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9872_BF_SWVSTEP) - 1; +} + +void tfa9872_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9872_specific; + ops->auto_copy_mtp_to_iic = tfa9872_auto_copy_mtp_to_iic; + ops->set_swprof = tfa9872_set_swprofile; + ops->get_swprof = tfa9872_get_swprofile; + ops->set_swvstep = tfa9872_set_swvstep; + ops->get_swvstep = tfa9872_get_swvstep; + ops->set_mute = tfa_set_mute_nodsp; +} + +/***********************************************************************************/ +/* TFA9874 */ +/***********************************************************************************/ + +static enum Tfa98xx_Error tfa9874_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + /* 0b = FAIM protection enabled 1b = FAIM protection disabled*/ + ret = tfa_set_bf_volatile(tfa, TFA9874_BF_OPENMTP, (uint16_t)(status)); + return ret; +} + + +static enum Tfa98xx_Error tfa9874_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock key 1 and 2 */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + tfa98xx_key2(tfa, 0); + + switch (tfa->rev) { + case 0x0a74: /* Initial revision ID */ + /* ----- generated code start ----- */ + /* V25 */ + reg_write(tfa, 0x02, 0x22a8); //POR=0x25c8 + reg_write(tfa, 0x51, 0x0020); //POR=0x0000 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x58, 0x16a4); //POR=0x1614 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6f, 0x00a3); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0007); //POR=0x0005 + reg_write(tfa, 0x74, 0x5068); //POR=0xcc80 + reg_write(tfa, 0x75, 0x0d28); //POR=0x1138 + reg_write(tfa, 0x83, 0x0594); //POR=0x061a + reg_write(tfa, 0x84, 0x0001); //POR=0x0021 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + reg_write(tfa, 0x88, 0x0000); //POR=0x0002 + reg_write(tfa, 0xc4, 0x2001); //POR=0x0001 + /* ----- generated code end ----- */ + break; + case 0x0b74: + /* ----- generated code start ----- */ + /* V1.6 */ + reg_write(tfa, 0x02, 0x22a8); //POR=0x25c8 + reg_write(tfa, 0x51, 0x0020); //POR=0x0000 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x58, 0x16a4); //POR=0x1614 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6f, 0x00a3); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0047); //POR=0x0045 + reg_write(tfa, 0x74, 0x5068); //POR=0xcc80 + reg_write(tfa, 0x75, 0x0d28); //POR=0x1138 + reg_write(tfa, 0x83, 0x0595); //POR=0x061a + reg_write(tfa, 0x84, 0x0001); //POR=0x0021 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + reg_write(tfa, 0x88, 0x0000); //POR=0x0002 + reg_write(tfa, 0xc4, 0x2001); //POR=0x0001 + /* ----- generated code end ----- */ + break; + case 0x0c74: + /* ----- generated code start ----- */ + /* V1.14 */ + reg_write(tfa, 0x02, 0x22c8); //POR=0x25c8 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x53, 0x003e); //POR=0x001e + reg_write(tfa, 0x56, 0x0400); //POR=0x0600 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x6f, 0x00a5); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0047); //POR=0x0045 + reg_write(tfa, 0x74, 0x5098); //POR=0xcc80 + reg_write(tfa, 0x75, 0x8d28); //POR=0x1138 + reg_write(tfa, 0x80, 0x0000); //POR=0x0003 + reg_write(tfa, 0x83, 0x0799); //POR=0x061a + reg_write(tfa, 0x84, 0x0081); //POR=0x0021 + /* ----- generated code end ----- */ + break; + default: + pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + break; + } + + return error; +} + +static int tfa9874_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value - 1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9874_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9874_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9874_BF_SWPROFIL) - 1; +} + +static int tfa9874_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + + /* Set the new value in the struct */ + tfa->vstep = new_value-1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9874_BF_SWVSTEP, new_value); + + return new_value; +} + +static int tfa9874_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9874_BF_SWVSTEP) - 1; +} + +/* tfa98xx_dsp_system_stable +* return: *ready = 1 when clocks are stable to allow DSP subsystem access +*/ +static enum Tfa98xx_Error tfa9874_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + /* check CLKS: ready if set */ + *ready = tfa_get_bf(tfa, TFA9874_BF_CLKS)==1; + + return error; +} + +static int tfa9874_get_mtpb(struct tfa_device *tfa) { + + int value; + value = tfa_get_bf(tfa, TFA9874_BF_MTPB); + return value; +} + +void tfa9874_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9874_specific; + ops->set_swprof = tfa9874_set_swprofile; + ops->get_swprof = tfa9874_get_swprofile; + ops->set_swvstep = tfa9874_set_swvstep; + ops->get_swvstep = tfa9874_get_swvstep; + ops->dsp_system_stable = tfa9874_dsp_system_stable; + ops->faim_protect = tfa9874_faim_protect; + ops->get_mtpb = tfa9874_get_mtpb; + ops->set_mute = tfa_set_mute_nodsp; +} + +/***********************************************************************************/ +/* TFA9888 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9888_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + int patch_version; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* Only N1C2 is supported */ + /* ----- generated code start ----- */ + /* --------- Version v1 ---------- */ + if (tfa->rev == 0x2c88) { + reg_write(tfa, 0x00, 0x164d); //POR=0x064d + reg_write(tfa, 0x01, 0x828b); //POR=0x92cb + reg_write(tfa, 0x02, 0x1dc8); //POR=0x1828 + reg_write(tfa, 0x0e, 0x0080); //POR=0x0000 + reg_write(tfa, 0x20, 0x089e); //POR=0x0890 + reg_write(tfa, 0x22, 0x543c); //POR=0x545c + reg_write(tfa, 0x23, 0x0006); //POR=0x0000 + reg_write(tfa, 0x24, 0x0014); //POR=0x0000 + reg_write(tfa, 0x25, 0x000a); //POR=0x0000 + reg_write(tfa, 0x26, 0x0100); //POR=0x0000 + reg_write(tfa, 0x28, 0x1000); //POR=0x0000 + reg_write(tfa, 0x51, 0x0000); //POR=0x00c0 + reg_write(tfa, 0x52, 0xfafe); //POR=0xbaf6 + reg_write(tfa, 0x70, 0x3ee4); //POR=0x3ee6 + reg_write(tfa, 0x71, 0x1074); //POR=0x3074 + reg_write(tfa, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } else { + pr_info("Warning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + } + + patch_version = tfa_cnt_get_patch_version(tfa); + if (patch_version >= 0x060401) + tfa->partial_enable = 1; + + return error; +} + +static enum Tfa98xx_Error tfa9888_tfa_dsp_write_tables(struct tfa_device *tfa, 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(tfa, FRACTDEL, 40); + break; + case 1: /* 11.025KHz */ + TFA_SET_BF(tfa, FRACTDEL, 38); + break; + case 2: /* 12kHz */ + TFA_SET_BF(tfa, FRACTDEL, 37); + break; + case 3: /* 16kHz */ + TFA_SET_BF(tfa, FRACTDEL, 59); + break; + case 4: /* 22.05KHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 5: /* 24kHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 6: /* 32kHz */ + TFA_SET_BF(tfa, FRACTDEL, 52); + break; + case 7: /* 44.1kHz */ + TFA_SET_BF(tfa, FRACTDEL, 48); + break; + case 8: + default:/* 48kHz */ + TFA_SET_BF(tfa, 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 dsp_msg(tfa, size, (char *)buffer); +} + +static enum Tfa98xx_Error tfa9888_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72 and 88 (see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static enum Tfa98xx_Error tfa9888_factory_trimmer(struct tfa_device *tfa) +{ + unsigned short currentValue, delta; + int result; + + /* Factory trimming for the Boost converter */ + /* check if there is a correction needed */ + result = TFA_GET_BF(tfa, DCMCCAPI); + if (result) { + /* Get currentvalue of DCMCC and the Delta value */ + currentValue = (unsigned short)TFA_GET_BF(tfa, DCMCC); + delta = (unsigned short)TFA_GET_BF(tfa, USERDEF); + + /* check the sign bit (+/-) */ + result = TFA_GET_BF(tfa, DCMCCSB); + if (result == 0) { + /* Do not exceed the maximum value of 15 */ + if (currentValue + delta < 15) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue + delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue + delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 15); + if (tfa->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(tfa, DCMCC, currentValue - delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue - delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 0); + if (tfa->verbose) + pr_debug("Max coil current is set to: 0 \n"); + } + } + } + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error +tfa9888_set_mute(struct tfa_device *tfa, int mute) +{ + TFA_SET_BF(tfa, CFSMR, (const uint16_t)mute); + TFA_SET_BF(tfa, CFSML, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +void tfa9888_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9888_specific; + ops->dsp_write_tables = tfa9888_tfa_dsp_write_tables; + ops->auto_copy_mtp_to_iic = tfa9888_auto_copy_mtp_to_iic; + ops->factory_trimmer = tfa9888_factory_trimmer; + ops->set_mute = tfa9888_set_mute; +} + +/***********************************************************************************/ +/* TFA9896 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9896_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + + if ( (tfa->rev == 0x2b96) || (tfa->rev == 0x3b96) ) { + ret = tfa_set_bf_volatile(tfa, TFA9896_BF_OPENMTP, (uint16_t)status); + } + + return ret; +} + +/***********************************************************************************/ +/* TFA9896 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9896_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short check_value; + + if (tfa->in_use == 0) + 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. + */ + if (tfa->rev == 0x1b96) { + /* ----- generated code start v17 ----- */ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + else if (tfa->rev == 0x2b96) { + /* ----- generated code start ----- v1*/ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + else if (tfa->rev == 0x3b96) { + /* ----- generated code start ----- v1*/ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + /* $49:[0] - 1 ==> 0; CLIP - default value changed. 0 means CLIPPER on */ + error = reg_read(tfa, 0x49, &check_value); + check_value &= ~0x1; + error = reg_write(tfa, 0x49, check_value); + return error; +} + +/* +* the int24 values for the vsfw delay table +*/ +static unsigned char tfa9896_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 tfa9896_dsp_write_vsfwdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURRENT_DELAY, sizeof(tfa9896_vsfwdelay_table), tfa9896_vsfwdelay_table); +} + +/* +* The int24 values for the fracdelay table +* For now applicable only for 8 and 48 kHz +*/ +static unsigned char tfa9896_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 */ +}; + +static enum Tfa98xx_Error tfa9896_dsp_write_cvfracdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURFRAC_DELAY, sizeof(tfa9896_cvfracdelay_table), tfa9896_cvfracdelay_table);; +} + +static enum Tfa98xx_Error tfa9896_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error; + + /* Not used for max1! */ + (void)sample_rate; + + error = tfa9896_dsp_write_vsfwdelay_table(tfa); + if (error == Tfa98xx_Error_Ok) { + error = tfa9896_dsp_write_cvfracdelay_table(tfa); + } + + return error; +} + +void tfa9896_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9896_specific; + ops->dsp_write_tables = tfa9896_tfa_dsp_write_tables; + ops->faim_protect = tfa9896_faim_protect; +} + +/***********************************************************************************/ +/* TFA9897 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9897_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short check_value; + + if (tfa->in_use == 0) + 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 = reg_write(tfa, 0x48, 0x0300); /* POR value = 0x308 */ + + /* $49:[0] - 1 ==> 0; CLIP - default value changed. 0 means CLIPPER on */ + error = reg_read(tfa, 0x49, &check_value); + check_value &= ~0x1; + error = reg_write(tfa, 0x49, check_value); + + return error; +} + +/* +* the int24 values for the vsfw delay table +*/ +static unsigned char tfa9897_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(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURRENT_DELAY, sizeof(tfa9897_vsfwdelay_table), tfa9897_vsfwdelay_table);; +} + +/* +* The int24 values for the fracdelay table +* For now applicable only for 8 and 48 kHz +*/ +static unsigned char tfa9897_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 */ +}; + +static enum Tfa98xx_Error tfa9897_dsp_write_cvfracdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURFRAC_DELAY, sizeof(tfa9897_cvfracdelay_table), tfa9897_cvfracdelay_table);; +} + +static enum Tfa98xx_Error tfa9897_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error; + + /* Not used for max1! */ + (void)sample_rate; + + error = tfa9897_dsp_write_vsfwdelay_table(tfa); + if (error == Tfa98xx_Error_Ok) { + error = tfa9897_dsp_write_cvfracdelay_table(tfa); + } + + return error; +} + +void tfa9897_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9897_specific; + ops->dsp_write_tables = tfa9897_tfa_dsp_write_tables; +} + +/***********************************************************************************/ +/* TFA9895 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9895_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int result; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default */ + + result = TFA_SET_BF(tfa, AMPE, 1); + if (result < 0) + return -result; + + /* some other registers must be set for optimal amplifier behaviour */ + reg_write(tfa, 0x05, 0x13AB); + reg_write(tfa, 0x06, 0x001F); + /* peak voltage protection is always on, but may be written */ + reg_write(tfa, 0x08, 0x3C4E); + /*TFA98XX_SYSCTRL_DCA=0*/ + reg_write(tfa, 0x09, 0x024D); + reg_write(tfa, 0x41, 0x0308); + error = reg_write(tfa, 0x49, 0x0E82); + + return error; +} + +void tfa9895_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9895_specific; +} + +/***********************************************************************************/ +/* TFA9891 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9891_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* ----- generated code start ----- */ + /* ----- version 18.0 ----- */ + reg_write(tfa, 0x09, 0x025d); //POR=0x024d + reg_write(tfa, 0x10, 0x0018); //POR=0x0024 + reg_write(tfa, 0x22, 0x0003); //POR=0x0023 + reg_write(tfa, 0x25, 0x0001); //POR=0x0000 + reg_write(tfa, 0x46, 0x0000); //POR=0x4000 + reg_write(tfa, 0x55, 0x3ffb); //POR=0x7fff + /* ----- generated code end ----- */ + + return error; +} + +void tfa9891_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9891_specific; +} + +/***********************************************************************************/ +/* TFA9890 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9890_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short regRead = 0; + + if(tfa->in_use == 0) + 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 = reg_write(tfa, 0x40, 0x5a6b); + if (error) + return error; + reg_read(tfa, 0x59, ®Read); + regRead |= 0x3; + reg_write(tfa, 0x59, regRead); + error = reg_write(tfa, 0x40, 0x0000); + error = reg_write(tfa, 0x47, 0x7BE1); + + return error; +} + +/* +* Disable clock gating +*/ +static enum Tfa98xx_Error tfa9890_clockgating(struct tfa_device *tfa, int on) +{ + enum Tfa98xx_Error error; + unsigned short value; + + /* for TFA9890 temporarily disable clock gating when dsp reset is used */ + error = reg_read(tfa, 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 = reg_write(tfa, 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(struct tfa_device *tfa, int state) +{ + enum Tfa98xx_Error error; + + /* for TFA9890 temporarily disable clock gating + when dsp reset is used */ + tfa9890_clockgating(tfa, 0); + + TFA_SET_BF(tfa, RST, (uint16_t)state); + + /* clock gating restore */ + error = tfa9890_clockgating(tfa, 1); + + 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(struct tfa_device *tfa, 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(tfa, 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(tfa, 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(tfa, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(tfa, 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(tfa, MTPB);/*TODO_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 = reg_read(tfa, 0x84, &mtp0); + if (error) + goto errorExit; + + *ready = (mtp0 != 0); /* The MTP register written? */ + + return error; + +errorExit: + *ready = 0; + return error; +} + +void tfa9890_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9890_specific; + ops->dsp_reset = tfa9890_dsp_reset; + ops->dsp_system_stable = tfa9890_dsp_system_stable; +} + +/***********************************************************************************/ +/* TFA9894 */ +/***********************************************************************************/ +static int tfa9894_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value - 1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9894_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9894_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9894_BF_SWPROFIL) - 1; +} + +static int tfa9894_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + /* Set the new value in the struct */ + tfa->vstep = new_value-1; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9894_BF_SWVSTEP, new_value); + + return new_value; +} + +static int tfa9894_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9894_BF_SWVSTEP) - 1; +} + +static int tfa9894_get_mtpb(struct tfa_device *tfa) { + + int value = 0; + + /* Set the new value in the hw register */ + value = tfa_get_bf(tfa, TFA9894_BF_MTPB); + + return value; +} + +/** Set internal oscillator into power down mode for TFA9894. +* +* This function is a worker for tfa98xx_set_osc_powerdown(). +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +static enum Tfa98xx_Error tfa9894_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (state == 1 || state == 0) { + return -tfa_set_bf(tfa, TFA9894_BF_MANAOOSC, (uint16_t)state); + } + + return Tfa98xx_Error_Bad_Parameter; +} + +static enum Tfa98xx_Error tfa9894_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + /* 0b = FAIM protection enabled 1b = FAIM protection disabled*/ + ret = tfa_set_bf_volatile(tfa, TFA9894_BF_OPENMTP, (uint16_t)(status)); + return ret; +} + +static enum Tfa98xx_Error tfa9894_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* The optimal settings */ + if (tfa->rev == 0x0a94) { + /* V36 */ + /* ----- generated code start ----- */ + reg_write(tfa, 0x00, 0xa245); //POR=0x8245 + reg_write(tfa, 0x02, 0x51e8); //POR=0x55c8 + reg_write(tfa, 0x52, 0xbe17); //POR=0xb617 + reg_write(tfa, 0x57, 0x0344); //POR=0x0366 + reg_write(tfa, 0x61, 0x0033); //POR=0x0073 + reg_write(tfa, 0x71, 0x00cf); //POR=0x018d + reg_write(tfa, 0x72, 0x34a9); //POR=0x44e8 + reg_write(tfa, 0x73, 0x3808); //POR=0x3806 + reg_write(tfa, 0x76, 0x0067); //POR=0x0065 + reg_write(tfa, 0x80, 0x0000); //POR=0x0003 + reg_write(tfa, 0x81, 0x5715); //POR=0x561a + reg_write(tfa, 0x82, 0x0104); //POR=0x0044 + /* ----- generated code end ----- */ + } else if (tfa->rev == 0x1a94) { + /* V14 */ + /* ----- generated code start ----- */ + reg_write(tfa, 0x00, 0xa245); //POR=0x8245 + reg_write(tfa, 0x01, 0x15da); //POR=0x11ca + reg_write(tfa, 0x02, 0x5288); //POR=0x55c8 + reg_write(tfa, 0x52, 0xbe17); //POR=0xb617 + reg_write(tfa, 0x53, 0x0dbe); //POR=0x0d9e + reg_write(tfa, 0x56, 0x05c3); //POR=0x07c3 + reg_write(tfa, 0x57, 0x0344); //POR=0x0366 + reg_write(tfa, 0x61, 0x0032); //POR=0x0073 + reg_write(tfa, 0x71, 0x00cf); //POR=0x018d + reg_write(tfa, 0x72, 0x34a9); //POR=0x44e8 + reg_write(tfa, 0x73, 0x38c8); //POR=0x3806 + reg_write(tfa, 0x76, 0x0067); //POR=0x0065 + reg_write(tfa, 0x80, 0x0000); //POR=0x0003 + reg_write(tfa, 0x81, 0x5799); //POR=0x561a + reg_write(tfa, 0x82, 0x0104); //POR=0x0044 + /* ----- generated code end ----- */ + + } + + return error; +} + +static enum Tfa98xx_Error +tfa9894_set_mute(struct tfa_device *tfa, int mute) +{ + tfa_set_bf(tfa, TFA9894_BF_CFSM, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa9894_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + /* check CLKS: ready if set */ + *ready = tfa_get_bf(tfa, TFA9894_BF_CLKS)==1; + + return error; +} + +void tfa9894_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9894_specific; + ops->dsp_system_stable = tfa9894_dsp_system_stable; + ops->set_mute = tfa9894_set_mute; + ops->faim_protect = tfa9894_faim_protect; + ops->get_mtpb = tfa9894_get_mtpb; + ops->set_swprof = tfa9894_set_swprofile; + ops->get_swprof = tfa9894_get_swprofile; + ops->set_swvstep = tfa9894_set_swvstep; + ops->get_swvstep = tfa9894_get_swvstep; + //ops->auto_copy_mtp_to_iic = tfa9894_auto_copy_mtp_to_iic; + ops->set_osc_powerdown = tfa9894_set_osc_powerdown; +} diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_internal.h b/techpack/audio/asoc/codecs/tfa9874/tfa_internal.h new file mode 100644 index 000000000000..9e8b35d3be76 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_internal.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2017 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. + */ + +/* + 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_ext.h" + +#if __GNUC__ >= 4 + #define TFA_INTERNAL __attribute__ ((visibility ("hidden"))) +#else + #define TFA_INTERNAL +#endif + +#define TFA98XX_GENERIC_SLAVE_ADDRESS 0x1C + +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_check_rpc_status(struct tfa_device *tfa, int *pRpcStatus); +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_wait_result(struct tfa_device *tfa, int waitRetryCount); + +#endif /* __TFA_INTERNAL_H__ */ + diff --git a/techpack/audio/asoc/codecs/tfa9874/tfa_service.h b/techpack/audio/asoc/codecs/tfa9874/tfa_service.h new file mode 100644 index 000000000000..64ae22f27984 --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/tfa_service.h @@ -0,0 +1,1021 @@ +/* + * Copyright 2014-2017 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 + +/* Linux kernel module defines TFA98XX_GIT_VERSIONS in the linux_driver/Makefile */ +#if !defined(TFA98XX_GIT_VERSIONS) +#include "versions.h" +#endif +#ifdef TFA98XX_GIT_VERSIONS + #define TFA98XX_API_REV_STR TFA98XX_GIT_VERSIONS +#else + #define TFA98XX_API_REV_STR "v6.5.2" +#endif + +#include "tfa_device.h" + +/* + * data previously defined in Tfa9888_dsp.h + */ +#define MEMTRACK_MAX_WORDS 150 +#define LSMODEL_MAX_WORDS 150 +#define TFA98XX_MAXTAG (150) +#define FW_VAR_API_VERSION (521) + +/* Indexes and scaling factors of GetLSmodel */ +#define tfa9888_fs_IDX 128 +#define tfa9888_leakageFactor_IDX 130 +#define tfa9888_ReCorrection_IDX 131 +#define tfa9888_Bl_IDX 132 +#define ReZ_IDX 147 + +#define tfa9872_leakageFactor_IDX 128 +#define tfa9872_ReCorrection_IDX 129 +#define tfa9872_Bl_IDX 130 + +#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 TFA72_ALGOPARAMETER_LENGTH_MONO (3*183) +#define TFA72_ALGOPARAMETER_LENGTH_STEREO (3*356) +#define TFA2_MBDRCPARAMETER_LENGTH (3*152) /* 154 is including the cmd-id */ +#define TFA72_MBDRCPARAMETER_LENGTH (3*98) +#define TFA1_PRESET_LENGTH 87 +#define TFA1_DRC_LENGTH 381 /* 127 words */ +#define TFA2_FILTERCOEFSPARAMETER_LENGTH (3*168) /* 170 is including the cmd-id */ +#define TFA72_FILTERCOEFSPARAMETER_LENGTH (3*156) + +/* 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 +#define TFA98XX_CONFIG_LENGTH 201 +#define TFA98XX_DRC_LENGTH 381 /* 127 words */ + +typedef unsigned char Tfa98xx_Config_t[TFA98XX_CONFIG_LENGTH]; +typedef unsigned char Tfa98xx_Preset_t[TFA98XX_PRESET_LENGTH]; +typedef unsigned char Tfa98xx_DrcParameters_t[TFA98XX_DRC_LENGTH]; + +/* Type containing all the possible errors that can occur + * + */ +enum Tfa98xx_Error { + Tfa98xx_Error_Ok = 0, + Tfa98xx_Error_Device, /* 1. Currently only used to keep in sync with tfa_error */ + Tfa98xx_Error_Bad_Parameter, /* 2. */ + Tfa98xx_Error_Fail, /* 3. generic failure, avoid mislead message */ + Tfa98xx_Error_NoClock, /* 4. no clock detected */ + Tfa98xx_Error_StateTimedOut, /* 5. */ + Tfa98xx_Error_DSP_not_running, /* 6. communication with the DSP failed */ + Tfa98xx_Error_AmpOn, /* 7. amp is still running */ + Tfa98xx_Error_NotOpen, /* 8. the given handle is not open */ + Tfa98xx_Error_InUse, /* 9. too many handles */ + Tfa98xx_Error_Buffer_too_small, /* 10. 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_RpcInvalidCC = 104, + Tfa98xx_Error_RpcInvalidSeq = 105, + Tfa98xx_Error_RpcInvalidParam = 106, + Tfa98xx_Error_RpcBufferOverflow = 107, + Tfa98xx_Error_RpcCalibBusy = 108, + Tfa98xx_Error_RpcCalibFailed = 109, + 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 + * //TODO move to tfa_dsp_fw.h + */ +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 */ + Tfa98xx_I2C_Req_Calib_Busy = 8, /* Calibration not finished */ + Tfa98xx_I2C_Req_Calib_Failed = 9 /* Calibration failed */ +}; + +/* + * speaker as microphone + */ +enum Tfa98xx_saam { + Tfa98xx_saam_none, /*< SAAM feature not available */ + Tfa98xx_saam /*< SAAM feature available */ +}; + +/* + * 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; + struct Tfa98xx_DrcStateInfo drcState; /* DRC state, when enabled */ +}; + +typedef struct nxpTfaMsg { + uint8_t msg_size; + unsigned char cmdId[3]; + int data[9]; +} nxpTfaMsg_t; + +typedef struct nxp_vstep_msg { + int fw_version; + uint8_t no_of_vsteps; + uint16_t reg_no; + uint8_t *msg_reg; + uint8_t msg_no; + uint32_t algo_param_length; + uint8_t *msg_algo_param; + uint32_t filter_coef_length; + uint8_t *msg_filter_coef; + uint32_t mbdrc_length; + uint8_t *msg_mbdrc; +} nxp_vstep_msg_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 trackers[MEMTRACK_MAX_WORDS]; + int scalingFactor[MEMTRACK_MAX_WORDS]; +}; + +/* possible memory values for DMEM in CF_CONTROLs */ +enum Tfa98xx_DMEM { + Tfa98xx_DMEM_ERR = -1, + 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 */ +}; + +enum Tfa98xx_DMEM tfa98xx_filter_mem(struct tfa_device *tfa, int filter_index, unsigned short *address, int channel); + +/** + * Load the default HW settings in the device + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa98xx_init(struct tfa_device *tfa); + +/** + * If needed, this function can be used to get a text version of the status ID code + * @param status the given status ID code + * @return the I2C status ID string + */ +const char *tfa98xx_get_i2c_status_id_string(int status); + +/* control the powerdown bit + * @param tfa the device struct pointer + * @param powerdown must be 1 or 0 + */ +enum Tfa98xx_Error tfa98xx_powerdown(struct tfa_device *tfa, int powerdown); + +/* indicates on which channel of DATAI2 the gain from the IC is set + * @param tfa the device struct pointer + * @param gain_sel, see Tfa98xx_StereoGainSel_t + */ +enum Tfa98xx_Error tfa98xx_select_stereo_gain_channel(struct tfa_device *tfa, + enum Tfa98xx_StereoGainSel gain_sel); + +/** + * set the mtp with user controllable values + * @param tfa the device struct pointer + * @param value to be written + * @param mask to be applied toi the bits affected + */ +enum Tfa98xx_Error tfa98xx_set_mtp(struct tfa_device *tfa, uint16_t value, uint16_t mask); +enum Tfa98xx_Error tfa98xx_get_mtp(struct tfa_device *tfa, 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(struct tfa_device *tfa, int lock); + +int tfa_calibrate(struct tfa_device *tfa) ; +void tfa98xx_set_exttemp(struct tfa_device *tfa, short ext_temp); +short tfa98xx_get_exttemp(struct tfa_device *tfa); + +/* 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(struct tfa_device *tfa, + unsigned short vol); + +/* set the input channel to use + * @param channel see Tfa98xx_Channel_t enumeration + */ +enum Tfa98xx_Error tfa98xx_select_channel(struct tfa_device *tfa, + enum Tfa98xx_Channel channel); + +/* set the mode for normal or receiver mode + * @param mode see Tfa98xx_Mode enumeration + */ +enum Tfa98xx_Error tfa98xx_select_mode(struct tfa_device *tfa, enum Tfa98xx_Mode mode ); + +/* mute/unmute the audio + * @param mute see Tfa98xx_Mute_t enumeration + */ +enum Tfa98xx_Error tfa98xx_set_mute(struct tfa_device *tfa, + enum Tfa98xx_Mute mute); + +/* + * tfa_supported_speakers - required for SmartStudio initialization + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa_supported_speakers(struct tfa_device *tfa, int* spkr_count); + +/** +* Return the tfa revision +*/ +void tfa98xx_rev(int *major, int *minor, int *revision); + +/* + * Return the feature bits from MTP and cnt file for comparison + */ +enum Tfa98xx_Error +tfa98xx_compare_features(struct tfa_device *tfa, int features_from_MTP[3], int features_from_cnt[3]); + +/* + * return feature bits + */ +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(struct tfa_device *tfa, int features[2]); +enum Tfa98xx_Error +tfa98xx_dsp_get_hw_feature_bits(struct tfa_device *tfa, int *features); + +/* + * tfa98xx_supported_saam + * returns the speaker as microphone feature + * @param saam enum pointer + * @return error code + */ +enum Tfa98xx_Error tfa98xx_supported_saam(struct tfa_device *tfa, 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(struct tfa_device *tfa, int sample_rate); + + +/* set or clear DSP reset signal + * @param new state + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_reset(struct tfa_device *tfa, int state); + +/* check the state of the DSP subsystem + * return ready = 1 when clocks are stable to allow safe DSP subsystem access + * @param tfa the device struct pointer + * @param ready pointer to state flag, non-zero if clocks are not stable + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(struct tfa_device *tfa, int *ready); + +enum Tfa98xx_Error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa); + +/** + * check the state of the DSP coolflux + * @param tfa the device struct pointer + * @return the value of CFE + */ +int tfa_cf_enabled(struct tfa_device *tfa); + +/* 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 tfa the device struct pointer + * @param patchLength the number of bytes of patchBytes + * @param patchBytes pointer to the bytes to patch + */ +enum Tfa98xx_Error tfa_dsp_patch(struct tfa_device *tfa, + int patchLength, + const unsigned char *patchBytes); + +/** + * 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( + struct tfa_device *tfa, + int length, + const unsigned char *pSpeakerBytes); + +/** + * read the speaker parameters as used by the SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_speaker_parameters( + struct tfa_device *tfa, + 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( + struct tfa_device *tfa, + 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(struct tfa_device *tfa, + int *pbSupportDrc); + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(struct tfa_device *tfa, int *pbSupportFramework); + +/** + * read the speaker excursion model as used by SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_excursion_model( + struct tfa_device *tfa, + int length, + unsigned char *pSpeakerBytes); + +/** + * load all the parameters for a preset from a file + */ +enum Tfa98xx_Error tfa98xx_dsp_write_preset(struct tfa_device *tfa, + int length, const unsigned char + *pPresetBytes); + +/** + * wrapper for dsp_msg that adds opcode and only writes + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(struct tfa_device *tfa, + 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(struct tfa_device *tfa, + 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(struct tfa_device *tfa, + 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(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]); + +/** + * Disable a certain biquad. + * @param tfa the device struct pointer + * @param biquad_index: 1-10 of the biquad that needs to be adressed +*/ +enum Tfa98xx_Error Tfa98xx_DspBiquad_Disable(struct tfa_device *tfa, + 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(struct tfa_device *tfa); + +/* + * return the mohm value + */ +int tfa_get_calibration_info(struct tfa_device *tfa, int channel); + +/* + * return sign extended tap pattern + */ +int tfa_get_tap_pattern(struct tfa_device *tfa); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @param subaddress write address to set in address register + * @param pValue pointer to read data +*/ +enum Tfa98xx_Error tfa98xx_read_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short *pValue); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @param subaddress write address to set in address register + * @param value value to write int the memory +*/ +enum Tfa98xx_Error tfa98xx_write_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short value); + +/** + * Intialise the dsp + * @param tfa the device struct pointer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_init_dsp(struct tfa_device *tfa); + +/** + * Get the status of the external DSP + * @param tfa the device struct pointer + * @return status +*/ +int tfa98xx_get_dsp_status(struct tfa_device *tfa); + +/** + * Write a command message (RPC) to the dsp + * @param tfa the device struct pointer + * @param num_bytes command buffer size in bytes + * @param command_buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_write_dsp(struct tfa_device *tfa, int num_bytes, const char *command_buffer); + +/** + * Read the result from the last message from the dsp + * @param tfa the device struct pointer + * @param num_bytes result buffer size in bytes + * @param result_buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_read_dsp(struct tfa_device *tfa, int num_bytes, unsigned char *result_buffer); + +/** + * Write a command message (RPC) to the dsp and return the result + * @param tfa the device struct pointer + * @param command_length command buffer size in bytes + * @param command_buffer command buffer + * @param result_length result buffer size in bytes + * @param result_buffer result buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_writeread_dsp(struct tfa_device *tfa, int command_length, void *command_buffer, + int result_length, void *result_buffer); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, + unsigned int start_offset, + int num_words, int *pValues); +/** + * Write a value to dsp memory + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, + unsigned short address, int value, int memtype); + +/** + * Read data from dsp memory + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, + unsigned char subaddress, + int num_bytes, unsigned char data[]); + +/** + * Write all the bytes specified by num_bytes and data to dsp memory + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, + unsigned char subaddress, + int num_bytes, + const unsigned char data[]); + +enum Tfa98xx_Error tfa98xx_write_raw(struct tfa_device *tfa, + 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[]); + +/** + * 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 tfa the device struct pointer + * @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(struct tfa_device *tfa, int memoryType, + int offset, int length, unsigned char bytes[]); + +/** + * Write a value to the dsp memory + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, int memoryType, + int offset, int length, int value); + +enum Tfa98xx_Error tfa98xx_dsp_write_config(struct tfa_device *tfa, int length, const unsigned char *p_config_bytes); +enum Tfa98xx_Error tfa98xx_dsp_write_drc(struct tfa_device *tfa, int length, const unsigned char *p_drc_bytes); + +/** + * 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 tfa the device struct pointer + * @param length length of the character buffer to write + * @param buf character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg(struct tfa_device *tfa, int length, const char *buf); + + +/** + * The wrapper functions to call the dsp msg, register and memory function for tfa or probus + */ +enum Tfa98xx_Error dsp_msg(struct tfa_device *tfa, int length, const char *buf); +enum Tfa98xx_Error dsp_msg_read(struct tfa_device *tfa, int length, unsigned char *bytes); +enum Tfa98xx_Error reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); +enum Tfa98xx_Error reg_read(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value); +enum Tfa98xx_Error mem_write(struct tfa_device *tfa, unsigned short address, int value, int memtype); +enum Tfa98xx_Error mem_read(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues); + +enum Tfa98xx_Error dsp_partial_coefficients(struct tfa_device *tfa, uint8_t *prev, uint8_t *next); + +/** + * 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 tfa the device struct pointer + * @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(struct tfa_device *tfa, int length, const char *buf, uint8_t cmdid[3]); + +/** + * write raw dsp msg functions + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buffer character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg_write(struct tfa_device *tfa, int length, const char *buffer); + +/** + * write raw dsp msg functions + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buffer character buffer to write + * @param cmdid command identifier +*/ +enum Tfa98xx_Error tfa_dsp_msg_write_id(struct tfa_device *tfa, 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 tfa the device struct pointer + * @param pRpcStatus status for remote processor communication +*/ +enum Tfa98xx_Error tfa_dsp_msg_status(struct tfa_device *tfa, int *pRpcStatus); + +/** + * Read a message from dsp + * @param tfa the device struct pointer + * @param length number of bytes of the message + * @param bytes pointer to unsigned char buffer +*/ +enum Tfa98xx_Error tfa_dsp_msg_read(struct tfa_device *tfa,int length, unsigned char *bytes); + +int tfa_set_bf(struct tfa_device *tfa, const uint16_t bf, const uint16_t value); +int tfa_set_bf_volatile(struct tfa_device *tfa, const uint16_t bf, const uint16_t value); + +/** + * Get the value of a given bitfield + * @param tfa the device struct pointer + * @param bf the value indicating which bitfield + */ +int tfa_get_bf(struct tfa_device *tfa, 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(struct tfa_device *tfa, const uint16_t bf, const uint16_t reg_value); +int tfa_read_reg(struct tfa_device *tfa, const uint16_t bf); + +/* bitfield */ +/** + * get the datasheet or 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 *tfaContBfName(uint16_t num, unsigned short rev); + +/** + * 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 *tfaContDsName(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(tfa, fieldname) ((tfa->tfa_family == 1) ? TFA1_BF_##fieldname : TFA2_BF_##fieldname) +#define TFA_FAM_FW(tfa, fwname) ((tfa->tfa_family == 1) ? TFA1_FW_##fwname : TFA2_FW_##fwname) + +/* set/get bit fields to HW register*/ +#define TFA_SET_BF(tfa, fieldname, value) tfa_set_bf(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_SET_BF_VOLATILE(tfa, fieldname, value) tfa_set_bf_volatile(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_GET_BF(tfa, fieldname) tfa_get_bf(tfa, TFA_FAM(tfa, fieldname)) + +/* set/get bit field in variable */ +#define TFA_SET_BF_VALUE(tfa, fieldname, bf_value, p_reg_value) tfa_set_bf_value(TFA_FAM(tfa, fieldname), bf_value, p_reg_value) +#define TFA_GET_BF_VALUE(tfa, fieldname, reg_value) tfa_get_bf_value(TFA_FAM(tfa, fieldname), reg_value) + +/* write/read registers using a bit field name to determine the register address */ +#define TFA_WRITE_REG(tfa, fieldname, value) tfa_write_reg(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_READ_REG(tfa, fieldname) tfa_read_reg(tfa, TFA_FAM(tfa, fieldname)) + +/* FOR CALIBRATION RETRIES */ +#define TFA98XX_API_WAITRESULT_NTRIES 3000 // defined in API + +/** + * run the startup/init sequence and set ACS bit + * @param tfa the device struct pointer + * @param state the cold start state that is requested + */ +enum Tfa98xx_Error tfaRunColdboot(struct tfa_device *tfa, int state); +enum Tfa98xx_Error tfaRunMute(struct tfa_device *tfa); +enum Tfa98xx_Error tfaRunUnmute(struct tfa_device *tfa); + +/** + * wait for calibrateDone + * @param tfa the device struct pointer + * @param calibrateDone pointer to status of calibration + */ +enum Tfa98xx_Error tfaRunWaitCalibration(struct tfa_device *tfa, int *calibrateDone); + +/** + * run the startup/init sequence and set ACS bit + * @param tfa the device struct pointer + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunColdStartup(struct tfa_device *tfa, int profile); + +/** + * this will load the patch witch will implicitly start the DSP + * if no patch is available the DPS is started immediately + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfaRunStartDSP(struct tfa_device *tfa); + +/** + * start the clocks and wait until the AMP is switching + * on return the DSP sub system will be ready for loading + * @param tfa the device struct pointer + * @param profile the profile that should be loaded on startup + */ +enum Tfa98xx_Error tfaRunStartup(struct tfa_device *tfa, int profile); + +/** + * start the maximus speakerboost algorithm + * this implies a full system startup when the system was not already started + * @param tfa the device struct pointer + * @param force indicates wether a full system startup should be allowed + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunSpeakerBoost(struct tfa_device *tfa, int force, int profile); + +/** + * Startup the device and write all files from device and profile section + * @param tfa the device struct pointer + * @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(struct tfa_device *tfa, int force, int profile); + +/** + * Run calibration + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(struct tfa_device *tfa); + +/** + * startup all devices. all step until patch loading is handled + * @param tfa the device struct pointer + */ +int tfaRunStartupAll(struct tfa_device *tfa); + +/** + * powerup the coolflux subsystem and wait for it + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa_cf_powerup(struct tfa_device *tfa); + +/* + * print the current device manager state + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error show_current_state(struct tfa_device *tfa); + +/** + * Init registers and coldboot dsp + * @param tfa the device struct pointer + */ +int tfa_reset(struct tfa_device *tfa); + +/** + * Get profile from a register + * @param tfa the device struct pointer + */ +int tfa_dev_get_swprof(struct tfa_device *tfa); + +/** + * Save profile in a register + */ +int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value); + +int tfa_dev_get_swvstep(struct tfa_device *tfa); + +int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value); + +int tfa_needs_reset(struct tfa_device *tfa); + +int tfa_is_cold(struct tfa_device *tfa); + +void tfa_set_query_info(struct tfa_device *tfa); + +int tfa_get_pga_gain(struct tfa_device *tfa); +int tfa_set_pga_gain(struct tfa_device *tfa, uint16_t value); +int tfa_get_noclk(struct tfa_device *tfa); + +/** + * Status of used for monitoring + * @param tfa the device struct pointer + * @return tfa error enum + */ + +enum Tfa98xx_Error tfa_status(struct tfa_device *tfa); + +/* + * function overload for flag_mtp_busy + */ +int tfa_dev_get_mtpb(struct tfa_device *tfa); + +#ifdef __cplusplus +} +#endif +#endif /* TFA_SERVICE_H */ diff --git a/techpack/audio/asoc/codecs/tfa9874/versions.h b/techpack/audio/asoc/codecs/tfa9874/versions.h new file mode 100644 index 000000000000..37e14f450a4e --- /dev/null +++ b/techpack/audio/asoc/codecs/tfa9874/versions.h @@ -0,0 +1,3 @@ +#ifndef _VERSIONS_H +#define _VERSIONS_H +#endif diff --git a/techpack/audio/asoc/codecs/wcd-mbhc-adc.c b/techpack/audio/asoc/codecs/wcd-mbhc-adc.c index 5865af3dfc9b..3093e0b3a070 100644 --- a/techpack/audio/asoc/codecs/wcd-mbhc-adc.c +++ b/techpack/audio/asoc/codecs/wcd-mbhc-adc.c @@ -23,9 +23,9 @@ #include #include -#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700 +#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1800 #define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75 -#define WCD_MBHC_ADC_MICBIAS_MV 1800 +#define WCD_MBHC_ADC_MICBIAS_MV 2700 #define WCD_MBHC_FAKE_INS_RETRY 4 static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc) @@ -958,6 +958,8 @@ static void wcd_correct_swch_plug(struct work_struct *work) if (mbhc->mbhc_cb->mbhc_micbias_control) wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); exit: + if (plug_type == MBHC_PLUG_TYPE_HEADSET) + mbhc->micbias_enable = true; if (mbhc->mbhc_cb->mbhc_micbias_control && !mbhc->micbias_enable) mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, @@ -1026,7 +1028,10 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); - adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + /* liuhaituo@MM.Audio 2018/6/8 modify adc_threshold is consistent with OMR1 */ + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc)) / + WCD_MBHC_ADC_MICBIAS_MV); do { retry++; diff --git a/techpack/audio/asoc/codecs/wcd-mbhc-v2.c b/techpack/audio/asoc/codecs/wcd-mbhc-v2.c index 1fd3f96a20a9..732f5b50f6ff 100644 --- a/techpack/audio/asoc/codecs/wcd-mbhc-v2.c +++ b/techpack/audio/asoc/codecs/wcd-mbhc-v2.c @@ -547,6 +547,92 @@ void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, } EXPORT_SYMBOL(wcd_mbhc_hs_elec_irq); +/* liuhaituo@MM.Audio add new function to adapt headset volume */ +bool headset_imp_enable = false; +EXPORT_SYMBOL_GPL(headset_imp_enable); + +static enum { + LOW_Z = 0, + HIGH_Z, + NONE_Z, +}current_imp = NONE_Z; +static int hp_volume_gain[3] ={80 /* LOW_Z_gain */, + 85 /* HIGH_Z_gain */, + 80 /* NONE_Z_gain */}; +static int original_imp = NONE_Z; +#define HIGH_Z_THR 55 //The actual threshold impedance is 45, phone own impedance ~10 +#define HIGH_Z_MIN_THR (HIGH_Z_THR - 3) +#define HIGH_Z_MAX_THR (HIGH_Z_THR + 3) + +static int adapt_headset_volume(struct wcd_mbhc *mbhc, int volume) +{ + int ret = 0; + struct snd_soc_component *component = mbhc->component; + int mask = (1 << (fls(40 - 84) -1)) - 1; + //because volume from -84 to 40; + volume -= 84; + + /* set RX1 Mix Digital Volume */ + ret = snd_soc_component_update_bits(component, 0xb5c, mask, volume); + if (ret < 0) + return ret; + + /* set RX2 Mix Digital Volume */ + ret = snd_soc_component_update_bits(component, 0xb70, mask, volume); + if (ret < 0) + return ret; + + /* set RX1 Digital Volume */ + ret = snd_soc_component_update_bits(component, 0xb59, mask, volume); + if (ret < 0) + return ret; + + /* set RX2 Digital Volume */ + ret = snd_soc_component_update_bits(component, 0xb6d, mask, volume); + if (ret < 0) + return ret; + + pr_info("%s: set %d success!\n", __func__, volume); + + return ret; +} + +static void judge_headset_impedance(struct wcd_mbhc *mbhc) +{ + int ret = 0; + int left_z = mbhc->zl; + int right_z = mbhc->zr; + + pr_err("%s: left_z is %d, right_z is %d\n", __func__, left_z, right_z); + + if ((original_imp != NONE_Z) && + (left_z > HIGH_Z_MIN_THR) && (left_z < HIGH_Z_MAX_THR) && + (right_z > HIGH_Z_MIN_THR) && (right_z < HIGH_Z_MAX_THR)) { + current_imp = original_imp; + } else if ((left_z < HIGH_Z_THR) || (right_z < HIGH_Z_THR)) { + if (((left_z == 0) || (right_z == 0)) && (original_imp != NONE_Z)) + current_imp = original_imp; + else { + current_imp = LOW_Z; + original_imp = current_imp; + } + } else { + current_imp = HIGH_Z; + original_imp = current_imp; + } + + pr_err("%s: current_imp is %d, original_imp is %d\n", + __func__, + current_imp, + original_imp); + + ret = adapt_headset_volume(mbhc, hp_volume_gain[current_imp]); + if (ret < 0) + pr_err("%s: set headset volume fail\n", __func__); + + return; +} + void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, enum snd_jack_types jack_type) { @@ -732,6 +818,9 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, } mbhc->hph_status |= jack_type; + /* liuhaituo@MM.Audio add new function to adapt headset volume */ + if (headset_imp_enable) + judge_headset_impedance(mbhc); if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control) @@ -1022,10 +1111,14 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) wcd_mbhc_report_plug(mbhc, 0, jack_type); if (mbhc->mbhc_cfg->enable_usbc_analog) { - WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); - if (mbhc->mbhc_cb->clk_setup) - mbhc->mbhc_cb->clk_setup( - mbhc->component, false); + if (mbhc->mbhc_cfg->usbc_analog_legacy) { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + } else { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup( + mbhc->component, false); + } } if (mbhc->mbhc_cfg->moisture_en || @@ -1382,6 +1475,11 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) * by an external source */ if (mbhc->mbhc_cfg->enable_usbc_analog) { + if (mbhc->mbhc_cfg->usbc_analog_legacy) { + mbhc->hphl_swh = 1; + mbhc->gnd_swh = 1; + } + if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(component, HS_PULLUP_I_OFF); @@ -1404,7 +1502,7 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted */ - if (mbhc->mbhc_cfg->enable_usbc_analog) + if (mbhc->mbhc_cfg->enable_usbc_analog && !mbhc->mbhc_cfg->usbc_analog_legacy) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); else WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); @@ -1424,7 +1522,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_bias(component, true); /* enable MBHC clock */ if (mbhc->mbhc_cb->clk_setup) { - if (mbhc->mbhc_cfg->enable_usbc_analog) + if (mbhc->mbhc_cfg->enable_usbc_analog && + !mbhc->mbhc_cfg->usbc_analog_legacy) mbhc->mbhc_cb->clk_setup(component, false); else mbhc->mbhc_cb->clk_setup(component, true); @@ -1598,6 +1697,204 @@ static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb, return 0; } +static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc, + bool active) +{ + int rc = 0; + struct snd_soc_component *component = mbhc->component; + struct usbc_ana_audio_config *config = + &mbhc->mbhc_cfg->usbc_analog_cfg; + union power_supply_propval pval; + + dev_dbg(component->dev, "%s: setting GPIOs active = %d\n", + __func__, active); + + memset(&pval, 0, sizeof(pval)); + + if (active) { + pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE; + if (power_supply_set_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) + dev_info(component->dev, "%s: force PR_SOURCE mode unsuccessful\n", + __func__); + else + mbhc->usbc_force_pr_mode = true; + + if (config->usbc_en1_gpio_p) + rc = msm_cdc_pinctrl_select_active_state( + config->usbc_en1_gpio_p); + if (rc == 0 && config->usbc_force_gpio_p) + rc = msm_cdc_pinctrl_select_active_state( + config->usbc_force_gpio_p); + mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; + } else { + /* no delay is required when disabling GPIOs */ + if (config->usbc_en1_gpio_p) + msm_cdc_pinctrl_select_sleep_state( + config->usbc_en1_gpio_p); + if (config->usbc_force_gpio_p) + msm_cdc_pinctrl_select_sleep_state( + config->usbc_force_gpio_p); + + if (mbhc->usbc_force_pr_mode) { + pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; + if (power_supply_set_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) + dev_info(component->dev, "%s: force PR_DUAL mode unsuccessful\n", + __func__); + + mbhc->usbc_force_pr_mode = false; + } + + mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE; + if (mbhc->mbhc_cfg->swap_gnd_mic) + mbhc->mbhc_cfg->swap_gnd_mic(component, false); + } + + return rc; +} + +/* workqueue */ +static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work) +{ + struct wcd_mbhc *mbhc = + container_of(work, struct wcd_mbhc, usbc_analog_work); + + wcd_mbhc_usb_c_analog_setup_gpios(mbhc, + mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE); +} + +/* this callback function is used to process PMI notification */ +static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb, + unsigned long evt, void *ptr) +{ + int ret; + union power_supply_propval mode; + struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb); + struct snd_soc_component *component = mbhc->component; + + if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED) + return 0; + + ret = power_supply_get_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &mode); + if (ret) { + dev_err(component->dev, "%s: Unable to read USB TYPEC_MODE: %d\n", + __func__, ret); + return ret; + } + + dev_dbg(component->dev, "%s: USB change event received\n", + __func__); + dev_dbg(component->dev, "%s: supply mode %d, expected %d\n", __func__, + mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER); + + switch (mode.intval) { + case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: + case POWER_SUPPLY_TYPEC_NONE: + dev_dbg(component->dev, "%s: usbc_mode: %d; mode.intval: %d\n", + __func__, mbhc->usbc_mode, mode.intval); + + if (mbhc->usbc_mode == mode.intval) + break; /* filter notifications received before */ + mbhc->usbc_mode = mode.intval; + + dev_dbg(component->dev, "%s: queueing usbc_analog_work\n", + __func__); + schedule_work(&mbhc->usbc_analog_work); + break; + default: + break; + } + return ret; +} + +/* PMI registration code */ +static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_component *component = mbhc->component; + + dev_dbg(component->dev, "%s: usb-c analog setup start\n", __func__); + INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn); + + mbhc->usb_psy = power_supply_get_by_name("usb"); + if (IS_ERR_OR_NULL(mbhc->usb_psy)) { + dev_err(component->dev, "%s: could not get USB psy info\n", + __func__); + ret = -EPROBE_DEFER; + if (IS_ERR(mbhc->usb_psy)) + ret = PTR_ERR(mbhc->usb_psy); + mbhc->usb_psy = NULL; + goto err; + } + + ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); + if (ret) { + dev_err(component->dev, "%s: error while setting USBC ana gpios\n", + __func__); + goto err; + } + + mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed; + mbhc->psy_nb.priority = 0; + ret = power_supply_reg_notifier(&mbhc->psy_nb); + if (ret) { + dev_err(component->dev, "%s: power supply registration failed\n", + __func__); + goto err; + } + + /* + * as part of the init sequence check if there is a connected + * USB C analog adapter + */ + dev_dbg(component->dev, "%s: verify if USB adapter is already inserted\n", + __func__); + ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb, + PSY_EVENT_PROP_CHANGED, + mbhc->usb_psy); + +err: + return ret; +} + +static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc) +{ + wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); + + /* deregister from PMI */ + power_supply_unreg_notifier(&mbhc->psy_nb); + + return 0; +} + +static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg, + const char *gpio_dt_str, + int *gpio, struct device_node **gpio_dn) +{ + int rc = 0; + struct snd_soc_component *component = mbhc->component; + struct snd_soc_card *card = component->card; + + dev_dbg(component->dev, "%s: gpio %s\n", __func__, gpio_dt_str); + + *gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0); + + if (!(*gpio_dn)) { + *gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0); + if (!gpio_is_valid(*gpio)) { + dev_err(card->dev, "%s, property %s not in node %s", + __func__, gpio_dt_str, + card->dev->of_node->full_name); + rc = -EINVAL; + } + } + + return rc; +} + int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) { int rc = 0; @@ -1638,9 +1935,43 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) mbhc->fsa_np = of_parse_phandle(card->dev->of_node, "fsa4480-i2c-handle", 0); if (!mbhc->fsa_np) { - dev_err(card->dev, "%s: fsa4480 i2c node not found\n", + dev_err(card->dev, "%s: fsa4480 i2c node not found, " + "trying legacy Type-C analog audio\n", __func__); - rc = -EINVAL; + + mbhc_cfg->usbc_analog_legacy = true; + /* goto err; */ + } + } + + if (mbhc_cfg->enable_usbc_analog && mbhc_cfg->usbc_analog_legacy) { + struct usbc_ana_audio_config *config = + &mbhc_cfg->usbc_analog_cfg; + + rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, + "qcom,usbc-analog-en1-gpio", + &config->usbc_en1_gpio, + &config->usbc_en1_gpio_p); + if (rc) + goto err; + + if (of_find_property(card->dev->of_node, + "qcom,usbc-analog-force_detect_gpio", + NULL)) { + rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, + "qcom,usbc-analog-force_detect_gpio", + &config->usbc_force_gpio, + &config->usbc_force_gpio_p); + if (rc) + goto err; + } + + dev_dbg(component->dev, "%s: calling usb_c_analog_init\n", + __func__); + /* init PMI notifier */ + rc = wcd_mbhc_usb_c_analog_init(mbhc); + if (rc) { + rc = -EPROBE_DEFER; goto err; } } @@ -1667,7 +1998,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } - if (mbhc_cfg->enable_usbc_analog) { + if (mbhc_cfg->enable_usbc_analog && !mbhc_cfg->usbc_analog_legacy) { mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler; mbhc->fsa_nb.priority = 0; rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); @@ -1706,8 +2037,26 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc) mbhc->mbhc_cal = NULL; } - if (mbhc->mbhc_cfg->enable_usbc_analog) - fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); + if (mbhc->mbhc_cfg->enable_usbc_analog) { + if (mbhc->mbhc_cfg->usbc_analog_legacy) { + struct usbc_ana_audio_config *config = + &mbhc->mbhc_cfg->usbc_analog_cfg; + + wcd_mbhc_usb_c_analog_deinit(mbhc); + /* free GPIOs */ + if (config->usbc_en1_gpio > 0) + gpio_free(config->usbc_en1_gpio); + if (config->usbc_force_gpio) + gpio_free(config->usbc_force_gpio); + + if (config->usbc_en1_gpio_p) + of_node_put(config->usbc_en1_gpio_p); + if (config->usbc_force_gpio_p) + of_node_put(config->usbc_force_gpio_p); + } else { + fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); + } + } pr_debug("%s: leave\n", __func__); } diff --git a/techpack/audio/asoc/codecs/wcd934x/Android.mk b/techpack/audio/asoc/codecs/wcd934x/Android.mk index 18170f404cb8..787298c1a2a3 100644 --- a/techpack/audio/asoc/codecs/wcd934x/Android.mk +++ b/techpack/audio/asoc/codecs/wcd934x/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform,msmnile),true) AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m endif @@ -17,7 +21,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) sdm660),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) sdm660 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/asoc/codecs/wcd934x/Kbuild b/techpack/audio/asoc/codecs/wcd934x/Kbuild index 404cc8dca141..18b28c466896 100644 --- a/techpack/audio/asoc/codecs/wcd934x/Kbuild +++ b/techpack/audio/asoc/codecs/wcd934x/Kbuild @@ -16,6 +16,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM6150), y) include $(AUDIO_ROOT)/config/sm6150auto.conf export diff --git a/techpack/audio/asoc/codecs/wcd934x/wcd934x-mbhc.c b/techpack/audio/asoc/codecs/wcd934x/wcd934x-mbhc.c index d777d8d56446..af9f97336eb5 100644 --- a/techpack/audio/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/techpack/audio/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -982,6 +982,36 @@ static const struct snd_kcontrol_new impedance_detect_controls[] = { tavil_hph_impedance_get, NULL), }; +/* liuhaituo@MM.Audio 2018/7/17 add for headset impedance detect */ +extern bool headset_imp_enable; + +static int headset_imp_feature_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int type = ucontrol->value.integer.value[0]; + if (type != 0) + headset_imp_enable = true; + + pr_info("%s: headset_imp_feature is %d\n", + __func__, headset_imp_enable); + + return 0; +} + +static int headset_imp_feature_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = (int)headset_imp_enable; + pr_info("%s: get success!\n", __func__); + + return 0; +} + +static const struct snd_kcontrol_new headset_feature_controls[] = { + SOC_SINGLE_EXT("headset_imp_feature", 0, 0, 1, 0, + headset_imp_feature_get, headset_imp_feature_put), +}; + /* * tavil_mbhc_get_impedance: get impedance of headphone left and right channels * @wcd934x_mbhc: handle to struct wcd934x_mbhc * @@ -1156,6 +1186,9 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, ARRAY_SIZE(impedance_detect_controls)); snd_soc_add_component_controls(component, hph_type_detect_controls, ARRAY_SIZE(hph_type_detect_controls)); + /* liuhaituo@MM.Audio 2018/7/17 add for headset impedance detect */ + snd_soc_add_component_controls(component, headset_feature_controls, + ARRAY_SIZE(headset_feature_controls)); if (wcd_mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY) { snd_soc_component_update_bits(component, WCD934X_MBHC_NEW_CTL_1, diff --git a/techpack/audio/asoc/codecs/wcd934x/wcd934x-regmap.c b/techpack/audio/asoc/codecs/wcd934x/wcd934x-regmap.c index a9642091a66e..5ae66a598224 100644 --- a/techpack/audio/asoc/codecs/wcd934x/wcd934x-regmap.c +++ b/techpack/audio/asoc/codecs/wcd934x/wcd934x-regmap.c @@ -365,7 +365,8 @@ static const struct reg_default wcd934x_defaults[] = { { WCD934X_INTR_LEVEL2, 0x94 }, { WCD934X_INTR_LEVEL3, 0x80 }, { WCD934X_INTR_BYPASS0, 0x00 }, - { WCD934X_INTR_BYPASS1, 0x00 }, + /* Bypass "Elect remove irq" and "Elect insert irq" */ + { WCD934X_INTR_BYPASS1, 0x12 }, { WCD934X_INTR_BYPASS2, 0x00 }, { WCD934X_INTR_BYPASS3, 0x00 }, { WCD934X_INTR_SET0, 0x00 }, diff --git a/techpack/audio/asoc/codecs/wcd934x/wcd934x.c b/techpack/audio/asoc/codecs/wcd934x/wcd934x.c index 49f164c9d9ff..967d9fd7d026 100644 --- a/techpack/audio/asoc/codecs/wcd934x/wcd934x.c +++ b/techpack/audio/asoc/codecs/wcd934x/wcd934x.c @@ -4429,6 +4429,11 @@ static void tavil_codec_set_tx_hold(struct snd_soc_component *component, case WCD934X_ANA_AMIC2: snd_soc_component_update_bits(component, WCD934X_ANA_AMIC2, mask, val); + // suzhiguang,sleep 150ms to avoid mic pop. + if (amic_reg == WCD934X_ANA_AMIC2) { + pr_err("begin to sleep 150 ms\n"); + usleep_range(150 * 1000, 150 * 1010); + } break; case WCD934X_ANA_AMIC3: case WCD934X_ANA_AMIC4: diff --git a/techpack/audio/asoc/msm-pcm-routing-v2.c b/techpack/audio/asoc/msm-pcm-routing-v2.c index 6eafbd0c3627..6b4ec1ccb0e7 100644 --- a/techpack/audio/asoc/msm-pcm-routing-v2.c +++ b/techpack/audio/asoc/msm-pcm-routing-v2.c @@ -3575,7 +3575,7 @@ static const char *const be_name[] = { "RX_CDC_DMA_RX_6", "RX_CDC_DMA_RX_7", "PRI_SPDIF_TX", "SEC_SPDIF_RX", "SEC_SPDIF_TX", "SLIM_9_RX", "SLIM_9_TX", "AFE_LOOPBACK_TX", "PRI_META_MI2S_RX", -"SEC_META_MI2S_RX", "PROXY_RX", "PROXY_TX", "HDMI_RX_MS" +"SEC_META_MI2S_RX", "PROXY_RX", "PROXY_TX", "HDMI_RX_MS","USB_AUDIO_TX" }; static SOC_ENUM_SINGLE_DECL(mm1_channel_mux, @@ -29705,6 +29705,8 @@ static const struct snd_soc_dapm_route intercon_tdm[] = { {"AUDIO_REF_EC_UL1 MUX", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_RX_0", "PRI_TDM_RX_0"}, {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"AUDIO_REF_EC_UL1 MUX", "SLIM_6_RX", "SLIM_6_RX"}, + {"AUDIO_REF_EC_UL1 MUX", "USB_AUDIO_RX", "USB_AUDIO_RX"}, {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"}, @@ -30381,7 +30383,8 @@ static const struct snd_soc_dapm_route intercon_mi2s[] = { {"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"}, + /* liuhaituo@MultiMedia 2018/4/18 add echo_test route */ + {"QUAT_MI2S_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"}, {"QUIN_MI2S_RX_DL_HL", "Switch", "QUIN_MI2S_DL_HL"}, {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_DL_HL"}, diff --git a/techpack/audio/asoc/sdm845.c b/techpack/audio/asoc/sdm845.c new file mode 100644 index 000000000000..fa28a29b6f83 --- /dev/null +++ b/techpack/audio/asoc/sdm845.c @@ -0,0 +1,8014 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wsa881x.h" + +#define DRV_NAME "sdm845-asoc-snd" + +#define __CHIPSET__ "SDM845 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define TDM_CHANNEL_MAX 8 + +#define MSM_HIFI_ON 1 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +#define TDM_MAX_SLOTS 8 +#define TDM_SLOT_WIDTH_BITS 32 + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + AUX_PCM_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +struct tdm_dev_config { + unsigned int tdm_slot_offset[TDM_MAX_SLOTS]; +}; + +enum { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +static atomic_t pinctrl_ref_count; + +enum { + AFE_LOOPBACK_TX_IDX = 0, + AFE_LOOPBACK_TX_IDX_MAX, +}; +struct msm_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; /* used by gpio driver API */ + int usbc_en2_gpio; /* used by gpio driver API */ + int us_euro_gpio_value; + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct snd_info_entry *codec_root; + struct msm_pinctrl_info pinctrl_info; + struct snd_soc_component *component; + struct work_struct adsp_power_up_work; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_component *component, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_component *component); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +static struct tdm_dev_config tdm_cfg[TDM_INTERFACE_MAX * 2] + [TDM_PORT_MAX] = { + { /* PRI TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* SEC TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* TERT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* QUAT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config afe_loopback_tx_cfg[] = { + [AFE_LOOPBACK_TX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; +static const char *const qos_text[] = {"Disable", "Enable"}; +static const char *const afe_loopback_tx_ch_text[] = {"One", "Two"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); +static SOC_ENUM_SINGLE_EXT_DECL(qos_vote, qos_text); +static SOC_ENUM_SINGLE_EXT_DECL(afe_loopback_tx_chs, afe_loopback_tx_ch_text); + +static struct platform_device *spdev; +static int msm_hifi_control; +static int qos_vote_status; + +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +int usb_sw_gpio = -1; +int hp_sw_gpio = -1; +int mbhc_sw_gpio = -1; +int ldo_sw_gpio = -1; +extern int smartpa_present; +extern bool fsa4480_enable; +extern bool audio_adapter_flag; +static void *def_tavil_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_component *component, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .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, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int afe_loopback_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: afe_loopback_tx_ch = %d\n", __func__, + afe_loopback_tx_cfg[0].channels); + ucontrol->value.enumerated.item[0] = + afe_loopback_tx_cfg[0].channels - 1; + + return 0; +} + +static int afe_loopback_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_tx_cfg[0].channels = + ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: afe_loopback_tx_ch = %d\n", __func__, + afe_loopback_tx_cfg[0].channels); + + return 1; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[SLIM_TX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n", + __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) { + idx = DP_RX_IDX; + } else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_slot_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int interface = ucontrol->value.integer.value[0]; + int channel = ucontrol->value.integer.value[1]; + unsigned int offset_val; + unsigned int *slot_offset; + + if (interface < 0 || interface >= (TDM_INTERFACE_MAX * 2)) { + pr_err("%s: incorrect interface = %d", __func__, interface); + return -EINVAL; + } + if (channel < 0 || channel >= TDM_PORT_MAX) { + pr_err("%s: incorrect channel = %d", __func__, channel); + return -EINVAL; + } + + pr_debug("%s: interface = %d, channel = %d", __func__, + interface, channel); + + slot_offset = tdm_cfg[interface][channel].tdm_slot_offset; + + /* Currenly can configure only two slots */ + offset_val = ucontrol->value.integer.value[2]; + /* Offset value can only be 0, 4, 8, ..28 */ + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[0] = offset_val; + pr_debug("%s: slot offset[0] = %d\n", __func__, slot_offset[0]); + + offset_val = ucontrol->value.integer.value[3]; + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[1] = offset_val; + pr_debug("%s: slot offset[1] = %d\n", __func__, slot_offset[1]); + + return 0; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_hifi_ctrl(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct snd_soc_card *card = component->card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(component); + + return 0; +} + +static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = qos_vote_status; + + return 0; +} + +static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct snd_soc_card *card = component->card; + const char *be_name = MSM_DAILINK_NAME(LowLatency); + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + s32 usecs; + + rtd = snd_soc_get_pcm_runtime(card, be_name); + if (!rtd) { + pr_err("%s: fail to get pcm runtime for %s\n", + __func__, be_name); + return -EINVAL; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s: substream is null\n", __func__); + return -EINVAL; + } + + qos_vote_status = ucontrol->value.enumerated.item[0]; + if (qos_vote_status) { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + if (!substream->runtime) { + pr_err("%s: runtime is null\n", __func__); + return -EINVAL; + } + usecs = MSM_LL_QOS_VALUE; + if (usecs >= 0) + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); + } else { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + } + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx, + msm_bt_sample_rate_rx_get, + msm_bt_sample_rate_rx_put), + SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx, + msm_bt_sample_rate_tx_get, + msm_bt_sample_rate_tx_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), + SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, + msm_qos_ctl_put), + SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, 4, + NULL, tdm_slot_map_put), + SOC_ENUM_EXT("AFE_LOOPBACK_TX Channels", afe_loopback_tx_chs, + afe_loopback_tx_ch_get, afe_loopback_tx_ch_put), + +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_component *component, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(component->name, "tavil_codec")) { + ret = tavil_cdc_mclk_enable(component, enable); + } else { + dev_err(component->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_component *component, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(component->name, "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(component, enable); + } else { + dev_err(component->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(component, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(component, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(component, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(component, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct snd_soc_card *card = component->card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + pr_err("%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + pr_debug("%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + 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_2 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), + SND_SOC_DAPM_MIC("Analog Mic5", 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) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx = 0; + void *config = NULL; + struct snd_soc_component *component = NULL; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + pr_debug("%s: dai_id= %d, format = %d, rate = %d\n", + __func__, dai_link->id, params_format(params), + params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) + component = snd_soc_rtdcom_lookup(rtd, "tavil_codec"); + + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(component, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[SLIM_TX_7].bit_format); + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_AFE_LOOPBACK_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + afe_loopback_tx_cfg[idx].bit_format); + rate->min = rate->max = afe_loopback_tx_cfg[idx].sample_rate; + channels->min = channels->max = + afe_loopback_tx_cfg[idx].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_usbc_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + int value = 0; + bool ret = 0; + struct snd_soc_card *card = component->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct pinctrl_state *en2_pinctrl_active; + struct pinctrl_state *en2_pinctrl_sleep; + + if (!pdata->usbc_en2_gpio_p) { + if (active) { + /* if active and usbc_en2_gpio undefined, get pin */ + pdata->usbc_en2_gpio_p = devm_pinctrl_get(card->dev); + if (IS_ERR_OR_NULL(pdata->usbc_en2_gpio_p)) { + dev_err(card->dev, + "%s: Can't get EN2 gpio pinctrl:%ld\n", + __func__, + PTR_ERR(pdata->usbc_en2_gpio_p)); + pdata->usbc_en2_gpio_p = NULL; + return false; + } + } else + /* if not active and usbc_en2_gpio undefined, return */ + return false; + } + + pdata->usbc_en2_gpio = of_get_named_gpio(card->dev->of_node, + "qcom,usbc-analog-en2-gpio", 0); + if (!gpio_is_valid(pdata->usbc_en2_gpio)) { + dev_err(card->dev, "%s, property %s not in node %s", + __func__, "qcom,usbc-analog-en2-gpio", + card->dev->of_node->full_name); + return false; + } + + en2_pinctrl_active = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_active"); + if (IS_ERR_OR_NULL(en2_pinctrl_active)) { + dev_err(card->dev, + "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_active)); + ret = false; + goto err_lookup_state; + } + + en2_pinctrl_sleep = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_sleep"); + if (IS_ERR_OR_NULL(en2_pinctrl_sleep)) { + dev_err(card->dev, + "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_sleep)); + ret = false; + goto err_lookup_state; + } + + /* if active and usbc_en2_gpio_p defined, swap using usbc_en2_gpio_p */ + if (active) { + dev_dbg(component->dev, "%s: enter\n", __func__); + if (pdata->usbc_en2_gpio_p) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + if (value) + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + else + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_active); + } else if (pdata->usbc_en2_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + gpio_set_value_cansleep(pdata->usbc_en2_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); + ret = true; + } else { + /* if not active, release usbc_en2_gpio_p pin */ + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + } + +err_lookup_state: + devm_pinctrl_put(pdata->usbc_en2_gpio_p); + pdata->usbc_en2_gpio_p = NULL; + return ret; +} + +extern void setHpSwGpioPin(int value); +static bool msm_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ +#if 0 + int value = 0; +#endif + int ret = 0; + struct snd_soc_card *card = component->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (!pdata) + return false; + + if (!wcd_mbhc_cfg.enable_usbc_analog) { + /* if usbc is not defined, swap using us_euro_gpio_p */ +#if 0 + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state( + pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep( + pdata->us_euro_gpio); + gpio_set_value_cansleep( + pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); +#else + if (pdata->us_euro_gpio_value == 0) { + pdata->us_euro_gpio_value = 1; + } else { + pdata->us_euro_gpio_value = 0; + } + setHpSwGpioPin(pdata->us_euro_gpio_value); + pr_err("%s set us_euro_gpio %d active = %d\n", __func__, + pdata->us_euro_gpio_value, active); +#endif + ret = true; + } else { + /* if usbc is defined, swap using usbc_en2 */ + ret = msm_usbc_swap_gnd_mic(component, active); + } + return ret; +} + +static int msm_afe_set_config(struct snd_soc_component *component) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(component->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(component, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(component->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(component, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(component->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(component, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(component->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_component *component, + struct snd_card *card) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + bool snd_card_online = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + + /* + * Sound card/ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); + ret = -ETIMEDOUT; + goto err; + } + + ret = msm_afe_set_config(component); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err: + return ret; +} + +static void msm_adsp_power_up_config_work(struct work_struct *work) +{ + struct msm_asoc_mach_data *pdata; + struct snd_soc_component *component; + struct snd_card *card; + + pdata = container_of(work, struct msm_asoc_mach_data, + adsp_power_up_work); + component = pdata->component; + card = component->card->snd_card; + msm_adsp_power_up_config(component, card); +} + +static int sdm845_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; + struct snd_soc_dai *codec_dai; + struct msm_asoc_mach_data *pdata; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + codec_dai = rtd->codec_dai; + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) + component = snd_soc_rtdcom_lookup(rtd, "tavil_codec"); + + pdata = snd_soc_card_get_drvdata(card); + pdata->component = component; + schedule_work(&pdata->adsp_power_up_work); + break; + default: + break; + } +err: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sdm845_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_component *component; + struct snd_soc_dapm_context *dapm; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, 149, + 150, 151}; + unsigned int tx_ch[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + component = snd_soc_rtdcom_lookup(rtd, "tavil_codec"); + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(component); + + ret = snd_soc_add_component_controls(component, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + + ret = msm_adsp_power_up_config(component, rtd->card->snd_card); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + + config_data = msm_codec_fn.get_afe_config_fn(component, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry( + &rtd->card->component_dev_list, + struct snd_soc_component, + card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(component, WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(component, + WCD934X_RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, component); + +done: + codec_reg_done = true; + return 0; + +err: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_tavil_mbhc_cal(void) +{ + void *tavil_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tavil_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) +#if 0 +/*wangdongdong@AudioDrv,2018-06-14,avoid JBL lock device not to enter standby*/ + S(v_hs_max, 1600); +#else + S(v_hs_max, 1700); +#endif +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + +#if 1 +/*wangdongdong@AudioDrv,20180113,modify for headset button*/ + btn_high[0] = 112; + btn_high[1] = 220; + btn_high[2] = 437; + btn_high[3] = 600; + btn_high[4] = 600; + btn_high[5] = 600; + btn_high[6] = 600; + btn_high[7] = 600; +#else + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; +#endif + + return tavil_wcd_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +err: + return ret; +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat_mi2s_disable"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat_mi2s_enable"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat_tdm_disable"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat_tdm_enable"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + atomic_set(&pinctrl_ref_count, 0); + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int slot_width = TDM_SLOT_WIDTH_BITS; + int channels, slots = TDM_MAX_SLOTS; + unsigned int slot_mask, rate, clk_freq; + unsigned int *slot_offset; + unsigned int path_dir = 0, interface = 0, channel_interface = 0; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + if (cpu_dai->id < AFE_PORT_ID_TDM_PORT_RANGE_START) { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + /* RX or TX */ + path_dir = cpu_dai->id % 2; + + /* PRI, SEC, TERT, QUAT, QUIN */ + interface = (cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) + / (2 * TDM_PORT_MAX); + + /* 0, 1, 2, .. 7 */ + channel_interface = + ((cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) / 2) + % TDM_PORT_MAX; + + pr_debug("%s: interface %u, channel interface %u\n", __func__, + interface, channel_interface); + + slot_offset = tdm_cfg[(interface * 2) + path_dir][channel_interface] + .tdm_slot_offset; + + if (path_dir) + channels = tdm_tx_cfg[interface][channel_interface].channels; + else + channels = tdm_rx_cfg[interface][channel_interface].channels; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + + pr_debug("%s: tdm rx slot_width %d slots %d slot_mask %x\n", + __func__, slot_width, slots, slot_mask); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm rx slot, err:%d\n", + __func__, ret); + goto end; + } + + pr_debug("%s: tdm rx slot_offset[0]: %d, slot_offset[1]: %d\n", + __func__, slot_offset[0], slot_offset[1]); + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm tx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static int sdm845_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + if (atomic_read(&pinctrl_ref_count) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + atomic_inc(&pinctrl_ref_count); + } + + return ret; +} + +static void sdm845_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + atomic_dec(&pinctrl_ref_count); + if (atomic_read(&pinctrl_ref_count) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + } +} + +static struct snd_soc_ops sdm845_tdm_be_ops = { + .hw_params = sdm845_tdm_snd_hw_params, + .startup = sdm845_tdm_snd_startup, + .shutdown = sdm845_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index > QUAT_MI2S) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index > QUAT_MI2S) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); + +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary MI2S_TX Hostless", + .stream_name = "Quaternary MI2S_TX Hostless", + .cpu_dai_name = "QUAT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MultiMedia21 Playback", + .stream_name = "MultiMedia21", + .cpu_dai_name = "MultiMedia21", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA21, + }, + { + .name = "MultiMedia22 Playback", + .stream_name = "MultiMedia22", + .cpu_dai_name = "MultiMedia22", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA22, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Proxy Tx BACK END DAI Link */ + { + .name = LPASS_BE_PROXY_TX, + .stream_name = "Proxy Capture", + .cpu_dai_name = "msm-dai-q6-dev.8195", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PROXY_TX, + .ignore_suspend = 1, + }, + /* Proxy Rx BACK END DAI Link */ + { + .name = LPASS_BE_PROXY_RX, + .stream_name = "Proxy Playback", + .cpu_dai_name = "msm-dai-q6-dev.8194", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PROXY_RX, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_1, + .stream_name = "Quaternary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36914", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .dynamic_be = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +#if 1 +// suzhiguang,different projects use different smartpa,2018-04-10 +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .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", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links_max[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "max98927", //"msm-stub-codec.1", + .codec_dai_name = "max98927-aif1", //"msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "max98927", //"msm-stub-codec.1", + .codec_dai_name = "max98927-aif1", //"msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link_component tfa98xx_dai_link_component[]= +{ + { + .name= "tfa98xx.2-0034", + .dai_name="tfa98xx-aif-2-34", + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links_tfa[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codecs = tfa98xx_dai_link_component, + .num_codecs = ARRAY_SIZE(tfa98xx_dai_link_component), + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codecs = tfa98xx_dai_link_component, + .num_codecs = ARRAY_SIZE(tfa98xx_dai_link_component), + .dynamic_be = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; +#endif + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_afe_rxtx_lb_be_dai_link[] = { + { + .name = LPASS_BE_AFE_LOOPBACK_TX, + .stream_name = "AFE Loopback Capture", + .cpu_dai_name = "msm-dai-q6-dev.24577", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_LOOPBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_snd_card_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links) + + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link)]; + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + + component = snd_soc_rtdcom_lookup(rtd, "tavil_codec"); + if (!component) { + dev_err(card->dev, + "%s: component is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + mbhc_calibration = def_tavil_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(component, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_free_mbhc_cal; + } + return 0; + +err_free_mbhc_cal: + kfree(mbhc_calibration); +err: + return ret; +} + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "sdm845-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + +#if 0 + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TAVIL_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +#else + pdata->us_euro_gpio_value = 1; + setHpSwGpioPin(pdata->us_euro_gpio_value); + + return ret; +#endif +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, "msm-stub-codec"); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return -EINVAL; + } + + ret = snd_soc_add_component_controls(component, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: add_codec_controls failed, err = %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "sdm845-stub-snd-card", +}; + +static const struct of_device_id sdm845_asoc_machine_of_match[] = { + { .compatible = "qcom,sdm845-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,sdm845-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +#if 1 +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +static int cc_audio_adapter_detect_callback(struct notifier_block *nb, + unsigned long value, void *data) +{ + + if (value == 1) { + pr_err("%s:audio_adapter attached!\n", __func__); + + if (gpio_is_valid(ldo_sw_gpio)) { + gpio_set_value_cansleep(ldo_sw_gpio, 1); + pr_err("ldo_sw_gpio set to 1\n"); + } else { + pr_err("ldo_sw_gpio gpio_is_valid failed\n"); + } + msleep_interruptible(20); + + if (gpio_is_valid(usb_sw_gpio)) { + gpio_set_value_cansleep(usb_sw_gpio, 1); + pr_err("usb_sw_gpio set to 1\n"); + } else { + pr_err("usb_sw_gpio gpio_is_valid failed\n"); + } + + if (gpio_is_valid(mbhc_sw_gpio)) { + gpio_set_value_cansleep(mbhc_sw_gpio, 0); + pr_err("mbhc_sw_gpio set to 0\n"); + } else { + pr_err("mbhc_sw_gpio gpio_is_valid failed\n"); + } + + } else if (value == 0) { + pr_err("%s:audio_adapter removal!\n", __func__); + + if (gpio_is_valid(mbhc_sw_gpio)) { + gpio_set_value_cansleep(mbhc_sw_gpio, 1); + pr_err("mbhc_sw_gpio set to 1\n"); + } else { + pr_err("mbhc_sw_gpio gpio_is_valid failed\n"); + } + + if (gpio_is_valid(usb_sw_gpio)) { + gpio_set_value_cansleep(usb_sw_gpio, 0); + } else { + pr_err("usb_sw_gpio gpio_is_valid failed\n"); + } + + if (gpio_is_valid(ldo_sw_gpio)) { + gpio_set_value_cansleep(ldo_sw_gpio, 0); + pr_err("ldo_sw_gpio set to 0\n"); + } else { + pr_err("ldo_sw_gpio gpio_is_valid failed\n"); + } + + } else + pr_err("%s:audio adapter value = %lu\n", __func__, value); + + return NOTIFY_OK; +} + +static struct notifier_block typec_cc_notifier = { + .notifier_call = cc_audio_adapter_detect_callback, +}; +#endif + +// suzhiguang,for usb sw/hp sw control +void setUsbSwGpioPin(int value) +{ + if (gpio_is_valid(usb_sw_gpio)) { + gpio_set_value_cansleep(usb_sw_gpio, value); + pr_err("%s usb_sw_gpio set to %d\n", __func__, value); + } else { + pr_err("%s usb_sw_gpio gpio_is_valid failed\n", __func__); + } +} +EXPORT_SYMBOL(setUsbSwGpioPin); + +void setHpSwGpioPin(int value) +{ + if (gpio_is_valid(hp_sw_gpio)) { + gpio_set_value_cansleep(hp_sw_gpio, value); + pr_err("%s hp_sw_gpio set to %d\n", __func__, value); + } else { + pr_err("%s hp_sw_gpio gpio_is_valid failed\n", __func__); + } +} +EXPORT_SYMBOL(setHpSwGpioPin); + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + int total_links; + int rc = 0; + u32 val = 0; + const struct of_device_id *match; + const char *smartpa_type; + + match = of_match_node(sdm845_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tavil_codec")) { + card = &snd_soc_card_tavil_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_snd_card_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_4, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_tavil_snd_card_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } +#if 0 + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } +#else + // suzhiguang,config smartpa dailink + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + rc = of_property_read_string(dev->of_node, + "op,smartpa", &smartpa_type); + if (rc) { + pr_err("read smartpa type error\n"); + } else { + pr_err("smartpa dts config is %s\n", smartpa_type); + if (smartpa_present) { + pr_err("smartpa_present\n"); + if (!strcmp(smartpa_type, "max98927")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links_max, + sizeof(msm_mi2s_be_dai_links_max)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links_max); + } else if (!strcmp(smartpa_type, "tfa98xx")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links_tfa, + sizeof(msm_mi2s_be_dai_links_tfa)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links_tfa); + } else { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + pr_err("no smartpa dts config match,use default\n"); + } + } else { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + pr_err("no smartpa found,use default\n"); + } + } + } +#endif + + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + + rc = of_property_read_u32(dev->of_node, "qcom,afe-rxtx-lb", + &val); + if (!rc && val) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_afe_rxtx_lb_be_dai_link, + sizeof(msm_afe_rxtx_lb_be_dai_link)); + total_links += + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link); + } + dailink = msm_tavil_snd_card_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + len_1 = ARRAY_SIZE(msm_stub_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links); + + memcpy(msm_stub_dai_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + memcpy(msm_stub_dai_links + len_1, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + + dailink = msm_stub_dai_links; + total_links = len_2; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!component) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_component_get_dapm(component); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(component->dev, "%s: setting left ch map to codec %s\n", + __func__, component->name); + wsa881x_set_channel_map(component, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(component->dev, "%s: setting right ch map to codec %s\n", + __func__, component->name); + wsa881x_set_channel_map(component, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(component->dev, "%s: wrong codec name %s\n", __func__, + component->name); + ret = -EINVAL; + goto err; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + component); + +err: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + card->num_aux_devs = 0; + return 0; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + card->num_aux_devs = 0; + return 0; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_free_dev_info; + } + if (soc_find_component_locked(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_free_dev_info; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_free_aux_dev; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_free_cdc_conf; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_free_dev_name_str; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err: + return ret; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + char *mclk_freq_prop_name; + const struct of_device_id *match; + int ret; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + // suzhiguang,add for parse dt. + struct device_node *np; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + np = pdev->dev.of_node; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(sdm845_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: no matched codec is found.\n", + __func__); + goto err; + } + + mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq"; + + ret = of_property_read_u32(pdev->dev.of_node, + mclk_freq_prop_name, &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "Looking up %s property in node %s failed, err%d\n", + mclk_freq_prop_name, + pdev->dev.of_node->full_name, ret); + goto err; + } + + if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) { + dev_err(&pdev->dev, "unsupported mclk freq %u\n", + pdata->mclk_freq); + ret = -EINVAL; + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + INIT_WORK(&pdata->adsp_power_up_work, msm_adsp_power_up_config_work); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); + } else { + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!pdata->hph_en1_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", + pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!pdata->hph_en0_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", + pdev->dev.of_node->full_name); + } + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "Unknown value, set to default"); + } + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + if (of_find_property(pdev->dev.of_node, usb_c_dt, NULL)) + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + +#if 1 + /*2018/06/14 @bsp add for support notify audio adapter switch*/ + pr_err("%s: fsa4480_enable is %s\n", __func__, fsa4480_enable ? "true": "false"); + if (of_property_read_bool(np, "op,usb_sw") && !fsa4480_enable) { + pr_err("%s usb_sw find\n", __func__); + if (audio_adapter_flag) + pr_err("%s audio_adapter_flag = %d\n",__func__,audio_adapter_flag); + + mbhc_sw_gpio = of_get_named_gpio(np, "mbhc_sw", 0); + if (mbhc_sw_gpio < 0){ + pr_err("mbhc_sw_gpio of get gpio failed\n"); + } else { + gpio_free(mbhc_sw_gpio); + ret = devm_gpio_request_one(&pdev->dev, mbhc_sw_gpio, + GPIOF_OUT_INIT_HIGH, "mbhc sw gpio"); + if (ret) { + pr_err("%s devm_gpio_request_one mbhc_sw_gpio failed\n",__func__); + } + } + + usb_sw_gpio = of_get_named_gpio(np, "usb_sw", 0); + if (usb_sw_gpio < 0){ + pr_err("usb_sw_gpio of get gpio failed\n"); + } else { + gpio_free(usb_sw_gpio); + ret = devm_gpio_request_one(&pdev->dev, usb_sw_gpio, + GPIOF_OUT_INIT_LOW, "usb sw gpio"); + if (ret) { + pr_err("%s devm_gpio_request_one usb sw gpio failed\n",__func__); + } + } + + hp_sw_gpio = of_get_named_gpio(np, "hp_sw", 0); + if (hp_sw_gpio < 0){ + pr_err("hp_sw_gpio of get gpio failed\n"); + } else { + pr_err("hp_sw_gpio of get gpio success\n"); + gpio_free(hp_sw_gpio); + ret = devm_gpio_request_one(&pdev->dev, hp_sw_gpio, + GPIOF_OUT_INIT_LOW, "hp sw gpio"); + if (ret) { + pr_err("%s devm_gpio_request_one hp_sw_gpio failed\n",__func__); + } + } + + ldo_sw_gpio = of_get_named_gpio(np, "ldo_sw", 0); + if (ldo_sw_gpio < 0){ + pr_err("ldo_sw_gpio of get gpio failed\n"); + } else { + pr_err("ldo_sw_gpio of get gpio success\n"); + gpio_free(ldo_sw_gpio); + ret = devm_gpio_request_one(&pdev->dev, ldo_sw_gpio, + GPIOF_OUT_INIT_LOW, "ldo sw gpio"); + if (ret) { + pr_err("%s devm_gpio_request_one ldo_sw_gpio failed\n",__func__); + } + } + + register_cc_notifier_client(&typec_cc_notifier); + if (audio_adapter_flag) { + pr_err("%s audio_adapter_flag init to handle usbtypeC headset\n",__func__); + cc_audio_adapter_detect_callback(NULL, 1, NULL); + } + } +#endif + + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + + is_initial_boot = true; + ret = audio_notifier_register("sdm845", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + msm_release_pinctrl(pdev); + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + audio_notifier_deregister("sdm845"); + if (pdata->us_euro_gpio > 0) { + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + msm_i2s_auxpcm_deinit(); + + msm_release_pinctrl(pdev); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver sdm845_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdm845_asoc_machine_of_match, + .suppress_bind_attrs = true, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sdm845_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdm845_asoc_machine_of_match); diff --git a/techpack/audio/config/sdm845auto.conf b/techpack/audio/config/sdm845auto.conf new file mode 100644 index 000000000000..ef914eb6fd1f --- /dev/null +++ b/techpack/audio/config/sdm845auto.conf @@ -0,0 +1,41 @@ +export CONFIG_PINCTRL_WCD=y +export CONFIG_SND_SOC_WCD934X=y +export CONFIG_SND_SOC_WCD9XXX_V2=y +export CONFIG_SND_SOC_WCD_CPE=y +export CONFIG_SND_SOC_WCD_MBHC=y +export CONFIG_SND_SOC_WSA881X=y +export CONFIG_SND_SOC_WCD_SPI=y +export CONFIG_SND_SOC_WCD934X_MBHC=y +export CONFIG_SND_SOC_WCD934X_DSD=y +export CONFIG_MSM_QDSP6V2_CODECS=y +export CONFIG_MSM_ULTRASOUND=y +export CONFIG_MSM_QDSP6_APRV2_RPMSG=y +export CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +export CONFIG_MSM_ADSP_LOADER=y +export CONFIG_REGMAP_SWR=y +export CONFIG_MSM_QDSP6_SSR=y +export CONFIG_MSM_QDSP6_PDR=y +export CONFIG_MSM_QDSP6_NOTIFIER=y +export CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +export CONFIG_SND_SOC_SDM845=y +export CONFIG_MSM_GLINK_SPI_XPRT=y +export CONFIG_SOUNDWIRE=y +export CONFIG_SOUNDWIRE_WCD_CTRL=y +export CONFIG_SND_SOC_WCD_MBHC_ADC=y +export CONFIG_SND_SOC_QDSP6V2=y +export CONFIG_MSM_CDC_PINCTRL=y +export CONFIG_QTI_PP=y +export CONFIG_SND_HWDEP_ROUTING=y +export CONFIG_DTS_EAGLE=y +export CONFIG_DOLBY_DS2=y +export CONFIG_DOLBY_LICENSE=y +export CONFIG_DTS_SRS_TM=y +export CONFIG_WCD9XXX_CODEC_CORE=y +export CONFIG_SND_SOC_MSM_STUB=y +export CONFIG_WCD_DSP_GLINK=y +export CONFIG_MSM_AVTIMER=y +export CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y +export CONFIG_WCD_SPI_DMA_MASKING=y +export CONFIG_SND_SOC_FSA4840=y +export CONFIG_SND_SOC_MAX98927=y +export CONFIG_SND_SOC_TFA98XX=y diff --git a/techpack/audio/config/sdm845autoconf.h b/techpack/audio/config/sdm845autoconf.h new file mode 100644 index 000000000000..831cd20aeedb --- /dev/null +++ b/techpack/audio/config/sdm845autoconf.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_RPMSG 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_SDM845 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_WCD_SPI_DMA_MASKING 1 +#define CONFIG_SND_SOC_FSA4840 1 +#define CONFIG_SND_SOC_MAX98927 1 +#define CONFIG_SND_SOC_TFA98XX 1 diff --git a/techpack/audio/dsp/Android.mk b/techpack/audio/dsp/Android.mk index 699b1c91b5cb..29ce598f86d5 100644 --- a/techpack/audio/dsp/Android.mk +++ b/techpack/audio/dsp/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform-in-list,msmnile sdmshrike),true) ifeq ($(TARGET_BOARD_AUTO),true) AUDIO_SELECT := CONFIG_SND_SOC_SA8155=m @@ -41,7 +45,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/dsp/Kbuild b/techpack/audio/dsp/Kbuild index aaa293697ad1..eef3189b42c5 100644 --- a/techpack/audio/dsp/Kbuild +++ b/techpack/audio/dsp/Kbuild @@ -14,6 +14,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM6150), y) ifdef CONFIG_SND_SOC_SA6155 include $(AUDIO_ROOT)/config/sa6155auto.conf diff --git a/techpack/audio/dsp/codecs/Android.mk b/techpack/audio/dsp/codecs/Android.mk index ceb1c8f2490b..0833b840c4dc 100644 --- a/techpack/audio/dsp/codecs/Android.mk +++ b/techpack/audio/dsp/codecs/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform-in-list,msmnile sdmshrike),true) AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m endif @@ -33,7 +37,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/dsp/codecs/Kbuild b/techpack/audio/dsp/codecs/Kbuild index 89aeab7fdcd3..3db3e7c152a3 100644 --- a/techpack/audio/dsp/codecs/Kbuild +++ b/techpack/audio/dsp/codecs/Kbuild @@ -14,6 +14,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM6150), y) include $(AUDIO_ROOT)/config/sm6150auto.conf export diff --git a/techpack/audio/dsp/q6afe.c b/techpack/audio/dsp/q6afe.c index 490f1708d372..9afe132e3f9d 100644 --- a/techpack/audio/dsp/q6afe.c +++ b/techpack/audio/dsp/q6afe.c @@ -252,6 +252,9 @@ struct afe_ctl { uint32_t cps_ch_mask; struct afe_cps_hw_intf_cfg *cps_config; int lsm_afe_ports[MAX_LSM_SESSIONS]; +#ifdef CONFIG_SND_SOC_MAX98927 + uint8_t *dsm_payload; +#endif }; struct afe_clkinfo_per_port { @@ -915,6 +918,33 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, atomic_set(&this_afe.state, -1); } +#ifdef CONFIG_SND_SOC_MAX98927 + if ( + param_hdr.param_id == AFE_PARAM_ID_DSM_CFG || + param_hdr.param_id == AFE_PARAM_ID_DSM_INFO || + param_hdr.param_id == AFE_PARAM_ID_CALIB){ + struct afe_dsm_get_resp *dsm_resp = + (struct afe_dsm_get_resp *) payload; + + if (payload_size < sizeof(*dsm_resp)) { + pr_err("%s: Error: received size %d, afe_dsm_get_resp size %zu\n", + __func__, payload_size, sizeof(*dsm_resp)); + return -EINVAL; + } + + if (this_afe.dsm_payload) + memcpy(this_afe.dsm_payload, dsm_resp->payload, + payload_size - sizeof(*dsm_resp)); + + if (!dsm_resp->status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: dsm resp status: %d", __func__, dsm_resp->status); + atomic_set(&this_afe.state, -1); + } + } +#endif + return 0; } @@ -2402,6 +2432,200 @@ static int afe_send_cps_config(int src_port) return ret; } +#ifdef CONFIG_SND_SOC_MAX98927 +int afe_dsm_setget_params(uint8_t *payload, int size, int dir, uint32_t dst_port, uint32_t mod_id, uint32_t param_id) +{ + struct afe_dsm_set_command *set = NULL; + struct afe_dsm_get_command *get = NULL; + uint32_t *config = NULL; + int index = 0, ret = -EINVAL; + + if (!payload || size <= 0) { + pr_err("%s: Invalid params\n", __func__); + goto fail_cmd; + } + ret = q6audio_validate_port(dst_port); + if (ret < 0) { + pr_err("%s: Invalid src port 0x%x ret %d", + __func__, dst_port, ret); + ret = -EINVAL; + goto fail_cmd; + } + + index = q6audio_get_port_index(dst_port); + if (index < 0 || index > AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + + if (dir){ + get = (struct afe_dsm_get_command *) (payload - sizeof(*get)); + + memset(get, 0 , sizeof(*get)); + + get->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + get->hdr.pkt_size = size + sizeof(*get); + get->hdr.token = index; + get->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + get->param.port_id = q6audio_get_port_id(dst_port); + get->param.payload_size = size + sizeof(struct afe_port_param_data_v2); + get->param.module_id = mod_id; + get->param.param_id = param_id; + get->pdata.module_id = mod_id; + get->pdata.param_id = param_id; + get->pdata.param_size = size; + + this_afe.dsm_payload = payload; + + config = (uint32_t *)get; + } + else{ + set = (struct afe_dsm_set_command *) (payload - sizeof(*set)); + + memset(set, 0 , sizeof(*set)); + + set->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + set->hdr.pkt_size = size + sizeof(*set); + set->hdr.token = index; + set->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + set->param.port_id = q6audio_get_port_id(dst_port); + set->param.payload_size = size + sizeof(struct afe_port_param_data_v2); + set->pdata.module_id = mod_id; + set->pdata.param_id = param_id; + set->pdata.param_size = size; + + config = (uint32_t *)set; + } + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, config); + if (ret < 0) { + pr_err("%s: port = 0x%x failed %d\n", __func__, dst_port, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + + ret = 0; + +fail_cmd: + pr_debug("%s: status %d 0x%x\n",__func__, ret, dst_port); + + this_afe.dsm_payload = NULL; + + return ret; +} +// below export to 98928 driver +int afe_dsm_ramp_dn_cfg(uint8_t *payload, int delay_in_ms) +{ + uint32_t *params = (uint32_t *)payload; + *(params) = 0; + *(params + 1) = 3; //three command will be sent + *(params + 2) = 0x03000063; // fade out time + *(params + 3) = 50; //5; // 5ms suzhiguang,change to 50 to avoid pop + *(params + 4) = 0x03000064; // mute time + *(params + 5) = 200; // 5s mute time will make sure silence output till PA software shutdown. + *(params + 6) = 0x03000066; // start fade + *(params + 7) = 1; + + afe_dsm_setget_params(payload, 8*sizeof(uint32_t), 0, DSM_RX_PORT_ID, AFE_MODULE_DSM_RX, AFE_PARAM_ID_DSM_CFG); + + usleep_range(delay_in_ms*1000, delay_in_ms*1000 + 10); + return 0; +} + +int afe_dsm_rx_set_params(uint8_t *payload, int size) +{ + return afe_dsm_setget_params(payload, size, 0, DSM_RX_PORT_ID, AFE_MODULE_DSM_RX, AFE_PARAM_ID_DSM_CFG); +} + +int afe_dsm_rx_get_params(uint8_t *payload, int size) +{ + return afe_dsm_setget_params(payload, size, 1, DSM_RX_PORT_ID, AFE_MODULE_DSM_RX, AFE_PARAM_ID_DSM_CFG); +} + +int afe_dsm_set_calib(uint8_t* payload) +{ + return afe_dsm_setget_params(payload, sizeof(uint32_t)*3, 0, DSM_TX_PORT_ID, AFE_MODULE_DSM_TX, AFE_PARAM_ID_CALIB); +} + +int afe_dsm_pre_calib(uint8_t* payload) +{ + uint32_t *params = (uint32_t *)payload; + *(params) = 0; + *(params + 1) = 1; //count + *(params + 2) = 0x03000001; // enable flag + *(params + 3) = 4; // mode 0: disable, 1: enable, 2: bypass and pilot tone, 4: pilot tone only + afe_dsm_setget_params(payload, 4*sizeof(uint32_t), 0, DSM_RX_PORT_ID, AFE_MODULE_DSM_RX, AFE_PARAM_ID_DSM_CFG); + usleep_range(1000*1000, 1000*1000 + 10); //make the stable iv data + return 0; +} + +int afe_dsm_post_calib(uint8_t* payload) +{ + uint32_t *params = (uint32_t *)payload; + *(params) = 0; + *(params + 1) = 1; //count + *(params + 2) = 0x03000001; // enable flag + *(params + 3) = 1; // mode 0: disable, 1: enable, 2: bypass and pilot tone, 4: pilot tone only + return afe_dsm_setget_params(payload, 4*sizeof(uint32_t), 0, DSM_RX_PORT_ID, AFE_MODULE_DSM_RX, AFE_PARAM_ID_DSM_CFG); +} + +int afe_dsm_get_calib(uint8_t* payload){ + return afe_dsm_setget_params(payload, sizeof(uint32_t)*14, 1, DSM_TX_PORT_ID, AFE_MODULE_DSM_TX, AFE_PARAM_ID_CALIB); +} + +int afe_dsm_get_average_calib(uint8_t* payload) +{ + uint32_t *params = (uint32_t *)payload; + uint64_t sum_rdc[2] ={0, 0}; + int i, rc = 0; + for (i = 0; i < 4; i++) { + rc = afe_dsm_setget_params(payload, sizeof(uint32_t)*14, 1, DSM_TX_PORT_ID, AFE_MODULE_DSM_TX, AFE_PARAM_ID_CALIB); + if (rc != 0) { + sum_rdc[0] = 0; + sum_rdc[1] = 0; + goto failed; + } + sum_rdc[0] += params[0]; + sum_rdc[1] += params[1]; + usleep_range(50 * 1000, 50 * 1000 + 10); + } + +failed: + params[0] = (sum_rdc[0] >> 2); + params[1] = (sum_rdc[1] >> 2); + return rc; +} + +int afe_dsm_get_libary_info(uint32_t* payload, int size) +{ + return afe_dsm_setget_params((int8_t*) payload, 4 * 100, 1, + DSM_TX_PORT_ID, AFE_MODULE_DSM_TX, + AFE_PARAM_ID_DSM_INFO); +} + +#endif + static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, union afe_spkr_prot_config *prot_config, uint32_t param_size) { diff --git a/techpack/audio/dsp/q6voice.c b/techpack/audio/dsp/q6voice.c index 9022251481a9..71fa562afdde 100644 --- a/techpack/audio/dsp/q6voice.c +++ b/techpack/audio/dsp/q6voice.c @@ -4680,7 +4680,8 @@ static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v) { int ret = 0; - if (common.cvp_version < CVP_VERSION_2) + if (common.cvp_version < CVP_VERSION_2 || + common.is_legacy_dsp_v2_firmware) ret = voice_send_cvp_device_channels_cmd(v); else ret = voice_send_cvp_channel_info_cmd(v); @@ -10131,6 +10132,10 @@ int __init voice_init(void) */ common.rec_channel_count = NUM_CHANNELS_MONO; + /* Support legacy DSP firmware with CVPv2.0+CVD2.3 */ + common.is_legacy_dsp_v2_firmware = + of_machine_is_compatible("qcom,sdm845"); + mutex_init(&common.common_lock); common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL); diff --git a/techpack/audio/include/asoc/wcd-mbhc-v2.h b/techpack/audio/include/asoc/wcd-mbhc-v2.h index 340d68cee76e..da972eb7ffed 100644 --- a/techpack/audio/include/asoc/wcd-mbhc-v2.h +++ b/techpack/audio/include/asoc/wcd-mbhc-v2.h @@ -416,6 +416,15 @@ enum mbhc_moisture_rref { R_184_KOHM, }; +struct usbc_ana_audio_config { + int usbc_en1_gpio; + int usbc_en2_gpio; + int usbc_force_gpio; + struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */ + struct device_node *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *usbc_force_gpio_p; /* used by pinctrl API */ +}; + struct wcd_mbhc_config { bool read_fw_bin; void *calibration; @@ -431,7 +440,9 @@ struct wcd_mbhc_config { int anc_micbias; bool enable_anc_mic_detect; u32 enable_usbc_analog; + bool usbc_analog_legacy; bool moisture_duty_cycle_en; + struct usbc_ana_audio_config usbc_analog_cfg; }; struct wcd_mbhc_intr { @@ -612,6 +623,12 @@ struct wcd_mbhc { unsigned long intr_status; bool is_hph_ocp_pending; + bool usbc_force_pr_mode; + int usbc_mode; + struct notifier_block psy_nb; + struct power_supply *usb_psy; + struct work_struct usbc_analog_work; + struct wcd_mbhc_fn *mbhc_fn; bool force_linein; struct device_node *fsa_np; diff --git a/techpack/audio/include/asoc/wcd9xxx-slimslave.h b/techpack/audio/include/asoc/wcd9xxx-slimslave.h index 70b9a4c92bbd..5f9d43acd635 100644 --- a/techpack/audio/include/asoc/wcd9xxx-slimslave.h +++ b/techpack/audio/include/asoc/wcd9xxx-slimslave.h @@ -7,6 +7,8 @@ #include #include "core.h" +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +#include /* @@ -108,4 +110,7 @@ int wcd9xxx_rx_vport_validation(u32 port_id, int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id, struct wcd9xxx_codec_dai_data *codec_dai, u32 num_codec_dais); +/*2018/06/14 @bsp add for support notify audio adapter switch*/ +extern int register_cc_notifier_client(struct notifier_block *nb); +extern int unregister_cc_notifier_client(struct notifier_block *nb); #endif /* __WCD9XXX_SLIMSLAVE_H_ */ diff --git a/techpack/audio/include/dsp/apr_audio-v2.h b/techpack/audio/include/dsp/apr_audio-v2.h index ea72c8148471..fdbd28b0cfa6 100644 --- a/techpack/audio/include/dsp/apr_audio-v2.h +++ b/techpack/audio/include/dsp/apr_audio-v2.h @@ -2047,6 +2047,30 @@ struct afe_port_cmd_set_param_v2 { u8 param_data[0]; } __packed; +struct afe_port_param_data_v2 { + u32 module_id; +/* ID of the module to be configured. + * Supported values: Valid module ID + */ + +u32 param_id; +/* ID of the parameter corresponding to the supported parameters + * for the module ID. + * Supported values: Valid parameter ID + */ + +u16 param_size; +/* Actual size of the data for the + * module_id/param_id pair. The size is a + * multiple of four bytes. + * Supported values: > 0 + */ + +u16 reserved; +/* This field must be set to zero. + */ +} __packed; + #define AFE_PORT_CMD_SET_PARAM_V3 0x000100FA struct afe_port_cmd_set_param_v3 { /* APR Header */ @@ -11484,6 +11508,45 @@ union afe_spkr_prot_config { struct afe_sp_v4_param_vi_channel_map_cfg v4_ch_map_cfg; } __packed; +#ifdef CONFIG_SND_SOC_MAX98927 +/*Maxim DSM module and parameters IDs*/ +#define AFE_RX_TOPOLOGY_ID_DSM 0x10001061 +#define AFE_TX_TOPOLOGY_ID_DSM 0x10001060 +#define AFE_MODULE_DSM_TX 0x10001068 +#define AFE_MODULE_DSM_RX 0x10001062 +#define AFE_PARAM_ID_DSM_ENABLE 0x10001063 +#define AFE_PARAM_ID_CALIB 0x10001065 +#define AFE_PARAM_ID_DSM_CFG 0x10001066 +#define AFE_PARAM_ID_DSM_INFO 0x10001067 + +#define DSM_RX_PORT_ID AFE_PORT_ID_QUATERNARY_MI2S_RX +#define DSM_TX_PORT_ID AFE_PORT_ID_QUATERNARY_MI2S_TX + +struct afe_dsm_param_array { + uint32_t count; + uint32_t flagToWrite; + uint32_t Reserve[2]; + uint32_t params[100]; +}; + +struct afe_dsm_set_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; +} __packed; + +struct afe_dsm_get_command { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 param; + struct afe_port_param_data_v2 pdata; +} __packed; + +struct afe_dsm_get_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + uint32_t payload[0]; +} __packed; +#endif /* SRS TRUMEDIA start */ /* topology */ diff --git a/techpack/audio/include/dsp/q6voice.h b/techpack/audio/include/dsp/q6voice.h index 35ace4eed388..ea72010233b8 100644 --- a/techpack/audio/include/dsp/q6voice.h +++ b/techpack/audio/include/dsp/q6voice.h @@ -1991,6 +1991,7 @@ struct common_data { bool is_destroy_cvd; char cvd_version[CVD_VERSION_STRING_MAX_SIZE]; int cvp_version; + bool is_legacy_dsp_v2_firmware; bool is_avcs_version_queried; bool is_per_vocoder_cal_enabled; bool is_sound_focus_resp_success; diff --git a/techpack/audio/ipc/Android.mk b/techpack/audio/ipc/Android.mk index 6364ed7d8102..96a8b1d0d5c2 100644 --- a/techpack/audio/ipc/Android.mk +++ b/techpack/audio/ipc/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform-in-list,msmnile sdmshrike),true) ifeq ($(TARGET_BOARD_AUTO),true) AUDIO_SELECT := CONFIG_SND_SOC_SA8155=m @@ -41,7 +45,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/ipc/Kbuild b/techpack/audio/ipc/Kbuild index 14c53d97279b..96ced6d770b1 100644 --- a/techpack/audio/ipc/Kbuild +++ b/techpack/audio/ipc/Kbuild @@ -15,6 +15,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM6150), y) ifdef CONFIG_SND_SOC_SA6155 include $(AUDIO_ROOT)/config/sa6155auto.conf diff --git a/techpack/audio/soc/Android.mk b/techpack/audio/soc/Android.mk index 858be88c455b..4eade15f6b7d 100644 --- a/techpack/audio/soc/Android.mk +++ b/techpack/audio/soc/Android.mk @@ -3,6 +3,10 @@ # Assume no targets will be supported # Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + ifeq ($(call is-board-platform-in-list,msmnile sdmshrike),true) ifeq ($(TARGET_BOARD_AUTO),true) AUDIO_SELECT := CONFIG_SND_SOC_SA8155=m @@ -41,7 +45,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937),true) +ifeq ($(call is-board-platform-in-list,msmnile $(MSMSTEPPE) $(TRINKET) kona lito bengal sdmshrike sdm660 msm8953 msm8937 sdm845),true) LOCAL_PATH := $(call my-dir) diff --git a/techpack/audio/soc/Kbuild b/techpack/audio/soc/Kbuild index 9beb8e839cc3..cb168336dafc 100644 --- a/techpack/audio/soc/Kbuild +++ b/techpack/audio/soc/Kbuild @@ -14,6 +14,10 @@ ifeq ($(KERNEL_BUILD), 1) endif ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif ifeq ($(CONFIG_ARCH_SM8150), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/techpack/display/Makefile b/techpack/display/Makefile index b2829628ff13..ce00931b0903 100644 --- a/techpack/display/Makefile +++ b/techpack/display/Makefile @@ -25,6 +25,14 @@ ifeq ($(CONFIG_ARCH_BENGAL), y) LINUXINCLUDE += -include $(srctree)/techpack/display/config/bengaldispconf.h endif +ifeq ($(CONFIG_ARCH_SDM845), y) +include $(srctree)/techpack/display/config/konadisp.conf +endif + +ifeq ($(CONFIG_ARCH_SDM845), y) +LINUXINCLUDE += -include $(srctree)/techpack/display/config/konadispconf.h +endif + obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/ obj-$(CONFIG_QCOM_MDSS_PLL) += pll/ diff --git a/techpack/display/msm/dsi/dsi_ctrl_hw_2_2.c b/techpack/display/msm/dsi/dsi_ctrl_hw_2_2.c index 56821e857a91..864094315531 100644 --- a/techpack/display/msm/dsi/dsi_ctrl_hw_2_2.c +++ b/techpack/display/msm/dsi/dsi_ctrl_hw_2_2.c @@ -8,7 +8,8 @@ #include "dsi_hw.h" #include "dsi_catalog.h" -#define DISP_CC_MISC_CMD_REG_OFF 0x00 +#define DISP_CC_MISC_CMD_REG_OFF 0x5000 +#define DISP_CC_CORE_GDSCR 0x00 /* register to configure DMA scheduling */ #define DSI_DMA_SCHEDULE_CTRL 0x100 @@ -26,14 +27,20 @@ void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl, bool enable) { u32 reg = 0; + u32 gdscr_reg = 0; reg = DSI_DISP_CC_R32(ctrl, DISP_CC_MISC_CMD_REG_OFF); /* Mask/unmask disable PHY reset bit */ - if (enable) + if (enable) { reg &= ~BIT(ctrl->index); - else + /* Enable GDSC retention flops during idle exit */ + gdscr_reg = DSI_DISP_CC_R32(ctrl, DISP_CC_CORE_GDSCR); + gdscr_reg |= BIT(11); + DSI_DISP_CC_W32(ctrl, DISP_CC_CORE_GDSCR, gdscr_reg); + } else { reg |= BIT(ctrl->index); + } DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg); } diff --git a/techpack/display/msm/dsi/dsi_defs.h b/techpack/display/msm/dsi/dsi_defs.h index 5179edfebd02..a4094c461dd0 100644 --- a/techpack/display/msm/dsi/dsi_defs.h +++ b/techpack/display/msm/dsi/dsi_defs.h @@ -307,6 +307,25 @@ enum dsi_cmd_set_type { DSI_CMD_SET_POST_TIMING_SWITCH, DSI_CMD_SET_QSYNC_ON, DSI_CMD_SET_QSYNC_OFF, + DSI_CMD_SET_HBM_OFF, + DSI_CMD_SET_HBM_ON, + DSI_CMD_SET_HBM_ON_1, + DSI_CMD_SET_HBM_ON_2, + DSI_CMD_SET_HBM_ON_3, + DSI_CMD_SET_HBM_ON_4, + DSI_CMD_SET_HBM_ON_5, + DSI_CMD_SET_HBM_ON_6, + DSI_CMD_SET_MODE_SRGB, + DSI_CMD_SET_MODE_DCI_P3, + DSI_CMD_SET_MODE_WIDE_COLOR, + DSI_CMD_SET_MODE_DEFAULT, + DSI_CMD_SET_AOD_ON_1, + DSI_CMD_SET_AOD_ON_2, + DSI_CMD_SET_AOD_OFF, + DSI_CMD_AOD_OFF_HBM_ON_SETTING, + DSI_CMD_HBM_OFF_AOD_ON_SETTING, + DSI_CMD_SET_AOD_OFF_SAMSUNG, + DSI_CMD_SET_AOD_OFF_NEW, DSI_CMD_SET_MAX }; diff --git a/techpack/display/msm/dsi/dsi_display.c b/techpack/display/msm/dsi/dsi_display.c index 148777e7b148..ca458fca5dea 100644 --- a/techpack/display/msm/dsi/dsi_display.c +++ b/techpack/display/msm/dsi/dsi_display.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "msm_drv.h" #include "sde_connector.h" @@ -21,6 +22,7 @@ #include "sde_dbg.h" #include "dsi_parser.h" +#define to_dsi_bridge(x) container_of((x), struct dsi_bridge, base) #define to_dsi_display(x) container_of(x, struct dsi_display, host) #define INT_BASE_10 10 @@ -45,6 +47,8 @@ static const struct of_device_id dsi_display_dt_match[] = { {} }; +static struct dsi_display *primary_display; + static void dsi_display_mask_ctrl_error_interrupts(struct dsi_display *display, u32 mask, bool enable) { @@ -206,10 +210,14 @@ int dsi_display_set_backlight(struct drm_connector *connector, mutex_lock(&panel->panel_lock); if (!dsi_panel_initialized(panel)) { + panel->hbm_backlight = bl_lvl; rc = -EINVAL; goto error; } + if (bl_lvl != 0 && panel->bl_config.bl_level == 0) + dsi_panel_apply_display_mode_locked(panel); + panel->bl_config.bl_level = bl_lvl; /* scale backlight */ @@ -1045,7 +1053,9 @@ int dsi_display_set_power(struct drm_connector *connector, int power_mode, void *disp) { struct dsi_display *display = disp; + struct msm_drm_notifier notifier_data; int rc = 0; + int blank; if (!display || !display->panel) { DSI_ERR("invalid display/panel\n"); @@ -1059,23 +1069,30 @@ int dsi_display_set_power(struct drm_connector *connector, case SDE_MODE_DPMS_LP2: rc = dsi_panel_set_lp2(display->panel); break; - case SDE_MODE_DPMS_ON: - if ((display->panel->power_mode == SDE_MODE_DPMS_LP1) || - (display->panel->power_mode == SDE_MODE_DPMS_LP2)) - rc = dsi_panel_set_nolp(display->panel); - break; - case SDE_MODE_DPMS_OFF: default: - return rc; + rc = dsi_panel_set_nolp(display->panel); + break; } - SDE_EVT32(display->panel->power_mode, power_mode, rc); - DSI_DEBUG("Power mode transition from %d to %d %s", - display->panel->power_mode, power_mode, - rc ? "failed" : "successful"); - if (!rc) - display->panel->power_mode = power_mode; - + if (power_mode == SDE_MODE_DPMS_ON) { + blank = MSM_DRM_BLANK_UNBLANK_CUST; + notifier_data.data = ␣ + notifier_data.id = connector_state_crtc_index; + msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK, + ¬ifier_data); + } else if (power_mode == SDE_MODE_DPMS_LP1) { + blank = MSM_DRM_BLANK_NORMAL; + notifier_data.data = ␣ + notifier_data.id = connector_state_crtc_index; + msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK, + ¬ifier_data); + } else if (power_mode == SDE_MODE_DPMS_OFF) { + blank = MSM_DRM_BLANK_POWERDOWN_CUST; + notifier_data.data = ␣ + notifier_data.id = connector_state_crtc_index; + msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK, + ¬ifier_data); + } return rc; } @@ -5004,6 +5021,176 @@ static int dsi_display_validate_split_link(struct dsi_display *display) return rc; } +static ssize_t sysfs_hbm_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dsi_display *display = dev_get_drvdata(dev); + if (!display->panel) + return 0; + + return scnprintf(buf, PAGE_SIZE, "%d\n", display->panel->hbm_mode); +} + +static ssize_t sysfs_hbm_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display = dev_get_drvdata(dev); + int ret, hbm_mode; + + if (!display->panel) + return -EINVAL; + + ret = kstrtoint(buf, 10, &hbm_mode); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", ret); + return ret; + } + + mutex_lock(&display->display_lock); + + display->panel->hbm_mode = hbm_mode; + if (!dsi_panel_initialized(display->panel)) { + goto error; + } + + ret = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (ret) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + display->name, ret); + goto error; + } + + ret = dsi_panel_apply_hbm_mode(display->panel); + if (ret) + pr_err("unable to set hbm mode\n"); + + if (hbm_mode == 0) { + /* hbm off cmd in some Samsung s6e3fc2x01 panels sets brightness to an + * arbitrary value; setting it to the right value needs to be done + * separately */ + dsi_panel_update_backlight(display->panel,display->panel->hbm_backlight); + } + + ret = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (ret) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + display->name, ret); + goto error; + } +error: + mutex_unlock(&display->display_lock); + return ret == 0 ? count : ret; +} + +static ssize_t sysfs_display_mode_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dsi_display *display = dev_get_drvdata(dev); + if (!display->panel) + return 0; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + display->panel->display_mode == DISPLAY_MODE_SRGB ? "srgb" : + display->panel->display_mode == DISPLAY_MODE_DCI_P3 ? "dci-p3" : + display->panel->display_mode == DISPLAY_MODE_WIDE_COLOR ? "widecolor" : + "default"); +} + +static ssize_t sysfs_display_mode_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display = dev_get_drvdata(dev); + enum dsi_panel_display_mode mode; + int ret = 0; + + if (!display->panel) + return -EINVAL; + + if (!strcmp(buf, "srgb")) + mode = DISPLAY_MODE_SRGB; + else if (!strcmp(buf, "dci-p3")) + mode = DISPLAY_MODE_DCI_P3; + else if (!strcmp(buf, "widecolor")) + mode = DISPLAY_MODE_WIDE_COLOR; + else if (!strcmp(buf, "default")) + mode = DISPLAY_MODE_DEFAULT; + else + return -EINVAL; + + mutex_lock(&display->display_lock); + + display->panel->display_mode = mode; + if (!dsi_panel_initialized(display->panel)) { + printk("not initialized\n"); + goto error; + } + + ret = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (ret) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + display->name, ret); + goto error; + } + + ret = dsi_panel_apply_display_mode(display->panel); + if (ret) + pr_err("unable to set display mode\n"); + + ret = dsi_display_clk_ctrl(display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (ret) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + display->name, ret); + goto error; + } +error: + mutex_unlock(&display->display_lock); + return ret == 0 ? count : ret; +} + +static DEVICE_ATTR(hbm, 0644, sysfs_hbm_read, sysfs_hbm_write); +static DEVICE_ATTR(display_mode, 0644, + sysfs_display_mode_read, + sysfs_display_mode_write); + +static struct attribute *display_attrs[] = { + &dev_attr_hbm.attr, + &dev_attr_display_mode.attr, + NULL, +}; + +static struct attribute_group display_attrs_group = { + .attrs = display_attrs, +}; + +static int dsi_display_sysfs_init(struct dsi_display *display, + struct device *master) +{ + int rc = 0; + struct device *dev = &display->pdev->dev; + struct dsi_panel *panel; + panel = display->panel; + + rc = sysfs_create_group(&dev->kobj, &display_attrs_group); + if (rc == 0 && !strcmp(display->display_type, "primary")) + rc = sysfs_create_link(&master->kobj, + &dev->kobj, "main_display"); + if (rc) + DSI_ERR("failed to create display sysfs attributes\n"); + + return rc; +} + +static void dsi_display_sysfs_deinit(struct dsi_display *display) +{ + struct device *dev = &display->pdev->dev; + + sysfs_remove_group(&dev->kobj, &display_attrs_group); +} + /** * dsi_display_bind - bind dsi device with controlling device * @dev: Pointer to base of platform device @@ -5072,6 +5259,10 @@ static int dsi_display_bind(struct device *dev, goto error; } + rc = dsi_display_sysfs_init(display, master); + if (rc) + goto error; + atomic_set(&display->clkrate_change_pending, 0); display->cached_clk_rate = 0; @@ -5225,6 +5416,7 @@ static int dsi_display_bind(struct device *dev, (void)dsi_ctrl_drv_deinit(display_ctrl->ctrl); } (void)dsi_display_debugfs_deinit(display); + dsi_display_sysfs_deinit(display); error: mutex_unlock(&display->display_lock); return rc; @@ -6425,6 +6617,7 @@ int dsi_display_get_modes(struct dsi_display *display, exit: *out_modes = display->modes; + primary_display = display; rc = 0; error: @@ -7124,6 +7317,147 @@ static void dsi_display_register_error_handler(struct dsi_display *display) } } +int dsi_display_set_hbm_mode(struct drm_connector *connector, int level) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_panel *panel = NULL; + struct dsi_bridge *c_bridge; + int rc = 0; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return -EINVAL; + + if (level == 7) { + // For some reason, OnePlus' user space treats 0 and 7 the same + level = 0; + } + + panel = dsi_display->panel; + + mutex_lock(&dsi_display->display_lock); + + panel->hbm_mode = level; + if (!dsi_panel_initialized(panel)) { + goto error; + } + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + + rc = dsi_panel_apply_hbm_mode(panel); + if (rc) + pr_err("unable to set hbm mode\n"); + + if (level == 0) { + printk(KERN_ERR "When HBM OFF -->hbm_backight = %d panel->bl_config.bl_level =%d\n", panel->hbm_backlight, panel->bl_config.bl_level); + dsi_panel_update_backlight(panel,panel->hbm_backlight); + } + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (rc) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } +error: + mutex_unlock(&dsi_display->display_lock); + return rc; +} + +int dsi_display_get_hbm_mode(struct drm_connector *connector) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_bridge *c_bridge; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return 0; + + return dsi_display->panel->hbm_mode; +} + + +extern int oneplus_force_screenfp; + +int dsi_display_set_fp_hbm_mode(struct drm_connector *connector, int level) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_panel *panel = NULL; + struct dsi_bridge *c_bridge; + int rc = 0; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return -EINVAL; + + panel = dsi_display->panel; + + mutex_lock(&dsi_display->display_lock); + + panel->hbm_mode = level > 0 ? 5 : 0; + panel->op_force_screenfp = level; + oneplus_force_screenfp=panel->op_force_screenfp; + + if (!dsi_panel_initialized(panel)) + goto error; + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + + rc = dsi_panel_apply_hbm_mode(panel); + if (rc) + pr_err("unable to set hbm mode\n"); + + if (level == 0) { + printk(KERN_ERR "When HBM OFF -->hbm_backight = %d panel->bl_config.bl_level =%d\n", panel->hbm_backlight, panel->bl_config.bl_level); + dsi_panel_update_backlight(panel,panel->hbm_backlight); + } + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (rc) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } +error: + mutex_unlock(&dsi_display->display_lock); + return rc; +} + static void dsi_display_unregister_error_handler(struct dsi_display *display) { int i = 0; @@ -7574,6 +7908,7 @@ int dsi_display_enable(struct dsi_display *display) { int rc = 0; struct dsi_display_mode *mode; + struct drm_connector *connector = NULL; if (!display || !display->panel) { DSI_ERR("Invalid params\n"); @@ -7601,6 +7936,7 @@ int dsi_display_enable(struct dsi_display *display) } display->panel->panel_initialized = true; + dsi_panel_init_display_modes(display->panel); DSI_DEBUG("cont splash enabled, display enable not required\n"); return 0; } @@ -7669,6 +8005,12 @@ int dsi_display_enable(struct dsi_display *display) goto error_disable_panel; } + rc = dsi_display_set_backlight(connector, display, + display->panel->bl_config.bl_level); + if (rc) + pr_warn("[%s]failed to restore previous brightness, rc=%d\n", + display->name, rc); + goto error; error_disable_panel: @@ -7922,6 +8264,104 @@ int dsi_display_unprepare(struct dsi_display *display) return rc; } +int dsi_display_set_aod_mode(struct drm_connector *connector, int level) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_panel *panel = NULL; + struct dsi_bridge *c_bridge; + int rc = 0; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return -EINVAL; + + panel = dsi_display->panel; + panel->aod_mode = level; + if (strcmp(dsi_display->panel->name, "samsung s6e3fc2x01 cmd mode dsi panel") == 0) { + printk(KERN_ERR "dsi_display_set_aod_mode\n"); + } else { + dsi_display->panel->aod_mode = 0; + return 0; + } + mutex_lock(&dsi_display->display_lock); + if (!dsi_panel_initialized(panel)) { + goto error; + } + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_ON); + if (rc) { + pr_err("[%s] failed to enable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + rc = dsi_panel_set_aod_mode(panel, level); + if (rc) + pr_err("unable to set aod mode\n"); + + rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, + DSI_CORE_CLK, DSI_CLK_OFF); + if (rc) { + pr_err("[%s] failed to disable DSI core clocks, rc=%d\n", + dsi_display->name, rc); + goto error; + } + +error: + mutex_unlock(&dsi_display->display_lock); + + return rc; +} + +int dsi_display_get_aod_mode(struct drm_connector *connector) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_bridge *c_bridge; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return 0; + + return dsi_display->panel->aod_mode; +} + +int dsi_display_get_fp_hbm_mode(struct drm_connector *connector) +{ + struct dsi_display *dsi_display = NULL; + struct dsi_bridge *c_bridge; + + if ((connector == NULL) || + (connector->encoder == NULL) || + (connector->encoder->bridge == NULL)) + return 0; + + c_bridge = to_dsi_bridge(connector->encoder->bridge); + dsi_display = c_bridge->display; + + if ((dsi_display == NULL) || (dsi_display->panel == NULL)) + return 0; + + return dsi_display->panel->op_force_screenfp; +} + +struct dsi_display *get_main_display(void) { + return primary_display; +} +EXPORT_SYMBOL(get_main_display); + static int __init dsi_display_register(void) { dsi_phy_drv_register(); diff --git a/techpack/display/msm/dsi/dsi_display.h b/techpack/display/msm/dsi/dsi_display.h index d254dfe739bd..e5a0208ed5b2 100644 --- a/techpack/display/msm/dsi/dsi_display.h +++ b/techpack/display/msm/dsi/dsi_display.h @@ -735,4 +735,9 @@ int dsi_display_cont_splash_config(void *display); int dsi_display_get_panel_vfp(void *display, int h_active, int v_active); +struct dsi_display *get_main_display(void); + +extern int connector_state_crtc_index; +extern int msm_drm_notifier_call_chain(unsigned long val, void *v); + #endif /* _DSI_DISPLAY_H_ */ diff --git a/techpack/display/msm/dsi/dsi_panel.c b/techpack/display/msm/dsi/dsi_panel.c index e1f231f13c00..b1a95d089e66 100644 --- a/techpack/display/msm/dsi/dsi_panel.c +++ b/techpack/display/msm/dsi/dsi_panel.c @@ -545,7 +545,7 @@ static int dsi_panel_tx_cmd_set(struct dsi_panel *panel, SDE_EVT32(type, state, count); if (count == 0) { - DSI_DEBUG("[%s] No commands to be sent for state(%d)\n", + DSI_ERR("[%s] No commands to be sent for state(%d)\n", panel->name, type); goto error; } @@ -654,7 +654,11 @@ static int dsi_panel_dcs_set_display_brightness_c2(struct mipi_dsi_device *dsi, -static int dsi_panel_update_backlight(struct dsi_panel *panel, +extern int op_dimlayer_bl_alpha; +extern int op_dimlayer_bl_enabled; +bool HBM_flag; + +int dsi_panel_update_backlight(struct dsi_panel *panel, u32 bl_lvl) { int rc = 0; @@ -669,13 +673,24 @@ static int dsi_panel_update_backlight(struct dsi_panel *panel, dsi = &panel->mipi_device; bl = &panel->bl_config; + if (op_dimlayer_bl_enabled) { + bl_lvl = op_dimlayer_bl_alpha; + } + if (panel->bl_config.bl_inverted_dbv) bl_lvl = (((bl_lvl & 0xff) << 8) | (bl_lvl >> 8)); if (panel->bl_config.bl_dcs_subtype == 0xc2) rc = dsi_panel_dcs_set_display_brightness_c2(dsi, bl_lvl); - else - rc = mipi_dsi_dcs_set_display_brightness(dsi, bl_lvl); + else { + if (panel->bl_config.bl_high2bit) { + if (HBM_flag == true) + return 0; + rc = mipi_dsi_dcs_set_display_brightness_samsung(dsi, bl_lvl); + } else { + rc = mipi_dsi_dcs_set_display_brightness(dsi, bl_lvl); + } + } if (rc < 0) DSI_ERR("failed to update dcs backlight:%d\n", bl_lvl); @@ -748,6 +763,7 @@ int dsi_panel_set_backlight(struct dsi_panel *panel, u32 bl_lvl) rc = backlight_device_set_brightness(bl->raw_bd, bl_lvl); break; case DSI_BACKLIGHT_DCS: + panel->hbm_backlight = bl_lvl; rc = dsi_panel_update_backlight(panel, bl_lvl); break; case DSI_BACKLIGHT_EXTERNAL: @@ -1830,6 +1846,25 @@ const char *cmd_set_prop_map[DSI_CMD_SET_MAX] = { "qcom,mdss-dsi-post-mode-switch-on-command", "qcom,mdss-dsi-qsync-on-commands", "qcom,mdss-dsi-qsync-off-commands", + "qcom,mdss-dsi-panel-hbm-off-command", + "qcom,mdss-dsi-panel-hbm-on-command", + "qcom,mdss-dsi-panel-hbm-on-command", + "qcom,mdss-dsi-panel-hbm-on-command-2", + "qcom,mdss-dsi-panel-hbm-on-command-3", + "qcom,mdss-dsi-panel-hbm-on-command-4", + "qcom,mdss-dsi-panel-hbm-on-command-5", + "qcom,mdss-dsi-panel-hbm-max-brightness-command-on", + "qcom,mdss-dsi-panel-display-srgb-color-mode-on-command", + "qcom,mdss-dsi-panel-display-p3-mode-on-command", + "qcom,mdss-dsi-panel-display-wide-color-mode-on-command", + "qcom,mdss-dsi-panel-dci-p3-off-command", // also disables SRGB and wide color modes + "qcom,mdss-dsi-panel-aod-on-command-1", + "qcom,mdss-dsi-panel-aod-on-command-2", + "qcom,mdss-dsi-panel-aod-off-command", + "qcom,mdss-dsi-panel-aod-off-hbm-on-command", + "qcom,mdss-dsi-panel-hbm-off-aod-on-command", + "qcom,mdss-dsi-panel-aod-off-samsung-command", + "qcom,mdss-dsi-panel-aod-off-new-command", }; const char *cmd_set_state_map[DSI_CMD_SET_MAX] = { @@ -1856,6 +1891,25 @@ const char *cmd_set_state_map[DSI_CMD_SET_MAX] = { "qcom,mdss-dsi-post-mode-switch-on-command-state", "qcom,mdss-dsi-qsync-on-commands-state", "qcom,mdss-dsi-qsync-off-commands-state", + "qcom,mdss-dsi-hbm-off-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-panel-hbm-max-brightness-command-on-state", + "qcom,mdss-dsi-panel-display-srgb-color-mode-on-command-state", + "qcom,mdss-dsi-panel-display-p3-mode-on-command-state", + "qcom,mdss-dsi-panel-display-wide-color-mode-on-command-state", + "qcom,mdss-dsi-panel-dci-p3-off-command-state", + "qcom,mdss-dsi-hbm-on-command-state", + "qcom,mdss-dsi-aod-on-command-state", + "qcom,mdss-dsi-aod-on-command-state", + "qcom,mdss-dsi-aod-off-command-state", + "qcom,mdss-dsi-panel-aod-off-hbm-on-command-state", + "qcom,mdss-dsi-panel-hbm-off-aod-on-command-state", + "qcom,mdss-dsi-panel-aod-off-samsung-command-state", + "qcom,mdss-dsi-panel-aod-off-new-command-state", }; static int dsi_panel_get_cmd_pkt_count(const char *data, u32 length, u32 *cnt) @@ -3363,6 +3417,22 @@ static void dsi_panel_update_util(struct dsi_panel *panel, utils->node = panel->panel_of_node; } +static int dsi_panel_parse_oem_config(struct dsi_panel *panel, + struct device_node *of_node) +{ + panel->lp11_init = of_property_read_bool(of_node, + "qcom,mdss-dsi-lp11-init"); + if (panel->lp11_init) + pr_debug("lp11_init: %d\n", panel->lp11_init); + + panel->bl_config.bl_high2bit = of_property_read_bool(of_node, + "qcom,mdss-bl-high2bit"); + if (panel->bl_config.bl_high2bit) + pr_debug("bl_high2bit: %d\n", panel->bl_config.bl_high2bit); + + return 0; +} + struct dsi_panel *dsi_panel_get(struct device *parent, struct device_node *of_node, struct device_node *parser_node, @@ -3378,6 +3448,10 @@ struct dsi_panel *dsi_panel_get(struct device *parent, if (!panel) return ERR_PTR(-ENOMEM); + rc = dsi_panel_parse_oem_config(panel, of_node); + if (rc) + pr_debug("failed to get oem config, rc=%d\n", rc); + panel->panel_of_node = of_node; panel->parent = parent; panel->type = type; @@ -4055,6 +4129,7 @@ int dsi_panel_update_pps(struct dsi_panel *panel) return rc; } +extern int oneplus_panel_status; int dsi_panel_set_lp1(struct dsi_panel *panel) { int rc = 0; @@ -4083,6 +4158,9 @@ int dsi_panel_set_lp1(struct dsi_panel *panel) if (rc) DSI_ERR("[%s] failed to send DSI_CMD_SET_LP1 cmd, rc=%d\n", panel->name, rc); + + oneplus_panel_status = 3; // DISPLAY_POWER_DOZE + exit: mutex_unlock(&panel->panel_lock); return rc; @@ -4105,6 +4183,9 @@ int dsi_panel_set_lp2(struct dsi_panel *panel) if (rc) DSI_ERR("[%s] failed to send DSI_CMD_SET_LP2 cmd, rc=%d\n", panel->name, rc); + + oneplus_panel_status = 4; // DISPLAY_POWER_DOZE_SUSPEND + exit: mutex_unlock(&panel->panel_lock); return rc; @@ -4135,6 +4216,9 @@ int dsi_panel_set_nolp(struct dsi_panel *panel) if (rc) DSI_ERR("[%s] failed to send DSI_CMD_SET_NOLP cmd, rc=%d\n", panel->name, rc); + + oneplus_panel_status = 2; // DISPLAY_POWER_ON + exit: mutex_unlock(&panel->panel_lock); return rc; @@ -4453,6 +4537,12 @@ int dsi_panel_post_switch(struct dsi_panel *panel) return rc; } +extern bool oneplus_dimlayer_hbm_enable; +extern bool backup_dimlayer_hbm; +extern int oneplus_auth_status; +extern int oneplus_dim_status; +extern int backup_dim_status; + int dsi_panel_enable(struct dsi_panel *panel) { int rc = 0; @@ -4470,7 +4560,45 @@ int dsi_panel_enable(struct dsi_panel *panel) panel->name, rc); else panel->panel_initialized = true; + + oneplus_panel_status = 2; // DISPLAY_POWER_ON + + if (oneplus_auth_status == 2) { + backup_dimlayer_hbm = 0; + backup_dim_status = 0; + } else if (oneplus_auth_status == 1) { + backup_dimlayer_hbm = 1; + backup_dim_status = 1; + } + + oneplus_dimlayer_hbm_enable = backup_dimlayer_hbm; + oneplus_dim_status = backup_dim_status; + if (oneplus_auth_status != 2) + pr_err("Restore dim when panel goes on"); + oneplus_auth_status = 0; + mutex_unlock(&panel->panel_lock); + + dsi_panel_init_display_modes(panel); + + return rc; +} + +int dsi_panel_init_display_modes(struct dsi_panel *panel) +{ + int rc; + + if (panel->hbm_mode) + dsi_panel_apply_hbm_mode(panel); + dsi_panel_apply_display_mode(panel); + if (panel->aod_mode == 2) { + rc = dsi_panel_set_aod_mode(panel, 2); + panel->aod_status = 1; + } else if (panel->aod_mode == 0) { + rc = dsi_panel_set_aod_mode(panel, 0); + panel->aod_status = 0; + } + return rc; } @@ -4528,8 +4656,13 @@ int dsi_panel_disable(struct dsi_panel *panel) return -EINVAL; } + panel->panel_initialized = false; + mutex_lock(&panel->panel_lock); + if (panel->aod_mode == 2) + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_AOD_OFF_SAMSUNG); + /* Avoid sending panel off commands when ESD recovery is underway */ if (!atomic_read(&panel->esd_recovery_pending)) { /* @@ -4541,6 +4674,14 @@ int dsi_panel_disable(struct dsi_panel *panel) panel->power_mode == SDE_MODE_DPMS_LP2)) dsi_pwr_panel_regulator_mode_set(&panel->power_info, "ibb", REGULATOR_MODE_STANDBY); + oneplus_dimlayer_hbm_enable = false; + oneplus_dim_status = 0; + pr_err("Kill dim when panel goes off"); + + if (panel->aod_mode == 2) + panel->aod_status = 1; + else if (panel->aod_mode == 0) + panel->aod_status = 0; rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_OFF); if (rc) { /* @@ -4554,6 +4695,7 @@ int dsi_panel_disable(struct dsi_panel *panel) rc = 0; } } + oneplus_panel_status = 0; // DISPLAY_POWER_OFF panel->panel_initialized = false; panel->power_mode = SDE_MODE_DPMS_OFF; @@ -4605,3 +4747,115 @@ int dsi_panel_post_unprepare(struct dsi_panel *panel) mutex_unlock(&panel->panel_lock); return rc; } + +int dsi_panel_apply_hbm_mode(struct dsi_panel *panel) +{ + static const enum dsi_cmd_set_type type_map[] = { + DSI_CMD_SET_HBM_OFF, + DSI_CMD_SET_HBM_ON_1, + DSI_CMD_SET_HBM_ON_2, + DSI_CMD_SET_HBM_ON_3, + DSI_CMD_SET_HBM_ON_4, + DSI_CMD_SET_HBM_ON_5, + DSI_CMD_SET_HBM_ON_6, + }; + + enum dsi_cmd_set_type type; + int rc; + + if (panel->hbm_mode >= 0 && panel->hbm_mode < ARRAY_SIZE(type_map)) + type = type_map[panel->hbm_mode]; + else + type = DSI_CMD_SET_HBM_OFF; + + mutex_lock(&panel->panel_lock); + rc = dsi_panel_tx_cmd_set(panel, type); + mutex_unlock(&panel->panel_lock); + + return rc; +} + +int dsi_panel_apply_display_mode_locked(struct dsi_panel *panel) +{ + enum dsi_cmd_set_type type; + int rc; + switch (panel->display_mode) { + case DISPLAY_MODE_SRGB: type = DSI_CMD_SET_MODE_SRGB; break; + case DISPLAY_MODE_DCI_P3: type = DSI_CMD_SET_MODE_DCI_P3; break; + case DISPLAY_MODE_WIDE_COLOR: type = DSI_CMD_SET_MODE_WIDE_COLOR; break; + default: type = DSI_CMD_SET_MODE_DEFAULT; break; + } + + rc = dsi_panel_tx_cmd_set(panel, type); + + return rc; +} + +int dsi_panel_apply_display_mode(struct dsi_panel *panel) +{ + int rc; + + mutex_lock(&panel->panel_lock); + rc = dsi_panel_apply_display_mode_locked(panel); + mutex_unlock(&panel->panel_lock); + + return rc; +} + +bool aod_real_flag = false; +bool aod_complete = false; + +int dsi_panel_set_aod_mode(struct dsi_panel *panel, int level) +{ + int rc = 0; + struct dsi_display_mode *mode; + if (!panel || !panel->cur_mode) { + pr_err("Invalid params\n"); + return -EINVAL; + } + if (panel->aod_disable) + return 0; + mode = panel->cur_mode; + if (level == 1) { + if (panel->aod_status == 0) { + printk(KERN_ERR "send AOD ON commd mode 1 start \n"); + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_AOD_ON_1); + printk(KERN_ERR "send AOD ON commd mode 1 end \n"); + panel->aod_status = 1; + } + } else if (level == 2) { + if (panel->aod_status == 0) { + panel->aod_status = 1; + printk(KERN_ERR "send AOD ON commd mode 2 start \n"); + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_AOD_ON_2); + aod_real_flag = false; + aod_complete = true; + printk(KERN_ERR "send AOD ON commd mode 2 end \n"); + + } + } else { + if (panel->aod_status) { + panel->aod_status = 0; + printk(KERN_ERR "send AOD OFF commd start \n"); + if (aod_real_flag) { + printk(KERN_ERR "send DSI_CMD_SET_AOD_OFF \n"); + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_AOD_OFF); + } + if (!aod_real_flag) { + printk(KERN_ERR "send DSI_CMD_SET_AOD_OFF_NEW \n"); + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_AOD_OFF_NEW); + rc = dsi_panel_update_backlight(panel,panel->bl_config.bl_level); + } + if (panel->display_mode != DISPLAY_MODE_DEFAULT) + dsi_panel_apply_display_mode(panel); + + printk(KERN_ERR "send AOD OFF commd end \n"); + aod_complete = false; + } + } + oneplus_panel_status = 0; // DISPLAY_POWER_OFF + + panel->aod_curr_mode = level; + pr_err("AOD MODE = %d\n", level); +return rc; +} diff --git a/techpack/display/msm/dsi/dsi_panel.h b/techpack/display/msm/dsi/dsi_panel.h index ee30cd5375b4..d2e67295eb57 100644 --- a/techpack/display/msm/dsi/dsi_panel.h +++ b/techpack/display/msm/dsi/dsi_panel.h @@ -123,6 +123,7 @@ struct dsi_backlight_config { u32 bl_scale_sv; bool bl_inverted_dbv; u32 bl_dcs_subtype; + bool bl_high2bit; int en_gpio; /* PWM params */ @@ -172,6 +173,13 @@ struct drm_panel_esd_config { u32 groups; }; +enum dsi_panel_display_mode { + DISPLAY_MODE_DEFAULT, + DISPLAY_MODE_SRGB, + DISPLAY_MODE_DCI_P3, + DISPLAY_MODE_WIDE_COLOR, +}; + struct dsi_panel { const char *name; const char *type; @@ -204,6 +212,18 @@ struct dsi_panel { struct drm_panel_hdr_properties hdr_props; struct drm_panel_esd_config esd_config; + int hbm_mode; + enum dsi_panel_display_mode display_mode; + + int aod_mode; + int aod_status; + int aod_curr_mode; + int aod_disable; + int hbm_backlight; + bool is_hbm_enabled; + int op_force_screenfp; + bool dim_status; + struct dsi_parser_utils utils; bool lp11_init; @@ -301,6 +321,8 @@ int dsi_panel_enable(struct dsi_panel *panel); int dsi_panel_post_enable(struct dsi_panel *panel); +int dsi_panel_init_display_modes(struct dsi_panel *panel); + int dsi_panel_pre_disable(struct dsi_panel *panel); int dsi_panel_disable(struct dsi_panel *panel); @@ -309,6 +331,11 @@ int dsi_panel_unprepare(struct dsi_panel *panel); int dsi_panel_post_unprepare(struct dsi_panel *panel); +int dsi_panel_apply_hbm_mode(struct dsi_panel *panel); + +int dsi_panel_apply_display_mode_locked(struct dsi_panel *panel); +int dsi_panel_apply_display_mode(struct dsi_panel *panel); + int dsi_panel_set_backlight(struct dsi_panel *panel, u32 bl_lvl); int dsi_panel_update_pps(struct dsi_panel *panel); @@ -345,4 +372,7 @@ void dsi_panel_ext_bridge_put(struct dsi_panel *panel); void dsi_panel_calc_dsi_transfer_time(struct dsi_host_common_cfg *config, struct dsi_display_mode *mode, u32 frame_threshold_us); +int dsi_panel_set_aod_mode(struct dsi_panel *panel, int level); +int dsi_panel_update_backlight(struct dsi_panel *panel, u32 bl_lvl); + #endif /* _DSI_PANEL_H_ */ diff --git a/techpack/display/msm/msm_atomic.c b/techpack/display/msm/msm_atomic.c index ddd966d7cd41..b8698f563354 100644 --- a/techpack/display/msm/msm_atomic.c +++ b/techpack/display/msm/msm_atomic.c @@ -16,6 +16,8 @@ * this program. If not, see . */ #include +#include +#include #include "msm_drv.h" #include "msm_gem.h" @@ -33,6 +35,52 @@ struct msm_commit { struct kthread_work commit_work; }; +static BLOCKING_NOTIFIER_HEAD(msm_drm_notifier_list); + +int connector_state_crtc_index; + +/** + * msm_drm_register_client - register a client notifier + * @nb: notifier block to callback on events + * + * This function registers a notifier callback function + * to msm_drm_notifier_list, which would be called when + * received unblank/power down event. + */ +int msm_drm_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&msm_drm_notifier_list, + nb); +} +EXPORT_SYMBOL(msm_drm_register_client); + +/** + * msm_drm_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + * + * This function unregisters the callback function from + * msm_drm_notifier_list. + */ +int msm_drm_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&msm_drm_notifier_list, + nb); +} +EXPORT_SYMBOL(msm_drm_unregister_client); + +/** + * msm_drm_notifier_call_chain - notify clients of drm_events + * @val: event MSM_DRM_EARLY_EVENT_BLANK or MSM_DRM_EVENT_BLANK + * @v: notifier data, inculde display id and display blank + * event(unblank or power down). + */ +int msm_drm_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&msm_drm_notifier_list, val, + v); +} +EXPORT_SYMBOL(msm_drm_notifier_call_chain); + static inline bool _msm_seamless_for_crtc(struct drm_device *dev, struct drm_atomic_state *state, struct drm_crtc_state *crtc_state, bool enable) @@ -156,7 +204,8 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) struct drm_connector_state *old_conn_state; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; - int i; + struct msm_drm_notifier notifier_data; + int i, blank; SDE_ATRACE_BEGIN("msm_disable"); for_each_old_connector_in_state(old_state, connector, @@ -164,6 +213,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_crtc_state *old_crtc_state; + unsigned int crtc_idx; /* * Shut down everything that's in the changeset and currently @@ -172,6 +222,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!old_conn_state->crtc) continue; + crtc_idx = drm_crtc_index(old_conn_state->crtc); old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); @@ -195,6 +246,12 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n", encoder->base.id, encoder->name); + blank = MSM_DRM_BLANK_POWERDOWN; + notifier_data.data = ␣ + notifier_data.id = crtc_idx; + connector_state_crtc_index = crtc_idx; + msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK, + ¬ifier_data); /* * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice. @@ -210,6 +267,8 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); drm_bridge_post_disable(encoder->bridge); + msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK, + ¬ifier_data); } for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { @@ -353,10 +412,11 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_crtc_state *new_crtc_state; struct drm_connector *connector; struct drm_connector_state *new_conn_state; + struct msm_drm_notifier notifier_data; struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; int bridge_enable_count = 0; - int i; + int i, blank; SDE_ATRACE_BEGIN("msm_enable"); for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, @@ -416,6 +476,16 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n", encoder->base.id, encoder->name); + if (connector->state->crtc->state->active_changed) { + blank = MSM_DRM_BLANK_UNBLANK; + notifier_data.data = ␣ + notifier_data.id = + connector->state->crtc->index; + connector_state_crtc_index = connector->state->crtc->index; + DRM_DEBUG_ATOMIC("Notify early unblank\n"); + msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK, + ¬ifier_data); + } /* * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice. @@ -464,6 +534,11 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, encoder->base.id, encoder->name); drm_bridge_enable(encoder->bridge); + if (connector->state->crtc->state->active_changed) { + DRM_DEBUG_ATOMIC("Notify unblank\n"); + msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK, + ¬ifier_data); + } } SDE_ATRACE_END("msm_enable"); } @@ -531,6 +606,8 @@ static void complete_commit(struct msm_commit *c) drm_atomic_state_put(state); + priv->commit_end_time = ktime_get(); + commit_destroy(c); } diff --git a/techpack/display/msm/msm_drv.h b/techpack/display/msm/msm_drv.h index 084aaab34e3d..39cf42921a9f 100644 --- a/techpack/display/msm/msm_drv.h +++ b/techpack/display/msm/msm_drv.h @@ -107,6 +107,7 @@ enum msm_mdp_plane_property { /* range properties */ PLANE_PROP_ZPOS = PLANE_PROP_BLOBCOUNT, + PLANE_PROP_CUSTOM, PLANE_PROP_ALPHA, PLANE_PROP_COLOR_FILL, PLANE_PROP_H_DECIMATE, @@ -164,6 +165,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_CAPTURE_OUTPUT, CRTC_PROP_IDLE_PC_STATE, + CRTC_PROP_CUSTOM, /* total # of properties */ CRTC_PROP_COUNT @@ -202,6 +204,8 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_QSYNC_MODE, CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE, + CONNECTOR_PROP_CUSTOM, + /* total # of properties */ CONNECTOR_PROP_COUNT }; @@ -712,6 +716,9 @@ struct msm_drm_private { bool shutdown_in_progress; struct msm_idle idle; + + /* commit end time */ + ktime_t commit_end_time; }; /* get struct msm_kms * from drm_device * */ diff --git a/techpack/display/msm/sde/sde_connector.c b/techpack/display/msm/sde/sde_connector.c index 35f678373c1d..a8fae56fc7c0 100644 --- a/techpack/display/msm/sde/sde_connector.c +++ b/techpack/display/msm/sde/sde_connector.c @@ -17,6 +17,7 @@ #include "dsi_display.h" #include "sde_crtc.h" #include "sde_rm.h" +#include "sde_trace.h" #define BL_NODE_NAME_SIZE 32 #define HDR10_PLUS_VSIF_TYPE_CODE 0x81 @@ -690,6 +691,180 @@ static int _sde_connector_update_hdr_metadata(struct sde_connector *c_conn, return rc; } +extern bool sde_crtc_get_fingerprint_mode(struct drm_crtc_state *crtc_state); +static int dsi_panel_tx_cmd_set_op(struct dsi_panel *panel, + enum dsi_cmd_set_type type) +{ + int rc = 0, i = 0; + ssize_t len; + struct dsi_cmd_desc *cmds; + u32 count; + enum dsi_cmd_set_state state; + struct dsi_display_mode *mode; + const struct mipi_dsi_host_ops *ops = panel->host->ops; + + if (!panel || !panel->cur_mode) + return -EINVAL; + + mode = panel->cur_mode; + + cmds = mode->priv_info->cmd_sets[type].cmds; + count = mode->priv_info->cmd_sets[type].count; + state = mode->priv_info->cmd_sets[type].state; + + if (count == 0) { + pr_debug("[%s] No commands to be sent for state(%d)\n", + panel->name, type); + goto error; + } + + for (i = 0; i < count; i++) { + if (state == DSI_CMD_SET_STATE_LP) + cmds->msg.flags |= MIPI_DSI_MSG_USE_LPM; + + if (cmds->last_command) + cmds->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND; + + len = ops->transfer(panel->host, &cmds->msg); + if (len < 0) { + rc = len; + pr_err("failed to set cmds(%d), rc=%d\n", type, rc); + goto error; + } + if (cmds->post_wait_ms) + usleep_range(cmds->post_wait_ms*1000, + ((cmds->post_wait_ms*1000)+10)); + cmds++; + } +error: + return rc; +} + +int aod_layer_hide; +extern bool HBM_flag; +extern int oneplus_dim_status; +extern bool aod_real_flag; +extern bool aod_complete; +extern int op_dimlayer_bl; +extern int op_dimlayer_bl_enabled; + +int sde_connector_update_backlight(struct drm_connector *connector) +{ + if (op_dimlayer_bl != op_dimlayer_bl_enabled) { + struct sde_connector *c_conn = to_sde_connector(connector); + + if (!c_conn) { + SDE_ERROR("Invalid params sde_connector null\n"); + return -EINVAL; + } + + op_dimlayer_bl_enabled = op_dimlayer_bl; + _sde_connector_update_bl_scale(c_conn); + } + + return 0; +} + +static int _sde_connector_update_hbm(struct sde_connector *c_conn) +{ + struct drm_connector *connector = &c_conn->base; + struct dsi_display *dsi_display; + struct sde_connector_state *c_state; + int rc = 0; + int fingerprint_mode = 0; + + if (!c_conn) { + SDE_ERROR("Invalid params sde_connector null\n"); + return -EINVAL; + } + + if (c_conn->connector_type != DRM_MODE_CONNECTOR_DSI) + return 0; + + c_state = to_sde_connector_state(connector->state); + + dsi_display = c_conn->display; + if (!dsi_display || !dsi_display->panel) { + SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n", + dsi_display, + ((dsi_display) ? dsi_display->panel : NULL)); + return -EINVAL; + } + + if (!c_conn->encoder || !c_conn->encoder->crtc || + !c_conn->encoder->crtc->state) { + return 0; + } + + // vikas.kala@MULTIMEDIA, 2019/09/20, [EIDQ-4842] Hide Aod Layer before HBM + if (dsi_display->panel->aod_status == 1) { + if (!(sde_crtc_get_fingerprint_mode(c_conn->encoder->crtc->state))) + fingerprint_mode = false; + else + fingerprint_mode = sde_crtc_get_fingerprint_mode(c_conn->encoder->crtc->state); + } else { + if (!(sde_crtc_get_fingerprint_mode(c_conn->encoder->crtc->state))) + fingerprint_mode = false; + else if (oneplus_dim_status == 1) + fingerprint_mode = !!oneplus_dim_status; + else + fingerprint_mode = sde_crtc_get_fingerprint_mode(c_conn->encoder->crtc->state); + } + + if (fingerprint_mode != dsi_display->panel->is_hbm_enabled) { + dsi_display->panel->is_hbm_enabled = fingerprint_mode; + if (fingerprint_mode) { + HBM_flag = true; + SDE_ATRACE_BEGIN("set_hbm_on"); + mutex_lock(&dsi_display->panel->panel_lock); + if (dsi_display->panel->aod_status == 1) { + pr_err("Send DSI_CMD_AOD_OFF_HBM_ON_SETTING cmds\n"); + rc = dsi_panel_tx_cmd_set_op(dsi_display->panel, DSI_CMD_AOD_OFF_HBM_ON_SETTING); + aod_real_flag = true; + } else { + pr_err("DSI_CMD_SET_HBM_ON_5\n"); + rc = dsi_panel_tx_cmd_set_op(dsi_display->panel, DSI_CMD_SET_HBM_ON_5); + } + + mutex_unlock(&dsi_display->panel->panel_lock); + SDE_ATRACE_END("set_hbm_on"); + if (rc) { + pr_err("failed to send DSI_CMD_HBM_ON rc=%d\n", rc); + return rc; + } + } else { + SDE_ATRACE_BEGIN("set_hbm_off"); + HBM_flag = false; + mutex_lock(&dsi_display->panel->panel_lock); + if (dsi_display->panel->aod_status == 1) { + if (oneplus_dim_status == 5) { + rc = dsi_panel_tx_cmd_set_op(dsi_display->panel, DSI_CMD_SET_HBM_OFF); + pr_err("Send DSI_CMD_SET_HBM_OFF cmds\n"); + aod_real_flag = true; + oneplus_dim_status = 0; + aod_layer_hide = 1; + } else { + rc = dsi_panel_tx_cmd_set_op(dsi_display->panel, DSI_CMD_HBM_OFF_AOD_ON_SETTING); + pr_err("Send DSI_CMD_HBM_OFF_AOD_ON_SETTING cmds\n"); + } + } else { + HBM_flag = false; + pr_info("DSI_CMD_SET_HBM_OFF\n"); + rc = dsi_panel_tx_cmd_set_op(dsi_display->panel, DSI_CMD_SET_HBM_OFF); + //oneplus_dim_status = 0; + } + SDE_ATRACE_END("set_hbm_off"); + mutex_unlock(&dsi_display->panel->panel_lock); + _sde_connector_update_bl_scale(c_conn); + if (rc) { + pr_err("failed to send DSI_CMD_HBM_OFF rc=%d\n", rc); + return rc; + } + } + } + return 0; +} + static int _sde_connector_update_dirty_properties( struct drm_connector *connector) { @@ -797,6 +972,12 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) goto end; } + rc = _sde_connector_update_hbm(c_conn); + if (rc) { + SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR); + goto end; + } + if (!c_conn->ops.pre_kickoff) return 0; @@ -2536,6 +2717,10 @@ static int _sde_connector_install_properties(struct drm_device *dev, 0x0, 0, MAX_SV_BL_SCALE_LEVEL, MAX_SV_BL_SCALE_LEVEL, CONNECTOR_PROP_SV_BL_SCALE); + msm_property_install_range(&c_conn->property_info, "CONNECTOR_CUST", + 0x0, 0, INT_MAX, 0, + CONNECTOR_PROP_CUSTOM); + c_conn->bl_scale_dirty = false; c_conn->bl_scale = MAX_BL_SCALE_LEVEL; c_conn->bl_scale_sv = MAX_SV_BL_SCALE_LEVEL; diff --git a/techpack/display/msm/sde/sde_crtc.c b/techpack/display/msm/sde/sde_crtc.c index 602476ba8a4a..5a0253327b29 100644 --- a/techpack/display/msm/sde/sde_crtc.c +++ b/techpack/display/msm/sde/sde_crtc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include +#include "dsi_display.h" #include "sde_kms.h" #include "sde_hw_lm.h" #include "sde_hw_ctl.h" @@ -1474,6 +1476,9 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, for (i = 0; i < cstate->num_dim_layers; i++) _sde_crtc_setup_dim_layer_cfg(crtc, sde_crtc, mixer, &cstate->dim_layer[i]); + if (cstate->fingerprint_dim_layer) + _sde_crtc_setup_dim_layer_cfg(crtc, sde_crtc, + mixer, cstate->fingerprint_dim_layer); } _sde_crtc_program_lm_output_roi(crtc); @@ -2453,6 +2458,8 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) SDE_ATRACE_END("crtc_frame_event"); } +extern int msm_drm_notifier_call_chain(unsigned long val, void *v); + void sde_crtc_complete_commit(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -2467,6 +2474,30 @@ void sde_crtc_complete_commit(struct drm_crtc *crtc, SDE_EVT32_VERBOSE(DRMID(crtc)); sde_core_perf_crtc_update(crtc, 0, false); + { + struct sde_crtc_state *old_cstate; + struct sde_crtc_state *cstate; + struct msm_drm_notifier notifier_data; + int blank; + + if (!old_state) { + SDE_ERROR("failed to find old cstate"); + return; + } + old_cstate = to_sde_crtc_state(old_state); + cstate = to_sde_crtc_state(crtc->state); + if (old_cstate->fingerprint_pressed != cstate->fingerprint_pressed) { + blank = cstate->fingerprint_pressed; + notifier_data.data = ␣ + notifier_data.id = MSM_DRM_PRIMARY_DISPLAY; + pr_err("fingerprint status: %s", + blank ? "pressed" : "up"); + SDE_ATRACE_BEGIN("press_event_notify"); + msm_drm_notifier_call_chain(MSM_DRM_ONSCREENFINGERPRINT_EVENT, + ¬ifier_data); + SDE_ATRACE_END("press_event_notify"); + } + } } /** @@ -2578,6 +2609,379 @@ static void _sde_crtc_set_dim_layer_v1(struct drm_crtc *crtc, } } +bool sde_crtc_get_dimlayer_mode(struct drm_crtc_state *crtc_state) +{ + struct sde_crtc_state *cstate; + + if (!crtc_state) + return false; + + cstate = to_sde_crtc_state(crtc_state); + return !!cstate->fingerprint_dim_layer; +} + +bool sde_crtc_get_fingerprint_mode(struct drm_crtc_state *crtc_state) +{ + struct sde_crtc_state *cstate; + + if (!crtc_state) + return false; + + cstate = to_sde_crtc_state(crtc_state); + return !!cstate->fingerprint_mode; +} + +bool sde_crtc_get_fingerprint_pressed(struct drm_crtc_state *crtc_state) +{ + struct sde_crtc_state *cstate; + + if (!crtc_state) + return false; + + cstate = to_sde_crtc_state(crtc_state); + return cstate->fingerprint_pressed; +} + +/*******************************************************************/ + +extern int oneplus_force_screenfp; +extern int oneplus_panel_alpha; +struct ba { + u32 brightness; + u32 alpha; +}; + +struct ba brightness_alpha_lut[] = { + {0, 0xff}, + {1, 0xf1}, + {2, 0xec}, + {3, 0xeb}, + {4, 0xea}, + {6, 0xe8}, + {10, 0xe4}, + {20, 0xdc}, + {30, 0xd4}, + {45, 0xcc}, + {70, 0xbe}, + {100, 0xb3}, + {150, 0xa6}, + {227, 0x90}, + {300, 0x83}, + {400, 0x70}, + {500, 0x60}, + {600, 0x53}, + {800, 0x3c}, + {1023, 0x22}, + {2000, 0x83}, +}; + +struct ba brightness_alpha_lut_dc[] = { + {0, 0xff}, + {1, 0xE0}, + {2, 0xd5}, + {3, 0xd3}, + {4, 0xd0}, + {5, 0xce}, + {6, 0xcb}, + {8, 0xc8}, + {10, 0xc4}, + {15, 0xba}, + {20, 0xb0}, + {30, 0xa0}, + {45, 0x8b}, + {70, 0x72}, + {100, 0x5a}, + {150, 0x38}, + {227, 0xe}, + {260, 0x00}, +}; + +static int interpolate(int x, int xa, int xb, int ya, int yb) +{ + int bf, factor, plus; + int sub = 0; + + bf = 2 * (yb - ya) * (x - xa) / (xb - xa); + factor = bf / 2; + plus = bf % 2; + if ((xa - xb) && (yb - ya)) + sub = 2 * (x - xa) * (x - xb) / (yb - ya) / (xa - xb); + + return ya + factor + plus + sub; +} + +int brightness_to_alpha(int brightness) +{ + int level = ARRAY_SIZE(brightness_alpha_lut); + int i = 0; + + for (i = 0; i < ARRAY_SIZE(brightness_alpha_lut); i++) { + if (brightness_alpha_lut[i].brightness >= brightness) + break; + } + + if (i == 0) + return brightness_alpha_lut[0].alpha; + else if (i == level) + return brightness_alpha_lut[level - 1].alpha; + + return interpolate(brightness, + brightness_alpha_lut[i - 1].brightness, + brightness_alpha_lut[i].brightness, + brightness_alpha_lut[i - 1].alpha, + brightness_alpha_lut[i].alpha); +} + +int bl_to_alpha_dc(int brightness) +{ + int level = ARRAY_SIZE(brightness_alpha_lut_dc); + int i = 0; + + for (i = 0; i < ARRAY_SIZE(brightness_alpha_lut_dc); i++) { + if (brightness_alpha_lut_dc[i].brightness >= brightness) + break; + } + + if (i == 0) + return brightness_alpha_lut_dc[0].alpha; + else if (i == level) + return brightness_alpha_lut_dc[level - 1].alpha; + + return interpolate(brightness, + brightness_alpha_lut_dc[i - 1].brightness, + brightness_alpha_lut_dc[i].brightness, + brightness_alpha_lut_dc[i - 1].alpha, + brightness_alpha_lut_dc[i].alpha); +} + +bool oneplus_dimlayer_hbm_enable; +int oneplus_get_panel_brightness_to_alpha(void) +{ + struct dsi_display *display = get_main_display(); + + if (!display) + return 0; + + if (oneplus_panel_alpha) + return oneplus_panel_alpha; + + if (oneplus_dimlayer_hbm_enable) + return brightness_to_alpha(display->panel->hbm_backlight); + else + return bl_to_alpha_dc(display->panel->hbm_backlight); +} + +int oneplus_onscreenaod_hid = 0; +int oneplus_aod_hid = 0; + +ssize_t oneplus_display_notify_aod_hid(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int onscreenaod_hid = 0; + + SDE_ATRACE_BEGIN("aod_hid_node"); + + sscanf(buf, "%du", &onscreenaod_hid); + oneplus_onscreenaod_hid = !!onscreenaod_hid; + if (onscreenaod_hid == oneplus_onscreenaod_hid) { + SDE_ATRACE_END("oneplus_display_notify_fp_press"); + return count; + } + + pr_err("notify aod hid %d\n", onscreenaod_hid); + + oneplus_onscreenaod_hid = onscreenaod_hid; + SDE_ATRACE_END("aod_hid_node"); + + return count; +} + +int oneplus_onscreenfp_status = 0; + +ssize_t oneplus_display_notify_fp_press(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct dsi_display *display = get_main_display(); + struct drm_device *drm_dev = display->drm_dev; + struct drm_connector *dsi_connector = display->drm_conn; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + struct msm_drm_private *priv; + int err; + ktime_t now; + bool need_commit = false; + + int onscreenfp_status = 0; + sscanf(buf, "%du", &onscreenfp_status); + onscreenfp_status = !!onscreenfp_status; + if (onscreenfp_status == oneplus_onscreenfp_status) { + SDE_ATRACE_END("oneplus_display_notify_fp_press"); + return count; + } + + pr_err("notify fingerpress %d\n", onscreenfp_status); + oneplus_onscreenfp_status = onscreenfp_status; + + drm_modeset_lock_all(drm_dev); + state = drm_atomic_state_alloc(drm_dev); + state->acquire_ctx = mode_config->acquire_ctx; + crtc = dsi_connector->state->crtc; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + priv = drm_dev->dev_private; + now = ktime_get(); + need_commit = (((now - priv->commit_end_time) > 20000000) && display->panel->aod_status == 0); + + if (need_commit) { + err = drm_atomic_commit(state); + if (err < 0) + drm_atomic_state_clear(state); + } + drm_modeset_unlock_all(drm_dev); + SDE_ATRACE_END("oneplus_display_notify_fp_press"); + return count; +} + +extern bool HBM_flag; +extern int aod_layer_hide; +extern int oneplus_panel_status; +int oneplus_aod_fod = 0; +int oneplus_aod_dc = 0; +int oneplus_dim_status = 0; +int backup_dim_status = 0; +bool backup_dimlayer_hbm = false; + +ssize_t oneplus_display_notify_dim(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct dsi_display *display = get_main_display(); + struct drm_device *drm_dev = display->drm_dev; + struct drm_connector *dsi_connector = display->drm_conn; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int dim_status = 0; + int err; + + SDE_ATRACE_BEGIN("oneplus_display_notify_dim"); + err = sscanf(buf, "%du", &dim_status); + if (err < 0) + pr_err("oneplus_display_notify_dim sscanf failed"); + + //dim_status = !!dim_status; + pr_info("notify dim %d\n", dim_status); + if (oneplus_panel_status == 0) + dim_status = 0; + + if (display->panel->aod_status == 0 && (dim_status == 2)) { + pr_err("fp set it in normal status\n"); + + if (dim_status == oneplus_dim_status) + return count; + + oneplus_dim_status = dim_status; + SDE_ATRACE_END("oneplus_display_notify_dim"); + return count; + + } else if (display->panel->aod_status == 1 && dim_status == 2) { + oneplus_onscreenfp_status = 1; + oneplus_aod_fod = 1; + } else if (display->panel->aod_status == 1 && dim_status == 0) { + oneplus_onscreenfp_status = 0; + } else if (display->panel->aod_status == 1 && dim_status == 5) { + oneplus_aod_dc = 1; + } + + if (dim_status == 0) + oneplus_onscreenfp_status = 0; + + if (dim_status == oneplus_dim_status) + return count; + + oneplus_dim_status = dim_status; + oneplus_dimlayer_hbm_enable = oneplus_dim_status != 0; + backup_dimlayer_hbm = oneplus_dimlayer_hbm_enable; + backup_dim_status = oneplus_dim_status; + if (oneplus_dim_status == 1 && HBM_flag) { + pr_err("notify dim not commit"); + return count; + } + drm_modeset_lock_all(drm_dev); + + state = drm_atomic_state_alloc(drm_dev); + state->acquire_ctx = mode_config->acquire_ctx; + crtc = dsi_connector->state->crtc; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if ((oneplus_dim_status != 0) && (oneplus_dim_status != 5)) { + err = drm_atomic_commit(state); + pr_info("oneplus_dim_status = %d, err = %d, dim_status = %d", + oneplus_dim_status, err, dim_status); + pr_info("oneplus_onscreenfp_status %d hide %d", + oneplus_onscreenfp_status, aod_layer_hide); + if (err < 0) + drm_atomic_state_clear(state); + } + drm_modeset_unlock_all(drm_dev); + SDE_ATRACE_END("oneplus_display_notify_dim"); + + return count; +} + +static int sde_crtc_config_fingerprint_dim_layer(struct drm_crtc_state *crtc_state, int stage) +{ + struct sde_crtc_state *cstate; + struct drm_display_mode *mode = &crtc_state->adjusted_mode; + struct sde_hw_dim_layer *fingerprint_dim_layer; + int alpha = oneplus_get_panel_brightness_to_alpha(); + struct sde_kms *kms; + struct dsi_display *display = get_main_display(); + if (display->panel->aod_status == 1 && oneplus_dim_status == 2) + alpha = 255; + + kms = _sde_crtc_get_kms(crtc_state->crtc); + + if (!kms || !kms->catalog) { + SDE_ERROR("invalid kms\n"); + return -EINVAL; + } + + cstate = to_sde_crtc_state(crtc_state); + + if (cstate->num_dim_layers == SDE_MAX_DIM_LAYERS - 1) { + pr_err("failed to get available dim layer for custom\n"); + return -EINVAL; + } + + if (!alpha) { + cstate->fingerprint_dim_layer = NULL; + return 0; + } + + if ((stage + SDE_STAGE_0) >= kms->catalog->mixer[0].sblk->maxblendstages) { + return -EINVAL; + } + + fingerprint_dim_layer = &cstate->dim_layer[cstate->num_dim_layers]; + fingerprint_dim_layer->flags = SDE_DRM_DIM_LAYER_INCLUSIVE; + fingerprint_dim_layer->stage = stage + SDE_STAGE_0; + + fingerprint_dim_layer->rect.x = 0; + fingerprint_dim_layer->rect.y = 0; + fingerprint_dim_layer->rect.w = mode->hdisplay; + fingerprint_dim_layer->rect.h = mode->vdisplay; + fingerprint_dim_layer->color_fill = (struct sde_mdss_color) {0, 0, 0, alpha}; + cstate->fingerprint_dim_layer = fingerprint_dim_layer; + + return 0; +} + /** * _sde_crtc_set_dest_scaler - copy dest scaler settings from userspace * @sde_crtc : Pointer to sde crtc @@ -4770,6 +5174,163 @@ static int _sde_crtc_check_zpos(struct drm_crtc_state *state, return rc; } +int op_dimlayer_bl_alpha = 260; +int op_dimlayer_bl_enabled = 0; +int op_dimlayer_bl = 0; +bool finger_type = false; +extern int op_dimlayer_bl_enable; +extern int op_dp_enable; + +extern int sde_plane_check_fingerprint_layer(const struct drm_plane_state *drm_state); +static int sde_crtc_onscreenfinger_atomic_check(struct sde_crtc_state *cstate, + struct plane_state *pstates, int cnt) +{ + int fp_index = -1; + int fppressed_index = -1; + int aod_index = -1, aod_mode = -1; + int fppressed_index_rt = -1; + int zpos = INT_MAX; + int mode = 0, i = 0; + int fp_mode = oneplus_onscreenfp_status; + int dim_mode = oneplus_dim_status; + struct dsi_display *display = get_main_display(); + int dim_backlight = 0; + + if (!display) + return 0; + + if (display->panel->aod_status == 1) { + if (oneplus_dim_status == 2 && oneplus_onscreenfp_status == 1) { + fp_mode = 1; + //dim_mode = 0; + } else if (oneplus_dim_status == 2 && oneplus_onscreenfp_status == 0) { + fp_mode = 0; + dim_mode = 0; + } + } + if (oneplus_dim_status == 5 || aod_layer_hide == 1) { + oneplus_aod_hid = 1; + dim_mode = 0; + } + aod_mode = oneplus_aod_hid; + + if ((oneplus_dim_status == 5) && display->panel->aod_status == 0) { + dim_mode = 0; + oneplus_dim_status = 0; + oneplus_dimlayer_hbm_enable = false; + pr_err("current dim = %d, oneplus_dimlayer_hbm_enable = %d\n", oneplus_dim_status, oneplus_dimlayer_hbm_enable); + } + + for (i = 0; i < cnt; i++) { + mode = sde_plane_check_fingerprint_layer(pstates[i].drm_pstate); + if (mode == 1) + fp_index = i; + if (mode == 2) { + fppressed_index = i; + fppressed_index_rt = i; + } + if (mode == 3) + aod_index = i; + } + + if (fp_index >= 0 && dim_mode != 0) + display->panel->dim_status = true; + else + display->panel->dim_status = false; + + if (aod_index < 0) { + oneplus_aod_hid = 0; + aod_layer_hide = 0; + } + + if (fppressed_index_rt < 0) { + oneplus_aod_fod = 0; + oneplus_aod_dc = 0; + } + + if ((fp_index >= 0 && dim_mode != 0) || + (display->panel->aod_status == 1 && oneplus_aod_dc == 0)) { + op_dimlayer_bl = 0; + } else { + if (op_dimlayer_bl_enable && !op_dp_enable) { + if (display->panel->bl_config.bl_level != 0 && + display->panel->bl_config.bl_level < op_dimlayer_bl_alpha) { + dim_backlight = 1; + op_dimlayer_bl = 1; + } else { + op_dimlayer_bl = 0; + } + } else { + op_dimlayer_bl = 0; + } + } + + if (oneplus_dimlayer_hbm_enable || oneplus_force_screenfp || dim_backlight == 1) { + if (fp_index >= 0 && fppressed_index >= 0) { + if (pstates[fp_index].stage >= + pstates[fppressed_index].stage) { + SDE_ERROR("fp layer top of fppressed layer\n"); + return -EINVAL; + } + } + if (fppressed_index >= 0) { + if (zpos > pstates[fppressed_index].stage) + zpos = pstates[fppressed_index].stage; + pstates[fppressed_index].stage++; + } + + if (fp_index >= 0) { + if (zpos > pstates[fp_index].stage) + zpos = pstates[fp_index].stage; + pstates[fp_index].stage++; + } + for (i = 0; i < cnt; i++) { + if (i == fp_index || i == fppressed_index) + continue; + if (pstates[i].stage >= zpos) { + // SDE_ERROR("Warn!!: the fp layer not on top"); + pstates[i].stage++; + } + } + if (zpos == INT_MAX) { + zpos = 0; + for (i = 0; i < cnt; i++) { + if (pstates[i].stage > zpos) + zpos = pstates[i].stage; + } + zpos++; + } + + if (oneplus_dimlayer_hbm_enable) + cstate->fingerprint_mode = true; + else + cstate->fingerprint_mode = false; + + if (fp_index >= 0) + pstates[fp_index].sde_pstate->property_values[PLANE_PROP_ALPHA].value = 0; + + if (sde_crtc_config_fingerprint_dim_layer(&cstate->base, zpos)) { + //SDE_ERROR("Failed to config dim layer\n"); + return -EINVAL; + } + + if (fppressed_index >= 0) + cstate->fingerprint_pressed = true; + else + cstate->fingerprint_pressed = false; + } else { + cstate->fingerprint_dim_layer = NULL; + cstate->fingerprint_pressed = false; + cstate->fingerprint_mode = false; + } + if (fp_mode == 1 && !oneplus_dimlayer_hbm_enable) { + cstate->fingerprint_mode = true; + cstate->fingerprint_pressed = true; + return 0; + } + return 0; +} + static int _sde_crtc_atomic_check_pstates(struct drm_crtc *crtc, struct drm_crtc_state *state, struct plane_state *pstates, @@ -4799,6 +5360,10 @@ static int _sde_crtc_atomic_check_pstates(struct drm_crtc *crtc, if (rc) return rc; + rc = sde_crtc_onscreenfinger_atomic_check(cstate, pstates, cnt); + if (rc) + return rc; + /* assign mixer stages based on sorted zpos property */ rc = _sde_crtc_check_zpos(state, sde_crtc, pstates, cstate, mode, cnt); if (rc) @@ -5125,6 +5690,10 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, "idle_time", 0, 0, U64_MAX, 0, CRTC_PROP_IDLE_TIMEOUT); + msm_property_install_range(&sde_crtc->property_info, + "CRTC_CUST", 0, 0, INT_MAX, 0, + CRTC_PROP_CUSTOM); + if (catalog->has_idle_pc) msm_property_install_enum(&sde_crtc->property_info, "idle_pc_state", 0x0, 0, e_idle_pc_state, diff --git a/techpack/display/msm/sde/sde_crtc.h b/techpack/display/msm/sde/sde_crtc.h index bd16cd3da6fe..270c8da1ee5b 100644 --- a/techpack/display/msm/sde/sde_crtc.h +++ b/techpack/display/msm/sde/sde_crtc.h @@ -439,6 +439,10 @@ struct sde_crtc_state { struct sde_core_perf_params new_perf; int secure_session; + + bool fingerprint_mode; + bool fingerprint_pressed; + struct sde_hw_dim_layer *fingerprint_dim_layer; }; enum sde_crtc_irq_state { diff --git a/techpack/display/msm/sde/sde_encoder.c b/techpack/display/msm/sde/sde_encoder.c index cbb8b5b87bbc..9dfb35d91f1d 100644 --- a/techpack/display/msm/sde/sde_encoder.c +++ b/techpack/display/msm/sde/sde_encoder.c @@ -4474,6 +4474,35 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) sde_enc->idle_pc_restore = false; } +extern bool sde_crtc_get_dimlayer_mode(struct drm_crtc_state *crtc_state); +static bool +_sde_encoder_setup_dither_for_onscreenfingerprint(struct sde_encoder_phys *phys, + void *dither_cfg, int len) +{ + struct drm_encoder *drm_enc = phys->parent; + struct drm_msm_dither dither; + + if (!drm_enc || !drm_enc->crtc) + return -EFAULT; + + if (!sde_crtc_get_dimlayer_mode(drm_enc->crtc->state)) + return -EINVAL; + + if (len != sizeof(dither)) + return -EINVAL; + + memcpy(&dither, dither_cfg, len); + dither.c0_bitdepth = 6; + dither.c1_bitdepth = 6; + dither.c2_bitdepth = 6; + dither.c3_bitdepth = 6; + dither.temporal_en = 1; + + phys->hw_pp->ops.setup_dither(phys->hw_pp, &dither, len); + + return 0; +} + static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) { void *dither_cfg; @@ -4517,7 +4546,8 @@ static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) } } } else { - phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len); + if (_sde_encoder_setup_dither_for_onscreenfingerprint(phys, dither_cfg, len)) + phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len); } } @@ -4874,6 +4904,7 @@ void sde_encoder_needs_hw_reset(struct drm_encoder *drm_enc) } } +extern int sde_connector_update_backlight(struct drm_connector *conn); int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, struct sde_encoder_kickoff_params *params) { @@ -4910,6 +4941,9 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, _sde_encoder_helper_hdr_plus_mempool_update(sde_enc); + if (sde_enc->cur_master) + sde_connector_update_backlight(sde_enc->cur_master->connector); + /* prepare for next kickoff, may include waiting on previous kickoff */ SDE_ATRACE_BEGIN("sde_encoder_prepare_for_kickoff"); for (i = 0; i < sde_enc->num_phys_encs; i++) { diff --git a/techpack/display/msm/sde/sde_encoder_phys_cmd.c b/techpack/display/msm/sde/sde_encoder_phys_cmd.c index 545b2e3284fa..f871f28c67ed 100644 --- a/techpack/display/msm/sde/sde_encoder_phys_cmd.c +++ b/techpack/display/msm/sde/sde_encoder_phys_cmd.c @@ -404,7 +404,9 @@ static void sde_encoder_phys_cmd_cont_splash_mode_set( } phys_enc->cached_mode = *adj_mode; +#ifndef CONFIG_ARCH_SDM845 phys_enc->enable_state = SDE_ENC_ENABLED; +#endif if (!phys_enc->hw_ctl || !phys_enc->hw_pp) { SDE_DEBUG("invalid ctl:%d pp:%d\n", diff --git a/techpack/display/msm/sde/sde_hw_catalog.c b/techpack/display/msm/sde/sde_hw_catalog.c index 6a20a15c55d0..b9678f7f35ab 100644 --- a/techpack/display/msm/sde/sde_hw_catalog.c +++ b/techpack/display/msm/sde/sde_hw_catalog.c @@ -4246,6 +4246,8 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->ts_prefill_rev = 2; sde_cfg->sui_misr_supported = true; sde_cfg->sui_block_xin_mask = 0x3F71; + clear_bit(MDSS_INTF_TEAR_1_INTR, sde_cfg->mdss_irqs); + clear_bit(MDSS_INTF_TEAR_2_INTR, sde_cfg->mdss_irqs); clear_bit(MDSS_INTR_LTM_0_INTR, sde_cfg->mdss_irqs); clear_bit(MDSS_INTR_LTM_1_INTR, sde_cfg->mdss_irqs); sde_cfg->has_decimation = true; diff --git a/techpack/display/msm/sde/sde_hw_ctl.c b/techpack/display/msm/sde/sde_hw_ctl.c index a06b52b24f17..d0c2f523c22e 100644 --- a/techpack/display/msm/sde/sde_hw_ctl.c +++ b/techpack/display/msm/sde/sde_hw_ctl.c @@ -619,7 +619,7 @@ static inline int sde_hw_ctl_trigger_flush_v1(struct sde_hw_ctl *ctx) return 0; } -static inline u32 sde_hw_ctl_get_intf_v1(struct sde_hw_ctl *ctx) +static inline u32 sde_hw_ctl_get_intf_v1(struct sde_hw_ctl *ctx, u32 hwversion) { struct sde_hw_blk_reg_map *c; u32 intf_active; @@ -635,7 +635,7 @@ static inline u32 sde_hw_ctl_get_intf_v1(struct sde_hw_ctl *ctx) return intf_active; } -static inline u32 sde_hw_ctl_get_intf(struct sde_hw_ctl *ctx) +static inline u32 sde_hw_ctl_get_intf(struct sde_hw_ctl *ctx, u32 hwversion) { struct sde_hw_blk_reg_map *c; u32 ctl_top; @@ -648,10 +648,15 @@ static inline u32 sde_hw_ctl_get_intf(struct sde_hw_ctl *ctx) c = &ctx->hw; ctl_top = SDE_REG_READ(c, CTL_TOP); + if (!ctl_top) + goto end; - intf_active = (ctl_top > 0) ? - BIT(ctl_top - 1) : 0; + if (IS_SDM845_TARGET(hwversion)) + intf_active = (ctl_top >> 4) & 0xf; + else + intf_active = BIT(ctl_top - 1); +end: return intf_active; } diff --git a/techpack/display/msm/sde/sde_hw_ctl.h b/techpack/display/msm/sde/sde_hw_ctl.h index 0c90f1156232..f95aa451cd98 100644 --- a/techpack/display/msm/sde/sde_hw_ctl.h +++ b/techpack/display/msm/sde/sde_hw_ctl.h @@ -422,9 +422,10 @@ struct sde_hw_ctl_ops { /** * get interfaces for the active CTL . * @ctx : ctl path ctx pointer + * @hwversion : mdss hw version number * @return : bit mask with the active interfaces for the CTL */ - u32 (*get_ctl_intf)(struct sde_hw_ctl *ctx); + u32 (*get_ctl_intf)(struct sde_hw_ctl *ctx, u32 hwversion); /** * read CTL layers register value and return diff --git a/techpack/display/msm/sde/sde_hw_mdss.h b/techpack/display/msm/sde/sde_hw_mdss.h index 15f0b91d3c2a..c12a58536f8b 100644 --- a/techpack/display/msm/sde/sde_hw_mdss.h +++ b/techpack/display/msm/sde/sde_hw_mdss.h @@ -568,6 +568,13 @@ struct sde_hw_dim_layer { struct sde_rect rect; }; +struct fingerprint_dim_layer { + uint32_t flags; + uint32_t stage; + struct sde_mdss_color color_fill; + struct sde_rect rect; +}; + /** * struct sde_splash_mem - Struct contains splah memory info * @splash_buf_size: Indicates the size of the memory region diff --git a/techpack/display/msm/sde/sde_hw_top.c b/techpack/display/msm/sde/sde_hw_top.c index b02cd17cbb28..d275224e61a2 100644 --- a/techpack/display/msm/sde/sde_hw_top.c +++ b/techpack/display/msm/sde/sde_hw_top.c @@ -457,6 +457,11 @@ struct sde_hw_sid *sde_hw_sid_init(void __iomem *addr, { struct sde_hw_sid *c; + if (!addr) { + SDE_DEBUG("Invalid addr\n"); + return NULL; + } + c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) return ERR_PTR(-ENOMEM); diff --git a/techpack/display/msm/sde/sde_kms.c b/techpack/display/msm/sde/sde_kms.c index ba5b2bc38fdb..fda79db32a3a 100644 --- a/techpack/display/msm/sde/sde_kms.c +++ b/techpack/display/msm/sde/sde_kms.c @@ -3225,7 +3225,8 @@ static void sde_kms_init_shared_hw(struct sde_kms *sde_kms) sde_kms->hw_mdp->ops.reset_ubwc(sde_kms->hw_mdp, sde_kms->catalog); - sde_hw_sid_rotator_set(sde_kms->hw_sid); + if (sde_kms->hw_sid) + sde_hw_sid_rotator_set(sde_kms->hw_sid); } static void _sde_kms_set_lutdma_vbif_remap(struct sde_kms *sde_kms) @@ -3541,17 +3542,16 @@ static int _sde_kms_hw_init_ioremap(struct sde_kms *sde_kms, sde_kms->sid = msm_ioremap(platformdev, "sid_phys", "sid_phys"); if (IS_ERR(sde_kms->sid)) { - rc = PTR_ERR(sde_kms->sid); - SDE_ERROR("sid register memory map failed: %d\n", rc); sde_kms->sid = NULL; - goto error; + SDE_DEBUG("SID_PHYS is not defined\n"); + } else { + sde_kms->sid_len = msm_iomap_size(platformdev, "sid_phys"); + rc = sde_dbg_reg_register_base("sid", sde_kms->sid, + sde_kms->sid_len); + if (rc) + SDE_ERROR("dbg base register sid failed: %d\n", rc); } - sde_kms->sid_len = msm_iomap_size(platformdev, "sid_phys"); - rc = sde_dbg_reg_register_base("sid", sde_kms->sid, sde_kms->sid_len); - if (rc) - SDE_ERROR("dbg base register sid failed: %d\n", rc); - error: return rc; } diff --git a/techpack/display/msm/sde/sde_plane.c b/techpack/display/msm/sde/sde_plane.c index 0264cefe1ddd..51b5962b1a06 100644 --- a/techpack/display/msm/sde/sde_plane.c +++ b/techpack/display/msm/sde/sde_plane.c @@ -1665,6 +1665,18 @@ void sde_plane_clear_multirect(const struct drm_plane_state *drm_state) pstate->multirect_mode = SDE_SSPP_MULTIRECT_NONE; } +int sde_plane_check_fingerprint_layer(const struct drm_plane_state *drm_state) +{ + struct sde_plane_state *pstate; + + if (!drm_state) + return 0; + + pstate = to_sde_plane_state(drm_state); + + return sde_plane_get_property(pstate, PLANE_PROP_CUSTOM); +} + /** * multi_rect validate API allows to validate only R0 and R1 RECT * passing for each plane. Client of this API must not pass multiple @@ -2857,7 +2869,8 @@ static void _sde_plane_map_prop_to_dirty_bits(void) plane_prop_array[PLANE_PROP_INFO] = plane_prop_array[PLANE_PROP_ALPHA] = plane_prop_array[PLANE_PROP_INPUT_FENCE] = - plane_prop_array[PLANE_PROP_BLEND_OP] = 0; + plane_prop_array[PLANE_PROP_BLEND_OP] = + plane_prop_array[PLANE_PROP_CUSTOM] = 0; plane_prop_array[PLANE_PROP_FB_TRANSLATION_MODE] = SDE_PLANE_DIRTY_FB_TRANSLATION_MODE; @@ -3541,7 +3554,7 @@ static void _sde_plane_install_properties(struct drm_plane *plane, const struct sde_format_extended *format_list; struct sde_kms_info *info; struct sde_plane *psde = to_sde_plane(plane); - int zpos_max = 255; + int zpos_max = INT_MAX; int zpos_def = 0; char feature_name[256]; @@ -3559,24 +3572,12 @@ static void _sde_plane_install_properties(struct drm_plane *plane, psde->catalog = catalog; - if (sde_is_custom_client()) { - if (catalog->mixer_count && - catalog->mixer[0].sblk->maxblendstages) { - zpos_max = catalog->mixer[0].sblk->maxblendstages - 1; - if (catalog->has_base_layer && - (zpos_max > SDE_STAGE_MAX - 1)) - zpos_max = SDE_STAGE_MAX - 1; - else if (zpos_max > SDE_STAGE_MAX - SDE_STAGE_0 - 1) - zpos_max = SDE_STAGE_MAX - SDE_STAGE_0 - 1; - } - } else if (plane->type != DRM_PLANE_TYPE_PRIMARY) { - /* reserve zpos == 0 for primary planes */ - zpos_def = drm_plane_index(plane) + 1; - } - msm_property_install_range(&psde->property_info, "zpos", 0x0, 0, zpos_max, zpos_def, PLANE_PROP_ZPOS); + msm_property_install_range(&psde->property_info, "PLANE_CUST", + 0x0, 0, INT_MAX, 0, PLANE_PROP_CUSTOM); + msm_property_install_range(&psde->property_info, "alpha", 0x0, 0, 255, 255, PLANE_PROP_ALPHA); @@ -3998,6 +3999,8 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, { struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; struct sde_plane_state *pstate; + struct drm_property *fod_property; + int fod_val = 0; int idx, ret = -EINVAL; SDE_DEBUG_PLANE(psde, "\n"); @@ -4008,11 +4011,25 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, SDE_ERROR_PLANE(psde, "invalid state\n"); } else { pstate = to_sde_plane_state(state); + idx = msm_property_index(&psde->property_info, + property); + if (idx == PLANE_PROP_ZPOS) { + if (val & FOD_PRESSED_LAYER_ZORDER) { + val &= ~FOD_PRESSED_LAYER_ZORDER; + fod_val = 2; // pressed + } + + fod_property = psde->property_info. + property_array[PLANE_PROP_CUSTOM]; + ret = msm_property_atomic_set(&psde->property_info, + &pstate->property_state, + fod_property, fod_val); + if (ret) + SDE_ERROR("failed to set fod prop"); + } ret = msm_property_atomic_set(&psde->property_info, &pstate->property_state, property, val); if (!ret) { - idx = msm_property_index(&psde->property_info, - property); switch (idx) { case PLANE_PROP_INPUT_FENCE: _sde_plane_set_input_fence(psde, pstate, val); diff --git a/techpack/display/msm/sde/sde_rm.c b/techpack/display/msm/sde/sde_rm.c index e77f9905ce3c..9e8a02f9eee9 100644 --- a/techpack/display/msm/sde/sde_rm.c +++ b/techpack/display/msm/sde/sde_rm.c @@ -1778,7 +1778,7 @@ int sde_rm_cont_splash_res_init(struct msm_drm_private *priv, return -EINVAL; } - intf_sel = ctl->ops.get_ctl_intf(ctl); + intf_sel = ctl->ops.get_ctl_intf(ctl, cat->hwversion); if (intf_sel) { splash_display = &splash_data->splash_display[index]; SDE_DEBUG("finding resources for display=%d ctl=%d\n", diff --git a/techpack/display/msm/sde_dbg.c b/techpack/display/msm/sde_dbg.c index 04037d5c5f20..016e038ed1cc 100644 --- a/techpack/display/msm/sde_dbg.c +++ b/techpack/display/msm/sde_dbg.c @@ -301,6 +301,1019 @@ static void _sde_debug_bus_ppb1_dump(void __iomem *mem_base, entry->wr_addr, entry->block_id, entry->test_id, val); } +#if defined(CONFIG_DEBUG_FS) +static void _sde_debug_bus_axi_dump_sdm845(void __iomem *mem_base, + struct sde_debug_bus_entry *entry, u32 val) +{ + u32 status, i; + + if (!mem_base || !entry) + return; + + for (i = 0; i <= RSC_DEBUG_MUX_SEL_SDM845; i++) { + sde_rsc_debug_dump(i); + + /* make sure that mux_sel updated */ + wmb(); + + /* read status again after rsc routes the debug bus */ + status = readl_relaxed(mem_base + DBGBUS_DSPP_STATUS); + + dev_err(sde_dbg_base.dev, "rsc mux_sel:%d 0x%x %d %d 0x%x\n", + i, entry->wr_addr, entry->block_id, + entry->test_id, status); + } +} +#endif + +static struct sde_debug_bus_entry dbg_bus_sde_sdm845[] = { + + /* Unpack 0 sspp 0*/ + { DBGBUS_SSPP0, 50, 2 }, + { DBGBUS_SSPP0, 60, 2 }, + { DBGBUS_SSPP0, 70, 2 }, + + /* Upack 0 sspp 1*/ + { DBGBUS_SSPP1, 50, 2 }, + { DBGBUS_SSPP1, 60, 2 }, + { DBGBUS_SSPP1, 70, 2 }, + + /* scheduler */ + { DBGBUS_DSPP, 130, 0 }, + { DBGBUS_DSPP, 130, 1 }, + { DBGBUS_DSPP, 130, 2 }, + { DBGBUS_DSPP, 130, 3 }, + { DBGBUS_DSPP, 130, 4 }, + { DBGBUS_DSPP, 130, 5 }, + + /* qseed */ + { DBGBUS_SSPP0, 6, 0}, + { DBGBUS_SSPP0, 6, 1}, + { DBGBUS_SSPP0, 26, 0}, + { DBGBUS_SSPP0, 26, 1}, + { DBGBUS_SSPP1, 6, 0}, + { DBGBUS_SSPP1, 6, 1}, + { DBGBUS_SSPP1, 26, 0}, + { DBGBUS_SSPP1, 26, 1}, + + /* scale */ + { DBGBUS_SSPP0, 16, 0}, + { DBGBUS_SSPP0, 16, 1}, + { DBGBUS_SSPP0, 36, 0}, + { DBGBUS_SSPP0, 36, 1}, + { DBGBUS_SSPP1, 16, 0}, + { DBGBUS_SSPP1, 16, 1}, + { DBGBUS_SSPP1, 36, 0}, + { DBGBUS_SSPP1, 36, 1}, + + /* fetch sspp0 */ + + /* vig 0 */ + { DBGBUS_SSPP0, 0, 0 }, + { DBGBUS_SSPP0, 0, 1 }, + { DBGBUS_SSPP0, 0, 2 }, + { DBGBUS_SSPP0, 0, 3 }, + { DBGBUS_SSPP0, 0, 4 }, + { DBGBUS_SSPP0, 0, 5 }, + { DBGBUS_SSPP0, 0, 6 }, + { DBGBUS_SSPP0, 0, 7 }, + + { DBGBUS_SSPP0, 1, 0 }, + { DBGBUS_SSPP0, 1, 1 }, + { DBGBUS_SSPP0, 1, 2 }, + { DBGBUS_SSPP0, 1, 3 }, + { DBGBUS_SSPP0, 1, 4 }, + { DBGBUS_SSPP0, 1, 5 }, + { DBGBUS_SSPP0, 1, 6 }, + { DBGBUS_SSPP0, 1, 7 }, + + { DBGBUS_SSPP0, 2, 0 }, + { DBGBUS_SSPP0, 2, 1 }, + { DBGBUS_SSPP0, 2, 2 }, + { DBGBUS_SSPP0, 2, 3 }, + { DBGBUS_SSPP0, 2, 4 }, + { DBGBUS_SSPP0, 2, 5 }, + { DBGBUS_SSPP0, 2, 6 }, + { DBGBUS_SSPP0, 2, 7 }, + + { DBGBUS_SSPP0, 4, 0 }, + { DBGBUS_SSPP0, 4, 1 }, + { DBGBUS_SSPP0, 4, 2 }, + { DBGBUS_SSPP0, 4, 3 }, + { DBGBUS_SSPP0, 4, 4 }, + { DBGBUS_SSPP0, 4, 5 }, + { DBGBUS_SSPP0, 4, 6 }, + { DBGBUS_SSPP0, 4, 7 }, + + { DBGBUS_SSPP0, 5, 0 }, + { DBGBUS_SSPP0, 5, 1 }, + { DBGBUS_SSPP0, 5, 2 }, + { DBGBUS_SSPP0, 5, 3 }, + { DBGBUS_SSPP0, 5, 4 }, + { DBGBUS_SSPP0, 5, 5 }, + { DBGBUS_SSPP0, 5, 6 }, + { DBGBUS_SSPP0, 5, 7 }, + + /* vig 2 */ + { DBGBUS_SSPP0, 20, 0 }, + { DBGBUS_SSPP0, 20, 1 }, + { DBGBUS_SSPP0, 20, 2 }, + { DBGBUS_SSPP0, 20, 3 }, + { DBGBUS_SSPP0, 20, 4 }, + { DBGBUS_SSPP0, 20, 5 }, + { DBGBUS_SSPP0, 20, 6 }, + { DBGBUS_SSPP0, 20, 7 }, + + { DBGBUS_SSPP0, 21, 0 }, + { DBGBUS_SSPP0, 21, 1 }, + { DBGBUS_SSPP0, 21, 2 }, + { DBGBUS_SSPP0, 21, 3 }, + { DBGBUS_SSPP0, 21, 4 }, + { DBGBUS_SSPP0, 21, 5 }, + { DBGBUS_SSPP0, 21, 6 }, + { DBGBUS_SSPP0, 21, 7 }, + + { DBGBUS_SSPP0, 22, 0 }, + { DBGBUS_SSPP0, 22, 1 }, + { DBGBUS_SSPP0, 22, 2 }, + { DBGBUS_SSPP0, 22, 3 }, + { DBGBUS_SSPP0, 22, 4 }, + { DBGBUS_SSPP0, 22, 5 }, + { DBGBUS_SSPP0, 22, 6 }, + { DBGBUS_SSPP0, 22, 7 }, + + { DBGBUS_SSPP0, 24, 0 }, + { DBGBUS_SSPP0, 24, 1 }, + { DBGBUS_SSPP0, 24, 2 }, + { DBGBUS_SSPP0, 24, 3 }, + { DBGBUS_SSPP0, 24, 4 }, + { DBGBUS_SSPP0, 24, 5 }, + { DBGBUS_SSPP0, 24, 6 }, + { DBGBUS_SSPP0, 24, 7 }, + + { DBGBUS_SSPP0, 25, 0 }, + { DBGBUS_SSPP0, 25, 1 }, + { DBGBUS_SSPP0, 25, 2 }, + { DBGBUS_SSPP0, 25, 3 }, + { DBGBUS_SSPP0, 25, 4 }, + { DBGBUS_SSPP0, 25, 5 }, + { DBGBUS_SSPP0, 25, 6 }, + { DBGBUS_SSPP0, 25, 7 }, + + /* dma 2 */ + { DBGBUS_SSPP0, 30, 0 }, + { DBGBUS_SSPP0, 30, 1 }, + { DBGBUS_SSPP0, 30, 2 }, + { DBGBUS_SSPP0, 30, 3 }, + { DBGBUS_SSPP0, 30, 4 }, + { DBGBUS_SSPP0, 30, 5 }, + { DBGBUS_SSPP0, 30, 6 }, + { DBGBUS_SSPP0, 30, 7 }, + + { DBGBUS_SSPP0, 31, 0 }, + { DBGBUS_SSPP0, 31, 1 }, + { DBGBUS_SSPP0, 31, 2 }, + { DBGBUS_SSPP0, 31, 3 }, + { DBGBUS_SSPP0, 31, 4 }, + { DBGBUS_SSPP0, 31, 5 }, + { DBGBUS_SSPP0, 31, 6 }, + { DBGBUS_SSPP0, 31, 7 }, + + { DBGBUS_SSPP0, 32, 0 }, + { DBGBUS_SSPP0, 32, 1 }, + { DBGBUS_SSPP0, 32, 2 }, + { DBGBUS_SSPP0, 32, 3 }, + { DBGBUS_SSPP0, 32, 4 }, + { DBGBUS_SSPP0, 32, 5 }, + { DBGBUS_SSPP0, 32, 6 }, + { DBGBUS_SSPP0, 32, 7 }, + + { DBGBUS_SSPP0, 33, 0 }, + { DBGBUS_SSPP0, 33, 1 }, + { DBGBUS_SSPP0, 33, 2 }, + { DBGBUS_SSPP0, 33, 3 }, + { DBGBUS_SSPP0, 33, 4 }, + { DBGBUS_SSPP0, 33, 5 }, + { DBGBUS_SSPP0, 33, 6 }, + { DBGBUS_SSPP0, 33, 7 }, + + { DBGBUS_SSPP0, 34, 0 }, + { DBGBUS_SSPP0, 34, 1 }, + { DBGBUS_SSPP0, 34, 2 }, + { DBGBUS_SSPP0, 34, 3 }, + { DBGBUS_SSPP0, 34, 4 }, + { DBGBUS_SSPP0, 34, 5 }, + { DBGBUS_SSPP0, 34, 6 }, + { DBGBUS_SSPP0, 34, 7 }, + + { DBGBUS_SSPP0, 35, 0 }, + { DBGBUS_SSPP0, 35, 1 }, + { DBGBUS_SSPP0, 35, 2 }, + { DBGBUS_SSPP0, 35, 3 }, + + /* dma 0 */ + { DBGBUS_SSPP0, 40, 0 }, + { DBGBUS_SSPP0, 40, 1 }, + { DBGBUS_SSPP0, 40, 2 }, + { DBGBUS_SSPP0, 40, 3 }, + { DBGBUS_SSPP0, 40, 4 }, + { DBGBUS_SSPP0, 40, 5 }, + { DBGBUS_SSPP0, 40, 6 }, + { DBGBUS_SSPP0, 40, 7 }, + + { DBGBUS_SSPP0, 41, 0 }, + { DBGBUS_SSPP0, 41, 1 }, + { DBGBUS_SSPP0, 41, 2 }, + { DBGBUS_SSPP0, 41, 3 }, + { DBGBUS_SSPP0, 41, 4 }, + { DBGBUS_SSPP0, 41, 5 }, + { DBGBUS_SSPP0, 41, 6 }, + { DBGBUS_SSPP0, 41, 7 }, + + { DBGBUS_SSPP0, 42, 0 }, + { DBGBUS_SSPP0, 42, 1 }, + { DBGBUS_SSPP0, 42, 2 }, + { DBGBUS_SSPP0, 42, 3 }, + { DBGBUS_SSPP0, 42, 4 }, + { DBGBUS_SSPP0, 42, 5 }, + { DBGBUS_SSPP0, 42, 6 }, + { DBGBUS_SSPP0, 42, 7 }, + + { DBGBUS_SSPP0, 44, 0 }, + { DBGBUS_SSPP0, 44, 1 }, + { DBGBUS_SSPP0, 44, 2 }, + { DBGBUS_SSPP0, 44, 3 }, + { DBGBUS_SSPP0, 44, 4 }, + { DBGBUS_SSPP0, 44, 5 }, + { DBGBUS_SSPP0, 44, 6 }, + { DBGBUS_SSPP0, 44, 7 }, + + { DBGBUS_SSPP0, 45, 0 }, + { DBGBUS_SSPP0, 45, 1 }, + { DBGBUS_SSPP0, 45, 2 }, + { DBGBUS_SSPP0, 45, 3 }, + { DBGBUS_SSPP0, 45, 4 }, + { DBGBUS_SSPP0, 45, 5 }, + { DBGBUS_SSPP0, 45, 6 }, + { DBGBUS_SSPP0, 45, 7 }, + + /* fetch sspp1 */ + /* vig 1 */ + { DBGBUS_SSPP1, 0, 0 }, + { DBGBUS_SSPP1, 0, 1 }, + { DBGBUS_SSPP1, 0, 2 }, + { DBGBUS_SSPP1, 0, 3 }, + { DBGBUS_SSPP1, 0, 4 }, + { DBGBUS_SSPP1, 0, 5 }, + { DBGBUS_SSPP1, 0, 6 }, + { DBGBUS_SSPP1, 0, 7 }, + + { DBGBUS_SSPP1, 1, 0 }, + { DBGBUS_SSPP1, 1, 1 }, + { DBGBUS_SSPP1, 1, 2 }, + { DBGBUS_SSPP1, 1, 3 }, + { DBGBUS_SSPP1, 1, 4 }, + { DBGBUS_SSPP1, 1, 5 }, + { DBGBUS_SSPP1, 1, 6 }, + { DBGBUS_SSPP1, 1, 7 }, + + { DBGBUS_SSPP1, 2, 0 }, + { DBGBUS_SSPP1, 2, 1 }, + { DBGBUS_SSPP1, 2, 2 }, + { DBGBUS_SSPP1, 2, 3 }, + { DBGBUS_SSPP1, 2, 4 }, + { DBGBUS_SSPP1, 2, 5 }, + { DBGBUS_SSPP1, 2, 6 }, + { DBGBUS_SSPP1, 2, 7 }, + + { DBGBUS_SSPP1, 4, 0 }, + { DBGBUS_SSPP1, 4, 1 }, + { DBGBUS_SSPP1, 4, 2 }, + { DBGBUS_SSPP1, 4, 3 }, + { DBGBUS_SSPP1, 4, 4 }, + { DBGBUS_SSPP1, 4, 5 }, + { DBGBUS_SSPP1, 4, 6 }, + { DBGBUS_SSPP1, 4, 7 }, + + { DBGBUS_SSPP1, 5, 0 }, + { DBGBUS_SSPP1, 5, 1 }, + { DBGBUS_SSPP1, 5, 2 }, + { DBGBUS_SSPP1, 5, 3 }, + { DBGBUS_SSPP1, 5, 4 }, + { DBGBUS_SSPP1, 5, 5 }, + { DBGBUS_SSPP1, 5, 6 }, + { DBGBUS_SSPP1, 5, 7 }, + + /* vig 3 */ + { DBGBUS_SSPP1, 20, 0 }, + { DBGBUS_SSPP1, 20, 1 }, + { DBGBUS_SSPP1, 20, 2 }, + { DBGBUS_SSPP1, 20, 3 }, + { DBGBUS_SSPP1, 20, 4 }, + { DBGBUS_SSPP1, 20, 5 }, + { DBGBUS_SSPP1, 20, 6 }, + { DBGBUS_SSPP1, 20, 7 }, + + { DBGBUS_SSPP1, 21, 0 }, + { DBGBUS_SSPP1, 21, 1 }, + { DBGBUS_SSPP1, 21, 2 }, + { DBGBUS_SSPP1, 21, 3 }, + { DBGBUS_SSPP1, 21, 4 }, + { DBGBUS_SSPP1, 21, 5 }, + { DBGBUS_SSPP1, 21, 6 }, + { DBGBUS_SSPP1, 21, 7 }, + + { DBGBUS_SSPP1, 22, 0 }, + { DBGBUS_SSPP1, 22, 1 }, + { DBGBUS_SSPP1, 22, 2 }, + { DBGBUS_SSPP1, 22, 3 }, + { DBGBUS_SSPP1, 22, 4 }, + { DBGBUS_SSPP1, 22, 5 }, + { DBGBUS_SSPP1, 22, 6 }, + { DBGBUS_SSPP1, 22, 7 }, + + { DBGBUS_SSPP1, 24, 0 }, + { DBGBUS_SSPP1, 24, 1 }, + { DBGBUS_SSPP1, 24, 2 }, + { DBGBUS_SSPP1, 24, 3 }, + { DBGBUS_SSPP1, 24, 4 }, + { DBGBUS_SSPP1, 24, 5 }, + { DBGBUS_SSPP1, 24, 6 }, + { DBGBUS_SSPP1, 24, 7 }, + + { DBGBUS_SSPP1, 25, 0 }, + { DBGBUS_SSPP1, 25, 1 }, + { DBGBUS_SSPP1, 25, 2 }, + { DBGBUS_SSPP1, 25, 3 }, + { DBGBUS_SSPP1, 25, 4 }, + { DBGBUS_SSPP1, 25, 5 }, + { DBGBUS_SSPP1, 25, 6 }, + { DBGBUS_SSPP1, 25, 7 }, + + /* dma 3 */ + { DBGBUS_SSPP1, 30, 0 }, + { DBGBUS_SSPP1, 30, 1 }, + { DBGBUS_SSPP1, 30, 2 }, + { DBGBUS_SSPP1, 30, 3 }, + { DBGBUS_SSPP1, 30, 4 }, + { DBGBUS_SSPP1, 30, 5 }, + { DBGBUS_SSPP1, 30, 6 }, + { DBGBUS_SSPP1, 30, 7 }, + + { DBGBUS_SSPP1, 31, 0 }, + { DBGBUS_SSPP1, 31, 1 }, + { DBGBUS_SSPP1, 31, 2 }, + { DBGBUS_SSPP1, 31, 3 }, + { DBGBUS_SSPP1, 31, 4 }, + { DBGBUS_SSPP1, 31, 5 }, + { DBGBUS_SSPP1, 31, 6 }, + { DBGBUS_SSPP1, 31, 7 }, + + { DBGBUS_SSPP1, 32, 0 }, + { DBGBUS_SSPP1, 32, 1 }, + { DBGBUS_SSPP1, 32, 2 }, + { DBGBUS_SSPP1, 32, 3 }, + { DBGBUS_SSPP1, 32, 4 }, + { DBGBUS_SSPP1, 32, 5 }, + { DBGBUS_SSPP1, 32, 6 }, + { DBGBUS_SSPP1, 32, 7 }, + + { DBGBUS_SSPP1, 33, 0 }, + { DBGBUS_SSPP1, 33, 1 }, + { DBGBUS_SSPP1, 33, 2 }, + { DBGBUS_SSPP1, 33, 3 }, + { DBGBUS_SSPP1, 33, 4 }, + { DBGBUS_SSPP1, 33, 5 }, + { DBGBUS_SSPP1, 33, 6 }, + { DBGBUS_SSPP1, 33, 7 }, + + { DBGBUS_SSPP1, 34, 0 }, + { DBGBUS_SSPP1, 34, 1 }, + { DBGBUS_SSPP1, 34, 2 }, + { DBGBUS_SSPP1, 34, 3 }, + { DBGBUS_SSPP1, 34, 4 }, + { DBGBUS_SSPP1, 34, 5 }, + { DBGBUS_SSPP1, 34, 6 }, + { DBGBUS_SSPP1, 34, 7 }, + + { DBGBUS_SSPP1, 35, 0 }, + { DBGBUS_SSPP1, 35, 1 }, + { DBGBUS_SSPP1, 35, 2 }, + + /* dma 1 */ + { DBGBUS_SSPP1, 40, 0 }, + { DBGBUS_SSPP1, 40, 1 }, + { DBGBUS_SSPP1, 40, 2 }, + { DBGBUS_SSPP1, 40, 3 }, + { DBGBUS_SSPP1, 40, 4 }, + { DBGBUS_SSPP1, 40, 5 }, + { DBGBUS_SSPP1, 40, 6 }, + { DBGBUS_SSPP1, 40, 7 }, + + { DBGBUS_SSPP1, 41, 0 }, + { DBGBUS_SSPP1, 41, 1 }, + { DBGBUS_SSPP1, 41, 2 }, + { DBGBUS_SSPP1, 41, 3 }, + { DBGBUS_SSPP1, 41, 4 }, + { DBGBUS_SSPP1, 41, 5 }, + { DBGBUS_SSPP1, 41, 6 }, + { DBGBUS_SSPP1, 41, 7 }, + + { DBGBUS_SSPP1, 42, 0 }, + { DBGBUS_SSPP1, 42, 1 }, + { DBGBUS_SSPP1, 42, 2 }, + { DBGBUS_SSPP1, 42, 3 }, + { DBGBUS_SSPP1, 42, 4 }, + { DBGBUS_SSPP1, 42, 5 }, + { DBGBUS_SSPP1, 42, 6 }, + { DBGBUS_SSPP1, 42, 7 }, + + { DBGBUS_SSPP1, 44, 0 }, + { DBGBUS_SSPP1, 44, 1 }, + { DBGBUS_SSPP1, 44, 2 }, + { DBGBUS_SSPP1, 44, 3 }, + { DBGBUS_SSPP1, 44, 4 }, + { DBGBUS_SSPP1, 44, 5 }, + { DBGBUS_SSPP1, 44, 6 }, + { DBGBUS_SSPP1, 44, 7 }, + + { DBGBUS_SSPP1, 45, 0 }, + { DBGBUS_SSPP1, 45, 1 }, + { DBGBUS_SSPP1, 45, 2 }, + { DBGBUS_SSPP1, 45, 3 }, + { DBGBUS_SSPP1, 45, 4 }, + { DBGBUS_SSPP1, 45, 5 }, + { DBGBUS_SSPP1, 45, 6 }, + { DBGBUS_SSPP1, 45, 7 }, + + /* dspp */ + { DBGBUS_DSPP, 13, 0 }, + { DBGBUS_DSPP, 19, 0 }, + { DBGBUS_DSPP, 14, 0 }, + { DBGBUS_DSPP, 14, 1 }, + { DBGBUS_DSPP, 14, 3 }, + { DBGBUS_DSPP, 20, 0 }, + { DBGBUS_DSPP, 20, 1 }, + { DBGBUS_DSPP, 20, 3 }, + + /* ppb_0 */ + { DBGBUS_DSPP, 31, 0, _sde_debug_bus_ppb0_dump }, + { DBGBUS_DSPP, 33, 0, _sde_debug_bus_ppb0_dump }, + { DBGBUS_DSPP, 35, 0, _sde_debug_bus_ppb0_dump }, + { DBGBUS_DSPP, 42, 0, _sde_debug_bus_ppb0_dump }, + + /* ppb_1 */ + { DBGBUS_DSPP, 32, 0, _sde_debug_bus_ppb1_dump }, + { DBGBUS_DSPP, 34, 0, _sde_debug_bus_ppb1_dump }, + { DBGBUS_DSPP, 36, 0, _sde_debug_bus_ppb1_dump }, + { DBGBUS_DSPP, 43, 0, _sde_debug_bus_ppb1_dump }, + + /* lm_lut */ + { DBGBUS_DSPP, 109, 0 }, + { DBGBUS_DSPP, 105, 0 }, + { DBGBUS_DSPP, 103, 0 }, + + /* crossbar */ + { DBGBUS_DSPP, 0, 0, _sde_debug_bus_xbar_dump }, + + /* rotator */ + { DBGBUS_DSPP, 9, 0}, + + /* blend */ + /* LM0 */ + { DBGBUS_DSPP, 63, 1}, + { DBGBUS_DSPP, 63, 2}, + { DBGBUS_DSPP, 63, 3}, + { DBGBUS_DSPP, 63, 4}, + { DBGBUS_DSPP, 63, 5}, + { DBGBUS_DSPP, 63, 6}, + { DBGBUS_DSPP, 63, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 64, 1}, + { DBGBUS_DSPP, 64, 2}, + { DBGBUS_DSPP, 64, 3}, + { DBGBUS_DSPP, 64, 4}, + { DBGBUS_DSPP, 64, 5}, + { DBGBUS_DSPP, 64, 6}, + { DBGBUS_DSPP, 64, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 65, 1}, + { DBGBUS_DSPP, 65, 2}, + { DBGBUS_DSPP, 65, 3}, + { DBGBUS_DSPP, 65, 4}, + { DBGBUS_DSPP, 65, 5}, + { DBGBUS_DSPP, 65, 6}, + { DBGBUS_DSPP, 65, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 66, 1}, + { DBGBUS_DSPP, 66, 2}, + { DBGBUS_DSPP, 66, 3}, + { DBGBUS_DSPP, 66, 4}, + { DBGBUS_DSPP, 66, 5}, + { DBGBUS_DSPP, 66, 6}, + { DBGBUS_DSPP, 66, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 67, 1}, + { DBGBUS_DSPP, 67, 2}, + { DBGBUS_DSPP, 67, 3}, + { DBGBUS_DSPP, 67, 4}, + { DBGBUS_DSPP, 67, 5}, + { DBGBUS_DSPP, 67, 6}, + { DBGBUS_DSPP, 67, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 68, 1}, + { DBGBUS_DSPP, 68, 2}, + { DBGBUS_DSPP, 68, 3}, + { DBGBUS_DSPP, 68, 4}, + { DBGBUS_DSPP, 68, 5}, + { DBGBUS_DSPP, 68, 6}, + { DBGBUS_DSPP, 68, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 69, 1}, + { DBGBUS_DSPP, 69, 2}, + { DBGBUS_DSPP, 69, 3}, + { DBGBUS_DSPP, 69, 4}, + { DBGBUS_DSPP, 69, 5}, + { DBGBUS_DSPP, 69, 6}, + { DBGBUS_DSPP, 69, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 84, 1}, + { DBGBUS_DSPP, 84, 2}, + { DBGBUS_DSPP, 84, 3}, + { DBGBUS_DSPP, 84, 4}, + { DBGBUS_DSPP, 84, 5}, + { DBGBUS_DSPP, 84, 6}, + { DBGBUS_DSPP, 84, 7, _sde_debug_bus_lm_dump }, + + + { DBGBUS_DSPP, 85, 1}, + { DBGBUS_DSPP, 85, 2}, + { DBGBUS_DSPP, 85, 3}, + { DBGBUS_DSPP, 85, 4}, + { DBGBUS_DSPP, 85, 5}, + { DBGBUS_DSPP, 85, 6}, + { DBGBUS_DSPP, 85, 7, _sde_debug_bus_lm_dump }, + + + { DBGBUS_DSPP, 86, 1}, + { DBGBUS_DSPP, 86, 2}, + { DBGBUS_DSPP, 86, 3}, + { DBGBUS_DSPP, 86, 4}, + { DBGBUS_DSPP, 86, 5}, + { DBGBUS_DSPP, 86, 6}, + { DBGBUS_DSPP, 86, 7, _sde_debug_bus_lm_dump }, + + + { DBGBUS_DSPP, 87, 1}, + { DBGBUS_DSPP, 87, 2}, + { DBGBUS_DSPP, 87, 3}, + { DBGBUS_DSPP, 87, 4}, + { DBGBUS_DSPP, 87, 5}, + { DBGBUS_DSPP, 87, 6}, + { DBGBUS_DSPP, 87, 7, _sde_debug_bus_lm_dump }, + + /* LM1 */ + { DBGBUS_DSPP, 70, 1}, + { DBGBUS_DSPP, 70, 2}, + { DBGBUS_DSPP, 70, 3}, + { DBGBUS_DSPP, 70, 4}, + { DBGBUS_DSPP, 70, 5}, + { DBGBUS_DSPP, 70, 6}, + { DBGBUS_DSPP, 70, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 71, 1}, + { DBGBUS_DSPP, 71, 2}, + { DBGBUS_DSPP, 71, 3}, + { DBGBUS_DSPP, 71, 4}, + { DBGBUS_DSPP, 71, 5}, + { DBGBUS_DSPP, 71, 6}, + { DBGBUS_DSPP, 71, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 72, 1}, + { DBGBUS_DSPP, 72, 2}, + { DBGBUS_DSPP, 72, 3}, + { DBGBUS_DSPP, 72, 4}, + { DBGBUS_DSPP, 72, 5}, + { DBGBUS_DSPP, 72, 6}, + { DBGBUS_DSPP, 72, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 73, 1}, + { DBGBUS_DSPP, 73, 2}, + { DBGBUS_DSPP, 73, 3}, + { DBGBUS_DSPP, 73, 4}, + { DBGBUS_DSPP, 73, 5}, + { DBGBUS_DSPP, 73, 6}, + { DBGBUS_DSPP, 73, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 74, 1}, + { DBGBUS_DSPP, 74, 2}, + { DBGBUS_DSPP, 74, 3}, + { DBGBUS_DSPP, 74, 4}, + { DBGBUS_DSPP, 74, 5}, + { DBGBUS_DSPP, 74, 6}, + { DBGBUS_DSPP, 74, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 75, 1}, + { DBGBUS_DSPP, 75, 2}, + { DBGBUS_DSPP, 75, 3}, + { DBGBUS_DSPP, 75, 4}, + { DBGBUS_DSPP, 75, 5}, + { DBGBUS_DSPP, 75, 6}, + { DBGBUS_DSPP, 75, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 76, 1}, + { DBGBUS_DSPP, 76, 2}, + { DBGBUS_DSPP, 76, 3}, + { DBGBUS_DSPP, 76, 4}, + { DBGBUS_DSPP, 76, 5}, + { DBGBUS_DSPP, 76, 6}, + { DBGBUS_DSPP, 76, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 88, 1}, + { DBGBUS_DSPP, 88, 2}, + { DBGBUS_DSPP, 88, 3}, + { DBGBUS_DSPP, 88, 4}, + { DBGBUS_DSPP, 88, 5}, + { DBGBUS_DSPP, 88, 6}, + { DBGBUS_DSPP, 88, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 89, 1}, + { DBGBUS_DSPP, 89, 2}, + { DBGBUS_DSPP, 89, 3}, + { DBGBUS_DSPP, 89, 4}, + { DBGBUS_DSPP, 89, 5}, + { DBGBUS_DSPP, 89, 6}, + { DBGBUS_DSPP, 89, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 90, 1}, + { DBGBUS_DSPP, 90, 2}, + { DBGBUS_DSPP, 90, 3}, + { DBGBUS_DSPP, 90, 4}, + { DBGBUS_DSPP, 90, 5}, + { DBGBUS_DSPP, 90, 6}, + { DBGBUS_DSPP, 90, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 91, 1}, + { DBGBUS_DSPP, 91, 2}, + { DBGBUS_DSPP, 91, 3}, + { DBGBUS_DSPP, 91, 4}, + { DBGBUS_DSPP, 91, 5}, + { DBGBUS_DSPP, 91, 6}, + { DBGBUS_DSPP, 91, 7, _sde_debug_bus_lm_dump }, + + /* LM2 */ + { DBGBUS_DSPP, 77, 0}, + { DBGBUS_DSPP, 77, 1}, + { DBGBUS_DSPP, 77, 2}, + { DBGBUS_DSPP, 77, 3}, + { DBGBUS_DSPP, 77, 4}, + { DBGBUS_DSPP, 77, 5}, + { DBGBUS_DSPP, 77, 6}, + { DBGBUS_DSPP, 77, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 78, 0}, + { DBGBUS_DSPP, 78, 1}, + { DBGBUS_DSPP, 78, 2}, + { DBGBUS_DSPP, 78, 3}, + { DBGBUS_DSPP, 78, 4}, + { DBGBUS_DSPP, 78, 5}, + { DBGBUS_DSPP, 78, 6}, + { DBGBUS_DSPP, 78, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 79, 0}, + { DBGBUS_DSPP, 79, 1}, + { DBGBUS_DSPP, 79, 2}, + { DBGBUS_DSPP, 79, 3}, + { DBGBUS_DSPP, 79, 4}, + { DBGBUS_DSPP, 79, 5}, + { DBGBUS_DSPP, 79, 6}, + { DBGBUS_DSPP, 79, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 80, 0}, + { DBGBUS_DSPP, 80, 1}, + { DBGBUS_DSPP, 80, 2}, + { DBGBUS_DSPP, 80, 3}, + { DBGBUS_DSPP, 80, 4}, + { DBGBUS_DSPP, 80, 5}, + { DBGBUS_DSPP, 80, 6}, + { DBGBUS_DSPP, 80, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 81, 0}, + { DBGBUS_DSPP, 81, 1}, + { DBGBUS_DSPP, 81, 2}, + { DBGBUS_DSPP, 81, 3}, + { DBGBUS_DSPP, 81, 4}, + { DBGBUS_DSPP, 81, 5}, + { DBGBUS_DSPP, 81, 6}, + { DBGBUS_DSPP, 81, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 82, 0}, + { DBGBUS_DSPP, 82, 1}, + { DBGBUS_DSPP, 82, 2}, + { DBGBUS_DSPP, 82, 3}, + { DBGBUS_DSPP, 82, 4}, + { DBGBUS_DSPP, 82, 5}, + { DBGBUS_DSPP, 82, 6}, + { DBGBUS_DSPP, 82, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 83, 0}, + { DBGBUS_DSPP, 83, 1}, + { DBGBUS_DSPP, 83, 2}, + { DBGBUS_DSPP, 83, 3}, + { DBGBUS_DSPP, 83, 4}, + { DBGBUS_DSPP, 83, 5}, + { DBGBUS_DSPP, 83, 6}, + { DBGBUS_DSPP, 83, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 92, 1}, + { DBGBUS_DSPP, 92, 2}, + { DBGBUS_DSPP, 92, 3}, + { DBGBUS_DSPP, 92, 4}, + { DBGBUS_DSPP, 92, 5}, + { DBGBUS_DSPP, 92, 6}, + { DBGBUS_DSPP, 92, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 93, 1}, + { DBGBUS_DSPP, 93, 2}, + { DBGBUS_DSPP, 93, 3}, + { DBGBUS_DSPP, 93, 4}, + { DBGBUS_DSPP, 93, 5}, + { DBGBUS_DSPP, 93, 6}, + { DBGBUS_DSPP, 93, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 94, 1}, + { DBGBUS_DSPP, 94, 2}, + { DBGBUS_DSPP, 94, 3}, + { DBGBUS_DSPP, 94, 4}, + { DBGBUS_DSPP, 94, 5}, + { DBGBUS_DSPP, 94, 6}, + { DBGBUS_DSPP, 94, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 95, 1}, + { DBGBUS_DSPP, 95, 2}, + { DBGBUS_DSPP, 95, 3}, + { DBGBUS_DSPP, 95, 4}, + { DBGBUS_DSPP, 95, 5}, + { DBGBUS_DSPP, 95, 6}, + { DBGBUS_DSPP, 95, 7, _sde_debug_bus_lm_dump }, + + /* LM5 */ + { DBGBUS_DSPP, 110, 1}, + { DBGBUS_DSPP, 110, 2}, + { DBGBUS_DSPP, 110, 3}, + { DBGBUS_DSPP, 110, 4}, + { DBGBUS_DSPP, 110, 5}, + { DBGBUS_DSPP, 110, 6}, + { DBGBUS_DSPP, 110, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 111, 1}, + { DBGBUS_DSPP, 111, 2}, + { DBGBUS_DSPP, 111, 3}, + { DBGBUS_DSPP, 111, 4}, + { DBGBUS_DSPP, 111, 5}, + { DBGBUS_DSPP, 111, 6}, + { DBGBUS_DSPP, 111, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 112, 1}, + { DBGBUS_DSPP, 112, 2}, + { DBGBUS_DSPP, 112, 3}, + { DBGBUS_DSPP, 112, 4}, + { DBGBUS_DSPP, 112, 5}, + { DBGBUS_DSPP, 112, 6}, + { DBGBUS_DSPP, 112, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 113, 1}, + { DBGBUS_DSPP, 113, 2}, + { DBGBUS_DSPP, 113, 3}, + { DBGBUS_DSPP, 113, 4}, + { DBGBUS_DSPP, 113, 5}, + { DBGBUS_DSPP, 113, 6}, + { DBGBUS_DSPP, 113, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 114, 1}, + { DBGBUS_DSPP, 114, 2}, + { DBGBUS_DSPP, 114, 3}, + { DBGBUS_DSPP, 114, 4}, + { DBGBUS_DSPP, 114, 5}, + { DBGBUS_DSPP, 114, 6}, + { DBGBUS_DSPP, 114, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 115, 1}, + { DBGBUS_DSPP, 115, 2}, + { DBGBUS_DSPP, 115, 3}, + { DBGBUS_DSPP, 115, 4}, + { DBGBUS_DSPP, 115, 5}, + { DBGBUS_DSPP, 115, 6}, + { DBGBUS_DSPP, 115, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 116, 1}, + { DBGBUS_DSPP, 116, 2}, + { DBGBUS_DSPP, 116, 3}, + { DBGBUS_DSPP, 116, 4}, + { DBGBUS_DSPP, 116, 5}, + { DBGBUS_DSPP, 116, 6}, + { DBGBUS_DSPP, 116, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 117, 1}, + { DBGBUS_DSPP, 117, 2}, + { DBGBUS_DSPP, 117, 3}, + { DBGBUS_DSPP, 117, 4}, + { DBGBUS_DSPP, 117, 5}, + { DBGBUS_DSPP, 117, 6}, + { DBGBUS_DSPP, 117, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 118, 1}, + { DBGBUS_DSPP, 118, 2}, + { DBGBUS_DSPP, 118, 3}, + { DBGBUS_DSPP, 118, 4}, + { DBGBUS_DSPP, 118, 5}, + { DBGBUS_DSPP, 118, 6}, + { DBGBUS_DSPP, 118, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 119, 1}, + { DBGBUS_DSPP, 119, 2}, + { DBGBUS_DSPP, 119, 3}, + { DBGBUS_DSPP, 119, 4}, + { DBGBUS_DSPP, 119, 5}, + { DBGBUS_DSPP, 119, 6}, + { DBGBUS_DSPP, 119, 7, _sde_debug_bus_lm_dump }, + + { DBGBUS_DSPP, 120, 1}, + { DBGBUS_DSPP, 120, 2}, + { DBGBUS_DSPP, 120, 3}, + { DBGBUS_DSPP, 120, 4}, + { DBGBUS_DSPP, 120, 5}, + { DBGBUS_DSPP, 120, 6}, + { DBGBUS_DSPP, 120, 7, _sde_debug_bus_lm_dump }, + + /* csc */ + { DBGBUS_SSPP0, 7, 0}, + { DBGBUS_SSPP0, 7, 1}, + { DBGBUS_SSPP0, 27, 0}, + { DBGBUS_SSPP0, 27, 1}, + { DBGBUS_SSPP1, 7, 0}, + { DBGBUS_SSPP1, 7, 1}, + { DBGBUS_SSPP1, 27, 0}, + { DBGBUS_SSPP1, 27, 1}, + + /* pcc */ + { DBGBUS_SSPP0, 3, 3}, + { DBGBUS_SSPP0, 23, 3}, + { DBGBUS_SSPP0, 33, 3}, + { DBGBUS_SSPP0, 43, 3}, + { DBGBUS_SSPP1, 3, 3}, + { DBGBUS_SSPP1, 23, 3}, + { DBGBUS_SSPP1, 33, 3}, + { DBGBUS_SSPP1, 43, 3}, + + /* spa */ + { DBGBUS_SSPP0, 8, 0}, + { DBGBUS_SSPP0, 28, 0}, + { DBGBUS_SSPP1, 8, 0}, + { DBGBUS_SSPP1, 28, 0}, + { DBGBUS_DSPP, 13, 0}, + { DBGBUS_DSPP, 19, 0}, + + /* igc */ + { DBGBUS_SSPP0, 17, 0}, + { DBGBUS_SSPP0, 17, 1}, + { DBGBUS_SSPP0, 17, 3}, + { DBGBUS_SSPP0, 37, 0}, + { DBGBUS_SSPP0, 37, 1}, + { DBGBUS_SSPP0, 37, 3}, + { DBGBUS_SSPP0, 46, 0}, + { DBGBUS_SSPP0, 46, 1}, + { DBGBUS_SSPP0, 46, 3}, + + { DBGBUS_SSPP1, 17, 0}, + { DBGBUS_SSPP1, 17, 1}, + { DBGBUS_SSPP1, 17, 3}, + { DBGBUS_SSPP1, 37, 0}, + { DBGBUS_SSPP1, 37, 1}, + { DBGBUS_SSPP1, 37, 3}, + { DBGBUS_SSPP1, 46, 0}, + { DBGBUS_SSPP1, 46, 1}, + { DBGBUS_SSPP1, 46, 3}, + + { DBGBUS_DSPP, 14, 0}, + { DBGBUS_DSPP, 14, 1}, + { DBGBUS_DSPP, 14, 3}, + { DBGBUS_DSPP, 20, 0}, + { DBGBUS_DSPP, 20, 1}, + { DBGBUS_DSPP, 20, 3}, + + /* intf0-3 */ + { DBGBUS_PERIPH, 0, 0}, + { DBGBUS_PERIPH, 1, 0}, + { DBGBUS_PERIPH, 2, 0}, + { DBGBUS_PERIPH, 3, 0}, + + /* te counter wrapper */ + { DBGBUS_PERIPH, 60, 0}, + + /* dsc0 */ + { DBGBUS_PERIPH, 47, 0}, + { DBGBUS_PERIPH, 47, 1}, + { DBGBUS_PERIPH, 47, 2}, + { DBGBUS_PERIPH, 47, 3}, + { DBGBUS_PERIPH, 47, 4}, + { DBGBUS_PERIPH, 47, 5}, + { DBGBUS_PERIPH, 47, 6}, + { DBGBUS_PERIPH, 47, 7}, + + /* dsc1 */ + { DBGBUS_PERIPH, 48, 0}, + { DBGBUS_PERIPH, 48, 1}, + { DBGBUS_PERIPH, 48, 2}, + { DBGBUS_PERIPH, 48, 3}, + { DBGBUS_PERIPH, 48, 4}, + { DBGBUS_PERIPH, 48, 5}, + { DBGBUS_PERIPH, 48, 6}, + { DBGBUS_PERIPH, 48, 7}, + + /* dsc2 */ + { DBGBUS_PERIPH, 51, 0}, + { DBGBUS_PERIPH, 51, 1}, + { DBGBUS_PERIPH, 51, 2}, + { DBGBUS_PERIPH, 51, 3}, + { DBGBUS_PERIPH, 51, 4}, + { DBGBUS_PERIPH, 51, 5}, + { DBGBUS_PERIPH, 51, 6}, + { DBGBUS_PERIPH, 51, 7}, + + /* dsc3 */ + { DBGBUS_PERIPH, 52, 0}, + { DBGBUS_PERIPH, 52, 1}, + { DBGBUS_PERIPH, 52, 2}, + { DBGBUS_PERIPH, 52, 3}, + { DBGBUS_PERIPH, 52, 4}, + { DBGBUS_PERIPH, 52, 5}, + { DBGBUS_PERIPH, 52, 6}, + { DBGBUS_PERIPH, 52, 7}, + + /* tear-check */ + { DBGBUS_PERIPH, 63, 0 }, + { DBGBUS_PERIPH, 64, 0 }, + { DBGBUS_PERIPH, 65, 0 }, + { DBGBUS_PERIPH, 73, 0 }, + { DBGBUS_PERIPH, 74, 0 }, + + /* cdwn */ + { DBGBUS_PERIPH, 80, 0}, + { DBGBUS_PERIPH, 80, 1}, + { DBGBUS_PERIPH, 80, 2}, + + { DBGBUS_PERIPH, 81, 0}, + { DBGBUS_PERIPH, 81, 1}, + { DBGBUS_PERIPH, 81, 2}, + + { DBGBUS_PERIPH, 82, 0}, + { DBGBUS_PERIPH, 82, 1}, + { DBGBUS_PERIPH, 82, 2}, + { DBGBUS_PERIPH, 82, 3}, + { DBGBUS_PERIPH, 82, 4}, + { DBGBUS_PERIPH, 82, 5}, + { DBGBUS_PERIPH, 82, 6}, + { DBGBUS_PERIPH, 82, 7}, + + /* hdmi */ + { DBGBUS_PERIPH, 68, 0}, + { DBGBUS_PERIPH, 68, 1}, + { DBGBUS_PERIPH, 68, 2}, + { DBGBUS_PERIPH, 68, 3}, + { DBGBUS_PERIPH, 68, 4}, + { DBGBUS_PERIPH, 68, 5}, + + /* edp */ + { DBGBUS_PERIPH, 69, 0}, + { DBGBUS_PERIPH, 69, 1}, + { DBGBUS_PERIPH, 69, 2}, + { DBGBUS_PERIPH, 69, 3}, + { DBGBUS_PERIPH, 69, 4}, + { DBGBUS_PERIPH, 69, 5}, + + /* dsi0 */ + { DBGBUS_PERIPH, 70, 0}, + { DBGBUS_PERIPH, 70, 1}, + { DBGBUS_PERIPH, 70, 2}, + { DBGBUS_PERIPH, 70, 3}, + { DBGBUS_PERIPH, 70, 4}, + { DBGBUS_PERIPH, 70, 5}, + + /* dsi1 */ + { DBGBUS_PERIPH, 71, 0}, + { DBGBUS_PERIPH, 71, 1}, + { DBGBUS_PERIPH, 71, 2}, + { DBGBUS_PERIPH, 71, 3}, + { DBGBUS_PERIPH, 71, 4}, + { DBGBUS_PERIPH, 71, 5}, + +#if defined(CONFIG_DEBUG_FS) + /* axi - should be last entry */ + { DBGBUS_AXI_INTF, 62, 0, _sde_debug_bus_axi_dump_sdm845}, +#endif +}; + static struct sde_debug_bus_entry dbg_bus_sde_sm8150[] = { /* Unpack 0 sspp 0*/ @@ -4693,7 +5706,22 @@ void sde_dbg_init_dbg_buses(u32 hwversion) memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde)); memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt)); - if (IS_SM8150_TARGET(hwversion) || IS_SM6150_TARGET(hwversion) || + if (IS_SDM845_TARGET(hwversion)) { + dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845; + dbg->dbgbus_sde.cmn.entries_size = + ARRAY_SIZE(dbg_bus_sde_sdm845); + dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP; + dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE; + dbg->dbgbus_sde.cmn.enable_mask = DEFAULT_DBGBUS_SDE; + + dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998; + dbg->dbgbus_vbif_rt.cmn.entries_size = + ARRAY_SIZE(vbif_dbg_bus_msm8998); + dbg->dbgbus_dsi.entries = dsi_dbg_bus_kona; + dbg->dbgbus_dsi.size = ARRAY_SIZE(dsi_dbg_bus_kona); + dbg->dbgbus_vbif_rt.cmn.name = DBGBUS_NAME_VBIF_RT; + dbg->dbgbus_vbif_rt.cmn.enable_mask = DEFAULT_DBGBUS_VBIFRT; + } else if (IS_SM8150_TARGET(hwversion) || IS_SM6150_TARGET(hwversion) || IS_SDMMAGPIE_TARGET(hwversion) || IS_SDMTRINKET_TARGET(hwversion)) { dbg->dbgbus_sde.entries = dbg_bus_sde_sm8150; diff --git a/techpack/display/rotator/sde_rotator_base.c b/techpack/display/rotator/sde_rotator_base.c index 3cf850753aca..fd893b4103ff 100644 --- a/techpack/display/rotator/sde_rotator_base.c +++ b/techpack/display/rotator/sde_rotator_base.c @@ -255,7 +255,8 @@ u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd) ot_lim = 16; } } else if (IS_SDE_MAJOR_SAME(mdata->mdss_version, - SDE_MDP_HW_REV_600) || is_yuv) { + SDE_MDP_HW_REV_600) || IS_SDE_MAJOR_SAME(mdata->mdss_version, + SDE_MDP_HW_REV_400) || is_yuv) { if (res <= (RES_1080p * 30)) ot_lim = 2; else if (res <= (RES_1080p * 60)) diff --git a/usr/Kconfig b/usr/Kconfig index 8b4826de1189..de9d04f2c0be 100644 --- a/usr/Kconfig +++ b/usr/Kconfig @@ -32,6 +32,17 @@ config INITRAMFS_FORCE and is useful if you cannot or don't want to change the image your bootloader passes to the kernel. +config INITRAMFS_IGNORE_SKIP_FLAG + bool "Force initramfs even when skip_initramfs is set" + default n + help + Ignore skip_initramfs cmdline flag. + + This should only be used if you have no control over cmdline + passed by your bootloader yet you can't use CMDLINE_FORCE. + + If unsure say N. + config INITRAMFS_ROOT_UID int "User ID to map to 0 (user root)" depends on INITRAMFS_SOURCE!=""