Merge "defconfig: sdm845: Enable llcc for sdm670"
diff --git a/Documentation/devicetree/bindings/scheduler/energy.txt b/Documentation/devicetree/bindings/scheduler/energy.txt
new file mode 100644
index 0000000..3c7121c
--- /dev/null
+++ b/Documentation/devicetree/bindings/scheduler/energy.txt
@@ -0,0 +1,13 @@
+* Scheduler Energy Driver
+
+Scheduler Energy Driver updates capacities in the scheduler group energy array.
+The array contains power cost at each CPU operating points so energy aware
+scheduler (EAS) can utilize it for task placement.
+
+Required properties:
+- compatible: Must be "sched-energy"
+
+Example:
+ energy-costs {
+ compatible = "sched-energy";
+ }
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
new file mode 100644
index 0000000..ef8fe1b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -0,0 +1,631 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&spmi_bus {
+ qcom,pm660@0 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm660_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ qcom,fab-id-valid;
+ };
+
+ pm660_misc: qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x1 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x4 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x5 IRQ_TYPE_NONE>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,kpdpwr-sw-debounce;
+ qcom,system-reset;
+ qcom,store-hard-reset-reason;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
+ label = "pm660_tz";
+ qcom,channel-num = <6>;
+ qcom,temp_alarm-vadc = <&pm660_vadc>;
+ };
+
+ pm660_gpios: gpios {
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm660-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ status = "disabled";
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ status = "disabled";
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ status = "disabled";
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ status = "disabled";
+ };
+
+ gpio@cc00 {
+ reg = <0xcc00 0x100>;
+ qcom,pin-num = <13>;
+ status = "disabled";
+ };
+ };
+
+ pm660_coincell: qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
+ pm660_rtc: qcom,pm660_rtc {
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pm660_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+ qcom,pm660_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+ };
+ };
+
+ pm660_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc-hc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1875>;
+
+ chan@6 {
+ label = "die_temp";
+ reg = <6>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@0 {
+ label = "ref_gnd";
+ reg = <0>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@1 {
+ label = "ref_1250v";
+ reg = <1>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@85 {
+ label = "vcoin";
+ reg = <0x85>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4e {
+ label = "emmc_therm";
+ reg = <0x4e>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4f {
+ label = "pa_therm0";
+ reg = <0x4f>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@1d {
+ label = "drax_temp";
+ reg = <0x1d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+ };
+
+ pm660_charger: qcom,qpnp-smb2 {
+ compatible = "qcom,qpnp-smb2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,pmic-revid = <&pm660_revid>;
+
+ io-channels = <&pm660_rradc 8>,
+ <&pm660_rradc 10>,
+ <&pm660_rradc 3>,
+ <&pm660_rradc 4>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max",
+ "usbin_i",
+ "usbin_v";
+
+ qcom,wipower-max-uw = <5000000>;
+ dpdm-supply = <&qusb_phy0>;
+
+ qcom,thermal-mitigation
+ = <3000000 2500000 2000000 1500000
+ 1000000 500000>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts =
+ <0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "chg-error",
+ "chg-state-change",
+ "step-chg-state-change",
+ "step-chg-soc-update-fail",
+ "step-chg-soc-update-request";
+ };
+
+ qcom,otg@1100 {
+ reg = <0x1100 0x100>;
+ interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "otg-fail",
+ "otg-overcurrent",
+ "otg-oc-dis-sw-sts",
+ "testmode-change-detect";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts =
+ <0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "bat-temp",
+ "bat-ocp",
+ "bat-ov",
+ "bat-low",
+ "bat-therm-or-id-missing",
+ "bat-terminal-missing";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts =
+ <0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "usbin-collapse",
+ "usbin-lt-3p6v",
+ "usbin-uv",
+ "usbin-ov",
+ "usbin-plugin",
+ "usbin-src-change",
+ "usbin-icl-change",
+ "type-c-change";
+ };
+
+ qcom,dc-chgpth@1400 {
+ reg = <0x1400 0x100>;
+ interrupts =
+ <0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "dcin-collapse",
+ "dcin-lt-3p6v",
+ "dcin-uv",
+ "dcin-ov",
+ "dcin-plugin",
+ "div2-en-dg",
+ "dcin-icl-change";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts =
+ <0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+ <0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "wdog-snarl",
+ "wdog-bark",
+ "aicl-fail",
+ "aicl-done",
+ "high-duty-cycle",
+ "input-current-limiting",
+ "temperature-change",
+ "switcher-power-ok";
+ };
+ };
+
+ pm660_pdphy: qcom,usb-pdphy@1700 {
+ compatible = "qcom,qpnp-pdphy";
+ reg = <0x1700 0x100>;
+ vdd-pdphy-supply = <&pm660l_l7>;
+ vbus-supply = <&smb2_vbus>;
+ vconn-supply = <&smb2_vconn>;
+ interrupts = <0x0 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x4 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x17 0x6 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "sig-tx",
+ "sig-rx",
+ "msg-tx",
+ "msg-rx",
+ "msg-tx-failed",
+ "msg-tx-discarded",
+ "msg-rx-discarded";
+
+ qcom,default-sink-caps = <5000 3000>, /* 5V @ 3A */
+ <9000 3000>; /* 9V @ 3A */
+ };
+
+ pm660_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm-hc";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1875>;
+ qcom,adc_tm-vadc = <&pm660_vadc>;
+ qcom,decimation = <0>;
+ qcom,fast-avg-setup = <0>;
+
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,btm-channel-number = <0x60>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+ };
+
+ pm660_rradc: rradc@4500 {
+ compatible = "qcom,rradc";
+ reg = <0x4500 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #io-channel-cells = <1>;
+ qcom,pmic-revid = <&pm660_revid>;
+ };
+
+ pm660_fg: qpnp,fg {
+ compatible = "qcom,fg-gen3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660_revid>;
+ io-channels = <&pm660_rradc 0>,
+ <&pm660_rradc 7>;
+ io-channel-names = "rradc_batt_id",
+ "rradc_die_temp";
+ qcom,rradc-base = <0x4500>;
+ qcom,fg-esr-timer-awake = <96 96>;
+ qcom,fg-esr-timer-asleep = <256 256>;
+ qcom,fg-esr-timer-charging = <0 96>;
+ qcom,cycle-counter-en;
+ status = "okay";
+
+ qcom,fg-batt-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x2
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x3
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x5
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "soc-update",
+ "soc-ready",
+ "bsoc-delta",
+ "msoc-delta",
+ "msoc-low",
+ "msoc-empty",
+ "msoc-high",
+ "msoc-full";
+ };
+
+ qcom,fg-batt-info@4100 {
+ status = "okay";
+ reg = <0x4100 0x100>;
+ interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "vbatt-pred-delta",
+ "vbatt-low",
+ "esr-delta",
+ "batt-missing",
+ "batt-temp-delta";
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "ima-rdy",
+ "mem-xcp",
+ "dma-grant";
+ };
+ };
+
+ bcl@4200 {
+ compatible = "qcom,msm-bcl-lmh";
+ reg = <0x4200 0xff>,
+ <0x4300 0xff>;
+ reg-names = "fg_user_adc",
+ "fg_lmh";
+ interrupts = <0x0 0x42 0x0 IRQ_TYPE_NONE>,
+ <0x0 0x42 0x2 IRQ_TYPE_NONE>;
+ interrupt-names = "bcl-high-ibat-int",
+ "bcl-low-vbat-int";
+ qcom,vbat-polling-delay-ms = <100>;
+ qcom,ibat-polling-delay-ms = <100>;
+ };
+ };
+
+ qcom,pm660@1 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/pm660a.dtsi b/arch/arm64/boot/dts/qcom/pm660a.dtsi
new file mode 100644
index 0000000..bfe1b5a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660a.dtsi
@@ -0,0 +1,29 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* Disable WLED */
+&pm660l_wled {
+ status = "disabled";
+};
+
+/* disable LCDB */
+&pm660l_lcdb {
+ status = "disabled";
+};
+
+&pm660a_oledb {
+ status = "okay";
+};
+
+&pm660a_labibb {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
new file mode 100644
index 0000000..0f18ba5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -0,0 +1,470 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+ qcom,pm660l@2 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm660l_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ pm660l_pbs: qcom,pbs@7300 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7300 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ qcom,secondary-pon-reset;
+ qcom,hard-reset-poweroff-type =
+ <PON_POWER_OFF_SHUTDOWN>;
+ };
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x2 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
+ label = "pm660l_tz";
+ };
+
+ pm660l_gpios: gpios {
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm660l-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ status = "disabled";
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ status = "disabled";
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ status = "disabled";
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ status = "disabled";
+ };
+
+ };
+ };
+
+ pm660l_3: qcom,pm660l@3 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm660l_pwm_1: pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ qcom,lpg-lut-size = <0x7e>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <0>;
+ #pwm-cells = <2>;
+ };
+
+ pm660l_pwm_2: pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ qcom,lpg-lut-size = <0x7e>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <1>;
+ #pwm-cells = <2>;
+ };
+
+ pm660l_pwm_3: pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ qcom,lpg-lut-size = <0x7e>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <2>;
+ #pwm-cells = <2>;
+ qcom,period = <6000000>;
+
+ qcom,lpg {
+ label = "lpg";
+ cell-index = <0>;
+ qcom,duty-percents =
+ <0x01 0x0a 0x14 0x1e 0x28 0x32 0x3c
+ 0x46 0x50 0x5a 0x64
+ 0x64 0x5a 0x50 0x46 0x3c 0x32 0x28 0x1e
+ 0x14 0x0a 0x01>;
+ };
+ };
+
+ pm660l_pwm_4: pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ qcom,lpg-lut-size = <0x7e>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <3>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ qcom,leds@d000 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd000 0x100>;
+ label = "rgb";
+
+ red_led: qcom,rgb_0 {
+ label = "rgb";
+ qcom,id = <3>;
+ qcom,mode = "pwm";
+ pwms = <&pm660l_pwm_3 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "red";
+ qcom,start-idx = <0>;
+ qcom,idx-len = <22>;
+ qcom,duty-pcts =
+ [01 0a 14 1e 28 32 3c 46 50 5a 64
+ 64 5a 50 46 3c 32 28 1e 14 0a 01];
+ qcom,use-blink;
+ };
+
+ green_led: qcom,rgb_1 {
+ label = "rgb";
+ qcom,id = <4>;
+ qcom,mode = "pwm";
+ pwms = <&pm660l_pwm_2 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "green";
+ };
+
+ blue_led: qcom,rgb_2 {
+ label = "rgb";
+ qcom,id = <5>;
+ qcom,mode = "pwm";
+ pwms = <&pm660l_pwm_1 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "blue";
+ };
+ };
+
+ pm660l_wled: qcom,leds@d800 {
+ compatible = "qcom,qpnp-wled";
+ reg = <0xd800 0x100>,
+ <0xd900 0x100>;
+ reg-names = "qpnp-wled-ctrl-base",
+ "qpnp-wled-sink-base";
+ interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "ovp-irq", "sc-irq";
+ linux,name = "wled";
+ linux,default-trigger = "bkl-trigger";
+ qcom,fdbk-output = "auto";
+ qcom,vref-uv = <127500>;
+ qcom,switch-freq-khz = <800>;
+ qcom,ovp-mv = <29600>;
+ qcom,ilim-ma = <970>;
+ qcom,boost-duty-ns = <26>;
+ qcom,mod-freq-khz = <9600>;
+ qcom,dim-mode = "hybrid";
+ qcom,hyb-thres = <625>;
+ qcom,sync-dly-us = <800>;
+ qcom,fs-curr-ua = <25000>;
+ qcom,cons-sync-write-delay-us = <1000>;
+ qcom,led-strings-list = [00 01 02];
+ qcom,en-ext-pfet-sc-pro;
+ qcom,loop-auto-gm-en;
+ qcom,pmic-revid = <&pm660l_revid>;
+ status = "ok";
+ };
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led-v2";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "led-fault-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq";
+ qcom,hdrm-auto-mode;
+ qcom,short-circuit-det;
+ qcom,open-circuit-det;
+ qcom,vph-droop-det;
+ qcom,thermal-derate-en;
+ qcom,thermal-derate-current = <200 500 1000>;
+ qcom,isc-delay = <192>;
+ qcom,pmic-revid = <&pm660l_revid>;
+
+ pm660l_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_flash2: qcom,flash_2 {
+ label = "flash";
+ qcom,led-name = "led:flash_2";
+ qcom,max-current = <750>;
+ qcom,default-led-trigger = "flash2_trigger";
+ qcom,id = <2>;
+ qcom,current-ma = <500>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_torch2: qcom,torch_2 {
+ label = "torch";
+ qcom,led-name = "led:torch_2";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch2_trigger";
+ qcom,id = <2>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm660l_switch0: qcom,led_switch_0 {
+ label = "switch";
+ qcom,led-name = "led:switch_0";
+ qcom,led-mask = <3>;
+ qcom,default-led-trigger = "switch0_trigger";
+ };
+
+ pm660l_switch1: qcom,led_switch_1 {
+ label = "switch";
+ qcom,led-name = "led:switch_1";
+ qcom,led-mask = <4>;
+ qcom,default-led-trigger = "switch1_trigger";
+ };
+ };
+
+ pm660l_lcdb: qpnp-lcdb@ec00 {
+ compatible = "qcom,qpnp-lcdb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xec00 0x100>;
+ interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "sc-irq";
+
+ qcom,pmic-revid = <&pm660l_revid>;
+
+ lcdb_ldo_vreg: ldo {
+ label = "ldo";
+ regulator-name = "lcdb_ldo";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+
+ lcdb_ncp_vreg: ncp {
+ label = "ncp";
+ regulator-name = "lcdb_ncp";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+ };
+
+ pm660a_oledb: qpnp-oledb@e000 {
+ compatible = "qcom,qpnp-oledb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
+ reg = <0xe000 0x100>;
+ qcom,pbs-client = <&pm660l_pbs>;
+
+ label = "oledb";
+ regulator-name = "regulator-oledb";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <8100000>;
+
+ qcom,swire-control;
+ qcom,ext-pin-control;
+ status = "disabled";
+ };
+
+ pm660a_labibb: qpnp-labibb-regulator {
+ compatible = "qcom,qpnp-labibb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
+ qcom,swire-control;
+ status = "disabled";
+
+ ibb_regulator: qcom,ibb@dc00 {
+ reg = <0xdc00 0x100>;
+ reg-names = "ibb_reg";
+ regulator-name = "ibb_reg";
+
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6300000>;
+
+ qcom,qpnp-ibb-min-voltage = <1400000>;
+ qcom,qpnp-ibb-step-size = <100000>;
+ qcom,qpnp-ibb-slew-rate = <2000000>;
+ qcom,qpnp-ibb-init-voltage = <4000000>;
+ qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+ };
+
+ lab_regulator: qcom,lab@de00 {
+ reg = <0xde00 0x100>;
+ reg-names = "lab";
+ regulator-name = "lab_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6100000>;
+
+ qcom,qpnp-lab-min-voltage = <4600000>;
+ qcom,qpnp-lab-step-size = <100000>;
+ qcom,qpnp-lab-slew-rate = <5000>;
+ qcom,qpnp-lab-init-voltage = <4600000>;
+ qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+ qcom,notify-lab-vreg-ok-sts;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
new file mode 100644
index 0000000..a1dc261
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
@@ -0,0 +1,363 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/* Stub regulators */
+
+/ {
+ pm660_s4: regulator-pm660-s4 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s4";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <2040000>;
+ regulator-max-microvolt = <2040000>;
+ };
+
+ /* pm660 S5 - VDD_MODEM supply */
+ pm660_s5_level: regulator-pm660-s5 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s5_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660_s6: regulator-pm660-s6 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s6";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <1352000>;
+ regulator-max-microvolt = <1352000>;
+ };
+
+ /* pm660l S1 - VDD_MX supply */
+ pm660l_s1_level: regulator-pm660l-s1 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s1_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_s1_floor_level: regulator-pm660l-s1-floor-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s1_floor_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_s1_level_ao: regulator-pm660l-s1-level-ao {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s1_level_ao";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ /* pm660l S2 - VDD_GFX supply */
+ pm660l_s2_level: regulator-pm660l-s2 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s2_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ /* pm660l S3 + S4 - VDD_CX supply */
+ pm660l_s3_level: regulator-pm660l-s3-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s3_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_s3_floor_level: regulator-pm660l-s3-floor-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_s3_floor_level";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_s3_level_ao: regulator-pm660l-s3-level-ao {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s3_level_ao";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660_l1: regulator-pm660-l1 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l1";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1250000>;
+ };
+
+ pm660_l2: regulator-pm660-l2 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l2";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ pm660_l3: regulator-pm660-l3 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l3";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ pm660_l5: regulator-pm660-l5 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l5";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <800000>;
+ };
+
+ pm660_l6: regulator-pm660-l6 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l6";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1304000>;
+ regulator-max-microvolt = <1304000>;
+ };
+
+ pm660_l7: regulator-pm660-l7 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l7";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ pm660_l8: regulator-pm660-l8 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l8";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l9: regulator-pm660-l9 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l9";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l10: regulator-pm660-l10 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l10";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l11: regulator-pm660-l11 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l11";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l12: regulator-pm660-l12 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l12";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l13: regulator-pm660-l13 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l13";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l14: regulator-pm660-l14 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l14";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ pm660_l15: regulator-pm660-l15 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l15";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ };
+
+ pm660_l16: regulator-pm660-l16 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l16";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <2700000>;
+ };
+
+ pm660_l17: regulator-pm660-l17 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l17";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ };
+
+ pm660_l19: regulator-pm660-l19 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l19";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3312000>;
+ };
+
+ pm660l_l1: regulator-pm660l-l1 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l1";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <880000>;
+ regulator-max-microvolt = <900000>;
+ };
+
+ pm660l_l2: regulator-pm660l-l2 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l2";
+ qcom,hpm-min-load = <5000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2960000>;
+ };
+
+ pm660l_l3: regulator-pm660l-l3 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l3";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <3008000>;
+ };
+
+ pm660l_l4: regulator-pm660l-l4 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l4";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <2960000>;
+ regulator-max-microvolt = <2960000>;
+ };
+
+ pm660l_l5: regulator-pm660l-l5 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l5";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <2960000>;
+ regulator-max-microvolt = <2960000>;
+ };
+
+ pm660l_l6: regulator-pm660l-l6 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l6";
+ qcom,hpm-min-load = <5000>;
+ regulator-min-microvolt = <3008000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ pm660l_l7: regulator-pm660l-l7 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l7";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <3088000>;
+ regulator-max-microvolt = <3100000>;
+ };
+
+ pm660l_l8: regulator-pm660l-l8 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l8";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3312000>;
+ };
+
+ /* pm660l L9 = VDD_LPI_CX supply */
+ pm660l_l9_level: regulator-pm660l-l9-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l9_level";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_l9_floor_level: regulator-pm660l-l9-floor-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l9_floor_level";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ /* pm660l L10 = VDD_LPI_MX supply */
+ pm660l_l10_level: regulator-pm660l-l10-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l10_level";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_l10_floor_level: regulator-pm660l-l10-floor-level {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_l10_floor_level";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm660l_bob: regulator-pm660l-bob {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660l_bob";
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3312000>;
+ };
+
+ apc0_pwrcl_vreg: regulator-pwrcl {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "apc0_pwrcl_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ };
+
+ apc0_l3_vreg: regulator-l3 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "apc0_l3_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ };
+
+ apc1_perfcl_vreg: regulator-perfcl {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "apc1_perfcl_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 3c1aeee..927e0b2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -19,6 +19,7 @@
#include <dt-bindings/clock/qcom,videocc-sdm845.h>
#include <dt-bindings/clock/qcom,cpucc-sdm845.h>
#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
/ {
model = "Qualcomm Technologies, Inc. SDM670";
@@ -825,3 +826,5 @@
&venus_gdsc {
status = "ok";
};
+
+#include "sdm670-regulator.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 8d9ef1f..7ea200e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -345,25 +345,31 @@
};
energy-costs {
+ compatible = "sched-energy";
+
CPU_COST_0: core-cost0 {
busy-cost-data = <
- 92 34 /* 300000 */
- 129 40 /* 422400 */
- 153 43 /* 499200 */
- 177 48 /* 576000 */
- 200 52 /* 652800 */
- 230 58 /* 748800 */
- 253 64 /* 825600 */
- 277 70 /* 902400 */
- 301 76 /* 979200 */
- 324 83 /* 1056000 */
- 348 90 /* 1132800 */
- 371 98 /* 1209600 */
- 395 105 /* 1286400 */
- 419 114 /* 1363200 */
- 442 123 /* 1440000 */
- 466 135 /* 1516800 */
- 490 152 /* 1593600 */
+ 300000 31
+ 422400 38
+ 499200 42
+ 576000 46
+ 652800 51
+ 748800 58
+ 825600 64
+ 902400 70
+ 979200 76
+ 1056000 83
+ 1132800 90
+ 1209600 97
+ 1286400 105
+ 1363200 114
+ 1440000 124
+ 1516800 136
+ 1593600 152
+ 1651200 167 /* speedbin 0,1 */
+ 1670400 173 /* speedbin 2 */
+ 1708800 186 /* speedbin 0,1 */
+ 1747200 201 /* speedbin 2 */
>;
idle-cost-data = <
22 18 14 12
@@ -371,28 +377,32 @@
};
CPU_COST_1: core-cost1 {
busy-cost-data = <
- 156 240 /* 300000 */
- 220 247 /* 422400 */
- 261 252 /* 499200 */
- 301 257 /* 576000 */
- 341 264 /* 652800 */
- 381 272 /* 729600 */
- 421 281 /* 806400 */
- 461 292 /* 883200 */
- 501 306 /* 960000 */
- 542 324 /* 1036800 */
- 582 346 /* 1113600 */
- 622 373 /* 1190400 */
- 662 407 /* 1267200 */
- 702 450 /* 1344000 */
- 742 504 /* 1420800 */
- 783 570 /* 1497600 */
- 823 649 /* 1574400 */
- 863 743 /* 1651200 */
- 903 849 /* 1728000 */
- 943 960 /* 1804800 */
- 983 1062 /* 1881600 */
- 1024 1131 /* 1958400 */
+ 300000 258
+ 422400 260
+ 499200 261
+ 576000 263
+ 652800 267
+ 729600 272
+ 806400 280
+ 883200 291
+ 960000 305
+ 1036800 324
+ 1113600 348
+ 1190400 378
+ 1267200 415
+ 1344000 460
+ 1420800 513
+ 1497600 576
+ 1574400 649
+ 1651200 732
+ 1728000 824
+ 1804800 923
+ 1881600 1027
+ 1958400 1131
+ 2035000 1228 /* speedbin 1,2 */
+ 2092000 1290 /* speedbin 1 */
+ 2112000 1308 /* speedbin 2 */
+ 2208000 1363 /* speedbin 2 */
>;
idle-cost-data = <
100 80 60 40
@@ -400,23 +410,27 @@
};
CLUSTER_COST_0: cluster-cost0 {
busy-cost-data = <
- 92 3 /* 300000 */
- 129 4 /* 422400 */
- 153 4 /* 499200 */
- 177 4 /* 576000 */
- 200 5 /* 652800 */
- 230 5 /* 748800 */
- 253 6 /* 825600 */
- 277 7 /* 902400 */
- 301 7 /* 979200 */
- 324 8 /* 1056000 */
- 348 9 /* 1132800 */
- 371 9 /* 1209600 */
- 395 10 /* 1286400 */
- 419 11 /* 1363200 */
- 442 12 /* 1440000 */
- 466 13 /* 1516800 */
- 490 15 /* 1593600 */
+ 300000 3
+ 422400 4
+ 499200 4
+ 576000 4
+ 652800 5
+ 748800 5
+ 825600 6
+ 902400 7
+ 979200 7
+ 1056000 8
+ 1132800 9
+ 1209600 9
+ 1286400 10
+ 1363200 11
+ 1440000 12
+ 1516800 13
+ 1593600 15
+ 1651200 17 /* speedbin 0,1 */
+ 1670400 19 /* speedbin 2 */
+ 1708800 21 /* speedbin 0,1 */
+ 1747200 23 /* speedbin 2 */
>;
idle-cost-data = <
4 3 2 1
@@ -424,28 +438,32 @@
};
CLUSTER_COST_1: cluster-cost1 {
busy-cost-data = <
- 156 24 /* 300000 */
- 220 24 /* 422400 */
- 261 25 /* 499200 */
- 301 25 /* 576000 */
- 341 26 /* 652800 */
- 381 27 /* 729600 */
- 421 28 /* 806400 */
- 461 29 /* 883200 */
- 501 30 /* 960000 */
- 542 32 /* 1036800 */
- 582 34 /* 1113600 */
- 622 37 /* 1190400 */
- 662 40 /* 1267200 */
- 702 45 /* 1344000 */
- 742 50 /* 1420800 */
- 783 57 /* 1497600 */
- 823 64 /* 1574400 */
- 863 74 /* 1651200 */
- 903 84 /* 1728000 */
- 943 96 /* 1804800 */
- 983 106 /* 1881600 */
- 1024 113 /* 1958400 */
+ 300000 24
+ 422400 24
+ 499200 25
+ 576000 25
+ 652800 26
+ 729600 27
+ 806400 28
+ 883200 29
+ 960000 30
+ 1036800 32
+ 1113600 34
+ 1190400 37
+ 1267200 40
+ 1344000 45
+ 1420800 50
+ 1497600 57
+ 1574400 64
+ 1651200 74
+ 1728000 84
+ 1804800 96
+ 1881600 106
+ 1958400 113
+ 2035000 120 /* speedbin 1,2 */
+ 2092000 125 /* speedbin 1 */
+ 2112000 127 /* speedbin 2 */
+ 2208000 130 /* speedbin 2 */
>;
idle-cost-data = <
4 3 2 1
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 36672cc..c69e015 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -83,7 +83,6 @@
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
-CONFIG_CPU_FREQ_MSM=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index bd1a031..2da9d07 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -89,7 +89,6 @@
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
-CONFIG_CPU_FREQ_MSM=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index 61e032f2..b58f429 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -41,6 +41,9 @@ extern unsigned long cpufreq_scale_max_freq_capacity(int cpu);
#define arch_scale_cpu_capacity scale_cpu_capacity
extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
+#define arch_update_cpu_capacity update_cpu_power_capacity
+extern void update_cpu_power_capacity(int cpu);
+
#include <asm-generic/topology.h>
#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index aaf4bd7..7b670f1 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -449,6 +449,12 @@ static void update_cpu_capacity(unsigned int cpu)
cpu, arch_scale_cpu_capacity(NULL, cpu));
}
+void update_cpu_power_capacity(int cpu)
+{
+ update_cpu_power(cpu);
+ update_cpu_capacity(cpu);
+}
+
static void update_siblings_masks(unsigned int cpuid)
{
struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
@@ -510,8 +516,6 @@ void store_cpu_topology(unsigned int cpuid)
topology_populated:
update_siblings_masks(cpuid);
- update_cpu_power(cpuid);
- update_cpu_capacity(cpuid);
}
static void __init reset_cpu_topology(void)
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index dff4f46..77dde55 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -206,6 +206,16 @@ enum msm_display_caps {
};
/**
+ * enum msm_event_wait - type of HW events to wait for
+ * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW
+ * @MSM_ENC_TX_COMPLETE - wait for the HW to transfer the frame to panel
+ */
+enum msm_event_wait {
+ MSM_ENC_COMMIT_DONE = 0,
+ MSM_ENC_TX_COMPLETE,
+};
+
+/**
* struct msm_roi_alignment - region of interest alignment restrictions
* @xstart_pix_align: left x offset alignment restriction
* @width_pix_align: width alignment restriction
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index aa1b090..d8ac407 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -62,6 +62,9 @@ struct msm_kms_funcs {
/* functions to wait for atomic commit completed on each CRTC */
void (*wait_for_crtc_commit_done)(struct msm_kms *kms,
struct drm_crtc *crtc);
+ /* function pointer to wait for pixel transfer to panel to complete*/
+ void (*wait_for_tx_complete)(struct msm_kms *kms,
+ struct drm_crtc *crtc);
/* get msm_format w/ optional format modifiers from drm_mode_fb_cmd2 */
const struct msm_format *(*get_format)(struct msm_kms *kms,
const uint32_t format,
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 7875d95..cec2b5f 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -195,6 +195,12 @@ u32 sde_core_irq_read(struct sde_kms *sde_kms, int irq_idx, bool clear)
!sde_kms->hw_intr->ops.get_interrupt_status)
return 0;
+ if (irq_idx < 0) {
+ SDE_ERROR("[%pS] invalid irq_idx=%d\n",
+ __builtin_return_address(0), irq_idx);
+ return 0;
+ }
+
return sde_kms->hw_intr->ops.get_interrupt_status(sde_kms->hw_intr,
irq_idx, clear);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index d5c23ea..e708290 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2143,6 +2143,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
SDE_DEBUG("crtc%d commit\n", crtc->base.id);
SDE_EVT32(DRMID(crtc), 2);
}
+ sde_crtc->play_count++;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
@@ -2828,43 +2829,67 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
goto end;
}
- /*
- * enforce pipe priority restrictions
+ /* validate source split:
* use pstates sorted by stage to check planes on same stage
* we assume that all pipes are in source split so its valid to compare
* without taking into account left/right mixer placement
*/
for (i = 1; i < cnt; i++) {
struct plane_state *prv_pstate, *cur_pstate;
- int32_t prv_x, cur_x, prv_id, cur_id;
+ struct sde_rect left_rect, right_rect;
+ int32_t left_pid, right_pid;
+ int32_t stage;
prv_pstate = &pstates[i - 1];
cur_pstate = &pstates[i];
if (prv_pstate->stage != cur_pstate->stage)
continue;
- prv_x = prv_pstate->drm_pstate->crtc_x;
- cur_x = cur_pstate->drm_pstate->crtc_x;
- prv_id = prv_pstate->sde_pstate->base.plane->base.id;
- cur_id = cur_pstate->sde_pstate->base.plane->base.id;
+ stage = cur_pstate->stage;
- /*
- * Planes are enumerated in pipe-priority order such that planes
- * with lower drm_id must be left-most in a shared blend-stage
- * when using source split.
+ left_pid = prv_pstate->sde_pstate->base.plane->base.id;
+ POPULATE_RECT(&left_rect, prv_pstate->drm_pstate->crtc_x,
+ prv_pstate->drm_pstate->crtc_y,
+ prv_pstate->drm_pstate->crtc_w,
+ prv_pstate->drm_pstate->crtc_h, false);
+
+ right_pid = cur_pstate->sde_pstate->base.plane->base.id;
+ POPULATE_RECT(&right_rect, cur_pstate->drm_pstate->crtc_x,
+ cur_pstate->drm_pstate->crtc_y,
+ cur_pstate->drm_pstate->crtc_w,
+ cur_pstate->drm_pstate->crtc_h, false);
+
+ if (right_rect.x < left_rect.x) {
+ swap(left_pid, right_pid);
+ swap(left_rect, right_rect);
+ }
+
+ /**
+ * - planes are enumerated in pipe-priority order such that
+ * planes with lower drm_id must be left-most in a shared
+ * blend-stage when using source split.
+ * - planes in source split must be contiguous in width
+ * - planes in source split must have same dest yoff and height
*/
- if (cur_x > prv_x && cur_id < prv_id) {
+ if (right_pid < left_pid) {
SDE_ERROR(
- "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
- cur_pstate->stage, cur_id, cur_x,
- prv_id, prv_x);
+ "invalid src split cfg. priority mismatch. stage: %d left: %d right: %d\n",
+ stage, left_pid, right_pid);
rc = -EINVAL;
goto end;
- } else if (cur_x < prv_x && cur_id > prv_id) {
+ } else if (right_rect.x != (left_rect.x + left_rect.w)) {
SDE_ERROR(
- "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
- cur_pstate->stage, prv_id, prv_x,
- cur_id, cur_x);
+ "non-contiguous coordinates for src split. stage: %d left: %d - %d right: %d - %d\n",
+ stage, left_rect.x, left_rect.w,
+ right_rect.x, right_rect.w);
+ rc = -EINVAL;
+ goto end;
+ } else if ((left_rect.y != right_rect.y) ||
+ (left_rect.h != right_rect.h)) {
+ SDE_ERROR(
+ "source split at stage: %d. invalid yoff/height: l_y: %d r_y: %d l_h: %d r_h: %d\n",
+ stage, left_rect.y, right_rect.y,
+ left_rect.h, right_rect.h);
rc = -EINVAL;
goto end;
}
@@ -3308,10 +3333,9 @@ static int _sde_debugfs_status_show(struct seq_file *s, void *data)
sde_crtc->vblank_cb_count * 1000, diff_ms) : 0;
seq_printf(s,
- "vblank fps:%lld count:%u total:%llums\n",
- fps,
- sde_crtc->vblank_cb_count,
- ktime_to_ms(diff));
+ "vblank fps:%lld count:%u total:%llums total_framecount:%llu\n",
+ fps, sde_crtc->vblank_cb_count,
+ ktime_to_ms(diff), sde_crtc->play_count);
/* reset time & count for next measurement */
sde_crtc->vblank_cb_count = 0;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index a622d9c..38311c1 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -121,6 +121,7 @@ struct sde_crtc_event {
* @stage_cfg : H/w mixer stage configuration
* @debugfs_root : Parent of debugfs node
* @vblank_cb_count : count of vblank callback since last reset
+ * @play_count : frame count between crtc enable and disable
* @vblank_cb_time : ktime at vblank count reset
* @vblank_refcount : reference count for vblank enable request
* @suspend : whether or not a suspend operation is in progress
@@ -167,6 +168,7 @@ struct sde_crtc {
struct dentry *debugfs_root;
u32 vblank_cb_count;
+ u64 play_count;
ktime_t vblank_cb_time;
atomic_t vblank_refcount;
bool suspend;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index c7ac00c..e1caeaf 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -36,6 +36,7 @@
#include "sde_hw_dsc.h"
#include "sde_crtc.h"
#include "sde_trace.h"
+#include "sde_core_irq.h"
#define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\
(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
@@ -43,6 +44,18 @@
#define SDE_ERROR_ENC(e, fmt, ...) SDE_ERROR("enc%d " fmt,\
(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
+#define SDE_DEBUG_PHYS(p, fmt, ...) SDE_DEBUG("enc%d intf%d pp%d " fmt,\
+ (p) ? (p)->parent->base.id : -1, \
+ (p) ? (p)->intf_idx - INTF_0 : -1, \
+ (p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
+ ##__VA_ARGS__)
+
+#define SDE_ERROR_PHYS(p, fmt, ...) SDE_ERROR("enc%d intf%d pp%d " fmt,\
+ (p) ? (p)->parent->base.id : -1, \
+ (p) ? (p)->intf_idx - INTF_0 : -1, \
+ (p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
+ ##__VA_ARGS__)
+
/* timeout in frames waiting for frame done */
#define SDE_ENCODER_FRAME_DONE_TIMEOUT 60
@@ -278,6 +291,174 @@ static inline int _sde_encoder_power_enable(struct sde_encoder_virt *sde_enc,
enable);
}
+void sde_encoder_helper_report_irq_timeout(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx)
+{
+ SDE_EVT32(DRMID(phys_enc->parent),
+ phys_enc->intf_idx - INTF_0,
+ phys_enc->hw_pp->idx - PINGPONG_0,
+ intr_idx);
+ SDE_ERROR_PHYS(phys_enc, "irq %d timeout\n", intr_idx);
+
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_ERROR);
+}
+
+int sde_encoder_helper_wait_for_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx,
+ struct sde_encoder_wait_info *wait_info)
+{
+ struct sde_encoder_irq *irq;
+ u32 irq_status;
+ int ret;
+
+ if (!phys_enc || !wait_info || intr_idx >= INTR_IDX_MAX) {
+ SDE_ERROR("invalid params\n");
+ return -EINVAL;
+ }
+ irq = &phys_enc->irq[intr_idx];
+
+ /* note: do master / slave checking outside */
+
+ /* return EWOULDBLOCK since we know the wait isn't necessary */
+ if (phys_enc->enable_state == SDE_ENC_DISABLED) {
+ SDE_ERROR_PHYS(phys_enc, "encoder is disabled\n");
+ return -EWOULDBLOCK;
+ }
+
+ if (irq->irq_idx < 0) {
+ SDE_DEBUG_PHYS(phys_enc, "irq %s hw %d disabled, skip wait\n",
+ irq->name, irq->hw_idx);
+ SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx,
+ irq->irq_idx);
+ return 0;
+ }
+
+ SDE_DEBUG_PHYS(phys_enc, "pending_cnt %d\n",
+ atomic_read(wait_info->atomic_cnt));
+ SDE_EVT32(DRMID(phys_enc->parent), irq->hw_idx,
+ atomic_read(wait_info->atomic_cnt),
+ SDE_EVTLOG_FUNC_ENTRY);
+
+ ret = sde_encoder_helper_wait_event_timeout(
+ DRMID(phys_enc->parent),
+ irq->hw_idx,
+ wait_info);
+
+ if (ret <= 0) {
+ irq_status = sde_core_irq_read(phys_enc->sde_kms,
+ irq->irq_idx, true);
+ if (irq_status) {
+ unsigned long flags;
+
+ SDE_EVT32(DRMID(phys_enc->parent),
+ irq->hw_idx,
+ atomic_read(wait_info->atomic_cnt));
+ SDE_DEBUG_PHYS(phys_enc,
+ "done but irq %d not triggered\n",
+ irq->irq_idx);
+ local_irq_save(flags);
+ irq->cb.func(phys_enc, irq->irq_idx);
+ local_irq_restore(flags);
+ ret = 0;
+ } else {
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ ret = 0;
+ }
+
+ SDE_EVT32(DRMID(phys_enc->parent), irq->hw_idx, ret,
+ SDE_EVTLOG_FUNC_EXIT);
+
+ return ret;
+}
+
+int sde_encoder_helper_register_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx)
+{
+ struct sde_encoder_irq *irq;
+ int ret = 0;
+
+ if (!phys_enc || intr_idx >= INTR_IDX_MAX) {
+ SDE_ERROR("invalid params\n");
+ return -EINVAL;
+ }
+ irq = &phys_enc->irq[intr_idx];
+
+ if (irq->irq_idx >= 0) {
+ SDE_ERROR_PHYS(phys_enc,
+ "skipping already registered irq %s type %d\n",
+ irq->name, irq->intr_type);
+ return 0;
+ }
+
+ irq->irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms,
+ irq->intr_type, irq->hw_idx);
+ if (irq->irq_idx < 0) {
+ SDE_ERROR_PHYS(phys_enc,
+ "failed to lookup IRQ index for %s type:%d\n",
+ irq->name, irq->intr_type);
+ return -EINVAL;
+ }
+
+ ret = sde_core_irq_register_callback(phys_enc->sde_kms, irq->irq_idx,
+ &irq->cb);
+ if (ret) {
+ SDE_ERROR_PHYS(phys_enc,
+ "failed to register IRQ callback for %s\n",
+ irq->name);
+ irq->irq_idx = -EINVAL;
+ return ret;
+ }
+
+ ret = sde_core_irq_enable(phys_enc->sde_kms, &irq->irq_idx, 1);
+ if (ret) {
+ SDE_ERROR_PHYS(phys_enc,
+ "enable IRQ for intr:%s failed, irq_idx %d\n",
+ irq->name, irq->irq_idx);
+
+ sde_core_irq_unregister_callback(phys_enc->sde_kms,
+ irq->irq_idx, &irq->cb);
+ irq->irq_idx = -EINVAL;
+ return ret;
+ }
+
+ SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx, irq->irq_idx);
+ SDE_DEBUG_PHYS(phys_enc, "registered irq %s idx: %d\n",
+ irq->name, irq->irq_idx);
+
+ return ret;
+}
+
+int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx)
+{
+ struct sde_encoder_irq *irq;
+
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return -EINVAL;
+ }
+ irq = &phys_enc->irq[intr_idx];
+
+ /* silently skip irqs that weren't registered */
+ if (irq->irq_idx < 0)
+ return 0;
+
+ sde_core_irq_disable(phys_enc->sde_kms, &irq->irq_idx, 1);
+ sde_core_irq_unregister_callback(phys_enc->sde_kms, irq->irq_idx,
+ &irq->cb);
+ irq->irq_idx = -EINVAL;
+
+ SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx, irq->irq_idx);
+ SDE_DEBUG_PHYS(phys_enc, "unregistered %d\n", irq->irq_idx);
+
+ return 0;
+}
+
void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
struct sde_encoder_hw_resources *hw_res,
struct drm_connector_state *conn_state)
@@ -1787,23 +1968,23 @@ void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc)
int sde_encoder_helper_wait_event_timeout(
int32_t drm_id,
int32_t hw_id,
- wait_queue_head_t *wq,
- atomic_t *cnt,
- s64 timeout_ms)
+ struct sde_encoder_wait_info *info)
{
int rc = 0;
- s64 expected_time = ktime_to_ms(ktime_get()) + timeout_ms;
- s64 jiffies = msecs_to_jiffies(timeout_ms);
+ s64 expected_time = ktime_to_ms(ktime_get()) + info->timeout_ms;
+ s64 jiffies = msecs_to_jiffies(info->timeout_ms);
s64 time;
do {
- rc = wait_event_timeout(*wq, atomic_read(cnt) == 0, jiffies);
+ rc = wait_event_timeout(*(info->wq),
+ atomic_read(info->atomic_cnt) == 0, jiffies);
time = ktime_to_ms(ktime_get());
SDE_EVT32(drm_id, hw_id, rc, time, expected_time,
- atomic_read(cnt));
+ atomic_read(info->atomic_cnt));
/* If we timed out, counter is valid and time is less, wait again */
- } while (atomic_read(cnt) && (rc == 0) && (time < expected_time));
+ } while (atomic_read(info->atomic_cnt) && (rc == 0) &&
+ (time < expected_time));
return rc;
}
@@ -2812,8 +2993,10 @@ struct drm_encoder *sde_encoder_init(
return ERR_PTR(ret);
}
-int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_enc)
+int sde_encoder_wait_for_event(struct drm_encoder *drm_enc,
+ enum msm_event_wait event)
{
+ int (*fn_wait)(struct sde_encoder_phys *phys_enc) = NULL;
struct sde_encoder_virt *sde_enc = NULL;
int i, ret = 0;
@@ -2827,8 +3010,17 @@ int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_enc)
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
- if (phys && phys->ops.wait_for_commit_done) {
- ret = phys->ops.wait_for_commit_done(phys);
+ switch (event) {
+ case MSM_ENC_COMMIT_DONE:
+ fn_wait = phys->ops.wait_for_commit_done;
+ break;
+ case MSM_ENC_TX_COMPLETE:
+ fn_wait = phys->ops.wait_for_tx_complete;
+ break;
+ };
+
+ if (phys && fn_wait) {
+ ret = fn_wait(phys);
if (ret)
return ret;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 28b6e1c..0b14a58 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -128,14 +128,24 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *encoder);
void sde_encoder_kickoff(struct drm_encoder *encoder);
/**
- * sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the
- * current pending frames to hardware at a vblank or ctl_start
- * Encoders will map this differently depending on irqs
- * vid mode -> vsync_irq
+ * sde_encoder_wait_for_event - Waits for encoder events
* @encoder: encoder pointer
+ * @event: event to wait for
+ * MSM_ENC_COMMIT_DONE - Wait for hardware to have flushed the current pending
+ * frames to hardware at a vblank or ctl_start
+ * Encoders will map this differently depending on the
+ * panel type.
+ * vid mode -> vsync_irq
+ * cmd mode -> ctl_start
+ * MSM_ENC_TX_COMPLETE - Wait for the hardware to transfer all the pixels to
+ * the panel. Encoders will map this differently
+ * depending on the panel type.
+ * vid mode -> vsync_irq
+ * cmd mode -> pp_done
* Returns: 0 on success, -EWOULDBLOCK if already signaled, error otherwise
*/
-int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_encoder);
+int sde_encoder_wait_for_event(struct drm_encoder *drm_encoder,
+ enum msm_event_wait event);
/*
* sde_encoder_get_intf_mode - get interface mode of the given encoder
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 9911666..6e6960a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -111,6 +111,8 @@ struct sde_encoder_virt_ops {
* @control_vblank_irq Register/Deregister for VBLANK IRQ
* @wait_for_commit_done: Wait for hardware to have flushed the
* current pending frames to hardware
+ * @wait_for_tx_complete: Wait for hardware to transfer the pixels
+ * to the panel
* @prepare_for_kickoff: Do any work necessary prior to a kickoff
* For CMD encoder, may wait for previous tx done
* @handle_post_kickoff: Do any work necessary post-kickoff work
@@ -147,6 +149,7 @@ struct sde_encoder_phys_ops {
struct drm_connector_state *conn_state);
int (*control_vblank_irq)(struct sde_encoder_phys *enc, bool enable);
int (*wait_for_commit_done)(struct sde_encoder_phys *phys_enc);
+ int (*wait_for_tx_complete)(struct sde_encoder_phys *phys_enc);
void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params);
void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
@@ -180,6 +183,25 @@ enum sde_intr_idx {
};
/**
+ * sde_encoder_irq - tracking structure for interrupts
+ * @name: string name of interrupt
+ * @intr_type: Encoder interrupt type
+ * @intr_idx: Encoder interrupt enumeration
+ * @hw_idx: HW Block ID
+ * @irq_idx: IRQ interface lookup index from SDE IRQ framework
+ * will be -EINVAL if IRQ is not registered
+ * @irq_cb: interrupt callback
+ */
+struct sde_encoder_irq {
+ const char *name;
+ enum sde_intr_type intr_type;
+ enum sde_intr_idx intr_idx;
+ int hw_idx;
+ int irq_idx;
+ struct sde_irq_callback cb;
+};
+
+/**
* struct sde_encoder_phys - physical encoder that drives a single INTF block
* tied to a specific panel / sub-panel. Abstract type, sub-classed by
* phys_vid or phys_cmd for video mode or command mode encs respectively.
@@ -211,6 +233,7 @@ enum sde_intr_idx {
* @pending_ctlstart_cnt: Atomic counter tracking the number of ctl start
* pending.
* @pending_kickoff_wq: Wait queue for blocking until kickoff completes
+ * @irq: IRQ tracking structures
*/
struct sde_encoder_phys {
struct drm_encoder *parent;
@@ -236,6 +259,7 @@ struct sde_encoder_phys {
atomic_t pending_ctlstart_cnt;
atomic_t pending_kickoff_cnt;
wait_queue_head_t pending_kickoff_wq;
+ struct sde_encoder_irq irq[INTR_IDX_MAX];
};
static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
@@ -248,16 +272,12 @@ static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
* struct sde_encoder_phys_vid - sub-class of sde_encoder_phys to handle video
* mode specific operations
* @base: Baseclass physical encoder structure
- * @irq_idx: IRQ interface lookup index
- * @irq_cb: interrupt callback
* @hw_intf: Hardware interface to the intf registers
* @timing_params: Current timing parameter
* @rot_prefill_line: number of line to prefill for inline rotation; 0 disable
*/
struct sde_encoder_phys_vid {
struct sde_encoder_phys base;
- int irq_idx[INTR_IDX_MAX];
- struct sde_irq_callback irq_cb[INTR_IDX_MAX];
struct sde_hw_intf *hw_intf;
struct intf_timing_params timing_params;
u64 rot_prefill_line;
@@ -269,10 +289,6 @@ struct sde_encoder_phys_vid {
* @base: Baseclass physical encoder structure
* @intf_idx: Intf Block index used by this phys encoder
* @stream_sel: Stream selection for multi-stream interfaces
- * @pp_rd_ptr_irq_idx: IRQ signifying panel's frame read pointer
- * For CMD encoders, VBLANK is driven by the PP RD Done IRQ
- * @pp_tx_done_irq_idx: IRQ signifying frame transmission to panel complete
- * @irq_cb: interrupt callback
* @serialize_wait4pp: serialize wait4pp feature waits for pp_done interrupt
* after ctl_start instead of before next frame kickoff
* @pp_timeout_report_cnt: number of pingpong done irq timeout errors
@@ -280,8 +296,6 @@ struct sde_encoder_phys_vid {
struct sde_encoder_phys_cmd {
struct sde_encoder_phys base;
int stream_sel;
- int irq_idx[INTR_IDX_MAX];
- struct sde_irq_callback irq_cb[INTR_IDX_MAX];
bool serialize_wait4pp;
int pp_timeout_report_cnt;
};
@@ -355,6 +369,18 @@ struct sde_enc_phys_init_params {
};
/**
+ * sde_encoder_wait_info - container for passing arguments to irq wait functions
+ * @wq: wait queue structure
+ * @atomic_cnt: wait until atomic_cnt equals zero
+ * @timeout_ms: timeout value in milliseconds
+ */
+struct sde_encoder_wait_info {
+ wait_queue_head_t *wq;
+ atomic_t *atomic_cnt;
+ s64 timeout_ms;
+};
+
+/**
* sde_encoder_phys_vid_init - Construct a new video mode physical encoder
* @p: Pointer to init params structure
* Return: Error code or newly allocated encoder
@@ -406,16 +432,12 @@ void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc);
* making sure that elapsed time during wait is valid.
* @drm_id: drm object id for logging
* @hw_id: hw instance id for logging
- * @wq: wait queue structure
- * @cnt: atomic counter to wait on
- * @timeout_ms: timeout value in milliseconds
+ * @info: wait info structure
*/
int sde_encoder_helper_wait_event_timeout(
int32_t drm_id,
int32_t hw_id,
- wait_queue_head_t *wq,
- atomic_t *cnt,
- s64 timeout_ms);
+ struct sde_encoder_wait_info *info);
/**
* sde_encoder_helper_hw_reset - issue ctl hw reset
@@ -463,4 +485,43 @@ void sde_encoder_helper_split_config(
int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
struct drm_framebuffer *fb);
+/**
+ * sde_encoder_helper_report_irq_timeout - utility to report error that irq has
+ * timed out, including reporting frame error event to crtc and debug dump
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: Failing interrupt index
+ */
+void sde_encoder_helper_report_irq_timeout(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx);
+
+/**
+ * sde_encoder_helper_wait_for_irq - utility to wait on an irq.
+ * note: will call sde_encoder_helper_wait_for_irq on timeout
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @wait_info: wait info struct
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_wait_for_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx,
+ struct sde_encoder_wait_info *wait_info);
+
+/**
+ * sde_encoder_helper_register_irq - register and enable an irq
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_register_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx);
+
+/**
+ * sde_encoder_helper_unregister_irq - unregister and disable an irq
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
+ enum sde_intr_idx intr_idx);
+
#endif /* __sde_encoder_phys_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index dbfdc89..447fdcc 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -99,6 +99,98 @@ static void _sde_encoder_phys_cmd_update_intf_cfg(
ctl->ops.setup_intf_cfg(ctl, &intf_cfg);
}
+static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
+{
+ struct sde_encoder_phys *phys_enc = arg;
+ unsigned long lock_flags;
+ int new_cnt;
+
+ if (!phys_enc)
+ return;
+
+ /* notify all synchronous clients first, then asynchronous clients */
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+ phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
+
+ spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
+ new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+ spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+
+ SDE_EVT32_IRQ(DRMID(phys_enc->parent),
+ phys_enc->hw_pp->idx - PINGPONG_0, new_cnt);
+
+ /* Signal any waiting atomic commit thread */
+ wake_up_all(&phys_enc->pending_kickoff_wq);
+}
+
+static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
+{
+ struct sde_encoder_phys *phys_enc = arg;
+
+ if (!phys_enc)
+ return;
+
+ SDE_EVT32_IRQ(DRMID(phys_enc->parent),
+ phys_enc->hw_pp->idx - PINGPONG_0, 0xfff);
+
+ if (phys_enc->parent_ops.handle_vblank_virt)
+ phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
+ phys_enc);
+}
+
+static void sde_encoder_phys_cmd_ctl_start_irq(void *arg, int irq_idx)
+{
+ struct sde_encoder_phys *phys_enc = arg;
+ struct sde_hw_ctl *ctl;
+
+ if (!phys_enc)
+ return;
+
+ if (!phys_enc->hw_ctl)
+ return;
+
+ ctl = phys_enc->hw_ctl;
+ SDE_EVT32_IRQ(DRMID(phys_enc->parent), ctl->idx - CTL_0, 0xfff);
+ atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
+
+ /* Signal any waiting ctl start interrupt */
+ wake_up_all(&phys_enc->pending_kickoff_wq);
+}
+
+static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx)
+{
+ struct sde_encoder_phys *phys_enc = arg;
+
+ if (!phys_enc)
+ return;
+
+ if (phys_enc->parent_ops.handle_underrun_virt)
+ phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
+ phys_enc);
+}
+
+static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_irq *irq;
+
+ irq = &phys_enc->irq[INTR_IDX_CTL_START];
+ irq->hw_idx = phys_enc->hw_ctl->idx;
+ irq->irq_idx = -EINVAL;
+
+ irq = &phys_enc->irq[INTR_IDX_PINGPONG];
+ irq->hw_idx = phys_enc->hw_pp->idx;
+ irq->irq_idx = -EINVAL;
+
+ irq = &phys_enc->irq[INTR_IDX_RDPTR];
+ irq->hw_idx = phys_enc->hw_pp->idx;
+ irq->irq_idx = -EINVAL;
+
+ irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+ irq->hw_idx = phys_enc->intf_idx;
+ irq->irq_idx = -EINVAL;
+}
static void sde_encoder_phys_cmd_mode_set(
struct sde_encoder_phys *phys_enc,
@@ -112,8 +204,7 @@ static void sde_encoder_phys_cmd_mode_set(
int i, instance;
if (!phys_enc || !mode || !adj_mode) {
- SDE_ERROR("invalid arg(s), enc %d mode %d adj_mode %d\n",
- phys_enc != 0, mode != 0, adj_mode != 0);
+ SDE_ERROR("invalid args\n");
return;
}
phys_enc->cached_mode = *adj_mode;
@@ -135,71 +226,8 @@ static void sde_encoder_phys_cmd_mode_set(
phys_enc->hw_ctl = NULL;
return;
}
-}
-static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
-{
- struct sde_encoder_phys_cmd *cmd_enc = arg;
- struct sde_encoder_phys *phys_enc;
- unsigned long lock_flags;
- int new_cnt;
-
- if (!cmd_enc)
- return;
-
- phys_enc = &cmd_enc->base;
-
- /* notify all synchronous clients first, then asynchronous clients */
- if (phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
- phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
-
- spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
- new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
- spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
-
- SDE_EVT32_IRQ(DRMID(phys_enc->parent),
- phys_enc->hw_pp->idx - PINGPONG_0, new_cnt);
-
- /* Signal any waiting atomic commit thread */
- wake_up_all(&phys_enc->pending_kickoff_wq);
-}
-
-static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
-{
- struct sde_encoder_phys_cmd *cmd_enc = arg;
- struct sde_encoder_phys *phys_enc = &cmd_enc->base;
-
- if (!cmd_enc)
- return;
-
- SDE_EVT32_IRQ(DRMID(phys_enc->parent),
- phys_enc->hw_pp->idx - PINGPONG_0, 0xfff);
-
- if (phys_enc->parent_ops.handle_vblank_virt)
- phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
- phys_enc);
-}
-
-static void sde_encoder_phys_cmd_ctl_start_irq(void *arg, int irq_idx)
-{
- struct sde_encoder_phys_cmd *cmd_enc = arg;
- struct sde_encoder_phys *phys_enc;
- struct sde_hw_ctl *ctl;
-
- if (!cmd_enc)
- return;
-
- phys_enc = &cmd_enc->base;
- if (!phys_enc->hw_ctl)
- return;
-
- ctl = phys_enc->hw_ctl;
- SDE_EVT32_IRQ(DRMID(phys_enc->parent), ctl->idx - CTL_0, 0xfff);
- atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
-
- /* Signal any waiting ctl start interrupt */
- wake_up_all(&phys_enc->pending_kickoff_wq);
+ _sde_encoder_phys_cmd_setup_irq_hw_idx(phys_enc);
}
static bool _sde_encoder_phys_is_ppsplit(struct sde_encoder_phys *phys_enc)
@@ -279,7 +307,7 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
{
struct sde_encoder_phys_cmd *cmd_enc =
to_sde_encoder_phys_cmd(phys_enc);
- u32 irq_status;
+ struct sde_encoder_wait_info wait_info;
int ret;
if (!phys_enc) {
@@ -287,154 +315,24 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
return -EINVAL;
}
+ wait_info.wq = &phys_enc->pending_kickoff_wq;
+ wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
+ wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
+
/* slave encoder doesn't enable for ppsplit */
if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
return 0;
- /* return EWOULDBLOCK since we know the wait isn't necessary */
- if (phys_enc->enable_state == SDE_ENC_DISABLED) {
- SDE_ERROR_CMDENC(cmd_enc, "encoder is disabled\n");
- return -EWOULDBLOCK;
- }
-
- /* wait for previous kickoff to complete */
- ret = sde_encoder_helper_wait_event_timeout(
- DRMID(phys_enc->parent),
- phys_enc->hw_pp->idx - PINGPONG_0,
- &phys_enc->pending_kickoff_wq,
- &phys_enc->pending_kickoff_cnt,
- KICKOFF_TIMEOUT_MS);
- if (ret <= 0) {
- /* read and clear interrupt */
- irq_status = sde_core_irq_read(phys_enc->sde_kms,
- cmd_enc->irq_idx[INTR_IDX_PINGPONG], true);
- if (irq_status) {
- unsigned long flags;
- SDE_EVT32(DRMID(phys_enc->parent),
- phys_enc->hw_pp->idx - PINGPONG_0);
- SDE_DEBUG_CMDENC(cmd_enc,
- "pp:%d done but irq not triggered\n",
- phys_enc->hw_pp->idx - PINGPONG_0);
- local_irq_save(flags);
- sde_encoder_phys_cmd_pp_tx_done_irq(cmd_enc,
- INTR_IDX_PINGPONG);
- local_irq_restore(flags);
- ret = 0;
- } else {
- ret = _sde_encoder_phys_cmd_handle_ppdone_timeout(
- phys_enc);
- }
- } else {
- ret = 0;
- }
-
- if (!ret)
+ ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_PINGPONG,
+ &wait_info);
+ if (ret == -ETIMEDOUT)
+ _sde_encoder_phys_cmd_handle_ppdone_timeout(phys_enc);
+ else if (!ret)
cmd_enc->pp_timeout_report_cnt = 0;
return ret;
}
-static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx)
-{
- struct sde_encoder_phys_cmd *cmd_enc = arg;
- struct sde_encoder_phys *phys_enc;
-
- if (!cmd_enc)
- return;
-
- phys_enc = &cmd_enc->base;
- if (phys_enc->parent_ops.handle_underrun_virt)
- phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
- phys_enc);
-}
-
-static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc,
- enum sde_intr_type intr_type, int idx,
- void (*irq_func)(void *, int), const char *irq_name)
-{
- struct sde_encoder_phys_cmd *cmd_enc =
- to_sde_encoder_phys_cmd(phys_enc);
- int idx_lookup = 0;
- int ret = 0;
-
- if (!phys_enc) {
- SDE_ERROR("invalid encoder\n");
- return -EINVAL;
- }
-
- if (intr_type == SDE_IRQ_TYPE_INTF_UNDER_RUN)
- idx_lookup = phys_enc->intf_idx;
- else if (intr_type == SDE_IRQ_TYPE_CTL_START)
- idx_lookup = phys_enc->hw_ctl ? phys_enc->hw_ctl->idx : -1;
- else
- idx_lookup = phys_enc->hw_pp->idx;
-
- cmd_enc->irq_idx[idx] = sde_core_irq_idx_lookup(phys_enc->sde_kms,
- intr_type, idx_lookup);
- if (cmd_enc->irq_idx[idx] < 0) {
- SDE_ERROR_CMDENC(cmd_enc,
- "failed to lookup IRQ index for %s with pp=%d\n",
- irq_name,
- phys_enc->hw_pp->idx - PINGPONG_0);
- return -EINVAL;
- }
-
- cmd_enc->irq_cb[idx].func = irq_func;
- cmd_enc->irq_cb[idx].arg = cmd_enc;
- ret = sde_core_irq_register_callback(phys_enc->sde_kms,
- cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
- if (ret) {
- SDE_ERROR_CMDENC(cmd_enc,
- "failed to register IRQ callback %s\n",
- irq_name);
- return ret;
- }
-
- ret = sde_core_irq_enable(phys_enc->sde_kms, &cmd_enc->irq_idx[idx], 1);
- if (ret) {
- SDE_ERROR_CMDENC(cmd_enc,
- "failed to enable IRQ for %s, pp %d, irq_idx %d\n",
- irq_name,
- phys_enc->hw_pp->idx - PINGPONG_0,
- cmd_enc->irq_idx[idx]);
- cmd_enc->irq_idx[idx] = -EINVAL;
-
- /* Unregister callback on IRQ enable failure */
- sde_core_irq_unregister_callback(phys_enc->sde_kms,
- cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
- return ret;
- }
-
- SDE_DEBUG_CMDENC(cmd_enc, "registered IRQ %s for pp %d, irq_idx %d\n",
- irq_name,
- phys_enc->hw_pp->idx - PINGPONG_0,
- cmd_enc->irq_idx[idx]);
-
- return ret;
-}
-
-static int sde_encoder_phys_cmd_unregister_irq(
- struct sde_encoder_phys *phys_enc, int idx)
-{
- struct sde_encoder_phys_cmd *cmd_enc =
- to_sde_encoder_phys_cmd(phys_enc);
-
- if (!phys_enc) {
- SDE_ERROR("invalid encoder\n");
- return -EINVAL;
- }
-
- sde_core_irq_disable(phys_enc->sde_kms, &cmd_enc->irq_idx[idx], 1);
- sde_core_irq_unregister_callback(phys_enc->sde_kms,
- cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
-
- SDE_DEBUG_CMDENC(cmd_enc, "unregistered IRQ for pp %d, irq_idx %d\n",
- phys_enc->hw_pp->idx - PINGPONG_0,
- cmd_enc->irq_idx[idx]);
-
- return 0;
-}
-
static int sde_encoder_phys_cmd_control_vblank_irq(
struct sde_encoder_phys *phys_enc,
bool enable)
@@ -460,13 +358,9 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
enable, atomic_read(&phys_enc->vblank_refcount));
if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
- ret = sde_encoder_phys_cmd_register_irq(phys_enc,
- SDE_IRQ_TYPE_PING_PONG_RD_PTR,
- INTR_IDX_RDPTR,
- sde_encoder_phys_cmd_pp_rd_ptr_irq,
- "pp_rd_ptr");
+ ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
- ret = sde_encoder_phys_cmd_unregister_irq(phys_enc,
+ ret = sde_encoder_helper_unregister_irq(phys_enc,
INTR_IDX_RDPTR);
end:
@@ -489,35 +383,22 @@ void sde_encoder_phys_cmd_irq_control(struct sde_encoder_phys *phys_enc,
cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
if (enable) {
- sde_encoder_phys_cmd_register_irq(phys_enc,
- SDE_IRQ_TYPE_PING_PONG_COMP,
- INTR_IDX_PINGPONG,
- sde_encoder_phys_cmd_pp_tx_done_irq,
- "pp_tx_done");
-
+ sde_encoder_helper_register_irq(phys_enc, INTR_IDX_PINGPONG);
+ sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true);
- sde_encoder_phys_cmd_register_irq(phys_enc,
- SDE_IRQ_TYPE_INTF_UNDER_RUN,
- INTR_IDX_UNDERRUN,
- sde_encoder_phys_cmd_underrun_irq,
- "underrun");
+ if (sde_encoder_phys_cmd_is_master(phys_enc))
+ sde_encoder_helper_register_irq(phys_enc,
+ INTR_IDX_CTL_START);
+ } else {
if (sde_encoder_phys_cmd_is_master(phys_enc))
- sde_encoder_phys_cmd_register_irq(phys_enc,
- SDE_IRQ_TYPE_CTL_START,
- INTR_IDX_CTL_START,
- sde_encoder_phys_cmd_ctl_start_irq,
- "ctl_start");
- } else {
- if (sde_encoder_phys_cmd_is_master(phys_enc))
- sde_encoder_phys_cmd_unregister_irq(
- phys_enc, INTR_IDX_CTL_START);
- sde_encoder_phys_cmd_unregister_irq(
- phys_enc, INTR_IDX_UNDERRUN);
+ sde_encoder_helper_unregister_irq(phys_enc,
+ INTR_IDX_CTL_START);
+
+ sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
- sde_encoder_phys_cmd_unregister_irq(
- phys_enc, INTR_IDX_PINGPONG);
+ sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_PINGPONG);
}
}
@@ -776,45 +657,51 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff(
static int _sde_encoder_phys_cmd_wait_for_ctl_start(
struct sde_encoder_phys *phys_enc)
{
- int rc = 0;
- struct sde_hw_ctl *ctl;
- u32 irq_status;
- struct sde_encoder_phys_cmd *cmd_enc;
+ struct sde_encoder_phys_cmd *cmd_enc =
+ to_sde_encoder_phys_cmd(phys_enc);
+ struct sde_encoder_wait_info wait_info;
+ int ret;
- if (!phys_enc->hw_ctl) {
- SDE_ERROR("invalid ctl\n");
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
return -EINVAL;
}
- ctl = phys_enc->hw_ctl;
- cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
- rc = sde_encoder_helper_wait_event_timeout(DRMID(phys_enc->parent),
- ctl->idx - CTL_0,
- &phys_enc->pending_kickoff_wq,
- &phys_enc->pending_ctlstart_cnt,
- CTL_START_TIMEOUT_MS);
- if (rc <= 0) {
- /* read and clear interrupt */
- irq_status = sde_core_irq_read(phys_enc->sde_kms,
- cmd_enc->irq_idx[INTR_IDX_CTL_START], true);
- if (irq_status) {
- unsigned long flags;
+ wait_info.wq = &phys_enc->pending_kickoff_wq;
+ wait_info.atomic_cnt = &phys_enc->pending_ctlstart_cnt;
+ wait_info.timeout_ms = CTL_START_TIMEOUT_MS;
- SDE_EVT32(DRMID(phys_enc->parent), ctl->idx - CTL_0);
- SDE_DEBUG_CMDENC(cmd_enc,
- "ctl:%d start done but irq not triggered\n",
- ctl->idx - CTL_0);
- local_irq_save(flags);
- sde_encoder_phys_cmd_ctl_start_irq(cmd_enc,
- INTR_IDX_CTL_START);
- local_irq_restore(flags);
- rc = 0;
- } else {
- SDE_ERROR("ctl start interrupt wait failed\n");
- rc = -EINVAL;
- }
- } else {
- rc = 0;
+ /* slave encoder doesn't enable for ppsplit */
+ if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
+ return 0;
+
+ ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START,
+ &wait_info);
+ if (ret == -ETIMEDOUT) {
+ SDE_ERROR_CMDENC(cmd_enc, "ctl start interrupt wait failed\n");
+ ret = -EINVAL;
+ } else if (!ret)
+ ret = 0;
+
+ return ret;
+}
+
+static int sde_encoder_phys_cmd_wait_for_tx_complete(
+ struct sde_encoder_phys *phys_enc)
+{
+ int rc;
+ struct sde_encoder_phys_cmd *cmd_enc;
+
+ if (!phys_enc)
+ return -EINVAL;
+
+ cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
+
+ rc = _sde_encoder_phys_cmd_wait_for_idle(phys_enc);
+ if (rc) {
+ SDE_EVT32(DRMID(phys_enc->parent),
+ phys_enc->intf_idx - INTF_0);
+ SDE_ERROR("failed wait_for_idle: %d\n", rc);
}
return rc;
@@ -894,6 +781,7 @@ static void sde_encoder_phys_cmd_init_ops(
ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq;
ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done;
ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
+ ops->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete;
ops->trigger_start = sde_encoder_helper_trigger_start;
ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
ops->hw_reset = sde_encoder_helper_hw_reset;
@@ -908,6 +796,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
struct sde_encoder_phys *phys_enc = NULL;
struct sde_encoder_phys_cmd *cmd_enc = NULL;
struct sde_hw_mdp *hw_mdp;
+ struct sde_encoder_irq *irq;
int i, ret = 0;
SDE_DEBUG("intf %d\n", p->intf_idx - INTF_0);
@@ -939,8 +828,38 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
cmd_enc->stream_sel = 0;
phys_enc->enable_state = SDE_ENC_DISABLED;
phys_enc->comp_type = p->comp_type;
- for (i = 0; i < INTR_IDX_MAX; i++)
- INIT_LIST_HEAD(&cmd_enc->irq_cb[i].list);
+ for (i = 0; i < INTR_IDX_MAX; i++) {
+ irq = &phys_enc->irq[i];
+ INIT_LIST_HEAD(&irq->cb.list);
+ irq->irq_idx = -EINVAL;
+ irq->hw_idx = -EINVAL;
+ irq->cb.arg = phys_enc;
+ }
+
+ irq = &phys_enc->irq[INTR_IDX_CTL_START];
+ irq->name = "ctl_start";
+ irq->intr_type = SDE_IRQ_TYPE_CTL_START;
+ irq->intr_idx = INTR_IDX_CTL_START;
+ irq->cb.func = sde_encoder_phys_cmd_ctl_start_irq;
+
+ irq = &phys_enc->irq[INTR_IDX_PINGPONG];
+ irq->name = "pp_done";
+ irq->intr_type = SDE_IRQ_TYPE_PING_PONG_COMP;
+ irq->intr_idx = INTR_IDX_PINGPONG;
+ irq->cb.func = sde_encoder_phys_cmd_pp_tx_done_irq;
+
+ irq = &phys_enc->irq[INTR_IDX_RDPTR];
+ irq->name = "pp_rd_ptr";
+ irq->intr_type = SDE_IRQ_TYPE_PING_PONG_RD_PTR;
+ irq->intr_idx = INTR_IDX_RDPTR;
+ irq->cb.func = sde_encoder_phys_cmd_pp_rd_ptr_irq;
+
+ irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+ irq->name = "underrun";
+ irq->intr_type = SDE_IRQ_TYPE_INTF_UNDER_RUN;
+ irq->intr_idx = INTR_IDX_UNDERRUN;
+ irq->cb.func = sde_encoder_phys_cmd_underrun_irq;
+
atomic_set(&phys_enc->vblank_refcount, 0);
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
atomic_set(&phys_enc->pending_ctlstart_cnt, 0);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 4f28a5f..007738a6 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -345,17 +345,17 @@ static void sde_encoder_phys_vid_setup_timing_engine(
static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
{
- struct sde_encoder_phys_vid *vid_enc = arg;
- struct sde_encoder_phys *phys_enc;
+ struct sde_encoder_phys *phys_enc = arg;
+ struct sde_encoder_phys_vid *vid_enc =
+ to_sde_encoder_phys_vid(phys_enc);
struct sde_hw_ctl *hw_ctl;
unsigned long lock_flags;
u32 flush_register = 0;
int new_cnt = -1, old_cnt = -1;
- if (!vid_enc)
+ if (!phys_enc)
return;
- phys_enc = &vid_enc->base;
hw_ctl = phys_enc->hw_ctl;
if (phys_enc->parent_ops.handle_vblank_virt)
@@ -387,13 +387,11 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx)
{
- struct sde_encoder_phys_vid *vid_enc = arg;
- struct sde_encoder_phys *phys_enc;
+ struct sde_encoder_phys *phys_enc = arg;
- if (!vid_enc)
+ if (!phys_enc)
return;
- phys_enc = &vid_enc->base;
if (phys_enc->parent_ops.handle_underrun_virt)
phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
phys_enc);
@@ -419,77 +417,18 @@ static bool sde_encoder_phys_vid_needs_single_flush(
return phys_enc && _sde_encoder_phys_is_ppsplit(phys_enc);
}
-static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
- enum sde_intr_type intr_type, int idx,
- void (*irq_func)(void *, int), const char *irq_name)
+static void _sde_encoder_phys_vid_setup_irq_hw_idx(
+ struct sde_encoder_phys *phys_enc)
{
- struct sde_encoder_phys_vid *vid_enc;
- int ret = 0;
+ struct sde_encoder_irq *irq;
- if (!phys_enc) {
- SDE_ERROR("invalid encoder\n");
- return -EINVAL;
- }
+ irq = &phys_enc->irq[INTR_IDX_VSYNC];
+ irq->hw_idx = phys_enc->intf_idx;
+ irq->irq_idx = -EINVAL;
- vid_enc = to_sde_encoder_phys_vid(phys_enc);
- vid_enc->irq_idx[idx] = sde_core_irq_idx_lookup(phys_enc->sde_kms,
- intr_type, vid_enc->hw_intf->idx);
- if (vid_enc->irq_idx[idx] < 0) {
- SDE_ERROR_VIDENC(vid_enc,
- "failed to lookup IRQ index for %s type:%d\n", irq_name,
- intr_type);
- return -EINVAL;
- }
-
- vid_enc->irq_cb[idx].func = irq_func;
- vid_enc->irq_cb[idx].arg = vid_enc;
- ret = sde_core_irq_register_callback(phys_enc->sde_kms,
- vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
- if (ret) {
- SDE_ERROR_VIDENC(vid_enc,
- "failed to register IRQ callback for %s\n", irq_name);
- return ret;
- }
-
- ret = sde_core_irq_enable(phys_enc->sde_kms, &vid_enc->irq_idx[idx], 1);
- if (ret) {
- SDE_ERROR_VIDENC(vid_enc,
- "enable IRQ for intr:%s failed, irq_idx %d\n",
- irq_name, vid_enc->irq_idx[idx]);
- vid_enc->irq_idx[idx] = -EINVAL;
-
- /* unregister callback on IRQ enable failure */
- sde_core_irq_unregister_callback(phys_enc->sde_kms,
- vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
- return ret;
- }
-
- SDE_DEBUG_VIDENC(vid_enc, "registered irq %s idx: %d\n",
- irq_name, vid_enc->irq_idx[idx]);
-
- return ret;
-}
-
-static int sde_encoder_phys_vid_unregister_irq(
- struct sde_encoder_phys *phys_enc, int idx)
-{
- struct sde_encoder_phys_vid *vid_enc;
-
- if (!phys_enc) {
- SDE_ERROR("invalid encoder\n");
- goto end;
- }
-
- vid_enc = to_sde_encoder_phys_vid(phys_enc);
- sde_core_irq_disable(phys_enc->sde_kms, &vid_enc->irq_idx[idx], 1);
-
- sde_core_irq_unregister_callback(phys_enc->sde_kms,
- vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
-
- SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", vid_enc->irq_idx[idx]);
-
-end:
- return 0;
+ irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+ irq->hw_idx = phys_enc->intf_idx;
+ irq->irq_idx = -EINVAL;
}
static void sde_encoder_phys_vid_mode_set(
@@ -527,6 +466,8 @@ static void sde_encoder_phys_vid_mode_set(
phys_enc->hw_ctl = NULL;
return;
}
+
+ _sde_encoder_phys_vid_setup_irq_hw_idx(phys_enc);
}
static int sde_encoder_phys_vid_control_vblank_irq(
@@ -555,13 +496,10 @@ static int sde_encoder_phys_vid_control_vblank_irq(
atomic_read(&phys_enc->vblank_refcount));
if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
- ret = sde_encoder_phys_vid_register_irq(phys_enc,
- SDE_IRQ_TYPE_INTF_VSYNC,
- INTR_IDX_VSYNC,
- sde_encoder_phys_vid_vblank_irq, "vsync_irq");
+ ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC);
else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
- ret = sde_encoder_phys_vid_unregister_irq(phys_enc,
- INTR_IDX_VSYNC);
+ ret = sde_encoder_helper_unregister_irq(phys_enc,
+ INTR_IDX_VSYNC);
if (ret)
SDE_ERROR_VIDENC(vid_enc,
@@ -608,10 +546,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
if (ret)
goto end;
- ret = sde_encoder_phys_vid_register_irq(phys_enc,
- SDE_IRQ_TYPE_INTF_UNDER_RUN,
- INTR_IDX_UNDERRUN,
- sde_encoder_phys_vid_underrun_irq, "underrun");
+ ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
if (ret) {
sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
goto end;
@@ -680,9 +615,11 @@ static void sde_encoder_phys_vid_get_hw_resources(
static int sde_encoder_phys_vid_wait_for_vblank(
struct sde_encoder_phys *phys_enc, bool notify)
{
- struct sde_encoder_phys_vid *vid_enc =
- to_sde_encoder_phys_vid(phys_enc);
- u32 irq_status;
+ struct sde_encoder_wait_info wait_info = {
+ .wq = &phys_enc->pending_kickoff_wq,
+ .atomic_cnt = &phys_enc->pending_kickoff_cnt,
+ .timeout_ms = KICKOFF_TIMEOUT_MS,
+ };
int ret;
if (!sde_encoder_phys_vid_is_master(phys_enc)) {
@@ -695,54 +632,18 @@ static int sde_encoder_phys_vid_wait_for_vblank(
return 0;
}
- if (phys_enc->enable_state != SDE_ENC_ENABLED) {
- SDE_ERROR("encoder not enabled\n");
- return -EWOULDBLOCK;
- }
-
- SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
- SDE_EVTLOG_FUNC_ENTRY);
-
/* Wait for kickoff to complete */
- ret = sde_encoder_helper_wait_event_timeout(
- DRMID(phys_enc->parent),
- vid_enc->hw_intf->idx - INTF_0,
- &phys_enc->pending_kickoff_wq,
- &phys_enc->pending_kickoff_cnt,
- KICKOFF_TIMEOUT_MS);
- if (ret <= 0) {
- irq_status = sde_core_irq_read(phys_enc->sde_kms,
- vid_enc->irq_idx[INTR_IDX_VSYNC], true);
- if (irq_status) {
- SDE_EVT32(DRMID(phys_enc->parent),
- vid_enc->hw_intf->idx - INTF_0);
- SDE_DEBUG_VIDENC(vid_enc, "done, irq not triggered\n");
- if (notify && phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(
- phys_enc->parent, phys_enc,
- SDE_ENCODER_FRAME_EVENT_DONE);
- sde_encoder_phys_vid_vblank_irq(vid_enc,
- INTR_IDX_VSYNC);
- ret = 0;
- } else {
- SDE_EVT32(DRMID(phys_enc->parent),
- vid_enc->hw_intf->idx - INTF_0);
- SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n");
- if (notify && phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(
- phys_enc->parent, phys_enc,
- SDE_ENCODER_FRAME_EVENT_ERROR);
- ret = -ETIMEDOUT;
- }
- } else {
- if (notify && phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(
- phys_enc->parent, phys_enc,
- SDE_ENCODER_FRAME_EVENT_DONE);
- ret = 0;
- }
+ ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
+ &wait_info);
- return 0;
+ if (ret == -ETIMEDOUT) {
+ sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_VSYNC);
+ } else if (!ret && notify && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_DONE);
+
+ return ret;
}
static int sde_encoder_phys_vid_wait_for_commit_done(
@@ -845,6 +746,8 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
}
+ sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
+
if (atomic_read(&phys_enc->vblank_refcount))
SDE_ERROR_VIDENC(vid_enc, "invalid vblank refcount %d\n",
atomic_read(&phys_enc->vblank_refcount));
@@ -932,6 +835,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init(
struct sde_encoder_phys_vid *vid_enc = NULL;
struct sde_rm_hw_iter iter;
struct sde_hw_mdp *hw_mdp;
+ struct sde_encoder_irq *irq;
int i, ret = 0;
if (!p) {
@@ -986,8 +890,26 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init(
phys_enc->intf_mode = INTF_MODE_VIDEO;
phys_enc->enc_spinlock = p->enc_spinlock;
phys_enc->comp_type = p->comp_type;
- for (i = 0; i < INTR_IDX_MAX; i++)
- INIT_LIST_HEAD(&vid_enc->irq_cb[i].list);
+ for (i = 0; i < INTR_IDX_MAX; i++) {
+ irq = &phys_enc->irq[i];
+ INIT_LIST_HEAD(&irq->cb.list);
+ irq->irq_idx = -EINVAL;
+ irq->hw_idx = -EINVAL;
+ irq->cb.arg = phys_enc;
+ }
+
+ irq = &phys_enc->irq[INTR_IDX_VSYNC];
+ irq->name = "vsync_irq";
+ irq->intr_type = SDE_IRQ_TYPE_INTF_VSYNC;
+ irq->intr_idx = INTR_IDX_VSYNC;
+ irq->cb.func = sde_encoder_phys_vid_vblank_irq;
+
+ irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+ irq->name = "underrun";
+ irq->intr_type = SDE_IRQ_TYPE_INTF_UNDER_RUN;
+ irq->intr_idx = INTR_IDX_UNDERRUN;
+ irq->cb.func = sde_encoder_phys_vid_underrun_irq;
+
atomic_set(&phys_enc->vblank_refcount, 0);
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
init_waitqueue_head(&phys_enc->pending_kickoff_wq);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
index 53a48c8..8c3d4fc 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
@@ -974,6 +974,11 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr,
if (!intr)
return 0;
+ if (irq_idx >= ARRAY_SIZE(sde_irq_map) || irq_idx < 0) {
+ pr_err("invalid IRQ index: [%d]\n", irq_idx);
+ return 0;
+ }
+
spin_lock_irqsave(&intr->mask_lock, irq_flags);
reg_idx = sde_irq_map[irq_idx].reg_idx;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 85e9c6c..bda89cf 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -412,6 +412,49 @@ static void sde_kms_complete_commit(struct msm_kms *kms,
SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
}
+static void sde_kms_wait_for_tx_complete(struct msm_kms *kms,
+ struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+ struct drm_device *dev;
+ int ret;
+
+ if (!kms || !crtc || !crtc->state || !crtc->dev) {
+ SDE_ERROR("invalid params\n");
+ return;
+ }
+
+ if (!crtc->state->enable) {
+ SDE_DEBUG("[crtc:%d] not enable\n", crtc->base.id);
+ return;
+ }
+
+ if (!crtc->state->active) {
+ SDE_DEBUG("[crtc:%d] not active\n", crtc->base.id);
+ return;
+ }
+
+ dev = crtc->dev;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc != crtc)
+ continue;
+ /*
+ * Video Mode - Wait for VSYNC
+ * Cmd Mode - Wait for PP_DONE. Will be no-op if transfer is
+ * complete
+ */
+ SDE_EVT32_VERBOSE(DRMID(crtc));
+ ret = sde_encoder_wait_for_event(encoder, MSM_ENC_TX_COMPLETE);
+ if (ret && ret != -EWOULDBLOCK) {
+ SDE_ERROR(
+ "[crtc: %d][enc: %d] wait for commit done returned %d\n",
+ crtc->base.id, encoder->base.id, ret);
+ break;
+ }
+ }
+}
+
static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
struct drm_crtc *crtc)
{
@@ -443,7 +486,7 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
* mode panels. This may be a no-op for command mode panels.
*/
SDE_EVT32_VERBOSE(DRMID(crtc));
- ret = sde_encoder_wait_for_commit_done(encoder);
+ ret = sde_encoder_wait_for_event(encoder, MSM_ENC_COMMIT_DONE);
if (ret && ret != -EWOULDBLOCK) {
SDE_ERROR("wait for commit done returned %d\n", ret);
break;
@@ -1340,6 +1383,7 @@ static const struct msm_kms_funcs kms_funcs = {
.commit = sde_kms_commit,
.complete_commit = sde_kms_complete_commit,
.wait_for_crtc_commit_done = sde_kms_wait_for_commit_done,
+ .wait_for_tx_complete = sde_kms_wait_for_tx_complete,
.enable_vblank = sde_kms_enable_vblank,
.disable_vblank = sde_kms_disable_vblank,
.check_modified_format = sde_format_check_modified_format,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index 21a61ff..2a0c4a7 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -60,10 +60,6 @@ int cam_context_buf_done_from_hw(struct cam_context *ctx,
cam_sync_signal(req->out_map_entries[j].sync_id,
CAM_SYNC_STATE_SIGNALED_SUCCESS);
req->num_out_acked++;
- trace_printk("Sync success req %lld, reset sync id 0x%x\n",
- req->request_id,
- req->out_map_entries[j].sync_id);
-
req->out_map_entries[j].sync_id = -1;
}
@@ -195,8 +191,6 @@ void cam_context_sync_callback(int32_t sync_obj, int status, void *data)
req->num_in_acked++;
if (req->num_in_acked == req->num_in_map_entries) {
apply.request_id = req->request_id;
- trace_printk("async cb for request :%llu",
- req->request_id);
cam_context_apply_req_to_hw(ctx, &apply);
}
}
@@ -287,8 +281,6 @@ int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx,
list_add_tail(&req->list, &ctx->pending_req_list);
spin_unlock(&ctx->lock);
for (i = 0; i < req->num_in_map_entries; i++) {
- trace_printk("register in fence callback: %d\n",
- req->in_map_entries[i].sync_id);
rc = cam_sync_register_callback(
cam_context_sync_callback,
(void *)ctx,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index 17f6973..74a94b2 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -16,6 +16,13 @@
#include "cam_node.h"
+static void __cam_node_handle_shutdown(struct cam_node *node)
+{
+ if (node->hw_mgr_intf.hw_close)
+ node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
+ NULL);
+}
+
static int __cam_node_handle_query_cap(struct cam_node *node,
struct cam_query_cap_cmd *query)
{
@@ -408,6 +415,9 @@ int cam_node_handle_ioctl(struct cam_node *node, struct cam_control *cmd)
}
break;
}
+ case CAM_SD_SHUTDOWN:
+ __cam_node_handle_shutdown(node);
+ break;
default:
pr_err("Unknown op code %d\n", cmd->op_code);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index b774625..aba0caa 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
@@ -327,7 +327,7 @@ EXPORT_SYMBOL(cam_cpas_register_client);
int cam_cpas_subdev_cmd(struct cam_cpas_intf *cpas_intf,
struct cam_control *cmd)
{
- int rc;
+ int rc = 0;
if (!cmd) {
pr_err("Invalid input cmd\n");
@@ -357,6 +357,8 @@ int cam_cpas_subdev_cmd(struct cam_cpas_intf *cpas_intf,
break;
}
+ case CAM_SD_SHUTDOWN:
+ break;
default:
pr_err("Unknown op code %d for CPAS\n", cmd->op_code);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
index 4cebb58..4c819cf 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
@@ -72,6 +72,7 @@ static int cam_isp_dev_probe(struct platform_device *pdev)
}
node = (struct cam_node *) g_isp_dev.sd.token;
+ memset(&hw_mgr_intf, 0, sizeof(hw_mgr_intf));
rc = cam_isp_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf);
if (rc != 0) {
pr_err("%s: Can not initialized ISP HW manager!\n", __func__);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 5e629b6..92a17d8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -1581,6 +1581,7 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t evt_id,
struct cam_vfe_bus_ver2_priv *bus_priv;
struct cam_irq_controller_reg_info *reg_info;
uint32_t irq_mask;
+ int found = 0;
handler_priv = th_payload->handler_priv;
core_info = handler_priv->core_info;
@@ -1613,6 +1614,8 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t evt_id,
irq_reg_offset[i] - (0xC * 2));
evt_payload->irq_reg_val[i] = irq_mask &
cam_io_r(handler_priv->mem_base + irq_reg_offset[i]);
+ if (evt_payload->irq_reg_val[i])
+ found = 1;
CDBG("irq_status%d = 0x%x\n", i, evt_payload->irq_reg_val[i]);
}
for (i = 0; i <= CAM_IFE_IRQ_BUS_REG_STATUS2; i++) {
@@ -1628,7 +1631,13 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t evt_id,
reg_info->global_clear_bitmask,
reg_info->global_clear_offset);
- th_payload->evt_payload_priv = evt_payload;
+ if (found)
+ th_payload->evt_payload_priv = evt_payload;
+ else {
+ cam_vfe_bus_put_evt_payload(evt_payload->core_info,
+ &evt_payload);
+ rc = -ENOMSG;
+ }
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index 1a8356a..c495088 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -20,6 +20,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/cam_req_mgr.h>
+#include <media/cam_defs.h>
#include "cam_req_mgr_dev.h"
#include "cam_req_mgr_util.h"
#include "cam_req_mgr_core.h"
@@ -151,6 +152,9 @@ static unsigned int cam_req_mgr_poll(struct file *f,
static int cam_req_mgr_close(struct file *filep)
{
+ struct v4l2_subdev *sd;
+ struct cam_control cam_ctrl;
+
mutex_lock(&g_dev.cam_lock);
if (g_dev.open_cnt <= 0) {
@@ -158,6 +162,14 @@ static int cam_req_mgr_close(struct file *filep)
return -EINVAL;
}
+ cam_ctrl.op_code = CAM_SD_SHUTDOWN;
+ list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) {
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ continue;
+ v4l2_subdev_call(sd, core, ioctl, VIDIOC_CAM_CONTROL,
+ &cam_ctrl);
+ }
+
g_dev.open_cnt--;
v4l2_fh_release(filep);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index 648617e..91b68cf 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -616,6 +616,8 @@ int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl,
}
}
break;
+ case CAM_SD_SHUTDOWN:
+ break;
default:
pr_err("%s:%d Invalid Opcode %d\n",
__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
index 789522d..6764b8a 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
@@ -34,6 +34,8 @@ static long cam_cci_subdev_ioctl(struct v4l2_subdev *sd,
case VIDIOC_MSM_CCI_CFG:
rc = cam_cci_core_cfg(sd, arg);
break;
+ case VIDIOC_CAM_CONTROL:
+ break;
default:
pr_err("%s:%d Invalid ioctl cmd: %d\n",
__func__, __LINE__, cmd);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index 8dc65f5..6751fdd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -486,6 +486,8 @@ int32_t cam_csiphy_core_cfg(void *phy_dev,
}
}
break;
+ case CAM_SD_SHUTDOWN:
+ break;
default:
pr_err("%s:%d :Error: Invalid Opcode: %d\n",
__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index 4888e5b..4fc3aa1 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -773,6 +773,8 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl,
}
}
break;
+ case CAM_SD_SHUTDOWN:
+ break;
default:
pr_err("%s:%d :Error: Invalid Opcode: %d\n",
__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
index d1bbe01..ff6b72a 100644
--- a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
@@ -92,17 +92,14 @@
/**
* @INVALID: Invalid state
- * @FW_LOAD_DONE: Firmware load is completed
- * @FW_RESP_DONE: Firmware response is received
- * @FW_START_SENT: firmware start is send
- * @FW_READY: firmware is ready to accept commands
+ * @HFI_DEINIT: HFI is not initialized yet
+ * @HFI_INIT: HFI is initialized
+ * @HFI_READY: HFI is ready to send/receive commands/messages
*/
enum hfi_state {
- INVALID,
- FW_LOAD_DONE,
- FW_RESP_DONE,
- FW_START_SENT,
- FW_READY
+ HFI_DEINIT,
+ HFI_INIT,
+ HFI_READY
};
/**
@@ -292,6 +289,9 @@ struct hfi_qtbl {
* @msgpacket_buf: message buffer
* @hfi_state: State machine for hfi
* @cmd_q_lock: Lock for command queue
+ * @cmd_q_state: State of command queue
+ * @mutex msg_q_lock: Lock for message queue
+ * @msg_q_state: State of message queue
* @csr_base: CSR base address
*/
struct hfi_info {
@@ -301,7 +301,9 @@ struct hfi_info {
uint32_t msgpacket_buf[ICP_HFI_MAX_MSG_SIZE_IN_WORDS];
uint8_t hfi_state;
struct mutex cmd_q_lock;
+ bool cmd_q_state;
struct mutex msg_q_lock;
+ bool msg_q_state;
void __iomem *csr_base;
};
diff --git a/drivers/media/platform/msm/camera/icp/hfi.c b/drivers/media/platform/msm/camera/icp/hfi.c
index 15e0315..170c8cf 100644
--- a/drivers/media/platform/msm/camera/icp/hfi.c
+++ b/drivers/media/platform/msm/camera/icp/hfi.c
@@ -42,7 +42,7 @@
#undef HFI_DBG
#define HFI_DBG(fmt, args...) pr_debug(fmt, ##args)
-struct hfi_info *g_hfi;
+static struct hfi_info *g_hfi;
unsigned int g_icp_mmu_hdl;
int hfi_write_cmd(void *cmd_ptr)
@@ -59,12 +59,17 @@ int hfi_write_cmd(void *cmd_ptr)
return -EINVAL;
}
- if (!g_hfi || g_hfi->hfi_state < FW_START_SENT) {
- pr_err("FW not ready yet\n");
+ if (!g_hfi || (g_hfi->hfi_state != HFI_READY)) {
+ pr_err("HFI interface not ready yet\n");
return -EIO;
}
mutex_lock(&g_hfi->cmd_q_lock);
+ if (!g_hfi->cmd_q_state) {
+ pr_err("HFI command interface not ready yet\n");
+ mutex_unlock(&g_hfi->cmd_q_lock);
+ return -EIO;
+ }
q_tbl = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
q = &q_tbl->q_hdr[Q_CMD];
@@ -78,11 +83,10 @@ int hfi_write_cmd(void *cmd_ptr)
goto err;
}
- HFI_DBG("size_in_words : %u\n", size_in_words);
- HFI_DBG("q->qhdr_write_idx %x\n", q->qhdr_write_idx);
+ HFI_DBG("size_in_words : %u, q->qhdr_write_idx %x\n", size_in_words,
+ q->qhdr_write_idx);
read_idx = q->qhdr_read_idx;
-
empty_space = (q->qhdr_write_idx >= read_idx) ?
(q->qhdr_q_size - (q->qhdr_write_idx - read_idx)) :
(read_idx - q->qhdr_write_idx);
@@ -115,7 +119,7 @@ int hfi_write_cmd(void *cmd_ptr)
g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT);
err:
mutex_unlock(&g_hfi->cmd_q_lock);
- return 0;
+ return rc;
}
int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
@@ -132,21 +136,28 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
return -EINVAL;
}
+ if (!g_hfi || (g_hfi->hfi_state != HFI_READY)) {
+ pr_err("HFI interface not ready yet\n");
+ return -EIO;
+ }
+
q_tbl_ptr = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
q = &q_tbl_ptr->q_hdr[q_id];
- if ((g_hfi->hfi_state < FW_START_SENT) ||
- (q->qhdr_read_idx == q->qhdr_write_idx)) {
+ if (q->qhdr_read_idx == q->qhdr_write_idx) {
pr_debug("FW or Q not ready, hfi state : %u, r idx : %u, w idx : %u\n",
g_hfi->hfi_state, q->qhdr_read_idx, q->qhdr_write_idx);
return -EIO;
}
mutex_lock(&g_hfi->msg_q_lock);
+ if (!g_hfi->msg_q_state) {
+ pr_err("HFI message interface not ready yet\n");
+ mutex_unlock(&g_hfi->msg_q_lock);
+ return -EIO;
+ }
- if (q_id == Q_CMD)
- read_q = (uint32_t *)g_hfi->map.cmd_q.kva;
- else if (q_id == Q_MSG)
+ if (q_id == Q_MSG)
read_q = (uint32_t *)g_hfi->map.msg_q.kva;
else
read_q = (uint32_t *)g_hfi->map.dbg_q.kva;
@@ -154,8 +165,8 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
read_ptr = (uint32_t *)(read_q + q->qhdr_read_idx);
size_in_words = (*read_ptr) >> BYTE_WORD_SHIFT;
- HFI_DBG("size_in_words : %u\n", size_in_words);
- HFI_DBG("read_ptr : %pK\n", (void *)read_ptr);
+ HFI_DBG("size_in_words : %u, read_ptr : %pK\n", size_in_words,
+ (void *)read_ptr);
if ((size_in_words == 0) ||
(size_in_words > ICP_HFI_MAX_MSG_SIZE_IN_WORDS)) {
@@ -180,13 +191,12 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
}
for (i = 0; i < size_in_words; i++)
- pr_debug("%x\n", read_ptr[i]);
+ HFI_DBG("%x\n", read_ptr[i]);
q->qhdr_read_idx = new_read_idx;
err:
mutex_unlock(&g_hfi->msg_q_lock);
- HFI_DBG("Exit\n");
- return 0;
+ return rc;
}
void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
@@ -307,8 +317,7 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
struct hfi_qtbl *qtbl;
struct hfi_qtbl_hdr *qtbl_hdr;
struct hfi_q_hdr *cmd_q_hdr, *msg_q_hdr, *dbg_q_hdr;
- uint32_t hw_version, fw_version;
- uint32_t status;
+ uint32_t hw_version, fw_version, status = 0;
if (!g_hfi) {
g_hfi = kzalloc(sizeof(struct hfi_info), GFP_KERNEL);
@@ -318,13 +327,12 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
}
}
- pr_debug("g_hfi: %pK\n", (void *)g_hfi);
- if (g_hfi->hfi_state != INVALID) {
+ HFI_DBG("g_hfi: %pK\n", (void *)g_hfi);
+ if (g_hfi->hfi_state != HFI_DEINIT) {
pr_err("hfi_init: invalid state\n");
return -EINVAL;
}
- g_hfi->hfi_state = FW_LOAD_DONE;
memcpy(&g_hfi->map, hfi_mem, sizeof(g_hfi->map));
if (debug) {
@@ -342,11 +350,6 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
}
- mutex_init(&g_hfi->cmd_q_lock);
- mutex_init(&g_hfi->msg_q_lock);
-
- g_hfi->csr_base = icp_base;
-
qtbl = (struct hfi_qtbl *)hfi_mem->qtbl.kva;
qtbl_hdr = &qtbl->q_tbl_hdr;
qtbl_hdr->qtbl_version = 0xFFFFFFFF;
@@ -474,7 +477,7 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
icp_base + HFI_REG_HOST_ICP_INIT_REQUEST);
hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);
- pr_debug("hw version : %u[%x]\n", hw_version, hw_version);
+ HFI_DBG("hw version : [%x]\n", hw_version);
rc = readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
status, status != ICP_INIT_RESP_SUCCESS, 15, 200);
@@ -484,14 +487,19 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
}
fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
- g_hfi->hfi_state = FW_START_SENT;
-
HFI_DBG("fw version : %u[%x]\n", fw_version, fw_version);
+
+ g_hfi->csr_base = icp_base;
+ g_hfi->hfi_state = HFI_READY;
+ g_hfi->cmd_q_state = true;
+ g_hfi->msg_q_state = true;
+ mutex_init(&g_hfi->cmd_q_lock);
+ mutex_init(&g_hfi->msg_q_lock);
cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
return rc;
regions_fail:
- kzfree(g_hfi);
+ kfree(g_hfi);
alloc_fail:
return rc;
}
@@ -499,6 +507,24 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
void cam_hfi_deinit(void)
{
+ if (!g_hfi) {
+ pr_err("hfi path not established yet\n");
+ return;
+ }
+ cam_io_w((uint32_t)INTR_DISABLE,
+ g_hfi->csr_base + HFI_REG_A5_CSR_A2HOSTINTEN);
+
+ mutex_lock(&g_hfi->cmd_q_lock);
+ g_hfi->cmd_q_state = false;
+ mutex_unlock(&g_hfi->cmd_q_lock);
+
+ mutex_lock(&g_hfi->msg_q_lock);
+ g_hfi->msg_q_state = false;
+ mutex_unlock(&g_hfi->msg_q_lock);
+
+ mutex_destroy(&g_hfi->cmd_q_lock);
+ mutex_destroy(&g_hfi->msg_q_lock);
+
kfree(g_hfi);
g_hfi = NULL;
}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
index f562bb9..39eacd8 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
@@ -278,19 +278,22 @@ int cam_a5_init_hw(void *device_priv,
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
- if (rc < 0) {
+ if (rc) {
pr_err("cpass start failed: %d\n", rc);
return rc;
}
+ core_info->cpas_start = true;
rc = cam_a5_enable_soc_resources(soc_info);
- if (rc < 0) {
- pr_err("soc enable is failed\n");
- rc = cam_cpas_stop(core_info->cpas_handle);
- return rc;
+ if (rc) {
+ pr_err("soc enable is failed: %d\n", rc);
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
}
- return 0;
+ return rc;
}
int cam_a5_deinit_hw(void *device_priv,
@@ -314,14 +317,17 @@ int cam_a5_deinit_hw(void *device_priv,
}
rc = cam_a5_disable_soc_resources(soc_info);
- if (rc < 0)
- pr_err("soc enable is failed\n");
+ if (rc)
+ pr_err("soc disable is failed: %d\n", rc);
- rc = cam_cpas_stop(core_info->cpas_handle);
- if (rc < 0)
- pr_err("cpas stop is failed: %d\n", rc);
+ if (core_info->cpas_start) {
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
+ }
- return 0;
+ return rc;
}
irqreturn_t cam_a5_irq(int irq_num, void *data)
@@ -443,13 +449,20 @@ int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
return -EINVAL;
}
- rc = cam_cpas_start(core_info->cpas_handle,
- &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ if (!core_info->cpas_start) {
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote,
+ &cpas_vote->axi_vote);
+ core_info->cpas_start = true;
+ }
break;
}
case CAM_ICP_A5_CMD_CPAS_STOP:
- cam_cpas_stop(core_info->cpas_handle);
+ if (core_info->cpas_start) {
+ cam_cpas_stop(core_info->cpas_handle);
+ core_info->cpas_start = false;
+ }
break;
default:
break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
index 8b84270..4aa6b4b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
@@ -62,6 +62,7 @@ struct cam_a5_device_hw_info {
* @a5_acquire: Acquire information of A5
* @irq_cb: IRQ callback
* @cpas_handle: CPAS handle for A5
+ * @cpast_start: state variable for cpas
*/
struct cam_a5_device_core_info {
struct cam_a5_device_hw_info *a5_hw_info;
@@ -74,6 +75,7 @@ struct cam_a5_device_core_info {
struct cam_icp_a5_acquire_dev a5_acquire[8];
struct cam_icp_a5_set_irq_cb irq_cb;
uint32_t cpas_handle;
+ bool cpas_start;
};
int cam_a5_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
index 641c154..d12b3b6 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
@@ -95,7 +95,7 @@ int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info)
rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
if (rc)
- pr_err("%s: enable platform failed\n", __func__);
+ pr_err("%s: disable platform failed\n", __func__);
return rc;
}
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
index 50863a5..91652d7 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
@@ -80,15 +80,19 @@ int cam_bps_init_hw(void *device_priv,
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
- if (rc < 0) {
+ if (rc) {
pr_err("cpass start failed: %d\n", rc);
return rc;
}
+ core_info->cpas_start = true;
rc = cam_bps_enable_soc_resources(soc_info);
- if (rc < 0) {
- pr_err("soc enable is failed\n");
- rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc) {
+ pr_err("soc enable is failed: %d\n", rc);
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
}
return rc;
@@ -115,12 +119,15 @@ int cam_bps_deinit_hw(void *device_priv,
}
rc = cam_bps_disable_soc_resources(soc_info);
- if (rc < 0)
- pr_err("soc enable is failed\n");
+ if (rc)
+ pr_err("soc disable is failed: %d\n", rc);
- rc = cam_cpas_stop(core_info->cpas_handle);
- if (rc < 0)
- pr_err("cpas stop is failed: %d\n", rc);
+ if (core_info->cpas_start) {
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
+ }
return rc;
}
@@ -169,13 +176,20 @@ int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type,
return -EINVAL;
}
- rc = cam_cpas_start(core_info->cpas_handle,
- &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ if (!core_info->cpas_start) {
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote,
+ &cpas_vote->axi_vote);
+ core_info->cpas_start = true;
+ }
break;
}
case CAM_ICP_BPS_CMD_CPAS_STOP:
- cam_cpas_stop(core_info->cpas_handle);
+ if (core_info->cpas_start) {
+ cam_cpas_stop(core_info->cpas_handle);
+ core_info->cpas_start = false;
+ }
break;
default:
break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
index 67e1c03..8a15a7b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
@@ -26,6 +26,7 @@ struct cam_bps_device_hw_info {
struct cam_bps_device_core_info {
struct cam_bps_device_hw_info *bps_hw_info;
uint32_t cpas_handle;
+ bool cpas_start;
};
int cam_bps_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 43491a9..677c24e 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -74,166 +74,6 @@ static int cam_icp_hw_mgr_create_debugfs_entry(void)
return 0;
}
-static int cam_icp_stop_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
-{
- struct cam_hw_intf *a5_dev_intf = NULL;
- struct cam_hw_intf *ipe0_dev_intf = NULL;
- struct cam_hw_intf *ipe1_dev_intf = NULL;
- struct cam_hw_intf *bps_dev_intf = NULL;
- struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
- struct cam_icp_cpas_vote cpas_vote;
- int rc = 0;
-
- if (!hw_mgr) {
- pr_err("Invalid params\n");
- return -EINVAL;
- }
-
- a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
- bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
- ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
-
- if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
- pr_err("dev intfs are NULL\n");
- return -EINVAL;
- }
-
- rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0)
- pr_err("CAM_ICP_A5_CMD_CPAS_STOP is failed: %d\n", rc);
-
- rc = bps_dev_intf->hw_ops.process_cmd(
- bps_dev_intf->hw_priv,
- CAM_ICP_BPS_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0)
- pr_err("CAM_ICP_BPS_CMD_CPAS_STOP is failed: %d\n", rc);
-
- rc = ipe0_dev_intf->hw_ops.process_cmd(
- ipe0_dev_intf->hw_priv,
- CAM_ICP_IPE_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0)
- pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
-
- ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
- if (!ipe1_dev_intf)
- return rc;
-
- rc = ipe1_dev_intf->hw_ops.process_cmd(
- ipe1_dev_intf->hw_priv,
- CAM_ICP_IPE_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0)
- pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
-
- return rc;
-}
-
-static int cam_icp_start_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
-{
- struct cam_hw_intf *a5_dev_intf = NULL;
- struct cam_hw_intf *ipe0_dev_intf = NULL;
- struct cam_hw_intf *ipe1_dev_intf = NULL;
- struct cam_hw_intf *bps_dev_intf = NULL;
- struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
- struct cam_icp_cpas_vote cpas_vote;
- int rc = 0;
-
- if (!hw_mgr) {
- pr_err("Invalid params\n");
- return -EINVAL;
- }
-
- a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
- bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
- ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
-
- if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
- pr_err("dev intfs are null\n");
- return -EINVAL;
- }
-
- cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
- cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
- cpas_vote.axi_vote.compressed_bw = 640000000;
- cpas_vote.axi_vote.uncompressed_bw = 640000000;
-
- rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_CMD_CPAS_START,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc) {
- pr_err("CAM_ICP_A5_CMD_CPAS_START is failed: %d\n", rc);
- goto a5_cpas_start_failed;
- }
-
- rc = bps_dev_intf->hw_ops.process_cmd(
- bps_dev_intf->hw_priv,
- CAM_ICP_BPS_CMD_CPAS_START,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0) {
- pr_err("CAM_ICP_BPS_CMD_CPAS_START is failed: %d\n", rc);
- goto bps_cpas_start_failed;
- }
-
- rc = ipe0_dev_intf->hw_ops.process_cmd(
- ipe0_dev_intf->hw_priv,
- CAM_ICP_IPE_CMD_CPAS_START,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0) {
- pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
- goto ipe0_cpas_start_failed;
- }
-
- ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
- if (!ipe1_dev_intf)
- return rc;
-
- rc = ipe1_dev_intf->hw_ops.process_cmd(
- ipe1_dev_intf->hw_priv,
- CAM_ICP_IPE_CMD_CPAS_START,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
- if (rc < 0) {
- pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
- goto ipe1_cpas_start_failed;
- }
-
- return rc;
-
-ipe1_cpas_start_failed:
- rc = ipe0_dev_intf->hw_ops.process_cmd(
- ipe0_dev_intf->hw_priv,
- CAM_ICP_IPE_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
-ipe0_cpas_start_failed:
- rc = bps_dev_intf->hw_ops.process_cmd(
- bps_dev_intf->hw_priv,
- CAM_ICP_BPS_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
-bps_cpas_start_failed:
- rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_CMD_CPAS_STOP,
- &cpas_vote,
- sizeof(struct cam_icp_cpas_vote));
-a5_cpas_start_failed:
- return rc;
-}
-
static int cam_icp_mgr_process_cmd(void *priv, void *data)
{
int rc;
@@ -566,7 +406,7 @@ int32_t cam_icp_hw_mgr_cb(uint32_t irq_status, void *data)
return rc;
}
-static int cam_icp_free_hfi_mem(void)
+static void cam_icp_free_hfi_mem(void)
{
cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl);
cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl);
@@ -574,8 +414,6 @@ static int cam_icp_free_hfi_mem(void)
cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q);
cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q);
cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.sec_heap);
-
- return 0;
}
static int cam_icp_allocate_hfi_mem(void)
@@ -806,18 +644,17 @@ static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id)
mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
if (!hw_mgr->ctx_data[ctx_id].in_use) {
- pr_err("ctx is already in use: %d\n", ctx_id);
+ ICP_DBG("ctx is not in use: %d\n", ctx_id);
mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
- return -EINVAL;
+ return 0;
}
mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
- mutex_lock(&hw_mgr->hw_mgr_mutex);
task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
if (task)
cam_icp_mgr_destroy_handle(&hw_mgr->ctx_data[ctx_id], task);
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
hw_mgr->ctx_data[ctx_id].in_use = 0;
hw_mgr->ctx_data[ctx_id].fw_handle = 0;
@@ -829,6 +666,7 @@ static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id)
mutex_destroy(&hw_mgr->ctx_data[ctx_id].hfi_frame_process.lock);
mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
return 0;
}
@@ -861,40 +699,64 @@ static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args)
struct cam_hw_intf *ipe0_dev_intf = NULL;
struct cam_hw_intf *ipe1_dev_intf = NULL;
struct cam_hw_intf *bps_dev_intf = NULL;
- int rc = 0;
+ struct cam_icp_a5_set_irq_cb irq_cb;
+ struct cam_icp_a5_set_fw_buf_info fw_buf_info;
+ struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ int i;
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ if (hw_mgr->fw_download == false) {
+ ICP_DBG("hw mgr is already closed\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return 0;
+ }
a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) {
- pr_err("dev intfs are wrong\n");
- return rc;
+ pr_err("dev intfs are wrong, failed to close\n");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ return -EINVAL;
}
+
+ irq_cb.icp_hw_mgr_cb = NULL;
+ irq_cb.data = NULL;
+ a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_SET_IRQ_CB,
+ &irq_cb, sizeof(irq_cb));
+
+ fw_buf_info.kva = 0;
+ fw_buf_info.iova = 0;
+ fw_buf_info.len = 0;
+ a5_dev_intf->hw_ops.process_cmd(
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_SET_FW_BUF,
+ &fw_buf_info,
+ sizeof(fw_buf_info));
+
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
+ ctx_data = &hw_mgr->ctx_data[i];
+ cam_icp_mgr_release_ctx(hw_mgr, i);
+ }
+
mutex_lock(&hw_mgr->hw_mgr_mutex);
- rc = a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
- if (rc < 0)
- pr_err("a5 dev de-init failed\n");
-
- rc = bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
- if (rc < 0)
- pr_err("bps dev de-init failed\n");
-
- rc = ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
- if (rc < 0)
- pr_err("ipe0 dev de-init failed\n");
-
ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
- if (ipe1_dev_intf) {
- rc = ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
- NULL, 0);
- if (rc < 0)
- pr_err("ipe1 dev de-init failed\n");
- }
+ if (ipe1_dev_intf)
+ ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
+ NULL, 0);
+ ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+ bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+ a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
+ cam_hfi_deinit();
cam_icp_free_hfi_mem();
hw_mgr->fw_download = false;
- debugfs_remove_recursive(icp_hw_mgr.dentry);
mutex_unlock(&hw_mgr->hw_mgr_mutex);
return 0;
@@ -975,9 +837,9 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
irq_cb.icp_hw_mgr_cb = cam_icp_hw_mgr_cb;
irq_cb.data = hw_mgr_priv;
rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_SET_IRQ_CB,
- &irq_cb, sizeof(irq_cb));
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_SET_IRQ_CB,
+ &irq_cb, sizeof(irq_cb));
if (rc < 0) {
pr_err("CAM_ICP_A5_SET_IRQ_CB failed\n");
rc = -EINVAL;
@@ -989,10 +851,10 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
fw_buf_info.len = icp_hw_mgr.hfi_mem.fw_buf.len;
rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_CMD_SET_FW_BUF,
- &fw_buf_info,
- sizeof(fw_buf_info));
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_SET_FW_BUF,
+ &fw_buf_info,
+ sizeof(fw_buf_info));
if (rc < 0) {
pr_err("CAM_ICP_A5_CMD_SET_FW_BUF failed\n");
goto set_irq_failed;
@@ -1001,9 +863,9 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
cam_hfi_enable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
rc = a5_dev_intf->hw_ops.process_cmd(
- a5_dev_intf->hw_priv,
- CAM_ICP_A5_CMD_FW_DOWNLOAD,
- NULL, 0);
+ a5_dev_intf->hw_priv,
+ CAM_ICP_A5_CMD_FW_DOWNLOAD,
+ NULL, 0);
if (rc < 0) {
pr_err("FW download is failed\n");
goto set_irq_failed;
@@ -1083,14 +945,8 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
}
hw_mgr->fw_download = true;
-
- rc = cam_icp_stop_cpas(hw_mgr);
- if (rc) {
- pr_err("cpas stop failed\n");
- goto set_irq_failed;
- }
-
hw_mgr->ctxt_cnt = 0;
+ ICP_DBG("FW download done successfully\n");
return rc;
@@ -1443,20 +1299,11 @@ static int cam_icp_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args)
mutex_unlock(&hw_mgr->hw_mgr_mutex);
return -EINVAL;
}
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
rc = cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
return -EINVAL;
- }
-
- --hw_mgr->ctxt_cnt;
- if (!hw_mgr->ctxt_cnt) {
- ICP_DBG("stop cpas for last context\n");
- cam_icp_stop_cpas(hw_mgr);
- }
- ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
ICP_DBG("fw handle %d\n", fw_handle);
return rc;
@@ -1662,13 +1509,6 @@ static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args)
/* Fill ctx with acquire info */
ctx_data = &hw_mgr->ctx_data[ctx_id];
-
- if (!hw_mgr->ctxt_cnt++) {
- ICP_DBG("starting cpas\n");
- cam_icp_start_cpas(hw_mgr);
- }
- ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
-
mutex_unlock(&hw_mgr->hw_mgr_mutex);
/* Fill ctx with acquire info */
@@ -1782,9 +1622,6 @@ static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args)
create_handle_failed:
get_create_task_failed:
cmd_cpu_buf_failed:
- --hw_mgr->ctxt_cnt;
- if (!hw_mgr->ctxt_cnt)
- cam_icp_stop_cpas(hw_mgr);
cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
kfree(tmp_acquire);
return rc;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
index 15cb943..07f63d2 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
@@ -43,7 +43,7 @@ static int cam_ipe_caps_vote(struct cam_ipe_device_core_info *core_info,
rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
&cpas_vote->axi_vote);
- if (rc < 0)
+ if (rc)
pr_err("cpas vote is failed: %d\n", rc);
return rc;
@@ -78,15 +78,19 @@ int cam_ipe_init_hw(void *device_priv,
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
- if (rc < 0) {
+ if (rc) {
pr_err("cpass start failed: %d\n", rc);
return rc;
}
+ core_info->cpas_start = true;
rc = cam_ipe_enable_soc_resources(soc_info);
- if (rc < 0) {
- pr_err("soc enable is failed\n");
- rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc) {
+ pr_err("soc enable is failed : %d\n", rc);
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
}
return rc;
@@ -113,12 +117,15 @@ int cam_ipe_deinit_hw(void *device_priv,
}
rc = cam_ipe_disable_soc_resources(soc_info);
- if (rc < 0)
- pr_err("soc enable is failed\n");
+ if (rc)
+ pr_err("soc disable is failed : %d\n", rc);
- rc = cam_cpas_stop(core_info->cpas_handle);
- if (rc < 0)
- pr_err("cpas stop is failed: %d\n", rc);
+ if (core_info->cpas_start) {
+ if (cam_cpas_stop(core_info->cpas_handle))
+ pr_err("cpas stop is failed\n");
+ else
+ core_info->cpas_start = false;
+ }
return rc;
}
@@ -163,13 +170,19 @@ int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type,
if (!cmd_args)
return -EINVAL;
- rc = cam_cpas_start(core_info->cpas_handle,
- &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ if (!core_info->cpas_start) {
+ rc = cam_cpas_start(core_info->cpas_handle,
+ &cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+ core_info->cpas_start = true;
+ }
break;
}
case CAM_ICP_IPE_CMD_CPAS_STOP:
- cam_cpas_stop(core_info->cpas_handle);
+ if (core_info->cpas_start) {
+ cam_cpas_stop(core_info->cpas_handle);
+ core_info->cpas_start = false;
+ }
break;
default:
break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
index 4818846..8f0e882 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
@@ -26,6 +26,7 @@ struct cam_ipe_device_hw_info {
struct cam_ipe_device_core_info {
struct cam_ipe_device_hw_info *ipe_hw_info;
uint32_t cpas_handle;
+ bool cpas_start;
};
int cam_ipe_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 44a29aa..a850bc0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -1021,6 +1021,7 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
{
int i, size, ret = 0;
char name[32];
+ struct sched_param param = { .sched_priority = 5 };
size = sizeof(struct sde_rot_queue) * mgr->queue_count;
mgr->commitq = devm_kzalloc(mgr->device, size, GFP_KERNEL);
@@ -1031,11 +1032,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
snprintf(name, sizeof(name), "rot_commitq_%d_%d",
mgr->device->id, i);
SDEROT_DBG("work queue name=%s\n", name);
- mgr->commitq[i].rot_work_queue =
- alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!mgr->commitq[i].rot_work_queue) {
+ kthread_init_worker(&mgr->commitq[i].rot_kw);
+ mgr->commitq[i].rot_thread = kthread_run(kthread_worker_fn,
+ &mgr->commitq[i].rot_kw, name);
+ if (IS_ERR(mgr->commitq[i].rot_thread)) {
ret = -EPERM;
+ mgr->commitq[i].rot_thread = NULL;
+ break;
+ }
+
+ ret = sched_setscheduler(mgr->commitq[i].rot_thread,
+ SCHED_FIFO, ¶m);
+ if (ret) {
+ SDEROT_ERR(
+ "failed to set kthread priority for commitq %d\n",
+ ret);
break;
}
@@ -1052,10 +1063,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
snprintf(name, sizeof(name), "rot_doneq_%d_%d",
mgr->device->id, i);
SDEROT_DBG("work queue name=%s\n", name);
- mgr->doneq[i].rot_work_queue = alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!mgr->doneq[i].rot_work_queue) {
+ kthread_init_worker(&mgr->doneq[i].rot_kw);
+ mgr->doneq[i].rot_thread = kthread_run(kthread_worker_fn,
+ &mgr->doneq[i].rot_kw, name);
+ if (IS_ERR(mgr->doneq[i].rot_thread)) {
ret = -EPERM;
+ mgr->doneq[i].rot_thread = NULL;
+ break;
+ }
+
+ ret = sched_setscheduler(mgr->doneq[i].rot_thread,
+ SCHED_FIFO, ¶m);
+ if (ret) {
+ SDEROT_ERR(
+ "failed to set kthread priority for doneq %d\n",
+ ret);
break;
}
@@ -1071,18 +1093,20 @@ static void sde_rotator_deinit_queue(struct sde_rot_mgr *mgr)
if (mgr->commitq) {
for (i = 0; i < mgr->queue_count; i++) {
- if (mgr->commitq[i].rot_work_queue)
- destroy_workqueue(
- mgr->commitq[i].rot_work_queue);
+ if (mgr->commitq[i].rot_thread) {
+ kthread_flush_worker(&mgr->commitq[i].rot_kw);
+ kthread_stop(mgr->commitq[i].rot_thread);
+ }
}
devm_kfree(mgr->device, mgr->commitq);
mgr->commitq = NULL;
}
if (mgr->doneq) {
for (i = 0; i < mgr->queue_count; i++) {
- if (mgr->doneq[i].rot_work_queue)
- destroy_workqueue(
- mgr->doneq[i].rot_work_queue);
+ if (mgr->doneq[i].rot_thread) {
+ kthread_flush_worker(&mgr->doneq[i].rot_kw);
+ kthread_stop(mgr->doneq[i].rot_thread);
+ }
}
devm_kfree(mgr->device, mgr->doneq);
mgr->doneq = NULL;
@@ -1203,7 +1227,7 @@ void sde_rotator_queue_request(struct sde_rot_mgr *mgr,
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_QUEUE] = ktime_get();
- queue_work(queue->rot_work_queue, &entry->commit_work);
+ kthread_queue_work(&queue->rot_kw, &entry->commit_work);
}
}
@@ -1423,12 +1447,13 @@ static void sde_rotator_release_entry(struct sde_rot_mgr *mgr,
*
* Note this asynchronous handler is protected by hal lock.
*/
-static void sde_rotator_commit_handler(struct work_struct *work)
+static void sde_rotator_commit_handler(struct kthread_work *work)
{
struct sde_rot_entry *entry;
struct sde_rot_entry_container *request;
struct sde_rot_hw_resource *hw;
struct sde_rot_mgr *mgr;
+ struct sched_param param = { .sched_priority = 5 };
int ret;
entry = container_of(work, struct sde_rot_entry, commit_work);
@@ -1439,6 +1464,12 @@ static void sde_rotator_commit_handler(struct work_struct *work)
return;
}
+ ret = sched_setscheduler(entry->fenceq->rot_thread, SCHED_FIFO, ¶m);
+ if (ret) {
+ SDEROT_WARN("Fail to set kthread priority for fenceq: %d\n",
+ ret);
+ }
+
mgr = entry->private->mgr;
SDEROT_EVTLOG(
@@ -1514,7 +1545,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
SDEROT_EVTLOG(entry->item.session_id, 1);
- queue_work(entry->doneq->rot_work_queue, &entry->done_work);
+ kthread_queue_work(&entry->doneq->rot_kw, &entry->done_work);
sde_rot_mgr_unlock(mgr);
return;
error:
@@ -1526,8 +1557,8 @@ static void sde_rotator_commit_handler(struct work_struct *work)
sde_rotator_release_entry(mgr, entry);
atomic_dec(&request->pending_count);
atomic_inc(&request->failed_count);
- if (request->retireq && request->retire_work)
- queue_work(request->retireq, request->retire_work);
+ if (request->retire_kw && request->retire_work)
+ kthread_queue_work(request->retire_kw, request->retire_work);
sde_rot_mgr_unlock(mgr);
}
@@ -1541,7 +1572,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
*
* Note this asynchronous handler is protected by hal lock.
*/
-static void sde_rotator_done_handler(struct work_struct *work)
+static void sde_rotator_done_handler(struct kthread_work *work)
{
struct sde_rot_entry *entry;
struct sde_rot_entry_container *request;
@@ -1606,8 +1637,8 @@ static void sde_rotator_done_handler(struct work_struct *work)
ATRACE_INT("sde_rot_done", 1);
sde_rotator_release_entry(mgr, entry);
atomic_dec(&request->pending_count);
- if (request->retireq && request->retire_work)
- queue_work(request->retireq, request->retire_work);
+ if (request->retire_kw && request->retire_work)
+ kthread_queue_work(request->retire_kw, request->retire_work);
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_RETIRE] = ktime_get();
sde_rot_mgr_unlock(mgr);
@@ -1966,8 +1997,10 @@ static int sde_rotator_add_request(struct sde_rot_mgr *mgr,
entry->request = req;
- INIT_WORK(&entry->commit_work, sde_rotator_commit_handler);
- INIT_WORK(&entry->done_work, sde_rotator_done_handler);
+ kthread_init_work(&entry->commit_work,
+ sde_rotator_commit_handler);
+ kthread_init_work(&entry->done_work,
+ sde_rotator_done_handler);
SDEROT_DBG(
"Entry added. wbidx=%u, src{%u,%u,%u,%u}f=%x dst{%u,%u,%u,%u}f=%x session_id=%u\n",
item->wb_idx,
@@ -2016,8 +2049,8 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr,
sde_rot_mgr_unlock(mgr);
for (i = req->count - 1; i >= 0; i--) {
entry = req->entries + i;
- cancel_work_sync(&entry->commit_work);
- cancel_work_sync(&entry->done_work);
+ kthread_cancel_work_sync(&entry->commit_work);
+ kthread_cancel_work_sync(&entry->done_work);
}
sde_rot_mgr_lock(mgr);
SDEROT_DBG("cancel work done\n");
@@ -2134,7 +2167,7 @@ void sde_rotator_commit_request(struct sde_rot_mgr *mgr,
sde_rot_mgr_unlock(mgr);
for (i = 0; i < req->count; i++)
- flush_work(&req->entries[i].commit_work);
+ kthread_flush_work(&req->entries[i].commit_work);
sde_rot_mgr_lock(mgr);
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 7b8a066..731ff1e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -21,7 +21,7 @@
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/pm_runtime.h>
-#include <linux/completion.h>
+#include <linux/kthread.h>
#include "sde_rotator_base.h"
#include "sde_rotator_util.h"
@@ -230,7 +230,8 @@ struct sde_rot_hw_resource {
};
struct sde_rot_queue {
- struct workqueue_struct *rot_work_queue;
+ struct kthread_worker rot_kw;
+ struct task_struct *rot_thread;
struct sde_rot_timeline *timeline;
struct sde_rot_hw_resource *hw;
};
@@ -253,8 +254,8 @@ struct sde_rot_entry_container {
u32 count;
atomic_t pending_count;
atomic_t failed_count;
- struct workqueue_struct *retireq;
- struct work_struct *retire_work;
+ struct kthread_worker *retire_kw;
+ struct kthread_work *retire_work;
bool finished;
struct sde_rot_entry *entries;
};
@@ -284,8 +285,8 @@ struct sde_rot_file_private;
*/
struct sde_rot_entry {
struct sde_rotation_item item;
- struct work_struct commit_work;
- struct work_struct done_work;
+ struct kthread_work commit_work;
+ struct kthread_work done_work;
struct sde_rot_queue *commitq;
struct sde_rot_queue *fenceq;
struct sde_rot_queue *doneq;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 2e91d54..f2778b0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -54,8 +54,8 @@
#define SDE_ROTATOR_DEGREE_180 180
#define SDE_ROTATOR_DEGREE_90 90
-static void sde_rotator_submit_handler(struct work_struct *work);
-static void sde_rotator_retire_handler(struct work_struct *work);
+static void sde_rotator_submit_handler(struct kthread_work *work);
+static void sde_rotator_retire_handler(struct kthread_work *work);
#ifdef CONFIG_COMPAT
static long sde_rotator_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg);
@@ -467,8 +467,8 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
SDEDEV_DBG(rot_dev->dev, "cancel request s:%d\n",
ctx->session_id);
mutex_unlock(q->lock);
- cancel_work_sync(&request->submit_work);
- cancel_work_sync(&request->retire_work);
+ kthread_cancel_work_sync(&request->submit_work);
+ kthread_cancel_work_sync(&request->retire_work);
mutex_lock(q->lock);
spin_lock(&ctx->list_lock);
list_del_init(&request->list);
@@ -926,9 +926,9 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
for (i = 0 ; i < ARRAY_SIZE(ctx->requests); i++) {
struct sde_rotator_request *request = &ctx->requests[i];
- INIT_WORK(&request->submit_work,
+ kthread_init_work(&request->submit_work,
sde_rotator_submit_handler);
- INIT_WORK(&request->retire_work,
+ kthread_init_work(&request->retire_work,
sde_rotator_retire_handler);
request->ctx = ctx;
INIT_LIST_HEAD(&request->list);
@@ -965,14 +965,16 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id,
ctx->session_id);
- ctx->work_queue.rot_work_queue = alloc_ordered_workqueue("%s",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
- if (!ctx->work_queue.rot_work_queue) {
- SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate workqueue\n");
+ kthread_init_worker(&ctx->work_queue.rot_kw);
+ ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn,
+ &ctx->work_queue.rot_kw, name);
+ if (IS_ERR(ctx->work_queue.rot_thread)) {
+ SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n");
ret = -EPERM;
+ ctx->work_queue.rot_thread = NULL;
goto error_alloc_workqueue;
}
- SDEDEV_DBG(ctx->rot_dev->dev, "work queue name=%s\n", name);
+ SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name);
snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
ctx->session_id);
@@ -1022,7 +1024,8 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
error_open_session:
sde_rot_mgr_unlock(rot_dev->mgr);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- destroy_workqueue(ctx->work_queue.rot_work_queue);
+ kthread_flush_worker(&ctx->work_queue.rot_kw);
+ kthread_stop(ctx->work_queue.rot_thread);
error_alloc_workqueue:
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
error_create_sysfs:
@@ -1072,7 +1075,7 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n",
session_id);
- cancel_work_sync(&request->submit_work);
+ kthread_cancel_work_sync(&request->submit_work);
}
SDEDEV_DBG(rot_dev->dev, "release session s:%d\n", session_id);
sde_rot_mgr_lock(rot_dev->mgr);
@@ -1085,12 +1088,13 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n",
session_id);
- cancel_work_sync(&request->retire_work);
+ kthread_cancel_work_sync(&request->retire_work);
}
mutex_lock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- destroy_workqueue(ctx->work_queue.rot_work_queue);
+ kthread_flush_worker(&ctx->work_queue.rot_kw);
+ kthread_stop(ctx->work_queue.rot_thread);
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
kobject_put(&ctx->kobj);
if (ctx->file) {
@@ -1609,7 +1613,7 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
} else {
SDEROT_ERR("invalid stats timestamp\n");
}
- req->retireq = ctx->work_queue.rot_work_queue;
+ req->retire_kw = &ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
trace_rot_entry_fence(
@@ -2719,7 +2723,7 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = {
*
* This function is scheduled in work queue context.
*/
-static void sde_rotator_retire_handler(struct work_struct *work)
+static void sde_rotator_retire_handler(struct kthread_work *work)
{
struct vb2_v4l2_buffer *src_buf;
struct vb2_v4l2_buffer *dst_buf;
@@ -2909,7 +2913,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
goto error_init_request;
}
- req->retireq = ctx->work_queue.rot_work_queue;
+ req->retire_kw = &ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
ret = sde_rotator_handle_request_common(
@@ -2938,7 +2942,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
*
* This function is scheduled in work queue context.
*/
-static void sde_rotator_submit_handler(struct work_struct *work)
+static void sde_rotator_submit_handler(struct kthread_work *work)
{
struct sde_rotator_ctx *ctx;
struct sde_rotator_device *rot_dev;
@@ -3203,7 +3207,7 @@ static int sde_rotator_job_ready(void *priv)
list_del_init(&request->list);
list_add_tail(&request->list, &ctx->pending_list);
spin_unlock(&ctx->list_lock);
- queue_work(ctx->work_queue.rot_work_queue,
+ kthread_queue_work(&ctx->work_queue.rot_kw,
&request->submit_work);
}
} else if (request && !atomic_read(&request->req->pending_count)) {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index 100ce27..627ea86 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -23,6 +23,7 @@
#include <linux/msm-bus.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/kthread.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ctrls.h>
@@ -95,8 +96,8 @@ struct sde_rotator_vbinfo {
*/
struct sde_rotator_request {
struct list_head list;
- struct work_struct submit_work;
- struct work_struct retire_work;
+ struct kthread_work submit_work;
+ struct kthread_work retire_work;
struct sde_rot_entry_container *req;
struct sde_rotator_ctx *ctx;
bool committed;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a36bcbb..564b5c9 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2696,6 +2696,9 @@ static int mmc_partial_init(struct mmc_host *host)
if (mmc_card_hs400(card)) {
if (card->ext_csd.strobe_support && host->ops->enhanced_strobe)
err = host->ops->enhanced_strobe(host);
+ else if (host->ops->execute_tuning)
+ err = host->ops->execute_tuning(host,
+ MMC_SEND_TUNING_BLOCK_HS200);
} else if (mmc_card_hs200(card) && host->ops->execute_tuning) {
err = host->ops->execute_tuning(host,
MMC_SEND_TUNING_BLOCK_HS200);
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 1fffa7c..7c77280 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -31,7 +31,7 @@
#define GENI_SE_IOMMU_VA_SIZE (0xC0000000)
#define NUM_LOG_PAGES 2
-
+#define MAX_CLK_PERF_LEVEL 32
static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000, 100000000};
/**
@@ -52,6 +52,8 @@ static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000, 100000000};
* @cur_ib: Current Bus Instantaneous BW request value.
* @bus_bw_set: Clock plan for the bus driver.
* @cur_bus_bw_idx: Current index within the bus clock plan.
+ * @num_clk_levels: Number of valid clock levels in clk_perf_tbl.
+ * @clk_perf_tbl: Table of clock frequency input to Serial Engine clock.
* @log_ctx: Logging context to hold the debug information
*/
struct geni_se_device {
@@ -72,6 +74,8 @@ struct geni_se_device {
int bus_bw_set_size;
unsigned long *bus_bw_set;
int cur_bus_bw_idx;
+ unsigned int num_clk_levels;
+ unsigned long *clk_perf_tbl;
void *log_ctx;
};
@@ -809,6 +813,111 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
EXPORT_SYMBOL(geni_se_resources_init);
/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc: Resource for which the clock table is requested.
+ * @tbl: Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return: number of valid performance levels in the table on success,
+ * standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct se_geni_rsc *rsc, unsigned long **tbl)
+{
+ struct geni_se_device *geni_se_dev;
+ int i;
+ unsigned long prev_freq = 0;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
+ return -EINVAL;
+
+ *tbl = NULL;
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -EPROBE_DEFER;
+
+ if (geni_se_dev->clk_perf_tbl) {
+ *tbl = geni_se_dev->clk_perf_tbl;
+ return geni_se_dev->num_clk_levels;
+ }
+
+ geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
+ MAX_CLK_PERF_LEVEL, GFP_KERNEL);
+ if (!geni_se_dev->clk_perf_tbl)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
+ geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
+ prev_freq + 1);
+ if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
+ geni_se_dev->clk_perf_tbl[i] = 0;
+ break;
+ }
+ prev_freq = geni_se_dev->clk_perf_tbl[i];
+ }
+ geni_se_dev->num_clk_levels = i;
+ *tbl = geni_se_dev->clk_perf_tbl;
+ return geni_se_dev->num_clk_levels;
+}
+EXPORT_SYMBOL(geni_se_clk_tbl_get);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc: Resource for which the clock frequency is requested.
+ * @req_freq: Requested clock frequency.
+ * @index: Index of the resultant frequency in the table.
+ * @res_freq: Resultant frequency which matches or is closer to the
+ * requested frequency.
+ * @exact: Flag to indicate exact multiple requirement of the requested
+ * frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct se_geni_rsc *rsc, unsigned long req_freq,
+ unsigned int *index, unsigned long *res_freq,
+ bool exact)
+{
+ unsigned long *tbl;
+ int num_clk_levels;
+ int i;
+
+ num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
+ if (num_clk_levels < 0)
+ return num_clk_levels;
+
+ if (num_clk_levels == 0)
+ return -EFAULT;
+
+ *res_freq = 0;
+ for (i = 0; i < num_clk_levels; i++) {
+ if (!(tbl[i] % req_freq)) {
+ *index = i;
+ *res_freq = tbl[i];
+ return 0;
+ }
+
+ if (!(*res_freq) || ((tbl[i] > *res_freq) &&
+ (tbl[i] < req_freq))) {
+ *index = i;
+ *res_freq = tbl[i];
+ }
+ }
+
+ if (exact || !(*res_freq))
+ return -ENOKEY;
+
+ return 0;
+}
+EXPORT_SYMBOL(geni_se_clk_freq_match);
+
+/**
* geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
* @wrapper_dev: QUPv3 Wrapper Device to which the TX buffer is mapped.
* @base: Base address of the SE register block.
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 08eb00a..ad3eb187 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -110,14 +110,6 @@ static struct spi_master *get_spi_master(struct device *dev)
return spi;
}
-static int get_sclk(u32 speed_hz, unsigned long *sclk_freq)
-{
- u32 root_freq[] = { 19200000 };
-
- *sclk_freq = root_freq[0];
- return 0;
-}
-
static int do_spi_clk_cfg(u32 speed_hz, struct spi_geni_master *mas)
{
unsigned long sclk_freq;
@@ -131,14 +123,20 @@ static int do_spi_clk_cfg(u32 speed_hz, struct spi_geni_master *mas)
clk_sel &= ~CLK_SEL_MSK;
m_clk_cfg &= ~CLK_DIV_MSK;
- idx = get_sclk(speed_hz, &sclk_freq);
- if (idx < 0)
- return -EINVAL;
+ ret = geni_se_clk_freq_match(&mas->spi_rsc, speed_hz, &idx,
+ &sclk_freq, true);
+ if (ret) {
+ dev_err(mas->dev, "%s: Failed(%d) to find src clk for 0x%x\n",
+ __func__, ret, speed_hz);
+ return ret;
+ }
div = ((sclk_freq / SPI_OVERSAMPLING) / speed_hz);
if (!div)
return -EINVAL;
+ dev_dbg(mas->dev, "%s: req %u sclk %lu, idx %d, div %d\n", __func__,
+ speed_hz, sclk_freq, idx, div);
clk_sel |= (idx & CLK_SEL_MSK);
m_clk_cfg |= ((div << CLK_DIV_SHFT) | SER_CLK_EN);
ret = clk_set_rate(rsc->se_clk, sclk_freq);
@@ -362,13 +360,13 @@ static int spi_geni_transfer_one(struct spi_master *spi,
reinit_completion(&mas->xfer_done);
/* Speed and bits per word can be overridden per transfer */
if (xfer->speed_hz != mas->cur_speed_hz) {
+ mas->cur_speed_hz = xfer->speed_hz;
ret = do_spi_clk_cfg(mas->cur_speed_hz, mas);
if (ret) {
dev_err(mas->dev, "%s:Err setting clks:%d\n",
__func__, ret);
goto geni_transfer_one_exit;
}
- mas->cur_speed_hz = xfer->speed_hz;
}
setup_fifo_xfer(xfer, mas, slv->mode, spi);
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index 5947107..6c9ddcd 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -565,6 +565,41 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
unsigned long ab, unsigned long ib);
/**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc: Resource for which the clock table is requested.
+ * @tbl: Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return: number of valid performance levels in the table on success,
+ * standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct se_geni_rsc *rsc, unsigned long **tbl);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc: Resource for which the clock frequency is requested.
+ * @req_freq: Requested clock frequency.
+ * @index: Index of the resultant frequency in the table.
+ * @res_freq: Resultant frequency which matches or is closer to the
+ * requested frequency.
+ * @exact: Flag to indicate exact multiple requirement of the requested
+ * frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct se_geni_rsc *rsc, unsigned long req_freq,
+ unsigned int *index, unsigned long *res_freq,
+ bool exact);
+
+/**
* geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
* @wrapper_dev: QUPv3 Wrapper Device to which the TX buffer is mapped.
* @base: Base address of the SE register block.
@@ -796,6 +831,19 @@ static inline int geni_se_resources_init(struct se_geni_rsc *rsc,
return -ENXIO;
}
+static inline int geni_se_clk_tbl_get(struct se_geni_rsc *rsc,
+ unsigned long **tbl)
+{
+ return -ENXIO;
+}
+
+static inline int geni_se_clk_freq_match(struct se_geni_rsc *rsc,
+ unsigned long req_freq, unsigned int *index,
+ unsigned long *res_freq, bool exact)
+{
+ return -ENXIO;
+}
+
static inline int geni_se_tx_dma_prep(struct device *wrapper_dev,
void __iomem *base, void *tx_buf, int tx_len, dma_addr_t *tx_dma)
{
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8d4813d..9e7ab05 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1155,8 +1155,9 @@ struct sched_domain_attr {
extern int sched_domain_level_max;
struct capacity_state {
- unsigned long cap; /* compute capacity */
- unsigned long power; /* power consumption at this compute capacity */
+ unsigned long cap; /* capacity - calculated by energy driver */
+ unsigned long frequency;/* frequency */
+ unsigned long power; /* power consumption at this frequency */
};
struct idle_state {
diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h
index a4557d1..5f0f070 100644
--- a/include/uapi/media/cam_defs.h
+++ b/include/uapi/media/cam_defs.h
@@ -7,14 +7,15 @@
/* camera op codes */
-#define CAM_COMMON_OPCODE_BASE 0
-#define CAM_QUERY_CAP 1
-#define CAM_ACQUIRE_DEV 2
-#define CAM_START_DEV 3
-#define CAM_STOP_DEV 4
-#define CAM_CONFIG_DEV 5
-#define CAM_RELEASE_DEV 6
-#define CAM_COMMON_OPCODE_MAX 7
+#define CAM_COMMON_OPCODE_BASE 0x100
+#define CAM_QUERY_CAP (CAM_COMMON_OPCODE_BASE + 0x1)
+#define CAM_ACQUIRE_DEV (CAM_COMMON_OPCODE_BASE + 0x2)
+#define CAM_START_DEV (CAM_COMMON_OPCODE_BASE + 0x3)
+#define CAM_STOP_DEV (CAM_COMMON_OPCODE_BASE + 0x4)
+#define CAM_CONFIG_DEV (CAM_COMMON_OPCODE_BASE + 0x5)
+#define CAM_RELEASE_DEV (CAM_COMMON_OPCODE_BASE + 0x6)
+#define CAM_SD_SHUTDOWN (CAM_COMMON_OPCODE_BASE + 0x7)
+#define CAM_COMMON_OPCODE_MAX (CAM_COMMON_OPCODE_BASE + 0x8)
/* camera handle type */
#define CAM_HANDLE_USER_POINTER 1
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
index 30ab6f4..c32defa 100644
--- a/kernel/sched/energy.c
+++ b/kernel/sched/energy.c
@@ -18,14 +18,15 @@
*/
#define pr_fmt(fmt) "sched-energy: " fmt
-#define DEBUG
-
#include <linux/gfp.h>
#include <linux/of.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/sched_energy.h>
#include <linux/stddef.h>
+#include <linux/cpu.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
@@ -97,7 +98,8 @@ void init_sched_energy_costs(void)
}
for (i = 0, val = prop->value; i < nstates; i++) {
- cap_states[i].cap = be32_to_cpup(val++);
+ cap_states[i].cap = SCHED_CAPACITY_SCALE;
+ cap_states[i].frequency = be32_to_cpup(val++);
cap_states[i].power = be32_to_cpup(val++);
}
@@ -138,3 +140,166 @@ void init_sched_energy_costs(void)
out:
free_resources();
}
+
+static int sched_energy_probe(struct platform_device *pdev)
+{
+ unsigned long max_freq = 0;
+ int max_efficiency = INT_MIN;
+ int cpu;
+ unsigned long *max_frequencies = NULL;
+ int ret;
+
+ if (!sched_is_energy_aware())
+ return 0;
+
+ max_frequencies = kmalloc_array(nr_cpu_ids, sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!max_frequencies) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /*
+ * Find system max possible frequency and max frequencies for each
+ * CPUs.
+ */
+ for_each_possible_cpu(cpu) {
+ struct device *cpu_dev;
+ struct dev_pm_opp *opp;
+ int efficiency = arch_get_cpu_efficiency(cpu);
+
+ max_efficiency = max(efficiency, max_efficiency);
+
+ cpu_dev = get_cpu_device(cpu);
+ if (IS_ERR_OR_NULL(cpu_dev)) {
+ if (!cpu_dev)
+ ret = -EINVAL;
+ else
+ ret = PTR_ERR(cpu_dev);
+ goto exit;
+ }
+
+ max_frequencies[cpu] = ULONG_MAX;
+
+ rcu_read_lock();
+ opp = dev_pm_opp_find_freq_floor(cpu_dev,
+ &max_frequencies[cpu]);
+ if (IS_ERR_OR_NULL(opp)) {
+ if (!opp || PTR_ERR(opp) == -ENODEV)
+ ret = -EPROBE_DEFER;
+ else
+ ret = PTR_ERR(opp);
+ goto exit_rcu_unlock;
+ }
+ rcu_read_unlock();
+
+ /* Convert HZ to KHZ */
+ max_frequencies[cpu] /= 1000;
+ max_freq = max(max_freq, max_frequencies[cpu]);
+ }
+
+ /* update capacity in energy model */
+ for_each_possible_cpu(cpu) {
+ unsigned long cpu_max_cap;
+ struct sched_group_energy *sge_l0, *sge;
+ int efficiency = arch_get_cpu_efficiency(cpu);
+
+ cpu_max_cap = DIV_ROUND_UP(SCHED_CAPACITY_SCALE *
+ max_frequencies[cpu], max_freq);
+ cpu_max_cap = DIV_ROUND_UP(cpu_max_cap * efficiency,
+ max_efficiency);
+
+ /*
+ * All the cap_states have same frequency table so use
+ * SD_LEVEL0's.
+ */
+ sge_l0 = sge_array[cpu][SD_LEVEL0];
+ if (sge_l0 && sge_l0->nr_cap_states > 0) {
+ int i;
+ int ncapstates = sge_l0->nr_cap_states;
+
+ for (i = 0; i < ncapstates; i++) {
+ int sd_level;
+ unsigned long freq, cap;
+
+ /*
+ * Energy model can contain more frequency
+ * steps than actual for multiple speedbin
+ * support. Ceil the max capacity with actual
+ * one.
+ */
+ freq = min(sge_l0->cap_states[i].frequency,
+ max_frequencies[cpu]);
+ cap = DIV_ROUND_UP(cpu_max_cap * freq,
+ max_frequencies[cpu]);
+
+ for_each_possible_sd_level(sd_level) {
+ sge = sge_array[cpu][sd_level];
+ if (!sge)
+ break;
+ sge->cap_states[i].cap = cap;
+ }
+
+ dev_dbg(&pdev->dev,
+ "cpu=%d freq=%ld cap=%ld power_d0=%ld\n",
+ cpu, freq, sge_l0->cap_states[i].cap,
+ sge_l0->cap_states[i].power);
+ }
+
+ dev_info(&pdev->dev,
+ "cpu=%d eff=%d [freq=%ld cap=%ld power_d0=%ld] -> [freq=%ld cap=%ld power_d0=%ld]\n",
+ cpu, efficiency,
+ sge_l0->cap_states[0].frequency,
+ sge_l0->cap_states[0].cap,
+ sge_l0->cap_states[0].power,
+ sge_l0->cap_states[ncapstates - 1].frequency,
+ sge_l0->cap_states[ncapstates - 1].cap,
+ sge_l0->cap_states[ncapstates - 1].power
+ );
+ }
+
+
+ dev_dbg(&pdev->dev,
+ "cpu=%d efficiency=%d max_frequency=%ld max_efficiency=%d cpu_max_capacity=%ld\n",
+ cpu, efficiency, max_frequencies[cpu], max_efficiency,
+ cpu_max_cap);
+
+ arch_update_cpu_capacity(cpu);
+ }
+
+ kfree(max_frequencies);
+
+ dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n");
+ return 0;
+
+exit_rcu_unlock:
+ rcu_read_unlock();
+
+exit:
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "error=%d\n", ret);
+
+ kfree(max_frequencies);
+ return ret;
+}
+
+static const struct of_device_id of_sched_energy_dt[] = {
+ {
+ .compatible = "sched-energy",
+ },
+ { }
+};
+
+static struct platform_driver energy_driver = {
+ .driver = {
+ .name = "sched-energy",
+ .of_match_table = of_sched_energy_dt,
+ },
+ .probe = sched_energy_probe,
+};
+
+static int __init sched_energy_init(void)
+{
+ return platform_driver_register(&energy_driver);
+}
+subsys_initcall(sched_energy_init);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a0b62d4..e4b706d 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5406,28 +5406,12 @@ static inline int task_util(struct task_struct *p)
return p->se.avg.util_avg;
}
-#define SCHED_ENABLE_WAKER_WAKEE 0
-
-static unsigned int sched_small_wakee_task_util = 102; /* ~10% of max cap */
-static unsigned int sched_big_waker_task_util = 256; /* 25% of max cap */
-
-static inline bool
-wake_on_waker_sibling(struct task_struct *p)
-{
- return SCHED_ENABLE_WAKER_WAKEE &&
- task_util(current) > sched_big_waker_task_util &&
- task_util(p) < sched_small_wakee_task_util;
-}
-
-#define sysctl_sched_prefer_sync_wakee_to_waker 0
-
static inline bool
bias_to_waker_cpu(struct task_struct *p, int cpu)
{
- return sysctl_sched_prefer_sync_wakee_to_waker &&
- cpu_rq(cpu)->nr_running == 1 &&
- cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
- cpu_active(cpu) && !cpu_isolated(cpu);
+ return cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
+ cpu_active(cpu) && !cpu_isolated(cpu) &&
+ task_fits_max(p, cpu);
}
static int calc_util_delta(struct energy_env *eenv, int cpu)
@@ -6738,10 +6722,8 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
unsigned int target_cpu_util = UINT_MAX;
long target_cpu_new_util_cum = LONG_MAX;
struct cpumask *rtg_target = NULL;
- bool wake_on_sibling = false;
int isolated_candidate = -1;
bool need_idle;
- bool skip_ediff = false;
enum sched_boost_policy placement_boost = task_sched_boost(p) ?
sched_boost_policy() : SCHED_BOOST_NONE;
@@ -6754,10 +6736,17 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
sg_target = sg;
sync = sync && sysctl_sched_sync_hint_enable;
+
curr_util = boosted_task_util(cpu_rq(cpu)->curr);
need_idle = wake_to_idle(p);
+ if (sync && bias_to_waker_cpu(p, cpu)) {
+ trace_sched_task_util_bias_to_waker(p, task_cpu(p),
+ task_util(p), cpu, cpu, 0, need_idle);
+ return cpu;
+ }
+
if (sysctl_sched_is_big_little) {
struct related_thread_group *grp;
@@ -6765,17 +6754,8 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
grp = task_related_thread_group(p);
rcu_read_unlock();
- if (grp && grp->preferred_cluster) {
+ if (grp && grp->preferred_cluster)
rtg_target = &grp->preferred_cluster->cpus;
- } else if (sync && wake_on_waker_sibling(p)) {
- if (bias_to_waker_cpu(p, cpu)) {
- trace_sched_task_util_bias_to_waker(p,
- task_cpu(p), task_util(p), cpu,
- cpu, 0, need_idle);
- return cpu;
- }
- wake_on_sibling = true;
- }
task_util_boosted = boosted_task_util(p);
@@ -6826,21 +6806,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
rtg_target))
break;
continue;
- } else if (wake_on_sibling) {
- /* Skip non-sibling CPUs */
- if (!cpumask_test_cpu(cpu,
- sched_group_cpus(sg)))
- continue;
- } else if (sync && curr_util >=
- task_util_boosted) {
- if (cpumask_test_cpu(cpu,
- sched_group_cpus(sg))) {
- if (!cpumask_test_cpu(task_cpu(p),
- sched_group_cpus(sg)))
- skip_ediff = true;
- break;
- }
- continue;
}
target_max_cap = capacity_of(max_cap_cpu);
@@ -6909,8 +6874,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
idle_get_state_idx(cpu_rq(i));
if (!need_idle &&
- (!wake_on_sibling ||
- (wake_on_sibling && i != cpu)) &&
add_capacity_margin(new_util_cum) <
capacity_curr_of(i)) {
if (sysctl_sched_cstate_aware) {
@@ -6944,9 +6907,7 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
target_cpu = i;
break;
}
- } else if (!need_idle &&
- (!wake_on_sibling ||
- (wake_on_sibling && i != cpu))) {
+ } else if (!need_idle) {
/*
* At least one CPU other than target_cpu is
* going to raise CPU's OPP higher than current
@@ -7017,13 +6978,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
}
}
- if (wake_on_sibling && target_cpu != -1) {
- trace_sched_task_util_bias_to_waker(p, task_cpu(p),
- task_util(p), target_cpu,
- target_cpu, 0, need_idle);
- return target_cpu;
- }
-
if (target_cpu != task_cpu(p) && !cpu_isolated(task_cpu(p))) {
struct energy_env eenv = {
.util_delta = task_util(p),
@@ -7059,8 +7013,7 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
return target_cpu;
}
- if (!skip_ediff)
- ediff = energy_diff(&eenv);
+ ediff = energy_diff(&eenv);
if (!sysctl_sched_cstate_aware) {
if (ediff >= 0) {
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 28d660b..d4a0612 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1700,6 +1700,13 @@ unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
}
#endif
+#ifndef arch_update_cpu_capacity
+static __always_inline
+void arch_update_cpu_capacity(int cpu)
+{
+}
+#endif
+
#ifdef CONFIG_SMP
static inline unsigned long capacity_of(int cpu)
{