From ab59ed04c64d9bd895176109921c99ab3e210a53 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 21 Sep 2020 00:36:35 +0200 Subject: [PATCH 001/212] arm64: dts: qcom: sdm630: Fix intc reg indentation Signed-off-by: Konrad Dybcio --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 9c7f87e42fcc..f045ff295780 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -2224,8 +2224,8 @@ intc: interrupt-controller@17a00000 { compatible = "arm,gic-v3"; - reg = <0x17a00000 0x10000>, /* GICD */ - <0x17b00000 0x100000>; /* GICR * 8 */ + reg = <0x17a00000 0x10000>, /* GICD */ + <0x17b00000 0x100000>; /* GICR * 8 */ #interrupt-cells = <3>; #address-cells = <1>; #size-cells = <1>; From 6ee5bcdadf10e9be43a06e33ce7b1f7aafc02461 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 15 Jan 2021 06:27:25 +0100 Subject: [PATCH 002/212] arm64: dts: qcom: sdm630-xperia-nile: Add all RPM and fixed regulators Add all of the RPM PM660/PM660L regulators and the fixed ones, defining the common electrical part of this platform. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Konrad Dybcio --- .../dts/qcom/sdm630-sony-xperia-nile.dtsi | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 849900e8b80e..53390b4a6c4d 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -615,3 +615,345 @@ dr_mode = "peripheral"; extcon = <&extcon_usb>; }; + +&rpm_requests { + pm660l-regulators { + compatible = "qcom,rpm-pm660l-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + + vdd_l1_l9_l10-supply = <&vreg_s2b_1p05>; + vdd_l2-supply = <&vreg_bob>; + vdd_l3_l5_l7_l8-supply = <&vreg_bob>; + vdd_l4_l6-supply = <&vreg_bob>; + vdd_bob-supply = <&vph_pwr>; + + vreg_s1b_1p125: s1 { + regulator-min-microvolt = <1125000>; + regulator-max-microvolt = <1125000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + vreg_s2b_1p05: s2 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + /* + * At least on Nile's configuration, S3B/S4B (VDD_CX) and + * S5B (VDD_MX) are managed only through RPM Power Domains. + * Trying to set a voltage on the main supply will create + * havoc and freeze the SoC. + * In any case, reference voltages for these regulators are: + * S3B/S4B: 0.870V + * S5B: 0.915V + */ + + /* LDOs */ + vreg_l1b_0p925: l1 { + regulator-min-microvolt = <920000>; + regulator-max-microvolt = <928000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l2b_2p95: l2 { + /* + * This regulator supports 1.648 - 3.104V on this board + * but we set a max voltage of anything less than 2.7V + * to satisfy a condition in sdhci.c that will disable + * 3.3V SDHCI signaling, which happens to be not really + * supported on the Xperia Nile/Ganges platform. + */ + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2696000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l3b_3p0: l3 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-min-microamp = <200>; + regulator-max-microamp = <600000>; + regulator-system-load = <100000>; + regulator-allow-set-load; + }; + + vreg_l4b_29p5: l4 { + regulator-min-microvolt = <2944000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + + regulator-min-microamp = <200>; + regulator-max-microamp = <600000>; + regulator-system-load = <570000>; + regulator-allow-set-load; + }; + + /* + * Downstream specifies a range of 1721-3600mV, + * but the only assigned consumers are SDHCI2 VMMC + * and Coresight QPDI that both request pinned 2.95V. + * Tighten the range to 1.8-3.328 (closest to 3.3) to + * make the mmc driver happy. + */ + vreg_l5b_29p5: l5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3328000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l6b_3p3: l6 { + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <3312000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l7b_3p125: l7 { + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <3128000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l8b_3p3: l8 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3400000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* L9B (870mV) is currently unused */ + /* L10B (915mV) is currently unused */ + + vreg_bob: bob { + regulator-min-microvolt = <3304000>; + regulator-max-microvolt = <3624000>; + regulator-enable-ramp-delay = <500>; + regulator-ramp-delay = <0>; + }; + }; + + pm660-regulators { + compatible = "qcom,rpm-pm660-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + + vdd_l1_l6_l7-supply = <&vreg_s5a_1p35>; + vdd_l2_l3-supply = <&vreg_s2b_1p05>; + vdd_l5-supply = <&vreg_s2b_1p05>; + vdd_l8_l9_l10_l11_l12_l13_l14-supply = <&vreg_s4a_2p04>; + vdd_l15_l16_l17_l18_l19-supply = <&vreg_bob>; + + /* + * S1A (FTAPC0), S2A (FTAPC1), S3A (HFAPC1) are managed + * by the Core Power Reduction hardened (CPRh) and the + * Operating State Manager (OSM) HW automatically. + */ + + vreg_s4a_2p04: s4 { + regulator-min-microvolt = <2040000>; + regulator-max-microvolt = <2040000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + regulator-always-on; + }; + + vreg_s5a_1p35: s5 { + regulator-min-microvolt = <1224000>; + regulator-max-microvolt = <1350000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + vreg_s6a_0p87: s6 { + regulator-min-microvolt = <504000>; + regulator-max-microvolt = <992000>; + regulator-enable-ramp-delay = <150>; + regulator-ramp-delay = <0>; + }; + + /* LDOs */ + vreg_l1a_1p225: l1 { + regulator-min-microvolt = <1226000>; + regulator-max-microvolt = <1250000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l2a_1p0: l2 { + regulator-min-microvolt = <944000>; + regulator-max-microvolt = <1008000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l3a_1p0: l3 { + regulator-min-microvolt = <944000>; + regulator-max-microvolt = <1008000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l5a_0p848: l5 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l6a_1p3: l6 { + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1368000>; + regulator-allow-set-load; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l7a_1p2: l7 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l8a_1p8: l8 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-system-load = <325000>; + regulator-allow-set-load; + }; + + + vreg_l9a_1p8: l9 { + regulator-min-microvolt = <1804000>; + regulator-max-microvolt = <1896000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l10a_1p8: l10 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l11a_1p8: l11 { + regulator-min-microvolt = <1784000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l12a_1p8: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* This gives power to the LPDDR4: never turn it off! */ + vreg_l13a_1p8: l13 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_l14a_1p8: l14 { + regulator-min-microvolt = <1710000>; + regulator-max-microvolt = <1904000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l15a_1p8: l15 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* L16A (2.70V) is unused */ + + vreg_l17a_1p8: l17 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l18a_1v8: l18 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <10>; + regulator-min-microamp = <200>; + regulator-max-microamp = <50000>; + regulator-system-load = <10000>; + regulator-allow-set-load; + }; + + vreg_l19a_3p3: l19 { + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3400000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + }; +}; + +&tlmm { + gpio-reserved-ranges = <8 4>; + + imx300_vana_default: imx300-vana-default { + pins = "gpio50"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + imx219_vana_default: imx219-vana-default { + pins = "gpio51"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + cam_vdig_default: cam-vdig-default { + pins = "gpio52"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; +}; From 3e6dad1592e9bea550cb25f6201864df198bef74 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 31 Jan 2021 19:37:45 +0100 Subject: [PATCH 003/212] arm64: dts: qcom: sdm630-nile: Configure WCN3990 Bluetooth Add a node for WCN3990 HCIUART Bluetooth. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Konrad Dybcio --- .../boot/dts/qcom/sdm630-sony-xperia-nile.dtsi | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 53390b4a6c4d..0f28aaf0c982 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -212,7 +212,18 @@ &blsp2_uart1 { status = "okay"; - /* HCI Bluetooth */ + bluetooth: wcn3990-bt { + compatible = "qcom,wcn3998-bt"; + + vddio-supply = <&vreg_l13a_1p8>; + vddxo-suppky = <&vreg_l9a_1p8>; + vddrf-supply = <&vreg_l6a_1p3>; + vddch0-supply = <&vreg_l19a_3p3>; + + max-speed = <3200000>; + + clocks = <&rpmcc RPM_SMD_RF_CLK1_PIN>; + }; }; &pon { From ee785e6e9909226b6d0d08bf25fa07652029b291 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 23 Oct 2020 13:27:40 +0200 Subject: [PATCH 004/212] drivers: interconnect: sdm660: MASTER_CRYPTO_C0 isn't AP-Owned Signed-off-by: Konrad Dybcio --- drivers/interconnect/qcom/sdm660.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index fb23a5b780a4..4d987b7d58e4 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -277,7 +277,7 @@ DEFINE_QNODE(mas_blsp_2, SDM660_MASTER_BLSP_2, 4, 39, -1, false, -1, 0, -1, SDM6 DEFINE_QNODE(mas_ufs, SDM660_MASTER_UFS, 8, 68, -1, true, NOC_QOS_MODE_FIXED, 1, 4, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_usb_hs, SDM660_MASTER_USB_HS, 8, 42, -1, true, NOC_QOS_MODE_FIXED, 1, 1, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_usb3, SDM660_MASTER_USB3, 8, 32, -1, true, NOC_QOS_MODE_FIXED, 1, 2, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_crypto, SDM660_MASTER_CRYPTO_C0, 8, 23, -1, true, NOC_QOS_MODE_FIXED, 1, 11, SDM660_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_crypto, SDM660_MASTER_CRYPTO_C0, 8, 23, -1, false, NOC_QOS_MODE_FIXED, 1, 11, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_gnoc_bimc, SDM660_MASTER_GNOC_BIMC, 4, 144, -1, true, NOC_QOS_MODE_FIXED, 0, 0, SDM660_SLAVE_EBI); DEFINE_QNODE(mas_oxili, SDM660_MASTER_OXILI, 4, 6, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI, SDM660_SLAVE_BIMC_SNOC); DEFINE_QNODE(mas_mnoc_bimc, SDM660_MASTER_MNOC_BIMC, 4, 2, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI, SDM660_SLAVE_BIMC_SNOC); From 95d7ebbcf1f22245d4752a9487fd396bb8236646 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 9 Nov 2020 22:05:58 +0100 Subject: [PATCH 005/212] drivers: interconnect: qcom: sdm660: Adjust to v5.10 changes Signed-off-by: Konrad Dybcio --- drivers/interconnect/qcom/sdm660.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 4d987b7d58e4..e4b94e323395 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -274,8 +274,8 @@ DEFINE_QNODE(mas_sdcc_1, SDM660_MASTER_SDCC_1, 8, 33, -1, false, -1, 0, -1, SDM6 DEFINE_QNODE(mas_sdcc_2, SDM660_MASTER_SDCC_2, 8, 35, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_blsp_1, SDM660_MASTER_BLSP_1, 4, 41, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_blsp_2, SDM660_MASTER_BLSP_2, 4, 39, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_ufs, SDM660_MASTER_UFS, 8, 68, -1, true, NOC_QOS_MODE_FIXED, 1, 4, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_usb_hs, SDM660_MASTER_USB_HS, 8, 42, -1, true, NOC_QOS_MODE_FIXED, 1, 1, SDM660_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_ufs, SDM660_MASTER_UFS, 8, 68, -1, true, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_usb_hs, SDM660_MASTER_USB_HS, 8, 42, -1, true, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_usb3, SDM660_MASTER_USB3, 8, 32, -1, true, NOC_QOS_MODE_FIXED, 1, 2, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_crypto, SDM660_MASTER_CRYPTO_C0, 8, 23, -1, false, NOC_QOS_MODE_FIXED, 1, 11, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_gnoc_bimc, SDM660_MASTER_GNOC_BIMC, 4, 144, -1, true, NOC_QOS_MODE_FIXED, 0, 0, SDM660_SLAVE_EBI); @@ -606,7 +606,8 @@ static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, if (qn->qos.qos_mode != -1) mode = qn->qos.qos_mode; - /* QoS Priority: The QoS Health parameters are getting considered + /* + * QoS Priority: The QoS Health parameters are getting considered * only if we are NOT in Bypass Mode. */ if (mode != NOC_QOS_MODE_BYPASS) { @@ -929,6 +930,7 @@ static struct platform_driver sdm660_noc_driver = { .driver = { .name = "qnoc-sdm660", .of_match_table = sdm660_noc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(sdm660_noc_driver); From 95faea9235eecaf786c5ead4c9487e75cbdc9db5 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 30 Dec 2020 22:07:16 +0100 Subject: [PATCH 006/212] drm/panel: Add support for Sony TD4322 Truly FHD panel This commit adds support for Sony TD4322 Truly Full HD command mode DSI panel, as found in Sony Xperia XA2 smartphones. Due to the nature of phone manufacturing, it is impossible to retrieve the actual panel name, hence the replacement one, detailing the device it's used on. Signed-off-by: Konrad Dybcio --- drivers/gpu/drm/panel/Kconfig | 10 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-td4322-truly-fhd.c | 277 ++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-td4322-truly-fhd.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 418638e6e3b0..afd392704832 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -547,6 +547,16 @@ config DRM_PANEL_SONY_ACX565AKM Say Y here if you want to enable support for the Sony ACX565AKM 800x600 3.5" panel (found on the Nokia N900). +config DRM_PANEL_SONY_PIONEER_TD4322_TRULY + tristate "Sony Pioneer TD4322 Truly panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable the Truly TD4322 1080x + 1920 command mode display panel (as found on Sony Xperia XA2 + devices). + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index c8132050bcec..99a2e845b8be 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +obj-$(CONFIG_DRM_PANEL_SONY_PIONEER_TD4322_TRULY) += panel-td4322-truly-fhd.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o diff --git a/drivers/gpu/drm/panel/panel-td4322-truly-fhd.c b/drivers/gpu/drm/panel/panel-td4322-truly-fhd.c new file mode 100644 index 000000000000..7dfc02e68812 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-td4322-truly-fhd.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct td4322_truly_fhd { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[4]; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline +struct td4322_truly_fhd *to_td4322_truly_fhd(struct drm_panel *panel) +{ + return container_of(panel, struct td4322_truly_fhd, panel); +} + +#define dsi_generic_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void td4322_truly_fhd_reset(struct td4322_truly_fhd *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(30); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(150); +} + +static int td4322_truly_fhd_on(struct td4322_truly_fhd *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_generic_write_seq(dsi, 0xb0, 0x00); + dsi_generic_write_seq(dsi, 0xd5, + 0x03, 0x00, 0x00, 0x02, 0x23, 0x02, 0x23); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + msleep(30); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + return 0; +} + +static int td4322_truly_fhd_off(struct td4322_truly_fhd *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(20); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + return 0; +} + +static int td4322_truly_fhd_prepare(struct drm_panel *panel) +{ + struct td4322_truly_fhd *ctx = to_td4322_truly_fhd(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + td4322_truly_fhd_reset(ctx); + + ret = td4322_truly_fhd_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int td4322_truly_fhd_unprepare(struct drm_panel *panel) +{ + struct td4322_truly_fhd *ctx = to_td4322_truly_fhd(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = td4322_truly_fhd_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode td4322_truly_fhd_mode = { + .clock = (1080 + 104 + 20 + 56) * (1920 + 10 + 2 + 8) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 104, + .hsync_end = 1080 + 104 + 20, + .htotal = 1080 + 104 + 20 + 56, + .vdisplay = 1920, + .vsync_start = 1920 + 10, + .vsync_end = 1920 + 10 + 2, + .vtotal = 1920 + 10 + 2 + 8, + .width_mm = 64, + .height_mm = 115, +}; + +static int td4322_truly_fhd_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &td4322_truly_fhd_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs td4322_truly_fhd_panel_funcs = { + .prepare = td4322_truly_fhd_prepare, + .unprepare = td4322_truly_fhd_unprepare, + .get_modes = td4322_truly_fhd_get_modes, +}; + +static int td4322_truly_fhd_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct td4322_truly_fhd *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->supplies[0].supply = "vdd"; + ctx->supplies[1].supply = "vddio"; + ctx->supplies[2].supply = "vsn"; + ctx->supplies[3].supply = "vsp"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + ret = PTR_ERR(ctx->reset_gpio); + dev_err(dev, "Failed to get reset-gpios: %d\n", ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &td4322_truly_fhd_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) { + dev_err(dev, "Failed to get backlight: %d\n", ret); + return ret; + } + + drm_panel_add(&ctx->panel); + if (ret < 0) { + dev_err(dev, "Failed to add panel: %d\n", ret); + return ret; + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + return ret; + } + + return 0; +} + +static int td4322_truly_fhd_remove(struct mipi_dsi_device *dsi) +{ + struct td4322_truly_fhd *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id td4322_truly_fhd_of_match[] = { + { .compatible = "sony,pioneer-td4322-truly" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, td4322_truly_fhd_of_match); + +static struct mipi_dsi_driver td4322_truly_fhd_driver = { + .probe = td4322_truly_fhd_probe, + .remove = td4322_truly_fhd_remove, + .driver = { + .name = "panel-td4322-truly-fhd", + .of_match_table = td4322_truly_fhd_of_match, + }, +}; +module_mipi_dsi_driver(td4322_truly_fhd_driver); + +MODULE_AUTHOR("Konrad Dybcio "); +MODULE_DESCRIPTION("DRM driver for TD4322 Truly FHD CMD mode panel"); +MODULE_LICENSE("GPL v2"); From ffdad2e91a8171f77604ff35b347749480b215ac Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Feb 2021 22:58:16 +0100 Subject: [PATCH 007/212] dt-bindings: display: panel: Document Sony Pioneer TD4322 Truly display panel Add bindings for the newly added panel. Signed-off-by: Konrad Dybcio --- .../panel/sony,pioneer-td4322-truly.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/sony,pioneer-td4322-truly.yaml diff --git a/Documentation/devicetree/bindings/display/panel/sony,pioneer-td4322-truly.yaml b/Documentation/devicetree/bindings/display/panel/sony,pioneer-td4322-truly.yaml new file mode 100644 index 000000000000..4d45f87e513b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sony,pioneer-td4322-truly.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/sony,pioneer-td4322-truly.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SONY Pioneer TD4322 Truly 5.2" 1080×1920 LCD Panel + +maintainers: + - Konrad Dybcio + +description: |+ + This panel seems to only be found in the Sony Xperia XA2 + smartphone and we have no straightforward way of + actually getting the correct model number, + as no schematics are released publicly. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: sony,pioneer-td4322-truly + reg: true + reset-gpios: true + vdd-supply: + description: core voltage supply + vddio-supply: + description: vddio supply + vsp-supply: + description: positive voltage supply + vsn-supply: + description: negative voltage supply + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vsp-supply + - vsn-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + reg = <0>; + compatible = "sony,pioneer-td4322-truly"; + + vdd-supply = <&pm8916_l8>; + vddio-supply = <&pm8916_l6>; + vsp-supply = <&lab>; + vsn-supply = <&ibb>; + reset-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>; + }; + }; From c783b6740a7a9dff6153f91052bcffa99a4aa1b4 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 16 Jul 2020 21:33:21 +0200 Subject: [PATCH 008/212] drm/panel: Add support for Sony TD4322 Innolux FHD panel This commit adds support for Sony TD4322 Innolux Full HD command mode DSI panel, as found in Sony Xperia XA2 Ultra smartphones. Due to the nature of phone manufacturing, it is impossible to retrieve the actual panel name, hence the replacement one, detailing the device it's used on. Signed-off-by: Konrad Dybcio --- drivers/gpu/drm/panel/Kconfig | 10 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-td4322-innolux-fhd.c | 274 ++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index afd392704832..710688899729 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -547,6 +547,16 @@ config DRM_PANEL_SONY_ACX565AKM Say Y here if you want to enable support for the Sony ACX565AKM 800x600 3.5" panel (found on the Nokia N900). +config DRM_PANEL_SONY_DISCOVERY_TD4322_INNOLUX + tristate "Sony Discovery TD4322 Innolux panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable the Innolux TD4322 + 1080-1920 command mode panel (as found on Sony Xperia + XA2 Ultra devices). + config DRM_PANEL_SONY_PIONEER_TD4322_TRULY tristate "Sony Pioneer TD4322 Truly panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 99a2e845b8be..d014fbf46782 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +obj-$(CONFIG_DRM_PANEL_SONY_DISCOVERY_TD4322_INNOLUX) += panel-td4322-innolux-fhd.o obj-$(CONFIG_DRM_PANEL_SONY_PIONEER_TD4322_TRULY) += panel-td4322-truly-fhd.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o diff --git a/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c new file mode 100644 index 000000000000..69a962cc106e --- /dev/null +++ b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct td4322_innolux_fhd { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[4]; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline +struct td4322_innolux_fhd *to_td4322_innolux_fhd(struct drm_panel *panel) +{ + return container_of(panel, struct td4322_innolux_fhd, panel); +} + +#define dsi_dcs_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void td4322_innolux_fhd_reset(struct td4322_innolux_fhd *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(30); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(150); +} + +static int td4322_innolux_fhd_on(struct td4322_innolux_fhd *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_dcs_write_seq(dsi, 0x35); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(70); + + return 0; +} + +static int td4322_innolux_fhd_off(struct td4322_innolux_fhd *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(20); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + return 0; +} + +static int td4322_innolux_fhd_prepare(struct drm_panel *panel) +{ + struct td4322_innolux_fhd *ctx = to_td4322_innolux_fhd(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + td4322_innolux_fhd_reset(ctx); + + ret = td4322_innolux_fhd_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int td4322_innolux_fhd_unprepare(struct drm_panel *panel) +{ + struct td4322_innolux_fhd *ctx = to_td4322_innolux_fhd(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = td4322_innolux_fhd_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode td4322_innolux_fhd_mode = { + .clock = (1080 + 96 + 20 + 48) * (1920 + 5 + 2 + 3) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 96, + .hsync_end = 1080 + 96 + 20, + .htotal = 1080 + 96 + 20 + 48, + .vdisplay = 1920, + .vsync_start = 1920 + 5, + .vsync_end = 1920 + 5 + 2, + .vtotal = 1920 + 5 + 2 + 3, + .width_mm = 74, + .height_mm = 132, +}; + +static int td4322_innolux_fhd_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &td4322_innolux_fhd_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs td4322_innolux_fhd_panel_funcs = { + .prepare = td4322_innolux_fhd_prepare, + .unprepare = td4322_innolux_fhd_unprepare, + .get_modes = td4322_innolux_fhd_get_modes, +}; + +static int td4322_innolux_fhd_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct td4322_innolux_fhd *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->supplies[0].supply = "vdd"; + ctx->supplies[1].supply = "vddio"; + ctx->supplies[2].supply = "vsn"; + ctx->supplies[3].supply = "vsp"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + ret = PTR_ERR(ctx->reset_gpio); + dev_err(dev, "Failed to get reset-gpios: %d\n", ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &td4322_innolux_fhd_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) { + dev_err(dev, "Failed to get backlight: %d\n", ret); + return ret; + } + + drm_panel_add(&ctx->panel); + if (ret < 0) { + dev_err(dev, "Failed to add panel: %d\n", ret); + return ret; + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + return ret; + } + + return 0; +} + +static int td4322_innolux_fhd_remove(struct mipi_dsi_device *dsi) +{ + struct td4322_innolux_fhd *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id td4322_innolux_fhd_of_match[] = { + { .compatible = "sony,discovery-td4322-innolux" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, td4322_innolux_fhd_of_match); + +static struct mipi_dsi_driver td4322_innolux_fhd_driver = { + .probe = td4322_innolux_fhd_probe, + .remove = td4322_innolux_fhd_remove, + .driver = { + .name = "panel-td4322-innolux-fhd", + .of_match_table = td4322_innolux_fhd_of_match, + }, +}; +module_mipi_dsi_driver(td4322_innolux_fhd_driver); + +MODULE_AUTHOR("Konrad Dybcio "); +MODULE_DESCRIPTION("DRM driver for Sony TD4322 Innolux FHD panel"); +MODULE_LICENSE("GPL v2"); From f286ad30390d0f5d03745ea76956837978804be4 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Feb 2021 22:58:03 +0100 Subject: [PATCH 009/212] dt-bindings: display: panel: Document Sony Discovery TD4322 Innolux display panel Add bindings for the newly added panel. Signed-off-by: Konrad Dybcio --- .../panel/sony,discovery-td4322-innolux.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/sony,discovery-td4322-innolux.yaml diff --git a/Documentation/devicetree/bindings/display/panel/sony,discovery-td4322-innolux.yaml b/Documentation/devicetree/bindings/display/panel/sony,discovery-td4322-innolux.yaml new file mode 100644 index 000000000000..b7e7c717c844 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sony,discovery-td4322-innolux.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/sony,discovery-td4322-innolux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SONY Discovery TD4322 Innolux 6.0" 1080×1920 LCD Panel + +maintainers: + - Konrad Dybcio + +description: |+ + This panel seems to only be found in the Sony Xperia XA2 Ultra + smartphone and we have no straightforward way of + actually getting the correct model number, + as no schematics are released publicly. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: sony,discovery-td4322-innolux + reg: true + reset-gpios: true + vdd-supply: + description: core voltage supply + vddio-supply: + description: vddio supply + vsp-supply: + description: positive voltage supply + vsn-supply: + description: negative voltage supply + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vsp-supply + - vsn-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + reg = <0>; + compatible = "sony,discovery-td4322-innolux"; + + vdd-supply = <&pm8916_l8>; + vddio-supply = <&pm8916_l6>; + vsp-supply = <&lab>; + vsn-supply = <&ibb>; + reset-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>; + }; + }; From 87960325401d7485cf7bbe613506932c3e296784 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 30 Dec 2020 22:08:09 +0100 Subject: [PATCH 010/212] drm/panel: Add support for Sony TD4328 Tianma FHD+ panel This commit adds support for Sony TD4328 Truly Full HD Plus command mode DSI panel, as found in Sony Xperia XA2 Plus smartphones. Due to the nature of phone manufacturing, it is impossible to retrieve the actual panel name, hence the replacement one, detailing the device it's used on. Signed-off-by: Konrad Dybcio --- drivers/gpu/drm/panel/Kconfig | 10 + drivers/gpu/drm/panel/Makefile | 1 + .../drm/panel/panel-td4328-tianma-fhdplus.c | 277 ++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 710688899729..4001589ea311 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -567,6 +567,16 @@ config DRM_PANEL_SONY_PIONEER_TD4322_TRULY 1920 command mode display panel (as found on Sony Xperia XA2 devices). +config DRM_PANEL_SONY_VOYAGER_TD4328_TIANMA + tristate "Sony Voyager TD4328 Tianma panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable the Tianma TD4328 1080x + 2160 command mode panel (as found on Sony Xperia XA2 Plus + devices). + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index d014fbf46782..6ad42ea4a2fa 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o obj-$(CONFIG_DRM_PANEL_SONY_DISCOVERY_TD4322_INNOLUX) += panel-td4322-innolux-fhd.o obj-$(CONFIG_DRM_PANEL_SONY_PIONEER_TD4322_TRULY) += panel-td4322-truly-fhd.o +obj-$(CONFIG_DRM_PANEL_SONY_VOYAGER_TD4328_TIANMA) += panel-td4328-tianma-fhdplus.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o diff --git a/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c b/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c new file mode 100644 index 000000000000..d1de8b549bd8 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct td4328_tianma_fhdplus { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[4]; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline +struct td4328_tianma_fhdplus *to_td4328_tianma_fhdplus(struct drm_panel *panel) +{ + return container_of(panel, struct td4328_tianma_fhdplus, panel); +} + +#define dsi_generic_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void td4328_tianma_fhdplus_reset(struct td4328_tianma_fhdplus *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(30); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(150); +} + +static int td4328_tianma_fhdplus_on(struct td4328_tianma_fhdplus *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_generic_write_seq(dsi, 0xb0, 0x04); + dsi_generic_write_seq(dsi, 0xb3, 0x00, 0x00, 0x06); + dsi_generic_write_seq(dsi, 0xd6, 0x01); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + msleep(70); + + return 0; +} + +static int td4328_tianma_fhdplus_off(struct td4328_tianma_fhdplus *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(50); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(100); + + return 0; +} + +static int td4328_tianma_fhdplus_prepare(struct drm_panel *panel) +{ + struct td4328_tianma_fhdplus *ctx = to_td4328_tianma_fhdplus(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + td4328_tianma_fhdplus_reset(ctx); + + ret = td4328_tianma_fhdplus_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int td4328_tianma_fhdplus_unprepare(struct drm_panel *panel) +{ + struct td4328_tianma_fhdplus *ctx = to_td4328_tianma_fhdplus(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = td4328_tianma_fhdplus_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode td4328_tianma_fhdplus_mode = { + .clock = (1080 + 76 + 4 + 20) * (2160 + 5 + 1 + 30) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 76, + .hsync_end = 1080 + 76 + 4, + .htotal = 1080 + 76 + 4 + 20, + .vdisplay = 2160, + .vsync_start = 2160 + 5, + .vsync_end = 2160 + 5 + 1, + .vtotal = 2160 + 5 + 1 + 30, + .width_mm = 68, + .height_mm = 136, +}; + +static int td4328_tianma_fhdplus_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &td4328_tianma_fhdplus_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs td4328_tianma_fhdplus_panel_funcs = { + .prepare = td4328_tianma_fhdplus_prepare, + .unprepare = td4328_tianma_fhdplus_unprepare, + .get_modes = td4328_tianma_fhdplus_get_modes, +}; + +static int td4328_tianma_fhdplus_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct td4328_tianma_fhdplus *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->supplies[0].supply = "vdd"; + ctx->supplies[1].supply = "vddio"; + ctx->supplies[2].supply = "vsn"; + ctx->supplies[3].supply = "vsp"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + ret = PTR_ERR(ctx->reset_gpio); + dev_err(dev, "Failed to get reset-gpios: %d\n", ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &td4328_tianma_fhdplus_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) { + dev_err(dev, "Failed to get backlight: %d\n", ret); + return ret; + } + + drm_panel_add(&ctx->panel); + if (ret < 0) { + dev_err(dev, "Failed to add panel: %d\n", ret); + return ret; + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + return ret; + } + + return 0; +} + +static int td4328_tianma_fhdplus_remove(struct mipi_dsi_device *dsi) +{ + struct td4328_tianma_fhdplus *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id td4328_tianma_fhdplus_of_match[] = { + { .compatible = "sony,voyager-td4328-tianma" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, td4328_tianma_fhdplus_of_match); + +static struct mipi_dsi_driver td4328_tianma_fhdplus_driver = { + .probe = td4328_tianma_fhdplus_probe, + .remove = td4328_tianma_fhdplus_remove, + .driver = { + .name = "panel-td4328-tianma-fhdplus", + .of_match_table = td4328_tianma_fhdplus_of_match, + }, +}; +module_mipi_dsi_driver(td4328_tianma_fhdplus_driver); + +MODULE_AUTHOR("Konrad Dybcio "); +MODULE_DESCRIPTION("DRM driver for 7"); +MODULE_LICENSE("GPL v2"); From 914ee6b355e3784f6a6a1f92597dca0fdd6c1fe9 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Feb 2021 22:58:36 +0100 Subject: [PATCH 011/212] dt-bindings: display: panel: Document Sony Voyager TD4328 Tianma display panel Add bindings for the newly added panel. Signed-off-by: Konrad Dybcio --- .../panel/sony,voyager-td4328-tianma.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/sony,voyager-td4328-tianma.yaml diff --git a/Documentation/devicetree/bindings/display/panel/sony,voyager-td4328-tianma.yaml b/Documentation/devicetree/bindings/display/panel/sony,voyager-td4328-tianma.yaml new file mode 100644 index 000000000000..0d48b623ce59 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sony,voyager-td4328-tianma.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/sony,voyager-td4328-tianma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SONY Voyager TD4328 Tianma 6.0" 1080×2160 LCD Panel + +maintainers: + - Konrad Dybcio + +description: |+ + This panel seems to only be found in the Sony Xperia XA2 Plus + smartphone and we have no straightforward way of + actually getting the correct model number, + as no schematics are released publicly. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: sony,voyager-td4328-tianma + reg: true + reset-gpios: true + vdd-supply: + description: core voltage supply + vddio-supply: + description: vddio supply + vsp-supply: + description: positive voltage supply + vsn-supply: + description: negative voltage supply + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vsp-supply + - vsn-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + reg = <0>; + compatible = "sony,voyager-td4328-tianma"; + + vdd-supply = <&pm8916_l8>; + vddio-supply = <&pm8916_l6>; + vsp-supply = <&lab>; + vsn-supply = <&ibb>; + reset-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>; + }; + }; From b4e0021277ada3fff8d2d923e001228d1a967d59 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 30 Dec 2020 22:08:42 +0100 Subject: [PATCH 012/212] drm/panel-simple: Add nt36672a Truly FHD+ panel This adds support for nt36672a Truly FHD+ (2520x1080) video mode panel as found on Sony Xperia 10 (kirin) devices. Due to the nature of phone manufacturing, it is impossible to retrieve the actual panel name, hence the replacement one, detailing the device it's used on. Signed-off-by: Konrad Dybcio --- .../display/panel/panel-simple-dsi.yaml | 2 ++ drivers/gpu/drm/panel/panel-simple.c | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index fbd71669248f..ed6f96ab6aa5 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -55,6 +55,8 @@ properties: - samsung,sofef00 # Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel - tdo,tl070wsh30 + # Sony Kirin nt36672a Truly FHD+ IPS LCD panel + - sony,kirin-nt36672a-truly reg: maxItems: 1 diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9b6c4e6c38a1..1ab6bd9ae171 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5158,6 +5158,36 @@ static const struct panel_desc_dsi osd101t2045_53ts = { .lanes = 4, }; +static const struct drm_display_mode sony_kirin_nt36672a_truly_mode = { + .clock = (1080 + 25 + 12 + 120) * (2520 + 12 + 4 + 10) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 25, + .hsync_end = 1080 + 25 + 12, + .htotal = 1080 + 25 + 12 + 120, + .vdisplay = 2520, + .vsync_start = 2520 + 12, + .vsync_end = 2520 + 12 + 4, + .vtotal = 2520 + 12 + 4 + 10, + .width_mm = 60, + .height_mm = 139, +}; + +static const struct panel_desc_dsi sony_kirin_nt36672a_truly = { + .desc = { + .modes = &sony_kirin_nt36672a_truly_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 60, + .height = 139, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static const struct of_device_id dsi_of_match[] = { { .compatible = "auo,b080uan01", @@ -5180,6 +5210,9 @@ static const struct of_device_id dsi_of_match[] = { }, { .compatible = "osddisplays,osd101t2045-53ts", .data = &osd101t2045_53ts + }, { + .compatible = "sony,kirin-nt36672a-truly", + .data = &sony_kirin_nt36672a_truly }, { /* sentinel */ } From e7523b153d4c85eb90e9869e1eef95896102cdcd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sat, 18 Jul 2020 18:41:17 +0200 Subject: [PATCH 013/212] drm/panel-simple: Add nt36672a Tianma FHD+ panel This adds support for nt36672a Tianma FHD+ (2520x1080) video mode panel as found on Sony Xperia 10 Plus (mermaid) devices. Due to the nature of phone manufacturing, it is impossible to retrieve the actual panel name, hence the replacement one, detailing the device it's used on. Signed-off-by: Konrad Dybcio --- .../display/panel/panel-simple-dsi.yaml | 2 ++ drivers/gpu/drm/panel/panel-simple.c | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index ed6f96ab6aa5..358969aab60a 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -57,6 +57,8 @@ properties: - tdo,tl070wsh30 # Sony Kirin nt36672a Truly FHD+ IPS LCD panel - sony,kirin-nt36672a-truly + # Sony mermaid nt36672a Tianma FHD+ IPS LCD panel + - sony,mermaid-nt36672a-tianma reg: maxItems: 1 diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 1ab6bd9ae171..29597db4e25f 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5188,6 +5188,36 @@ static const struct panel_desc_dsi sony_kirin_nt36672a_truly = { .lanes = 4, }; +static const struct drm_display_mode sony_mermaid_nt36672a_tianma_mode = { + .clock = (1080 + 102 + 20 + 40) * (2520 + 10 + 2 + 8) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 102, + .hsync_end = 1080 + 102 + 20, + .htotal = 1080 + 102 + 20 + 40, + .vdisplay = 2520, + .vsync_start = 2520 + 10, + .vsync_end = 2520 + 10 + 2, + .vtotal = 2520 + 10 + 2 + 8, + .width_mm = 65, + .height_mm = 151, +}; + +static const struct panel_desc_dsi sony_mermaid_nt36672a_tianma = { + .desc = { + .modes = &sony_mermaid_nt36672a_tianma_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 65, + .height = 151, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static const struct of_device_id dsi_of_match[] = { { .compatible = "auo,b080uan01", @@ -5213,6 +5243,9 @@ static const struct of_device_id dsi_of_match[] = { }, { .compatible = "sony,kirin-nt36672a-truly", .data = &sony_kirin_nt36672a_truly + }, { + .compatible = "sony,mermaid-nt36672a-tianma", + .data = &sony_mermaid_nt36672a_tianma }, { /* sentinel */ } From 0fde91e372b52c11eb19860c2a9886583ca6d7fc Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 20 Sep 2020 14:11:50 +0200 Subject: [PATCH 014/212] drm/msm/mdp5: Fix SDM630/660 MDP5 configuration SDM630 has two usable layer mixers (LM0, LM2) and pingpongs (PP0, PP2), with writeback located on LM2, has no RGB pipe and does not support source split. SDM660 has four usable layer mixers (LM0, LM1, LM2, LM5) and three pingpongs (PP0, PP1, PP4), with writeback located on LM2, has no RGB pipes and does support source split; also, the HDMI is on INTF0. As cherry on top, let's also write the correct flush_hw_mask for both MDP configurations to stop flushing useless and unused pipes for performance purposes. Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c | 56 +++++++----------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c index 9741544ffc35..92d85ba69488 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c @@ -950,13 +950,12 @@ static const struct mdp5_cfg_hw sdm630_config = { .mdp = { .count = 1, .caps = MDP_CAP_CDM | - MDP_CAP_SRC_SPLIT | 0, }, .ctl = { .count = 5, .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 }, - .flush_hw_mask = 0xf4ffffff, + .flush_hw_mask = 0xf54338c1, }, .pipe_vig = { .count = 1, @@ -969,16 +968,6 @@ static const struct mdp5_cfg_hw sdm630_config = { MDP_PIPE_CAP_SW_PIX_EXT | 0, }, - .pipe_rgb = { - .count = 4, - .base = { 0x14000, 0x16000, 0x18000, 0x1a000 }, - .caps = MDP_PIPE_CAP_HFLIP | - MDP_PIPE_CAP_VFLIP | - MDP_PIPE_CAP_SCALE | - MDP_PIPE_CAP_DECIMATION | - MDP_PIPE_CAP_SW_PIX_EXT | - 0, - }, .pipe_dma = { .count = 2, /* driver supports max of 2 currently */ .base = { 0x24000, 0x26000, 0x28000 }, @@ -998,13 +987,12 @@ static const struct mdp5_cfg_hw sdm630_config = { }, .lm = { - .count = 2, - .base = { 0x44000, 0x46000 }, + .count = 3, /* LM_1 seems broken */ + .base = { 0x44000, 0x45000, 0x46000 }, .instances = { { .id = 0, .pp = 0, .dspp = 0, - .caps = MDP_LM_CAP_DISPLAY | - MDP_LM_CAP_PAIR, }, - { .id = 1, .pp = 1, .dspp = -1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = -1, .caps = MDP_LM_CAP_WB, }, }, .nb_stages = 8, @@ -1020,8 +1008,8 @@ static const struct mdp5_cfg_hw sdm630_config = { .base = { 0x78000, 0x78800 }, }, .pp = { - .count = 3, - .base = { 0x70000, 0x71000, 0x72000 }, + .count = 3, /* Do not use PP1 */ + .base = { 0x70000, 0x70800, 0x71000 }, }, .cdm = { .count = 1, @@ -1049,11 +1037,11 @@ static const struct mdp5_cfg_hw sdm660_config = { .ctl = { .count = 5, .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 }, - .flush_hw_mask = 0xf4ffffff, + .flush_hw_mask = 0xf5437bc3, }, .pipe_vig = { .count = 2, - .base = { 0x04000, 0x6000 }, + .base = { 0x04000, 0x06000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | @@ -1062,16 +1050,6 @@ static const struct mdp5_cfg_hw sdm660_config = { MDP_PIPE_CAP_SW_PIX_EXT | 0, }, - .pipe_rgb = { - .count = 4, - .base = { 0x14000, 0x16000, 0x18000, 0x1a000 }, - .caps = MDP_PIPE_CAP_HFLIP | - MDP_PIPE_CAP_VFLIP | - MDP_PIPE_CAP_SCALE | - MDP_PIPE_CAP_DECIMATION | - MDP_PIPE_CAP_SW_PIX_EXT | - 0, - }, .pipe_dma = { .count = 2, /* driver supports max of 2 currently */ .base = { 0x24000, 0x26000, 0x28000 }, @@ -1091,19 +1069,19 @@ static const struct mdp5_cfg_hw sdm660_config = { }, .lm = { - .count = 4, - .base = { 0x44000, 0x45000, 0x46000, 0x49000 }, + .count = 6, /* Do not use LM_3, LM_4 */ + .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, .instances = { { .id = 0, .pp = 0, .dspp = 0, .caps = MDP_LM_CAP_DISPLAY | MDP_LM_CAP_PAIR, }, { .id = 1, .pp = 1, .dspp = 1, .caps = MDP_LM_CAP_DISPLAY, }, - { .id = 2, .pp = 2, .dspp = -1, + { .id = 2, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 5, .pp = 4, .dspp = -1, .caps = MDP_LM_CAP_DISPLAY | MDP_LM_CAP_PAIR, }, - { .id = 3, .pp = 3, .dspp = -1, - .caps = MDP_LM_CAP_WB, }, }, .nb_stages = 8, .max_width = 2560, @@ -1118,7 +1096,7 @@ static const struct mdp5_cfg_hw sdm660_config = { .base = { 0x78000, 0x78800 }, }, .pp = { - .count = 5, + .count = 5, /* Do not use PP_2 and PP_3 */ .base = { 0x70000, 0x70800, 0x71000, 0x71800, 0x72000 }, }, .cdm = { @@ -1132,10 +1110,10 @@ static const struct mdp5_cfg_hw sdm660_config = { .intf = { .base = { 0x6a000, 0x6a800, 0x6b000, 0x6b800 }, .connect = { - [0] = INTF_DISABLED, + [0] = INTF_HDMI, [1] = INTF_DSI, [2] = INTF_DSI, - [3] = INTF_HDMI, + [3] = INTF_DISABLED, }, }, .max_clk = 412500000, From 1e696c47d1c94ad4bed0de39d63e530cad4dab3a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sat, 18 Jul 2020 22:01:12 +0200 Subject: [PATCH 015/212] remoteproc: Add support for SDM630/636/660 MSS remoteproc This adds support for sdm630/636/660 modem subsystem remote processor. Currently an issue persists, where the remote processor will restart itself every 40 seconds, but it seems to work otherwise. Signed-off-by: Konrad Dybcio --- drivers/remoteproc/qcom_q6v5_mss.c | 108 +++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 423b31dfa574..07235b07b01c 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -68,6 +68,7 @@ #define QDSP6SS_MEM_PWR_CTL 0x0B0 #define QDSP6V6SS_MEM_PWR_CTL 0x034 #define QDSP6SS_STRAP_ACC 0x110 +#define QDSP6V62SS_BHS_STATUS 0x0C4 /* AXI Halt Register Offsets */ #define AXI_HALTREQ_REG 0x0 @@ -109,6 +110,9 @@ #define QDSP6v56_CLAMP_QMC_MEM BIT(22) #define QDSP6SS_XO_CBCR 0x0038 #define QDSP6SS_ACC_OVERRIDE_VAL 0x20 +#define QDSP6v55_BHS_EN_REST_ACK BIT(0) + +#define BHS_CHECK_MAX_LOOPS (200) /* QDSP6v65 parameters */ #define QDSP6SS_CORE_CBCR 0x20 @@ -216,6 +220,7 @@ enum { MSS_MSM8996, MSS_MSM8998, MSS_SC7180, + MSS_SDM660, MSS_SDM845, }; @@ -725,6 +730,80 @@ static int q6v5proc_reset(struct q6v5 *qproc) val |= readl(qproc->reg_base + mem_pwr_ctl); udelay(1); } + /* Remove word line clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_WL; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } else if (qproc->version == MSS_SDM660) { + int mem_pwr_ctl; + + /* Override the ACC value if required */ + writel(QDSP6SS_ACC_OVERRIDE_VAL, + qproc->reg_base + QDSP6SS_STRAP_ACC); + + /* Assert resets, stop core */ + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); + + /* BHS require xo cbcr to be enabled */ + val = readl(qproc->reg_base + QDSP6SS_XO_CBCR); + val |= 1; + writel(val, qproc->reg_base + QDSP6SS_XO_CBCR); + + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= QDSP6v56_BHS_ON; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + //val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + mb(); + udelay(1); + + for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) { + if (readl_relaxed(qproc->reg_base + QDSP6V62SS_BHS_STATUS) + & QDSP6v55_BHS_EN_REST_ACK) + break; + udelay(1); + } + if (!i) { + pr_err("%s: BHS_EN_REST_ACK not set!\n", __func__); + return -ETIMEDOUT; + } + + /* Put LDO in bypass mode */ + val |= QDSP6v56_LDO_BYP; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Remove QMC_MEM clamp */ + val &= ~QDSP6v56_CLAMP_QMC_MEM; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert QDSP6 compiler memory clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_QMC_MEM; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + mem_pwr_ctl = QDSP6V6SS_MEM_PWR_CTL; + i = 29; + + val = readl(qproc->reg_base + mem_pwr_ctl); //? + for (; i >= 0; i--) { + val |= BIT(i); + writel(val, qproc->reg_base + mem_pwr_ctl); + /* + * Read back value to ensure the write is done then + * wait for 1us for both memory peripheral and data + * array to turn on. + */ + val |= readl(qproc->reg_base + mem_pwr_ctl); + udelay(1); + } + /* Remove word line clamp */ val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); val &= ~QDSP6v56_CLAMP_WL; @@ -756,6 +835,7 @@ static int q6v5proc_reset(struct q6v5 *qproc) val |= Q6SS_L2DATA_SLP_NRET_N_0; writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); } + /* Remove IO clamp */ val &= ~Q6SS_CLAMP_IO; writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); @@ -1884,6 +1964,33 @@ static const struct rproc_hexagon_res sc7180_mss = { .version = MSS_SC7180, }; +static const struct rproc_hexagon_res sdm660_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_clk_names = (char*[]){ + "xo", + "qdss", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "gpll0_mss", + "mnoc_axi", + "snoc_axi", + "mem", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .need_mem_protection = true, + .has_alt_reset = false, + .has_spare_reg = false, + .version = MSS_SDM660, +}; + static const struct rproc_hexagon_res sdm845_mss = { .hexagon_mba_image = "mba.mbn", .proxy_clk_names = (char*[]){ @@ -2078,6 +2185,7 @@ static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss}, { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss}, + { .compatible = "qcom,sdm660-mss-pil", .data = &sdm660_mss}, { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss}, { }, }; From f71ea3441bcba4a5df7dddac675d60209454e39f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sun, 31 Jan 2021 21:01:36 +0100 Subject: [PATCH 016/212] arm64: dts: qcom: sdm630: Enable SMMUs Now that the quirks are in, SMMUs are fine to enable. Signed-off-by: Konrad Dybcio --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index f045ff295780..f602d143ca09 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -646,8 +646,6 @@ , , ; - - status = "disabled"; }; a2noc: interconnect@1704000 { @@ -1125,8 +1123,6 @@ , , ; - - status = "disabled"; }; gpucc: clock-controller@5065000 { @@ -1172,8 +1168,6 @@ , , ; - - status = "disabled"; }; spmi_bus: spmi@800f000 { @@ -2061,8 +2055,6 @@ , , ; - - status = "disabled"; }; adsp_pil: remoteproc@15700000 { From 328f064e3ac09c52060c1b4b0ada76732a758909 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 21 Mar 2021 19:19:30 +0100 Subject: [PATCH 017/212] 660 defconfig [Fixed up for 5.15 by Jami] --- arch/arm64/configs/defconfig | 1282 +++++++--------------------------- 1 file changed, 262 insertions(+), 1020 deletions(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 545197bc0501..ed67c7f9a12d 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -3,139 +3,84 @@ CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_JIT_ALWAYS_ON=y CONFIG_PREEMPT=y CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_NUMA_BALANCING=y CONFIG_MEMCG=y -CONFIG_MEMCG_SWAP=y CONFIG_BLK_CGROUP=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_HUGETLB=y -CONFIG_CPUSETS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y -CONFIG_BLK_DEV_INITRD=y +CONFIG_BOOT_CONFIG=y CONFIG_KALLSYMS_ALL=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y -CONFIG_ARCH_ACTIONS=y -CONFIG_ARCH_AGILEX=y -CONFIG_ARCH_N5X=y -CONFIG_ARCH_SUNXI=y -CONFIG_ARCH_ALPINE=y -CONFIG_ARCH_APPLE=y -CONFIG_ARCH_BCM2835=y -CONFIG_ARCH_BCM4908=y -CONFIG_ARCH_BCM_IPROC=y -CONFIG_ARCH_BERLIN=y -CONFIG_ARCH_BRCMSTB=y -CONFIG_ARCH_EXYNOS=y -CONFIG_ARCH_K3=y -CONFIG_ARCH_LAYERSCAPE=y -CONFIG_ARCH_LG1K=y -CONFIG_ARCH_HISI=y -CONFIG_ARCH_KEEMBAY=y -CONFIG_ARCH_MEDIATEK=y -CONFIG_ARCH_MESON=y -CONFIG_ARCH_MVEBU=y -CONFIG_ARCH_MXC=y CONFIG_ARCH_QCOM=y -CONFIG_ARCH_RENESAS=y -CONFIG_ARCH_ROCKCHIP=y -CONFIG_ARCH_S32=y -CONFIG_ARCH_SEATTLE=y -CONFIG_ARCH_INTEL_SOCFPGA=y -CONFIG_ARCH_SYNQUACER=y -CONFIG_ARCH_TEGRA=y -CONFIG_ARCH_SPRD=y -CONFIG_ARCH_THUNDER=y -CONFIG_ARCH_THUNDER2=y -CONFIG_ARCH_UNIPHIER=y -CONFIG_ARCH_VEXPRESS=y -CONFIG_ARCH_VISCONTI=y -CONFIG_ARCH_XGENE=y -CONFIG_ARCH_ZX=y -CONFIG_ARCH_ZYNQMP=y CONFIG_ARM64_VA_BITS_48=y CONFIG_SCHED_MC=y -CONFIG_SCHED_SMT=y -CONFIG_NUMA=y -CONFIG_SECCOMP=y +CONFIG_NR_CPUS=4 +CONFIG_HZ_100=y CONFIG_KEXEC=y -CONFIG_KEXEC_FILE=y CONFIG_CRASH_DUMP=y -CONFIG_XEN=y CONFIG_COMPAT=y CONFIG_RANDOMIZE_BASE=y +# CONFIG_EFI is not set CONFIG_HIBERNATION=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_DEBUG=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y CONFIG_ENERGY_MODEL=y +CONFIG_CPU_IDLE=y CONFIG_ARM_CPUIDLE=y CONFIG_ARM_PSCI_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m -CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPUFREQ_DT=y -CONFIG_ACPI_CPPC_CPUFREQ=m -CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=m -CONFIG_ARM_ARMADA_37XX_CPUFREQ=y -CONFIG_ARM_SCPI_CPUFREQ=y -CONFIG_ARM_IMX_CPUFREQ_DT=m CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y CONFIG_ARM_QCOM_CPUFREQ_HW=y -CONFIG_ARM_RASPBERRYPI_CPUFREQ=m -CONFIG_ARM_SCMI_CPUFREQ=y -CONFIG_ARM_TEGRA186_CPUFREQ=y -CONFIG_QORIQ_CPUFREQ=y -CONFIG_ARM_SCMI_PROTOCOL=y -CONFIG_ARM_SCPI_PROTOCOL=y -CONFIG_RASPBERRYPI_FIRMWARE=y -CONFIG_INTEL_STRATIX10_SERVICE=y -CONFIG_INTEL_STRATIX10_RSU=m -CONFIG_QCOM_SCM=y -CONFIG_EFI_CAPSULE_LOADER=y -CONFIG_IMX_SCU=y -CONFIG_IMX_SCU_PD=y -CONFIG_ACPI=y -CONFIG_ACPI_APEI=y -CONFIG_ACPI_APEI_GHES=y -CONFIG_ACPI_APEI_PCIEAER=y -CONFIG_ACPI_APEI_MEMORY_FAILURE=y -CONFIG_ACPI_APEI_EINJ=y -CONFIG_VIRTUALIZATION=y -CONFIG_KVM=y +CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y -CONFIG_CRYPTO_SHA512_ARM64_CE=m -CONFIG_CRYPTO_SHA3_ARM64=m -CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_SHA512_ARM64_CE=y +CONFIG_CRYPTO_SHA3_ARM64=y +CONFIG_CRYPTO_SM3_ARM64_CE=y +CONFIG_CRYPTO_SM4_ARM64_CE=y CONFIG_CRYPTO_GHASH_ARM64_CE=y -CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m CONFIG_CRYPTO_AES_ARM64_CE_CCM=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y -CONFIG_CRYPTO_CHACHA20_NEON=m -CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_CRYPTO_POLY1305_NEON=y +CONFIG_CRYPTO_NHPOLY1305_NEON=y +CONFIG_CRYPTO_AES_ARM64_BS=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_KSM=y CONFIG_MEMORY_FAILURE=y CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_FRONTSWAP=y +CONFIG_CMA=y +CONFIG_ZSMALLOC=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -144,1072 +89,369 @@ CONFIG_IP_MULTICAST=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y -CONFIG_IPV6=m CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m -CONFIG_NETFILTER_XT_TARGET_LOG=m -CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_MANGLE=m -CONFIG_IP6_NF_IPTABLES=m -CONFIG_IP6_NF_FILTER=m -CONFIG_IP6_NF_TARGET_REJECT=m -CONFIG_IP6_NF_MANGLE=m -CONFIG_IP6_NF_NAT=m -CONFIG_IP6_NF_TARGET_MASQUERADE=m -CONFIG_BRIDGE=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_IP_NF_IPTABLES=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_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_NAT=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +CONFIG_BRIDGE=y CONFIG_BRIDGE_VLAN_FILTERING=y -CONFIG_NET_DSA=m -CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q=y CONFIG_VLAN_8021Q_GVRP=y CONFIG_VLAN_8021Q_MVRP=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_CBS=m -CONFIG_NET_SCH_ETF=m -CONFIG_NET_SCH_TAPRIO=m -CONFIG_NET_SCH_MQPRIO=m -CONFIG_NET_SCH_INGRESS=m -CONFIG_NET_CLS_BASIC=m -CONFIG_NET_CLS_FLOWER=m -CONFIG_NET_CLS_ACT=y -CONFIG_NET_ACT_GACT=m -CONFIG_NET_ACT_MIRRED=m -CONFIG_NET_ACT_GATE=m -CONFIG_QRTR=m -CONFIG_QRTR_SMD=m -CONFIG_QRTR_TUN=m -CONFIG_BPF_JIT=y -CONFIG_CAN=m -CONFIG_CAN_RCAR=m -CONFIG_CAN_RCAR_CANFD=m -CONFIG_CAN_FLEXCAN=m -CONFIG_BT=m -CONFIG_BT_HIDP=m -# CONFIG_BT_HS is not set -# CONFIG_BT_LE is not set +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_QRTR_TUN=y +CONFIG_BT=y +CONFIG_BT_HIDP=y CONFIG_BT_LEDS=y -# CONFIG_BT_DEBUGFS is not set -CONFIG_BT_HCIBTUSB=m -CONFIG_BT_HCIUART=m -CONFIG_BT_HCIUART_LL=y -CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_QCA=y -CONFIG_CFG80211=m -CONFIG_MAC80211=m +CONFIG_BT_QCOMSMD=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y -CONFIG_RFKILL=m -CONFIG_NET_9P=y -CONFIG_NET_9P_VIRTIO=y -CONFIG_NFC=m -CONFIG_NFC_NCI=m -CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_RFKILL=y +CONFIG_NFC=y +CONFIG_NFC_NCI=y +CONFIG_NFC_NXP_NCI=y +CONFIG_NFC_NXP_NCI_I2C=y +CONFIG_NFC_S3FWRN5_I2C=y CONFIG_PCI=y -CONFIG_PCIEPORTBUS=y -CONFIG_PCI_IOV=y -CONFIG_PCI_PASID=y -CONFIG_HOTPLUG_PCI=y -CONFIG_HOTPLUG_PCI_ACPI=y -CONFIG_PCI_AARDVARK=y -CONFIG_PCI_TEGRA=y -CONFIG_PCIE_RCAR_HOST=y -CONFIG_PCIE_RCAR_EP=y -CONFIG_PCI_HOST_GENERIC=y -CONFIG_PCI_XGENE=y -CONFIG_PCIE_ALTERA=y -CONFIG_PCIE_ALTERA_MSI=y -CONFIG_PCI_HOST_THUNDER_PEM=y -CONFIG_PCI_HOST_THUNDER_ECAM=y -CONFIG_PCIE_ROCKCHIP_HOST=m -CONFIG_PCIE_BRCMSTB=m -CONFIG_PCI_IMX6=y -CONFIG_PCI_LAYERSCAPE=y -CONFIG_PCIE_LAYERSCAPE_GEN4=y -CONFIG_PCI_HISI=y CONFIG_PCIE_QCOM=y -CONFIG_PCIE_ARMADA_8K=y -CONFIG_PCIE_KIRIN=y -CONFIG_PCIE_HISI_STB=y -CONFIG_PCIE_TEGRA194_HOST=m -CONFIG_PCI_ENDPOINT=y -CONFIG_PCI_ENDPOINT_CONFIGFS=y -CONFIG_PCI_EPF_TEST=m +CONFIG_UEVENT_HELPER=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_FW_LOADER_USER_HELPER=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y -CONFIG_HISILICON_LPC=y -CONFIG_FSL_MC_BUS=y -CONFIG_TEGRA_ACONNECT=m -CONFIG_GNSS=m -CONFIG_GNSS_MTK_SERIAL=m -CONFIG_MTD=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_CFI=y -CONFIG_MTD_CFI_ADV_OPTIONS=y -CONFIG_MTD_CFI_INTELEXT=y -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_CFI_STAA=y -CONFIG_MTD_PHYSMAP=y -CONFIG_MTD_PHYSMAP_OF=y -CONFIG_MTD_DATAFLASH=y -CONFIG_MTD_SST25L=y -CONFIG_MTD_RAW_NAND=y -CONFIG_MTD_NAND_DENALI_DT=y -CONFIG_MTD_NAND_MARVELL=y -CONFIG_MTD_NAND_FSL_IFC=y -CONFIG_MTD_NAND_QCOM=y -CONFIG_MTD_SPI_NOR=y -CONFIG_MTK_DEVAPC=m -CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_FW_LOADER_COMPRESS=y +# CONFIG_QCOM_EBI2 is not set +CONFIG_OF_OVERLAY=y +CONFIG_ZRAM=y +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM_MEMORY_TRACKING=y CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_NBD=m -CONFIG_VIRTIO_BLK=y -CONFIG_BLK_DEV_NVME=m -CONFIG_SRAM=y -CONFIG_PCI_ENDPOINT_TEST=m -CONFIG_EEPROM_AT24=m -CONFIG_EEPROM_AT25=m -CONFIG_UACCE=m -# CONFIG_SCSI_PROC_FS is not set -CONFIG_BLK_DEV_SD=y -CONFIG_SCSI_SAS_ATA=y -CONFIG_SCSI_HISI_SAS=y -CONFIG_SCSI_HISI_SAS_PCI=y -CONFIG_MEGARAID_SAS=y -CONFIG_SCSI_MPT3SAS=m -CONFIG_SCSI_UFSHCD=y -CONFIG_SCSI_UFSHCD_PLATFORM=y -CONFIG_SCSI_UFS_QCOM=m -CONFIG_SCSI_UFS_HISI=y -CONFIG_SCSI_UFS_EXYNOS=y -CONFIG_ATA=y -CONFIG_SATA_AHCI=y -CONFIG_SATA_AHCI_PLATFORM=y -CONFIG_AHCI_CEVA=y -CONFIG_AHCI_MVEBU=y -CONFIG_AHCI_XGENE=y -CONFIG_AHCI_QORIQ=y -CONFIG_SATA_SIL24=y -CONFIG_SATA_RCAR=y -CONFIG_PATA_PLATFORM=y -CONFIG_PATA_OF_PLATFORM=y +CONFIG_BLK_DEV_RAM=y +CONFIG_QCOM_COINCELL=y +CONFIG_QCOM_FASTRPC=y CONFIG_MD=y -CONFIG_BLK_DEV_MD=m -CONFIG_BLK_DEV_DM=m -CONFIG_DM_MIRROR=m -CONFIG_DM_ZERO=m +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y CONFIG_NETDEVICES=y -CONFIG_MACVLAN=m -CONFIG_MACVTAP=m +CONFIG_MACVLAN=y +CONFIG_MACVTAP=y +CONFIG_VXLAN=y CONFIG_TUN=y -CONFIG_VETH=m -CONFIG_VIRTIO_NET=y -CONFIG_NET_DSA_MSCC_FELIX=m -CONFIG_AMD_XGBE=y -CONFIG_NET_XGENE=y -CONFIG_ATL1C=m -CONFIG_BCMGENET=m -CONFIG_BNX2X=m -CONFIG_MACB=y -CONFIG_THUNDER_NIC_PF=y -CONFIG_FEC=y -CONFIG_FSL_FMAN=y -CONFIG_FSL_DPAA_ETH=y -CONFIG_FSL_DPAA2_ETH=y -CONFIG_FSL_ENETC=y -CONFIG_FSL_ENETC_VF=y -CONFIG_FSL_ENETC_QOS=y -CONFIG_HIX5HD2_GMAC=y -CONFIG_HNS_DSAF=y -CONFIG_HNS_ENET=y -CONFIG_HNS3=y -CONFIG_HNS3_HCLGE=y -CONFIG_HNS3_ENET=y -CONFIG_E1000=y -CONFIG_E1000E=y -CONFIG_IGB=y -CONFIG_IGBVF=y -CONFIG_MVNETA=y -CONFIG_MVPP2=y -CONFIG_SKY2=y -CONFIG_MLX4_EN=m -CONFIG_MLX5_CORE=m -CONFIG_MLX5_CORE_EN=y -CONFIG_QCOM_EMAC=m -CONFIG_RMNET=m -CONFIG_SH_ETH=y -CONFIG_RAVB=y -CONFIG_SMC91X=y -CONFIG_SMSC911X=y -CONFIG_SNI_AVE=y -CONFIG_SNI_NETSEC=y -CONFIG_STMMAC_ETH=m -CONFIG_TI_K3_AM65_CPSW_NUSS=y -CONFIG_QCOM_IPA=m -CONFIG_MDIO_BUS_MUX_MMIOREG=y -CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y -CONFIG_AQUANTIA_PHY=y -CONFIG_BCM54140_PHY=m -CONFIG_MARVELL_PHY=m -CONFIG_MARVELL_10G_PHY=m -CONFIG_MESON_GXL_PHY=m -CONFIG_MICREL_PHY=y -CONFIG_MICROSEMI_PHY=y -CONFIG_AT803X_PHY=y -CONFIG_REALTEK_PHY=y -CONFIG_ROCKCHIP_PHY=y -CONFIG_VITESSE_PHY=y -CONFIG_USB_PEGASUS=m -CONFIG_USB_RTL8150=m -CONFIG_USB_RTL8152=m -CONFIG_USB_LAN78XX=m -CONFIG_USB_USBNET=m -CONFIG_USB_NET_DM9601=m -CONFIG_USB_NET_SR9800=m -CONFIG_USB_NET_SMSC75XX=m -CONFIG_USB_NET_SMSC95XX=m -CONFIG_USB_NET_PLUSB=m -CONFIG_USB_NET_MCS7830=m -CONFIG_ATH10K=m -CONFIG_ATH10K_PCI=m -CONFIG_ATH10K_SNOC=m -CONFIG_BRCMFMAC=m -CONFIG_MWIFIEX=m -CONFIG_MWIFIEX_PCIE=m -CONFIG_WL18XX=m -CONFIG_WLCORE_SDIO=m +CONFIG_VETH=y +CONFIG_QCOM_IPA=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_ASYNC=y +CONFIG_USB_USBNET=y +CONFIG_ATH10K=y +CONFIG_ATH10K_SNOC=y +CONFIG_WCN36XX=y +CONFIG_INPUT_MATRIXKMAP=y CONFIG_INPUT_EVDEV=y -CONFIG_KEYBOARD_ADC=m +# CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=y -CONFIG_KEYBOARD_SNVS_PWRKEY=m -CONFIG_KEYBOARD_IMX_SC_KEY=m -CONFIG_KEYBOARD_CROS_EC=y +CONFIG_KEYBOARD_TM2_TOUCHKEY=y +# CONFIG_MOUSE_PS2 is not set CONFIG_INPUT_TOUCHSCREEN=y -CONFIG_TOUCHSCREEN_ATMEL_MXT=m -CONFIG_TOUCHSCREEN_GOODIX=m -CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_MMS114=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=y CONFIG_INPUT_MISC=y CONFIG_INPUT_PM8941_PWRKEY=y -CONFIG_INPUT_PM8XXX_VIBRATOR=m -CONFIG_INPUT_PWM_VIBRA=m -CONFIG_INPUT_HISI_POWERKEY=y -# CONFIG_SERIO_SERPORT is not set -CONFIG_SERIO_AMBAKMI=y +CONFIG_INPUT_PM8XXX_VIBRATOR=y +CONFIG_INPUT_GPIO_VIBRA=y +CONFIG_RMI4_CORE=y +CONFIG_RMI4_I2C=y +CONFIG_RMI4_F11=y +CONFIG_RMI4_F12=y +# CONFIG_SERIO is not set CONFIG_LEGACY_PTY_COUNT=16 -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -CONFIG_SERIAL_8250_BCM2835AUX=y -CONFIG_SERIAL_8250_DW=y -CONFIG_SERIAL_8250_OMAP=y -CONFIG_SERIAL_8250_MT6577=y -CONFIG_SERIAL_8250_UNIPHIER=y -CONFIG_SERIAL_OF_PLATFORM=y -CONFIG_SERIAL_AMBA_PL011=y -CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -CONFIG_SERIAL_MESON=y -CONFIG_SERIAL_MESON_CONSOLE=y -CONFIG_SERIAL_SAMSUNG=y -CONFIG_SERIAL_SAMSUNG_CONSOLE=y -CONFIG_SERIAL_TEGRA=y -CONFIG_SERIAL_TEGRA_TCU=y -CONFIG_SERIAL_IMX=y -CONFIG_SERIAL_IMX_CONSOLE=y -CONFIG_SERIAL_SH_SCI=y CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y -CONFIG_SERIAL_QCOM_GENI=y -CONFIG_SERIAL_QCOM_GENI_CONSOLE=y -CONFIG_SERIAL_XILINX_PS_UART=y -CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y -CONFIG_SERIAL_FSL_LPUART=y -CONFIG_SERIAL_FSL_LPUART_CONSOLE=y -CONFIG_SERIAL_FSL_LINFLEXUART=y -CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y -CONFIG_SERIAL_MVEBU_UART=y -CONFIG_SERIAL_OWL=y CONFIG_SERIAL_DEV_BUS=y -CONFIG_VIRTIO_CONSOLE=y -CONFIG_IPMI_HANDLER=m -CONFIG_IPMI_DEVICE_INTERFACE=m -CONFIG_IPMI_SI=m -CONFIG_TCG_TPM=y -CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_HW_RANDOM=y CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y -CONFIG_I2C_MUX_PCA954x=y -CONFIG_I2C_BCM2835=m -CONFIG_I2C_DESIGNWARE_PLATFORM=y -CONFIG_I2C_GPIO=m -CONFIG_I2C_IMX=y -CONFIG_I2C_IMX_LPI2C=y -CONFIG_I2C_MESON=y -CONFIG_I2C_MT65XX=y -CONFIG_I2C_MV64XXX=y -CONFIG_I2C_OMAP=y -CONFIG_I2C_OWL=y -CONFIG_I2C_PXA=y -CONFIG_I2C_QCOM_CCI=m -CONFIG_I2C_QCOM_GENI=m +CONFIG_I2C_GPIO=y +CONFIG_I2C_QCOM_CCI=y CONFIG_I2C_QUP=y -CONFIG_I2C_RK3X=y -CONFIG_I2C_SH_MOBILE=y -CONFIG_I2C_TEGRA=y -CONFIG_I2C_UNIPHIER_F=y -CONFIG_I2C_RCAR=y -CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_I2C_SLAVE=y CONFIG_SPI=y -CONFIG_SPI_ARMADA_3700=y -CONFIG_SPI_BCM2835=m -CONFIG_SPI_BCM2835AUX=m -CONFIG_SPI_DESIGNWARE=m -CONFIG_SPI_DW_DMA=y -CONFIG_SPI_DW_MMIO=m -CONFIG_SPI_FSL_LPSPI=y -CONFIG_SPI_FSL_QUADSPI=y -CONFIG_SPI_NXP_FLEXSPI=y -CONFIG_SPI_IMX=m -CONFIG_SPI_FSL_DSPI=y -CONFIG_SPI_MESON_SPICC=m -CONFIG_SPI_MESON_SPIFC=m -CONFIG_SPI_ORION=y -CONFIG_SPI_PL022=y -CONFIG_SPI_ROCKCHIP=y -CONFIG_SPI_RPCIF=m -CONFIG_SPI_QCOM_QSPI=m +CONFIG_SPI_MEM=y +CONFIG_SPI_BITBANG=y CONFIG_SPI_QUP=y -CONFIG_SPI_QCOM_GENI=m -CONFIG_SPI_S3C64XX=y -CONFIG_SPI_SH_MSIOF=m -CONFIG_SPI_SUN6I=y -CONFIG_SPI_SPIDEV=m +CONFIG_SPI_SPIDEV=y CONFIG_SPMI=y +CONFIG_PTP_1588_CLOCK=y CONFIG_PINCTRL_SINGLE=y -CONFIG_PINCTRL_MAX77620=y -CONFIG_PINCTRL_OWL=y -CONFIG_PINCTRL_S700=y -CONFIG_PINCTRL_S900=y -CONFIG_PINCTRL_IMX8MM=y -CONFIG_PINCTRL_IMX8MN=y -CONFIG_PINCTRL_IMX8MP=y -CONFIG_PINCTRL_IMX8MQ=y -CONFIG_PINCTRL_IMX8QM=y -CONFIG_PINCTRL_IMX8QXP=y -CONFIG_PINCTRL_IMX8DXL=y CONFIG_PINCTRL_MSM=y -CONFIG_PINCTRL_IPQ8074=y -CONFIG_PINCTRL_IPQ6018=y -CONFIG_PINCTRL_MSM8916=y -CONFIG_PINCTRL_MSM8994=y +CONFIG_PINCTRL_MSM8976=y CONFIG_PINCTRL_MSM8996=y CONFIG_PINCTRL_MSM8998=y -CONFIG_PINCTRL_QCS404=y -CONFIG_PINCTRL_QDF2XXX=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y -CONFIG_PINCTRL_SC7180=y -CONFIG_PINCTRL_SDM845=y -CONFIG_PINCTRL_SM8150=y -CONFIG_PINCTRL_SM8250=y -CONFIG_PINCTRL_SM8350=y -CONFIG_PINCTRL_LPASS_LPI=m -CONFIG_GPIO_ALTERA=m -CONFIG_GPIO_DAVINCI=y -CONFIG_GPIO_DWAPB=y -CONFIG_GPIO_MB86S7X=y -CONFIG_GPIO_MPC8XXX=y -CONFIG_GPIO_MXC=y -CONFIG_GPIO_PL061=y -CONFIG_GPIO_RCAR=y -CONFIG_GPIO_UNIPHIER=y -CONFIG_GPIO_VISCONTI=y -CONFIG_GPIO_WCD934X=m -CONFIG_GPIO_XGENE=y -CONFIG_GPIO_XGENE_SB=y -CONFIG_GPIO_MAX732X=y -CONFIG_GPIO_PCA953X=y -CONFIG_GPIO_PCA953X_IRQ=y -CONFIG_GPIO_BD9571MWV=m -CONFIG_GPIO_MAX77620=y -CONFIG_GPIO_SL28CPLD=m -CONFIG_POWER_AVS=y -CONFIG_QCOM_CPR=y -CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_PINCTRL_SDM660=y +CONFIG_GPIO_WCD934X=y CONFIG_POWER_RESET_MSM=y -CONFIG_POWER_RESET_QCOM_PON=m -CONFIG_POWER_RESET_XGENE=y -CONFIG_POWER_RESET_SYSCON=y -CONFIG_SYSCON_REBOOT_MODE=y -CONFIG_BATTERY_SBS=m -CONFIG_BATTERY_BQ27XXX=y -CONFIG_SENSORS_ARM_SCMI=y -CONFIG_BATTERY_MAX17042=m -CONFIG_CHARGER_BQ25890=m -CONFIG_CHARGER_BQ25980=m -CONFIG_SENSORS_ARM_SCPI=y -CONFIG_SENSORS_LM90=m -CONFIG_SENSORS_PWM_FAN=m -CONFIG_SENSORS_RASPBERRYPI_HWMON=m -CONFIG_SENSORS_SL28CPLD=m -CONFIG_SENSORS_INA2XX=m -CONFIG_SENSORS_INA3221=m -CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_POWER_RESET_QCOM_PON=y +CONFIG_THERMAL=y CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y CONFIG_THERMAL_EMULATION=y -CONFIG_QORIQ_THERMAL=m -CONFIG_SUN8I_THERMAL=y -CONFIG_IMX_SC_THERMAL=m -CONFIG_IMX8MM_THERMAL=m -CONFIG_ROCKCHIP_THERMAL=m -CONFIG_RCAR_THERMAL=y -CONFIG_RCAR_GEN3_THERMAL=y -CONFIG_ARMADA_THERMAL=y -CONFIG_BCM2711_THERMAL=m -CONFIG_BCM2835_THERMAL=m -CONFIG_BRCMSTB_THERMAL=m -CONFIG_EXYNOS_THERMAL=y -CONFIG_TEGRA_BPMP_THERMAL=m -CONFIG_TEGRA_SOCTHERM=m CONFIG_QCOM_TSENS=y -CONFIG_QCOM_SPMI_TEMP_ALARM=m -CONFIG_UNIPHIER_THERMAL=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y CONFIG_WATCHDOG=y -CONFIG_SL28CPLD_WATCHDOG=m -CONFIG_ARM_SP805_WATCHDOG=y -CONFIG_ARM_SBSA_WATCHDOG=y -CONFIG_ARM_SMC_WATCHDOG=y -CONFIG_S3C2410_WATCHDOG=y -CONFIG_DW_WATCHDOG=y -CONFIG_SUNXI_WATCHDOG=m -CONFIG_IMX2_WDT=y -CONFIG_IMX_SC_WDT=m -CONFIG_QCOM_WDT=m -CONFIG_MESON_GXBB_WATCHDOG=m -CONFIG_MESON_WATCHDOG=m -CONFIG_RENESAS_WDT=y -CONFIG_UNIPHIER_WATCHDOG=y -CONFIG_BCM2835_WDT=y -CONFIG_MFD_ALTERA_SYSMGR=y -CONFIG_MFD_BD9571MWV=y -CONFIG_MFD_AXP20X_I2C=y -CONFIG_MFD_AXP20X_RSB=y -CONFIG_MFD_EXYNOS_LPASS=m -CONFIG_MFD_HI6421_PMIC=y -CONFIG_MFD_HI655X_PMIC=y -CONFIG_MFD_MAX77620=y -CONFIG_MFD_MT6397=y +CONFIG_WATCHDOG_CORE=y +CONFIG_MFD_QCOM_RPM=y CONFIG_MFD_SPMI_PMIC=y -CONFIG_MFD_RK808=y -CONFIG_MFD_SEC_CORE=y -CONFIG_MFD_SL28CPLD=y -CONFIG_MFD_ROHM_BD718XX=y -CONFIG_MFD_WCD934X=m +CONFIG_MFD_RT5033=y +CONFIG_MFD_WCD934X=y CONFIG_REGULATOR_FIXED_VOLTAGE=y -CONFIG_REGULATOR_AXP20X=y -CONFIG_REGULATOR_BD718XX=y -CONFIG_REGULATOR_BD9571MWV=y -CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_VIRTUAL_CONSUMER=y +CONFIG_REGULATOR_USERSPACE_CONSUMER=y CONFIG_REGULATOR_GPIO=y -CONFIG_REGULATOR_HI6421V530=y -CONFIG_REGULATOR_HI655X=y -CONFIG_REGULATOR_MAX77620=y -CONFIG_REGULATOR_MAX8973=y -CONFIG_REGULATOR_MP8859=y -CONFIG_REGULATOR_MT6358=y -CONFIG_REGULATOR_MT6397=y -CONFIG_REGULATOR_PCA9450=y -CONFIG_REGULATOR_PF8X00=y -CONFIG_REGULATOR_PFUZE100=y -CONFIG_REGULATOR_PWM=y -CONFIG_REGULATOR_QCOM_RPMH=y CONFIG_REGULATOR_QCOM_SMD_RPM=y CONFIG_REGULATOR_QCOM_SPMI=y -CONFIG_REGULATOR_RK808=y -CONFIG_REGULATOR_S2MPS11=y -CONFIG_REGULATOR_TPS65132=m -CONFIG_REGULATOR_VCTRL=m -CONFIG_RC_CORE=m -CONFIG_RC_DECODERS=y -CONFIG_RC_DEVICES=y -CONFIG_IR_MESON=m -CONFIG_IR_SUNXI=m -CONFIG_MEDIA_SUPPORT=m +CONFIG_REGULATOR_VCTRL=y +CONFIG_REGULATOR_QCOM_LABIBB=y +CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y -CONFIG_MEDIA_ANALOG_TV_SUPPORT=y -CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y -CONFIG_MEDIA_SDR_SUPPORT=y -CONFIG_MEDIA_CONTROLLER=y -CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_MEDIA_PLATFORM_SUPPORT=y -# CONFIG_DVB_NET is not set -CONFIG_MEDIA_USB_SUPPORT=y -CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_VIDEO_RCAR_CSI2=m -CONFIG_VIDEO_RCAR_VIN=m -CONFIG_VIDEO_SUN6I_CSI=m +CONFIG_VIDEO_QCOM_CAMSS=y CONFIG_V4L_MEM2MEM_DRIVERS=y -CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m -CONFIG_VIDEO_SAMSUNG_S5P_MFC=m -CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m -CONFIG_VIDEO_RENESAS_FDP1=m -CONFIG_VIDEO_RENESAS_FCP=m -CONFIG_VIDEO_RENESAS_VSP1=m -CONFIG_VIDEO_QCOM_VENUS=m -CONFIG_SDR_PLATFORM_DRIVERS=y -CONFIG_VIDEO_RCAR_DRIF=m -CONFIG_VIDEO_IMX219=m -CONFIG_VIDEO_OV5645=m -CONFIG_VIDEO_QCOM_CAMSS=m -CONFIG_DRM=m -CONFIG_DRM_I2C_NXP_TDA998X=m -CONFIG_DRM_MALI_DISPLAY=m -CONFIG_DRM_NOUVEAU=m -CONFIG_DRM_EXYNOS=m -CONFIG_DRM_EXYNOS5433_DECON=y -CONFIG_DRM_EXYNOS7_DECON=y -CONFIG_DRM_EXYNOS_DSI=y -# CONFIG_DRM_EXYNOS_DP is not set -CONFIG_DRM_EXYNOS_HDMI=y -CONFIG_DRM_EXYNOS_MIC=y -CONFIG_DRM_ROCKCHIP=m -CONFIG_ROCKCHIP_ANALOGIX_DP=y -CONFIG_ROCKCHIP_CDN_DP=y -CONFIG_ROCKCHIP_DW_HDMI=y -CONFIG_ROCKCHIP_DW_MIPI_DSI=y -CONFIG_ROCKCHIP_INNO_HDMI=y -CONFIG_ROCKCHIP_LVDS=y -CONFIG_DRM_RCAR_DU=m -CONFIG_DRM_RCAR_DW_HDMI=m -CONFIG_DRM_SUN4I=m -CONFIG_DRM_SUN6I_DSI=m -CONFIG_DRM_SUN8I_DW_HDMI=m -CONFIG_DRM_SUN8I_MIXER=m -CONFIG_DRM_MSM=m -CONFIG_DRM_TEGRA=m -CONFIG_DRM_PANEL_LVDS=m -CONFIG_DRM_PANEL_SIMPLE=m -CONFIG_DRM_PANEL_BOE_TV101WUM_NL6=m -CONFIG_DRM_PANEL_MANTIX_MLAF057WE51=m -CONFIG_DRM_PANEL_RAYDIUM_RM67191=m -CONFIG_DRM_PANEL_SITRONIX_ST7703=m -CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=m -CONFIG_DRM_DISPLAY_CONNECTOR=m -CONFIG_DRM_LONTIUM_LT8912B=m -CONFIG_DRM_NWL_MIPI_DSI=m -CONFIG_DRM_LONTIUM_LT9611=m -CONFIG_DRM_PARADE_PS8640=m -CONFIG_DRM_SII902X=m -CONFIG_DRM_SIMPLE_BRIDGE=m -CONFIG_DRM_THINE_THC63LVD1024=m -CONFIG_DRM_TI_SN65DSI86=m -CONFIG_DRM_LONTIUM_LT9611UXC=m -CONFIG_DRM_I2C_ADV7511=m -CONFIG_DRM_I2C_ADV7511_AUDIO=y -CONFIG_DRM_DW_HDMI_AHB_AUDIO=m -CONFIG_DRM_DW_HDMI_CEC=m -CONFIG_DRM_IMX_DCSS=m -CONFIG_DRM_VC4=m -CONFIG_DRM_ETNAVIV=m -CONFIG_DRM_HISI_HIBMC=m -CONFIG_DRM_HISI_KIRIN=m -CONFIG_DRM_MEDIATEK=m -CONFIG_DRM_MEDIATEK_HDMI=m -CONFIG_DRM_MXSFB=m -CONFIG_DRM_MESON=m -CONFIG_DRM_PL111=m -CONFIG_DRM_LIMA=m -CONFIG_DRM_PANFROST=m -CONFIG_FB=y -CONFIG_FB_MODE_HELPERS=y -CONFIG_FB_EFI=y -CONFIG_BACKLIGHT_PWM=m -CONFIG_BACKLIGHT_LP855X=m -CONFIG_LOGO=y -# CONFIG_LOGO_LINUX_MONO is not set -# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_VIDEO_MEM2MEM_DEINTERLACE=y +CONFIG_VIDEO_QCOM_VENUS=y +CONFIG_VIDEO_IMX219=y +CONFIG_DRM=y +CONFIG_DRM_MSM=y +# CONFIG_DRM_MSM_DSI_20NM_PHY is not set +# CONFIG_DRM_MSM_DSI_28NM_8960_PHY is not set +# CONFIG_DRM_MSM_DSI_7NM_PHY is not set +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_LEGACY=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_QCOM_WLED=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y CONFIG_SOUND=y CONFIG_SND=y -CONFIG_SND_HDA_TEGRA=m -CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_DYNAMIC_MINORS=y CONFIG_SND_SOC=y -CONFIG_SND_BCM2835_SOC_I2S=m -CONFIG_SND_SOC_FSL_SAI=m -CONFIG_SND_SOC_FSL_ASRC=m -CONFIG_SND_SOC_FSL_MICFIL=m -CONFIG_SND_SOC_FSL_EASRC=m -CONFIG_SND_IMX_SOC=m -CONFIG_SND_SOC_IMX_SGTL5000=m -CONFIG_SND_SOC_IMX_SPDIF=m -CONFIG_SND_SOC_IMX_AUDMIX=m -CONFIG_SND_SOC_FSL_ASOC_CARD=m -CONFIG_SND_MESON_AXG_SOUND_CARD=m -CONFIG_SND_MESON_GX_SOUND_CARD=m -CONFIG_SND_SOC_QCOM=m -CONFIG_SND_SOC_APQ8016_SBC=m -CONFIG_SND_SOC_MSM8996=m -CONFIG_SND_SOC_SDM845=m -CONFIG_SND_SOC_SM8250=m -CONFIG_SND_SOC_ROCKCHIP=m -CONFIG_SND_SOC_ROCKCHIP_SPDIF=m -CONFIG_SND_SOC_ROCKCHIP_RT5645=m -CONFIG_SND_SOC_RK3399_GRU_SOUND=m -CONFIG_SND_SOC_SAMSUNG=y -CONFIG_SND_SOC_RCAR=m -CONFIG_SND_SUN4I_I2S=m -CONFIG_SND_SUN4I_SPDIF=m -CONFIG_SND_SOC_TEGRA=m -CONFIG_SND_SOC_TEGRA210_AHUB=m -CONFIG_SND_SOC_TEGRA210_DMIC=m -CONFIG_SND_SOC_TEGRA210_I2S=m -CONFIG_SND_SOC_TEGRA186_DSPK=m -CONFIG_SND_SOC_TEGRA210_ADMAIF=m -CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD=m -CONFIG_SND_SOC_AK4613=m -CONFIG_SND_SOC_ES7134=m -CONFIG_SND_SOC_ES7241=m -CONFIG_SND_SOC_GTM601=m -CONFIG_SND_SOC_MSM8916_WCD_ANALOG=m -CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=m -CONFIG_SND_SOC_PCM3168A_I2C=m -CONFIG_SND_SOC_RT5659=m -CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m -CONFIG_SND_SOC_SIMPLE_MUX=m -CONFIG_SND_SOC_TAS571X=m -CONFIG_SND_SOC_WCD934X=m -CONFIG_SND_SOC_WM8904=m -CONFIG_SND_SOC_WM8960=m -CONFIG_SND_SOC_WM8962=m -CONFIG_SND_SOC_WSA881X=m -CONFIG_SND_SOC_LPASS_WSA_MACRO=m -CONFIG_SND_SOC_LPASS_VA_MACRO=m -CONFIG_SND_SIMPLE_CARD=m -CONFIG_SND_AUDIO_GRAPH_CARD=m -CONFIG_HID_MULTITOUCH=m -CONFIG_I2C_HID_ACPI=m -CONFIG_I2C_HID_OF=m -CONFIG_USB_CONN_GPIO=m +CONFIG_SND_SOC_QCOM=y +CONFIG_SND_SOC_APQ8016_SBC=y +CONFIG_SND_SOC_MSM8996=y +CONFIG_SND_SOC_MSM8916_WCD_ANALOG=y +CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=y +CONFIG_SND_SOC_WCD9335=y +CONFIG_USB_LED_TRIG=y +CONFIG_USB_CONN_GPIO=y CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_OTG=y +CONFIG_USB_OTG_FSM=y CONFIG_USB_XHCI_HCD=y -CONFIG_USB_XHCI_PCI=m -CONFIG_USB_XHCI_PCI_RENESAS=m -CONFIG_USB_XHCI_TEGRA=y CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_EXYNOS=y CONFIG_USB_EHCI_HCD_PLATFORM=y -CONFIG_USB_OHCI_HCD=y -CONFIG_USB_OHCI_EXYNOS=y -CONFIG_USB_OHCI_HCD_PLATFORM=y -CONFIG_USB_RENESAS_USBHS_HCD=m -CONFIG_USB_RENESAS_USBHS=m -CONFIG_USB_ACM=m -CONFIG_USB_STORAGE=y -CONFIG_USB_MTU3=y -CONFIG_USB_MUSB_HDRC=y -CONFIG_USB_MUSB_SUNXI=y CONFIG_USB_DWC3=y -CONFIG_USB_DWC2=y +CONFIG_USB_DWC3_ULPI=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y -CONFIG_USB_ISP1760=y -CONFIG_USB_SERIAL=m -CONFIG_USB_SERIAL_CP210X=m -CONFIG_USB_SERIAL_FTDI_SIO=m -CONFIG_USB_SERIAL_OPTION=m -CONFIG_USB_HSIC_USB3503=y -CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_ULPI=y CONFIG_USB_GADGET=y -CONFIG_USB_RENESAS_USBHS_UDC=m -CONFIG_USB_RENESAS_USB3=m -CONFIG_USB_TEGRA_XUDC=m -CONFIG_USB_CONFIGFS=m +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_U_SERIAL_CONSOLE=y +CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_SERIAL=y -CONFIG_USB_CONFIGFS_ACM=y -CONFIG_USB_CONFIGFS_OBEX=y -CONFIG_USB_CONFIGFS_NCM=y -CONFIG_USB_CONFIGFS_ECM=y -CONFIG_USB_CONFIGFS_ECM_SUBSET=y CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_EEM=y -CONFIG_USB_CONFIGFS_MASS_STORAGE=y -CONFIG_USB_CONFIGFS_F_FS=y -CONFIG_TYPEC=m -CONFIG_TYPEC_TCPM=m -CONFIG_TYPEC_TCPCI=m -CONFIG_TYPEC_FUSB302=m -CONFIG_TYPEC_HD3SS3220=m -CONFIG_TYPEC_TPS6598X=m +CONFIG_USB_ETH=y +CONFIG_USB_ETH_EEM=y CONFIG_MMC=y -CONFIG_MMC_BLOCK_MINORS=32 -CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BLOCK_MINORS=64 +CONFIG_MMC_DEBUG=y CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_ACPI=y CONFIG_MMC_SDHCI_PLTFM=y -CONFIG_MMC_SDHCI_OF_ARASAN=y -CONFIG_MMC_SDHCI_OF_ESDHC=y -CONFIG_MMC_SDHCI_CADENCE=y -CONFIG_MMC_SDHCI_ESDHC_IMX=y -CONFIG_MMC_SDHCI_TEGRA=y -CONFIG_MMC_SDHCI_F_SDH30=y -CONFIG_MMC_MESON_GX=y CONFIG_MMC_SDHCI_MSM=y -CONFIG_MMC_SPI=y -CONFIG_MMC_SDHI=y -CONFIG_MMC_UNIPHIER=y -CONFIG_MMC_DW=y -CONFIG_MMC_DW_EXYNOS=y -CONFIG_MMC_DW_HI3798CV200=y -CONFIG_MMC_DW_K3=y -CONFIG_MMC_DW_ROCKCHIP=y -CONFIG_MMC_SUNXI=y -CONFIG_MMC_BCM2835=y -CONFIG_MMC_MTK=y -CONFIG_MMC_SDHCI_XENON=y -CONFIG_MMC_SDHCI_AM654=y -CONFIG_MMC_OWL=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y -CONFIG_LEDS_LM3692X=m -CONFIG_LEDS_PCA9532=m -CONFIG_LEDS_GPIO=y -CONFIG_LEDS_PWM=y -CONFIG_LEDS_SYSCON=y -CONFIG_LEDS_TRIGGER_TIMER=y -CONFIG_LEDS_TRIGGER_DISK=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_DEFAULT_ON=y CONFIG_LEDS_TRIGGER_PANIC=y -CONFIG_EDAC=y -CONFIG_EDAC_GHES=y -CONFIG_EDAC_LAYERSCAPE=m CONFIG_RTC_CLASS=y -CONFIG_RTC_DRV_DS1307=m -CONFIG_RTC_DRV_HYM8563=m -CONFIG_RTC_DRV_MAX77686=y -CONFIG_RTC_DRV_RK808=m -CONFIG_RTC_DRV_PCF85363=m -CONFIG_RTC_DRV_M41T80=m -CONFIG_RTC_DRV_RX8581=m -CONFIG_RTC_DRV_RV3028=m -CONFIG_RTC_DRV_RV8803=m -CONFIG_RTC_DRV_S5M=y -CONFIG_RTC_DRV_DS3232=y -CONFIG_RTC_DRV_PCF2127=m -CONFIG_RTC_DRV_EFI=y -CONFIG_RTC_DRV_CROS_EC=y -CONFIG_RTC_DRV_FSL_FTM_ALARM=m -CONFIG_RTC_DRV_S3C=y -CONFIG_RTC_DRV_PL031=y -CONFIG_RTC_DRV_SUN6I=y -CONFIG_RTC_DRV_ARMADA38X=y -CONFIG_RTC_DRV_PM8XXX=m -CONFIG_RTC_DRV_TEGRA=y -CONFIG_RTC_DRV_SNVS=m -CONFIG_RTC_DRV_IMX_SC=m -CONFIG_RTC_DRV_XGENE=y +CONFIG_RTC_DRV_PM8XXX=y CONFIG_DMADEVICES=y -CONFIG_DMA_BCM2835=y -CONFIG_DMA_SUN6I=m -CONFIG_FSL_EDMA=y -CONFIG_IMX_SDMA=y -CONFIG_K3_DMA=y -CONFIG_MV_XOR=y -CONFIG_MV_XOR_V2=y -CONFIG_OWL_DMA=y -CONFIG_PL330_DMA=y -CONFIG_TEGRA20_APB_DMA=y -CONFIG_TEGRA210_ADMA=m CONFIG_QCOM_BAM_DMA=y CONFIG_QCOM_HIDMA_MGMT=y CONFIG_QCOM_HIDMA=y -CONFIG_RCAR_DMAC=y -CONFIG_RENESAS_USB_DMAC=m -CONFIG_TI_K3_UDMA=y -CONFIG_TI_K3_UDMA_GLUE_LAYER=y -CONFIG_VFIO=y -CONFIG_VFIO_PCI=y -CONFIG_VIRTIO_PCI=y -CONFIG_VIRTIO_BALLOON=y -CONFIG_VIRTIO_MMIO=y -CONFIG_XEN_GNTDEV=y -CONFIG_XEN_GRANT_DEV_ALLOC=y -CONFIG_MFD_CROS_EC_DEV=y -CONFIG_STAGING=y -CONFIG_STAGING_MEDIA=y -CONFIG_VIDEO_HANTRO=m -CONFIG_CHROME_PLATFORMS=y -CONFIG_CROS_EC=y -CONFIG_CROS_EC_I2C=y -CONFIG_CROS_EC_SPI=y -CONFIG_CROS_EC_CHARDEV=m -CONFIG_COMMON_CLK_SCMI=y -CONFIG_COMMON_CLK_RK808=y -CONFIG_COMMON_CLK_SCPI=y -CONFIG_COMMON_CLK_CS2000_CP=y -CONFIG_COMMON_CLK_FSL_SAI=y -CONFIG_COMMON_CLK_S2MPS11=y -CONFIG_COMMON_CLK_PWM=y -CONFIG_COMMON_CLK_VC5=y -CONFIG_COMMON_CLK_ZYNQMP=y -CONFIG_COMMON_CLK_BD718XX=m -CONFIG_CLK_RASPBERRYPI=m -CONFIG_CLK_IMX8MM=y -CONFIG_CLK_IMX8MN=y -CONFIG_CLK_IMX8MP=y -CONFIG_CLK_IMX8MQ=y -CONFIG_CLK_IMX8QXP=y -CONFIG_TI_SCI_CLK=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set CONFIG_COMMON_CLK_QCOM=y -CONFIG_QCOM_A53PLL=y -CONFIG_QCOM_CLK_APCS_MSM8916=y CONFIG_QCOM_CLK_APCC_MSM8996=y +CONFIG_QCOM_CLK_RPM=y CONFIG_QCOM_CLK_SMD_RPM=y -CONFIG_QCOM_CLK_RPMH=y -CONFIG_IPQ_GCC_8074=y -CONFIG_IPQ_GCC_6018=y -CONFIG_MSM_GCC_8916=y -CONFIG_MSM_GCC_8994=y CONFIG_MSM_MMCC_8996=y -CONFIG_MSM_GCC_8998=y -CONFIG_QCS_GCC_404=y -CONFIG_SC_GCC_7180=y -CONFIG_SDM_CAMCC_845=m -CONFIG_SDM_GCC_845=y -CONFIG_SDM_GPUCC_845=y -CONFIG_SDM_VIDEOCC_845=y -CONFIG_SDM_DISPCC_845=y -CONFIG_SM_GCC_8150=y -CONFIG_SM_GCC_8250=y -CONFIG_SM_GCC_8350=y -CONFIG_SM_GPUCC_8150=y -CONFIG_SM_GPUCC_8250=y -CONFIG_SM_DISPCC_8250=y +CONFIG_MSM_GPUCC_8998=y +CONFIG_MSM_MMCC_8998=y +CONFIG_SDM_GCC_660=y +CONFIG_SPMI_PMIC_CLKDIV=y CONFIG_QCOM_HFPLL=y -CONFIG_CLK_GFM_LPASS_SM8250=m -CONFIG_CLK_RCAR_USB2_CLOCK_SEL=y CONFIG_HWSPINLOCK=y CONFIG_HWSPINLOCK_QCOM=y -CONFIG_ARM_MHU=y -CONFIG_IMX_MBOX=y -CONFIG_PLATFORM_MHU=y -CONFIG_BCM2835_MBOX=y +CONFIG_MAILBOX=y CONFIG_QCOM_APCS_IPC=y CONFIG_QCOM_IPCC=y -CONFIG_ROCKCHIP_IOMMU=y -CONFIG_TEGRA_IOMMU_SMMU=y CONFIG_ARM_SMMU=y -CONFIG_ARM_SMMU_V3=y -CONFIG_MTK_IOMMU=y +CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS=y CONFIG_QCOM_IOMMU=y CONFIG_REMOTEPROC=y -CONFIG_QCOM_Q6V5_MSS=m -CONFIG_QCOM_Q6V5_PAS=m -CONFIG_QCOM_SYSMON=m +CONFIG_REMOTEPROC_CDEV=y +CONFIG_QCOM_Q6V5_ADSP=y +CONFIG_QCOM_Q6V5_MSS=y +CONFIG_QCOM_Q6V5_PAS=y +CONFIG_QCOM_Q6V5_WCSS=y +CONFIG_QCOM_SYSMON=y +CONFIG_QCOM_WCNSS_PIL=y +CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_RPM=y -CONFIG_RPMSG_QCOM_GLINK_SMEM=m +CONFIG_RPMSG_QCOM_GLINK_SMEM=y CONFIG_RPMSG_QCOM_SMD=y -CONFIG_SOUNDWIRE=m -CONFIG_SOUNDWIRE_QCOM=m -CONFIG_OWL_PM_DOMAINS=y -CONFIG_RASPBERRYPI_POWER=y -CONFIG_FSL_DPAA=y -CONFIG_FSL_MC_DPIO=y -CONFIG_FSL_RCPM=y -CONFIG_MTK_PMIC_WRAP=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_QCOM=y CONFIG_QCOM_AOSS_QMP=y -CONFIG_QCOM_COMMAND_DB=y -CONFIG_QCOM_GENI_SE=y -CONFIG_QCOM_RMTFS_MEM=m -CONFIG_QCOM_RPMH=y -CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_CPR=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_OCMEM=y +CONFIG_QCOM_RMTFS_MEM=y CONFIG_QCOM_RPMPD=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SMP2P=y CONFIG_QCOM_SMSM=y -CONFIG_QCOM_SOCINFO=m -CONFIG_QCOM_APR=m -CONFIG_ARCH_R8A774A1=y -CONFIG_ARCH_R8A774B1=y -CONFIG_ARCH_R8A774C0=y -CONFIG_ARCH_R8A774E1=y -CONFIG_ARCH_R8A77950=y -CONFIG_ARCH_R8A77951=y -CONFIG_ARCH_R8A77960=y -CONFIG_ARCH_R8A77961=y -CONFIG_ARCH_R8A77965=y -CONFIG_ARCH_R8A77970=y -CONFIG_ARCH_R8A77980=y -CONFIG_ARCH_R8A77990=y -CONFIG_ARCH_R8A77995=y -CONFIG_ARCH_R8A779A0=y -CONFIG_ARCH_R9A07G044=y -CONFIG_ROCKCHIP_PM_DOMAINS=y -CONFIG_ARCH_TEGRA_132_SOC=y -CONFIG_ARCH_TEGRA_210_SOC=y -CONFIG_ARCH_TEGRA_186_SOC=y -CONFIG_ARCH_TEGRA_194_SOC=y -CONFIG_ARCH_TEGRA_234_SOC=y -CONFIG_TI_SCI_PM_DOMAINS=y -CONFIG_ARM_IMX_BUS_DEVFREQ=m -CONFIG_ARM_IMX8M_DDRC_DEVFREQ=m -CONFIG_EXTCON_PTN5150=m +CONFIG_QCOM_SOCINFO=y +CONFIG_QCOM_WCNSS_CTRL=y +CONFIG_QCOM_APR=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_EXTCON_ADC_JACK=y +CONFIG_EXTCON_GPIO=y +CONFIG_EXTCON_QCOM_SPMI_MISC=y +CONFIG_EXTCON_SM5502=y CONFIG_EXTCON_USB_GPIO=y -CONFIG_EXTCON_USBC_CROS_EC=y -CONFIG_RENESAS_RPCIF=m +CONFIG_MEMORY=y CONFIG_IIO=y -CONFIG_EXYNOS_ADC=y -CONFIG_MAX9611=m -CONFIG_QCOM_SPMI_VADC=m -CONFIG_QCOM_SPMI_ADC5=m -CONFIG_ROCKCHIP_SARADC=m -CONFIG_IIO_CROS_EC_SENSORS_CORE=m -CONFIG_IIO_CROS_EC_SENSORS=m -CONFIG_IIO_ST_LSM6DSX=m -CONFIG_IIO_CROS_EC_LIGHT_PROX=m -CONFIG_SENSORS_ISL29018=m -CONFIG_VCNL4000=m -CONFIG_IIO_ST_MAGN_3AXIS=m -CONFIG_IIO_CROS_EC_BARO=m -CONFIG_MPL3115=m +CONFIG_BMA180=y +CONFIG_BMC150_ACCEL=y +CONFIG_QCOM_SPMI_IADC=y +CONFIG_QCOM_SPMI_VADC=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_BMG160=y +CONFIG_INV_MPU6050_SPI=y +CONFIG_IIO_ST_LSM6DSX=y +CONFIG_LTR501=y +CONFIG_AK09911=y +CONFIG_BMC150_MAGN_I2C=y CONFIG_PWM=y -CONFIG_PWM_BCM2835=m -CONFIG_PWM_CROS_EC=m -CONFIG_PWM_IMX27=m -CONFIG_PWM_MESON=m -CONFIG_PWM_MTK_DISP=m -CONFIG_PWM_MEDIATEK=m -CONFIG_PWM_RCAR=m -CONFIG_PWM_ROCKCHIP=y -CONFIG_PWM_SAMSUNG=y -CONFIG_PWM_SL28CPLD=m -CONFIG_PWM_SUN4I=m -CONFIG_PWM_TEGRA=m -CONFIG_PWM_VISCONTI=m -CONFIG_SL28CPLD_INTC=y CONFIG_QCOM_PDC=y -CONFIG_RESET_IMX7=y -CONFIG_RESET_QCOM_AOSS=y -CONFIG_RESET_QCOM_PDC=m -CONFIG_RESET_TI_SCI=y -CONFIG_PHY_XGENE=y -CONFIG_PHY_SUN4I_USB=y -CONFIG_PHY_MIXEL_MIPI_DPHY=m -CONFIG_PHY_HI6220_USB=y -CONFIG_PHY_HISTB_COMBPHY=y -CONFIG_PHY_HISI_INNO_USB2=y -CONFIG_PHY_MVEBU_CP110_COMPHY=y -CONFIG_PHY_MTK_TPHY=y -CONFIG_PHY_QCOM_QMP=m -CONFIG_PHY_QCOM_QUSB2=m +CONFIG_PHY_QCOM_PCIE2=y +CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_QUSB2=y CONFIG_PHY_QCOM_USB_HS=y -CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y -CONFIG_PHY_RCAR_GEN3_PCIE=y -CONFIG_PHY_RCAR_GEN3_USB2=y -CONFIG_PHY_RCAR_GEN3_USB3=m -CONFIG_PHY_ROCKCHIP_EMMC=y -CONFIG_PHY_ROCKCHIP_INNO_HDMI=m -CONFIG_PHY_ROCKCHIP_INNO_USB2=y -CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=m -CONFIG_PHY_ROCKCHIP_PCIE=m -CONFIG_PHY_ROCKCHIP_TYPEC=y -CONFIG_PHY_SAMSUNG_UFS=y -CONFIG_PHY_UNIPHIER_USB2=y -CONFIG_PHY_UNIPHIER_USB3=y -CONFIG_PHY_TEGRA_XUSB=y -CONFIG_ARM_SMMU_V3_PMU=m -CONFIG_FSL_IMX8_DDR_PMU=m -CONFIG_HISI_PMU=y -CONFIG_QCOM_L2_PMU=y -CONFIG_QCOM_L3_PMU=y -CONFIG_NVMEM_IMX_OCOTP=y -CONFIG_NVMEM_IMX_OCOTP_SCU=y +CONFIG_PHY_QCOM_USB_HSIC=y +CONFIG_PHY_QCOM_USB_HS_28NM=y +CONFIG_POWERCAP=y +CONFIG_IDLE_INJECT=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y CONFIG_QCOM_QFPROM=y -CONFIG_MTK_EFUSE=y -CONFIG_ROCKCHIP_EFUSE=y -CONFIG_NVMEM_SUNXI_SID=y -CONFIG_UNIPHIER_EFUSE=y -CONFIG_MESON_EFUSE=m -CONFIG_NVMEM_RMEM=m -CONFIG_FPGA=y -CONFIG_FPGA_MGR_STRATIX10_SOC=m -CONFIG_FPGA_BRIDGE=m -CONFIG_ALTERA_FREEZE_BRIDGE=m -CONFIG_FPGA_REGION=m -CONFIG_OF_FPGA_REGION=m -CONFIG_TEE=y -CONFIG_OPTEE=y -CONFIG_SLIMBUS=m -CONFIG_SLIM_QCOM_CTRL=m -CONFIG_SLIM_QCOM_NGD_CTRL=m -CONFIG_MUX_MMIO=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SLIM_QCOM_CTRL=y +CONFIG_SLIM_QCOM_NGD_CTRL=y CONFIG_INTERCONNECT=y -CONFIG_INTERCONNECT_IMX=m -CONFIG_INTERCONNECT_IMX8MQ=m CONFIG_INTERCONNECT_QCOM=y -CONFIG_INTERCONNECT_QCOM_MSM8916=m -CONFIG_INTERCONNECT_QCOM_OSM_L3=m -CONFIG_INTERCONNECT_QCOM_SDM845=y -CONFIG_INTERCONNECT_QCOM_SM8150=m -CONFIG_INTERCONNECT_QCOM_SM8250=m -CONFIG_INTERCONNECT_QCOM_SM8350=m -CONFIG_EXT2_FS=y -CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y -CONFIG_BTRFS_FS=m -CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_COMPRESSION=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y +CONFIG_QFMT_V1=y +CONFIG_QFMT_V2=y CONFIG_AUTOFS4_FS=y -CONFIG_FUSE_FS=m -CONFIG_CUSE=m -CONFIG_OVERLAY_FS=m +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_OVERLAY_FS=y CONFIG_VFAT_FS=y +CONFIG_EXFAT_FS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y -CONFIG_CONFIGFS_FS=y -CONFIG_EFIVAR_FS=y -CONFIG_SQUASHFS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V4=y -CONFIG_NFS_V4_1=y -CONFIG_NFS_V4_2=y -CONFIG_ROOT_NFS=y -CONFIG_9P_FS=y +CONFIG_PSTORE=y +# CONFIG_PSTORE_DEFLATE_COMPRESS is not set +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_SECURITY=y -CONFIG_CRYPTO_ECHAINIV=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_LSM="selinux" +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_842=y +CONFIG_CRYPTO_LZ4=y +CONFIG_CRYPTO_LZ4HC=y +CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_ANSI_CPRNG=y -CONFIG_CRYPTO_USER_API_RNG=m -CONFIG_CRYPTO_DEV_SUN8I_CE=m -CONFIG_CRYPTO_DEV_FSL_CAAM=m -CONFIG_CRYPTO_DEV_FSL_DPAA2_CAAM=m -CONFIG_CRYPTO_DEV_QCOM_RNG=m -CONFIG_CRYPTO_DEV_CCREE=m -CONFIG_CRYPTO_DEV_HISI_SEC2=m -CONFIG_CRYPTO_DEV_HISI_ZIP=m -CONFIG_CRYPTO_DEV_HISI_HPRE=m -CONFIG_CRYPTO_DEV_HISI_TRNG=m +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=32 CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y CONFIG_DEBUG_INFO=y -CONFIG_DEBUG_INFO_REDUCED=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y +CONFIG_PANIC_TIMEOUT=-1 # CONFIG_SCHED_DEBUG is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_FTRACE is not set -CONFIG_MEMTEST=y From 8f263ba9515dd3e2666dac1068436b3361646907 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 10:10:22 +0100 Subject: [PATCH 018/212] Android defconfig cat ../../configs/r/android-5.4/android-{base,recommended,recommended-arm64}.config >> arch/arm64/configs/defconfig --- arch/arm64/configs/defconfig | 188 ++++++++++++++++++++++++++++++++--- 1 file changed, 174 insertions(+), 14 deletions(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index ed67c7f9a12d..56609d9cceed 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1,7 +1,6 @@ -CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y -CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y @@ -18,15 +17,21 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_MEMCG=y CONFIG_BLK_CGROUP=y +CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_HUGETLB=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y -CONFIG_USER_NS=y +CONFIG_CGROUP_BPF=y +CONFIG_CGROUP_DEBUG=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BOOT_CONFIG=y +# CONFIG_FHANDLE is not set +# CONFIG_AIO is not set CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y CONFIG_ARCH_QCOM=y @@ -36,12 +41,19 @@ CONFIG_NR_CPUS=4 CONFIG_HZ_100=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y CONFIG_RANDOMIZE_BASE=y # CONFIG_EFI is not set CONFIG_HIBERNATION=y CONFIG_PM_AUTOSLEEP=y CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set CONFIG_PM_DEBUG=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y CONFIG_ENERGY_MODEL=y @@ -74,7 +86,9 @@ CONFIG_CRYPTO_AES_ARM64_BS=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_KSM=y CONFIG_MEMORY_FAILURE=y CONFIG_TRANSPARENT_HUGEPAGE=y @@ -84,28 +98,93 @@ CONFIG_ZSMALLOC=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y +CONFIG_INET_ESP=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_MULTIPLE_TABLES=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_CHECKSUM=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_ADDRTYPE=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_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y CONFIG_IP_NF_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_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y CONFIG_IP6_NF_NAT=y CONFIG_IP6_NF_TARGET_MASQUERADE=y CONFIG_BRIDGE=y @@ -113,6 +192,12 @@ CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_VLAN_8021Q=y CONFIG_VLAN_8021Q_GVRP=y CONFIG_VLAN_8021Q_MVRP=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y CONFIG_QRTR=y CONFIG_QRTR_SMD=y CONFIG_QRTR_TUN=y @@ -147,11 +232,15 @@ CONFIG_ZRAM_WRITEBACK=y CONFIG_ZRAM_MEMORY_TRACKING=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QCOM_COINCELL=y CONFIG_QCOM_FASTRPC=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_MACVLAN=y CONFIG_MACVTAP=y @@ -174,7 +263,16 @@ CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=y CONFIG_KEYBOARD_TM2_TOUCHKEY=y -# CONFIG_MOUSE_PS2 is not set +# 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_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_MMS114=y @@ -183,16 +281,19 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_PM8941_PWRKEY=y CONFIG_INPUT_PM8XXX_VIBRATOR=y CONFIG_INPUT_GPIO_VIBRA=y +CONFIG_INPUT_UINPUT=y CONFIG_RMI4_CORE=y CONFIG_RMI4_I2C=y CONFIG_RMI4_F11=y CONFIG_RMI4_F12=y # CONFIG_SERIO is not set -CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SERIAL_DEV_BUS=y CONFIG_HW_RANDOM=y +# CONFIG_DEVMEM is not set CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y CONFIG_I2C_QCOM_CCI=y @@ -236,8 +337,6 @@ CONFIG_REGULATOR_QCOM_SPMI=y CONFIG_REGULATOR_VCTRL=y CONFIG_REGULATOR_QCOM_LABIBB=y CONFIG_MEDIA_SUPPORT=y -CONFIG_MEDIA_CAMERA_SUPPORT=y -CONFIG_MEDIA_PLATFORM_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_QCOM_CAMSS=y CONFIG_V4L_MEM2MEM_DRIVERS=y @@ -251,6 +350,7 @@ CONFIG_DRM_MSM=y # CONFIG_DRM_MSM_DSI_7NM_PHY is not set CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_LEGACY=y +CONFIG_FB=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_PLATFORM=y CONFIG_BACKLIGHT_CLASS_DEVICE=y @@ -268,9 +368,67 @@ CONFIG_SND_SOC_MSM8996=y CONFIG_SND_SOC_MSM8916_WCD_ANALOG=y CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=y CONFIG_SND_SOC_WCD9335=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y CONFIG_USB_LED_TRIG=y CONFIG_USB_CONN_GPIO=y -CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_OTG=y CONFIG_USB_OTG_FSM=y @@ -291,6 +449,8 @@ CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_ETH=y CONFIG_USB_ETH_EEM=y CONFIG_MMC=y @@ -299,8 +459,6 @@ CONFIG_MMC_DEBUG=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_DEFAULT_ON=y @@ -313,6 +471,8 @@ CONFIG_QCOM_HIDMA_MGMT=y CONFIG_QCOM_HIDMA=y # CONFIG_VIRTIO_MENU is not set # CONFIG_VHOST_MENU is not set +CONFIG_STAGING=y +CONFIG_ASHMEM=y CONFIG_COMMON_CLK_QCOM=y CONFIG_QCOM_CLK_APCC_MSM8996=y CONFIG_QCOM_CLK_RPM=y @@ -411,6 +571,7 @@ CONFIG_AUTOFS4_FS=y CONFIG_FUSE_FS=y CONFIG_CUSE=y CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_EXFAT_FS=y CONFIG_TMPFS_POSIX_ACL=y @@ -425,13 +586,12 @@ CONFIG_NLS_ISO8859_1=y CONFIG_SECURITY=y CONFIG_SECURITYFS=y CONFIG_SECURITY_NETWORK=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_LSM="selinux" -CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_LZO=y CONFIG_CRYPTO_842=y CONFIG_CRYPTO_LZ4=y @@ -450,8 +610,8 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_DEBUG_INFO=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y -CONFIG_PANIC_TIMEOUT=-1 +CONFIG_PANIC_TIMEOUT=5 # CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y # CONFIG_DEBUG_PREEMPT is not set # CONFIG_FTRACE is not set From f5bd27247f296375cb57b162defaf002ba826baf Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 21 Mar 2021 20:24:56 +0100 Subject: [PATCH 019/212] defconfig; MMCC and GPUCC 660 --- arch/arm64/configs/defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 56609d9cceed..18c3b733ab2f 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -480,7 +480,8 @@ CONFIG_QCOM_CLK_SMD_RPM=y CONFIG_MSM_MMCC_8996=y CONFIG_MSM_GPUCC_8998=y CONFIG_MSM_MMCC_8998=y -CONFIG_SDM_GCC_660=y +CONFIG_SDM_MMCC_660=y +CONFIG_SDM_GPUCC_660=y CONFIG_SPMI_PMIC_CLKDIV=y CONFIG_QCOM_HFPLL=y CONFIG_HWSPINLOCK=y From 93af1dbe3e0d04ebaec3c715145e411ce3645c26 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 21 Mar 2021 20:30:19 +0100 Subject: [PATCH 020/212] nile: Enable gpucc --- arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 0f28aaf0c982..6e6809975d2b 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -226,6 +226,10 @@ }; }; +&gpucc { + status = "okay"; +}; + &pon { volup { compatible = "qcom,pm8941-resin"; From be3b2acd0d41e3c859717a2dd765d5c99bf50449 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 21 Mar 2021 21:11:52 +0100 Subject: [PATCH 021/212] defconfig: Interconnect --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 18c3b733ab2f..23d386453a27 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -558,6 +558,7 @@ CONFIG_SLIM_QCOM_CTRL=y CONFIG_SLIM_QCOM_NGD_CTRL=y CONFIG_INTERCONNECT=y CONFIG_INTERCONNECT_QCOM=y +CONFIG_INTERCONNECT_QCOM_SDM660=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y From 0b8a882b92b4ec1ae8a9d614228a450e34b97583 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 00:18:38 +0100 Subject: [PATCH 022/212] bye framebuffer and sdhc_2 --- .../dts/qcom/sdm630-sony-xperia-nile.dtsi | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 6e6809975d2b..7f2fc58b7102 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -25,17 +25,17 @@ #size-cells = <2>; ranges; - stdout-path = "framebuffer0"; + // stdout-path = "framebuffer0"; - framebuffer0: framebuffer@9d400000 { - compatible = "simple-framebuffer"; - reg = <0 0x9d400000 0 (1920 * 1080 * 4)>; - width = <1080>; - height = <1920>; - stride = <(1080 * 4)>; - format = "a8r8g8b8"; - status= "okay"; - }; + // framebuffer0: framebuffer@9d400000 { + // compatible = "simple-framebuffer"; + // reg = <0 0x9d400000 0 (1920 * 1080 * 4)>; + // width = <1080>; + // height = <1920>; + // stride = <(1080 * 4)>; + // format = "a8r8g8b8"; + // status= "okay"; + // }; }; board_vbat: vbat-regulator { @@ -579,7 +579,7 @@ }; &sdhc_2 { - status = "okay"; + status = "disabled"; vmmc-supply = <&vreg_l5b_29p5>; vqmmc-supply = <&vreg_l2b_2p95>; From d492687bfa05f16436d295d6c5c4cc8d157fd097 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 13 Aug 2020 17:49:29 +0200 Subject: [PATCH 023/212] Adreno firmware in .c file; vendor DT --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 14 ++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_device.c | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index f602d143ca09..677bcd885a34 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -314,6 +314,20 @@ scm { compatible = "qcom,scm-msm8998", "qcom,scm"; }; + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/c0c4000.sdhci/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,slotselect"; + status = "ok"; + }; + }; + }; }; memory@80000000 { diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 2a6ce76656aa..850c69e1bfbf 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -338,6 +338,10 @@ MODULE_FIRMWARE("qcom/a530_zap.mdt"); MODULE_FIRMWARE("qcom/a530_zap.b00"); MODULE_FIRMWARE("qcom/a530_zap.b01"); MODULE_FIRMWARE("qcom/a530_zap.b02"); +MODULE_FIRMWARE("qcom/a508_zap.mdt"); +MODULE_FIRMWARE("qcom/a508_zap.b00"); +MODULE_FIRMWARE("qcom/a508_zap.b01"); +MODULE_FIRMWARE("qcom/a508_zap.b02"); MODULE_FIRMWARE("qcom/a630_sqe.fw"); MODULE_FIRMWARE("qcom/a630_gmu.bin"); MODULE_FIRMWARE("qcom/a630_zap.mbn"); From 4835b657d60ed04a78140858a89bb55d506a9dd4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 09:50:53 +0100 Subject: [PATCH 024/212] defconfig: a508 zap, a530 firmware --- arch/arm64/configs/defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 23d386453a27..14c0fe510241 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -222,6 +222,8 @@ CONFIG_PCIE_QCOM=y CONFIG_UEVENT_HELPER=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y +CONFIG_EXTRA_FIRMWARE="qcom/a508_zap.b00 qcom/a508_zap.b01 qcom/a508_zap.b02 qcom/a508_zap.elf qcom/a508_zap.mdt qcom/a530_gpmu.fw2 qcom/a530_pfp.fw qcom/a530_pm4.fw" +CONFIG_EXTRA_FIRMWARE_DIR="../firmware/" CONFIG_FW_LOADER_USER_HELPER=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_FW_LOADER_COMPRESS=y From c6fea63d41ae4eb635b91e81770ec8914d8575d0 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 14 Aug 2020 10:08:14 +0200 Subject: [PATCH 025/212] bye slotselect --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 677bcd885a34..a8a617a81f4e 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -320,10 +320,10 @@ compatible = "android,fstab"; vendor { compatible = "android,vendor"; - dev = "/dev/block/platform/soc/c0c4000.sdhci/by-name/vendor"; + dev = "/dev/block/platform/soc/c0c4000.sdhci/by-name/vendor_a"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait,slotselect"; + fsmgr_flags = "wait"; status = "ok"; }; }; From 84f338c4d327cc04852ec760b001bd9be6f19d6d Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 10:06:57 +0100 Subject: [PATCH 026/212] 630: Also system part, root fstab not found --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index a8a617a81f4e..bc3c1dbc2239 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -326,6 +326,14 @@ fsmgr_flags = "wait"; status = "ok"; }; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/c0c4000.sdhci/by-name/system_a"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait"; + status = "ok"; + }; }; }; }; From e5b76990e82b2624521dd0732c45b66aa086d6a4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 30 Sep 2020 20:36:31 +0200 Subject: [PATCH 027/212] arm64: defconfig: Disable legacy USB networking in favour of configfs Otherwise adb cannot get itself up --- arch/arm64/configs/defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 14c0fe510241..644cdb13b628 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -453,8 +453,6 @@ CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_EEM=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_USB_ETH=y -CONFIG_USB_ETH_EEM=y CONFIG_MMC=y CONFIG_MMC_BLOCK_MINORS=64 CONFIG_MMC_DEBUG=y From 7ed205734ac91fde0e8857a26dcde92173c90245 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 21 Sep 2020 01:30:01 +0200 Subject: [PATCH 028/212] arm64: dts: qcom: sdm63*-xperia: Add display panels Signed-off-by: Konrad Dybcio --- arch/arm64/boot/dts/qcom/sdm630-sony-xperia-ganges-kirin.dts | 5 +++++ .../boot/dts/qcom/sdm630-sony-xperia-nile-discovery.dts | 4 ++++ arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-pioneer.dts | 4 ++++ arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-voyager.dts | 4 ++++ .../boot/dts/qcom/sdm636-sony-xperia-ganges-mermaid.dts | 4 ++++ 5 files changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-ganges-kirin.dts b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-ganges-kirin.dts index a4e1fb8ca52d..35ea248a06df 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-ganges-kirin.dts +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-ganges-kirin.dts @@ -22,4 +22,9 @@ /* Ganges devices feature a Novatek touchscreen instead. */ /delete-node/ &touchscreen; + +&panel { + compatible = "sony,kirin-nt36672a-truly"; +}; + /delete-node/ &vreg_l18a_1v8; diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-discovery.dts b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-discovery.dts index c574e430ba67..740ee3c20826 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-discovery.dts +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-discovery.dts @@ -12,3 +12,7 @@ model = "Sony Xperia XA2 Ultra"; compatible = "sony,discovery-row", "qcom,sdm630"; }; + +&panel { + compatible = "sony,discovery-td4322-innolux"; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-pioneer.dts b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-pioneer.dts index a93ff3ab1b6d..b6b7535c2514 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-pioneer.dts +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-pioneer.dts @@ -12,3 +12,7 @@ model = "Sony Xperia XA2"; compatible = "sony,pioneer-row", "qcom,sdm630"; }; + +&panel { + compatible = "sony,pioneer-td4322-truly"; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-voyager.dts b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-voyager.dts index 59a679c205e0..32eaea6bcfde 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-voyager.dts +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile-voyager.dts @@ -19,3 +19,7 @@ }; }; }; + +&panel { + compatible = "sony,voyager-td4328-tianma"; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm636-sony-xperia-ganges-mermaid.dts b/arch/arm64/boot/dts/qcom/sdm636-sony-xperia-ganges-mermaid.dts index bba1c2bce213..4166e50a9f55 100644 --- a/arch/arm64/boot/dts/qcom/sdm636-sony-xperia-ganges-mermaid.dts +++ b/arch/arm64/boot/dts/qcom/sdm636-sony-xperia-ganges-mermaid.dts @@ -17,6 +17,10 @@ qcom,pmic-id = <0x1001b 0x101011a 0x00 0x00 0x1001b 0x201011a 0x00 0x00 0x1001b 0x102001a 0x00 0x00>; }; +&panel { + compatible = "sony,mermaid-nt36672a-tianma"; +}; + &sdc2_state_on { pinconf-clk { drive-strength = <14>; From 9deca0ad10f290caa288dd03d109ecceffff464b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 11:06:21 +0100 Subject: [PATCH 029/212] PANELS TODO FINISHME: use dev_err_probe in _probe! --- .../gpu/drm/panel/panel-td4322-innolux-fhd.c | 29 +++++-------------- .../drm/panel/panel-td4328-tianma-fhdplus.c | 29 +++++-------------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c index 69a962cc106e..8959dfc00c40 100644 --- a/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c +++ b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c @@ -195,17 +195,12 @@ static int td4322_innolux_fhd_probe(struct mipi_dsi_device *dsi) ctx->supplies[3].supply = "vsp"; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); - if (ret < 0) { - dev_err(dev, "Failed to get regulators: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset_gpio)) { - ret = PTR_ERR(ctx->reset_gpio); - dev_err(dev, "Failed to get reset-gpios: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); @@ -219,22 +214,14 @@ static int td4322_innolux_fhd_probe(struct mipi_dsi_device *dsi) DRM_MODE_CONNECTOR_DSI); ret = drm_panel_of_backlight(&ctx->panel); - if (ret) { - dev_err(dev, "Failed to get backlight: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); drm_panel_add(&ctx->panel); - if (ret < 0) { - dev_err(dev, "Failed to add panel: %d\n", ret); - return ret; - } ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); return 0; } diff --git a/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c b/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c index d1de8b549bd8..6a1ff6fdfa55 100644 --- a/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c +++ b/drivers/gpu/drm/panel/panel-td4328-tianma-fhdplus.c @@ -198,17 +198,12 @@ static int td4328_tianma_fhdplus_probe(struct mipi_dsi_device *dsi) ctx->supplies[3].supply = "vsp"; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); - if (ret < 0) { - dev_err(dev, "Failed to get regulators: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset_gpio)) { - ret = PTR_ERR(ctx->reset_gpio); - dev_err(dev, "Failed to get reset-gpios: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); @@ -222,22 +217,14 @@ static int td4328_tianma_fhdplus_probe(struct mipi_dsi_device *dsi) DRM_MODE_CONNECTOR_DSI); ret = drm_panel_of_backlight(&ctx->panel); - if (ret) { - dev_err(dev, "Failed to get backlight: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); drm_panel_add(&ctx->panel); - if (ret < 0) { - dev_err(dev, "Failed to add panel: %d\n", ret); - return ret; - } ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); return 0; } From 345e2402f91a172e83cc36e663603dae34fa5428 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 22 Mar 2021 11:12:13 +0100 Subject: [PATCH 030/212] DISCO PANELS?!?!? --- .../dts/qcom/sdm630-sony-xperia-nile.dtsi | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 7f2fc58b7102..003b3032e9dd 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -19,7 +19,7 @@ /* This part enables graphical output via bootloader-enabled display */ chosen { - bootargs = "earlycon=tty0 console=tty0"; + // bootargs = "earlycon=tty0 console=tty0"; #address-cells = <2>; #size-cells = <2>; @@ -226,6 +226,39 @@ }; }; +&dsi0 { + #address-cells = <1>; + #size-cells = <0>; + + vdda-supply = <&vreg_l1a_1p225>; + + panel: panel@0 { + reg = <0>; + + reset-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>; + disp-te-gpios = <&tlmm 59 GPIO_ACTIVE_HIGH>; + + backlight = <&pm660l_wled>; + + pinctrl-names = "default"; + pinctrl-0 = <&panel_reset_n &mdp_vsync_n>; + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; +}; + +&dsi0_out { + remote-endpoint = <&panel_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss { + status = "okay"; +}; + &gpucc { status = "okay"; }; @@ -620,6 +653,20 @@ bias-disable; drive-strength = <2>; }; + + panel_reset_n: panel-rst-n { + pins = "gpio53"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + mdp_vsync_n: mdp-vsync-n { + pins = "gpio59"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; }; &usb3 { From 80d6ecc10ab930b831c05499f9a36aa2cba1f364 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 29 Mar 2021 11:12:02 +0200 Subject: [PATCH 031/212] defconfig: Disco panel! --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 644cdb13b628..63d016aab380 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -351,6 +351,7 @@ CONFIG_DRM_MSM=y # CONFIG_DRM_MSM_DSI_28NM_8960_PHY is not set # CONFIG_DRM_MSM_DSI_7NM_PHY is not set CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_SONY_DISCOVERY_TD4322_INNOLUX=y CONFIG_DRM_LEGACY=y CONFIG_FB=y CONFIG_LCD_CLASS_DEVICE=y From ad3c1d83ab31c455a463c2c1b70bd225468f3bd1 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 30 Mar 2021 23:30:27 +0200 Subject: [PATCH 032/212] nile: Turn on pm660l_wled to not -EPROBE_DEFER the panel --- arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 003b3032e9dd..2d7b584b3d0f 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -259,6 +259,10 @@ status = "okay"; }; +&pm660l_wled { + status = "okay"; +}; + &gpucc { status = "okay"; }; From 3f134d4eab452204465f966cb694d16fbafb9a88 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 30 Mar 2021 23:40:47 +0200 Subject: [PATCH 033/212] sdm630: Add missing zap-shader! --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index bc3c1dbc2239..790f57312a3c 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1111,6 +1111,10 @@ opp-supported-hw = <0xFF>; }; }; + + zap-shader { + memory-region = <&zap_shader_region>; + }; }; kgsl_smmu: iommu@5040000 { From 3b2ed600560a4a6b4f241b1ef0caf29e215427c1 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 18 Oct 2020 16:10:41 +0200 Subject: [PATCH 034/212] gpu go 700mhz --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 790f57312a3c..3c402ebf3b9c 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1068,6 +1068,20 @@ gpu_sdm630_opp_table: opp-table { compatible = "operating-points-v2"; + + opp-700000000 { + opp-hz = /bits/ 64 <700000000>; + opp-level = ; + opp-peak-kBps = <5184000>; + opp-supported-hw = <0xFF>; + }; + + /* + * 775MHz is only available on default speed bin + * or 0xA2 (speed bin 1). Though it cannot be used + * for now due to interconnect framework not supporting + * multiple frequencies at the same opp-level + opp-775000000 { opp-hz = /bits/ 64 <775000000>; opp-level = ; @@ -1110,6 +1124,7 @@ opp-peak-kBps = <1200000>; opp-supported-hw = <0xFF>; }; + */ }; zap-shader { From 645c2d229c20f1315015a4302fbcd3318b08e7ff Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 31 Jul 2021 13:25:42 +0200 Subject: [PATCH 035/212] PANEL INNOLUX FIXUP!: Use set_tear_on for VBLANK instead of magic const --- drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c index 8959dfc00c40..77cd13d2d353 100644 --- a/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c +++ b/drivers/gpu/drm/panel/panel-td4322-innolux-fhd.c @@ -48,7 +48,11 @@ static int td4322_innolux_fhd_on(struct td4322_innolux_fhd *ctx) dsi->mode_flags |= MIPI_DSI_MODE_LPM; - dsi_dcs_write_seq(dsi, 0x35); + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) { + dev_err(dev, "Failed to set tear on: %d\n", ret); + return ret; + } ret = mipi_dsi_dcs_set_display_on(dsi); if (ret < 0) { From 4121e0bc1cff7f49c39a879355dcda6f447f10b7 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 8 Sep 2020 20:52:53 +0200 Subject: [PATCH 036/212] android-recommended lies: AIO is needed for adb :| --- arch/arm64/configs/defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 63d016aab380..302b038c497f 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -29,7 +29,6 @@ CONFIG_CGROUP_DEBUG=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BOOT_CONFIG=y # CONFIG_FHANDLE is not set -# CONFIG_AIO is not set CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set From b2a234e86193afc8f02a6e7b51ad4d42013919d1 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 15 Jun 2021 01:00:20 +0200 Subject: [PATCH 037/212] FIXUP: simple-panel: add missing connector_type! --- drivers/gpu/drm/panel/panel-simple.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 29597db4e25f..da4e7e1781eb 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5181,6 +5181,7 @@ static const struct panel_desc_dsi sony_kirin_nt36672a_truly = { .width = 60, .height = 139, }, + .connector_type = DRM_MODE_CONNECTOR_DSI, }, .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS, @@ -5211,6 +5212,7 @@ static const struct panel_desc_dsi sony_mermaid_nt36672a_tianma = { .width = 65, .height = 151, }, + .connector_type = DRM_MODE_CONNECTOR_DSI, }, .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS, From e6dc461e838facd48306e992f667e3e461a64519 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 11 Apr 2021 23:04:31 +0200 Subject: [PATCH 038/212] leds: ledtrig-pattern: Use last_repeat when applying hw pattern `last_repeat` holds the actual value requested by the user whereas `repeat` is a software iteration variable that is unused in hardware patterns. Furthermore `last_repeat` is the field returned to the user when reading the `repeat` sysfs property. This field is initialized to `-1` which is - together with `1` - the only valid value in the upcoming Qualcomm LPG driver. It is thus unexpected when `repeat` with an initialization value of `0` is passed into the the driver, when the sysfs property clearly presents a value of `-1`. Signed-off-by: Marijn Suijten --- drivers/leds/trigger/ledtrig-pattern.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index 43a265dc4696..a11d68143757 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -126,7 +126,8 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev) if (data->is_hw_pattern) { return led_cdev->pattern_set(led_cdev, data->patterns, - data->npatterns, data->repeat); + data->npatterns, + data->last_repeat); } /* At least 2 tuples for software pattern. */ From 0b099e0d7e278b9dacbd1dc0779a5916f634e69f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:12 -0700 Subject: [PATCH 039/212] dt-bindings: leds: Add Qualcomm Light Pulse Generator binding This adds the binding document describing the three hardware blocks related to the Light Pulse Generator found in a wide range of Qualcomm PMICs. Signed-off-by: Bjorn Andersson --- .../bindings/leds/leds-qcom-lpg.yaml | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml new file mode 100644 index 000000000000..2998598e8785 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-qcom-lpg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Light Pulse Generator + +maintainers: + - Bjorn Andersson + +description: > + The Qualcomm Light Pulse Generator consists of three different hardware blocks; + a ramp generator with lookup table, the light pulse generator and a three + channel current sink. These blocks are found in a wide range of Qualcomm PMICs. + +properties: + compatible: + enum: + - qcom,pm8150b-lpg + - qcom,pm8150l-lpg + - qcom,pm8916-pwm + - qcom,pm8941-lpg + - qcom,pm8994-lpg + - qcom,pmi8994-lpg + - qcom,pmi8998-lpg + + "#pwm-cells": + const: 2 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + qcom,power-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + power-source used to drive the output, as defined in the datasheet. + Should be specified if the TRILED block is present + + qcom,dtest: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: > + A list of integer pairs, where each pair represent the dtest line the + particular channel should be connected to and the flags denoting how the + value should be outputed, as defined in the datasheet. The number of + pairs should be the same as the number of channels. + + multi-led: + type: object + $ref: leds-class-multicolor.yaml# + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + "^led@[0-9a-f]$": + type: object + $ref: common.yaml# + +patternProperties: + "^led@[0-9a-f]$": + type: object + $ref: common.yaml# + + properties: + reg: true + + required: + - reg + +required: + - compatible + +additionalProperties: false + +examples: + - | + #include + + lpg { + compatible = "qcom,pmi8994-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + + qcom,power-source = <1>; + + qcom,dtest = <0 0 + 0 0 + 0 0 + 4 1>; + + led@1 { + reg = <1>; + label = "green:user1"; + }; + + led@2 { + reg = <2>; + label = "green:user0"; + default-state = "on"; + }; + + led@3 { + reg = <3>; + label = "green:user2"; + }; + + led@4 { + reg = <4>; + label = "green:user3"; + }; + }; + - | + #include + + lpg { + compatible = "qcom,pmi8994-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + + qcom,power-source = <1>; + + multi-led { + color = ; + label = "rgb:notification"; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + + led@3 { + reg = <3>; + color = ; + }; + }; + }; + - | + lpg { + compatible = "qcom,pm8916-pwm"; + #pwm-cells = <2>; + }; +... From 25281c8b706ad8724b7866a6aee5c59f511795c6 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:13 -0700 Subject: [PATCH 040/212] leds: Add driver for Qualcomm LPG The Light Pulse Generator (LPG) is a PWM-block found in a wide range of PMICs from Qualcomm. It can operate on fixed parameters or based on a lookup-table, altering the duty cycle over time - which provides the means for e.g. hardware assisted transitions of LED brightness. Signed-off-by: Bjorn Andersson --- drivers/leds/Kconfig | 3 + drivers/leds/Makefile | 3 + drivers/leds/rgb/leds-qcom-lpg.c | 1286 ++++++++++++++++++++++++++++++ 3 files changed, 1292 insertions(+) create mode 100644 drivers/leds/rgb/leds-qcom-lpg.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ed800f5da7d8..ffb8777842ff 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -876,6 +876,9 @@ source "drivers/leds/blink/Kconfig" comment "Flash and Torch LED drivers" source "drivers/leds/flash/Kconfig" +comment "RGB LED drivers" +source "drivers/leds/rgb/Kconfig" + comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c636ec069612..351153f84070 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -100,6 +100,9 @@ obj-$(CONFIG_LEDS_USER) += uleds.o # Flash and Torch LED Drivers obj-$(CONFIG_LEDS_CLASS_FLASH) += flash/ +# RGB LED Drivers +obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += rgb/ + # LED Triggers obj-$(CONFIG_LEDS_TRIGGERS) += trigger/ diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c new file mode 100644 index 000000000000..c68233b43c2c --- /dev/null +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2021 Linaro Ltd + * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPG_PATTERN_CONFIG_REG 0x40 +#define LPG_SIZE_CLK_REG 0x41 +#define LPG_PREDIV_CLK_REG 0x42 +#define PWM_TYPE_CONFIG_REG 0x43 +#define PWM_VALUE_REG 0x44 +#define PWM_ENABLE_CONTROL_REG 0x46 +#define PWM_SYNC_REG 0x47 +#define LPG_RAMP_DURATION_REG 0x50 +#define LPG_HI_PAUSE_REG 0x52 +#define LPG_LO_PAUSE_REG 0x54 +#define LPG_HI_IDX_REG 0x56 +#define LPG_LO_IDX_REG 0x57 +#define PWM_SEC_ACCESS_REG 0xd0 +#define PWM_DTEST_REG(x) (0xe2 + (x) - 1) + +#define TRI_LED_SRC_SEL 0x45 +#define TRI_LED_EN_CTL 0x46 +#define TRI_LED_ATC_CTL 0x47 + +#define LPG_LUT_REG(x) (0x40 + (x) * 2) +#define RAMP_CONTROL_REG 0xc8 + +struct lpg_channel; +struct lpg_data; + +/** + * struct lpg - LPG device context + * @dev: struct device for LPG device + * @map: regmap for register access + * @pwm: PWM-chip object, if operating in PWM mode + * @lut_base: base address of the LUT block (optional) + * @lut_size: number of entries in the LUT block + * @lut_bitmap: allocation bitmap for LUT entries + * @triled_base: base address of the TRILED block (optional) + * @triled_src: power-source for the TRILED + * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register + * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register + * @channels: list of PWM channels + * @num_channels: number of @channels + */ +struct lpg { + struct device *dev; + struct regmap *map; + + struct pwm_chip pwm; + + const struct lpg_data *data; + + u32 lut_base; + u32 lut_size; + unsigned long *lut_bitmap; + + u32 triled_base; + u32 triled_src; + bool triled_has_atc_ctl; + bool triled_has_src_sel; + + struct lpg_channel *channels; + unsigned int num_channels; +}; + +/** + * struct lpg_channel - per channel data + * @lpg: reference to parent lpg + * @base: base address of the PWM channel + * @triled_mask: mask in TRILED to enable this channel + * @lut_mask: mask in LUT to start pattern generator for this channel + * @in_use: channel is exposed to LED framework + * @color: color of the LED attached to this channel + * @dtest_line: DTEST line for output, or 0 if disabled + * @dtest_value: DTEST line configuration + * @pwm_value: duty (in microseconds) of the generated pulses, overridden by LUT + * @enabled: output enabled? + * @period_us: period (in microseconds) of the generated pulses + * @pwm_size: resolution of the @pwm_value, 6 or 9 bits + * @clk: base frequency of the clock generator + * @pre_div: divider of @clk + * @pre_div_exp: exponential divider of @clk + * @ramp_enabled: duty cycle is driven by iterating over lookup table + * @ramp_ping_pong: reverse through pattern, rather than wrapping to start + * @ramp_oneshot: perform only a single pass over the pattern + * @ramp_reverse: iterate over pattern backwards + * @ramp_tick_ms: length (in milliseconds) of one step in the pattern + * @ramp_lo_pause_ms: pause (in milliseconds) before iterating over pattern + * @ramp_hi_pause_ms: pause (in milliseconds) after iterating over pattern + * @pattern_lo_idx: start index of associated pattern + * @pattern_hi_idx: last index of associated pattern + */ +struct lpg_channel { + struct lpg *lpg; + + u32 base; + unsigned int triled_mask; + unsigned int lut_mask; + + bool in_use; + + int color; + + u32 dtest_line; + u32 dtest_value; + + u16 pwm_value; + bool enabled; + + unsigned int period_us; + unsigned int pwm_size; + unsigned int clk; + unsigned int pre_div; + unsigned int pre_div_exp; + + bool ramp_enabled; + bool ramp_ping_pong; + bool ramp_oneshot; + bool ramp_reverse; + unsigned long ramp_tick_ms; + unsigned long ramp_lo_pause_ms; + unsigned long ramp_hi_pause_ms; + + unsigned int pattern_lo_idx; + unsigned int pattern_hi_idx; +}; + +/** + * struct lpg_led - logical LED object + * @lpg: lpg context reference + * @cdev: LED class device + * @mcdev: Multicolor LED class device + * @num_channels: number of @channels + * @channels: list of channels associated with the LED + */ +struct lpg_led { + struct lpg *lpg; + + struct led_classdev cdev; + struct led_classdev_mc mcdev; + + unsigned int num_channels; + struct lpg_channel *channels[]; +}; + +/** + * struct lpg_channel_data - per channel initialization data + * @base: base address for PWM channel registers + * @triled_mask: bitmask for controlling this channel in TRILED + */ +struct lpg_channel_data { + unsigned int base; + u8 triled_mask; +}; + +/** + * struct lpg_data - initialization data + * @lut_base: base address of LUT block + * @lut_size: number of entries in LUT + * @triled_base: base address of TRILED + * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register + * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register + * @pwm_9bit_mask: bitmask for switching from 6bit to 9bit pwm + * @num_channels: number of channels in LPG + * @channels: list of channel initialization data + */ +struct lpg_data { + unsigned int lut_base; + unsigned int lut_size; + unsigned int triled_base; + bool triled_has_atc_ctl; + bool triled_has_src_sel; + unsigned int pwm_9bit_mask; + int num_channels; + struct lpg_channel_data *channels; +}; + +static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable) +{ + /* Skip if we don't have a triled block */ + if (!lpg->triled_base) + return 0; + + return regmap_update_bits(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, + mask, enable); +} + +static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern, + size_t len, unsigned int *lo_idx, unsigned int *hi_idx) +{ + unsigned int idx; + u16 val; + int i; + + /* Hardware does not behave when LO_IDX == HI_IDX */ + if (len == 1) + return -EINVAL; + + idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, + 0, len, 0); + if (idx >= lpg->lut_size) + return -ENOMEM; + + for (i = 0; i < len; i++) { + val = pattern[i].brightness; + + regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), &val, 1); + } + + bitmap_set(lpg->lut_bitmap, idx, len); + + *lo_idx = idx; + *hi_idx = idx + len - 1; + + return 0; +} + +static void lpg_lut_free(struct lpg *lpg, unsigned int lo_idx, unsigned int hi_idx) +{ + int len; + + if (lo_idx == hi_idx) + return; + + len = hi_idx - lo_idx + 1; + bitmap_clear(lpg->lut_bitmap, lo_idx, len); +} + +static int lpg_lut_sync(struct lpg *lpg, unsigned int mask) +{ + return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask); +} + +#define NUM_PWM_PREDIV 4 +#define NUM_PWM_CLK 3 +#define NUM_EXP 7 + +static const unsigned int lpg_clk_table[NUM_PWM_PREDIV][NUM_PWM_CLK] = { + { + 1 * (NSEC_PER_SEC / 1024), + 1 * (NSEC_PER_SEC / 32768), + 1 * (NSEC_PER_SEC / 19200000), + }, + { + 3 * (NSEC_PER_SEC / 1024), + 3 * (NSEC_PER_SEC / 32768), + 3 * (NSEC_PER_SEC / 19200000), + }, + { + 5 * (NSEC_PER_SEC / 1024), + 5 * (NSEC_PER_SEC / 32768), + 5 * (NSEC_PER_SEC / 19200000), + }, + { + 6 * (NSEC_PER_SEC / 1024), + 6 * (NSEC_PER_SEC / 32768), + 6 * (NSEC_PER_SEC / 19200000), + }, +}; + +/* + * PWM Frequency = Clock Frequency / (N * T) + * or + * PWM Period = Clock Period * (N * T) + * where + * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size + * T = Pre-divide * 2^m, where m = 0..7 (exponent) + * + * This is the formula to figure out m for the best pre-divide and clock: + * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m + */ +static void lpg_calc_freq(struct lpg_channel *chan, unsigned int period_us) +{ + int n, m, clk, div; + int best_m, best_div, best_clk; + unsigned int last_err, cur_err, min_err; + unsigned int tmp_p, period_n; + + if (period_us == chan->period_us) + return; + + /* PWM Period / N */ + if (period_us < UINT_MAX / NSEC_PER_USEC) + n = 6; + else + n = 9; + + period_n = ((u64)period_us * NSEC_PER_USEC) >> n; + + min_err = UINT_MAX; + last_err = UINT_MAX; + best_m = 0; + best_clk = 0; + best_div = 0; + for (clk = 0; clk < NUM_PWM_CLK; clk++) { + for (div = 0; div < NUM_PWM_PREDIV; div++) { + /* period_n = (PWM Period / N) */ + /* tmp_p = (Pre-divide * Clock Period) * 2^m */ + tmp_p = lpg_clk_table[div][clk]; + for (m = 0; m <= NUM_EXP; m++) { + cur_err = abs(period_n - tmp_p); + if (cur_err < min_err) { + min_err = cur_err; + best_m = m; + best_clk = clk; + best_div = div; + } + + if (m && cur_err > last_err) + /* Break for bigger cur_err */ + break; + + last_err = cur_err; + tmp_p <<= 1; + } + } + } + + /* Use higher resolution */ + if (best_m >= 3 && n == 6) { + n += 3; + best_m -= 3; + } + + chan->clk = best_clk; + chan->pre_div = best_div; + chan->pre_div_exp = best_m; + chan->pwm_size = n; + + chan->period_us = period_us; +} + +static void lpg_calc_duty(struct lpg_channel *chan, unsigned int duty_us) +{ + unsigned int max = (1 << chan->pwm_size) - 1; + unsigned int val = div_u64((u64)duty_us << chan->pwm_size, chan->period_us); + + chan->pwm_value = min(val, max); +} + +static void lpg_apply_freq(struct lpg_channel *chan) +{ + unsigned long val; + struct lpg *lpg = chan->lpg; + + if (!chan->enabled) + return; + + /* Clock register values are off-by-one from lpg_clk_table */ + val = chan->clk + 1; + + if (chan->pwm_size == 9) + val |= lpg->data->pwm_9bit_mask; + + regmap_write(lpg->map, chan->base + LPG_SIZE_CLK_REG, val); + + val = chan->pre_div << 5 | chan->pre_div_exp; + regmap_write(lpg->map, chan->base + LPG_PREDIV_CLK_REG, val); +} + +#define LPG_ENABLE_GLITCH_REMOVAL BIT(5) + +static void lpg_enable_glitch(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + + regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, + LPG_ENABLE_GLITCH_REMOVAL, 0); +} + +static void lpg_disable_glitch(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + + regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, + LPG_ENABLE_GLITCH_REMOVAL, + LPG_ENABLE_GLITCH_REMOVAL); +} + +static void lpg_apply_pwm_value(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + u16 val = chan->pwm_value; + + if (!chan->enabled) + return; + + regmap_bulk_write(lpg->map, chan->base + PWM_VALUE_REG, &val, 1); +} + +#define LPG_PATTERN_CONFIG_LO_TO_HI BIT(4) +#define LPG_PATTERN_CONFIG_REPEAT BIT(3) +#define LPG_PATTERN_CONFIG_TOGGLE BIT(2) +#define LPG_PATTERN_CONFIG_PAUSE_HI BIT(1) +#define LPG_PATTERN_CONFIG_PAUSE_LO BIT(0) + +static void lpg_apply_lut_control(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + unsigned int hi_pause; + unsigned int lo_pause; + unsigned int step; + unsigned int conf = 0; + unsigned int lo_idx = chan->pattern_lo_idx; + unsigned int hi_idx = chan->pattern_hi_idx; + int pattern_len; + + if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) + return; + + pattern_len = hi_idx - lo_idx + 1; + + step = chan->ramp_tick_ms; + hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, step); + lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, step); + + if (!chan->ramp_reverse) + conf |= LPG_PATTERN_CONFIG_LO_TO_HI; + if (!chan->ramp_oneshot) + conf |= LPG_PATTERN_CONFIG_REPEAT; + if (chan->ramp_ping_pong) + conf |= LPG_PATTERN_CONFIG_TOGGLE; + if (chan->ramp_hi_pause_ms) + conf |= LPG_PATTERN_CONFIG_PAUSE_HI; + if (chan->ramp_lo_pause_ms) + conf |= LPG_PATTERN_CONFIG_PAUSE_LO; + + regmap_write(lpg->map, chan->base + LPG_PATTERN_CONFIG_REG, conf); + regmap_write(lpg->map, chan->base + LPG_HI_IDX_REG, hi_idx); + regmap_write(lpg->map, chan->base + LPG_LO_IDX_REG, lo_idx); + + regmap_write(lpg->map, chan->base + LPG_RAMP_DURATION_REG, step); + regmap_write(lpg->map, chan->base + LPG_HI_PAUSE_REG, hi_pause); + regmap_write(lpg->map, chan->base + LPG_LO_PAUSE_REG, lo_pause); +} + +#define LPG_ENABLE_CONTROL_OUTPUT BIT(7) +#define LPG_ENABLE_CONTROL_BUFFER_TRISTATE BIT(5) +#define LPG_ENABLE_CONTROL_SRC_PWM BIT(2) +#define LPG_ENABLE_CONTROL_RAMP_GEN BIT(1) + +static void lpg_apply_control(struct lpg_channel *chan) +{ + unsigned int ctrl; + struct lpg *lpg = chan->lpg; + + ctrl = LPG_ENABLE_CONTROL_BUFFER_TRISTATE; + + if (chan->enabled) + ctrl |= LPG_ENABLE_CONTROL_OUTPUT; + + if (chan->pattern_lo_idx != chan->pattern_hi_idx) + ctrl |= LPG_ENABLE_CONTROL_RAMP_GEN; + else + ctrl |= LPG_ENABLE_CONTROL_SRC_PWM; + + regmap_write(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, ctrl); + + /* + * Due to LPG hardware bug, in the PWM mode, having enabled PWM, + * We have to write PWM values one more time. + */ + if (chan->enabled) + lpg_apply_pwm_value(chan); +} + +#define LPG_SYNC_PWM BIT(0) + +static void lpg_apply_sync(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + + regmap_write(lpg->map, chan->base + PWM_SYNC_REG, LPG_SYNC_PWM); +} + +static int lpg_parse_dtest(struct lpg *lpg) +{ + struct lpg_channel *chan; + struct device_node *np = lpg->dev->of_node; + int count; + int ret; + int i; + + count = of_property_count_u32_elems(np, "qcom,dtest"); + if (count == -EINVAL) { + return 0; + } else if (count < 0) { + ret = count; + goto err_malformed; + } else if (count != lpg->data->num_channels * 2) { + dev_err(lpg->dev, "qcom,dtest needs to be %d items\n", + lpg->data->num_channels * 2); + return -EINVAL; + } + + for (i = 0; i < lpg->data->num_channels; i++) { + chan = &lpg->channels[i]; + + ret = of_property_read_u32_index(np, "qcom,dtest", i * 2, + &chan->dtest_line); + if (ret) + goto err_malformed; + + ret = of_property_read_u32_index(np, "qcom,dtest", i * 2 + 1, + &chan->dtest_value); + if (ret) + goto err_malformed; + } + + return 0; + +err_malformed: + dev_err(lpg->dev, "malformed qcom,dtest\n"); + return ret; +} + +static void lpg_apply_dtest(struct lpg_channel *chan) +{ + struct lpg *lpg = chan->lpg; + + if (!chan->dtest_line) + return; + + regmap_write(lpg->map, chan->base + PWM_SEC_ACCESS_REG, 0xa5); + regmap_write(lpg->map, chan->base + PWM_DTEST_REG(chan->dtest_line), + chan->dtest_value); +} + +static void lpg_apply(struct lpg_channel *chan) +{ + lpg_disable_glitch(chan); + lpg_apply_freq(chan); + lpg_apply_pwm_value(chan); + lpg_apply_control(chan); + lpg_apply_sync(chan); + lpg_apply_lut_control(chan); + lpg_enable_glitch(chan); +} + +static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, + struct mc_subled *subleds) +{ + enum led_brightness brightness; + struct lpg_channel *chan; + unsigned int triled_enabled = 0; + unsigned int triled_mask = 0; + unsigned int lut_mask = 0; + unsigned int duty_us; + struct lpg *lpg = led->lpg; + int i; + + for (i = 0; i < led->num_channels; i++) { + chan = led->channels[i]; + brightness = subleds[i].brightness; + + if (brightness == LED_OFF) { + chan->enabled = false; + chan->ramp_enabled = false; + } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { + lpg_calc_freq(chan, NSEC_PER_USEC); + + chan->enabled = true; + chan->ramp_enabled = true; + + lut_mask |= chan->lut_mask; + triled_enabled |= chan->triled_mask; + } else { + lpg_calc_freq(chan, NSEC_PER_USEC); + + duty_us = brightness * chan->period_us / cdev->max_brightness; + lpg_calc_duty(chan, duty_us); + chan->enabled = true; + chan->ramp_enabled = false; + + triled_enabled |= chan->triled_mask; + } + + triled_mask |= chan->triled_mask; + + lpg_apply(chan); + } + + /* Toggle triled lines */ + if (triled_mask) + triled_set(lpg, triled_mask, triled_enabled); + + /* Trigger start of ramp generator(s) */ + if (lut_mask) + lpg_lut_sync(lpg, lut_mask); +} + +static void lpg_brightness_single_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); + struct mc_subled info; + + info.brightness = value; + lpg_brightness_set(led, cdev, &info); +} + +static void lpg_brightness_mc_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); + + led_mc_calc_color_components(mc, value); + lpg_brightness_set(led, cdev, mc->subled_info); +} + +static int lpg_blink_set(struct lpg_led *led, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct lpg_channel *chan; + unsigned int period_us; + unsigned int duty_us; + int i; + + if (!*delay_on && !*delay_off) { + *delay_on = 500; + *delay_off = 500; + } + + duty_us = *delay_on * USEC_PER_MSEC; + period_us = (*delay_on + *delay_off) * USEC_PER_MSEC; + + for (i = 0; i < led->num_channels; i++) { + chan = led->channels[i]; + + lpg_calc_freq(chan, period_us); + lpg_calc_duty(chan, duty_us); + + chan->enabled = true; + chan->ramp_enabled = false; + + lpg_apply(chan); + } + + return 0; +} + +static int lpg_blink_single_set(struct led_classdev *cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); + + return lpg_blink_set(led, delay_on, delay_off); +} + +static int lpg_blink_mc_set(struct led_classdev *cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); + + return lpg_blink_set(led, delay_on, delay_off); +} + +static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *pattern, + u32 len, int repeat) +{ + struct lpg_channel *chan; + struct lpg *lpg = led->lpg; + unsigned int hi_pause; + unsigned int lo_pause; + unsigned int lo_idx; + unsigned int hi_idx; + bool ping_pong = true; + int brightness_a; + int brightness_b; + int ret; + int i; + + /* Only support oneshot or indefinite loops, due to limited pattern space */ + if (repeat != -1 && repeat != 1) + return -EINVAL; + + /* + * The LPG plays patterns with at a fixed pace, a "low pause" can be + * performed before the pattern and a "high pause" after. In order to + * save space the pattern can be played in "ping pong" mode, in which + * the pattern is first played forward, then "high pause" is applied, + * then the pattern is played backwards and finally the "low pause" is + * applied. + * + * The delta_t of the first entry is used to determine the pace of the + * pattern. + * + * If the specified pattern is a palindrome the ping pong mode is + * enabled. In this scenario the delta_t of the last entry determines + * the "low pause" time and the delta_t of the middle entry (i.e. the + * last in the programmed pattern) determines the "high pause". If the + * pattern consists of an odd number of values, no "high pause" is + * used. + * + * When ping pong mode is not selected, the delta_t of the last entry + * is used as "high pause". No "low pause" is used. + * + * delta_t of any other members of the pattern is ignored. + */ + + /* Detect palindromes and use "ping pong" to reduce LUT usage */ + for (i = 0; i < len / 2; i++) { + brightness_a = pattern[i].brightness; + brightness_b = pattern[len - i - 1].brightness; + + if (brightness_a != brightness_b) { + ping_pong = false; + break; + } + } + + if (ping_pong) { + if (len % 2) + hi_pause = 0; + else + hi_pause = pattern[(len + 1) / 2].delta_t; + lo_pause = pattern[len - 1].delta_t; + + len = (len + 1) / 2; + } else { + hi_pause = pattern[len - 1].delta_t; + lo_pause = 0; + } + + ret = lpg_lut_store(lpg, pattern, len, &lo_idx, &hi_idx); + if (ret < 0) + return ret; + + for (i = 0; i < led->num_channels; i++) { + chan = led->channels[i]; + + chan->ramp_tick_ms = pattern[0].delta_t; + chan->ramp_ping_pong = ping_pong; + chan->ramp_oneshot = repeat != -1; + + chan->ramp_lo_pause_ms = lo_pause; + chan->ramp_hi_pause_ms = hi_pause; + + chan->pattern_lo_idx = lo_idx; + chan->pattern_hi_idx = hi_idx; + } + + return 0; +} + +static int lpg_pattern_single_set(struct led_classdev *cdev, + struct led_pattern *pattern, u32 len, + int repeat) +{ + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); + int ret; + + ret = lpg_pattern_set(led, pattern, len, repeat); + if (ret < 0) + return ret; + + lpg_brightness_single_set(cdev, LED_FULL); + + return 0; +} + +static int lpg_pattern_mc_set(struct led_classdev *cdev, + struct led_pattern *pattern, u32 len, + int repeat) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); + int ret; + + ret = lpg_pattern_set(led, pattern, len, repeat); + if (ret < 0) + return ret; + + led_mc_calc_color_components(mc, LED_FULL); + lpg_brightness_set(led, cdev, mc->subled_info); + + return 0; +} + +static int lpg_pattern_clear(struct lpg_led *led) +{ + struct lpg_channel *chan; + struct lpg *lpg = led->lpg; + int i; + + chan = led->channels[0]; + lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); + + for (i = 0; i < led->num_channels; i++) { + chan = led->channels[i]; + chan->pattern_lo_idx = 0; + chan->pattern_hi_idx = 0; + } + + return 0; +} + +static int lpg_pattern_single_clear(struct led_classdev *cdev) +{ + struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); + + return lpg_pattern_clear(led); +} + +static int lpg_pattern_mc_clear(struct led_classdev *cdev) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); + + return lpg_pattern_clear(led); +} + +static int lpg_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct lpg *lpg = container_of(chip, struct lpg, pwm); + struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; + + return chan->in_use ? -EBUSY : 0; +} + +static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct lpg *lpg = container_of(chip, struct lpg, pwm); + struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; + + lpg_calc_freq(chan, div_u64(state->period, NSEC_PER_USEC)); + lpg_calc_duty(chan, div_u64(state->duty_cycle, NSEC_PER_USEC)); + chan->enabled = state->enabled; + + lpg_apply(chan); + + triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0); + + return 0; +} + +static const struct pwm_ops lpg_pwm_ops = { + .request = lpg_pwm_request, + .apply = lpg_pwm_apply, + .owner = THIS_MODULE, +}; + +static int lpg_add_pwm(struct lpg *lpg) +{ + int ret; + + lpg->pwm.base = -1; + lpg->pwm.dev = lpg->dev; + lpg->pwm.npwm = lpg->num_channels; + lpg->pwm.ops = &lpg_pwm_ops; + + ret = pwmchip_add(&lpg->pwm); + if (ret) + dev_err(lpg->dev, "failed to add PWM chip: ret %d\n", ret); + + return ret; +} + +static int lpg_parse_channel(struct lpg *lpg, struct device_node *np, + struct lpg_channel **channel) +{ + struct lpg_channel *chan; + u32 color = LED_COLOR_ID_GREEN; + u32 reg; + int ret; + + ret = of_property_read_u32(np, "reg", ®); + if (ret || !reg || reg > lpg->num_channels) { + dev_err(lpg->dev, "invalid reg of %pOFn\n", np); + return -EINVAL; + } + + chan = &lpg->channels[reg - 1]; + chan->in_use = true; + + ret = of_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) { + dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); + return ret; + } + + chan->color = color; + + *channel = chan; + + return 0; +} + +static int lpg_add_led(struct lpg *lpg, struct device_node *np) +{ + struct led_classdev *cdev; + struct device_node *child; + struct mc_subled *info; + struct lpg_led *led; + const char *state; + int num_channels; + u32 color = 0; + int ret; + int i; + + ret = of_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) { + dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); + return ret; + } + + if (color == LED_COLOR_ID_MULTI) + num_channels = of_get_available_child_count(np); + else + num_channels = 1; + + led = devm_kzalloc(lpg->dev, struct_size(led, channels, num_channels), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->lpg = lpg; + led->num_channels = num_channels; + + if (color == LED_COLOR_ID_MULTI) { + info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + i = 0; + for_each_available_child_of_node(np, child) { + ret = lpg_parse_channel(lpg, child, &led->channels[i]); + if (ret < 0) + return ret; + + info[i].color_index = led->channels[i]->color; + info[i].intensity = LED_FULL; + i++; + } + + led->mcdev.subled_info = info; + led->mcdev.num_colors = num_channels; + + cdev = &led->mcdev.led_cdev; + cdev->brightness_set = lpg_brightness_mc_set; + cdev->blink_set = lpg_blink_mc_set; + + /* Register pattern accessors only if we have a LUT block */ + if (lpg->lut_base) { + cdev->pattern_set = lpg_pattern_mc_set; + cdev->pattern_clear = lpg_pattern_mc_clear; + } + } else { + ret = lpg_parse_channel(lpg, np, &led->channels[0]); + if (ret < 0) + return ret; + + cdev = &led->cdev; + cdev->brightness_set = lpg_brightness_single_set; + cdev->blink_set = lpg_blink_single_set; + + /* Register pattern accessors only if we have a LUT block */ + if (lpg->lut_base) { + cdev->pattern_set = lpg_pattern_single_set; + cdev->pattern_clear = lpg_pattern_single_clear; + } + } + + /* Use label else node name */ + cdev->name = of_get_property(np, "label", NULL) ? : np->name; + cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); + cdev->max_brightness = 255; + + if (!of_property_read_string(np, "default-state", &state) && + !strcmp(state, "on")) + cdev->brightness = LED_FULL; + else + cdev->brightness = LED_OFF; + + cdev->brightness_set(cdev, cdev->brightness); + + if (color == LED_COLOR_ID_MULTI) + ret = devm_led_classdev_multicolor_register(lpg->dev, &led->mcdev); + else + ret = devm_led_classdev_register(lpg->dev, &led->cdev); + if (ret) + dev_err(lpg->dev, "unable to register %s\n", cdev->name); + + return ret; +} + +static int lpg_init_channels(struct lpg *lpg) +{ + const struct lpg_data *data = lpg->data; + int i; + + lpg->num_channels = data->num_channels; + lpg->channels = devm_kcalloc(lpg->dev, data->num_channels, + sizeof(struct lpg_channel), GFP_KERNEL); + if (!lpg->channels) + return -ENOMEM; + + for (i = 0; i < data->num_channels; i++) { + lpg->channels[i].lpg = lpg; + lpg->channels[i].base = data->channels[i].base; + lpg->channels[i].triled_mask = data->channels[i].triled_mask; + lpg->channels[i].lut_mask = BIT(i); + } + + return 0; +} + +static int lpg_init_triled(struct lpg *lpg) +{ + struct device_node *np = lpg->dev->of_node; + int ret; + + /* Skip initialization if we don't have a triled block */ + if (!lpg->data->triled_base) + return 0; + + lpg->triled_base = lpg->data->triled_base; + lpg->triled_has_atc_ctl = lpg->data->triled_has_atc_ctl; + lpg->triled_has_src_sel = lpg->data->triled_has_src_sel; + + if (lpg->triled_has_src_sel) { + ret = of_property_read_u32(np, "qcom,power-source", &lpg->triled_src); + if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) { + dev_err(lpg->dev, "invalid power source\n"); + return -EINVAL; + } + } + + /* Disable automatic trickle charge LED */ + if (lpg->triled_has_atc_ctl) + regmap_write(lpg->map, lpg->triled_base + TRI_LED_ATC_CTL, 0); + + /* Configure power source */ + if (lpg->triled_has_src_sel) + regmap_write(lpg->map, lpg->triled_base + TRI_LED_SRC_SEL, lpg->triled_src); + + /* Default all outputs to off */ + regmap_write(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, 0); + + return 0; +} + +static int lpg_init_lut(struct lpg *lpg) +{ + const struct lpg_data *data = lpg->data; + size_t bitmap_size; + + if (!data->lut_base) + return 0; + + lpg->lut_base = data->lut_base; + lpg->lut_size = data->lut_size; + + bitmap_size = BITS_TO_BYTES(lpg->lut_size); + lpg->lut_bitmap = devm_kzalloc(lpg->dev, bitmap_size, GFP_KERNEL); + if (!lpg->lut_bitmap) + return -ENOMEM; + + bitmap_clear(lpg->lut_bitmap, 0, lpg->lut_size); + + return 0; +} + +static int lpg_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct lpg *lpg; + int ret; + int i; + + lpg = devm_kzalloc(&pdev->dev, sizeof(*lpg), GFP_KERNEL); + if (!lpg) + return -ENOMEM; + + lpg->data = of_device_get_match_data(&pdev->dev); + if (!lpg->data) + return -EINVAL; + + lpg->dev = &pdev->dev; + + lpg->map = dev_get_regmap(pdev->dev.parent, NULL); + if (!lpg->map) { + dev_err(&pdev->dev, "parent regmap unavailable\n"); + return -ENXIO; + } + + ret = lpg_init_channels(lpg); + if (ret < 0) + return ret; + + ret = lpg_parse_dtest(lpg); + if (ret < 0) + return ret; + + ret = lpg_init_triled(lpg); + if (ret < 0) + return ret; + + ret = lpg_init_lut(lpg); + if (ret < 0) + return ret; + + for_each_available_child_of_node(pdev->dev.of_node, np) { + ret = lpg_add_led(lpg, np); + if (ret) + return ret; + } + + for (i = 0; i < lpg->num_channels; i++) + lpg_apply_dtest(&lpg->channels[i]); + + ret = lpg_add_pwm(lpg); + if (ret) + return ret; + + platform_set_drvdata(pdev, lpg); + + return 0; +} + +static int lpg_remove(struct platform_device *pdev) +{ + struct lpg *lpg = platform_get_drvdata(pdev); + + pwmchip_remove(&lpg->pwm); + + return 0; +} + +static const struct lpg_data pm8916_pwm_data = { + .pwm_9bit_mask = BIT(2), + + .num_channels = 1, + .channels = (struct lpg_channel_data[]) { + { .base = 0xbc00 }, + }, +}; + +static const struct lpg_data pm8941_lpg_data = { + .lut_base = 0xb000, + .lut_size = 64, + + .triled_base = 0xd000, + .triled_has_atc_ctl = true, + .triled_has_src_sel = true, + + .pwm_9bit_mask = 3 << 4, + + .num_channels = 8, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100 }, + { .base = 0xb200 }, + { .base = 0xb300 }, + { .base = 0xb400 }, + { .base = 0xb500, .triled_mask = BIT(5) }, + { .base = 0xb600, .triled_mask = BIT(6) }, + { .base = 0xb700, .triled_mask = BIT(7) }, + { .base = 0xb800 }, + }, +}; + +static const struct lpg_data pm8994_lpg_data = { + .lut_base = 0xb000, + .lut_size = 64, + + .pwm_9bit_mask = 3 << 4, + + .num_channels = 6, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100 }, + { .base = 0xb200 }, + { .base = 0xb300 }, + { .base = 0xb400 }, + { .base = 0xb500 }, + { .base = 0xb600 }, + }, +}; + +static const struct lpg_data pmi8994_lpg_data = { + .lut_base = 0xb000, + .lut_size = 24, + + .triled_base = 0xd000, + .triled_has_atc_ctl = true, + .triled_has_src_sel = true, + + .pwm_9bit_mask = BIT(4), + + .num_channels = 4, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100, .triled_mask = BIT(5) }, + { .base = 0xb200, .triled_mask = BIT(6) }, + { .base = 0xb300, .triled_mask = BIT(7) }, + { .base = 0xb400 }, + }, +}; + +static const struct lpg_data pmi8998_lpg_data = { + .lut_base = 0xb000, + .lut_size = 49, + + .triled_base = 0xd000, + + .pwm_9bit_mask = BIT(4), + + .num_channels = 6, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100 }, + { .base = 0xb200 }, + { .base = 0xb300, .triled_mask = BIT(5) }, + { .base = 0xb400, .triled_mask = BIT(6) }, + { .base = 0xb500, .triled_mask = BIT(7) }, + { .base = 0xb600 }, + }, +}; + +static const struct lpg_data pm8150b_lpg_data = { + .lut_base = 0xb000, + .lut_size = 49, + + .triled_base = 0xd000, + + .pwm_9bit_mask = BIT(4), + + .num_channels = 2, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100, .triled_mask = BIT(7) }, + { .base = 0xb200, .triled_mask = BIT(6) }, + }, +}; + +static const struct lpg_data pm8150l_lpg_data = { + .lut_base = 0xb000, + .lut_size = 49, + + .triled_base = 0xd000, + + .pwm_9bit_mask = BIT(4), + + .num_channels = 5, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100, .triled_mask = BIT(7) }, + { .base = 0xb200, .triled_mask = BIT(6) }, + { .base = 0xb300, .triled_mask = BIT(5) }, + { .base = 0xbc00 }, + { .base = 0xbd00 }, + + }, +}; + +static const struct of_device_id lpg_of_table[] = { + { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data }, + { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data }, + { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data }, + { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data }, + { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data }, + { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data }, + { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data }, + {} +}; +MODULE_DEVICE_TABLE(of, lpg_of_table); + +static struct platform_driver lpg_driver = { + .probe = lpg_probe, + .remove = lpg_remove, + .driver = { + .name = "qcom-spmi-lpg", + .of_match_table = lpg_of_table, + }, +}; +module_platform_driver(lpg_driver); + +MODULE_DESCRIPTION("Qualcomm LPG LED driver"); +MODULE_LICENSE("GPL v2"); From 3a24655b154811aba2453a6fbf6125b58c47ea25 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:14 -0700 Subject: [PATCH 041/212] arm64: dts: qcom: Add LPG to pm8916, pm8994, pmi8994 and pmi8998 Add PWM/LPG nodes to the PMICs currently supported by the binding. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/pm8916.dtsi | 8 ++++++++ arch/arm64/boot/dts/qcom/pm8994.dtsi | 10 ++++++++++ arch/arm64/boot/dts/qcom/pmi8994.dtsi | 10 ++++++++++ arch/arm64/boot/dts/qcom/pmi8998.dtsi | 10 ++++++++++ 4 files changed, 38 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pm8916.dtsi b/arch/arm64/boot/dts/qcom/pm8916.dtsi index f931cb0de231..db973e4ef9d3 100644 --- a/arch/arm64/boot/dts/qcom/pm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi @@ -119,6 +119,14 @@ #address-cells = <1>; #size-cells = <0>; + pm8916_pwm: pwm { + compatible = "qcom,pm8916-pwm"; + + #pwm-cells = <2>; + + status = "disabled"; + }; + pm8916_vib: vibrator@c000 { compatible = "qcom,pm8916-vib"; reg = <0xc000>; diff --git a/arch/arm64/boot/dts/qcom/pm8994.dtsi b/arch/arm64/boot/dts/qcom/pm8994.dtsi index ad19016df047..666eafb54d21 100644 --- a/arch/arm64/boot/dts/qcom/pm8994.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8994.dtsi @@ -140,6 +140,16 @@ #address-cells = <1>; #size-cells = <0>; + pm8994_lpg: lpg { + compatible = "qcom,pm8994-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <2>; + + status = "disabled"; + }; + pm8994_spmi_regulators: regulators { compatible = "qcom,pm8994-regulators"; }; diff --git a/arch/arm64/boot/dts/qcom/pmi8994.dtsi b/arch/arm64/boot/dts/qcom/pmi8994.dtsi index b4ac900ab115..2ae01082ea66 100644 --- a/arch/arm64/boot/dts/qcom/pmi8994.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8994.dtsi @@ -27,6 +27,16 @@ #address-cells = <1>; #size-cells = <0>; + pmi8994_lpg: lpg { + compatible = "qcom,pmi8994-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <2>; + + status = "disabled"; + }; + pmi8994_spmi_regulators: regulators { compatible = "qcom,pmi8994-regulators"; #address-cells = <1>; diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index d230c510d4b7..a600c25d6667 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -41,5 +41,15 @@ interrupt-names = "sc-err", "ocp"; }; }; + + pmi8998_lpg: lpg { + compatible = "qcom,pmi8998-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <2>; + + status = "disabled"; + }; }; }; From 1bdbb50dd59f7a788d825f96bde7714cfa0159a4 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:15 -0700 Subject: [PATCH 042/212] arm64: dts: qcom: sdm845: Enable user LEDs on DB845c The DB845c has 4 "user LEDs", the last one is already supported as it's just wired to a gpio. Now that the LPG binding is in place we can wire up the other 3 LEDs as well. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts index 2d5533dd4ec2..e00a8dca2c64 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts @@ -593,6 +593,30 @@ }; }; +&pmi8998_lpg { + status = "okay"; + + qcom,power-source = <1>; + + led@3 { + reg = <3>; + label = "green:user3"; + + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + + led@4 { + reg = <4>; + label = "green:user2"; + }; + + led@5 { + reg = <5>; + label = "green:user1"; + }; +}; + /* QUAT I2S Uses 4 I2S SD Lines for audio on LT9611 HDMI Bridge */ &q6afedai { qi2s@22 { From 2b7bfae14aac58858c2162461f7089e462e0acdc Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:16 -0700 Subject: [PATCH 043/212] arm64: dts: qcom: pmi8994: Define MPP block The pmi8994 has 4 multi-purpose-pins, add these to the definition. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/pmi8994.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8994.dtsi b/arch/arm64/boot/dts/qcom/pmi8994.dtsi index 2ae01082ea66..c04b0c4949d5 100644 --- a/arch/arm64/boot/dts/qcom/pmi8994.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8994.dtsi @@ -19,6 +19,18 @@ interrupt-controller; #interrupt-cells = <2>; }; + + pmi8994_mpps: mpps@a000 { + compatible = "qcom,pmi8994-mpp"; + reg = <0xa000>; + gpio-controller; + gpio-ranges = <&pmi8994_mpps 0 0 4>; + #gpio-cells = <2>; + interrupts = <0 0xa0 0 IRQ_TYPE_NONE>, + <0 0xa1 0 IRQ_TYPE_NONE>, + <0 0xa2 0 IRQ_TYPE_NONE>, + <0 0xa3 0 IRQ_TYPE_NONE>; + }; }; pmic@3 { From 4ddfcc0a1055d6f1eb83a1cc4302970ac2de1e76 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 29 Apr 2021 14:15:17 -0700 Subject: [PATCH 044/212] arm64: dts: qcom: db820c: Add user LEDs The db820c has 4 "user LEDs", all connected to the PMI8994. The first three are connected to the three current sinks provided by the TRILED and the fourth is connected to MPP2. By utilizing the DTEST bus the MPP is fed the control signal from the fourth LPG block, providing a consistent interface to the user. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi index 51e17094d7b1..a98e47cfe702 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi @@ -8,6 +8,7 @@ #include "pmi8994.dtsi" #include #include +#include #include #include #include @@ -695,6 +696,54 @@ }; }; +&pmi8994_lpg { + qcom,power-source = <1>; + + pinctrl-names = "default"; + pinctrl-0 = <&pmi8994_mpp2_userled4>; + + qcom,dtest = <0 0 + 0 0 + 0 0 + 4 1>; + + status = "okay"; + + led@1 { + reg = <1>; + label = "green:user1"; + + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + + led@2 { + reg = <2>; + label = "green:user0"; + default-state = "on"; + }; + + led@3 { + reg = <3>; + label = "green:user2"; + }; + + led@4 { + reg = <4>; + label = "green:user3"; + }; +}; + +&pmi8994_mpps { + pmi8994_mpp2_userled4: mpp2-userled4-state { + pins = "mpp2"; + function = "sink"; + + output-low; + qcom,dtest = <4>; + }; +}; + &pmi8994_spmi_regulators { vdd_gfx: s2@1700 { reg = <0x1700 0x100>; From 4419531c3db8930ffb680fa805829097b99eb805 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 8 Apr 2021 19:47:12 +0200 Subject: [PATCH 045/212] defconfig: Leds, QCOM_LPG, and all the led triggers! --- arch/arm64/configs/defconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 302b038c497f..4b3ad71efd6d 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -459,10 +459,22 @@ CONFIG_MMC_DEBUG=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y +CONFIG_LEDS_CLASS_MULTICOLOR=y +CONFIG_LEDS_QCOM_LPG=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_GPIO=y CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_PATTERN=y +CONFIG_LEDS_TRIGGER_AUDIO=y +CONFIG_LEDS_TRIGGER_TTY=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_PM8XXX=y CONFIG_DMADEVICES=y From 9a151a1e8dfa657e800362877cef2a04c47f4d68 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 8 Apr 2021 19:47:33 +0200 Subject: [PATCH 046/212] NILE/pm660l: LPG --- arch/arm64/boot/dts/qcom/pm660l.dtsi | 9 +++++ .../dts/qcom/sdm630-sony-xperia-nile.dtsi | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi index 05086cbe573b..f0a7c458d3a3 100644 --- a/arch/arm64/boot/dts/qcom/pm660l.dtsi +++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi @@ -65,6 +65,15 @@ #address-cells = <1>; #size-cells = <0>; + pm660l_lpg: lpg@b100 { + compatible = "qcom,pm660l-lpg"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + pm660l_wled: leds@d800 { compatible = "qcom,pm660l-wled"; reg = <0xd800 0xd900>; diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 2d7b584b3d0f..34618f77936d 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -10,6 +10,7 @@ #include #include #include +#include / { /* required for bootloader to select correct board */ @@ -259,6 +260,38 @@ status = "okay"; }; +&pm660l_lpg { + qcom,power-source = <1>; + + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + + led@3 { + reg = <3>; + color = ; + }; + }; +}; + &pm660l_wled { status = "okay"; }; From 86f859fc4b174a777ff2f03e95b2affdddeca80e Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 8 Apr 2021 19:47:50 +0200 Subject: [PATCH 047/212] leds: qcom-lpg: Add PM660L --- drivers/leds/rgb/leds-qcom-lpg.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index c68233b43c2c..eb0ab3d1015c 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -1139,6 +1139,25 @@ static int lpg_remove(struct platform_device *pdev) return 0; } +static const struct lpg_data pm660l_lpg_data = { + .lut_base = 0xb000, + .lut_size = 0x100, + + .triled_base = 0xd000, + + // UHH? + // Subtype is 0x11, LPG_LITE, uses this downstream: + .pwm_9bit_mask = BIT(4), + + .num_channels = 4, + .channels = (struct lpg_channel_data[]) { + { .base = 0xb100, .triled_mask = BIT(5) }, + { .base = 0xb200, .triled_mask = BIT(6) }, + { .base = 0xb300, .triled_mask = BIT(7) }, + { .base = 0xb400 }, + }, +}; + static const struct lpg_data pm8916_pwm_data = { .pwm_9bit_mask = BIT(2), @@ -1261,6 +1280,7 @@ static const struct lpg_data pm8150l_lpg_data = { }; static const struct of_device_id lpg_of_table[] = { + { .compatible = "qcom,pm660l-lpg", .data = &pm660l_lpg_data }, { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data }, { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data }, { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data }, From 28337278f56f4937612b602df4d98c73cf5728da Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 1 May 2021 22:57:57 +0200 Subject: [PATCH 048/212] qcom-lpg: Review fixups! --- .../boot/dts/qcom/sdm630-sony-xperia-nile.dtsi | 3 ++- drivers/leds/Kconfig | 13 +++++++++++-- drivers/leds/Makefile | 4 +++- drivers/leds/rgb/leds-qcom-lpg.c | 6 +++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi index 34618f77936d..a1b0928679ee 100644 --- a/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630-sony-xperia-nile.dtsi @@ -261,7 +261,8 @@ }; &pm660l_lpg { - qcom,power-source = <1>; + // No idea, and optional... + // qcom,power-source = <1>; status = "okay"; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ffb8777842ff..c0de85d3b432 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -876,8 +876,17 @@ source "drivers/leds/blink/Kconfig" comment "Flash and Torch LED drivers" source "drivers/leds/flash/Kconfig" -comment "RGB LED drivers" -source "drivers/leds/rgb/Kconfig" +#comment "RGB LED drivers" +#source "drivers/leds/rgb/Kconfig" + +config LEDS_QCOM_LPG + tristate "LED support for Qualcomm LPG" + depends on LEDS_CLASS_MULTICOLOR + depends on OF + depends on SPMI + help + This option enables support for the Light Pulse Generator found in a + wide variety of Qualcomm PMICs. comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 351153f84070..4cd1e18b37e5 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -101,7 +101,9 @@ obj-$(CONFIG_LEDS_USER) += uleds.o obj-$(CONFIG_LEDS_CLASS_FLASH) += flash/ # RGB LED Drivers -obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += rgb/ +# obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += rgb/ + +obj-$(CONFIG_LEDS_QCOM_LPG) += rgb/leds-qcom-lpg.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGERS) += trigger/ diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index eb0ab3d1015c..d35106f86775 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -918,7 +918,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) return ret; } - if (color == LED_COLOR_ID_MULTI) + if (color == LED_COLOR_ID_RGB) num_channels = of_get_available_child_count(np); else num_channels = 1; @@ -930,7 +930,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) led->lpg = lpg; led->num_channels = num_channels; - if (color == LED_COLOR_ID_MULTI) { + if (color == LED_COLOR_ID_RGB) { info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -986,7 +986,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) cdev->brightness_set(cdev, cdev->brightness); - if (color == LED_COLOR_ID_MULTI) + if (color == LED_COLOR_ID_RGB) ret = devm_led_classdev_multicolor_register(lpg->dev, &led->mcdev); else ret = devm_led_classdev_register(lpg->dev, &led->cdev); From b3bad314cde3d5e3fcf1f966c542c8835656ae18 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 4 Aug 2021 16:39:35 +0200 Subject: [PATCH 049/212] iommu/arm-smmu-qcom: Allow choosing a custom bypass emulation context It cannot be taken for granted that the last IOMMU context is free and available for us to use it to emulate bypass streams and, at least on MSM8998's lpass iommu, using the last one will produce a crash; please note that this may not be only dependant on the SoC, but also on the firmware version. To overcome to this issue, allow specifying a different context for bypass emulation with the optional DT property "qcom,bypass-cbndx": if this property is not found this means that we are either booting with ACPI instead or that we don't want to specify a custom cb because the default one (the last context bank) is fine. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 55690af1b25d..c5fd20e68a2a 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -266,7 +266,8 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu) reg = arm_smmu_gr0_read(smmu, last_s2cr); if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) { qsmmu->bypass_quirk = true; - qsmmu->bypass_cbndx = smmu->num_context_banks - 1; + if (qsmmu->bypass_cbndx == 0xff) + qsmmu->bypass_cbndx = smmu->num_context_banks - 1; set_bit(qsmmu->bypass_cbndx, smmu->context_map); @@ -386,6 +387,7 @@ static const struct arm_smmu_impl qcom_adreno_smmu_impl = { static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, const struct arm_smmu_impl *impl) { + const struct device_node *np = smmu->dev->of_node; struct qcom_smmu *qsmmu; /* Check to make sure qcom_scm has finished probing */ @@ -397,6 +399,16 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, return ERR_PTR(-ENOMEM); qsmmu->smmu.impl = impl; + qsmmu->bypass_cbndx = 0xff; + + if (np != NULL) { + /* + * This property is optional and we expect to fail finding it if: + * - Using the default bypass_cbndx (in the .cfg_probe cb) is fine; or + * - We are booting on ACPI + */ + of_property_read_u8(np, "qcom,bypass-cbndx", &qsmmu->bypass_cbndx); + } return &qsmmu->smmu; } From 76330e01e0c54219c6dbe429c7ce354847afc395 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 13:14:41 +0200 Subject: [PATCH 050/212] iommu/arm-smmu: Allow skipping context bank disable at reset time On some SoCs some IOMMU context banks are actively used from TZ during system boot, or some hypervisor configurations will trigger a system reset upon disabling some protected/secured CBs. Allow skipping the disablement of such contexts at IOMMU reset time during initialization with a new implementation detail to work around this quirk. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu.c | 10 ++++++++++ drivers/iommu/arm/arm-smmu/arm-smmu.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c index 4bc75c4ce402..2c75ef23deec 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c @@ -1622,6 +1622,16 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) /* Make sure all context banks are disabled and clear CB_FSR */ for (i = 0; i < smmu->num_context_banks; ++i) { + /* + * Some context banks cannot be disabled due to hypervisor + * configuration on some systems; if this is the case, + * skip disabling and writing FAULT on the CB FSR in order + * to avoid a system crash. + */ + if (smmu->impl && smmu->impl->reset_cb_nodisable && + smmu->impl->reset_cb_nodisable(smmu, i)) { + continue; + } arm_smmu_write_context_bank(smmu, i); arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT); } diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h index 432de2f742c3..43051ed940e5 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.h +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h @@ -430,6 +430,7 @@ struct arm_smmu_impl { u64 val); int (*cfg_probe)(struct arm_smmu_device *smmu); int (*reset)(struct arm_smmu_device *smmu); + bool (*reset_cb_nodisable)(struct arm_smmu_device *smmu, int cbndx); int (*init_context)(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *cfg, struct device *dev); void (*tlb_sync)(struct arm_smmu_device *smmu, int page, int sync, From b766d992cda8ed0590d6f7b51f5ed47907c24c70 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 13:19:14 +0200 Subject: [PATCH 051/212] iommu/arm-smmu-qcom: Avoid disabling secured context banks Some Qualcomm SoCs' TZ/hypervisor configuration is disallowing the disablement of some context banks, being them used for tzapps and/or remote processors; any attempt to disable such CBs will result in triggering a fault and the system will freeze and/or reset. For this reason, get a list of context banks that should never get disabled during smmu initialization through a DT array property `qcom,reset-nodisable-cbs`. It was chosen to not hardcode the CBs as this is dependant on the SoC's firmware, which may vary on different boards. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index c5fd20e68a2a..78fcb1fdfe40 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -12,6 +13,7 @@ struct qcom_smmu { struct arm_smmu_device smmu; + DECLARE_BITMAP(reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS); bool bypass_quirk; u8 bypass_cbndx; u32 stall_enabled; @@ -368,11 +370,20 @@ static int qcom_smmu500_reset(struct arm_smmu_device *smmu) return 0; } +static bool qcom_smmu500_reset_cb_nodisable(struct arm_smmu_device *smmu, + int cbndx) +{ + struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); + + return test_bit(cbndx, qsmmu->reset_cb_nodisable_mask); +} + static const struct arm_smmu_impl qcom_smmu_impl = { .init_context = qcom_smmu_init_context, .cfg_probe = qcom_smmu_cfg_probe, .def_domain_type = qcom_smmu_def_domain_type, .reset = qcom_smmu500_reset, + .reset_cb_nodisable = qcom_smmu500_reset_cb_nodisable, .write_s2cr = qcom_smmu_write_s2cr, }; @@ -380,6 +391,7 @@ static const struct arm_smmu_impl qcom_adreno_smmu_impl = { .init_context = qcom_adreno_smmu_init_context, .def_domain_type = qcom_smmu_def_domain_type, .reset = qcom_smmu500_reset, + .reset_cb_nodisable = qcom_smmu500_reset_cb_nodisable, .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank, .write_sctlr = qcom_adreno_smmu_write_sctlr, }; @@ -389,6 +401,8 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, { const struct device_node *np = smmu->dev->of_node; struct qcom_smmu *qsmmu; + u8 reset_nodisable_cbs[ARM_SMMU_MAX_CBS]; + int i, sz; /* Check to make sure qcom_scm has finished probing */ if (!qcom_scm_is_available()) @@ -400,6 +414,7 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, qsmmu->smmu.impl = impl; qsmmu->bypass_cbndx = 0xff; + bitmap_zero(qsmmu->reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS); if (np != NULL) { /* @@ -408,6 +423,23 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, * - We are booting on ACPI */ of_property_read_u8(np, "qcom,bypass-cbndx", &qsmmu->bypass_cbndx); + + /* + * Some context banks may not be disabled because they are + * secured: read from DT a list of secured contexts that cannot + * be disabled without crashing the system. + * This list is optional, as not all firmware configurations do + * require us skipping disablement of context banks. + */ + sz = of_property_read_variable_u8_array(np, "qcom,reset-nodisable-cbs", + reset_nodisable_cbs, + 1, ARM_SMMU_MAX_CBS); + if (sz > 0) { + for (i = 0; i < sz; i++) { + __set_bit(reset_nodisable_cbs[i], + qsmmu->reset_cb_nodisable_mask); + } + } } return &qsmmu->smmu; From 44fde9148e244600b92aa674e03dbb4ec896e9da Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 15:15:33 +0200 Subject: [PATCH 052/212] iommu/arm-smmu-qcom: Don't modify sACR on hypervisor secured iommus Avoid modifying the contents of the secure Auxiliary Control Register on some Qualcomm SoCs: due to a hypervisor configuration on some firmware versions, this would result in a system crash. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 78fcb1fdfe40..690c43697430 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -361,8 +361,15 @@ static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) static int qcom_smmu500_reset(struct arm_smmu_device *smmu) { const struct device_node *np = smmu->dev->of_node; + struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); - arm_mmu500_reset(smmu); + /* + * Execute the mmu-500 reset implementation detail only if there + * are no secured untouchable contexts in this iommu, otherwise + * the system will crash. + */ + if (bitmap_empty(qsmmu->reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS)) + arm_mmu500_reset(smmu); if (of_device_is_compatible(np, "qcom,sdm845-smmu-500")) return qcom_sdm845_smmu500_reset(smmu); From 59598b642c4491161c559aff958cb5574b14c1b3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 4 Aug 2021 17:38:27 +0200 Subject: [PATCH 053/212] arm64: dts: sdm630: Override bypass emulation context for lpass, anoc2 On the LPASS and ANOC2 IOMMUs, we cannot use the last context to emulate bypass streams: set the right context banks to avoid crashes. --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 3c402ebf3b9c..e8c0466afa77 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -634,6 +634,7 @@ clock-names = "bus"; #global-interrupts = <2>; #iommu-cells = <1>; + qcom,bypass-cbndx = /bits/ 8 <3>; interrupts = , @@ -1186,6 +1187,7 @@ compatible = "qcom,sdm630-smmu-v2", "qcom,smmu-v2"; reg = <0x05100000 0x40000>; #iommu-cells = <1>; + qcom,bypass-cbndx = /bits/ 8 <12>; #global-interrupts = <2>; interrupts = From 1b3bf3b1ce484bda3fa6b076ce09f41c64dd19f1 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 4 Aug 2021 23:10:04 +0200 Subject: [PATCH 054/212] arm64: dts: qcom: sdm630: Disallow disabling secured iommu context banks Some IOMMU context banks are secured and any attempt to disable them during arm-smmu initialization will result in a system crash. To work around this issue, add a list of context banks that will never be disabled, but only reconfigured, at initialization time. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index e8c0466afa77..2a692871aee0 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -634,7 +634,9 @@ clock-names = "bus"; #global-interrupts = <2>; #iommu-cells = <1>; - qcom,bypass-cbndx = /bits/ 8 <3>; + qcom,bypass-cbndx = /bits/ 8 <6>; + qcom,reset-nodisable-cbs = /bits/ 8 <7 8 9 10 11 12 13 14 + 15 16 17 18 19 20>; interrupts = , @@ -1152,6 +1154,7 @@ clock-names = "iface", "mem", "mem_iface"; #global-interrupts = <2>; #iommu-cells = <1>; + qcom,reset-nodisable-cbs = /bits/ 8 <2 3 4>; interrupts = , @@ -1188,6 +1191,7 @@ reg = <0x05100000 0x40000>; #iommu-cells = <1>; qcom,bypass-cbndx = /bits/ 8 <12>; + qcom,reset-nodisable-cbs = /bits/ 8 <13>; #global-interrupts = <2>; interrupts = From 534226dd9284a121b01e34e0d2abf0fe8b0c4f3b Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 5 Aug 2021 15:34:21 +0200 Subject: [PATCH 055/212] arm64: dts: qcom: Introduce SDM660 Xiaomi Mi 8 Lite (platina) Add support for the SDM660 Xiaomi Platina (Mi 8 Lite). Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/Makefile | 1 + .../boot/dts/qcom/sdm660-xiaomi-platina.dts | 717 ++++++++++++++++++ 2 files changed, 718 insertions(+) create mode 100644 arch/arm64/boot/dts/qcom/sdm660-xiaomi-platina.dts diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 70516508be56..8e06f17d7ee0 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -74,6 +74,7 @@ dtb-$(CONFIG_ARCH_QCOM) += sdm630-sony-xperia-nile-pioneer.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm630-sony-xperia-nile-voyager.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm636-sony-xperia-ganges-mermaid.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm660-xiaomi-lavender.dtb +dtb-$(CONFIG_ARCH_QCOM) += sdm660-xiaomi-platina.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm845-cheza-r1.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm845-cheza-r2.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm845-cheza-r3.dtb diff --git a/arch/arm64/boot/dts/qcom/sdm660-xiaomi-platina.dts b/arch/arm64/boot/dts/qcom/sdm660-xiaomi-platina.dts new file mode 100644 index 000000000000..0c7dc90f4661 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm660-xiaomi-platina.dts @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +/dts-v1/; + +#include "sdm660.dtsi" +#include "pm660.dtsi" +#include "pm660l.dtsi" +#include +#include +#include +#include +#include + +/ { + model = "Xiaomi Mi 8 Lite"; + compatible = "xiaomi,platina", "qcom,sdm660"; + + aliases { + serial0 = &blsp1_uart2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ramoops@ffc00000 { + compatible = "ramoops"; + reg = <0x0 0xffc00000 0x0 0x100000>; + record-size = <0x10000>; + console-size = <0x60000>; + ftrace-size = <0x10000>; + pmsg-size = <0x20000>; + ecc-size = <16>; + status = "okay"; + }; + + debug_region@ffb00000 { + reg = <0x00 0xffb00000 0x00 0x100000>; + no-map; + }; + + cdsp-region@94e00000 { + reg = <0x00 0x94e00000 0x00 0x600000>; + no-map; + }; + + removed_region@85800000 { + reg = <0x00 0x85800000 0x00 0x3700000>; + no-map; + }; + }; + + board_vbat: vbat-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT"; + + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <4000000>; + regulator-always-on; + regulator-boot-on; + }; + + cam_avdd_front_vreg: cam-avdd-front-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam-avdd-front-vreg"; + + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 49 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_avdd_front_default>; + vin-supply = <&vreg_bob>; + }; + + cam_actuator_rear_vreg: cam-actuator-rear-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam-actuator-rear-vreg"; + + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 50 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_actuator_rear_default>; + vin-supply = <&vreg_bob>; + }; + + cam_avdd_rear_vreg: cam-avdd-rear-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam-avdd-rear-vreg"; + + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 51 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_avdd_rear_default>; + vin-supply = <&vreg_bob>; + }; + + cam_dvdd_front_vreg: cam-dvdd-front-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam-dvdd-front-vreg"; + + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&pm660l_gpios 3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_dvdd_front_default>; + vin-supply = <&vreg_s5a_1p35>; + }; + + cam_dvdd_rear_vreg: cam-dvdd-rear-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam-dvdd-rear-vreg"; + + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&pm660l_gpios 4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_dvdd_rear_default>; + vin-supply = <&vreg_s5a_1p35>; + }; + + disp_vdd_vreg: disp-vdd-vreg { + compatible = "regulator-fixed"; + regulator-name = "display-vdd"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ts_vdd_vreg: ts-vdd-vreg { + compatible = "regulator-fixed"; + regulator-name = "ts-vdd"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <4000>; + enable-active-high; + regulator-boot-on; + gpio = <&tlmm 73 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&ts_vdd_default>; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + + regulator-always-on; + regulator-boot-on; + }; + + gpio_keys { + status = "okay"; + compatible = "gpio-keys"; + input-name = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + + vol_up { + label = "Volume Up"; + gpios = <&pm660l_gpios 7 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + wakeup-source; + debounce-interval = <15>; + }; + }; + + gpio-hall-sensor { + compatible = "gpio-keys"; + label = "Hall effect sensor"; + + pinctrl-names = "default"; + pinctrl-0 = <&hall_sensor_default>; + + hall-sensor { + label = "Hall Effect Sensor"; + gpios = <&tlmm 75 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + linux,can-disable; + wakeup-source; + }; + }; + + + /* + * Until we hook up type-c detection, we + * have to stick with this. But it works. + */ + extcon_usb: extcon-usb { + compatible = "linux,extcon-usb-gpio"; + id-gpio = <&tlmm 58 GPIO_ACTIVE_HIGH>; + }; +}; + +&blsp_i2c2 { + status = "okay"; + + /* + * Two instances of SMB1355 charger: 0x8, 0xc but only + * one SMB1355 chip is present on board. What's the + * right address? + */ +}; + +&blsp1_uart2 { + status = "okay"; +}; + +&rpm_requests { + pm660l-regulators { + compatible = "qcom,rpm-pm660l-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + + vdd_l1_l9_l10-supply = <&vreg_s2b_1p05>; + vdd_l2-supply = <&vreg_bob>; + vdd_l3_l5_l7_l8-supply = <&vreg_bob>; + vdd_l4_l6-supply = <&vreg_bob>; + vdd_bob-supply = <&vph_pwr>; + + vreg_s1b_1p125: s1 { + regulator-min-microvolt = <1125000>; + regulator-max-microvolt = <1125000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + vreg_s2b_1p05: s2 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + /* + * At least on Nile's configuration, S3B/S4B (VDD_CX) and + * S5B (VDD_MX) are managed only through RPM Power Domains. + * Trying to set a voltage on the main supply will create + * havoc and freeze the SoC. + * In any case, reference voltages for these regulators are: + * S3B/S4B: 0.870V + * S5B: 0.915V + */ + + /* LDOs */ + vreg_l1b_0p925: l1 { + regulator-min-microvolt = <920000>; + regulator-max-microvolt = <928000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l2b_2p95: l2 { + /* + * This regulator supports 1.648 - 3.104V on this board + * but we set a max voltage of anything less than 2.7V + * to satisfy a condition in sdhci.c that will disable + * 3.3V SDHCI signaling, which happens to be not really + * supported on the Xperia Nile/Ganges platform. + */ + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2696000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l3b_3p3: l3 { + regulator-min-microvolt = <3296000>; + regulator-max-microvolt = <3312000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-min-microamp = <200>; + regulator-max-microamp = <600000>; + regulator-system-load = <100000>; + regulator-allow-set-load; + }; + + vreg_l4b_2p95: l4 { + regulator-min-microvolt = <2944000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + + regulator-min-microamp = <200>; + regulator-max-microamp = <600000>; + regulator-system-load = <570000>; + regulator-allow-set-load; + }; + + /* + * Downstream specifies a range of 1721-3600mV, + * but the only assigned consumers are SDHCI2 VMMC + * and Coresight QPDI that both request pinned 2.95V. + * Tighten the range to 1.8-3.328 (closest to 3.3) to + * make the mmc driver happy. + */ + vreg_l5b_29p5: l5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3328000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + regulator-system-load = <800000>; + }; + + vreg_l7b_3p125: l7 { + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <3128000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l8b_3p3: l8 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3400000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* L9B (870mV) is currently unused */ + /* L10B (915mV) is currently unused */ + + vreg_bob: bob { + regulator-min-microvolt = <3304000>; + regulator-max-microvolt = <3624000>; + regulator-enable-ramp-delay = <500>; + regulator-ramp-delay = <0>; + }; + }; + + + pm660-regulators { + compatible = "qcom,rpm-pm660-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + + vdd_l1_l6_l7-supply = <&vreg_s5a_1p35>; + vdd_l2_l3-supply = <&vreg_s2b_1p05>; + vdd_l5-supply = <&vreg_s2b_1p05>; + vdd_l8_l9_l10_l11_l12_l13_l14-supply = <&vreg_s4a_2p04>; + vdd_l15_l16_l17_l18_l19-supply = <&vreg_bob>; + + /* + * S1A (FTAPC0), S2A (FTAPC1), S3A (HFAPC1) are managed + * by the Core Power Reduction hardened (CPRh) and the + * Operating State Manager (OSM) HW automatically. + */ + + vreg_s4a_2p04: s4 { + regulator-min-microvolt = <2040000>; + regulator-max-microvolt = <2040000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + regulator-always-on; + }; + + vreg_s5a_1p35: s5 { + regulator-min-microvolt = <1224000>; + regulator-max-microvolt = <1350000>; + regulator-enable-ramp-delay = <200>; + regulator-ramp-delay = <0>; + }; + + vreg_s6a_0p87: s6 { + regulator-min-microvolt = <504000>; + regulator-max-microvolt = <992000>; + regulator-enable-ramp-delay = <150>; + regulator-ramp-delay = <0>; + }; + + /* LDOs */ + vreg_l1a_1p225: l1 { + regulator-min-microvolt = <1226000>; + regulator-max-microvolt = <1250000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l2a_1p0: l2 { + regulator-min-microvolt = <944000>; + regulator-max-microvolt = <1008000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l3a_1p0: l3 { + regulator-min-microvolt = <944000>; + regulator-max-microvolt = <1008000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l5a_0p848: l5 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l6a_1p3: l6 { + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1368000>; + regulator-allow-set-load; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l7a_1p2: l7 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l8a_1p8: l8 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-system-load = <325000>; + regulator-allow-set-load; + }; + + + vreg_l9a_1p8: l9 { + regulator-min-microvolt = <1804000>; + regulator-max-microvolt = <1896000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l10a_1p8: l10 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + + vreg_l11a_1p8: l11 { + regulator-min-microvolt = <1784000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l12a_1p8: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l13a_1p8: l13 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1944000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_l14a_1p8: l14 { + regulator-min-microvolt = <1710000>; + regulator-max-microvolt = <1952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l15a_1p8: l15 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* DRX: QM13111 */ + vreg_l16a_2p7: l16 { + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2712000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + vreg_l17a_1p8: l17 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + }; + + /* L18A (2.7V) is unused */ + + vreg_l19a_3p3: l19 { + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3328000>; + regulator-enable-ramp-delay = <250>; + regulator-ramp-delay = <0>; + regulator-allow-set-load; + }; + }; +}; + +&sdhc_1 { + status = "okay"; + supports-cqe; + + mmc-ddr-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + + vmmc-supply = <&vreg_l4b_2p95>; + vqmmc-supply = <&vreg_l8a_1p8>; +}; + +&sdhc_2 { + status = "disabled"; + + vmmc-supply = <&vreg_l5b_29p5>; + vqmmc-supply = <&vreg_l2b_2p95>; +}; + +&pm660_gpios { + nfc_clk_req_n: nfc-clkreq-n { + pins = "gpio4"; + function = PMIC_GPIO_FUNC_NORMAL; + bias-disable; + input-enable; + power-source = <1>; + }; +}; + +&pm660l_gpios { + cam_dvdd_front_default: cam-dvdd-front-default { + pins = "gpio3"; + function = "normal"; + output-low; + power-source = <0>; + }; + + cam_dvdd_rear_default: cam-dvdd-rear-default { + pins = "gpio4"; + function = "normal"; + output-low; + power-source = <0>; + }; + + vol_key_gpio_default: vol-key-gpio-default { + pins = "gpio7"; + function = "normal"; + bias-pull-up; + input-enable; + qcom,drive-strength = ; + }; +}; + +&tlmm { + gpio-reserved-ranges = <8 4>; + + camera_rear_default: camera-rear-default { + mclk0 { + pins = "gpio32"; + function = "cam_mclk"; + drive-strength = <4>; + bias-disable; + }; + + rst { + pins = "gpio46"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + }; + + camera_front_default: camera-front-default { + mclk1 { + pins = "gpio33"; + function = "cam_mclk"; + drive-strength = <4>; + bias-disable; + }; + + rst { + pins = "gpio47"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + }; + + camera_front_iris_default: camera-front-iris-default { + mclk1 { + pins = "gpio35"; + function = "cam_mclk"; + drive-strength = <4>; + bias-disable; + }; + + rst { + pins = "gpio52"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + }; + + cam_avdd_front_default: cam-avdd-front-default { + pins = "gpio49"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + cam_actuator_rear_default: cam-actuator-rear-default { + pins = "gpio50"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + cam_avdd_rear_default: cam-avdd-rear-default { + pins = "gpio51"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + panel_reset_n: panel-rst-n { + pins = "gpio53"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + mdp_vsync_n: mdp-vsync-n { + pins = "gpio59"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + ts_rst_n: ts-rst-n { + pins = "gpio66"; + function = "gpio"; + bias-disable; + drive-strength = <8>; + }; + + ts_int_active: ts-int-active { + pins = "gpio67"; + drive-strength = <16>; + bias-pull-up; + }; + + ts_vdd_default: ts-vdd-default { + pins = "gpio73"; + function = "gpio"; + bias-disable; + drive-strength = <8>; + }; + + hall_sensor_default: hall-sensor-default { + pins = "gpio75"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + +}; + +&usb3 { + status = "okay"; +}; + +&usb3_dwc3 { + dr_mode = "peripheral"; + extcon = <&extcon_usb>; +}; From e113e17634d05118bc6212b7c08b48bdf29c86b8 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 15 Nov 2020 16:19:08 +0100 Subject: [PATCH 056/212] cpuidle: qcom_spm: Detach state machine from main SPM handling In commit a871be6b8eee ("cpuidle: Convert Qualcomm SPM driver to a generic CPUidle driver") the SPM driver has been converted to a generic CPUidle driver: that was mainly made to simplify the driver and that was a great accomplishment; Though, at that time, this driver was only applicable to ARM 32-bit SoCs, lacking logic about the handling of newer generation SAW. In preparation for the enablement of SPM features on AArch64/ARM64, split the cpuidle-qcom-spm driver in two: the CPUIdle related state machine (currently used only on ARM SoCs) stays there, while the SPM communication handling lands back in soc/qcom/spm.c and also making sure to not discard the simplifications that were introduced in the aforementioned commit. Since now the "two drivers" are split, the SCM dependency in the main SPM handling is gone and for this reason it was also possible to move the SPM initialization early: this will also make sure that whenever the SAW CPUIdle driver is getting initialized, the SPM driver will be ready to do the job. Please note that the anticipation of the SPM initialization was also done to optimize the boot times on platforms that have their CPU/L2 idle states managed by other means (such as PSCI), while needing SAW initialization for other purposes, like AVS control. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Stephan Gerhold Tested-by: Stephan Gerhold --- drivers/cpuidle/Kconfig.arm | 1 + drivers/cpuidle/cpuidle-qcom-spm.c | 326 +++++++---------------------- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/spm.c | 212 +++++++++++++++++++ include/soc/qcom/spm.h | 41 ++++ 6 files changed, 336 insertions(+), 254 deletions(-) create mode 100644 drivers/soc/qcom/spm.c create mode 100644 include/soc/qcom/spm.h diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 334f83e56120..8a02213c8391 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -112,6 +112,7 @@ config ARM_QCOM_SPM_CPUIDLE select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES select QCOM_SCM + select QCOM_SPM help Select this to enable cpuidle for Qualcomm processors. The Subsystem Power Manager (SPM) controls low power modes for the diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c index c0e7971da2da..01e77913a414 100644 --- a/drivers/cpuidle/cpuidle-qcom-spm.c +++ b/drivers/cpuidle/cpuidle-qcom-spm.c @@ -18,158 +18,18 @@ #include #include #include +#include #include #include #include "dt_idle_states.h" -#define MAX_PMIC_DATA 2 -#define MAX_SEQ_DATA 64 -#define SPM_CTL_INDEX 0x7f -#define SPM_CTL_INDEX_SHIFT 4 -#define SPM_CTL_EN BIT(0) - -enum pm_sleep_mode { - PM_SLEEP_MODE_STBY, - PM_SLEEP_MODE_RET, - PM_SLEEP_MODE_SPC, - PM_SLEEP_MODE_PC, - PM_SLEEP_MODE_NR, -}; - -enum spm_reg { - SPM_REG_CFG, - SPM_REG_SPM_CTL, - SPM_REG_DLY, - SPM_REG_PMIC_DLY, - SPM_REG_PMIC_DATA_0, - SPM_REG_PMIC_DATA_1, - SPM_REG_VCTL, - SPM_REG_SEQ_ENTRY, - SPM_REG_SPM_STS, - SPM_REG_PMIC_STS, - SPM_REG_NR, -}; - -struct spm_reg_data { - const u8 *reg_offset; - u32 spm_cfg; - u32 spm_dly; - u32 pmic_dly; - u32 pmic_data[MAX_PMIC_DATA]; - u8 seq[MAX_SEQ_DATA]; - u8 start_index[PM_SLEEP_MODE_NR]; -}; - -struct spm_driver_data { +struct cpuidle_qcom_spm_data { struct cpuidle_driver cpuidle_driver; - void __iomem *reg_base; - const struct spm_reg_data *reg_data; + struct spm_driver_data *spm; }; -static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { - [SPM_REG_CFG] = 0x08, - [SPM_REG_SPM_CTL] = 0x30, - [SPM_REG_DLY] = 0x34, - [SPM_REG_SEQ_ENTRY] = 0x80, -}; - -/* SPM register data for 8974, 8084 */ -static const struct spm_reg_data spm_reg_8974_8084_cpu = { - .reg_offset = spm_reg_offset_v2_1, - .spm_cfg = 0x1, - .spm_dly = 0x3C102800, - .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, - 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, - 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 3, -}; - -/* SPM register data for 8226 */ -static const struct spm_reg_data spm_reg_8226_cpu = { - .reg_offset = spm_reg_offset_v2_1, - .spm_cfg = 0x0, - .spm_dly = 0x3C102800, - .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, - 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, - 0x80, 0x10, 0x26, 0x30, 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 5, -}; - -static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { - [SPM_REG_CFG] = 0x08, - [SPM_REG_SPM_CTL] = 0x20, - [SPM_REG_PMIC_DLY] = 0x24, - [SPM_REG_PMIC_DATA_0] = 0x28, - [SPM_REG_PMIC_DATA_1] = 0x2C, - [SPM_REG_SEQ_ENTRY] = 0x80, -}; - -/* SPM register data for 8064 */ -static const struct spm_reg_data spm_reg_8064_cpu = { - .reg_offset = spm_reg_offset_v1_1, - .spm_cfg = 0x1F, - .pmic_dly = 0x02020004, - .pmic_data[0] = 0x0084009C, - .pmic_data[1] = 0x00A4001C, - .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, - 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, - .start_index[PM_SLEEP_MODE_STBY] = 0, - .start_index[PM_SLEEP_MODE_SPC] = 2, -}; - -static inline void spm_register_write(struct spm_driver_data *drv, - enum spm_reg reg, u32 val) -{ - if (drv->reg_data->reg_offset[reg]) - writel_relaxed(val, drv->reg_base + - drv->reg_data->reg_offset[reg]); -} - -/* Ensure a guaranteed write, before return */ -static inline void spm_register_write_sync(struct spm_driver_data *drv, - enum spm_reg reg, u32 val) -{ - u32 ret; - - if (!drv->reg_data->reg_offset[reg]) - return; - - do { - writel_relaxed(val, drv->reg_base + - drv->reg_data->reg_offset[reg]); - ret = readl_relaxed(drv->reg_base + - drv->reg_data->reg_offset[reg]); - if (ret == val) - break; - cpu_relax(); - } while (1); -} - -static inline u32 spm_register_read(struct spm_driver_data *drv, - enum spm_reg reg) -{ - return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); -} - -static void spm_set_low_power_mode(struct spm_driver_data *drv, - enum pm_sleep_mode mode) -{ - u32 start_index; - u32 ctl_val; - - start_index = drv->reg_data->start_index[mode]; - - ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); - ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); - ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; - ctl_val |= SPM_CTL_EN; - spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); -} - static int qcom_pm_collapse(unsigned long int unused) { qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON); @@ -201,10 +61,10 @@ static int qcom_cpu_spc(struct spm_driver_data *drv) static int spm_enter_idle_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx) { - struct spm_driver_data *data = container_of(drv, struct spm_driver_data, - cpuidle_driver); + struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data, + cpuidle_driver); - return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data); + return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm); } static struct cpuidle_driver qcom_spm_idle_driver = { @@ -225,134 +85,92 @@ static const struct of_device_id qcom_idle_state_match[] = { { }, }; -static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu) +static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu) { + struct platform_device *pdev = NULL; + struct device_node *cpu_node, *saw_node; + struct cpuidle_qcom_spm_data *data = NULL; int ret; - memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv)); - drv->cpumask = (struct cpumask *)cpumask_of(cpu); + cpu_node = of_cpu_device_node_get(cpu); + if (!cpu_node) + return -ENODEV; - /* Parse idle states from device tree */ - ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1); + saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); + if (!saw_node) + return -ENODEV; + + pdev = of_find_device_by_node(saw_node); + of_node_put(saw_node); + of_node_put(cpu_node); + if (!pdev) + return -ENODEV; + + data = devm_kzalloc(cpuidle_dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->spm = dev_get_drvdata(&pdev->dev); + if (!data->spm) + return -EINVAL; + + data->cpuidle_driver = qcom_spm_idle_driver; + data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu); + + ret = dt_init_idle_driver(&data->cpuidle_driver, + qcom_idle_state_match, 1); if (ret <= 0) return ret ? : -ENODEV; - /* We have atleast one power down mode */ - return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask); + ret = qcom_scm_set_warm_boot_addr(cpu_resume_arm, cpumask_of(cpu)); + if (ret) + return ret; + + return cpuidle_register(&data->cpuidle_driver, NULL); } -static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, - int *spm_cpu) +static int spm_cpuidle_drv_probe(struct platform_device *pdev) { - struct spm_driver_data *drv = NULL; - struct device_node *cpu_node, *saw_node; - int cpu; - bool found = 0; - - for_each_possible_cpu(cpu) { - cpu_node = of_cpu_device_node_get(cpu); - if (!cpu_node) - continue; - saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); - found = (saw_node == pdev->dev.of_node); - of_node_put(saw_node); - of_node_put(cpu_node); - if (found) - break; - } - - if (found) { - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); - if (drv) - *spm_cpu = cpu; - } - - return drv; -} - -static const struct of_device_id spm_match_table[] = { - { .compatible = "qcom,msm8226-saw2-v2.1-cpu", - .data = &spm_reg_8226_cpu }, - { .compatible = "qcom,msm8974-saw2-v2.1-cpu", - .data = &spm_reg_8974_8084_cpu }, - { .compatible = "qcom,apq8084-saw2-v2.1-cpu", - .data = &spm_reg_8974_8084_cpu }, - { .compatible = "qcom,apq8064-saw2-v1.1-cpu", - .data = &spm_reg_8064_cpu }, - { }, -}; - -static int spm_dev_probe(struct platform_device *pdev) -{ - struct spm_driver_data *drv; - struct resource *res; - const struct of_device_id *match_id; - void __iomem *addr; int cpu, ret; if (!qcom_scm_is_available()) return -EPROBE_DEFER; - drv = spm_get_drv(pdev, &cpu); - if (!drv) - return -EINVAL; - platform_set_drvdata(pdev, drv); + for_each_possible_cpu(cpu) { + ret = spm_cpuidle_register(&pdev->dev, cpu); + if (ret && ret != -ENODEV) { + dev_err(&pdev->dev, + "Cannot register for CPU%d: %d\n", cpu, ret); + } + } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(drv->reg_base)) - return PTR_ERR(drv->reg_base); - - match_id = of_match_node(spm_match_table, pdev->dev.of_node); - if (!match_id) - return -ENODEV; - - drv->reg_data = match_id->data; - - ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu); - if (ret) - return ret; - - /* Write the SPM sequences first.. */ - addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; - __iowrite32_copy(addr, drv->reg_data->seq, - ARRAY_SIZE(drv->reg_data->seq) / 4); - - /* - * ..and then the control registers. - * On some SoC if the control registers are written first and if the - * CPU was held in reset, the reset signal could trigger the SPM state - * machine, before the sequences are completely written. - */ - spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); - spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); - spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); - spm_register_write(drv, SPM_REG_PMIC_DATA_0, - drv->reg_data->pmic_data[0]); - spm_register_write(drv, SPM_REG_PMIC_DATA_1, - drv->reg_data->pmic_data[1]); - - /* Set up Standby as the default low power mode */ - spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); - - return cpuidle_register(&drv->cpuidle_driver, NULL); -} - -static int spm_dev_remove(struct platform_device *pdev) -{ - struct spm_driver_data *drv = platform_get_drvdata(pdev); - - cpuidle_unregister(&drv->cpuidle_driver); return 0; } -static struct platform_driver spm_driver = { - .probe = spm_dev_probe, - .remove = spm_dev_remove, +static struct platform_driver spm_cpuidle_driver = { + .probe = spm_cpuidle_drv_probe, .driver = { - .name = "saw", - .of_match_table = spm_match_table, + .name = "qcom-spm-cpuidle", + .suppress_bind_attrs = true, }, }; -builtin_platform_driver(spm_driver); +static int __init qcom_spm_cpuidle_init(void) +{ + struct platform_device *pdev; + int ret; + + ret = platform_driver_register(&spm_cpuidle_driver); + if (ret) + return ret; + + pdev = platform_device_register_simple("qcom-spm-cpuidle", + -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&spm_cpuidle_driver); + return PTR_ERR(pdev); + } + + return 0; +} +device_initcall(qcom_spm_cpuidle_init); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 79b568f82a1c..fe3c486ae32d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -190,6 +190,15 @@ config QCOM_SOCINFO Say yes here to support the Qualcomm socinfo driver, providing information about the SoC to user space. +config QCOM_SPM + tristate "Qualcomm Subsystem Power Manager (SPM)" + depends on ARCH_QCOM + select QCOM_SCM + help + Enable the support for the Qualcomm Subsystem Power Manager, used + to manage cores, L2 low power modes and to configure the internal + Adaptive Voltage Scaler parameters, where supported. + config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ad675a6593d0..24514c722832 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o +obj-$(CONFIG_QCOM_SPM) += spm.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c new file mode 100644 index 000000000000..d5ba0b5488fc --- /dev/null +++ b/drivers/soc/qcom/spm.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2015, Linaro Ltd. + * + * SAW power controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPM_CTL_INDEX 0x7f +#define SPM_CTL_INDEX_SHIFT 4 +#define SPM_CTL_EN BIT(0) + +enum spm_reg { + SPM_REG_CFG, + SPM_REG_SPM_CTL, + SPM_REG_DLY, + SPM_REG_PMIC_DLY, + SPM_REG_PMIC_DATA_0, + SPM_REG_PMIC_DATA_1, + SPM_REG_VCTL, + SPM_REG_SEQ_ENTRY, + SPM_REG_SPM_STS, + SPM_REG_PMIC_STS, + SPM_REG_NR, +}; + +static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { + [SPM_REG_CFG] = 0x08, + [SPM_REG_SPM_CTL] = 0x30, + [SPM_REG_DLY] = 0x34, + [SPM_REG_SEQ_ENTRY] = 0x80, +}; + +/* SPM register data for 8974, 8084 */ +static const struct spm_reg_data spm_reg_8974_8084_cpu = { + .reg_offset = spm_reg_offset_v2_1, + .spm_cfg = 0x1, + .spm_dly = 0x3C102800, + .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, + 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, + 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 3, +}; + +/* SPM register data for 8226 */ +static const struct spm_reg_data spm_reg_8226_cpu = { + .reg_offset = spm_reg_offset_v2_1, + .spm_cfg = 0x0, + .spm_dly = 0x3C102800, + .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, + 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, + 0x80, 0x10, 0x26, 0x30, 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 5, +}; + +static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { + [SPM_REG_CFG] = 0x08, + [SPM_REG_SPM_CTL] = 0x20, + [SPM_REG_PMIC_DLY] = 0x24, + [SPM_REG_PMIC_DATA_0] = 0x28, + [SPM_REG_PMIC_DATA_1] = 0x2C, + [SPM_REG_SEQ_ENTRY] = 0x80, +}; + +/* SPM register data for 8064 */ +static const struct spm_reg_data spm_reg_8064_cpu = { + .reg_offset = spm_reg_offset_v1_1, + .spm_cfg = 0x1F, + .pmic_dly = 0x02020004, + .pmic_data[0] = 0x0084009C, + .pmic_data[1] = 0x00A4001C, + .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, + 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 2, +}; + +static inline void spm_register_write(struct spm_driver_data *drv, + enum spm_reg reg, u32 val) +{ + if (drv->reg_data->reg_offset[reg]) + writel_relaxed(val, drv->reg_base + + drv->reg_data->reg_offset[reg]); +} + +/* Ensure a guaranteed write, before return */ +static inline void spm_register_write_sync(struct spm_driver_data *drv, + enum spm_reg reg, u32 val) +{ + u32 ret; + + if (!drv->reg_data->reg_offset[reg]) + return; + + do { + writel_relaxed(val, drv->reg_base + + drv->reg_data->reg_offset[reg]); + ret = readl_relaxed(drv->reg_base + + drv->reg_data->reg_offset[reg]); + if (ret == val) + break; + cpu_relax(); + } while (1); +} + +static inline u32 spm_register_read(struct spm_driver_data *drv, + enum spm_reg reg) +{ + return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); +} + +void spm_set_low_power_mode(struct spm_driver_data *drv, + enum pm_sleep_mode mode) +{ + u32 start_index; + u32 ctl_val; + + start_index = drv->reg_data->start_index[mode]; + + ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); + ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); + ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; + ctl_val |= SPM_CTL_EN; + spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); +} + +static const struct of_device_id spm_match_table[] = { + { .compatible = "qcom,msm8226-saw2-v2.1-cpu", + .data = &spm_reg_8226_cpu }, + { .compatible = "qcom,msm8974-saw2-v2.1-cpu", + .data = &spm_reg_8974_8084_cpu }, + { .compatible = "qcom,apq8084-saw2-v2.1-cpu", + .data = &spm_reg_8974_8084_cpu }, + { .compatible = "qcom,apq8064-saw2-v1.1-cpu", + .data = &spm_reg_8064_cpu }, + { }, +}; + +static int spm_dev_probe(struct platform_device *pdev) +{ + const struct of_device_id *match_id; + struct spm_driver_data *drv; + struct resource *res; + void __iomem *addr; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(drv->reg_base)) + return PTR_ERR(drv->reg_base); + + match_id = of_match_node(spm_match_table, pdev->dev.of_node); + if (!match_id) + return -ENODEV; + + drv->reg_data = match_id->data; + platform_set_drvdata(pdev, drv); + + /* Write the SPM sequences first.. */ + addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; + __iowrite32_copy(addr, drv->reg_data->seq, + ARRAY_SIZE(drv->reg_data->seq) / 4); + + /* + * ..and then the control registers. + * On some SoC if the control registers are written first and if the + * CPU was held in reset, the reset signal could trigger the SPM state + * machine, before the sequences are completely written. + */ + spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); + spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); + spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); + spm_register_write(drv, SPM_REG_PMIC_DATA_0, + drv->reg_data->pmic_data[0]); + spm_register_write(drv, SPM_REG_PMIC_DATA_1, + drv->reg_data->pmic_data[1]); + + /* Set up Standby as the default low power mode */ + spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); + + return 0; +} + +static struct platform_driver spm_driver = { + .probe = spm_dev_probe, + .driver = { + .name = "qcom_spm", + .of_match_table = spm_match_table, + }, +}; + +static int __init qcom_spm_init(void) +{ + return platform_driver_register(&spm_driver); +} +arch_initcall(qcom_spm_init); diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h new file mode 100644 index 000000000000..4c7e5ac2583d --- /dev/null +++ b/include/soc/qcom/spm.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2015, Linaro Ltd. + */ + +#ifndef __SPM_H__ +#define __SPM_H__ + +#include + +#define MAX_PMIC_DATA 2 +#define MAX_SEQ_DATA 64 + +enum pm_sleep_mode { + PM_SLEEP_MODE_STBY, + PM_SLEEP_MODE_RET, + PM_SLEEP_MODE_SPC, + PM_SLEEP_MODE_PC, + PM_SLEEP_MODE_NR, +}; + +struct spm_reg_data { + const u8 *reg_offset; + u32 spm_cfg; + u32 spm_dly; + u32 pmic_dly; + u32 pmic_data[MAX_PMIC_DATA]; + u8 seq[MAX_SEQ_DATA]; + u8 start_index[PM_SLEEP_MODE_NR]; +}; + +struct spm_driver_data { + void __iomem *reg_base; + const struct spm_reg_data *reg_data; +}; + +void spm_set_low_power_mode(struct spm_driver_data *drv, + enum pm_sleep_mode mode); + +#endif /* __SPM_H__ */ From 8d6b6e3078001e3c25bbd06a5babd1894f15f9dc Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 21 Jun 2021 19:29:00 +0200 Subject: [PATCH 057/212] dt-bindings: soc: qcom: Add devicetree binding for QCOM SPM Add devicetree binding for Qualcomm Subsystem Power Manager (SPM). Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/soc/qcom/qcom,spm.yaml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml new file mode 100644 index 000000000000..507216100e3f --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Subsystem Power Manager binding + +maintainers: + - Andy Gross + - Bjorn Andersson + +description: | + This binding describes the Qualcomm Subsystem Power Manager, used to control + the peripheral logic surrounding the application cores in Qualcomm platforms. + +properties: + compatible: + items: + - enum: + - qcom,msm8226-saw2-v2.1-cpu + - qcom,msm8974-saw2-v2.1-cpu + - qcom,apq8084-saw2-v2.1-cpu + - qcom,apq8064-saw2-v1.1-cpu + - const: qcom,saw2 + + reg: + description: Base address and size of the SPM register region + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "qcom,kryo"; + device_type = "cpu"; + enable-method = "qcom,kpss-acc-v2"; + qcom,saw = <&saw0>; + reg = <0x0>; + operating-points-v2 = <&cpu_opp_table>; + }; + }; + + saw0: power-manager@f9089000 { + compatible = "qcom,msm8974-saw2-v2.1-cpu", "qcom,saw2"; + reg = <0xf9089000 0x1000>; + }; + +... From f4d9f13eb79dc38eb045c295105909c8b3ec73ce Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 15 Nov 2020 16:25:23 +0100 Subject: [PATCH 058/212] soc: qcom: spm: Implement support for SAWv4.1, SDM630/660 L2 AVS Implement the support for SAW v4.1, used in at least MSM8998, SDM630, SDM660 and APQ variants and, while at it, also add the configuration for the SDM630/660 Silver and Gold cluster L2 Adaptive Voltage Scaler: this is also one of the prerequisites to allow the OSM controller to perform DCVS. Please note that despite there are various "versions" of these values downstream, these are the only ones that are perfectly stable on the entire set of tested devices. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Stephan Gerhold --- drivers/soc/qcom/spm.c | 32 +++++++++++++++++++++++++++++--- include/soc/qcom/spm.h | 4 +++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index d5ba0b5488fc..28ffaa2a3509 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -32,10 +32,29 @@ enum spm_reg { SPM_REG_SEQ_ENTRY, SPM_REG_SPM_STS, SPM_REG_PMIC_STS, + SPM_REG_AVS_CTL, + SPM_REG_AVS_LIMIT, SPM_REG_NR, }; -static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { +static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { + [SPM_REG_AVS_CTL] = 0x904, + [SPM_REG_AVS_LIMIT] = 0x908, +}; + +static const struct spm_reg_data spm_reg_660_gold_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4580458, +}; + +static const struct spm_reg_data spm_reg_660_silver_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x101c031, + .avs_limit = 0x4580458, +}; + +static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, [SPM_REG_SPM_CTL] = 0x30, [SPM_REG_DLY] = 0x34, @@ -66,7 +85,7 @@ static const struct spm_reg_data spm_reg_8226_cpu = { .start_index[PM_SLEEP_MODE_SPC] = 5, }; -static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { +static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, [SPM_REG_SPM_CTL] = 0x20, [SPM_REG_PMIC_DLY] = 0x24, @@ -138,6 +157,10 @@ void spm_set_low_power_mode(struct spm_driver_data *drv, } static const struct of_device_id spm_match_table[] = { + { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", + .data = &spm_reg_660_gold_l2 }, + { .compatible = "qcom,sdm660-silver-saw2-v4.1-l2", + .data = &spm_reg_660_silver_l2 }, { .compatible = "qcom,msm8226-saw2-v2.1-cpu", .data = &spm_reg_8226_cpu }, { .compatible = "qcom,msm8974-saw2-v2.1-cpu", @@ -183,6 +206,8 @@ static int spm_dev_probe(struct platform_device *pdev) * CPU was held in reset, the reset signal could trigger the SPM state * machine, before the sequences are completely written. */ + spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl); + spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit); spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); @@ -192,7 +217,8 @@ static int spm_dev_probe(struct platform_device *pdev) drv->reg_data->pmic_data[1]); /* Set up Standby as the default low power mode */ - spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); + if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) + spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); return 0; } diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h index 4c7e5ac2583d..4951f9d8b0bd 100644 --- a/include/soc/qcom/spm.h +++ b/include/soc/qcom/spm.h @@ -21,11 +21,13 @@ enum pm_sleep_mode { }; struct spm_reg_data { - const u8 *reg_offset; + const u16 *reg_offset; u32 spm_cfg; u32 spm_dly; u32 pmic_dly; u32 pmic_data[MAX_PMIC_DATA]; + u32 avs_ctl; + u32 avs_limit; u8 seq[MAX_SEQ_DATA]; u8 start_index[PM_SLEEP_MODE_NR]; }; From 441ab88ea675b0922e5f4c32ef13b3f9f025e384 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 15 Nov 2020 16:31:33 +0100 Subject: [PATCH 059/212] soc: qcom: spm: Add compatible for MSM8998 SAWv4.1 L2 Add the SAWv4.1 parameters for MSM8998's Gold and Silver clusters. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Stephan Gerhold --- drivers/soc/qcom/spm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 28ffaa2a3509..6974f911ba80 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -54,6 +54,18 @@ static const struct spm_reg_data spm_reg_660_silver_l2 = { .avs_limit = 0x4580458, }; +static const struct spm_reg_data spm_reg_8998_gold_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4700470, +}; + +static const struct spm_reg_data spm_reg_8998_silver_l2 = { + .reg_offset = spm_reg_offset_v4_1, + .avs_ctl = 0x1010031, + .avs_limit = 0x4200420, +}; + static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, [SPM_REG_SPM_CTL] = 0x30, @@ -165,6 +177,10 @@ static const struct of_device_id spm_match_table[] = { .data = &spm_reg_8226_cpu }, { .compatible = "qcom,msm8974-saw2-v2.1-cpu", .data = &spm_reg_8974_8084_cpu }, + { .compatible = "qcom,msm8998-gold-saw2-v4.1-l2", + .data = &spm_reg_8998_gold_l2 }, + { .compatible = "qcom,msm8998-silver-saw2-v4.1-l2", + .data = &spm_reg_8998_silver_l2 }, { .compatible = "qcom,apq8084-saw2-v2.1-cpu", .data = &spm_reg_8974_8084_cpu }, { .compatible = "qcom,apq8064-saw2-v1.1-cpu", From f7add980210c5765bedd65937c6968ea22208b75 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 21 Jun 2021 19:47:08 +0200 Subject: [PATCH 060/212] dt-bindings: soc: qcom: spm: Document SDM660 and MSM8998 compatibles The driver was updated to add SAW2 v4.1 support for new SoCs: document the new compatibles. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/soc/qcom/qcom,spm.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml index 507216100e3f..d68c002527fa 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,spm.yaml @@ -18,6 +18,10 @@ properties: compatible: items: - enum: + - qcom,sdm660-gold-saw2-v4.1-l2 + - qcom,sdm660-silver-saw2-v4.1-l2 + - qcom,msm8998-gold-saw2-v4.1-l2 + - qcom,msm8998-silver-saw2-v4.1-l2 - qcom,msm8226-saw2-v2.1-cpu - qcom,msm8974-saw2-v2.1-cpu - qcom,apq8084-saw2-v2.1-cpu @@ -36,6 +40,8 @@ additionalProperties: false examples: - | + + /* Example 1: SoC using SAW2 and kpss-acc-v2 CPUIdle */ cpus { #address-cells = <1>; #size-cells = <0>; @@ -55,4 +61,20 @@ examples: reg = <0xf9089000 0x1000>; }; + - | + + /* + * Example 2: New-gen multi cluster SoC using SAW only for L2; + * This does not require any cpuidle driver, nor any cpu phandle. + */ + power-manager@17812000 { + compatible = "qcom,msm8998-gold-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x17812000 0x1000>; + }; + + power-manager@17912000 { + compatible = "qcom,msm8998-silver-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x17912000 0x1000>; + }; + ... From 848a1bbf75779fd3ccb00aeaf35c57a00ea44c19 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 26 Nov 2020 17:40:05 +0100 Subject: [PATCH 061/212] arm64: DT: sdm630: Configure CPRh and cpufreq-hw for CPU scaling Now that the CPR v3/v4/Hardened is ready and the cpufreq-hw driver got OSM programming, we can finally enable CPU scaling on both of the clusters on SDM630. --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 511 ++++++++++++++++++++++++++- 1 file changed, 505 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 2a692871aee0..61aa14eab697 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -52,6 +52,12 @@ &PERF_CLUSTER_SLEEP_1 &PERF_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1126>; + dynamic-power-coefficient = <118>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 1>; #cooling-cells = <2>; next-level-cache = <&L2_1>; L2_1: l2-cache { @@ -71,6 +77,12 @@ &PERF_CLUSTER_SLEEP_1 &PERF_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1126>; + dynamic-power-coefficient = <118>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 1>; #cooling-cells = <2>; next-level-cache = <&L2_1>; }; @@ -86,6 +98,12 @@ &PERF_CLUSTER_SLEEP_1 &PERF_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1126>; + dynamic-power-coefficient = <118>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 1>; #cooling-cells = <2>; next-level-cache = <&L2_1>; }; @@ -101,6 +119,12 @@ &PERF_CLUSTER_SLEEP_1 &PERF_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1126>; + dynamic-power-coefficient = <118>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 1>; #cooling-cells = <2>; next-level-cache = <&L2_1>; }; @@ -116,6 +140,12 @@ &PWR_CLUSTER_SLEEP_1 &PWR_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 0>; #cooling-cells = <2>; next-level-cache = <&L2_0>; L2_0: l2-cache { @@ -135,6 +165,12 @@ &PWR_CLUSTER_SLEEP_1 &PWR_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 0>; #cooling-cells = <2>; next-level-cache = <&L2_0>; }; @@ -150,6 +186,12 @@ &PWR_CLUSTER_SLEEP_1 &PWR_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 0>; #cooling-cells = <2>; next-level-cache = <&L2_0>; }; @@ -165,6 +207,12 @@ &PWR_CLUSTER_SLEEP_1 &PWR_CLUSTER_SLEEP_2>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + clocks = <&xo_board>; + qcom,freq-domain = <&cpufreq_hw 0>; #cooling-cells = <2>; next-level-cache = <&L2_0>; }; @@ -204,6 +252,7 @@ core3 { cpu = <&CPU3>; }; + }; }; @@ -354,6 +403,175 @@ method = "smc"; }; + cpu_silver_opp_table: cpu-silver-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1843200000 { + opp-hz = /bits/ 64 <1843200000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x094c004c>; + qcom,spare-data = <3>; + }; + opp-1728000000 { + opp-hz = /bits/ 64 <1728000000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x09480048>; + qcom,spare-data = <2>; + }; + opp-1536000000 { + opp-hz = /bits/ 64 <1536000000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x08400040>; + qcom,spare-data = <2>; + }; + opp-1382400000 { + opp-hz = /bits/ 64 <1382400000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x07390039>; + qcom,spare-data = <2>; + }; + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x052e002e>; + qcom,spare-data = <2>; + }; + opp-883200000 { + opp-hz = /bits/ 64 <883200000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x04250025>; + qcom,spare-data = <1>; + }; + opp-614400000 { + opp-hz = /bits/ 64 <614400000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + qcom,spare-data = <1>; + }; + }; + + cpu_gold_opp_table: cpu-gold-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + required-opps = <&cprh_opp10>; + qcom,pll-override = <0x0b5c005c>; + qcom,spare-data = <3>; + }; + opp-2150400000 { + opp-hz = /bits/ 64 <2150400000>; + required-opps = <&cprh_opp9>; + qcom,pll-override = <0x0b590059>; + qcom,spare-data = <2>; + }; + opp-2016000000 { + opp-hz = /bits/ 64 <2016000000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x0a540054>; + qcom,spare-data = <2>; + }; + opp-1881600000 { + opp-hz = /bits/ 64 <1881600000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x094e004e>; + qcom,spare-data = <2>; + }; + opp-1670400000 { + opp-hz = /bits/ 64 <1670400000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x08450045>; + qcom,spare-data = <2>; + }; + opp-1516800000 { + opp-hz = /bits/ 64 <1516800000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x073f003f>; + qcom,spare-data = <2>; + }; + opp-1344000000 { + opp-hz = /bits/ 64 <1344000000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x07380038>; + qcom,spare-data = <2>; + + }; + opp-1113600000 { + opp-hz = /bits/ 64 <1113600000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x052e002e>; + qcom,spare-data = <2>; + }; + opp-787200000 { + opp-hz = /bits/ 64 <787200000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x4200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + qcom,spare-data = <1>; + }; + }; + + + cprh_opp_table: cpr-gold-opp-table { + compatible = "operating-points-v2-qcom-level"; + + cprh_opp1: opp1 { + opp-level = <1>; + qcom,opp-fuse-level = <1>; + }; + cprh_opp2: opp2 { + opp-level = <2>; + qcom,opp-fuse-level = <1>; + }; + cprh_opp3: opp3 { + opp-level = <3>; + qcom,opp-fuse-level = <2>; + }; + cprh_opp4: opp4 { + opp-level = <4>; + qcom,opp-fuse-level = <2>; + }; + cprh_opp5: opp5 { + opp-level = <5>; + qcom,opp-fuse-level = <3>; + }; + cprh_opp6: opp6 { + opp-level = <6>; + qcom,opp-fuse-level = <3>; + }; + cprh_opp7: opp7 { + opp-level = <7>; + qcom,opp-fuse-level = <4 3>; + }; + cprh_opp8: opp8 { + opp-level = <8>; + qcom,opp-fuse-level = <4 3>; + }; + cprh_opp9: opp9 { + opp-level = <9>; + qcom,opp-fuse-level = <4>; + }; + cprh_opp10: opp10 { + opp-level = <10>; + qcom,opp-fuse-level = <5>; + }; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -574,14 +792,190 @@ #address-cells = <1>; #size-cells = <1>; - qusb2_hstx_trim: hstx-trim@240 { - reg = <0x240 0x1>; - bits = <25 3>; + cpr_efuse_speedbin: speedbin@133 { + reg = <0x133 0x8>; + bits = <5 3>; }; - gpu_speed_bin: gpu-speed-bin@41a0 { - reg = <0x41a0 0x1>; - bits = <21 7>; + /* CPR Quotients: Power Cluster */ + cpr_quot00_pwrcl: quot00_pwrcl@221 { + reg = <0x221 0x4>; + bits = <4 12>; + }; + + cpr_quot01_pwrcl: quot01_pwrcl@239 { + reg = <0x239 0x4>; + bits = <1 12>; + }; + + cpr_quot02_pwrcl: quot02_pwrcl@21d { + reg = <0x21d 0x4>; + bits = <0 12>; + }; + + cpr_quot_offset01_pwrcl: qoff01_pwrcl@23a { + reg = <0x23a 0x4>; + bits = <5 7>; + }; + + cpr_quot_offset02_pwrcl: qoff02_pwrcl@223 { + reg = <0x223 0x4>; + bits = <0 7>; + }; + + /* CPR Quotients: Performance Cluster */ + cpr_quot00_perfcl: quot00_perfcl@22e { + reg = <0x22e 0x4>; + bits = <5 11>; + }; + + cpr_quot10_perfcl: quot10_perfcl@230 { + reg = <0x230 0x4>; + bits = <0 1>; + }; + + cpr_quot01_perfcl: quot01_perfcl@230 { + reg = <0x230 0x4>; + bits = <1 12>; + }; + + cpr_quot02_perfcl: quot02_perfcl@22d { + reg = <0x22d 0x4>; + bits = <1 12>; + }; + + cpr_quot03_perfcl: quot03_perfcl@22b { + reg = <0x22b 0x8>; + bits = <5 12>; + }; + + cpr_quot04_perfcl: quot04_perfcl@23e { + reg = <0x236 0x4>; + bits = <0 12>; + }; + + cpr_quot_offset01_perfcl: qoff01_perfcl@233 { + reg = <0x233 0x4>; + bits = <3 7>; + }; + + cpr_quot_offset02_perfcl: qoff02_perfcl@232 { + reg = <0x232 0x4>; + bits = <4 7>; + }; + + cpr_quot_offset03_perfcl: qoff03_perfcl@231 { + reg = <0x231 0x4>; + bits = <5 7>; + }; + + cpr_quot_offset04_perfcl: qoff04_perfcl@237 { + reg = <0x237 0x4>; + bits = <4 4>; + }; + + cpr_quot_offset14_perfcl: qoff14_perfcl@230 { + reg = <0x230 0x4>; + bits = <0 3>; + }; + + /* CPR Init Voltage: Power Cluster */ + cpr_init_voltage00_pwrcl: ivolt00_pwrcl@21c { + reg = <0x21c 0x4>; + bits = <2 6>; + }; + + cpr_init_voltage01_pwrcl: ivolt01_pwrcl@238 { + reg = <0x238 0x4>; + bits = <3 6>; + }; + + cpr_init_voltage02_pwrcl: ivolt02_pwrcl@219 { + reg = <0x219 0x4>; + bits = <8 6>; + }; + + /* CPR Init Voltage: Performance Cluster */ + cpr_init_voltage00_perfcl: ivolt00_perfcl@22a { + reg = <0x22a 0x4>; + bits = <1 6>; + }; + + cpr_init_voltage01_perfcl: ivolt01_perfcl@22a { + reg = <0x22a 0x4>; + bits = <7 6>; + }; + + cpr_init_voltage02_perfcl: ivolt02_perfcl@229 { + reg = <0x229 0x4>; + bits = <3 6>; + }; + + cpr_init_voltage03_perfcl: ivolt03_perfcl@228 { + reg = <0x228 0x4>; + bits = <5 6>; + }; + + cpr_init_voltage04_perfcl: ivolt04_perfcl@235 { + reg = <0x235 0x4>; + bits = <2 6>; + }; + + /* CPR Ring Oscillator: Power Cluster */ + cpr_ro_sel00_pwrcl: rosel00_pwrcl@219 { + reg = <0x219 0x4>; + bits = <4 4>; + }; + + cpr_ro_sel01_pwrcl: rosel01_pwrcl@20f { + reg = <0x20f 0x4>; + bits = <0 4>; + }; + + cpr_ro_sel02_pwrcl: rosel02_pwrcl@218 { + reg = <0x218 0x4>; + bits = <0 4>; + }; + + /* CPR Ring Oscillator: Performance Cluster */ + cpr_ro_sel00_perfcl: rosel00_perfcl@227 { + reg = <0x227 0x4>; + bits = <5 4>; + }; + + cpr_ro_sel01_perfcl: rosel01_perfcl@228 { + reg = <0x228 0x4>; + bits = <1 4>; + }; + + cpr_ro_sel02_perfcl: rosel02_perfcl@227 { + reg = <0x227 0x4>; + bits = <1 4>; + }; + + cpr_ro_sel03_perfcl: rosel03_perfcl@226 { + reg = <0x226 0x4>; + bits = <5 4>; + }; + + cpr_ro_sel04_perfcl: rosel04_perfcl@211 { + reg = <0x211 0x4>; + bits = <6 4>; + }; + + cpr_fuse_revision: cpr_fusing_rev@23b { + reg = <0x23b 0x1>; + bits = <4 3>; + }; + + qusb2_hstx_trim: hstx-trim@243 { + reg = <0x243 0x1>; + bits = <1 3>; + }; + + gpu_speed_bin: gpu_speed_bin@1a2 { + reg = <0x1a2 0x1>; + bits = <5 7>; }; }; @@ -703,6 +1097,111 @@ <&mmcc AHB_CLK_SRC>; }; + saw1: power-controller@17812000 { + compatible = "qcom,sdm660-gold-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x017812000 0x1000>; + }; + + saw0: power-controller@17912000 { + compatible = "qcom,sdm660-gold-saw2-v4.1-l2", "qcom,saw2"; + reg = <0x017912000 0x1000>; + }; + + cpufreq_hw: cpufreq_hw@17816000 { + compatible = "qcom,cpufreq-hw-8998"; + + reg = <0x0179c0000 0x1000>, <0x0179c1000 0x1000>, + <0x0179c2000 0x1000>, <0x0179c3000 0x1000>; + reg-names = "osm-domain0", "freq-domain0", + "osm-domain1", "freq-domain1"; + + clocks = <&rpmcc RPM_SMD_XO_A_CLK_SRC>, + <&gcc HMSS_GPLL0_CLK_SRC>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; + }; + + /* Gold and Silver cluster */ + apc_cprh: power-controller@179c4000 { + compatible = "qcom,sdm630-cprh"; + reg = <0x0179c4000 0x4000>, <0x0179c8000 0x4000>; + + clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + clock-names = "ref"; + assigned-clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + assigned-clock-rates = <19200000>; + + #power-domain-cells = <1>; + operating-points-v2 = <&cprh_opp_table>; + + nvmem-cells = <&cpr_efuse_speedbin>, + <&cpr_fuse_revision>, + <&cpr_quot00_perfcl>, + <&cpr_quot01_perfcl>, + <&cpr_quot02_perfcl>, + <&cpr_quot03_perfcl>, + <&cpr_quot04_perfcl>, + <&cpr_quot_offset01_perfcl>, + <&cpr_quot_offset02_perfcl>, + <&cpr_quot_offset03_perfcl>, + <&cpr_quot_offset04_perfcl>, + <&cpr_init_voltage00_perfcl>, + <&cpr_init_voltage01_perfcl>, + <&cpr_init_voltage02_perfcl>, + <&cpr_init_voltage03_perfcl>, + <&cpr_init_voltage04_perfcl>, + <&cpr_ro_sel00_perfcl>, + <&cpr_ro_sel01_perfcl>, + <&cpr_ro_sel02_perfcl>, + <&cpr_ro_sel03_perfcl>, + <&cpr_ro_sel04_perfcl>, + <&cpr_quot00_pwrcl>, + <&cpr_quot01_pwrcl>, + <&cpr_quot02_pwrcl>, + <&cpr_quot_offset01_pwrcl>, + <&cpr_quot_offset02_pwrcl>, + <&cpr_init_voltage00_pwrcl>, + <&cpr_init_voltage01_pwrcl>, + <&cpr_init_voltage02_pwrcl>, + <&cpr_ro_sel00_pwrcl>, + <&cpr_ro_sel01_pwrcl>, + <&cpr_ro_sel02_pwrcl>; + + nvmem-cell-names = "cpr_speed_bin", + "cpr_fuse_revision", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_quotient4", + "cpr0_quotient5", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_quotient_offset4", + "cpr0_quotient_offset5", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_init_voltage4", + "cpr0_init_voltage5", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_ring_osc4", + "cpr0_ring_osc5", + "cpr1_quotient1", + "cpr1_quotient2", + "cpr1_quotient3", + "cpr1_quotient_offset2", + "cpr1_quotient_offset3", + "cpr1_init_voltage1", + "cpr1_init_voltage2", + "cpr1_init_voltage3", + "cpr1_ring_osc1", + "cpr1_ring_osc2", + "cpr1_ring_osc3"; + }; + tsens: thermal-sensor@10ae000 { compatible = "qcom,sdm630-tsens", "qcom,tsens-v2"; reg = <0x010ae000 0x1000>, /* TM */ From 567ae5929526a6af480cc12ad1f22fe7a00bf1cb Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 13 Nov 2020 22:04:43 +0100 Subject: [PATCH 062/212] cpufreq: Add SDM630/636/660 to cpufreq-dt-platdev blocklist Add the SDM630, SDM636 and SDM660 to the blocklist since the CPU scaling is handled out of this. Signed-off-by: AngeloGioacchino Del Regno --- drivers/cpufreq/cpufreq-dt-platdev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index ca1d103ec449..b255388c235c 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -141,6 +141,9 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,sc7180", }, { .compatible = "qcom,sc7280", }, { .compatible = "qcom,sc8180x", }, + { .compatible = "qcom,sdm630", }, + { .compatible = "qcom,sdm636", }, + { .compatible = "qcom,sdm660", }, { .compatible = "qcom,sdm845", }, { .compatible = "qcom,sm6350", }, { .compatible = "qcom,sm8150", }, From 0b5e268a0d5f71b70752403b5db06ef56337c785 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 15 Dec 2020 01:06:27 +0100 Subject: [PATCH 063/212] cpufreq: Add MSM8998 to cpufreq-dt-platdev blocklist Add the MSM8998 to the blocklist since the CPU scaling is handled out of this. Signed-off-by: AngeloGioacchino Del Regno --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index b255388c235c..acd15986deac 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -136,6 +136,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, + { .compatible = "qcom,msm8998", }, { .compatible = "qcom,qcs404", }, { .compatible = "qcom,sa8155p" }, { .compatible = "qcom,sc7180", }, From f5514fdd59d1b9ed4a59589cbf725e03c2415dae Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 20 Oct 2020 21:09:43 +0530 Subject: [PATCH 064/212] dt-bindings: arm: cpus: Document 'qcom,freq-domain' property Add devicetree documentation for 'qcom,freq-domain' property specific to Qualcomm CPUs. This property is used to reference the CPUFREQ node along with Domain ID (0/1). Signed-off-by: Manivannan Sadhasivam Signed-off-by: AngeloGioacchino Del Regno --- Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 9a2432a88074..28b9e0705ff9 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -297,6 +297,12 @@ properties: * arm/msm/qcom,kpss-acc.txt + qcom,freq-domain: + $ref: '/schemas/types.yaml#/definitions/phandle-array' + description: | + CPUs supporting freq-domain must set their "qcom,freq-domain" property + with phandle to a cpufreq_hw node followed by the Domain ID(0/1). + rockchip,pmu: $ref: '/schemas/types.yaml#/definitions/phandle' description: | From 2bc4ab0787b62e6eb2bfe693d4fe77c32e257612 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 20 Oct 2020 21:09:44 +0530 Subject: [PATCH 065/212] dt-bindings: cpufreq: cpufreq-qcom-hw: Convert to YAML bindings Convert Qualcomm cpufreq devicetree binding to YAML. Signed-off-by: Manivannan Sadhasivam Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/cpufreq/cpufreq-qcom-hw.txt | 172 --------------- .../bindings/cpufreq/cpufreq-qcom-hw.yaml | 204 ++++++++++++++++++ 2 files changed, 204 insertions(+), 172 deletions(-) delete mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt deleted file mode 100644 index 9299028ee712..000000000000 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt +++ /dev/null @@ -1,172 +0,0 @@ -Qualcomm Technologies, Inc. CPUFREQ Bindings - -CPUFREQ HW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) -SoCs to manage frequency in hardware. It is capable of controlling frequency -for multiple clusters. - -Properties: -- compatible - Usage: required - Value type: - Definition: must be "qcom,cpufreq-hw" or "qcom,cpufreq-epss". - -- clocks - Usage: required - Value type: From common clock binding. - Definition: clock handle for XO clock and GPLL0 clock. - -- clock-names - Usage: required - Value type: From common clock binding. - Definition: must be "xo", "alternate". - -- reg - Usage: required - Value type: - Definition: Addresses and sizes for the memory of the HW bases in - each frequency domain. -- reg-names - Usage: Optional - Value type: - Definition: Frequency domain name i.e. - "freq-domain0", "freq-domain1". - -- #freq-domain-cells: - Usage: required. - Definition: Number of cells in a freqency domain specifier. - -* Property qcom,freq-domain -Devices supporting freq-domain must set their "qcom,freq-domain" property with -phandle to a cpufreq_hw followed by the Domain ID(0/1) in the CPU DT node. - - -Example: - -Example 1: Dual-cluster, Quad-core per cluster. CPUs within a cluster switch -DCVS state together. - -/ { - cpus { - #address-cells = <2>; - #size-cells = <0>; - - CPU0: cpu@0 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x0>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - qcom,freq-domain = <&cpufreq_hw 0>; - L2_0: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - L3_0: l3-cache { - compatible = "cache"; - }; - }; - }; - - CPU1: cpu@100 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x100>; - enable-method = "psci"; - next-level-cache = <&L2_100>; - qcom,freq-domain = <&cpufreq_hw 0>; - L2_100: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU2: cpu@200 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x200>; - enable-method = "psci"; - next-level-cache = <&L2_200>; - qcom,freq-domain = <&cpufreq_hw 0>; - L2_200: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU3: cpu@300 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x300>; - enable-method = "psci"; - next-level-cache = <&L2_300>; - qcom,freq-domain = <&cpufreq_hw 0>; - L2_300: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU4: cpu@400 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x400>; - enable-method = "psci"; - next-level-cache = <&L2_400>; - qcom,freq-domain = <&cpufreq_hw 1>; - L2_400: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU5: cpu@500 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x500>; - enable-method = "psci"; - next-level-cache = <&L2_500>; - qcom,freq-domain = <&cpufreq_hw 1>; - L2_500: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU6: cpu@600 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x600>; - enable-method = "psci"; - next-level-cache = <&L2_600>; - qcom,freq-domain = <&cpufreq_hw 1>; - L2_600: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - - CPU7: cpu@700 { - device_type = "cpu"; - compatible = "qcom,kryo385"; - reg = <0x0 0x700>; - enable-method = "psci"; - next-level-cache = <&L2_700>; - qcom,freq-domain = <&cpufreq_hw 1>; - L2_700: l2-cache { - compatible = "cache"; - next-level-cache = <&L3_0>; - }; - }; - }; - - soc { - cpufreq_hw: cpufreq@17d43000 { - compatible = "qcom,cpufreq-hw"; - reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>; - reg-names = "freq-domain0", "freq-domain1"; - - clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>; - clock-names = "xo", "alternate"; - - #freq-domain-cells = <1>; - }; -} diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml new file mode 100644 index 000000000000..bc81b6203e27 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cpufreq/cpufreq-qcom-hw.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. CPUFREQ + +maintainers: + - Manivannan Sadhasivam + +description: | + + CPUFREQ HW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) + SoCs to manage frequency in hardware. It is capable of controlling frequency + for multiple clusters. + +properties: + compatible: + oneOf: + - description: v1 of CPUFREQ HW + items: + - const: qcom,cpufreq-hw + + - description: v2 of CPUFREQ HW (EPSS) + items: + - enum: + - qcom,sm8250-cpufreq-epss + - const: qcom,cpufreq-epss + + reg: + minItems: 2 + maxItems: 3 + items: + - description: Frequency domain 0 register region + - description: Frequency domain 1 register region + - description: Frequency domain 2 register region + + reg-names: + minItems: 2 + maxItems: 3 + items: + - const: freq-domain0 + - const: freq-domain1 + - const: freq-domain2 + + clocks: + items: + - description: XO Clock + - description: GPLL0 Clock + + clock-names: + items: + - const: xo + - const: alternate + + '#freq-domain-cells': + const: 1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - '#freq-domain-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + // Example 1: Dual-cluster, Quad-core per cluster. CPUs within a cluster + // switch DCVS state together. + cpus { + #address-cells = <2>; + #size-cells = <0>; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + qcom,freq-domain = <&cpufreq_hw 0>; + L2_0: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + L3_0: l3-cache { + compatible = "cache"; + }; + }; + }; + + CPU1: cpu@100 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x100>; + enable-method = "psci"; + next-level-cache = <&L2_100>; + qcom,freq-domain = <&cpufreq_hw 0>; + L2_100: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU2: cpu@200 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x200>; + enable-method = "psci"; + next-level-cache = <&L2_200>; + qcom,freq-domain = <&cpufreq_hw 0>; + L2_200: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU3: cpu@300 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x300>; + enable-method = "psci"; + next-level-cache = <&L2_300>; + qcom,freq-domain = <&cpufreq_hw 0>; + L2_300: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU4: cpu@400 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x400>; + enable-method = "psci"; + next-level-cache = <&L2_400>; + qcom,freq-domain = <&cpufreq_hw 1>; + L2_400: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU5: cpu@500 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x500>; + enable-method = "psci"; + next-level-cache = <&L2_500>; + qcom,freq-domain = <&cpufreq_hw 1>; + L2_500: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU6: cpu@600 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x600>; + enable-method = "psci"; + next-level-cache = <&L2_600>; + qcom,freq-domain = <&cpufreq_hw 1>; + L2_600: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + + CPU7: cpu@700 { + device_type = "cpu"; + compatible = "qcom,kryo385"; + reg = <0x0 0x700>; + enable-method = "psci"; + next-level-cache = <&L2_700>; + qcom,freq-domain = <&cpufreq_hw 1>; + L2_700: l2-cache { + compatible = "cache"; + next-level-cache = <&L3_0>; + }; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + + cpufreq@17d43000 { + compatible = "qcom,cpufreq-hw"; + reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>; + reg-names = "freq-domain0", "freq-domain1"; + + clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; + }; + }; +... From 1199c5a501eadaf42d929ea809bfc4eca490267c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 22:35:49 +0200 Subject: [PATCH 066/212] cpufreq: qcom-hw: Add kerneldoc to some functions Some functions may not be very straightforward to understand: add kerneldoc to some ones in order to improve readability. Signed-off-by: AngeloGioacchino Del Regno --- drivers/cpufreq/qcom-cpufreq-hw.c | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index a2be0df7e174..fd55ed92a263 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -54,6 +54,13 @@ struct qcom_cpufreq_data { static unsigned long cpu_hw_rate, xo_rate; static bool icc_scaling_enabled; +/** + * qcom_cpufreq_set_bw() - Set interconnect bandwidth + * @policy: CPUFreq policy structure + * @freq_khz: CPU Frequency in KHz + * + * Returns: Zero for success, otherwise negative value on errors + */ static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz) { @@ -75,6 +82,20 @@ static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, return ret; } +/** + * qcom_cpufreq_update_opp() - Update CPU OPP tables + * @policy: CPUFreq policy structure + * @freq_khz: CPU Frequency for OPP entry in KHz + * @volt: CPU Voltage for OPP entry in microvolts + * + * The CPU frequencies and voltages are being read from the Operating + * State Manager (OSM) and the related OPPs, read from DT, need to be + * updated to reflect what the hardware will set for each p-state. + * If there is no OPP table specified in DT, then this function will + * add dynamic ones. + * + * Returns: Zero for success, otherwise negative value on errors + */ static int qcom_cpufreq_update_opp(struct device *cpu_dev, unsigned long freq_khz, unsigned long volt) @@ -95,6 +116,17 @@ static int qcom_cpufreq_update_opp(struct device *cpu_dev, return dev_pm_opp_enable(cpu_dev, freq_hz); } +/** + * qcom_cpufreq_hw_target_index() - Set frequency/voltage + * @policy: CPUFreq policy structure + * @index: Performance state index to be set + * + * This function sends a request to the Operating State Manager + * to set a Performance State index, so, to set frequency and + * voltage for the target CPU/cluster. + * + * Returns: Always zero + */ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { @@ -110,6 +142,12 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, return 0; } +/** + * qcom_cpufreq_hw_get() - Get current Performance State from OSM + * @cpu: CPU number + * + * Returns: Current CPU/Cluster frequency or zero + */ static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) { struct qcom_cpufreq_data *data; @@ -143,6 +181,17 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, return policy->freq_table[index].frequency; } +/** + * qcom_cpufreq_hw_read_lut() - Read Lookup Table from the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * + * The Operating State Manager Lookup Table can always be read, even + * in case it was pre-programmed by the bootloader or by TrustZone. + * Read the LUT from it in order to build OPPs containing DVFS info. + * + * Returns: Zero for success, otherwise negative number on errors. + */ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct cpufreq_policy *policy) { @@ -243,6 +292,11 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, return 0; } +/* + * qcom_get_related_cpus - Get mask of CPUs in the same frequency domain + * @index: CPU number + * @m: Returned CPU mask + */ static void qcom_get_related_cpus(int index, struct cpumask *m) { struct device_node *cpu_np; From 804f95c769ae3ecc0e22f776d8cabcb3882ec637 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 22:42:25 +0200 Subject: [PATCH 067/212] cpufreq: qcom-hw: Implement CPRh aware OSM programming On new SoCs (SDM845 onwards) the Operating State Manager (OSM) is being programmed in the bootloader and write-protected by the hypervisor, leaving to the OS read-only access to some of its registers (in order to read the Lookup Tables and also some status registers) and write access to the p-state register, for for the OS to request a specific performance state to trigger a DVFS switch on the CPU through the OSM hardware. On old SoCs though (MSM8998, SDM630/660 and variants), the bootloader will *not* initialize the OSM (and the CPRh, as it is a requirement for it) before booting the OS, making any request to trigger a performance state change ineffective, as the hardware doesn't have any Lookup Table, nor is storing any parameter to trigger a DVFS switch. In this case, basically all of the OSM registers are *not* write protected for the OS, even though some are - but write access is granted through SCM calls. This commit introduces support for OSM programming, which has to be done on these old SoCs that were distributed (almost?) always with a bootloader that does not do any CPRh nor OSM init before booting the kernel. In order to program the OSM on these SoCs, it is necessary to fullfill a "special" requirement: the Core Power Reduction Hardened (CPRh) hardware block must be initialized, as the OSM is "talking" to it in order to perform the Voltage part of DVFS; here, we are calling initialization of this through Linux generic power domains, specifically by requesting a genpd attach from the qcom-cpufreq-hw driver, which will give back voltages associated to each CPU frequency that has been declared in the OPPs, scaled and interpolated with the previous one, and will also give us parameters for the Array Power Mux (APM) and mem-acc, in order for this driver to be then able to generate the Lookup Tables that will be finally programmed to the OSM hardware. After writing the parameters to the OSM and enabling it, all the programming work will never happen anymore until a OS reboot, so all of the allocations and "the rest" will be disposed-of: this is done mainly to leave the code that was referred only to the new SoCs intact, as to also emphasize on the fact that the OSM HW is, in the end, the exact same; apart some register offsets that are slightly different, the entire logic is the same. This also adds the parameters to support CPU scaling on SDM630 and MSM8998. Signed-off-by: AngeloGioacchino Del Regno [Fixup for 5.15 by Jami] --- drivers/cpufreq/qcom-cpufreq-hw.c | 1078 ++++++++++++++++++++++++++++- 1 file changed, 1057 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index fd55ed92a263..d88f193b73cd 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1,38 +1,261 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * OSM hardware initial programming + * Copyright (C) 2020, AngeloGioacchino Del Regno + * */ #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include +#include +#include #define LUT_MAX_ENTRIES 40U -#define LUT_SRC GENMASK(31, 30) +#define LUT_SRC_845 GENMASK(31, 30) +#define LUT_SRC_8998 GENMASK(27, 26) +#define LUT_PLL_DIV GENMASK(25, 24) #define LUT_L_VAL GENMASK(7, 0) #define LUT_CORE_COUNT GENMASK(18, 16) +#define LUT_VOLT_VC GENMASK(21, 16) #define LUT_VOLT GENMASK(11, 0) -#define CLK_HW_DIV 2 #define LUT_TURBO_IND 1 +#define OSM_BOOT_TIME_US 5 + +#define CYCLE_COUNTER_CLK_RATIO GENMASK(5, 1) +#define OSM_XO_RATIO_VAL (10 - 1) +#define CYCLE_COUNTER_USE_XO_EDGE BIT(8) + +/* FSM Boost Control */ +#define CC_BOOST_EN BIT(0) +#define PS_BOOST_EN BIT(1) +#define DCVS_BOOST_EN BIT(2) +#define BOOST_TIMER_REG_HI GENMASK(31, 16) +#define BOOST_TIMER_REG_LO GENMASK(15, 0) + +#define PLL_WAIT_LOCK_TIME_NS 2000 +#define SAFE_FREQ_WAIT_NS 1000 +#define DEXT_DECREMENT_WAIT_NS 200 + +#define BOOST_SYNC_DELAY 5 + +#define HYSTERESIS_UP_MASK GENMASK(31, 16) +#define HYSTERESIS_DN_MASK GENMASK(15, 0) +#define HYSTERESIS_CC_NS 200 +#define HYSTERESIS_LLM_NS 65535 + +/* FSM Droop Control */ +#define PC_RET_EXIT_DROOP_EN BIT(3) +#define WFX_DROOP_EN BIT(4) +#define DCVS_DROOP_EN BIT(5) +#define DROOP_TIMER1 GENMASK(31, 16) +#define DROOP_TIMER0 GENMASK(15, 0) +#define DROOP_CTRL_VAL (BIT(3) | BIT(17) | BIT(31)) +#define DROOP_TIMER_NS 100 +#define DROOP_WAIT_RELEASE_TIMER_NS 50 +#define DROOP_RELEASE_TIMER_NS 1 + +/* PLL Override Control */ +#define PLL_OVERRIDE_DROOP_EN BIT(0) + +/* Sequencer */ +#define SEQUENCER_REG(base, n) (base + (n * 4)) +#define SEQ_APM_THRESH_VC 15 +#define SEQ_APM_THRESH_PREVC 31 +#define SEQ_MEM_ACC_LVAL 32 +#define SEQ_MEM_ACC_0 55 +#define SEQ_APM_CROSSOVER_VC 72 +#define SEQ_APM_PARAM 76 +#define SEQ_MEM_ACC_CROSSOVER_VC 88 +#define SEQ_MEM_ACC_MAX_LEVELS 4 +#define SEQ_MEMACC_REG(base, n) SEQUENCER_REG(base, SEQ_MEM_ACC_0 + n) + +/* ACD */ +#define ACD_WRITE_CTL_UPDATE_EN BIT(0) +#define ACD_WRITE_CTL_SELECT_SHIFT 1 + +/** + * struct qcom_cpufreq_soc_setup_data - Register offsets for OSM setup + * + * @reg_osm_sequencer: OSM Sequencer (used to get physical address) + * @reg_override: Override parameters + * @reg_spare: Spare parameters (MEMACC-to-VC) + * @reg_cc_zero_behav: Virtual Corner for cluster power collapse + * @reg_spm_cc_hyst: DCVS-CC Wait time for frequency inc/decrement + * @reg_spm_cc_dcvs_dis: DCVS-CC en/disable control + * @reg_spm_core_ret_map: Treat cores in retention as active/inactive + * @reg_llm_freq_vote_hyst: DCVS-LLM Wait time for frequency inc/decrement + * @reg_llm_volt_vote_hyst: DCVS-LLM Wait time for voltage inc/decrement + * @reg_llm_intf_dcvs_dis: DCVS-LLM en/disable control + * @reg_seq1: Sequencer extra register + * @reg_pdn_fsm_ctrl: Boost and Droop FSMs en/disable control + * @reg_cc_boost_timer: CC-Boost FSM wait first timer register + * @reg_dcvs_boost_timer: DCVS-Boost FSM wait first timer register + * @reg_ps_boost_timer: PS-Boost FSM wait first timer register + * @boost_timer_reg_len: Length of boost timer registers + * @reg_boost_sync_delay: PLL signal timing control for Boost + * @reg_droop_ctrl: Droop control value + * @reg_droop_release_ctrl: Wait for Droop release + * @reg_droop_unstall_ctrl: Wait for Droop unstall + * @reg_droop_wait_release_ctrl: Time to wait for state release + * @reg_droop_timer_ctrl: Droop timer + * @reg_droop_sync_delay: PLL signal timing control for Droop + * @reg_pll_override: PLL Droop Override en/disable control + * @reg_cycle_counter: OSM CPU cycle counter + * + * This structure holds the register offsets that are used to set-up + * the Operating State Manager (OSM) parameters, when it is not (or + * not entirely) configured from the bootloader and TrustZone. + * + * Acronyms used in this documentation: + * CC = Core Count + * PS = Power-Save + * VC = Virtual Corner + * LLM = Limits Load Management + * DCVS = Dynamic Clock and Voltage Scaling + */ +struct qcom_cpufreq_soc_setup_data { + /* OSM phys register offsets */ + u16 reg_osm_sequencer; + + /* Frequency domain register offsets */ + u16 reg_override; + u16 reg_spare; + u16 reg_cc_zero_behav; + u16 reg_spm_cc_hyst; + u16 reg_spm_cc_dcvs_dis; + u16 reg_spm_core_ret_map; + u16 reg_llm_freq_vote_hyst; + u16 reg_llm_volt_vote_hyst; + u16 reg_llm_intf_dcvs_dis; + u16 reg_seq1; + u16 reg_pdn_fsm_ctrl; + u16 reg_cc_boost_timer; + u16 reg_dcvs_boost_timer; + u16 reg_ps_boost_timer; + u16 boost_timer_reg_len; + u16 reg_boost_sync_delay; + u16 reg_droop_ctrl; + u16 reg_droop_release_ctrl; + u16 reg_droop_unstall_ctrl; + u16 reg_droop_wait_release_ctrl; + u16 reg_droop_timer_ctrl; + u16 reg_droop_sync_delay; + u16 reg_pll_override; + u16 reg_cycle_counter; +}; + +/** + * struct qcom_cpufreq_soc_acd_data - Adaptive Clock Distribution data + * + * @tl_delay_reg: Tunable-Length Delay (TLD) register offset + * @acd_ctrl_reg: Control Register (CR) register offset + * @softstart_reg: Soft Start Control Register (SSCR) register offset + * @ext_intf_reg: External interface configuration register offset + * @auto_xfer_reg: Auto Register-Transfer register offset + * @auto_xfer_cfg_reg: Auto Register-Transfer Configuration reg offset + * @auto_xfer_ctl_reg: Auto Register-Transfer Control register offset + * @auto_xfer_sts_reg: Auto Register-Transfer Status register offset + * @dcvs_sw_reg: Software DCVS register offset + * @gfmux_cfg_reg: Glitch-Free MUX configuration register offset + * @write_ctl_reg: Write Control register + * @write_sts_reg: Write Status register + * @tl_delay_val: Tunable-Length Delay (TLD) value + * @acd_ctrl_val: Control Register (CR) value + * @softstart_val: Soft Start Control Register (SSCR) value + * @ext_intf0_val: Initial external interface configuration value + * @ext_intf1_val: Final external interface configuration value + * @auto_xfer_val: Auto-register Transfer Control value + * + * This structure holds the register offsets (from the ACD iospace base) + * and the parameters that are required to configure the OSM to + * initialize the Adaptive Clock Distribution (ACD) system. + */ +struct qcom_cpufreq_soc_acd_data { + u8 tl_delay_reg; + u8 acd_ctrl_reg; + u8 softstart_reg; + u8 ext_intf_reg; + u8 auto_xfer_reg; + u8 auto_xfer_cfg_reg; + u8 auto_xfer_ctl_reg; + u8 auto_xfer_sts_reg; + u8 dcvs_sw_reg; + u8 gfmux_cfg_reg; + u8 write_ctl_reg; + u8 write_sts_reg; + u32 tl_delay_val; + u32 acd_ctrl_val; + u32 softstart_val; + u32 ext_intf0_val; + u32 ext_intf1_val; + u32 auto_xfer_val; +}; + +/** + * struct qcom_cpufreq_hw_params - Operating State Manager (OSM) Parameters + * + * @volt_lut_val: Value composed of: virtual corner (vc) and voltage in mV. + * @freq_lut_val: Value composed of: core count, clock source and output + * frequency in MHz. + * @override_val: PLL parameters that the OSM uses to override the previous + * setting coming from the bootloader, or when uninitialized. + * @spare_val: Spare register, used by both this driver and the OSM HW + * to identify MEM-ACC levels in relation to virtual corners. + * + * This structure holds the parameters to write to the OSM registers for + * one "Virtual Corner" (VC), or one Performance State (p-state). + */ +struct qcom_cpufreq_hw_params { + u32 volt_lut_val; + u32 freq_lut_val; + u32 override_val; + u32 spare_val; +}; #define HZ_PER_KHZ 1000 +/** + * struct qcom_cpufreq_soc_data - SoC specific register offsets of the OSM + * + * @reg_enable: OSM enable status + * @reg_index: Index of the Virtual Corner + * @reg_freq_lut: Frequency Lookup Table + * @reg_freq_lut_src_mask: Frequency Lookup Table clock-source mask + * @reg_volt_lut: Voltage Lookup Table + * @reg_perf_state: Performance State request register + * @lut_row_size: Lookup Table row size + * @clk_hw_div: Divider for "alternate" OSM clock-source + * @uses_tz: OSM already set-up and protected by TrustZone + * @setup_regs: Register offsets for OSM setup + */ struct qcom_cpufreq_soc_data { u32 reg_enable; + u32 reg_index; u32 reg_freq_lut; + u32 reg_freq_lut_src_mask; u32 reg_volt_lut; u32 reg_current_vote; u32 reg_perf_state; u8 lut_row_size; + u8 clk_hw_div; + bool uses_tz; + const struct qcom_cpufreq_soc_setup_data setup_regs; + const struct qcom_cpufreq_soc_acd_data acd_data; }; struct qcom_cpufreq_data { @@ -51,6 +274,7 @@ struct qcom_cpufreq_data { struct cpufreq_policy *policy; }; +static const char *cprh_genpd_names[] = { "cprh", NULL }; static unsigned long cpu_hw_rate, xo_rate; static bool icc_scaling_enabled; @@ -181,6 +405,574 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, return policy->freq_table[index].frequency; } +/** + * qcom_cpufreq_hw_boost_setup() - Sets up OSM boost timer registers + * @timer0_addr: Start of boost timer0 register group + * @len: Length (size) of "sub" registers in timer0 group + */ +static void qcom_cpufreq_hw_boost_setup(void __iomem *timer0_addr, u32 len) +{ + u32 val; + + /* timer_reg0 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, PLL_WAIT_LOCK_TIME_NS); + val |= FIELD_PREP(BOOST_TIMER_REG_HI, SAFE_FREQ_WAIT_NS); + writel(val, timer0_addr); + + /* timer_reg1 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, PLL_WAIT_LOCK_TIME_NS); + val |= FIELD_PREP(BOOST_TIMER_REG_HI, PLL_WAIT_LOCK_TIME_NS); + writel(val, timer0_addr + len); + + /* timer_reg2 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, DEXT_DECREMENT_WAIT_NS); + writel(val, timer0_addr + (2 * len)); +} + +/** + * qcom_cpufreq_gen_params() - Generate parameters to send to the hardware + * @cpu_dev: CPU device + * @data: SoC specific register offsets + * @hw_tbl: Pointer to return the array of parameters + * @apm_vc: APM Virtual Corner crossover number, returned to the caller + * @acc_vc: MEMACC Virtual Corner crossover number, returned to the caller + * @cpu_count: Number of CPUs in the frequency domain + * @num_entries: Number of allocated (and filled) elements in the table, + * returned to the caller + * + * This function allocates a 'qcom_cpufreq_hw_params' parameters table, + * fills it and returns it to the consumer, ready to get sent to the HW. + * Since the APM threshold is just one + * Freeing the table after usage is left to the caller. + * + * Returns: Zero for success, otherwise negative value on errors. + */ +static int qcom_cpufreq_gen_params(struct device *cpu_dev, + struct qcom_cpufreq_data *data, + struct qcom_cpufreq_hw_params **hw_tbl, + int *apm_vc, int *acc_vc, int cpu_count, + u8 *num_entries) +{ + struct device **genpd_cpr_vdev; + struct platform_device *pdev = cpufreq_get_driver_data(); + const struct qcom_cpufreq_soc_data *soc_data = data->soc_data; + struct cpr_ext_data *cpr_data; + struct dev_pm_opp *genpd_opp; + unsigned long rate; + int apm_uV, acc_uV, i, gpd_opp_cnt, ret = 0; + + ret = devm_pm_opp_attach_genpd(cpu_dev, cprh_genpd_names, &genpd_cpr_vdev); + if (ret) { + dev_err(&pdev->dev, "Could not attach to pm_domain: %d\n", ret); + return ret; + } + + if (IS_ERR_OR_NULL(*genpd_cpr_vdev)) + return -EINVAL; + + /* + * In the CPR3 driver we have assigned data to the genpd newly created + * virtual device: this contains MEMACC and APM thresholds, as passing + * them through OPPs would be an API abuse. + */ + cpr_data = dev_get_drvdata(*genpd_cpr_vdev); + if (cpr_data == NULL) { + dev_err(&pdev->dev, "Cannot get CPR data\n"); + return -ENODATA; + } + + /* Get the count of available OPPs coming from the power domain */ + gpd_opp_cnt = dev_pm_opp_get_opp_count(cpu_dev); + if (gpd_opp_cnt < 2) { + ret = gpd_opp_cnt > 0 ? -EINVAL : gpd_opp_cnt; + goto detach_gpd; + } + + /* If we get no APM voltage, the system is going to be unstable */ + apm_uV = cpr_data->apm_threshold_uV; + if (apm_uV <= 0) { + ret = -EINVAL; + goto detach_gpd; + } + + /* + * Set apm_vc to a less than zero value: this is used later in the + * logic making sure that we're returning the right virtual corner + * for APM switch. + */ + *apm_vc = -1; + + /* + * Get the ACC threshold voltage: this is optional and not every + * SoC, or every SoC version, or every binning, needs it. + */ + if (cpr_data->mem_acc_threshold_uV <= 0) { + acc_uV = INT_MAX; + *acc_vc = U8_MAX; + } else { + acc_uV = cpr_data->mem_acc_threshold_uV; + *acc_vc = -1; + } + + *hw_tbl = devm_kmalloc_array(&pdev->dev, gpd_opp_cnt, + sizeof(**hw_tbl), GFP_KERNEL); + if (!hw_tbl) { + ret = -ENOMEM; + goto detach_gpd; + } + + for (i = 0, rate = 1000; i <= gpd_opp_cnt ; rate++, i++) { + struct qcom_cpufreq_hw_params *entry = *hw_tbl + i; + struct device_node *np; + u32 pll_div, millivolts, f_src; + + /* + * Find the next enabled OPP's frequency (ignores APM/ACC). + * + * We expect to get an error when we try to go past the last + * defined frequency, so we quit the loop gracefully without + * signaling any error, as this is the expected behavior. + */ + genpd_opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(genpd_opp)) + break; + + /* Get mandatory and optional properties from the OPP DT */ + np = dev_pm_opp_get_of_node(genpd_opp); + if (!np) { + ret = -ENOENT; + goto detach_gpd; + } + + if (of_property_read_u32(np, "qcom,pll-override", + &entry->override_val)) { + ret = -EINVAL; + of_node_put(np); + goto detach_gpd; + } + + if (of_property_read_u32(np, "qcom,spare-data", + &entry->spare_val)) + entry->spare_val = 0; + + if (of_property_read_u32(np, "qcom,pll-div", &pll_div)) + pll_div = 0; + + of_node_put(np); + + /* Get voltage in microvolts, then convert to millivolts */ + millivolts = dev_pm_opp_get_voltage(genpd_opp); + if (millivolts >= apm_uV && *apm_vc < 0) + *apm_vc = i; + if (millivolts >= acc_uV && *acc_vc < 0) + *acc_vc = i; + + millivolts /= 1000; + + if (millivolts < 150 || millivolts > 1400) { + dev_err(&pdev->dev, + "Read invalid voltage: %u.\n", millivolts); + return -EINVAL; + } + + /* In the OSM firmware, "Virtual Corner" levels start from 0 */ + entry->volt_lut_val = FIELD_PREP(LUT_VOLT_VC, i); + entry->volt_lut_val |= FIELD_PREP(LUT_VOLT, millivolts); + + /* + * Only the first frequency has alternate source, as it is + * always that one that is used for low power idle states. + */ + f_src = i ? 1 : 0; + f_src <<= ffs(soc_data->reg_freq_lut_src_mask) - 1; + entry->freq_lut_val = f_src | div_u64(rate, xo_rate); + entry->freq_lut_val |= FIELD_PREP(LUT_CORE_COUNT, cpu_count); + + /* + * PLL divider is not always 0 and there is no way to determine + * it automatically, as setting this value higher than DIV1 + * will make the OSM HW to effectively set the PLL at 2-4x + * the CPU frequency and then divide the CPU clock by this div, + * so this value is effectively used as both a multiplier and + * divider. + * This value cannot be calculated because it depends on + * manual calibration and is (most probably) used to choose + * a PLL frequency that gives the least possible jitter. + */ + entry->freq_lut_val |= FIELD_PREP(LUT_PLL_DIV, pll_div); + + dev_dbg(&pdev->dev, + "[%d] freq=0x%x volt=0x%x override=0x%x spare=0x%x\n", + i, entry->freq_lut_val, entry->volt_lut_val, + entry->override_val, entry->spare_val); + dev_pm_opp_put(genpd_opp); + genpd_opp = NULL; + } + + /* + * If we've got a customized mem-acc corner but we couldn't + * find any suitable crossover, or the corner is less than + * the minimum amount of required corners for mem-acc scaling, + * the values are not valid, hence fall back to LUT values. + */ + if (acc_uV != INT_MAX && *acc_vc < SEQ_MEM_ACC_MAX_LEVELS - 1) { + dev_dbg(&pdev->dev, + "MEM-ACC corner: invalid values VC%d %duV\n", + *acc_vc, acc_uV); + *acc_vc = U8_MAX; + } + + /* + * If we have probed less params than what we need, then the + * OPP table that we got from the genpd is malformed for some + * reason: in this case, do not apply the table to the HW. + */ + if (i < gpd_opp_cnt) { + dev_err(&pdev->dev, "Got bad OPP table from power domain.\n"); + ret = -EINVAL; + goto detach_gpd; + } + *num_entries = i; + +detach_gpd: + return ret; +} + +static inline u32 qcom_cpufreq_acd_regbit(u8 acd_reg_offset) +{ + return BIT(acd_reg_offset / 4); +} + +static int qcom_cpufreq_hw_acd_write_autoxfer(struct qcom_cpufreq_data *data, + void __iomem *acd_base, u32 val) +{ + const struct qcom_cpufreq_soc_data *sdata = data->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + u32 regval = 0; + + writel(val, acd_base + aregs->auto_xfer_cfg_reg); + + /* (Clear, then) Set AUTOXFER START */ + writel(0, acd_base + aregs->auto_xfer_reg); + writel(1, acd_base + aregs->auto_xfer_reg); + + /* Poll for status: if the first bit is set the transfer is done. */ + return readl_poll_timeout(acd_base + aregs->auto_xfer_sts_reg, regval, + regval & BIT(0), 1, 3); +} + +static int qcom_cpufreq_hw_acd_write_xfer(struct qcom_cpufreq_data *data, + void __iomem *acd_base, u8 reg, + u32 val) +{ + const struct qcom_cpufreq_soc_data *sdata = data->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + u32 regval = 0; + + /* Write to the register, then initiate manual transfer */ + writel(val, acd_base + reg); + + /* Clear write control register */ + writel(0, acd_base + aregs->write_ctl_reg); + + regval = (reg / 4) << ACD_WRITE_CTL_SELECT_SHIFT; + regval |= ACD_WRITE_CTL_UPDATE_EN; + writel(regval, acd_base + aregs->write_ctl_reg); + + /* Wait until ACD Local Transfer is done */ + return readl_poll_timeout(acd_base + aregs->write_sts_reg, regval, + regval & qcom_cpufreq_acd_regbit(reg), 1, 3); +} + +/** + * qcom_cpufreq_hw_acd_init() - Initialize ACD params in the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @index: Instance number (CPU cluster number) + * + * On some SoCs it is required to send the ACD configuration parameters + * to the OSM. This function takes the parameters from the SoC specific + * configuration and writes them only if a "osm-acdN" iospace has been + * declared (hence, it's present). + * + * Returns: Zero for success, otherwise negative number on error. + */ +static int qcom_cpufreq_hw_acd_init(struct device *cpu_dev, + struct cpufreq_policy *policy, + int index) +{ + struct platform_device *pdev = cpufreq_get_driver_data(); + struct qcom_cpufreq_data *ddata = policy->driver_data; + const struct qcom_cpufreq_soc_data *sdata = ddata->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + char acd_resname[] = "osm-acdX"; + void __iomem *acd_base; + u32 rmask; + int ret; + + snprintf(acd_resname, sizeof(acd_resname), "osm-acd%d", index); + + acd_base = devm_platform_ioremap_resource_byname(pdev, acd_resname); + if (IS_ERR(acd_base)) { + dev_vdbg(cpu_dev, "Skipping ACD initialization.\n"); + return 0; + } + + writel(aregs->tl_delay_val, acd_base + aregs->tl_delay_reg); + writel(aregs->acd_ctrl_val, acd_base + aregs->acd_ctrl_reg); + writel(aregs->softstart_val, acd_base + aregs->softstart_reg); + writel(aregs->ext_intf0_val, acd_base + aregs->ext_intf_reg); + writel(aregs->auto_xfer_val, acd_base + aregs->auto_xfer_ctl_reg); + + rmask = qcom_cpufreq_acd_regbit(aregs->acd_ctrl_reg) | + qcom_cpufreq_acd_regbit(aregs->tl_delay_reg) | + qcom_cpufreq_acd_regbit(aregs->softstart_reg) | + qcom_cpufreq_acd_regbit(aregs->ext_intf_reg); + ret = qcom_cpufreq_hw_acd_write_autoxfer(ddata, acd_base, rmask); + if (ret) + return ret; + + /* Switch CPUSS clock source to ACD clock */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->gfmux_cfg_reg, 1); + if (ret) + return ret; + + /* (Set, then) Clear DCVS_SW */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->dcvs_sw_reg, 1); + if (ret) + return ret; + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->dcvs_sw_reg, 0); + if (ret) + return ret; + + /* Wait for clock switch time */ + udelay(1); + + /* Program the final ACD external interface */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->ext_intf_reg, + aregs->ext_intf1_val); + if (ret) + return ret; + + /* Initiate transfer of the final ACD value */ + rmask |= qcom_cpufreq_acd_regbit(aregs->gfmux_cfg_reg); + writel(rmask, acd_base + aregs->auto_xfer_cfg_reg); + + /* Wait for ACD to stabilize. Same wait as the OSM boot time... */ + udelay(OSM_BOOT_TIME_US); + return 0; +} + +/** + * qcom_cpufreq_hw_write_lut() - Write Lookup Table params to the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @cpu_count: Number of CPUs in the frequency domain + * @index: Instance number (CPU cluster number) + * + * Program all the Lookup Table (LUT) entries and related thresholds + * to the Operating State Manager on platforms where the same hasn't + * been done already by the bootloader or TrustZone before booting + * the operating system's kernel; + * On these platforms, write access to the OSM is (obviously) not + * blocked by the hypervisor. + * + * Returns: Zero for success, otherwise negative number on error. + */ +static int qcom_cpufreq_hw_write_lut(struct device *cpu_dev, + struct cpufreq_policy *policy, + int cpu_count, int index) +{ + struct platform_device *pdev = cpufreq_get_driver_data(); + struct qcom_cpufreq_data *ddata = policy->driver_data; + const struct qcom_cpufreq_soc_data *sdata = ddata->soc_data; + const struct qcom_cpufreq_soc_setup_data *sregs = &sdata->setup_regs; + struct qcom_cpufreq_hw_params *hw_tbl; + struct resource *osm_rsrc; + char osm_resname[] = "osm-domainX"; + u32 sreg, seq_addr, acc_lval = 0, last_spare = 1; + u8 num_entries = 0; + int apm_vc = INT_MAX, acc_vc = U8_MAX, acc_idx = 0; + int acc_val[SEQ_MEM_ACC_MAX_LEVELS], i, ret; + + snprintf(osm_resname, sizeof(osm_resname), "osm-domain%d", index); + + /* + * On some SoCs the OSM is not getting programmed from bootloader + * and needs to be done here: in this case, we need to retrieve + * the base physical address for the "Sequencer", so we will get + * the OSM base phys and apply the sequencer offset. + * + * Note: We are not remapping this iospace because we are really + * sending the physical address through SCM calls later. + */ + osm_rsrc = platform_get_resource_byname(pdev, IORESOURCE_MEM, osm_resname); + if (!osm_rsrc) + return -ENODEV; + + seq_addr = osm_rsrc->start + sregs->reg_osm_sequencer; + + ret = qcom_cpufreq_gen_params(cpu_dev, ddata, &hw_tbl, &apm_vc, + &acc_vc, cpu_count, &num_entries); + if (ret) + return ret; + + /* If we get less than 2 entries, scaling doesn't make sense */ + if (num_entries < 2) { + dev_err(&pdev->dev, "Not enough LUT entries found (%u)\n", num_entries); + return -EINVAL; + } + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + struct qcom_cpufreq_hw_params *entry; + int pos = i * sdata->lut_row_size; + + /* + * If we have reached the end of the params table, write + * the last valid entry until the end of the OSM table. + */ + if (i < num_entries) + entry = &hw_tbl[i]; + else + entry = &hw_tbl[num_entries - 1]; + + writel(i, ddata->base + sdata->reg_index + pos); + writel(entry->volt_lut_val, ddata->base + sdata->reg_volt_lut + pos); + writel(entry->freq_lut_val, ddata->base + sdata->reg_freq_lut + pos); + writel(entry->override_val, ddata->base + sregs->reg_override + pos); + writel(entry->spare_val, ddata->base + sregs->reg_spare + pos); + + dev_dbg(cpu_dev, "Writing [%d] v:0x%x f:0x%x ovr:0x%x s:0x%x\n", i, + entry->volt_lut_val, entry->freq_lut_val, + entry->override_val, entry->spare_val); + + /* + * MEM-ACC Virtual Corner threshold voltage: this gets set + * as the pairs of corners in which there is a transition + * between one MEM-ACC level and the next one. + * + * Notes: The spare_val can never be zero; + * The first spare_val is always 1; + * The maximum number of pairs is two (four registers). + * + * Example: (C = Corner Level - M = MEM-ACC Level) + * C0 M1 - C1 M1 - C2 M2 - C3 M2 - C4 M2 - C5 M3 + * Pairs: 1-2, 4-5 + */ + if (entry->spare_val <= last_spare || + acc_idx >= SEQ_MEM_ACC_MAX_LEVELS - 1) + continue; + + /* Standard mem-acc pairs using spare_val LUT crossovers */ + last_spare = entry->spare_val; + acc_val[acc_idx] = i - 1; + acc_idx++; + acc_val[acc_idx] = i; + acc_idx++; + } + + /* Sanity check: we *must* have two mem-acc crossovers (four values) */ + if (acc_idx < SEQ_MEM_ACC_MAX_LEVELS - 1) + return -EINVAL; + + /* + * Customized mem-acc corners, if any; in this case, the last corner + * in the external (CPRh) LUT is this one, placed after the APM one. + */ + if (acc_vc > 0 && acc_vc != U8_MAX) { + sreg = SEQUENCER_REG(seq_addr, SEQ_MEM_ACC_CROSSOVER_VC); + ret = qcom_scm_io_writel(sreg, num_entries + 1); + if (ret) + return ret; + + /* + * At the price of very-slightly higher power consumption, + * switch the ACC at one corner lower than what we've found, + * as this seems to be needed on at least some MSM8998 chips + * to achieve full system stability + */ + acc_vc--; + + /* Change only if we have to move the corner down */ + if (acc_vc < acc_val[3]) { + acc_val[2] = acc_vc - 1; + acc_val[3] = acc_vc; + } + + /* If needed, sanitize previously stored vals from the LUT */ + if (acc_val[2] <= acc_val[1]) + acc_val[1] = acc_val[2] - 1; + if (acc_val[1] <= acc_val[0]) + acc_val[0] = acc_val[1] - 1; + } + + for (i = 0; i < SEQ_MEM_ACC_MAX_LEVELS; i++) { + ret = qcom_scm_io_writel(SEQ_MEMACC_REG(seq_addr, i), acc_val[i]); + if (ret) + return ret; + } + dev_dbg(cpu_dev, "Wrote MEM-ACC Pairs: [%u-%u] [%u-%u]\n", + acc_val[0], acc_val[1], acc_val[2], acc_val[3]); + + /* + * Program the L_VAL of the first corner requesting MEM-ACC + * voltage level 3 to the right sequencer register + */ + acc_lval = FIELD_GET(LUT_L_VAL, hw_tbl[acc_val[3]].freq_lut_val); + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_MEM_ACC_LVAL), acc_lval); + if (ret) { + dev_dbg(cpu_dev, "Cannot send memacc l_val\n"); + return ret; + } + dev_dbg(cpu_dev, "MEM-ACC L-Val is %u\n", acc_lval); + + /* + * Array Power Mux threshold level: the first virtual corner + * that requires a switch sequence of the APM from MX to APC. + */ + if (apm_vc == INT_MAX) + apm_vc = LUT_MAX_ENTRIES - 1; + + /* + * APM crossover virtual corner refers to CPRh: there, the APM corner + * is always appended to the table (so, at the end of it, right after + * the cluster dvfs entries). + */ + writel(num_entries, ddata->base + sregs->reg_seq1); + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_CROSSOVER_VC), num_entries); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_THRESH_VC), apm_vc); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_THRESH_PREVC), apm_vc - 1); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_PARAM), + (0x39 | apm_vc << 6)); + if (ret) + return ret; + dev_dbg(cpu_dev, "Wrote APM Pair: [%u-%u]\n", apm_vc - 1, apm_vc); + + /* + * We succeeded! Dispose of the table that got allocated during + * qcom_cpufreq_gen_params, as that contains parameters that are + * relevant only to the context of OSM programming, which is done + * only once. + */ + if (hw_tbl) + devm_kfree(&pdev->dev, hw_tbl); + + return 0; +} + /** * qcom_cpufreq_hw_read_lut() - Read Lookup Table from the OSM * @cpu_dev: CPU device @@ -229,14 +1021,16 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, } for (i = 0; i < LUT_MAX_ENTRIES; i++) { - data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut + - i * soc_data->lut_row_size); - src = FIELD_GET(LUT_SRC, data); + data = readl(drv_data->base + soc_data->reg_freq_lut + + i * soc_data->lut_row_size); + src = data & soc_data->reg_freq_lut_src_mask; + src >>= ffs(soc_data->reg_freq_lut_src_mask) - 1; + lval = FIELD_GET(LUT_L_VAL, data); core_count = FIELD_GET(LUT_CORE_COUNT, data); - data = readl_relaxed(drv_data->base + soc_data->reg_volt_lut + - i * soc_data->lut_row_size); + data = readl(drv_data->base + soc_data->reg_volt_lut + + i * soc_data->lut_row_size); volt = FIELD_GET(LUT_VOLT, data) * 1000; if (src) @@ -274,8 +1068,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, prev->frequency = prev_freq; prev->flags = CPUFREQ_BOOST_FREQ; } else { - dev_warn(cpu_dev, "failed to update OPP for freq=%d\n", - freq); + dev_warn(cpu_dev, "can't update OPP for freq=%u\n", freq); } } @@ -296,11 +1089,14 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, * qcom_get_related_cpus - Get mask of CPUs in the same frequency domain * @index: CPU number * @m: Returned CPU mask + * + * Returns: Count of CPUs inserted in the cpumask or negative number for error. */ -static void qcom_get_related_cpus(int index, struct cpumask *m) +static int qcom_get_related_cpus(int index, struct cpumask *m) { struct device_node *cpu_np; struct of_phandle_args args; + int count = 0; int cpu, ret; for_each_possible_cpu(cpu) { @@ -309,15 +1105,18 @@ static void qcom_get_related_cpus(int index, struct cpumask *m) continue; ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain", - "#freq-domain-cells", 0, - &args); + "#freq-domain-cells", 0, &args); of_node_put(cpu_np); if (ret < 0) continue; - if (index == args.args[0]) + if (index == args.args[0]) { cpumask_set_cpu(cpu, m); + count++; + } } + + return count > 0 ? count : -EINVAL; } static unsigned int qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data) @@ -404,22 +1203,91 @@ static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data) static const struct qcom_cpufreq_soc_data qcom_soc_data = { .reg_enable = 0x0, .reg_freq_lut = 0x110, + .reg_freq_lut_src_mask = LUT_SRC_845, .reg_volt_lut = 0x114, .reg_current_vote = 0x704, .reg_perf_state = 0x920, .lut_row_size = 32, + .clk_hw_div = 2, + .uses_tz = true, +}; + +static const struct qcom_cpufreq_soc_data msm8998_soc_data = { + .reg_enable = 0x4, + .reg_index = 0x150, + .reg_freq_lut = 0x154, + .reg_freq_lut_src_mask = LUT_SRC_8998, + .reg_volt_lut = 0x158, + .reg_perf_state = 0xf10, + .lut_row_size = 32, + .clk_hw_div = 1, + .uses_tz = false, + .setup_regs = { + /* Physical offset for sequencer scm calls */ + .reg_osm_sequencer = 0x300, + + /* Frequency domain offsets */ + .reg_override = 0x15c, + .reg_spare = 0x164, + .reg_cc_zero_behav = 0x0c, + .reg_spm_cc_hyst = 0x1c, + .reg_spm_cc_dcvs_dis = 0x20, + .reg_spm_core_ret_map = 0x24, + .reg_llm_freq_vote_hyst = 0x2c, + .reg_llm_volt_vote_hyst = 0x30, + .reg_llm_intf_dcvs_dis = 0x34, + .reg_seq1 = 0x48, + .reg_pdn_fsm_ctrl = 0x70, + .reg_cc_boost_timer = 0x74, + .reg_dcvs_boost_timer = 0x84, + .reg_ps_boost_timer = 0x94, + .boost_timer_reg_len = 0x4, + .reg_boost_sync_delay = 0xa0, + .reg_droop_ctrl = 0xa4, + .reg_droop_release_ctrl = 0xa8, + .reg_droop_unstall_ctrl = 0xac, + .reg_droop_wait_release_ctrl = 0xb0, + .reg_droop_timer_ctrl = 0xb8, + .reg_droop_sync_delay = 0xbc, + .reg_pll_override = 0xc0, + .reg_cycle_counter = 0xf00, + }, + .acd_data = { + .acd_ctrl_reg = 0x4, + .tl_delay_reg = 0x8, + .softstart_reg = 0x28, + .ext_intf_reg = 0x30, + .dcvs_sw_reg = 0x34, + .gfmux_cfg_reg = 0x3c, + .auto_xfer_cfg_reg = 0x80, + .auto_xfer_reg = 0x84, + .auto_xfer_ctl_reg = 0x88, + .auto_xfer_sts_reg = 0x8c, + .write_ctl_reg = 0x90, + .write_sts_reg = 0x94, + .tl_delay_val = 38417, + .acd_ctrl_val = 0x2b5ffd, + .softstart_val = 0x501, + .ext_intf0_val = 0x2cf9ae8, + .ext_intf1_val = 0x2cf9afe, + .auto_xfer_val = 0x15, + }, }; static const struct qcom_cpufreq_soc_data epss_soc_data = { .reg_enable = 0x0, .reg_freq_lut = 0x100, + .reg_freq_lut_src_mask = LUT_SRC_845, .reg_volt_lut = 0x200, .reg_perf_state = 0x320, .lut_row_size = 4, + .clk_hw_div = 2, + .uses_tz = true, }; static const struct of_device_id qcom_cpufreq_hw_match[] = { { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, + { .compatible = "qcom,cpufreq-hw-8998", .data = &msm8998_soc_data }, { .compatible = "qcom,cpufreq-epss", .data = &epss_soc_data }, {} }; @@ -470,6 +1338,130 @@ static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data) free_irq(data->throttle_irq, data); } +/** + * qcom_cpufreq_hw_osm_setup() - Setup and enable the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @cpu_count: Number of CPUs in the frequency domain + * + * On some platforms, the Operating State Manager (OSM) is not getting + * programmed by the bootloader, nor by TrustZone before booting the OS + * and its register space is not write-protected by the hypervisor. + * In this case, to achieve CPU DVFS, it is needed to program it from + * the OS itself, which includes setting LUT and all the various tunables + * that are required for it to manage the CPU frequencies and voltages + * on its own. + * Calling this function on a platform that had the OSM set-up by TZ + * will result in a hypervisor fault with system reboot in most cases. + * + * Returns: Zero for success, otherwise negative number on errors. + */ +static int qcom_cpufreq_hw_osm_setup(struct device *cpu_dev, + struct cpufreq_policy *policy, + int cpu_count, int index) +{ + struct qcom_cpufreq_data *drv_data = policy->driver_data; + const struct qcom_cpufreq_soc_setup_data *setup_regs; + u32 val; + int ret; + + ret = qcom_cpufreq_hw_write_lut(cpu_dev, policy, cpu_count, index); + if (ret) + return ret; + + setup_regs = &drv_data->soc_data->setup_regs; + + /* Set OSM to XO clock ratio and use XO edge for the cycle counter */ + val = FIELD_PREP(CYCLE_COUNTER_CLK_RATIO, OSM_XO_RATIO_VAL); + val |= CYCLE_COUNTER_USE_XO_EDGE; + + /* Enable the CPU cycle counter */ + val |= BIT(0); + writel(val, drv_data->base + setup_regs->reg_cycle_counter); + + /* CoreCount DCVS Policy: Wait time for frequency inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_CC_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_CC_NS); + writel(val, drv_data->base + setup_regs->reg_spm_cc_hyst); + + /* Set the frequency index 0 and override for cluster power collapse */ + writel(BIT(0), drv_data->base + setup_regs->reg_cc_zero_behav); + + /* Treat cores in retention as active */ + writel(0, drv_data->base + setup_regs->reg_spm_core_ret_map); + + /* Enable CoreCount based DCVS */ + writel(0, drv_data->base + setup_regs->reg_spm_cc_dcvs_dis); + + /* CoreCount DCVS-LLM Policy: Wait time for frequency inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_LLM_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_LLM_NS); + writel(val, drv_data->base + setup_regs->reg_llm_freq_vote_hyst); + + /* CoreCount DCVS-LLM Policy: Wait time for voltage inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_LLM_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_LLM_NS); + writel(val, drv_data->base + setup_regs->reg_llm_volt_vote_hyst); + + /* Enable LLM frequency+voltage voting */ + writel(0, drv_data->base + setup_regs->reg_llm_intf_dcvs_dis); + + /* Setup Boost FSM Timers */ + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_cc_boost_timer, + setup_regs->boost_timer_reg_len); + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_dcvs_boost_timer, + setup_regs->boost_timer_reg_len); + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_ps_boost_timer, + setup_regs->boost_timer_reg_len); + + /* PLL signal timing control for Boost */ + writel(BOOST_SYNC_DELAY, drv_data->base + setup_regs->reg_boost_sync_delay); + + /* Setup WFx and PC/RET droop unstall */ + val = FIELD_PREP(DROOP_TIMER1, DROOP_TIMER_NS); + val |= FIELD_PREP(DROOP_TIMER0, DROOP_TIMER_NS); + writel(val, drv_data->base + setup_regs->reg_droop_unstall_ctrl); + + /* Setup WFx and PC/RET droop wait-to-release */ + val = FIELD_PREP(DROOP_TIMER1, DROOP_WAIT_RELEASE_TIMER_NS); + val |= FIELD_PREP(DROOP_TIMER0, DROOP_WAIT_RELEASE_TIMER_NS); + writel(val, drv_data->base + setup_regs->reg_droop_wait_release_ctrl); + + /* PLL signal timing control for Droop */ + writel(1, drv_data->base + setup_regs->reg_droop_sync_delay); + + /* Setup DCVS timers */ + writel(DROOP_RELEASE_TIMER_NS, + drv_data->base + setup_regs->reg_droop_release_ctrl); + writel(DROOP_TIMER_NS, drv_data->base + setup_regs->reg_droop_timer_ctrl); + + /* Setup Droop control */ + val = readl(drv_data->base + setup_regs->reg_droop_ctrl); + val |= DROOP_CTRL_VAL; + writel(val, drv_data->base + setup_regs->reg_droop_ctrl); + + /* Enable CC-Boost, DCVS-Boost, PS-Boost, WFx, PC/RET, DCVS FSM */ + val = readl(drv_data->base + setup_regs->reg_pdn_fsm_ctrl); + val |= CC_BOOST_EN | PS_BOOST_EN | DCVS_BOOST_EN; + val |= WFX_DROOP_EN | PC_RET_EXIT_DROOP_EN | DCVS_DROOP_EN; + writel(val, drv_data->base + setup_regs->reg_pdn_fsm_ctrl); + + /* Enable PLL Droop Override */ + val = PLL_OVERRIDE_DROOP_EN; + writel(val, drv_data->base + setup_regs->reg_pll_override); + + /* Initialize the Adaptive Clock Distribution */ + ret = qcom_cpufreq_hw_acd_init(cpu_dev, policy, index); + if (ret) + return ret; + + /* We're ready: enable the OSM and give it time to boot (5uS) */ + writel(1, drv_data->base + drv_data->soc_data->reg_enable); + udelay(OSM_BOOT_TIME_US); + + return 0; +} + static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) { struct platform_device *pdev = cpufreq_get_driver_data(); @@ -480,7 +1472,8 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) struct resource *res; void __iomem *base; struct qcom_cpufreq_data *data; - int ret, index; + char fdom_resname[] = "freq-domainX"; + int cpu_count, index, ret; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -501,7 +1494,9 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) index = args.args[0]; - res = platform_get_resource(pdev, IORESOURCE_MEM, index); + snprintf(fdom_resname, sizeof(fdom_resname), "freq-domain%d", index); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, fdom_resname); if (!res) { dev_err(dev, "failed to get mem resource %d\n", index); return -ENODEV; @@ -620,9 +1615,50 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) { + const struct qcom_cpufreq_soc_data *soc_data; + struct device_node *pd_node; + struct platform_device *pd_dev; struct device *cpu_dev; struct clk *clk; - int ret; + int clk_div, ret; + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -EPROBE_DEFER; + + soc_data = of_device_get_match_data(&pdev->dev); + if (!soc_data) + return -EINVAL; + + if (!soc_data->uses_tz) { + /* + * When the OSM is not pre-programmed from TZ, we will + * need to program the sequencer through SCM calls. + */ + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + /* + * If there are no power-domains, OSM programming cannot be + * performed, as in that case, we wouldn't know where to take + * the params from... + */ + pd_node = of_parse_phandle(cpu_dev->of_node, "power-domains", 0); + if (!pd_node) { + ret = PTR_ERR(pd_node); + dev_err(cpu_dev, "power domain not found: %d\n", ret); + return ret; + } + + /* + * If the power domain device is not registered yet, then + * defer probing this driver until that is available. + */ + pd_dev = of_find_device_by_node(pd_node); + if (!pd_dev || !pd_dev->dev.driver || + !device_is_bound(&pd_dev->dev)) + return -EPROBE_DEFER; + } clk = clk_get(&pdev->dev, "xo"); if (IS_ERR(clk)) @@ -635,16 +1671,16 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV; + clk_div = soc_data->clk_hw_div; + if (clk_div == 0) + clk_div++; + + cpu_hw_rate = clk_get_rate(clk) / clk_div; clk_put(clk); cpufreq_qcom_hw_driver.driver_data = pdev; /* Check for optional interconnect paths on CPU0 */ - cpu_dev = get_cpu_device(0); - if (!cpu_dev) - return -EPROBE_DEFER; - ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL); if (ret) return ret; From 01a24161e1ed99e6895d2685d188af3654861efc Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 13:03:11 +0100 Subject: [PATCH 068/212] cpufreq: qcom-hw: Allow getting the maximum transition latency for OPPs In order to fine-tune the frequency scaling from various governors, allow to set a maximum transition latency from OPPs, which may be different depending on the SoC. Signed-off-by: AngeloGioacchino Del Regno [Fixup for 5.15 by Jami] --- drivers/cpufreq/qcom-cpufreq-hw.c | 37 ++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index d88f193b73cd..e215ca0f6560 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1473,6 +1473,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) void __iomem *base; struct qcom_cpufreq_data *data; char fdom_resname[] = "freq-domainX"; + unsigned int transition_latency; int cpu_count, index, ret; cpu_dev = get_cpu_device(policy->cpu); @@ -1523,6 +1524,25 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) data->soc_data = of_device_get_match_data(&pdev->dev); data->base = base; data->res = res; + policy->driver_data = data; + + cpu_count = qcom_get_related_cpus(index, policy->cpus); + if (!cpumask_weight(policy->cpus)) { + dev_err(dev, "Domain-%d failed to get related CPUs\n", index); + ret = -ENOENT; + goto error; + } + + policy->dvfs_possible_from_any_cpu = true; + if (!data->soc_data->uses_tz) { + ret = qcom_cpufreq_hw_osm_setup(cpu_dev, policy, + cpu_count, index); + if (ret) { + dev_err(dev, "Cannot setup the OSM for CPU%d: %d\n", + policy->cpu, ret); + goto error; + } + } /* HW should be in enabled state to proceed */ if (!(readl_relaxed(base + data->soc_data->reg_enable) & 0x1)) { @@ -1531,16 +1551,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) goto error; } - qcom_get_related_cpus(index, policy->cpus); - if (!cpumask_weight(policy->cpus)) { - dev_err(dev, "Domain-%d failed to get related CPUs\n", index); - ret = -ENOENT; - goto error; - } - - policy->driver_data = data; - policy->dvfs_possible_from_any_cpu = true; - ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy); if (ret) { dev_err(dev, "Domain-%d failed to read LUT\n", index); @@ -1554,6 +1564,12 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) goto error; } + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); + if (!transition_latency) + transition_latency = CPUFREQ_ETERNAL; + + policy->cpuinfo.transition_latency = transition_latency; + if (policy_has_boost_freq(policy)) { ret = cpufreq_enable_boost_support(); if (ret) @@ -1566,6 +1582,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) return 0; error: + policy->driver_data = NULL; kfree(data); unmap_base: iounmap(base); From 5691e1fe7dbe6ee472a3f9764f2a9bce059c2a53 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 9 Jan 2021 12:58:27 +0100 Subject: [PATCH 069/212] dt-bindings: cpufreq: qcom-hw: Add bindings for 8998 The OSM programming addition has been done under the qcom,cpufreq-hw-8998 compatible name: specify the requirement of two additional register spaces for this functionality. This implementation, with the same compatible, has been tested on MSM8998 and SDM630. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/cpufreq/cpufreq-qcom-hw.yaml | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml index bc81b6203e27..29b663321a0b 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -18,6 +18,10 @@ description: | properties: compatible: oneOf: + - description: Non-secure v1 of CPUFREQ HW + items: + - const: qcom,cpufreq-hw-8998 + - description: v1 of CPUFREQ HW items: - const: qcom,cpufreq-hw @@ -28,21 +32,9 @@ properties: - qcom,sm8250-cpufreq-epss - const: qcom,cpufreq-epss - reg: - minItems: 2 - maxItems: 3 - items: - - description: Frequency domain 0 register region - - description: Frequency domain 1 register region - - description: Frequency domain 2 register region + reg: {} - reg-names: - minItems: 2 - maxItems: 3 - items: - - const: freq-domain0 - - const: freq-domain1 - - const: freq-domain2 + reg-names: {} clocks: items: @@ -57,10 +49,55 @@ properties: '#freq-domain-cells': const: 1 +if: + properties: + compatible: + contains: + const: qcom,cpufreq-hw-8998 +then: + properties: + reg: + minItems: 2 + maxItems: 6 + items: + - description: Frequency domain 0 register region + - description: Operating State Manager domain 0 register region + - description: Frequency domain 1 register region + - description: Operating State Manager domain 1 register region + - description: PLL ACD domain 0 register region (if ACD programming required) + - description: PLL ACD domain 1 register region (if ACD programming required) + + reg-names: + minItems: 2 + maxItems: 6 + items: + - const: "osm-domain0" + - const: "freq-domain0" + - const: "osm-domain1" + - const: "freq-domain1" + - const: "osm-acd0" + - const: "osm-acd1" + +else: + properties: + reg: + minItems: 2 + maxItems: 3 + items: + - description: Frequency domain 0 register region + - description: Frequency domain 1 register region + - description: Frequency domain 2 register region + reg-names: + minItems: 2 + maxItems: 3 + items: + - const: "freq-domain0" + - const: "freq-domain1" + - const: "freq-domain2" + required: - compatible - reg - - reg-names - clocks - clock-names - '#freq-domain-cells' From f67d486afc9689258bf7fbf960c94361dce9d5ec Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 23:05:40 +0200 Subject: [PATCH 070/212] dt-bindings: cpufreq: qcom-hw: Make reg-names a required property The property reg-names is required after the addition of the OSM programming sequence, as that mandates specifying different register domains; to avoid confusion and improve devicetree readability, specifying the regions names was made mandatory. --- Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml index 29b663321a0b..17fd6a6cefb0 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -98,6 +98,7 @@ else: required: - compatible - reg + - reg-names - clocks - clock-names - '#freq-domain-cells' From 3cf0279920c533665d4de64ba6db2e1591581441 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 24 Nov 2020 20:31:05 +0100 Subject: [PATCH 071/212] soc: qcom: cpr: Move common functions to new file In preparation for implementing a new driver that will be handling CPRv3, CPRv4 and CPR-Hardened, format out common functions to a new file. Signed-off-by: AngeloGioacchino Del Regno [Fixup for 5.15 by Jami] --- drivers/soc/qcom/Makefile | 2 +- drivers/soc/qcom/cpr-common.c | 349 +++++++++++++++++++++++++++++ drivers/soc/qcom/cpr-common.h | 113 ++++++++++ drivers/soc/qcom/cpr.c | 408 +++------------------------------- 4 files changed, 490 insertions(+), 382 deletions(-) create mode 100644 drivers/soc/qcom/cpr-common.c create mode 100644 drivers/soc/qcom/cpr-common.h diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 24514c722832..778a2a5f07bb 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -3,7 +3,7 @@ CFLAGS_rpmh-rsc.o := -I$(src) obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o -obj-$(CONFIG_QCOM_CPR) += cpr.o +obj-$(CONFIG_QCOM_CPR) += cpr-common.o cpr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/drivers/soc/qcom/cpr-common.c b/drivers/soc/qcom/cpr-common.c new file mode 100644 index 000000000000..53d4827973cb --- /dev/null +++ b/drivers/soc/qcom/cpr-common.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpr-common.h" + +int cpr_populate_ring_osc_idx(struct device *dev, + struct fuse_corner *fuse_corner, + const struct cpr_fuse *cpr_fuse, + int num_fuse_corners) +{ + struct fuse_corner *end = fuse_corner + num_fuse_corners; + u32 data; + int ret; + + for (; fuse_corner < end; fuse_corner++, cpr_fuse++) { + ret = nvmem_cell_read_variable_le_u32(dev, cpr_fuse->ring_osc, &data); + if (ret) + return ret; + fuse_corner->ring_osc_idx = data; + } + + return 0; +} + +int cpr_read_fuse_uV(int init_v_width, int step_size_uV, int ref_uV, + int adj, int step_volt, const char *init_v_efuse, + struct device *dev) +{ + int steps, uV; + u32 bits = 0; + int ret; + + ret = nvmem_cell_read_variable_le_u32(dev, init_v_efuse, &bits); + if (ret) + return ret; + + steps = bits & (BIT(init_v_width - 1) - 1); + /* Not two's complement.. instead highest bit is sign bit */ + if (bits & BIT(init_v_width - 1)) + steps = -steps; + + uV = ref_uV + steps * step_size_uV; + + /* Apply open-loop fixed adjustments to fused values */ + uV += adj; + + return DIV_ROUND_UP(uV, step_volt) * step_volt; +} + +const struct cpr_fuse *cpr_get_fuses(struct device *dev, int tid, + int num_fuse_corners) +{ + struct cpr_fuse *fuses; + int i; + + fuses = devm_kcalloc(dev, num_fuse_corners, + sizeof(struct cpr_fuse), + GFP_KERNEL); + if (!fuses) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_fuse_corners; i++) { + char tbuf[50]; + + snprintf(tbuf, sizeof(tbuf), "cpr%d_ring_osc%d", tid, i + 1); + fuses[i].ring_osc = devm_kstrdup(dev, tbuf, GFP_KERNEL); + if (!fuses[i].ring_osc) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), + "cpr%d_init_voltage%d", tid, i + 1); + fuses[i].init_voltage = devm_kstrdup(dev, tbuf, + GFP_KERNEL); + if (!fuses[i].init_voltage) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), "cpr%d_quotient%d", tid, i + 1); + fuses[i].quotient = devm_kstrdup(dev, tbuf, GFP_KERNEL); + if (!fuses[i].quotient) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), + "cpr%d_quotient_offset%d", tid, i + 1); + fuses[i].quotient_offset = devm_kstrdup(dev, tbuf, + GFP_KERNEL); + if (!fuses[i].quotient_offset) + return ERR_PTR(-ENOMEM); + } + + return fuses; +} + +int cpr_populate_fuse_common(struct device *dev, + struct fuse_corner_data *fdata, + const struct cpr_fuse *cpr_fuse, + struct fuse_corner *fuse_corner, + int step_volt, int init_v_width, + int init_v_step) +{ + int uV, ret; + + /* Populate uV */ + uV = cpr_read_fuse_uV(init_v_width, init_v_step, + fdata->ref_uV, fdata->volt_oloop_adjust, + step_volt, cpr_fuse->init_voltage, dev); + if (uV < 0) + return uV; + + /* + * Update SoC voltages: platforms might choose a different + * regulators than the one used to characterize the algorithms + * (ie, init_voltage_step). + */ + fdata->min_uV = roundup(fdata->min_uV, step_volt); + fdata->max_uV = roundup(fdata->max_uV, step_volt); + + fuse_corner->min_uV = fdata->min_uV; + fuse_corner->max_uV = fdata->max_uV; + fuse_corner->uV = clamp(uV, fuse_corner->min_uV, fuse_corner->max_uV); + + /* Populate target quotient by scaling */ + ret = nvmem_cell_read_variable_le_u32(dev, cpr_fuse->quotient, &fuse_corner->quot); + if (ret) + return ret; + + fuse_corner->quot *= fdata->quot_scale; + fuse_corner->quot += fdata->quot_offset; + fuse_corner->quot += fdata->quot_adjust; + + return 0; +} + +/* + * Returns: Index of the initial corner or negative number for error. + */ +int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, + struct corner *corners, int num_corners) +{ + unsigned long rate; + struct corner *iter, *corner; + const struct corner *end; + unsigned int ret = 0; + + if (!cpu_clk) + return -EINVAL; + + end = &corners[num_corners - 1]; + rate = clk_get_rate(cpu_clk); + + /* + * Some bootloaders set a CPU clock frequency that is not defined + * in the OPP table. When running at an unlisted frequency, + * cpufreq_online() will change to the OPP which has the lowest + * frequency, at or above the unlisted frequency. + * Since cpufreq_online() always "rounds up" in the case of an + * unlisted frequency, this function always "rounds down" in case + * of an unlisted frequency. That way, when cpufreq_online() + * triggers the first ever call to cpr_set_performance_state(), + * it will correctly determine the direction as UP. + */ + for (iter = corners; iter <= end; iter++) { + if (iter->freq > rate) + break; + ret++; + if (iter->freq == rate) { + corner = iter; + break; + } + if (iter->freq < rate) + corner = iter; + } + + if (!corner) { + dev_err(dev, "boot up corner not found\n"); + return -EINVAL; + } + + dev_dbg(dev, "boot up perf state: %u\n", ret); + + return ret; +} + +u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid) +{ + struct device_node *np; + u32 fc; + + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32_index(np, "qcom,opp-fuse-level", tid, &fc)) { + pr_debug("%s: missing 'qcom,opp-fuse-level' property\n", + __func__); + fc = 0; + } + + of_node_put(np); + + return fc; +} + +unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, + struct device *cpu_dev) +{ + u64 rate = 0; + struct device_node *ref_np; + struct device_node *desc_np; + struct device_node *child_np = NULL; + struct device_node *child_req_np = NULL; + + desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); + if (!desc_np) + return 0; + + ref_np = dev_pm_opp_get_of_node(ref); + if (!ref_np) + goto out_ref; + + do { + of_node_put(child_req_np); + child_np = of_get_next_available_child(desc_np, child_np); + child_req_np = of_parse_phandle(child_np, "required-opps", 0); + } while (child_np && child_req_np != ref_np); + + if (child_np && child_req_np == ref_np) + of_property_read_u64(child_np, "opp-hz", &rate); + + of_node_put(child_req_np); + of_node_put(child_np); + of_node_put(ref_np); +out_ref: + of_node_put(desc_np); + + return (unsigned long) rate; +} + +int cpr_calculate_scaling(const char *quot_offset, + struct device *dev, + const struct fuse_corner_data *fdata, + const struct corner *corner) +{ + u32 quot_diff = 0; + unsigned long freq_diff; + int scaling; + const struct fuse_corner *fuse, *prev_fuse; + int ret; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + if (quot_offset) { + ret = nvmem_cell_read_variable_le_u32(dev, quot_offset, "_diff); + if (ret) + return ret; + + quot_diff *= fdata->quot_offset_scale; + quot_diff += fdata->quot_offset_adjust; + } else { + quot_diff = fuse->quot - prev_fuse->quot; + } + + freq_diff = fuse->max_freq - prev_fuse->max_freq; + freq_diff /= 1000000; /* Convert to MHz */ + scaling = 1000 * quot_diff / freq_diff; + return min(scaling, fdata->max_quot_scale); +} + +int cpr_interpolate(const struct corner *corner, int step_volt, + const struct fuse_corner_data *fdata) +{ + unsigned long f_high, f_low, f_diff; + int uV_high, uV_low, uV; + u64 temp, temp_limit; + const struct fuse_corner *fuse, *prev_fuse; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + f_high = fuse->max_freq; + f_low = prev_fuse->max_freq; + uV_high = fuse->uV; + uV_low = prev_fuse->uV; + f_diff = fuse->max_freq - corner->freq; + + /* + * Don't interpolate in the wrong direction. This could happen + * if the adjusted fuse voltage overlaps with the previous fuse's + * adjusted voltage. + */ + if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) + return corner->uV; + + temp = f_diff * (uV_high - uV_low); + do_div(temp, f_high - f_low); + + /* + * max_volt_scale has units of uV/MHz while freq values + * have units of Hz. Divide by 1000000 to convert to. + */ + temp_limit = f_diff * fdata->max_volt_scale; + do_div(temp_limit, 1000000); + + uV = uV_high - min(temp, temp_limit); + return roundup(uV, step_volt); +} + +int cpr_check_vreg_constraints(struct device *dev, struct regulator *vreg, + struct fuse_corner *f) +{ + int ret; + + ret = regulator_is_supported_voltage(vreg, f->min_uV, f->min_uV); + if (!ret) { + dev_err(dev, "min uV: %d not supported by regulator\n", + f->min_uV); + return -EINVAL; + } + + ret = regulator_is_supported_voltage(vreg, f->max_uV, f->max_uV); + if (!ret) { + dev_err(dev, "max uV: %d not supported by regulator\n", + f->max_uV); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/soc/qcom/cpr-common.h b/drivers/soc/qcom/cpr-common.h new file mode 100644 index 000000000000..83a1f7c941b8 --- /dev/null +++ b/drivers/soc/qcom/cpr-common.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include + +enum voltage_change_dir { + NO_CHANGE, + DOWN, + UP, +}; + +struct fuse_corner_data { + int ref_uV; + int max_uV; + int min_uV; + int range_uV; + /* fuse volt: closed/open loop */ + int volt_cloop_adjust; + int volt_oloop_adjust; + int max_volt_scale; + int max_quot_scale; + /* fuse quot */ + int quot_offset; + int quot_scale; + int quot_adjust; + /* fuse quot_offset */ + int quot_offset_scale; + int quot_offset_adjust; +}; + +struct cpr_fuse { + char *ring_osc; + char *init_voltage; + char *quotient; + char *quotient_offset; +}; + +struct fuse_corner { + int min_uV; + int max_uV; + int uV; + int quot; + int step_quot; + const struct reg_sequence *accs; + int num_accs; + unsigned long max_freq; + u8 ring_osc_idx; +}; + +struct corner { + int min_uV; + int max_uV; + int uV; + int last_uV; + int quot_adjust; + u32 save_ctl; + u32 save_irq; + unsigned long freq; + bool is_open_loop; + struct fuse_corner *fuse_corner; +}; + +struct corner_data { + unsigned int fuse_corner; + unsigned long freq; +}; + +struct acc_desc { + unsigned int enable_reg; + u32 enable_mask; + + struct reg_sequence *config; + struct reg_sequence *settings; + int num_regs_per_fuse; +}; + +struct cpr_acc_desc { + const struct cpr_desc *cpr_desc; + const struct acc_desc *acc_desc; +}; + + +int cpr_read_efuse(struct device *dev, const char *cname, u32 *data); +int cpr_populate_ring_osc_idx(struct device *dev, + struct fuse_corner *fuse_corner, + const struct cpr_fuse *cpr_fuse, + int num_fuse_corners); +int cpr_read_fuse_uV(int init_v_width, int step_size_uV, int ref_uV, + int adj, int step_volt, const char *init_v_efuse, + struct device *dev); +const struct cpr_fuse *cpr_get_fuses(struct device *dev, int tid, + int num_fuse_corners); +int cpr_populate_fuse_common(struct device *dev, + struct fuse_corner_data *fdata, + const struct cpr_fuse *cpr_fuse, + struct fuse_corner *fuse_corner, + int step_volt, int init_v_width, + int init_v_step); +int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, + struct corner *corners, int num_corners); +u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid); +unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, + struct device *cpu_dev); +int cpr_calculate_scaling(const char *quot_offset, + struct device *dev, + const struct fuse_corner_data *fdata, + const struct corner *corner); +int cpr_interpolate(const struct corner *corner, int step_volt, + const struct fuse_corner_data *fdata); +int cpr_check_vreg_constraints(struct device *dev, struct regulator *vreg, + struct fuse_corner *f); diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c index 4ce8e816154f..bf489e0b7f5e 100644 --- a/drivers/soc/qcom/cpr.c +++ b/drivers/soc/qcom/cpr.c @@ -25,6 +25,7 @@ #include #include #include +#include "cpr-common.h" /* Register Offsets for RB-CPR and Bit Definitions */ @@ -124,45 +125,12 @@ #define FUSE_REVISION_UNKNOWN (-1) -enum voltage_change_dir { - NO_CHANGE, - DOWN, - UP, -}; - -struct cpr_fuse { - char *ring_osc; - char *init_voltage; - char *quotient; - char *quotient_offset; -}; - -struct fuse_corner_data { - int ref_uV; - int max_uV; - int min_uV; - int max_volt_scale; - int max_quot_scale; - /* fuse quot */ - int quot_offset; - int quot_scale; - int quot_adjust; - /* fuse quot_offset */ - int quot_offset_scale; - int quot_offset_adjust; -}; - struct cpr_fuses { int init_voltage_step; int init_voltage_width; struct fuse_corner_data *fuse_corner_data; }; -struct corner_data { - unsigned int fuse_corner; - unsigned long freq; -}; - struct cpr_desc { unsigned int num_fuse_corners; int min_diff_quot; @@ -184,44 +152,6 @@ struct cpr_desc { bool reduce_to_corner_uV; }; -struct acc_desc { - unsigned int enable_reg; - u32 enable_mask; - - struct reg_sequence *config; - struct reg_sequence *settings; - int num_regs_per_fuse; -}; - -struct cpr_acc_desc { - const struct cpr_desc *cpr_desc; - const struct acc_desc *acc_desc; -}; - -struct fuse_corner { - int min_uV; - int max_uV; - int uV; - int quot; - int step_quot; - const struct reg_sequence *accs; - int num_accs; - unsigned long max_freq; - u8 ring_osc_idx; -}; - -struct corner { - int min_uV; - int max_uV; - int uV; - int last_uV; - int quot_adjust; - u32 save_ctl; - u32 save_irq; - unsigned long freq; - struct fuse_corner *fuse_corner; -}; - struct cpr_drv { unsigned int num_corners; unsigned int ref_clk_khz; @@ -801,62 +731,16 @@ unlock: return ret; } -static int -cpr_populate_ring_osc_idx(struct cpr_drv *drv) -{ - struct fuse_corner *fuse = drv->fuse_corners; - struct fuse_corner *end = fuse + drv->desc->num_fuse_corners; - const struct cpr_fuse *fuses = drv->cpr_fuses; - u32 data; - int ret; - - for (; fuse < end; fuse++, fuses++) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data); - if (ret) - return ret; - fuse->ring_osc_idx = data; - } - - return 0; -} - -static int cpr_read_fuse_uV(const struct cpr_desc *desc, - const struct fuse_corner_data *fdata, - const char *init_v_efuse, - int step_volt, - struct cpr_drv *drv) -{ - int step_size_uV, steps, uV; - u32 bits = 0; - int ret; - - ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits); - if (ret) - return ret; - - steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1); - /* Not two's complement.. instead highest bit is sign bit */ - if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1)) - steps = -steps; - - step_size_uV = desc->cpr_fuses.init_voltage_step; - - uV = fdata->ref_uV + steps * step_size_uV; - return DIV_ROUND_UP(uV, step_volt) * step_volt; -} - static int cpr_fuse_corner_init(struct cpr_drv *drv) { const struct cpr_desc *desc = drv->desc; - const struct cpr_fuse *fuses = drv->cpr_fuses; + const struct cpr_fuse *cpr_fuse = drv->cpr_fuses; const struct acc_desc *acc_desc = drv->acc_desc; - int i; - unsigned int step_volt; struct fuse_corner_data *fdata; struct fuse_corner *fuse, *end; - int uV; const struct reg_sequence *accs; - int ret; + unsigned int step_volt; + int i, ret; accs = acc_desc->settings; @@ -869,24 +753,16 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) end = &fuse[desc->num_fuse_corners - 1]; fdata = desc->cpr_fuses.fuse_corner_data; - for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) { - /* - * Update SoC voltages: platforms might choose a different - * regulators than the one used to characterize the algorithms - * (ie, init_voltage_step). - */ - fdata->min_uV = roundup(fdata->min_uV, step_volt); - fdata->max_uV = roundup(fdata->max_uV, step_volt); + for (i = 0; fuse <= end; fuse++, cpr_fuse++, i++, fdata++) { + ret = cpr_populate_fuse_common( + drv->dev, fdata, cpr_fuse, + fuse, step_volt, + desc->cpr_fuses.init_voltage_width, + desc->cpr_fuses.init_voltage_step); + if (ret) + return ret; - /* Populate uV */ - uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage, - step_volt, drv); - if (uV < 0) - return uV; - - fuse->min_uV = fdata->min_uV; - fuse->max_uV = fdata->max_uV; - fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV); + fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; if (fuse == end) { /* @@ -898,16 +774,6 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) end->max_uV = max(end->max_uV, end->uV); } - /* Populate target quotient by scaling */ - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot); - if (ret) - return ret; - - fuse->quot *= fdata->quot_scale; - fuse->quot += fdata->quot_offset; - fuse->quot += fdata->quot_adjust; - fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; - /* Populate acc settings */ fuse->accs = accs; fuse->num_accs = acc_desc->num_regs_per_fuse; @@ -924,25 +790,9 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) else if (fuse->uV < fuse->min_uV) fuse->uV = fuse->min_uV; - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->min_uV, - fuse->min_uV); - if (!ret) { - dev_err(drv->dev, - "min uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->min_uV, i); - return -EINVAL; - } - - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->max_uV, - fuse->max_uV); - if (!ret) { - dev_err(drv->dev, - "max uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->max_uV, i); - return -EINVAL; - } + ret = cpr_check_vreg_constraints(drv->dev, drv->vdd_apc, fuse); + if (ret) + return ret; dev_dbg(drv->dev, "fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n", @@ -953,126 +803,6 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) return 0; } -static int cpr_calculate_scaling(const char *quot_offset, - struct cpr_drv *drv, - const struct fuse_corner_data *fdata, - const struct corner *corner) -{ - u32 quot_diff = 0; - unsigned long freq_diff; - int scaling; - const struct fuse_corner *fuse, *prev_fuse; - int ret; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - if (quot_offset) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, "_diff); - if (ret) - return ret; - - quot_diff *= fdata->quot_offset_scale; - quot_diff += fdata->quot_offset_adjust; - } else { - quot_diff = fuse->quot - prev_fuse->quot; - } - - freq_diff = fuse->max_freq - prev_fuse->max_freq; - freq_diff /= 1000000; /* Convert to MHz */ - scaling = 1000 * quot_diff / freq_diff; - return min(scaling, fdata->max_quot_scale); -} - -static int cpr_interpolate(const struct corner *corner, int step_volt, - const struct fuse_corner_data *fdata) -{ - unsigned long f_high, f_low, f_diff; - int uV_high, uV_low, uV; - u64 temp, temp_limit; - const struct fuse_corner *fuse, *prev_fuse; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - f_high = fuse->max_freq; - f_low = prev_fuse->max_freq; - uV_high = fuse->uV; - uV_low = prev_fuse->uV; - f_diff = fuse->max_freq - corner->freq; - - /* - * Don't interpolate in the wrong direction. This could happen - * if the adjusted fuse voltage overlaps with the previous fuse's - * adjusted voltage. - */ - if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) - return corner->uV; - - temp = f_diff * (uV_high - uV_low); - do_div(temp, f_high - f_low); - - /* - * max_volt_scale has units of uV/MHz while freq values - * have units of Hz. Divide by 1000000 to convert to. - */ - temp_limit = f_diff * fdata->max_volt_scale; - do_div(temp_limit, 1000000); - - uV = uV_high - min(temp, temp_limit); - return roundup(uV, step_volt); -} - -static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp) -{ - struct device_node *np; - unsigned int fuse_corner = 0; - - np = dev_pm_opp_get_of_node(opp); - if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner)) - pr_err("%s: missing 'qcom,opp-fuse-level' property\n", - __func__); - - of_node_put(np); - - return fuse_corner; -} - -static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, - struct device *cpu_dev) -{ - u64 rate = 0; - struct device_node *ref_np; - struct device_node *desc_np; - struct device_node *child_np = NULL; - struct device_node *child_req_np = NULL; - - desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); - if (!desc_np) - return 0; - - ref_np = dev_pm_opp_get_of_node(ref); - if (!ref_np) - goto out_ref; - - do { - of_node_put(child_req_np); - child_np = of_get_next_available_child(desc_np, child_np); - child_req_np = of_parse_phandle(child_np, "required-opps", 0); - } while (child_np && child_req_np != ref_np); - - if (child_np && child_req_np == ref_np) - of_property_read_u64(child_np, "opp-hz", &rate); - - of_node_put(child_req_np); - of_node_put(child_np); - of_node_put(ref_np); -out_ref: - of_node_put(desc_np); - - return (unsigned long) rate; -} - static int cpr_corner_init(struct cpr_drv *drv) { const struct cpr_desc *desc = drv->desc; @@ -1110,7 +840,7 @@ static int cpr_corner_init(struct cpr_drv *drv) opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level); if (IS_ERR(opp)) return -EINVAL; - fc = cpr_get_fuse_corner(opp); + fc = cpr_get_fuse_corner(opp, 0); if (!fc) { dev_pm_opp_put(opp); return -EINVAL; @@ -1186,7 +916,7 @@ static int cpr_corner_init(struct cpr_drv *drv) corner->uV = fuse->uV; if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) { - scaling = cpr_calculate_scaling(quot_offset, drv, + scaling = cpr_calculate_scaling(quot_offset, drv->dev, fdata, corner); if (scaling < 0) return scaling; @@ -1224,47 +954,6 @@ static int cpr_corner_init(struct cpr_drv *drv) return 0; } -static const struct cpr_fuse *cpr_get_fuses(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - struct cpr_fuse *fuses; - int i; - - fuses = devm_kcalloc(drv->dev, desc->num_fuse_corners, - sizeof(struct cpr_fuse), - GFP_KERNEL); - if (!fuses) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < desc->num_fuse_corners; i++) { - char tbuf[32]; - - snprintf(tbuf, 32, "cpr_ring_osc%d", i + 1); - fuses[i].ring_osc = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].ring_osc) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_init_voltage%d", i + 1); - fuses[i].init_voltage = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].init_voltage) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient%d", i + 1); - fuses[i].quotient = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].quotient) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient_offset%d", i + 1); - fuses[i].quotient_offset = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].quotient_offset) - return ERR_PTR(-ENOMEM); - } - - return fuses; -} - static void cpr_set_loop_allowed(struct cpr_drv *drv) { drv->loop_disabled = false; @@ -1296,54 +985,6 @@ static int cpr_init_parameters(struct cpr_drv *drv) return 0; } -static int cpr_find_initial_corner(struct cpr_drv *drv) -{ - unsigned long rate; - const struct corner *end; - struct corner *iter; - unsigned int i = 0; - - if (!drv->cpu_clk) { - dev_err(drv->dev, "cannot get rate from NULL clk\n"); - return -EINVAL; - } - - end = &drv->corners[drv->num_corners - 1]; - rate = clk_get_rate(drv->cpu_clk); - - /* - * Some bootloaders set a CPU clock frequency that is not defined - * in the OPP table. When running at an unlisted frequency, - * cpufreq_online() will change to the OPP which has the lowest - * frequency, at or above the unlisted frequency. - * Since cpufreq_online() always "rounds up" in the case of an - * unlisted frequency, this function always "rounds down" in case - * of an unlisted frequency. That way, when cpufreq_online() - * triggers the first ever call to cpr_set_performance_state(), - * it will correctly determine the direction as UP. - */ - for (iter = drv->corners; iter <= end; iter++) { - if (iter->freq > rate) - break; - i++; - if (iter->freq == rate) { - drv->corner = iter; - break; - } - if (iter->freq < rate) - drv->corner = iter; - } - - if (!drv->corner) { - dev_err(drv->dev, "boot up corner not found\n"); - return -EINVAL; - } - - dev_dbg(drv->dev, "boot up perf state: %u\n", i); - - return 0; -} - static const struct cpr_desc qcs404_cpr_desc = { .num_fuse_corners = 3, .min_diff_quot = CPR_FUSE_MIN_QUOT_DIFF, @@ -1531,8 +1172,9 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, if (ret) goto unlock; - ret = cpr_find_initial_corner(drv); - if (ret) + ret = cpr_find_initial_corner(drv->dev, drv->cpu_clk, drv->corners, + drv->num_corners); + if (ret < 0) goto unlock; if (acc_desc->config) @@ -1617,6 +1259,7 @@ static int cpr_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct cpr_drv *drv; + const struct cpr_desc *desc; int irq, ret; const struct cpr_acc_desc *data; struct device_node *np; @@ -1632,6 +1275,7 @@ static int cpr_probe(struct platform_device *pdev) drv->dev = dev; drv->desc = data->cpr_desc; drv->acc_desc = data->acc_desc; + desc = drv->desc; drv->fuse_corners = devm_kcalloc(dev, drv->desc->num_fuse_corners, sizeof(*drv->fuse_corners), @@ -1672,11 +1316,13 @@ static int cpr_probe(struct platform_device *pdev) if (ret) return ret; - drv->cpr_fuses = cpr_get_fuses(drv); + drv->cpr_fuses = cpr_get_fuses(drv->dev, 0, desc->num_fuse_corners); if (IS_ERR(drv->cpr_fuses)) return PTR_ERR(drv->cpr_fuses); - ret = cpr_populate_ring_osc_idx(drv); + ret = cpr_populate_ring_osc_idx(drv->dev, drv->fuse_corners, + drv->cpr_fuses, + desc->num_fuse_corners); if (ret) return ret; From 54cffae7098d8a10c671bd0f002505011b5e6757 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 20:29:26 +0100 Subject: [PATCH 072/212] dt-bindings: avs: cpr: Convert binding to YAML schema Convert the qcom,cpr.txt document to YAML schema and place it in the appropriate directory, since this driver was moved from power/avs to soc/qcom, but forgets to move the documentation. Fixes: a7305e684fcf ("PM: AVS: qcom-cpr: Move the driver to the qcom specific drivers") Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/power/avs/qcom,cpr.txt | 131 +------------- .../bindings/soc/qcom/qcom,cpr.yaml | 167 ++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 169 insertions(+), 131 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml diff --git a/Documentation/devicetree/bindings/power/avs/qcom,cpr.txt b/Documentation/devicetree/bindings/power/avs/qcom,cpr.txt index ab0d5ebbad4e..2ada8cd08949 100644 --- a/Documentation/devicetree/bindings/power/avs/qcom,cpr.txt +++ b/Documentation/devicetree/bindings/power/avs/qcom,cpr.txt @@ -1,130 +1 @@ -QCOM CPR (Core Power Reduction) - -CPR (Core Power Reduction) is a technology to reduce core power on a CPU -or other device. Each OPP of a device corresponds to a "corner" that has -a range of valid voltages for a particular frequency. While the device is -running at a particular frequency, CPR monitors dynamic factors such as -temperature, etc. and suggests adjustments to the voltage to save power -and meet silicon characteristic requirements. - -- compatible: - Usage: required - Value type: - Definition: should be "qcom,qcs404-cpr", "qcom,cpr" for qcs404 - -- reg: - Usage: required - Value type: - Definition: base address and size of the rbcpr register region - -- interrupts: - Usage: required - Value type: - Definition: should specify the CPR interrupt - -- clocks: - Usage: required - Value type: - Definition: phandle to the reference clock - -- clock-names: - Usage: required - Value type: - Definition: must be "ref" - -- vdd-apc-supply: - Usage: required - Value type: - Definition: phandle to the vdd-apc-supply regulator - -- #power-domain-cells: - Usage: required - Value type: - Definition: should be 0 - -- operating-points-v2: - Usage: required - Value type: - Definition: A phandle to the OPP table containing the - performance states supported by the CPR - power domain - -- acc-syscon: - Usage: optional - Value type: - Definition: phandle to syscon for writing ACC settings - -- nvmem-cells: - Usage: required - Value type: - Definition: phandle to nvmem cells containing the data - that makes up a fuse corner, for each fuse corner. - As well as the CPR fuse revision. - -- nvmem-cell-names: - Usage: required - Value type: - Definition: should be "cpr_quotient_offset1", "cpr_quotient_offset2", - "cpr_quotient_offset3", "cpr_init_voltage1", - "cpr_init_voltage2", "cpr_init_voltage3", "cpr_quotient1", - "cpr_quotient2", "cpr_quotient3", "cpr_ring_osc1", - "cpr_ring_osc2", "cpr_ring_osc3", "cpr_fuse_revision" - for qcs404. - -Example: - - cpr_opp_table: cpr-opp-table { - compatible = "operating-points-v2-qcom-level"; - - cpr_opp1: opp1 { - opp-level = <1>; - qcom,opp-fuse-level = <1>; - }; - cpr_opp2: opp2 { - opp-level = <2>; - qcom,opp-fuse-level = <2>; - }; - cpr_opp3: opp3 { - opp-level = <3>; - qcom,opp-fuse-level = <3>; - }; - }; - - power-controller@b018000 { - compatible = "qcom,qcs404-cpr", "qcom,cpr"; - reg = <0x0b018000 0x1000>; - interrupts = <0 15 IRQ_TYPE_EDGE_RISING>; - clocks = <&xo_board>; - clock-names = "ref"; - vdd-apc-supply = <&pms405_s3>; - #power-domain-cells = <0>; - operating-points-v2 = <&cpr_opp_table>; - acc-syscon = <&tcsr>; - - nvmem-cells = <&cpr_efuse_quot_offset1>, - <&cpr_efuse_quot_offset2>, - <&cpr_efuse_quot_offset3>, - <&cpr_efuse_init_voltage1>, - <&cpr_efuse_init_voltage2>, - <&cpr_efuse_init_voltage3>, - <&cpr_efuse_quot1>, - <&cpr_efuse_quot2>, - <&cpr_efuse_quot3>, - <&cpr_efuse_ring1>, - <&cpr_efuse_ring2>, - <&cpr_efuse_ring3>, - <&cpr_efuse_revision>; - nvmem-cell-names = "cpr_quotient_offset1", - "cpr_quotient_offset2", - "cpr_quotient_offset3", - "cpr_init_voltage1", - "cpr_init_voltage2", - "cpr_init_voltage3", - "cpr_quotient1", - "cpr_quotient2", - "cpr_quotient3", - "cpr_ring_osc1", - "cpr_ring_osc2", - "cpr_ring_osc3", - "cpr_fuse_revision"; - }; +This file has been moved to ../../soc/qcom/qcom,cpr.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml new file mode 100644 index 000000000000..f58dc6c138a6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/qcom/qcom,cpr.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Core Power Reduction (CPR) + +description: | + CPR (Core Power Reduction) is a technology to reduce core power on a CPU + or other device. Each OPP of a device corresponds to a "corner" that has + a range of valid voltages for a particular frequency. While the device is + running at a particular frequency, CPR monitors dynamic factors such as + temperature, etc. and suggests adjustments to the voltage to save power + and meet silicon characteristic requirements. + +maintainers: + - Niklas Cassel + +properties: + compatible: + items: + - enum: + - qcom,qcs404-cpr + - const: qcom,cpr + + reg: + description: Base address and size of the RBCPR register region + maxItems: 1 + + interrupts: + maxItems: 1 + + clock-names: + items: + - const: ref + + clocks: + items: + - description: CPR reference clock + + vdd-apc-supply: + description: Autonomous Phase Control (APC) power supply + + '#power-domain-cells': + const: 0 + + acc-syscon: + description: phandle to syscon for writing ACC settings + + nvmem-cells: + minItems: 9 + maxItems: 32 + description: Cells containing the fuse corners and revision data + + nvmem-cell-names: + minItems: 9 + maxItems: 32 + + operating-points-v2: true + +required: + - compatible + - reg + - interrupts + - clock-names + - clocks + - vdd-apc-supply + - "#power-domain-cells" + - nvmem-cells + - nvmem-cell-names + - operating-points-v2 + +additionalProperties: false + +examples: + - | + #include + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@100 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <0x100>; + operating-points-v2 = <&cpu_opp_table>; + power-domains = <&cpr>; + power-domain-names = "cpr"; + }; + }; + + cpu_opp_table: cpu-opp-table { + compatible = "operating-points-v2-kryo-cpu"; + opp-shared; + + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cpr_opp1>; + }; + opp-1248000000 { + opp-hz = /bits/ 64 <1248000000>; + required-opps = <&cpr_opp2>; + }; + opp-1401600000 { + opp-hz = /bits/ 64 <1401600000>; + required-opps = <&cpr_opp3>; + }; + }; + + cpr_opp_table: cpr-opp-table { + compatible = "operating-points-v2-qcom-level"; + + cpr_opp1: opp1 { + opp-level = <1>; + qcom,opp-fuse-level = <1>; + }; + cpr_opp2: opp2 { + opp-level = <2>; + qcom,opp-fuse-level = <2>; + }; + cpr_opp3: opp3 { + opp-level = <3>; + qcom,opp-fuse-level = <3>; + }; + }; + + power-controller@b018000 { + compatible = "qcom,qcs404-cpr", "qcom,cpr"; + reg = <0x0b018000 0x1000>; + interrupts = <0 15 IRQ_TYPE_EDGE_RISING>; + clocks = <&xo_board>; + clock-names = "ref"; + vdd-apc-supply = <&pms405_s3>; + #power-domain-cells = <0>; + operating-points-v2 = <&cpr_opp_table>; + acc-syscon = <&tcsr>; + + nvmem-cells = <&cpr_efuse_quot_offset1>, + <&cpr_efuse_quot_offset2>, + <&cpr_efuse_quot_offset3>, + <&cpr_efuse_init_voltage1>, + <&cpr_efuse_init_voltage2>, + <&cpr_efuse_init_voltage3>, + <&cpr_efuse_quot1>, + <&cpr_efuse_quot2>, + <&cpr_efuse_quot3>, + <&cpr_efuse_ring1>, + <&cpr_efuse_ring2>, + <&cpr_efuse_ring3>, + <&cpr_efuse_revision>; + nvmem-cell-names = "cpr0_quotient_offset1", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_fuse_revision"; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 3b79fd441dde..406be2428e35 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15490,7 +15490,7 @@ M: Niklas Cassel L: linux-pm@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/power/avs/qcom,cpr.txt +F: Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml F: drivers/soc/qcom/cpr.c QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 From e9fe4aa5dae3d421b45c066f5d147ba201ac56ae Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 11:23:17 +0100 Subject: [PATCH 073/212] arm64: qcom: qcs404: Change CPR nvmem-names The CPR driver's common functions were split and put in another file in order to support newer CPR revisions: to simplify the commonization, the expected names of the fuses had to be changed in order for both new and old support to use the same fuse name retrieval function and keeping the naming consistent. The thread id was added to the fuse name and, since CPRv1 does not support threads, it is expected to always read ID 0, which means that the expected name here is now "cpr0_(fuse_name)" instead of "cpr_(fuse_name)": luckily, QCS404 is the only user so change it accordingly. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/qcs404.dtsi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs404.dtsi b/arch/arm64/boot/dts/qcom/qcs404.dtsi index 339790ba585d..f9350b028435 100644 --- a/arch/arm64/boot/dts/qcom/qcs404.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs404.dtsi @@ -1168,19 +1168,19 @@ <&cpr_efuse_ring2>, <&cpr_efuse_ring3>, <&cpr_efuse_revision>; - nvmem-cell-names = "cpr_quotient_offset1", - "cpr_quotient_offset2", - "cpr_quotient_offset3", - "cpr_init_voltage1", - "cpr_init_voltage2", - "cpr_init_voltage3", - "cpr_quotient1", - "cpr_quotient2", - "cpr_quotient3", - "cpr_ring_osc1", - "cpr_ring_osc2", - "cpr_ring_osc3", - "cpr_fuse_revision"; + nvmem-cell-names = "cpr0_quotient_offset1", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_fuse_revision"; }; timer@b120000 { From b845614d932f173b9d60785f3d4c8a920965ba03 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 24 Nov 2020 20:39:22 +0100 Subject: [PATCH 074/212] soc: qcom: Add support for Core Power Reduction v3, v4 and Hardened This commit introduces a new driver, based on the one for cpr v1, to enable support for the newer Qualcomm Core Power Reduction hardware, known downstream as CPR3, CPR4 and CPRh, and support for MSM8998 and SDM630 CPU power reduction. In these new versions of the hardware, support for various new features was introduced, including voltage reduction for the GPU, security hardening and a new way of controlling CPU DVFS, consisting in internal communication between microcontrollers, specifically the CPR-Hardened and the Operating State Manager. The CPR v3, v4 and CPRh are present in a broad range of SoCs, from the mid-range to the high end ones including, but not limited to, MSM8953/8996/8998, SDM630/636/660/845. Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/qcom/Kconfig | 17 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/cpr-common.c | 35 +- drivers/soc/qcom/cpr-common.h | 4 + drivers/soc/qcom/cpr3.c | 2897 +++++++++++++++++++++++++++++++++ include/soc/qcom/cpr.h | 17 + 6 files changed, 2965 insertions(+), 6 deletions(-) create mode 100644 drivers/soc/qcom/cpr3.c create mode 100644 include/soc/qcom/cpr.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index fe3c486ae32d..10b365f0bb8f 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -42,6 +42,23 @@ config QCOM_CPR To compile this driver as a module, choose M here: the module will be called qcom-cpr +config QCOM_CPR3 + tristate "QCOM Core Power Reduction (CPR v3/v4/Hardened) support" + depends on ARCH_QCOM && HAS_IOMEM + select PM_OPP + select REGMAP + help + Say Y here to enable support for the CPR hardware found on a broad + variety of Qualcomm SoCs like MSM8996, MSM8998, SDM630, SDM660, + SDM845 and others. + + This driver populates OPP tables and makes adjustments to them + based on feedback from the CPR hardware. If you want to do CPU + and/or GPU frequency scaling say Y here. + + To compile this driver as a module, choose M here: the module will + be called qcom-cpr3 + config QCOM_GENI_SE tristate "QCOM GENI Serial Engine Driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 778a2a5f07bb..0500283c2dc5 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_CPR) += cpr-common.o cpr.o +obj-$(CONFIG_QCOM_CPR3) += cpr-common.o cpr3.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/drivers/soc/qcom/cpr-common.c b/drivers/soc/qcom/cpr-common.c index 53d4827973cb..e5229a356329 100644 --- a/drivers/soc/qcom/cpr-common.c +++ b/drivers/soc/qcom/cpr-common.c @@ -219,6 +219,29 @@ u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid) of_node_put(np); return fc; + +} + +void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid, + s32 *open_loop, s32 *closed_loop) +{ + struct device_node *np; + + /* + * There is no of_property_read_s32_index, so we just store the + * result into a s32 variable. After all, the OF API is doing + * the exact same for of_property_read_s32... + */ + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32_index(np, "qcom,opp-oloop-vadj", tid, + open_loop)) + *open_loop = 0; + + if (of_property_read_u32_index(np, "qcom,opp-cloop-vadj", tid, + closed_loop)) + *closed_loop = 0; + + of_node_put(np); } unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, @@ -261,11 +284,10 @@ int cpr_calculate_scaling(const char *quot_offset, const struct fuse_corner_data *fdata, const struct corner *corner) { - u32 quot_diff = 0; - unsigned long freq_diff; - int scaling; + u64 freq_diff; const struct fuse_corner *fuse, *prev_fuse; - int ret; + u32 quot_diff; + int scaling, ret; fuse = corner->fuse_corner; prev_fuse = fuse - 1; @@ -282,8 +304,9 @@ int cpr_calculate_scaling(const char *quot_offset, } freq_diff = fuse->max_freq - prev_fuse->max_freq; - freq_diff /= 1000000; /* Convert to MHz */ - scaling = 1000 * quot_diff / freq_diff; + freq_diff = div_u64(freq_diff, 1000000); /* Convert to MHz */ + scaling = 1000 * quot_diff; + do_div(scaling, freq_diff); return min(scaling, fdata->max_quot_scale); } diff --git a/drivers/soc/qcom/cpr-common.h b/drivers/soc/qcom/cpr-common.h index 83a1f7c941b8..96ff6301c81e 100644 --- a/drivers/soc/qcom/cpr-common.h +++ b/drivers/soc/qcom/cpr-common.h @@ -65,6 +65,8 @@ struct corner { struct corner_data { unsigned int fuse_corner; unsigned long freq; + int oloop_vadj; + int cloop_vadj; }; struct acc_desc { @@ -101,6 +103,8 @@ int cpr_populate_fuse_common(struct device *dev, int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, struct corner *corners, int num_corners); u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid); +void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid, + s32 *open_loop, s32 *closed_loop); unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, struct device *cpu_dev); int cpr_calculate_scaling(const char *quot_offset, diff --git a/drivers/soc/qcom/cpr3.c b/drivers/soc/qcom/cpr3.c new file mode 100644 index 000000000000..7e368b39d07c --- /dev/null +++ b/drivers/soc/qcom/cpr3.c @@ -0,0 +1,2897 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019 Linaro Limited + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpr-common.h" + +#define CPR3_RO_COUNT 16 +#define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0) + +/* CPR3 registers */ +#define CPR3_REG_CPR_VERSION 0x0 +#define CPRH_CPR_VERSION_4P5 0x40050000 + +#define CPR3_REG_CPR_CTL 0x4 +#define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0) +#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1) +#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1 +#define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6) +#define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1 +#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3 +#define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9) +#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9 + +#define CPR3_REG_CPR_STATUS 0x8 +#define CPR3_CPR_STATUS_BUSY_MASK BIT(0) + +/* + * This register is not present on controllers that support HW closed-loop + * except CPR4 APSS controller. + */ +#define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC + +#define CPR3_REG_CPR_STEP_QUOT 0x14 +#define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0) +#define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0 +#define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6) +#define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6 +#define CPRH_DELTA_QUOT_STEP_FACTOR 4 + +#define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro)) +#define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor)) + +#define CPR3_REG_CONT_CMD 0x800 +#define CPR3_CONT_CMD_ACK 0x1 +#define CPR3_CONT_CMD_NACK 0x0 + +#define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread)) +#define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0) +#define CPR3_THRESH_CONS_DOWN_SHIFT 0 +#define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4) +#define CPR3_THRESH_CONS_UP_SHIFT 4 +#define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8) +#define CPR3_THRESH_DOWN_THRESH_SHIFT 8 +#define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13) +#define CPR3_THRESH_UP_THRESH_SHIFT 13 + +#define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread)) + +#define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread)) +#define CPR3_RESULT0_BUSY_MASK BIT(0) +#define CPR3_RESULT0_STEP_DN_MASK BIT(1) +#define CPR3_RESULT0_STEP_UP_MASK BIT(2) +#define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3) +#define CPR3_RESULT0_ERROR_STEPS_SHIFT 3 +#define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8) +#define CPR3_RESULT0_ERROR_SHIFT 8 + +#define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread)) +#define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0) +#define CPR3_RESULT1_QUOT_MIN_SHIFT 0 +#define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12) +#define CPR3_RESULT1_QUOT_MAX_SHIFT 12 +#define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24) +#define CPR3_RESULT1_RO_MIN_SHIFT 24 +#define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28) +#define CPR3_RESULT1_RO_MAX_SHIFT 28 + +#define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread)) +#define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0) +#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0 +#define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6) +#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6 +#define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16) +#define CPR3_RESULT2_SENSOR_MIN_SHIFT 16 +#define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24) +#define CPR3_RESULT2_SENSOR_MAX_SHIFT 24 + +#define CPR3_REG_IRQ_EN 0x81C +#define CPR3_REG_IRQ_CLEAR 0x820 +#define CPR3_REG_IRQ_STATUS 0x824 +#define CPR3_IRQ_UP BIT(3) +#define CPR3_IRQ_MID BIT(2) +#define CPR3_IRQ_DOWN BIT(1) +#define CPR3_IRQ_ALL (CPR3_IRQ_UP | CPR3_IRQ_MID | CPR3_IRQ_DOWN) + +#define CPR3_REG_TARGET_QUOT(thread, ro) (0x840 + 0x440 * (thread) + 0x4 * (ro)) + +/* Registers found only on controllers that support HW closed-loop. */ +#define CPR3_REG_PD_THROTTLE 0xE8 + +#define CPR3_REG_HW_CLOSED_LOOP_DISABLED 0x3000 +#define CPR3_REG_CPR_TIMER_MID_CONT 0x3004 +#define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008 + +/* CPR4 controller specific registers and bit definitions */ +#define CPR4_REG_CPR_TIMER_CLAMP 0x10 +#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) + +#define CPR4_REG_MISC 0x700 +#define CPR4_MISC_RESET_STEP_QUOT_LOOP_EN BIT(2) +#define CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN BIT(3) + +#define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4 +#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0) +#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0 +#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5) +#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5 + +#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8 +#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18) +#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHFT 18 + +#define CPR4_REG_MARGIN_ADJ_CTL 0x7F8 +#define CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN BIT(4) +#define CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN BIT(7) +#define CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_MASK GENMASK(16, 12) +#define CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_SHIFT 12 +#define CPR4_MARGIN_ADJ_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26) +#define CPR4_MARGIN_ADJ_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26 + +#define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread)) +#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31) +#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0) + +/* CPRh controller specific registers and bit definitions */ +#define __CPRH_REG_CORNER(rbase, tbase, tid, cnum) (rbase + (tbase * tid) + (0x4 * cnum)) +#define CPRH_REG_CORNER(d, t, c) __CPRH_REG_CORNER(d->reg_corner, d->reg_corner_tid, t, c) + +#define CPRH_CTL_OSM_ENABLED BIT(0) +#define CPRH_CTL_BASE_VOLTAGE_MASK GENMASK(10, 1) +#define CPRH_CTL_BASE_VOLTAGE_SHIFT 1 +#define CPRH_CTL_MODE_SWITCH_DELAY_MASK GENMASK(24, 17) +#define CPRH_CTL_MODE_SWITCH_DELAY_SHIFT 17 +#define CPRH_CTL_VOLTAGE_MULTIPLIER_MASK GENMASK(28, 25) +#define CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT 25 + +#define CPRH_CORNER_INIT_VOLTAGE_MASK GENMASK(7, 0) +#define CPRH_CORNER_INIT_VOLTAGE_SHIFT 0 +#define CPRH_CORNER_FLOOR_VOLTAGE_MASK GENMASK(15, 8) +#define CPRH_CORNER_FLOOR_VOLTAGE_SHIFT 8 +#define CPRH_CORNER_QUOT_DELTA_MASK GENMASK(24, 16) +#define CPRH_CORNER_QUOT_DELTA_SHIFT 16 +#define CPRH_CORNER_RO_SEL_MASK GENMASK(28, 25) +#define CPRH_CORNER_RO_SEL_SHIFT 25 +#define CPRH_CORNER_CPR_CL_DISABLE BIT(29) + +#define CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE 255 +#define CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE 255 +#define CPRH_CORNER_QUOT_DELTA_MAX_VALUE 511 + +enum cpr_type { + CTRL_TYPE_CPR3, + CTRL_TYPE_CPR4, + CTRL_TYPE_CPRH, + CTRL_TYPE_MAX, +}; + +/* + * struct cpr_thread_desc - CPR Thread-specific parameters + * + * @controller_id: Identifier of the CPR controller expected by the HW + * @ro_scaling_factor: Scaling factor for each ring oscillator entry + * @hw_tid: Identifier of the CPR thread expected by the HW + * @init_voltage_step: Voltage in uV for number of steps read from fuse array + * @init_voltage_width: Bit-width of the voltage read from the fuse array + * @sensor_range_start: First sensor ID used by a thread + * @sensor_range_end: Last sensor ID used by a thread + * @num_fuse_corners: Number of valid entries in fuse_corner_data + * @step_quot_init_min: Minimum achievable step quotient for this corner + * @step_quot_init_max: Maximum achievable step quotient for this corner + * @fuse_corner_data: Parameters for calculation of each fuse corner + */ +struct cpr_thread_desc { + u8 controller_id; + u8 hw_tid; + const int (*ro_scaling_factor)[CPR3_RO_COUNT]; + int ro_avail_corners; + int init_voltage_step; + int init_voltage_width; + u8 sensor_range_start; + u8 sensor_range_end; + u8 step_quot_init_min; + u8 step_quot_init_max; + unsigned int num_fuse_corners; + struct fuse_corner_data *fuse_corner_data; +}; + +/* + * struct cpr_desc - Driver instance-wide CPR parameters + * + * @cpr_type: Type (base version) of the CPR controller + * @num_threads: Max. number of threads supported by this controller + * @timer_delay_us: Loop delay time in uS + * @timer_updn_delay_us: Voltage after-up/before-down delay time in uS + * @timer_cons_up: Wait between consecutive up requests in uS + * @timer_cons_down: Wait between consecutive down requests in uS + * @up_threshold: Generic corner up threshold + * @down_threshold: Generic corner down threshold + * @idle_clocks: CPR Sensor: idle timer in cpr clocks unit + * @count_mode: CPR Sensor: counting mode + * @count_repeat: CPR Sensor: number of times to repeat reading + * @gcnt_us: CPR measurement interval in uS + * @vreg_step_fixed: Regulator voltage per step (if vreg unusable) + * @vreg_step_up_limit: Num. of steps up at once before re-measuring sensors + * @vreg_step_down_limit: Num. of steps dn at once before re-measuring sensors + * @vdd_settle_time_us: Settling timer to account for one VDD supply step + * @corner_settle_time_us: Settle time for corner switch request + * @mem_acc_threshold: Memory Accelerator (MEM-ACC) voltage threshold + * @apm_threshold: Array Power Mux (APM) voltage threshold + * @apm_crossover: Array Power Mux (APM) corner crossover voltage + * @apm_hysteresis: Hysteresis for APM V-threshold related calculations + * @cpr_base_voltage: Safety: Absolute minimum voltage (uV) on this CPR + * @cpr_max_voltage: Safety: Absolute maximum voltage (uV) on this CPR + * @pd_throttle_val: CPR Power Domain throttle during voltage switch + * @threads: Array containing "CPR Thread" specific parameters + * @reduce_to_fuse_uV: Reduce corner max volts (if higher) to fuse ceiling + * @reduce_to_corner_uV: Reduce corner max volts (if higher) to corner ceil. + * @hw_closed_loop_en: Enable CPR HW Closed-Loop voltage auto-adjustment + */ +struct cpr_desc { + enum cpr_type cpr_type; + unsigned int num_threads; + unsigned int timer_delay_us; + u8 timer_updn_delay_us; + u8 timer_cons_up; + u8 timer_cons_down; + u8 up_threshold; + u8 down_threshold; + u8 idle_clocks; + u8 count_mode; + u8 count_repeat; + u8 gcnt_us; + u16 vreg_step_fixed; + u8 vreg_step_up_limit; + u8 vreg_step_down_limit; + u8 vdd_settle_time_us; + u8 corner_settle_time_us; + int mem_acc_threshold; + int apm_threshold; + int apm_crossover; + int apm_hysteresis; + u32 cpr_base_voltage; + u32 cpr_max_voltage; + u32 pd_throttle_val; + + const struct cpr_thread_desc **threads; + bool reduce_to_fuse_uV; + bool reduce_to_corner_uV; + bool hw_closed_loop_en; +}; + +struct cpr_drv; +struct cpr_thread { + int num_corners; + int id; + bool enabled; + void __iomem *base; + struct clk *cpu_clk; + struct corner *corner; + struct corner *corners; + struct fuse_corner *fuse_corners; + struct cpr_drv *drv; + struct cpr_ext_data ext_data; + struct generic_pm_domain pd; + struct device *attached_cpu_dev; + struct work_struct restart_work; + bool restarting; + + const struct cpr_fuse *cpr_fuses; + const struct cpr_thread_desc *desc; +}; + +struct cpr_drv { + int irq; + unsigned int ref_clk_khz; + struct device *dev; + struct mutex lock; + struct regulator *vreg; + struct regmap *tcsr; + u32 gcnt; + u32 speed_bin; + u32 fusing_rev; + u32 last_uV; + u32 cpr_hw_rev; + u32 reg_corner; + u32 reg_corner_tid; + u32 reg_ctl; + u32 reg_status; + int fuse_level_set; + int extra_corners; + unsigned int vreg_step; + bool enabled; + + struct cpr_thread *threads; + struct genpd_onecell_data cell_data; + + const struct cpr_desc *desc; + const struct acc_desc *acc_desc; + struct dentry *debugfs; +}; + +/** + * cpr_get_ro_factor() - Get fuse corner ring oscillator factor + * + * Not all threads have different scaling factors for each + * Fuse Corner: if the RO factors are the same for all corners, + * then only one is specified, instead of uselessly repeating + * the same array for FC-times. + * This function checks for the same and gives back the right + * factor for the requested ring oscillator. + * + * Return: Ring oscillator factor + */ +static int cpr_get_ro_factor(const struct cpr_thread_desc *tdesc, + int fnum, int ro_idx) +{ + int ro_fnum; + + if (tdesc->ro_avail_corners == tdesc->num_fuse_corners) + ro_fnum = fnum; + else + ro_fnum = 0; + + return tdesc->ro_scaling_factor[ro_fnum][ro_idx]; +} + +static void cpr_write(struct cpr_thread *thread, u32 offset, u32 value) +{ + writel(value, thread->base + offset); +} + +static u32 cpr_read(struct cpr_thread *thread, u32 offset) +{ + return readl(thread->base + offset); +} + +static void +cpr_masked_write(struct cpr_thread *thread, u32 offset, u32 mask, u32 value) +{ + u32 val; + + val = readl(thread->base + offset); + val &= ~mask; + val |= value & mask; + writel(val, thread->base + offset); +} + +static void cpr_irq_clr(struct cpr_thread *thread) +{ + cpr_write(thread, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_ALL); +} + +static void cpr_irq_clr_nack(struct cpr_thread *thread) +{ + cpr_irq_clr(thread); + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK); +} + +static void cpr_irq_clr_ack(struct cpr_thread *thread) +{ + cpr_irq_clr(thread); + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_ACK); +} + +static void cpr_irq_set(struct cpr_thread *thread, u32 int_bits) +{ + /* On CPR-hardened, interrupts are managed by and on firmware */ + if (thread->drv->desc->cpr_type == CTRL_TYPE_CPRH) + return; + + cpr_write(thread, CPR3_REG_IRQ_EN, int_bits); +} + +/** + * cpr_ctl_enable() - Enable CPR thread + * @thread: Structure holding CPR thread-specific parameters + */ +static void cpr_ctl_enable(struct cpr_thread *thread) +{ + if (thread->drv->enabled && !thread->restarting) { + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, + CPR3_CPR_CTL_LOOP_EN_MASK); + } +} + +/** + * cpr_ctl_disable() - Disable CPR thread + * @thread: Structure holding CPR thread-specific parameters + */ +static void cpr_ctl_disable(struct cpr_thread *thread) +{ + const struct cpr_desc *desc = thread->drv->desc; + + if (desc->cpr_type != CTRL_TYPE_CPRH) { + cpr_irq_set(thread, 0); + cpr_irq_clr(thread); + } + + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, 0); +} + +/** + * cpr_ctl_is_enabled() - Check if thread is enabled + * @thread: Structure holding CPR thread-specific parameters + * + * Return: true if the CPR is enabled, false if it is disabled. + */ +static bool cpr_ctl_is_enabled(struct cpr_thread *thread) +{ + u32 reg_val; + + reg_val = cpr_read(thread, CPR3_REG_CPR_CTL); + return reg_val & CPR3_CPR_CTL_LOOP_EN_MASK; +} + +/** + * cpr_check_any_thread_busy() - Check if HW is done processing + * @thread: Structure holding CPR thread-specific parameters + * + * Return: true if the CPR is busy, false if it is ready. + */ +static bool cpr_check_any_thread_busy(struct cpr_thread *thread) +{ + int i; + + for (i = 0; i < thread->drv->desc->num_threads; i++) + if (cpr_read(thread, CPR3_REG_RESULT0(i)) & + CPR3_RESULT0_BUSY_MASK) + return true; + + return false; +} + +static void cpr_restart_worker(struct work_struct *work) +{ + struct cpr_thread *thread = container_of(work, struct cpr_thread, + restart_work); + struct cpr_drv *drv = thread->drv; + int i; + + mutex_lock(&drv->lock); + + thread->restarting = true; + cpr_ctl_disable(thread); + disable_irq(drv->irq); + + mutex_unlock(&drv->lock); + + for (i = 0; i < 20; i++) { + u32 cpr_status = cpr_read(thread, CPR3_REG_CPR_STATUS); + u32 ctl = cpr_read(thread, CPR3_REG_CPR_CTL); + + if ((cpr_status & CPR3_CPR_STATUS_BUSY_MASK) && + !(ctl & CPR3_CPR_CTL_LOOP_EN_MASK)) + break; + + udelay(10); + } + + cpr_irq_clr(thread); + + for (i = 0; i < 20; i++) { + u32 status = cpr_read(thread, CPR3_REG_IRQ_STATUS); + + if (!(status & CPR3_IRQ_ALL)) + break; + udelay(10); + } + + mutex_lock(&drv->lock); + + thread->restarting = false; + enable_irq(drv->irq); + cpr_ctl_enable(thread); + + mutex_unlock(&drv->lock); +} + +/** + * cpr_corner_restore() - Restore saved corner level + * @thread: Structure holding CPR thread-specific parameters + * @corner: Structure holding the saved corner level + */ +static void cpr_corner_restore(struct cpr_thread *thread, + struct corner *corner) +{ + struct cpr_drv *drv = thread->drv; + struct fuse_corner *fuse = corner->fuse_corner; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 ro_sel = fuse->ring_osc_idx; + + cpr_write(thread, CPR3_REG_GCNT(ro_sel), drv->gcnt); + + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), + CPR3_RO_MASK & ~BIT(ro_sel)); + + cpr_write(thread, CPR3_REG_TARGET_QUOT(tdesc->hw_tid, ro_sel), + fuse->quot - corner->quot_adjust); + + if (drv->desc->cpr_type == CTRL_TYPE_CPR4) { + cpr_masked_write(thread, + CPR4_REG_CPR_MASK_THREAD(tdesc->hw_tid), + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, 0); + } + + thread->corner = corner; + corner->last_uV = corner->uV; +} + +/** + * cpr_set_acc() - Set fuse level to the mem-acc + * @thread: Structure holding CPR thread-specific parameters + * @f: Fuse level + */ +static void cpr_set_acc(struct cpr_drv *drv, int f) +{ + const struct acc_desc *desc = drv->acc_desc; + struct reg_sequence *s = desc->settings; + int n = desc->num_regs_per_fuse; + + if (!s || f == drv->fuse_level_set) + return; + + regmap_multi_reg_write(drv->tcsr, s + (n * f), n); + drv->fuse_level_set = f; +} + +/** + * cpr_post_voltage() - Actions to execute before setting voltage + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * @fuse_level: Fuse corner for mem-acc, if supported. + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_pre_voltage(struct cpr_thread *thread, + enum voltage_change_dir dir, + int fuse_level) +{ + struct cpr_drv *drv = thread->drv; + + if (drv->desc->cpr_type == CTRL_TYPE_CPR3 && + drv->desc->pd_throttle_val) + cpr_write(thread, CPR3_REG_PD_THROTTLE, + drv->desc->pd_throttle_val); + + if (drv->tcsr && dir == DOWN) + cpr_set_acc(drv, fuse_level); + + return 0; +} + +/** + * cpr_post_voltage() - Actions to execute after setting voltage + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * @fuse_level: Fuse corner for mem-acc, if supported. + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_post_voltage(struct cpr_thread *thread, + enum voltage_change_dir dir, + int fuse_level) +{ + struct cpr_drv *drv = thread->drv; + + if (drv->tcsr && dir == UP) + cpr_set_acc(drv, fuse_level); + + if (drv->desc->cpr_type == CTRL_TYPE_CPR3) + cpr_write(thread, CPR3_REG_PD_THROTTLE, 0); + + return 0; +} + +/** + * cpr_commit_state() - Set the newly requested voltage + * @thread: Structure holding CPR thread-specific parameters + * + * Return: IRQ_SUCCESS for success, IRQ_NONE if the CPR is disabled. + */ +static int cpr_commit_state(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int min_uV = 0, max_uV = 0, new_uV = 0, fuse_level = 0; + enum voltage_change_dir dir; + u32 next_irqmask = 0; + int ret, i; + + /* On CPRhardened, control states are managed in firmware */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + return 0; + + for (i = 0; i < drv->desc->num_threads; i++) { + struct cpr_thread *thread = &drv->threads[i]; + + if (!thread->corner) + continue; + + fuse_level = max(fuse_level, + (int) (thread->corner->fuse_corner - + &thread->fuse_corners[0])); + + max_uV = max(max_uV, thread->corner->max_uV); + min_uV = max(min_uV, thread->corner->min_uV); + new_uV = max(new_uV, thread->corner->last_uV); + } + dev_vdbg(drv->dev, "%s: new uV: %d, last uV: %d\n", + __func__, new_uV, drv->last_uV); + + /* + * Safety measure: if the voltage is out of the globally allowed + * range, then go out and warn the user. + * This should *never* happen. + */ + if (new_uV > drv->desc->cpr_max_voltage || + new_uV < drv->desc->cpr_base_voltage) { + dev_warn(drv->dev, "Voltage (%u uV) out of range.", new_uV); + return -EINVAL; + } + + if (new_uV == drv->last_uV || fuse_level == drv->fuse_level_set) + goto out; + + if (fuse_level > drv->fuse_level_set) + dir = UP; + else + dir = DOWN; + + ret = cpr_pre_voltage(thread, fuse_level, dir); + if (ret) + return ret; + + dev_vdbg(drv->dev, "setting voltage: %d\n", new_uV); + + ret = regulator_set_voltage(drv->vreg, new_uV, new_uV); + if (ret) { + dev_err_ratelimited(drv->dev, "failed to set voltage %d: %d\n", new_uV, ret); + return ret; + } + + ret = cpr_post_voltage(thread, fuse_level, dir); + if (ret) + return ret; + + drv->last_uV = new_uV; +out: + if (new_uV > min_uV) + next_irqmask |= CPR3_IRQ_DOWN; + if (new_uV < max_uV) + next_irqmask |= CPR3_IRQ_UP; + + cpr_irq_set(thread, next_irqmask); + + return 0; +} + +static unsigned int cpr_get_cur_perf_state(struct cpr_thread *thread) +{ + return thread->corner ? thread->corner - thread->corners + 1 : 0; +} + +/** + * cpr_scale() - Calculate new voltage for the received direction + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * + * The CPR scales one by one: this function calculates the new + * voltage to set when a voltage-UP or voltage-DOWN request comes + * and stores it into the per-thread structure that gets passed. + */ +static void cpr_scale(struct cpr_thread *thread, enum voltage_change_dir dir) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 val, error_steps; + int last_uV, new_uV; + struct corner *corner; + + if (dir != UP && dir != DOWN) + return; + + corner = thread->corner; + val = cpr_read(thread, CPR3_REG_RESULT0(tdesc->hw_tid)); + error_steps = val >> CPR3_RESULT0_ERROR_STEPS_SHIFT; + error_steps &= CPR3_RESULT0_ERROR_STEPS_MASK; + + last_uV = corner->last_uV; + + if (dir == UP) { + if (!(val & CPR3_RESULT0_STEP_UP_MASK)) + return; + + /* Calculate new voltage */ + new_uV = last_uV + drv->vreg_step; + new_uV = min(new_uV, corner->max_uV); + + dev_vdbg(drv->dev, "[T%u] UP - new_uV=%d last_uV=%d p-state=%u st=%u\n", + thread->id, new_uV, last_uV, + cpr_get_cur_perf_state(thread), error_steps); + } else { + if (!(val & CPR3_RESULT0_STEP_DN_MASK)) + return; + + /* Calculate new voltage */ + new_uV = last_uV - drv->vreg_step; + new_uV = max(new_uV, corner->min_uV); + dev_vdbg(drv->dev, "[T%u] DOWN - new_uV=%d last_uV=%d p-state=%u st=%u\n", + thread->id, new_uV, last_uV, + cpr_get_cur_perf_state(thread), error_steps); + } + corner->last_uV = new_uV; +} + +/** + * cpr_irq_handler() - Handle CPR3/CPR4 status interrupts + * @irq: Number of the interrupt + * @dev: Pointer to the cpr_thread structure + * + * Handle the interrupts coming from non-hardened CPR HW as to get + * an ok to scale voltages immediately, or to pass error status to + * the hardware (either success/ACK or failure/NACK). + * + * Return: IRQ_SUCCESS for success, IRQ_NONE if the CPR is disabled. + */ +static irqreturn_t cpr_irq_handler(int irq, void *dev) +{ + struct cpr_thread *thread = dev; + struct cpr_drv *drv = thread->drv; + irqreturn_t ret = IRQ_HANDLED; + int i, rc; + enum voltage_change_dir dir = NO_CHANGE; + u32 val; + + mutex_lock(&drv->lock); + + val = cpr_read(thread, CPR3_REG_IRQ_STATUS); + + dev_vdbg(drv->dev, "IRQ_STATUS = %#02x\n", val); + + if (!cpr_ctl_is_enabled(thread)) { + dev_vdbg(drv->dev, "CPR is disabled\n"); + ret = IRQ_NONE; + } else if (cpr_check_any_thread_busy(thread)) { + cpr_irq_clr_nack(thread); + dev_dbg(drv->dev, "CPR measurement is not ready\n"); + } else { + /* + * Following sequence of handling is as per each IRQ's + * priority + */ + if (val & CPR3_IRQ_UP) + dir = UP; + else if (val & CPR3_IRQ_DOWN) + dir = DOWN; + + if (dir != NO_CHANGE) { + for (i = 0; i < drv->desc->num_threads; i++) { + thread = &drv->threads[i]; + cpr_scale(thread, dir); + } + + rc = cpr_commit_state(thread); + if (rc) + cpr_irq_clr_nack(thread); + else + cpr_irq_clr_ack(thread); + } else if (val & CPR3_IRQ_MID) { + dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n"); + } else { + dev_warn(drv->dev, "IRQ occurred for unknown flag (%#08x)\n", val); + schedule_work(&thread->restart_work); + } + } + + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr_switch(struct cpr_drv *drv) +{ + int i, ret; + bool enabled = false; + + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + return 0; + + for (i = 0; i < drv->desc->num_threads && !enabled; i++) + enabled = drv->threads[i].enabled; + + dev_vdbg(drv->dev, "%s: enabled = %d\n", __func__, enabled); + + if (enabled == drv->enabled) + return 0; + + if (enabled) { + ret = regulator_enable(drv->vreg); + if (ret) + return ret; + + drv->enabled = enabled; + + for (i = 0; i < drv->desc->num_threads; i++) + if (drv->threads[i].corner) + break; + + if (i < drv->desc->num_threads) { + cpr_irq_clr(&drv->threads[i]); + + cpr_commit_state(&drv->threads[i]); + cpr_ctl_enable(&drv->threads[i]); + } + } else { + for (i = 0; i < drv->desc->num_threads && !enabled; i++) + cpr_ctl_disable(&drv->threads[i]); + + drv->enabled = enabled; + + ret = regulator_disable(drv->vreg); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * cpr_enable() - Enables a CPR thread + * @thread: Structure holding CPR thread-specific parameters + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_enable(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int ret; + + dev_dbg(drv->dev, "Enabling thread %d\n", thread->id); + + mutex_lock(&drv->lock); + + thread->enabled = true; + ret = cpr_switch(thread->drv); + + mutex_unlock(&drv->lock); + + return ret; +} + +/** + * cpr_disable() - Disables a CPR thread + * @thread: Structure holding CPR thread-specific parameters + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_disable(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int ret; + + dev_dbg(drv->dev, "Disabling thread %d\n", thread->id); + + mutex_lock(&drv->lock); + + thread->enabled = false; + ret = cpr_switch(thread->drv); + + mutex_unlock(&drv->lock); + + return ret; +} + +/** + * cpr_configure() - Configure main HW parameters + * @thread: Structure holding CPR thread-specific parameters + * + * This function configures the main CPR hardware parameters, such as + * internal timers (and delays), sensor ownerships, activates and/or + * deactivates cpr-threads and others, as one sequence for all of the + * versions supported in this driver. By design, the function may + * return a success earlier if the sequence for "a previous version" + * has ended. + * + * Context: The CPR must be clocked before calling this function! + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_configure(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 val; + int i; + + /* Disable interrupt and CPR */ + cpr_irq_set(thread, 0); + cpr_write(thread, CPR3_REG_CPR_CTL, 0); + + /* Init and save gcnt */ + drv->gcnt = drv->ref_clk_khz * desc->gcnt_us; + do_div(drv->gcnt, 1000); + + /* Program the delay count for the timer */ + val = drv->ref_clk_khz * desc->timer_delay_us; + do_div(val, 1000); + if (desc->cpr_type == CTRL_TYPE_CPR3) { + cpr_write(thread, CPR3_REG_CPR_TIMER_MID_CONT, val); + + val = drv->ref_clk_khz * desc->timer_updn_delay_us; + do_div(val, 1000); + cpr_write(thread, CPR3_REG_CPR_TIMER_UP_DN_CONT, val); + } else { + cpr_write(thread, CPR3_REG_CPR_TIMER_AUTO_CONT, val); + } + dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val, + desc->timer_delay_us); + + /* Program the control register */ + val = desc->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT; + val |= desc->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT; + val |= desc->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT; + cpr_write(thread, CPR3_REG_CPR_CTL, val); + + /* Configure CPR default step quotients */ + val = tdesc->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT; + val |= tdesc->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT; + + cpr_write(thread, CPR3_REG_CPR_STEP_QUOT, val); + + /* + * Configure the CPR sensor ownership always on thread 0 + * TODO: SDM845 has different ownership for sensors!! + */ + for (i = tdesc->sensor_range_start; i < tdesc->sensor_range_end; i++) + cpr_write(thread, CPR3_REG_SENSOR_OWNER(i), 0); + + /* Program Consecutive Up & Down */ + val = desc->timer_cons_up << CPR3_THRESH_CONS_UP_SHIFT; + val |= desc->timer_cons_down << CPR3_THRESH_CONS_DOWN_SHIFT; + val |= desc->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT; + val |= desc->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT; + cpr_write(thread, CPR3_REG_THRESH(tdesc->hw_tid), val); + + /* Mask all ring oscillators for all threads initially */ + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), CPR3_RO_MASK); + + /* HW Closed-loop control */ + if (desc->cpr_type == CTRL_TYPE_CPR3) { + cpr_write(thread, CPR3_REG_HW_CLOSED_LOOP_DISABLED, + !desc->hw_closed_loop_en); + } else { + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN, + desc->hw_closed_loop_en ? + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN : 0); + } + + /* Additional configuration for CPR4 and beyond */ + if (desc->cpr_type < CTRL_TYPE_CPR4) + return 0; + + /* Disable threads initially only on non-hardened CPR4 */ + if (desc->cpr_type == CTRL_TYPE_CPR4) { + cpr_masked_write(thread, CPR4_REG_CPR_MASK_THREAD(1), + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); + } + + if (tdesc->hw_tid > 0) { + cpr_masked_write(thread, CPR4_REG_MISC, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN | + CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN | + CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN); + } + + val = drv->vreg_step; + do_div(val, 1000); + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_MASK, + val << CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_SHIFT); + + cpr_masked_write(thread, CPR4_REG_SAW_ERROR_STEP_LIMIT, + CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK, + desc->vreg_step_down_limit << + CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT); + + cpr_masked_write(thread, CPR4_REG_SAW_ERROR_STEP_LIMIT, + CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK, + desc->vreg_step_up_limit << + CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT); + + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN, + CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN); + + if (tdesc->hw_tid > 0) { + cpr_masked_write(thread, CPR4_REG_CPR_TIMER_CLAMP, + CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, + CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN); + } + + /* Settling timer to account for one VDD supply step */ + if (desc->vdd_settle_time_us > 0) { + u32 m = CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK; + u32 s = CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHFT; + + cpr_masked_write(thread, CPR4_REG_MARGIN_TEMP_CORE_TIMERS, + m, desc->vdd_settle_time_us << s); + } + + /* Additional configuration for CPR-hardened */ + if (desc->cpr_type < CTRL_TYPE_CPRH) + return 0; + + /* Settling timer to account for one corner-switch request */ + if (desc->corner_settle_time_us > 0) { + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_MODE_SWITCH_DELAY_MASK, + desc->corner_settle_time_us << + CPRH_CTL_MODE_SWITCH_DELAY_SHIFT); + } + + /* Base voltage and multiplier values for CPRh internal calculations */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_BASE_VOLTAGE_MASK, + (DIV_ROUND_UP(desc->cpr_base_voltage, + drv->vreg_step) << + CPRH_CTL_BASE_VOLTAGE_SHIFT)); + + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_VOLTAGE_MULTIPLIER_MASK, + DIV_ROUND_UP(drv->vreg_step, 1000) << + CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT); + + return 0; +} + + +static int cprh_dummy_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + return 0; +} + +static int cpr_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + struct corner *corner, *end; + int ret = 0; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "setting perf state: %u (prev state: %u thread: %u)\n", + state, cpr_get_cur_perf_state(thread), thread->id); + + /* + * Determine new corner we're going to. + * Remove one since lowest performance state is 1. + */ + corner = thread->corners + state - 1; + end = &thread->corners[thread->num_corners - 1]; + if (corner > end || corner < thread->corners) { + ret = -EINVAL; + goto unlock; + } + + cpr_ctl_disable(thread); + + cpr_irq_clr(thread); + if (thread->corner != corner) + cpr_corner_restore(thread, corner); + + ret = cpr_commit_state(thread); + if (ret) + goto unlock; + + cpr_ctl_enable(thread); +unlock: + mutex_unlock(&drv->lock); + + dev_dbg(drv->dev, "set perf state %u on thread %u\n", state, thread->id); + + return ret; +} + +/** + * cpr3_adjust_quot - Adjust the closed-loop quotients + * @thread: Structure holding CPR thread-specific parameters + * + * Calculates the quotient adjustment factor based on closed-loop + * quotients and ring oscillator factor. + * + * Return: Adjusted quotient + */ +static int cpr3_adjust_quot(int ring_osc_factor, int volt_closed_loop) +{ + s64 temp; + + if (ring_osc_factor == 0 || volt_closed_loop == 0) + return 0; + + temp = (s64)(ring_osc_factor * volt_closed_loop); + return (int)div_s64(temp, 1000000); +} + +/** + * cpr_fuse_corner_init() - Calculate fuse corner table + * @thread: Structure holding CPR thread-specific parameters + * + * This function populates the fuse corners table by reading the + * values from the fuses, eventually adjusting them with a fixed + * per-corner offset and doing basic checks about them being + * supported by the regulator that is assigned to this CPR - if + * it is available (on CPR-Hardened, there is no usable vreg, as + * that is protected by the hypervisor). + * + * Return: Zero for success, negative number on error + */ +static int cpr_fuse_corner_init(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_thread_desc *desc = thread->desc; + const struct cpr_fuse *cpr_fuse = thread->cpr_fuses; + struct fuse_corner_data *fdata; + struct fuse_corner *fuse, *prev_fuse, *end; + int i, ret; + + /* Populate fuse_corner members */ + fuse = thread->fuse_corners; + prev_fuse = &fuse[0]; + end = &fuse[desc->num_fuse_corners - 1]; + fdata = desc->fuse_corner_data; + + for (i = 0; fuse <= end; fuse++, cpr_fuse++, i++, fdata++) { + int factor = cpr_get_ro_factor(desc, i, fuse->ring_osc_idx); + + ret = cpr_populate_fuse_common(drv->dev, fdata, cpr_fuse, + fuse, drv->vreg_step, + desc->init_voltage_width, + desc->init_voltage_step); + if (ret) + return ret; + + /* + * Adjust the fuse quot with per-fuse-corner closed-loop + * voltage adjustment parameters. + */ + fuse->quot += cpr3_adjust_quot(factor, fdata->volt_cloop_adjust); + + /* CPRh: no regulator access... */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + goto skip_pvs_restrict; + + /* Re-check if corner voltage range is supported by regulator */ + ret = cpr_check_vreg_constraints(drv->dev, drv->vreg, fuse); + if (ret) + return ret; + +skip_pvs_restrict: + if (fuse->uV < prev_fuse->uV) + fuse->uV = prev_fuse->uV; + prev_fuse = fuse; + dev_dbg(drv->dev, "fuse corner %d: [%d %d %d] RO%hhu quot %d\n", + i, fuse->min_uV, fuse->uV, fuse->max_uV, + fuse->ring_osc_idx, fuse->quot); + + /* Check if constraints are valid */ + if (fuse->uV < fuse->min_uV || fuse->uV > fuse->max_uV) { + dev_err(drv->dev, "fuse corner %d: Bad voltage range.\n", i); + return -EINVAL; + } + } + + return 0; +} + +static void cpr3_restrict_corner(struct corner *corner, int threshold, + int hysteresis, int step) +{ + if (threshold > corner->min_uV && threshold <= corner->max_uV) { + if (corner->uV >= threshold) { + corner->min_uV = max(corner->min_uV, + threshold - hysteresis); + if (corner->min_uV > corner->uV) + corner->uV = corner->min_uV; + } else { + corner->max_uV = threshold; + corner->max_uV -= step; + } + } +} + +/* + * cprh_corner_adjust_opps() - Set voltage on each CPU OPP table entry + * + * On CPR-Hardened, the voltage level is controlled internally through + * the OSM hardware: in order to initialize the latter, we have to + * communicate the voltage to its driver, so that it will be able to + * write the right parameters (as they have to be set both on the CPRh + * and on the OSM) on it. + * This function is called only for CPRh. + * + * Return: Zero for success, negative number for error. + */ +static int cprh_corner_adjust_opps(struct cpr_thread *thread) +{ + struct corner *corner = thread->corners; + struct cpr_drv *drv = thread->drv; + int i, ret; + + for (i = 0; i < thread->num_corners; i++) { + ret = dev_pm_opp_adjust_voltage(thread->attached_cpu_dev, + corner[i].freq, + corner[i].uV, + corner[i].min_uV, + corner[i].max_uV); + if (ret) + break; + + dev_dbg(drv->dev, "OPP voltage adjusted for %lu kHz, %d uV\n", + corner[i].freq, corner[i].uV); + } + + /* If we couldn't adjust voltage for all corners, something went wrong */ + if (i < thread->num_corners) + return -EINVAL; + + return ret; +} + +/** + * cpr3_corner_init() - Calculate and set-up corners for the CPR HW + * @thread: Structure holding CPR thread-specific parameters + * + * This function calculates all the corner parameters by comparing + * and interpolating the values read from the various set-points + * read from the fuses (also called "fuse corners") to generate and + * program to the CPR a lookup table that describes each voltage + * step, mapped to a performance level (or corner number). + * + * It also programs other essential parameters on the CPR and - if + * we are dealing with CPR-Hardened, it will also enable the internal + * interface between the Operating State Manager (OSM) and the CPRh + * in order to achieve CPU DVFS. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_corner_init(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = thread->desc; + const struct cpr_fuse *fuses = thread->cpr_fuses; + int i, ret, total_corners, extra_corners, level, scaling = 0; + unsigned int fnum, fc; + const char *quot_offset; + const struct fuse_corner_data *fdata; + struct fuse_corner *fuse, *prev_fuse; + struct corner *corner, *prev_corner, *end; + struct corner_data *cdata; + struct dev_pm_opp *opp; + unsigned long freq; + u32 ring_osc_mask = CPR3_RO_MASK, min_quotient = U32_MAX; + + corner = thread->corners; + prev_corner = &thread->corners[0]; + end = &corner[thread->num_corners - 1]; + + cdata = devm_kcalloc(drv->dev, thread->num_corners + drv->extra_corners, + sizeof(struct corner_data), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + for (level = 1; level <= thread->num_corners; level++) { + opp = dev_pm_opp_find_level_exact(&thread->pd.dev, level); + if (IS_ERR(opp)) + return -EINVAL; + + /* + * If there is only one specified qcom,opp-fuse-level, then + * it is assumed that this only one is global and valid for + * all IDs, so try to get the specific one but, on failure, + * go for the global one. + */ + fc = cpr_get_fuse_corner(opp, thread->id); + if (fc == 0) { + fc = cpr_get_fuse_corner(opp, 0); + if (fc == 0) { + dev_err(drv->dev, "qcom,opp-fuse-level is missing!\n"); + dev_pm_opp_put(opp); + return -EINVAL; + } + } + fnum = fc - 1; + + freq = cpr_get_opp_hz_for_req(opp, thread->attached_cpu_dev); + if (!freq) { + thread->num_corners = max(level - 1, 0); + end = &thread->corners[thread->num_corners - 1]; + break; + } + + /* + * If any post-vadj (open/closed loop) is not specified, then + * it's zero, meaning that it is not required for this corner. + */ + cpr_get_corner_post_vadj(opp, thread->id, + &cdata[level - 1].oloop_vadj, + &cdata[level - 1].cloop_vadj); + cdata[level - 1].fuse_corner = fnum; + cdata[level - 1].freq = freq; + + fuse = &thread->fuse_corners[fnum]; + dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n", + freq, dev_pm_opp_get_level(opp) - 1, fnum); + if (freq > fuse->max_freq) + fuse->max_freq = freq; + dev_pm_opp_put(opp); + + /* + * Make sure that the frequencies in the table are in ascending + * order, as this is critical for the algorithm to work. + */ + if (cdata[level - 2].freq > freq) { + dev_err(drv->dev, "Frequency table not in ascending order.\n"); + return -EINVAL; + } + } + + if (thread->num_corners < 2) { + dev_err(drv->dev, "need at least 2 OPPs to use CPR\n"); + return -EINVAL; + } + + /* + * Get the quotient adjustment scaling factor, according to: + * + * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1)) + * / (freq(corner_N) - freq(corner_N-1)), max_factor) + * + * QUOT(corner_N): quotient read from fuse for fuse corner N + * QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1) + * freq(corner_N): max frequency in MHz supported by fuse corner N + * freq(corner_N-1): max frequency in MHz supported by fuse corner + * (N - 1) + * + * Then walk through the corners mapped to each fuse corner + * and calculate the quotient adjustment for each one using the + * following formula: + * + * quot_adjust = (freq_max - freq_corner) * scaling / 1000 + * + * freq_max: max frequency in MHz supported by the fuse corner + * freq_corner: frequency in MHz corresponding to the corner + * scaling: calculated from above equation + * + * + * + + + * | v | + * q | f c o | f c + * u | c l | c + * o | f t | f + * t | c a | c + * | c f g | c f + * | e | + * +--------------- +---------------- + * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 + * corner corner + * + * c = corner + * f = fuse corner + * + */ + for (i = 0; corner <= end; corner++, i++) { + unsigned long freq_diff_mhz; + int ro_fac, vadj, prev_quot; + + fnum = cdata[i].fuse_corner; + fdata = &tdesc->fuse_corner_data[fnum]; + quot_offset = fuses[fnum].quotient_offset; + fuse = &thread->fuse_corners[fnum]; + ring_osc_mask &= (u16)(~BIT(fuse->ring_osc_idx)); + if (fnum) + prev_fuse = &thread->fuse_corners[fnum - 1]; + else + prev_fuse = NULL; + + corner->fuse_corner = fuse; + corner->freq = cdata[i].freq; + corner->uV = fuse->uV; + + if (prev_fuse) { + if (prev_fuse->ring_osc_idx == fuse->ring_osc_idx) + quot_offset = NULL; + + scaling = cpr_calculate_scaling(quot_offset, drv->dev, + fdata, corner); + if (scaling < 0) + return scaling; + + freq_diff_mhz = fuse->max_freq - corner->freq; + do_div(freq_diff_mhz, 1000000); /* now in MHz */ + + corner->quot_adjust = scaling * freq_diff_mhz; + do_div(corner->quot_adjust, 1000); + + /* Fine-tune QUOT (closed-loop) based on fixed values */ + ro_fac = cpr_get_ro_factor(tdesc, fnum, fuse->ring_osc_idx); + vadj = cdata[i].cloop_vadj; + corner->quot_adjust -= cpr3_adjust_quot(ro_fac, vadj); + dev_vdbg(drv->dev, "Quot fine-tuning to %d for post-vadj=%d\n", + corner->quot_adjust, vadj); + + /* + * Make sure that we scale (up) monotonically. + * P.S.: Fuse quots can never be descending. + */ + prev_quot = prev_corner->fuse_corner->quot; + prev_quot -= prev_corner->quot_adjust; + if (fuse->quot - corner->quot_adjust < prev_quot) { + int new_adj = prev_corner->fuse_corner->quot; + new_adj -= fuse->quot; + dev_vdbg(drv->dev, "Monotonic increase forced: %d->%d\n", + corner->quot_adjust, new_adj); + corner->quot_adjust = new_adj; + } + + corner->uV = cpr_interpolate(corner, + drv->vreg_step, fdata); + } + /* Negative fuse quotients are nonsense. */ + if (fuse->quot < corner->quot_adjust) + return -EINVAL; + + min_quotient = min(min_quotient, + (u32)(fuse->quot - corner->quot_adjust)); + + /* Fine-tune voltages (open-loop) based on fixed values */ + corner->uV += cdata[i].oloop_vadj; + dev_dbg(drv->dev, "Voltage fine-tuning to %d for post-vadj=%d\n", + corner->uV, cdata[i].oloop_vadj); + + corner->max_uV = fuse->max_uV; + corner->min_uV = fuse->min_uV; + corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV); + dev_vdbg(drv->dev, "Clamped after interpolation: [%d %d %d]\n", + corner->min_uV, corner->uV, corner->max_uV); + + /* Make sure that we scale monotonically here, too. */ + if (corner->uV < prev_corner->uV) + corner->uV = prev_corner->uV; + + corner->last_uV = corner->uV; + + /* Reduce the ceiling voltage if needed */ + if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV) + corner->max_uV = corner->uV; + else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV) + corner->max_uV = max(corner->min_uV, fuse->uV); + + corner->min_uV = max(corner->max_uV - fdata->range_uV, + corner->min_uV); + + /* + * Adjust per-corner floor and ceiling voltages so that + * they do not overlap the memory Array Power Mux (APM) + * nor the Memory Accelerator (MEM-ACC) threshold voltages. + */ + if (desc->apm_threshold) + cpr3_restrict_corner(corner, desc->apm_threshold, + desc->apm_hysteresis, + drv->vreg_step); + if (desc->mem_acc_threshold) + cpr3_restrict_corner(corner, desc->mem_acc_threshold, + 0, drv->vreg_step); + + prev_corner = corner; + dev_dbg(drv->dev, "corner %d: [%d %d %d] scaling %d quot %d\n", i, + corner->min_uV, corner->uV, corner->max_uV, scaling, + fuse->quot - corner->quot_adjust); + } + + /* Additional setup for CPRh only */ + if (desc->cpr_type < CTRL_TYPE_CPRH) + return 0; + + /* If the OPPs can't be adjusted, programming the CPRh is useless */ + ret = cprh_corner_adjust_opps(thread); + if (ret) { + dev_err(drv->dev, "Cannot adjust CPU OPP voltages: %d\n", ret); + return ret; + } + + total_corners = thread->num_corners; + extra_corners = drv->extra_corners; + + /* If the APM extra corner exists, add it now. */ + if (desc->apm_crossover && desc->apm_threshold && extra_corners) { + /* Program the APM crossover corner on the CPR-Hardened */ + thread->corners[total_corners].uV = desc->apm_crossover; + thread->corners[total_corners].min_uV = desc->apm_crossover; + thread->corners[total_corners].max_uV = desc->apm_crossover; + thread->corners[total_corners].is_open_loop = true; + + /* + * We have calculated the APM parameters for this clock plan: + * make the APM *threshold* available to external callers. + * The crossover is used only internally in the CPR. + */ + thread->ext_data.apm_threshold_uV = desc->apm_threshold; + + dev_dbg(drv->dev, "corner %d (APM): [%d %d %d] Open-Loop\n", + total_corners, desc->apm_crossover, + desc->apm_crossover, desc->apm_crossover); + + total_corners++; + extra_corners--; + } + + if (desc->mem_acc_threshold && extra_corners) { + /* Program the Memory Accelerator threshold corner to CPRh */ + thread->corners[total_corners].uV = desc->mem_acc_threshold; + thread->corners[total_corners].min_uV = desc->mem_acc_threshold; + thread->corners[total_corners].max_uV = desc->mem_acc_threshold; + thread->corners[total_corners].is_open_loop = true; + + /* + * We have calculated a mem-acc threshold for this clock plan: + * make it available to external callers. + */ + thread->ext_data.mem_acc_threshold_uV = desc->mem_acc_threshold; + + dev_dbg(drv->dev, "corner %d (MEMACC): [%d %d %d] Open-Loop\n", + total_corners, desc->mem_acc_threshold, + desc->mem_acc_threshold, desc->mem_acc_threshold); + + total_corners++; + extra_corners--; + } + + /* + * If there are any extra corners left, it means that even though we + * expect to fill in both APM and MEM-ACC crossovers, one couldn't + * satisfy requirements, which means that the specified parameters + * are wrong: in this case, inform the user and bail out, otherwise + * if we go on writing the (invalid) table to the CPR-Hardened, the + * hardware (in this case, the CPU) will surely freeze and crash. + */ + if (unlikely(extra_corners)) { + dev_err(drv->dev, "APM/MEM-ACC corners: bad parameters.\n"); + return -EINVAL; + } + /* Reassign extra_corners, as we have to exclude delta_quot for them */ + extra_corners = drv->extra_corners; + + /* Disable the interface between OSM and CPRh */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_OSM_ENABLED, 0); + + /* Program the GCNT before unmasking ring oscillator(s) */ + for (i = 0; i < CPR3_RO_COUNT; i++) { + if (!(ring_osc_mask & BIT(i))) { + cpr_write(thread, CPR3_REG_GCNT(i), drv->gcnt); + dev_vdbg(drv->dev, "RO%d gcnt=%d\n", i, drv->gcnt); + } + } + + /* + * Unmask the ring oscillator(s) that we're going to use: it seems + * to be mandatory to do this *before* sending the rest of the + * CPRhardened specific configuration. + */ + dev_dbg(drv->dev, "Unmasking ring oscillators with mask 0x%x\n", ring_osc_mask); + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), ring_osc_mask); + + /* Setup minimum quotients for ring oscillators */ + for (i = 0; i < CPR3_RO_COUNT; i++) { + u32 tgt_quot_reg = CPR3_REG_TARGET_QUOT(tdesc->hw_tid, i); + u32 tgt_quot_val = 0; + + if (!(ring_osc_mask & BIT(i))) + tgt_quot_val = min_quotient; + + cpr_write(thread, tgt_quot_reg, tgt_quot_val); + dev_vdbg(drv->dev, "Programmed min quotient %u for Ring Oscillator %d\n", + tgt_quot_val, tgt_quot_reg); + } + + for (i = 0; i < total_corners; i++) { + int volt_oloop_steps, volt_floor_steps, delta_quot_steps; + int ring_osc; + u32 val; + + fnum = cdata[i].fuse_corner; + fuse = &thread->fuse_corners[fnum]; + + val = thread->corners[i].uV - desc->cpr_base_voltage; + volt_oloop_steps = DIV_ROUND_UP(val, drv->vreg_step); + + val = thread->corners[i].min_uV - desc->cpr_base_voltage; + volt_floor_steps = DIV_ROUND_UP(val, drv->vreg_step); + + /* + * If we are accessing corners that are not used as + * an active DCVS set-point, then always select RO 0 + * and zero out the delta quotient. + */ + if (i >= thread->num_corners) { + ring_osc = 0; + delta_quot_steps = 0; + } else { + ring_osc = fuse->ring_osc_idx; + val = fuse->quot - thread->corners[i].quot_adjust; + val -= min_quotient; + delta_quot_steps = DIV_ROUND_UP(val, + CPRH_DELTA_QUOT_STEP_FACTOR); + } + + if (volt_oloop_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE || + volt_floor_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE || + delta_quot_steps > CPRH_CORNER_QUOT_DELTA_MAX_VALUE) { + dev_err(drv->dev, "Invalid cfg: oloop=%d, floor=%d, delta=%d\n", + volt_oloop_steps, volt_floor_steps, + delta_quot_steps); + return -EINVAL; + } + /* Green light: Go, Go, Go! */ + + /* Set number of open-loop steps */ + val = volt_oloop_steps << CPRH_CORNER_INIT_VOLTAGE_SHIFT; + val &= CPRH_CORNER_INIT_VOLTAGE_MASK; + + /* Set number of floor voltage steps */ + val |= (volt_floor_steps << CPRH_CORNER_FLOOR_VOLTAGE_SHIFT) & + CPRH_CORNER_FLOOR_VOLTAGE_MASK; + + /* Set number of target quotient delta steps */ + val |= (delta_quot_steps << CPRH_CORNER_QUOT_DELTA_SHIFT) & + CPRH_CORNER_QUOT_DELTA_MASK; + + /* Select ring oscillator for this corner */ + val |= (ring_osc << CPRH_CORNER_RO_SEL_SHIFT) & + CPRH_CORNER_RO_SEL_MASK; + + /* Open loop corner is usually APM/ACC crossover */ + if (thread->corners[i].is_open_loop) { + dev_dbg(drv->dev, "Disabling Closed-Loop on corner %d\n", i); + val |= CPRH_CORNER_CPR_CL_DISABLE; + } + cpr_write(thread, CPRH_REG_CORNER(drv, tdesc->hw_tid, i), val); + + dev_dbg(drv->dev, "steps [%d]: open-loop %d, floor %d, delta_quot %d\n", + i, volt_oloop_steps, volt_floor_steps, + delta_quot_steps); + } + + /* YAY! Setup is done! Enable the internal loop to start CPR. */ + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, + CPR3_CPR_CTL_LOOP_EN_MASK); + + /* + * All the writes are going through before enabling internal + * communication between the OSM and the CPRh controllers + * because we are never using relaxed accessors, but should + * we use them, it would be critical to issue a barrier here, + * otherwise there is a high risk of hardware lockups due to + * under-voltage for the selected CPU clock. + * + * Please note that the CPR-hardened gets set-up in Linux but + * then gets actually used in firmware (and only by the OSM); + * after handing it off we will have no more control on it. + */ + + /* Enable the interface between OSM and CPRh */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_OSM_ENABLED, + CPRH_CTL_OSM_ENABLED); + + /* On success, free cdata manually */ + devm_kfree(drv->dev, cdata); + return 0; +} + +/** + * cpr3_init_parameters() - Initialize CPR global parameters + * @drv: Main driver structure + * + * Initial "integrity" checks and setup for the thread-independent parameters. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_init_parameters(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct clk *clk; + + clk = devm_clk_get(drv->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + drv->ref_clk_khz = clk_get_rate(clk); + do_div(drv->ref_clk_khz, 1000); + + /* On CPRh this clock is not always-on... */ + if (desc->cpr_type == CTRL_TYPE_CPRH) + clk_prepare_enable(clk); + else + devm_clk_put(drv->dev, clk); + + if (desc->timer_cons_up > CPR3_THRESH_CONS_UP_MASK || + desc->timer_cons_down > CPR3_THRESH_CONS_DOWN_MASK || + desc->up_threshold > CPR3_THRESH_UP_THRESH_MASK || + desc->down_threshold > CPR3_THRESH_DOWN_THRESH_MASK || + desc->idle_clocks > CPR3_CPR_CTL_IDLE_CLOCKS_MASK || + desc->count_mode > CPR3_CPR_CTL_COUNT_MODE_MASK || + desc->count_repeat > CPR3_CPR_CTL_COUNT_REPEAT_MASK) + return -EINVAL; + + /* + * Read the CPR version register only from CPR3 onwards: + * this is needed to get the additional register offsets. + * + * Note: When threaded, even if multi-controller, there + * is no chance to have different versions at the + * same time in the same domain, so it is safe to + * check this only on the first controller/thread. + */ + drv->cpr_hw_rev = cpr_read(&drv->threads[0], + CPR3_REG_CPR_VERSION); + dev_dbg(drv->dev, "CPR hardware revision: 0x%x\n", drv->cpr_hw_rev); + + if (drv->cpr_hw_rev >= CPRH_CPR_VERSION_4P5) { + drv->reg_corner = 0x3500; + drv->reg_corner_tid = 0xa0; + drv->reg_ctl = 0x3a80; + drv->reg_status = 0x3a84; + } else { + drv->reg_corner = 0x3a00; + drv->reg_corner_tid = 0; + drv->reg_ctl = 0x3aa0; + drv->reg_status = 0x3aa4; + } + + dev_dbg(drv->dev, "up threshold = %u, down threshold = %u\n", + desc->up_threshold, desc->down_threshold); + + return 0; +} + +/** + * cpr3_find_initial_corner() - Finds boot-up p-state and enables CPR + * @thread: Structure holding CPR thread-specific parameters + * + * Differently from CPRv1, from CPRv3 onwards when we successfully find + * the target boot-up performance state, we must refresh the HW + * immediately to guarantee system stability and to avoid overheating + * during the boot process, thing that would more likely happen without + * this driver doing its job. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_find_initial_corner(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + struct corner *corner; + int uV, idx; + + idx = cpr_find_initial_corner(drv->dev, thread->cpu_clk, + thread->corners, + thread->num_corners); + if (idx < 0) + return idx; + + cpr_ctl_disable(thread); + + corner = &thread->corners[idx]; + cpr_corner_restore(thread, corner); + + uV = regulator_get_voltage(drv->vreg); + uV = clamp(uV, corner->min_uV, corner->max_uV); + + corner->last_uV = uV; + if (!drv->last_uV) + drv->last_uV = uV; + + cpr_commit_state(thread); + thread->enabled = true; + cpr_switch(drv); + + return 0; +} + +static const int msm8998_gold_scaling_factor[][CPR3_RO_COUNT] = { + /* Fuse Corner 0 */ + { + 2857, 3057, 2828, 2952, 2699, 2798, 2446, 2631, + 2629, 2578, 2244, 3344, 3289, 3137, 3164, 2655 + }, + /* Fuse Corner 1 */ + { + 2857, 3057, 2828, 2952, 2699, 2798, 2446, 2631, + 2629, 2578, 2244, 3344, 3289, 3137, 3164, 2655 + }, + /* Fuse Corner 2 */ + { + 2603, 2755, 2676, 2777, 2573, 2685, 2465, 2610, + 2312, 2423, 2243, 3104, 3022, 3036, 2740, 2303 + }, + /* Fuse Corner 3 */ + { + 1901, 2016, 2096, 2228, 2034, 2161, 2077, 2188, + 1565, 1870, 1925, 2235, 2205, 2413, 1762, 1478 + } +}; + +static const int msm8998_silver_scaling_factor[][CPR3_RO_COUNT] = { + /* Fuse Corner 0 */ + { + 2595, 2794, 2577, 2762, 2471, 2674, 2199, 2553, + 3189, 3255, 3192, 2962, 3054, 2982, 2042, 2945 + }, + /* Fuse Corner 1 */ + { + 2595, 2794, 2577, 2762, 2471, 2674, 2199, 2553, + 3189, 3255, 3192, 2962, 3054, 2982, 2042, 2945 + }, + /* Fuse Corner 2 */ + { + 2391, 2550, 2483, 2638, 2382, 2564, 2259, 2555, + 2766, 3041, 2988, 2935, 2873, 2688, 2013, 2784 + }, + /* Fuse Corner 3 */ + { + 2066, 2153, 2300, 2434, 2220, 2386, 2288, 2465, + 2028, 2511, 2487, 2734, 2554, 2117, 1892, 2377 + } +}; + +static const struct cpr_thread_desc msm8998_thread_gold = { + .controller_id = 1, + .hw_tid = 0, + .ro_scaling_factor = msm8998_gold_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(msm8998_gold_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 9, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 9, + .step_quot_init_max = 14, + .num_fuse_corners = 4, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 756000, + .max_uV = 828000, + .min_uV = 568000, + .range_uV = 32000, + .volt_cloop_adjust = 0, + .volt_oloop_adjust = 8000, + .max_volt_scale = 4, + .max_quot_scale = 10, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 756000, + .max_uV = 900000, + .min_uV = 624000, + .range_uV = 32000, + .volt_cloop_adjust = 0, + .volt_oloop_adjust = 0, + .max_volt_scale = 320, + .max_quot_scale = 350, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 828000, + .max_uV = 952000, + .min_uV = 632000, + .range_uV = 32000, + .volt_cloop_adjust = 12000, + .volt_oloop_adjust = 12000, + .max_volt_scale = 620, + .max_quot_scale = 750, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 3 */ + { + .ref_uV = 1056000, + .max_uV = 1136000, + .min_uV = 772000, + .range_uV = 40000, + .volt_cloop_adjust = 50000, + .volt_oloop_adjust = 52000, + .max_volt_scale = 580, + .max_quot_scale = 1040, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_thread_desc msm8998_thread_silver = { + .controller_id = 0, + .hw_tid = 0, + .ro_scaling_factor = msm8998_silver_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(msm8998_silver_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 11, + .step_quot_init_max = 12, + .num_fuse_corners = 4, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 688000, + .max_uV = 828000, + .min_uV = 568000, + .range_uV = 32000, + .volt_cloop_adjust = 20000, + .volt_oloop_adjust = 40000, + .max_volt_scale = 4, + .max_quot_scale = 10, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 756000, + .max_uV = 900000, + .min_uV = 632000, + .range_uV = 32000, + .volt_cloop_adjust = 26000, + .volt_oloop_adjust = 24000, + .max_volt_scale = 500, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 828000, + .max_uV = 952000, + .min_uV = 664000, + .range_uV = 32000, + .volt_cloop_adjust = 12000, + .volt_oloop_adjust = 12000, + .max_volt_scale = 280, + .max_quot_scale = 650, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + + }, + /* fuse corner 3 */ + { + .ref_uV = 1056000, + .max_uV = 1056000, + .min_uV = 772000, + .range_uV = 40000, + .volt_cloop_adjust = 30000, + .volt_oloop_adjust = 30000, + .max_volt_scale = 430, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_desc msm8998_cpr_desc = { + .cpr_type = CTRL_TYPE_CPRH, + .num_threads = 2, + .mem_acc_threshold = 852000, + .apm_threshold = 800000, + .apm_crossover = 880000, + .apm_hysteresis = 0, + .cpr_base_voltage = 352000, + .cpr_max_voltage = 1200000, + .timer_delay_us = 5000, + .timer_cons_up = 0, + .timer_cons_down = 2, + .up_threshold = 2, + .down_threshold = 2, + .idle_clocks = 15, + .count_mode = CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN, + .count_repeat = 14, + .gcnt_us = 1, + .vreg_step_fixed = 4000, + .vreg_step_up_limit = 1, + .vreg_step_down_limit = 1, + .vdd_settle_time_us = 34, + .corner_settle_time_us = 6, + .reduce_to_corner_uV = true, + .hw_closed_loop_en = true, + .threads = (const struct cpr_thread_desc *[]) { + &msm8998_thread_silver, + &msm8998_thread_gold, + }, +}; + +static const struct cpr_acc_desc msm8998_cpr_acc_desc = { + .cpr_desc = &msm8998_cpr_desc, +}; + +static const int sdm630_gold_scaling_factor[][CPR3_RO_COUNT] = { + /* Same RO factors for all fuse corners */ + { + 4040, 3230, 0, 2210, 2560, 2450, 2230, 2220, + 2410, 2300, 2560, 2470, 1600, 3120, 2620, 2280 + } +}; + +static const int sdm630_silver_scaling_factor[][CPR3_RO_COUNT] = { + /* Same RO factors for all fuse corners */ + { + 3600, 3600, 3830, 2430, 2520, 2700, 1790, 1760, + 1970, 1880, 2110, 2010, 2510, 4900, 4370, 4780, + } +}; + +static const struct cpr_thread_desc sdm630_thread_gold = { + .controller_id = 0, + .hw_tid = 0, + .ro_scaling_factor = sdm630_gold_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(sdm630_gold_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 12, + .step_quot_init_max = 14, + .num_fuse_corners = 5, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 644000, + .max_uV = 724000, + .min_uV = 588000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 15000, + .max_volt_scale = 10, + .max_quot_scale = 300, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 788000, + .max_uV = 788000, + .min_uV = 652000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 5000, + .max_volt_scale = 320, + .max_quot_scale = 275, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 868000, + .max_uV = 868000, + .min_uV = 712000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 5000, + .max_volt_scale = 350, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 3 */ + { + .ref_uV = 988000, + .max_uV = 988000, + .min_uV = 784000, + .range_uV = 66000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 868, + .max_quot_scale = 980, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 4 */ + { + .ref_uV = 1068000, + .max_uV = 1068000, + .min_uV = 844000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 868, + .max_quot_scale = 980, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_thread_desc sdm630_thread_silver = { + .controller_id = 1, + .hw_tid = 0, + .ro_scaling_factor = sdm630_silver_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(sdm630_silver_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 12, + .step_quot_init_max = 14, + .num_fuse_corners = 3, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 644000, + .max_uV = 724000, + .min_uV = 588000, + .range_uV = 32000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 10, + .max_quot_scale = 360, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 788000, + .max_uV = 788000, + .min_uV = 652000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 500, + .max_quot_scale = 550, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 1068000, + .max_uV = 1068000, + .min_uV = 800000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 2370, + .max_quot_scale = 550, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_desc sdm630_cpr_desc = { + .cpr_type = CTRL_TYPE_CPRH, + .num_threads = 2, + .apm_threshold = 872000, + .apm_crossover = 872000, + .apm_hysteresis = 20000, + .cpr_base_voltage = 400000, + .cpr_max_voltage = 1300000, + .timer_delay_us = 5000, + .timer_cons_up = 0, + .timer_cons_down = 2, + .up_threshold = 2, + .down_threshold = 2, + .idle_clocks = 15, + .count_mode = CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN, + .count_repeat = 14, + .gcnt_us = 1, + .vreg_step_fixed = 4000, + .vreg_step_up_limit = 1, + .vreg_step_down_limit = 1, + .vdd_settle_time_us = 34, + .corner_settle_time_us = 5, + .reduce_to_corner_uV = true, + .hw_closed_loop_en = true, + .threads = (const struct cpr_thread_desc *[]) { + &sdm630_thread_gold, + &sdm630_thread_silver, + }, +}; + +static const struct cpr_acc_desc sdm630_cpr_acc_desc = { + .cpr_desc = &sdm630_cpr_desc, +}; + +static unsigned int cpr_get_performance_state(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int cpr_power_off(struct generic_pm_domain *domain) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + + return cpr_disable(thread); +} + +static int cpr_power_on(struct generic_pm_domain *domain) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + + return cpr_enable(thread); +} + +static void cpr_pd_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "detach callback for: %s\n", dev_name(dev)); + thread->attached_cpu_dev = NULL; + + mutex_unlock(&drv->lock); +} + +static int cpr_pd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + const struct acc_desc *acc_desc = drv->acc_desc; + bool cprh_opp_remove_table = false; + int ret = 0; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev)); + + /* + * This driver only supports scaling voltage for a CPU cluster + * where all CPUs in the cluster share a single regulator. + * Therefore, save the struct device pointer only for the first + * CPU device that gets attached. There is no need to do any + * additional initialization when further CPUs get attached. + * This is not an error condition. + */ + if (thread->attached_cpu_dev) + goto unlock; + + /* + * cpr_scale_voltage() requires the direction (if we are changing + * to a higher or lower OPP). The first time + * cpr_set_performance_state() is called, there is no previous + * performance state defined. Therefore, we call + * cpr_find_initial_corner() that gets the CPU clock frequency + * set by the bootloader, so that we can determine the direction + * the first time cpr_set_performance_state() is called. + */ + thread->cpu_clk = devm_clk_get(dev, NULL); + if (drv->desc->cpr_type < CTRL_TYPE_CPRH && IS_ERR(thread->cpu_clk)) { + ret = PTR_ERR(thread->cpu_clk); + if (ret != -EPROBE_DEFER) + dev_err(drv->dev, "could not get cpu clk: %d\n", ret); + goto unlock; + } + thread->attached_cpu_dev = dev; + + /* + * We are exporting the APM and MEM-ACC thresholds to the caller; + * while APM is necessary in the CPU CPR case, MEM-ACC may not be, + * depending on the SoC and on fuses. + * Initialize both to an invalid value, so that the caller can check + * if they got calculated or read from fuses in this driver. + */ + thread->ext_data.apm_threshold_uV = -1; + thread->ext_data.mem_acc_threshold_uV = -1; + dev_set_drvdata(thread->attached_cpu_dev, &thread->ext_data); + + dev_dbg(drv->dev, "using cpu clk from: %s\n", + dev_name(thread->attached_cpu_dev)); + + /* + * Everything related to (virtual) corners has to be initialized + * here, when attaching to the power domain, since we need to know + * the maximum frequency for each fuse corner, and this is only + * available after the cpufreq driver has attached to us. + * The reason for this is that we need to know the highest + * frequency associated with each fuse corner. + */ + ret = dev_pm_opp_get_opp_count(&thread->pd.dev); + if (ret < 0) { + dev_err(drv->dev, "could not get OPP count\n"); + thread->attached_cpu_dev = NULL; + goto unlock; + } + thread->num_corners = ret; + + thread->corners = devm_kcalloc(drv->dev, + thread->num_corners + + drv->extra_corners, + sizeof(*thread->corners), + GFP_KERNEL); + if (!thread->corners) { + ret = -ENOMEM; + goto unlock; + } + + /* + * If we are on CPR-Hardened we have to make sure that the attached + * device has a OPP table installed, as we're going to modify it here + * with our calculations based on qfprom values. + */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) { + ret = dev_pm_opp_of_add_table(dev); + if (ret && ret != -EEXIST) { + dev_err(drv->dev, "Cannot add table: %d\n", ret); + goto unlock; + } + cprh_opp_remove_table = true; + } + + ret = cpr3_corner_init(thread); + if (ret) + goto exit; + + if (drv->desc->cpr_type < CTRL_TYPE_CPRH) { + ret = cpr3_find_initial_corner(thread); + if (ret) + goto exit; + + if (acc_desc->config) + regmap_multi_reg_write(drv->tcsr, acc_desc->config, + acc_desc->num_regs_per_fuse); + + /* Enable ACC if required */ + if (acc_desc->enable_mask) + regmap_update_bits(drv->tcsr, acc_desc->enable_reg, + acc_desc->enable_mask, + acc_desc->enable_mask); + } + dev_info(drv->dev, "thread %d initialized with %u OPPs\n", + thread->id, thread->num_corners); +exit: + /* + * If we are on CPRh and we reached an error condition, we installed + * the OPP table but we haven't done any setup on it, nor we ever will. + * In order to leave a clean state, remove the table. + */ + if (ret && cprh_opp_remove_table) + dev_pm_opp_of_remove_table(thread->attached_cpu_dev); +unlock: + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr3_debug_info_show(struct seq_file *s, void *unused) +{ + u32 ro_sel, ctl, irq_status, reg, quot; + struct cpr_thread *thread = s->private; + struct corner *corner = thread->corners; + struct fuse_corner *fuse = thread->fuse_corners; + unsigned int i; + + const struct { + const char *name; + uint32_t mask; + uint8_t shift; + } result0_fields[] = { + { "busy", 1, 0 }, + { "step_dn", 1, 1 }, + { "step_up", 1, 2 }, + { "error_steps", CPR3_RESULT0_ERROR_STEPS_MASK, + CPR3_RESULT0_ERROR_STEPS_SHIFT }, + { "error", CPR3_RESULT0_ERROR_MASK, CPR3_RESULT0_ERROR_SHIFT }, + { "negative", 1, 20 }, + }, result1_fields[] = { + { "quot_min", CPR3_RESULT1_QUOT_MIN_MASK, + CPR3_RESULT1_QUOT_MIN_SHIFT }, + { "quot_max", CPR3_RESULT1_QUOT_MAX_MASK, + CPR3_RESULT1_QUOT_MAX_SHIFT }, + { "ro_min", CPR3_RESULT1_RO_MIN_MASK, + CPR3_RESULT1_RO_MIN_SHIFT }, + { "ro_max", CPR3_RESULT1_RO_MAX_MASK, + CPR3_RESULT1_RO_MAX_SHIFT }, + }, result2_fields[] = { + { "qout_step_min", CPR3_RESULT2_STEP_QUOT_MIN_MASK, + CPR3_RESULT2_STEP_QUOT_MIN_SHIFT }, + { "qout_step_max", CPR3_RESULT2_STEP_QUOT_MAX_MASK, + CPR3_RESULT2_STEP_QUOT_MAX_SHIFT }, + { "sensor_min", CPR3_RESULT2_SENSOR_MIN_MASK, + CPR3_RESULT2_SENSOR_MIN_SHIFT }, + { "sensor_max", CPR3_RESULT2_SENSOR_MAX_MASK, + CPR3_RESULT2_SENSOR_MAX_SHIFT }, + }; + + if (thread->drv->desc->cpr_type < CTRL_TYPE_CPRH) + seq_printf(s, "current_volt = %d uV\n", thread->drv->last_uV); + + irq_status = cpr_read(thread, CPR3_REG_IRQ_STATUS); + seq_printf(s, "irq_status = %#02X\n", irq_status); + + ctl = cpr_read(thread, CPR3_REG_CPR_CTL); + seq_printf(s, "cpr_ctl = %#02X\n", ctl); + + seq_printf(s, "thread %d - hw tid: %u - enabled: %d:\n", + thread->id, thread->desc->hw_tid, thread->enabled); + seq_printf(s, "%d corners, derived from %d fuse corners\n", + thread->num_corners, thread->desc->num_fuse_corners); + + for (i = 0; i < thread->num_corners; i++, corner++) + seq_printf(s, "corner %d - uV=[%d %d %d] quot=%d freq=%lu\n", + i, corner->min_uV, corner->uV, corner->max_uV, + corner->quot_adjust, corner->freq); + + for (i = 0; i < thread->desc->num_fuse_corners; i++, fuse++) + seq_printf(s, "fuse %d - uV=[%d %d %d] quot=%d freq=%lu\n", + i, fuse->min_uV, fuse->uV, fuse->max_uV, + fuse->quot, corner->freq); + + seq_printf(s, "requested voltage: %d uV\n", thread->corner->last_uV); + + ro_sel = corner->fuse_corner->ring_osc_idx; + quot = cpr_read(thread, CPR3_REG_TARGET_QUOT(i, ro_sel)); + seq_printf(s, "quot_target (%u) = %#02X\n", ro_sel, quot); + + reg = cpr_read(thread, CPR3_REG_RESULT0(i)); + seq_printf(s, "cpr_result_0 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result0_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result0_fields[i].name, + (reg >> result0_fields[i].shift) & + result0_fields[i].mask); + seq_puts(s, "]\n"); + reg = cpr_read(thread, CPR3_REG_RESULT1(i)); + seq_printf(s, "cpr_result_1 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result1_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result1_fields[i].name, + (reg >> result1_fields[i].shift) & + result1_fields[i].mask); + seq_puts(s, "]\n"); + reg = cpr_read(thread, CPR3_REG_RESULT2(i)); + seq_printf(s, "cpr_result_2 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result2_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result2_fields[i].name, + (reg >> result2_fields[i].shift) & + result2_fields[i].mask); + seq_puts(s, "]\n"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(cpr3_debug_info); + +static void cpr3_debugfs_init(struct cpr_drv *drv) +{ + int i; + + drv->debugfs = debugfs_create_dir("qcom_cpr3", NULL); + + for (i = 0; i < drv->desc->num_threads; i++) { + char buf[50]; + + snprintf(buf, sizeof(buf), "thread%d", i); + + debugfs_create_file(buf, 0444, drv->debugfs, &drv->threads[i], + &cpr3_debug_info_fops); + } +} + +/** + * cpr_thread_init() - Initialize CPR thread related parameters + * @drv: Main driver structure + * @tid: Thread ID + * + * Return: Zero for success, negative number on error + */ +static int cpr_thread_init(struct cpr_drv *drv, int tid) +{ + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = desc->threads[tid]; + struct cpr_thread *thread = &drv->threads[tid]; + int ret; + + if (tdesc->step_quot_init_min > CPR3_CPR_STEP_QUOT_MIN_MASK || + tdesc->step_quot_init_max > CPR3_CPR_STEP_QUOT_MAX_MASK) + return -EINVAL; + + thread->id = tid; + thread->drv = drv; + thread->desc = tdesc; + thread->fuse_corners = devm_kcalloc(drv->dev, + tdesc->num_fuse_corners + + drv->extra_corners, + sizeof(*thread->fuse_corners), + GFP_KERNEL); + if (!thread->fuse_corners) + return -ENOMEM; + + thread->cpr_fuses = cpr_get_fuses(drv->dev, tid, + tdesc->num_fuse_corners); + if (IS_ERR(thread->cpr_fuses)) + return PTR_ERR(thread->cpr_fuses); + + ret = cpr_populate_ring_osc_idx(thread->drv->dev, thread->fuse_corners, + thread->cpr_fuses, + tdesc->num_fuse_corners); + if (ret) + return ret; + + ret = cpr_fuse_corner_init(thread); + if (ret) + return ret; + + thread->pd.name = devm_kasprintf(drv->dev, GFP_KERNEL, + "%s_thread%d", + drv->dev->of_node->full_name, + thread->id); + if (!thread->pd.name) + return -EINVAL; + + thread->pd.power_off = cpr_power_off; + thread->pd.power_on = cpr_power_on; + thread->pd.opp_to_performance_state = cpr_get_performance_state; + thread->pd.attach_dev = cpr_pd_attach_dev; + thread->pd.detach_dev = cpr_pd_detach_dev; + + /* CPR-Hardened performance states are managed in firmware */ + if (desc->cpr_type == CTRL_TYPE_CPRH) { + thread->pd.set_performance_state = cprh_dummy_set_performance_state; + } else { + thread->pd.set_performance_state = cpr_set_performance_state; + } + + /* Anything later than CPR1 must be always-on for now */ + thread->pd.flags = GENPD_FLAG_ALWAYS_ON; + + drv->cell_data.domains[tid] = &thread->pd; + + ret = pm_genpd_init(&thread->pd, NULL, false); + if (ret) + return ret; + + /* On CPRhardened, the interrupts are managed in firmware */ + if (desc->cpr_type < CTRL_TYPE_CPRH) { + INIT_WORK(&thread->restart_work, cpr_restart_worker); + + ret = devm_request_threaded_irq(drv->dev, drv->irq, + NULL, cpr_irq_handler, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING, + "cpr", drv); + if (ret) + return ret; + } + + return 0; +} + +/** + * cpr3_resources_init() - Initialize resources used by this driver + * @pdev: Platform device + * @drv: Main driver structure + * + * Return: Zero for success, negative number on error + */ +static int cpr3_resources_init(struct platform_device *pdev, + struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct cpr_thread *threads = drv->threads; + unsigned int i; + u8 cid_mask = 0; + + /* + * Here, we are accounting for the following usecases: + * - One controller + * - One or multiple threads on the same iospace + * + * - Multiple controllers + * - Each controller has its own iospace and each + * may have one or multiple threads in their + * parent controller's iospace + * + * Then, to avoid complicating the code for no reason, + * this also needs a mandatory order in the list of + * threads which implies that all of them from the same + * controllers are specified sequentially. As an example: + * + * C0-T0, C0-T1...C0-Tn, C1-T0, C1-T1...C1-Tn + */ + for (i = 0; i < desc->num_threads; i++) { + u8 cid = desc->threads[i]->controller_id; + + if (cid_mask & BIT(cid)) { + if (desc->threads[i - 1]->controller_id != cid) { + dev_err(drv->dev, "Bad threads order. Please fix!\n"); + return -EINVAL; + } + threads[i].base = threads[i - 1].base; + continue; + } + threads[i].base = devm_platform_ioremap_resource(pdev, cid); + if (IS_ERR(threads[i].base)) + return PTR_ERR(threads[i].base); + cid_mask |= BIT(cid); + } + return 0; +} + +static int cpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cpr_drv *drv; + const struct cpr_desc *desc; + const struct cpr_acc_desc *data; + struct device_node *np; + unsigned int i; + int ret; + + data = of_device_get_match_data(dev); + if (!data || !data->cpr_desc) + return -EINVAL; + + desc = data->cpr_desc; + + /* CPRh disallows MEM-ACC access from the HLOS */ + if (!data->acc_desc && desc->cpr_type < CTRL_TYPE_CPRH) + return -EINVAL; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->dev = dev; + drv->desc = desc; + drv->threads = devm_kcalloc(dev, desc->num_threads, + sizeof(*drv->threads), GFP_KERNEL); + if (!drv->threads) + return -ENOMEM; + + drv->cell_data.num_domains = desc->num_threads; + drv->cell_data.domains = devm_kcalloc(drv->dev, + drv->cell_data.num_domains, + sizeof(*drv->cell_data.domains), + GFP_KERNEL); + if (!drv->cell_data.domains) + return -ENOMEM; + + if (data->acc_desc) + drv->acc_desc = data->acc_desc; + + mutex_init(&drv->lock); + + if (desc->cpr_type < CTRL_TYPE_CPRH) { + np = of_parse_phandle(dev->of_node, "acc-syscon", 0); + if (!np) + return -ENODEV; + + drv->tcsr = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(drv->tcsr)) + return PTR_ERR(drv->tcsr); + } + + ret = cpr3_resources_init(pdev, drv); + if (ret) + return ret; + + drv->irq = platform_get_irq_optional(pdev, 0); + if ((desc->cpr_type != CTRL_TYPE_CPRH) && (drv->irq < 0)) + return -EINVAL; + + /* On CPRhardened, vreg access it not allowed */ + drv->vreg = devm_regulator_get_optional(dev, "vdd"); + if (desc->cpr_type != CTRL_TYPE_CPRH && IS_ERR(drv->vreg)) + return PTR_ERR(drv->vreg); + + /* + * On at least CPRhardened, vreg is unaccessible and there is no + * way to read linear step from that regulator, hence it is hardcoded + * in the driver; + * When the vreg_step is not declared in the cpr data (or is zero), + * then having access to the vreg regulator is mandatory, as this + * will be retrieved through the regulator API. + */ + if (desc->vreg_step_fixed) + drv->vreg_step = desc->vreg_step_fixed; + else + drv->vreg_step = regulator_get_linear_step(drv->vreg); + + if (!drv->vreg_step) + return -EINVAL; + + /* + * Initialize fuse corners, since it simply depends + * on data in efuses. + * Everything related to (virtual) corners has to be + * initialized after attaching to the power domain, + * since it depends on the CPU's OPP table. + */ + ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &drv->fusing_rev); + if (ret) + return ret; + + ret = nvmem_cell_read_variable_le_u32(dev, "cpr_speed_bin", &drv->speed_bin); + if (ret) + return ret; + + /* + * Some SoCs require extra corners for MEM-ACC or APM: if + * the related parameters have been specified, then reserve + * a corner for the APM and/or MEM-ACC crossover, used by + * OSM and CPRh HW to set the supply voltage during the APM + * and/or MEM-ACC switch routine. + */ + if (desc->cpr_type == CTRL_TYPE_CPRH) { + if (desc->apm_crossover && desc->apm_hysteresis >= 0) + drv->extra_corners++; + + if (desc->mem_acc_threshold) + drv->extra_corners++; + } + + /* Initialize all threads */ + for (i = 0; i < desc->num_threads; i++) { + ret = cpr_thread_init(drv, i); + if (ret) + return ret; + } + + /* Initialize global parameters */ + ret = cpr3_init_parameters(drv); + if (ret) + return ret; + + /* Write initial configuration on all threads */ + for (i = 0; i < desc->num_threads; i++) { + ret = cpr_configure(&drv->threads[i]); + if (ret) + return ret; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, &drv->cell_data); + if (ret) + return ret; + + platform_set_drvdata(pdev, drv); + cpr3_debugfs_init(drv); + + return 0; +} + +static int cpr_remove(struct platform_device *pdev) +{ + struct cpr_drv *drv = platform_get_drvdata(pdev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; i < drv->desc->num_threads; i++) { + cpr_ctl_disable(&drv->threads[i]); + cpr_irq_set(&drv->threads[i], 0); + pm_genpd_remove(&drv->threads[i].pd); + } + + debugfs_remove_recursive(drv->debugfs); + + return 0; +} + +static const struct of_device_id cpr3_match_table[] = { + { .compatible = "qcom,msm8998-cprh", .data = &msm8998_cpr_acc_desc }, + { .compatible = "qcom,sdm630-cprh", .data = &sdm630_cpr_acc_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, cpr3_match_table); + +static struct platform_driver cpr3_driver = { + .probe = cpr_probe, + .remove = cpr_remove, + .driver = { + .name = "qcom-cpr3", + .of_match_table = cpr3_match_table, + }, +}; +module_platform_driver(cpr3_driver) + +MODULE_DESCRIPTION("Core Power Reduction (CPR) v3/v4 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/qcom/cpr.h b/include/soc/qcom/cpr.h new file mode 100644 index 000000000000..2ba4324d18f6 --- /dev/null +++ b/include/soc/qcom/cpr.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019 Linaro Limited + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +#ifndef __CPR_H__ +#define __CPR_H__ + +struct cpr_ext_data { + int mem_acc_threshold_uV; + int apm_threshold_uV; +}; + +#endif /* __CPR_H__ */ From acf7cceaf4748a2502e6470729d80ac682f073a8 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 20:40:26 +0100 Subject: [PATCH 075/212] MAINTAINERS: Add entry for Qualcomm CPRv3/v4/Hardened driver Add maintainers entry for the Qualcomm CPR3/CPR4/CPRh driver. Signed-off-by: AngeloGioacchino Del Regno --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 406be2428e35..de57f8a850e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15493,6 +15493,12 @@ S: Maintained F: Documentation/devicetree/bindings/soc/qcom/qcom,cpr.yaml F: drivers/soc/qcom/cpr.c +QUALCOMM CORE POWER REDUCTION v3/v4/Hardened AVS DRIVER +M: AngeloGioacchino Del Regno +S: Maintained +F: Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml +F: drivers/soc/qcom/cpr3.c + QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin L: linux-pm@vger.kernel.org From 0670a851095ed96e6aa9191d5ee75b711578d067 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 20:31:49 +0100 Subject: [PATCH 076/212] dt-bindings: soc: qcom: cpr3: Add bindings for CPR3 driver Add the bindings for the CPR3 driver to the documentation. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Rob Herring --- .../bindings/soc/qcom/qcom,cpr3.yaml | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml new file mode 100644 index 000000000000..e2753740c86b --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/qcom/qcom,cpr3.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Core Power Reduction v3/v4/Hardened (CPR3, CPR4, CPRh) + +description: | + CPR (Core Power Reduction) is a technology to reduce core power on a CPU + or other device. Each OPP of a device corresponds to a "corner" that has + a range of valid voltages for a particular frequency. While the device is + running at a particular frequency, CPR monitors dynamic factors such as + temperature, etc. and suggests or, in the CPR-Hardened case performs, + adjustments to the voltage to save power and meet silicon characteristic + requirements. + +maintainers: + - AngeloGioacchino Del Regno + +properties: + compatible: + oneOf: + - description: CPRv3 controller + items: + - const: qcom,cpr3 + - description: CPRv4 controller + items: + - const: qcom,cpr4 + - description: CPRv4-Hardened controller + items: + - enum: + - qcom,msm8998-cprh + - qcom,sdm630-cprh + - const: qcom,cprh + + reg: + description: Base address and size of the CPR controller(s) + minItems: 1 + maxItems: 2 + + interrupts: + maxItems: 1 + + clock-names: + items: + - const: "ref" + + clocks: + items: + - description: CPR reference clock + + vdd-supply: + description: Autonomous Phase Control (APC) or other power supply + + '#power-domain-cells': + const: 1 + + acc-syscon: + description: phandle to syscon for writing ACC settings + + nvmem-cells: + description: Cells containing the fuse corners and revision data + minItems: 10 + maxItems: 32 + + nvmem-cell-names: + minItems: 10 + maxItems: 32 + + operating-points-v2: true + +required: + - compatible + - reg + - clock-names + - clocks + - "#power-domain-cells" + - nvmem-cells + - nvmem-cell-names + - operating-points-v2 + +additionalProperties: false + +examples: + - | + #include + #include + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu@0 { + compatible = "qcom,kryo280"; + device_type = "cpu"; + reg = <0x0 0x0>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + }; + + cpu@100 { + compatible = "qcom,kryo280"; + device_type = "cpu"; + reg = <0x0 0x0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + }; + }; + + cpu_silver_opp_table: cpu-silver-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1843200000 { + opp-hz = /bits/ 64 <1843200000>; + required-opps = <&cprh_opp3>; + }; + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cprh_opp2>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + }; + }; + + cpu_gold_opp_table: cpu-gold-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + required-opps = <&cprh_opp3>; + }; + opp-1113600000 { + opp-hz = /bits/ 64 <1113600000>; + required-opps = <&cprh_opp2>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + }; + }; + + cprh_opp_table: cpr-hardened-opp-table { + compatible = "operating-points-v2-qcom-level"; + + cprh_opp1: opp1 { + opp-level = <1>; + qcom,opp-fuse-level = <1>; + }; + cprh_opp2: opp2 { + opp-level = <2>; + qcom,opp-fuse-level = <2>; + }; + cprh_opp3: opp3 { + opp-level = <3>; + qcom,opp-fuse-level = <2 3>; + }; + }; + + apc_cprh: power-controller@179c8000 { + compatible = "qcom,msm8998-cprh", "qcom,cprh"; + reg = <0x0179c8000 0x4000>, <0x0179c4000 0x4000>; + clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + clock-names = "ref"; + + #power-domain-cells = <1>; + operating-points-v2 = <&cprh_opp_table>; + + nvmem-cells = <&cpr_efuse_speedbin>, + <&cpr_fuse_revision>, + <&cpr_quot0_pwrcl>, + <&cpr_quot1_pwrcl>, + <&cpr_quot2_pwrcl>, + <&cpr_quot3_pwrcl>, + <&cpr_quot_offset1_pwrcl>, + <&cpr_quot_offset2_pwrcl>, + <&cpr_quot_offset3_pwrcl>, + <&cpr_init_voltage0_pwrcl>, + <&cpr_init_voltage1_pwrcl>, + <&cpr_init_voltage2_pwrcl>, + <&cpr_init_voltage3_pwrcl>, + <&cpr_ro_sel0_pwrcl>, + <&cpr_ro_sel1_pwrcl>, + <&cpr_ro_sel2_pwrcl>, + <&cpr_ro_sel3_pwrcl>, + <&cpr_quot0_perfcl>, + <&cpr_quot1_perfcl>, + <&cpr_quot2_perfcl>, + <&cpr_quot3_perfcl>, + <&cpr_quot_offset1_perfcl>, + <&cpr_quot_offset2_perfcl>, + <&cpr_quot_offset3_perfcl>, + <&cpr_init_voltage0_perfcl>, + <&cpr_init_voltage1_perfcl>, + <&cpr_init_voltage2_perfcl>, + <&cpr_init_voltage3_perfcl>, + <&cpr_ro_sel0_perfcl>, + <&cpr_ro_sel1_perfcl>, + <&cpr_ro_sel2_perfcl>, + <&cpr_ro_sel3_perfcl>; + + nvmem-cell-names = "cpr_speed_bin", + "cpr_fuse_revision", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_quotient4", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_quotient_offset4", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_init_voltage4", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_ring_osc4", + "cpr1_quotient1", + "cpr1_quotient2", + "cpr1_quotient3", + "cpr1_quotient4", + "cpr1_quotient_offset2", + "cpr1_quotient_offset3", + "cpr1_quotient_offset4", + "cpr1_init_voltage1", + "cpr1_init_voltage2", + "cpr1_init_voltage3", + "cpr1_init_voltage4", + "cpr1_ring_osc1", + "cpr1_ring_osc2", + "cpr1_ring_osc3", + "cpr1_ring_osc4"; + }; +... From 636e73b34ef77ddcbe477100c26897eeb1cdc09c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 7 Aug 2021 16:16:28 +0200 Subject: [PATCH 077/212] arm64: dts: sdm630: Assign 300MHz frequency to HMSS GPLL0 clock At cpufreq_hw initialization, we need to assign a frequency of 300MHz to the HMSS GPLL0 clock in order to achieve the recommended low sleep frequency (and low voltage) for both the gold and silver clusters. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 61aa14eab697..f43aa2335195 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1115,6 +1115,9 @@ reg-names = "osm-domain0", "freq-domain0", "osm-domain1", "freq-domain1"; + assigned-clocks = <&gcc HMSS_GPLL0_CLK_SRC>; + assigned-clock-rates = <300000000>; + clocks = <&rpmcc RPM_SMD_XO_A_CLK_SRC>, <&gcc HMSS_GPLL0_CLK_SRC>; clock-names = "xo", "alternate"; From 85a24731ff099ee1f8ce8ce22213cf4a8abbedc4 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 17:53:51 +0100 Subject: [PATCH 078/212] arm64: dts: qcom: Introduce support for MSM8998 Sony Yoshino platform This commit introduces support for the Sony Yoshino platform, using the MSM8998 SoC, including: - Sony Xperia XZ1 (codename Poplar), - Sony Xperia XZ1 Compact (codename Lilac), - Sony Xperia XZ Premium (codename Maple). All of the three aforementioned smartphones are sharing a 99% equal board configuration, with very small differences between each other, which is the reason for the introduction of a common msm8998-sony-xperia-yoshino DT. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/Makefile | 3 + .../msm8998-sony-xperia-yoshino-lilac.dts | 30 + .../msm8998-sony-xperia-yoshino-maple.dts | 102 +++ .../msm8998-sony-xperia-yoshino-poplar.dts | 47 + .../dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 823 ++++++++++++++++++ 5 files changed, 1005 insertions(+) create mode 100644 arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts create mode 100644 arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts create mode 100644 arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts create mode 100644 arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 8e06f17d7ee0..3241d7550b4d 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -39,6 +39,9 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8998-lenovo-miix-630.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-oneplus-cheeseburger.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-oneplus-dumpling.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-lilac.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-maple.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-poplar.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-1000.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-4000.dtb dtb-$(CONFIG_ARCH_QCOM) += qrb5165-rb5.dtb diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts new file mode 100644 index 000000000000..0de919357de4 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +/dts-v1/; + +#include "msm8998-sony-xperia-yoshino.dtsi" + +/ { + model = "Sony Xperia XZ1 Compact"; + compatible = "sony,xperia-lilac", "qcom,msm8998"; +}; + +&ibb { + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; +}; + +&lab { + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + qcom,soft-start-us = <800>; +}; + +&vreg_l22a_2p85 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts new file mode 100644 index 000000000000..90b3bee08751 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +/dts-v1/; + +#include "msm8998-sony-xperia-yoshino.dtsi" + +/ { + model = "Sony Xperia XZ Premium"; + compatible = "sony,xperia-maple", "qcom,msm8998"; + + disp_dvdd_vreg: disp-dvdd-vreg { + compatible = "regulator-fixed"; + regulator-name = "disp_dvdd_en"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + startup-delay-us = <0>; + enable-active-high; + gpio = <&pmi8998_gpio 10 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&disp_dvdd_en>; + }; +}; + +&dsi0 { + qcom,dual-dsi-mode; + qcom,master-dsi; +}; + +&dsi1 { + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + qcom,dual-dsi-mode; + status = "ok"; +}; + +&dsi1_out { + remote-endpoint = <&panel_in1>; + data-lanes = <0 1 2 3>; +}; + +&dsi1_phy { + vdds-supply = <&vreg_l1a_0p875>; + status = "ok"; +}; + +&ibb { + regulator-min-microvolt = <5600000>; + regulator-max-microvolt = <5600000>; +}; + +&lab { + regulator-min-microvolt = <5800000>; + regulator-max-microvolt = <5800000>; + qcom,soft-start-us = <200>; +}; + +&panel { + compatible = "sharp,ls055d1sx04"; + + dvdd-supply = <&disp_dvdd_vreg>; + ports { + port@1 { + reg = <1>; + panel_in1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; +}; + +&pm8005_gpio { + ear_en_default: ear-en-active { + pins = "gpio1"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <1>; + }; +}; + +&pmi8998_gpio { + disp_dvdd_en: disp-dvdd-en-active { + pins = "gpio10"; + function = PMIC_GPIO_FUNC_NORMAL; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <0>; + output-high; + }; +}; + +&vreg_l22a_2p85 { + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2704000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts new file mode 100644 index 000000000000..c3095acb10e2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +/dts-v1/; + +#include "msm8998-sony-xperia-yoshino.dtsi" + +/ { + model = "Sony Xperia XZ1"; + compatible = "sony,xperia-poplar", "qcom,msm8998"; +}; + +&ibb { + regulator-min-microvolt = <5600000>; + regulator-max-microvolt = <5600000>; +}; + +&lab { + regulator-min-microvolt = <5600000>; + regulator-max-microvolt = <5600000>; + qcom,soft-start-us = <800>; +}; + +&pm8005_gpio { + ear_en_default: ear-en-active { + pins = "gpio1"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <1>; + }; +}; + +&vreg_l18a_2p85 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; +}; + +&vreg_l22a_2p85 { + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi new file mode 100644 index 000000000000..ea4fec550919 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + * Copyright (c) 2021, Konrad Dybcio + */ + +#include "msm8998.dtsi" +#include "pm8005.dtsi" +#include "pm8998.dtsi" +#include "pmi8998.dtsi" +#include +#include +#include + +/ { + /* required for bootloader to select correct board */ + qcom,msm-id = <0x124 0x20000>, <0x124 0x20001>; /* 8998v2, v2.1 */ + qcom,board-id = <8 0>; + + chosen { + bootargs = "clk_ignore_unused"; + }; + + board_vbat: vbat-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT"; + + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <4000000>; + regulator-always-on; + regulator-boot-on; + }; + + cam0_vdig_vreg: cam0-vdig { + compatible = "regulator-fixed"; + regulator-name = "cam0_vdig"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 21 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam0_vdig_default>; + }; + + cam1_vdig_vreg: cam1-vdig { + compatible = "regulator-fixed"; + regulator-name = "cam1_vdig"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 25 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam1_vdig_default>; + vin-supply = <&vreg_s3a_1p35>; + }; + + cam_vio_vreg: cam-vio-vreg { + compatible = "regulator-fixed"; + regulator-name = "cam_vio_vreg"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&pmi8998_gpio 1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_vio_default>; + vin-supply = <&vreg_lvs1a_1p8>; + }; + + touch_vddio_vreg: touch-vddio-vreg { + compatible = "regulator-fixed"; + regulator-name = "touch_vddio_vreg"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 133 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&ts_vddio_en>; + }; + + /* The gpio-vibrator driver enforces requiring a regulator */ + vib_vreg: vib-regulator { + compatible = "regulator-fixed"; + regulator-name = "vib"; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + regulator-always-on; + regulator-boot-on; + }; + + gpio_keys { + compatible = "gpio-keys"; + input-name = "gpio-keys"; + label = "Side buttons"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&vol_down_pin_a>, <&cam_focus_pin_a>, + <&cam_snapshot_pin_a>; + vol-down { + label = "Volume Down"; + gpios = <&pm8998_gpio 5 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + }; + + camera-snapshot { + label = "Camera Snapshot"; + gpios = <&pm8998_gpio 7 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + debounce-interval = <15>; + }; + + camera-focus { + label = "Camera Focus"; + gpios = <&pm8998_gpio 8 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + debounce-interval = <15>; + }; + }; + + gpio_hall_sensor { + compatible = "gpio-keys"; + input-name = "hall-sensors"; + label = "Hall sensors"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&hall_sensor0_default>; + + hall_sensor0 { + label = "Cover Hall Sensor"; + gpios = <&tlmm 124 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <30>; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hyp_mem: memory@85800000 { + reg = <0x0 0x85800000 0x0 0x3700000>; + no-map; + }; + + cont_splash_mem: cont-splash-region@9d400000 { + reg = <0x0 0x9d400000 0x0 0x2400000>; + no-map; + }; + + zap_shader_region: gpu@f6400000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xf6400000 0x0 0x2000>; + no-map; + }; + + adsp_region: memory@fe000000 { + reg = <0x0 0xfe000000 0x0 0x800000>; + no-map; + }; + + qseecom_region: memory@fe800000 { + reg = <0x0 0xfe800000 0x0 0x1400000>; + no-map; + }; + + ramoops@ffc00000 { + compatible = "ramoops"; + reg = <0x0 0xffc00000 0x0 0x100000>; + record-size = <0x10000>; + console-size = <0x60000>; + ftrace-size = <0x10000>; + pmsg-size = <0x20000>; + ecc-size = <16>; + }; + }; + + vibrator { + compatible = "gpio-vibrator"; + enable-gpios = <&pmi8998_gpio 5 GPIO_ACTIVE_HIGH>; + vcc-supply = <&vib_vreg>; + pinctrl-names = "default"; + pinctrl-0 = <&vib_default>; + }; +}; + +&adreno_gpu { + status = "ok"; + + zap-shader { + memory-region = <&zap_shader_region>; + }; +}; + +&adreno_smmu { + status = "ok"; +}; + +&apc_cprh { + status = "ok"; +}; + +&blsp1_i2c5 { + status = "okay"; + clock-frequency = <355000>; + + touchscreen: synaptics-rmi4-i2c@2c { + compatible = "syna,rmi4-i2c"; + reg = <0x2c>; + #address-cells = <1>; + #size-cells = <0>; + interrupts-extended = <&tlmm 125 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-names = "default"; + pinctrl-0 = <&ts_int_n>; + + syna,reset-delay-ms = <220>; + syna,startup-delay-ms = <1000>; + + rmi4-f01@1 { + reg = <0x01>; + syna,nosleep-mode = <1>; + }; + + rmi4-f11@11 { + reg = <0x11>; + syna,sensor-type = <1>; + }; + }; +}; + +&blsp1_i2c5_sleep { + bias-disable; +}; + +&blsp2_i2c1 { + tof-sensor@29 { + compatible = "st,vl53l0x"; + reg = <0x29>; + interrupt-parent = <&tlmm>; + interrupts = <23 IRQ_TYPE_EDGE_FALLING>; + #io-channel-cells = <1>; + label = "back_camera_tof"; + pinctrl-names = "default"; + pinctrl-0 = <&tof_int_n &tof_rst_n>; + }; +}; + +&blsp2_uart1 { + status = "okay"; +}; + +&blsp1_uart3 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn3990-bt"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + max-speed = <3200000>; + + clocks = <&rpmcc RPM_SMD_RF_CLK2_PIN>; + }; +}; + +&cpufreq_hw { + status = "ok"; +}; + +&dsi0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + + panel: panel@0 { + reg = <0>; + + backlight = <&pmi8998_wled>; + disp-te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>; + reset-gpios = <&tlmm 94 GPIO_ACTIVE_HIGH>; + + avdd-supply = <&lab>; + avee-supply = <&ibb>; + vddio-supply = <&vreg_l14a_1p85>; + tavdd-supply = <&vreg_l28_3p0>; + tvddio-supply = <&touch_vddio_vreg>; + + pinctrl-names = "default"; + pinctrl-0 = <&panel_reset_n &mdp_vsync_n>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; +}; + +&dsi0_out { + remote-endpoint = <&panel_in0>; + data-lanes = <0 1 2 3>; +}; + +&dsi0_phy { + status = "okay"; + vdds-supply = <&vreg_l1a_0p875>; +}; + +&ibb { + regulator-min-microamp = <800000>; + regulator-max-microamp = <800000>; + regulator-enable-ramp-delay = <200>; + regulator-over-current-protection; + regulator-pull-down; + regulator-ramp-delay = <1>; + regulator-settling-time-up-us = <600>; + regulator-settling-time-down-us = <1000>; + regulator-soft-start; + qcom,discharge-resistor-kohms = <300>; +}; + +&lab { + regulator-min-microamp = <200000>; + regulator-max-microamp = <200000>; + regulator-enable-ramp-delay = <500>; + regulator-over-current-protection; + regulator-pull-down; + regulator-ramp-delay = <1>; + regulator-settling-time-up-us = <50000>; + regulator-settling-time-down-us = <3000>; + regulator-soft-start; +}; + +&mdss { + status = "okay"; +}; + +&pm8005_lsid1 { + pm8005-regulators { + compatible = "qcom,pm8005-regulators"; + + vdd_s1-supply = <&vph_pwr>; + + pm8005_s1: s1 { /* VDD_GFX supply */ + /* + * HACK: Set enough voltage for max GPU frequency + * and set the regulator always on until the + * GPU Core Power Reduction gets available + */ + regulator-min-microvolt = <988000>; + regulator-max-microvolt = <1088000>; + regulator-enable-ramp-delay = <500>; + + regulator-always-on; + }; + }; +}; + +&pm8998_gpio { + vol_down_pin_a: vol-down-active { + pins = "gpio5"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + cam_focus_pin_a: cam-focus-btn-active { + pins = "gpio7"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + cam_snapshot_pin_a: cam-snapshot-btn-active { + pins = "gpio8"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + nfc_clk_req_pin: nfc-clk-req-active { + pins = "gpio21"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + power-source = <1>; + }; +}; + +&pmi8998_gpio { + cam_vio_default: cam-vio-active { + pins = "gpio1"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <1>; + }; + + vib_default: vib-en { + pins = "gpio5"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <0>; + }; +}; + +&pm8998_pon { + resin { + compatible = "pm8941-resin"; + interrupts = ; + debounce = <15625>; + bias-pull-up; + linux,code = ; + }; +}; + +&pmi8998_wled { + status = "okay"; + + default-brightness = <3000>; + qcom,switching-freq = <800>; + qcom,ovp-millivolt = <29600>; + qcom,current-boost-limit = <970>; + qcom,current-limit-microamp = <25000>; + qcom,num-strings = <2>; + qcom,enabled-strings = <0 1>; +}; + +&qusb2phy { + status = "okay"; + + vdda-pll-supply = <&vreg_l12a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; +}; + +&remoteproc_adsp { + firmware-name = "adsp.mdt"; +}; + +&remoteproc_mss { + status = "disabled"; +}; + +&remoteproc_slpi { + firmware-name = "slpi.mdt"; +}; + +&rpm_requests { + pm8998-regulators { + compatible = "qcom,rpm-pm8998-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + vdd_s7-supply = <&vph_pwr>; + vdd_s8-supply = <&vph_pwr>; + vdd_s9-supply = <&vph_pwr>; + vdd_s10-supply = <&vph_pwr>; + vdd_s11-supply = <&vph_pwr>; + vdd_s12-supply = <&vph_pwr>; + vdd_s13-supply = <&vph_pwr>; + vdd_l1_l27-supply = <&vreg_s7a_1p025>; + vdd_l2_l8_l17-supply = <&vreg_s3a_1p35>; + vdd_l3_l11-supply = <&vreg_s7a_1p025>; + vdd_l4_l5-supply = <&vreg_s7a_1p025>; + vdd_l6-supply = <&vreg_s5a_2p04>; + vdd_l7_l12_l14_l15-supply = <&vreg_s5a_2p04>; + vdd_l9-supply = <&vreg_bob>; + vdd_l10_l23_l25-supply = <&vreg_bob>; + vdd_l13_l19_l21-supply = <&vreg_bob>; + vdd_l16_l28-supply = <&vreg_bob>; + vdd_l18_l22-supply = <&vreg_bob>; + vdd_l20_l24-supply = <&vreg_bob>; + vdd_l26-supply = <&vreg_s3a_1p35>; + vdd_lvs1_lvs2-supply = <&vreg_s4a_1p8>; + + vreg_s3a_1p35: s3 { + regulator-min-microvolt = <1352000>; + regulator-max-microvolt = <1352000>; + }; + vreg_s4a_1p8: s4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-allow-set-load; + regulator-system-load = <100000>; + }; + vreg_s5a_2p04: s5 { + regulator-min-microvolt = <1904000>; + regulator-max-microvolt = <2032000>; + }; + vreg_s7a_1p025: s7 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1028000>; + }; + vreg_l1a_0p875: l1 { + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-system-load = <73400>; + regulator-allow-set-load; + }; + vreg_l2a_1p2: l2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-system-load = <12560>; + regulator-allow-set-load; + }; + vreg_l3a_1p0: l3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + vreg_l5a_0p8: l5 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + }; + vreg_l6a_1p8: l6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l7a_1p8: l7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l8a_1p2: l8 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + vreg_l9a_1p8: l9 { + regulator-min-microvolt = <1808000>; + regulator-max-microvolt = <2960000>; + }; + vreg_l10a_1p8: l10 { + regulator-min-microvolt = <1808000>; + regulator-max-microvolt = <2960000>; + }; + vreg_l11a_1p0: l11 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + vreg_l12a_1p8: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l13a_2p95: l13 { + regulator-min-microvolt = <1808000>; + regulator-max-microvolt = <2960000>; + regulator-allow-set-load; + }; + vreg_l14a_1p85: l14 { + regulator-min-microvolt = <1848000>; + regulator-max-microvolt = <1856000>; + regulator-system-load = <32000>; + regulator-allow-set-load; + }; + vreg_l15a_1p8: l15 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l16a_2p7: l16 { + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2704000>; + }; + vreg_l17a_1p3: l17 { + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1304000>; + }; + vreg_l18a_2p85: l18 {}; + vreg_l19a_2p7: l19 { + regulator-min-microvolt = <2696000>; + regulator-max-microvolt = <2704000>; + }; + vreg_l20a_2p95: l20 { + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2960000>; + regulator-allow-set-load; + regulator-system-load = <10000>; + }; + vreg_l21a_2p95: l21 { + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2960000>; + regulator-allow-set-load; + regulator-system-load = <800000>; + }; + vreg_l22a_2p85: l22 { }; + vreg_l23a_3p3: l23 { + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3312000>; + }; + vreg_l24a_3p075: l24 { + regulator-min-microvolt = <3088000>; + regulator-max-microvolt = <3088000>; + }; + vreg_l25a_3p3: l25 { + regulator-min-microvolt = <3104000>; + regulator-max-microvolt = <3312000>; + }; + vreg_l26a_1p2: l26 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-allow-set-load; + }; + vreg_l28_3p0: l28 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + }; + vreg_lvs1a_1p8: lvs1 { }; + vreg_lvs2a_1p8: lvs2 { }; + + }; + + pmi8998-regulators { + compatible = "qcom,rpm-pmi8998-regulators"; + + vdd_bob-supply = <&vph_pwr>; + + vreg_bob: bob { + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3600000>; + }; + }; +}; + +&sdhc2 { + status = "okay"; + cd-gpios = <&tlmm 95 GPIO_ACTIVE_HIGH>; + + vmmc-supply = <&vreg_l21a_2p95>; + vqmmc-supply = <&vreg_l13a_2p95>; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>; +}; + +&tlmm { + gpio-reserved-ranges = <0 4>, <81 4>; + + mdp_vsync_n: mdp-vsync-n { + pins = "gpio10"; + function = "mdp_vsync_a"; + drive-strength = <2>; + bias-pull-down; + }; + + nfc_ven: nfc-ven { + pins = "gpio12"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-low; + }; + + msm_mclk0_default: msm-mclk0-active { + pins = "gpio13"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + msm_mclk1_default: msm-mclk1-active { + pins = "gpio14"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cci1_default: cci1-default { + pins = "gpio18", "gpio19"; + function = "cci_i2c"; + bias-disable; + drive-strength = <2>; + }; + + cci1_default: cci1-default { + pins = "gpio19", "gpio20"; + function = "cci_i2c"; + bias-disable; + drive-strength = <2>; + }; + + cam0_vdig_default: cam0-vdig-default { + pins = "gpio21"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + tof_int_n: tof-int-n { + pins = "gpio22"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + input-enable; + }; + + cam1_vdig_default: cam1-vdig-default { + pins = "gpio25"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + tof_rst_n: tof-rst-n { + pins = "gpio27"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-low; + }; + + cam1_rst_default: cam1-rst-n { + pins = "gpio28"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + cam0_rst_default: cam0-rst-n { + pins = "gpio30"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + ts_reset_n: ts-reset-n { + pins = "gpio89"; + function = "gpio"; + bias-pull-up; + drive-strength = <8>; + }; + + panel_reset_n: panel-rst-n { + pins = "gpio94"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + hall_sensor0_default: acc-cover-open { + pins = "gpio124"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + input-enable; + }; + + ts_int_n: ts-int-n { + pins = "gpio125"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + ts_vddio_en: ts-vddio-en-default { + pins = "gpio133"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-low; + }; +}; + +/* + * WARNING: + * Disable UFS until card quirks are in to avoid unrecoverable hard-brick + * that would happen as soon as the UFS card gets probed as, without the + * required quirks, the bootloader will be erased right after card probe. + */ +&ufshc { + status = "disabled"; +}; + +&ufsphy { + status = "disabled"; +}; + +&usb3 { + status = "okay"; +}; + +&usb3_dwc3 { + /* Force to peripheral until we have Type-C hooked up */ + dr_mode = "peripheral"; +}; + +&usb3phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l2a_1p2>; +}; From 673461e76a4f186bbefea4c8b3280609f81a42be Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 11:12:53 +0100 Subject: [PATCH 079/212] arm64: dts: msm8998: Configure the MultiMedia Clock Controller (MMCC) The MSM8998 MMCC is supported and has a driver: configure it as a preparation for a later enablement of multimedia nodes (mdp, venus and others). Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 34039b5c8017..1a53f15f1266 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -2330,6 +2331,36 @@ #size-cells = <0>; }; + mmcc: clock-controller@c8c0000 { + compatible = "qcom,mmcc-msm8998"; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + reg = <0xc8c0000 0x40000>; + status = "disabled"; + + clock-names = "xo", + "gpll0", + "dsi0dsi", + "dsi0byte", + "dsi1dsi", + "dsi1byte", + "hdmipll", + "dplink", + "dpvco", + "core_bi_pll_test_se"; + clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, + <&gcc GCC_MMSS_GPLL0_CLK>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>; + }; + remoteproc_adsp: remoteproc@17300000 { compatible = "qcom,msm8998-adsp-pas"; reg = <0x17300000 0x4040>; From b92c738248d25bb410a653b02bfac2f5cb86d3b9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 11:30:33 +0100 Subject: [PATCH 080/212] arm64: dts: msm8998: Configure the multimedia subsystem iommu In preparation for enabling various components of the multimedia subsystem, write configuration for its related IOMMU. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 1a53f15f1266..c83e54a84bca 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2361,6 +2361,43 @@ <0>; }; + mmss_smmu: iommu@cd00000 { + compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; + reg = <0x0cd00000 0x40000>; + #iommu-cells = <1>; + + clocks = <&mmcc MNOC_AHB_CLK>, + <&mmcc BIMC_SMMU_AHB_CLK>, + <&rpmcc RPM_SMD_MMAXI_CLK>, + <&mmcc BIMC_SMMU_AXI_CLK>; + clock-names = "iface-mm", "iface-smmu", + "bus-mm", "bus-smmu"; + status = "disabled"; + + #global-interrupts = <0>; + interrupts = + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; + remoteproc_adsp: remoteproc@17300000 { compatible = "qcom,msm8998-adsp-pas"; reg = <0x17300000 0x4040>; From 497945318d1f080324561a2661e1eaa27cd1e225 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 12:45:50 +0100 Subject: [PATCH 081/212] arm64: dts: msm8998: Add disabled configuration for DPU1/DSI This SoC supports both the MDP5 and DPU1 drivers, but the latter was chosen as it's more feature-complete; Configure the DPU1, DSI and related phy and pll in order to achieve display functionality and keep it disabled. Enabling it will be done on board specific DT when needed, as not all boards have a usable display attached to them. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 264 +++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index c83e54a84bca..9c2105838df6 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2351,16 +2351,272 @@ "core_bi_pll_test_se"; clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, <&gcc GCC_MMSS_GPLL0_CLK>, - <0>, - <0>, - <0>, - <0>, + <&dsi0_phy 1>, + <&dsi0_phy 0>, + <&dsi1_phy 1>, + <&dsi1_phy 0>, <0>, <0>, <0>, <0>; }; + dsi_opp_table: dsi-opp-table { + compatible = "operating-points-v2"; + + opp-131250000 { + opp-hz = /bits/ 64 <131250000>; + required-opps = <&rpmpd_opp_low_svs>; + }; + + opp-210000000 { + opp-hz = /bits/ 64 <210000000>; + required-opps = <&rpmpd_opp_svs>; + }; + + opp-312500000 { + opp-hz = /bits/ 64 <312500000>; + required-opps = <&rpmpd_opp_nom>; + }; + }; + + mdss: mdss@c900000 { + compatible = "qcom,mdss-dpu1"; + reg = <0x0c900000 0x1000>; + reg-names = "mdss"; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MDSS_MDP_CLK>; + clock-names = "iface", "bus", "core"; + + assigned-clocks = <&mmcc MDSS_MDP_CLK>; + assigned-clock-rates = <300000000>; + interrupts = ; + interrupt-controller; + iommus = <&mmss_smmu 0>; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&mmcc MDSS_GDSC>; + ranges; + status = "disabled"; + + mdss_mdp: mdp@c901000 { + compatible = "qcom,msm8998-dpu"; + reg = <0x0c901000 0x8f000>, + <0x0c9a8e00 0xf0>, + <0x0c9b0000 0x2008>, + <0x0c9b8000 0x1040>; + reg-names = "mdp", "regdma", "vbif", + "vbif_nrt"; + + assigned-clocks = <&mmcc MDSS_MDP_CLK>, + <&mmcc MDSS_VSYNC_CLK>; + assigned-clock-rates = <412500000>, + <19200000>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MDSS_MDP_CLK>, + <&mmcc MDSS_VSYNC_CLK>; + clock-names = "iface", "bus", "mnoc", + "core", "vsync"; + + interrupt-parent = <&mdss>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&mdp_opp_table>; + power-domains = <&rpmpd MSM8998_VDDMX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dpu_intf1_out: endpoint { + remote-endpoint = <&dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + dpu_intf2_out: endpoint { + remote-endpoint = <&dsi1_in>; + }; + }; + }; + + mdp_opp_table: mdp-opp-table { + compatible = "operating-points-v2"; + + opp-19200000 { + opp-hz = /bits/ 64 <19200000>; + required-opps = <&rpmpd_opp_min_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmpd_opp_low_svs>; + }; + + opp-275000000 { + opp-hz = /bits/ 64 <275000000>; + required-opps = <&rpmpd_opp_svs>; + }; + + opp-330000000 { + opp-hz = /bits/ 64 <330000000>; + required-opps = <&rpmpd_opp_nom>; + }; + + opp-412500000 { + opp-hz = /bits/ 64 <412500000>; + required-opps = <&rpmpd_opp_turbo>; + }; + }; + }; + + dsi0: dsi@c994000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0x0c994000 0x400>; + reg-names = "dsi_ctrl"; + + clocks = <&mmcc MDSS_BYTE0_CLK>, + <&mmcc MDSS_BYTE0_INTF_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MISC_AHB_CLK>, + <&mmcc MDSS_PCLK0_CLK>, + <&mmcc MDSS_ESC0_CLK>; + clock-names = "byte", + "byte_intf", + "mnoc", + "iface", + "bus", + "core_mmss", + "pixel", + "core"; + + interrupt-parent = <&mdss>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&dsi_opp_table>; + phys = <&dsi0_phy>; + phy-names = "dsi"; + power-domains = <&rpmpd MSM8998_VDDCX>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + dsi0_out: endpoint { + }; + }; + }; + }; + + dsi0_phy: dsi-phy@c994400 { + compatible = "qcom,dsi-phy-10nm-8998"; + reg = <0x0c994400 0x200>, + <0x0c994600 0x280>, + <0x0c994a00 0x1c0>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "iface", "ref"; + power-domains = <&rpmpd MSM8998_VDDMX>; + + status = "disabled"; + }; + + dsi1: dsi@c996000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0x0c996000 0x400>; + reg-names = "dsi_ctrl"; + + clocks = <&mmcc MDSS_BYTE1_CLK>, + <&mmcc MDSS_BYTE1_INTF_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MISC_AHB_CLK>, + <&mmcc MDSS_PCLK1_CLK>, + <&mmcc MDSS_ESC1_CLK>, + <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "mnoc", + "iface_mmss", + "pixel", + "core", + "iface", + "bus"; + + interrupt-parent = <&mdss>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&dsi_opp_table>; + phys = <&dsi1_phy>; + phy-names = "dsi"; + power-domains = <&rpmpd MSM8998_VDDCX>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi1_in: endpoint { + remote-endpoint = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + dsi1_out: endpoint { + }; + }; + }; + }; + + dsi1_phy: dsi-phy@c996400 { + compatible = "qcom,dsi-phy-10nm-8998"; + reg = <0x0c996400 0x200>, + <0x0c996600 0x280>, + <0x0c996a00 0x10e>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "iface", "ref"; + power-domains = <&rpmpd MSM8998_VDDMX>; + + status = "disabled"; + }; + }; + mmss_smmu: iommu@cd00000 { compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; reg = <0x0cd00000 0x40000>; From 40e0553a6de619dfc572edd602c77bd8728e14ac Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 11:44:05 +0100 Subject: [PATCH 082/212] arm64: dts: msm8998: Fix CPU/L2 idle state latency and residency The entry/exit latency and minimum residency in state for the idle states of MSM8998 were ..bad: first of all, for all of them the timings were written for CPU sleep but the min-residency-us param was miscalculated (supposedly, while porting this from downstream); Then, the power collapse states are setting PC on both the CPU cluster *and* the L2 cache, which have different timings: in the specific case of L2 the times are higher so these ones should be taken into account instead of the CPU ones. This parameter misconfiguration was not giving particular issues because on MSM8998 there was no CPU scaling at all, so cluster/L2 power collapse was rarely (if ever) hit. When CPU scaling is enabled, though, the wrong timings will produce SoC unstability shown to the user as random, apparently error-less, sudden reboots and/or lockups. This set of parameters are stabilizing the SoC when CPU scaling is ON and when power collapse is frequently hit. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 9c2105838df6..79bbef3e3feb 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -309,38 +309,42 @@ LITTLE_CPU_SLEEP_0: cpu-sleep-0-0 { compatible = "arm,idle-state"; idle-state-name = "little-retention"; + /* CPU Retention (C2D), L2 Active */ arm,psci-suspend-param = <0x00000002>; entry-latency-us = <81>; exit-latency-us = <86>; - min-residency-us = <200>; + min-residency-us = <504>; }; LITTLE_CPU_SLEEP_1: cpu-sleep-0-1 { compatible = "arm,idle-state"; idle-state-name = "little-power-collapse"; + /* CPU + L2 Power Collapse (C3, D4) */ arm,psci-suspend-param = <0x40000003>; - entry-latency-us = <273>; - exit-latency-us = <612>; - min-residency-us = <1000>; + entry-latency-us = <814>; + exit-latency-us = <4562>; + min-residency-us = <9183>; local-timer-stop; }; BIG_CPU_SLEEP_0: cpu-sleep-1-0 { compatible = "arm,idle-state"; idle-state-name = "big-retention"; + /* CPU Retention (C2D), L2 Active */ arm,psci-suspend-param = <0x00000002>; entry-latency-us = <79>; exit-latency-us = <82>; - min-residency-us = <200>; + min-residency-us = <1302>; }; BIG_CPU_SLEEP_1: cpu-sleep-1-1 { compatible = "arm,idle-state"; idle-state-name = "big-power-collapse"; + /* CPU + L2 Power Collapse (C3, D4) */ arm,psci-suspend-param = <0x40000003>; - entry-latency-us = <336>; - exit-latency-us = <525>; - min-residency-us = <1000>; + entry-latency-us = <724>; + exit-latency-us = <2027>; + min-residency-us = <9419>; local-timer-stop; }; }; From 309939ed8af40b7a9df8801a2bb03217426e11e2 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 11:56:04 +0100 Subject: [PATCH 083/212] arm64: dts: msm8998: Move qfprom iospace to calibrated values The QFPROM iospace was (erroneously, I believe) set to the uncalibrated fuse start address, but every driver only needs - and will always only need - only calibrated values. Move the iospace forward to the calibrated values start to avoid offsetting every fuse definition. Obviously, the only defined fuse (qusb2_hstx_trim) was also fixed to remove the offset, in order to comply with this change. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 79bbef3e3feb..f97dfcb5862d 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -867,14 +867,14 @@ reg = <0x00778000 0x7000>; }; - qfprom: qfprom@780000 { + qfprom: qfprom@784000 { compatible = "qcom,qfprom"; - reg = <0x00780000 0x621c>; + reg = <0x00784000 0x621c>; #address-cells = <1>; #size-cells = <1>; - qusb2_hstx_trim: hstx-trim@423a { - reg = <0x423a 0x1>; + qusb2_hstx_trim: hstx-trim@23a { + reg = <0x23a 0x1>; bits = <0 4>; }; }; From bf8ab5ac8df09a16fe558ef0299c160b6cdc0c5d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 4 Jan 2021 18:17:58 +0100 Subject: [PATCH 084/212] arm64: dts: msm8998: Add interconnect nodes This SoC features Network-on-Chip (NoC) and Bus Integrated Memory Controller (BIMC) interconnects: add the required nodes now that the driver is present. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index f97dfcb5862d..90b161a357af 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -879,6 +880,15 @@ }; }; + bimc: interconnect@1008000 { + compatible = "qcom,msm8998-bimc"; + reg = <0x01008000 0x78000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; + tsens0: thermal@10ab000 { compatible = "qcom,msm8998-tsens", "qcom,tsens-v2"; reg = <0x010ab000 0x1000>, /* TM */ @@ -901,6 +911,33 @@ #thermal-sensor-cells = <1>; }; + cnoc: interconnect@1500000 { + compatible = "qcom,msm8998-cnoc"; + reg = <0x01500000 0x10000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_CNOC_CLK>, + <&rpmcc RPM_SMD_CNOC_A_CLK>; + }; + + snoc: interconnect@1625000 { + compatible = "qcom,msm8998-snoc"; + reg = <0x01625000 0x6100>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + }; + + a1noc: interconnect@1669000 { + compatible = "qcom,msm8998-a1noc"; + reg = <0x01669000 0x5020>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>; + }; + anoc1_smmu: iommu@1680000 { compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; reg = <0x01680000 0x10000>; @@ -935,6 +972,25 @@ ; }; + a2noc: interconnect@1705000 { + compatible = "qcom,msm8998-a2noc"; + reg = <0x01705000 0xa090>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>; + }; + + mnoc: interconnect@1744000 { + compatible = "qcom,msm8998-mnoc"; + reg = <0x01744000 0xb010>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a", "iface"; + clocks = <&rpmcc RPM_SMD_MMAXI_CLK>, + <&rpmcc RPM_SMD_MMAXI_A_CLK>, + <&mmcc AHB_CLK_SRC>; + }; + pcie0: pci@1c00000 { compatible = "qcom,pcie-msm8996"; reg = <0x01c00000 0x2000>, @@ -2691,6 +2747,18 @@ }; }; + gnoc: interconnect@17900000 { + compatible = "qcom,msm8998-gnoc"; + reg = <0x17900000 0xe000>; + #interconnect-cells = <1>; + /* + * This one apparently features no clocks, + * so let's not mess with the driver needlessly + */ + clock-names = "bus", "bus_a"; + clocks = <&xo>, <&xo>; + }; + apcs_glb: mailbox@17911000 { compatible = "qcom,msm8998-apcs-hmss-global"; reg = <0x17911000 0x1000>; From 3851b91c8fff24eed3dbd78bdc88a261168cf4ca Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 20 Jan 2021 15:27:42 +0100 Subject: [PATCH 085/212] arm64: dts: msm8998: Wire up interconnects and OPPs to sdhci port 2 Wire up the OPP table and interconnects to the SDHCI port 2 to improve performance and power consumption. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 90b161a357af..649f7e18788b 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2089,6 +2089,7 @@ reg = <0x0c0a4900 0x314>, <0x0c0a4000 0x800>; reg-names = "hc_mem", "core_mem"; + interconnects = <&a2noc MASTER_SDCC_2 &a2noc SLAVE_A2NOC_SNOC>; interrupts = , ; interrupt-names = "hc_irq", "pwr_irq"; @@ -2099,6 +2100,26 @@ <&xo>; bus-width = <4>; status = "disabled"; + + sdhc2_opp_table: sdhc2-opp-table { + compatible = "operating-points-v2"; + + opp-50000000 { + opp-hz = /bits/ 64 <50000000>; + opp-peak-kBps = <400000>; + opp-avg-kBps = <200000>; + }; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + opp-peak-kBps = <800000>; + opp-avg-kBps = <400000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-peak-kBps = <800000>; + opp-avg-kBps = <800000>; + }; + }; }; blsp1_dma: dma-controller@c144000 { From 5abec5d77e2cb3aa7bbcdfefc21e3c728187c693 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 14 Dec 2020 17:19:11 +0100 Subject: [PATCH 086/212] arm64: dts: qcom: pm8998: Add VREF_1P25 and REF_GND VADC channels Signed-off-by: Konrad Dybcio --- arch/arm64/boot/dts/qcom/pm8998.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi index 6f5bb6b37ec2..ebe8a918b19f 100644 --- a/arch/arm64/boot/dts/qcom/pm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -78,6 +78,16 @@ #size-cells = <0>; #io-channel-cells = <1>; + adc-chan@0 { + reg = ; + label = "ref_gnd"; + }; + + adc-chan@1 { + reg = ; + label = "vref_1p25"; + }; + adc-chan@6 { reg = ; label = "die_temp"; From 76fb26265fc3aa7f9e7697dad25ec1e4c8bea129 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 14:31:40 +0100 Subject: [PATCH 087/212] arm64: dts: msm8998: Configure Adreno GPU and related IOMMU The MSM8998 SoC includes an Adreno 540.1 GPU, with a maximum frequency of 710MHz. This GPU may or may not accept a ZAP shader, depending on platform configuration, so adding a zap-shader node is left to the board DT. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 97 +++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 649f7e18788b..f380abbd7151 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -1477,6 +1477,103 @@ }; }; + adreno_gpu: gpu@5000000 { + compatible = "qcom,adreno-540.1", "qcom,adreno"; + reg = <0x05000000 0x40000>; + reg-names = "kgsl_3d0_reg_memory"; + + clocks = <&gcc GCC_GPU_CFG_AHB_CLK>, + <&gpucc RBBMTIMER_CLK>, + <&gcc GCC_BIMC_GFX_CLK>, + <&gcc GCC_GPU_BIMC_GFX_CLK>, + <&gpucc RBCPR_CLK>, + <&gpucc GFX3D_CLK>; + clock-names = "iface", + "rbbmtimer", + "mem", + "mem_iface", + "rbcpr", + "core"; + + interrupts = <0 300 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&adreno_smmu 0>; + operating-points-v2 = <&gpu_opp_table>; + power-domains = <&rpmpd MSM8998_VDDMX>; + #stream-id-cells = <16>; + status = "disabled"; + + gpu_opp_table: opp-table { + compatible = "operating-points-v2"; + opp-710000097 { + opp-hz = /bits/ 64 <710000097>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-670000048 { + opp-hz = /bits/ 64 <670000048>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-596000097 { + opp-hz = /bits/ 64 <596000097>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-515000097 { + opp-hz = /bits/ 64 <515000097>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-414000000 { + opp-hz = /bits/ 64 <414000000>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-342000000 { + opp-hz = /bits/ 64 <342000000>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + + opp-257000000 { + opp-hz = /bits/ 64 <257000000>; + opp-level = ; + opp-supported-hw = <0xFF>; + }; + }; + }; + + adreno_smmu: iommu@5040000 { + compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; + reg = <0x05040000 0x10000>; + clocks = <&gcc GCC_GPU_CFG_AHB_CLK>, + <&gcc GCC_BIMC_GFX_CLK>, + <&gcc GCC_GPU_BIMC_GFX_CLK>; + clock-names = "iface", "mem", "mem_iface"; + + #global-interrupts = <0>; + #iommu-cells = <1>; + interrupts = + , + , + ; + /* + * GPU-GX GDSC's parent is GPU-CX. We need to bring up the + * GPU-CX for SMMU but we need both of them up for Adreno. + * Contemporarily, we also need to manage the VDDMX rpmpd + * domain in the Adreno driver. + * Enable GPU CX/GX GDSCs here so that we can manage the + * SoC VDDMX RPM Power Domain in the Adreno driver. + */ + power-domains = <&gpucc GPU_GX_GDSC>; + status = "disabled"; + }; + gpucc: clock-controller@5065000 { compatible = "qcom,msm8998-gpucc"; #clock-cells = <1>; From 02524dfa62bd1c4d7bff9c1172fa2f6432ac508d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 4 Dec 2020 23:04:52 +0100 Subject: [PATCH 088/212] arm64: dts: qcom: Add support for MSM8998 F(x)tec Pro1 QX1000 Add device tree support for the F(x)tec Pro 1 (QX1000) smartphone. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/Makefile | 1 + .../boot/dts/qcom/msm8998-fxtec-pro1.dts | 651 ++++++++++++++++++ 2 files changed, 652 insertions(+) create mode 100644 arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 3241d7550b4d..6b5bf8ea427f 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -34,6 +34,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8996-sony-xperia-tone-dora.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8996-sony-xperia-tone-kagura.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8996-sony-xperia-tone-keyaki.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-asus-novago-tp370ql.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8998-fxtec-pro1.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-hp-envy-x2.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-lenovo-miix-630.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-mtp.dtb diff --git a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts new file mode 100644 index 000000000000..5464e724d04c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +/dts-v1/; + +#include "msm8998-mtp.dtsi" + +#include +#include +#include + +/ { + model = "F(x)tec Pro1 (QX1000)"; + compatible = "fxtec,pro1", "qcom,msm8998"; + qcom,board-id = <0x02000b 0x10>; + + /* This part enables graphical output via bootloader-enabled display */ + chosen { + bootargs = "earlycon=tty0 console=tty0 clk_ignore_unused"; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + }; + + reserved-memory { + cont_splash_mem: cont-splash-region@9d400000 { + reg = <0x0 0x9d400000 0x0 0x2000000>; + no-map; + }; + + zap_shader_region: gpu@f6400000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xf6400000 0x0 0x2000>; + no-map; + }; + + ramoops@ffc00000 { + compatible = "ramoops"; + reg = <0x0 0xffc00000 0x0 0x100000>; + record-size = <0x10000>; + console-size = <0x60000>; + ftrace-size = <0x10000>; + pmsg-size = <0x20000>; + ecc-size = <16>; + status = "okay"; + }; + }; + + keyboard-leds { + compatible = "gpio-leds"; + + backlight { + color = ; + default-state = "off"; + function = LED_FUNCTION_KBD_BACKLIGHT; + gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>; + label = "white:kbd_backlight"; + retain-state-suspended; + }; + + caps-lock { + color = ; + default-state = "off"; + function = LED_FUNCTION_CAPSLOCK; + gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; + label = "yellow:capslock"; + linux,default-trigger = "kbd-capslock"; + }; + }; + + gpio_keyboard: gpio-keyboard { + compatible = "gpio-fastmatrix-keyboard"; + label = "F(x)Tec Pro1 Hardware Keyboard"; + row-gpios = + <&gpioext0 0 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 1 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 2 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 3 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 4 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 5 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 6 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 7 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; + + col-gpios = + <&gpioext0 8 GPIO_ACTIVE_LOW>, + <&gpioext0 9 GPIO_ACTIVE_LOW>, + <&gpioext0 10 GPIO_ACTIVE_LOW>, + <&gpioext0 11 GPIO_ACTIVE_LOW>, + <&gpioext0 12 GPIO_ACTIVE_LOW>, + <&gpioext0 13 GPIO_ACTIVE_LOW>, + <&gpioext0 14 GPIO_ACTIVE_LOW>, + <&gpioext0 15 GPIO_ACTIVE_LOW>; + keypad,num-rows = <8>; + keypad,num-columns = <8>; + + pinctrl-names = "default"; + pinctrl-0 = <&keyboard_pins_col>, <&keyboard_pins_row>; + + autorescan-ms = <5>; + debounce-delay-ms = <1>; + col-scan-delay-us = <1500>; + }; + + gpio_kb_extra_keys { + compatible = "gpio-keys"; + input-name = "extra-kb-keys"; + label = "Keyboard extra keys"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gpio_kb_extra_active>; + + home { + label = "Home"; + gpios = <&tlmm 21 GPIO_ACTIVE_LOW>; + linux,code = <172>; + debounce-interval = <15>; + linux,can-disable; + }; + + super_l { + label = "Super Left"; + gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <15>; + linux,can-disable; + }; + + super_r { + label = "Super Right"; + gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <15>; + linux,can-disable; + }; + + shift { + label = "Shift"; + gpios = <&tlmm 114 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <15>; + linux,can-disable; + }; + + ctrl { + label = "Ctrl"; + gpios = <&tlmm 128 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <15>; + linux,can-disable; + }; + + alt { + label = "Alt"; + gpios = <&tlmm 129 GPIO_ACTIVE_LOW>; + linux,code = <56>; + debounce-interval = <15>; + linux,can-disable; + }; + }; + + gpio_hall_sensors { + compatible = "gpio-keys"; + input-name = "hall-sensors"; + label = "Hall sensors"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&hall_sensor0_default>, <&hall_sensor1_default>; +/* + hall_sensor0 { + label = "Cover Hall Sensor"; + gpios = <&tlmm 82 GPIO_ACTIVE_HIGH>; + linux,input-type = ; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + }; +*/ + hall_sensor1 { + label = "Keyboard Hall Sensor"; + gpios = <&tlmm 124 GPIO_ACTIVE_HIGH>; + linux,input-type = ; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + input-name = "side-buttons"; + label = "Side buttons"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&vol_up_pin_a>, <&cam_focus_pin_a>, + <&cam_snapshot_pin_a>; + vol_up { + label = "Volume Up"; + gpios = <&pm8998_gpio 6 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + }; + + camera_snapshot { + label = "Camera Snapshot"; + gpios = <&pm8998_gpio 7 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + debounce-interval = <15>; + }; + + camera_focus { + label = "Camera Focus"; + gpios = <&pm8998_gpio 8 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + debounce-interval = <15>; + }; + }; + + disp_vcc_vreg: disp-vcc-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vcc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + disp_vddio_vreg: disp-vddio-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vddio"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + regulator-allow-set-load; + }; + + disp_vci_vreg: disp-vci-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vci"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + regulator-allow-set-load; + }; + + disp_elvdd_vreg: disp-elvdd-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_elvdd"; + regulator-min-microvolt = <4600000>; + regulator-max-microvolt = <4600000>; + regulator-boot-on; + regulator-always-on; + }; + + disp_elvss_vreg: disp-elvss-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_elvss"; + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <2400000>; + regulator-boot-on; + regulator-always-on; + }; + + ts_vio_vreg: ts-vio-vreg { + compatible = "regulator-fixed"; + regulator-name = "ts_vio_reg"; + startup-delay-us = <2>; + enable-active-high; + gpio = <&tlmm 81 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&ts_vio_default>; + regulator-always-on; + }; + + /* + * Until we hook up type-c detection, we + * have to stick with this. But it works. + */ + extcon_usb: extcon-usb { + compatible = "linux,extcon-usb-gpio"; + id-gpio = <&tlmm 38 GPIO_ACTIVE_HIGH>; + }; +}; + +&adreno_gpu { + status = "ok"; + + zap-shader { + memory-region = <&zap_shader_region>; + }; +}; + +&adreno_smmu { + status = "ok"; +}; + +&apc_cprh { + status = "ok"; +}; + +&blsp1_i2c6 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c6_default>; + pinctrl-1 = <&i2c6_sleep>; + dmas = <&blsp1_dma 16>, <&blsp1_dma 17>; + dma-names = "tx", "rx"; + status = "ok"; + + gpioext0: gpio-expander@58 { + compatible = "awinic,aw9523-pinctrl"; + reg = <0x58>; + interrupt-parent = <&tlmm>; + interrupts = <50 IRQ_TYPE_EDGE_FALLING>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&gpioext0 0 0 16>; + interrupt-controller; + #interrupt-cells = <2>; + + pinctrl-0 = <&gpio_expander_int_n>, <&gpio_expander_rst_n>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + + keyboard_pins_col: keyboard-matrix-col-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15"; + function = "gpio"; + output-low; + }; + keyboard_pins_row: keyboard-matrix-row-pins { + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7"; + function = "gpio"; + drive-open-drain; + input-enable; + }; + }; +}; + +&blsp2_i2c1 { + status = "ok"; + qcom,noise-reject-scl = <3>; + qcom,noise-reject-sda = <3>; + + touchscreen@14 { + compatible = "goodix,gt9286"; + reg = <0x14>; + interrupt-parent = <&tlmm>; + interrupts = <125 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&tlmm 89 GPIO_ACTIVE_HIGH>; + AVDD28-supply = <&vreg_l28_3p0>; + VDDIO-supply = <&ts_vio_vreg>; + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_sleep>; + }; +}; + +&cpufreq_hw { + status = "ok"; +}; + +&dsi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "ok"; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + + panel: panel@0 { + compatible = "boe,bf060y8m-aj0"; + reg = <0>; + + reset-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>; + disp-te-gpios = <&tlmm 10 GPIO_ACTIVE_LOW>; + no-hpd; + + vcc-supply = <&disp_vcc_vreg>; + vddio-supply = <&disp_vddio_vreg>; + vci-supply = <&disp_vci_vreg>; + elvdd-supply = <&disp_elvdd_vreg>; + elvss-supply = <&disp_elvss_vreg>; + + pinctrl-names = "default"; + pinctrl-0 = <&panel_reset_n &disp_en_default &mdp_vsync_n>; + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; +}; + +&dsi0_phy { + status = "ok"; +}; + +&dsi0_out { + remote-endpoint = <&panel_in>; + data-lanes = <0 1 2 3>; +}; + +&gcc { + status = "ok"; +}; + +&gpucc { + status = "ok"; +}; + +&gpio_keyboard { + linux,keymap = < + MATRIX_KEY(0, 0, KEY_F1) MATRIX_KEY(1, 0, KEY_H) + MATRIX_KEY(2, 0, KEY_B) MATRIX_KEY(3, 0, KEY_7) + MATRIX_KEY(4, 0, KEY_UP) MATRIX_KEY(5, 0, KEY_ENTER) + MATRIX_KEY(6, 0, KEY_Y) MATRIX_KEY(7, 0, KEY_COMMA) + MATRIX_KEY(0, 1, KEY_3) MATRIX_KEY(1, 1, KEY_S) + MATRIX_KEY(2, 1, KEY_Z) MATRIX_KEY(3, 1, KEY_M) + MATRIX_KEY(4, 1, KEY_I) MATRIX_KEY(5, 1, KEY_9) + MATRIX_KEY(6, 1, KEY_W) MATRIX_KEY(7, 1, KEY_J) + MATRIX_KEY(0, 2, KEY_LEFT) MATRIX_KEY(1, 2, KEY_G) + MATRIX_KEY(2, 2, KEY_V) MATRIX_KEY(3, 2, KEY_6) + MATRIX_KEY(4, 2, KEY_RIGHT) MATRIX_KEY(5, 2, KEY_DELETE) + MATRIX_KEY(6, 2, KEY_T) MATRIX_KEY(7, 2, KEY_DOT) + MATRIX_KEY(0, 3, KEY_SLASH) MATRIX_KEY(1, 3, KEY_A) + MATRIX_KEY(2, 3, KEY_RIGHTBRACE) MATRIX_KEY(3, 3, KEY_HOMEPAGE) + MATRIX_KEY(4, 3, KEY_P) MATRIX_KEY(5, 3, KEY_MINUS) + MATRIX_KEY(6, 3, KEY_Q) MATRIX_KEY(7, 3, KEY_L) + MATRIX_KEY(0, 4, KEY_BACKSPACE) MATRIX_KEY(1, 4, KEY_D) + MATRIX_KEY(2, 4, KEY_X) MATRIX_KEY(3, 4, KEY_K) + MATRIX_KEY(4, 4, KEY_SEMICOLON) MATRIX_KEY(5, 4, KEY_EQUAL) + MATRIX_KEY(6, 4, KEY_E) MATRIX_KEY(7, 4, KEY_APOSTROPHE) + MATRIX_KEY(0, 5, KEY_CAPSLOCK) MATRIX_KEY(1, 5, KEY_BACKSLASH) + MATRIX_KEY(2, 5, KEY_LEFTBRACE) MATRIX_KEY(3, 5, KEY_DOWN) + MATRIX_KEY(4, 5, KEY_O) MATRIX_KEY(5, 5, KEY_0) + MATRIX_KEY(6, 5, KEY_GRAVE) MATRIX_KEY(7, 5, KEY_K) + MATRIX_KEY(0, 6, KEY_SPACE) MATRIX_KEY(1, 6, KEY_F) + MATRIX_KEY(2, 6, KEY_C) MATRIX_KEY(3, 6, KEY_N) + MATRIX_KEY(4, 6, KEY_U) MATRIX_KEY(5, 6, KEY_8) + MATRIX_KEY(6, 6, KEY_R) MATRIX_KEY(7, 6, KEY_5) + MATRIX_KEY(0, 7, KEY_ESC) MATRIX_KEY(1, 7, KEY_1) + MATRIX_KEY(2, 7, KEY_RESERVED) MATRIX_KEY(3, 7, KEY_RESERVED) + MATRIX_KEY(4, 7, KEY_2) MATRIX_KEY(5, 7, KEY_4) + MATRIX_KEY(6, 7, KEY_TAB) MATRIX_KEY(7, 7, KEY_RESERVED) + >; +}; + +&mdss { + status = "ok"; +}; + +&mdss_mdp { + status = "ok"; +}; + +&mmcc { + status = "ok"; +}; + +&mmss_smmu { + status = "ok"; +}; + +/* HACK! Push GPU voltage high until GPU CPR is hooked up */ +&pm8005_s1 { + regulator-min-microvolt = <988000>; + regulator-max-microvolt = <1100000>; +}; + +&pm8998_gpio { + unknown_pin_a: unk-active { + pins = "gpio5"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + vol_up_pin_a: vol-up-active { + pins = "gpio6"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + cam_focus_pin_a: cam-focus-btn-active { + pins = "gpio7"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + + cam_snapshot_pin_a: cam-snapshot-btn-active { + pins = "gpio8"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; +}; + +&pm8998_pon { + resin { + compatible = "pm8941-resin"; + interrupts = ; + debounce = <15625>; + bias-pull-up; + linux,code = ; + }; +}; + +&tlmm { + gpio-reserved-ranges = <0 4>; + + mdp_vsync_n: mdp-vsync-n { + pins = "gpio10"; + function = "mdp_vsync_a"; + drive-strength = <2>; + bias-pull-down; + }; + + i2c6_default: i2c6-default { + pins = "gpio43", "gpio44"; + function = "blsp_i2c6"; + drive-strength = <2>; + bias-disable; + }; + + i2c6_sleep: i2c6-sleep { + pins = "gpio43", "gpio44"; + function = "blsp_i2c6"; + drive-strength = <2>; + bias-pull-up; + }; + + gpio_kb_extra_active: gpio-kb-extra-active { + pins = "gpio21", "gpio32", "gpio33", "gpio114", + "gpio128", "gpio129"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + gpio_expander_int_n: gpio-exp-intn-def { + pins = "gpio50"; + drive-strength = <2>; + input-enable; + }; + + gpio_expander_rst_n: gpio-exp-rst-def { + pins = "gpio51"; + drive-strength = <8>; + }; + + disp_en_default: disp-en { + pins = "gpio62"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + ts_vio_default: ts-vio-def { + pins = "gpio81"; + drive-strength = <2>; + bias-disable; + }; + + hall_sensor0_default: hall-sensor0-def { + pins = "gpio82"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + + hall_sensor1_default: hall-sensor1-def { + pins = "gpio124"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + + panel_reset_n: panel-rst-n { + pins = "gpio94"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + ts_active: ts-active { + ts-rst-n { + pins = "gpio89"; + drive-strength = <8>; + bias-pull-up; + }; + + ts-intr-n { + pins = "gpio125"; + drive-strength = <8>; + bias-disable; + }; + }; + + ts_sleep: ts-sleep { + ts-rst-n { + pins = "gpio89"; + drive-strength = <2>; + bias-disable; + }; + + ts-intr-n { + pins = "gpio125"; + drive-strength = <2>; + bias-pull-down; + }; + }; +}; + +&ufshc { + status = "ok"; +}; + +&ufsphy { + status = "ok"; +}; + +&usb3_dwc3 { + dr_mode = "peripheral"; + extcon = <&extcon_usb>; +}; + +/* GT9286 analog supply */ +&vreg_l28_3p0 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; +}; From eec3b9f69909ec4c6a660835f47378bac769fed4 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 21 Jan 2021 20:05:15 +0100 Subject: [PATCH 089/212] arm64: dts: msm8998: Wire up interconnects to MDP and GPU Wire up the interconnects to both the MDP and the Adreno GPU in order to get the right balance between performance and power consumption of both devices. --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index f380abbd7151..6838c1918754 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -1495,6 +1495,8 @@ "rbcpr", "core"; + interconnects = <&bimc MASTER_OXILI &bimc SLAVE_EBI>; + interconnect-names = "gfx-mem"; interrupts = <0 300 IRQ_TYPE_LEVEL_HIGH>; iommus = <&adreno_smmu 0>; operating-points-v2 = <&gpu_opp_table>; @@ -1507,42 +1509,55 @@ opp-710000097 { opp-hz = /bits/ 64 <710000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <14400000>; opp-supported-hw = <0xFF>; }; opp-670000048 { opp-hz = /bits/ 64 <670000048>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <14390000>; opp-supported-hw = <0xFF>; }; - opp-596000097 { opp-hz = /bits/ 64 <596000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <12440000>; opp-supported-hw = <0xFF>; }; opp-515000097 { opp-hz = /bits/ 64 <515000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <10368000>; opp-supported-hw = <0xFF>; }; opp-414000000 { opp-hz = /bits/ 64 <414000000>; opp-level = ; + opp-peak-kBps = <12440000>; + opp-avg-kBps = <8136000>; opp-supported-hw = <0xFF>; }; opp-342000000 { opp-hz = /bits/ 64 <342000000>; opp-level = ; + opp-peak-kBps = <8136000>; + opp-avg-kBps = <6144000>; opp-supported-hw = <0xFF>; }; opp-257000000 { opp-hz = /bits/ 64 <257000000>; opp-level = ; + opp-peak-kBps = <6144000>; + opp-avg-kBps = <3296000>; opp-supported-hw = <0xFF>; }; }; @@ -2570,6 +2585,10 @@ assigned-clocks = <&mmcc MDSS_MDP_CLK>; assigned-clock-rates = <300000000>; + interconnects = <&mnoc MASTER_MDP_P0 &bimc SLAVE_EBI>, + <&mnoc MASTER_MDP_P1 &bimc SLAVE_EBI>; + interconnect-names = "mdp0-mem", + "mdp1-mem"; interrupts = ; interrupt-controller; iommus = <&mmss_smmu 0>; From ed5d4496d7a25a49f61c9ecc2585da1fe06ee8cc Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 12:10:53 +0100 Subject: [PATCH 090/212] arm64: dts: msm8998: Add SAW, CPRh and CPUFREQ to enable CPU scaling Add the SAW (SPM), CPR-Hardened, CPUFREQ-HW nodes and relative OPP tables (and also assign them to the CPU nodes, as required) in order to enable CPU scaling on the MSM8998 SoC. The CPR-Hardened and CPUFREQ-HW nodes are disabled by default as to not change the previous default behavior. Since the drivers are not yet accounting for speed-binning, these OPPs are referred to the most common binning for this chip, which I have found on six phones from Sony and one from FxTec (silver bin0, perf bin2). At least until speed-binning gets done in the cpufreq-hw and CPR drivers, users should enable CPR-Hardened and CPUFREQ in their own board DT. This is done like that because these drivers are really big, so the idea is to keep the "base" version easier (but perfectly working), before adding speed-binning "complications", which may... or may not be necessary. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 721 +++++++++++++++++++++++++- 1 file changed, 720 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 6838c1918754..ab1524347bd5 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -10,6 +10,15 @@ #include #include +/* Macro for CPR-Hardened OPP entries - Example phandle: cprh_opp0 */ +#define CPRH_OPP_ENTRY(lvl, _fuselevel, _oadj, _cadj) \ + cprh_opp##lvl##: opp-##lvl { \ + opp-level = ; \ + qcom,opp-fuse-level = <_fuselevel>; \ + qcom,opp-oloop-vadj = <_oadj>; \ + qcom,opp-cloop-vadj = <_cadj>; \ + } + / { interrupt-parent = <&intc>; @@ -136,8 +145,13 @@ reg = <0x0 0x0>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; L2_0: l2-cache { compatible = "arm,arch-cache"; cache-level = <2>; @@ -156,8 +170,13 @@ reg = <0x0 0x1>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; L1_I_1: l1-icache { compatible = "arm,arch-cache"; }; @@ -172,8 +191,13 @@ reg = <0x0 0x2>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; L1_I_2: l1-icache { compatible = "arm,arch-cache"; }; @@ -188,8 +212,13 @@ reg = <0x0 0x3>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; L1_I_3: l1-icache { compatible = "arm,arch-cache"; }; @@ -204,8 +233,13 @@ reg = <0x0 0x100>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; L2_1: l2-cache { compatible = "arm,arch-cache"; cache-level = <2>; @@ -224,8 +258,13 @@ reg = <0x0 0x101>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; L1_I_101: l1-icache { compatible = "arm,arch-cache"; }; @@ -240,8 +279,13 @@ reg = <0x0 0x102>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; L1_I_102: l1-icache { compatible = "arm,arch-cache"; }; @@ -256,8 +300,13 @@ reg = <0x0 0x103>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; L1_I_103: l1-icache { compatible = "arm,arch-cache"; }; @@ -351,6 +400,378 @@ }; }; + cpu_gold_opp_table: cpu-gold-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-2361600000 { + opp-hz = /bits/ 64 <2361600000>; + required-opps = <&cprh_opp30>; + qcom,pll-override = <0x0a620062>; + qcom,spare-data = <3>; + }; + opp-2342400000 { + opp-hz = /bits/ 64 <2342400000>; + required-opps = <&cprh_opp29>; + qcom,pll-override = <0x0a620062>; + qcom,spare-data = <3>; + }; + opp-2323200000 { + opp-hz = /bits/ 64 <2323200000>; + required-opps = <&cprh_opp28>; + qcom,pll-override = <0x0a610061>; + qcom,spare-data = <3>; + }; + opp-2265600000 { + opp-hz = /bits/ 64 <2265600000>; + required-opps = <&cprh_opp27>; + qcom,pll-override = <0x0a5e005e>; + qcom,spare-data = <3>; + }; + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + required-opps = <&cprh_opp26>; + qcom,pll-override = <0x0a5c005c>; + qcom,spare-data = <3>; + }; + opp-2112000000 { + opp-hz = /bits/ 64 <2112000000>; + required-opps = <&cprh_opp25>; + qcom,pll-override = <0x0a580058>; + qcom,spare-data = <3>; + }; + opp-2035200000 { + opp-hz = /bits/ 64 <2035200000>; + required-opps = <&cprh_opp24>; + qcom,pll-override = <0x09550055>; + qcom,spare-data = <3>; + }; + opp-1958400000 { + opp-hz = /bits/ 64 <1958400000>; + required-opps = <&cprh_opp23>; + qcom,pll-override = <0x09520052>; + qcom,spare-data = <2>; + }; + opp-1881600000 { + opp-hz = /bits/ 64 <1881600000>; + required-opps = <&cprh_opp22>; + qcom,pll-override = <0x094e004e>; + qcom,spare-data = <2>; + }; + opp-1804800000 { + opp-hz = /bits/ 64 <1804800000>; + required-opps = <&cprh_opp21>; + qcom,pll-override = <0x084b004b>; + qcom,spare-data = <2>; + }; + opp-1728000000 { + opp-hz = /bits/ 64 <1728000000>; + required-opps = <&cprh_opp20>; + qcom,pll-override = <0x08480048>; + qcom,spare-data = <2>; + }; + opp-1651200000 { + opp-hz = /bits/ 64 <1651200000>; + required-opps = <&cprh_opp19>; + qcom,pll-override = <0x07450045>; + qcom,spare-data = <2>; + }; + opp-1574400000 { + opp-hz = /bits/ 64 <1574400000>; + required-opps = <&cprh_opp18>; + qcom,pll-override = <0x07420042>; + qcom,spare-data = <2>; + }; + opp-1497600000 { + opp-hz = /bits/ 64 <1497600000>; + required-opps = <&cprh_opp17>; + qcom,pll-override = <0x073e003e>; + qcom,spare-data = <2>; + }; + opp-1420800000 { + opp-hz = /bits/ 64 <1420800000>; + required-opps = <&cprh_opp16>; + qcom,pll-override = <0x063b003b>; + qcom,spare-data = <2>; + }; + opp-1344000000 { + opp-hz = /bits/ 64 <1344000000>; + required-opps = <&cprh_opp15>; + qcom,pll-override = <0x06380038>; + qcom,spare-data = <2>; + }; + + opp-1267200000 { + opp-hz = /bits/ 64 <1267200000>; + required-opps = <&cprh_opp14>; + qcom,pll-override = <0x06350035>; + qcom,spare-data = <2>; + }; + opp-1190400000 { + opp-hz = /bits/ 64 <1190400000>; + required-opps = <&cprh_opp13>; + qcom,pll-override = <0x05320032>; + qcom,spare-data = <2>; + }; + opp-1132800000 { + opp-hz = /bits/ 64 <1132800000>; + required-opps = <&cprh_opp12>; + qcom,pll-override = <0x052f002f>; + qcom,spare-data = <1>; + }; + opp-1056000000 { + opp-hz = /bits/ 64 <1056000000>; + required-opps = <&cprh_opp11>; + qcom,pll-override = <0x052c002c>; + qcom,spare-data = <1>; + }; + opp-979200000 { + opp-hz = /bits/ 64 <979200000>; + required-opps = <&cprh_opp10>; + qcom,pll-override = <0x4290029>; + qcom,spare-data = <1>; + }; + opp-902400000 { + opp-hz = /bits/ 64 <902400000>; + required-opps = <&cprh_opp9>; + qcom,pll-override = <0x4260026>; + qcom,spare-data = <1>; + }; + opp-806400000 { + opp-hz = /bits/ 64 <806400000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x3200022>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-729600000 { + opp-hz = /bits/ 64 <729600000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-652800000 { + opp-hz = /bits/ 64 <652800000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-576000000 { + opp-hz = /bits/ 64 <576000000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-499200000 { + opp-hz = /bits/ 64 <499200000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-422400000 { + opp-hz = /bits/ 64 <422400000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-345600000 { + opp-hz = /bits/ 64 <345600000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x1200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + qcom,spare-data = <1>; + }; + }; + + cpu_silver_opp_table: cpu-silver-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1900800000 { + opp-hz = /bits/ 64 <1900800000>; + required-opps = <&cprh_opp22>; + qcom,pll-override = <0x094f004f>; + qcom,spare-data = <3>; + }; + opp-1824000000 { + opp-hz = /bits/ 64 <1824000000>; + required-opps = <&cprh_opp21>; + qcom,pll-override = <0x084c004c>; + qcom,spare-data = <3>; + }; + opp-1747200000 { + opp-hz = /bits/ 64 <1747200000>; + required-opps = <&cprh_opp20>; + qcom,pll-override = <0x08490049>; + qcom,spare-data = <2>; + }; + opp-1670400000 { + opp-hz = /bits/ 64 <1670400000>; + required-opps = <&cprh_opp19>; + qcom,pll-override = <0x08460046>; + qcom,spare-data = <2>; + }; + opp-1555200000 { + opp-hz = /bits/ 64 <1555200000>; + required-opps = <&cprh_opp18>; + qcom,pll-override = <0x07410041>; + qcom,spare-data = <2>; + }; + opp-1478400000 { + opp-hz = /bits/ 64 <1478400000>; + required-opps = <&cprh_opp17>; + qcom,pll-override = <0x073e003e>; + qcom,spare-data = <2>; + }; + opp-1401600000 { + opp-hz = /bits/ 64 <1401600000>; + required-opps = <&cprh_opp16>; + qcom,pll-override = <0x063a003a>; + qcom,spare-data = <2>; + }; + opp-1324800000 { + opp-hz = /bits/ 64 <1324800000>; + required-opps = <&cprh_opp15>; + qcom,pll-override = <0x06370037>; + qcom,spare-data = <2>; + }; + opp-1248000000 { + opp-hz = /bits/ 64 <1248000000>; + required-opps = <&cprh_opp14>; + qcom,pll-override = <0x05340034>; + qcom,spare-data = <2>; + }; + opp-1171200000 { + opp-hz = /bits/ 64 <1171200000>; + required-opps = <&cprh_opp13>; + qcom,pll-override = <0x05310031>; + qcom,spare-data = <2>; + }; + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cprh_opp12>; + qcom,pll-override = <0x052e002e>; + qcom,spare-data = <2>; + }; + opp-1036800000 { + opp-hz = /bits/ 64 <1036800000>; + required-opps = <&cprh_opp11>; + qcom,pll-override = <0x042b002b>; + qcom,spare-data = <1>; + }; + opp-960000000 { + opp-hz = /bits/ 64 <960000000>; + required-opps = <&cprh_opp10>; + qcom,pll-override = <0x4280028>; + qcom,spare-data = <1>; + }; + opp-883200000 { + opp-hz = /bits/ 64 <883200000>; + required-opps = <&cprh_opp9>; + qcom,pll-override = <0x4250025>; + qcom,spare-data = <1>; + }; + opp-825600000 { + opp-hz = /bits/ 64 <825600000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x3200022>; + qcom,spare-data = <1>; + }; + opp-748800000 { + opp-hz = /bits/ 64 <748800000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-672000000 { + opp-hz = /bits/ 64 <672000000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-595200000 { + opp-hz = /bits/ 64 <595200000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-518400000 { + opp-hz = /bits/ 64 <518400000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-441600000 { + opp-hz = /bits/ 64 <441600000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-364800000 { + opp-hz = /bits/ 64 <364800000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x1200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + }; + }; + + cprh_opp_table: cpr-opp-table { + compatible = "operating-points-v2-qcom-level"; + + CPRH_OPP_ENTRY(1, 1, 0, 0); + CPRH_OPP_ENTRY(2, 1, 0, 0); + CPRH_OPP_ENTRY(3, 1, 0, 0); + CPRH_OPP_ENTRY(4, 1, 0, 0); + CPRH_OPP_ENTRY(5, 1, 0, 0); + CPRH_OPP_ENTRY(6, 1, 0, 0); + CPRH_OPP_ENTRY(7, 1, 0, 0); + CPRH_OPP_ENTRY(8, 1, 0, 0); + CPRH_OPP_ENTRY(9, 2, 0, 0); + CPRH_OPP_ENTRY(10, 2, 0, 0); + CPRH_OPP_ENTRY(11, 2, 0, 0); + CPRH_OPP_ENTRY(12, 3 2, (-12000) (-8000), (-10000) (-10000)); + CPRH_OPP_ENTRY(13, 3, (-16000) (-16000), (-11000) (-10000)); + CPRH_OPP_ENTRY(14, 3, (-16000) (-12000), (-12000) (-11000)); + CPRH_OPP_ENTRY(15, 3, (-12000) (-16000), (-13000) (-12000)); + CPRH_OPP_ENTRY(16, 3, (-12000) (-16000), (-14000) (-12000)); + CPRH_OPP_ENTRY(17, 3, (-16000) (-12000), (-14000) (-13000)); + CPRH_OPP_ENTRY(18, 3, (-16000) (-16000), (-15000) (-14000)); + CPRH_OPP_ENTRY(19, 4 3, (-20000) (-16000), (-21000) (-14000)); + CPRH_OPP_ENTRY(20, 4 3, (-24000) (-16000), (-24000) (-15000)); + CPRH_OPP_ENTRY(21, 4, (-28000) (-24000), (-26000) (-16000)); + CPRH_OPP_ENTRY(22, 4, (-28000) (-16000), (-28000) (-16000)); + CPRH_OPP_ENTRY(23, 4, 0 (-20000), 0 (-17000)); + CPRH_OPP_ENTRY(24, 4, 0 (-16000), 0 (-15000)); + CPRH_OPP_ENTRY(25, 4, 0 (-12000), 0 (-14000)); + CPRH_OPP_ENTRY(26, 4, 0 (-28000), 0 (-27000)); + CPRH_OPP_ENTRY(27, 4, 0 (-28000), 0 (-27000)); + CPRH_OPP_ENTRY(28, 4, 0 (-28000), 0 (-28000)); + CPRH_OPP_ENTRY(29, 4, 0 (-28000), 0 (-28000)); + CPRH_OPP_ENTRY(30, 4, 0 (-28000), 0 (-28000)); + }; + firmware { scm { compatible = "qcom,scm-msm8998", "qcom,scm"; @@ -870,7 +1291,7 @@ qfprom: qfprom@784000 { compatible = "qcom,qfprom"; - reg = <0x00784000 0x621c>; + reg = <0x00784000 0x221c>; #address-cells = <1>; #size-cells = <1>; @@ -878,6 +1299,186 @@ reg = <0x23a 0x1>; bits = <0 4>; }; + + cpr_efuse_speedbin: speedbin@133 { + reg = <0x133 0x2>; + bits = <5 3>; + }; + + cpr_fuse_revision: cpr_fusing_rev@13e { + reg = <0x13E 0x1>; + bits = <3 3>; + }; + + /* CPR Ring Oscillator: Power Cluster */ + cpr_ro_sel0_pwrcl: rosel0_pwrcl@219 { + reg = <0x219 0x2>; + bits = <4 4>; + }; + + cpr_ro_sel1_pwrcl: rosel1_pwrcl@219 { + reg = <0x219 0x1>; + bits = <0 4>; + }; + + cpr_ro_sel2_pwrcl: rosel2_pwrcl@218 { + reg = <0x218 0x2>; + bits = <4 4>; + }; + + cpr_ro_sel3_pwrcl: rosel3_pwrcl@218 { + reg = <0x218 0x1>; + bits = <0 4>; + }; + + /* CPR Init Voltage: Power Cluster */ + cpr_init_voltage0_pwrcl: ivolt0_pwrcl@21c { + reg = <0x21C 0x2>; + bits = <2 6>; + }; + + cpr_init_voltage1_pwrcl: ivolt1_pwrcl@21b { + reg = <0x21B 0x2>; + bits = <4 6>; + }; + + cpr_init_voltage2_pwrcl: ivolt2_pwrcl@21a { + reg = <0x21A 0x2>; + bits = <6 6>; + }; + + cpr_init_voltage3_pwrcl: ivolt3_pwrcl@21a { + reg = <0x21A 0x1>; + bits = <0 6>; + }; + + /* CPR Target Quotients: Power Cluster */ + cpr_quot0_pwrcl: quot0_pwrcl@222 { + reg = <0x222 0x3>; + bits = <2 12>; + }; + + cpr_quot1_pwrcl: quot1_pwrcl@220 { + reg = <0x220 0x3>; + bits = <6 12>; + }; + + cpr_quot2_pwrcl: quot2_pwrcl@21f { + reg = <0x21F 0x2>; + bits = <2 11>; + }; + + cpr_quot3_pwrcl: quot3_pwrcl@21d { + reg = <0x21D 0x3>; + bits = <6 12>; + }; + + /* CPR Quotient Offsets: Power Cluster */ + cpr_quot_offset1_pwrcl: qoff1_pwrcl@227 { + reg = <0x227 0x2>; + bits = <7 6>; + }; + + cpr_quot_offset2_pwrcl: qoff2_pwrcl@227 { + reg = <0x227 0x1>; + bits = <0 7>; + }; + + cpr_quot_offset3_pwrcl: qoff3_pwrcl@226 { + reg = <0x226 0x2>; + bits = <1 7>; + }; + + /* CPR Aging Quotient Offsets: Power Cluster */ + cpr_aging_quot_off_pwrcl: qoff_aging_pwrcl@228 { + reg = <0x228 0x2>; + bits = <6 8>; + }; + + /* CPR Ring Oscillator: Performance Cluster */ + cpr_ro_sel0_perfcl: rosel0_perfcl@22b { + reg = <0x22B 0x1>; + bits = <2 4>; + }; + + cpr_ro_sel1_perfcl: rosel1_perfcl@22a { + reg = <0x22A 0x2>; + bits = <6 4>; + }; + + cpr_ro_sel2_perfcl: rosel2_perfcl@22a { + reg = <0x22A 0x1>; + bits = <2 4>; + }; + + cpr_ro_sel3_perfcl: rosel3_perfcl@229 { + reg = <0x229 0x2>; + bits = <6 4>; + }; + + /* CPR Init Voltage: Performance Cluster */ + cpr_init_voltage0_perfcl: ivolt0_perfcl@22e { + reg = <0x22E 0x1>; + bits = <0 6>; + }; + + cpr_init_voltage1_perfcl: ivolt1_perfcl@22d { + reg = <0x22D 0x2>; + bits = <2 6>; + }; + + cpr_init_voltage2_perfcl: ivolt2_perfcl@22c { + reg = <0x22C 0x2>; + bits = <4 6>; + }; + + cpr_init_voltage3_perfcl: ivolt3_perfcl@22b { + reg = <0x22B 0x2>; + bits = <6 6>; + }; + + /* CPR Target Quotients: Performance Cluster */ + cpr_quot0_perfcl: quot0_perfcl@234 { + reg = <0x234 0x2>; + bits = <0 12>; + }; + + cpr_quot1_perfcl: quot1_perfcl@232 { + reg = <0x232 0x2>; + bits = <4 12>; + }; + + cpr_quot2_perfcl: quot2_perfcl@231 { + reg = <0x231 0x2>; + bits = <0 12>; + }; + + cpr_quot3_perfcl: quot3_perfcl@22f { + reg = <0x22F 0x2>; + bits = <4 11>; + }; + + /* CPR Quotient Offsets: Performance Cluster */ + cpr_quot_offset1_perfcl: qoff1_perfcl@239 { + reg = <0x239 0x2>; + bits = <5 3>; + }; + + cpr_quot_offset2_perfcl: qoff2_perfcl@238 { + reg = <0x238 0x2>; + bits = <6 7>; + }; + + cpr_quot_offset3_perfcl: qoff3_perfcl@237 { + reg = <0x237 0x2>; + bits = <7 7>; + }; + + /* CPR Aging Quotient Offsets: Performance Cluster */ + cpr_aging_quot_off_perfcl: qoff_aging_perfcl@23b { + reg = <0x23b 0x2>; + bits = <1 8>; + }; }; bimc: interconnect@1008000 { @@ -2896,6 +3497,38 @@ clocks = <&xo>, <&xo>; }; + power-controller@17812000 { + compatible = "qcom,msm8998-gold-saw2-v4.1-l2", + "qcom,saw2"; + reg = <0x017812000 0x1000>; + }; + + power-controller@17912000 { + compatible = "qcom,msm8998-silver-saw2-v4.1-l2", + "qcom,saw2"; + reg = <0x017912000 0x1000>; + }; + + cpufreq_hw: cpufreq_hw@17814800 { + compatible = "qcom,cpufreq-hw-8998"; + reg = <0x017914800 0x100>, <0x017814800 0x100>, + <0x0179c0000 0x1000>, <0x0179c1000 0x1000>, + <0x0179c2000 0x1000>, <0x0179c3000 0x1000>; + reg-names = "osm-acd0", "osm-acd1", + "osm-domain0", "freq-domain0", + "osm-domain1", "freq-domain1"; + + assigned-clocks = <&gcc HMSS_GPLL0_CLK_SRC>; + assigned-clock-rates = <300000000>; + + clocks = <&rpmcc RPM_SMD_XO_A_CLK_SRC>, + <&gcc HMSS_GPLL0_CLK_SRC>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; + status = "disabled"; + }; + apcs_glb: mailbox@17911000 { compatible = "qcom,msm8998-apcs-hmss-global"; reg = <0x17911000 0x1000>; @@ -2961,6 +3594,92 @@ }; }; + /* Gold and Silver cluster */ + apc_cprh: power-controller@179c8000 { + compatible = "qcom,msm8998-cprh"; + reg = <0x0179c8000 0x4000>, <0x0179c4000 0x4000>; + + assigned-clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + assigned-clock-rates = <19200000>; + clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + clock-names = "ref"; + + operating-points-v2 = <&cprh_opp_table>; + power-domains = <&rpmpd MSM8998_VDDCX_AO>; + #power-domain-cells = <1>; + status = "disabled"; + + nvmem-cells = <&cpr_efuse_speedbin>, + <&cpr_fuse_revision>, + <&cpr_quot0_pwrcl>, + <&cpr_quot1_pwrcl>, + <&cpr_quot2_pwrcl>, + <&cpr_quot3_pwrcl>, + <&cpr_quot_offset1_pwrcl>, + <&cpr_quot_offset2_pwrcl>, + <&cpr_quot_offset3_pwrcl>, + <&cpr_init_voltage0_pwrcl>, + <&cpr_init_voltage1_pwrcl>, + <&cpr_init_voltage2_pwrcl>, + <&cpr_init_voltage3_pwrcl>, + <&cpr_ro_sel0_pwrcl>, + <&cpr_ro_sel1_pwrcl>, + <&cpr_ro_sel2_pwrcl>, + <&cpr_ro_sel3_pwrcl>, + <&cpr_aging_quot_off_pwrcl>, + <&cpr_quot0_perfcl>, + <&cpr_quot1_perfcl>, + <&cpr_quot2_perfcl>, + <&cpr_quot3_perfcl>, + <&cpr_quot_offset1_perfcl>, + <&cpr_quot_offset2_perfcl>, + <&cpr_quot_offset3_perfcl>, + <&cpr_init_voltage0_perfcl>, + <&cpr_init_voltage1_perfcl>, + <&cpr_init_voltage2_perfcl>, + <&cpr_init_voltage3_perfcl>, + <&cpr_ro_sel0_perfcl>, + <&cpr_ro_sel1_perfcl>, + <&cpr_ro_sel2_perfcl>, + <&cpr_ro_sel3_perfcl>, + <&cpr_aging_quot_off_perfcl>; + + nvmem-cell-names = "cpr_speed_bin", + "cpr_fuse_revision", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_quotient4", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_quotient_offset4", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_init_voltage4", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_ring_osc4", + "cpr0_aging_quotient", + "cpr1_quotient1", + "cpr1_quotient2", + "cpr1_quotient3", + "cpr1_quotient4", + "cpr1_quotient_offset2", + "cpr1_quotient_offset3", + "cpr1_quotient_offset4", + "cpr1_init_voltage1", + "cpr1_init_voltage2", + "cpr1_init_voltage3", + "cpr1_init_voltage4", + "cpr1_ring_osc1", + "cpr1_ring_osc2", + "cpr1_ring_osc3", + "cpr1_ring_osc4", + "cpr1_aging_quotient"; + }; + intc: interrupt-controller@17a00000 { compatible = "arm,gic-v3"; reg = <0x17a00000 0x10000>, /* GICD */ From fe7b8b61c636ca2f96c7fa59a73bcefe955101ca Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 3 Dec 2020 12:24:21 +0100 Subject: [PATCH 091/212] arm64: dts: qcom: pmi8998: Add node for WLED The PMI8998 PMIC has a WLED backlight controller, which is used on most MSM8998 and SDM845 based devices: add a base configuration for it and keep it disabled. This contains only the PMIC specific configuration that does not change across boards; parameters like number of strings, OVP and current limits are product specific and shall be specified in the product DT in order to achieve functionality. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index a600c25d6667..cff83299e8dd 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -51,5 +51,17 @@ status = "disabled"; }; + + pmi8998_wled: leds@d800 { + compatible = "qcom,pm660l-wled"; + reg = <0xd800 0xd900>; + interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ovp", "short"; + label = "backlight"; + + status = "disabled"; + }; + }; }; From 0e156db8007b83c801dd4dbf36b2b304160c5065 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 9 Jan 2021 14:37:35 +0100 Subject: [PATCH 092/212] drm/msm/dpu: Add a function to retrieve the current CTL status Add a function that returns whether the requested CTL is active or not: this will be used in a later commit to fix command mode panel issues. Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 6 ++++++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 64740ddb983e..3b6fd73eb3a8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -91,6 +91,11 @@ static inline void dpu_hw_ctl_trigger_start(struct dpu_hw_ctl *ctx) DPU_REG_WRITE(&ctx->hw, CTL_START, 0x1); } +static inline bool dpu_hw_ctl_is_started(struct dpu_hw_ctl *ctx) +{ + return !!(DPU_REG_READ(&ctx->hw, CTL_START) & BIT(0)); +} + static inline void dpu_hw_ctl_trigger_pending(struct dpu_hw_ctl *ctx) { trace_dpu_hw_ctl_trigger_prepare(ctx->pending_flush_mask, @@ -579,6 +584,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops, ops->get_pending_flush = dpu_hw_ctl_get_pending_flush; ops->get_flush_register = dpu_hw_ctl_get_flush_register; ops->trigger_start = dpu_hw_ctl_trigger_start; + ops->is_started = dpu_hw_ctl_is_started; ops->trigger_pending = dpu_hw_ctl_trigger_pending; ops->reset = dpu_hw_ctl_reset_control; ops->wait_reset_status = dpu_hw_ctl_wait_reset_status; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h index 806c171e5df2..ac1544474022 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h @@ -61,6 +61,13 @@ struct dpu_hw_ctl_ops { */ void (*trigger_start)(struct dpu_hw_ctl *ctx); + /** + * check if the ctl is started + * @ctx : ctl path ctx pointer + * @Return: true if started, false if stopped + */ + bool (*is_started)(struct dpu_hw_ctl *ctx); + /** * kickoff prepare is in progress hw operation for sw * controlled interfaces: DSI cmd mode and WB interface From 8580c66f3c1b3ec2525b670e81d8d2f83280aa25 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 9 Jan 2021 14:37:36 +0100 Subject: [PATCH 093/212] drm/msm/dpu: Fix timeout issues on command mode panels In function dpu_encoder_phys_cmd_wait_for_commit_done we are always checking if the relative CTL is started by waiting for an interrupt to fire: it is fine to do that, but then sometimes we call this function while the CTL is up and has never been put down, but that interrupt gets raised only when the CTL gets a state change from 0 to 1 (disabled to enabled), so we're going to wait for something that will never happen on its own. Solving this while avoiding to restart the CTL is actually possible and can be done by just checking if it is already up and running when the wait_for_commit_done function is called: in this case, so, if the CTL was already running, we can say that the commit is done if the command transmission is complete (in other terms, if the interface has been flushed). Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index aa01698d6b25..b5b1b555ac4e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -682,6 +682,9 @@ static int dpu_encoder_phys_cmd_wait_for_commit_done( if (!dpu_encoder_phys_cmd_is_master(phys_enc)) return 0; + if (phys_enc->hw_ctl->ops.is_started) + return dpu_encoder_phys_cmd_wait_for_tx_complete(phys_enc); + return _dpu_encoder_phys_cmd_wait_for_ctl_start(phys_enc); } From 3fbf48ec45de88d91c9f3cd91dc83dbcf046a448 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 11 Dec 2020 19:24:41 +0100 Subject: [PATCH 094/212] clk: qcom: smd: Add missing MSM8998 RPM clocks Signed-off-by: Konrad Dybcio JAMI: fixed for a0384ecfe2aa ("clk: qcom: smd-rpm: De-duplicate identical entries") --- drivers/clk/qcom/clk-smd-rpm.c | 64 +++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 66d7807ee38e..ae2b901a634d 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -810,15 +810,20 @@ static const struct rpm_smd_clk_desc rpm_clk_qcs404 = { .num_clks = ARRAY_SIZE(qcs404_clks), }; -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, ln_bb_clk3_pin, ln_bb_clk3_a_pin, - 3); +DEFINE_CLK_SMD_RPM_BRANCH(msm8998, bi_tcxo, bi_tcxo_a, QCOM_SMD_RPM_MISC_CLK, 0, + 19200000); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk3, ln_bb_clk3_a, 3); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, ln_bb_clk3_pin, ln_bb_clk3_a_pin, 3); DEFINE_CLK_SMD_RPM(msm8998, aggre1_noc_clk, aggre1_noc_a_clk, QCOM_SMD_RPM_AGGR_CLK, 1); DEFINE_CLK_SMD_RPM(msm8998, aggre2_noc_clk, aggre2_noc_a_clk, QCOM_SMD_RPM_AGGR_CLK, 2); DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk3, rf_clk3_a, 6); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk3_pin, rf_clk3_a_pin, 6); + static struct clk_smd_rpm *msm8998_clks[] = { + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, @@ -831,12 +836,22 @@ static struct clk_smd_rpm *msm8998_clks[] = { [RPM_SMD_CE1_A_CLK] = &msm8992_ce1_a_clk, [RPM_SMD_DIV_CLK1] = &msm8974_div_clk1, [RPM_SMD_DIV_A_CLK1] = &msm8974_div_a_clk1, + [RPM_SMD_DIV_CLK2] = &msm8974_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8974_div_a_clk2, + [RPM_SMD_DIV_CLK3] = &msm8992_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8992_div_clk3_a, [RPM_SMD_IPA_CLK] = &msm8976_ipa_clk, [RPM_SMD_IPA_A_CLK] = &msm8976_ipa_a_clk, [RPM_SMD_LN_BB_CLK1] = &msm8916_bb_clk1, [RPM_SMD_LN_BB_CLK1_A] = &msm8916_bb_clk1_a, [RPM_SMD_LN_BB_CLK2] = &msm8916_bb_clk2, [RPM_SMD_LN_BB_CLK2_A] = &msm8916_bb_clk2_a, + [RPM_SMD_LN_BB_CLK3] = &msm8998_ln_bb_clk3, + [RPM_SMD_LN_BB_CLK3_A] = &msm8998_ln_bb_clk3_a, + [RPM_SMD_LN_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, + [RPM_SMD_LN_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, + [RPM_SMD_LN_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, + [RPM_SMD_LN_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, [RPM_SMD_LN_BB_CLK3_PIN] = &msm8998_ln_bb_clk3_pin, [RPM_SMD_LN_BB_CLK3_A_PIN] = &msm8998_ln_bb_clk3_a_pin, [RPM_SMD_MMAXI_CLK] = &msm8996_mmssnoc_axi_rpm_clk, @@ -849,10 +864,14 @@ static struct clk_smd_rpm *msm8998_clks[] = { [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, - [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, - [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, + [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, [RPM_SMD_RF_CLK3] = &msm8998_rf_clk3, [RPM_SMD_RF_CLK3_A] = &msm8998_rf_clk3_a, + [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, [RPM_SMD_RF_CLK3_PIN] = &msm8998_rf_clk3_pin, [RPM_SMD_RF_CLK3_A_PIN] = &msm8998_rf_clk3_a_pin, }; @@ -862,14 +881,9 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8998 = { .num_clks = ARRAY_SIZE(msm8998_clks), }; -DEFINE_CLK_SMD_RPM_BRANCH(sdm660, bi_tcxo, bi_tcxo_a, QCOM_SMD_RPM_MISC_CLK, 0, - 19200000); -DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk3, ln_bb_clk3_a, 3); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk3_pin, ln_bb_clk3_pin_a, 3); - static struct clk_smd_rpm *sdm660_clks[] = { - [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, - [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, [RPM_SMD_CNOC_CLK] = &msm8974_cnoc_clk, @@ -896,16 +910,16 @@ static struct clk_smd_rpm *sdm660_clks[] = { [RPM_SMD_LN_BB_A_CLK] = &msm8916_bb_clk1_a, [RPM_SMD_LN_BB_CLK2] = &msm8916_bb_clk2, [RPM_SMD_LN_BB_CLK2_A] = &msm8916_bb_clk2_a, - [RPM_SMD_LN_BB_CLK3] = &sdm660_ln_bb_clk3, - [RPM_SMD_LN_BB_CLK3_A] = &sdm660_ln_bb_clk3_a, + [RPM_SMD_LN_BB_CLK3] = &msm8998_ln_bb_clk3, + [RPM_SMD_LN_BB_CLK3_A] = &msm8998_ln_bb_clk3_a, [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, [RPM_SMD_LN_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, [RPM_SMD_LN_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, [RPM_SMD_LN_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, [RPM_SMD_LN_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, - [RPM_SMD_LN_BB_CLK3_PIN] = &sdm660_ln_bb_clk3_pin, - [RPM_SMD_LN_BB_CLK3_A_PIN] = &sdm660_ln_bb_clk3_pin_a, + [RPM_SMD_LN_BB_CLK3_PIN] = &msm8998_ln_bb_clk3_pin, + [RPM_SMD_LN_BB_CLK3_A_PIN] = &msm8998_ln_bb_clk3_a_pin, }; static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { @@ -914,8 +928,8 @@ static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { }; static struct clk_smd_rpm *mdm9607_clks[] = { - [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, - [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, @@ -936,8 +950,8 @@ static const struct rpm_smd_clk_desc rpm_clk_mdm9607 = { }; static struct clk_smd_rpm *msm8953_clks[] = { - [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, - [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, @@ -985,8 +999,8 @@ DEFINE_CLK_SMD_RPM(sm6125, snoc_lpass_clk, snoc_lpass_a_clk, QCOM_SMD_RPM_BUS_CLK, 5); static struct clk_smd_rpm *sm6125_clks[] = { - [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, - [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_SNOC_CLK] = &sm6125_snoc_clk, [RPM_SMD_SNOC_A_CLK] = &sm6125_snoc_a_clk, [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, @@ -1007,8 +1021,8 @@ static struct clk_smd_rpm *sm6125_clks[] = { [RPM_SMD_LN_BB_CLK1_A] = &msm8916_bb_clk1_a, [RPM_SMD_LN_BB_CLK2] = &msm8916_bb_clk2, [RPM_SMD_LN_BB_CLK2_A] = &msm8916_bb_clk2_a, - [RPM_SMD_LN_BB_CLK3] = &sdm660_ln_bb_clk3, - [RPM_SMD_LN_BB_CLK3_A] = &sdm660_ln_bb_clk3_a, + [RPM_SMD_LN_BB_CLK3] = &msm8998_ln_bb_clk3, + [RPM_SMD_LN_BB_CLK3_A] = &msm8998_ln_bb_clk3_a, [RPM_SMD_QUP_CLK] = &sm6125_qup_clk, [RPM_SMD_QUP_A_CLK] = &sm6125_qup_a_clk, [RPM_SMD_MMRT_CLK] = &sm6125_mmrt_clk, @@ -1028,8 +1042,8 @@ static const struct rpm_smd_clk_desc rpm_clk_sm6125 = { /* SM6115 */ static struct clk_smd_rpm *sm6115_clks[] = { - [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, - [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_XO_CLK_SRC] = &msm8998_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &msm8998_bi_tcxo_a, [RPM_SMD_SNOC_CLK] = &sm6125_snoc_clk, [RPM_SMD_SNOC_A_CLK] = &sm6125_snoc_a_clk, [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, From 22227d74fd8713efc863f02989da3fc9be883708 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 10 Dec 2020 17:27:40 +0100 Subject: [PATCH 095/212] drm/msm/dpu1: Add MSM8998 to hw catalog Bringup functionality for MSM8998 in the DPU, driver which is mostly the same as SDM845 (just a few variations). Signed-off-by: AngeloGioacchino Del Regno --- .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 331 +++++++++++++++++- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 1 + 2 files changed, 321 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 700d65e39feb..0868ab84f2e6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -16,6 +16,9 @@ BIT(DPU_SSPP_CSC_10BIT) | BIT(DPU_SSPP_CDP) |\ BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_EXCL_RECT)) +#define VIG_MSM8998_MASK \ + (VIG_MASK | BIT(DPU_SSPP_SCALER_QSEED3)) + #define VIG_SDM845_MASK \ (VIG_MASK | BIT(DPU_SSPP_QOS_8LVL) | BIT(DPU_SSPP_SCALER_QSEED3)) @@ -25,6 +28,11 @@ #define VIG_SM8250_MASK \ (VIG_MASK | BIT(DPU_SSPP_QOS_8LVL) | BIT(DPU_SSPP_SCALER_QSEED3LITE)) +#define DMA_MSM8998_MASK \ + (BIT(DPU_SSPP_SRC) | BIT(DPU_SSPP_QOS) |\ + BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_TS_PREFILL_REC1) |\ + BIT(DPU_SSPP_CDP) | BIT(DPU_SSPP_EXCL_RECT)) + #define DMA_SDM845_MASK \ (BIT(DPU_SSPP_SRC) | BIT(DPU_SSPP_QOS) | BIT(DPU_SSPP_QOS_8LVL) |\ BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_TS_PREFILL_REC1) |\ @@ -33,6 +41,9 @@ #define DMA_CURSOR_SDM845_MASK \ (DMA_SDM845_MASK | BIT(DPU_SSPP_CURSOR)) +#define DMA_CURSOR_MSM8998_MASK \ + (DMA_MSM8998_MASK | BIT(DPU_SSPP_CURSOR)) + #define MIXER_SDM845_MASK \ (BIT(DPU_MIXER_SOURCESPLIT) | BIT(DPU_DIM_LAYER)) @@ -49,6 +60,8 @@ #define MERGE_3D_SM8150_MASK (0) +#define DSPP_MSM8998_MASK BIT(DPU_DSPP_PCC) | BIT(DPU_DSPP_GC) + #define DSPP_SC7180_MASK BIT(DPU_DSPP_PCC) #define INTF_SDM845_MASK (0) @@ -181,6 +194,22 @@ static const uint32_t plane_formats_yuv[] = { * DPU sub blocks config *************************************************************/ /* DPU top level caps */ +static const struct dpu_caps msm8998_dpu_caps = { + .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_mixer_blendstages = 0x7, + .qseed_type = DPU_SSPP_SCALER_QSEED3, + .smart_dma_rev = DPU_SSPP_SMART_DMA_V1, + .ubwc_version = DPU_HW_UBWC_VER_10, + .has_src_split = true, + .has_dim_layer = true, + .has_idle_pc = true, + .has_3d_merge = true, + .max_linewidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE, + .max_hdeci_exp = MAX_HORZ_DECIMATION, + .max_vdeci_exp = MAX_VERT_DECIMATION, +}; + static const struct dpu_caps sdm845_dpu_caps = { .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, @@ -251,6 +280,35 @@ static const struct dpu_caps sc7280_dpu_caps = { .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE, }; +static const struct dpu_mdp_cfg msm8998_mdp[] = { + { + .name = "top_0", .id = MDP_TOP, + .base = 0x0, .len = 0x458, + .features = 0, + .highest_bank_bit = 0x2, + .clk_ctrls[DPU_CLK_CTRL_VIG0] = { + .reg_off = 0x2AC, .bit_off = 0}, + .clk_ctrls[DPU_CLK_CTRL_VIG1] = { + .reg_off = 0x2B4, .bit_off = 0}, + .clk_ctrls[DPU_CLK_CTRL_VIG2] = { + .reg_off = 0x2BC, .bit_off = 0}, + .clk_ctrls[DPU_CLK_CTRL_VIG3] = { + .reg_off = 0x2C4, .bit_off = 0}, + .clk_ctrls[DPU_CLK_CTRL_DMA0] = { + .reg_off = 0x2AC, .bit_off = 8}, + .clk_ctrls[DPU_CLK_CTRL_DMA1] = { + .reg_off = 0x2B4, .bit_off = 8}, + .clk_ctrls[DPU_CLK_CTRL_DMA2] = { + .reg_off = 0x2C4, .bit_off = 8}, + .clk_ctrls[DPU_CLK_CTRL_DMA3] = { + .reg_off = 0x2C4, .bit_off = 12}, + .clk_ctrls[DPU_CLK_CTRL_CURSOR0] = { + .reg_off = 0x3A8, .bit_off = 15}, + .clk_ctrls[DPU_CLK_CTRL_CURSOR1] = { + .reg_off = 0x3B0, .bit_off = 15}, + }, +}; + static const struct dpu_mdp_cfg sdm845_mdp[] = { { .name = "top_0", .id = MDP_TOP, @@ -339,6 +397,34 @@ static const struct dpu_mdp_cfg sc7280_mdp[] = { /************************************************************* * CTL sub blocks config *************************************************************/ +static const struct dpu_ctl_cfg msm8998_ctl[] = { + { + .name = "ctl_0", .id = CTL_0, + .base = 0x1000, .len = 0x94, + .features = BIT(DPU_CTL_SPLIT_DISPLAY) + }, + { + .name = "ctl_1", .id = CTL_1, + .base = 0x1200, .len = 0x94, + .features = 0 + }, + { + .name = "ctl_2", .id = CTL_2, + .base = 0x1400, .len = 0x94, + .features = BIT(DPU_CTL_SPLIT_DISPLAY) + }, + { + .name = "ctl_3", .id = CTL_3, + .base = 0x1600, .len = 0x94, + .features = 0 + }, + { + .name = "ctl_4", .id = CTL_4, + .base = 0x1800, .len = 0x94, + .features = 0 + }, +}; + static const struct dpu_ctl_cfg sdm845_ctl[] = { { .name = "ctl_0", .id = CTL_0, @@ -497,6 +583,15 @@ static const struct dpu_ctl_cfg sc7280_ctl[] = { .virt_num_formats = ARRAY_SIZE(plane_formats), \ } +static const struct dpu_sspp_sub_blks msm8998_vig_sblk_0 = + _VIG_SBLK("0", 0, DPU_SSPP_SCALER_QSEED3); +static const struct dpu_sspp_sub_blks msm8998_vig_sblk_1 = + _VIG_SBLK("1", 0, DPU_SSPP_SCALER_QSEED3); +static const struct dpu_sspp_sub_blks msm8998_vig_sblk_2 = + _VIG_SBLK("2", 0, DPU_SSPP_SCALER_QSEED3); +static const struct dpu_sspp_sub_blks msm8998_vig_sblk_3 = + _VIG_SBLK("3", 0, DPU_SSPP_SCALER_QSEED3); + static const struct dpu_sspp_sub_blks sdm845_vig_sblk_0 = _VIG_SBLK("0", 5, DPU_SSPP_SCALER_QSEED3); static const struct dpu_sspp_sub_blks sdm845_vig_sblk_1 = @@ -523,6 +618,25 @@ static const struct dpu_sspp_sub_blks sdm845_dma_sblk_3 = _DMA_SBLK("11", 4); .clk_ctrl = _clkctrl \ } +static const struct dpu_sspp_cfg msm8998_sspp[] = { + SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_MSM8998_MASK, + msm8998_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0), + SSPP_BLK("sspp_1", SSPP_VIG1, 0x6000, VIG_MSM8998_MASK, + msm8998_vig_sblk_1, 4, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG1), + SSPP_BLK("sspp_2", SSPP_VIG2, 0x8000, VIG_MSM8998_MASK, + msm8998_vig_sblk_2, 8, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG2), + SSPP_BLK("sspp_3", SSPP_VIG3, 0xa000, VIG_MSM8998_MASK, + msm8998_vig_sblk_3, 12, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG3), + SSPP_BLK("sspp_8", SSPP_DMA0, 0x24000, DMA_MSM8998_MASK, + sdm845_dma_sblk_0, 1, SSPP_TYPE_DMA, DPU_CLK_CTRL_DMA0), + SSPP_BLK("sspp_9", SSPP_DMA1, 0x26000, DMA_MSM8998_MASK, + sdm845_dma_sblk_1, 5, SSPP_TYPE_DMA, DPU_CLK_CTRL_DMA1), + SSPP_BLK("sspp_10", SSPP_DMA2, 0x28000, DMA_CURSOR_MSM8998_MASK, + sdm845_dma_sblk_2, 9, SSPP_TYPE_DMA, DPU_CLK_CTRL_CURSOR0), + SSPP_BLK("sspp_11", SSPP_DMA3, 0x2a000, DMA_CURSOR_MSM8998_MASK, + sdm845_dma_sblk_3, 13, SSPP_TYPE_DMA, DPU_CLK_CTRL_CURSOR1), +}; + static const struct dpu_sspp_cfg sdm845_sspp[] = { SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SDM845_MASK, sdm845_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0), @@ -599,17 +713,6 @@ static const struct dpu_sspp_cfg sc7280_sspp[] = { * MIXER sub blocks config *************************************************************/ -/* SDM845 */ - -static const struct dpu_lm_sub_blks sdm845_lm_sblk = { - .maxwidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH, - .maxblendstages = 11, /* excluding base layer */ - .blendstage_base = { /* offsets relative to mixer base */ - 0x20, 0x38, 0x50, 0x68, 0x80, 0x98, - 0xb0, 0xc8, 0xe0, 0xf8, 0x110 - }, -}; - #define LM_BLK(_name, _id, _base, _fmask, _sblk, _pp, _lmpair, _dspp) \ { \ .name = _name, .id = _id, \ @@ -621,6 +724,43 @@ static const struct dpu_lm_sub_blks sdm845_lm_sblk = { .dspp = _dspp \ } +/* MSM8998 */ + +static const struct dpu_lm_sub_blks msm8998_lm_sblk = { + .maxwidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .maxblendstages = 7, /* excluding base layer */ + .blendstage_base = { /* offsets relative to mixer base */ + 0x20, 0x50, 0x80, 0xb0, 0x230, + 0x260, 0x290 + }, +}; + +static const struct dpu_lm_cfg msm8998_lm[] = { + LM_BLK("lm_0", LM_0, 0x44000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_0, LM_2, DSPP_0), + LM_BLK("lm_1", LM_1, 0x45000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_1, LM_5, DSPP_1), + LM_BLK("lm_2", LM_2, 0x46000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_2, LM_0, 0), + LM_BLK("lm_3", LM_3, 0x47000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_MAX, 0, 0), + LM_BLK("lm_4", LM_4, 0x48000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_MAX, 0, 0), + LM_BLK("lm_5", LM_5, 0x49000, MIXER_SDM845_MASK, + &msm8998_lm_sblk, PINGPONG_3, LM_1, 0), +}; + +/* SDM845 */ + +static const struct dpu_lm_sub_blks sdm845_lm_sblk = { + .maxwidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .maxblendstages = 11, /* excluding base layer */ + .blendstage_base = { /* offsets relative to mixer base */ + 0x20, 0x38, 0x50, 0x68, 0x80, 0x98, + 0xb0, 0xc8, 0xe0, 0xf8, 0x110 + }, +}; + static const struct dpu_lm_cfg sdm845_lm[] = { LM_BLK("lm_0", LM_0, 0x44000, MIXER_SDM845_MASK, &sdm845_lm_sblk, PINGPONG_0, LM_1, 0), @@ -682,6 +822,13 @@ static const struct dpu_lm_cfg sc7280_lm[] = { /************************************************************* * DSPP sub blocks config *************************************************************/ +static const struct dpu_dspp_sub_blks msm8998_dspp_sblk = { + .pcc = {.id = DPU_DSPP_PCC, .base = 0x1700, + .len = 0x90, .version = 0x10007}, + .gc = { .id = DPU_DSPP_GC, .base = 0x17c0, + .len = 0x90, .version = 0x10007}, +}; + static const struct dpu_dspp_sub_blks sc7180_dspp_sblk = { .pcc = {.id = DPU_DSPP_PCC, .base = 0x1700, .len = 0x90, .version = 0x10000}, @@ -700,6 +847,13 @@ static const struct dpu_dspp_sub_blks sm8150_dspp_sblk = { .sblk = _sblk \ } +static const struct dpu_dspp_cfg msm8998_dspp[] = { + DSPP_BLK("dspp_0", DSPP_0, 0x54000, DSPP_MSM8998_MASK, + &msm8998_dspp_sblk), + DSPP_BLK("dspp_1", DSPP_1, 0x56000, DSPP_MSM8998_MASK, + &msm8998_dspp_sblk), +}; + static const struct dpu_dspp_cfg sc7180_dspp[] = { DSPP_BLK("dspp_0", DSPP_0, 0x54000, DSPP_SC7180_MASK, &sc7180_dspp_sblk), @@ -836,6 +990,13 @@ static const struct dpu_pingpong_cfg sc7280_pp[] = { .intr_vsync = DPU_IRQ_IDX(_reg, _vsync_bit), \ } +static const struct dpu_intf_cfg msm8998_intf[] = { + INTF_BLK("intf_0", INTF_0, 0x6A000, INTF_DP, 0, 25, INTF_SDM845_MASK), + INTF_BLK("intf_1", INTF_1, 0x6A800, INTF_DSI, 0, 25, INTF_SDM845_MASK), + INTF_BLK("intf_2", INTF_2, 0x6B000, INTF_DSI, 1, 25, INTF_SDM845_MASK), + INTF_BLK("intf_3", INTF_3, 0x6B800, INTF_HDMI, 0, 25, INTF_SDM845_MASK), +}; + static const struct dpu_intf_cfg sdm845_intf[] = { INTF_BLK("intf_0", INTF_0, 0x6A000, INTF_DP, 0, 24, INTF_SDM845_MASK, MDP_SSPP_TOP0_INTR, 24, 25), INTF_BLK("intf_1", INTF_1, 0x6A800, INTF_DSI, 0, 24, INTF_SDM845_MASK, MDP_SSPP_TOP0_INTR, 26, 27), @@ -865,9 +1026,55 @@ static const struct dpu_intf_cfg sc7280_intf[] = { * VBIF sub blocks config *************************************************************/ /* VBIF QOS remap */ +static const u32 msm8998_rt_pri_lvl[] = {1, 2, 2, 2}; +static const u32 msm8998_nrt_pri_lvl[] = {1, 1, 1, 1}; static const u32 sdm845_rt_pri_lvl[] = {3, 3, 4, 4, 5, 5, 6, 6}; static const u32 sdm845_nrt_pri_lvl[] = {3, 3, 3, 3, 3, 3, 3, 3}; +static const struct dpu_vbif_dynamic_ot_cfg msm8998_ot_rdwr_cfg[] = { + { + .pps = 1088 * 1920 * 30, + .ot_limit = 2, + }, + { + .pps = 1088 * 1920 * 60, + .ot_limit = 6, + }, + { + .pps = 3840 * 2160 * 30, + .ot_limit = 16, + }, +}; + +static const struct dpu_vbif_cfg msm8998_vbif[] = { + { + .name = "vbif_0", .id = VBIF_0, + .base = 0, .len = 0x1040, + .default_ot_rd_limit = 32, + .default_ot_wr_limit = 32, + .features = BIT(DPU_VBIF_QOS_REMAP) | BIT(DPU_VBIF_QOS_OTLIM), + .xin_halt_timeout = 0x4000, + .dynamic_ot_rd_tbl = { + .count = ARRAY_SIZE(msm8998_ot_rdwr_cfg), + .cfg = msm8998_ot_rdwr_cfg, + }, + .dynamic_ot_wr_tbl = { + .count = ARRAY_SIZE(msm8998_ot_rdwr_cfg), + .cfg = msm8998_ot_rdwr_cfg, + }, + .qos_rt_tbl = { + .npriority_lvl = ARRAY_SIZE(msm8998_rt_pri_lvl), + .priority_lvl = msm8998_rt_pri_lvl, + }, + .qos_nrt_tbl = { + .npriority_lvl = ARRAY_SIZE(msm8998_nrt_pri_lvl), + .priority_lvl = msm8998_nrt_pri_lvl, + }, + .memtype_count = 14, + .memtype = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + }, +}; + static const struct dpu_vbif_cfg sdm845_vbif[] = { { .name = "vbif_0", .id = VBIF_0, @@ -908,6 +1115,22 @@ static const struct dpu_reg_dma_cfg sm8250_regdma = { *************************************************************/ /* SSPP QOS LUTs */ +static const struct dpu_qos_lut_entry msm8998_qos_linear[] = { + {.fl = 4, .lut = 0x1b}, + {.fl = 5, .lut = 0x5b}, + {.fl = 6, .lut = 0x15b}, + {.fl = 7, .lut = 0x55b}, + {.fl = 8, .lut = 0x155b}, + {.fl = 9, .lut = 0x555b}, + {.fl = 10, .lut = 0x1555b}, + {.fl = 11, .lut = 0x5555b}, + {.fl = 12, .lut = 0x15555b}, + {.fl = 13, .lut = 0x55555b}, + {.fl = 14, .lut = 0}, + {.fl = 1, .lut = 0x1b}, + {.fl = 0, .lut = 0} +}; + static const struct dpu_qos_lut_entry sdm845_qos_linear[] = { {.fl = 4, .lut = 0x357}, {.fl = 5, .lut = 0x3357}, @@ -923,6 +1146,15 @@ static const struct dpu_qos_lut_entry sdm845_qos_linear[] = { {.fl = 0, .lut = 0x11222222223357} }; +static const struct dpu_qos_lut_entry msm8998_qos_macrotile[] = { + {.fl = 10, .lut = 0x1aaff}, + {.fl = 11, .lut = 0x5aaff}, + {.fl = 12, .lut = 0x15aaff}, + {.fl = 13, .lut = 0x55aaff}, + {.fl = 1, .lut = 0x1aaff}, + {.fl = 0, .lut = 0}, +}; + static const struct dpu_qos_lut_entry sc7180_qos_linear[] = { {.fl = 0, .lut = 0x0011222222335777}, }; @@ -944,6 +1176,10 @@ static const struct dpu_qos_lut_entry sc7180_qos_macrotile[] = { {.fl = 0, .lut = 0x0011223344556677}, }; +static const struct dpu_qos_lut_entry msm8998_qos_nrt[] = { + {.fl = 0, .lut = 0x0}, +}; + static const struct dpu_qos_lut_entry sdm845_qos_nrt[] = { {.fl = 0, .lut = 0x0}, }; @@ -952,6 +1188,48 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = { {.fl = 0, .lut = 0x0}, }; +static const struct dpu_perf_cfg msm8998_perf_data = { + .max_bw_low = 6700000, + .max_bw_high = 6700000, + .min_core_ib = 2400000, + .min_llcc_ib = 800000, + .min_dram_ib = 800000, + .core_ib_ff = "6.0", + .core_clk_ff = "1.0", + .comp_ratio_rt = + "NV12/5/1/1.23 AB24/5/1/1.23 XB24/5/1/1.23", + .comp_ratio_nrt = + "NV12/5/1/1.25 AB24/5/1/1.25 XB24/5/1/1.25", + .undersized_prefill_lines = 2, + .xtra_prefill_lines = 2, + .dest_scale_prefill_lines = 3, + .macrotile_prefill_lines = 4, + .yuv_nv12_prefill_lines = 8, + .linear_prefill_lines = 1, + .downscaling_prefill_lines = 1, + .amortizable_threshold = 25, + .min_prefill_lines = 25, + .danger_lut_tbl = {0xf, 0xffff, 0x0}, + .safe_lut_tbl = {0xfffc, 0xff00, 0xffff}, + .qos_lut_tbl = { + {.nentry = ARRAY_SIZE(msm8998_qos_linear), + .entries = msm8998_qos_linear + }, + {.nentry = ARRAY_SIZE(msm8998_qos_macrotile), + .entries = msm8998_qos_macrotile + }, + {.nentry = ARRAY_SIZE(msm8998_qos_nrt), + .entries = msm8998_qos_nrt + }, + }, + .cdp_cfg = { + {.rd_enable = 1, .wr_enable = 1}, + {.rd_enable = 1, .wr_enable = 0} + }, + .clk_inefficiency_factor = 200, + .bw_inefficiency_factor = 120, +}; + static const struct dpu_perf_cfg sdm845_perf_data = { .max_bw_low = 6800000, .max_bw_high = 6800000, @@ -1112,6 +1390,35 @@ static const struct dpu_perf_cfg sc7280_perf_data = { * Hardware catalog init *************************************************************/ +/* + * msm8998_cfg_init(): populate sdm845 dpu sub-blocks reg offsets + * and instance counts. + */ +static void msm8998_cfg_init(struct dpu_mdss_cfg *dpu_cfg) +{ + *dpu_cfg = (struct dpu_mdss_cfg){ + .caps = &msm8998_dpu_caps, + .mdp_count = ARRAY_SIZE(msm8998_mdp), + .mdp = msm8998_mdp, + .ctl_count = ARRAY_SIZE(msm8998_ctl), + .ctl = msm8998_ctl, + .sspp_count = ARRAY_SIZE(msm8998_sspp), + .sspp = msm8998_sspp, + .mixer_count = ARRAY_SIZE(msm8998_lm), + .mixer = msm8998_lm, + .pingpong_count = ARRAY_SIZE(sdm845_pp), + .pingpong = sdm845_pp, + .intf_count = ARRAY_SIZE(msm8998_intf), + .intf = msm8998_intf, + .vbif_count = ARRAY_SIZE(msm8998_vbif), + .vbif = msm8998_vbif, + .reg_dma_count = 0, + .perf = msm8998_perf_data, + /* There are no AD4 interrupts (bits 8-9) */ + .mdss_irqs = 0xff, + }; +} + /* * sdm845_cfg_init(): populate sdm845 dpu sub-blocks reg offsets * and instance counts. @@ -1262,6 +1569,8 @@ static void sc7280_cfg_init(struct dpu_mdss_cfg *dpu_cfg) } static const struct dpu_mdss_hw_cfg_handler cfg_handler[] = { + { .hw_rev = DPU_HW_VER_300, .cfg_init = msm8998_cfg_init}, + { .hw_rev = DPU_HW_VER_301, .cfg_init = msm8998_cfg_init}, { .hw_rev = DPU_HW_VER_400, .cfg_init = sdm845_cfg_init}, { .hw_rev = DPU_HW_VER_401, .cfg_init = sdm845_cfg_init}, { .hw_rev = DPU_HW_VER_500, .cfg_init = sm8150_cfg_init}, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index ae48f41821cf..c3305f10ca13 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1270,6 +1270,7 @@ static const struct dev_pm_ops dpu_pm_ops = { }; static const struct of_device_id dpu_dt_match[] = { + { .compatible = "qcom,msm8998-dpu", }, { .compatible = "qcom,sdm845-dpu", }, { .compatible = "qcom,sc7180-dpu", }, { .compatible = "qcom,sc7280-dpu", }, From 3b3668af631fd31167578d59fff939b509db59b6 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 13 Oct 2020 18:58:43 +0200 Subject: [PATCH 096/212] media: i2c: Add driver for the Sony Exmor-RS IMX300 camera sensor This is a custom multi-aspect 25MegaPixels sensor from Sony, found in many Sony Xperia smartphones from various eras. The camera assembly for this sensor usually (at least in Xperia phones) has a lens that does not cover the entire sensor area, which means that the real corners are blind and that, in many lighting conditions, some more pixels in the corners are very getting obscured (as no decent amount of light can get in)... so, the maximum resolution that can produce a good image is: - In 4:3 aspect ratio, 5520x4160 (23.0MP) - In 16:9 aspect ratio, 5984x3392 (20.3MP). This sensor supports high frame rates (>=60FPS) when in binning mode and both RAW8 and RAW10 output modes. In this version of the driver, support has been provided for the following resolutions: W x H SZ MAX_FPS BINNING - 5520x4160 23.0MP 23 No - 5984x3392 20.3MP 26 No - 2992x1696 3.8MP 60 Yes - 1424x800 1.2MP 120 Yes Note 1: The "standard" camera assy for IMX300 also contains an actuator (to focus the image), but this driver only manages the actual image sensor. Note 2: The command tables for this sensor were reverse engineered from a downstream "userspace driver" that has been released in various versions on various Xperia smartphones. Register layout seems to be only vaguely similar to IMX219, which has a public datasheet from where some names for the figured out registers were taken and added to the driver: these names are probably not the right ones, but they surely represent the intended thing. Signed-off-by: AngeloGioacchino Del Regno --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx300.c | 3084 ++++++++++++++++++++++++++++++++++++ 3 files changed, 3098 insertions(+) create mode 100644 drivers/media/i2c/imx300.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 08feb3e8c1bf..b61c240d090e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -816,6 +816,19 @@ config VIDEO_IMX290 To compile this driver as a module, choose M here: the module will be called imx290. +config VIDEO_IMX300 + tristate "Sony IMX300 Exmor RS sensor support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX300 Exmor RS multi-aspect sensor. + + To compile this driver as a module, choose M here: the + module will be called imx300. + config VIDEO_IMX319 tristate "Sony IMX319 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 83268f20aa3a..2cacbf1d6885 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -123,6 +123,7 @@ obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o +obj-$(CONFIG_VIDEO_IMX300) += imx300.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX334) += imx334.o obj-$(CONFIG_VIDEO_IMX335) += imx335.o diff --git a/drivers/media/i2c/imx300.c b/drivers/media/i2c/imx300.c new file mode 100644 index 000000000000..e9eba755b063 --- /dev/null +++ b/drivers/media/i2c/imx300.c @@ -0,0 +1,3084 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony IMX300 Exmor RS multi-aspect image sensors. + * Copyright (C) 2020, AngeloGioacchino Del Regno + * + * + * Based on Sony imx219 camera driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX300_REG_VALUE_08BIT 1 +#define IMX300_REG_VALUE_16BIT 2 + +/* + * Supported external clock frequency is from (around) 6 to 26MHz + * but there is no information about how to configure this sensor + * for anything else but 24MHz, since there is no datasheet... + */ +#define IMX300_XCLK_FREQ_24M 24000000 + +/* Delay after XCLK/RESET during power up for sensor boot/stabilization */ +#define IMX300_XCLK_STABLE_DELAY_US 10000 +#define IMX300_XCLK_DELAY_RANGE_US 1000 +#define IMX300_XCLR_MIN_DELAY_US 25000 +#define IMX300_XCLR_DELAY_RANGE_US 1000 + +/* + * Pixel rates: max resolution + max FPS uses high bw; low resolution + * can use low bw in order to save power and limit sensor heating + */ +#define IMX300_HIGH_BW_PIXEL_RATE 624000000 +#define IMX300_LOW_BW_PIXEL_RATE 384000000 +#define IMX300_HIGH_BW_LINK_FREQ 780000000 +#define IMX300_LOW_BW_LINK_FREQ 480000000 + +/* + * About the Chip ID: + * + * IMX300 seems to be sort of flawed... scanning the registers reveals + * that there's no reg having the expected 0x300 ChipID, like literally + * all of the other Sony IMX sensors. + * There seem to be no meaningful values and, even during reverse + * engineering of three "userspace drivers" for this sensor, found on + * three different series of smartphones, there is no trace of any ChipID. + * These userspace drivers seem to be reading the Sensor ID at register + * 0x0016 and comparing it to zero: it looks like being a dummy and not + * reliable at all, since I imagine that *many* camera sensors may contain + * zero at that register address, but that's still better than nothing... + * + * After all, if you don't have this camera sensor attached to your board, + * you shouldn't be adding it in your DT... :( + */ +#define IMX300_REG_CHIP_ID 0x0016 +#define IMX300_CHIP_ID 0x0 + +#define IMX300_REG_MODE_SELECT 0x0100 +#define IMX300_MODE_STANDBY 0x00 +#define IMX300_MODE_STREAMING 0x01 + +/* Orientation: changes bayer order */ +#define IMX300_REG_ORIENTATION 0x0101 + +/* SW Reset: set to 1 to reset. Gets automatically set back to 0. */ +#define IMX300_REG_SW_RESET 0x0103 + +/* Data format */ +#define IMX300_REG_CSI_DATA_FORMAT_HI 0x0112 +#define IMX300_REG_CSI_DATA_FORMAT_LO 0x0113 +#define IMX300_CSI_DATA_FMT_8BIT 8 +#define IMX300_CSI_DATA_FMT_10BIT 10 + +/* Output Set-up */ +#define IMX300_REG_CSI_LANE_MODE 0x0114 +#define IMX300_CSI_LANE_MODE_2LANE BIT(0) +#define IMX300_CSI_LANE_MODE_4LANE (BIT(0) | BIT(1)) + +/* EXCK: Min 6MHz, Max 27MHz */ +#define IMX300_REG_EXCK_FREQ_MHZ 0x0136 +#define IMX300_REG_TEMP_SENSOR 0x0138 + +/* Exposure control */ +#define IMX300_REG_EXPOSURE 0x0202 +#define IMX300_EXPOSURE_MIN 4 +#define IMX300_EXPOSURE_STEP 1 +#define IMX300_EXPOSURE_DEFAULT 3556 +#define IMX300_EXPOSURE_MAX 65535 + +/* Analog gain control */ +#define IMX300_REG_ANALOG_GAIN 0x0204 +#define IMX300_ANA_GAIN_MIN 0 +#define IMX300_ANA_GAIN_MAX 0x1fff +#define IMX300_ANA_GAIN_STEP 1 +#define IMX300_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX300_REG_GR_DIGITAL_GAIN 0x020e +#define IMX300_REG_R_DIGITAL_GAIN 0x0210 +#define IMX300_REG_B_DIGITAL_GAIN 0x0212 +#define IMX300_REG_GB_DIGITAL_GAIN 0x0214 +#define IMX300_DGTL_GAIN_MIN 0 +#define IMX300_DGTL_GAIN_MAX 4095 +#define IMX300_DGTL_GAIN_DEFAULT 1024 +#define IMX300_DGTL_GAIN_STEP 1 + +/* Clock generator */ +#define IMX300_REG_OP_PIX_CLK_DIV 0x0309 +#define IMX300_REG_OP_SYS_CLK_DIV 0x030b + +/* V_TIMING internal */ +#define IMX300_REG_VTS 0x0340 +#define IMX300_VTS_MIN 41 +#define IMX300_VTS_MAX (131071 - IMX300_VTS_MIN) + +/* Line Length */ +#define IMX300_REG_LINE_LEN_PCK 0x0342 +#define IMX300_LINELEN_MIN 7000 +#define IMX300_LINELEN_MAX 8230 +#define IMX300_LINELEN_STEP 1 +#define IMX300_LINELEN_DEFAULT 7096 + +/* Output configuration */ +#define IMX300_REG_X_ADDR_START 0x0344 +#define IMX300_REG_Y_ADDR_START 0x0346 +#define IMX300_REG_X_ADDR_END 0x0348 +#define IMX300_REG_Y_ADDR_END 0x034a +#define IMX300_REG_X_OUTPUT_SIZE 0x034c +#define IMX300_REG_Y_OUTPUT_SIZE 0x034e +#define IMX300_REG_X_ODD_INC 0x0383 +#define IMX300_REG_Y_ODD_INC 0x0387 + +/* HBLANK control - read only */ +#define IMX300_PPL_DEFAULT 8224 + +/* Test Pattern Control */ +#define IMX300_REG_TEST_PATTERN 0x0600 +#define IMX300_REG_TEST_PATTERN_WIDTH 0x0624 +#define IMX300_REG_TEST_PATTERN_HEIGHT 0x0626 +#define IMX300_TEST_PATTERN_DISABLE 0 +#define IMX300_TEST_PATTERN_SOLID_COLOR 1 +#define IMX300_TEST_PATTERN_COLOR_BARS 2 +#define IMX300_TEST_PATTERN_GREY_COLOR 3 +#define IMX300_TEST_PATTERN_PN9 4 + +/* Test pattern colour components */ +#define IMX300_REG_TESTP_RED 0x0602 +#define IMX300_REG_TESTP_GREENR 0x0604 +#define IMX300_REG_TESTP_BLUE 0x0606 +#define IMX300_REG_TESTP_GREENB 0x0608 +#define IMX300_TESTP_COLOUR_MIN 0 +#define IMX300_TESTP_COLOUR_MAX 0x03ff +#define IMX300_TESTP_COLOUR_STEP 1 +#define IMX300_TESTP_RED_DEFAULT IMX300_TESTP_COLOUR_MAX +#define IMX300_TESTP_GREENR_DEFAULT 0 +#define IMX300_TESTP_BLUE_DEFAULT 0 +#define IMX300_TESTP_GREENB_DEFAULT 0 + +/* Binning */ +#define IMX300_REG_BINNING_EN 0x0900 +#define IMX300_REG_BINNING_TYPE 0x0902 + +/* + * ** IMX300 native and active pixel array size ** + * + * Being this a multi-aspect sensor, the following native W/H apply, but + * beware: the module assembly usually has a (round) lens that is shadowing + * or covering the corners of the (square) image sensor, so the maximum + * output resolution must be lower than the maximum sensor resolution + * otherwise we get something like a view from a porthole... :) + * + * For 4:3 aspect ratio, max is: 5984x4140 (25MP) + * For 16:9 aspect ratio, max is: 5984x3392 (20.3MP) + */ +#define IMX300_NATIVE_WIDTH 5984U +#define IMX300_NATIVE_HEIGHT 4140U +#define IMX300_PIXEL_ARRAY_LEFT 0U +#define IMX300_PIXEL_ARRAY_TOP 0U +#define IMX300_PIXEL_ARRAY_WIDTH 5984U +#define IMX300_PIXEL_ARRAY_HEIGHT 4140U + +struct imx300_reg { + u16 address; + u16 val; + u8 reg_len; +}; + +struct imx300_reg_list { + unsigned int num_of_regs; + const struct imx300_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx300_mode { + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + /* Maximum achievable FPS */ + unsigned int max_fps; + /* Needs high data rate */ + bool high_bw; + /* Needs binning setup */ + bool binned; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* V-timing default */ + unsigned int vts_def; + + /* Default register values */ + struct imx300_reg_list reg_list; +}; + +/* + * I have no idea what this very long initialization sequence is for... + * but missing writes in this makes the sensor to output corrupted + * frames or nothing at all... + */ +static const struct imx300_reg init_sequence[] = { + { IMX300_REG_EXCK_FREQ_MHZ, 24, IMX300_REG_VALUE_08BIT }, + { 0x0137, 0x00, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_ORIENTATION, 0x03, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_TEMP_SENSOR, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3154, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3155, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x3156, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3157, 0x66, IMX300_REG_VALUE_08BIT }, + { 0x305d, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x31b0, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3208, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3210, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x406c, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x406d, 0x39, IMX300_REG_VALUE_08BIT }, + { 0x430d, 0xaa, IMX300_REG_VALUE_08BIT }, + { 0x4313, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x431e, 0x88, IMX300_REG_VALUE_08BIT }, + { 0x431f, 0x8f, IMX300_REG_VALUE_08BIT }, + { 0x4321, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x4324, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x4325, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x4326, 0x28, IMX300_REG_VALUE_08BIT }, + { 0x4328, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0x4329, 0x5b, IMX300_REG_VALUE_08BIT }, + { 0x432c, 0x06, IMX300_REG_VALUE_08BIT }, + { 0x432d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x432e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x4554, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x4811, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4819, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x481d, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x481f, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4838, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x4839, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x483a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x483b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x4871, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4877, 0x9b, IMX300_REG_VALUE_08BIT }, + { 0x487d, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4883, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4891, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4893, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x48ad, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x48af, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x48b1, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x48b5, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x48bb, 0x9b, IMX300_REG_VALUE_08BIT }, + { 0x48c1, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x48c7, 0x9b, IMX300_REG_VALUE_08BIT }, + { 0x48c9, 0x95, IMX300_REG_VALUE_08BIT }, + { 0x48cb, 0x94, IMX300_REG_VALUE_08BIT }, + { 0x4bd7, 0x24, IMX300_REG_VALUE_08BIT }, + { 0x48dd, 0x95, IMX300_REG_VALUE_08BIT }, + { 0x48e5, 0x97, IMX300_REG_VALUE_08BIT }, + { 0x49d1, 0x9a, IMX300_REG_VALUE_08BIT }, + { 0x4a00, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x4a01, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x4a02, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x4a03, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x4a1d, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4a1f, 0x9b, IMX300_REG_VALUE_08BIT }, + { 0x4a27, 0x97, IMX300_REG_VALUE_08BIT }, + { 0x4a29, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x4a2f, 0x97, IMX300_REG_VALUE_08BIT }, + { 0x4a4d, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x4a4f, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4a51, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x4a53, 0x99, IMX300_REG_VALUE_08BIT }, + { 0x4a6d, 0x9e, IMX300_REG_VALUE_08BIT }, + { 0x4a6f, 0x9d, IMX300_REG_VALUE_08BIT }, + { 0x4be7, 0x24, IMX300_REG_VALUE_08BIT }, + { 0x4c07, 0x24, IMX300_REG_VALUE_08BIT }, + { 0x4c17, 0x24, IMX300_REG_VALUE_08BIT }, + { 0x4c97, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x4ca7, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x4cc7, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x4cd7, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x4d57, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x4d67, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x4d87, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x4d97, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x4db7, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x4dc7, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x4de7, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x4df7, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x4e15, 0x43, IMX300_REG_VALUE_08BIT }, + { 0x4e25, 0x43, IMX300_REG_VALUE_08BIT }, + { 0x4e45, 0x43, IMX300_REG_VALUE_08BIT }, + { 0x4e55, 0x43, IMX300_REG_VALUE_08BIT }, + { 0x4f40, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x4f42, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x4f48, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x4f4e, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x505c, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x505d, 0x36, IMX300_REG_VALUE_08BIT }, + { 0x505f, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x5069, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x5e6a, 0xfe, IMX300_REG_VALUE_08BIT }, + { 0x5e70, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6153, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6156, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x656a, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x656b, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x7300, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9006, 0x09, IMX300_REG_VALUE_08BIT }, + { 0xb000, 0x6e, IMX300_REG_VALUE_08BIT }, + { 0xb001, 0xfd, IMX300_REG_VALUE_08BIT }, + { 0xb002, 0x65, IMX300_REG_VALUE_08BIT }, + { 0xb003, 0x2a, IMX300_REG_VALUE_08BIT }, + { 0xb004, 0x83, IMX300_REG_VALUE_08BIT }, + { 0xb005, 0x41, IMX300_REG_VALUE_08BIT }, + { 0xb006, 0x83, IMX300_REG_VALUE_08BIT }, + { 0xb007, 0x43, IMX300_REG_VALUE_08BIT }, + { 0xb008, 0x82, IMX300_REG_VALUE_08BIT }, + { 0xb009, 0x49, IMX300_REG_VALUE_08BIT }, + { 0xb00a, 0x82, IMX300_REG_VALUE_08BIT }, + { 0xb00b, 0x4a, IMX300_REG_VALUE_08BIT }, + { 0xb00c, 0x6e, IMX300_REG_VALUE_08BIT }, + { 0xb00d, 0x93, IMX300_REG_VALUE_08BIT }, + { 0xb00e, 0x82, IMX300_REG_VALUE_08BIT }, + { 0xb00f, 0x41, IMX300_REG_VALUE_08BIT }, + { 0xb010, 0x82, IMX300_REG_VALUE_08BIT }, + { 0xb011, 0x4b, IMX300_REG_VALUE_08BIT }, + { 0xb100, 0x35, IMX300_REG_VALUE_08BIT }, + { 0xb101, 0x5e, IMX300_REG_VALUE_08BIT }, + { 0xb102, 0x80, IMX300_REG_VALUE_08BIT }, + { 0xb103, 0x9a, IMX300_REG_VALUE_08BIT }, + { 0xb104, 0xaf, IMX300_REG_VALUE_08BIT }, + { 0xb105, 0xc0, IMX300_REG_VALUE_08BIT }, + { 0xb106, 0xcd, IMX300_REG_VALUE_08BIT }, + { 0xb107, 0xd7, IMX300_REG_VALUE_08BIT }, + { 0xb123, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xb125, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xb127, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xb129, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xb12b, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xb136, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb137, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb138, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb139, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13a, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13b, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13c, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13d, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13e, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb13f, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb140, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xb141, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xb142, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xb143, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb144, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb145, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb146, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb147, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb148, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb149, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb14a, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb14b, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb14c, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb14d, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb14e, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb14f, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb150, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb151, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb152, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb153, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xb154, 0x03, IMX300_REG_VALUE_08BIT }, + { 0xb155, 0x03, IMX300_REG_VALUE_08BIT }, + { 0xb156, 0x03, IMX300_REG_VALUE_08BIT }, + { 0xb157, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb158, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb159, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb15a, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb15b, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb15c, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb15d, 0x05, IMX300_REG_VALUE_08BIT }, + { 0xb210, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x313c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3198, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x31a0, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31a1, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31a2, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31a3, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x31a8, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x3290, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6d20, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6d21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6d22, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6d23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6d26, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6d27, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6e07, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x6e35, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6e55, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6e7c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6e93, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6efb, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x7c68, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x845c, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x8469, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9257, 0x96, IMX300_REG_VALUE_08BIT }, + { 0x9258, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x933a, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x933b, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x933d, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x933e, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x933f, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x934b, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x934c, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9356, 0x8c, IMX300_REG_VALUE_08BIT }, + { 0x9357, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9358, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9359, 0x8c, IMX300_REG_VALUE_08BIT }, + { 0x935a, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x935b, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9360, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9361, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9362, 0x8c, IMX300_REG_VALUE_08BIT }, + { 0x9363, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9364, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9365, 0x8c, IMX300_REG_VALUE_08BIT }, + { 0x9366, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9367, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x940d, 0x07, IMX300_REG_VALUE_08BIT }, + { 0x940e, 0x07, IMX300_REG_VALUE_08BIT }, + { 0x9414, 0x06, IMX300_REG_VALUE_08BIT }, + { 0x942b, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x942c, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x942d, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x942e, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x942f, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9430, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9431, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9432, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9433, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9434, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9435, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9436, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9437, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9438, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9439, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x943b, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x943d, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x943f, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9441, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9443, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9445, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9447, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9449, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x944b, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x944d, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x944f, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9451, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9452, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9454, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9456, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9457, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9458, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9459, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x945a, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x945b, 0x07, IMX300_REG_VALUE_08BIT }, + { 0x945c, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x945d, 0x07, IMX300_REG_VALUE_08BIT }, + { 0x945e, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x945f, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9460, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9461, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9462, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9463, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9464, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9465, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9466, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x947c, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x947d, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9480, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9481, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9503, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x9504, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x9505, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x9506, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9507, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9508, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9526, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9527, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9528, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9619, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x961b, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x961d, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x961f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9621, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9623, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9625, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9627, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9629, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x962b, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x962d, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x962f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9719, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x971b, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x971d, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x971f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9721, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9723, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9725, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9727, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9729, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x972b, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x972d, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x972f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9901, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9903, 0x23, IMX300_REG_VALUE_08BIT }, + { 0x9905, 0x23, IMX300_REG_VALUE_08BIT }, + { 0x9906, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9907, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9908, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9909, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x990a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x990b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x990d, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x990f, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9911, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9913, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9915, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9917, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9919, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x991b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x991d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x991f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9921, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9923, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9925, 0x23, IMX300_REG_VALUE_08BIT }, + { 0x9927, 0x23, IMX300_REG_VALUE_08BIT }, + { 0x9929, 0x23, IMX300_REG_VALUE_08BIT }, + { 0x992b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x992d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x992f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9931, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9933, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9935, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9937, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9939, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x993b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9943, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9945, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9947, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9949, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x994b, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x994d, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x994f, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9951, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9953, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9955, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9957, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x9959, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x995a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x995b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x995c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x996b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x996d, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x996f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9971, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0x9973, 0x32, IMX300_REG_VALUE_08BIT }, + { 0x9975, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9976, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9979, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x997a, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x997b, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9981, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9983, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9985, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x99a4, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99a5, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99a6, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99a7, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99a8, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99a9, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99aa, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99ab, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99ac, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x99ad, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x99ae, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x99af, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x99b0, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x99b1, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x99b2, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x99b3, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x99b4, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x99b5, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x99bb, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99bd, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99bf, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x99c0, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x99c1, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x99c2, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x99c6, 0x3c, IMX300_REG_VALUE_08BIT }, + { 0x99c7, 0x3c, IMX300_REG_VALUE_08BIT }, + { 0x99c8, 0x3c, IMX300_REG_VALUE_08BIT }, + { 0x99c9, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x99ca, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x99cb, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9a01, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9a03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a07, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9a09, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9a0b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9a0d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a0f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a11, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a13, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9a15, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9a17, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9a19, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9a1b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9a1d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9a1f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9a21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9a23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9a25, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a27, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a29, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a2b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9a2d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9a2f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a31, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a33, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a35, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9a37, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9a39, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9a3b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9a3d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a3f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a41, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a4f, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9a51, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9a53, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9a55, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9a57, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x9a59, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x9a5a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a5b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a5c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a6b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9a6d, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9a6f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9a71, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0x9a73, 0x32, IMX300_REG_VALUE_08BIT }, + { 0x9a75, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9a79, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9a7a, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9a7b, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9a81, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a83, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9a85, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9aa4, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aa5, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aa6, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aa7, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9aa8, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9aa9, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9aaa, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aab, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aac, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9aad, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9aae, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9aaf, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ab0, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9ab1, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9ab2, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9ab3, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9ab4, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9ab5, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9ab6, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9ab7, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9ab8, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9abb, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9abd, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9abf, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9ac0, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9ac1, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9ac2, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9ac6, 0x2d, IMX300_REG_VALUE_08BIT }, + { 0x9ac7, 0x2d, IMX300_REG_VALUE_08BIT }, + { 0x9ac8, 0x2d, IMX300_REG_VALUE_08BIT }, + { 0x9ac9, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9aca, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9acb, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9b01, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9b03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b07, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9b08, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9b09, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9b0a, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9b0b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9b0d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b0f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b11, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b13, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9b15, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9b17, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9b19, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9b1b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9b1d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9b1f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9b21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9b23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9b25, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b27, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b29, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b2b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9b2d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9b2f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b31, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b33, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b35, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9b37, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9b39, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9b3b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9b43, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b45, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b47, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b49, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b4b, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b4d, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9b4f, 0x2d, IMX300_REG_VALUE_08BIT }, + { 0x9b51, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9b53, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9b55, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9b57, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0x9b59, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9b5a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9b5b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9b5c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9b5d, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9b5e, 0x0e, IMX300_REG_VALUE_08BIT }, + { 0x9b60, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9b61, 0x0e, IMX300_REG_VALUE_08BIT }, + { 0x9b6b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9b6d, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9b6f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9b71, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0x9b73, 0x32, IMX300_REG_VALUE_08BIT }, + { 0x9b75, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9b76, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9b79, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9b7a, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9b7b, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9b81, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b83, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9b85, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9bb0, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9bb1, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9bb2, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9bb3, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9bb4, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9bb5, 0x30, IMX300_REG_VALUE_08BIT }, + { 0x9bbb, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9bbd, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9bbf, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9bc0, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9bc1, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9bc2, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9bc6, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9bc7, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9bc8, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9bc9, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9bca, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9bcb, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9bcc, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9bcd, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9bce, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9c01, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9c03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c07, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9c09, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9c0b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9c0d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c0f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c11, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c13, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9c15, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9c17, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9c19, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9c1b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9c1d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9c1f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9c21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9c23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9c25, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c27, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c29, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c2b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9c2d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9c2f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c31, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c33, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c35, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9c37, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9c39, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9c3b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9c3d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c3f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c41, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c4f, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9c51, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9c53, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9c55, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9c57, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0x9c59, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9c5a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c5b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c5c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c6b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9c6d, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9c6f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9c71, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0x9c73, 0x32, IMX300_REG_VALUE_08BIT }, + { 0x9c75, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9c79, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9c7a, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9c7b, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9c81, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c83, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c85, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9c87, 0x48, IMX300_REG_VALUE_08BIT }, + { 0x9c89, 0x48, IMX300_REG_VALUE_08BIT }, + { 0x9c8b, 0x48, IMX300_REG_VALUE_08BIT }, + { 0x9ca4, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ca5, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ca6, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ca7, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ca8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9ca9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9caa, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9cab, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9cac, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9cad, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9cae, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9caf, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9cb0, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9cb1, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9cb2, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9cb3, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9cb4, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9cb5, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x9cbb, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9cbd, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9cbf, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9cc0, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9cc1, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9cc2, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x9cc6, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9cc7, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9cc8, 0x18, IMX300_REG_VALUE_08BIT }, + { 0x9cc9, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9cca, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9ccb, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x9d01, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9d03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d07, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9d09, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9d0b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9d0d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d0f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d11, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d13, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d15, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d17, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d19, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9d1b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9d1d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9d1f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d25, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d27, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d29, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d2b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9d2d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9d2f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d31, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d33, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d35, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d37, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9d39, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9d3b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9d3d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9d3f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9d41, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9d4f, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9d50, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9d51, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9d53, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9d55, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9d57, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x9d59, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x9d5b, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9d5d, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d5f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d61, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9d63, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d65, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9d67, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9d69, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9d6b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9d6d, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9d6f, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9d71, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9d73, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d75, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d77, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d79, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d7b, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d7d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9d7f, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d81, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d83, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d85, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d87, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d89, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9d8b, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9d8d, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9d8f, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9d91, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9d93, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9d95, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9d97, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d99, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d9b, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d9d, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9d9f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9da1, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e01, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9e03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e07, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9e09, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9e0b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9e0d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e0f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e11, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e13, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e15, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e17, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e19, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9e1b, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9e1d, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9e1f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e21, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e23, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e25, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e27, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e29, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e2b, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9e2d, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9e2f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e31, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e33, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e35, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e37, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9e39, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9e3b, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9e3d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9e3f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9e41, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9e4f, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9e51, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9e53, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9e55, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9e57, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0x9e59, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x9e5b, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9e5d, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e5f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e61, 0x35, IMX300_REG_VALUE_08BIT }, + { 0x9e63, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e65, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9e67, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9e69, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9e6b, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9e6d, 0x31, IMX300_REG_VALUE_08BIT }, + { 0x9e6f, 0x1b, IMX300_REG_VALUE_08BIT }, + { 0x9e71, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x9e73, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e75, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e77, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e79, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e7b, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e7d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9e7f, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e81, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e83, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e85, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e87, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e89, 0x64, IMX300_REG_VALUE_08BIT }, + { 0x9e8b, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9e8d, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9e8f, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9e91, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x9e93, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x9e95, 0x65, IMX300_REG_VALUE_08BIT }, + { 0x9e97, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e99, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e9b, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e9d, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9e9f, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9ea1, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x9f01, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f05, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f07, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f09, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f0b, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f0d, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9f0f, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9f11, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f13, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0x9f15, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x9f17, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f19, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f1b, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f1d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f1f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f21, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f23, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x9f25, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9f27, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9f29, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9f2b, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0x9f2d, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0x9f2f, 0x81, IMX300_REG_VALUE_08BIT }, + { 0x9f31, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f33, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f35, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f37, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f39, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f3b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f3c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f3d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f3e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f41, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f43, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f45, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f47, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0x9f49, 0x32, IMX300_REG_VALUE_08BIT }, + { 0x9f4b, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9f4d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f4f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f51, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f53, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f55, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f57, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x9f59, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9f5b, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9f5d, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9f5f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x9f61, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9f63, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x9f64, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9f65, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9f66, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0x9f6a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f6b, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f6c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f6d, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f6e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f6f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x9f77, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9f78, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f79, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9f7b, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9f7d, 0x42, IMX300_REG_VALUE_08BIT }, + { 0x9f7e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9f7f, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x9f81, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0x9f83, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9f85, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x9f87, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x9f89, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0x9f8b, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x9f8d, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x9f8f, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0x9fa6, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9fa7, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9fa8, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9fa9, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9faa, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9fab, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x9fac, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9fad, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9fae, 0x3f, IMX300_REG_VALUE_08BIT }, + { 0x9faf, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9fb0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x9fb1, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa001, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa003, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa005, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa007, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa009, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa00b, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa00d, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0xa00f, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0xa011, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa013, 0x2f, IMX300_REG_VALUE_08BIT }, + { 0xa015, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0xa017, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa019, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa01b, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa01d, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa01f, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa021, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa023, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0xa025, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0xa027, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0xa029, 0x81, IMX300_REG_VALUE_08BIT }, + { 0xa02b, 0x6b, IMX300_REG_VALUE_08BIT }, + { 0xa02d, 0x7c, IMX300_REG_VALUE_08BIT }, + { 0xa02f, 0x81, IMX300_REG_VALUE_08BIT }, + { 0xa031, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa033, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa035, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa037, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa039, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa03b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa03c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa03d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa03e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa041, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa043, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa045, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa047, 0xc8, IMX300_REG_VALUE_08BIT }, + { 0xa049, 0x32, IMX300_REG_VALUE_08BIT }, + { 0xa04b, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xa04d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa04f, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa051, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa053, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa055, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa057, 0x10, IMX300_REG_VALUE_08BIT }, + { 0xa059, 0x20, IMX300_REG_VALUE_08BIT }, + { 0xa05b, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xa05d, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xa05f, 0x20, IMX300_REG_VALUE_08BIT }, + { 0xa061, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xa063, 0x04, IMX300_REG_VALUE_08BIT }, + { 0xa064, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0xa065, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0xa066, 0xe0, IMX300_REG_VALUE_08BIT }, + { 0xa067, 0x20, IMX300_REG_VALUE_08BIT }, + { 0xa068, 0x20, IMX300_REG_VALUE_08BIT }, + { 0xa069, 0x20, IMX300_REG_VALUE_08BIT }, + { 0xa06b, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa06d, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa06f, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xa071, 0x48, IMX300_REG_VALUE_08BIT }, + { 0xa073, 0x48, IMX300_REG_VALUE_08BIT }, + { 0xa075, 0x48, IMX300_REG_VALUE_08BIT }, + { 0xa077, 0x42, IMX300_REG_VALUE_08BIT }, + { 0xa079, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0xa07b, 0x08, IMX300_REG_VALUE_08BIT }, + { 0xa07d, 0x42, IMX300_REG_VALUE_08BIT }, + { 0xa07f, 0x0b, IMX300_REG_VALUE_08BIT }, + { 0xa081, 0x08, IMX300_REG_VALUE_08BIT }, + { 0xa083, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0xa085, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0xa087, 0x08, IMX300_REG_VALUE_08BIT }, + { 0xa089, 0x5a, IMX300_REG_VALUE_08BIT }, + { 0xa08b, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0xa08d, 0x08, IMX300_REG_VALUE_08BIT }, + { 0xa08f, 0xa0, IMX300_REG_VALUE_08BIT }, + { 0xa091, 0x3a, IMX300_REG_VALUE_08BIT }, + { 0xa093, 0x3a, IMX300_REG_VALUE_08BIT }, + { 0xa095, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xa097, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xa099, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0xa0a9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa0aa, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa0ab, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa0af, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa0b0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xa0b1, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xf800, 0x5c, IMX300_REG_VALUE_08BIT }, + { 0xf801, 0x5c, IMX300_REG_VALUE_08BIT }, + { 0xf802, 0x92, IMX300_REG_VALUE_08BIT }, + { 0xf803, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xf804, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xf805, 0xbc, IMX300_REG_VALUE_08BIT }, + { 0xf806, 0x22, IMX300_REG_VALUE_08BIT }, + { 0xf807, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xf808, 0x1c, IMX300_REG_VALUE_08BIT }, + { 0xf809, 0x80, IMX300_REG_VALUE_08BIT }, + { 0xf80a, 0xfa, IMX300_REG_VALUE_08BIT }, + { 0xf80b, 0x21, IMX300_REG_VALUE_08BIT }, + { 0xf80c, 0x55, IMX300_REG_VALUE_08BIT }, + { 0xf80d, 0x14, IMX300_REG_VALUE_08BIT }, + { 0xf80e, 0xba, IMX300_REG_VALUE_08BIT }, + { 0xf80f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0xf810, 0x81, IMX300_REG_VALUE_08BIT }, + { 0xf811, 0xd1, IMX300_REG_VALUE_08BIT }, + { 0xf812, 0x31, IMX300_REG_VALUE_08BIT }, + { 0xf813, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x5869, 0x01, IMX300_REG_VALUE_08BIT }, +}; + +/* + * Magic registers sequence, common for all of the specified resolutions. + * + * Note: Binaries seem to send data to 0x0220, 0x0221 when enabling HDR mode... + */ +static const struct imx300_reg mode_common_regs[] = { + { IMX300_REG_ORIENTATION, 0x03, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_CSI_DATA_FORMAT_HI, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_CSI_DATA_FORMAT_LO, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_CSI_LANE_MODE, IMX300_CSI_LANE_MODE_4LANE, + IMX300_REG_VALUE_08BIT }, + { 0x0221, 0x11, IMX300_REG_VALUE_08BIT }, + { 0x0381, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0383, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0385, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0387, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0401, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0404, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0405, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0408, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0409, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040c, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x040d, 0x90, IMX300_REG_VALUE_08BIT }, + { 0x040e, 0x10, IMX300_REG_VALUE_08BIT }, + { 0x040f, 0x40, IMX300_REG_VALUE_08BIT }, + { 0x30e4, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x30e5, 0x90, IMX300_REG_VALUE_08BIT }, + { 0x30e6, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x30e7, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x30e8, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x30e9, 0x90, IMX300_REG_VALUE_08BIT }, + { 0x30ea, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x30eb, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x30ec, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x30ed, 0x90, IMX300_REG_VALUE_08BIT }, + { 0x30ee, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x30ef, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x30f4, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x30f5, 0x90, IMX300_REG_VALUE_08BIT }, + { 0x30f6, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x30f7, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3294, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3295, 0xe8, IMX300_REG_VALUE_08BIT }, + { 0x3296, 0x16, IMX300_REG_VALUE_08BIT }, + { 0x3297, 0x77, IMX300_REG_VALUE_08BIT }, + { 0x0210, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0211, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0212, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0213, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0214, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0215, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0216, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0217, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3220, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3006, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3007, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31e0, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x31e1, 0xff, IMX300_REG_VALUE_08BIT }, + { 0x31e4, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x4301, 0x94, IMX300_REG_VALUE_08BIT }, + { 0x4302, 0x15, IMX300_REG_VALUE_08BIT }, + { 0x4303, 0x44, IMX300_REG_VALUE_08BIT }, + { 0x4304, 0x09, IMX300_REG_VALUE_08BIT }, + { 0x4305, 0x07, IMX300_REG_VALUE_08BIT }, + { 0x4306, 0x87, IMX300_REG_VALUE_08BIT }, + { 0x4307, 0x88, IMX300_REG_VALUE_08BIT }, + { 0x4308, 0xba, IMX300_REG_VALUE_08BIT }, + { 0x4309, 0x21, IMX300_REG_VALUE_08BIT }, + { 0x4f7e, 0x12, IMX300_REG_VALUE_08BIT }, + { 0x0220, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x0222, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0224, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0225, 0xf4, IMX300_REG_VALUE_08BIT }, + { 0x3000, 0x54, IMX300_REG_VALUE_08BIT }, + { 0x3001, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0600, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0601, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3022, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b05, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b06, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3018, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3019, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x301a, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3025, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3130, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3004, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x30a2, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0b00, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3250, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3251, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3011, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3013, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3129, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3125, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3127, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3140, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3141, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3142, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x314f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0b8e, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b8f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0b90, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b91, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0b92, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b93, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0b94, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x0b95, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3121, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3123, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31b0, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3158, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3159, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x315f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3160, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3161, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3162, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3163, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3164, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3165, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3166, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3167, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3168, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3169, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x316f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3170, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3171, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3172, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3173, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3174, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3175, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3176, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3177, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3178, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3179, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317b, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317c, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317d, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x317f, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3180, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3181, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3182, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3183, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3184, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3185, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3186, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3187, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b4, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b5, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b6, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b7, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31b9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31ba, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31bb, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3291, 0x01, IMX300_REG_VALUE_08BIT }, +}; + +/* Data rate configuration: HIGH (780MHz) */ +static const struct imx300_reg mipi_data_rate_1560mbps[] = { + { 0x0301, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x0303, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x0305, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x0306, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0307, 0xbe, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_PIX_CLK_DIV, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_SYS_CLK_DIV, 1, IMX300_REG_VALUE_08BIT }, + { 0x030d, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x030e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x030f, 0xbb, IMX300_REG_VALUE_08BIT }, +}; + +/* Data rate configuration: LOW (480MHz) */ +static const struct imx300_reg mipi_data_rate_960mbps[] = { + { 0x0301, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x0303, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x0305, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x0306, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0307, 0xbe, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_PIX_CLK_DIV, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_SYS_CLK_DIV, 1, IMX300_REG_VALUE_08BIT }, + { 0x030d, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x030e, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x030f, 0x4b, IMX300_REG_VALUE_08BIT }, +}; + +static const struct imx300_reg binning_mode_off[] = { + { IMX300_REG_BINNING_EN, 0, IMX300_REG_VALUE_08BIT }, + { 0x0901, 0x11, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_BINNING_TYPE, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a00, 0x19, IMX300_REG_VALUE_08BIT }, + { 0x3a01, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a02, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a03, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x3a04, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a05, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a06, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a07, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a08, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dc2, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x6dc3, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6dc4, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x6dc7, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x6dca, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dcb, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dcc, 0x01, IMX300_REG_VALUE_08BIT }, +}; + +static const struct imx300_reg binning_mode_2x2[] = { + { IMX300_REG_BINNING_EN, 1, IMX300_REG_VALUE_08BIT }, + { 0x0901, 0x22, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_BINNING_TYPE, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x3a00, 0x19, IMX300_REG_VALUE_08BIT }, + { 0x3a01, 0x0a, IMX300_REG_VALUE_08BIT }, + { 0x3a02, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x3a03, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x3a04, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a05, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a06, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3a07, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3a08, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dc2, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x6dc3, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x6dc4, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x6dc7, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x6dca, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dcb, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x6dcc, 0x01, IMX300_REG_VALUE_08BIT }, +}; + + +/* Output resolution configuration */ +static const struct imx300_reg mode_5520x4160[] = { + { IMX300_REG_EXPOSURE, 4230, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_ANALOG_GAIN, 0, IMX300_REG_VALUE_16BIT }, + + { IMX300_REG_VTS, 4250, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_LINE_LEN_PCK, 8008, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_START, 232, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_START, 0, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_END, 5751, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_END, 4159, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_OUTPUT_SIZE, 5520, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_OUTPUT_SIZE, 4160, IMX300_REG_VALUE_16BIT }, + + { 0x0408, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0409, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040b, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x040c, 5520, IMX300_REG_VALUE_16BIT }, + { 0x040e, 4160, IMX300_REG_VALUE_16BIT }, + + { 0x3150, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3151, 0x49, IMX300_REG_VALUE_08BIT }, + { 0x3152, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3153, 0x20, IMX300_REG_VALUE_08BIT }, + { 0x3154, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x3155, 0x9f, IMX300_REG_VALUE_08BIT }, + { 0x3156, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x3157, 0xaa, IMX300_REG_VALUE_08BIT }, + + { 0x31e8, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31e9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31ec, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31f0, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31f1, 0xbc, IMX300_REG_VALUE_08BIT }, + { 0x3221, 0x01, IMX300_REG_VALUE_08BIT }, + + { 0x7ea0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea3, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x7ea5, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x7ea6, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x7ea8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea9, 0x78, IMX300_REG_VALUE_08BIT }, + { 0x7eac, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb3, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb4, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb5, 0x00, IMX300_REG_VALUE_08BIT }, +}; + + +static const struct imx300_reg mode_5984x3392[] = { + { IMX300_REG_EXPOSURE, 3556, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_ANALOG_GAIN, 0, IMX300_REG_VALUE_16BIT }, + + { IMX300_REG_VTS, 3576, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_LINE_LEN_PCK, 8224, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_START, 0, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_START, 384, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_END, 5751, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_END, 3775, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_OUTPUT_SIZE, 5984, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_OUTPUT_SIZE, 3392, IMX300_REG_VALUE_16BIT }, + + { 0x0408, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0409, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040b, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x040c, 5984, IMX300_REG_VALUE_16BIT }, + { 0x040e, 3392, IMX300_REG_VALUE_16BIT }, + + { 0x3150, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3151, 0x50, IMX300_REG_VALUE_08BIT }, + { 0x3152, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3153, 0x1a, IMX300_REG_VALUE_08BIT }, + { 0x3154, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x3155, 0xd8, IMX300_REG_VALUE_08BIT }, + { 0x3156, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x3157, 0x2c, IMX300_REG_VALUE_08BIT }, + + { 0x31e8, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31e9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31ec, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31f0, 0x03, IMX300_REG_VALUE_08BIT }, + { 0x31f1, 0x84, IMX300_REG_VALUE_08BIT }, + { 0x3221, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x7ea0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea3, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x7ea5, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x7ea6, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x7ea8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea9, 0x78, IMX300_REG_VALUE_08BIT }, + { 0x7eac, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb3, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb4, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb5, 0x00, IMX300_REG_VALUE_08BIT }, +}; + +static const struct imx300_reg mode_2992x1696[] = { + { IMX300_REG_EXPOSURE, 3566, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_ANALOG_GAIN, 0, IMX300_REG_VALUE_16BIT }, + + { IMX300_REG_VTS, 1772, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_LINE_LEN_PCK, 7064, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_START, 0, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_START, 384, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_END, 5983, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_END, 3775, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_OUTPUT_SIZE, 2992, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_OUTPUT_SIZE, 1696, IMX300_REG_VALUE_16BIT }, + + { 0x0408, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0409, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040b, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x040c, 2992, IMX300_REG_VALUE_16BIT }, + { 0x040e, 1696, IMX300_REG_VALUE_16BIT }, + + { 0x3150, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3151, 0x28, IMX300_REG_VALUE_08BIT }, + { 0x3152, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3153, 0x0d, IMX300_REG_VALUE_08BIT }, + { 0x3154, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3155, 0x6c, IMX300_REG_VALUE_08BIT }, + { 0x3156, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x3157, 0x16, IMX300_REG_VALUE_08BIT }, + + { 0x31e8, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31e9, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31ec, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31f0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31f1, 0x14, IMX300_REG_VALUE_08BIT }, + { 0x3221, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x7ea0, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x7ea3, 0x05, IMX300_REG_VALUE_08BIT }, + { 0x7ea5, 0x1e, IMX300_REG_VALUE_08BIT }, + { 0x7ea6, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x7ea8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eac, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x7eb3, 0x04, IMX300_REG_VALUE_08BIT }, + { 0x7eb4, 0x0c, IMX300_REG_VALUE_08BIT }, + { 0x7eb5, 0x10, IMX300_REG_VALUE_08BIT }, +}; + +static const struct imx300_reg mode_1424x800[] = { + { IMX300_REG_EXPOSURE, 3692, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_ANALOG_GAIN, 0, IMX300_REG_VALUE_16BIT }, + + { IMX300_REG_VTS, 3712, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_LINE_LEN_PCK, 7064, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_START, 1568, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_START, 1280, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_ADDR_END, 4415, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_ADDR_END, 2879, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_X_OUTPUT_SIZE, 1424, IMX300_REG_VALUE_16BIT }, + { IMX300_REG_Y_OUTPUT_SIZE, 800, IMX300_REG_VALUE_16BIT }, + + { 0x0408, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x0409, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040a, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x040b, 0x00, IMX300_REG_VALUE_08BIT }, + + { 0x040c, 1424, IMX300_REG_VALUE_16BIT }, + { 0x040e, 800, IMX300_REG_VALUE_16BIT }, + + { 0x3150, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3151, 0x13, IMX300_REG_VALUE_08BIT }, + { 0x3152, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3153, 0x06, IMX300_REG_VALUE_08BIT }, + { 0x3154, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3155, 0xad, IMX300_REG_VALUE_08BIT }, + { 0x3156, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x3157, 0x83, IMX300_REG_VALUE_08BIT }, + + { 0x31e8, 0x01, IMX300_REG_VALUE_08BIT }, + { 0x31e9, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31ec, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x31f0, 0x02, IMX300_REG_VALUE_08BIT }, + { 0x31f1, 0xbc, IMX300_REG_VALUE_08BIT }, + { 0x3221, 0x01, IMX300_REG_VALUE_08BIT }, + + { 0x7ea0, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea3, 0x08, IMX300_REG_VALUE_08BIT }, + { 0x7ea5, 0x0f, IMX300_REG_VALUE_08BIT }, + { 0x7ea6, 0x60, IMX300_REG_VALUE_08BIT }, + { 0x7ea8, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7ea9, 0x78, IMX300_REG_VALUE_08BIT }, + { 0x7eac, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb3, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb4, 0x00, IMX300_REG_VALUE_08BIT }, + { 0x7eb5, 0x00, IMX300_REG_VALUE_08BIT }, +}; + +/* Output bits per sample: This sensor support RAW8 and RAW10 */ +static const struct imx300_reg raw8_framefmt_regs[] = { + { IMX300_REG_CSI_DATA_FORMAT_HI, 8, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_CSI_DATA_FORMAT_LO, 8, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_PIX_CLK_DIV, 8, IMX300_REG_VALUE_08BIT }, +}; + +static const struct imx300_reg raw10_framefmt_regs[] = { + { IMX300_REG_CSI_DATA_FORMAT_HI, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_CSI_DATA_FORMAT_LO, 10, IMX300_REG_VALUE_08BIT }, + { IMX300_REG_OP_PIX_CLK_DIV, 10, IMX300_REG_VALUE_08BIT }, +}; + +static const char * const imx300_test_pattern_menu[] = { + "Disabled", + "Solid Color", + "Color Bars Solid", + "Color Bars Fade To Gray", + "Pseudorandom Sequence (PN9)" +}; + +static const int imx300_test_pattern_val[] = { + IMX300_TEST_PATTERN_DISABLE, + IMX300_TEST_PATTERN_SOLID_COLOR, + IMX300_TEST_PATTERN_COLOR_BARS, + IMX300_TEST_PATTERN_GREY_COLOR, + IMX300_TEST_PATTERN_PN9, +}; + +/* regulator supplies */ +static const char * const imx300_supply_name[] = { + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.2V) supply */ + "VDIG", /* Digital Core (1.15-1.20V) supply */ + "VDDL", /* IF (1.8V) supply */ +}; + +#define IMX300_NUM_SUPPLIES ARRAY_SIZE(imx300_supply_name) + +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 codes[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, +}; + +/* Mode configs */ +static const struct imx300_mode supported_modes[] = { + { + /* 23MP 23fps mode */ + .width = 5520, + .height = 4160, + .max_fps = 23, + .crop = { + .left = IMX300_PIXEL_ARRAY_LEFT, + .top = IMX300_PIXEL_ARRAY_TOP, + .width = 5520, + .height = 4160 + }, + .vts_def = 4250, + .high_bw = true, + .binned = false, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_5520x4160), + .regs = mode_5520x4160, + }, + }, + { + /* 20.3MP 26fps mode */ + .width = 5984, + .height = 3392, + .max_fps = 26, + .crop = { + .left = IMX300_PIXEL_ARRAY_LEFT, + .top = 384, + .width = 5984, + .height = 3392 + }, + .vts_def = 3576, + .high_bw = true, + .binned = false, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_5984x3392), + .regs = mode_5984x3392, + }, + }, + { + /* 3.8MP 60fps 2x2 binning */ + .width = 2992, + .height = 1696, + .max_fps = 60, + .crop = { + .left = IMX300_PIXEL_ARRAY_LEFT, + .top = 384, + .width = 2292, + .height = 1696 + }, + .vts_def = 1792, + .high_bw = true, + .binned = true, + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mode_2992x1696), + .regs = mode_2992x1696, + }, + }, + { + /* 1.2MP binned 120fps mode */ + .width = 1424, + .height = 800, + .max_fps = 120, + .crop = { + .left = IMX300_PIXEL_ARRAY_LEFT, + .top = 384, + .width = 1424, + .height = 800, + }, + .vts_def = 896, + .high_bw = false, + .binned = true, + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mode_1424x800), + .regs = mode_1424x800, + }, + }, +}; + +struct imx300 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to IMX300 */ + u32 xclk_freq; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[IMX300_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + /* Frame rate */ + struct v4l2_fract frame_rate; + + /* Current mode */ + const struct imx300_mode *mode; + u32 cur_bps; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct imx300 *to_imx300(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx300, sd); +} + +static s64 get_pixel_rate(struct imx300 *imx300) +{ + s64 prate; + + if (imx300->mode->high_bw) + prate = IMX300_HIGH_BW_PIXEL_RATE; + else + prate = IMX300_LOW_BW_PIXEL_RATE; + + /* Satisfy the settle time for 8bits */ + if (imx300->cur_bps == 8) { + prate = div_s64(prate, (s32)10); + prate *= 12; + } + + return prate; +} + +/* Read registers up to 2 at a time */ +static int imx300_read_reg(struct imx300 *imx300, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = { reg >> 8, reg & 0xff }; + u8 data_buf[4] = { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 4 at a time */ +static int imx300_write_reg(struct imx300 *imx300, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx300_write_regs(struct imx300 *imx300, + const struct imx300_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx300_write_reg(imx300, regs[i].address, + regs[i].reg_len, + regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "Cannot write reg 0x%4.4x. (%d)\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Get bayer order based on flip setting. */ +static u32 imx300_get_format_code(struct imx300 *imx300, u32 code) +{ + unsigned int i; + + lockdep_assert_held(&imx300->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) + i = 0; + + i = (i & ~3) | (imx300->vflip->val ? 2 : 0) | + (imx300->hflip->val ? 1 : 0); + + return codes[i]; +} + +static void imx300_set_default_format(struct imx300 *imx300) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &imx300->fmt; + fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int imx300_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx300 *imx300 = to_imx300(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_rect *try_crop; + + mutex_lock(&imx300->mutex); + + /* Initialize try_fmt */ + try_fmt->width = supported_modes[0].width; + try_fmt->height = supported_modes[0].height; + try_fmt->code = imx300_get_format_code(imx300, + MEDIA_BUS_FMT_SRGGB10_1X10); + try_fmt->field = V4L2_FIELD_NONE; + + /* Initialize try_crop rectangle. */ + try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + try_crop->top = IMX300_PIXEL_ARRAY_TOP; + try_crop->left = IMX300_PIXEL_ARRAY_LEFT; + try_crop->width = IMX300_PIXEL_ARRAY_WIDTH; + try_crop->height = IMX300_PIXEL_ARRAY_HEIGHT; + + mutex_unlock(&imx300->mutex); + + return 0; +} + +static int imx300_update_digital_gain(struct imx300 *imx300, u32 gain) +{ + int ret; + + ret = imx300_write_reg(imx300, IMX300_REG_GR_DIGITAL_GAIN, + IMX300_REG_VALUE_16BIT, + gain); + if (ret) + return ret; + + ret = imx300_write_reg(imx300, IMX300_REG_GB_DIGITAL_GAIN, + IMX300_REG_VALUE_16BIT, + gain); + if (ret) + return ret; + + ret = imx300_write_reg(imx300, IMX300_REG_R_DIGITAL_GAIN, + IMX300_REG_VALUE_16BIT, + gain); + if (ret) + return ret; + + ret = imx300_write_reg(imx300, IMX300_REG_B_DIGITAL_GAIN, + IMX300_REG_VALUE_16BIT, + gain); + if (ret) + return ret; + + return 0; +} + +static int imx300_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx300 *imx300 = + container_of(ctrl->handler, struct imx300, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + int ret; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = imx300->mode->height + ctrl->val - 10; + exposure_def = (exposure_max < IMX300_EXPOSURE_DEFAULT) ? + exposure_max : IMX300_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx300->exposure, + imx300->exposure->minimum, + exposure_max, imx300->exposure->step, + exposure_def); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = imx300_write_reg(imx300, IMX300_REG_ANALOG_GAIN, + IMX300_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx300_write_reg(imx300, IMX300_REG_EXPOSURE, + IMX300_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx300_update_digital_gain(imx300, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx300_write_reg(imx300, IMX300_REG_TEST_PATTERN, + IMX300_REG_VALUE_16BIT, + imx300_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = imx300_write_reg(imx300, IMX300_REG_ORIENTATION, + IMX300_REG_VALUE_08BIT, + imx300->hflip->val | + imx300->vflip->val << 1); + break; + case V4L2_CID_VBLANK: + ret = imx300_write_reg(imx300, IMX300_REG_VTS, + IMX300_REG_VALUE_16BIT, + imx300->mode->height + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_RED: + ret = imx300_write_reg(imx300, IMX300_REG_TESTP_RED, + IMX300_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + ret = imx300_write_reg(imx300, IMX300_REG_TESTP_GREENR, + IMX300_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + ret = imx300_write_reg(imx300, IMX300_REG_TESTP_BLUE, + IMX300_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + ret = imx300_write_reg(imx300, IMX300_REG_TESTP_GREENB, + IMX300_REG_VALUE_16BIT, ctrl->val); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx300_ctrl_ops = { + .s_ctrl = imx300_set_ctrl, +}; + +static int imx300_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx300 *imx300 = to_imx300(sd); + + if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; + + code->code = imx300_get_format_code(imx300, codes[code->index * 4]); + + return 0; +} + +static int imx300_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx300 *imx300 = to_imx300(sd); + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != imx300_get_format_code(imx300, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int imx300_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + unsigned int i; + + if (fie->pad || fie->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) + if (fie->width == supported_modes[i].width && + fie->height == supported_modes[i].height) + break; + + if (i == ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = supported_modes[i].max_fps; + + return 0; +} + +static int imx300_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct imx300 *imx300 = to_imx300(sd); + + ival->interval.numerator = imx300->frame_rate.denominator; + ival->interval.denominator = imx300->frame_rate.numerator; + + return 0; +} + +static int imx300_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct imx300 *imx300 = to_imx300(sd); + const struct imx300_mode *cur_mode = imx300->mode; + struct v4l2_fract *tpf = &ival->interval; + int exposure_max, exposure_def; + u32 new_vts; + u32 vblank = 0; + + if (tpf->numerator == 0 || tpf->denominator == 0 || + (tpf->denominator > tpf->numerator * cur_mode->max_fps)) { + /* reset to max frame rate */ + tpf->numerator = 1; + tpf->denominator = cur_mode->max_fps; + new_vts = cur_mode->vts_def; + } else { + /* Approximation of new VTS: recalculate default vblank */ + vblank = cur_mode->vts_def - cur_mode->height; + + /* Avoid floating point */ + new_vts = vblank * 1000; + new_vts = new_vts / cur_mode->max_fps; + new_vts = (new_vts * tpf->denominator) / 1000; + new_vts += vblank + cur_mode->height; + } + + imx300->frame_rate.numerator = tpf->numerator; + imx300->frame_rate.denominator = tpf->denominator; + + /* + * Note: VTS cannot be less than cur_mode->height, but that's useless + * to check at this point, since we are surely complying here. + * + * Now that we've got a new VTS, let's update the exposure control + * min/max in order to avoid impossible and/or useless combinations. + */ + exposure_max = new_vts - 4; + exposure_def = (exposure_max < IMX300_EXPOSURE_DEFAULT) ? + exposure_max : IMX300_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx300->exposure, + imx300->exposure->minimum, + exposure_max, imx300->exposure->step, + exposure_def); + + return imx300_write_reg(imx300, IMX300_REG_VTS, + IMX300_REG_VALUE_16BIT, + new_vts); +} + +static void imx300_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +} + +static void imx300_update_pad_format(struct imx300 *imx300, + const struct imx300_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + imx300_reset_colorspace(&fmt->format); +} + +static int __imx300_get_pad_format(struct imx300 *imx300, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&imx300->sd, cfg, fmt->pad); + /* update the code which could change due to vflip or hflip: */ + try_fmt->code = imx300_get_format_code(imx300, try_fmt->code); + fmt->format = *try_fmt; + } else { + imx300_update_pad_format(imx300, imx300->mode, fmt); + fmt->format.code = imx300_get_format_code(imx300, + imx300->fmt.code); + } + + return 0; +} + +static int imx300_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx300 *imx300 = to_imx300(sd); + int ret; + + mutex_lock(&imx300->mutex); + ret = __imx300_get_pad_format(imx300, cfg, fmt); + mutex_unlock(&imx300->mutex); + + return ret; +} + +static int imx300_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx300 *imx300 = to_imx300(sd); + const struct imx300_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + int exposure_max, exposure_def, hblank; + unsigned int i; + + mutex_lock(&imx300->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == fmt->format.code) + break; + if (i >= ARRAY_SIZE(codes)) + i = 0; + + /* Bayer order varies with flips */ + fmt->format.code = imx300_get_format_code(imx300, codes[i]); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + imx300_update_pad_format(imx300, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else if (imx300->mode != mode || + imx300->fmt.code != fmt->format.code) { + imx300->fmt = fmt->format; + imx300->mode = mode; + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx300->vblank, IMX300_VTS_MIN, + IMX300_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + __v4l2_ctrl_s_ctrl(imx300->vblank, + mode->vts_def - mode->height); + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX300_EXPOSURE_DEFAULT) ? + exposure_max : IMX300_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx300->exposure, + imx300->exposure->minimum, + exposure_max, imx300->exposure->step, + exposure_def); + /* + * Currently PPL is fixed to IMX300_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeble in any + * way other than changing the mode. + */ + hblank = IMX300_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx300->hblank, hblank, hblank, 1, + hblank); + } + + mutex_unlock(&imx300->mutex); + + return 0; +} + +static int imx300_set_framefmt(struct imx300 *imx300) +{ + int ret; + + switch (imx300->fmt.code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + ret = imx300_write_regs(imx300, raw8_framefmt_regs, + ARRAY_SIZE(raw8_framefmt_regs)); + if (ret) + return ret; + imx300->cur_bps = 8; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + ret = imx300_write_regs(imx300, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs)); + if (ret) + return ret; + imx300->cur_bps = 10; + break; + } + + /* Update the pixel rate to eventually save some power */ + __v4l2_ctrl_s_ctrl_int64(imx300->pixel_rate, get_pixel_rate(imx300)); + + return ret; +} + +static const struct v4l2_rect * +__imx300_get_pad_crop(struct imx300 *imx300, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&imx300->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx300->mode->crop; + } + + return NULL; +} + +static int imx300_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx300 *imx300 = to_imx300(sd); + + mutex_lock(&imx300->mutex); + sel->r = *__imx300_get_pad_crop(imx300, cfg, sel->pad, + sel->which); + mutex_unlock(&imx300->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX300_NATIVE_WIDTH; + sel->r.height = IMX300_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX300_PIXEL_ARRAY_TOP; + sel->r.left = IMX300_PIXEL_ARRAY_LEFT; + sel->r.width = IMX300_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX300_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int imx300_start_streaming(struct imx300 *imx300) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + const struct imx300_reg_list *reg_list; + int ret; + + ret = imx300_write_regs(imx300, init_sequence, + ARRAY_SIZE(init_sequence)); + if (ret) { + dev_err(&client->dev, + "Cannot write init sequence\n"); + return ret; + } + + if (imx300->mode->high_bw) + ret = imx300_write_regs(imx300, mipi_data_rate_1560mbps, + ARRAY_SIZE(mipi_data_rate_1560mbps)); + else + ret = imx300_write_regs(imx300, mipi_data_rate_960mbps, + ARRAY_SIZE(mipi_data_rate_960mbps)); + if (ret) { + dev_err(&client->dev, "Failed to set MIPI data rate\n"); + return ret; + } + + /* Common between all resolutions */ + ret = imx300_write_regs(imx300, mode_common_regs, + ARRAY_SIZE(mode_common_regs)); + if (ret) { + dev_err(&client->dev, + "Cannot write init sequence\n"); + return ret; + } + + /* Apply default values of the selected mode */ + reg_list = &imx300->mode->reg_list; + ret = imx300_write_regs(imx300, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "Failed to set resolution\n"); + return ret; + } + + if (imx300->mode->binned) + ret = imx300_write_regs(imx300, binning_mode_2x2, + ARRAY_SIZE(binning_mode_2x2)); + else + ret = imx300_write_regs(imx300, binning_mode_off, + ARRAY_SIZE(binning_mode_off)); + if (ret) { + dev_err(&client->dev, "Failed to set binning mode\n"); + return ret; + } + + ret = imx300_set_framefmt(imx300); + if (ret) { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx300->sd.ctrl_handler); + if (ret) + return ret; + + /* set stream on register */ + return imx300_write_reg(imx300, IMX300_REG_MODE_SELECT, + IMX300_REG_VALUE_08BIT, IMX300_MODE_STREAMING); +} + +static void imx300_stop_streaming(struct imx300 *imx300) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + int ret; + + /* set stream off register */ + ret = imx300_write_reg(imx300, IMX300_REG_MODE_SELECT, + IMX300_REG_VALUE_08BIT, IMX300_MODE_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); +} + +static int imx300_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx300 *imx300 = to_imx300(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx300->mutex); + if (imx300->streaming == enable) { + mutex_unlock(&imx300->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx300_start_streaming(imx300); + if (ret) + goto err_rpm_put; + } else { + imx300_stop_streaming(imx300); + pm_runtime_put(&client->dev); + } + + imx300->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx300->vflip, enable); + __v4l2_ctrl_grab(imx300->hflip, enable); + + mutex_unlock(&imx300->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx300->mutex); + + return ret; +} + +/* Power/clock management functions */ +static int imx300_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx300 *imx300 = to_imx300(sd); + int ret; + + ret = regulator_bulk_enable(IMX300_NUM_SUPPLIES, + imx300->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret = clk_prepare_enable(imx300->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + /* Wait for the internal PLLs stabilization time */ + usleep_range(IMX300_XCLK_STABLE_DELAY_US, + IMX300_XCLK_STABLE_DELAY_US + IMX300_XCLK_DELAY_RANGE_US); + + /* PLLs are stable now: get out of reset! */ + gpiod_set_value_cansleep(imx300->reset_gpio, 1); + usleep_range(IMX300_XCLR_MIN_DELAY_US, + IMX300_XCLR_MIN_DELAY_US + IMX300_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(IMX300_NUM_SUPPLIES, imx300->supplies); + + return ret; +} + +static int imx300_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx300 *imx300 = to_imx300(sd); + + gpiod_set_value_cansleep(imx300->reset_gpio, 0); + regulator_bulk_disable(IMX300_NUM_SUPPLIES, imx300->supplies); + clk_disable_unprepare(imx300->xclk); + + return 0; +} + +static int __maybe_unused imx300_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx300 *imx300 = to_imx300(sd); + + if (imx300->streaming) + imx300_stop_streaming(imx300); + + return 0; +} + +static int __maybe_unused imx300_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx300 *imx300 = to_imx300(sd); + int ret; + + if (imx300->streaming) { + ret = imx300_start_streaming(imx300); + if (ret) + goto error; + } + + return 0; + +error: + imx300_stop_streaming(imx300); + imx300->streaming = false; + + return ret; +} + +static int imx300_get_regulators(struct imx300 *imx300) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + unsigned int i; + + for (i = 0; i < IMX300_NUM_SUPPLIES; i++) + imx300->supplies[i].supply = imx300_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + IMX300_NUM_SUPPLIES, + imx300->supplies); +} + +/* Verify chip ID */ +static int imx300_identify_module(struct imx300 *imx300) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + int ret; + u32 val; + + ret = imx300_read_reg(imx300, IMX300_REG_CHIP_ID, + IMX300_REG_VALUE_16BIT, &val); + if (ret) { + dev_err(&client->dev, "failed to read chip id %x\n", + IMX300_CHIP_ID); + return ret; + } + + if (val != IMX300_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + IMX300_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops imx300_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx300_video_ops = { + .s_stream = imx300_set_stream, + .g_frame_interval = imx300_g_frame_interval, + .s_frame_interval = imx300_s_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx300_pad_ops = { + .enum_mbus_code = imx300_enum_mbus_code, + .get_fmt = imx300_get_pad_format, + .set_fmt = imx300_set_pad_format, + .get_selection = imx300_get_selection, + .enum_frame_size = imx300_enum_frame_size, + .enum_frame_interval = imx300_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops imx300_subdev_ops = { + .core = &imx300_core_ops, + .video = &imx300_video_ops, + .pad = &imx300_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx300_internal_ops = { + .open = imx300_open, +}; + +/* Initialize control handlers */ +static int imx300_init_controls(struct imx300 *imx300) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx300->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + unsigned int height = imx300->mode->height; + struct v4l2_fwnode_device_properties props; + int exposure_max, exposure_def, hblank; + int i, ret; + + ctrl_hdlr = &imx300->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 11); + if (ret) + return ret; + + mutex_init(&imx300->mutex); + ctrl_hdlr->lock = &imx300->mutex; + + /* By default, PIXEL_RATE is read only */ + imx300->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_PIXEL_RATE, + IMX300_LOW_BW_PIXEL_RATE, + IMX300_HIGH_BW_PIXEL_RATE, 1, + IMX300_HIGH_BW_PIXEL_RATE); + + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx300->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_VBLANK, IMX300_VTS_MIN, + IMX300_VTS_MAX - height, 1, + imx300->mode->vts_def - height); + hblank = IMX300_PPL_DEFAULT - imx300->mode->width; + imx300->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx300->hblank) + imx300->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + exposure_max = imx300->mode->vts_def - 4; + exposure_def = (exposure_max < IMX300_EXPOSURE_DEFAULT) ? + exposure_max : IMX300_EXPOSURE_DEFAULT; + imx300->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX300_EXPOSURE_MIN, exposure_max, + IMX300_EXPOSURE_STEP, + exposure_def); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX300_ANA_GAIN_MIN, IMX300_ANA_GAIN_MAX, + IMX300_ANA_GAIN_STEP, IMX300_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX300_DGTL_GAIN_MIN, IMX300_DGTL_GAIN_MAX, + IMX300_DGTL_GAIN_STEP, IMX300_DGTL_GAIN_DEFAULT); + + imx300->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx300->hflip) + imx300->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx300->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx300->vflip) + imx300->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx300_test_pattern_menu) - 1, + 0, 0, imx300_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx300_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX300_TESTP_COLOUR_MIN, + IMX300_TESTP_COLOUR_MAX, + IMX300_TESTP_COLOUR_STEP, + IMX300_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx300_ctrl_ops, + &props); + if (ret) + goto error; + + imx300->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx300->mutex); + + return ret; +} + +static void imx300_free_controls(struct imx300 *imx300) +{ + v4l2_ctrl_handler_free(imx300->sd.ctrl_handler); + mutex_destroy(&imx300->mutex); +} + +static int imx300_match_link_freq(u64 link_freq) +{ + if (link_freq == IMX300_HIGH_BW_LINK_FREQ || + link_freq == IMX300_LOW_BW_LINK_FREQ) + return 0; + + return -EINVAL; +} + +static int imx300_check_hwcfg(struct device *dev, struct imx300 *imx300) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "Endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "Cannot parse endpoint\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (ep_cfg.nr_of_link_frequencies != 2) { + dev_err(dev, "This sensor uses two link frequencies.\n"); + goto error_out; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 4) { + dev_err(dev, "This sensor needs 4 MIPI Lanes!\n"); + goto error_out; + } + + if (imx300_match_link_freq(ep_cfg.link_frequencies[0]) || + imx300_match_link_freq(ep_cfg.link_frequencies[1])) { + dev_err(dev, "Unsupported link frequencies.\n"); + goto error_out; + } + + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx300_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx300 *imx300; + int ret; + + imx300 = devm_kzalloc(&client->dev, sizeof(*imx300), GFP_KERNEL); + if (!imx300) + return -ENOMEM; + + v4l2_i2c_subdev_init(&imx300->sd, client, &imx300_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (imx300_check_hwcfg(dev, imx300)) + return -EINVAL; + + /* Get system clock (xclk) */ + imx300->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(imx300->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(imx300->xclk); + } + + imx300->xclk_freq = clk_get_rate(imx300->xclk); + if (imx300->xclk_freq != IMX300_XCLK_FREQ_24M) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + imx300->xclk_freq); + return -EINVAL; + } + + ret = imx300_get_regulators(imx300); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Request optional enable pin */ + imx300->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + /* + * The sensor must be powered for imx300_identify_module() + * to be able to read the CHIP_ID register + */ + ret = imx300_power_on(dev); + if (ret) + return ret; + + ret = imx300_identify_module(imx300); + if (ret) + goto error_power_off; + + /* Set default mode to max resolution */ + imx300->mode = &supported_modes[0]; + + /* Default is 10 bits per sample */ + imx300->cur_bps = 10; + + /* + * Sensor doesn't enter LP-11 state upon power up until and unless + * streaming is started, so upon power up switch the modes to: + * streaming -> standby + */ + ret = imx300_write_reg(imx300, IMX300_REG_MODE_SELECT, + IMX300_REG_VALUE_08BIT, IMX300_MODE_STREAMING); + if (ret < 0) + goto error_power_off; + + /* put sensor back to standby mode */ + ret = imx300_write_reg(imx300, IMX300_REG_MODE_SELECT, + IMX300_REG_VALUE_08BIT, IMX300_MODE_STANDBY); + if (ret < 0) + goto error_power_off; + + ret = imx300_init_controls(imx300); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + imx300->sd.internal_ops = &imx300_internal_ops; + imx300->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx300->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx300->pad.flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize default format */ + imx300_set_default_format(imx300); + + ret = media_entity_pads_init(&imx300->sd.entity, 1, &imx300->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&imx300->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor subdevice: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + /* Due to the sensor id being odd, let's inform about us... */ + dev_info(dev, "Sony Exmor-RS IMX300 camera sensor is up.\n"); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx300->sd.entity); + +error_handler_free: + imx300_free_controls(imx300); + +error_power_off: + imx300_power_off(dev); + + return ret; +} + +static int imx300_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx300 *imx300 = to_imx300(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx300_free_controls(imx300); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx300_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id imx300_dt_ids[] = { + { .compatible = "sony,imx300" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx300_dt_ids); + +static const struct dev_pm_ops imx300_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx300_suspend, imx300_resume) + SET_RUNTIME_PM_OPS(imx300_power_off, imx300_power_on, NULL) +}; + +static struct i2c_driver imx300_i2c_driver = { + .driver = { + .name = "imx300", + .of_match_table = imx300_dt_ids, + .pm = &imx300_pm_ops, + }, + .probe_new = imx300_probe, + .remove = imx300_remove, +}; + +module_i2c_driver(imx300_i2c_driver); + +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_DESCRIPTION("Sony Exmor-RS IMX300 camera sensor driver"); +MODULE_LICENSE("GPL v2"); From 632174d48a2a2aabf72be5185f93f098a3accfec Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sun, 18 Oct 2020 00:03:02 +0200 Subject: [PATCH 097/212] media: dt-bindings: media: i2c: Add IMX300 CMOS sensor binding Add YAML device tree binding for IMX300 CMOS image sensor, and the relevant MAINTAINERS entries. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/media/i2c/sony,imx300.yaml | 112 ++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 119 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx300.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx300.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx300.yaml new file mode 100644 index 000000000000..4fa767feea80 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx300.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx300.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony 1/2.3-Inch 25Mpixel Stacked CMOS Digital Image Sensor + +maintainers: + - AngeloGioacchino Del Regno + +description: |- + The Sony IMX300 is a 1/2.3-inch Stacked CMOS (Exmor-RS) digital image + sensor with a pixel size of 1.08um and an active array size of + 5948H x 4140V. It is programmable through I2C interface at address 0x10. + Image data is sent through MIPI CSI-2, which is configured as either 2 or + 4 data lanes. + +properties: + compatible: + const: sony,imx300 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + vdig-supply: + description: + Digital I/O voltage supply, 1.15-1.20 volts + + vana-supply: + description: + Analog voltage supply, 2.2 volts + + vddl-supply: + description: + Digital core voltage supply, 1.8 volts + + reset-gpios: + description: |- + Reference to the GPIO connected to the xclr pin, if any. + Must be released (set high) after all supplies are applied. + + # See ../video-interfaces.txt for more details + port: + type: object + properties: + endpoint: + type: object + + properties: + data-lanes: + description: |- + The driver only supports four-lane operation. + items: + - const: 0 + - const: 1 + - const: 2 + - const: 3 + + clock-noncontinuous: true + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. The driver currently needs + to switch between 780000000 and 480000000 Hz in order to + guarantee functionality of all modes. + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - vana-supply + - vdig-supply + - vddl-supply + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + imx300: sensor@10 { + compatible = "sony,imx300"; + reg = <0x10>; + clocks = <&imx300_xclk>; + vana-supply = <&imx300_vana>; /* 2.2v */ + vdig-supply = <&imx300_vdig>; /* 1.2v */ + vddl-supply = <&imx300_vddl>; /* 1.8v */ + + port { + imx300_0: endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <0 1 2 3>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <780000000 480000000>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index de57f8a850e5..0fbb2fc2eb03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17464,6 +17464,13 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/imx290.txt F: drivers/media/i2c/imx290.c +SONY IMX300 SENSOR DRIVER +M: AngeloGioacchino Del Regno +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/sony,imx300.yaml +F: drivers/media/i2c/imx300.c + SONY IMX319 SENSOR DRIVER M: Bingbu Cao L: linux-media@vger.kernel.org From dc3e650ce14140659289252ae1aab9070b471e08 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Sep 2020 16:54:33 +0200 Subject: [PATCH 098/212] dt-bindings: Add vendor prefix for Novatek Microelectronics Corp. Add prefix for Novatek Microelectronics Corp. Signed-off-by: AngeloGioacchino Del Regno --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index a867f7102c35..0579031a4797 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -812,6 +812,8 @@ patternProperties: description: Nokia "^nordic,.*": description: Nordic Semiconductor + "^novatek,.*": + description: Novatek Microelectronics Corp. "^novtech,.*": description: NovTech, Inc. "^nutsboard,.*": From f7eba7b59e937c8871c62e3b7bc9ad7451ae13d7 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Sep 2020 06:14:29 +0200 Subject: [PATCH 099/212] Input: Add Novatek NT36xxx touchscreen driver This is a driver for the Novatek in-cell touch controller and supports various chips from the NT36xxx family, currently including NT36525, NT36672A, NT36676F, NT36772 and NT36870. Functionality like wake gestures and firmware flashing is not included: I am not aware of any of these DrIC+Touch combo chips not including a non-volatile memory and it should be highly unlikely to find one, since the touch firmware is embedded into the DriverIC one, which is obviously necessary to drive the display unit. However, the necessary address for the firmware update procedure was included into the address table in this driver so, in the event that someone finds the need to implement it for a reason or another, it will be pretty straightforward to. This driver is lightly based on the downstream implementation [1]. [1] https://github.com/Rasenkai/caf-tsoft-Novatek-nt36xxx Signed-off-by: AngeloGioacchino Del Regno --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/nt36xxx.c | 894 ++++++++++++++++++++++++++++ drivers/input/touchscreen/nt36xxx.h | 122 ++++ 4 files changed, 1029 insertions(+) create mode 100644 drivers/input/touchscreen/nt36xxx.c create mode 100644 drivers/input/touchscreen/nt36xxx.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d4e74738c5a8..1b0575617ced 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -637,6 +637,18 @@ config TOUCHSCREEN_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouch. +config TOUCHSCREEN_NT36XXX + tristate "Novatek NT36XXX In-Cell I2C touchscreen controller" + depends on I2C + help + Say Y here if you have a Novatek NT36xxx series In-Cell + touchscreen connected to your system over I2C. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called nt36xxx. + config TOUCHSCREEN_IMX6UL_TSC tristate "Freescale i.MX6UL touchscreen controller" depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 7d34100f7f22..f6694eab8082 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o +obj-$(CONFIG_TOUCHSCREEN_NT36XXX) += nt36xxx.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o diff --git a/drivers/input/touchscreen/nt36xxx.c b/drivers/input/touchscreen/nt36xxx.c new file mode 100644 index 000000000000..a572d2b87464 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx.c @@ -0,0 +1,894 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Novatek NT36xxx series touchscreens + * + * Copyright (C) 2010 - 2017 Novatek, Inc. + * Copyright (C) 2020 AngeloGioacchino Del Regno + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FW Param address */ +#define NT36XXX_FW_ADDR 0x01 + +/* Number of bytes for chip identification */ +#define NT36XXX_ID_LEN_MAX 6 + +/* Touch info */ +#define TOUCH_DEFAULT_MAX_WIDTH 1080 +#define TOUCH_DEFAULT_MAX_HEIGHT 2246 +#define TOUCH_MAX_FINGER_NUM 10 +#define TOUCH_MAX_PRESSURE 1000 + +/* Point data length */ +#define POINT_DATA_LEN 65 + +/* Global pages */ +#define NT36XXX_PAGE_CHIP_INFO 0x0001f64e +#define NT36XXX_PAGE_CRC 0x0003f135 + +/* Misc */ +#define NT36XXX_NUM_SUPPLIES 2 +#define NT36XXX_MAX_RETRIES 5 +#define NT36XXX_MAX_FW_RST_RETRY 50 + +struct nt36xxx_abs_object { + u16 x; + u16 y; + u16 z; + u8 tm; +}; + +struct nt36xxx_fw_info { + u8 fw_ver; + u8 x_num; + u8 y_num; + u8 max_buttons; + u16 abs_x_max; + u16 abs_y_max; + u16 nvt_pid; +}; + +struct nt36xxx_mem_map { + u32 evtbuf_addr; + u32 pipe0_addr; + u32 pipe1_addr; + u32 flash_csum_addr; + u32 flash_data_addr; +}; + +struct nt36xxx_i2c { + struct i2c_client *hw_client; + struct i2c_client *fw_client; + struct regmap *regmap; + struct regmap *fw_regmap; + struct input_dev *input; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; + + struct mutex lock; + + struct touchscreen_properties prop; + struct nt36xxx_fw_info fw_info; + struct nt36xxx_abs_object abs_obj; + + const struct nt36xxx_mem_map *mmap; +}; + +enum nt36xxx_chips { + NT36525_IC = 0, + NT36672A_IC, + NT36676F_IC, + NT36772_IC, + NT36870_IC, + NTMAX_IC, +}; + +struct nt36xxx_trim_table { + u8 id[NT36XXX_ID_LEN_MAX]; + u8 mask[NT36XXX_ID_LEN_MAX]; + enum nt36xxx_chips mapid; +}; + +enum nt36xxx_cmds { + NT36XXX_CMD_ENTER_SLEEP = 0x11, + NT36XXX_CMD_ENTER_WKUP_GESTURE = 0x13, + NT36XXX_CMD_UNLOCK = 0x35, + NT36XXX_CMD_BOOTLOADER_RESET = 0x69, + NT36XXX_CMD_SW_RESET = 0xa5, + NT36XXX_CMD_SET_PAGE = 0xff, +}; + +/** + * enum nt36xxx_fw_state - Firmware state + * @NT36XXX_STATE_INIT: IC Reset + * @NT36XXX_STATE_REK: ReK baseline + * @NT36XXX_STATE_REK_FINISH: Baseline is ready + * @NT36XXX_STATE_NORMAL_RUN: Firmware is running + */ +enum nt36xxx_fw_state { + NT36XXX_STATE_INIT = 0xa0, + NT36XXX_STATE_REK, + NT36XXX_STATE_REK_FINISH, + NT36XXX_STATE_NORMAL_RUN, + NT36XXX_STATE_MAX = 0xaf +}; + +enum nt36xxx_i2c_events { + NT36XXX_EVT_REPORT = 0x00, + NT36XXX_EVT_CRC = 0x35, + NT36XXX_EVT_CHIPID = 0x4e, + NT36XXX_EVT_HOST_CMD = 0x50, + NT36XXX_EVT_HS_OR_SUBCMD = 0x51, /* Handshake or subcommand byte */ + NT36XXX_EVT_RESET_COMPLETE = 0x60, + NT36XXX_EVT_FWINFO = 0x78, + NT36XXX_EVT_PROJECTID = 0x9a, +}; + +static const struct nt36xxx_mem_map nt36xxx_memory_maps[] = { + [NT36525_IC] = { 0x11a00, 0x10000, 0x12000, 0x14000, 0x14002 }, + [NT36672A_IC] = { 0x21c00, 0x20000, 0x23000, 0x24000, 0x24002 }, + [NT36676F_IC] = { 0x11a00, 0x10000, 0x12000, 0x14000, 0x14002 }, + [NT36772_IC] = { 0x11e00, 0x10000, 0x12000, 0x14000, 0x14002 }, + [NT36870_IC] = { 0x25000, 0x20000, 0x23000, 0x24000, 0x24002 }, +}; + +static const struct nt36xxx_trim_table trim_id_table[] = { + { + .id = { 0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03 }, + .mask = { 1, 0, 0, 1, 1, 1 }, + .mapid = NT36672A_IC, + }, + { + .id = { 0x55, 0x00, 0xFF, 0x00, 0x00, 0x00 }, + .mask = { 1, 1, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0x55, 0x72, 0xFF, 0x00, 0x00, 0x00 }, + .mask = { 1, 1, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00 }, + .mask = { 1, 1, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00 }, + .mask = { 1, 1, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x68, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36772_IC, + }, + { + .id = { 0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03 }, + .mask = { 0, 0, 0, 1, 1, 1 }, + .mapid = NT36676F_IC, + }, +}; + +/** + * nt36xxx_set_page - Set page number for read/write + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_set_page(struct nt36xxx_i2c *ts, u32 pageaddr) +{ + u32 data = cpu_to_be32(pageaddr) >> 8; + int ret; + + ret = regmap_noinc_write(ts->fw_regmap, NT36XXX_CMD_SET_PAGE, + &data, sizeof(data)); + if (ret) + return ret; + + usleep_range(100, 200); + return ret; +} + +/** + * nt36xxx_sw_reset_idle - Warm restart the firmware + * @ts: Main driver structure + * + * This function restarts the running firmware without rebooting to + * the bootloader (warm restart) + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_sw_reset_idle(struct nt36xxx_i2c *ts) +{ + int ret; + + ret = regmap_write(ts->regmap, ts->hw_client->addr, + NT36XXX_CMD_SW_RESET); + if (ret) + return ret; + + /* Wait until the MCU resets the fw state */ + usleep_range(15000, 16000); + return ret; +} + +/** + * nt36xxx_bootloader_reset - Reset MCU to bootloader + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_bootloader_reset(struct nt36xxx_i2c *ts) +{ + int ret; + + ret = regmap_write(ts->regmap, ts->hw_client->addr, + NT36XXX_CMD_BOOTLOADER_RESET); + if (ret) + return ret; + + /* MCU has to reboot from bootloader: this is the typical boot time */ + msleep(35); + return ret; +} + +/** + * nt36xxx_check_reset_state - Check the boot state during reset + * @ts: Main driver structure + * @fw_state: Enumeration containing firmware states + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_check_reset_state(struct nt36xxx_i2c *ts, + enum nt36xxx_fw_state fw_state) +{ + u8 buf[2] = { 0 }; + int ret, retry = NT36XXX_MAX_FW_RST_RETRY; + + do { + ret = regmap_noinc_read(ts->fw_regmap, + NT36XXX_EVT_RESET_COMPLETE, + buf, sizeof(buf)); + if (likely(ret == 0) && + (buf[0] >= fw_state) && + (buf[0] <= NT36XXX_STATE_MAX)) { + ret = 0; + break; + } + usleep_range(10000, 11000); + } while (--retry); + + if (!retry) { + dev_err(&ts->hw_client->dev, "Firmware reset failed.\n"); + ret = -EBUSY; + } + + return ret; +} + +/** + * nt36xxx_read_pid - Read Novatek Project ID + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_read_pid(struct nt36xxx_i2c *ts) +{ + __be16 pid; + int ret; + + ret = nt36xxx_set_page(ts, ts->mmap->evtbuf_addr); + if (ret) + return ret; + + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_PROJECTID, + &pid, sizeof(pid)); + if (ret < 0) + return ret; + + ts->fw_info.nvt_pid = be16_to_cpu(pid); + return 0; +} + +/** + * __nt36xxx_get_fw_info - Get working params from firmware + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static int __nt36xxx_get_fw_info(struct nt36xxx_i2c *ts) +{ + struct nt36xxx_fw_info *fwi = &ts->fw_info; + u8 buf[11] = { 0 }; + int ret = 0; + + ret = nt36xxx_set_page(ts, ts->mmap->evtbuf_addr); + if (ret) + return ret; + + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_FWINFO, + buf, sizeof(buf)); + if (ret) + return ret; + + fwi->fw_ver = buf[0]; + fwi->x_num = buf[2]; + fwi->y_num = buf[3]; + fwi->abs_x_max = get_unaligned_be16(&buf[4]); + fwi->abs_y_max = get_unaligned_be16(&buf[6]); + fwi->max_buttons = buf[10]; + + /* Check fw info integrity and clear x_num, y_num if broken */ + if ((buf[0] + buf[1]) != 0xFF) { + dev_err(&ts->hw_client->dev, + "FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", + buf[0], buf[1]); + fwi->fw_ver = 0; + fwi->x_num = 18; + fwi->y_num = 32; + fwi->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; + fwi->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; + fwi->max_buttons = 0; + return -EINVAL; + } + + /* Get Novatek ProjectID */ + return nt36xxx_read_pid(ts); +} + +static int nt36xxx_get_fw_info(struct nt36xxx_i2c *ts) +{ + struct nt36xxx_fw_info *fwi = &ts->fw_info; + int i, ret = 0; + + for (i = 0; i < NT36XXX_MAX_RETRIES; i++) { + ret = __nt36xxx_get_fw_info(ts); + if (ret == 0) + break; + } + + dev_dbg(&ts->hw_client->dev, + "FW Info: PID=0x%x, ver=0x%x res=%ux%u max=%ux%u buttons=%u", + fwi->nvt_pid, fwi->fw_ver, fwi->x_num, fwi->y_num, + fwi->abs_x_max, fwi->abs_y_max, fwi->max_buttons); + + return ret; +} + +/** + * nt36xxx_report - Report touch events + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static void nt36xxx_report(struct nt36xxx_i2c *ts) +{ + struct nt36xxx_abs_object *obj = &ts->abs_obj; + struct input_dev *input = ts->input; + u8 input_id = 0; + u8 point[POINT_DATA_LEN + 1] = { 0 }; + unsigned int ppos = 0; + int i, ret, finger_cnt = 0; + + mutex_lock(&ts->lock); + + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_REPORT, + point, sizeof(point)); + if (ret < 0) { + dev_err(&ts->hw_client->dev, + "Cannot read touch point data: %d\n", ret); + goto xfer_error; + } + + for (i = 0; i < TOUCH_MAX_FINGER_NUM; i++) { + ppos = 6 * i; + input_id = point[ppos + 0] >> 3; + if ((input_id == 0) || (input_id > TOUCH_MAX_FINGER_NUM)) + continue; + + if (((point[ppos] & 0x07) == 0x01) || + ((point[ppos] & 0x07) == 0x02)) { + obj->x = (point[ppos + 1] << 4) + + (point[ppos + 3] >> 4); + obj->y = (point[ppos + 2] << 4) + + (point[ppos + 3] & 0xf); + if ((obj->x > ts->prop.max_x) || + (obj->y > ts->prop.max_y)) + continue; + + obj->tm = point[ppos + 4]; + if (obj->tm == 0) + obj->tm = 1; + + obj->z = point[ppos + 5]; + if (i < 2) { + obj->z += point[i + 63] << 8; + if (obj->z > TOUCH_MAX_PRESSURE) + obj->z = TOUCH_MAX_PRESSURE; + } + + if (obj->z == 0) + obj->z = 1; + + input_mt_slot(input, input_id - 1); + input_mt_report_slot_state(input, + MT_TOOL_FINGER, true); + touchscreen_report_pos(input, &ts->prop, obj->x, + obj->y, true); + + input_report_abs(input, ABS_MT_TOUCH_MAJOR, obj->tm); + input_report_abs(input, ABS_MT_PRESSURE, obj->z); + + finger_cnt++; + } + } + input_mt_sync_frame(input); + input_sync(input); + +xfer_error: + enable_irq(ts->hw_client->irq); + + mutex_unlock(&ts->lock); +} + +static irqreturn_t nt36xxx_i2c_irq_handler(int irq, void *dev_id) +{ + struct nt36xxx_i2c *ts = dev_id; + + disable_irq_nosync(ts->hw_client->irq); + nt36xxx_report(ts); + + return IRQ_HANDLED; +} + +static bool nt36xxx_in_crc_reboot_loop(u8 *buf) +{ + return ((buf[0] == 0xFC) && (buf[1] == 0xFC) && (buf[2] == 0xFC)) || + ((buf[0] == 0xFF) && (buf[1] == 0xFF) && (buf[2] == 0xFF)); +} + +/** + * nt36xxx_stop_crc_reboot - Stop CRC reboot loop and warm-reboot the firmware + * @ts: Main driver structure + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_stop_crc_reboot(struct nt36xxx_i2c *ts) +{ + u8 buf[3] = { 0 }; + u8 val; + int ret, retry = NT36XXX_MAX_RETRIES; + + /* Read dummy buffer to check CRC fail reboot is happening or not */ + + /* Change I2C index to prevent getting 0xFF, but not 0xFC */ + ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CHIP_INFO); + if (ret) { + dev_dbg(&ts->hw_client->dev, + "CRC reset failed: Cannot select page.\n"); + return ret; + } + + /* If ChipID command returns 0xFC or 0xFF, the MCU is in CRC reboot */ + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CHIPID, + buf, sizeof(buf)); + if (ret) + return ret; + + if (!nt36xxx_in_crc_reboot_loop(buf)) + return 0; + + /* IC is in CRC fail reboot loop, needs to be stopped! */ + do { + /* Special reset-idle sequence for CRC failure */ + ret = regmap_write(ts->regmap, ts->hw_client->addr, + NT36XXX_CMD_SW_RESET); + if (ret) + dev_dbg(&ts->hw_client->dev, + "SW Reset 1 failed: may not recover\n"); + + ret = regmap_write(ts->regmap, ts->hw_client->addr, + NT36XXX_CMD_SW_RESET); + if (ret) + dev_dbg(&ts->hw_client->dev, + "SW Reset 2 failed: may not recover\n"); + usleep_range(1000, 1100); + + /* Clear CRC_ERR_FLAG */ + ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CRC); + if (ret) + continue; + + val = 0xA5; + ret = regmap_raw_write(ts->fw_regmap, NT36XXX_EVT_CRC, + &val, sizeof(val)); + if (ret) + continue; + + /* Check CRC_ERR_FLAG */ + ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CRC); + if (ret) + continue; + + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CRC, + &buf, sizeof(buf)); + if (ret) + continue; + + if (buf[0] == 0xA5) + break; + } while (--retry); + + if (retry == 0) { + dev_err(&ts->hw_client->dev, + "CRC reset failed: buf=0x%2ph\n", buf); + } + + return ret; +} + +/** + * nt36xxx_i2c_chip_version_init - Detect Novatek NT36xxx family IC + * @ts: Main driver structure + * + * This function reads the ChipID from the IC and sets the right + * memory map for the detected chip. + * + * Return: Always zero for success, negative number for error + */ +static int nt36xxx_i2c_chip_version_init(struct nt36xxx_i2c *ts) +{ + u8 buf[7] = { 0 }; + int retry = NT36XXX_MAX_RETRIES; + int sz = sizeof(trim_id_table) / sizeof(struct nt36xxx_trim_table); + int i, list, mapid, ret; + + ret = nt36xxx_bootloader_reset(ts); + if (ret) { + dev_err(&ts->hw_client->dev, "Can't reset the nvt IC\n"); + return ret; + } + + do { + ret = nt36xxx_sw_reset_idle(ts); + if (ret) + continue; + + ret = regmap_write(ts->regmap, ts->hw_client->addr, NT36XXX_CMD_UNLOCK); + if (ret) + continue; + usleep_range(10000, 11000); + + ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CHIP_INFO); + if (ret) + continue; + + memset(buf, 0, ARRAY_SIZE(buf)); + ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CHIPID, + buf, sizeof(buf)); + if (ret) + continue; + + /* Compare read chip id with trim list */ + for (list = 0; list < sz; list++) { + /* Compare each not masked byte */ + for (i = 0; i < NT36XXX_ID_LEN_MAX; i++) { + if (trim_id_table[list].mask[i] && + buf[i] != trim_id_table[list].id[i]) + break; + } + + if (i == NT36XXX_ID_LEN_MAX) { + mapid = trim_id_table[list].mapid; + ts->mmap = &nt36xxx_memory_maps[mapid]; + return 0; + } + + ts->mmap = NULL; + ret = -ENOENT; + } + + /* Stop CRC check to prevent IC auto reboot */ + if (nt36xxx_in_crc_reboot_loop(buf)) { + ret = nt36xxx_stop_crc_reboot(ts); + if (ret) + continue; + } + + usleep_range(10000, 11000); + } while (--retry); + + return ret; +} + +static const struct regmap_config nt36xxx_i2c_regmap_hw_config = { + .name = "nt36xxx_i2c_hw", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config nt36xxx_i2c_regmap_fw_config = { + .name = "nt36xxx_i2c_fw", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static void nt36xxx_disable_regulators(void *data) +{ + struct nt36xxx_i2c *ts = data; + + regulator_bulk_disable(NT36XXX_NUM_SUPPLIES, ts->supplies); +} + +static int nt36xxx_i2c_probe(struct i2c_client *hw_client, + const struct i2c_device_id *id) +{ + struct nt36xxx_i2c *ts; + struct input_dev *input; + int ret; + + if (!i2c_check_functionality(hw_client->adapter, I2C_FUNC_I2C)) { + dev_err(&hw_client->dev, "i2c_check_functionality error\n"); + return -EIO; + } + + if (!hw_client->irq) { + dev_err(&hw_client->dev, "No irq specified\n"); + return -EINVAL; + } + + ts = devm_kzalloc(&hw_client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->supplies = devm_kcalloc(&hw_client->dev, + NT36XXX_NUM_SUPPLIES, + sizeof(*ts->supplies), + GFP_KERNEL); + if (!ts->supplies) + return -ENOMEM; + + input = devm_input_allocate_device(&hw_client->dev); + if (!input) + return -ENOMEM; + + ts->fw_client = i2c_new_dummy_device(hw_client->adapter, + NT36XXX_FW_ADDR); + if (IS_ERR(ts->fw_client)) { + dev_err(&hw_client->dev, "Cannot add FW I2C device\n"); + return PTR_ERR(ts->fw_client); + } + + ts->hw_client = hw_client; + ts->input = input; + i2c_set_clientdata(ts->hw_client, ts); + i2c_set_clientdata(ts->fw_client, ts); + + ts->reset_gpio = devm_gpiod_get_optional(&hw_client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) + return PTR_ERR(ts->reset_gpio); + gpiod_set_consumer_name(ts->reset_gpio, "nt36xxx reset"); + + /* These supplies are optional */ + ts->supplies[0].supply = "vdd"; + ts->supplies[1].supply = "vio"; + ret = devm_regulator_bulk_get(&hw_client->dev, + NT36XXX_NUM_SUPPLIES, + ts->supplies); + if (ret) + return dev_err_probe(&hw_client->dev, ret, + "Cannot get supplies: %d\n", ret); + + ts->regmap = devm_regmap_init_i2c(ts->hw_client, + &nt36xxx_i2c_regmap_hw_config); + if (IS_ERR(ts->regmap)) { + dev_err(&hw_client->dev, "regmap (hw-addr) init failed\n"); + return PTR_ERR(ts->regmap); + } + + ts->fw_regmap = devm_regmap_init_i2c(ts->fw_client, + &nt36xxx_i2c_regmap_fw_config); + if (IS_ERR(ts->fw_regmap)) { + dev_err(&hw_client->dev, "regmap (fw-addr) init failed\n"); + return PTR_ERR(ts->fw_regmap); + } + + ret = regulator_bulk_enable(NT36XXX_NUM_SUPPLIES, ts->supplies); + if (ret) + return ret; + + usleep_range(10000, 11000); + + ret = devm_add_action_or_reset(&hw_client->dev, + nt36xxx_disable_regulators, ts); + if (ret) + return ret; + + mutex_init(&ts->lock); + + /* Set memory maps for the specific chip version */ + ret = nt36xxx_i2c_chip_version_init(ts); + if (ret) { + dev_err(&hw_client->dev, "Failed to check chip version\n"); + return ret; + } + + /* Reset the MCU */ + ret = nt36xxx_bootloader_reset(ts); + if (ret < 0) + return ret; + + /* Check and eventually wait until the MCU goes in reset state */ + ret = nt36xxx_check_reset_state(ts, NT36XXX_STATE_INIT); + if (ret < 0) + return ret; + + /* Get informations from the TS firmware */ + ret = nt36xxx_get_fw_info(ts); + if (ret < 0) + return ret; + + input->phys = devm_kasprintf(&hw_client->dev, GFP_KERNEL, + "%s/input0", dev_name(&hw_client->dev)); + if (!input->phys) + return -ENOMEM; + + input->name = "Novatek NT36XXX Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = &hw_client->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + input_set_capability(input, EV_KEY, BTN_TOUCH); + + ret = input_mt_init_slots(input, TOUCH_MAX_FINGER_NUM, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (ret) { + dev_err(&hw_client->dev, "Cannot init MT slots (%d)\n", ret); + return ret; + } + + input_set_abs_params(input, ABS_MT_PRESSURE, 0, + TOUCH_MAX_PRESSURE, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + ts->fw_info.abs_x_max - 1, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + ts->fw_info.abs_y_max - 1, 0, 0); + + /* Override the firmware defaults, if needed */ + touchscreen_parse_properties(input, true, &ts->prop); + + input_set_drvdata(input, ts); + + ret = input_register_device(ts->input); + if (ret) { + dev_err(&hw_client->dev, "Failed to register input device: %d\n", + ret); + return ret; + } + + ret = devm_request_threaded_irq(&hw_client->dev, hw_client->irq, NULL, + nt36xxx_i2c_irq_handler, IRQF_ONESHOT, + hw_client->name, ts); + if (ret) { + dev_err(&hw_client->dev, "request irq failed: %d\n", ret); + return ret; + } + + return ret; +} + +static int __maybe_unused nt36xxx_i2c_suspend(struct device *dev) +{ + struct nt36xxx_i2c *ts = i2c_get_clientdata(to_i2c_client(dev)); + int ret; + + disable_irq(ts->hw_client->irq); + + ret = regmap_write(ts->fw_regmap, NT36XXX_EVT_HOST_CMD, + NT36XXX_CMD_ENTER_SLEEP); + if (ret) { + dev_err(&ts->hw_client->dev, "Cannot enter suspend!!\n"); + return ret; + } + + gpiod_set_value(ts->reset_gpio, 1); + + return 0; +} + +static int __maybe_unused nt36xxx_i2c_resume(struct device *dev) +{ + struct nt36xxx_i2c *ts = i2c_get_clientdata(to_i2c_client(dev)); + int ret; + + mutex_lock(&ts->lock); + + gpiod_set_value(ts->reset_gpio, 0); + + /* Reboot the MCU (also recalibrates the TS) */ + ret = nt36xxx_bootloader_reset(ts); + if (ret < 0) + goto end; + + ret = nt36xxx_check_reset_state(ts, NT36XXX_STATE_REK); + if (ret < 0) + goto end; + + enable_irq(ts->hw_client->irq); +end: + mutex_unlock(&ts->lock); + return ret; +} + +static SIMPLE_DEV_PM_OPS(nt36xxx_i2c_pm, + nt36xxx_i2c_suspend, nt36xxx_i2c_resume); + +static const struct of_device_id nt36xxx_of_match[] = { + { .compatible = "novatek,nt36525" }, + { } +}; +MODULE_DEVICE_TABLE(of, nt36xxx_of_match); + +static const struct i2c_device_id nt36xxx_i2c_ts_id[] = { + { "NVT-ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nt36xxx_i2c_ts_id); + +static struct i2c_driver nt36xxx_i2c_ts_driver = { + .driver = { + .name = "nt36xxx_ts", + .pm = &nt36xxx_i2c_pm, + .of_match_table = nt36xxx_of_match, + }, + .id_table = nt36xxx_i2c_ts_id, + .probe = nt36xxx_i2c_probe, +}; +module_i2c_driver(nt36xxx_i2c_ts_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Novatek NT36XXX Touchscreen Driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); diff --git a/drivers/input/touchscreen/nt36xxx.h b/drivers/input/touchscreen/nt36xxx.h new file mode 100644 index 000000000000..6f03dfb45656 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * Copyright (C) 2020 AngeloGioacchino Del Regno + */ + +#ifndef NT36XXX_H +#define NT36XXX_H + +#define NT36XXX_INPUT_DEVICE_NAME "Novatek NT36XXX Touch Sensor" + +/* These chips have this fixed address when in bootloader :( */ +#define NT36XXX_BLDR_ADDR 0x01 + +/* Input device info */ +#define NVT_TS_NAME "NVTCapacitiveTouchScreen" + +/* Number of bytes for chip identification */ +#define NT36XXX_ID_LEN_MAX 6 + +/* Touch info */ +#define TOUCH_DEFAULT_MAX_WIDTH 1080 +#define TOUCH_DEFAULT_MAX_HEIGHT 2246 +#define TOUCH_MAX_FINGER_NUM 10 +#define TOUCH_MAX_PRESSURE 1000 + +/* Point data length */ +#define POINT_DATA_LEN 65 + +/* Global pages */ +#define NT36XXX_PAGE_CHIP_INFO 0x0001f64e +#define NT36XXX_PAGE_CRC 0x0003f135 + +/* Misc */ +#define NT36XXX_NUM_SUPPLIES 2 +#define NT36XXX_MAX_RETRIES 5 +#define NT36XXX_MAX_FW_RST_RETRY 50 + +struct nt36xxx_abs_object { + u16 x; + u16 y; + u16 z; + u8 tm; +}; + +struct nt36xxx_fw_info { + u8 fw_ver; + u8 x_num; + u8 y_num; + u8 max_buttons; + u16 abs_x_max; + u16 abs_y_max; + u16 nvt_pid; +}; + +struct nt36xxx_mem_map { + u32 evtbuf_addr; + u32 pipe0_addr; + u32 pipe1_addr; + u32 flash_csum_addr; + u32 flash_data_addr; +}; + +struct nt36xxx_i2c { + struct i2c_client *client; + struct input_dev *input; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; + + struct work_struct ts_work; + struct workqueue_struct *ts_workq; + struct mutex lock; + + struct nt36xxx_fw_info fw_info; + struct nt36xxx_abs_object abs_obj; + + const struct nt36xxx_mem_map *mmap; + u8 max_fingers; +}; + +enum nt36xxx_chips { + NT36525_IC = 0, + NT36672A_IC, + NT36676F_IC, + NT36772_IC, + NT36870_IC, + NTMAX_IC, +}; + +struct nt36xxx_trim_table { + u8 id[NT36XXX_ID_LEN_MAX]; + u8 mask[NT36XXX_ID_LEN_MAX]; + enum nt36xxx_chips mapid; +}; + +enum nt36xxx_cmds { + NT36XXX_CMD_ENTER_SLEEP = 0x11, + NT36XXX_CMD_ENTER_WKUP_GESTURE = 0x13, + NT36XXX_CMD_UNLOCK = 0x35, + NT36XXX_CMD_BOOTLOADER_RESET = 0x69, + NT36XXX_CMD_SW_RESET = 0xa5, + NT36XXX_CMD_SET_PAGE = 0xff, +}; + +enum nt36xxx_fw_state { + NT36XXX_STATE_INIT = 0xa0, /* IC reset */ + NT36XXX_STATE_REK, /* ReK baseline */ + NT36XXX_STATE_REK_FINISH, /* Baseline is ready */ + NT36XXX_STATE_NORMAL_RUN, /* Normal run */ + NT36XXX_STATE_MAX = 0xaf +}; + +enum nt36xxx_i2c_events { + NT36XXX_EVT_CHIPID = 0x4e, + NT36XXX_EVT_HOST_CMD = 0x50, + NT36XXX_EVT_HS_OR_SUBCMD = 0x51, /* Handshake or subcommand byte */ + NT36XXX_EVT_RESET_COMPLETE = 0x60, + NT36XXX_EVT_FWINFO = 0x78, + NT36XXX_EVT_PROJECTID = 0x9a, +}; + +#endif From 50f5ca35f9c09ff391041c23cc7ee6d1a00adba5 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 24 Sep 2020 13:42:31 +0200 Subject: [PATCH 100/212] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver Add binding for the Novatek NT36xxx series touchscreen driver. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Krzysztof Kozlowski Reviewed-by: Rob Herring --- .../bindings/input/touchscreen/nt36xxx.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml diff --git a/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml new file mode 100644 index 000000000000..a360a9f5d43b --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/nt36xxx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT36xxx series touchscreen controller Bindings + +maintainers: + - AngeloGioacchino Del Regno + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + const: novatek,nt36525 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vdd-supply: + description: Power supply regulator for VDD pin + + vio-supply: + description: Power supply regulator on VDD-IO pin + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@62 { + compatible = "novatek,nt36525"; + reg = <0x62>; + interrupt-parent = <&tlmm>; + interrupts = <45 IRQ_TYPE_EDGE_RISING>; + reset-gpios = <&tlmm 102 GPIO_ACTIVE_HIGH>; + }; + }; + +... From 75eb1f0a6bf5cedd97874c2fce06088ef5fb487d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 18:56:29 +0100 Subject: [PATCH 101/212] dt-bindings: i2c: qcom,i2c-qup: Convert txt to YAML schema Convert the qcom,i2c-qup binding to YAML schema. Signed-off-by: AngeloGioacchino Del Regno --- .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 40 --------- .../devicetree/bindings/i2c/qcom,i2c-qup.yaml | 87 +++++++++++++++++++ 2 files changed, 87 insertions(+), 40 deletions(-) delete mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt deleted file mode 100644 index dc71754a56af..000000000000 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +++ /dev/null @@ -1,40 +0,0 @@ -Qualcomm Universal Peripheral (QUP) I2C controller - -Required properties: - - compatible: Should be: - * "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064. - * "qcom,i2c-qup-v2.1.1" for 8974 v1. - * "qcom,i2c-qup-v2.2.1" for 8974 v2 and later. - - reg: Should contain QUP register address and length. - - interrupts: Should contain I2C interrupt. - - - clocks: A list of phandles + clock-specifiers, one for each entry in - clock-names. - - clock-names: Should contain: - * "core" for the core clock - * "iface" for the AHB clock - - - #address-cells: Should be <1> Address cells for i2c device address - - #size-cells: Should be <0> as i2c addresses have no size component - -Optional properties: - - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, - defaults to 100kHz if omitted. - -Child nodes should conform to i2c bus binding. - -Example: - - i2c@f9924000 { - compatible = "qcom,i2c-qup-v2.2.1"; - reg = <0xf9924000 0x1000>; - interrupts = <0 96 0>; - - clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>; - clock-names = "core", "iface"; - - clock-frequency = <355000>; - - #address-cells = <1>; - #size-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml new file mode 100644 index 000000000000..c5c7db3ac2a6 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/i2c/qcom,i2c-qup.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Universal Peripheral (QUP) I2C controller + +maintainers: + - Andy Gross + - Bjorn Andersson + +description: Binding for Qualcomm "QUP" I2C controllers + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + enum: + - qcom,i2c-qup-v1.1.1 + - qcom,i2c-qup-v2.1.1 + - qcom,i2c-qup-v2.2.1 + + reg: + items: + - description: QUP I2C register iospace + + clocks: + items: + - description: Core QUP I2C clock + - description: AHB clock + + clock-names: + items: + - const: core + - const: iface + + clock-frequency: + minimum: 100000 + maximum: 1000000 + default: 100000 + + dmas: + items: + - description: RX DMA Channel phandle + - description: TX DMA Channel phandle + + dma-names: + items: + - const: rx + - const: tx + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +required: + - compatible + - clocks + - clock-names + - reg + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + i2c@c175000 { + compatible = "qcom,i2c-qup-v2.2.1"; + reg = <0x0c175000 0x600>; + interrupts = ; + clocks = <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>, + <&gcc GCC_BLSP1_AHB_CLK>; + clock-names = "core", "iface"; + clock-frequency = <400000>; + dmas = <&blsp_dma 4>, <&blsp_dma 5>; + dma-names = "rx", "tx"; + #address-cells = <1>; + #size-cells = <0>; + }; From 153366a6f5465e7fc4d01921a56a55901203cb5a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 19:22:24 +0100 Subject: [PATCH 102/212] i2c: qup: Introduce SCL/SDA noise rejection Some I2C devices may be glitchy due to electrical noise coming from the device itself or because of possible board design issues. To overcome this issue, the QUP's I2C in Qualcomm SoCs supports a noise rejection setting for both SCL and SDA lines. Introduce a setting for noise rejection through device properties, "qcom,noise-reject-sda" and "qcom,noise-reject-scl", which will be used to set the level of noise rejection sensitivity. If the properties are not specified, noise rejection will not be enabled. Signed-off-by: AngeloGioacchino Del Regno --- drivers/i2c/busses/i2c-qup.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index fcd35e8de83c..c6b815b33b2f 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -39,6 +39,8 @@ #define QUP_MX_READ_CNT 0x208 #define QUP_IN_FIFO_BASE 0x218 #define QUP_I2C_CLK_CTL 0x400 +#define QUP_I2C_CLK_CTL_SDA_NR GENMASK(27, 26) +#define QUP_I2C_CLK_CTL_SCL_NR GENMASK(25, 24) #define QUP_I2C_STATUS 0x404 #define QUP_I2C_MASTER_GEN 0x408 @@ -1663,6 +1665,7 @@ static int qup_i2c_probe(struct platform_device *pdev) int ret, fs_div, hs_div; u32 src_clk_freq = DEFAULT_SRC_CLK; u32 clk_freq = DEFAULT_CLK_FREQ; + u32 noise_reject_scl = 0, noise_reject_sda = 0; int blocks; bool is_qup_v1; @@ -1860,6 +1863,19 @@ nodma: qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff); } + /* SCL/SDA Noise rejection (optional) */ + ret = device_property_read_u32(qup->dev, "qcom,noise-reject-scl", + &noise_reject_scl); + if (ret == 0) + qup->clk_ctl |= FIELD_PREP(QUP_I2C_CLK_CTL_SCL_NR, + noise_reject_scl); + + ret = device_property_read_u32(qup->dev, "qcom,noise-reject-sda", + &noise_reject_sda); + if (ret == 0) + qup->clk_ctl |= FIELD_PREP(QUP_I2C_CLK_CTL_SDA_NR, + noise_reject_sda); + /* * Time it takes for a byte to be clocked out on the bus. * Each byte takes 9 clock cycles (8 bits + 1 ack). From 2c1bb0a77eb863b83b7d6adcaae9b96fbee86178 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 19:24:33 +0100 Subject: [PATCH 103/212] dt-bindings: i2c: qcom,i2c-qup: Document noise rejection properties Document the new noise rejection properties "qcom,noise-reject-sda" and "qcom,noise-reject-scl". Signed-off-by: AngeloGioacchino Del Regno --- .../devicetree/bindings/i2c/qcom,i2c-qup.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml index c5c7db3ac2a6..3f14dd65c6b9 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml @@ -58,6 +58,20 @@ properties: '#size-cells': const: 0 + qcom,noise-reject-sda: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Noise rejection level for the SDA line. + minimum: 0 + maximum: 3 + default: 0 + + qcom,noise-reject-scl: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Noise rejection level for the SCL line. + minimum: 0 + maximum: 3 + default: 0 + required: - compatible - clocks From 77aec8f806844c5b37c83ca004bb9f5a850e477c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 20 Jan 2021 13:32:32 +0100 Subject: [PATCH 104/212] dt-bindings: interconnect: Add bindings for Qualcomm MSM8998 NoC Add the bindings for the Qualcomm MSM8998 NoC interconnects. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/interconnect/qcom,msm8998.yaml | 158 ++++++++++++++++++ .../dt-bindings/interconnect/qcom,msm8998.h | 128 ++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml create mode 100644 include/dt-bindings/interconnect/qcom,msm8998.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml new file mode 100644 index 000000000000..3bea90cbe053 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,msm8998.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8998 Network-On-Chip interconnect + +maintainers: + - AngeloGioacchino Del Regno + - Konrad Dybcio + +description: | + The Qualcomm MSM8998 interconnect providers support adjusting the + bandwidth requirements between the various NoC fabrics. + +properties: + reg: + maxItems: 1 + + compatible: + enum: + - qcom,msm8998-a1noc + - qcom,msm8998-a2noc + - qcom,msm8998-bimc + - qcom,msm8998-cnoc + - qcom,msm8998-gnoc + - qcom,msm8998-mnoc + - qcom,msm8998-snoc + + '#interconnect-cells': + const: 1 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + minItems: 1 + maxItems: 3 + +required: + - compatible + - reg + - '#interconnect-cells' + - clock-names + - clocks + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8998-mnoc + then: + properties: + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + - description: CPU-NoC High-performance Bus Clock. + clock-names: + items: + - const: bus + - const: bus_a + - const: iface + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8998-a2noc + - qcom,msm8998-bimc + - qcom,msm8998-cnoc + - qcom,msm8998-gnoc + - qcom,msm8998-snoc + then: + properties: + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + clock-names: + items: + - const: bus + - const: bus_a + +examples: + - | + #include + #include + + bimc: interconnect@1008000 { + compatible = "qcom,msm8998-bimc"; + reg = <0x01008000 0x78000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; + + cnoc: interconnect@1500000 { + compatible = "qcom,msm8998-cnoc"; + reg = <0x01500000 0x10000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_CNOC_CLK>, + <&rpmcc RPM_SMD_CNOC_A_CLK>; + }; + + snoc: interconnect@1625000 { + compatible = "qcom,msm8998-snoc"; + reg = <0x01625000 0x6100>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + }; + + a1noc: interconnect@1669000 { + compatible = "qcom,msm8998-a1noc"; + reg = <0x01669000 0x5020>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>; + }; + + a2noc: interconnect@1705000 { + compatible = "qcom,msm8998-a2noc"; + reg = <0x01705000 0xa090>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>; + }; + + mnoc: interconnect@1744000 { + compatible = "qcom,msm8998-mnoc"; + reg = <0x01744000 0xb010>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a", "iface"; + clocks = <&rpmcc RPM_SMD_MMAXI_CLK>, + <&rpmcc RPM_SMD_MMAXI_A_CLK>, + <&mmcc AHB_CLK_SRC>; + }; + + gnoc: interconnect@17900000 { + compatible = "qcom,msm8998-gnoc"; + reg = <0x17900000 0xe000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&xo_board>, <&xo_board>; + }; diff --git a/include/dt-bindings/interconnect/qcom,msm8998.h b/include/dt-bindings/interconnect/qcom,msm8998.h new file mode 100644 index 000000000000..5b94403ef178 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8998.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* MSM8998 interconnect IDs */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8998_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8998_H + +/* A1NOC */ +#define MASTER_PCIE_0 0 +#define MASTER_USB3 1 +#define MASTER_UFS 2 +#define MASTER_BLSP_2 3 +#define SLAVE_A1NOC_SNOC 4 + +/* A2NOC */ +#define MASTER_IPA 0 +#define MASTER_CNOC_A2NOC 1 +#define MASTER_SDCC_2 2 +#define MASTER_SDCC_4 3 +#define MASTER_TSIF 4 +#define MASTER_BLSP_1 5 +#define MASTER_CRVIRT_A2NOC 6 +#define MASTER_CRYPTO_C0 7 +#define SLAVE_A2NOC_SNOC 8 +#define SLAVE_CRVIRT_A2NOC 9 + +/* BIMC */ +#define MASTER_GNOC_BIMC 0 +#define MASTER_OXILI 1 +#define MASTER_MNOC_BIMC 2 +#define MASTER_SNOC_BIMC 3 +#define SLAVE_EBI 4 +#define SLAVE_HMSS_L3 5 +#define SLAVE_BIMC_SNOC_0 6 +#define SLAVE_BIMC_SNOC_1 7 + +/* CNOC */ +#define MASTER_SNOC_CNOC 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_CNOC_A2NOC 2 +#define SLAVE_SSC_CFG 3 +#define SLAVE_MPM 4 +#define SLAVE_PMIC_ARB 5 +#define SLAVE_TLMM_NORTH 6 +#define SLAVE_PIMEM_CFG 7 +#define SLAVE_IMEM_CFG 8 +#define SLAVE_MESSAGE_RAM 9 +#define SLAVE_SKL 10 +#define SLAVE_BIMC_CFG 11 +#define SLAVE_PRNG 12 +#define SLAVE_A2NOC_CFG 13 +#define SLAVE_IPA 14 +#define SLAVE_TCSR 15 +#define SLAVE_SNOC_CFG 16 +#define SLAVE_CLK_CTL 17 +#define SLAVE_GLM 18 +#define SLAVE_SPDM 19 +#define SLAVE_GPUSS_CFG 20 +#define SLAVE_CNOC_MNOC_CFG 21 +#define SLAVE_QM_CFG 22 +#define SLAVE_MSS_CFG 23 +#define SLAVE_UFS_CFG 24 +#define SLAVE_TLMM_WEST 25 +#define SLAVE_A1NOC_CFG 26 +#define SLAVE_AHB2PHY 27 +#define SLAVE_BLSP_2 28 +#define SLAVE_PDM 29 +#define SLAVE_USB3_0 30 +#define SLAVE_A1NOC_SMMU_CFG 31 +#define SLAVE_BLSP_1 32 +#define SLAVE_SDCC_2 33 +#define SLAVE_SDCC_4 34 +#define SLAVE_TSIF 35 +#define SLAVE_QDSS_CFG 36 +#define SLAVE_TLMM_EAST 37 +#define SLAVE_CNOC_MNOC_MMSS_CFG 38 +#define SLAVE_SRVC_CNOC 39 + +/* GNOC */ +#define MASTER_APSS_PROC 0 +#define SLAVE_GNOC_BIMC 1 + +/* MNOC */ +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CPP 1 +#define MASTER_JPEG 2 +#define MASTER_MDP_P0 3 +#define MASTER_MDP_P1 4 +#define MASTER_ROTATOR 5 +#define MASTER_VENUS 6 +#define MASTER_VFE 7 +#define MASTER_VENUS_VMEM 8 +#define SLAVE_MNOC_BIMC 9 +#define SLAVE_VMEM 10 +#define SLAVE_SRVC_MNOC 11 +#define MASTER_CNOC_MNOC_MMSS_CFG 12 +#define SLAVE_CAMERA_CFG 13 +#define SLAVE_CAMERA_THROTTLE_CFG 14 +#define SLAVE_MISC_CFG 15 +#define SLAVE_VENUS_THROTTLE_CFG 16 +#define SLAVE_VENUS_CFG 17 +#define SLAVE_VMEM_CFG 18 +#define SLAVE_MMSS_CLK_XPU_CFG 19 +#define SLAVE_MMSS_CLK_CFG 20 +#define SLAVE_DISPLAY_CFG 21 +#define SLAVE_DISPLAY_THROTTLE_CFG 22 +#define SLAVE_SMMU_CFG 23 + +/* SNOC */ +#define MASTER_HMSS 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_SNOC_CFG 2 +#define MASTER_BIMC_SNOC_0 3 +#define MASTER_BIMC_SNOC_1 4 +#define MASTER_A1NOC_SNOC 5 +#define MASTER_A2NOC_SNOC 6 +#define MASTER_QDSS_ETR 7 +#define SLAVE_HMSS 8 +#define SLAVE_LPASS 9 +#define SLAVE_WLAN 10 +#define SLAVE_SNOC_BIMC 11 +#define SLAVE_SNOC_CNOC 12 +#define SLAVE_IMEM 13 +#define SLAVE_PIMEM 14 +#define SLAVE_QDSS_STM 15 +#define SLAVE_PCIE_0 16 +#define SLAVE_SRVC_SNOC 17 + +#endif From c0287eb90546b755406ed94cb3c4a1c24ffa3c38 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 20 Jan 2021 13:33:59 +0100 Subject: [PATCH 105/212] interconnect: qcom: Add MSM8998 interconnect provider driver Introduce a driver for the Qualcomm interconnect busses found in the MSM/APQ8998 SoCs. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. On a note, these chips are managing the "bus QoS" in a "hybrid" fashion: some of the paths in the topology are managed through and by, of course) the RPM uC, while some others are "AP Owned", meaning that the AP shall do direct writes to the appropriate QoS registers for the specific paths and ports, instead of sending an indication to the RPM and leaving the job to that one. Co-authored-by: AngeloGioacchino Del Regno Signed-off-by: Konrad Dybcio Signed-off-by: AngeloGioacchino Del Regno --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8998.c | 981 ++++++++++++++++++++++++++++ 3 files changed, 992 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8998.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index daf1e25f6042..56391f3c5312 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -35,6 +35,15 @@ config INTERCONNECT_QCOM_MSM8974 This is a driver for the Qualcomm Network-on-Chip on msm8974-based platforms. +config INTERCONNECT_QCOM_MSM8998 + tristate "Qualcomm MSM8998 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8998-based + platforms. + config INTERCONNECT_QCOM_OSM_L3 tristate "Qualcomm OSM L3 interconnect driver" depends on INTERCONNECT_QCOM || COMPILE_TEST diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 69300b1d48ef..3a7591ca60d5 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -4,6 +4,7 @@ icc-bcm-voter-objs := bcm-voter.o qnoc-msm8916-objs := msm8916.o qnoc-msm8939-objs := msm8939.o qnoc-msm8974-objs := msm8974.o +qnoc-msm8998-objs := msm8998.o icc-osm-l3-objs := osm-l3.o qnoc-qcs404-objs := qcs404.o icc-rpmh-obj := icc-rpmh.o @@ -22,6 +23,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8998) += qnoc-msm8998.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o diff --git a/drivers/interconnect/qcom/msm8998.c b/drivers/interconnect/qcom/msm8998.c new file mode 100644 index 000000000000..764c2fc18a62 --- /dev/null +++ b/drivers/interconnect/qcom/msm8998.c @@ -0,0 +1,981 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm MSM8998 Network-on-Chip (NoC) QoS driver + * Copyright (c) 2020, AngeloGioacchino Del Regno + * + * Copyright (C) 2020, Konrad Dybcio + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd-rpm.h" + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +/* BIMC QoS */ +#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) +#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) +#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i)) + +#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000 +#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300 +#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3 +#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8 +#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f + +#define M_BKE_EN_EN_BMASK 0x1 + +/* Valid for both NoC and BIMC */ +#define NOC_QOS_MODE_FIXED 0x0 +#define NOC_QOS_MODE_LIMITER 0x1 +#define NOC_QOS_MODE_BYPASS 0x2 + +/* NoC QoS */ +#define NOC_PERM_MODE_FIXED 1 +#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) + +#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000)) +#define NOC_QOS_PRIORITY_MASK 0xf +#define NOC_QOS_PRIORITY_P1_SHIFT 0x2 +#define NOC_QOS_PRIORITY_P0_SHIFT 0x3 + +#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) +#define NOC_QOS_MODEn_MASK 0x3 + +enum { + MSM8998_MASTER_IPA = 1, + MSM8998_MASTER_CNOC_A2NOC, + MSM8998_MASTER_SDCC_2, + MSM8998_MASTER_SDCC_4, + MSM8998_MASTER_BLSP_1, + MSM8998_MASTER_BLSP_2, + MSM8998_MASTER_UFS, + MSM8998_MASTER_USB_HS, + MSM8998_MASTER_USB3, + MSM8998_MASTER_CRYPTO_C0, + MSM8998_MASTER_GNOC_BIMC, + MSM8998_MASTER_OXILI, + MSM8998_MASTER_MNOC_BIMC, + MSM8998_MASTER_SNOC_BIMC, + MSM8998_MASTER_PIMEM, + MSM8998_MASTER_SNOC_CNOC, + MSM8998_MASTER_QDSS_DAP, + MSM8998_MASTER_APPS_PROC, + MSM8998_MASTER_CNOC_MNOC_MMSS_CFG, + MSM8998_MASTER_CNOC_MNOC_CFG, + MSM8998_MASTER_CPP, + MSM8998_MASTER_JPEG, + MSM8998_MASTER_MDP_P0, + MSM8998_MASTER_MDP_P1, + MSM8998_MASTER_VENUS, + MSM8998_MASTER_VFE, + MSM8998_MASTER_QDSS_ETR, + MSM8998_MASTER_QDSS_BAM, + MSM8998_MASTER_SNOC_CFG, + MSM8998_MASTER_BIMC_SNOC, + MSM8998_MASTER_A1NOC_SNOC, + MSM8998_MASTER_A2NOC_SNOC, + MSM8998_MASTER_GNOC_SNOC, + MSM8998_MASTER_PCIE_0, + MSM8998_MASTER_A2NOC_TSIF, + MSM8998_MASTER_CRVIRT_A2NOC, + MSM8998_MASTER_ROTATOR, + MSM8998_MASTER_VENUS_VMEM, + MSM8998_MASTER_HMSS, + MSM8998_MASTER_BIMC_SNOC_0, + MSM8998_MASTER_BIMC_SNOC_1, + + MSM8998_SLAVE_A1NOC_SNOC, + MSM8998_SLAVE_A2NOC_SNOC, + MSM8998_SLAVE_EBI, + MSM8998_SLAVE_HMSS_L3, + MSM8998_SLAVE_CNOC_A2NOC, + MSM8998_SLAVE_MPM, + MSM8998_SLAVE_PMIC_ARB, + MSM8998_SLAVE_TLMM_NORTH, + MSM8998_SLAVE_TCSR, + MSM8998_SLAVE_PIMEM_CFG, + MSM8998_SLAVE_IMEM_CFG, + MSM8998_SLAVE_MESSAGE_RAM, + MSM8998_SLAVE_GLM, + MSM8998_SLAVE_BIMC_CFG, + MSM8998_SLAVE_PRNG, + MSM8998_SLAVE_SPDM, + MSM8998_SLAVE_QDSS_CFG, + MSM8998_SLAVE_CNOC_MNOC_CFG, + MSM8998_SLAVE_SNOC_CFG, + MSM8998_SLAVE_QM_CFG, + MSM8998_SLAVE_CLK_CTL, + MSM8998_SLAVE_MSS_CFG, + MSM8998_SLAVE_UFS_CFG, + MSM8998_SLAVE_A2NOC_CFG, + MSM8998_SLAVE_A2NOC_SMMU_CFG, + MSM8998_SLAVE_GPUSS_CFG, + MSM8998_SLAVE_AHB2PHY, + MSM8998_SLAVE_BLSP_1, + MSM8998_SLAVE_SDCC_2, + MSM8998_SLAVE_SDCC_4, + MSM8998_SLAVE_BLSP_2, + MSM8998_SLAVE_PDM, + MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, + MSM8998_SLAVE_USB_HS, + MSM8998_SLAVE_USB3_0, + MSM8998_SLAVE_SRVC_CNOC, + MSM8998_SLAVE_GNOC_BIMC, + MSM8998_SLAVE_GNOC_SNOC, + MSM8998_SLAVE_CAMERA_CFG, + MSM8998_SLAVE_CAMERA_THROTTLE_CFG, + MSM8998_SLAVE_MISC_CFG, + MSM8998_SLAVE_VENUS_THROTTLE_CFG, + MSM8998_SLAVE_VENUS_CFG, + MSM8998_SLAVE_MMSS_CLK_XPU_CFG, + MSM8998_SLAVE_MMSS_CLK_CFG, + MSM8998_SLAVE_MNOC_MPU_CFG, + MSM8998_SLAVE_DISPLAY_CFG, + MSM8998_SLAVE_CSI_PHY_CFG, + MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, + MSM8998_SLAVE_SMMU_CFG, + MSM8998_SLAVE_MNOC_BIMC, + MSM8998_SLAVE_SRVC_MNOC, + MSM8998_SLAVE_HMSS, + MSM8998_SLAVE_LPASS, + MSM8998_SLAVE_WLAN, + MSM8998_SLAVE_CDSP, + MSM8998_SLAVE_IPA, + MSM8998_SLAVE_SNOC_BIMC, + MSM8998_SLAVE_SNOC_CNOC, + MSM8998_SLAVE_IMEM, + MSM8998_SLAVE_PIMEM, + MSM8998_SLAVE_QDSS_STM, + MSM8998_SLAVE_SRVC_SNOC, + MSM8998_SLAVE_BIMC_SNOC_0, + MSM8998_SLAVE_BIMC_SNOC_1, + MSM8998_SLAVE_SSC_CFG, + MSM8998_SLAVE_SKL, + MSM8998_SLAVE_TLMM_WEST, + MSM8998_SLAVE_A1NOC_CFG, + MSM8998_SLAVE_A1NOC_SMMU_CFG, + MSM8998_SLAVE_TSIF, + MSM8998_SLAVE_TLMM_EAST, + MSM8998_SLAVE_CRVIRT_A2NOC, + MSM8998_SLAVE_VMEM_CFG, + MSM8998_SLAVE_VMEM, + MSM8998_SLAVE_PCIE_0, +}; + +#define to_qcom_provider(_provider) \ + container_of(_provider, struct qcom_icc_provider, provider) + +static const struct clk_bulk_data bus_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, +}; + +static const struct clk_bulk_data bus_mm_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, + { .id = "iface" }, +}; + +/** + * struct qcom_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + * @is_bimc_node: indicates whether to use bimc specific setting + * @mmio: NoC base iospace + */ +struct qcom_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; + bool is_bimc_node; + struct regmap *regmap; + void __iomem *mmio; +}; + +#define MSM8998_MAX_LINKS 38 + +/** + * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters + * @areq_prio: node requests priority + * @prio_level: priority level for bus communication + * @limit_commands: activate/deactivate limiter mode during runtime + * @ap_owned: indicates if the node is owned by the AP or by the RPM + * @qos_mode: default qos mode for this node + * @qos_port: qos port number for finding qos registers of this node + */ +struct qcom_icc_qos { + u32 areq_prio; + u32 prio_level; + bool limit_commands; + bool ap_owned; + int qos_mode; + int qos_port; +}; + +/** + * struct qcom_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @mas_rpm_id: RPM id for devices that are bus masters + * @slv_rpm_id: RPM id for devices that are bus slaves + * @qos: NoC QoS setting parameters + * @rate: current bus clock rate in Hz + */ +struct qcom_icc_node { + unsigned char *name; + u16 id; + u16 links[MSM8998_MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + struct qcom_icc_qos qos; + u64 rate; +}; + +struct qcom_icc_desc { + struct qcom_icc_node **nodes; + size_t num_nodes; + const struct regmap_config *regmap_cfg; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ + static struct qcom_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .qos.ap_owned = _ap_owned, \ + .qos.qos_mode = _qos_mode, \ + .qos.areq_prio = _qos_prio, \ + .qos.prio_level = _qos_prio, \ + .qos.qos_port = _qos_port, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +/* masters */ +DEFINE_QNODE(mas_pcie_0, MSM8998_MASTER_PCIE_0, 16, 45, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_usb3, MSM8998_MASTER_USB3, 16, 32, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_ufs, MSM8998_MASTER_UFS, 16, 68, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_blsp_2, MSM8998_MASTER_BLSP_2, 16, 39, -1, false, NOC_QOS_MODE_FIXED, 0, 4, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_cnoc_a2noc, MSM8998_MASTER_CNOC_A2NOC, 8, 146, -1, true, -1, 0, -1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_ipa, MSM8998_MASTER_IPA, 8, 59, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_sdcc_2, MSM8998_MASTER_SDCC_2, 8, 35, -1, false, NOC_QOS_MODE_FIXED, 0, 6, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_sdcc_4, MSM8998_MASTER_SDCC_4, 8, 36, -1, false, NOC_QOS_MODE_FIXED, 0, 7, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_tsif, MSM8998_MASTER_A2NOC_TSIF, 4, 37, -1, true, -1, 0, -1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_blsp_1, MSM8998_MASTER_BLSP_1, 16, 41, -1, false, NOC_QOS_MODE_FIXED, 0, 8, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_crvirt_a2noc, MSM8998_MASTER_CRVIRT_A2NOC, 8, 145, -1, false, NOC_QOS_MODE_FIXED, 0, 9, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_gnoc_bimc, MSM8998_MASTER_GNOC_BIMC, 8, 144, -1, true, NOC_QOS_MODE_FIXED, 0, 0, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_oxili, MSM8998_MASTER_OXILI, 8, 6, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, MSM8998_SLAVE_BIMC_SNOC_1, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_mnoc_bimc, MSM8998_MASTER_MNOC_BIMC, 8, 2, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, MSM8998_SLAVE_BIMC_SNOC_1, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_snoc_bimc, MSM8998_MASTER_SNOC_BIMC, 8, 3, -1, false, NOC_QOS_MODE_BYPASS, 0, 3, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI); +DEFINE_QNODE(mas_snoc_cnoc, MSM8998_MASTER_SNOC_CNOC, 8, 52, -1, true, -1, 0, -1, MSM8998_SLAVE_SKL, MSM8998_SLAVE_BLSP_2, MSM8998_SLAVE_MESSAGE_RAM, MSM8998_SLAVE_TLMM_WEST, MSM8998_SLAVE_TSIF, MSM8998_SLAVE_MPM, MSM8998_SLAVE_BIMC_CFG, MSM8998_SLAVE_TLMM_EAST, MSM8998_SLAVE_SPDM, MSM8998_SLAVE_PIMEM_CFG, MSM8998_SLAVE_A1NOC_SMMU_CFG, MSM8998_SLAVE_BLSP_1, MSM8998_SLAVE_CLK_CTL, MSM8998_SLAVE_PRNG, MSM8998_SLAVE_USB3_0, MSM8998_SLAVE_QDSS_CFG, MSM8998_SLAVE_QM_CFG, MSM8998_SLAVE_A2NOC_CFG, MSM8998_SLAVE_PMIC_ARB, MSM8998_SLAVE_UFS_CFG, MSM8998_SLAVE_SRVC_CNOC, MSM8998_SLAVE_AHB2PHY, MSM8998_SLAVE_IPA, MSM8998_SLAVE_GLM, MSM8998_SLAVE_SNOC_CFG, MSM8998_SLAVE_SSC_CFG, MSM8998_SLAVE_SDCC_2, MSM8998_SLAVE_SDCC_4, MSM8998_SLAVE_PDM, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, MSM8998_SLAVE_CNOC_MNOC_CFG, MSM8998_SLAVE_MSS_CFG, MSM8998_SLAVE_IMEM_CFG, MSM8998_SLAVE_A1NOC_CFG, MSM8998_SLAVE_GPUSS_CFG, MSM8998_SLAVE_TCSR, MSM8998_SLAVE_TLMM_NORTH); +DEFINE_QNODE(mas_qdss_dap, MSM8998_MASTER_QDSS_DAP, 8, 49, -1, true, -1, 0, -1, MSM8998_SLAVE_SKL, MSM8998_SLAVE_BLSP_2, MSM8998_SLAVE_MESSAGE_RAM, MSM8998_SLAVE_TLMM_WEST, MSM8998_SLAVE_TSIF, MSM8998_SLAVE_MPM, MSM8998_SLAVE_BIMC_CFG, MSM8998_SLAVE_TLMM_EAST, MSM8998_SLAVE_SPDM, MSM8998_SLAVE_PIMEM_CFG, MSM8998_SLAVE_A1NOC_SMMU_CFG, MSM8998_SLAVE_BLSP_1, MSM8998_SLAVE_CLK_CTL, MSM8998_SLAVE_PRNG, MSM8998_SLAVE_USB3_0, MSM8998_SLAVE_QDSS_CFG, MSM8998_SLAVE_QM_CFG, MSM8998_SLAVE_A2NOC_CFG, MSM8998_SLAVE_PMIC_ARB, MSM8998_SLAVE_UFS_CFG, MSM8998_SLAVE_SRVC_CNOC, MSM8998_SLAVE_AHB2PHY, MSM8998_SLAVE_IPA, MSM8998_SLAVE_GLM, MSM8998_SLAVE_SNOC_CFG, MSM8998_SLAVE_SDCC_2, MSM8998_SLAVE_SDCC_4, MSM8998_SLAVE_PDM, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, MSM8998_SLAVE_CNOC_MNOC_CFG, MSM8998_SLAVE_MSS_CFG, MSM8998_SLAVE_IMEM_CFG, MSM8998_SLAVE_A1NOC_CFG, MSM8998_SLAVE_GPUSS_CFG, MSM8998_SLAVE_SSC_CFG, MSM8998_SLAVE_TCSR, MSM8998_SLAVE_TLMM_NORTH, MSM8998_SLAVE_CNOC_A2NOC); +DEFINE_QNODE(mas_crypto, MSM8998_MASTER_CRYPTO_C0, 650, 23, -1, false, -1, 0, -1, MSM8998_MASTER_CRVIRT_A2NOC); +DEFINE_QNODE(mas_apss_proc, MSM8998_MASTER_APPS_PROC, 32, 0, -1, true, -1, 0, -1, MSM8998_SLAVE_GNOC_BIMC); +DEFINE_QNODE(mas_cnoc_mnoc_mmss_cfg, MSM8998_MASTER_CNOC_MNOC_MMSS_CFG, 8, 4, -1, true, -1, 0, -1, MSM8998_SLAVE_CAMERA_THROTTLE_CFG, MSM8998_SLAVE_VENUS_CFG, MSM8998_SLAVE_MISC_CFG, MSM8998_SLAVE_CAMERA_CFG, MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, MSM8998_SLAVE_VENUS_THROTTLE_CFG, MSM8998_SLAVE_DISPLAY_CFG, MSM8998_SLAVE_MMSS_CLK_CFG, MSM8998_SLAVE_VMEM_CFG, MSM8998_SLAVE_MMSS_CLK_XPU_CFG, MSM8998_SLAVE_SMMU_CFG); +DEFINE_QNODE(mas_cnoc_mnoc_cfg, MSM8998_MASTER_CNOC_MNOC_CFG, 8, 5, -1, true, -1, 0, -1, MSM8998_SLAVE_SRVC_MNOC); +DEFINE_QNODE(mas_cpp, MSM8998_MASTER_CPP, 32, 115, -1, true, NOC_QOS_MODE_BYPASS, 0, 5, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_jpeg, MSM8998_MASTER_JPEG, 32, 7, -1, true, NOC_QOS_MODE_BYPASS, 0, 7, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_mdp_p0, MSM8998_MASTER_MDP_P0, 32, 8, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, MSM8998_SLAVE_MNOC_BIMC); /* vrail-comp???? */ +DEFINE_QNODE(mas_mdp_p1, MSM8998_MASTER_MDP_P1, 32, 61, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, MSM8998_SLAVE_MNOC_BIMC); /* vrail-comp??? */ +DEFINE_QNODE(mas_rotator, MSM8998_MASTER_ROTATOR, 32, 120, -1, true, NOC_QOS_MODE_BYPASS, 0, 0, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_venus, MSM8998_MASTER_VENUS, 32, 9, -1, true, NOC_QOS_MODE_BYPASS, 0, 3, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_vfe, MSM8998_MASTER_VFE, 32, 11, -1, true, NOC_QOS_MODE_BYPASS, 0, 6, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_venus_vmem, MSM8998_MASTER_VENUS_VMEM, 32, 121, -1, true, -1, 0, -1, MSM8998_SLAVE_VMEM); +DEFINE_QNODE(mas_hmss, MSM8998_MASTER_HMSS, 16, 118, -1, true, NOC_QOS_MODE_FIXED, 1, 3, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_SNOC_BIMC); +DEFINE_QNODE(mas_qdss_bam, MSM8998_MASTER_QDSS_BAM, 16, 19, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_SNOC_BIMC); +DEFINE_QNODE(mas_snoc_cfg, MSM8998_MASTER_SNOC_CFG, 16, 20, -1, false, -1, 0, -1, MSM8998_SLAVE_SRVC_SNOC); +DEFINE_QNODE(mas_bimc_snoc_0, MSM8998_MASTER_BIMC_SNOC_0, 16, 21, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_WLAN, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_bimc_snoc_1, MSM8998_MASTER_BIMC_SNOC_1, 16, 109, -1, true, -1, 0, -1, MSM8998_SLAVE_PCIE_0); +DEFINE_QNODE(mas_a1noc_snoc, MSM8998_MASTER_A1NOC_SNOC, 16, 111, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_PCIE_0, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_SNOC_BIMC, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_a2noc_snoc, MSM8998_MASTER_A2NOC_SNOC, 16, 112, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_PCIE_0, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_SNOC_BIMC, MSM8998_SLAVE_WLAN, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_qdss_etr, MSM8998_MASTER_QDSS_ETR, 16, 31, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_IMEM, MSM8998_MASTER_PIMEM, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_SNOC_BIMC); + +/* slaves */ +DEFINE_QNODE(slv_a1noc_snoc, MSM8998_SLAVE_A1NOC_SNOC, 16, -1, 142, false, -1, 0, -1, MSM8998_MASTER_A1NOC_SNOC); +DEFINE_QNODE(slv_a2noc_snoc, MSM8998_SLAVE_A2NOC_SNOC, 16, -1, 143, false, -1, 0, -1, MSM8998_MASTER_A2NOC_SNOC); +DEFINE_QNODE(slv_ebi, MSM8998_SLAVE_EBI, 8, -1, 0, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_hmss_l3, MSM8998_SLAVE_HMSS_L3, 8, -1, 160, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_bimc_snoc_0, MSM8998_SLAVE_BIMC_SNOC_0, 8, -1, 2, false, -1, 0, -1, MSM8998_MASTER_BIMC_SNOC_0); +DEFINE_QNODE(slv_bimc_snoc_1, MSM8998_SLAVE_BIMC_SNOC_1, 8, -1, 138, true, -1, 0, -1, MSM8998_MASTER_BIMC_SNOC_1); +DEFINE_QNODE(slv_cnoc_a2noc, MSM8998_SLAVE_CNOC_A2NOC, 4, -1, 208, true, -1, 0, -1, MSM8998_MASTER_CNOC_A2NOC); +DEFINE_QNODE(slv_ssc_cfg, MSM8998_SLAVE_SSC_CFG, 4, -1, 177, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mpm, MSM8998_SLAVE_MPM, 4, -1, 62, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pmic_arb, MSM8998_SLAVE_PMIC_ARB, 4, -1, 59, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_north, MSM8998_SLAVE_TLMM_NORTH, 4, -1, 214, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pimem_cfg, MSM8998_SLAVE_PIMEM_CFG, 4, -1, 167, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_imem_cfg, MSM8998_SLAVE_IMEM_CFG, 4, -1, 54, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_message_ram, MSM8998_SLAVE_MESSAGE_RAM, 4, -1, 55, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_skl, MSM8998_SLAVE_SKL, 4, -1, 196, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_bimc_cfg, MSM8998_SLAVE_BIMC_CFG, 4, -1, 56, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_prng, MSM8998_SLAVE_PRNG, 4, -1, 44, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a2noc_cfg, MSM8998_SLAVE_A2NOC_CFG, 4, -1, 150, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ipa, MSM8998_SLAVE_IPA, 4, -1, 183, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tcsr, MSM8998_SLAVE_TCSR, 4, -1, 50, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_snoc_cfg, MSM8998_SLAVE_SNOC_CFG, 4, -1, 70, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_clk_ctl, MSM8998_SLAVE_CLK_CTL, 4, -1, 47, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_glm, MSM8998_SLAVE_GLM, 4, -1, 209, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_spdm, MSM8998_SLAVE_SPDM, 4, -1, 60, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_gpuss_cfg, MSM8998_SLAVE_GPUSS_CFG, 4, -1, 11, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8998_SLAVE_BLSP_1, 4, -1, 66, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_CFG); +DEFINE_QNODE(slv_qm_cfg, MSM8998_SLAVE_QM_CFG, 4, -1, 212, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mss_cfg, MSM8998_SLAVE_MSS_CFG, 4, -1, 48, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ufs_cfg, MSM8998_SLAVE_UFS_CFG, 4, -1, 92, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_west, MSM8998_SLAVE_TLMM_WEST, 4, -1, 215, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a1noc_cfg, MSM8998_SLAVE_A1NOC_CFG, 4, -1, 147, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ahb2phy, MSM8998_SLAVE_AHB2PHY, 4, -1, 163, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_blsp_2, MSM8998_SLAVE_BLSP_2, 4, -1, 37, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pdm, MSM8998_SLAVE_PDM, 4, -1, 41, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_usb3_0, MSM8998_SLAVE_USB3_0, 4, -1, 22, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a1noc_smmu_cfg, MSM8998_SLAVE_A1NOC_SMMU_CFG, 8, -1, 149, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_blsp_1, MSM8998_SLAVE_BLSP_1, 4, -1, 39, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_sdcc_2, MSM8998_SLAVE_SDCC_2, 4, -1, 33, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_sdcc_4, MSM8998_SLAVE_SDCC_4, 4, -1, 34, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tsif, MSM8998_SLAVE_TSIF, 4, -1, 35, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_qdss_cfg, MSM8998_SLAVE_QDSS_CFG, 4, -1, 63, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_east, MSM8998_SLAVE_TLMM_EAST, 4, -1, 213, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, 8, -1, 58, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_MMSS_CFG); +DEFINE_QNODE(slv_srvc_cnoc, MSM8998_SLAVE_SRVC_CNOC, 4, -1, 76, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_crvirt_a2noc, MSM8998_SLAVE_CRVIRT_A2NOC, 8, -1, 207, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_gnoc_bimc, MSM8998_SLAVE_GNOC_BIMC, 32, -1, 210, true, -1, 0, -1, MSM8998_MASTER_GNOC_BIMC); +DEFINE_QNODE(slv_camera_cfg, MSM8998_SLAVE_CAMERA_CFG, 8, -1, 3, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_camera_throttle_cfg, MSM8998_SLAVE_CAMERA_THROTTLE_CFG, 8, -1, 154, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_misc_cfg, MSM8998_SLAVE_MISC_CFG, 8, -1, 8, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_venus_throttle_cfg, MSM8998_SLAVE_VENUS_THROTTLE_CFG, 8, -1, 178, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_venus_cfg, MSM8998_SLAVE_VENUS_CFG, 8, -1, 10, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_vmem_cfg, MSM8998_SLAVE_VMEM_CFG, 8, -1, 180, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8998_SLAVE_MMSS_CLK_XPU_CFG, 8, -1, 13, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mmss_clk_cfg, MSM8998_SLAVE_MMSS_CLK_CFG, 8, -1, 12, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_display_cfg, MSM8998_SLAVE_DISPLAY_CFG, 8, -1, 4, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_display_throttle_cfg, MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, 4, -1, 156, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_smmu_cfg, MSM8998_SLAVE_SMMU_CFG, 8, -1, 205, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mnoc_bimc, MSM8998_SLAVE_MNOC_BIMC, 32, -1, 16, true, -1, 0, -1, MSM8998_MASTER_MNOC_BIMC); +DEFINE_QNODE(slv_vmem, MSM8998_SLAVE_VMEM, 32, -1, 179, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_srvc_mnoc, MSM8998_SLAVE_SRVC_MNOC, 8, -1, 17, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_hmss, MSM8998_SLAVE_HMSS, 16, -1, 20, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_lpass, MSM8998_SLAVE_LPASS, 16, -1, 21, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_wlan, MSM8998_SLAVE_WLAN, 16, -1, 206, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_snoc_bimc, MSM8998_SLAVE_SNOC_BIMC, 32, -1, 24, false, -1, 0, -1, MSM8998_MASTER_SNOC_BIMC); +DEFINE_QNODE(slv_snoc_cnoc, MSM8998_SLAVE_SNOC_CNOC, 16, -1, 25, false, -1, 0, -1, MSM8998_MASTER_SNOC_CNOC); +DEFINE_QNODE(slv_imem, MSM8998_SLAVE_IMEM, 16, -1, 26, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_pimem, MSM8998_SLAVE_PIMEM, 16, -1, 166, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_qdss_stm, MSM8998_SLAVE_QDSS_STM, 16, -1, 30, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_pcie_0, MSM8998_SLAVE_PCIE_0, 16, -1, 84, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_srvc_snoc, MSM8998_SLAVE_SRVC_SNOC, 16, -1, 29, false, -1, 0, -1, 0); + +static struct qcom_icc_node *msm8998_a1noc_nodes[] = { + [MASTER_PCIE_0] = &mas_pcie_0, + [MASTER_USB3] = &mas_usb3, + [MASTER_UFS] = &mas_ufs, + [MASTER_BLSP_2] = &mas_blsp_2, + [SLAVE_A1NOC_SNOC] = &slv_a1noc_snoc, +}; + +static const struct regmap_config msm8998_a1noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x60000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_a1noc = { + .nodes = msm8998_a1noc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_a1noc_nodes), + .regmap_cfg = &msm8998_a1noc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_a2noc_nodes[] = { + [MASTER_IPA] = &mas_ipa, + [MASTER_CNOC_A2NOC] = &mas_cnoc_a2noc, + [MASTER_SDCC_2] = &mas_sdcc_2, + [MASTER_SDCC_4] = &mas_sdcc_4, + [MASTER_TSIF] = &mas_tsif, + [MASTER_BLSP_1] = &mas_blsp_1, + [MASTER_CRVIRT_A2NOC] = &mas_crvirt_a2noc, + [MASTER_CRYPTO_C0] = &mas_crypto, + [SLAVE_A2NOC_SNOC] = &slv_a2noc_snoc, + [SLAVE_CRVIRT_A2NOC] = &slv_crvirt_a2noc, +}; + +static const struct regmap_config msm8998_a2noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x60000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_a2noc = { + .nodes = msm8998_a2noc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_a2noc_nodes), + .regmap_cfg = &msm8998_a2noc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_bimc_nodes[] = { + [MASTER_GNOC_BIMC] = &mas_gnoc_bimc, + [MASTER_OXILI] = &mas_oxili, + [MASTER_MNOC_BIMC] = &mas_mnoc_bimc, + [MASTER_SNOC_BIMC] = &mas_snoc_bimc, + [SLAVE_EBI] = &slv_ebi, + [SLAVE_HMSS_L3] = &slv_hmss_l3, + [SLAVE_BIMC_SNOC_0] = &slv_bimc_snoc_0, + [SLAVE_BIMC_SNOC_1] = &slv_bimc_snoc_1, +}; + +static const struct regmap_config msm8998_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_bimc = { + .nodes = msm8998_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_bimc_nodes), + .regmap_cfg = &msm8998_bimc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_cnoc_nodes[] = { + [MASTER_SNOC_CNOC] = &mas_snoc_cnoc, + [MASTER_QDSS_DAP] = &mas_qdss_dap, + [SLAVE_CNOC_A2NOC] = &slv_cnoc_a2noc, + [SLAVE_SSC_CFG] = &slv_ssc_cfg, + [SLAVE_MPM] = &slv_mpm, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_TLMM_NORTH] = &slv_tlmm_north, + [SLAVE_PIMEM_CFG] = &slv_pimem_cfg, + [SLAVE_IMEM_CFG] = &slv_imem_cfg, + [SLAVE_MESSAGE_RAM] = &slv_message_ram, + [SLAVE_SKL] = &slv_skl, + [SLAVE_BIMC_CFG] = &slv_bimc_cfg, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_A2NOC_CFG] = &slv_a2noc_cfg, + [SLAVE_IPA] = &slv_ipa, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_CLK_CTL] = &slv_clk_ctl, + [SLAVE_GLM] = &slv_glm, + [SLAVE_SPDM] = &slv_spdm, + [SLAVE_GPUSS_CFG] = &slv_gpuss_cfg, + [SLAVE_CNOC_MNOC_CFG] = &slv_cnoc_mnoc_cfg, + [SLAVE_QM_CFG] = &slv_qm_cfg, + [SLAVE_MSS_CFG] = &slv_mss_cfg, + [SLAVE_UFS_CFG] = &slv_ufs_cfg, + [SLAVE_TLMM_WEST] = &slv_tlmm_west, + [SLAVE_A1NOC_CFG] = &slv_a1noc_cfg, + [SLAVE_AHB2PHY] = &slv_ahb2phy, + [SLAVE_BLSP_2] = &slv_blsp_2, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_USB3_0] = &slv_usb3_0, + [SLAVE_A1NOC_SMMU_CFG] = &slv_a1noc_smmu_cfg, + [SLAVE_BLSP_1] = &slv_blsp_1, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_SDCC_4] = &slv_sdcc_4, + [SLAVE_TSIF] = &slv_tsif, + [SLAVE_QDSS_CFG] = &slv_qdss_cfg, + [SLAVE_TLMM_EAST] = &slv_tlmm_east, + [SLAVE_CNOC_MNOC_MMSS_CFG] = &slv_cnoc_mnoc_mmss_cfg, + [SLAVE_SRVC_CNOC] = &slv_srvc_cnoc, +}; + +static const struct regmap_config msm8998_cnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_cnoc = { + .nodes = msm8998_cnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_cnoc_nodes), + .regmap_cfg = &msm8998_cnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_gnoc_nodes[] = { + [MASTER_APSS_PROC] = &mas_apss_proc, + [SLAVE_GNOC_BIMC] = &slv_gnoc_bimc, +}; + +static const struct regmap_config msm8998_gnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_gnoc = { + .nodes = msm8998_gnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_gnoc_nodes), + .regmap_cfg = &msm8998_gnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_mnoc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &mas_cnoc_mnoc_cfg, + [MASTER_CPP] = &mas_cpp, + [MASTER_JPEG] = &mas_jpeg, + [MASTER_MDP_P0] = &mas_mdp_p0, + [MASTER_MDP_P1] = &mas_mdp_p1, + [MASTER_ROTATOR] = &mas_rotator, + [MASTER_VENUS] = &mas_venus, + [MASTER_VFE] = &mas_vfe, + [MASTER_VENUS_VMEM] = &mas_venus_vmem, + [SLAVE_MNOC_BIMC] = &slv_mnoc_bimc, + [SLAVE_VMEM] = &slv_vmem, + [SLAVE_SRVC_MNOC] = &slv_srvc_mnoc, + [MASTER_CNOC_MNOC_MMSS_CFG] = &mas_cnoc_mnoc_mmss_cfg, + [SLAVE_CAMERA_CFG] = &slv_camera_cfg, + [SLAVE_CAMERA_THROTTLE_CFG] = &slv_camera_throttle_cfg, + [SLAVE_MISC_CFG] = &slv_misc_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &slv_venus_throttle_cfg, + [SLAVE_VENUS_CFG] = &slv_venus_cfg, + [SLAVE_VMEM_CFG] = &slv_vmem_cfg, + [SLAVE_MMSS_CLK_XPU_CFG] = &slv_mmss_clk_xpu_cfg, + [SLAVE_MMSS_CLK_CFG] = &slv_mmss_clk_cfg, + [SLAVE_DISPLAY_CFG] = &slv_display_cfg, + [SLAVE_DISPLAY_THROTTLE_CFG] = &slv_display_throttle_cfg, + [SLAVE_SMMU_CFG] = &slv_smmu_cfg, +}; + +static const struct regmap_config msm8998_mnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_mnoc = { + .nodes = msm8998_mnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_mnoc_nodes), + .regmap_cfg = &msm8998_mnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_snoc_nodes[] = { + [MASTER_HMSS] = &mas_hmss, + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_SNOC_CFG] = &mas_snoc_cfg, + [MASTER_BIMC_SNOC_0] = &mas_bimc_snoc_0, + [MASTER_BIMC_SNOC_1] = &mas_bimc_snoc_1, + [MASTER_A1NOC_SNOC] = &mas_a1noc_snoc, + [MASTER_A2NOC_SNOC] = &mas_a2noc_snoc, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [SLAVE_HMSS] = &slv_hmss, + [SLAVE_LPASS] = &slv_lpass, + [SLAVE_WLAN] = &slv_wlan, + [SLAVE_SNOC_BIMC] = &slv_snoc_bimc, + [SLAVE_SNOC_CNOC] = &slv_snoc_cnoc, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_PIMEM] = &slv_pimem, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_PCIE_0] = &slv_pcie_0, + [SLAVE_SRVC_SNOC] = &slv_srvc_snoc, +}; + +static const struct regmap_config msm8998_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_snoc = { + .nodes = msm8998_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_snoc_nodes), + .regmap_cfg = &msm8998_snoc_regmap_config, +}; + +static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, + struct qcom_icc_qos *qos, + int regnum) +{ + u32 val; + u32 mask; + + val = qos->prio_level; + mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; + + val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; + mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; + + /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ + if (regnum != 3) { + val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; + mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; + } + + return regmap_update_bits(rmap, + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), + mask, val); +} + +static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, + bool bypass_mode) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + u32 mode = NOC_QOS_MODE_BYPASS; + u32 val = 0; + int i, rc = 0; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + /* + * QoS Priority: The QoS Health parameters are getting considered + * only if we are NOT in Bypass Mode. + */ + if (mode != NOC_QOS_MODE_BYPASS) { + for (i = 3; i >= 0; i--) { + rc = qcom_icc_bimc_set_qos_health(qp->regmap, + &qn->qos, i); + if (rc) + return rc; + } + + /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ + val = 1; + } + + return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), + M_BKE_EN_EN_BMASK, val); +} + +static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, + struct qcom_icc_qos *qos) +{ + u32 val; + int rc; + + /* Must be updated one at a time, P1 first, P0 last */ + val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; + rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_MASK, val); + if (rc) + return rc; + + val = qos->prio_level << NOC_QOS_PRIORITY_P0_SHIFT; + return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_MASK, val); +} + +static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + u32 mode = NOC_QOS_MODE_BYPASS; + int rc = 0; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + if (qn->qos.qos_port < 0) { + dev_dbg(src->provider->dev, + "NoC QoS: Skipping %s: vote aggregated on parent.\n", + qn->name); + return 0; + } + + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + if (mode == NOC_QOS_MODE_FIXED) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", + qn->name); + rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); + if (rc) + return rc; + } else if (mode == NOC_QOS_MODE_BYPASS) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", + qn->name); + } + + return regmap_update_bits(qp->regmap, + NOC_QOS_MODEn_ADDR(qn->qos.qos_port), + NOC_QOS_MODEn_MASK, mode); +} + +static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) +{ + struct qcom_icc_provider *qp = to_qcom_provider(node->provider); + struct qcom_icc_node *qn = node->data; + + dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); + + if (qp->is_bimc_node) + return qcom_icc_set_bimc_qos(node, sum_bw, + (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS)); + + return qcom_icc_set_noc_qos(node, sum_bw); +} + +static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) +{ + int ret = 0; + + if (mas_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_MASTER_REQ, + mas_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", + mas_rpm_id, ret); + return ret; + } + } + + if (slv_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_SLAVE_REQ, + slv_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", + slv_rpm_id, ret); + return ret; + } + } + + return ret; +} + +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + struct icc_node *n; + u64 sum_bw; + u64 max_peak_bw; + u64 rate; + u32 agg_avg = 0; + u32 agg_peak = 0; + int ret, i; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + if (!qn->qos.ap_owned) { + /* send bandwidth request message to the RPM processor */ + ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); + if (ret) + return ret; + } else if (qn->qos.qos_mode != -1) { + /* set bandwidth directly from the AP */ + ret = qcom_icc_qos_set(src, sum_bw); + if (ret) + return ret; + } + + rate = max(sum_bw, max_peak_bw); + + do_div(rate, qn->buswidth); + + if (qn->rate == rate) + return 0; + + for (i = 0; i < qp->num_clks; i++) { + ret = clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + pr_err("%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + return ret; + } + } + + qn->rate = rate; + + return 0; +} + +static int qnoc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct qcom_icc_desc *desc; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct qcom_icc_node **qnodes; + struct qcom_icc_provider *qp; + struct icc_node *node; + struct resource *res; + size_t num_nodes, i; + int ret; + + /* wait for the RPM proxy */ + if (!qcom_icc_rpm_smd_available()) + return -EPROBE_DEFER; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (of_device_is_compatible(dev->of_node, "qcom,msm8998-mnoc")) { + qp->bus_clks = devm_kmemdup(dev, bus_mm_clocks, + sizeof(bus_mm_clocks), GFP_KERNEL); + qp->num_clks = ARRAY_SIZE(bus_mm_clocks); + } else { + if (of_device_is_compatible(dev->of_node, "qcom,msm8998-bimc")) + qp->is_bimc_node = true; + + qp->bus_clks = devm_kmemdup(dev, bus_clocks, sizeof(bus_clocks), + GFP_KERNEL); + qp->num_clks = ARRAY_SIZE(bus_clocks); + } + if (!qp->bus_clks) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + qp->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(qp->mmio)) { + dev_err(dev, "Cannot ioremap interconnect bus resource\n"); + return PTR_ERR(qp->mmio); + } + + qp->regmap = devm_regmap_init_mmio(dev, qp->mmio, desc->regmap_cfg); + if (IS_ERR(qp->regmap)) { + dev_err(dev, "Cannot regmap interconnect bus resource\n"); + return PTR_ERR(qp->regmap); + } + + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + provider = &qp->provider; + INIT_LIST_HEAD(&provider->nodes); + provider->dev = dev; + provider->set = qcom_icc_set; + provider->aggregate = icc_std_aggregate; + provider->xlate = of_icc_xlate_onecell; + provider->data = data; + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return ret; + } + + for (i = 0; i < num_nodes; i++) { + size_t j; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + platform_set_drvdata(pdev, qp); + + return 0; +err: + icc_nodes_remove(provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + icc_provider_del(provider); + + return ret; +} + +static int qnoc_remove(struct platform_device *pdev) +{ + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + + icc_nodes_remove(&qp->provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return icc_provider_del(&qp->provider); +} + +static const struct of_device_id msm8998_noc_of_match[] = { + { .compatible = "qcom,msm8998-a1noc", .data = &msm8998_a1noc }, + { .compatible = "qcom,msm8998-a2noc", .data = &msm8998_a2noc }, + { .compatible = "qcom,msm8998-bimc", .data = &msm8998_bimc }, + { .compatible = "qcom,msm8998-cnoc", .data = &msm8998_cnoc }, + { .compatible = "qcom,msm8998-gnoc", .data = &msm8998_gnoc }, + { .compatible = "qcom,msm8998-mnoc", .data = &msm8998_mnoc }, + { .compatible = "qcom,msm8998-snoc", .data = &msm8998_snoc }, + { }, +}; +MODULE_DEVICE_TABLE(of, msm8998_noc_of_match); + +static struct platform_driver msm8998_noc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-msm8998", + .of_match_table = msm8998_noc_of_match, + .sync_state = icc_sync_state, + }, +}; +module_platform_driver(msm8998_noc_driver); +MODULE_DESCRIPTION("Qualcomm msm8998 NoC driver"); +MODULE_LICENSE("GPL v2"); From b74746724f2b2b1b7a86b6f7cc124c6c7a13e998 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 4 Dec 2020 22:34:58 +0100 Subject: [PATCH 106/212] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander The Awinic AW9523(B) is a multi-function I2C gpio expander in a TQFN-24L package, featuring PWM (max 37mA per pin, or total max power 3.2Watts) for LED driving capability. It has two ports with 8 pins per port (for a total of 16 pins), configurable as either PWM with 1/256 stepping or GPIO input/output, 1.8V logic input; each GPIO can be configured as input or output independently from each other. This IC also has an internal interrupt controller, which is capable of generating an interrupt for each GPIO, depending on the configuration, and will raise an interrupt on the INTN pin to advertise this to an external interrupt controller. Signed-off-by: AngeloGioacchino Del Regno --- drivers/pinctrl/Kconfig | 17 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-aw9523.c | 1122 ++++++++++++++++++++++++++++++ 3 files changed, 1140 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-aw9523.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 31921108e456..c2907a3603f3 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -111,6 +111,23 @@ config PINCTRL_AMD Requires ACPI/FDT device enumeration code to set up a platform device. +config PINCTRL_AW9523 + bool "Awinic AW9523/AW9523B I2C GPIO expander pinctrl driver" + depends on OF && I2C + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + select REGMAP + help + The Awinic AW9523/AW9523B is a multi-function I2C GPIO + expander with PWM functionality. This driver bundles a + pinctrl driver to select the function muxing and a GPIO + driver to handle GPIO, when the GPIO function is selected. + + Say yes to enable pinctrl and GPIO support for the AW9523(B). + config PINCTRL_BM1880 bool "Bitmain BM1880 Pinctrl driver" depends on OF && (ARCH_BITMAIN || COMPILE_TEST) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 200073bcc2c1..3e3db718e7c0 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o +obj-$(CONFIG_PINCTRL_AW9523) += pinctrl-aw9523.o obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o obj-$(CONFIG_PINCTRL_DA9062) += pinctrl-da9062.o diff --git a/drivers/pinctrl/pinctrl-aw9523.c b/drivers/pinctrl/pinctrl-aw9523.c new file mode 100644 index 000000000000..bc8e1e9d6876 --- /dev/null +++ b/drivers/pinctrl/pinctrl-aw9523.c @@ -0,0 +1,1122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Awinic AW9523B i2c pin controller driver + * Copyright (c) 2020, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define AW9523_MAX_FUNCS 2 +#define AW9523_NUM_PORTS 2 +#define AW9523_PINS_PER_PORT 8 + +/* + * HW needs at least 20uS for reset and at least 1-2uS to recover from + * reset, but we have to account for eventual board quirks, if any: + * for this reason, keep reset asserted for 50uS and wait for 20uS + * to recover from the reset. + */ +#define AW9523_HW_RESET_US 50 +#define AW9523_HW_RESET_RECOVERY_US 20 + +/* Port 0: P0_0...P0_7 - Port 1: P1_0...P1_7 */ +#define AW9523_PIN_TO_PORT(pin) (pin >> 3) +#define AW9523_REG_IN_STATE(pin) (0x00 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_OUT_STATE(pin) (0x02 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_CONF_STATE(pin) (0x04 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_INTR_DIS(pin) (0x06 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_CHIPID 0x10 +#define AW9523_VAL_EXPECTED_CHIPID 0x23 + +#define AW9523_REG_GCR 0x11 +#define AW9523_GCR_ISEL_MASK GENMASK(0, 1) +#define AW9523_GCR_GPOMD_MASK BIT(4) + +#define AW9523_REG_PORT_MODE(pin) (0x12 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_SOFT_RESET 0x7f +#define AW9523_VAL_RESET 0x00 + +/* + * struct aw9523_irq - Interrupt controller structure + * @lock: mutex locking for the irq bus + * @irqchip: structure holding irqchip params + * @cached_gpio: stores the previous gpio status for bit comparison + */ +struct aw9523_irq { + struct mutex lock; + struct irq_chip *irqchip; + u16 cached_gpio; +}; + +/* + * struct aw9523_pinmux - Pin mux params + * @name: Name of the mux + * @grps: Groups of the mux + * @num_grps: Number of groups (sizeof array grps) + */ +struct aw9523_pinmux { + const char *name; + const char * const *grps; + const u8 num_grps; +}; + +/* + * struct aw9523 - Main driver structure + * @dev: device handle + * @regmap: regmap handle for current device + * @i2c_lock: Mutex lock for i2c operations + * @reset_gpio: Hardware reset (RSTN) signal GPIO + * @vio_vreg: VCC regulator (Optional) + * @pctl: pinctrl handle for current device + * @gpio: structure holding gpiochip params + * @irq: Interrupt controller structure + */ +struct aw9523 { + struct device *dev; + struct regmap *regmap; + struct mutex i2c_lock; + struct gpio_desc *reset_gpio; + struct regulator *vio_vreg; + struct pinctrl_dev *pctl; + struct gpio_chip gpio; + struct aw9523_irq *irq; +}; + +static const struct pinctrl_pin_desc aw9523_pins[] = { + /* Port 0 */ + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + + /* Port 1 */ + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), +}; + +static int aw9523_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(aw9523_pins); +} + +static const char *aw9523_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return aw9523_pins[selector].name; +} + +static int aw9523_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &aw9523_pins[selector].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops aw9523_pinctrl_ops = { + .get_groups_count = aw9523_pinctrl_get_groups_count, + .get_group_pins = aw9523_pinctrl_get_group_pins, + .get_group_name = aw9523_pinctrl_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static const char * const gpio_pwm_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15" +}; + +/* Warning: Do NOT reorder this array */ +static const struct aw9523_pinmux aw9523_pmx[] = { + { + .name = "pwm", + .grps = gpio_pwm_groups, + .num_grps = ARRAY_SIZE(gpio_pwm_groups), + }, + { + .name = "gpio", + .grps = gpio_pwm_groups, + .num_grps = ARRAY_SIZE(gpio_pwm_groups), + }, +}; + +static int aw9523_pmx_get_funcs_count(struct pinctrl_dev *pctl) +{ + return ARRAY_SIZE(aw9523_pmx); +} + +static const char *aw9523_pmx_get_fname(struct pinctrl_dev *pctl, + unsigned int sel) +{ + return aw9523_pmx[sel].name; +} + +static int aw9523_pmx_get_groups(struct pinctrl_dev *pctl, unsigned int sel, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = aw9523_pmx[sel].grps; + *num_groups = aw9523_pmx[sel].num_grps; + return 0; +} + +static int aw9523_pmx_set_mux(struct pinctrl_dev *pctl, unsigned int fsel, + unsigned int grp) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctl); + int ret, pin = aw9523_pins[grp].number % AW9523_PINS_PER_PORT; + + if (fsel >= ARRAY_SIZE(aw9523_pmx)) + return -EINVAL; + + /* + * This maps directly to the aw9523_pmx array: programming a + * high bit means "gpio" and a low bit means "pwm". + */ + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_PORT_MODE(pin), + BIT(pin), (fsel ? BIT(pin) : 0)); + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static const struct pinmux_ops aw9523_pinmux_ops = { + .get_functions_count = aw9523_pmx_get_funcs_count, + .get_function_name = aw9523_pmx_get_fname, + .get_function_groups = aw9523_pmx_get_groups, + .set_mux = aw9523_pmx_set_mux, +}; + +static int aw9523_pcfg_param_to_reg(enum pin_config_param pcp, int pin, u8 *r) +{ + u8 reg; + + switch (pcp) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + reg = AW9523_REG_IN_STATE(pin); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + reg = AW9523_REG_GCR; + break; + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT_ENABLE: + reg = AW9523_REG_CONF_STATE(pin); + break; + case PIN_CONFIG_OUTPUT: + reg = AW9523_REG_OUT_STATE(pin); + break; + default: + return -ENOTSUPP; + } + *r = reg; + + return 0; +} + +static int aw9523_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int regbit = pin % AW9523_PINS_PER_PORT; + unsigned int val; + u8 reg; + int rc; + + rc = aw9523_pcfg_param_to_reg(param, pin, ®); + if (rc) + return rc; + + mutex_lock(&awi->i2c_lock); + rc = regmap_read(awi->regmap, reg, &val); + mutex_unlock(&awi->i2c_lock); + if (rc) + return rc; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + val &= BIT(regbit); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_OUTPUT_ENABLE: + val &= BIT(regbit); + val = !val; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (pin >= AW9523_PINS_PER_PORT) + val = 0; + else + val = !FIELD_GET(AW9523_GCR_GPOMD_MASK, val); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (pin >= AW9523_PINS_PER_PORT) + val = 1; + else + val = FIELD_GET(AW9523_GCR_GPOMD_MASK, val); + break; + default: + return -ENOTSUPP; + } + if (val < 1) + return -EINVAL; + + *config = pinconf_to_config_packed(param, !!val); + + return rc; +} + +static int aw9523_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + int regbit = pin % AW9523_PINS_PER_PORT; + u32 arg; + u8 reg; + unsigned int mask, val; + int i, rc; + + mutex_lock(&awi->i2c_lock); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + rc = aw9523_pcfg_param_to_reg(param, pin, ®); + if (rc) + goto end; + + switch (param) { + case PIN_CONFIG_OUTPUT: + /* First, enable pin output */ + rc = regmap_update_bits(awi->regmap, + AW9523_REG_CONF_STATE(pin), + BIT(regbit), 0); + if (rc) + goto end; + + /* Then, fall through to config output level */ + fallthrough; + case PIN_CONFIG_OUTPUT_ENABLE: + arg = !arg; + fallthrough; + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_INPUT_ENABLE: + mask = BIT(regbit); + val = arg ? BIT(regbit) : 0; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + /* Open-Drain is supported only on port 0 */ + if (pin >= AW9523_PINS_PER_PORT) { + rc = -ENOTSUPP; + goto end; + } + mask = AW9523_GCR_GPOMD_MASK; + val = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + /* Port 1 is always Push-Pull */ + if (pin >= AW9523_PINS_PER_PORT) { + mask = 0; + val = 0; + continue; + } + mask = AW9523_GCR_GPOMD_MASK; + val = AW9523_GCR_GPOMD_MASK; + break; + default: + rc = -ENOTSUPP; + goto end; + } + + rc = regmap_update_bits(awi->regmap, reg, mask, val); + if (rc) + goto end; + } +end: + mutex_unlock(&awi->i2c_lock); + return rc; +} + +static const struct pinconf_ops aw9523_pinconf_ops = { + .pin_config_get = aw9523_pconf_get, + .pin_config_set = aw9523_pconf_set, + .is_generic = true, +}; + +/* + * aw9523_get_pin_direction - Get pin direction + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @n: pin index in port register + * + * Return: Pin direction for success or negative number for error + */ +static int aw9523_get_pin_direction(struct regmap *regmap, u8 pin, u8 n) +{ + int ret; + + ret = regmap_test_bits(regmap, AW9523_REG_CONF_STATE(pin), BIT(n)); + if (ret < 0) + return ret; + + return ret ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +/* + * aw9523_get_port_state - Get input or output state for entire port + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @regbit: hw pin index, used to retrieve port number + * @state: returned port state + * + * Return: Zero for success or negative number for error + */ +static int aw9523_get_port_state(struct regmap *regmap, u8 pin, + u8 regbit, unsigned int *state) +{ + u8 reg; + int dir; + + dir = aw9523_get_pin_direction(regmap, pin, regbit); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) + reg = AW9523_REG_IN_STATE(pin); + else + reg = AW9523_REG_OUT_STATE(pin); + + return regmap_read(regmap, reg, state); +} + +static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_BOTH: + return 0; + default: + return -EINVAL; + }; +} + +/* + * aw9523_irq_mask - Mask interrupt + * @d: irq data + * + * Sets which interrupt to mask in the bitmap; + * The interrupt will be masked when unlocking the irq bus. + */ +static void aw9523_irq_mask(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; + + regmap_update_bits(awi->regmap, + AW9523_REG_INTR_DIS(d->hwirq), + BIT(n), BIT(n)); +} + +/* + * aw9523_irq_unmask - Unmask interrupt + * @d: irq data + * + * Sets which interrupt to unmask in the bitmap; + * The interrupt will be masked when unlocking the irq bus. + */ +static void aw9523_irq_unmask(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; + + regmap_update_bits(awi->regmap, + AW9523_REG_INTR_DIS(d->hwirq), + BIT(n), 0); +} + +static irqreturn_t aw9523_irq_thread_func(int irq, void *dev_id) +{ + struct aw9523 *awi = (struct aw9523 *)dev_id; + unsigned long n, val = 0; + unsigned long changed_gpio; + unsigned int tmp, port_pin, i, ret; + + for (i = 0; i < AW9523_NUM_PORTS; i++) { + port_pin = i * AW9523_PINS_PER_PORT; + ret = regmap_read(awi->regmap, + AW9523_REG_IN_STATE(port_pin), + &tmp); + if (ret) + return ret; + val |= (u8)tmp << (i * 8); + } + + /* Handle GPIO input release interrupt as well */ + changed_gpio = awi->irq->cached_gpio ^ val; + awi->irq->cached_gpio = val; + + /* + * To avoid up to four *slow* i2c reads from any driver hooked + * up to our interrupts, just check for the irq_find_mapping + * result: if the interrupt is not mapped, then we don't want + * to care about it. + */ + for_each_set_bit(n, &changed_gpio, awi->gpio.ngpio) { + tmp = irq_find_mapping(awi->gpio.irq.domain, n); + if (tmp <= 0) + continue; + handle_nested_irq(tmp); + } + + return IRQ_HANDLED; +} + +/* + * aw9523_irq_bus_lock - Grab lock for interrupt operation + * @d: irq data + */ +static void aw9523_irq_bus_lock(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + + mutex_lock(&awi->irq->lock); + regcache_cache_only(awi->regmap, true); +} + +/* + * aw9523_irq_bus_sync_unlock - Synchronize state and unlock + * @d: irq data + * + * Writes the interrupt mask bits (found in the bit map) to the + * hardware, then unlocks the bus. + */ +static void aw9523_irq_bus_sync_unlock(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + + regcache_cache_only(awi->regmap, false); + regcache_sync(awi->regmap); + mutex_unlock(&awi->irq->lock); +} + +static int aw9523_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = aw9523_get_pin_direction(awi->regmap, offset, regbit); + mutex_unlock(&awi->i2c_lock); + + return ret; +} + +static int aw9523_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + unsigned int val; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = aw9523_get_port_state(awi->regmap, offset, regbit, &val); + mutex_unlock(&awi->i2c_lock); + if (ret) + return ret; + + return !!(val & BIT(regbit)); +} + +/** + * _aw9523_gpio_get_multiple - Get I/O state for an entire port + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @regbit: hw pin index, used to retrieve port number + * @state: returned port I/O state + * + * Return: Zero for success or negative number for error + */ +static int _aw9523_gpio_get_multiple(struct aw9523 *awi, u8 regbit, + u8 *state, u8 mask) +{ + u32 dir_in, val; + u8 m; + int ret; + + /* Registers are 8-bits wide */ + ret = regmap_read(awi->regmap, AW9523_REG_CONF_STATE(regbit), &dir_in); + if (ret) + return ret; + *state = 0; + + m = mask & dir_in; + if (m) { + ret = regmap_read(awi->regmap, AW9523_REG_IN_STATE(regbit), + &val); + if (ret) + return ret; + *state |= (u8)val & m; + } + + m = mask & ~dir_in; + if (m) { + ret = regmap_read(awi->regmap, AW9523_REG_OUT_STATE(regbit), + &val); + if (ret) + return ret; + *state |= (u8)val & m; + } + + return 0; +} + +static int aw9523_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 m, state = 0; + int ret; + + mutex_lock(&awi->i2c_lock); + + /* Port 0 (gpio 0-7) */ + m = *mask & U8_MAX; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, 0, &state, m); + if (ret) + goto out; + } + *bits = state; + + /* Port 1 (gpio 8-15) */ + m = (*mask >> 8) & U8_MAX; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, AW9523_PINS_PER_PORT, + &state, m); + if (ret) + goto out; + + *bits |= (state << 8); + } +out: + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static void aw9523_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 mask_lo, mask_hi, bits_lo, bits_hi; + unsigned int reg; + int ret = 0; + + mask_lo = *mask & U8_MAX; + mask_hi = (*mask >> 8) & U8_MAX; + mutex_lock(&awi->i2c_lock); + if (mask_hi) { + reg = AW9523_REG_OUT_STATE(AW9523_PINS_PER_PORT); + bits_hi = (*bits >> 8) & U8_MAX; + + ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); + if (ret) { + dev_warn(awi->dev, "Cannot write port1 out level\n"); + goto out; + } + } + if (mask_lo) { + reg = AW9523_REG_OUT_STATE(0); + bits_lo = *bits & U8_MAX; + ret = regmap_write_bits(awi->regmap, reg, mask_lo, bits_lo); + if (ret) + dev_warn(awi->dev, "Cannot write port0 out level\n"); + } +out: + mutex_unlock(&awi->i2c_lock); +} + +static void aw9523_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + + mutex_lock(&awi->i2c_lock); + regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), + BIT(regbit), value ? BIT(regbit) : 0); + mutex_unlock(&awi->i2c_lock); +} + + +static int aw9523_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), + BIT(regbit), BIT(regbit)); + mutex_unlock(&awi->i2c_lock); + + return ret; +} + +static int aw9523_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), + BIT(regbit), value ? BIT(regbit) : 0); + if (ret) + goto end; + + ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), + BIT(regbit), 0); +end: + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static int aw9523_drive_reset_gpio(struct aw9523 *awi) +{ + unsigned int chip_id; + int ret; + + /* + * If the chip is already configured for any reason, then we + * will probably succeed in sending the soft reset signal to + * the hardware through I2C: this operation takes less time + * compared to a full HW reset and it gives the same results. + */ + ret = regmap_write(awi->regmap, AW9523_REG_SOFT_RESET, 0); + if (ret == 0) + goto done; + + dev_dbg(awi->dev, "Cannot execute soft reset: trying hard reset\n"); + ret = gpiod_direction_output(awi->reset_gpio, 0); + if (ret) + return ret; + + /* The reset pulse has to be longer than 20uS due to deglitch */ + usleep_range(AW9523_HW_RESET_US, AW9523_HW_RESET_US + 1); + + ret = gpiod_direction_output(awi->reset_gpio, 1); + if (ret) + return ret; +done: + /* The HW needs at least 1uS to reliably recover after reset */ + usleep_range(AW9523_HW_RESET_RECOVERY_US, + AW9523_HW_RESET_RECOVERY_US + 1); + + /* Check the ChipID */ + ret = regmap_read(awi->regmap, AW9523_REG_CHIPID, &chip_id); + if (ret) { + dev_err(awi->dev, "Cannot read Chip ID: %d\n", ret); + return ret; + } + if (chip_id != AW9523_VAL_EXPECTED_CHIPID) { + dev_err(awi->dev, "Bad ChipID; read 0x%x, expected 0x%x\n", + chip_id, AW9523_VAL_EXPECTED_CHIPID); + return -EINVAL; + } + + return 0; +} + +static int aw9523_hw_reset(struct aw9523 *awi) +{ + int ret, max_retries = 2; + + /* Sometimes the chip needs more than one reset cycle */ + do { + ret = aw9523_drive_reset_gpio(awi); + if (ret == 0) + break; + max_retries--; + } while (max_retries); + + return ret; +} + +static int aw9523_init_gpiochip(struct aw9523 *awi, unsigned int npins) +{ + struct device *dev = awi->dev; + struct gpio_chip *gpiochip = &awi->gpio; + + gpiochip->label = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!gpiochip->label) + return -ENOMEM; + + gpiochip->base = -1; + gpiochip->ngpio = npins; + gpiochip->get_direction = aw9523_gpio_get_direction; + gpiochip->direction_input = aw9523_direction_input; + gpiochip->direction_output = aw9523_direction_output; + gpiochip->get = aw9523_gpio_get; + gpiochip->get_multiple = aw9523_gpio_get_multiple; + gpiochip->set = aw9523_gpio_set; + gpiochip->set_multiple = aw9523_gpio_set_multiple; + gpiochip->set_config = gpiochip_generic_config; + gpiochip->parent = dev; + gpiochip->of_node = dev->of_node; + gpiochip->owner = THIS_MODULE; + gpiochip->can_sleep = false; + + return 0; +} + +static int aw9523_init_irq(struct aw9523 *awi, int irq) +{ + struct device *dev = awi->dev; + struct gpio_irq_chip *gpioirq; + struct irq_chip *irqchip; + int ret; + + if (!device_property_read_bool(dev, "interrupt-controller")) + return 0; + + irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL); + if (!irqchip) + return -ENOMEM; + + awi->irq = devm_kzalloc(dev, sizeof(*awi->irq), GFP_KERNEL); + if (!awi->irq) + return -ENOMEM; + + irqchip->name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!irqchip->name) + return -ENOMEM; + + irqchip->irq_mask = aw9523_irq_mask; + irqchip->irq_unmask = aw9523_irq_unmask; + irqchip->irq_bus_lock = aw9523_irq_bus_lock; + irqchip->irq_bus_sync_unlock = aw9523_irq_bus_sync_unlock; + irqchip->irq_set_type = aw9523_gpio_irq_type; + awi->irq->irqchip = irqchip; + mutex_init(&awi->irq->lock); + + ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, + IRQF_ONESHOT, dev_name(dev), awi); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", irq); + return ret; + } + + gpioirq = &awi->gpio.irq; + gpioirq->chip = irqchip; + gpioirq->parent_handler = NULL; + gpioirq->num_parents = 0; + gpioirq->parents = NULL; + gpioirq->default_type = IRQ_TYPE_LEVEL_MASK; + gpioirq->handler = handle_simple_irq; + gpioirq->threaded = true; + gpioirq->first = 0; + + return 0; +} + +static bool aw9523_is_reg_hole(unsigned int reg) +{ + return (reg > AW9523_REG_PORT_MODE(AW9523_PINS_PER_PORT) && + reg < AW9523_REG_SOFT_RESET) || + (reg > AW9523_REG_INTR_DIS(AW9523_PINS_PER_PORT) && + reg < AW9523_REG_CHIPID); +} + +static bool aw9523_readable_reg(struct device *dev, unsigned int reg) +{ + /* All available registers (minus holes) can be read */ + return !aw9523_is_reg_hole(reg); +} + +static bool aw9523_volatile_reg(struct device *dev, unsigned int reg) +{ + return aw9523_is_reg_hole(reg) || + reg == AW9523_REG_IN_STATE(0) || + reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT) || + reg == AW9523_REG_CHIPID || + reg == AW9523_REG_SOFT_RESET; +} + +static bool aw9523_writeable_reg(struct device *dev, unsigned int reg) +{ + return !aw9523_is_reg_hole(reg) && reg != AW9523_REG_CHIPID; +} + +static bool aw9523_precious_reg(struct device *dev, unsigned int reg) +{ + /* Reading AW9523_REG_IN_STATE clears interrupt status */ + return aw9523_is_reg_hole(reg) || + reg == AW9523_REG_IN_STATE(0) || + reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT); +} + +static const struct regmap_config aw9523_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, + + .precious_reg = aw9523_precious_reg, + .readable_reg = aw9523_readable_reg, + .volatile_reg = aw9523_volatile_reg, + .writeable_reg = aw9523_writeable_reg, + + .cache_type = REGCACHE_FLAT, + .disable_locking = true, + + .num_reg_defaults_raw = AW9523_REG_SOFT_RESET, +}; + +static int aw9523_hw_init(struct aw9523 *awi) +{ + u8 p1_pin = AW9523_PINS_PER_PORT; + unsigned int val; + int ret; + + /* No register caching during initialization */ + regcache_cache_bypass(awi->regmap, true); + + /* Bring up the chip */ + ret = aw9523_hw_reset(awi); + if (ret) { + dev_err(awi->dev, "HW Reset failed: %d\n", ret); + return ret; + } + + /* + * This is the expected chip and it is running: it's time to + * set a safe default configuration in case the user doesn't + * configure (all of the available) pins in this chip. + * P.S.: The writes order doesn't matter. + */ + + /* Set all pins as GPIO */ + ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Set Open-Drain mode on Port 0 (Port 1 is always P-P) */ + ret = regmap_write(awi->regmap, AW9523_REG_GCR, 0); + if (ret) + return ret; + + /* Set all pins as inputs */ + ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Disable all interrupts to avoid unreasoned wakeups */ + ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Clear setup-generated interrupts by performing a port state read */ + ret = aw9523_get_port_state(awi->regmap, 0, 0, &val); + if (ret) + return ret; + ret = aw9523_get_port_state(awi->regmap, p1_pin, 0, &val); + if (ret) + return ret; + + /* Everything went fine: activate and reinitialize register cache */ + regcache_cache_bypass(awi->regmap, false); + return regmap_reinit_cache(awi->regmap, &aw9523_regmap); +} + +static int aw9523_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct pinctrl_desc *pdesc; + struct aw9523 *awi; + int ret; + + awi = devm_kzalloc(dev, sizeof(*awi), GFP_KERNEL); + if (!awi) + return -ENOMEM; + + i2c_set_clientdata(client, awi); + + awi->dev = dev; + awi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(awi->reset_gpio)) + return PTR_ERR(awi->reset_gpio); + gpiod_set_consumer_name(awi->reset_gpio, "aw9523 reset"); + + awi->regmap = devm_regmap_init_i2c(client, &aw9523_regmap); + if (IS_ERR(awi->regmap)) + return PTR_ERR(awi->regmap); + + awi->vio_vreg = devm_regulator_get_optional(dev, "vio"); + if (IS_ERR(awi->vio_vreg)) { + if (PTR_ERR(awi->vio_vreg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + awi->vio_vreg = NULL; + } else { + ret = regulator_enable(awi->vio_vreg); + if (ret) + return ret; + } + + mutex_init(&awi->i2c_lock); + lockdep_set_subclass(&awi->i2c_lock, + i2c_adapter_depth(client->adapter)); + + pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) + return -ENOMEM; + + ret = aw9523_hw_init(awi); + if (ret) + goto err_disable_vregs; + + pdesc->name = dev_name(dev); + pdesc->owner = THIS_MODULE; + pdesc->pctlops = &aw9523_pinctrl_ops; + pdesc->pmxops = &aw9523_pinmux_ops; + pdesc->confops = &aw9523_pinconf_ops; + pdesc->pins = aw9523_pins; + pdesc->npins = ARRAY_SIZE(aw9523_pins); + + ret = aw9523_init_gpiochip(awi, pdesc->npins); + if (ret) + goto err_disable_vregs; + + if (client->irq) { + ret = aw9523_init_irq(awi, client->irq); + if (ret) + goto err_disable_vregs; + } + + awi->pctl = devm_pinctrl_register(dev, pdesc, awi); + if (IS_ERR(awi->pctl)) { + ret = PTR_ERR(awi->pctl); + dev_err(dev, "Cannot register pinctrl: %d", ret); + goto err_disable_vregs; + } + + ret = devm_gpiochip_add_data(dev, &awi->gpio, awi); + if (ret) + goto err_disable_vregs; + + return ret; + +err_disable_vregs: + if (awi->vio_vreg) + regulator_disable(awi->vio_vreg); + mutex_destroy(&awi->i2c_lock); + return ret; +} + +static int aw9523_remove(struct i2c_client *client) +{ + struct aw9523 *awi = i2c_get_clientdata(client); + int ret; + + if (!awi) + return 0; + + /* + * If the chip VIO is connected to a regulator that we can turn + * off, life is easy... otherwise, reinitialize the chip and + * set the pins to hardware defaults before removing the driver + * to leave it in a clean, safe and predictable state. + */ + if (awi->vio_vreg) { + regulator_disable(awi->vio_vreg); + } else { + mutex_lock(&awi->i2c_lock); + ret = aw9523_hw_init(awi); + mutex_unlock(&awi->i2c_lock); + if (ret) + return ret; + } + + mutex_destroy(&awi->i2c_lock); + return 0; +} + +static const struct i2c_device_id aw9523_i2c_id_table[] = { + { "aw9523_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); + +static const struct of_device_id of_aw9523_i2c_match[] = { + { .compatible = "awinic,aw9523-pinctrl", }, +}; +MODULE_DEVICE_TABLE(of, of_aw9523_i2c_match); + +static struct i2c_driver aw9523_driver = { + .driver = { + .name = "aw9523-pinctrl", + .of_match_table = of_aw9523_i2c_match, + }, + .probe = aw9523_probe, + .remove = aw9523_remove, + .id_table = aw9523_i2c_id_table, +}; +module_i2c_driver(aw9523_driver); + +MODULE_DESCRIPTION("Awinic AW9523 I2C GPIO Expander driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:aw9523"); From 4ad208b26fb9b6d280edca09132ab9c2ef57641c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 14:54:10 +0100 Subject: [PATCH 107/212] dt-bindings: pinctrl: Add bindings for Awinic AW9523/AW9523B Add bindings for the Awinic AW9523/AW9523B I2C GPIO Expander driver. Signed-off-by: AngeloGioacchino Del Regno --- .../pinctrl/awinic,aw9523-pinctrl.yaml | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml new file mode 100644 index 000000000000..640d4d7e4cab --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/awinic,aw9523-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW9523/AW9523B I2C GPIO Expander + +maintainers: + - AngeloGioacchino Del Regno + +description: | + The Awinic AW9523/AW9523B I2C GPIO Expander featuring 16 multi-function + I/O, 256 steps PWM mode and interrupt support. + +properties: + compatible: + const: awinic,aw9523-pinctrl + + reg: + maxItems: 1 + + '#gpio-cells': + description: | + Specifying the pin number and flags, as defined in + include/dt-bindings/gpio/gpio.h + const: 2 + + gpio-controller: true + + gpio-ranges: + maxItems: 1 + + interrupt-controller: true + + interrupts: + maxItems: 1 + description: Specifies the INTN pin IRQ. + + '#interrupt-cells': + description: + Specifies the PIN numbers and Flags, as defined in defined in + include/dt-bindings/interrupt-controller/irq.h + const: 2 + + reset-gpios: + maxItems: 1 + +#PIN CONFIGURATION NODES +patternProperties: + '-pins$': + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: "/schemas/pinctrl/pincfg-node.yaml" + + properties: + pins: + description: + List of gpio pins affected by the properties specified in + this subnode. + items: + pattern: "^gpio([0-9]|1[0-5])$" + minItems: 1 + maxItems: 16 + + function: + description: + Specify the alternative function to be configured for the + specified pins. + + enum: [ gpio, pwm ] + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-open-drain: true + drive-push-pull: true + input-enable: true + input-disable: true + output-high: true + output-low: true + + required: + - pins + - function + + additionalProperties: false + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - gpio-ranges + +additionalProperties: false + +examples: + # Example configuration to drive pins for a keyboard matrix + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + aw9523: gpio-expander@58 { + compatible = "awinic,aw9523-pinctrl"; + reg = <0x58>; + interrupt-parent = <&tlmm>; + interrupts = <50 IRQ_TYPE_EDGE_FALLING>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 16>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + + keyboard-matrix-col-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15"; + function = "gpio"; + input-disable; + output-low; + }; + + keyboard-matrix-row-pins { + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7"; + function = "gpio"; + bias-pull-up; + drive-open-drain; + input-enable; + }; + }; + }; From ecfb13285d5f3bf707108263117f4c9dce5127b3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 6 Aug 2020 20:09:51 +0200 Subject: [PATCH 108/212] media: venus: core: Add sdm660 DT compatible and resource struct Add the SDM660 DT compatible and its resource structure, also including support for the Venus pmdomains, in order to support the Venus block in SDM630, SDM636, SDM660 and SDA variants. This SoC features Venus 4.4 (HFI3XX), with one vcodec used for both encoding and decoding, switched on through two GDSCs. The core clock for this Venus chip is powered by the RPM VDD_CX power domain. Signed-off-by: AngeloGioacchino Del Regno --- drivers/media/platform/qcom/venus/core.c | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 91b15842c555..43ed807d49b5 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -640,6 +640,71 @@ static const struct venus_resources sdm845_res_v2 = { .fwname = "qcom/venus-5.2/venus.mdt", }; +static const struct freq_tbl sdm660_freq_table[] = { + { 0, 518400000 }, + { 0, 441600000 }, + { 0, 404000000 }, + { 0, 320000000 }, + { 0, 269330000 }, + { 0, 133330000 }, +}; + +static const struct reg_val sdm660_reg_preset[] = { + { 0x80010, 0x001f001f }, + { 0x80018, 0x00000156 }, + { 0x8001C, 0x00000156 }, +}; + +static const struct bw_tbl sdm660_bw_table_enc[] = { + { 979200, 1044000, 0, 2446336, 0 }, /* 4k UHD @ 30 */ + { 864000, 887000, 0, 2108416, 0 }, /* 720p @ 240 */ + { 489600, 666000, 0, 1207296, 0 }, /* 1080p @ 60 */ + { 432000, 578000, 0, 1058816, 0 }, /* 720p @ 120 */ + { 244800, 346000, 0, 616448, 0 }, /* 1080p @ 30 */ + { 216000, 293000, 0, 534528, 0 }, /* 720p @ 60 */ + { 108000, 151000, 0, 271360, 0 }, /* 720p @ 30 */ +}; + +static const struct bw_tbl sdm660_bw_table_dec[] = { + { 979200, 2365000, 0, 1892000, 0 }, /* 4k UHD @ 30 */ + { 864000, 1978000, 0, 1554000, 0 }, /* 720p @ 240 */ + { 489600, 1133000, 0, 895000, 0 }, /* 1080p @ 60 */ + { 432000, 994000, 0, 781000, 0 }, /* 720p @ 120 */ + { 244800, 580000, 0, 460000, 0 }, /* 1080p @ 30 */ + { 216000, 501000, 0, 301000, 0 }, /* 720p @ 60 */ + { 108000, 255000, 0, 202000, 0 }, /* 720p @ 30 */ +}; + +static const struct venus_resources sdm660_res = { + .freq_tbl = sdm660_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm660_freq_table), + .reg_tbl = sdm660_reg_preset, + .reg_tbl_size = ARRAY_SIZE(sdm660_reg_preset), + .bw_tbl_enc = sdm660_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sdm660_bw_table_enc), + .bw_tbl_dec = sdm660_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sdm660_bw_table_dec), + .clks = {"core", "iface", "bus_throttle", "bus" }, + .clks_num = 4, + .vcodec0_clks = { "vcodec0_core" }, + .vcodec_clks_num = 1, + .vcodec_pmdomains = { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .opp_pmdomain = (const char *[]) { "cx", NULL }, + .vcodec_num = 1, + .max_load = 1036800, + .hfi_version = HFI_VERSION_3XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .cp_start = 0, + .cp_size = 0x79000000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x28000000, + .dma_mask = 0xd9000000 - 1, + .fwname = "qcom/venus-4.4/venus.mdt", +}; + static const struct freq_tbl sc7180_freq_table[] = { { 0, 500000000 }, { 0, 434000000 }, @@ -739,6 +804,7 @@ static const struct venus_resources sm8250_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, From 6f697fc0634de69144a53bd037066021e12cf320 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 18 Sep 2020 19:21:06 +0200 Subject: [PATCH 109/212] media: dt-bindings: media: venus: Add sdm660 DT schema Add new qcom,sdm660-venus DT binding schema. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/media/qcom,sdm660-venus.yaml | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sdm660-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sdm660-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sdm660-venus.yaml new file mode 100644 index 000000000000..e7568d4007af --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sdm660-venus.yaml @@ -0,0 +1,164 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sdm660-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sdm660-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: bus_throttle + - const: vcodec0_core + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: video-mem + - const: cpu-cfg + + iommus: + maxItems: 20 + + memory-region: + maxItems: 1 + + power-domains: + minItems: 3 + + power-domain-names: + items: + - const: cx + - const: vcodec0 + - const: venus + + video-decoder: + type: object + + properties: + compatible: + const: venus-decoder + + required: + - compatible + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: venus-encoder + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 2 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +additionalProperties: false + +examples: + - | + #include + #include + #include + + video-codec@cc00000 { + compatible = "qcom,sdm660-venus"; + reg = <0x0cc00000 0xff000>; + interrupts = ; + power-domains = <&rpmpd SDM660_VDDCX>, + <&mmcc VENUS_CORE0_GDSC>, + <&mmcc VENUS_GDSC>; + power-domain-names = "cx", "vcodec0", "venus"; + clocks = <&mmcc VIDEO_CORE_CLK>, + <&mmcc VIDEO_AHB_CLK>, + <&mmcc VIDEO_AXI_CLK>, + <&mmcc THROTTLE_VIDEO_AXI_CLK>, + <&mmcc VIDEO_SUBCORE0_CLK>; + clock-names = "core", "iface", "bus", "bus_throttle", + "vcodec0_core"; + iommus = <&mmss_smmu 0x400>, + <&mmss_smmu 0x401>, + <&mmss_smmu 0x40a>, + <&mmss_smmu 0x407>, + <&mmss_smmu 0x40e>, + <&mmss_smmu 0x40f>, + <&mmss_smmu 0x408>, + <&mmss_smmu 0x409>, + <&mmss_smmu 0x40b>, + <&mmss_smmu 0x40c>, + <&mmss_smmu 0x40d>, + <&mmss_smmu 0x410>, + <&mmss_smmu 0x421>, + <&mmss_smmu 0x428>, + <&mmss_smmu 0x429>, + <&mmss_smmu 0x42b>, + <&mmss_smmu 0x42c>, + <&mmss_smmu 0x42d>, + <&mmss_smmu 0x411>, + <&mmss_smmu 0x431>; + memory-region = <&venus_region>; + + video-decoder { + compatible = "venus-decoder"; + }; + + video-encoder { + compatible = "venus-encoder"; + }; + }; From e82d3a0bb17877a14e7c4a9b246c8b1f58433731 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 13:02:27 +0100 Subject: [PATCH 110/212] drm/panel: Add BOE BF060Y8M-AJ0 5.99" AMOLED panel driver This adds support for the BOE BF060Y8M-AJ0 5.99" AMOLED module that can be found in some F(x)Tec Pro1 and Elephone U1 devices. Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/panel/Kconfig | 11 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-boe-bf060y8m-aj0.c | 445 ++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 4001589ea311..487fb38c8f4c 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -37,6 +37,17 @@ config DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 NT35596 1080x1920 video mode panel as found in some Asus Zenfone 2 Laser Z00T devices. +config DRM_PANEL_BOE_BF060Y8M_AJ0 + tristate "Boe BF060Y8M-AJ0 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Boe BF060Y8M-AJ0 + 5.99" AMOLED modules. The panel has a 1080x2160 resolution and + uses 24 bit RGB per pixel. It provides a MIPI DSI interface to + the host and backlight is controlled through DSI commands. + config DRM_PANEL_BOE_HIMAX8279D tristate "Boe Himax8279d panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 6ad42ea4a2fa..c5d1a6e6f9b0 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o +obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o diff --git a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c new file mode 100644 index 000000000000..cb4fbd21c69e --- /dev/null +++ b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BOE BF060Y8M-AJ0 5.99" MIPI-DSI OLED Panel on SW43404 DriverIC + * + * Copyright (c) 2020 AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include