Merge "msm: ADSPRPC: Avoid race condition during map creation and free"
diff --git a/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
new file mode 100644
index 0000000..c088d42
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
@@ -0,0 +1,60 @@
+Qualcomm Technologies, Inc. TRI_LED driver specific bindings
+
+This binding document describes the properties of TRI_LED module in
+Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,tri-led".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base of the TRI_LED module and length.
+
+Properties for child nodes:
+- pwms:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The PWM device (phandle) used for controlling LED.
+
+- led-sources:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+ Device current output identifiers are: 0 - LED1_EN,
+ 1 - LED2_EN, 2 - LED3_EN.
+
+- label:
+ Usage: optional
+ Value type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+- linux,default-trigger:
+ Usage: optional
+ Value_type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+Example:
+
+ pmi8998_rgb: tri-led@d000{
+ compatible = "qcom,tri-led";
+ reg = <0xd000 0x100>;
+
+ red {
+ label = "red";
+ pwms = <&pmi8998_lpg 4 1000000>;
+ led-sources = <0>;
+ };
+ green {
+ label = "green";
+ pwms = <&pmi8998_lpg 3 1000000>;
+ led-sources = <1>;
+ };
+ blue {
+ label = "blue";
+ pwms = <&pmi8998_lpg 2 1000000>;
+ led-sources = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 6af2bac..dfe5852 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -37,6 +37,7 @@
MSIs, virtual IRQ's (INT#), link state notifications.
- perst-gpio: PERST GPIO specified by PCIe spec.
- wake-gpio: WAKE GPIO specified by PCIe spec.
+ - phy-status-offset: Offset from PCIe PHY base to check if PCIe PHY is up.
- <supply-name>-supply: phandle to the regulator device tree node.
Refer to the schematics for the corresponding voltage regulators.
vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
@@ -274,6 +275,7 @@
qcom,switch-latency = <100>;
qcom,wr-halt-size = <0xa>; /* 1KB */
qcom,slv-addr-space-size = <0x1000000>; /* 16MB */
+ qcom,phy-status-offset = <0x800>;
qcom,cpl-timeout = <0x2>;
iommus = <&anoc0_smmu>;
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
new file mode 100644
index 0000000..3174ccb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -0,0 +1,36 @@
+Qualcomm Technologies, Inc. LPG driver specific bindings
+
+This binding document describes the properties of LPG (Light Pulse Generator)
+device module in Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,pwm-lpg".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base and length for LPG modules. The length
+ varies based on the number of channels available in
+ the PMIC chips.
+
+- reg-names:
+ Usage: required
+ Value type: <string>
+ Definition: The name of the register defined in the reg property.
+ It must be "lpg-base".
+
+- #pwm-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
+
+Example:
+
+ pmi8998_lpg: lpg@b100 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb100 0x600>;
+ reg-names = "lpg-base";
+ #pwm-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index fba7204..be8c2f0 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -14,6 +14,7 @@
- qcom,fastrpc-glink: Flag to use glink instead of smd for IPC
- qcom,rpc-latency-us: FastRPC QoS latency vote
- qcom,adsp-remoteheap-vmid: FastRPC remote heap VMID list
+- qcom,fastrpc-adsp-audio-pdr: Flag to enable ADSP Audio PDR
Optional subnodes:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a37e441..e996ba5 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -272,6 +272,7 @@
SUNW Sun Microsystems, Inc
swir Sierra Wireless
syna Synaptics Inc.
+synaptics Synaptics Inc.
synology Synology, Inc.
tbs TBS Technologies
tcg Trusted Computing Group
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
index 0b94534..e939bd2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
@@ -139,6 +139,8 @@
qcom,slv-addr-space-size = <0x40000000>;
+ qcom,phy-status-offset = <0x814>;
+
qcom,cpl-timeout = <0x2>;
qcom,boot-option = <0x1>;
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 7d5ee77..5829942 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -17,6 +17,7 @@
#include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+#include <dt-bindings/clock/qcom,aop-qmp.h>
/ {
model = "Qualcomm Technologies, Inc. SDX POORWILLS";
@@ -223,6 +224,13 @@
mbox-names = "apps";
};
+ clock_aop: qcom,aopclk {
+ compatible = "qcom,aop-qmp-clk-v1";
+ #clock-cells = <1>;
+ mboxes = <&qmp_aop 0>;
+ mbox-names = "qdss_clk";
+ };
+
snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive {
compatible = "qcom,devbw";
governor = "powersave";
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index b9628eb..1fb8f20 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -25,6 +25,7 @@
CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -316,6 +317,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index b5268d6..61c362e 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -27,6 +27,7 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
# CONFIG_VDSO is not set
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -315,6 +316,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index f9dfe80..4952b98 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -44,6 +44,7 @@
select HAVE_ARM_ARCH_TIMER
select MSM_CORTEX_A7
select PINCTRL
+ select PCI
select QCOM_SCM if SMP
select MSM_JTAG_MM if CORESIGHT_ETM
select PM_DEVFREQ
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
index c059443..1b38d06 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -38,17 +38,13 @@
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
- qcom,mdss-dsi-panel-timings =
- [da 34 24 00 64 68 28 38 2a 03 04 00];
- qcom,mdss-dsi-t-clk-pre = <0x29>;
- qcom,mdss-dsi-t-clk-post = <0x03>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 50>;
qcom,mdss-dsi-display-timings {
timing@0 {
qcom,mdss-dsi-panel-framerate = <60>;
@@ -77,11 +73,11 @@
05 01 00 00 0a 00 02 20 00
15 01 00 00 00 00 02 bb 10
05 01 00 00 78 00 02 11 00
- 05 01 00 00 14 00 02 29 00
+ 05 01 00 00 78 00 02 29 00
];
qcom,mdss-dsi-off-command = [
- 05 01 00 00 14 00 02
- 28 00 05 01 00 00 78 00 02 10 00
+ 05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index a142840..04202e5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -675,6 +675,8 @@
reg = <0x1800000 0x80000>;
reg-names = "cc_base";
vdd_gfx-supply = <&gfx_vreg_corner>;
+ clocks = <&clock_gcc clk_xo_clk_src>;
+ clock-names = "xo";
qcom,gfxfreq-corner =
< 0 0 >,
< 133330000 1 >, /* Min SVS */
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index f287b21..a280fd3 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a615_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index d2a6640..9a7e742 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -307,6 +307,12 @@
qcom,dsi-display-active;
};
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ qcom,supply-post-off-sleep = <5>;
+ };
+};
+
&pm660l_wled {
status = "okay";
qcom,led-strings-list = [00 01];
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index ce88d14..007f937 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -826,8 +826,15 @@
};
&dsi_dual_nt36850_truly_cmd {
- qcom,mdss-dsi-t-clk-post = <0x0E>;
+ qcom,mdss-dsi-t-clk-post = <0x28>;
qcom,mdss-dsi-t-clk-pre = <0x30>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
qcom,mdss-dsi-display-timings {
timing@0{
qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 2b391a5..88117cf 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -529,6 +529,30 @@
reg = <0 0x93e00000 0 0x1e00000>;
};
+ pil_ipa_fw_mem: ips_fw_region@0x95c00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c00000 0 0x10000>;
+ };
+
+ pil_ipa_gsi_mem: ipa_gsi_region@0x95c10000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c10000 0 0x5000>;
+ };
+
+ pil_gpu_mem: gpu_region@0x95c15000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c15000 0 0x2000>;
+ };
+
+ qseecom_mem: qseecom_region@0x9e400000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0 0x9e400000 0 0x1400000>;
+ };
+
adsp_mem: adsp_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -537,14 +561,6 @@
size = <0 0xc00000>;
};
- qseecom_mem: qseecom_region {
- compatible = "shared-dma-pool";
- alloc-ranges = <0 0x00000000 0 0xffffffff>;
- no-map;
- alignment = <0 0x400000>;
- size = <0 0x1400000>;
- };
-
qseecom_ta_mem: qseecom_ta_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 33bcaa6..000f5d3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a630_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index daf5687..af7feb5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -202,6 +202,8 @@
qcom,ep-latency = <10>;
+ qcom,phy-status-offset = <0x974>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <0>;
@@ -535,6 +537,8 @@
qcom,slv-addr-space-size = <0x20000000>;
+ qcom,phy-status-offset = <0x1aac>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 11c48bd..78be790 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -749,6 +749,123 @@
};
};
+ /* add pingrp for touchscreen */
+ pmx_ts_int_active {
+ ts_int_active: ts_int_active {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_int_suspend {
+ ts_int_suspend1: ts_int_suspend1 {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_reset_active {
+ ts_reset_active: ts_reset_active {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_reset_suspend {
+ ts_reset_suspend1: ts_reset_suspend1 {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_release {
+ ts_release: ts_release {
+ mux {
+ pins = "gpio122", "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122", "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ ts_mux {
+ ts_active: ts_active {
+ mux {
+ pins = "gpio99", "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99", "gpio122";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+
+ ts_reset_suspend: ts_reset_suspend {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ ts_int_suspend: ts_int_suspend {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
sec_aux_pcm {
sec_aux_pcm_sleep: sec_aux_pcm_sleep {
mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d95dda0..a5c6ab5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -206,8 +206,8 @@
qcom,vdd-io-current-level = <200 22000>;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &storage_cd>;
- pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &storage_cd>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
cd-gpios = <&tlmm 126 GPIO_ACTIVE_HIGH>;
@@ -217,3 +217,30 @@
&wil6210 {
status = "ok";
};
+
+&qupv3_se5_i2c {
+ status = "ok";
+ synaptics_dsx@20 {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x20>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0x2008>;
+ vdd-supply = <&pm8998_l14>;
+ avdd-supply = <&pm8998_l28>;
+ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend",
+ "pmx_ts_release";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ pinctrl-2 = <&ts_release>;
+ synaptics,pwr-reg-name = "avdd";
+ synaptics,bus-reg-name = "vdd";
+ synaptics,ub-i2c-addr = <0x2c>;
+ synaptics,irq-gpio = <&tlmm 122 0x2008>;
+ synaptics,reset-gpio = <&tlmm 99 0x0>;
+ synaptics,irq-on-state = <0>;
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,reset-on-state = <0>;
+ synaptics,reset-active-ms = <20>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 9d7c519..ec8665b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -559,7 +559,7 @@
regulator-min-microvolt = <2704000>;
regulator-max-microvolt = <2960000>;
qcom,init-voltage = <2704000>;
- qcom,init-mode = <RPMH_REGULATOR_MODE_LPM>;
+ qcom,init-mode = <RPMH_REGULATOR_MODE_HPM>;
};
};
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b08ccbb..8ba0af7 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -185,7 +185,8 @@
}
wb_congested = wb_congested_get_create(&q->backing_dev_info,
- blkcg->css.id, GFP_NOWAIT);
+ blkcg->css.id,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!wb_congested) {
ret = -ENOMEM;
goto err_put_css;
@@ -193,7 +194,7 @@
/* allocate */
if (!new_blkg) {
- new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT);
+ new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT | __GFP_NOWARN);
if (unlikely(!new_blkg)) {
ret = -ENOMEM;
goto err_put_congested;
@@ -1022,7 +1023,7 @@
}
spin_lock_init(&blkcg->lock);
- INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT);
+ INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT | __GFP_NOWARN);
INIT_HLIST_HEAD(&blkcg->blkg_list);
#ifdef CONFIG_CGROUP_WRITEBACK
INIT_LIST_HEAD(&blkcg->cgwb_list);
@@ -1240,7 +1241,7 @@
if (blkg->pd[pol->plid])
continue;
- pd = pol->pd_alloc_fn(GFP_NOWAIT, q->node);
+ pd = pol->pd_alloc_fn(GFP_NOWAIT | __GFP_NOWARN, q->node);
if (!pd)
swap(pd, pd_prealloc);
if (!pd) {
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 4ac4910..6a90155 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -3868,7 +3868,8 @@
goto out;
}
- cfqq = kmem_cache_alloc_node(cfq_pool, GFP_NOWAIT | __GFP_ZERO,
+ cfqq = kmem_cache_alloc_node(cfq_pool,
+ GFP_NOWAIT | __GFP_ZERO | __GFP_NOWARN,
cfqd->queue->node);
if (!cfqq) {
cfqq = &cfqd->oom_cfqq;
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 5cf41d5..3f35d54 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -28,6 +28,8 @@
#include <soc/qcom/glink.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/service-locator.h>
#include <linux/scatterlist.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
@@ -58,6 +60,9 @@
#define VMID_ADSP_Q6 6
#define DEBUGFS_SIZE 1024
+#define AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME "audio_pdr_adsprpc"
+#define AUDIO_PDR_ADSP_SERVICE_NAME "avs/audio"
+
#define RPC_TIMEOUT (5 * HZ)
#define BALIGN 128
#define NUM_CHANNELS 4 /* adsp, mdsp, slpi, cdsp*/
@@ -110,6 +115,9 @@
static int fastrpc_glink_open(int cid);
static void fastrpc_glink_close(void *chan, int cid);
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *nb,
+ unsigned long code,
+ void *data);
static struct dentry *debugfs_root;
static struct dentry *debugfs_global_file;
@@ -224,6 +232,16 @@
int used;
};
+struct fastrpc_static_pd {
+ char *spdname;
+ struct notifier_block pdrnb;
+ struct notifier_block get_service_nb;
+ void *pdrhandle;
+ int pdrcount;
+ int prevpdrcount;
+ int ispdup;
+};
+
struct fastrpc_glink_info {
int link_state;
int port_state;
@@ -238,6 +256,7 @@
void *chan;
struct device *dev;
struct fastrpc_session_ctx session[NUM_SESSIONS];
+ struct fastrpc_static_pd spd[NUM_SESSIONS];
struct completion work;
struct completion workport;
struct notifier_block nb;
@@ -334,6 +353,7 @@
int cid;
int ssrcount;
int pd;
+ char *spdname;
int file_close;
struct fastrpc_apps *apps;
struct hlist_head perf;
@@ -353,6 +373,14 @@
.subsys = "adsp",
.link.link_info.edge = "lpass",
.link.link_info.transport = "smem",
+ .spd = {
+ {
+ .spdname =
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ .pdrnb.notifier_call =
+ fastrpc_audio_pdr_notifier_cb,
+ }
+ },
},
{
.name = "mdsprpc-smd",
@@ -1123,6 +1151,21 @@
spin_unlock(&me->hlock);
}
+
+static void fastrpc_notify_pdr_drivers(struct fastrpc_apps *me, char *spdname)
+{
+ struct fastrpc_file *fl;
+ struct hlist_node *n;
+
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
+ if (fl->spdname && !strcmp(spdname, fl->spdname))
+ fastrpc_notify_users(fl);
+ }
+ spin_unlock(&me->hlock);
+
+}
+
static void context_list_ctor(struct fastrpc_ctx_lst *me)
{
INIT_HLIST_HEAD(&me->interrupted);
@@ -1706,7 +1749,28 @@
return err;
}
+static int fastrpc_get_adsp_session(char *name, int *session)
+{
+ struct fastrpc_apps *me = &gfa;
+ int err = 0, i;
+
+ for (i = 0; i < NUM_SESSIONS; i++) {
+ if (!me->channel[0].spd[i].spdname)
+ continue;
+ if (!strcmp(name, me->channel[0].spd[i].spdname))
+ break;
+ }
+ VERIFY(err, i < NUM_SESSIONS);
+ if (err)
+ goto bail;
+ *session = i;
+bail:
+ return err;
+}
+
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl);
static int fastrpc_channel_open(struct fastrpc_file *fl);
+static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl);
static int fastrpc_init_process(struct fastrpc_file *fl,
struct fastrpc_ioctl_init_attrs *uproc)
{
@@ -1846,6 +1910,12 @@
inbuf.pgid = current->tgid;
inbuf.namelen = init->filelen;
inbuf.pageslen = 0;
+
+ if (!strcmp(proc_name, "audiopd")) {
+ fl->spdname = AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME;
+ VERIFY(err, !fastrpc_mmap_remove_pdr(fl));
+ }
+
if (!me->staticpd_flags) {
inbuf.pageslen = 1;
mutex_lock(&fl->fl_map_mutex);
@@ -2157,6 +2227,33 @@
return err;
}
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl)
+{
+ struct fastrpc_apps *me = &gfa;
+ int session = 0, err = 0;
+
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto bail;
+ if (me->channel[fl->cid].spd[session].pdrcount !=
+ me->channel[fl->cid].spd[session].prevpdrcount) {
+ if (fastrpc_mmap_remove_ssr(fl))
+ pr_err("ADSPRPC: SSR: Failed to unmap remote heap\n");
+ me->channel[fl->cid].spd[session].prevpdrcount =
+ me->channel[fl->cid].spd[session].pdrcount;
+ }
+ if (!me->channel[fl->cid].spd[session].ispdup) {
+ VERIFY(err, 0);
+ if (err) {
+ err = -ENOTCONN;
+ goto bail;
+ }
+ }
+bail:
+ return err;
+}
+
static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va,
size_t len, struct fastrpc_mmap **ppmap);
@@ -3077,6 +3174,64 @@
return NOTIFY_DONE;
}
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *pdrnb,
+ unsigned long code,
+ void *data)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct fastrpc_static_pd *spd;
+ struct notif_data *notifdata = data;
+
+ spd = container_of(pdrnb, struct fastrpc_static_pd, pdrnb);
+ if (code == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01) {
+ mutex_lock(&me->smd_mutex);
+ spd->pdrcount++;
+ spd->ispdup = 0;
+ pr_info("ADSPRPC: Audio PDR notifier %d %s\n",
+ MAJOR(me->dev_no), spd->spdname);
+ mutex_unlock(&me->smd_mutex);
+ if (!strcmp(spd->spdname,
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME))
+ me->staticpd_flags = 0;
+ fastrpc_notify_pdr_drivers(me, spd->spdname);
+ } else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
+ if (me->channel[0].remoteheap_ramdump_dev &&
+ notifdata->enable_ramdump) {
+ me->channel[0].ramdumpenabled = 1;
+ }
+ } else if (code == SERVREG_NOTIF_SERVICE_STATE_UP_V01) {
+ spd->ispdup = 1;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int fastrpc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode, void *data)
+{
+ struct fastrpc_static_pd *spd;
+ struct pd_qmi_client_data *pdr = data;
+ int curr_state = 0;
+
+ spd = container_of(nb, struct fastrpc_static_pd, get_service_nb);
+ if (opcode == LOCATOR_DOWN) {
+ pr_err("ADSPRPC: Audio PD restart notifier locator down\n");
+ return NOTIFY_DONE;
+ }
+
+ if (pdr->total_domains == 1) {
+ spd->pdrhandle = service_notif_register_notifier(
+ pdr->domain_list[0].name,
+ pdr->domain_list[0].instance_id,
+ &spd->pdrnb, &curr_state);
+ if (IS_ERR(spd->pdrhandle))
+ pr_err("ADSPRPC: Unable to register notifier\n");
+ } else
+ pr_err("ADSPRPC: Service returned invalid domains\n");
+
+ return NOTIFY_DONE;
+}
+
static const struct file_operations fops = {
.open = fastrpc_device_open,
.release = fastrpc_device_release,
@@ -3206,6 +3361,7 @@
struct platform_device *ion_pdev;
struct cma *cma;
uint32_t val;
+ int ret = 0;
if (of_device_is_compatible(dev->of_node,
@@ -3260,7 +3416,26 @@
}
return 0;
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,fastrpc-adsp-audio-pdr")) {
+ int session;
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto spdbail;
+ me->channel[0].spd[session].get_service_nb.notifier_call =
+ fastrpc_get_service_location_notify;
+ ret = get_service_location(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ AUDIO_PDR_ADSP_SERVICE_NAME,
+ &me->channel[0].spd[session].get_service_nb);
+ if (ret)
+ pr_err("ADSPRPC: Get service location failed: %d\n",
+ ret);
+ }
+spdbail:
+ err = 0;
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
fastrpc_match_table,
NULL, &pdev->dev));
diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c
index e25da83..b2dc3d26 100644
--- a/drivers/clk/msm/clock-gcc-8953.c
+++ b/drivers/clk/msm/clock-gcc-8953.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -4072,8 +4072,17 @@
struct resource *res;
int ret;
u32 regval;
+ struct clk *xo_clk;
bool compat_bin = false;
+ /* Require the GCC-RPM-XO clock to be registered first */
+ xo_clk = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(xo_clk)) {
+ if (PTR_ERR(xo_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get xo clock\n");
+ return PTR_ERR(xo_clk);
+ }
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
if (!res) {
dev_err(&pdev->dev, "Register base not defined\n");
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 4a4ee0f..5aa9914 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -443,13 +443,12 @@
{
u32 cntkctl = arch_timer_get_cntkctl();
- /* Disable user access to the timers */
+ /* Disable user access to the timers and the physical counter */
/* Also disable virtual event stream */
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
| ARCH_TIMER_USR_VT_ACCESS_EN
- | ARCH_TIMER_VIRT_EVT_EN);
-
- cntkctl |= ARCH_TIMER_USR_PCT_ACCESS_EN;
+ | ARCH_TIMER_VIRT_EVT_EN
+ | ARCH_TIMER_USR_PCT_ACCESS_EN);
/* Enable user access to the virtual counter */
if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS))
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 006f723..cb54b5f 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -71,6 +71,8 @@
struct completion video_comp;
bool orientation;
+ bool power_on;
+
atomic_t aborted;
u32 pixel_rate;
@@ -128,6 +130,11 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return;
+ }
+
reinit_completion(&ctrl->idle_comp);
dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
@@ -833,7 +840,7 @@
tries = 0;
old_v_level = ctrl->link->phy_params.v_level;
- while (1) {
+ while (!atomic_read(&ctrl->aborted)) {
drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -960,7 +967,7 @@
ret = -EINVAL;
break;
}
- } while (1);
+ } while (!atomic_read(&ctrl->aborted));
return ret;
}
@@ -1180,6 +1187,11 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return -EINVAL;
+ }
+
ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
@@ -1341,7 +1353,6 @@
atomic_set(&ctrl->aborted, 0);
rate = ctrl->panel->link_info.rate;
- ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
ctrl->catalog->hpd_config(ctrl->catalog, true);
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
@@ -1396,6 +1407,7 @@
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
dp_ctrl_send_phy_test_pattern(ctrl);
+ ctrl->power_on = true;
pr_debug("End-\n");
end:
@@ -1419,6 +1431,7 @@
dp_ctrl_disable_mainlink_clocks(ctrl);
+ ctrl->power_on = false;
pr_debug("DP off done\n");
}
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 0b3d903..054b3c8 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -665,6 +665,8 @@
pr_err("invalid input\n");
len = -EINVAL;
}
+
+ debug->panel->setup_hdr(debug->panel, &c_state->hdr_meta);
end:
return len;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index f2c0a0e..d19058f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,8 @@
#include <linux/of_irq.h>
#include <linux/hdcp_qseecom.h>
+#include "sde_connector.h"
+
#include "msm_drv.h"
#include "dp_usbpd.h"
#include "dp_parser.h"
@@ -87,11 +89,12 @@
struct workqueue_struct *wq;
struct delayed_work hdcp_cb_work;
- struct work_struct connect_work;
+ struct delayed_work connect_work;
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
int hdcp_status;
+ unsigned long audio_status;
};
static const struct of_device_id dp_dt_match[] = {
@@ -99,6 +102,11 @@
{}
};
+static bool dp_display_framework_ready(struct dp_display_private *dp)
+{
+ return dp->dp_display.post_open ? false : true;
+}
+
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
return dp->hdcp.feature_enabled &&
@@ -448,31 +456,30 @@
}
/* if cable is already connected, send notification */
- if (dp_display->is_connected)
- dp_display_send_hpd_event(dp);
+ if (dp->usbpd->hpd_high)
+ queue_delayed_work(dp->wq, &dp->connect_work, HZ * 10);
else
dp_display->post_open = NULL;
-
}
static int dp_display_send_hpd_notification(struct dp_display_private *dp,
bool hpd)
{
+ u32 timeout_sec;
+
dp->dp_display.is_connected = hpd;
- /* in case, framework is not yet up, don't notify hpd */
- if (dp->dp_display.post_open)
- return 0;
+ if (dp_display_framework_ready(dp))
+ timeout_sec = 5;
+ else
+ timeout_sec = 10;
reinit_completion(&dp->notification_comp);
dp_display_send_hpd_event(dp);
- if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
+ if (!wait_for_completion_timeout(&dp->notification_comp,
+ HZ * timeout_sec)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
- /* cancel any pending request */
- dp->ctrl->abort(dp->ctrl);
- dp->aux->abort(dp->aux);
-
return -EINVAL;
}
@@ -497,26 +504,21 @@
rc = dp->panel->read_sink_caps(dp->panel,
dp->dp_display.connector, dp->usbpd->multi_func);
if (rc) {
- if (rc == -ETIMEDOUT) {
- pr_err("Sink cap read failed, skip notification\n");
+ /*
+ * ETIMEDOUT --> cable may have been removed
+ * ENOTCONN --> no downstream device connected
+ */
+ if (rc == -ETIMEDOUT || rc == -ENOTCONN)
goto end;
- } else {
+ else
goto notify;
- }
- }
-
- dp->link->process_request(dp->link);
-
- if (dp_display_is_sink_count_zero(dp)) {
- pr_debug("no downstream devices connected\n");
- rc = -EINVAL;
- goto end;
}
edid = dp->panel->edid_ctrl->edid;
dp->audio_supported = drm_detect_monitor_audio(edid);
+ dp->link->process_request(dp->link);
dp->panel->handle_sink_request(dp->panel);
dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
@@ -573,9 +575,9 @@
if (dp->audio_supported)
dp->audio->off(dp->audio);
- rc = dp_display_send_hpd_notification(dp, false);
+ dp->audio_status = -ENODEV;
- dp->aux->deinit(dp->aux);
+ rc = dp_display_send_hpd_notification(dp, false);
dp->panel->video_test = false;
@@ -602,8 +604,9 @@
dp_display_host_init(dp);
- if (dp->usbpd->hpd_high)
- queue_work(dp->wq, &dp->connect_work);
+ /* check for hpd high and framework ready */
+ if (dp->usbpd->hpd_high && dp_display_framework_ready(dp))
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
end:
return rc;
}
@@ -620,6 +623,8 @@
dp->ctrl->push_idle(dp->ctrl);
dp->ctrl->off(dp->ctrl);
+ dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
dp->power_on = false;
}
@@ -667,6 +672,7 @@
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -678,13 +684,13 @@
{
mutex_lock(&dp->audio->ops_lock);
- if (dp->audio_supported)
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
dp->audio->off(dp->audio);
dp->ctrl->link_maintenance(dp->ctrl);
- if (dp->audio_supported)
- dp->audio->on(dp->audio);
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
+ dp->audio_status = dp->audio->on(dp->audio);
mutex_unlock(&dp->audio->ops_lock);
}
@@ -707,7 +713,7 @@
return;
}
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
return;
}
@@ -753,17 +759,19 @@
return -ENODEV;
}
- if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high) {
+ if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high &&
+ dp->power_on) {
dp->link->process_request(dp->link);
queue_work(dp->wq, &dp->attention_work);
} else if (dp->usbpd->hpd_high) {
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
} else {
/* cancel any pending request */
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -774,7 +782,8 @@
static void dp_display_connect_work(struct work_struct *work)
{
- struct dp_display_private *dp = container_of(work,
+ struct delayed_work *dw = to_delayed_work(work);
+ struct dp_display_private *dp = container_of(dw,
struct dp_display_private, connect_work);
if (dp->dp_display.is_connected) {
@@ -1070,7 +1079,7 @@
if (dp->audio_supported) {
dp->audio->bw_code = dp->link->link_params.bw_code;
dp->audio->lane_count = dp->link->link_params.lane_count;
- dp->audio->on(dp->audio);
+ dp->audio_status = dp->audio->on(dp->audio);
}
dp_display_update_hdcp_info(dp);
@@ -1081,6 +1090,8 @@
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
}
+
+ dp->panel->setup_hdr(dp->panel, NULL);
end:
/* clear framework event notifier */
dp_display->post_open = NULL;
@@ -1125,6 +1136,8 @@
static int dp_display_disable(struct dp_display *dp_display)
{
struct dp_display_private *dp;
+ struct drm_connector *connector;
+ struct sde_connector_state *c_state;
if (!dp_display) {
pr_err("invalid input\n");
@@ -1132,6 +1145,8 @@
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ connector = dp->dp_display.connector;
+ c_state = to_sde_connector_state(connector->state);
mutex_lock(&dp->session_lock);
@@ -1142,6 +1157,15 @@
dp->ctrl->off(dp->ctrl);
dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
+
+ connector->hdr_eotf = 0;
+ connector->hdr_metadata_type_one = 0;
+ connector->hdr_max_luminance = 0;
+ connector->hdr_avg_luminance = 0;
+ connector->hdr_min_luminance = 0;
+
+ memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
dp->power_on = false;
@@ -1252,8 +1276,7 @@
return ret;
}
-
-static int dp_display_pre_kickoff(struct dp_display *dp_display,
+static int dp_display_config_hdr(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr)
{
int rc = 0;
@@ -1266,8 +1289,7 @@
dp = container_of(dp_display, struct dp_display_private, dp_display);
- if (hdr->hdr_supported && dp->panel->hdr_supported(dp->panel))
- rc = dp->panel->setup_hdr(dp->panel, hdr);
+ rc = dp->panel->setup_hdr(dp->panel, hdr);
return rc;
}
@@ -1281,7 +1303,7 @@
}
INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
- INIT_WORK(&dp->connect_work, dp_display_connect_work);
+ INIT_DELAYED_WORK(&dp->connect_work, dp_display_connect_work);
INIT_WORK(&dp->attention_work, dp_display_attention_work);
return 0;
@@ -1308,6 +1330,7 @@
dp->pdev = pdev;
dp->name = "drm_dp";
+ dp->audio_status = -ENODEV;
rc = dp_display_create_workqueue(dp);
if (rc) {
@@ -1332,7 +1355,7 @@
g_dp_display->get_debug = dp_get_debug;
g_dp_display->post_open = dp_display_post_open;
g_dp_display->post_init = dp_display_post_init;
- g_dp_display->pre_kickoff = dp_display_pre_kickoff;
+ g_dp_display->config_hdr = dp_display_config_hdr;
rc = component_add(&pdev->dev, &dp_display_comp_ops);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index c55e6c8..266de5f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,7 @@
int (*request_irq)(struct dp_display *dp_display);
struct dp_debug *(*get_debug)(struct dp_display *dp_display);
void (*post_open)(struct dp_display *dp_display);
- int (*pre_kickoff)(struct dp_display *dp_display,
+ int (*config_hdr)(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr_meta);
void (*post_init)(struct dp_display *dp_display);
};
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 7746b8e..b834230 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -276,18 +276,17 @@
.mode_set = dp_bridge_mode_set,
};
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params)
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state)
{
struct dp_display *dp = display;
- if (!connector || !display || !params) {
+ if (!display || !c_state) {
pr_err("invalid params\n");
return -EINVAL;
}
- return dp->pre_kickoff(dp, params->hdr_meta);
+ return dp->config_hdr(dp, &c_state->hdr_meta);
}
int dp_connector_post_init(struct drm_connector *connector, void *display)
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 89b0a7e..3ca10c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,15 +32,13 @@
};
/**
- * dp_connector_pre_kickoff - callback to perform pre kickoff initialization
- * @connector: Pointer to drm connector structure
+ * dp_connector_config_hdr - callback to configure HDR
* @display: Pointer to private display handle
- * @params: Pointer to kickoff parameters
+ * @c_state: connect state data
* Returns: Zero on success
*/
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params);
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state);
/**
* dp_connector_post_init - callback to perform additional initialization steps
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 0401760..e0aedc0 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -309,12 +309,14 @@
static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
struct drm_connector *connector, bool multi_func)
{
- int rc = 0;
+ int rc = 0, rlen, count, downstream_ports;
+ const int count_len = 1;
struct dp_panel_private *panel;
if (!dp_panel || !connector) {
pr_err("invalid input\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
@@ -327,19 +329,35 @@
dp_panel->max_bw_code)) {
if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
pr_err("DPCD read failed, return early\n");
- return rc;
+ goto end;
}
pr_err("panel dpcd read failed/incorrect, set default params\n");
dp_panel_set_default_link_params(dp_panel);
}
+ downstream_ports = dp_panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+ DP_DWN_STRM_PORT_PRESENT;
+
+ if (downstream_ports) {
+ rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_SINK_COUNT,
+ &count, count_len);
+ if (rlen == count_len) {
+ count = DP_GET_SINK_COUNT(count);
+ if (!count) {
+ pr_err("no downstream ports connected\n");
+ rc = -ENOTCONN;
+ goto end;
+ }
+ }
+ }
+
rc = dp_panel_read_edid(dp_panel, connector);
if (rc) {
pr_err("panel edid read failed, set failsafe mode\n");
return rc;
}
-
- return 0;
+end:
+ return rc;
}
static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
@@ -648,6 +666,7 @@
{
int rc = 0;
struct dp_panel_private *panel;
+ struct dp_catalog_hdr_data *hdr;
if (!dp_panel) {
pr_err("invalid input\n");
@@ -655,11 +674,13 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
if (!panel->custom_edid)
sde_free_edid((void **)&dp_panel->edid_ctrl);
memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
+ memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
panel->panel_on = false;
return rc;
@@ -706,30 +727,6 @@
(panel->minor >= 4 || panel->vscext_supported);
}
-static bool dp_panel_is_validate_hdr_state(struct dp_panel_private *panel,
- struct drm_msm_ext_hdr_metadata *hdr_meta)
-{
- struct drm_msm_ext_hdr_metadata *panel_hdr_meta =
- &panel->catalog->hdr_data.hdr_meta;
-
- if (!hdr_meta)
- goto end;
-
- /* bail out if HDR not active */
- if (hdr_meta->hdr_state == HDR_DISABLED &&
- panel->hdr_state == HDR_DISABLED)
- goto end;
-
- /* bail out if same meta data is received */
- if (hdr_meta->hdr_state == HDR_ENABLED &&
- panel_hdr_meta->eotf == hdr_meta->eotf)
- goto end;
-
- return true;
-end:
- return false;
-}
-
static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
struct drm_msm_ext_hdr_metadata *hdr_meta)
{
@@ -744,14 +741,18 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
- if (!dp_panel_is_validate_hdr_state(panel, hdr_meta))
- goto end;
+ /* use cached meta data in case meta data not provided */
+ if (!hdr_meta) {
+ if (hdr->hdr_meta.hdr_state)
+ goto cached;
+ else
+ goto end;
+ }
panel->hdr_state = hdr_meta->hdr_state;
- hdr = &panel->catalog->hdr_data;
-
hdr->ext_header_byte0 = 0x00;
hdr->ext_header_byte1 = 0x04;
hdr->ext_header_byte2 = 0x1F;
@@ -786,8 +787,9 @@
memcpy(&hdr->hdr_meta, hdr_meta, sizeof(hdr->hdr_meta));
else
memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
-
- panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
+cached:
+ if (panel->panel_on)
+ panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
end:
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d5c4386..df0aad5e 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -939,34 +939,38 @@
struct sde_connector_state *c_state,
void *usr_ptr)
{
+ int rc = 0;
struct drm_connector *connector;
struct drm_msm_ext_hdr_metadata *hdr_meta;
int i;
if (!c_conn || !c_state) {
SDE_ERROR_CONN(c_conn, "invalid args\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
connector = &c_conn->base;
if (!connector->hdr_supported) {
SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n");
- return -ENOTSUPP;
+ rc = -ENOTSUPP;
+ goto end;
}
memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
if (!usr_ptr) {
SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n");
- return 0;
+ goto end;
}
if (copy_from_user(&c_state->hdr_meta,
(void __user *)usr_ptr,
sizeof(*hdr_meta))) {
SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto end;
}
hdr_meta = &c_state->hdr_meta;
@@ -989,7 +993,10 @@
hdr_meta->display_primaries_y[i]);
}
- return 0;
+ if (c_conn->ops.config_hdr)
+ rc = c_conn->ops.config_hdr(c_conn->display, c_state);
+end:
+ return rc;
}
static int sde_connector_atomic_set_property(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 7cf09b7..9c37869 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -241,6 +241,16 @@
*/
int (*cmd_transfer)(void *display, const char *cmd_buf,
u32 cmd_buf_len);
+
+ /**
+ * config_hdr - configure HDR
+ * @display: Pointer to private display handle
+ * @c_state: Pointer to connector state
+ * Returns: Zero on success, negative error code for failures
+ */
+ int (*config_hdr)(void *display,
+ struct sde_connector_state *c_state);
+
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index a6f22c9..442104b 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -513,6 +513,7 @@
struct msm_drm_private *priv;
int i;
int rc;
+ unsigned long irq_flags;
if (!sde_kms) {
SDE_ERROR("invalid sde_kms\n");
@@ -543,6 +544,7 @@
sde_disable_all_irqs(sde_kms);
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
kfree(sde_kms->irq_obj.irq_cb_tbl);
kfree(sde_kms->irq_obj.enable_counts);
kfree(sde_kms->irq_obj.irq_counts);
@@ -550,6 +552,7 @@
sde_kms->irq_obj.enable_counts = NULL;
sde_kms->irq_obj.irq_counts = NULL;
sde_kms->irq_obj.total_irqs = 0;
+ spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
}
static void sde_core_irq_mask(struct irq_data *irqd)
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index b3eb101..73e2af0 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3276,7 +3276,8 @@
* smmu state is attached,
*/
if ((smmu_state->state != DETACHED) &&
- (smmu_state->state != DETACH_ALL_REQ))
+ (smmu_state->state != DETACH_ALL_REQ) &&
+ sde_crtc->enabled)
sde_cp_crtc_apply_properties(crtc);
/*
@@ -4487,28 +4488,41 @@
}
static int _sde_crtc_excl_rect_overlap_check(struct plane_state pstates[],
- int cnt, int curr_cnt, struct sde_rect *excl_rect, int z_pos)
+ int cnt, int curr_cnt, struct sde_rect *excl_rect)
{
struct sde_rect dst_rect, intersect;
int i, rc = -EINVAL;
const struct drm_plane_state *pstate;
- /* start checking from next plane */
- for (i = curr_cnt; i < cnt; i++) {
+ for (i = 0; i < cnt; i++) {
+ if (i == curr_cnt)
+ continue;
+
pstate = pstates[i].drm_pstate;
POPULATE_RECT(&dst_rect, pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h, false);
sde_kms_rect_intersect(&dst_rect, excl_rect, &intersect);
+ /* complete intersection of excl_rect is required */
if (intersect.w == excl_rect->w && intersect.h == excl_rect->h
- /* next plane may be on same z-order */
- && z_pos != pstates[i].stage) {
+ /* intersecting rect should be in another z_order */
+ && pstates[curr_cnt].stage != pstates[i].stage) {
rc = 0;
goto end;
}
}
- SDE_ERROR("excl rect does not find top overlapping rect\n");
+ SDE_ERROR(
+ "no overlapping rect for [%d] z_pos:%d, excl_rect:{%d,%d,%d,%d}\n",
+ i, pstates[curr_cnt].stage,
+ excl_rect->x, excl_rect->y, excl_rect->w, excl_rect->h);
+ for (i = 0; i < cnt; i++) {
+ pstate = pstates[i].drm_pstate;
+ SDE_ERROR("[%d] p:%d, z_pos:%d, src:{%d,%d,%d,%d}\n",
+ i, pstate->plane->base.id, pstates[i].stage,
+ pstate->crtc_x, pstate->crtc_y,
+ pstate->crtc_w, pstate->crtc_h);
+ }
end:
return rc;
}
@@ -4550,9 +4564,9 @@
pstate = pstates[i].drm_pstate;
sde_pstate = to_sde_plane_state(pstate);
if (sde_pstate->excl_rect.w && sde_pstate->excl_rect.h) {
- /* check overlap on all top z-order */
+ /* check overlap on any other z-order */
rc = _sde_crtc_excl_rect_overlap_check(pstates, cnt,
- i + 1, &sde_pstate->excl_rect, pstates[i].stage);
+ i, &sde_pstate->excl_rect);
if (rc)
goto end;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3b5e3f5..92ab669 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -3739,6 +3739,7 @@
SDE_ATRACE_BEGIN("enc_prepare_for_kickoff");
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
+ params->is_primary = sde_enc->disp_info.is_primary;
if (phys) {
if (phys->ops.prepare_for_kickoff) {
rc = phys->ops.prepare_for_kickoff(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 8038eb6..2c84e20 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -54,11 +54,13 @@
/**
* sde_encoder_kickoff_params - info encoder requires at kickoff
* @inline_rotate_prefill: number of lines to prefill for inline rotation
+ * @is_primary: set to true if the display is primary display
* @affected_displays: bitmask, bit set means the ROI of the commit lies within
* the bounds of the physical display at the bit index
*/
struct sde_encoder_kickoff_params {
u32 inline_rotate_prefill;
+ u32 is_primary;
unsigned long affected_displays;
};
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 4a15e6f..69dc385 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -245,9 +245,10 @@
* HW layer requires VSYNC counter of first pixel of tgt VFP line.
* @phys_enc: Pointer to physical encoder
* @rot_fetch_lines: number of line to prefill, or 0 to disable
+ * @is_primary: set true if the display is primary display
*/
static void programmable_rot_fetch_config(struct sde_encoder_phys *phys_enc,
- u32 rot_fetch_lines)
+ u32 rot_fetch_lines, u32 is_primary)
{
struct sde_encoder_phys_vid *vid_enc =
to_sde_encoder_phys_vid(phys_enc);
@@ -264,7 +265,8 @@
!phys_enc->hw_ctl->ops.get_bitmask_intf ||
!phys_enc->hw_ctl->ops.update_pending_flush ||
!vid_enc->hw_intf->ops.setup_rot_start ||
- !phys_enc->sde_kms)
+ !phys_enc->sde_kms ||
+ !is_primary)
return;
timing = &vid_enc->timing_params;
@@ -873,7 +875,8 @@
vid_enc->error_count = 0;
}
- programmable_rot_fetch_config(phys_enc, params->inline_rotate_prefill);
+ programmable_rot_fetch_config(phys_enc,
+ params->inline_rotate_prefill, params->is_primary);
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 545ed65..cdc6a9c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1681,6 +1681,8 @@
if (sde_cfg->has_wb_ubwc)
set_bit(SDE_WB_UBWC, &wb->features);
+ set_bit(SDE_WB_XY_ROI_OFFSET, &wb->features);
+
for (j = 0; j < sde_cfg->mdp_count; j++) {
sde_cfg->mdp[j].clk_ctrls[wb->clk_ctrl].reg_off =
PROP_BITVALUE_ACCESS(prop_value,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 2b0aa37..5d3835c 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -949,7 +949,7 @@
.get_mode_info = dp_connector_get_mode_info,
.post_open = dp_connector_post_open,
.check_status = NULL,
- .pre_kickoff = dp_connector_pre_kickoff,
+ .config_hdr = dp_connector_config_hdr,
.cmd_transfer = NULL,
};
struct msm_display_info info;
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 66fc011..52d45bb 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1307,10 +1307,13 @@
do {
if (!regulator_is_enabled(gmu->cx_gdsc))
return 0;
- cond_resched();
+ usleep_range(10, 100);
} while (!(time_after(jiffies, t)));
+ if (!regulator_is_enabled(gmu->cx_gdsc))
+ return 0;
+
dev_err(&gmu->pdev->dev, "GMU CX gdsc off timeout");
return -ETIMEDOUT;
}
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 22a708e..25b85ab 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,7 +28,7 @@
#include <asm/cacheflush.h>
#include <asm/dma-iommu.h>
-#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS)
+#if defined(CONFIG_IOMMU_TESTS)
static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
{
@@ -170,6 +170,8 @@
u64 phys;
size_t len;
struct list_head list;
+ struct mutex clk_lock;
+ unsigned int clk_count;
};
static int iommu_debug_build_phoney_sg_table(struct device *dev,
@@ -1195,6 +1197,7 @@
return -ENOMEM;
}
+ val = VMID_CP_CAMERA;
if (is_secure && iommu_domain_set_attr(ddev->domain,
DOMAIN_ATTR_SECURE_VMID,
&val)) {
@@ -1485,6 +1488,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -1552,6 +1559,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!ddev->domain) {
pr_err("No domain. Did you already attach?\n");
return -EINVAL;
@@ -1600,6 +1611,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -2046,20 +2061,34 @@
return -EFAULT;
}
+ mutex_lock(&ddev->clk_lock);
switch (buf) {
case '0':
+ if (ddev->clk_count == 0) {
+ dev_err(dev, "Config clocks already disabled\n");
+ break;
+ }
+
+ if (--ddev->clk_count > 0)
+ break;
+
dev_err(dev, "Disabling config clocks\n");
iommu_disable_config_clocks(ddev->domain);
break;
case '1':
+ if (ddev->clk_count++ > 0)
+ break;
+
dev_err(dev, "Enabling config clocks\n");
if (iommu_enable_config_clocks(ddev->domain))
dev_err(dev, "Failed!\n");
break;
default:
dev_err(dev, "Invalid value. Should be 0 or 1.\n");
+ mutex_unlock(&ddev->clk_lock);
return -EINVAL;
}
+ mutex_unlock(&ddev->clk_lock);
return count;
}
@@ -2109,6 +2138,9 @@
if (!of_find_property(dev->of_node, "iommus", NULL))
return 0;
+ if (!of_device_is_compatible(dev->of_node, "iommu-debug-test"))
+ return 0;
+
/* Hold a reference count */
if (!iommu_group_get(dev))
return 0;
@@ -2116,6 +2148,7 @@
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if (!ddev)
return -ENODEV;
+ mutex_init(&ddev->clk_lock);
ddev->dev = dev;
dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
if (!dir) {
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 26e03ba..787bda3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -625,6 +625,15 @@
To compile this driver as a module, choose 'm' here: the module
will be called leds-powernv.
+config LEDS_QTI_TRI_LED
+ tristate "LED support for Qualcomm Technologies, Inc. TRI_LED"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC && PWM && OF
+ help
+ This driver supports the TRI_LED module found in Qualcomm
+ Technologies, Inc. PMIC chips. TRI_LED supports 3 LED drivers
+ at max and each is controlled by a PWM channel used for dimming
+ or blinking.
+
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 5514391..96df61b 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_QTI_TRI_LED) += leds-qti-tri-led.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 1a2aea9..7cf17c9 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -99,11 +99,8 @@
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
-#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
-#define CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua) ((curr_ma * 1000) / ires_ua - 1)
-#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1)
#define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
@@ -317,6 +314,14 @@
FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA
};
+static inline int get_current_reg_code(int target_curr_ma, int ires_ua)
+{
+ if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
+ return 0;
+
+ return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1;
+}
+
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
{
int rc;
@@ -542,7 +547,7 @@
return rc;
if (led->pdata->led1n2_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_low_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base),
@@ -552,7 +557,7 @@
}
if (led->pdata->led1n2_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_mid_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base),
@@ -562,7 +567,7 @@
}
if (led->pdata->led3_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_low_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_LOW(led->base),
@@ -572,7 +577,7 @@
}
if (led->pdata->led3_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_mid_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_MID(led->base),
@@ -992,7 +997,7 @@
}
fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
- fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
+ fnode->current_reg_val = get_current_reg_code(prgm_current_ma,
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
@@ -1103,10 +1108,11 @@
return rc;
}
- /* Iterate over all leds for this switch node */
+ /* Iterate over all active leds for this switch node */
val = 0;
for (i = 0; i < led->num_fnodes; i++)
- if (snode->led_mask & BIT(led->fnode[i].id))
+ if (led->fnode[i].led_on &&
+ snode->led_mask & BIT(led->fnode[i].id))
val |= led->fnode[i].ires_idx << (led->fnode[i].id * 2);
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
@@ -1430,6 +1436,22 @@
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
}
+static inline u8 get_safety_timer_code(u32 duration_ms)
+{
+ if (!duration_ms)
+ return 0;
+
+ return (duration_ms / 10) - 1;
+}
+
+static inline u8 get_vph_droop_thresh_code(u32 val_mv)
+{
+ if (!val_mv)
+ return 0;
+
+ return (val_mv / 100) - 25;
+}
+
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
@@ -1521,8 +1543,9 @@
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
if (!rc) {
- fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) |
- FLASH_LED_SAFETY_TMR_ENABLE);
+ fnode->duration = get_safety_timer_code(val);
+ if (fnode->duration)
+ fnode->duration |= FLASH_LED_SAFETY_TMR_ENABLE;
} else if (rc == -EINVAL) {
if (fnode->type == FLASH_LED_TYPE_FLASH) {
pr_err("Timer duration is required for flash LED\n");
@@ -1968,7 +1991,7 @@
rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
if (!rc) {
led->pdata->vph_droop_threshold =
- VPH_DROOP_THRESH_MV_TO_VAL(val);
+ get_vph_droop_thresh_code(val);
} else if (rc != -EINVAL) {
pr_err("Unable to read VPH droop threshold, rc=%d\n", rc);
return rc;
diff --git a/drivers/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c
new file mode 100644
index 0000000..ab5e876
--- /dev/null
+++ b/drivers/leds/leds-qti-tri-led.c
@@ -0,0 +1,512 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define TRILED_REG_TYPE 0x04
+#define TRILED_REG_SUBTYPE 0x05
+#define TRILED_REG_EN_CTL 0x46
+
+/* TRILED_REG_EN_CTL */
+#define TRILED_EN_CTL_MASK GENMASK(7, 5)
+#define TRILED_EN_CTL_MAX_BIT 7
+
+#define TRILED_TYPE 0x19
+#define TRILED_SUBTYPE_LED3H0L12 0x02
+#define TRILED_SUBTYPE_LED2H0L12 0x03
+#define TRILED_SUBTYPE_LED1H2L12 0x04
+
+#define TRILED_NUM_MAX 3
+
+#define PWM_PERIOD_DEFAULT_NS 1000000
+#define LED_BLINK_ON_MS 125
+#define LED_BLINK_OFF_MS 875
+
+struct pwm_setting {
+ u32 pre_period_ns;
+ u32 period_ns;
+ u32 duty_ns;
+};
+
+struct led_setting {
+ u32 on_ms;
+ u32 off_ms;
+ enum led_brightness brightness;
+ bool blink;
+};
+
+struct qpnp_led_dev {
+ struct led_classdev cdev;
+ struct pwm_device *pwm_dev;
+ struct pwm_setting pwm_setting;
+ struct led_setting led_setting;
+ struct qpnp_tri_led_chip *chip;
+ struct mutex lock;
+ const char *label;
+ const char *default_trigger;
+ u8 id;
+ bool blinking;
+};
+
+struct qpnp_tri_led_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct qpnp_led_dev *leds;
+ struct mutex bus_lock;
+ int num_leds;
+ u16 reg_base;
+ u8 subtype;
+};
+
+static int qpnp_tri_led_read(struct qpnp_tri_led_chip *chip, u16 addr, u8 *val)
+{
+ int rc;
+ unsigned int tmp;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_read(chip->regmap, chip->reg_base + addr, &tmp);
+ if (rc < 0)
+ dev_err(chip->dev, "Read addr 0x%x failed, rc=%d\n", addr, rc);
+ else
+ *val = (u8)tmp;
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_masked_write(struct qpnp_tri_led_chip *chip,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ addr, val, mask, rc);
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int __tri_led_config_pwm(struct qpnp_led_dev *led,
+ struct pwm_setting *pwm)
+{
+ struct pwm_state pstate;
+ int rc;
+
+ pwm_get_state(led->pwm_dev, &pstate);
+ pstate.enabled = !!(pwm->duty_ns != 0);
+ pstate.period = pwm->period_ns;
+ pstate.duty_cycle = pwm->duty_ns;
+ rc = pwm_apply_state(led->pwm_dev, &pstate);
+
+ if (rc < 0)
+ dev_err(led->chip->dev, "Apply PWM state for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+
+ return rc;
+}
+
+static int __tri_led_set(struct qpnp_led_dev *led)
+{
+ int rc = 0;
+ u8 val = 0, mask = 0;
+
+ rc = __tri_led_config_pwm(led, &led->pwm_setting);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ mask |= 1 << (TRILED_EN_CTL_MAX_BIT - led->id);
+
+ if (led->pwm_setting.duty_ns == 0)
+ val = 0;
+ else
+ val = mask;
+
+ rc = qpnp_tri_led_masked_write(led->chip, TRILED_REG_EN_CTL,
+ mask, val);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
+ TRILED_REG_EN_CTL, rc);
+
+ return rc;
+}
+
+static int qpnp_tri_led_set(struct qpnp_led_dev *led)
+{
+ u32 on_ms, off_ms, period_ns, duty_ns;
+ enum led_brightness brightness = led->led_setting.brightness;
+ int rc = 0;
+
+ if (led->led_setting.blink) {
+ on_ms = led->led_setting.on_ms;
+ off_ms = led->led_setting.off_ms;
+ if (on_ms > INT_MAX / NSEC_PER_MSEC)
+ duty_ns = INT_MAX - 1;
+ else
+ duty_ns = on_ms * NSEC_PER_MSEC;
+
+ if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
+ period_ns = INT_MAX;
+ duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
+ } else {
+ period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
+ }
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ } else {
+ /* Use initial period if no blinking is required */
+ period_ns = led->pwm_setting.pre_period_ns;
+
+ if (period_ns > INT_MAX / brightness)
+ duty_ns = (period_ns / LED_FULL) * brightness;
+ else
+ duty_ns = (period_ns * brightness) / LED_FULL;
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ }
+ dev_dbg(led->chip->dev, "PWM settings for %s led: period = %dns, duty = %dns\n",
+ led->cdev.name, period_ns, duty_ns);
+
+ led->pwm_setting.duty_ns = duty_ns;
+ led->pwm_setting.period_ns = period_ns;
+
+ rc = __tri_led_set(led);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "__tri_led_set %s failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ if (led->led_setting.blink) {
+ led->cdev.brightness = LED_FULL;
+ led->blinking = true;
+ } else {
+ led->cdev.brightness = led->led_setting.brightness;
+ led->blinking = false;
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (brightness > LED_FULL)
+ brightness = LED_FULL;
+
+ if (brightness == led->led_setting.brightness &&
+ !led->blinking) {
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ led->led_setting.brightness = brightness;
+ if (!!brightness)
+ led->led_setting.off_ms = 0;
+ else
+ led->led_setting.on_ms = 0;
+ led->led_setting.blink = false;
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+
+ return rc;
+}
+
+static enum led_brightness qpnp_tri_led_get_brightness(
+ struct led_classdev *led_cdev)
+{
+ return led_cdev->brightness;
+}
+
+static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *on_ms, unsigned long *off_ms)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (led->blinking && *on_ms == led->led_setting.on_ms &&
+ *off_ms == led->led_setting.off_ms) {
+ dev_dbg(led_cdev->dev, "Ignore, on/off setting is not changed: on %lums, off %lums\n",
+ *on_ms, *off_ms);
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ if (*on_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = LED_OFF;
+ } else if (*off_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = led->cdev.brightness;
+ } else {
+ led->led_setting.on_ms = *on_ms;
+ led->led_setting.off_ms = *off_ms;
+ led->led_setting.blink = true;
+ }
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+ return rc;
+}
+
+static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
+{
+ struct qpnp_led_dev *led;
+ int rc, i, j;
+
+ for (i = 0; i < chip->num_leds; i++) {
+ led = &chip->leds[i];
+ mutex_init(&led->lock);
+ led->cdev.name = led->label;
+ led->cdev.max_brightness = LED_FULL;
+ led->cdev.brightness_set_blocking = qpnp_tri_led_set_brightness;
+ led->cdev.brightness_get = qpnp_tri_led_get_brightness;
+ led->cdev.blink_set = qpnp_tri_led_set_blink;
+ led->cdev.default_trigger = led->default_trigger;
+ led->cdev.brightness = LED_OFF;
+ led->cdev.blink_delay_on = LED_BLINK_ON_MS;
+ led->cdev.blink_delay_off = LED_BLINK_OFF_MS;
+
+ rc = devm_led_classdev_register(chip->dev, &led->cdev);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
+ led->label, rc);
+ goto destroy;
+ }
+ }
+
+ return 0;
+destroy:
+ for (j = 0; j <= i; j++)
+ mutex_destroy(&chip->leds[i].lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_hw_init(struct qpnp_tri_led_chip *chip)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_TYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_TYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (val != TRILED_TYPE) {
+ dev_err(chip->dev, "invalid subtype(%d)\n", val);
+ return -ENODEV;
+ }
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_SUBTYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_SUBTYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->subtype = val;
+
+ return 0;
+}
+
+static int qpnp_tri_led_parse_dt(struct qpnp_tri_led_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node, *child_node;
+ struct qpnp_led_dev *led;
+ struct pwm_args pargs;
+ const __be32 *addr;
+ int rc, id, i = 0;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ chip->reg_base = be32_to_cpu(addr[0]);
+
+ chip->num_leds = of_get_available_child_count(node);
+ if (chip->num_leds == 0) {
+ dev_err(chip->dev, "No led child node defined\n");
+ return -ENODEV;
+ }
+
+ if (chip->num_leds > TRILED_NUM_MAX) {
+ dev_err(chip->dev, "can't support %d leds(max %d)\n",
+ chip->num_leds, TRILED_NUM_MAX);
+ return -EINVAL;
+ }
+
+ chip->leds = devm_kcalloc(chip->dev, chip->num_leds,
+ sizeof(struct qpnp_led_dev), GFP_KERNEL);
+ if (!chip->leds)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(node, child_node) {
+ rc = of_property_read_u32(child_node, "led-sources", &id);
+ if (rc) {
+ dev_err(chip->dev, "Get led-sources failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (id >= TRILED_NUM_MAX) {
+ dev_err(chip->dev, "only support 0~%d current source\n",
+ TRILED_NUM_MAX - 1);
+ return -EINVAL;
+ }
+
+ led = &chip->leds[i++];
+ led->chip = chip;
+ led->id = id;
+ led->label =
+ of_get_property(child_node, "label", NULL) ? :
+ child_node->name;
+
+ led->pwm_dev =
+ devm_of_pwm_get(chip->dev, child_node, NULL);
+ if (IS_ERR(led->pwm_dev)) {
+ rc = PTR_ERR(led->pwm_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
+ led->label, rc);
+ return rc;
+ }
+
+ pwm_get_args(led->pwm_dev, &pargs);
+ if (pargs.period == 0)
+ led->pwm_setting.pre_period_ns = PWM_PERIOD_DEFAULT_NS;
+ else
+ led->pwm_setting.pre_period_ns = pargs.period;
+
+ led->default_trigger = of_get_property(child_node,
+ "linux,default-trigger", NULL);
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_probe(struct platform_device *pdev)
+{
+ struct qpnp_tri_led_chip *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_tri_led_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ mutex_init(&chip->bus_lock);
+
+ rc = qpnp_tri_led_hw_init(chip);
+ if (rc) {
+ dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
+ goto destroy;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+ rc = qpnp_tri_led_register(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
+ rc);
+ goto destroy;
+ }
+
+ dev_dbg(chip->dev, "Tri-led module with subtype 0x%x is detected\n",
+ chip->subtype);
+ return 0;
+destroy:
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static int qpnp_tri_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&chip->bus_lock);
+ for (i = 0; i < chip->num_leds; i++)
+ mutex_destroy(&chip->leds[i].lock);
+ dev_set_drvdata(chip->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qpnp_tri_led_of_match[] = {
+ { .compatible = "qcom,tri-led",},
+ { },
+};
+
+static struct platform_driver qpnp_tri_led_driver = {
+ .driver = {
+ .name = "qcom,tri-led",
+ .of_match_table = qpnp_tri_led_of_match,
+ },
+ .probe = qpnp_tri_led_probe,
+ .remove = qpnp_tri_led_remove,
+};
+module_platform_driver(qpnp_tri_led_driver);
+
+MODULE_DESCRIPTION("QTI TRI_LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:qpnp-tri-led");
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 69c8cb0..b897813 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -50,8 +50,7 @@
#include <linux/ipc_logging.h>
#include <linux/msm_pcie.h>
-#define PCIE_VENDOR_ID_RCP 0x17cb
-#define PCIE_DEVICE_ID_RCP 0x0106
+#define PCIE_VENDOR_ID_QCOM 0x17cb
#define PCIE20_L1SUB_CONTROL1 0x1E4
#define PCIE20_PARF_DBI_BASE_ADDR 0x350
@@ -63,7 +62,6 @@
#define PCIE_N_SW_RESET(n) (PCS_PORT(n) + 0x00)
#define PCIE_N_POWER_DOWN_CONTROL(n) (PCS_PORT(n) + 0x04)
-#define PCIE_N_PCS_STATUS(n) (PCS_PORT(n) + 0x174)
#define PCIE_GEN3_COM_INTEGLOOP_GAIN1_MODE0 0x0154
#define PCIE_GEN3_L0_DRVR_CTRL0 0x080c
@@ -71,7 +69,6 @@
#define PCIE_GEN3_L0_BIST_ERR_CNT1_STATUS 0x08a8
#define PCIE_GEN3_L0_BIST_ERR_CNT2_STATUS 0x08ac
#define PCIE_GEN3_L0_DEBUG_BUS_STATUS4 0x08bc
-#define PCIE_GEN3_PCIE_PHY_PCS_STATUS 0x1aac
#define PCIE20_PARF_SYS_CTRL 0x00
#define PCIE20_PARF_PM_CTRL 0x20
@@ -590,6 +587,7 @@
uint32_t switch_latency;
uint32_t wr_halt_size;
uint32_t slv_addr_space_size;
+ uint32_t phy_status_offset;
uint32_t cpl_timeout;
uint32_t current_bdf;
uint32_t perst_delay_us_min;
@@ -996,11 +994,7 @@
static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
{
- u32 pos = (dev->max_link_speed == GEN2_SPEED) ?
- PCIE_N_PCS_STATUS(dev->rc_idx) :
- PCIE_GEN3_PCIE_PHY_PCS_STATUS;
-
- if (readl_relaxed(dev->phy + pos) & BIT(6))
+ if (readl_relaxed(dev->phy + dev->phy_status_offset) & BIT(6))
return false;
else
return true;
@@ -1258,6 +1252,8 @@
dev->wr_halt_size);
PCIE_DBG_FS(dev, "slv_addr_space_size: 0x%x\n",
dev->slv_addr_space_size);
+ PCIE_DBG_FS(dev, "phy_status_offset: 0x%x\n",
+ dev->phy_status_offset);
PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
dev->cpl_timeout);
PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
@@ -5812,6 +5808,21 @@
"RC%d: slv-addr-space-size: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].slv_addr_space_size);
+ msm_pcie_dev[rc_idx].phy_status_offset = 0;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,phy-status-offset",
+ &msm_pcie_dev[rc_idx].phy_status_offset);
+ if (ret) {
+ PCIE_ERR(&msm_pcie_dev[rc_idx],
+ "RC%d: failed to get PCIe PHY status offset.\n",
+ rc_idx);
+ goto decrease_rc_num;
+ } else {
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: phy-status-offset: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].phy_status_offset);
+ }
+
msm_pcie_dev[rc_idx].cpl_timeout = 0;
ret = of_property_read_u32((&pdev->dev)->of_node,
"qcom,cpl-timeout",
@@ -6208,10 +6219,10 @@
struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
PCIE_DBG(pcie_dev, "hdr_type %d\n", dev->hdr_type);
- if (dev->hdr_type == 1)
+ if (pci_is_root_bus(dev->bus))
dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
}
-DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_early);
/* Suspend the PCIe link */
@@ -6294,7 +6305,8 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
- if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED)
+ if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED ||
+ !pci_is_root_bus(dev->bus))
return;
spin_lock_irqsave(&pcie_dev->cfg_lock,
@@ -6319,7 +6331,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_suspend);
/* Resume the PCIe link */
@@ -6393,7 +6405,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6405,7 +6417,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume);
static void msm_pcie_fixup_resume_early(struct pci_dev *dev)
@@ -6416,7 +6428,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6427,7 +6439,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume_early);
int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
diff --git a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
index 613bed3..2723a35 100644
--- a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
+++ b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -140,7 +140,7 @@
ipa_rm_it_handles[resource_name].work_in_progress = false;
pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
name = ipa_rm_it_handles[resource_name].w_lock_name;
- snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
+ snprintf(name, MAX_WS_NAME, "IPA_RM%d", resource_name);
wakeup_source_init(pwlock, name);
INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
ipa_rm_inactivity_timer_func);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index c0fe730..90edd2b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -222,7 +222,13 @@
tx_pkt->no_unmap_dma = true;
tx_pkt->sys = sys;
spin_lock_bh(&sys->spinlock);
+ if (unlikely(!sys->nop_pending)) {
+ spin_unlock_bh(&sys->spinlock);
+ kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
+ return;
+ }
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
+ sys->nop_pending = false;
spin_unlock_bh(&sys->spinlock);
memset(&nop_xfer, 0, sizeof(nop_xfer));
@@ -273,6 +279,7 @@
int result;
u32 mem_flag = GFP_ATOMIC;
const struct ipa_gsi_ep_config *gsi_ep_cfg;
+ bool send_nop = false;
if (unlikely(!in_atomic))
mem_flag = GFP_KERNEL;
@@ -410,10 +417,14 @@
}
kfree(gsi_xfer_elem_array);
+ if (sys->use_comm_evt_ring && !sys->nop_pending) {
+ sys->nop_pending = true;
+ send_nop = true;
+ }
spin_unlock_bh(&sys->spinlock);
/* set the timer for sending the NOP descriptor */
- if (sys->use_comm_evt_ring && !hrtimer_active(&sys->db_timer)) {
+ if (send_nop) {
ktime_t time = ktime_set(0, IPA_TX_SEND_COMPL_NOP_DELAY_NS);
IPADBG_LOW("scheduling timer for ch %lu\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index d7d74a3..03e224b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -626,6 +626,7 @@
struct delayed_work switch_to_intr_work;
enum ipa3_sys_pipe_policy policy;
bool use_comm_evt_ring;
+ bool nop_pending;
int (*pyld_hdlr)(struct sk_buff *skb, struct ipa3_sys_context *sys);
struct sk_buff * (*get_skb)(unsigned int len, gfp_t flags);
void (*free_skb)(struct sk_buff *skb);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 1782fdc..9974b87 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1117,7 +1117,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 7, 0, 8, 16, IPA_EE_UC } },
+ { 6, 2, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_USB_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1153,13 +1153,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 9, 1, 8, 16, IPA_EE_UC } },
- [IPA_4_0][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_GROUP_UL_DL,
- true,
- IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
+ { 9, 0, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1196,13 +1190,13 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {7, 0, 8, 16, IPA_EE_UC } },
+ { 7, 9, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ {8, 10, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_WLAN1_CONS] = {
@@ -1210,7 +1204,7 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 18, 3, 6, 9, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_WLAN2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1258,7 +1252,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 22, 3, 17, 17, IPA_EE_UC } },
+ { 22, 1, 17, 17, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_LAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1284,25 +1278,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1318,12 +1312,6 @@
{ 31, 31, 8, 8, IPA_EE_AP } },
/* IPA_4_0_MHI */
- [IPA_4_0_MHI][IPA_CLIENT_USB_PROD] = {
- false, IPA_EP_NOT_ALLOCATED,
- true,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_WAN_PROD] = {
true, IPA_v4_0_MHI_GROUP_DDR,
true,
@@ -1342,12 +1330,6 @@
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_PCIE,
{ 1, 0, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_MHI_GROUP_DDR,
- true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1404,18 +1386,7 @@
QMB_MASTER_SELECT_DDR,
{ 8, 10, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_DPL_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
+
[IPA_4_0_MHI][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
@@ -1470,25 +1441,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 18, 11, 6, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index bc4df04..7a93e0e 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -44,6 +44,20 @@
EXTCON_NONE,
};
+static int msm_ext_disp_find_index(struct extcon_dev *edev,
+ enum msm_ext_disp_type id)
+{
+ int i;
+
+ /* Find the the index of extcon cable in edev->supported_cable */
+ for (i = 0; i < edev->max_supported; i++) {
+ if (edev->supported_cable[i] == id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int msm_ext_disp_extcon_register(struct msm_ext_disp *ext_disp)
{
int ret = 0;
@@ -145,7 +159,8 @@
enum msm_ext_disp_cable_state new_state)
{
int ret = 0;
- int state;
+ int state, index;
+ enum msm_ext_disp_cable_state current_state;
if (!ext_disp->ops) {
pr_err("codec not registered, skip notification\n");
@@ -154,13 +169,27 @@
}
state = ext_disp->audio_sdev.state;
- ret = extcon_set_state_sync(&ext_disp->audio_sdev,
- ext_disp->current_disp, !!new_state);
- pr_debug("Audio state %s %d\n",
- ext_disp->audio_sdev.state == state ?
- "is same" : "switched to",
- ext_disp->audio_sdev.state);
+ index = msm_ext_disp_find_index(&ext_disp->audio_sdev, type);
+ if (index < 0 || index >= ext_disp->audio_sdev.max_supported) {
+ pr_err("invalid index\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (state & BIT(index))
+ current_state = EXT_DISPLAY_CABLE_CONNECT;
+ else
+ current_state = EXT_DISPLAY_CABLE_DISCONNECT;
+
+ if (current_state == new_state) {
+ ret = -EEXIST;
+ pr_debug("same state\n");
+ } else {
+ ret = extcon_set_state_sync(&ext_disp->audio_sdev,
+ ext_disp->current_disp, !!new_state);
+ pr_debug("state changed to %d\n", new_state);
+ }
end:
return ret;
}
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e7d13ae..8e367c5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -350,6 +350,16 @@
To compile this driver as a module, choose M here: the module
will be called pwm-rcar.
+config PWM_QTI_LPG
+ tristate "Qualcomm Technologies, Inc. LPG driver"
+ depends on MFD_SPMI_PMIC && OF
+ help
+ This driver supports the LPG (Light Pulse Generator) module found in
+ Qualcomm Technologies, Inc. PMIC chips. Each LPG channel can be
+ configured to operate in PWM mode to output a fixed amplitude with
+ variable duty cycle or in LUT (Look up table) mode to output PWM
+ signal with a modulated amplitude.
+
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 24c1baf..9453eb0 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
obj-$(CONFIG_PWM_QPNP) += pwm-qpnp.o
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
+obj-$(CONFIG_PWM_QTI_LPG) += pwm-qti-lpg.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
new file mode 100644
index 0000000..328f4b6
--- /dev/null
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -0,0 +1,570 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define REG_SIZE_PER_LPG 0x100
+
+#define REG_LPG_PWM_SIZE_CLK 0x41
+#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
+#define REG_LPG_PWM_TYPE_CONFIG 0x43
+#define REG_LPG_PWM_VALUE_LSB 0x44
+#define REG_LPG_PWM_VALUE_MSB 0x45
+#define REG_LPG_ENABLE_CONTROL 0x46
+#define REG_LPG_PWM_SYNC 0x47
+
+/* REG_LPG_PWM_SIZE_CLK */
+#define LPG_PWM_SIZE_MASK BIT(4)
+#define LPG_PWM_SIZE_SHIFT 4
+#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
+
+/* REG_LPG_PWM_FREQ_PREDIV_CLK */
+#define LPG_PWM_FREQ_PREDIV_MASK GENMASK(6, 5)
+#define LPG_PWM_FREQ_PREDIV_SHIFT 5
+#define LPG_PWM_FREQ_EXPONENT_MASK GENMASK(2, 0)
+
+/* REG_LPG_PWM_TYPE_CONFIG */
+#define LPG_PWM_EN_GLITCH_REMOVAL_MASK BIT(5)
+
+/* REG_LPG_PWM_VALUE_LSB */
+#define LPG_PWM_VALUE_LSB_MASK GENMASK(7, 0)
+
+/* REG_LPG_PWM_VALUE_MSB */
+#define LPG_PWM_VALUE_MSB_MASK BIT(0)
+
+/* REG_LPG_ENABLE_CONTROL */
+#define LPG_EN_LPG_OUT_BIT BIT(7)
+#define LPG_PWM_SRC_SELECT_MASK BIT(2)
+#define LPG_PWM_SRC_SELECT_SHIFT 2
+#define LPG_EN_RAMP_GEN_MASK BIT(1)
+#define LPG_EN_RAMP_GEN_SHIFT 1
+
+/* REG_LPG_PWM_SYNC */
+#define LPG_PWM_VALUE_SYNC BIT(0)
+
+#define NUM_PWM_SIZE 2
+#define NUM_PWM_CLK 3
+#define NUM_CLK_PREDIV 4
+#define NUM_PWM_EXP 8
+
+enum {
+ LUT_PATTERN = 0,
+ PWM_OUTPUT,
+};
+
+static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
+static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
+static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
+static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+struct lpg_pwm_config {
+ u32 pwm_size;
+ u32 pwm_clk;
+ u32 prediv;
+ u32 clk_exp;
+ u16 pwm_value;
+ u32 best_period_ns;
+};
+
+struct qpnp_lpg_channel {
+ struct qpnp_lpg_chip *chip;
+ struct lpg_pwm_config pwm_config;
+ u32 lpg_idx;
+ u32 reg_base;
+ u8 src_sel;
+ int current_period_ns;
+ int current_duty_ns;
+};
+
+struct qpnp_lpg_chip {
+ struct pwm_chip pwm_chip;
+ struct regmap *regmap;
+ struct device *dev;
+ struct qpnp_lpg_channel *lpgs;
+ struct mutex bus_lock;
+ u32 num_lpgs;
+};
+
+static int qpnp_lpg_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ lpg->reg_base + addr, val, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_lpg_masked_write(struct qpnp_lpg_channel *lpg,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_update_bits(lpg->chip->regmap, lpg->reg_base + addr,
+ mask, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ lpg->reg_base + addr, val, mask, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm) {
+
+ struct qpnp_lpg_chip *chip = container_of(pwm_chip,
+ struct qpnp_lpg_chip, pwm_chip);
+ u32 hw_idx = pwm->hwpwm;
+
+ if (hw_idx >= chip->num_lpgs) {
+ dev_err(chip->dev, "hw index %d out of range [0-%d]\n",
+ hw_idx, chip->num_lpgs - 1);
+ return NULL;
+ }
+
+ return &chip->lpgs[hw_idx];
+}
+
+static int __find_index_in_array(int member, const int array[], int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ if (member == array[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
+{
+ int rc;
+ u8 val, mask;
+ int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx;
+
+ pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size,
+ pwm_size, ARRAY_SIZE(pwm_size));
+ pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk,
+ clk_freq_hz, ARRAY_SIZE(clk_freq_hz));
+ prediv_idx = __find_index_in_array(lpg->pwm_config.prediv,
+ clk_prediv, ARRAY_SIZE(clk_prediv));
+ clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp,
+ pwm_exponent, ARRAY_SIZE(pwm_exponent));
+
+ if (pwm_size_idx < 0 || pwm_clk_idx < 0
+ || prediv_idx < 0 || clk_exp_idx < 0)
+ return -EINVAL;
+
+ /* pwm_clk_idx is 1 bit lower than the register value */
+ pwm_clk_idx += 1;
+ val = pwm_size_idx << LPG_PWM_SIZE_SHIFT | pwm_clk_idx;
+ mask = LPG_PWM_SIZE_MASK | LPG_PWM_CLK_FREQ_SEL_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = prediv_idx << LPG_PWM_FREQ_PREDIV_SHIFT | clk_exp_idx;
+ mask = LPG_PWM_FREQ_PREDIV_MASK | LPG_PWM_FREQ_EXPONENT_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_FREQ_PREDIV_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_FREQ_PREDIV_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_LSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value >> 8;
+ mask = LPG_PWM_VALUE_MSB_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = LPG_PWM_VALUE_SYNC;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_SYNC, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SYNC failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void __qpnp_lpg_calc_pwm_period(int period_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ struct lpg_pwm_config configs[NUM_PWM_SIZE];
+ int i, j, m, n;
+ int tmp1, tmp2;
+ int clk_period_ns = 0, pwm_clk_period_ns;
+ int clk_delta_ns = INT_MAX, min_clk_delta_ns = INT_MAX;
+ int pwm_period_delta = INT_MAX, min_pwm_period_delta = INT_MAX;
+ int pwm_size_step;
+
+ /*
+ * (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC
+ * pwm_period = ---------------------------------------------------
+ * clk_freq_hz
+ *
+ * Searching the closest settings for the requested PWM period.
+ */
+ for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+ pwm_clk_period_ns = period_ns >> pwm_size[n];
+ for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
+ for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
+ for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) {
+ tmp1 = 1 << pwm_exponent[m];
+ tmp1 *= clk_prediv[j];
+ tmp2 = NSEC_PER_SEC / clk_freq_hz[i];
+
+ clk_period_ns = tmp1 * tmp2;
+
+ clk_delta_ns = abs(pwm_clk_period_ns
+ - clk_period_ns);
+ /*
+ * Find the closest setting for
+ * PWM frequency predivide value
+ */
+ if (clk_delta_ns < min_clk_delta_ns) {
+ min_clk_delta_ns
+ = clk_delta_ns;
+ configs[n].pwm_clk
+ = clk_freq_hz[i];
+ configs[n].prediv
+ = clk_prediv[j];
+ configs[n].clk_exp
+ = pwm_exponent[m];
+ configs[n].pwm_size
+ = pwm_size[n];
+ configs[n].best_period_ns
+ = clk_period_ns;
+ }
+ }
+ }
+ }
+
+ configs[n].best_period_ns *= 1 << pwm_size[n];
+ /* Find the closest setting for PWM period */
+ if (min_clk_delta_ns < INT_MAX >> pwm_size[n])
+ pwm_period_delta = min_clk_delta_ns << pwm_size[n];
+ else
+ pwm_period_delta = INT_MAX;
+ if (pwm_period_delta < min_pwm_period_delta) {
+ min_pwm_period_delta = pwm_period_delta;
+ memcpy(pwm_config, &configs[n],
+ sizeof(struct lpg_pwm_config));
+ }
+ }
+
+ /* Larger PWM size can achieve better resolution for PWM duty */
+ for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) {
+ if (pwm_config->pwm_size >= pwm_size[n])
+ break;
+ pwm_size_step = pwm_size[n] - pwm_config->pwm_size;
+ if (pwm_config->clk_exp >= pwm_size_step) {
+ pwm_config->pwm_size = pwm_size[n];
+ pwm_config->clk_exp -= pwm_size_step;
+ }
+ }
+ pr_debug("PWM setting for period_ns %d: pwm_clk = %dHZ, prediv = %d, exponent = %d, pwm_size = %d\n",
+ period_ns, pwm_config->pwm_clk, pwm_config->prediv,
+ pwm_config->clk_exp, pwm_config->pwm_size);
+ pr_debug("Actual period: %dns\n", pwm_config->best_period_ns);
+}
+
+static void __qpnp_lpg_calc_pwm_duty(int period_ns, int duty_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ u16 pwm_value, max_pwm_value;
+
+ if ((1 << pwm_config->pwm_size) > (INT_MAX / duty_ns))
+ pwm_value = duty_ns / (period_ns >> pwm_config->pwm_size);
+ else
+ pwm_value = (duty_ns << pwm_config->pwm_size) / period_ns;
+
+ max_pwm_value = (1 << pwm_config->pwm_size) - 1;
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ pwm_config->pwm_value = pwm_value;
+}
+
+static int qpnp_lpg_pwm_config(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (duty_ns > period_ns) {
+ dev_err(pwm_chip->dev, "Duty %dns is larger than period %dns\n",
+ duty_ns, period_ns);
+ return -EINVAL;
+ }
+
+ if (period_ns != lpg->current_period_ns)
+ __qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+
+ if (period_ns != lpg->current_period_ns ||
+ duty_ns != lpg->current_duty_ns)
+ __qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
+
+ rc = qpnp_lpg_set_pwm_config(lpg);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
+{
+ struct qpnp_lpg_channel *lpg;
+ struct lpg_pwm_config *cfg;
+ struct pwm_device *pwm;
+ int i;
+
+ for (i = 0; i < pwm_chip->npwm; i++) {
+ pwm = &pwm_chip->pwms[i];
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+ seq_printf(s, "LPG %d is requested by %s\n",
+ lpg->lpg_idx + 1, pwm->label);
+ } else {
+ seq_printf(s, "LPG %d is free\n",
+ lpg->lpg_idx + 1);
+ continue;
+ }
+
+ if (pwm_is_enabled(pwm)) {
+ seq_puts(s, " enabled\n");
+ } else {
+ seq_puts(s, " disabled\n");
+ continue;
+ }
+
+ cfg = &lpg->pwm_config;
+ seq_printf(s, " clk = %dHz\n", cfg->pwm_clk);
+ seq_printf(s, " pwm_size = %d\n", cfg->pwm_size);
+ seq_printf(s, " prediv = %d\n", cfg->prediv);
+ seq_printf(s, " exponent = %d\n", cfg->clk_exp);
+ seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
+ seq_printf(s, " Requested period: %dns, best period = %dns\n",
+ pwm_get_period(pwm), cfg->best_period_ns);
+ }
+}
+#endif
+
+static const struct pwm_ops qpnp_lpg_pwm_ops = {
+ .config = qpnp_lpg_pwm_config,
+ .enable = qpnp_lpg_pwm_enable,
+ .disable = qpnp_lpg_pwm_disable,
+#ifdef CONFIG_DEBUG_FS
+ .dbg_show = qpnp_lpg_pwm_dbg_show,
+#endif
+ .owner = THIS_MODULE,
+};
+
+static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
+{
+ int rc = 0, i;
+ u64 base, length;
+ const __be32 *addr;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ base = be32_to_cpu(addr[0]);
+ length = be32_to_cpu(addr[1]);
+
+ chip->num_lpgs = length / REG_SIZE_PER_LPG;
+ chip->lpgs = devm_kcalloc(chip->dev, chip->num_lpgs,
+ sizeof(*chip->lpgs), GFP_KERNEL);
+ if (!chip->lpgs)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->num_lpgs; i++) {
+ chip->lpgs[i].chip = chip;
+ chip->lpgs[i].lpg_idx = i;
+ chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
+ chip->lpgs[i].src_sel = PWM_OUTPUT;
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct qpnp_lpg_chip *chip;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_lpg_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+
+ mutex_init(&chip->bus_lock);
+ chip->pwm_chip.dev = chip->dev;
+ chip->pwm_chip.base = -1;
+ chip->pwm_chip.npwm = chip->num_lpgs;
+ chip->pwm_chip.ops = &qpnp_lpg_pwm_ops;
+
+ rc = pwmchip_add(&chip->pwm_chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+ mutex_destroy(&chip->bus_lock);
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_remove(struct platform_device *pdev)
+{
+ struct qpnp_lpg_chip *chip = dev_get_drvdata(&pdev->dev);
+ int rc = 0;
+
+ rc = pwmchip_remove(&chip->pwm_chip);
+ if (rc < 0)
+ dev_err(chip->dev, "Remove pwmchip failed, rc=%d\n", rc);
+
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static const struct of_device_id qpnp_lpg_of_match[] = {
+ { .compatible = "qcom,pwm-lpg",},
+ { },
+};
+
+static struct platform_driver qpnp_lpg_driver = {
+ .driver = {
+ .name = "qcom,pwm-lpg",
+ .of_match_table = qpnp_lpg_of_match,
+ },
+ .probe = qpnp_lpg_probe,
+ .remove = qpnp_lpg_remove,
+};
+module_platform_driver(qpnp_lpg_driver);
+
+MODULE_DESCRIPTION("QTI LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("pwm:pwm-lpg");
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index a28644d..2fb95fe 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2262,8 +2262,6 @@
set_bit(ICNSS_FW_READY, &penv->state);
- icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
-
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
icnss_hw_power_off(penv);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 63ce902..d5e79f1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -55,6 +55,12 @@
*/
#define CFG80211_ROAMED_API_UNIFIED 1
+/* Indicate backport support for DBS scan control */
+#define CFG80211_SCAN_DBS_CONTROL_SUPPORT 1
+
+/* Indicate backport support for per chain rssi scan */
+#define CFG80211_SCAN_PER_CHAIN_RSSI_SUPPORT 1
+
/**
* DOC: Introduction
*
@@ -1742,6 +1748,8 @@
* by %parent_bssid.
* @parent_bssid: the BSS according to which %parent_tsf is set. This is set to
* the BSS that requested the scan in which the beacon/probe was received.
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
*/
struct cfg80211_inform_bss {
struct ieee80211_channel *chan;
@@ -1750,6 +1758,8 @@
u64 boottime_ns;
u64 parent_tsf;
u8 parent_bssid[ETH_ALEN] __aligned(2);
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
};
/**
@@ -1793,6 +1803,8 @@
* that holds the beacon data. @beacon_ies is still valid, of course, and
* points to the same data as hidden_beacon_bss->beacon_ies in that case.
* @signal: signal strength value (type depends on the wiphy's signal_type)
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/
struct cfg80211_bss {
@@ -1811,6 +1823,8 @@
u16 capability;
u8 bssid[ETH_ALEN];
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 priv[0] __aligned(sizeof(void *));
};
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index ad38816..31b4cce 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,6 @@
#endif
enum icnss_uevent {
- ICNSS_UEVENT_FW_READY,
ICNSS_UEVENT_FW_CRASHED,
ICNSS_UEVENT_FW_DOWN,
};
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
index 7ba1817..52b52c4 100644
--- a/include/sound/wcd-dsp-mgr.h
+++ b/include/sound/wcd-dsp-mgr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -80,6 +80,7 @@
/* Software generated signal indicating debug dumps to be collected */
WDSP_DEBUG_DUMP,
+ WDSP_DEBUG_DUMP_INTERNAL,
};
/*
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9fbdc11..3092188 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3782,6 +3782,9 @@
* @NL80211_BSS_PARENT_BSSID. (u64).
* @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
* is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3805,6 +3808,7 @@
NL80211_BSS_PAD,
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4835,6 +4839,27 @@
* RSSI threshold values to monitor rather than exactly one threshold.
* @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
* authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4855,6 +4880,16 @@
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
NL80211_EXT_FEATURE_CQM_RSSI_LIST,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -4915,6 +4950,10 @@
* of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
* requests.
*
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
* @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
@@ -4931,12 +4970,29 @@
* locally administered 1, multicast 0) is assumed.
* This flag must not be requested when the feature isn't supported, check
* the nl80211 feature flags for the device.
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
NL80211_SCAN_FLAG_AP = 1<<2,
NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
};
/**
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 59fcdbe..7b02ae6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1051,8 +1051,13 @@
*/
static struct rq *__migrate_task(struct rq *rq, struct task_struct *p, int dest_cpu)
{
- if (unlikely(!cpu_active(dest_cpu)))
- return rq;
+ if (p->flags & PF_KTHREAD) {
+ if (unlikely(!cpu_online(dest_cpu)))
+ return rq;
+ } else {
+ if (unlikely(!cpu_active(dest_cpu)))
+ return rq;
+ }
/* Affinity changed (again). */
if (!cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p)))
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 645b472..73f11c4 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2178,9 +2178,8 @@
* the rt_loop_next will cause the iterator to perform another scan.
*
*/
-static int rto_next_cpu(struct rq *rq)
+static int rto_next_cpu(struct root_domain *rd)
{
- struct root_domain *rd = rq->rd;
int next;
int cpu;
@@ -2256,7 +2255,7 @@
* Otherwise it is finishing up and an ipi needs to be sent.
*/
if (rq->rd->rto_cpu < 0)
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rq->rd);
raw_spin_unlock(&rq->rd->rto_lock);
@@ -2269,6 +2268,8 @@
/* Called from hardirq context */
void rto_push_irq_work_func(struct irq_work *work)
{
+ struct root_domain *rd =
+ container_of(work, struct root_domain, rto_push_work);
struct rq *rq;
int cpu;
@@ -2284,18 +2285,18 @@
raw_spin_unlock(&rq->lock);
}
- raw_spin_lock(&rq->rd->rto_lock);
+ raw_spin_lock(&rd->rto_lock);
/* Pass the IPI to the next rt overloaded queue */
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rd);
- raw_spin_unlock(&rq->rd->rto_lock);
+ raw_spin_unlock(&rd->rto_lock);
if (cpu < 0)
return;
/* Try the next RT overloaded CPU */
- irq_work_queue_on(&rq->rd->rto_push_work, cpu);
+ irq_work_queue_on(&rd->rto_push_work, cpu);
}
#endif /* HAVE_RT_PUSH_IPI */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d8387b1..8f9bd38 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6701,8 +6701,17 @@
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
+ if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_SPAN) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_POWER) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_HIGH_ACCURACY) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN))) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -7585,6 +7594,11 @@
intbss->ts_boottime, NL80211_BSS_PAD))
goto nla_put_failure;
+ if (!nl80211_put_signal(msg, intbss->pub.chains,
+ intbss->pub.chain_signal,
+ NL80211_BSS_CHAIN_SIGNAL))
+ goto nla_put_failure;
+
switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 35ad69f..ad8611b 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -904,6 +904,9 @@
found->ts = tmp->ts;
found->ts_boottime = tmp->ts_boottime;
found->parent_tsf = tmp->parent_tsf;
+ found->pub.chains = tmp->pub.chains;
+ memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
+ IEEE80211_MAX_CHAINS);
ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
} else {
struct cfg80211_internal_bss *new;
@@ -1156,6 +1159,8 @@
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
tmp.ts_boottime = data->boottime_ns;
tmp.parent_tsf = data->parent_tsf;
+ tmp.pub.chains = data->chains;
+ memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=