summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt4
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-ba.txt41
-rw-r--r--Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt14
-rw-r--r--Documentation/devicetree/bindings/regulator/max20010.txt77
-rw-r--r--Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt1
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-ba.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts7
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi67
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi21
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-vidc.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts3
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-vidc.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi1
-rw-r--r--arch/arm64/configs/msmcortex_defconfig1
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig2
-rw-r--r--arch/arm64/configs/sdm660_defconfig3
-rw-r--r--drivers/base/firmware_class.c1
-rw-r--r--drivers/char/adsprpc.c3
-rw-r--r--drivers/char/diag/diag_dci.c5
-rw-r--r--drivers/char/diag/diag_memorydevice.c4
-rw-r--r--drivers/char/diag/diagchar.h7
-rw-r--r--drivers/char/diag/diagchar_core.c18
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c71
-rw-r--r--drivers/char/diag/diagfwd_peripheral.h6
-rw-r--r--drivers/clk/msm/clock-osm.c174
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.c255
-rw-r--r--drivers/clk/qcom/clk-regmap-mux-div.h65
-rw-r--r--drivers/crypto/msm/qcrypto.c29
-rw-r--r--drivers/gpu/drm/msm/Makefile9
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c426
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h113
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c34
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c121
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c994
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c827
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h164
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c41
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h36
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c80
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_i2c.c81
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_util.c143
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c48
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h14
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c10
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h7
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c53
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c56
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h18
-rw-r--r--drivers/gpu/drm/msm/msm_rd.c61
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c1
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.h1
-rw-r--r--drivers/gpu/drm/msm/msm_smmu.c28
-rw-r--r--drivers/gpu/drm/msm/msm_submitqueue.c125
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.c107
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp.h84
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp_1x.c1722
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c25
-rw-r--r--drivers/gpu/msm/kgsl.c1
-rw-r--r--drivers/gpu/msm/kgsl_device.h5
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c134
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c12
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c91
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c146
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h3
-rw-r--r--drivers/media/platform/msm/vidc/msm_smem.c15
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c28
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c62
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c23
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.h4
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c277
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.h2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_internal.h4
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c2
-rw-r--r--drivers/misc/hdcp.c303
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c58
-rw-r--r--drivers/misc/qseecom.c24
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/core/sd.c5
-rw-r--r--drivers/mmc/host/sdhci-msm.c10
-rw-r--r--drivers/mmc/host/sdhci-msm.h2
-rw-r--r--drivers/mmc/host/sdhci.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c20
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c12
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h2
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.h6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c71
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_utils.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c53
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c5
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c9
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h3
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c12
-rw-r--r--drivers/power/supply/qcom/fg-core.h1
-rw-r--r--drivers/power/supply/qcom/fg-util.c41
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c36
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/max20010-regulator.c490
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c21
-rw-r--r--drivers/scsi/ufs/ufshcd.c11
-rw-r--r--drivers/soc/qcom/glink.c1
-rw-r--r--drivers/soc/qcom/icnss.c307
-rw-r--r--drivers/soc/qcom/icnss_utils.c14
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c5
-rwxr-xr-xdrivers/soundwire/soundwire.c71
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c14
-rw-r--r--drivers/thermal/msm_thermal.c2
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fbdev/msm/mdss.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c9
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h36
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c25
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c57
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.h2
-rw-r--r--drivers/video/msm/ba/Kconfig12
-rw-r--r--drivers/video/msm/ba/Makefile5
-rw-r--r--drivers/video/msm/ba/msm_ba.c892
-rw-r--r--drivers/video/msm/ba/msm_ba_common.c645
-rw-r--r--drivers/video/msm/ba/msm_ba_common.h40
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.c223
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.h84
-rw-r--r--drivers/video/msm/ba/msm_ba_internal.h220
-rw-r--r--drivers/video/msm/ba/msm_v4l2_ba.c615
-rw-r--r--include/dt-bindings/regulator/max20010.h20
-rw-r--r--include/linux/hdcp_qseecom.h6
-rw-r--r--include/linux/mmc/host.h2
-rwxr-xr-xinclude/linux/soundwire/soundwire.h6
-rw-r--r--include/media/msm_ba.h81
-rw-r--r--include/soc/qcom/icnss.h9
-rw-r--r--include/soc/qcom/minidump.h5
-rw-r--r--include/uapi/drm/msm_drm.h25
-rw-r--r--include/uapi/linux/v4l2-controls.h6
-rw-r--r--include/uapi/linux/videodev2.h32
-rw-r--r--net/core/dev.c37
-rw-r--r--net/wireless/db.txt6
-rw-r--r--sound/soc/codecs/wsa881x.c85
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c1
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c11
172 files changed, 10875 insertions, 1285 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 1f8458cd0659..cc55f6e2bfa0 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -187,6 +187,10 @@ Optional properties:
"bl_ctrl_wled" = Backlight controlled by WLED.
"bl_ctrl_dcs" = Backlight controlled by DCS commands.
other: Unknown backlight control. (default)
+- qcom,mdss-dsi-bl-dcs-command-state: A string that specifies the ctrl state for sending brightness
+ controlling commands, this is only available when backlight is controlled by DCS commands.
+ "dsi_lp_mode" = DSI low power mode (default).
+ "dsi_hs_mode" = DSI high speed mode.
- qcom,mdss-dsi-bl-pwm-pmi: Boolean to indicate that PWM control is through second pmic chip.
- qcom,mdss-dsi-bl-pmic-bank-select: LPG channel for backlight.
Requred if blpmiccontroltype is PWM
diff --git a/Documentation/devicetree/bindings/media/video/msm-ba.txt b/Documentation/devicetree/bindings/media/video/msm-ba.txt
new file mode 100644
index 000000000000..9a6fe4d7e8ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ba.txt
@@ -0,0 +1,41 @@
+* Qualcomm Technologies Inc MSM BA
+
+[Root level node]
+==================
+Required properties:
+- compatible: Must be "qcom,msm-ba".
+
+[Subnode]
+==========
+- qcom,ba-input-profile-#: Defines child nodes for the profiles supported
+ by BA driver. Each profile should have properties "qcom,type",
+ "qcom,name", "qcom,ba-input", "qcom,ba-output", "qcom,sd-name",
+ "qcom,ba-node" and "qcom,user-type".
+Required properties:
+- qcom,type: Input type such as CVBS(0), HDMI(4) etc as defined in BA driver.
+ This property is of type u32.
+- qcom,name: Name of the input type. This property is of type string.
+- qcom,ba-input: BA input id supported by a bridge chip for this profile.
+ This property is of type u32.
+- qcom,ba-output: BA output id for the profile. This property is of type u32.
+- qcom,sd-name: Name of the sub-device driver associated with this profile.
+ This property is of type string.
+- qcom,ba-node: Defines the ba node id. This is the avdevice node used by camera
+ for this profile. This property is of type u32.
+- qcom,user-type: This property defines how the profile is being used. If this
+ profile is used by kernel it is set to 0 and if used by userspace
+ it is set to 1. This property is of type u32.
+Example:
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
diff --git a/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt b/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt
new file mode 100644
index 000000000000..8d5f55d7a8ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt
@@ -0,0 +1,14 @@
+MSM HDCP driver
+
+Standalone driver managing HDCP related communications
+between TZ and HLOS for MSM chipset.
+
+Required properties:
+
+compatible = "qcom,msm-hdcp";
+
+Example:
+
+qcom_msmhdcp: qcom,msm_hdcp {
+ compatible = "qcom,msm-hdcp";
+};
diff --git a/Documentation/devicetree/bindings/regulator/max20010.txt b/Documentation/devicetree/bindings/regulator/max20010.txt
new file mode 100644
index 000000000000..3dd8f6d1cf19
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max20010.txt
@@ -0,0 +1,77 @@
+Binding for Maxim MAX20010 regulator
+
+MAX20010 is a synchronous step-down converter. It is able to deliver upto 6A
+with 2 different programmable output voltages from 0.5V to 1.27V in 10mV steps
+and from 0.625V to 1.5875V in 12.5mV steps. It supports synchronous
+rectification and automatic PWM/PFM transitions.
+
+The MAX20010 interface is via I2C bus.
+
+=======================
+Supported Properties
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: should be "maxim,max20010".
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: The device 8-bit I2C address.
+
+- vin-supply
+ Usage: optional
+ Value type: <phandle>
+ Definition: This is the phandle for the parent regulator. Typically used
+ for EN pin control of the buck.
+
+- regulator-initial-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: The regulator operating mode. Should be either
+ "MAX20010_OPMODE_SYNC" or "MAX20010_OPMODE_FPWM".
+ These constants are defined in file
+ include/dt-bindings/regulator/max20010.h
+
+- maxim,vrange-sel
+ Usage: optional
+ Value type: <u32>
+ Definition: Integer value specifies the voltage range to be used.
+ Supported values are 0 or 1.
+ Value 0 supports voltage range from 0.5V to 1.27V in 10mV
+ steps. Value 1 supports voltage range from 0.625V to 1.5875V
+ in 12.5mV steps.
+
+- maxim,soft-start-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for soft-start operation of the buck. Supported values are
+ 5500, 11000, 22000 and 44000.
+
+- maxim,dvs-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for buck dynamic voltage scaling operations. Supported
+ values are 5500, 11000, 22000 and 44000.
+
+=======
+Example
+=======
+
+ i2c_0 {
+ max20010-regulator@74 {
+ compatible = "maxim,max20010";
+ reg = <0x74>;
+ vin-supply = <&parent_reg>;
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1270000>;
+ regulator-initial-mode = <MAX20010_OPMODE_SYNC>;
+ maxim,vrange-sel = <0>;
+ maxim,soft-start-slew-rate = <5500>;
+ maxim,dvs-slew-rate = <5500>;
+ }
+ }
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 8b99dbce871b..9e6dd4905ca9 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -58,6 +58,7 @@ Optional properties:
- pinctrl-names, pinctrl-0, pinctrl-1,.. pinctrl-n: Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
for these optional properties
+- non-removable : defines if the connected ufs device is not removable
Note: If above properties are not defined it can be assumed that the supply
diff --git a/arch/arm/boot/dts/qcom/apq8096-ba.dtsi b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
new file mode 100644
index 000000000000..e6524593e502
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
@@ -0,0 +1,18 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ msm_ba: qcom,ba {
+ compatible = "qcom,msm-ba";
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
index bfc6f210a0bb..e731c7edd518 100644
--- a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
@@ -12,6 +12,7 @@
#include "msm8996-pinctrl.dtsi"
#include "apq8096-camera-sensor-dragonboard.dtsi"
+#include "apq8096-ba.dtsi"
/ {
bluetooth: bt_qca6174 {
diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
index 9c4ff9f184e7..fee184663336 100644
--- a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts
@@ -42,15 +42,16 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
- adv7533@3d {
- status = "disabled";
- };
adv7533@39 {
status = "disabled";
};
};
};
+&dsi_adv_7533_2 {
+ /delete-property/ qcom,dsi-display-active;
+};
+
&pil_modem {
pinctrl-names = "default";
pinctrl-0 = <&modem_mux>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
index 6d91e72851ec..5aa2e1ee8316 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi
@@ -51,7 +51,6 @@
39 01 00 00 78 00 03 f0 a5 a5
39 01 00 00 00 00 02 35 00
39 01 00 00 00 00 02 53 20
- 39 01 00 00 00 00 02 51 60
05 01 00 00 05 00 02 29 00];
qcom,mdss-dsi-off-command = [05 01 00 00 3c 00 02 28 00
05 01 00 00 b4 00 02 10 00];
@@ -136,6 +135,7 @@
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-dcs-command-state = "dsi_hs_mode";
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <255>;
qcom,mdss-pan-physical-width-dimension = <68>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index a600008341c2..3c9b8e445ff3 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -1098,17 +1098,58 @@
pinctrl-0 = <&quat_tdm_dout_active>;
pinctrl-1 = <&quat_tdm_dout_sleep>;
};
+
+ qcom,adv7481@70 {
+ compatible = "qcom,adv7481";
+ reg = <0x70 0xff>;
+ cam_vdig-supply = <&pm8994_s3>;
+ /* Cameras powered by PMIC: */
+ cam_vio-supply = <&pm8994_lvs1>;
+ cam_vana-supply = <&pm8994_l17>;
+ /* Self-powered cameras: */
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 17 0>, /* I2C SDA */
+ <&tlmm 18 0>, /* I2C SCL */
+ <&pm8994_gpios 4 0>, /* RST */
+ <&pm8994_gpios 5 0>, /* INT1 */
+ <&pm8994_gpios 6 0>, /* INT2 */
+ <&pm8994_gpios 7 0>; /* INT3 */
+ };
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+
+ qcom,ba-input-profile-1 {
+ qcom,type = <0>; /* input type */
+ qcom,name = "CVBS-0"; /* input name */
+ qcom,ba-input = <0>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <1>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
};
&pm8994_gpios {
- gpio@c600 { /* GPIO 7 - NFC DWL REQ */
- qcom,mode = <1>;
- qcom,output-type = <0>;
- qcom,pull = <5>;
+ gpio@c600 { /* GPIO 7 - adv7481 INT3 */
+ qcom,mode = <0>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
qcom,src-sel = <0>;
- qcom,master-en = <1>;
status = "okay";
};
@@ -1159,17 +1200,23 @@
status = "okay";
};
- gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
+ gpio@c300 { /* GPIO 4 - adv7481 RST */
+ qcom,mode = <1>;
qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
};
- gpio@c400 { /* GPIO 5 */
+ gpio@c400 { /* GPIO 5 - adv7481 INT1 */
+ qcom,mode = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ status = "okay";
+ };
+
+ gpio@c500 { /* GPIO 6 - adv7481 INT2*/
qcom,mode = <0>;
- qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 7c07102a1fed..797b8a8d8e82 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -868,11 +868,12 @@
asoc-codec-names = "msm-stub-codec.1";
};
- usb_detect {
+ usb_detect: usb_detect {
compatible = "qcom,gpio-usbdetect";
+ qcom,vbus-det-gpio = <&pm8994_gpios 17 0>;
interrupt-parent = <&spmi_bus>;
- interrupts = <0x0 0xd0 0x0>; /* PM8994 GPIO17 */
- interrupt-names = "vbus_det_irq";
+ interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>;
+ interrupt-names ="pmic_id_irq";
};
loopback1: qcom,msm-pcm-loopback-low-latency {
@@ -1071,18 +1072,10 @@
};
&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1 2 3>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 0 347 0
- 0x0 1 &intc 0 0 243 0
- 0x0 2 &intc 0 0 180 0
- 0x0 3 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "ss_phy_irq", "pwr_event_irq",
- "pmic_id_irq";
-
+ extcon = <&usb_detect>;
vbus_dwc3-supply = <&usb_otg_switch>;
+ vdda33-supply = <&pm8994_l24>;
+ vdda18-supply = <&pm8994_l12>;
};
&blsp1_uart2 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi b/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
index 5ac31e3dd0cb..21aa1db446e2 100644
--- a/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-vidc.dtsi
@@ -226,6 +226,7 @@
clocks = <&clock_mmss clk_vmem_ahb_clk>,
<&clock_mmss clk_vmem_maxi_clk>;
clock-names = "ahb", "maxi";
+ clock-config = <0x0 0x0 0x0 0x1>;
qcom,msm-bus,name = "vmem";
qcom,msm-bus,num-cases = <2>;
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
index 668cb2844363..1cf61486c9e8 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
@@ -42,9 +42,6 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
- adv7533@3d {
- status = "disabled";
- };
adv7533@39 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
index 1ab8ee9cd538..7f6f3d5d4a4c 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
@@ -46,6 +46,11 @@
qcom,hotplug-temp-hysteresis = <25>;
qcom,therm-reset-temp = <119>;
};
+
+ qcom,adv7481@70 {
+ qcom,cam-vreg-min-voltage = <1300000 0 1800000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 1800000>;
+ };
};
&pil_modem {
diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
index ca89a517df5c..252940c9c3e5 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
@@ -1331,6 +1331,10 @@
qcom,poll-ms = <50>;
qcom,limit-temp = <80>;
qcom,core-limit-temp = <90>;
+ msm_thermal_freq: qcom,vdd-apps-rstr {
+ qcom,max-freq-level = <1209600>;
+ qcom,levels = <1056000 1516800 1516800>;
+ };
qcom,vdd-gfx-rstr{
qcom,levels = <6 8 9>; /* Nominal, Turbo, Turbo_L1 */
};
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 5218a1d86e6d..fc546512992d 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -2366,6 +2366,10 @@
hyplog-size-offset = <0x414>; /* 0x066BFB34 */
};
+ qcom_msmhdcp: qcom,msm_hdcp {
+ compatible = "qcom,msm-hdcp";
+ };
+
qcom_crypto: qcrypto@1DE0000 {
compatible = "qcom,qcrypto";
reg = <0x1DE0000 0x20000>,
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index f75794ba942f..99766dbcdfe5 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -268,14 +268,18 @@
compatible = "qcom,qusb2phy";
reg = <0x0c012000 0x180>,
<0x01fcb24c 0x4>,
+ <0x00780240 0x4>,
<0x00188018 0x4>;
reg-names = "qusb_phy_base",
"tcsr_clamp_dig_n_1p8",
+ "tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm660l_l1>;
vdda18-supply = <&pm660_l10>;
vdda33-supply = <&pm660l_l7>;
qcom,vdd-voltage-level = <0 925000 925000>;
+ qcom,tune2-efuse-bit-pos = <25>;
+ qcom,tune2-efuse-num-bits = <4>;
qcom,qusb-phy-init-seq = <0xf8 0x80
0xb3 0x84
0x83 0x88
diff --git a/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi b/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
index 06b3be2d5c0a..588973fbd840 100644
--- a/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-vidc.dtsi
@@ -209,7 +209,7 @@
<&mmss_bimc_smmu 0x411>,
<&mmss_bimc_smmu 0x431>;
buffer-types = <0xfff>;
- virtual-addr-pool = <0x70800000 0x6f800000>;
+ virtual-addr-pool = <0x79000000 0x60000000>;
};
firmware_cb {
@@ -231,7 +231,7 @@
<&mmss_bimc_smmu 0x529>,
<&mmss_bimc_smmu 0x52b>;
buffer-types = <0x241>;
- virtual-addr-pool = <0x4b000000 0x25800000>;
+ virtual-addr-pool = <0x51000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -243,7 +243,7 @@
<&mmss_bimc_smmu 0x510>,
<&mmss_bimc_smmu 0x52c>;
buffer-types = <0x106>;
- virtual-addr-pool = <0x25800000 0x25800000>;
+ virtual-addr-pool = <0x29000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -260,7 +260,7 @@
<&mmss_bimc_smmu 0x52d>,
<&mmss_bimc_smmu 0x540>;
buffer-types = <0x480>;
- virtual-addr-pool = <0x1000000 0x24800000>;
+ virtual-addr-pool = <0x1000000 0x28000000>;
qcom,secure-context-bank;
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index 2e576a51677f..d13a359bae2e 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -2287,6 +2287,7 @@
lanes-per-direction = <1>;
spm-level = <5>;
+ non-removable;
qcom,msm-bus,name = "ufs1";
qcom,msm-bus,num-cases = <12>;
qcom,msm-bus,num-paths = <2>;
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 124a342fc1a6..b362a0561d33 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -485,6 +485,7 @@ CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_USB_CONFIGFS_F_CCID=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index 939b34f7d6dd..6d6fd23095d5 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -7,6 +7,8 @@ CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index aafde733099b..25566e45c46f 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -8,6 +8,9 @@ CONFIG_TASKSTATS=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 212ca2eee257..68561696f31b 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -295,6 +295,7 @@ static void fw_free_buf(struct firmware_buf *buf)
{
struct firmware_cache *fwc = buf->fwc;
if (!fwc) {
+ kfree_const(buf->fw_id);
kfree(buf);
return;
}
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index a7c21407a814..ed0226131b90 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -670,7 +670,8 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned attr,
init_dma_attrs(&attrs);
dma_set_attr(DMA_ATTR_EXEC_MAPPING, &attrs);
- if (map->attr & FASTRPC_ATTR_NON_COHERENT)
+ if ((map->attr & FASTRPC_ATTR_NON_COHERENT) ||
+ (sess->smmu.coherent && map->uncached))
dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT,
&attrs);
else if (map->attr & FASTRPC_ATTR_COHERENT)
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index fb45af9c49d3..196e87b61705 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -2910,6 +2910,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
new_entry->num_buffers = 1;
break;
}
+
+ new_entry->buffers = NULL;
new_entry->real_time = MODE_REALTIME;
new_entry->in_service = 0;
INIT_LIST_HEAD(&new_entry->list_write_buf);
@@ -2983,7 +2985,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
fail_alloc:
if (new_entry) {
- for (i = 0; i < new_entry->num_buffers; i++) {
+ for (i = 0; ((i < new_entry->num_buffers) &&
+ new_entry->buffers); i++) {
proc_buf = &new_entry->buffers[i];
if (proc_buf) {
mutex_destroy(&proc_buf->health_mutex);
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 7b414bd7d808..06b83f5230bf 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -254,8 +254,6 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
struct diag_md_session_t *session_info = NULL;
struct pid *pid_struct = NULL;
- mutex_lock(&driver->diagfwd_untag_mutex);
-
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
for (j = 0; j < ch->num_tbl_entries && !err; j++) {
@@ -365,8 +363,6 @@ drop_data:
if (drain_again)
chk_logging_wakeup();
- mutex_unlock(&driver->diagfwd_untag_mutex);
-
return err;
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a7d7fd176302..92cf24dcab5e 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -547,7 +547,6 @@ struct diagchar_dev {
struct mutex cmd_reg_mutex;
uint32_t cmd_reg_count;
struct mutex diagfwd_channel_mutex[NUM_PERIPHERALS];
- struct mutex diagfwd_untag_mutex;
/* Sizes that reflect memory pool sizes */
unsigned int poolsize;
unsigned int poolsize_hdlc;
@@ -613,12 +612,6 @@ struct diagchar_dev {
int pd_logging_mode[NUM_UPD];
int pd_session_clear[NUM_UPD];
int num_pd_session;
- int cpd_len_1[NUM_PERIPHERALS];
- int cpd_len_2[NUM_PERIPHERALS];
- int upd_len_1_a[NUM_PERIPHERALS];
- int upd_len_1_b[NUM_PERIPHERALS];
- int upd_len_2_a;
- int upd_len_2_b;
int mask_check;
uint32_t md_session_mask;
uint8_t md_session_mode;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 6be7c48f75a8..0bc23199b92e 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -481,20 +481,21 @@ static void diag_close_logging_process(const int pid)
params.req_mode = USB_MODE;
params.mode_param = 0;
+ params.pd_mask = 0;
params.peripheral_mask =
diag_translate_kernel_to_user_mask(session_mask);
- for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
- if (session_mask &
- MD_PERIPHERAL_MASK(i)) {
+ if (driver->num_pd_session > 0) {
+ for (i = UPD_WLAN; ((i < NUM_MD_SESSIONS) &&
+ (session_mask & MD_PERIPHERAL_MASK(i)));
+ i++) {
j = i - UPD_WLAN;
driver->pd_session_clear[j] = 1;
driver->pd_logging_mode[j] = 0;
driver->num_pd_session -= 1;
params.pd_mask =
diag_translate_kernel_to_user_mask(session_mask);
- } else
- params.pd_mask = 0;
+ }
}
diag_switch_logging(&params);
@@ -1612,7 +1613,7 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
- int new_mode, i;
+ int new_mode, i = 0;
int curr_mode;
int err = 0;
uint8_t do_switch = 1;
@@ -1653,6 +1654,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
diag_mux->mux_mask)) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
+ i = upd - UPD_WLAN;
+ driver->pd_session_clear[i] = 0;
return -EINVAL;
}
peripheral_mask =
@@ -1662,8 +1665,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
if (!driver->pd_session_clear[i]) {
driver->pd_logging_mode[i] = 1;
driver->num_pd_session += 1;
- driver->pd_session_clear[i] = 0;
}
+ driver->pd_session_clear[i] = 0;
} else {
peripheral_mask =
diag_translate_mask(param->peripheral_mask);
@@ -3623,7 +3626,6 @@ static int __init diagchar_init(void)
mutex_init(&driver->msg_mask_lock);
for (i = 0; i < NUM_PERIPHERALS; i++)
mutex_init(&driver->diagfwd_channel_mutex[i]);
- mutex_init(&driver->diagfwd_untag_mutex);
init_waitqueue_head(&driver->wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
INIT_WORK(&(driver->update_user_clients),
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index e86dc8292bf0..e209039bed5a 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -363,7 +363,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
if (driver->feature[peripheral].encode_hdlc &&
driver->feature[peripheral].untag_header &&
driver->peripheral_untag[peripheral]) {
- mutex_lock(&driver->diagfwd_untag_mutex);
temp_buf_cpd = buf;
temp_buf_main = buf;
if (fwd_info->buf_1 &&
@@ -463,10 +462,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
if (peripheral == PERIPHERAL_LPASS &&
fwd_info->type == TYPE_DATA && len_upd_2) {
if (flag_buf_1) {
- driver->upd_len_2_a = len_upd_2;
+ fwd_info->upd_len_2_a = len_upd_2;
temp_ptr_upd = fwd_info->buf_upd_2_a;
} else {
- driver->upd_len_2_b = len_upd_2;
+ fwd_info->upd_len_2_b = len_upd_2;
temp_ptr_upd = fwd_info->buf_upd_2_b;
}
temp_ptr_upd->ctxt &= 0x00FFFFFF;
@@ -477,17 +476,17 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_upd, len_upd_2);
} else {
if (flag_buf_1)
- driver->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
if (flag_buf_2)
- driver->upd_len_2_b = 0;
+ fwd_info->upd_len_2_b = 0;
}
if (fwd_info->type == TYPE_DATA && len_upd_1) {
if (flag_buf_1) {
- driver->upd_len_1_a[peripheral] =
+ fwd_info->upd_len_1_a =
len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_a;
} else {
- driver->upd_len_1_b[peripheral] =
+ fwd_info->upd_len_1_b =
len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_b;
}
@@ -499,15 +498,15 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_upd, len_upd_1);
} else {
if (flag_buf_1)
- driver->upd_len_1_a[peripheral] = 0;
+ fwd_info->upd_len_1_a = 0;
if (flag_buf_2)
- driver->upd_len_1_b[peripheral] = 0;
+ fwd_info->upd_len_1_b = 0;
}
if (len_cpd) {
if (flag_buf_1)
- driver->cpd_len_1[peripheral] = len_cpd;
+ fwd_info->cpd_len_1 = len_cpd;
else
- driver->cpd_len_2[peripheral] = len_cpd;
+ fwd_info->cpd_len_2 = len_cpd;
temp_ptr_cpd->ctxt &= 0x00FFFFFF;
temp_ptr_cpd->ctxt |=
(SET_PD_CTXT(ctxt_cpd));
@@ -515,11 +514,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_cpd, len_cpd);
} else {
if (flag_buf_1)
- driver->cpd_len_1[peripheral] = 0;
+ fwd_info->cpd_len_1 = 0;
if (flag_buf_2)
- driver->cpd_len_2[peripheral] = 0;
+ fwd_info->cpd_len_2 = 0;
}
- mutex_unlock(&driver->diagfwd_untag_mutex);
return;
} else {
diagfwd_data_read_done(fwd_info, buf, len);
@@ -527,7 +525,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
}
end:
diag_ws_release();
- mutex_unlock(&driver->diagfwd_untag_mutex);
if (temp_ptr_cpd) {
diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
GET_BUF_NUM(temp_ptr_cpd->ctxt));
@@ -759,6 +756,12 @@ int diagfwd_peripheral_init(void)
fwd_info->inited = 1;
fwd_info->read_bytes = 0;
fwd_info->write_bytes = 0;
+ fwd_info->cpd_len_1 = 0;
+ fwd_info->cpd_len_2 = 0;
+ fwd_info->upd_len_1_a = 0;
+ fwd_info->upd_len_1_b = 0;
+ fwd_info->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
mutex_init(&fwd_info->buf_mutex);
mutex_init(&fwd_info->data_mutex);
spin_lock_init(&fwd_info->write_buf_lock);
@@ -775,6 +778,12 @@ int diagfwd_peripheral_init(void)
fwd_info->ch_open = 0;
fwd_info->read_bytes = 0;
fwd_info->write_bytes = 0;
+ fwd_info->cpd_len_1 = 0;
+ fwd_info->cpd_len_2 = 0;
+ fwd_info->upd_len_1_a = 0;
+ fwd_info->upd_len_1_b = 0;
+ fwd_info->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
spin_lock_init(&fwd_info->write_buf_lock);
mutex_init(&fwd_info->buf_mutex);
mutex_init(&fwd_info->data_mutex);
@@ -1273,11 +1282,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
if (ctxt == 1 && fwd_info->buf_1) {
/* Buffer 1 for core PD is freed */
atomic_set(&fwd_info->buf_1->in_busy, 0);
- driver->cpd_len_1[peripheral] = 0;
+ fwd_info->cpd_len_1 = 0;
} else if (ctxt == 2 && fwd_info->buf_2) {
/* Buffer 2 for core PD is freed */
atomic_set(&fwd_info->buf_2->in_busy, 0);
- driver->cpd_len_2[peripheral] = 0;
+ fwd_info->cpd_len_2 = 0;
} else if (ctxt == 3 && fwd_info->buf_upd_1_a) {
/* Buffer 1 for user pd 1 is freed */
atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0);
@@ -1286,17 +1295,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* if not data in cpd and other user pd
* free the core pd buffer for LPASS
*/
- if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
- !driver->upd_len_2_a)
+ if (!fwd_info->cpd_len_1 &&
+ !fwd_info->upd_len_2_a)
atomic_set(&fwd_info->buf_1->in_busy, 0);
} else {
/* if not data in cpd
* free the core pd buffer for MPSS
*/
- if (!driver->cpd_len_1[PERIPHERAL_MODEM])
+ if (!fwd_info->cpd_len_1)
atomic_set(&fwd_info->buf_1->in_busy, 0);
}
- driver->upd_len_1_a[peripheral] = 0;
+ fwd_info->upd_len_1_a = 0;
} else if (ctxt == 4 && fwd_info->buf_upd_1_b) {
/* Buffer 2 for user pd 1 is freed */
@@ -1305,17 +1314,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* if not data in cpd and other user pd
* free the core pd buffer for LPASS
*/
- if (!driver->cpd_len_2[peripheral] &&
- !driver->upd_len_2_b)
+ if (!fwd_info->cpd_len_2 &&
+ !fwd_info->upd_len_2_b)
atomic_set(&fwd_info->buf_2->in_busy, 0);
} else {
/* if not data in cpd
* free the core pd buffer for MPSS
*/
- if (!driver->cpd_len_2[PERIPHERAL_MODEM])
+ if (!fwd_info->cpd_len_2)
atomic_set(&fwd_info->buf_2->in_busy, 0);
}
- driver->upd_len_1_b[peripheral] = 0;
+ fwd_info->upd_len_1_b = 0;
} else if (ctxt == 5 && fwd_info->buf_upd_2_a) {
/* Buffer 1 for user pd 2 is freed */
@@ -1323,11 +1332,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* if not data in cpd and other user pd
* free the core pd buffer for LPASS
*/
- if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
- !driver->upd_len_1_a[PERIPHERAL_LPASS])
+ if (!fwd_info->cpd_len_1 &&
+ !fwd_info->upd_len_1_a)
atomic_set(&fwd_info->buf_1->in_busy, 0);
- driver->upd_len_2_a = 0;
+ fwd_info->upd_len_2_a = 0;
} else if (ctxt == 6 && fwd_info->buf_upd_2_b) {
/* Buffer 2 for user pd 2 is freed */
@@ -1335,11 +1344,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
/* if not data in cpd and other user pd
* free the core pd buffer for LPASS
*/
- if (!driver->cpd_len_2[PERIPHERAL_LPASS] &&
- !driver->upd_len_1_b[PERIPHERAL_LPASS])
+ if (!fwd_info->cpd_len_2 &&
+ !fwd_info->upd_len_1_b)
atomic_set(&fwd_info->buf_2->in_busy, 0);
- driver->upd_len_2_b = 0;
+ fwd_info->upd_len_2_b = 0;
} else
pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index 760f139ff428..037eeebdeb35 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -83,6 +83,12 @@ struct diagfwd_info {
struct diagfwd_buf_t *buf_upd_2_a;
struct diagfwd_buf_t *buf_upd_2_b;
struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS];
+ int cpd_len_1;
+ int cpd_len_2;
+ int upd_len_1_a;
+ int upd_len_1_b;
+ int upd_len_2_a;
+ int upd_len_2_b;
struct diag_peripheral_ops *p_ops;
struct diag_channel_ops *c_ops;
};
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 7cc1c56a2090..72a75873b810 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -606,6 +606,83 @@ static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
return 0;
}
+static int clk_osm_acd_init(struct clk_osm *c)
+{
+
+ int rc = 0;
+ u32 auto_xfer_mask = 0;
+
+ if (!c->acd_init)
+ return 0;
+
+ c->acd_debugfs_addr = ACD_HW_VERSION;
+
+ /* Program ACD tunable-length delay register */
+ clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
+
+ /* Program ACD control register */
+ clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
+
+ /* Program ACD soft start control register */
+ clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
+
+ /* Program initial ACD external interface configuration register */
+ clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
+
+ /* Program ACD auto-register transfer control register */
+ clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
+
+ /* Ensure writes complete before transfers to local copy */
+ clk_osm_acd_mb(c);
+
+ /* Transfer master copies */
+ rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
+ if (rc)
+ return rc;
+
+ /* Switch CPUSS clock source to ACD clock */
+ rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
+ ACD_GFMUX_CFG);
+ if (rc)
+ return rc;
+
+ /* Program ACD_DCVS_SW */
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_SET,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ udelay(1);
+
+ /* Program final ACD external interface configuration register */
+ rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
+ ACD_EXTINT_CFG);
+ if (rc)
+ return rc;
+
+ /*
+ * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
+ * must be copied from master to local copy on PC exit.
+ */
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
+ clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
+
+ /* ACD has been initialized and enabled for this cluster */
+ c->acd_init = false;
+ return 0;
+}
+
static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
{
u64 temp;
@@ -729,6 +806,17 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate)
static int clk_osm_enable(struct clk *c)
{
struct clk_osm *cpuclk = to_clk_osm(c);
+ int rc;
+
+ rc = clk_osm_acd_init(cpuclk);
+ if (rc) {
+ pr_err("Failed to initialize ACD for cluster %d, rc=%d\n",
+ cpuclk->cluster_num, rc);
+ return rc;
+ }
+
+ /* Wait for 5 usecs before enabling OSM */
+ udelay(5);
clk_osm_write_reg(cpuclk, 1, ENABLE_REG);
@@ -3105,81 +3193,6 @@ static int clk_osm_panic_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static int clk_osm_acd_init(struct clk_osm *c)
-{
-
- int rc = 0;
- u32 auto_xfer_mask = 0;
-
- if (!c->acd_init)
- return 0;
-
- c->acd_debugfs_addr = ACD_HW_VERSION;
-
- /* Program ACD tunable-length delay register */
- clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
-
- /* Program ACD control register */
- clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
-
- /* Program ACD soft start control register */
- clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
-
- /* Program initial ACD external interface configuration register */
- clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
-
- /* Program ACD auto-register transfer control register */
- clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
-
- /* Ensure writes complete before transfers to local copy */
- clk_osm_acd_mb(c);
-
- /* Transfer master copies */
- rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
- if (rc)
- return rc;
-
- /* Switch CPUSS clock source to ACD clock */
- rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
- ACD_GFMUX_CFG);
- if (rc)
- return rc;
-
- /* Program ACD_DCVS_SW */
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_SET,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- udelay(1);
-
- /* Program final ACD external interface configuration register */
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
- ACD_EXTINT_CFG);
- if (rc)
- return rc;
-
- /*
- * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
- * must be copied from master to local copy on PC exit.
- */
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
- clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
-
- return 0;
-}
-
static unsigned long init_rate = 300000000;
static unsigned long osm_clk_init_rate = 200000000;
@@ -3362,17 +3375,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
clk_osm_setup_cluster_pll(&perfcl_clk);
}
- rc = clk_osm_acd_init(&pwrcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc);
- return rc;
- }
- rc = clk_osm_acd_init(&perfcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc);
- return rc;
- }
-
spin_lock_init(&pwrcl_clk.lock);
spin_lock_init(&perfcl_clk.lock);
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 4c18181c047c..d3e88f40bdfd 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
+clk-qcom-y += clk-regmap-mux-div.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o clk-voter.o
diff --git a/drivers/clk/qcom/clk-regmap-mux-div.c b/drivers/clk/qcom/clk-regmap-mux-div.c
new file mode 100644
index 000000000000..9593aefb0bf6
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "clk-regmap-mux-div.h"
+
+#define CMD_RCGR 0x0
+#define CMD_RCGR_UPDATE BIT(0)
+#define CMD_RCGR_DIRTY_CFG BIT(4)
+#define CMD_RCGR_ROOT_OFF BIT(31)
+#define CFG_RCGR 0x4
+
+#define to_clk_regmap_mux_div(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
+
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
+{
+ int ret, count;
+ u32 val, mask;
+ const char *name = clk_hw_get_name(&md->clkr.hw);
+
+ val = (div << md->hid_shift) | (src << md->src_shift);
+ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
+ ((BIT(md->src_width) - 1) << md->src_shift);
+
+ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
+ mask, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
+ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
+ if (ret)
+ return ret;
+
+ /* Wait for update to take effect */
+ for (count = 500; count > 0; count--) {
+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
+ &val);
+ if (ret)
+ return ret;
+ if (!(val & CMD_RCGR_UPDATE))
+ return 0;
+ udelay(1);
+ }
+
+ pr_err("%s: RCG did not update its configuration", name);
+ return -EBUSY;
+}
+
+static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
+ u32 *div)
+{
+ u32 val, __div, __src;
+ const char *name = clk_hw_get_name(&md->clkr.hw);
+
+ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
+
+ if (val & CMD_RCGR_DIRTY_CFG) {
+ pr_err("%s: RCG configuration is pending\n", name);
+ return;
+ }
+
+ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
+ __src = (val >> md->src_shift);
+ __src &= BIT(md->src_width) - 1;
+ *src = __src;
+
+ __div = (val >> md->hid_shift);
+ __div &= BIT(md->hid_width) - 1;
+ *div = __div;
+}
+
+static int mux_div_enable(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_src_div(md, md->src, md->div);
+}
+
+static inline bool is_better_rate(unsigned long req, unsigned long best,
+ unsigned long new)
+{
+ return (req <= new && new < best) || (best < req && best < new);
+}
+
+static int mux_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ unsigned int i, div, max_div;
+ unsigned long actual_rate, best_rate = 0;
+ unsigned long req_rate = req->rate;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ max_div = BIT(md->hid_width) - 1;
+ for (div = 1; div < max_div; div++) {
+ parent_rate = mult_frac(req_rate, div, 2);
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
+ actual_rate = mult_frac(parent_rate, 2, div);
+
+ if (is_better_rate(req_rate, best_rate, actual_rate)) {
+ best_rate = actual_rate;
+ req->rate = best_rate;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+
+ if (actual_rate < req_rate || best_rate <= req_rate)
+ break;
+ }
+ }
+
+ if (!best_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate, u32 src)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ int ret;
+ u32 div, max_div, best_src = 0, best_div = 0;
+ unsigned int i;
+ unsigned long actual_rate, best_rate = 0;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+
+ max_div = BIT(md->hid_width) - 1;
+ for (div = 1; div < max_div; div++) {
+ parent_rate = mult_frac(rate, div, 2);
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
+ actual_rate = mult_frac(parent_rate, 2, div);
+
+ if (is_better_rate(rate, best_rate, actual_rate)) {
+ best_rate = actual_rate;
+ best_src = md->parent_map[i].cfg;
+ best_div = div - 1;
+ }
+
+ if (actual_rate < rate || best_rate <= rate)
+ break;
+ }
+ }
+
+ ret = __mux_div_set_src_div(md, best_src, best_div);
+ if (!ret) {
+ md->div = best_div;
+ md->src = best_src;
+ }
+
+ return ret;
+}
+
+static u8 mux_div_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ const char *name = clk_hw_get_name(hw);
+ u32 i, div, src = 0;
+
+ __mux_div_get_src_div(md, &src, &div);
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
+ if (src == md->parent_map[i].cfg)
+ return i;
+
+ pr_err("%s: Can't find parent with src %d\n", name, src);
+ return 0;
+}
+
+static int mux_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
+}
+
+static int mux_div_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long prate)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
+}
+
+static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate, u8 index)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ return __mux_div_set_rate_and_parent(hw, rate, prate,
+ md->parent_map[index].cfg);
+}
+
+static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+ u32 div, src;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ const char *name = clk_hw_get_name(hw);
+
+ __mux_div_get_src_div(md, &src, &div);
+ for (i = 0; i < num_parents; i++)
+ if (src == md->parent_map[i].cfg) {
+ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
+ unsigned long parent_rate = clk_hw_get_rate(p);
+
+ return mult_frac(parent_rate, 2, div + 1);
+ }
+
+ pr_err("%s: Can't find parent %d\n", name, src);
+ return 0;
+}
+
+static void mux_div_disable(struct clk_hw *hw)
+{
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
+
+ __mux_div_set_src_div(md, md->safe_src, md->safe_div);
+}
+
+const struct clk_ops clk_regmap_mux_div_ops = {
+ .enable = mux_div_enable,
+ .disable = mux_div_disable,
+ .get_parent = mux_div_get_parent,
+ .set_parent = mux_div_set_parent,
+ .set_rate = mux_div_set_rate,
+ .set_rate_and_parent = mux_div_set_rate_and_parent,
+ .determine_rate = mux_div_determine_rate,
+ .recalc_rate = mux_div_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
diff --git a/drivers/clk/qcom/clk-regmap-mux-div.h b/drivers/clk/qcom/clk-regmap-mux-div.h
new file mode 100644
index 000000000000..6fac5c54a824
--- /dev/null
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
+#define __QCOM_CLK_REGMAP_MUX_DIV_H__
+
+#include <linux/clk-provider.h>
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+
+/**
+ * struct mux_div_clk - combined mux/divider clock
+ * @reg_offset: offset of the mux/divider register
+ * @hid_width: number of bits in half integer divider
+ * @hid_shift: lowest bit of hid value field
+ * @src_width: number of bits in source select
+ * @src_shift: lowest bit of source select field
+ * @div: the divider raw configuration value
+ * @src: the mux index which will be used if the clock is enabled
+ * @safe_src: the safe source mux value we switch to, while the main PLL is
+ * reconfigured
+ * @safe_div: the safe divider value that we set, while the main PLL is
+ * reconfigured
+ * @safe_freq: When switching rates from A to B, the mux div clock will
+ * instead switch from A -> safe_freq -> B. This allows the
+ * mux_div clock to change rates while enabled, even if this
+ * behavior is not supported by the parent clocks.
+ * If changing the rate of parent A also causes the rate of
+ * parent B to change, then safe_freq must be defined.
+ * safe_freq is expected to have a source clock which is always
+ * on and runs at only one rate.
+ * @parent_map: pointer to parent_map struct
+ * @clkr: handle between common and hardware-specific interfaces
+ */
+
+struct clk_regmap_mux_div {
+ u32 reg_offset;
+ u32 hid_width;
+ u32 hid_shift;
+ u32 src_width;
+ u32 src_shift;
+ u32 div;
+ u32 src;
+ u32 safe_src;
+ u32 safe_div;
+ unsigned long safe_freq;
+ const struct parent_map *parent_map;
+ struct clk_regmap clkr;
+};
+
+extern const struct clk_ops clk_regmap_mux_div_ops;
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
+
+#endif
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 893b0b6da6b8..b5c5dc035c66 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -437,6 +437,7 @@ struct qcrypto_cipher_req_ctx {
u8 rfc4309_iv[QCRYPTO_MAX_IV_LENGTH];
unsigned int ivsize;
int aead;
+ int ccmtype; /* default: 0, rfc4309: 1 */
struct scatterlist asg; /* Formatted associated data sg */
unsigned char *adata; /* Pointer to formatted assoc data */
enum qce_cipher_alg_enum alg;
@@ -1897,9 +1898,8 @@ static int aead_ccm_set_msg_len(u8 *block, unsigned int msglen, int csize)
return 0;
}
-static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
+static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq, uint32_t assoclen)
{
- struct aead_request *areq = (struct aead_request *) qreq->areq;
unsigned int i = ((unsigned int)qreq->iv[0]) + 1;
memcpy(&qreq->nonce[0] , qreq->iv, qreq->ivsize);
@@ -1908,7 +1908,7 @@ static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
* NIST Special Publication 800-38C
*/
qreq->nonce[0] |= (8 * ((qreq->authsize - 2) / 2));
- if (areq->assoclen)
+ if (assoclen)
qreq->nonce[0] |= 64;
if (i > MAX_NONCE)
@@ -2118,24 +2118,31 @@ static int _qcrypto_process_aead(struct crypto_engine *pengine,
qreq.flags = cipher_ctx->flags;
if (qreq.mode == QCE_MODE_CCM) {
+ uint32_t assoclen;
+
if (qreq.dir == QCE_ENCRYPT)
qreq.cryptlen = req->cryptlen;
else
qreq.cryptlen = req->cryptlen -
qreq.authsize;
+
+ /* if rfc4309 ccm, adjust assoclen */
+ assoclen = req->assoclen;
+ if (rctx->ccmtype)
+ assoclen -= 8;
/* Get NONCE */
- ret = qccrypto_set_aead_ccm_nonce(&qreq);
+ ret = qccrypto_set_aead_ccm_nonce(&qreq, assoclen);
if (ret)
return ret;
- if (req->assoclen) {
- rctx->adata = kzalloc((req->assoclen + 0x64),
+ if (assoclen) {
+ rctx->adata = kzalloc((assoclen + 0x64),
GFP_ATOMIC);
if (!rctx->adata)
return -ENOMEM;
/* Format Associated data */
ret = qcrypto_aead_ccm_format_adata(&qreq,
- req->assoclen,
+ assoclen,
req->src,
rctx->adata);
} else {
@@ -2592,6 +2599,7 @@ static int _qcrypto_aead_encrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_ENCRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2606,6 +2614,8 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2615,6 +2625,7 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2922,6 +2933,7 @@ static int _qcrypto_aead_decrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_DECRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2935,6 +2947,8 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
struct crypto_stat *pstat;
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2944,6 +2958,7 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index f3a8a8416c7a..999d5e45e5c5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -13,6 +13,7 @@ msm_drm-y := \
hdmi/hdmi_connector.o \
hdmi/hdmi_hdcp.o \
hdmi/hdmi_i2c.o \
+ hdmi/hdmi_util.o \
hdmi/hdmi_phy_8960.o \
hdmi/hdmi_phy_8x60.o \
hdmi/hdmi_phy_8x74.o \
@@ -50,7 +51,8 @@ msm_drm-y := \
sde_dbg_evtlog.o \
sde_io_util.o \
dba_bridge.o \
- sde_edid_parser.o
+ sde_edid_parser.o \
+ sde_hdcp_1x.o
# use drm gpu driver only if qcom_kgsl driver not available
ifneq ($(CONFIG_QCOM_KGSL),y)
@@ -101,9 +103,11 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
dsi-staging/dsi_display_test.o
msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
+ hdmi-staging/sde_hdmi_util.o \
hdmi-staging/sde_hdmi.o \
hdmi-staging/sde_hdmi_bridge.o \
hdmi-staging/sde_hdmi_audio.o \
+ hdmi-staging/sde_hdmi_hdcp2p2.o \
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
@@ -144,6 +148,7 @@ msm_drm-$(CONFIG_DRM_MSM) += \
msm_rd.o \
msm_ringbuffer.o \
msm_prop.o \
- msm_snapshot.o
+ msm_snapshot.o \
+ msm_submitqueue.o
obj-$(CONFIG_DRM_MSM) += msm_drm.o
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 437f88f29a69..47bee42c59fa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -29,6 +29,8 @@
#include "sde_connector.h"
#include "msm_drv.h"
#include "sde_hdmi.h"
+#include "sde_hdmi_regs.h"
+#include "hdmi.h"
static DEFINE_MUTEX(sde_hdmi_list_lock);
static LIST_HEAD(sde_hdmi_list);
@@ -425,6 +427,118 @@ static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
return pclk_clip;
}
+static void sde_hdmi_tx_hdcp_cb(void *ptr, enum sde_hdcp_states status)
+{
+ struct sde_hdmi *hdmi_ctrl = (struct sde_hdmi *)ptr;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdmi_ctrl->hdcp_status = status;
+ queue_delayed_work(hdmi->workq, &hdmi_ctrl->hdcp_cb_work, HZ/4);
+}
+
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl)
+{
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (hdmi_ctrl->hdcp_ops)
+ hdmi_ctrl->hdcp_ops->off(hdmi_ctrl->hdcp_data);
+
+ flush_delayed_work(&hdmi_ctrl->hdcp_cb_work);
+
+ hdmi_ctrl->hdcp_ops = NULL;
+}
+
+static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work)
+{
+ struct sde_hdmi *hdmi_ctrl = NULL;
+ struct delayed_work *dw = to_delayed_work(work);
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ hdmi_ctrl = container_of(dw, struct sde_hdmi, hdcp_cb_work);
+ if (!hdmi_ctrl) {
+ DEV_DBG("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ switch (hdmi_ctrl->hdcp_status) {
+ case HDCP_STATE_AUTHENTICATED:
+ hdmi_ctrl->auth_state = true;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ }
+
+ if (hdmi_ctrl->hdcp1_use_sw_keys &&
+ hdmi_ctrl->hdcp14_present) {
+ if (!hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(true);
+ }
+ break;
+ case HDCP_STATE_AUTH_FAIL:
+ if (hdmi_ctrl->hdcp1_use_sw_keys && hdmi_ctrl->hdcp14_present) {
+ if (hdmi_ctrl->auth_state && !hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(false);
+ }
+
+ hdmi_ctrl->auth_state = false;
+
+ if (sde_hdmi_tx_is_encryption_set(hdmi_ctrl) ||
+ !sde_hdmi_tx_is_stream_shareable(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, true);
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl)) {
+ pr_debug("%s: Reauthenticating\n", __func__);
+ if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_data) {
+ rc = hdmi_ctrl->hdcp_ops->reauthenticate(
+ hdmi_ctrl->hdcp_data);
+ if (rc)
+ pr_err("%s: HDCP reauth failed. rc=%d\n",
+ __func__, rc);
+ } else
+ pr_err("%s: NULL HDCP Ops and Data\n",
+ __func__);
+ } else {
+ pr_debug("%s: Not reauthenticating. Cable not conn\n",
+ __func__);
+ }
+
+ break;
+ case HDCP_STATE_AUTH_ENC_NONE:
+ hdmi_ctrl->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ case HDCP_STATE_AUTH_ENC_2P2:
+ hdmi_ctrl->enc_lvl = hdmi_ctrl->hdcp_status;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ } else {
+ rc = sde_hdmi_config_avmute(hdmi, true);
+ }
+ break;
+ default:
+ break;
+ /* do nothing */
+ }
+}
+
/**
* _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm
*
@@ -919,6 +1033,23 @@ static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display)
else
cec_notifier_set_phys_addr(display->notifier,
CEC_PHYS_ADDR_INVALID);
+
+}
+
+static void _sde_hdmi_init_ddc(struct sde_hdmi *display, struct hdmi *hdmi)
+{
+ display->ddc_ctrl.io = &display->io[HDMI_TX_CORE_IO];
+ init_completion(&display->ddc_ctrl.rx_status_done);
+}
+
+static void _sde_hdmi_map_regs(struct sde_hdmi *display, struct hdmi *hdmi)
+{
+ display->io[HDMI_TX_CORE_IO].base = hdmi->mmio;
+ display->io[HDMI_TX_CORE_IO].len = hdmi->mmio_len;
+ display->io[HDMI_TX_QFPROM_IO].base = hdmi->qfprom_mmio;
+ display->io[HDMI_TX_QFPROM_IO].len = hdmi->qfprom_mmio_len;
+ display->io[HDMI_TX_HDCP_IO].base = hdmi->hdcp_mmio;
+ display->io[HDMI_TX_HDCP_IO].len = hdmi->hdcp_mmio_len;
}
static void _sde_hdmi_hotplug_work(struct work_struct *work)
@@ -997,26 +1128,39 @@ static void _sde_hdmi_cec_irq(struct sde_hdmi *sde_hdmi)
static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id)
{
- struct sde_hdmi *sde_hdmi = dev_id;
+ struct sde_hdmi *display = dev_id;
struct hdmi *hdmi;
- if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) {
- SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi);
+ if (!display || !display->ctrl.ctrl) {
+ SDE_ERROR("sde_hdmi=%pK or hdmi is NULL\n", display);
return IRQ_NONE;
}
- hdmi = sde_hdmi->ctrl.ctrl;
+
+ hdmi = display->ctrl.ctrl;
/* Process HPD: */
- _sde_hdmi_connector_irq(sde_hdmi);
+ _sde_hdmi_connector_irq(display);
+
+ /* Process Scrambling ISR */
+ sde_hdmi_ddc_scrambling_isr((void *)display);
+
+ /* Process DDC2 */
+ sde_hdmi_ddc_hdcp2p2_isr((void *)display);
/* Process DDC: */
hdmi_i2c_irq(hdmi->i2c);
/* Process HDCP: */
- if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
- hdmi_hdcp_ctrl_irq(hdmi->hdcp_ctrl);
+ if (display->hdcp_ops && display->hdcp_data) {
+ if (display->hdcp_ops->isr) {
+ if (display->hdcp_ops->isr(
+ display->hdcp_data))
+ DEV_ERR("%s: hdcp_1x_isr failed\n",
+ __func__);
+ }
+ }
/* Process CEC: */
- _sde_hdmi_cec_irq(sde_hdmi);
+ _sde_hdmi_cec_irq(display);
return IRQ_HANDLED;
}
@@ -1192,84 +1336,8 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
power_on ? "Enable" : "Disable", ctrl);
}
-int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 5;
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- .buf = &offset,
- }, {
- .addr = addr >> 1,
- .flags = I2C_M_RD,
- .len = data_len,
- .buf = data,
- }
- };
-
- SDE_HDMI_DEBUG("Start DDC read");
- retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 2);
-
- retry--;
- if (rc == 2)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- SDE_HDMI_DEBUG("End DDC read %d", rc);
-
- return rc;
-}
-
#define DDC_WRITE_MAX_BYTE_NUM 32
-int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 10;
- u8 buf[DDC_WRITE_MAX_BYTE_NUM];
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- }
- };
-
- SDE_HDMI_DEBUG("Start DDC write");
- if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) {
- SDE_ERROR("%s: write size too big\n", __func__);
- return -ERANGE;
- }
-
- buf[0] = offset;
- memcpy(&buf[1], data, data_len);
- msgs[0].buf = buf;
- msgs[0].len = data_len + 1;
- retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 1);
-
- retry--;
- if (rc == 1)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- SDE_HDMI_DEBUG("End DDC write %d", rc);
-
- return rc;
-}
-
int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
{
int rc = 0;
@@ -1326,7 +1394,8 @@ int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val)
break;
}
- rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len);
+ rc = hdmi_ddc_read(hdmi, dev_addr, offset, data_buf,
+ data_len, true);
if (rc) {
SDE_ERROR("DDC Read failed for %d\n", data_type);
return rc;
@@ -1398,8 +1467,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
dev_addr = 0xA8;
data_len = 1;
offset = HDMI_SCDC_TMDS_CONFIG;
- rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
- data_len);
+ rc = hdmi_ddc_read(hdmi, dev_addr, offset, &read_val,
+ data_len, true);
if (rc) {
SDE_ERROR("scdc read failed\n");
return rc;
@@ -1423,7 +1492,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val)
return -EINVAL;
}
- rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len);
+ rc = hdmi_ddc_write(hdmi, dev_addr, offset, data_buf,
+ data_len, true);
if (rc) {
SDE_ERROR("DDC Read failed for %d\n", data_type);
return rc;
@@ -1540,6 +1610,141 @@ int sde_hdmi_connector_pre_deinit(struct drm_connector *connector,
return 0;
}
+static void _sde_hdmi_get_tx_version(struct sde_hdmi *sde_hdmi)
+{
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+
+ sde_hdmi->hdmi_tx_version = hdmi_read(hdmi, REG_HDMI_VERSION);
+ sde_hdmi->hdmi_tx_major_version =
+ SDE_GET_MAJOR_VER(sde_hdmi->hdmi_tx_version);
+
+ switch (sde_hdmi->hdmi_tx_major_version) {
+ case (HDMI_TX_VERSION_3):
+ sde_hdmi->max_pclk_khz = HDMI_TX_3_MAX_PCLK_RATE;
+ break;
+ case (HDMI_TX_VERSION_4):
+ sde_hdmi->max_pclk_khz = HDMI_TX_4_MAX_PCLK_RATE;
+ break;
+ default:
+ sde_hdmi->max_pclk_khz = HDMI_DEFAULT_MAX_PCLK_RATE;
+ break;
+ }
+ SDE_DEBUG("sde_hdmi->hdmi_tx_version = 0x%x\n",
+ sde_hdmi->hdmi_tx_version);
+ SDE_DEBUG("sde_hdmi->hdmi_tx_major_version = 0x%x\n",
+ sde_hdmi->hdmi_tx_major_version);
+ SDE_DEBUG("sde_hdmi->max_pclk_khz = 0x%x\n",
+ sde_hdmi->max_pclk_khz);
+}
+
+static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi)
+{
+ u32 hdmi_disabled, hdcp_disabled, reg_val;
+ int ret = 0;
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+
+ /* check if hdmi and hdcp are disabled */
+ if (sde_hdmi->hdmi_tx_major_version < HDMI_TX_VERSION_4) {
+ hdcp_disabled = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_LSB) & BIT(31);
+
+ hdmi_disabled = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB) & BIT(0);
+ } else {
+ reg_val = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_LSB + QFPROM_RAW_VERSION_4);
+ hdcp_disabled = reg_val & BIT(12);
+
+ hdmi_disabled = reg_val & BIT(13);
+
+ reg_val = hdmi_qfprom_read(hdmi, SEC_CTRL_HW_VERSION);
+
+ SDE_DEBUG("SEC_CTRL_HW_VERSION reg_val = 0x%x\n", reg_val);
+ /*
+ * With HDCP enabled on capable hardware, check if HW
+ * or SW keys should be used.
+ */
+ if (!hdcp_disabled && (reg_val >= HDCP_SEL_MIN_SEC_VERSION)) {
+ reg_val = hdmi_qfprom_read(hdmi,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
+ QFPROM_RAW_VERSION_4);
+
+ if (!(reg_val & BIT(23)))
+ sde_hdmi->hdcp1_use_sw_keys = true;
+ }
+ }
+
+ SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__,
+ hdmi_disabled ? "OFF" : "ON",
+ hdcp_disabled ? "OFF" : "ON");
+
+ if (hdmi_disabled) {
+ DEV_ERR("%s: HDMI disabled\n", __func__);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ sde_hdmi->hdcp14_present = !hdcp_disabled;
+
+ end:
+ return ret;
+} /* hdmi_tx_check_capability */
+
+static int _sde_hdmi_init_hdcp(struct sde_hdmi *hdmi_ctrl)
+{
+ struct sde_hdcp_init_data hdcp_init_data;
+ void *hdcp_data;
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("sde_hdmi is NULL\n");
+ return -EINVAL;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdcp_init_data.phy_addr = hdmi->mmio_phy_addr;
+ hdcp_init_data.core_io = &hdmi_ctrl->io[HDMI_TX_CORE_IO];
+ hdcp_init_data.qfprom_io = &hdmi_ctrl->io[HDMI_TX_QFPROM_IO];
+ hdcp_init_data.hdcp_io = &hdmi_ctrl->io[HDMI_TX_HDCP_IO];
+ hdcp_init_data.mutex = &hdmi_ctrl->hdcp_mutex;
+ hdcp_init_data.workq = hdmi->workq;
+ hdcp_init_data.notify_status = sde_hdmi_tx_hdcp_cb;
+ hdcp_init_data.cb_data = (void *)hdmi_ctrl;
+ hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_major_version;
+ hdcp_init_data.sec_access = true;
+ hdcp_init_data.client_id = HDCP_CLIENT_HDMI;
+ hdcp_init_data.ddc_ctrl = &hdmi_ctrl->ddc_ctrl;
+
+ if (hdmi_ctrl->hdcp14_present) {
+ hdcp_data = sde_hdcp_1x_init(&hdcp_init_data);
+
+ if (IS_ERR_OR_NULL(hdcp_data)) {
+ DEV_ERR("%s: hdcp 1.4 init failed\n", __func__);
+ rc = -EINVAL;
+ kfree(hdcp_data);
+ goto end;
+ } else {
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_1x] = hdcp_data;
+ SDE_HDMI_DEBUG("%s: HDCP 1.4 initialized\n", __func__);
+ }
+ }
+
+ hdcp_data = sde_hdmi_hdcp2p2_init(&hdcp_init_data);
+
+ if (IS_ERR_OR_NULL(hdcp_data)) {
+ DEV_ERR("%s: hdcp 2.2 init failed\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ } else {
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_2P2] = hdcp_data;
+ SDE_HDMI_DEBUG("%s: HDCP 2.2 initialized\n", __func__);
+ }
+
+end:
+ return rc;
+}
+
int sde_hdmi_connector_post_init(struct drm_connector *connector,
void *info,
void *display)
@@ -1572,6 +1777,37 @@ int sde_hdmi_connector_post_init(struct drm_connector *connector,
if (rc)
SDE_ERROR("failed to enable HPD: %d\n", rc);
+ _sde_hdmi_get_tx_version(sde_hdmi);
+
+ sde_hdmi_tx_check_capability(sde_hdmi);
+
+ _sde_hdmi_init_hdcp(sde_hdmi);
+
+ return rc;
+}
+
+int sde_hdmi_start_hdcp(struct drm_connector *connector)
+{
+ int rc;
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!sde_hdmi_tx_is_hdcp_enabled(display))
+ return 0;
+
+ if (sde_hdmi_tx_is_encryption_set(display))
+ sde_hdmi_config_avmute(hdmi, true);
+
+ rc = display->hdcp_ops->authenticate(display->hdcp_data);
+ if (rc)
+ SDE_ERROR("%s: hdcp auth failed. rc=%d\n", __func__, rc);
+
return rc;
}
@@ -1688,6 +1924,12 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display)
SDE_ERROR("Invalid params\n");
return -EINVAL;
}
+ if (display->hdcp_feat_data[SDE_HDCP_1x])
+ sde_hdcp_1x_deinit(display->hdcp_feat_data[SDE_HDCP_1x]);
+
+ if (display->hdcp_feat_data[SDE_HDCP_2P2])
+ sde_hdmi_hdcp2p2_deinit(display->hdcp_feat_data[SDE_HDCP_2P2]);
+
return 0;
}
@@ -1770,6 +2012,14 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
display_ctrl->ctrl = priv->hdmi;
display->drm_dev = drm;
+ _sde_hdmi_map_regs(display, priv->hdmi);
+ _sde_hdmi_init_ddc(display, priv->hdmi);
+
+ display->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+
+ INIT_DELAYED_WORK(&display->hdcp_cb_work,
+ sde_hdmi_tx_hdcp_cb_work);
+ mutex_init(&display->hdcp_mutex);
mutex_unlock(&display->display_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index dff245dec764..6b6518287028 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -20,13 +20,18 @@
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/msm_ext_display.h>
+#include <linux/hdcp_qseecom.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <media/cec-notifier.h>
#include "hdmi.h"
-
+#include "sde_kms.h"
+#include "sde_connector.h"
+#include "msm_drv.h"
#include "sde_edid_parser.h"
+#include "sde_hdmi_util.h"
+#include "sde_hdcp.h"
#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
@@ -34,6 +39,10 @@
#define SDE_HDMI_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
#endif
+/* HW Revisions for different SDE targets */
+#define SDE_GET_MAJOR_VER(rev)((rev) >> 28)
+#define SDE_GET_MINOR_VER(rev)(((rev) >> 16) & 0xFFF)
+
/**
* struct sde_hdmi_info - defines hdmi display properties
* @display_type: Display type as defined by device tree.
@@ -69,6 +78,18 @@ struct sde_hdmi_ctrl {
u32 hdmi_ctrl_idx;
};
+enum hdmi_tx_io_type {
+ HDMI_TX_CORE_IO,
+ HDMI_TX_QFPROM_IO,
+ HDMI_TX_HDCP_IO,
+ HDMI_TX_MAX_IO
+};
+
+enum hdmi_tx_feature_type {
+ SDE_HDCP_1x,
+ SDE_HDCP_2P2
+};
+
/**
* struct sde_hdmi - hdmi display information
* @pdev: Pointer to platform device.
@@ -99,7 +120,7 @@ struct sde_hdmi {
const char *display_type;
struct list_head list;
struct mutex display_lock;
-
+ struct mutex hdcp_mutex;
struct sde_hdmi_ctrl ctrl;
struct platform_device *ext_pdev;
@@ -112,7 +133,23 @@ struct sde_hdmi {
struct drm_display_mode mode;
bool connected;
bool is_tpg_enabled;
-
+ u32 hdmi_tx_version;
+ u32 hdmi_tx_major_version;
+ u32 max_pclk_khz;
+ bool hdcp1_use_sw_keys;
+ u32 hdcp14_present;
+ u32 hdcp22_present;
+ u8 hdcp_status;
+ u32 enc_lvl;
+ bool auth_state;
+ /*hold final data
+ *based on hdcp support
+ */
+ void *hdcp_data;
+ /*hold hdcp init data*/
+ void *hdcp_feat_data[2];
+ struct sde_hdcp_ops *hdcp_ops;
+ struct sde_hdmi_tx_ddc_ctrl ddc_ctrl;
struct work_struct hpd_work;
bool codec_ready;
bool client_notify_pending;
@@ -120,6 +157,8 @@ struct sde_hdmi {
struct irq_domain *irq_domain;
struct cec_notifier *notifier;
+ struct delayed_work hdcp_cb_work;
+ struct dss_io_data io[HDMI_TX_MAX_IO];
/* DEBUG FS */
struct dentry *root;
};
@@ -144,6 +183,11 @@ enum hdmi_tx_scdc_access_type {
#define HDMI_KHZ_TO_HZ 1000
#define HDMI_MHZ_TO_HZ 1000000
+
+/* Maximum pixel clock rates for hdmi tx */
+#define HDMI_DEFAULT_MAX_PCLK_RATE 148500
+#define HDMI_TX_3_MAX_PCLK_RATE 297000
+#define HDMI_TX_4_MAX_PCLK_RATE 600000
/**
* hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities.
*/
@@ -315,32 +359,6 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi);
void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on);
/**
- * sde_hdmi_ddc_read() - common hdmi ddc read API.
- * @hdmi: Handle to the hdmi.
- * @addr: Command address.
- * @offset: Command offset.
- * @data: Data buffer for read back.
- * @data_len: Data buffer length.
- *
- * Return: error code.
- */
-int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len);
-
-/**
- * sde_hdmi_ddc_write() - common hdmi ddc write API.
- * @hdmi: Handle to the hdmi.
- * @addr: Command address.
- * @offset: Command offset.
- * @data: Data buffer for write.
- * @data_len: Data buffer length.
- *
- * Return: error code.
- */
-int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len);
-
-/**
* sde_hdmi_scdc_read() - hdmi 2.0 ddc read API.
* @hdmi: Handle to the hdmi.
* @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type.
@@ -406,6 +424,13 @@ void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected);
void sde_hdmi_ack_state(struct drm_connector *connector,
enum drm_connector_status status);
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl);
+int sde_hdmi_start_hdcp(struct drm_connector *connector);
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl);
+
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
@@ -464,12 +489,42 @@ static inline int sde_hdmi_dev_deinit(struct sde_hdmi *display)
return 0;
}
+bool hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
static inline int sde_hdmi_drm_init(struct sde_hdmi *display,
struct drm_encoder *enc)
{
return 0;
}
+int sde_hdmi_start_hdcp(struct drm_connector *connector)
+{
+ return 0;
+}
+
+void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl)
+{
+
+}
+
static inline int sde_hdmi_drm_deinit(struct sde_hdmi *display)
{
return 0;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
index 48a3a9316a41..d6213dc0a4aa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
@@ -355,37 +355,3 @@ void sde_hdmi_audio_off(struct hdmi *hdmi)
SDE_DEBUG("HDMI Audio: Disabled\n");
}
-int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
-{
- u32 av_mute_status;
- bool av_pkt_en = false;
-
- if (!hdmi) {
- SDE_ERROR("invalid HDMI Ctrl\n");
- return -ENODEV;
- }
-
- av_mute_status = hdmi_read(hdmi, HDMI_GC);
-
- if (set) {
- if (!(av_mute_status & BIT(0))) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
- av_pkt_en = true;
- }
- } else {
- if (av_mute_status & BIT(0)) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
- av_pkt_en = true;
- }
- }
-
- /* Enable AV Mute tranmission here */
- if (av_pkt_en)
- hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
- hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
-
- SDE_DEBUG("AVMUTE %s\n", set ? "set" : "cleared");
-
- return 0;
-}
-
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index 26a0638f7792..6b01d02930f8 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -32,8 +32,7 @@ struct sde_hdmi_bridge {
#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
-/* default hsyncs for 4k@60 for 200ms */
-#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
/* for AVI program */
#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
@@ -177,39 +176,22 @@ static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi)
return rc;
}
-static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- /* clear ack and disable interrupts */
- reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
- hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
-
- /* Reset DDC timers */
- reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-
- reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- reg_val &= ~BIT(0);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-}
-
-static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- _sde_hdmi_bridge_scrambler_ddc_reset(hdmi);
- /* Disable HW DDC access to RxStatus register */
- reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
- reg_val &= ~(BIT(8) | BIT(9));
- hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
-}
-
static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
u32 timeout_hsync)
{
u32 reg_val;
int rc;
+ struct sde_connector *c_conn;
+ struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+ connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
_sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler");
@@ -243,7 +225,7 @@ static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
if (rc)
SDE_ERROR("scrambling ddc error %d\n", rc);
- _sde_hdmi_bridge_scrambler_ddc_disable(hdmi);
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
return rc;
}
@@ -269,20 +251,6 @@ static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi,
return 0;
}
-static inline int _sde_hdmi_bridge_get_timeout_in_hysnc(
- struct drm_display_mode *mode, u32 timeout_ms)
-{
- /*
- * pixel clock = h_total * v_total * fps
- * 1 sec = pixel clock number of pixels are transmitted.
- * time taken by one line (h_total) = 1s / (v_total * fps).
- * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
- * = (time_ms * clock) / h_total
- */
-
- return (timeout_ms * mode->clock / mode->htotal);
-}
-
static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -291,14 +259,17 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
u32 reg_val = 0;
u32 tmds_clock_ratio = 0;
bool scrambler_on = false;
-
+ struct sde_connector *c_conn;
struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
if (!hdmi || !mode) {
SDE_ERROR("invalid input\n");
return -EINVAL;
}
connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
/* Read HDMI version */
reg_val = hdmi_read(hdmi, REG_HDMI_VERSION);
@@ -344,9 +315,10 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
* status bit on the sink. Sink should set this bit
* with in 200ms after scrambler is enabled.
*/
- timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc(
- mode,
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)display,
HDMI_TX_SCRAMBLER_TIMEOUT_MSEC);
+
if (timeout_hsync <= 0) {
SDE_ERROR("err in timeout hsync calc\n");
timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
@@ -396,8 +368,55 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
mutex_unlock(&display->display_lock);
}
+static void sde_hdmi_update_hdcp_info(struct drm_connector *connector)
+{
+ void *fd = NULL;
+ struct sde_hdcp_ops *ops = NULL;
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ if (!display) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ /* check first if hdcp2p2 is supported */
+ fd = display->hdcp_feat_data[SDE_HDCP_2P2];
+ if (fd)
+ ops = sde_hdmi_hdcp2p2_start(fd);
+
+ if (ops && ops->feature_supported)
+ display->hdcp22_present = ops->feature_supported(fd);
+ else
+ display->hdcp22_present = false;
+
+ if (!display->hdcp22_present) {
+ if (display->hdcp1_use_sw_keys) {
+ display->hdcp14_present =
+ hdcp1_check_if_supported_load_app();
+ }
+ if (display->hdcp14_present) {
+ fd = display->hdcp_feat_data[SDE_HDCP_1x];
+ if (fd)
+ ops = sde_hdcp_1x_start(fd);
+ }
+ }
+
+ /* update internal data about hdcp */
+ display->hdcp_data = fd;
+ display->hdcp_ops = ops;
+}
+
static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge)
{
+ struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
+ struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
+
+ /* need to update hdcp info here to ensure right HDCP support*/
+ sde_hdmi_update_hdcp_info(hdmi->connector);
+
+ /* start HDCP authentication */
+ sde_hdmi_start_hdcp(hdmi->connector);
}
static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge)
@@ -414,8 +433,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
sde_hdmi_notify_clients(display, display->connected);
- if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
- hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl);
+ if (sde_hdmi_tx_is_hdcp_enabled(display))
+ sde_hdmi_hdcp_off(display);
sde_hdmi_audio_off(hdmi);
@@ -629,9 +648,9 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
DRM_DEBUG("hdmi setup info frame\n");
}
- _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
_sde_hdmi_save_mode(hdmi, mode);
+ _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
new file mode 100644
index 000000000000..1e673440f399
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
@@ -0,0 +1,994 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/kthread.h>
+
+#include <linux/hdcp_qseecom.h>
+#include "sde_hdcp.h"
+#include "video/msm_hdmi_hdcp_mgr.h"
+#include "sde_hdmi_util.h"
+
+/*
+ * Defined addresses and offsets of standard HDCP 2.2 sink registers
+ * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7
+ */
+#define HDCP_SINK_DDC_SLAVE_ADDR 0x74 /* Sink DDC slave address */
+#define HDCP_SINK_DDC_HDCP2_VERSION 0x50 /* Does sink support HDCP2.2 */
+#define HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE 0x60 /* HDCP Tx writes here */
+#define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70 /* RxStatus, 2 bytes */
+#define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80 /* HDCP Tx reads here */
+
+#define HDCP2P2_DEFAULT_TIMEOUT 500
+
+/*
+ * HDCP 2.2 encryption requires the data encryption block that is present in
+ * HDMI controller version 4.0.0 and above
+ */
+#define MIN_HDMI_TX_MAJOR_VERSION 4
+
+enum sde_hdmi_hdcp2p2_sink_status {
+ SINK_DISCONNECTED,
+ SINK_CONNECTED
+};
+
+enum sde_hdmi_auth_status {
+ HDMI_HDCP_AUTH_STATUS_FAILURE,
+ HDMI_HDCP_AUTH_STATUS_SUCCESS
+};
+
+struct sde_hdmi_hdcp2p2_ctrl {
+ atomic_t auth_state;
+ enum sde_hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */
+ struct sde_hdcp_init_data init_data; /* Feature data from HDMI drv */
+ struct mutex mutex; /* mutex to protect access to ctrl */
+ struct mutex msg_lock; /* mutex to protect access to msg buffer */
+ struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/
+ struct sde_hdcp_ops *ops;
+ void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */
+ struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */
+
+ enum hdmi_hdcp_wakeup_cmd wakeup_cmd;
+ enum sde_hdmi_auth_status auth_status;
+ char *send_msg_buf;
+ uint32_t send_msg_len;
+ uint32_t timeout;
+ uint32_t timeout_left;
+
+ struct task_struct *thread;
+ struct kthread_worker worker;
+ struct kthread_work status;
+ struct kthread_work auth;
+ struct kthread_work send_msg;
+ struct kthread_work recv_msg;
+ struct kthread_work link;
+ struct kthread_work poll;
+};
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+
+static bool sde_hdcp2p2_is_valid_state(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
+ return true;
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ return true;
+
+ return false;
+}
+
+static int sde_hdmi_hdcp2p2_copy_buf(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdmi_hdcp_wakeup_data *data)
+{
+ mutex_lock(&ctrl->msg_lock);
+
+ if (!data->send_msg_len) {
+ mutex_unlock(&ctrl->msg_lock);
+ return 0;
+ }
+
+ ctrl->send_msg_len = data->send_msg_len;
+
+ kzfree(ctrl->send_msg_buf);
+
+ ctrl->send_msg_buf = kzalloc(data->send_msg_len, GFP_KERNEL);
+
+ if (!ctrl->send_msg_buf) {
+ mutex_unlock(&ctrl->msg_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len);
+
+ mutex_unlock(&ctrl->msg_lock);
+
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ if (!data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ctrl = data->context;
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctrl->wakeup_mutex);
+
+ SDE_HDCP_DEBUG("cmd: %s, timeout %dms\n",
+ hdmi_hdcp_cmd_to_str(data->cmd),
+ data->timeout);
+
+ ctrl->wakeup_cmd = data->cmd;
+
+ if (data->timeout)
+ ctrl->timeout = data->timeout * 2;
+ else
+ ctrl->timeout = HDCP2P2_DEFAULT_TIMEOUT;
+
+ if (!sde_hdcp2p2_is_valid_state(ctrl)) {
+ SDE_ERROR("invalid state\n");
+ goto exit;
+ }
+
+ if (sde_hdmi_hdcp2p2_copy_buf(ctrl, data))
+ goto exit;
+
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS;
+ else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE;
+
+ switch (ctrl->wakeup_cmd) {
+ case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->send_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->recv_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
+ case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
+ queue_kthread_work(&ctrl->worker, &ctrl->status);
+ break;
+ case HDMI_HDCP_WKUP_CMD_LINK_POLL:
+ queue_kthread_work(&ctrl->worker, &ctrl->poll);
+ break;
+ case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
+ queue_kthread_work(&ctrl->worker, &ctrl->auth);
+ break;
+ default:
+ SDE_ERROR("invalid wakeup command %d\n", ctrl->wakeup_cmd);
+ }
+exit:
+ mutex_unlock(&ctrl->wakeup_mutex);
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup_lib(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdcp_lib_wakeup_data *data)
+{
+ int rc = 0;
+
+ if (ctrl && ctrl->lib && ctrl->lib->wakeup &&
+ data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) {
+ rc = ctrl->lib->wakeup(data);
+ if (rc)
+ SDE_ERROR("error sending %s to lib\n",
+ hdcp_lib_cmd_to_str(data->cmd));
+ }
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_reset(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+}
+
+static void sde_hdmi_hdcp2p2_off(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+}
+
+static int sde_hdmi_hdcp2p2_authenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+ u32 regval;
+ int rc = 0;
+
+ /* Enable authentication success interrupt */
+ regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2);
+ regval |= BIT(1) | BIT(2);
+
+ DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ ctrl->sink_status = SINK_CONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING);
+
+ /* make sure ddc is idle before starting hdcp 2.2 authentication */
+ _sde_hdmi_scrambler_ddc_disable((void *)ctrl->init_data.cb_data);
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_reauthenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ return sde_hdmi_hdcp2p2_authenticate(input);
+}
+
+static void sde_hdmi_hdcp2p2_min_level_change(void *client_ctx,
+int min_enc_lvl)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl =
+ (struct sde_hdmi_hdcp2p2_ctrl *)client_ctx;
+ struct hdcp_lib_wakeup_data cdata = {
+ HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};
+ bool enc_notify = true;
+ enum sde_hdcp_states enc_lvl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ switch (min_enc_lvl) {
+ case 0:
+ enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ break;
+ case 1:
+ enc_lvl = HDCP_STATE_AUTH_ENC_1X;
+ break;
+ case 2:
+ enc_lvl = HDCP_STATE_AUTH_ENC_2P2;
+ break;
+ default:
+ enc_notify = false;
+ }
+
+ SDE_HDCP_DEBUG("enc level changed %d\n", min_enc_lvl);
+
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ if (enc_notify && ctrl->init_data.notify_status)
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_failed(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
+
+ sde_hdmi_hdcp2p2_ddc_disable(ctrl->init_data.cb_data);
+
+ /* notify hdmi tx about HDCP failure */
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTH_FAIL);
+}
+
+static int sde_hdmi_hdcp2p2_ddc_rd_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, int size, u32 timeout)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->request_len = size;
+ ddc_data->retry = 0;
+ ddc_data->hard_timeout = timeout;
+ ddc_data->what = "HDCP2ReadMessage";
+
+ rc = sde_hdmi_ddc_read(ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot read HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_ddc_wt_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, size_t size)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->hard_timeout = ctrl->timeout;
+ ddc_data->what = "HDCP2WriteMessage";
+
+ rc = sde_hdmi_ddc_write((void *)ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot write HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_read_version(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *hdcp2version)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_VERSION;
+ ddc_data->data_buf = hdcp2version;
+ ddc_data->data_len = 1;
+ ddc_data->request_len = 1;
+ ddc_data->retry = 1;
+ ddc_data->what = "HDCP2Version";
+
+ rc = sde_hdmi_ddc_read((void *)ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("Cannot read HDCP2Version register");
+ return rc;
+ }
+
+ SDE_HDCP_DEBUG("Read HDCP2Version as %u\n", *hdcp2version);
+ return rc;
+}
+
+static bool sde_hdmi_hdcp2p2_feature_supported(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdcp_txmtr_ops *lib = NULL;
+ bool supported = false;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ goto end;
+ }
+
+ lib = ctrl->lib;
+ if (!lib) {
+ SDE_ERROR("invalid lib ops data\n");
+ goto end;
+ }
+
+ if (lib->feature_supported) {
+ supported = lib->feature_supported(
+ ctrl->lib_ctx);
+ }
+
+end:
+ return supported;
+}
+
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int rc = 0;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ uint32_t msglen;
+ char *msg = NULL;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ mutex_lock(&ctrl->msg_lock);
+ msglen = ctrl->send_msg_len;
+
+ if (!msglen) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ msg = kzalloc(msglen, GFP_KERNEL);
+ if (!msg) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(msg, ctrl->send_msg_buf, msglen);
+ mutex_unlock(&ctrl->msg_lock);
+
+ /* Forward the message to the sink */
+ rc = sde_hdmi_hdcp2p2_ddc_wt_message(ctrl,
+ msg, (size_t)msglen);
+ if (rc) {
+ SDE_ERROR("Error sending msg to sink %d\n", rc);
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED;
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS;
+ cdata.timeout = ctrl->timeout_left;
+ }
+exit:
+ kfree(msg);
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static void sde_hdmi_hdcp2p2_send_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, send_msg);
+
+ sde_hdmi_hdcp2p2_send_msg(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_cb(void *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = data;
+
+ if (!ctrl) {
+ SDE_HDCP_DEBUG("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ queue_kthread_work(&ctrl->worker, &ctrl->link);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int timeout_hsync = 0, rc = 0;
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ pr_err("invalid ddc ctrl\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data, ctrl->timeout);
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+
+ SDE_HDCP_DEBUG("timeout for rxstatus %dms, %d hsync\n",
+ ctrl->timeout, timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE | RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_ms = ctrl->timeout;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync / 20;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->wait = true;
+
+ rc = sde_hdmi_hdcp2p2_read_rxstatus(ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("error reading rxstatus %d\n", rc);
+ goto exit;
+ }
+
+ if (ddc_data->reauth_req) {
+ ddc_data->reauth_req = false;
+
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ SDE_HDCP_DEBUG("timeout left after rxstatus %dms, msg size %d\n",
+ ctrl->timeout_left, ddc_data->message_size);
+
+ if (!ddc_data->message_size) {
+ SDE_ERROR("recvd invalid message size\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, ctrl->timeout_left);
+ if (rc) {
+ SDE_ERROR("error reading message %d\n", rc);
+ goto exit;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ cdata.timeout = ctrl->timeout_left;
+exit:
+ if (rc == -ETIMEDOUT)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT;
+ else if (rc)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED;
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, recv_msg);
+
+ sde_hdmi_hdcp2p2_recv_msg(ctrl);
+}
+
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ int timeout_hsync;
+ int ret;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+
+ if (!ddc_ctrl)
+ return -EINVAL;
+
+ sde_hdmi_ddc_config(ctrl->init_data.cb_data);
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data,
+ jiffies_to_msecs(HZ / 2));
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+ SDE_HDCP_DEBUG("timeout for rxstatus %d hsyncs\n", timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE |
+ RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->link_cb = sde_hdmi_hdcp2p2_link_cb;
+ ddc_data->link_data = ctrl;
+
+ ret = sde_hdmi_hdcp2p2_read_rxstatus((void *)ctrl->init_data.cb_data);
+ return ret;
+}
+
+static void sde_hdmi_hdcp2p2_poll_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, poll);
+
+ sde_hdmi_hdcp2p2_link_check(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return;
+ }
+
+ if (ctrl->auth_status == HDMI_HDCP_AUTH_STATUS_SUCCESS) {
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTHENTICATED);
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED);
+ } else {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ }
+}
+
+static void sde_hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, status);
+
+ sde_hdmi_hdcp2p2_auth_status(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_work(struct kthread_work *work)
+{
+ int rc = 0;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, link);
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ rc = -EINVAL;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ if (ddc_data->reauth_req) {
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+
+ ddc_data->reauth_req = false;
+ rc = -ENOLINK;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ if (ddc_data->ready && ddc_data->message_size) {
+ SDE_HDCP_DEBUG("topology changed. rxstatus msg size %d\n",
+ ddc_data->message_size);
+
+ ddc_data->ready = false;
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, HDCP2P2_DEFAULT_TIMEOUT);
+ if (rc) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ SDE_ERROR("error reading message %d\n", rc);
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ }
+
+ ddc_data->message_size = 0;
+ }
+exit:
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+
+ if (rc) {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ return;
+ }
+}
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ int rc = 0;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_START;
+ else
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+
+ rc = sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ if (rc)
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_auth_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, auth);
+
+ sde_hdmi_hdcp2p2_auth(ctrl);
+}
+
+void sde_hdmi_hdcp2p2_deinit(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ kthread_stop(ctrl->thread);
+
+ mutex_destroy(&ctrl->mutex);
+ mutex_destroy(&ctrl->msg_lock);
+ mutex_destroy(&ctrl->wakeup_mutex);
+ kfree(ctrl);
+}
+
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
+{
+ int rc;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ static struct sde_hdcp_ops ops = {
+ .reauthenticate = sde_hdmi_hdcp2p2_reauthenticate,
+ .authenticate = sde_hdmi_hdcp2p2_authenticate,
+ .feature_supported = sde_hdmi_hdcp2p2_feature_supported,
+ .off = sde_hdmi_hdcp2p2_off
+ };
+
+ static struct hdcp_client_ops client_ops = {
+ .wakeup = sde_hdmi_hdcp2p2_wakeup,
+ .notify_lvl_change = sde_hdmi_hdcp2p2_min_level_change,
+ };
+
+ static struct hdcp_txmtr_ops txmtr_ops;
+ struct hdcp_register_data register_data;
+
+ SDE_HDCP_DEBUG("HDCP2P2 feature initialization\n");
+
+ if (!init_data || !init_data->core_io || !init_data->mutex ||
+ !init_data->ddc_ctrl || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
+ SDE_ERROR("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) {
+ SDE_ERROR("HDMI Tx does not support HDCP 2.2\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ ctrl->init_data = *init_data;
+ ctrl->lib = &txmtr_ops;
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+
+ ctrl->ops = &ops;
+ mutex_init(&ctrl->mutex);
+ mutex_init(&ctrl->msg_lock);
+ mutex_init(&ctrl->wakeup_mutex);
+
+ register_data.hdcp_ctx = &ctrl->lib_ctx;
+ register_data.client_ops = &client_ops;
+ register_data.txmtr_ops = &txmtr_ops;
+ register_data.device_type = HDCP_TXMTR_HDMI;
+ register_data.client_ctx = ctrl;
+
+ rc = hdcp_library_register(&register_data);
+ if (rc) {
+ SDE_ERROR("Unable to register with HDCP 2.2 library\n");
+ goto error;
+ }
+
+ init_kthread_worker(&ctrl->worker);
+
+ init_kthread_work(&ctrl->auth, sde_hdmi_hdcp2p2_auth_work);
+ init_kthread_work(&ctrl->send_msg, sde_hdmi_hdcp2p2_send_msg_work);
+ init_kthread_work(&ctrl->recv_msg, sde_hdmi_hdcp2p2_recv_msg_work);
+ init_kthread_work(&ctrl->status, sde_hdmi_hdcp2p2_auth_status_work);
+ init_kthread_work(&ctrl->link, sde_hdmi_hdcp2p2_link_work);
+ init_kthread_work(&ctrl->poll, sde_hdmi_hdcp2p2_poll_work);
+
+ ctrl->thread = kthread_run(kthread_worker_fn,
+ &ctrl->worker, "hdmi_hdcp2p2");
+
+ if (IS_ERR(ctrl->thread)) {
+ SDE_ERROR("unable to start hdcp2p2 thread\n");
+ rc = PTR_ERR(ctrl->thread);
+ ctrl->thread = NULL;
+ goto error;
+ }
+
+ return ctrl;
+error:
+ kfree(ctrl);
+ return ERR_PTR(rc);
+}
+
+static bool sde_hdmi_hdcp2p2_supported(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ u8 hdcp2version = 0;
+ int rc = sde_hdmi_hdcp2p2_read_version(ctrl, &hdcp2version);
+
+ if (rc)
+ goto error;
+
+ if (hdcp2version & BIT(2)) {
+ SDE_HDCP_DEBUG("Sink is HDCP 2.2 capable\n");
+ return true;
+ }
+
+error:
+ SDE_HDCP_DEBUG("Sink is not HDCP 2.2 capable\n");
+ return false;
+}
+
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ SDE_HDCP_DEBUG("Checking sink capability\n");
+ if (sde_hdmi_hdcp2p2_supported(ctrl))
+ return ctrl->ops;
+ else
+ return NULL;
+
+}
+
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
new file mode 100644
index 000000000000..a7887d2c84b0
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
@@ -0,0 +1,827 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/iopoll.h>
+#include <linux/types.h>
+#include <linux/switch.h>
+#include <linux/gcd.h>
+
+#include "drm_edid.h"
+#include "sde_kms.h"
+#include "sde_hdmi.h"
+#include "sde_hdmi_regs.h"
+#include "hdmi.h"
+
+#define HDMI_SEC_TO_MS 1000
+#define HDMI_MS_TO_US 1000
+#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_BUSY_WAIT_DELAY_US 100
+
+static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display)
+{
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!display) {
+ pr_err("invalid ddc ctrl\n");
+ return;
+ }
+ hdmi = display->ctrl.ctrl;
+ /* check for errors and clear status */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_STATUS);
+
+ if (reg_val & BIT(4)) {
+ pr_debug("ddc aborted\n");
+ reg_val |= BIT(5);
+ }
+
+ if (reg_val & BIT(8)) {
+ pr_debug("timed out\n");
+ reg_val |= BIT(9);
+ }
+
+ if (reg_val & BIT(12)) {
+ pr_debug("NACK0\n");
+ reg_val |= BIT(13);
+ }
+
+ if (reg_val & BIT(14)) {
+ pr_debug("NACK1\n");
+ reg_val |= BIT(15);
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val);
+}
+
+/**
+ * sde_hdmi_dump_regs - utility to dump HDMI regs
+ * @hdmi_display: Pointer to private display handle
+ * Return : void
+ */
+
+void sde_hdmi_dump_regs(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+ int i;
+ u32 addr_off = 0;
+ u32 len = 0;
+
+ if (!display) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (!hdmi->power_on || !display->connected) {
+ SDE_ERROR("HDMI display is not ready\n");
+ return;
+ }
+
+ len = hdmi->mmio_len;
+
+ if (len % 16)
+ len += 16;
+ len /= 16;
+
+ pr_info("HDMI CORE regs\n");
+ for (i = 0; i < len; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = hdmi_read(hdmi, addr_off+0x0);
+ x4 = hdmi_read(hdmi, addr_off+0x4);
+ x8 = hdmi_read(hdmi, addr_off+0x8);
+ xc = hdmi_read(hdmi, addr_off+0xc);
+
+ pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8,
+ xc);
+
+ addr_off += 16;
+ }
+}
+
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display)
+{
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ u32 intr0, intr2, intr5;
+ u32 msg_size;
+ int rc = 0;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr0 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL0);
+ intr2 = hdmi_read(hdmi, HDMI_HDCP_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr0: 0x%x, intr2: 0x%x, intr5: 0x%x\n",
+ intr0, intr2, intr5);
+
+ /* check if encryption is enabled */
+ if (intr2 & BIT(0)) {
+ /*
+ * ack encryption ready interrupt.
+ * disable encryption ready interrupt.
+ * enable encryption not ready interrupt.
+ */
+ intr2 &= ~BIT(2);
+ intr2 |= BIT(1) | BIT(6);
+
+ pr_info("HDCP 2.2 Encryption enabled\n");
+ data->encryption_ready = true;
+ }
+
+ /* check if encryption is disabled */
+ if (intr2 & BIT(4)) {
+ /*
+ * ack encryption not ready interrupt.
+ * disable encryption not ready interrupt.
+ * enable encryption ready interrupt.
+ */
+ intr2 &= ~BIT(6);
+ intr2 |= BIT(5) | BIT(2);
+
+ pr_info("HDCP 2.2 Encryption disabled\n");
+ data->encryption_ready = false;
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP_INT_CTRL2, intr2);
+
+ /* get the message size bits 29:20 */
+ msg_size = (intr0 & (0x3FF << 20)) >> 20;
+
+ if (msg_size) {
+ /* ack and disable message size interrupt */
+ intr0 |= BIT(30);
+ intr0 &= ~BIT(31);
+
+ data->message_size = msg_size;
+ }
+
+ /* check and disable ready interrupt */
+ if (intr0 & BIT(16)) {
+ /* ack ready/not ready interrupt */
+ intr0 |= BIT(17);
+ intr0 &= ~BIT(18);
+ pr_debug("got ready interrupt\n");
+ data->ready = true;
+ }
+
+ /* check for reauth req interrupt */
+ if (intr0 & BIT(12)) {
+ /* ack and disable reauth req interrupt */
+ intr0 |= BIT(13);
+ intr0 &= ~BIT(14);
+ pr_err("got reauth interrupt\n");
+ data->reauth_req = true;
+ }
+
+ /* check for ddc fail interrupt */
+ if (intr0 & BIT(8)) {
+ /* ack ddc fail interrupt */
+ intr0 |= BIT(9);
+ pr_err("got ddc fail interrupt\n");
+ data->ddc_max_retries_fail = true;
+ }
+
+ /* check for ddc done interrupt */
+ if (intr0 & BIT(4)) {
+ /* ack ddc done interrupt */
+ intr0 |= BIT(5);
+ pr_debug("got ddc done interrupt\n");
+ data->ddc_done = true;
+ }
+
+ /* check for ddc read req interrupt */
+ if (intr0 & BIT(0)) {
+ /* ack read req interrupt */
+ intr0 |= BIT(1);
+
+ data->ddc_read_req = true;
+ }
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, intr0);
+
+ if (intr5 & BIT(0)) {
+ pr_err("RXSTATUS_DDC_REQ_TIMEOUT\n");
+
+ /* ack and disable timeout interrupt */
+ intr5 |= BIT(1);
+ intr5 &= ~BIT(2);
+
+ data->ddc_timeout = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (data->message_size || data->ready || data->reauth_req) {
+ if (data->wait) {
+ complete(&ddc_ctrl->rx_status_done);
+ } else if (data->link_cb && data->link_data) {
+ data->link_cb(data->link_data);
+ } else {
+ pr_err("new msg/reauth not handled\n");
+ rc = -EINVAL;
+ }
+ }
+
+ sde_hdmi_hdcp2p2_ddc_clear_status(display);
+
+ return rc;
+}
+
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display)
+{
+
+ bool scrambler_timer_off = false;
+ u32 intr2, intr5;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr2 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr2: 0x%x, intr5: 0x%x\n", intr2, intr5);
+
+ if (intr2 & BIT(12)) {
+ pr_err("SCRAMBLER_STATUS_NOT\n");
+
+ intr2 |= BIT(14);
+ scrambler_timer_off = true;
+ }
+
+ if (intr2 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_FAILED\n");
+
+ intr2 |= BIT(9);
+
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL2, intr2);
+
+ if (intr5 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_REQ_TIMEOUT\n");
+ intr5 |= BIT(9);
+ intr5 &= ~BIT(10);
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (scrambler_timer_off)
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
+
+ return 0;
+}
+
+static int sde_hdmi_ddc_read_retry(struct sde_hdmi *display)
+{
+ int status;
+ int busy_wait_us;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct hdmi *hdmi;
+
+ if (!display) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ hdmi = display->ctrl.ctrl;
+ ddc_ctrl = &display->ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (!ddc_data->data_buf) {
+ status = -EINVAL;
+ SDE_ERROR("%s: invalid buf\n", ddc_data->what);
+ goto error;
+ }
+
+ if (ddc_data->retry < 0) {
+ SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
+ status = -EINVAL;
+ goto error;
+ }
+
+ do {
+ if (ddc_data->hard_timeout) {
+ HDMI_UTIL_DEBUG("using hard_timeout %dms\n",
+ ddc_data->hard_timeout);
+
+ busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
+ hdmi->use_hard_timeout = true;
+ hdmi->busy_wait_us = busy_wait_us;
+ }
+
+ /* Calling upstream ddc read method */
+ status = hdmi_ddc_read(hdmi, ddc_data->dev_addr,
+ ddc_data->offset,
+ ddc_data->data_buf, ddc_data->request_len,
+ false);
+
+ if (ddc_data->hard_timeout)
+ ddc_data->timeout_left = hdmi->timeout_count;
+
+
+ if (ddc_data->hard_timeout && !hdmi->timeout_count) {
+ HDMI_UTIL_DEBUG("%s: timedout\n", ddc_data->what);
+ status = -ETIMEDOUT;
+ }
+
+ } while (status && ddc_data->retry--);
+
+ if (status) {
+ HDMI_UTIL_ERROR("%s: failed status = %d\n",
+ ddc_data->what, status);
+ goto error;
+ }
+
+ HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what);
+
+error:
+ return status;
+} /* sde_hdmi_ddc_read_retry */
+
+int sde_hdmi_ddc_read(void *cb_data)
+{
+ int rc = 0;
+ int retry;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi *display = (struct sde_hdmi *)cb_data;
+
+ if (!display) {
+ SDE_ERROR("invalid ddc ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = &display->ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+ retry = ddc_data->retry;
+
+ rc = sde_hdmi_ddc_read_retry(display);
+ if (!rc)
+ return rc;
+
+ if (ddc_data->retry_align) {
+ ddc_data->retry = retry;
+
+ ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32);
+ rc = sde_hdmi_ddc_read_retry(display);
+ }
+
+ return rc;
+} /* hdmi_ddc_read */
+
+int sde_hdmi_ddc_write(void *cb_data)
+{
+ int status;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ int busy_wait_us;
+ struct hdmi *hdmi;
+ struct sde_hdmi *display = (struct sde_hdmi *)cb_data;
+
+ if (!display) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ hdmi = display->ctrl.ctrl;
+ ddc_ctrl = &display->ddc_ctrl;
+
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (!ddc_data->data_buf) {
+ status = -EINVAL;
+ SDE_ERROR("%s: invalid buf\n", ddc_data->what);
+ goto error;
+ }
+
+ if (ddc_data->retry < 0) {
+ SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry);
+ status = -EINVAL;
+ goto error;
+ }
+
+ do {
+ if (ddc_data->hard_timeout) {
+ busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
+ hdmi->use_hard_timeout = true;
+ hdmi->busy_wait_us = busy_wait_us;
+ }
+
+ status = hdmi_ddc_write(hdmi,
+ ddc_data->dev_addr, ddc_data->offset,
+ ddc_data->data_buf, ddc_data->data_len,
+ false);
+
+ if (ddc_data->hard_timeout)
+ ddc_data->timeout_left = hdmi->timeout_count;
+
+ if (ddc_data->hard_timeout && !hdmi->timeout_count) {
+ HDMI_UTIL_ERROR("%s timout\n", ddc_data->what);
+ status = -ETIMEDOUT;
+ }
+
+ } while (status && ddc_data->retry--);
+
+ if (status) {
+ HDMI_UTIL_ERROR("%s: failed status = %d\n",
+ ddc_data->what, status);
+ goto error;
+ }
+
+ HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what);
+error:
+ return status;
+} /* hdmi_ddc_write */
+
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ return (hdmi_ctrl->hdcp14_present || hdmi_ctrl->hdcp22_present) &&
+ hdmi_ctrl->hdcp_ops;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ bool enc_en = true;
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ goto end;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ reg_val = hdmi_read(hdmi, HDMI_HDCP_CTRL2);
+ if ((reg_val & BIT(0)) && (reg_val & BIT(1)))
+ goto end;
+
+ if (hdmi_read(hdmi, HDMI_CTRL) & BIT(2))
+ goto end;
+
+ return false;
+
+end:
+ return enc_en;
+} /* sde_hdmi_tx_is_encryption_set */
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ bool ret;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ switch (hdmi_ctrl->enc_lvl) {
+ case HDCP_STATE_AUTH_ENC_NONE:
+ ret = true;
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ ret = sde_hdmi_tx_is_hdcp_enabled(hdmi_ctrl) &&
+ hdmi_ctrl->auth_state;
+ break;
+ case HDCP_STATE_AUTH_ENC_2P2:
+ ret = hdmi_ctrl->hdcp22_present &&
+ hdmi_ctrl->auth_state;
+ break;
+ default:
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ return hdmi_ctrl->connected && hdmi->power_on;
+}
+
+int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
+{
+ u32 av_mute_status;
+ bool av_pkt_en = false;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid HDMI Ctrl\n");
+ return -ENODEV;
+ }
+
+ av_mute_status = hdmi_read(hdmi, HDMI_GC);
+
+ if (set) {
+ if (!(av_mute_status & BIT(0))) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
+ av_pkt_en = true;
+ }
+ } else {
+ if (av_mute_status & BIT(0)) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
+ av_pkt_en = true;
+ }
+ }
+
+ /* Enable AV Mute tranmission here */
+ if (av_pkt_en)
+ hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
+ hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
+
+ pr_info("AVMUTE %s\n", set ? "set" : "cleared");
+
+ return 0;
+}
+
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct drm_display_mode mode = display->mode;
+ /*
+ * pixel clock = h_total * v_total * fps
+ * 1 sec = pixel clock number of pixels are transmitted.
+ * time taken by one line (h_total) = 1s / (v_total * fps).
+ * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
+ * = (time_ms * clock) / h_total
+ */
+
+ return (timeout_ms * mode.clock / mode.htotal);
+}
+
+static void sde_hdmi_hdcp2p2_ddc_reset(struct sde_hdmi *hdmi_ctrl)
+{
+ u32 reg_val;
+ struct hdmi *hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ /*
+ * Clear acks for DDC_REQ, DDC_DONE, DDC_FAILED, RXSTATUS_READY,
+ * RXSTATUS_MSG_SIZE
+ */
+ reg_val = BIT(30) | BIT(17) | BIT(13) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_ddc_reset(display);
+
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+}
+
+static void _sde_hdmi_scrambler_ddc_reset(struct hdmi *hdmi)
+{
+ u32 reg_val;
+
+ /* clear ack and disable interrupts */
+ reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
+
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+}
+
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ _sde_hdmi_scrambler_ddc_reset(hdmi);
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(8) | BIT(9));
+ hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_ddc_config(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+ hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
+ HDMI_DDC_SPEED_THRESHOLD(2) |
+ HDMI_DDC_SPEED_PRESCALE(10));
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
+ HDMI_DDC_SETUP_TIMEOUT(0xff));
+
+ /* enable reference timer for 19us */
+ hdmi_write(hdmi, REG_HDMI_DDC_REF,
+ HDMI_DDC_REF_REFTIMER_ENABLE |
+ HDMI_DDC_REF_REFTIMER(19));
+}
+
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display)
+{
+ u32 reg_val;
+ u32 intr_en_mask;
+ u32 timeout;
+ u32 timer;
+ int rc = 0;
+ int busy_wait_us;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u32 rem;
+
+ if (!hdmi) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ if (!data) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ rc = ddc_clear_irq(hdmi);
+ if (rc) {
+ pr_err("DDC clear irq failed\n");
+ return rc;
+ }
+ intr_en_mask = data->intr_mask;
+ intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
+
+ /* Disable short read for now, sinks don't support it */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val |= BIT(4);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ /*
+ * Setup the DDC timers for HDMI_HDCP2P2_DDC_TIMER_CTRL1 and
+ * HDMI_HDCP2P2_DDC_TIMER_CTRL2.
+ * Following are the timers:
+ * 1. DDC_REQUEST_TIMER: Timeout in hsyncs in which to wait for the
+ * HDCP 2.2 sink to respond to an RxStatus request
+ * 2. DDC_URGENT_TIMER: Time period in hsyncs to issue an urgent flag
+ * when an RxStatus DDC request is made but not accepted by I2C
+ * engine
+ * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when
+ * a request is made and stops when it is accepted by DDC arbiter
+ */
+
+ timeout = data->timeout_hsync;
+ timer = data->periodic_timer_hsync;
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer);
+ /* Set both urgent and hw-timeout fields to the same value */
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL2,
+ (timeout << 16 | timeout));
+ /* enable interrupts */
+ reg_val = intr_en_mask;
+ /* Clear interrupt status bits */
+ reg_val |= intr_en_mask >> 1;
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+ /* clear and enable RxStatus read timeout */
+ reg_val |= BIT(2) | BIT(1);
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, reg_val);
+ /*
+ * Enable hardware DDC access to RxStatus register
+ *
+ * HDMI_HW_DDC_CTRL:Bits 1:0 (RXSTATUS_DDC_ENABLE) read like this:
+ *
+ * 0 = disable HW controlled DDC access to RxStatus
+ * 1 = automatic on when HDCP 2.2 is authenticated and loop based on
+ * request timer (i.e. the hardware will loop automatically)
+ * 2 = force on and loop based on request timer (hardware will loop)
+ * 3 = enable by sw trigger and loop until interrupt is generated for
+ * RxStatus.reauth_req, RxStatus.ready or RxStatus.message_Size.
+ *
+ * Depending on the value of ddc_data::poll_sink, we make the decision
+ * to use either SW_TRIGGER(3) (poll_sink = false) which means that the
+ * hardware will poll sink and generate interrupt when sink responds,
+ * or use AUTOMATIC_LOOP(1) (poll_sink = true) which will poll the sink
+ * based on request timer
+ */
+
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ busy_wait_us = data->timeout_ms * HDMI_MS_TO_US;
+
+ /* read method: HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER */
+ reg_val |= BIT(1) | BIT(0);
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);
+ if (data->wait) {
+ reinit_completion(&ddc_ctrl->rx_status_done);
+ rem = wait_for_completion_timeout(&ddc_ctrl->rx_status_done,
+ HZ);
+ data->timeout_left = jiffies_to_msecs(rem);
+
+ if (!data->timeout_left) {
+ pr_err("sw ddc rxstatus timeout\n");
+ rc = -ETIMEDOUT;
+ }
+ sde_hdmi_hdcp2p2_ddc_disable((void *)display);
+ }
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
new file mode 100644
index 000000000000..6b310acee0ff
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_HDMI_UTIL_H_
+#define _SDE_HDMI_UTIL_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/msm_ext_display.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "hdmi.h"
+#include "sde_kms.h"
+#include "sde_connector.h"
+#include "msm_drv.h"
+#include "sde_hdmi_regs.h"
+
+#ifdef HDMI_UTIL_DEBUG_ENABLE
+#define HDMI_UTIL_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define HDMI_UTIL_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+#define HDMI_UTIL_ERROR(fmt, args...) SDE_ERROR(fmt, ##args)
+
+/*
+ * Offsets in HDMI_DDC_INT_CTRL0 register
+ *
+ * The HDMI_DDC_INT_CTRL0 register is intended for HDCP 2.2 RxStatus
+ * register manipulation. It reads like this:
+ *
+ * Bit 31: RXSTATUS_MESSAGE_SIZE_MASK (1 = generate interrupt when size > 0)
+ * Bit 30: RXSTATUS_MESSAGE_SIZE_ACK (1 = Acknowledge message size intr)
+ * Bits 29-20: RXSTATUS_MESSAGE_SIZE (Actual size of message available)
+ * Bits 19-18: RXSTATUS_READY_MASK (1 = generate interrupt when ready = 1
+ * 2 = generate interrupt when ready = 0)
+ * Bit 17: RXSTATUS_READY_ACK (1 = Acknowledge ready bit interrupt)
+ * Bit 16: RXSTATUS_READY (1 = Rxstatus ready bit read is 1)
+ * Bit 15: RXSTATUS_READY_NOT (1 = Rxstatus ready bit read is 0)
+ * Bit 14: RXSTATUS_REAUTH_REQ_MASK (1 = generate interrupt when reauth is
+ * requested by sink)
+ * Bit 13: RXSTATUS_REAUTH_REQ_ACK (1 = Acknowledge Reauth req interrupt)
+ * Bit 12: RXSTATUS_REAUTH_REQ (1 = Rxstatus reauth req bit read is 1)
+ * Bit 10: RXSTATUS_DDC_FAILED_MASK (1 = generate interrupt when DDC
+ * tranasaction fails)
+ * Bit 9: RXSTATUS_DDC_FAILED_ACK (1 = Acknowledge ddc failure interrupt)
+ * Bit 8: RXSTATUS_DDC_FAILED (1 = DDC transaction failed)
+ * Bit 6: RXSTATUS_DDC_DONE_MASK (1 = generate interrupt when DDC
+ * transaction completes)
+ * Bit 5: RXSTATUS_DDC_DONE_ACK (1 = Acknowledge ddc done interrupt)
+ * Bit 4: RXSTATUS_DDC_DONE (1 = DDC transaction is done)
+ * Bit 2: RXSTATUS_DDC_REQ_MASK (1 = generate interrupt when DDC Read
+ * request for RXstatus is made)
+ * Bit 1: RXSTATUS_DDC_REQ_ACK (1 = Acknowledge Rxstatus read interrupt)
+ * Bit 0: RXSTATUS_DDC_REQ (1 = RXStatus DDC read request is made)
+ *
+ */
+
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_SHIFT 20
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK 0x3ff00000
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_ACK_SHIFT 30
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_INTR_SHIFT 31
+
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_SHIFT 12
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_MASK 1
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_ACK_SHIFT 13
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_INTR_SHIFT 14
+
+#define HDCP2P2_RXSTATUS_READY_SHIFT 16
+#define HDCP2P2_RXSTATUS_READY_MASK 1
+#define HDCP2P2_RXSTATUS_READY_ACK_SHIFT 17
+#define HDCP2P2_RXSTATUS_READY_INTR_SHIFT 18
+#define HDCP2P2_RXSTATUS_READY_INTR_MASK 18
+
+#define HDCP2P2_RXSTATUS_DDC_FAILED_SHIFT 8
+#define HDCP2P2_RXSTATUS_DDC_FAILED_ACKSHIFT 9
+#define HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK 10
+#define HDCP2P2_RXSTATUS_DDC_DONE 6
+
+/* default hsyncs for 4k@60 for 200ms */
+#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
+/*
+ * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be
+ * read by the hardware
+ */
+#define HDCP2P2_RXSTATUS_HW_DDC_DISABLE 0
+#define HDCP2P2_RXSTATUS_HW_DDC_AUTOMATIC_LOOP 1
+#define HDCP2P2_RXSTATUS_HW_DDC_FORCE_LOOP 2
+#define HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER 3
+
+struct sde_hdmi_tx_ddc_data {
+ char *what;
+ u8 *data_buf;
+ u32 data_len;
+ u32 dev_addr;
+ u32 offset;
+ u32 request_len;
+ u32 retry_align;
+ u32 hard_timeout;
+ u32 timeout_left;
+ int retry;
+};
+
+enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask {
+ RXSTATUS_MESSAGE_SIZE = BIT(31),
+ RXSTATUS_READY = BIT(18),
+ RXSTATUS_REAUTH_REQ = BIT(14),
+};
+
+struct sde_hdmi_tx_hdcp2p2_ddc_data {
+ enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask;
+ u32 timeout_ms;
+ u32 timeout_hsync;
+ u32 periodic_timer_hsync;
+ u32 timeout_left;
+ u32 read_method;
+ u32 message_size;
+ bool encryption_ready;
+ bool ready;
+ bool reauth_req;
+ bool ddc_max_retries_fail;
+ bool ddc_done;
+ bool ddc_read_req;
+ bool ddc_timeout;
+ bool wait;
+ int irq_wait_count;
+ void (*link_cb)(void *data);
+ void *link_data;
+};
+
+struct sde_hdmi_tx_ddc_ctrl {
+ struct completion rx_status_done;
+ struct dss_io_data *io;
+ struct sde_hdmi_tx_ddc_data ddc_data;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data sde_hdcp2p2_ddc_data;
+};
+
+/* DDC */
+int sde_hdmi_ddc_write(void *cb_data);
+int sde_hdmi_ddc_read(void *cb_data);
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display);
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms);
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display);
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display);
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display);
+void sde_hdmi_ddc_config(void *hdmi_display);
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display);
+void sde_hdmi_dump_regs(void *hdmi_display);
+#endif /* _SDE_HDMI_UTIL_H_ */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 7915562057d6..7d660ba56594 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -95,7 +95,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
struct hdmi_platform_config *config = pdev->dev.platform_data;
struct hdmi *hdmi = NULL;
struct resource *res;
- int i, ret;
+ int i, ret = 0;
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi) {
@@ -119,9 +119,19 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
}
}
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, config->mmio_name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find ctrl resource\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->mmio_len = (u32)resource_size(res);
hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
if (IS_ERR(hdmi->mmio)) {
ret = PTR_ERR(hdmi->mmio);
+ dev_info(&pdev->dev, "can't map hdmi resource\n");
+ hdmi->mmio = NULL;
goto fail;
}
@@ -130,13 +140,39 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
config->mmio_name);
hdmi->mmio_phy_addr = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ config->qfprom_mmio_name);
+
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find qfprom resource\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->qfprom_mmio_len = (u32)resource_size(res);
+
hdmi->qfprom_mmio = msm_ioremap(pdev,
config->qfprom_mmio_name, "HDMI_QFPROM");
+
if (IS_ERR(hdmi->qfprom_mmio)) {
- dev_info(&pdev->dev, "can't find qfprom resource\n");
+ dev_info(&pdev->dev, "can't map qfprom resource\n");
hdmi->qfprom_mmio = NULL;
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ config->hdcp_mmio_name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find hdcp resource: %d\n", ret);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ hdmi->hdcp_mmio_len = (u32)resource_size(res);
+ hdmi->hdcp_mmio = msm_ioremap(pdev,
+ config->hdcp_mmio_name, "HDMI_HDCP");
+ if (IS_ERR(hdmi->hdcp_mmio)) {
+ dev_info(&pdev->dev, "can't map hdcp resource\n");
+ hdmi->hdcp_mmio = NULL;
+ }
+
hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) *
config->hpd_reg_cnt, GFP_KERNEL);
if (!hdmi->hpd_regs) {
@@ -468,6 +504,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
hdmi_cfg->mmio_name = "core_physical";
hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
+ hdmi_cfg->hdcp_mmio_name = "hdcp_physical";
hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk");
hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data");
hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd");
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 9ce8ff513210..84b578eaad47 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -27,6 +27,11 @@
#include "msm_drv.h"
#include "hdmi.xml.h"
+#define HDMI_SEC_TO_MS 1000
+#define HDMI_MS_TO_US 1000
+#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_BUSY_WAIT_DELAY_US 100
struct hdmi_phy;
struct hdmi_platform_config;
@@ -54,6 +59,10 @@ struct hdmi {
void __iomem *mmio;
void __iomem *qfprom_mmio;
+ void __iomem *hdcp_mmio;
+ u32 mmio_len;
+ u32 qfprom_mmio_len;
+ u32 hdcp_mmio_len;
phys_addr_t mmio_phy_addr;
struct regulator **hpd_regs;
@@ -72,10 +81,14 @@ struct hdmi {
bool hdmi_mode; /* are we in hdmi mode? */
bool is_hdcp_supported;
int irq;
+ void (*ddc_sw_done_cb)(void *data);
+ void *sw_done_cb_data;
struct workqueue_struct *workq;
struct hdmi_hdcp_ctrl *hdcp_ctrl;
-
+ bool use_hard_timeout;
+ int busy_wait_us;
+ u32 timeout_count;
/*
* spinlock to protect registers shared by different execution
* REG_HDMI_CTRL
@@ -91,7 +104,7 @@ struct hdmi_platform_config {
struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
const char *mmio_name;
const char *qfprom_mmio_name;
-
+ const char *hdcp_mmio_name;
/* regulators that need to be on for hpd: */
const char **hpd_reg_names;
int hpd_reg_cnt;
@@ -116,8 +129,20 @@ struct hdmi_platform_config {
int mux_lpm_gpio;
};
+struct hdmi_i2c_adapter {
+ struct i2c_adapter base;
+ struct hdmi *hdmi;
+ bool sw_done;
+ wait_queue_head_t ddc_event;
+};
+
void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
+#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
+
+int ddc_clear_irq(struct hdmi *hdmi);
+void init_ddc(struct hdmi *hdmi);
+
static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data)
{
msm_writel(data, hdmi->mmio + reg);
@@ -187,6 +212,13 @@ void hdmi_i2c_destroy(struct i2c_adapter *i2c);
struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi);
/*
+ * DDC utility functions
+ */
+int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry);
+int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry);
+/*
* hdcp
*/
struct hdmi_hdcp_ctrl *hdmi_hdcp_ctrl_init(struct hdmi *hdmi);
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
index e56a8675c0a4..66be37bea4f6 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -85,84 +85,6 @@ struct hdmi_hdcp_ctrl {
bool max_dev_exceeded;
};
-static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 5;
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- .buf = &offset,
- }, {
- .addr = addr >> 1,
- .flags = I2C_M_RD,
- .len = data_len,
- .buf = data,
- }
- };
-
- DBG("Start DDC read");
-retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 2);
-
- retry--;
- if (rc == 2)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- DBG("End DDC read %d", rc);
-
- return rc;
-}
-
-#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32
-
-static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
- u8 *data, u16 data_len)
-{
- int rc;
- int retry = 10;
- u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM];
- struct i2c_msg msgs[] = {
- {
- .addr = addr >> 1,
- .flags = 0,
- .len = 1,
- }
- };
-
- DBG("Start DDC write");
- if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) {
- pr_err("%s: write size too big\n", __func__);
- return -ERANGE;
- }
-
- buf[0] = offset;
- memcpy(&buf[1], data, data_len);
- msgs[0].buf = buf;
- msgs[0].len = data_len + 1;
-retry:
- rc = i2c_transfer(hdmi->i2c, msgs, 1);
-
- retry--;
- if (rc == 1)
- rc = 0;
- else if (retry > 0)
- goto retry;
- else
- rc = -EIO;
-
- DBG("End DDC write %d", rc);
-
- return rc;
-}
-
static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg,
u32 *pdata, u32 count)
{
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
index f4ab7f70fed1..c65cc908b882 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
@@ -17,66 +17,16 @@
#include "hdmi.h"
-struct hdmi_i2c_adapter {
- struct i2c_adapter base;
- struct hdmi *hdmi;
- bool sw_done;
- wait_queue_head_t ddc_event;
-};
-#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
-
-static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c)
-{
- struct hdmi *hdmi = hdmi_i2c->hdmi;
-
- hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
- HDMI_DDC_CTRL_SW_STATUS_RESET);
- hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
- HDMI_DDC_CTRL_SOFT_RESET);
-
- hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
- HDMI_DDC_SPEED_THRESHOLD(2) |
- HDMI_DDC_SPEED_PRESCALE(10));
-
- hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
- HDMI_DDC_SETUP_TIMEOUT(0xff));
+#define MAX_TRANSACTIONS 4
- /* enable reference timer for 27us */
- hdmi_write(hdmi, REG_HDMI_DDC_REF,
- HDMI_DDC_REF_REFTIMER_ENABLE |
- HDMI_DDC_REF_REFTIMER(27));
-}
+#define SDE_DDC_TXN_CNT_MASK 0x07ff0000
+#define SDE_DDC_TXN_CNT_SHIFT 16
-static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c)
+static inline uint32_t SDE_HDMI_I2C_TRANSACTION_REG_CNT(uint32_t val)
{
- struct hdmi *hdmi = hdmi_i2c->hdmi;
- struct drm_device *dev = hdmi->dev;
- uint32_t retry = 0xffff;
- uint32_t ddc_int_ctrl;
-
- do {
- --retry;
-
- hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
- HDMI_DDC_INT_CTRL_SW_DONE_ACK |
- HDMI_DDC_INT_CTRL_SW_DONE_MASK);
-
- ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
-
- } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
-
- if (!retry) {
- dev_err(dev->dev, "timeout waiting for DDC\n");
- return -ETIMEDOUT;
- }
-
- hdmi_i2c->sw_done = false;
-
- return 0;
+ return ((val) << SDE_DDC_TXN_CNT_SHIFT) & SDE_DDC_TXN_CNT_MASK;
}
-#define MAX_TRANSACTIONS 4
-
static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c)
{
struct hdmi *hdmi = hdmi_i2c->hdmi;
@@ -115,12 +65,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE));
+
if (num == 0)
return num;
- init_ddc(hdmi_i2c);
+ init_ddc(hdmi);
- ret = ddc_clear_irq(hdmi_i2c);
+ ret = ddc_clear_irq(hdmi);
if (ret)
return ret;
@@ -155,7 +106,7 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
}
}
- i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
+ i2c_trans = SDE_HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
HDMI_I2C_TRANSACTION_REG_RW(
(p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) |
HDMI_I2C_TRANSACTION_REG_START;
@@ -177,9 +128,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
ret = -ETIMEDOUT;
dev_warn(dev->dev, "DDC timeout: %d\n", ret);
DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x",
- hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
- hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
- hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
+ hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
+ hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
+ hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
+ if (hdmi->use_hard_timeout) {
+ hdmi->use_hard_timeout = false;
+ hdmi->timeout_count = 0;
+ }
return ret;
}
@@ -213,6 +168,10 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c,
}
}
+ if (hdmi->use_hard_timeout) {
+ hdmi->use_hard_timeout = false;
+ hdmi->timeout_count = jiffies_to_msecs(ret);
+ }
return i;
}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_util.c b/drivers/gpu/drm/msm/hdmi/hdmi_util.c
new file mode 100644
index 000000000000..c7cfa38ed3ad
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_util.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_irq.h>
+#include "hdmi.h"
+
+void init_ddc(struct hdmi *hdmi)
+{
+ hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
+ HDMI_DDC_CTRL_SW_STATUS_RESET);
+ hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
+ HDMI_DDC_CTRL_SOFT_RESET);
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
+ HDMI_DDC_SPEED_THRESHOLD(2) |
+ HDMI_DDC_SPEED_PRESCALE(10));
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
+ HDMI_DDC_SETUP_TIMEOUT(0xff));
+
+ /* enable reference timer for 19us */
+ hdmi_write(hdmi, REG_HDMI_DDC_REF,
+ HDMI_DDC_REF_REFTIMER_ENABLE |
+ HDMI_DDC_REF_REFTIMER(19));
+}
+
+int ddc_clear_irq(struct hdmi *hdmi)
+{
+ struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(hdmi->i2c);
+ struct drm_device *dev = hdmi->dev;
+ uint32_t retry = 0xffff;
+ uint32_t ddc_int_ctrl;
+
+ do {
+ --retry;
+
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
+ HDMI_DDC_INT_CTRL_SW_DONE_ACK |
+ HDMI_DDC_INT_CTRL_SW_DONE_MASK);
+
+ ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
+
+ } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
+
+ if (!retry) {
+ dev_err(dev->dev, "timeout waiting for DDC\n");
+ return -ETIMEDOUT;
+ }
+
+ hdmi_i2c->sw_done = false;
+
+ return 0;
+}
+
+int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
+u8 *data, u16 data_len, bool self_retry)
+{
+ int rc;
+ int retry = 10;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ .buf = &offset,
+ }, {
+ .addr = addr >> 1,
+ .flags = I2C_M_RD,
+ .len = data_len,
+ .buf = data,
+ }
+ };
+
+ DBG("Start DDC read");
+retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 2);
+ retry--;
+
+ if (rc == 2)
+ rc = 0;
+ else if (self_retry && (retry > 0))
+ goto retry;
+ else
+ rc = -EIO;
+
+ DBG("End DDC read %d", rc);
+
+ return rc;
+}
+
+#define HDCP_DDC_WRITE_MAX_BYTE_NUM 1024
+
+int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
+ u8 *data, u16 data_len, bool self_retry)
+{
+ int rc;
+ int retry = 10;
+ u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr >> 1,
+ .flags = 0,
+ .len = 1,
+ }
+ };
+
+ pr_debug("TESTING ! REMOVE RETRY Start DDC write");
+ if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) {
+ pr_err("%s: write size too big\n", __func__);
+ return -ERANGE;
+ }
+
+ buf[0] = offset;
+ memcpy(&buf[1], data, data_len);
+ msgs[0].buf = buf;
+ msgs[0].len = data_len + 1;
+retry:
+ rc = i2c_transfer(hdmi->i2c, msgs, 1);
+ retry--;
+ if (rc == 1)
+ rc = 0;
+ else if (self_retry && (retry > 0))
+ goto retry;
+ else
+ rc = -EIO;
+
+ DBG("End DDC write %d", rc);
+
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 849f5ffc2f1b..a12da5b745f4 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -604,6 +604,8 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
INIT_LIST_HEAD(&ctx->counters);
+ msm_submitqueue_init(ctx);
+
file->driver_priv = ctx;
kms = priv->kms;
@@ -632,12 +634,18 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file)
if (kms && kms->funcs && kms->funcs->postclose)
kms->funcs->postclose(kms, file);
- if (priv->gpu)
+ if (!ctx)
+ return;
+
+ msm_submitqueue_close(ctx);
+
+ if (priv->gpu) {
msm_gpu_cleanup_counters(priv->gpu, ctx);
- if (ctx && ctx->aspace && ctx->aspace != priv->gpu->aspace) {
- ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu);
- msm_gem_address_space_put(ctx->aspace);
+ if (ctx->aspace && ctx->aspace != priv->gpu->aspace) {
+ ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu);
+ msm_gem_address_space_put(ctx->aspace);
+ }
}
kfree(ctx);
@@ -1683,6 +1691,34 @@ static int msm_ioctl_counter_read(struct drm_device *dev, void *data,
return -ENODEV;
}
+
+static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_submitqueue *args = data;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_gpu *gpu = priv->gpu;
+
+ if (args->flags & ~MSM_SUBMITQUEUE_FLAGS)
+ return -EINVAL;
+
+ if (!file->is_master && args->prio >= gpu->nr_rings - 1) {
+ DRM_ERROR("Only DRM master can set highest priority ringbuffer\n");
+ return -EPERM;
+ }
+
+ return msm_submitqueue_create(file->driver_priv, args->prio,
+ args->flags, &args->id);
+}
+
+static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_msm_submitqueue *args = data;
+
+ return msm_submitqueue_remove(file->driver_priv, args->id);
+}
+
int msm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
@@ -1728,6 +1764,10 @@ static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_GEM_SVM_NEW, msm_ioctl_gem_svm_new,
DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new,
+ DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close,
+ DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 8f56a3126008..660c608b64f0 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -78,6 +78,9 @@ struct msm_gem_vma;
struct msm_file_private {
struct msm_gem_address_space *aspace;
struct list_head counters;
+ rwlock_t queuelock;
+ struct list_head submitqueues;
+ int queueid;
};
enum msm_mdp_plane_property {
@@ -437,6 +440,7 @@ struct msm_gem_address_space *
msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu,
const char *name);
+void msm_gem_submit_free(struct msm_gem_submit *submit);
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_file *file);
@@ -506,6 +510,16 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
+struct msm_gpu_submitqueue;
+int msm_submitqueue_init(struct msm_file_private *ctx);
+struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
+ u32 id);
+int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio,
+ u32 flags, u32 *id);
+int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id);
+void msm_submitqueue_close(struct msm_file_private *ctx);
+void msm_submitqueue_destroy(struct kref *kref);
+
struct hdmi;
int hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,
struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 6bb29c62378d..b7dd84cb23b9 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -653,7 +653,15 @@ void *msm_gem_vaddr(struct drm_gem_object *obj)
struct msm_gem_object *msm_obj = to_msm_bo(obj);
mutex_lock(&msm_obj->lock);
- if (!msm_obj->vaddr) {
+
+ if (msm_obj->vaddr) {
+ mutex_unlock(&msm_obj->lock);
+ return msm_obj->vaddr;
+ }
+
+ if (obj->import_attach) {
+ msm_obj->vaddr = dma_buf_vmap(obj->import_attach->dmabuf);
+ } else {
struct page **pages = get_pages(obj);
if (IS_ERR(pages)) {
mutex_unlock(&msm_obj->lock);
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 04e6c658b5f3..e8528892939f 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -132,6 +132,9 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
return fence;
}
+/* Internal submit flags */
+#define SUBMIT_FLAG_SKIP_HANGCHECK 0x00000001
+
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
* associated with the cmdstream submission for synchronization (and
* make it easier to unwind when things go wrong, etc). This only
@@ -140,15 +143,17 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
struct msm_gem_submit {
struct drm_device *dev;
struct msm_gem_address_space *aspace;
- struct list_head node; /* node in gpu submit_list */
+ struct list_head node; /* node in ring submit list */
struct list_head bo_list;
struct ww_acquire_ctx ticket;
uint32_t fence;
int ring;
+ u32 flags;
bool valid;
uint64_t profile_buf_iova;
void *profile_buf_vaddr;
bool secure;
+ struct msm_gpu_submitqueue *queue;
unsigned int nr_cmds;
unsigned int nr_bos;
struct {
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index c861bfd77537..b9ace0d9e69b 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -35,7 +35,8 @@ static inline void __user *to_user_ptr(u64 address)
static struct msm_gem_submit *submit_create(struct drm_device *dev,
struct msm_gem_address_space *aspace,
- uint32_t nr_bos, uint32_t nr_cmds)
+ uint32_t nr_bos, uint32_t nr_cmds,
+ struct msm_gpu_submitqueue *queue)
{
struct msm_gem_submit *submit;
uint64_t sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) +
@@ -48,6 +49,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
if (submit) {
submit->dev = dev;
submit->aspace = aspace;
+ submit->queue = queue;
/* initially, until copy_from_user() and bo lookup succeeds: */
submit->nr_bos = 0;
@@ -59,6 +61,11 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
submit->secure = false;
+ /*
+ * Initalize node so we can safely list_del() on it if
+ * we fail in the submit path
+ */
+ INIT_LIST_HEAD(&submit->node);
INIT_LIST_HEAD(&submit->bo_list);
ww_acquire_init(&submit->ticket, &reservation_ww_class);
}
@@ -74,6 +81,16 @@ copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
return -EFAULT;
}
+void msm_gem_submit_free(struct msm_gem_submit *submit)
+{
+ if (!submit)
+ return;
+
+ msm_submitqueue_put(submit->queue);
+ list_del(&submit->node);
+ kfree(submit);
+}
+
static int submit_lookup_objects(struct msm_gpu *gpu,
struct msm_gem_submit *submit,
struct drm_msm_gem_submit *args, struct drm_file *file)
@@ -381,6 +398,9 @@ static void submit_cleanup(struct msm_gpu *gpu, struct msm_gem_submit *submit,
{
unsigned i;
+ if (!submit)
+ return;
+
for (i = 0; i < submit->nr_bos; i++) {
struct msm_gem_object *msm_obj = submit->bos[i].obj;
submit_unlock_unpin_bo(gpu, submit, i);
@@ -398,6 +418,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_msm_gem_submit *args = data;
struct msm_file_private *ctx = file->driver_priv;
struct msm_gem_submit *submit;
+ struct msm_gpu_submitqueue *queue;
struct msm_gpu *gpu;
unsigned i;
int ret;
@@ -412,9 +433,14 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
if (!gpu)
return -ENXIO;
+ queue = msm_submitqueue_get(ctx, args->queueid);
+ if (!queue)
+ return -ENOENT;
+
mutex_lock(&dev->struct_mutex);
- submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds);
+ submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds,
+ queue);
if (!submit) {
ret = -ENOMEM;
goto out;
@@ -434,6 +460,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
struct msm_gem_object *msm_obj;
uint64_t iova;
+ size_t size;
ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
if (ret) {
@@ -466,10 +493,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
goto out;
}
- if (!(submit_cmd.size) ||
- ((submit_cmd.size + submit_cmd.submit_offset) >
- msm_obj->base.size)) {
- DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
+ size = submit_cmd.size + submit_cmd.submit_offset;
+
+ if (!submit_cmd.size || (size < submit_cmd.size) ||
+ (size > msm_obj->base.size)) {
+ DRM_ERROR("invalid cmdstream offset/size: %u/%u\n",
+ submit_cmd.submit_offset, submit_cmd.size);
ret = -EINVAL;
goto out;
}
@@ -482,7 +511,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) {
submit->profile_buf_iova = submit->cmd[i].iova;
submit->profile_buf_vaddr =
- msm_gem_vaddr(&msm_obj->base);
+ msm_gem_vaddr(&msm_obj->base) +
+ submit_cmd.submit_offset;
}
if (submit->valid)
@@ -497,17 +527,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
submit->nr_cmds = i;
/* Clamp the user submitted ring to the range of available rings */
- submit->ring = clamp_t(uint32_t,
- (args->flags & MSM_SUBMIT_RING_MASK) >> MSM_SUBMIT_RING_SHIFT,
- 0, gpu->nr_rings - 1);
+ submit->ring = clamp_t(uint32_t, queue->prio, 0, gpu->nr_rings - 1);
ret = msm_gpu_submit(gpu, submit);
args->fence = submit->fence;
out:
- if (submit)
- submit_cleanup(gpu, submit, !!ret);
+ submit_cleanup(gpu, submit, !!ret);
+ if (ret)
+ msm_gem_submit_free(submit);
mutex_unlock(&dev->struct_mutex);
return ret;
}
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 81bab9cc22af..45d7a658f022 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -278,7 +278,8 @@ static void inactive_start(struct msm_gpu *gpu)
* Hangcheck detection for locked gpu:
*/
-static void retire_submits(struct msm_gpu *gpu, uint32_t fence);
+static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence);
static void recover_worker(struct work_struct *work)
{
@@ -296,29 +297,22 @@ static void recover_worker(struct work_struct *work)
inactive_cancel(gpu);
FOR_EACH_RING(gpu, ring, i) {
- uint32_t fence;
+ uint32_t fence = gpu->funcs->last_fence(gpu, ring);
- if (!ring)
- continue;
-
- fence = gpu->funcs->last_fence(gpu, ring);
-
- /*
- * Retire the faulting command on the active ring and
- * make sure the other rings are cleaned up
- */
- if (ring == gpu->funcs->active_ring(gpu))
- retire_submits(gpu, fence + 1);
- else
- retire_submits(gpu, fence);
+ retire_submits(gpu, ring,
+ gpu->funcs->active_ring(gpu) == ring ?
+ fence + 1 : fence);
}
/* Recover the GPU */
gpu->funcs->recover(gpu);
- /* replay the remaining submits for all rings: */
- list_for_each_entry(submit, &gpu->submit_list, node) {
- gpu->funcs->submit(gpu, submit);
+ /* Replay the remaining on all rings, highest priority first */
+ for (i = gpu->nr_rings - 1; i >= 0; i--) {
+ struct msm_ringbuffer *ring = gpu->rb[i];
+
+ list_for_each_entry(submit, &ring->submits, node)
+ gpu->funcs->submit(gpu, submit);
}
}
mutex_unlock(&dev->struct_mutex);
@@ -465,23 +459,19 @@ out:
* Cmdstream submission/retirement:
*/
-static void retire_submits(struct msm_gpu *gpu, uint32_t fence)
+static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence)
{
struct drm_device *dev = gpu->dev;
struct msm_gem_submit *submit, *tmp;
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
- /*
- * Find and retire all the submits in the same ring that are older than
- * or equal to 'fence'
- */
+ list_for_each_entry_safe(submit, tmp, &ring->submits, node) {
+ if (submit->fence > fence)
+ break;
- list_for_each_entry_safe(submit, tmp, &gpu->submit_list, node) {
- if (COMPARE_FENCE_LTE(submit->fence, fence)) {
- list_del(&submit->node);
- kfree(submit);
- }
+ msm_gem_submit_free(submit);
}
}
@@ -493,11 +483,12 @@ static bool _fence_signaled(struct msm_gem_object *obj, uint32_t fence)
return COMPARE_FENCE_LTE(obj->read_fence, fence);
}
-static void _retire_ring(struct msm_gpu *gpu, uint32_t fence)
+static void _retire_ring(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+ uint32_t fence)
{
struct msm_gem_object *obj, *tmp;
- retire_submits(gpu, fence);
+ retire_submits(gpu, ring, fence);
list_for_each_entry_safe(obj, tmp, &gpu->active_list, mm_list) {
if (_fence_signaled(obj, fence)) {
@@ -525,7 +516,7 @@ static void retire_worker(struct work_struct *work)
msm_update_fence(gpu->dev, fence);
mutex_lock(&dev->struct_mutex);
- _retire_ring(gpu, fence);
+ _retire_ring(gpu, ring, fence);
mutex_unlock(&dev->struct_mutex);
}
@@ -555,7 +546,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
inactive_cancel(gpu);
- list_add_tail(&submit->node, &gpu->submit_list);
+ list_add_tail(&submit->node, &ring->submits);
msm_rd_dump_submit(submit);
@@ -818,7 +809,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
INIT_WORK(&gpu->inactive_work, inactive_worker);
INIT_WORK(&gpu->recover_work, recover_worker);
- INIT_LIST_HEAD(&gpu->submit_list);
setup_timer(&gpu->inactive_timer, inactive_handler,
(unsigned long)gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index a47eae68dd9b..cddbcbbb8557 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -147,12 +147,18 @@ struct msm_gpu {
struct timer_list hangcheck_timer;
uint32_t hangcheck_fence[MSM_GPU_MAX_RINGS];
struct work_struct recover_work;
-
- struct list_head submit_list;
-
struct msm_snapshot *snapshot;
};
+struct msm_gpu_submitqueue {
+ int id;
+ u32 flags;
+ u32 prio;
+ int faults;
+ struct list_head node;
+ struct kref ref;
+};
+
/* It turns out that all targets use the same ringbuffer size. */
#define MSM_GPU_RINGBUFFER_SZ SZ_32K
#define MSM_GPU_RINGBUFFER_BLKSIZE 32
@@ -280,4 +286,10 @@ void msm_gpu_cleanup_counters(struct msm_gpu *gpu,
u64 msm_gpu_counter_read(struct msm_gpu *gpu,
struct drm_msm_counter_read *data);
+static inline void msm_submitqueue_put(struct msm_gpu_submitqueue *queue)
+{
+ if (queue)
+ kref_put(&queue->ref, msm_submitqueue_destroy);
+}
+
#endif /* __MSM_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c
index 2d112f24a902..edf3ff2a7a61 100644
--- a/drivers/gpu/drm/msm/msm_rd.c
+++ b/drivers/gpu/drm/msm/msm_rd.c
@@ -27,6 +27,11 @@
* This bypasses drm_debugfs_create_files() mainly because we need to use
* our own fops for a bit more control. In particular, we don't want to
* do anything if userspace doesn't have the debugfs file open.
+ *
+ * The module-param "rd_full", which defaults to false, enables snapshotting
+ * all (non-written) buffers in the submit, rather than just cmdstream bo's.
+ * This is useful to capture the contents of (for example) vbo's or textures,
+ * or shader programs (if not emitted inline in cmdstream).
*/
#ifdef CONFIG_DEBUG_FS
@@ -40,6 +45,10 @@
#include "msm_gpu.h"
#include "msm_gem.h"
+static bool rd_full = false;
+MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents");
+module_param_named(rd_full, rd_full, bool, 0600);
+
enum rd_sect_type {
RD_NONE,
RD_TEST, /* ascii text */
@@ -277,6 +286,36 @@ void msm_rd_debugfs_cleanup(struct drm_minor *minor)
kfree(rd);
}
+static void snapshot_buf(struct msm_rd_state *rd,
+ struct msm_gem_submit *submit, int idx,
+ uint64_t iova, uint32_t size)
+{
+ struct msm_gem_object *obj = submit->bos[idx].obj;
+ uint64_t offset = 0;
+
+ if (iova) {
+ offset = iova - submit->bos[idx].iova;
+ } else {
+ iova = submit->bos[idx].iova;
+ size = obj->base.size;
+ }
+
+ /* Always write the RD_GPUADDR so we know how big the buffer is */
+ rd_write_section(rd, RD_GPUADDR,
+ (uint64_t[2]) { iova, size }, 16);
+
+ /* But only dump contents for buffers marked as read and not secure */
+ if (submit->bos[idx].flags & MSM_SUBMIT_BO_READ &&
+ !(obj->flags & MSM_BO_SECURE)) {
+ const char *buf = msm_gem_vaddr(&obj->base);
+
+ if (IS_ERR_OR_NULL(buf))
+ return;
+
+ rd_write_section(rd, RD_BUFFER_CONTENTS, buf + offset, size);
+ }
+}
+
/* called under struct_mutex */
void msm_rd_dump_submit(struct msm_gem_submit *submit)
{
@@ -300,24 +339,20 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit)
rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
- /* could be nice to have an option (module-param?) to snapshot
- * all the bo's associated with the submit. Handy to see vtx
- * buffers, etc. For now just the cmdstream bo's is enough.
- */
+ if (rd_full) {
+ for (i = 0; i < submit->nr_bos; i++)
+ snapshot_buf(rd, submit, i, 0, 0);
+ }
for (i = 0; i < submit->nr_cmds; i++) {
- uint32_t idx = submit->cmd[i].idx;
uint64_t iova = submit->cmd[i].iova;
uint32_t szd = submit->cmd[i].size; /* in dwords */
- struct msm_gem_object *obj = submit->bos[idx].obj;
- const char *buf = msm_gem_vaddr(&obj->base);
- buf += iova - submit->bos[idx].iova;
-
- rd_write_section(rd, RD_GPUADDR,
- (uint64_t[2]) { iova, szd * 4 }, 16);
- rd_write_section(rd, RD_BUFFER_CONTENTS,
- buf, szd * 4);
+ /* snapshot cmdstream bo's (if we haven't already): */
+ if (!rd_full) {
+ snapshot_buf(rd, submit, submit->cmd[i].idx,
+ submit->cmd[i].iova, szd * 4);
+ }
switch (submit->cmd[i].type) {
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 382c71bb0ebe..458f80426b39 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -47,6 +47,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id)
ring->next = ring->start;
ring->cur = ring->start;
+ INIT_LIST_HEAD(&ring->submits);
spin_lock_init(&ring->lock);
return ring;
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
index 1e84905073bf..70643a7b4cc4 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -28,6 +28,7 @@ struct msm_ringbuffer {
uint64_t iova;
uint32_t submitted_fence;
spinlock_t lock;
+ struct list_head submits;
};
struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id);
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index 7d0dda032c59..7673f54b7691 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -120,16 +120,30 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
+ struct iommu_domain *domain;
int ret;
- if (priv)
- ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL, priv);
- else
- ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL);
+ if (!client || !sgt)
+ return -EINVAL;
- return (ret != sgt->nents) ? -ENOMEM : 0;
+ if (iova != 0) {
+ if (!client->mmu_mapping || !client->mmu_mapping->domain)
+ return -EINVAL;
+
+ domain = client->mmu_mapping->domain;
+
+ return iommu_map_sg(domain, iova, sgt->sgl,
+ sgt->nents, flags);
+ } else {
+ if (priv)
+ ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl,
+ sgt->nents, DMA_BIDIRECTIONAL, priv);
+ else
+ ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
+
+ return (ret != sgt->nents) ? -ENOMEM : 0;
+ }
}
static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
new file mode 100644
index 000000000000..8e29021bb0d8
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -0,0 +1,125 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kref.h>
+#include "msm_gpu.h"
+
+void msm_submitqueue_destroy(struct kref *kref)
+{
+ struct msm_gpu_submitqueue *queue = container_of(kref,
+ struct msm_gpu_submitqueue, ref);
+
+ kfree(queue);
+}
+
+struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
+ u32 id)
+{
+ struct msm_gpu_submitqueue *entry;
+
+ read_lock(&ctx->queuelock);
+
+ list_for_each_entry(entry, &ctx->submitqueues, node) {
+ if (entry->id == id) {
+ kref_get(&entry->ref);
+ read_unlock(&ctx->queuelock);
+
+ return entry;
+ }
+ }
+
+ read_unlock(&ctx->queuelock);
+ return NULL;
+}
+
+void msm_submitqueue_close(struct msm_file_private *ctx)
+{
+ struct msm_gpu_submitqueue *entry, *tmp;
+
+ /*
+ * No lock needed in close and there won't
+ * be any more user ioctls coming our way
+ */
+
+ list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node)
+ msm_submitqueue_put(entry);
+}
+
+int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags,
+ u32 *id)
+{
+ struct msm_gpu_submitqueue *queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+
+ if (!queue)
+ return -ENOMEM;
+
+ kref_init(&queue->ref);
+ queue->flags = flags;
+ queue->prio = prio;
+
+ write_lock(&ctx->queuelock);
+
+ queue->id = ctx->queueid++;
+
+ if (id)
+ *id = queue->id;
+
+ list_add_tail(&queue->node, &ctx->submitqueues);
+
+ write_unlock(&ctx->queuelock);
+
+ return 0;
+}
+
+int msm_submitqueue_init(struct msm_file_private *ctx)
+{
+ INIT_LIST_HEAD(&ctx->submitqueues);
+
+ rwlock_init(&ctx->queuelock);
+
+ /*
+ * Add the "default" submitqueue with id 0
+ * "medium" priority (3) and no flags
+ */
+
+ return msm_submitqueue_create(ctx, 3, 0, NULL);
+}
+
+int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
+{
+ struct msm_gpu_submitqueue *entry;
+
+ /*
+ * id 0 is the "default" queue and can't be destroyed
+ * by the user
+ */
+
+ if (!id)
+ return -ENOENT;
+
+ write_lock(&ctx->queuelock);
+
+ list_for_each_entry(entry, &ctx->submitqueues, node) {
+ if (entry->id == id) {
+ list_del(&entry->node);
+ write_unlock(&ctx->queuelock);
+
+ msm_submitqueue_put(entry);
+ return 0;
+ }
+ }
+
+ write_unlock(&ctx->queuelock);
+ return -ENOENT;
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3ccad0c0c08d..85ebff08761b 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -147,6 +147,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state)
return state && state->fb && state->crtc;
}
+static struct sde_kms *_sde_plane_get_kms(struct drm_plane *plane)
+{
+ struct msm_drm_private *priv;
+
+ if (!plane || !plane->dev)
+ return NULL;
+
+ priv = plane->dev->dev_private;
+ if (!priv)
+ return NULL;
+
+ return to_sde_kms(priv->kms);
+}
+
/**
* _sde_plane_calc_fill_level - calculate fill level of the given source format
* @plane: Pointer to drm plane
@@ -582,12 +596,62 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
return ret;
}
+/**
+ * _sde_plane_get_aspace: gets the address space based on the
+ * fb_translation mode property
+ */
+static int _sde_plane_get_aspace(
+ struct sde_plane *psde,
+ struct sde_plane_state *pstate,
+ struct msm_gem_address_space **aspace)
+{
+ struct sde_kms *kms;
+ int mode;
+
+ if (!psde || !pstate || !aspace) {
+ SDE_ERROR("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ kms = _sde_plane_get_kms(&psde->base);
+ if (!kms) {
+ SDE_ERROR("invalid kms\n");
+ return -EINVAL;
+ }
+
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+
+ switch (mode) {
+ case SDE_DRM_FB_NON_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_SECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC_DIR_TRANS:
+ case SDE_DRM_FB_NON_SEC_DIR_TRANS:
+ *aspace = NULL;
+ break;
+ default:
+ SDE_ERROR("invalid fb_translation mode:%d\n", mode);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp,
struct sde_plane_state *pstate,
struct sde_hw_pipe_cfg *pipe_cfg,
struct drm_framebuffer *fb)
{
struct sde_plane *psde;
+ struct msm_gem_address_space *aspace = NULL;
int ret;
if (!pp || !pstate || !pipe_cfg || !fb) {
@@ -603,7 +667,13 @@ static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp,
return;
}
- ret = sde_format_populate_layout(psde->aspace, fb, &pipe_cfg->layout);
+ ret = _sde_plane_get_aspace(psde, pstate, &aspace);
+ if (ret) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", ret);
+ return;
+ }
+
+ ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout);
if (ret == -EAGAIN)
SDE_DEBUG_PLANE(psde, "not updating same src addrs\n");
else if (ret)
@@ -1132,9 +1202,9 @@ static int _sde_plane_color_fill(struct sde_phy_plane *pp,
}
static int _sde_plane_mode_set(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *state)
{
- uint32_t nplanes, src_flags;
+ uint32_t nplanes, src_flags = 0x0;
struct sde_plane *psde;
struct sde_plane_state *pstate;
const struct sde_format *fmt;
@@ -1145,6 +1215,7 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
int idx;
struct sde_phy_plane *pp;
uint32_t num_of_phy_planes = 0, maxlinewidth = 0xFFFF;
+ int mode = 0;
if (!plane) {
SDE_ERROR("invalid plane\n");
@@ -1220,6 +1291,15 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
return 0;
memset(&src, 0, sizeof(struct sde_rect));
+
+ /* update secure session flag */
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+ if ((mode == SDE_DRM_FB_SEC) ||
+ (mode == SDE_DRM_FB_SEC_DIR_TRANS))
+ src_flags |= SDE_SSPP_SECURE_OVERLAY_SESSION;
+
+
/* update roi config */
if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) {
POPULATE_RECT(&src, state->src_x, state->src_y,
@@ -1286,10 +1366,11 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
pp->scaler3_cfg);
}
- if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
+ if (((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) ||
+ (src_flags &
+ SDE_SSPP_SECURE_OVERLAY_SESSION)) &&
pp->pipe_hw->ops.setup_format) {
- src_flags = 0x0;
- SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n",
+ SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n",
sde_plane_get_property(pstate, PLANE_PROP_ROTATION));
if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION)
& BIT(DRM_REFLECT_X))
@@ -1344,10 +1425,23 @@ static int sde_plane_prepare_fb(struct drm_plane *plane,
{
struct drm_framebuffer *fb = new_state->fb;
struct sde_plane *psde = to_sde_plane(plane);
+ struct sde_plane_state *pstate;
+ int rc;
+
+ if (!psde || !new_state)
+ return -EINVAL;
if (!new_state->fb)
return 0;
+ pstate = to_sde_plane_state(new_state);
+ rc = _sde_plane_get_aspace(psde, pstate, &psde->aspace);
+
+ if (rc) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", rc);
+ return rc;
+ }
+
SDE_DEBUG_PLANE(psde, "FB[%u]\n", fb->base.id);
return msm_framebuffer_prepare(fb, psde->aspace);
}
@@ -2631,7 +2725,6 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
/* cache local stuff for later */
plane = &psde->base;
- psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
INIT_LIST_HEAD(&psde->phy_plane_head);
diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
new file mode 100644
index 000000000000..49cca9399cb0
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDE_HDCP_H__
+#define __SDE_HDCP_H__
+
+#include <soc/qcom/scm.h>
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include "hdmi.h"
+#include "sde_kms.h"
+#include "sde_hdmi_util.h"
+
+#ifdef SDE_HDCP_DEBUG_ENABLE
+#define SDE_HDCP_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_HDCP_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+enum sde_hdcp_client_id {
+ HDCP_CLIENT_HDMI,
+ HDCP_CLIENT_DP,
+};
+
+enum sde_hdcp_states {
+ HDCP_STATE_INACTIVE,
+ HDCP_STATE_AUTHENTICATING,
+ HDCP_STATE_AUTHENTICATED,
+ HDCP_STATE_AUTH_FAIL,
+ HDCP_STATE_AUTH_ENC_NONE,
+ HDCP_STATE_AUTH_ENC_1X,
+ HDCP_STATE_AUTH_ENC_2P2
+};
+
+struct sde_hdcp_init_data {
+ struct dss_io_data *core_io;
+ struct dss_io_data *qfprom_io;
+ struct dss_io_data *hdcp_io;
+ struct mutex *mutex;
+ struct workqueue_struct *workq;
+ void *cb_data;
+ void (*notify_status)(void *cb_data, enum sde_hdcp_states status);
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u8 sink_rx_status;
+ u16 *version;
+ u32 phy_addr;
+ u32 hdmi_tx_ver;
+ bool sec_access;
+ enum sde_hdcp_client_id client_id;
+};
+
+struct sde_hdcp_ops {
+ int (*isr)(void *ptr);
+ int (*cp_irq)(void *ptr);
+ int (*reauthenticate)(void *input);
+ int (*authenticate)(void *hdcp_ctrl);
+ bool (*feature_supported)(void *input);
+ void (*off)(void *hdcp_ctrl);
+};
+
+void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
+void sde_hdcp_1x_deinit(void *input);
+struct sde_hdcp_ops *sde_hdcp_1x_start(void *input);
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data);
+void sde_hdmi_hdcp2p2_deinit(void *input);
+const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input);
+#endif /* __SDE_HDCP_H__ */
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
new file mode 100644
index 000000000000..3aba9e307732
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -0,0 +1,1722 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/iopoll.h>
+#include <linux/hdcp_qseecom.h>
+#include "sde_hdcp.h"
+#include "sde_hdmi_util.h"
+#include "video/msm_hdmi_hdcp_mgr.h"
+
+#define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state))
+
+/* HDCP Keys state based on HDMI_HDCP_LINK0_STATUS:KEYS_STATE */
+#define HDCP_KEYS_STATE_NO_KEYS 0
+#define HDCP_KEYS_STATE_NOT_CHECKED 1
+#define HDCP_KEYS_STATE_CHECKING 2
+#define HDCP_KEYS_STATE_VALID 3
+#define HDCP_KEYS_STATE_AKSV_NOT_VALID 4
+#define HDCP_KEYS_STATE_CHKSUM_MISMATCH 5
+#define HDCP_KEYS_STATE_PROD_AKSV 6
+#define HDCP_KEYS_STATE_RESERVED 7
+
+#define TZ_HDCP_CMD_ID 0x00004401
+
+#define HDCP_INT_CLR (isr->auth_success_ack | isr->auth_fail_ack | \
+ isr->auth_fail_info_ack | isr->tx_req_ack | \
+ isr->encryption_ready_ack | \
+ isr->encryption_not_ready_ack | isr->tx_req_done_ack)
+
+#define HDCP_INT_EN (isr->auth_success_mask | isr->auth_fail_mask | \
+ isr->encryption_ready_mask | \
+ isr->encryption_not_ready_mask)
+
+#define HDCP_POLL_SLEEP_US (20 * 1000)
+#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 100)
+
+#define sde_hdcp_1x_state(x) (hdcp->hdcp_state == x)
+
+struct sde_hdcp_sink_addr {
+ char *name;
+ u32 addr;
+ u32 len;
+};
+
+struct sde_hdcp_1x_reg_data {
+ u32 reg_id;
+ struct sde_hdcp_sink_addr *sink;
+};
+
+struct sde_hdcp_skaddr_map {
+ /* addresses to read from sink */
+ struct sde_hdcp_sink_addr bcaps;
+ struct sde_hdcp_sink_addr bksv;
+ struct sde_hdcp_sink_addr r0;
+ struct sde_hdcp_sink_addr bstatus;
+ struct sde_hdcp_sink_addr cp_irq_status;
+ struct sde_hdcp_sink_addr ksv_fifo;
+ struct sde_hdcp_sink_addr v_h0;
+ struct sde_hdcp_sink_addr v_h1;
+ struct sde_hdcp_sink_addr v_h2;
+ struct sde_hdcp_sink_addr v_h3;
+ struct sde_hdcp_sink_addr v_h4;
+
+ /* addresses to write to sink */
+ struct sde_hdcp_sink_addr an;
+ struct sde_hdcp_sink_addr aksv;
+ struct sde_hdcp_sink_addr ainfo;
+};
+
+struct sde_hdcp_int_set {
+ /* interrupt register */
+ u32 int_reg;
+
+ /* interrupt enable/disable masks */
+ u32 auth_success_mask;
+ u32 auth_fail_mask;
+ u32 encryption_ready_mask;
+ u32 encryption_not_ready_mask;
+ u32 tx_req_mask;
+ u32 tx_req_done_mask;
+
+ /* interrupt acknowledgment */
+ u32 auth_success_ack;
+ u32 auth_fail_ack;
+ u32 auth_fail_info_ack;
+ u32 encryption_ready_ack;
+ u32 encryption_not_ready_ack;
+ u32 tx_req_ack;
+ u32 tx_req_done_ack;
+
+ /* interrupt status */
+ u32 auth_success_int;
+ u32 auth_fail_int;
+ u32 encryption_ready;
+ u32 encryption_not_ready;
+ u32 tx_req_int;
+ u32 tx_req_done_int;
+};
+
+struct sde_hdcp_reg_set {
+ u32 status;
+ u32 keys_offset;
+ u32 r0_offset;
+ u32 v_offset;
+ u32 ctrl;
+ u32 aksv_lsb;
+ u32 aksv_msb;
+ u32 entropy_ctrl0;
+ u32 entropy_ctrl1;
+ u32 sec_sha_ctrl;
+ u32 sec_sha_data;
+ u32 sha_status;
+
+ u32 data2_0;
+ u32 data3;
+ u32 data4;
+ u32 data5;
+ u32 data6;
+
+ u32 sec_data0;
+ u32 sec_data1;
+ u32 sec_data7;
+ u32 sec_data8;
+ u32 sec_data9;
+ u32 sec_data10;
+ u32 sec_data11;
+ u32 sec_data12;
+
+ u32 reset;
+ u32 reset_bit;
+
+ u32 repeater;
+};
+
+#define HDCP_REG_SET_CLIENT_HDMI \
+ {HDMI_HDCP_LINK0_STATUS, 28, 24, 20, HDMI_HDCP_CTRL, \
+ HDMI_HDCP_SW_LOWER_AKSV, HDMI_HDCP_SW_UPPER_AKSV, \
+ HDMI_HDCP_ENTROPY_CTRL0, HDMI_HDCP_ENTROPY_CTRL1, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA, \
+ HDMI_HDCP_SHA_STATUS, HDMI_HDCP_RCVPORT_DATA2_0, \
+ HDMI_HDCP_RCVPORT_DATA3, HDMI_HDCP_RCVPORT_DATA4, \
+ HDMI_HDCP_RCVPORT_DATA5, HDMI_HDCP_RCVPORT_DATA6, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \
+ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \
+ HDMI_HDCP_RESET, BIT(0), BIT(6)}
+
+/* To do for DP */
+#define HDCP_REG_SET_CLIENT_DP \
+ {0}
+
+#define HDCP_HDMI_SINK_ADDR_MAP \
+ {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \
+ {"bstatus", 0x41, 2}, {"??", 0x0, 0}, {"ksv-fifo", 0x43, 0}, \
+ {"v_h0", 0x20, 4}, {"v_h1", 0x24, 4}, {"v_h2", 0x28, 4}, \
+ {"v_h3", 0x2c, 4}, {"v_h4", 0x30, 4}, {"an", 0x18, 8}, \
+ {"aksv", 0x10, 5}, {"ainfo", 0x00, 0},}
+
+#define HDCP_DP_SINK_ADDR_MAP \
+ {{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \
+ {"binfo", 0x6802A, 2}, {"cp_irq_status", 0x68029, 1}, \
+ {"ksv-fifo", 0x6802C, 0}, {"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, \
+ {"v_h2", 0x6801C, 4}, {"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, \
+ {"an", 0x6800C, 8}, {"aksv", 0x68007, 5}, {"ainfo", 0x6803B, 1} }
+
+#define HDCP_HDMI_INT_SET \
+ {HDMI_HDCP_INT_CTRL, \
+ BIT(2), BIT(6), 0, 0, 0, 0, \
+ BIT(1), BIT(5), BIT(7), 0, 0, 0, 0, \
+ BIT(0), BIT(4), 0, 0, 0, 0}
+
+#define HDCP_DP_INT_SET \
+ {DP_INTR_STATUS2, \
+ BIT(17), BIT(20), BIT(24), BIT(27), 0, 0, \
+ BIT(16), BIT(19), BIT(21), BIT(23), BIT(26), 0, 0, \
+ BIT(15), BIT(18), BIT(22), BIT(25), 0, 0}
+
+struct sde_hdcp_1x {
+ u8 bcaps;
+ u32 tp_msgid;
+ u32 an_0, an_1, aksv_0, aksv_1;
+ bool sink_r0_ready;
+ bool reauth;
+ bool ksv_ready;
+ enum sde_hdcp_states hdcp_state;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ struct HDCP_V2V1_MSG_TOPOLOGY current_tp;
+ struct delayed_work hdcp_auth_work;
+ struct completion r0_checked;
+ struct completion sink_r0_available;
+ struct sde_hdcp_init_data init_data;
+ struct sde_hdcp_ops *ops;
+ struct sde_hdcp_reg_set reg_set;
+ struct sde_hdcp_int_set int_set;
+ struct sde_hdcp_skaddr_map sink_addr;
+ struct workqueue_struct *workq;
+};
+
+const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state)
+{
+ switch (hdcp_state) {
+ case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
+ case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
+ case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
+ case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
+ default: return "???";
+ }
+}
+
+static int sde_hdcp_1x_count_one(u8 *array, u8 len)
+{
+ int i, j, count = 0;
+
+ for (i = 0; i < len; i++)
+ for (j = 0; j < 8; j++)
+ count += (((array[i] >> j) & 0x1) ? 1 : 0);
+ return count;
+}
+
+static void reset_hdcp_ddc_failures(struct sde_hdcp_1x *hdcp)
+{
+ int hdcp_ddc_ctrl1_reg;
+ int hdcp_ddc_status;
+ int failure;
+ int nack0;
+ struct dss_io_data *io;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+
+ /* Check for any DDC transfer failures */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ failure = (hdcp_ddc_status >> 16) & BIT(0);
+ nack0 = (hdcp_ddc_status >> 14) & BIT(0);
+ SDE_HDCP_DEBUG("%s: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+
+ if (failure) {
+ /*
+ * Indicates that the last HDCP HW DDC transfer failed.
+ * This occurs when a transfer is attempted with HDCP DDC
+ * disabled (HDCP_DDC_DISABLE=1) or the number of retries
+ * matches HDCP_DDC_RETRY_CNT.
+ * Failure occurred, let's clear it.
+ */
+ SDE_HDCP_DEBUG("%s: DDC failure HDCP_DDC_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status);
+
+ /* First, Disable DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, BIT(0));
+
+ /* ACK the Failure to Clear it */
+ hdcp_ddc_ctrl1_reg = DSS_REG_R(io, HDMI_HDCP_DDC_CTRL_1);
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_1,
+ hdcp_ddc_ctrl1_reg | BIT(0));
+
+ /* Check if the FAILURE got Cleared */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ hdcp_ddc_status = (hdcp_ddc_status >> 16) & BIT(0);
+ if (hdcp_ddc_status == 0x0)
+ SDE_HDCP_DEBUG("%s: HDCP DDC Failure cleared\n",
+ SDE_HDCP_STATE_NAME);
+ else
+ SDE_ERROR("%s: Unable to clear HDCP DDC Failure",
+ SDE_HDCP_STATE_NAME);
+
+ /* Re-Enable HDCP DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, 0);
+ }
+
+ if (nack0) {
+ SDE_HDCP_DEBUG("%s: Before: HDMI_DDC_SW_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ /* Reset HDMI DDC software status */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(3));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~(BIT(3)));
+
+ /* Reset HDMI DDC Controller */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(1));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~BIT(1));
+ SDE_HDCP_DEBUG("%s: After: HDMI_DDC_SW_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ }
+
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+
+ failure = (hdcp_ddc_status >> 16) & BIT(0);
+ nack0 = (hdcp_ddc_status >> 14) & BIT(0);
+ SDE_HDCP_DEBUG("%s: On Exit: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+} /* reset_hdcp_ddc_failures */
+
+static void sde_hdcp_1x_hw_ddc_clean(struct sde_hdcp_1x *hdcp)
+{
+ struct dss_io_data *io = NULL;
+ u32 hdcp_ddc_status, ddc_hw_status;
+ u32 ddc_xfer_done, ddc_xfer_req;
+ u32 ddc_hw_req, ddc_hw_not_idle;
+ bool ddc_hw_not_ready, xfer_not_done, hw_not_done;
+ u32 timeout_count;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+ if (!io->base) {
+ pr_err("core io not inititalized\n");
+ return;
+ }
+
+ /* Wait to be clean on DDC HW engine */
+ timeout_count = 100;
+ do {
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ ddc_xfer_req = hdcp_ddc_status & BIT(4);
+ ddc_xfer_done = hdcp_ddc_status & BIT(10);
+
+ ddc_hw_status = DSS_REG_R(io, HDMI_DDC_HW_STATUS);
+ ddc_hw_req = ddc_hw_status & BIT(16);
+ ddc_hw_not_idle = ddc_hw_status & (BIT(0) | BIT(1));
+
+ /* ddc transfer was requested but not completed */
+ xfer_not_done = ddc_xfer_req && !ddc_xfer_done;
+
+ /* ddc status is not idle or a hw request pending */
+ hw_not_done = ddc_hw_not_idle || ddc_hw_req;
+
+ ddc_hw_not_ready = xfer_not_done || hw_not_done;
+
+ SDE_HDCP_DEBUG("%s: timeout count(%d): ddc hw%sready\n",
+ SDE_HDCP_STATE_NAME, timeout_count,
+ ddc_hw_not_ready ? " not " : " ");
+ SDE_HDCP_DEBUG("hdcp_ddc_status[0x%x], ddc_hw_status[0x%x]\n",
+ hdcp_ddc_status, ddc_hw_status);
+ if (ddc_hw_not_ready)
+ msleep(20);
+ } while (ddc_hw_not_ready && --timeout_count);
+} /* hdcp_1x_hw_ddc_clean */
+
+static int sde_hdcp_1x_load_keys(void *input)
+{
+ int rc = 0;
+ bool use_sw_keys = false;
+ u32 reg_val;
+ u32 ksv_lsb_addr, ksv_msb_addr;
+ u32 aksv_lsb, aksv_msb;
+ u8 aksv[5];
+ struct dss_io_data *io;
+ struct dss_io_data *qfprom_io;
+ struct sde_hdcp_1x *hdcp = input;
+ struct sde_hdcp_reg_set *reg_set;
+
+ if (!hdcp || !hdcp->init_data.core_io ||
+ !hdcp->init_data.qfprom_io) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE) &&
+ !sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
+ pr_err("%s: invalid state. returning\n",
+ SDE_HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ io = hdcp->init_data.core_io;
+ qfprom_io = hdcp->init_data.qfprom_io;
+ reg_set = &hdcp->reg_set;
+
+ /* On compatible hardware, use SW keys */
+ reg_val = DSS_REG_R(qfprom_io, SEC_CTRL_HW_VERSION);
+ if (reg_val >= HDCP_SEL_MIN_SEC_VERSION) {
+ reg_val = DSS_REG_R(qfprom_io,
+ QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
+ QFPROM_RAW_VERSION_4);
+
+ if (!(reg_val & BIT(23)))
+ use_sw_keys = true;
+ }
+
+ if (use_sw_keys) {
+ if (hdcp1_set_keys(&aksv_msb, &aksv_lsb)) {
+ pr_err("setting hdcp SW keys failed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+ } else {
+ /* Fetch aksv from QFPROM, this info should be public. */
+ ksv_lsb_addr = HDCP_KSV_LSB;
+ ksv_msb_addr = HDCP_KSV_MSB;
+
+ if (hdcp->init_data.sec_access) {
+ ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET;
+ ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET;
+ }
+
+ aksv_lsb = DSS_REG_R(qfprom_io, ksv_lsb_addr);
+ aksv_msb = DSS_REG_R(qfprom_io, ksv_msb_addr);
+ }
+
+ SDE_HDCP_DEBUG("%s: AKSV=%02x%08x\n", SDE_HDCP_STATE_NAME,
+ aksv_msb, aksv_lsb);
+
+ aksv[0] = aksv_lsb & 0xFF;
+ aksv[1] = (aksv_lsb >> 8) & 0xFF;
+ aksv[2] = (aksv_lsb >> 16) & 0xFF;
+ aksv[3] = (aksv_lsb >> 24) & 0xFF;
+ aksv[4] = aksv_msb & 0xFF;
+
+ /* check there are 20 ones in AKSV */
+ if (sde_hdcp_1x_count_one(aksv, 5) != 20) {
+ pr_err("AKSV bit count failed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ DSS_REG_W(io, reg_set->aksv_lsb, aksv_lsb);
+ DSS_REG_W(io, reg_set->aksv_msb, aksv_msb);
+
+ /* Setup seed values for random number An */
+ DSS_REG_W(io, reg_set->entropy_ctrl0, 0xB1FFB0FF);
+ DSS_REG_W(io, reg_set->entropy_ctrl1, 0xF00DFACE);
+
+ /* make sure hw is programmed */
+ wmb();
+
+ /* enable hdcp engine */
+ DSS_REG_W(io, reg_set->ctrl, 0x1);
+
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATING;
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_read(struct sde_hdcp_1x *hdcp,
+ struct sde_hdcp_sink_addr *sink,
+ u8 *buf, bool realign)
+{
+ u32 rc = 0;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ reset_hdcp_ddc_failures(hdcp);
+
+ ddc_ctrl = hdcp->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = 0x74;
+ ddc_data->offset = sink->addr;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = sink->len;
+ ddc_data->request_len = sink->len;
+ ddc_data->retry = 5;
+ ddc_data->what = sink->name;
+ ddc_data->retry_align = realign;
+
+ rc = sde_hdmi_ddc_read((void *)hdcp->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("%s: %s read failed\n",
+ SDE_HDCP_STATE_NAME, sink->name);
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To-do DP APIs go here */
+ }
+
+ return rc;
+}
+
+static int sde_hdcp_1x_write(struct sde_hdcp_1x *hdcp,
+ struct sde_hdcp_sink_addr *sink, u8 *buf)
+{
+ int rc = 0;
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ ddc_ctrl = hdcp->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ ddc_data->dev_addr = 0x74;
+ ddc_data->offset = sink->addr;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = sink->len;
+ ddc_data->what = sink->name;
+
+ rc = sde_hdmi_ddc_write((void *)hdcp->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("%s: %s write failed\n",
+ SDE_HDCP_STATE_NAME, sink->name);
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To-do DP APIs go here */
+ }
+
+ return rc;
+}
+
+static void sde_hdcp_1x_enable_interrupts(struct sde_hdcp_1x *hdcp)
+{
+ u32 intr_reg;
+ struct dss_io_data *io;
+ struct sde_hdcp_int_set *isr;
+
+ io = hdcp->init_data.core_io;
+ isr = &hdcp->int_set;
+
+ intr_reg = DSS_REG_R(io, isr->int_reg);
+
+ intr_reg |= HDCP_INT_CLR | HDCP_INT_EN;
+
+ DSS_REG_W(io, isr->int_reg, intr_reg);
+}
+
+static int sde_hdcp_1x_read_bcaps(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("bcaps read: 0x%x\n", hdcp->bcaps);
+
+ hdcp->current_tp.ds_type = hdcp->bcaps & reg_set->repeater ?
+ DS_REPEATER : DS_RECEIVER;
+
+ SDE_HDCP_DEBUG("ds: %s\n", hdcp->current_tp.ds_type == DS_REPEATER ?
+ "repeater" : "receiver");
+
+ /* Write BCAPS to the hardware */
+ DSS_REG_W(hdcp_io, reg_set->sec_data12, hdcp->bcaps);
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_wait_for_hw_ready(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u32 link0_status;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* Wait for HDCP keys to be checked and validated */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ ((link0_status >> reg_set->keys_offset) & 0x7)
+ == HDCP_KEYS_STATE_VALID ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("key not ready\n");
+ goto error;
+ }
+
+ /*
+ * 1.1_Features turned off by default.
+ * No need to write AInfo since 1.1_Features is disabled.
+ */
+ DSS_REG_W(io, reg_set->data4, 0);
+
+ /* Wait for An0 and An1 bit to be ready */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ (link0_status & (BIT(8) | BIT(9))) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("An not ready\n");
+ goto error;
+ }
+
+ /* As per hardware recommendations, wait before reading An */
+ msleep(20);
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_send_an_aksv_to_sink(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 an[8], aksv[5];
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ an[0] = hdcp->an_0 & 0xFF;
+ an[1] = (hdcp->an_0 >> 8) & 0xFF;
+ an[2] = (hdcp->an_0 >> 16) & 0xFF;
+ an[3] = (hdcp->an_0 >> 24) & 0xFF;
+ an[4] = hdcp->an_1 & 0xFF;
+ an[5] = (hdcp->an_1 >> 8) & 0xFF;
+ an[6] = (hdcp->an_1 >> 16) & 0xFF;
+ an[7] = (hdcp->an_1 >> 24) & 0xFF;
+
+ SDE_HDCP_DEBUG("an read: 0x%2x%2x%2x%2x%2x%2x%2x%2x\n",
+ an[7], an[6], an[5], an[4], an[3], an[2], an[1], an[0]);
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.an, an);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error writing an to sink\n");
+ goto error;
+ }
+
+ /* Copy An and AKSV to byte arrays for transmission */
+ aksv[0] = hdcp->aksv_0 & 0xFF;
+ aksv[1] = (hdcp->aksv_0 >> 8) & 0xFF;
+ aksv[2] = (hdcp->aksv_0 >> 16) & 0xFF;
+ aksv[3] = (hdcp->aksv_0 >> 24) & 0xFF;
+ aksv[4] = hdcp->aksv_1 & 0xFF;
+
+ SDE_HDCP_DEBUG("aksv read: 0x%2x%2x%2x%2x%2x\n",
+ aksv[4], aksv[3], aksv[2], aksv[1], aksv[0]);
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.aksv, aksv);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error writing aksv to sink\n");
+ goto error;
+ }
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_read_an_aksv_from_hw(struct sde_hdcp_1x *hdcp)
+{
+ struct dss_io_data *io = hdcp->init_data.core_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+ if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ udelay(1);
+ hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+ }
+
+ hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+ if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ udelay(1);
+ hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+ }
+
+ /* Read AKSV */
+ hdcp->aksv_0 = DSS_REG_R(io, reg_set->data3);
+ hdcp->aksv_1 = DSS_REG_R(io, reg_set->data4);
+
+ return 0;
+}
+
+static int sde_hdcp_1x_get_bksv_from_sink(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 *bksv = hdcp->current_tp.bksv;
+ u32 link0_bksv_0, link0_bksv_1;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io;
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bksv, bksv, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bksv from sink\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("bksv read: 0x%2x%2x%2x%2x%2x\n",
+ bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]);
+
+ /* check there are 20 ones in BKSV */
+ if (sde_hdcp_1x_count_one(bksv, 5) != 20) {
+ pr_err("%s: BKSV doesn't have 20 1's and 20 0's\n",
+ SDE_HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link0_bksv_0 = bksv[3];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0];
+ link0_bksv_1 = bksv[4];
+
+ DSS_REG_W(hdcp_io, reg_set->sec_data0, link0_bksv_0);
+ DSS_REG_W(hdcp_io, reg_set->sec_data1, link0_bksv_1);
+error:
+ return rc;
+}
+
+static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 enable_hpd_irq = 0x1;
+
+ if (hdcp->current_tp.ds_type != DS_REPEATER)
+ return;
+
+ rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.ainfo, &enable_hpd_irq);
+ if (IS_ERR_VALUE(rc))
+ SDE_HDCP_DEBUG("error writing ainfo to sink\n");
+}
+
+static int sde_hdcp_1x_verify_r0(struct sde_hdcp_1x *hdcp)
+{
+ int rc, r0_retry = 3;
+ u8 buf[2];
+ u32 link0_status, timeout_count;
+ u32 const r0_read_delay_us = 1;
+ u32 const r0_read_timeout_us = r0_read_delay_us * 10;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* Wait for HDCP R0 computation to be completed */
+ rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+ (link0_status & BIT(reg_set->r0_offset)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("R0 not ready\n");
+ goto error;
+ }
+
+ /*
+ * HDCP Compliace Test case 1A-01:
+ * Wait here at least 100ms before reading R0'
+ */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ msleep(100);
+ } else {
+ if (!hdcp->sink_r0_ready) {
+ reinit_completion(&hdcp->sink_r0_available);
+ timeout_count = wait_for_completion_timeout(
+ &hdcp->sink_r0_available, HZ / 2);
+
+ if (hdcp->reauth) {
+ pr_err("sink R0 not ready\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+ }
+
+ do {
+ memset(buf, 0, sizeof(buf));
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.r0,
+ buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading R0' from sink\n");
+ goto error;
+ }
+
+ SDE_HDCP_DEBUG("sink R0'read: %2x%2x\n", buf[1], buf[0]);
+
+ DSS_REG_W(io, reg_set->data2_0, (((u32)buf[1]) << 8) | buf[0]);
+
+ rc = readl_poll_timeout(io->base + reg_set->status,
+ link0_status, (link0_status & BIT(12)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ r0_read_delay_us, r0_read_timeout_us);
+ } while (rc && --r0_retry);
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_authentication_part1(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ sde_hdcp_1x_enable_interrupts(hdcp);
+
+ rc = sde_hdcp_1x_read_bcaps(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_wait_for_hw_ready(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_read_an_aksv_from_hw(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_get_bksv_from_sink(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_send_an_aksv_to_sink(hdcp);
+ if (rc)
+ goto error;
+
+ sde_hdcp_1x_enable_sink_irq_hpd(hdcp);
+
+ rc = sde_hdcp_1x_verify_r0(hdcp);
+ if (rc)
+ goto error;
+
+ pr_info("SUCCESSFUL\n");
+
+ return 0;
+error:
+ pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME);
+
+ return rc;
+}
+
+static int sde_hdcp_1x_transfer_v_h(struct sde_hdcp_1x *hdcp)
+{
+ int rc = 0;
+ struct dss_io_data *io = hdcp->init_data.hdcp_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ struct sde_hdcp_1x_reg_data reg_data[] = {
+ {reg_set->sec_data7, &hdcp->sink_addr.v_h0},
+ {reg_set->sec_data8, &hdcp->sink_addr.v_h1},
+ {reg_set->sec_data9, &hdcp->sink_addr.v_h2},
+ {reg_set->sec_data10, &hdcp->sink_addr.v_h3},
+ {reg_set->sec_data11, &hdcp->sink_addr.v_h4},
+ };
+ struct sde_hdcp_sink_addr sink = {"V", reg_data->sink->addr};
+ u32 size = ARRAY_SIZE(reg_data);
+ u8 buf[0xFF] = {0};
+ u32 i = 0, len = 0;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++) {
+ struct sde_hdcp_1x_reg_data *rd = reg_data + i;
+
+ len += rd->sink->len;
+ }
+
+ sink.len = len;
+
+ rc = sde_hdcp_1x_read(hdcp, &sink, buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading %s\n", sink.name);
+ goto end;
+ }
+
+
+ for (i = 0; i < size; i++) {
+ struct sde_hdcp_1x_reg_data *rd = reg_data + i;
+ u32 reg_data;
+
+ memcpy(&reg_data, buf + (sizeof(u32) * i), sizeof(u32));
+ DSS_REG_W(io, rd->reg_id, reg_data);
+ }
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_validate_downstream(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ u8 buf[2] = {0, 0};
+ u8 device_count, depth;
+ u8 max_cascade_exceeded, max_devs_exceeded;
+ u16 bstatus;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bstatus,
+ buf, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bstatus\n");
+ goto end;
+ }
+
+ bstatus = buf[1];
+ bstatus = (bstatus << 8) | buf[0];
+
+ device_count = bstatus & 0x7F;
+
+ SDE_HDCP_DEBUG("device count %d\n", device_count);
+
+ /* Cascaded repeater depth */
+ depth = (bstatus >> 8) & 0x7;
+ SDE_HDCP_DEBUG("depth %d\n", depth);
+
+ /*
+ * HDCP Compliance 1B-05:
+ * Check if no. of devices connected to repeater
+ * exceed max_devices_connected from bit 7 of Bstatus.
+ */
+ max_devs_exceeded = (bstatus & BIT(7)) >> 7;
+ if (max_devs_exceeded == 0x01) {
+ pr_err("no. of devs connected exceed max allowed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /*
+ * HDCP Compliance 1B-06:
+ * Check if no. of cascade connected to repeater
+ * exceed max_cascade_connected from bit 11 of Bstatus.
+ */
+ max_cascade_exceeded = (bstatus & BIT(11)) >> 11;
+ if (max_cascade_exceeded == 0x01) {
+ pr_err("no. of cascade connections exceed max allowed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* Update topology information */
+ hdcp->current_tp.dev_count = device_count;
+ hdcp->current_tp.max_cascade_exceeded = max_cascade_exceeded;
+ hdcp->current_tp.max_dev_exceeded = max_devs_exceeded;
+ hdcp->current_tp.depth = depth;
+
+ DSS_REG_W(hdcp->init_data.hdcp_io,
+ reg_set->sec_data12, hdcp->bcaps | (bstatus << 8));
+end:
+ return rc;
+}
+
+static int sde_hdcp_1x_read_ksv_fifo(struct sde_hdcp_1x *hdcp)
+{
+ u32 ksv_read_retry = 20, ksv_bytes, rc = 0;
+ u8 *ksv_fifo = hdcp->current_tp.ksv_list;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ memset(ksv_fifo, 0, sizeof(hdcp->current_tp.ksv_list));
+
+ /* each KSV is 5 bytes long */
+ ksv_bytes = 5 * hdcp->current_tp.dev_count;
+ hdcp->sink_addr.ksv_fifo.len = ksv_bytes;
+
+ while (ksv_bytes && --ksv_read_retry) {
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.ksv_fifo,
+ ksv_fifo, true);
+ if (IS_ERR_VALUE(rc))
+ pr_err("could not read ksv fifo (%d)\n",
+ ksv_read_retry);
+ else
+ break;
+ }
+
+ if (rc)
+ pr_err("error reading ksv_fifo\n");
+
+ return rc;
+}
+
+static int sde_hdcp_1x_write_ksv_fifo(struct sde_hdcp_1x *hdcp)
+{
+ int i, rc = 0;
+ u8 *ksv_fifo = hdcp->current_tp.ksv_list;
+ u32 ksv_bytes = hdcp->sink_addr.ksv_fifo.len;
+ struct dss_io_data *io = hdcp->init_data.core_io;
+ struct dss_io_data *sec_io = hdcp->init_data.hdcp_io;
+ struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
+ u32 sha_status = 0, status;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /* reset SHA Controller */
+ DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x1);
+ DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x0);
+
+ for (i = 0; i < ksv_bytes - 1; i++) {
+ /* Write KSV byte and do not set DONE bit[0] */
+ DSS_REG_W_ND(sec_io, reg_set->sec_sha_data, ksv_fifo[i] << 16);
+
+ /*
+ * Once 64 bytes have been written, we need to poll for
+ * HDCP_SHA_BLOCK_DONE before writing any further
+ */
+ if (i && !((i + 1) % 64)) {
+ rc = readl_poll_timeout(io->base + reg_set->sha_status,
+ sha_status, (sha_status & BIT(0)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("block not done\n");
+ goto error;
+ }
+ }
+ }
+
+ /* Write l to DONE bit[0] */
+ DSS_REG_W_ND(sec_io, reg_set->sec_sha_data,
+ (ksv_fifo[ksv_bytes - 1] << 16) | 0x1);
+
+ /* Now wait for HDCP_SHA_COMP_DONE */
+ rc = readl_poll_timeout(io->base + reg_set->sha_status, sha_status,
+ (sha_status & BIT(4)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("V computation not done\n");
+ goto error;
+ }
+
+ /* Wait for V_MATCHES */
+ rc = readl_poll_timeout(io->base + reg_set->status, status,
+ (status & BIT(reg_set->v_offset)) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
+ HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("V mismatch\n");
+ rc = -EINVAL;
+ }
+error:
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int sde_hdcp_1x_wait_for_ksv_ready(struct sde_hdcp_1x *hdcp)
+{
+ int rc, timeout;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Wait until READY bit is set in BCAPS, as per HDCP specifications
+ * maximum permitted time to check for READY bit is five seconds.
+ */
+ rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ timeout = 50;
+
+ while (!(hdcp->bcaps & BIT(5)) && --timeout) {
+ rc = sde_hdcp_1x_read(hdcp,
+ &hdcp->sink_addr.bcaps,
+ &hdcp->bcaps, false);
+ if (IS_ERR_VALUE(rc) ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("error reading bcaps\n");
+ goto error;
+ }
+ msleep(100);
+ }
+ } else {
+ u8 cp_buf = 0;
+ struct sde_hdcp_sink_addr *sink =
+ &hdcp->sink_addr.cp_irq_status;
+
+ timeout = jiffies_to_msecs(jiffies);
+
+ while (1) {
+ rc = sde_hdcp_1x_read(hdcp, sink, &cp_buf, false);
+ if (rc)
+ goto error;
+
+ if (cp_buf & BIT(0))
+ break;
+
+ /* max timeout of 5 sec as per hdcp 1.x spec */
+ if (abs(timeout - jiffies_to_msecs(jiffies)) > 5000) {
+ timeout = 0;
+ break;
+ }
+
+ if (hdcp->ksv_ready || hdcp->reauth ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ break;
+
+ /* re-read after a minimum delay */
+ msleep(20);
+ }
+ }
+
+ if (!timeout || hdcp->reauth ||
+ !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("DS KSV not ready\n");
+ rc = -EINVAL;
+ } else {
+ hdcp->ksv_ready = true;
+ }
+error:
+ return rc;
+}
+
+static int sde_hdcp_1x_authentication_part2(struct sde_hdcp_1x *hdcp)
+{
+ int rc;
+ int v_retry = 3;
+
+ rc = sde_hdcp_1x_validate_downstream(hdcp);
+ if (rc)
+ goto error;
+
+ rc = sde_hdcp_1x_read_ksv_fifo(hdcp);
+ if (rc)
+ goto error;
+
+ do {
+ rc = sde_hdcp_1x_transfer_v_h(hdcp);
+ if (rc)
+ goto error;
+
+ /* do not proceed further if no device connected */
+ if (!hdcp->current_tp.dev_count)
+ goto error;
+
+ rc = sde_hdcp_1x_write_ksv_fifo(hdcp);
+ } while (--v_retry && rc);
+error:
+ if (rc) {
+ pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME);
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED;
+
+ pr_info("SUCCESSFUL\n");
+ }
+
+ return rc;
+}
+
+static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
+{
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ memcpy((void *)&hdcp->cached_tp,
+ (void *) &hdcp->current_tp,
+ sizeof(hdcp->cached_tp));
+ hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp);
+}
+
+static void sde_hdcp_1x_notify_topology(void)
+{
+ hdcp1_notify_topology();
+}
+
+static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp)
+{
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
+ sde_hdcp_1x_cache_topology(hdcp);
+ sde_hdcp_1x_notify_topology();
+ }
+
+ if (hdcp->init_data.notify_status &&
+ !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ hdcp->init_data.notify_status(
+ hdcp->init_data.cb_data,
+ hdcp->hdcp_state);
+ }
+}
+
+static void sde_hdcp_1x_auth_work(struct work_struct *work)
+{
+ int rc;
+ struct delayed_work *dw = to_delayed_work(work);
+ struct sde_hdcp_1x *hdcp = container_of(dw,
+ struct sde_hdcp_1x, hdcp_auth_work);
+ struct dss_io_data *io;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ pr_err("invalid state\n");
+ return;
+ }
+
+ hdcp->sink_r0_ready = false;
+ hdcp->reauth = false;
+ hdcp->ksv_ready = false;
+
+ io = hdcp->init_data.core_io;
+ /* Enabling Software DDC for HDMI and REF timer for DP */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
+ DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
+ HDMI_DDC_ARBITRATION) & ~(BIT(4)));
+ else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* To do for DP */
+ }
+
+ /*
+ * program hw to enable encryption as soon as
+ * authentication is successful.
+ */
+ hdcp1_set_enc(true);
+
+ rc = sde_hdcp_1x_authentication_part1(hdcp);
+ if (rc)
+ goto end;
+
+ if (hdcp->current_tp.ds_type == DS_REPEATER) {
+ rc = sde_hdcp_1x_wait_for_ksv_ready(hdcp);
+ if (rc)
+ goto end;
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED;
+ goto end;
+ }
+
+ hdcp->ksv_ready = false;
+
+ rc = sde_hdcp_1x_authentication_part2(hdcp);
+ if (rc)
+ goto end;
+
+ /*
+ * Disabling software DDC before going into part3 to make sure
+ * there is no Arbitration between software and hardware for DDC
+ */
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI)
+ DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
+ HDMI_DDC_ARBITRATION) | (BIT(4)));
+end:
+ if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+
+ sde_hdcp_1x_update_auth_status(hdcp);
+}
+
+static int sde_hdcp_1x_authenticate(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ flush_delayed_work(&hdcp->hdcp_auth_work);
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ if (!sde_hdcp_1x_load_keys(input)) {
+
+ queue_delayed_work(hdcp->workq,
+ &hdcp->hdcp_auth_work, HZ/2);
+ } else {
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ sde_hdcp_1x_update_auth_status(hdcp);
+ }
+
+ return 0;
+} /* hdcp_1x_authenticate */
+
+static int sde_hdcp_1x_reauthenticate(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ struct dss_io_data *io;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+ u32 hdmi_hw_version;
+ u32 ret = 0, reg;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
+ pr_err("invalid state\n");
+ return -EINVAL;
+ }
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION);
+ if (hdmi_hw_version >= 0x30030000) {
+ DSS_REG_W(io, HDMI_CTRL_SW_RESET, BIT(1));
+ DSS_REG_W(io, HDMI_CTRL_SW_RESET, 0);
+ }
+
+ /* Wait to be clean on DDC HW engine */
+ sde_hdcp_1x_hw_ddc_clean(hdcp);
+ }
+
+ /* Disable HDCP interrupts */
+ DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
+
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, reg_set->ctrl, 0);
+
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ sde_hdcp_1x_authenticate(hdcp);
+
+ return ret;
+} /* hdcp_1x_reauthenticate */
+
+static void sde_hdcp_1x_off(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ struct dss_io_data *io;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+ int rc = 0;
+ u32 reg;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ pr_err("invalid state\n");
+ return;
+ }
+
+ /*
+ * Disable HDCP interrupts.
+ * Also, need to set the state to inactive here so that any ongoing
+ * reauth works will know that the HDCP session has been turned off.
+ */
+ mutex_lock(hdcp->init_data.mutex);
+ DSS_REG_W(io, isr->int_reg,
+ DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ mutex_unlock(hdcp->init_data.mutex);
+
+ /* complete any wait pending */
+ complete_all(&hdcp->sink_r0_available);
+ complete_all(&hdcp->r0_checked);
+ /*
+ * Cancel any pending auth/reauth attempts.
+ * If one is ongoing, this will wait for it to finish.
+ * No more reauthentiaction attempts will be scheduled since we
+ * set the currect state to inactive.
+ */
+ rc = cancel_delayed_work_sync(&hdcp->hdcp_auth_work);
+ if (rc)
+ SDE_HDCP_DEBUG("%s: Deleted hdcp auth work\n",
+ SDE_HDCP_STATE_NAME);
+
+ hdcp1_set_enc(false);
+
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, reg_set->ctrl, 0);
+
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
+ hdcp->sink_r0_ready = false;
+
+ SDE_HDCP_DEBUG("%s: HDCP: Off\n", SDE_HDCP_STATE_NAME);
+} /* hdcp_1x_off */
+
+static int sde_hdcp_1x_isr(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ int rc = 0;
+ struct dss_io_data *io;
+ u32 hdcp_int_val;
+ struct sde_hdcp_reg_set *reg_set;
+ struct sde_hdcp_int_set *isr;
+
+ if (!hdcp || !hdcp->init_data.core_io) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ io = hdcp->init_data.core_io;
+ reg_set = &hdcp->reg_set;
+ isr = &hdcp->int_set;
+
+ hdcp_int_val = DSS_REG_R(io, isr->int_reg);
+
+ /* Ignore HDCP interrupts if HDCP is disabled */
+ if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ DSS_REG_W(io, isr->int_reg, hdcp_int_val | HDCP_INT_CLR);
+ return 0;
+ }
+
+ if (hdcp_int_val & isr->auth_success_int) {
+ /* AUTH_SUCCESS_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_success_ack));
+ SDE_HDCP_DEBUG("%s: AUTH SUCCESS\n", SDE_HDCP_STATE_NAME);
+
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING))
+ complete_all(&hdcp->r0_checked);
+ }
+
+ if (hdcp_int_val & isr->auth_fail_int) {
+ /* AUTH_FAIL_INT */
+ u32 link_status = DSS_REG_R(io, reg_set->status);
+
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_fail_ack));
+
+ SDE_HDCP_DEBUG("%s: AUTH FAIL, LINK0_STATUS=0x%08x\n",
+ SDE_HDCP_STATE_NAME, link_status);
+
+ if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ sde_hdcp_1x_update_auth_status(hdcp);
+ } else if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
+ complete_all(&hdcp->r0_checked);
+ }
+
+ /* Clear AUTH_FAIL_INFO as well */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->auth_fail_info_ack));
+ }
+
+ if (hdcp_int_val & isr->tx_req_int) {
+ /* DDC_XFER_REQ_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->tx_req_ack));
+ SDE_HDCP_DEBUG("%s: DDC_XFER_REQ_INT received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->tx_req_done_int) {
+ /* DDC_XFER_DONE_INT */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->tx_req_done_ack));
+ SDE_HDCP_DEBUG("%s: DDC_XFER_DONE received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->encryption_ready) {
+ /* Encryption enabled */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->encryption_ready_ack));
+ SDE_HDCP_DEBUG("%s: encryption ready received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & isr->encryption_not_ready) {
+ /* Encryption enabled */
+ DSS_REG_W(io, isr->int_reg,
+ (hdcp_int_val | isr->encryption_not_ready_ack));
+ SDE_HDCP_DEBUG("%s: encryption not ready received\n",
+ SDE_HDCP_STATE_NAME);
+ }
+
+error:
+ return rc;
+}
+
+void sde_hdcp_1x_deinit(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (hdcp->workq)
+ destroy_workqueue(hdcp->workq);
+
+ kfree(hdcp);
+} /* hdcp_1x_deinit */
+
+static void sde_hdcp_1x_update_client_reg_set(struct sde_hdcp_1x *hdcp)
+{
+
+ if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) {
+ struct sde_hdcp_reg_set reg_set = HDCP_REG_SET_CLIENT_HDMI;
+ struct sde_hdcp_skaddr_map sink_addr = HDCP_HDMI_SINK_ADDR_MAP;
+ struct sde_hdcp_int_set isr = HDCP_HDMI_INT_SET;
+
+ hdcp->reg_set = reg_set;
+ hdcp->sink_addr = sink_addr;
+ hdcp->int_set = isr;
+ } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+ /* TO DO for DP
+ * Will be filled later
+ */
+ }
+}
+
+static bool sde_hdcp_1x_is_cp_irq_raised(struct sde_hdcp_1x *hdcp)
+{
+ int ret;
+ u8 buf = 0;
+ struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1};
+
+ ret = sde_hdcp_1x_read(hdcp, &sink, &buf, false);
+ if (IS_ERR_VALUE(ret))
+ pr_err("error reading irq_vector\n");
+
+ return buf & BIT(2) ? true : false;
+}
+
+static void sde_hdcp_1x_clear_cp_irq(struct sde_hdcp_1x *hdcp)
+{
+ int ret;
+ u8 buf = BIT(2);
+ struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1};
+
+ ret = sde_hdcp_1x_write(hdcp, &sink, &buf);
+ if (IS_ERR_VALUE(ret))
+ pr_err("error clearing irq_vector\n");
+}
+
+static int sde_hdcp_1x_cp_irq(void *input)
+{
+ struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input;
+ u8 buf = 0;
+ int ret;
+
+ if (!hdcp) {
+ pr_err("invalid input\n");
+ goto irq_not_handled;
+ }
+
+ if (!sde_hdcp_1x_is_cp_irq_raised(hdcp)) {
+ SDE_HDCP_DEBUG("cp_irq not raised\n");
+ goto irq_not_handled;
+ }
+
+ ret = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.cp_irq_status,
+ &buf, false);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("error reading cp_irq_status\n");
+ goto irq_not_handled;
+ }
+
+ if ((buf & BIT(2)) || (buf & BIT(3))) {
+ pr_err("%s\n",
+ buf & BIT(2) ? "LINK_INTEGRITY_FAILURE" :
+ "REAUTHENTICATION_REQUEST");
+
+ hdcp->reauth = true;
+
+ if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE))
+ hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL;
+
+ complete_all(&hdcp->sink_r0_available);
+ sde_hdcp_1x_update_auth_status(hdcp);
+ } else if (buf & BIT(1)) {
+ SDE_HDCP_DEBUG("R0' AVAILABLE\n");
+ hdcp->sink_r0_ready = true;
+ complete_all(&hdcp->sink_r0_available);
+ } else if ((buf & BIT(0))) {
+ SDE_HDCP_DEBUG("KSVs READY\n");
+
+ hdcp->ksv_ready = true;
+ } else {
+ SDE_HDCP_DEBUG("spurious interrupt\n");
+ }
+
+ sde_hdcp_1x_clear_cp_irq(hdcp);
+ return 0;
+
+irq_not_handled:
+ return -EINVAL;
+}
+
+void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data)
+{
+ struct sde_hdcp_1x *hdcp = NULL;
+ char name[20];
+ static struct sde_hdcp_ops ops = {
+ .isr = sde_hdcp_1x_isr,
+ .cp_irq = sde_hdcp_1x_cp_irq,
+ .reauthenticate = sde_hdcp_1x_reauthenticate,
+ .authenticate = sde_hdcp_1x_authenticate,
+ .off = sde_hdcp_1x_off
+ };
+
+ if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
+ !init_data->mutex || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
+ pr_err("invalid input\n");
+ goto error;
+ }
+
+ if (init_data->sec_access && !init_data->hdcp_io) {
+ pr_err("hdcp_io required\n");
+ goto error;
+ }
+
+ hdcp = kzalloc(sizeof(*hdcp), GFP_KERNEL);
+ if (!hdcp)
+ goto error;
+
+ hdcp->init_data = *init_data;
+ hdcp->ops = &ops;
+
+ snprintf(name, sizeof(name), "hdcp_1x_%d",
+ hdcp->init_data.client_id);
+
+ hdcp->workq = create_workqueue(name);
+ if (!hdcp->workq) {
+ pr_err("Error creating workqueue\n");
+ kfree(hdcp);
+ goto error;
+ }
+
+ sde_hdcp_1x_update_client_reg_set(hdcp);
+
+ INIT_DELAYED_WORK(&hdcp->hdcp_auth_work, sde_hdcp_1x_auth_work);
+
+ hdcp->hdcp_state = HDCP_STATE_INACTIVE;
+ init_completion(&hdcp->r0_checked);
+ init_completion(&hdcp->sink_r0_available);
+
+ SDE_HDCP_DEBUG("HDCP module initialized. HDCP_STATE=%s\n",
+ SDE_HDCP_STATE_NAME);
+
+ return (void *)hdcp;
+
+error:
+ return NULL;
+} /* hdcp_1x_init */
+
+struct sde_hdcp_ops *sde_hdcp_1x_start(void *input)
+{
+ return ((struct sde_hdcp_1x *)input)->ops;
+}
+
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 55f906c9cb90..b4d0656d062b 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -979,6 +979,13 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
spin_unlock(&dispatcher->plist_lock);
}
+static inline void _decrement_submit_now(struct kgsl_device *device)
+{
+ spin_lock(&device->submit_lock);
+ device->submit_now--;
+ spin_unlock(&device->submit_lock);
+}
+
/**
* adreno_dispatcher_issuecmds() - Issue commmands from pending contexts
* @adreno_dev: Pointer to the adreno device struct
@@ -988,15 +995,29 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
static void adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev)
{
struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+
+ spin_lock(&device->submit_lock);
+ /* If state transition to SLUMBER, schedule the work for later */
+ if (device->slumber == true) {
+ spin_unlock(&device->submit_lock);
+ goto done;
+ }
+ device->submit_now++;
+ spin_unlock(&device->submit_lock);
/* If the dispatcher is busy then schedule the work for later */
if (!mutex_trylock(&dispatcher->mutex)) {
- adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev));
- return;
+ _decrement_submit_now(device);
+ goto done;
}
_adreno_dispatcher_issuecmds(adreno_dev);
mutex_unlock(&dispatcher->mutex);
+ _decrement_submit_now(device);
+ return;
+done:
+ adreno_dispatcher_schedule(device);
}
/**
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index b2def8dea954..7584811f388a 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4719,6 +4719,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
device->id, device->reg_phys, device->reg_len);
rwlock_init(&device->context_lock);
+ spin_lock_init(&device->submit_lock);
setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index d93fd9bfbcd0..64dd45a30612 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -256,6 +256,11 @@ struct kgsl_device {
struct kgsl_pwrctrl pwrctrl;
int open_count;
+ /* For GPU inline submission */
+ uint32_t submit_now;
+ spinlock_t submit_lock;
+ bool slumber;
+
struct mutex mutex;
uint32_t state;
uint32_t requested_state;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 0150d50c925b..e42f92392e8d 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -81,6 +81,12 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
unsigned int state);
static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level);
+static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq,
+ const char *name);
+static void _gpu_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name);
+static void _bimc_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name);
/**
* _record_pwrevent() - Record the history of the new event
@@ -405,7 +411,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel];
/* Change register settings if any BEFORE pwrlevel change*/
kgsl_pwrctrl_pwrlevel_change_settings(device, 0);
- clk_set_rate(pwr->grp_clks[0], pwrlevel->gpu_freq);
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
+ pwrlevel->gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr, pwr->active_pwrlevel);
trace_kgsl_pwrlevel(device,
@@ -423,9 +430,12 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
if (pwr->gpu_bimc_int_clk) {
if (pwr->active_pwrlevel == 0 &&
!pwr->gpu_bimc_interface_enabled) {
- clk_set_rate(pwr->gpu_bimc_int_clk,
- pwr->gpu_bimc_int_clk_freq);
- clk_prepare_enable(pwr->gpu_bimc_int_clk);
+ kgsl_pwrctrl_clk_set_rate(pwr->gpu_bimc_int_clk,
+ pwr->gpu_bimc_int_clk_freq,
+ "bimc_gpu_clk");
+ _bimc_clk_prepare_enable(device,
+ pwr->gpu_bimc_int_clk,
+ "bimc_gpu_clk");
pwr->gpu_bimc_interface_enabled = 1;
} else if (pwr->previous_pwrlevel == 0
&& pwr->gpu_bimc_interface_enabled) {
@@ -1650,9 +1660,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
(requested_state != KGSL_STATE_NAP)) {
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
clk_unprepare(pwr->grp_clks[i]);
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->num_pwrlevels - 1);
}
@@ -1664,9 +1674,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
clk_unprepare(pwr->grp_clks[i]);
if ((pwr->pwrlevels[0].gpu_freq > 0)) {
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->num_pwrlevels - 1);
}
@@ -1679,29 +1689,31 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
/* High latency clock maintenance. */
if (device->state != KGSL_STATE_NAP) {
if (pwr->pwrlevels[0].gpu_freq > 0) {
- clk_set_rate(pwr->grp_clks[0],
+ kgsl_pwrctrl_clk_set_rate(
+ pwr->grp_clks[0],
pwr->pwrlevels
[pwr->active_pwrlevel].
- gpu_freq);
+ gpu_freq, clocks[0]);
_isense_clk_set_rate(pwr,
pwr->active_pwrlevel);
}
-
- for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
- clk_prepare(pwr->grp_clks[i]);
}
- /* as last step, enable grp_clk
- this is to let GPU interrupt to come */
+
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
- clk_enable(pwr->grp_clks[i]);
+ _gpu_clk_prepare_enable(device,
+ pwr->grp_clks[i], clocks[i]);
+
/* Enable the gpu-bimc-interface clocks */
if (pwr->gpu_bimc_int_clk) {
if (pwr->active_pwrlevel == 0 &&
!pwr->gpu_bimc_interface_enabled) {
- clk_set_rate(pwr->gpu_bimc_int_clk,
- pwr->gpu_bimc_int_clk_freq);
- clk_prepare_enable(
- pwr->gpu_bimc_int_clk);
+ kgsl_pwrctrl_clk_set_rate(
+ pwr->gpu_bimc_int_clk,
+ pwr->gpu_bimc_int_clk_freq,
+ "bimc_gpu_clk");
+ _bimc_clk_prepare_enable(device,
+ pwr->gpu_bimc_int_clk,
+ "bimc_gpu_clk");
pwr->gpu_bimc_interface_enabled = 1;
}
}
@@ -2022,7 +2034,54 @@ static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level)
rate = clk_round_rate(pwr->grp_clks[pwr->isense_clk_indx],
level > pwr->isense_clk_on_level ?
KGSL_XO_CLK_FREQ : KGSL_ISENSE_CLK_FREQ);
- return clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx], rate);
+ return kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx],
+ rate, clocks[pwr->isense_clk_indx]);
+}
+
+/*
+ * _gpu_clk_prepare_enable - Enable the specified GPU clock
+ * Try once to enable it and then BUG() for debug
+ */
+static void _gpu_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name)
+{
+ int ret;
+
+ if (device->state == KGSL_STATE_NAP) {
+ ret = clk_enable(clk);
+ if (ret)
+ goto err;
+ return;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (!ret)
+ return;
+err:
+ /* Failure is fatal so BUG() to facilitate debug */
+ KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret);
+}
+
+/*
+ * _bimc_clk_prepare_enable - Enable the specified GPU clock
+ * Try once to enable it and then BUG() for debug
+ */
+static void _bimc_clk_prepare_enable(struct kgsl_device *device,
+ struct clk *clk, const char *name)
+{
+ int ret = clk_prepare_enable(clk);
+ /* Failure is fatal so BUG() to facilitate debug */
+ if (ret)
+ KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret);
+}
+
+static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq,
+ const char *name)
+{
+ int ret = clk_set_rate(grp_clk, freq);
+
+ WARN(ret, "KGSL:%s set freq %d failed:%d\n", name, freq, ret);
+ return ret;
}
static inline void _close_pcl(struct kgsl_pwrctrl *pwr)
@@ -2117,11 +2176,12 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
pwr->pwrlevels[i].gpu_freq = freq;
}
- clk_set_rate(pwr->grp_clks[0],
- pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq);
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0],
+ pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq, clocks[0]);
- clk_set_rate(pwr->grp_clks[6],
- clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ));
+ kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6],
+ clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ),
+ clocks[6]);
_isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1);
@@ -2347,9 +2407,24 @@ void kgsl_idle_check(struct work_struct *work)
|| device->state == KGSL_STATE_NAP) {
if (!atomic_read(&device->active_cnt)) {
+ spin_lock(&device->submit_lock);
+ if (device->submit_now) {
+ spin_unlock(&device->submit_lock);
+ goto done;
+ }
+ /* Don't allow GPU inline submission in SLUMBER */
+ if (requested_state == KGSL_STATE_SLUMBER)
+ device->slumber = true;
+ spin_unlock(&device->submit_lock);
+
ret = kgsl_pwrctrl_change_state(device,
device->requested_state);
if (ret == -EBUSY) {
+ if (requested_state == KGSL_STATE_SLUMBER) {
+ spin_lock(&device->submit_lock);
+ device->slumber = false;
+ spin_unlock(&device->submit_lock);
+ }
/*
* If the GPU is currently busy, restore
* the requested state and reschedule
@@ -2360,7 +2435,7 @@ void kgsl_idle_check(struct work_struct *work)
kgsl_schedule_work(&device->idle_check_ws);
}
}
-
+done:
if (!ret)
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
@@ -2789,6 +2864,13 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
trace_kgsl_pwr_set_state(device, state);
device->state = state;
device->requested_state = KGSL_STATE_NONE;
+
+ spin_lock(&device->submit_lock);
+ if (state == KGSL_STATE_SLUMBER || state == KGSL_STATE_SUSPEND)
+ device->slumber = true;
+ else
+ device->slumber = false;
+ spin_unlock(&device->submit_lock);
}
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index 03d1b3c22d61..6515e3d6ecbc 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -1360,6 +1360,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
{
uint16_t first_pixel, last_pixel, first_line, last_line;
struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ struct msm_vfe_testgen_cfg *testgen_cfg = &pix_cfg->testgen_cfg;
uint32_t val, subsample_period, subsample_pattern;
uint32_t irq_sub_period = 32;
uint32_t frame_sub_period = 32;
@@ -1383,8 +1384,15 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
- msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
- (camif_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0x484);
+ if (pix_cfg->input_mux == TESTGEN)
+ msm_camera_io_w((testgen_cfg->lines_per_frame - 1) << 16 |
+ (testgen_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+ else
+ msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
+ (camif_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+
if (bus_sub_en) {
val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
val &= 0xFFFFFFDF;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 7e74f2f10c8c..e2f068a21c28 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -2498,7 +2498,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
intf = SRC_TO_INTF(stream_info->stream_src);
vfe_dev->axi_data.src_info[intf].lpm =
ab_ib_vote->lpm_mode;
- if (stream_info->lpm_mode) {
+ if (stream_info->lpm_mode ||
+ stream_info->state == INACTIVE) {
spin_unlock_irqrestore(&stream_info->lock,
flags);
continue;
@@ -2518,7 +2519,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
intf = SRC_TO_INTF(stream_info->stream_src);
vfe_dev->axi_data.src_info[intf].lpm =
ab_ib_vote->lpm_mode;
- if (stream_info->lpm_mode == 0) {
+ if (stream_info->lpm_mode == 0 ||
+ stream_info->state == INACTIVE) {
spin_unlock_irqrestore(&stream_info->lock,
flags);
continue;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index ee695bf5dfd9..f0831e64f250 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -267,7 +267,9 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
int result = 0;
memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
- buf_event.timestamp = ts->buf_time;
+ buf_event.timestamp = ts->event_time;
+ buf_event.mono_timestamp = ts->buf_time;
+
buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
pingpong_status = vfe_dev->hw_info->
vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
@@ -1263,7 +1265,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
&update_cmd->update_info[i];
/*check array reference bounds*/
if (STATS_IDX(update_info->stream_handle)
- > vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s: stats idx %d out of bound!", __func__,
STATS_IDX(update_info->stream_handle));
return -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index f19e6dd1cb01..d30d8022b7ab 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -479,8 +479,12 @@ static int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
if (input_cfg->d.pix_cfg.input_mux == CAMIF ||
input_cfg->d.pix_cfg.input_mux == TESTGEN) {
- vfe_dev->axi_data.src_info[VFE_PIX_0].width =
- input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == TESTGEN)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.testgen_cfg.pixels_per_line;
if (input_cfg->d.pix_cfg.camif_cfg.subsample_cfg.
sof_counter_step > 0) {
vfe_dev->axi_data.src_info[VFE_PIX_0].
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index ab7d4e86dcac..ab981f762dd2 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1021,6 +1021,11 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif,
for (i = 0; i < params->num; i++) {
vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type %d\n", __func__,
+ vfe_intf);
+ return;
+ }
if (params->entries[i].intftype == PIX0 &&
params->stereo_enable &&
params->right_entries[i].csid < CSID_MAX &&
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index f95cc37f5c2c..9cb7d5299ef8 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -32,7 +32,6 @@
#include "cam_hw_ops.h"
#include <media/msmb_generic_buf_mgr.h>
-
static struct v4l2_device *msm_v4l2_dev;
static struct list_head ordered_sd_list;
@@ -149,7 +148,7 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2);
#define msm_queue_find(queue, type, member, func, data) ({\
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
typeof(node) __ret = NULL; \
msm_queue_find_func __f = (func); \
spin_lock_irqsave(&__q->lock, flags); \
@@ -279,22 +278,47 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
struct msm_session *session = NULL;
struct msm_stream *stream = NULL;
unsigned long flags;
+ int try_count = 0;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
+
if (!session)
return;
- stream = msm_queue_find(&session->stream_q, struct msm_stream,
- list, __msm_queue_find_stream, &stream_id);
- if (!stream)
- return;
- spin_lock_irqsave(&(session->stream_q.lock), flags);
- list_del_init(&stream->list);
- session->stream_q.len--;
- kfree(stream);
- stream = NULL;
- spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ while (1) {
+
+ if (try_count > 5) {
+ pr_err("%s : not able to delete stream %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ write_lock(&session->stream_rwlock);
+ try_count++;
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream) {
+ write_unlock(&session->stream_rwlock);
+ return;
+ }
+
+ if (msm_vb2_get_stream_state(stream) != 1) {
+ write_unlock(&session->stream_rwlock);
+ continue;
+ }
+
+ spin_lock_irqsave(&(session->stream_q.lock), flags);
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+ stream = NULL;
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ write_unlock(&session->stream_rwlock);
+ break;
+ }
+
}
EXPORT_SYMBOL(msm_delete_stream);
@@ -444,6 +468,7 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev)
mutex_init(&session->lock);
mutex_init(&session->lock_q);
mutex_init(&session->close_lock);
+ rwlock_init(&session->stream_rwlock);
if (gpu_limit) {
session->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0);
@@ -1048,17 +1073,25 @@ static struct v4l2_file_operations msm_fops = {
#endif
};
-struct msm_stream *msm_get_stream(unsigned int session_id,
- unsigned int stream_id)
+struct msm_session *msm_get_session(unsigned int session_id)
{
struct msm_session *session;
- struct msm_stream *stream;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
if (!session)
return ERR_PTR(-EINVAL);
+ return session;
+}
+EXPORT_SYMBOL(msm_get_session);
+
+
+struct msm_stream *msm_get_stream(struct msm_session *session,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+
stream = msm_queue_find(&session->stream_q, struct msm_stream,
list, __msm_queue_find_stream, &stream_id);
@@ -1115,6 +1148,34 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q)
}
EXPORT_SYMBOL(msm_get_stream_from_vb2q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+ unsigned long flags1;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&msm_session_q->lock, flags1);
+ list_for_each_entry(session, &(msm_session_q->list), list) {
+ spin_lock_irqsave(&(session->stream_q.lock), flags2);
+ list_for_each_entry(
+ stream, &(session->stream_q.list), list) {
+ if (stream->vb2_q == q) {
+ spin_unlock_irqrestore
+ (&(session->stream_q.lock), flags2);
+ spin_unlock_irqrestore
+ (&msm_session_q->lock, flags1);
+ return session;
+ }
+ }
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags2);
+ }
+ spin_unlock_irqrestore(&msm_session_q->lock, flags1);
+ return NULL;
+}
+EXPORT_SYMBOL(msm_get_session_from_vb2q);
+
+
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
struct msm_camera_private_ioctl_arg *k_ioctl,
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index 7474cb119147..dce47bc7249c 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -111,6 +111,7 @@ struct msm_session {
struct mutex lock;
struct mutex lock_q;
struct mutex close_lock;
+ rwlock_t stream_rwlock;
struct kgsl_pwr_limit *sysfs_pwr_limit;
};
@@ -129,11 +130,13 @@ int msm_create_stream(unsigned int session_id,
void msm_delete_stream(unsigned int session_id, unsigned int stream_id);
int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id);
void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id);
-struct msm_stream *msm_get_stream(unsigned int session_id,
+struct msm_session *msm_get_session(unsigned int session_id);
+struct msm_stream *msm_get_stream(struct msm_session *session,
unsigned int stream_id);
struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
unsigned int stream_id);
struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q);
struct msm_session *msm_session_find(unsigned int session_id);
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index c779ee46c19a..ba9b4df6bf22 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -44,17 +44,25 @@ static int msm_vb2_queue_setup(struct vb2_queue *q,
int msm_vb2_buf_init(struct vb2_buffer *vb)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2_buf;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s: Couldn't find stream\n", __func__);
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
}
msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
msm_vb2_buf->in_freeq = 0;
-
+ read_unlock(&session->stream_rwlock);
return 0;
}
@@ -62,6 +70,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -71,21 +80,30 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
spin_lock_irqsave(&stream->stream_lock, flags);
list_add_tail(&msm_vb2->list, &stream->queued_list);
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
}
static void msm_vb2_buf_finish(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct msm_vb2_buffer *msm_vb2_entry, *temp;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -96,9 +114,16 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -111,6 +136,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
}
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -118,12 +144,20 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
{
struct msm_vb2_buffer *msm_vb2, *temp;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vb2_v4l2_buf;
+ session = msm_get_session_from_vb2q(q);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(q);
if (!stream) {
pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -143,7 +177,27 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
+}
+
+int msm_vb2_get_stream_state(struct msm_stream *stream)
+{
+ struct msm_vb2_buffer *msm_vb2, *temp;
+ unsigned long flags;
+ int rc = 1;
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) {
+ if (msm_vb2->in_freeq != 0) {
+ rc = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ return rc;
}
+EXPORT_SYMBOL(msm_vb2_get_stream_state);
+
static struct vb2_ops msm_vb2_get_q_op = {
.queue_setup = msm_vb2_queue_setup,
@@ -198,14 +252,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return NULL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return NULL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -228,6 +291,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -235,13 +299,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
unsigned int stream_id, uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return NULL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return NULL;
+ }
spin_lock_irqsave(&stream->stream_lock, flags);
@@ -263,6 +337,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -270,14 +345,24 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -305,6 +390,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -315,12 +401,22 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -352,6 +448,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -359,15 +456,24 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
long rc = -EINVAL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return rc;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -393,6 +499,7 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
EXPORT_SYMBOL(msm_vb2_return_buf_by_idx);
@@ -402,11 +509,21 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
@@ -415,6 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
index 53511d5416d7..c65cb58128d9 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -67,5 +67,6 @@ struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void);
int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd);
long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index);
+int msm_vb2_get_stream_state(struct msm_stream *stream);
#endif /*_MSM_VB_H */
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index c9dfb52861bc..1d30a869d754 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -490,11 +490,13 @@ bool msm_smem_compare_buffers(void *clt, int fd, void *priv)
}
static int ion_cache_operations(struct smem_client *client,
- struct msm_smem *mem, enum smem_cache_ops cache_op)
+ struct msm_smem *mem, enum smem_cache_ops cache_op,
+ int size)
{
unsigned long ionflag = 0;
int rc = 0;
int msm_cache_ops = 0;
+ int op_size = 0;
if (!mem || !client) {
dprintk(VIDC_ERR, "Invalid params: %pK, %pK\n",
mem, client);
@@ -523,10 +525,15 @@ static int ion_cache_operations(struct smem_client *client,
rc = -EINVAL;
goto cache_op_failed;
}
+ if (size <= 0)
+ op_size = mem->size;
+ else
+ op_size = mem->size < size ? mem->size : size;
+
rc = msm_ion_do_cache_offset_op(client->clnt,
(struct ion_handle *)mem->smem_priv,
0, mem->offset,
- (unsigned long)mem->size, msm_cache_ops);
+ (unsigned long)op_size, msm_cache_ops);
if (rc) {
dprintk(VIDC_ERR,
"cache operation failed %d\n", rc);
@@ -538,7 +545,7 @@ cache_op_failed:
}
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
- enum smem_cache_ops cache_op)
+ enum smem_cache_ops cache_op, int size)
{
struct smem_client *client = clt;
int rc = 0;
@@ -549,7 +556,7 @@ int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
}
switch (client->mem_type) {
case SMEM_ION:
- rc = ion_cache_operations(client, mem, cache_op);
+ rc = ion_cache_operations(client, mem, cache_op, size);
if (rc)
dprintk(VIDC_ERR,
"Failed cache operations: %d\n", rc);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index a8dc1d010d62..c0271c757020 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -766,7 +766,6 @@ static int __init msm_vidc_init(void)
if (rc) {
dprintk(VIDC_ERR,
"Failed to register platform driver\n");
- msm_vidc_debugfs_deinit_drv();
debugfs_remove_recursive(vidc_driver->debugfs_root);
kfree(vidc_driver);
vidc_driver = NULL;
@@ -778,7 +777,6 @@ static int __init msm_vidc_init(void)
static void __exit msm_vidc_exit(void)
{
platform_driver_unregister(&msm_vidc_driver);
- msm_vidc_debugfs_deinit_drv();
debugfs_remove_recursive(vidc_driver->debugfs_root);
mutex_destroy(&vidc_driver->lock);
kfree(vidc_driver);
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 8ac84ece2c2a..0764a18a7993 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -1781,8 +1781,10 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_UNINIT)
- return -EINVAL;
+ inst->core->state == VIDC_CORE_UNINIT) {
+ rc = -EINVAL;
+ goto stream_start_failed;
+ }
hdev = inst->core->device;
dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
@@ -2233,6 +2235,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct hal_enable_picture enable_picture;
struct hal_enable hal_property;
enum hal_property property_id = 0;
+ enum hal_video_codec codec;
u32 property_val = 0;
void *pdata = NULL;
struct hfi_device *hdev;
@@ -2287,12 +2290,23 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE:
property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE;
if (ctrl->val ==
- V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON)
+ V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) {
enable_picture.picture_type = HAL_PICTURE_I;
- else
- enable_picture.picture_type = HAL_PICTURE_I |
- HAL_PICTURE_P | HAL_PICTURE_B |
- HAL_PICTURE_IDR;
+ } else {
+ codec = get_hal_codec(inst->fmts[OUTPUT_PORT].fourcc);
+ if (codec == HAL_VIDEO_CODEC_H264) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR;
+ } else if (codec == HAL_VIDEO_CODEC_HEVC) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR | HAL_PICTURE_CRA;
+ } else {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B;
+ }
+ }
pdata = &enable_picture;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO:
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index cdf91dd80ed3..a4cd4e34c7d0 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1908,8 +1908,10 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_UNINIT)
- return -EINVAL;
+ inst->core->state == VIDC_CORE_UNINIT) {
+ rc = -EINVAL;
+ goto stream_start_failed;
+ }
dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
q->type, inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f09c28fed6d2..3e4be4418f80 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -670,10 +670,11 @@ int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
}
int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
- struct buffer_info *binfo)
+ struct buffer_info *binfo, struct v4l2_buffer *b)
{
int i = 0;
int rc = 0;
+ int size = -1;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
@@ -686,23 +687,34 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
return -EINVAL;
}
- for (i = 0; i < binfo->num_planes; i++) {
- if (binfo->handle[i]) {
- struct msm_smem smem = *binfo->handle[i];
-
- smem.offset = (unsigned int)(binfo->buff_off[i]);
- smem.size = binfo->size[i];
- rc = msm_comm_smem_cache_operations(inst,
- &smem, SMEM_CACHE_INVALIDATE);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: Failed to clean caches: %d\n",
- __func__, rc);
- return -EINVAL;
- }
- } else
- dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n",
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ for (i = 0; i < binfo->num_planes; i++) {
+ if (binfo->handle[i]) {
+ struct msm_smem smem = *binfo->handle[i];
+
+ if (inst->session_type == MSM_VIDC_ENCODER &&
+ !i)
+ size = b->m.planes[i].bytesused;
+ else
+ size = -1;
+
+ smem.offset =
+ (unsigned int)(binfo->buff_off[i]);
+ smem.size = binfo->size[i];
+ rc = msm_comm_smem_cache_operations(inst,
+ &smem, SMEM_CACHE_INVALIDATE,
+ size);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: Failed to clean caches: %d\n",
+ __func__, rc);
+ return -EINVAL;
+ }
+ } else
+ dprintk(VIDC_DBG,
+ "%s: NULL handle for plane %d\n",
__func__, i);
+ }
}
return 0;
}
@@ -858,6 +870,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
int plane = 0;
int rc = 0;
int i;
+ int size = -1;
if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst))
return -EINVAL;
@@ -905,7 +918,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
V4L2_PIX_FMT_HEVC_HYBRID && binfo->handle[i] &&
b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
rc = msm_comm_smem_cache_operations(inst,
- binfo->handle[i], SMEM_CACHE_INVALIDATE);
+ binfo->handle[i], SMEM_CACHE_INVALIDATE, -1);
if (rc) {
dprintk(VIDC_ERR,
"Failed to inv caches: %d\n", rc);
@@ -915,8 +928,13 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
if (binfo->handle[i] &&
(b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+ if (inst->session_type == MSM_VIDC_DECODER && !i)
+ size = b->m.planes[i].bytesused;
+ else
+ size = -1;
rc = msm_comm_smem_cache_operations(inst,
- binfo->handle[i], SMEM_CACHE_CLEAN);
+ binfo->handle[i], SMEM_CACHE_CLEAN,
+ size);
if (rc) {
dprintk(VIDC_ERR,
"Failed to clean caches: %d\n", rc);
@@ -985,7 +1003,7 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
return -EINVAL;
}
- rc = output_buffer_cache_invalidate(inst, buffer_info);
+ rc = output_buffer_cache_invalidate(inst, buffer_info, b);
if (rc)
return rc;
@@ -1347,8 +1365,6 @@ static void cleanup_instance(struct msm_vidc_inst *inst)
"Failed to release output buffers\n");
}
- debugfs_remove_recursive(inst->debugfs_root);
-
mutex_lock(&inst->pending_getpropq.lock);
if (!list_empty(&inst->pending_getpropq.list)) {
dprintk(VIDC_ERR,
@@ -1390,6 +1406,8 @@ int msm_vidc_destroy(struct msm_vidc_inst *inst)
mutex_destroy(&inst->bufq[OUTPUT_PORT].lock);
mutex_destroy(&inst->lock);
+ msm_vidc_debugfs_deinit_inst(inst);
+
pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n",
VIDC_MSG_PRIO2STRING(VIDC_INFO), inst);
kfree(inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7b28e80979f2..c9a871bae25b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -3174,7 +3174,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst,
goto err_no_mem;
}
rc = msm_comm_smem_cache_operations(inst,
- handle, SMEM_CACHE_CLEAN);
+ handle, SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache may cause undefined behavior\n");
@@ -3265,7 +3265,7 @@ static int set_internal_buf_on_fw(struct msm_vidc_inst *inst,
hdev = inst->core->device;
rc = msm_comm_smem_cache_operations(inst,
- handle, SMEM_CACHE_CLEAN);
+ handle, SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN,
"Failed to clean cache. Undefined behavior\n");
@@ -4524,10 +4524,15 @@ static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
struct vb2_buffer *vb = container_of(ptr,
struct vb2_buffer, queued_entry);
- vb->planes[0].bytesused = 0;
- vb->planes[0].data_offset = 0;
-
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ if (vb->state == VB2_BUF_STATE_ACTIVE) {
+ vb->planes[0].bytesused = 0;
+ vb->planes[0].data_offset = 0;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ } else {
+ dprintk(VIDC_WARN,
+ "%s VB is in state %d not in ACTIVE state\n"
+ , __func__, vb->state);
+ }
}
mutex_unlock(&inst->bufq[port].lock);
}
@@ -5154,14 +5159,16 @@ void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem)
}
int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
- struct msm_smem *mem, enum smem_cache_ops cache_ops)
+ struct msm_smem *mem, enum smem_cache_ops cache_ops,
+ int size)
{
if (!inst || !mem) {
dprintk(VIDC_ERR,
"%s: invalid params: %pK %pK\n", __func__, inst, mem);
return -EINVAL;
}
- return msm_smem_cache_operations(inst->mem_client, mem, cache_ops);
+ return msm_smem_cache_operations(inst->mem_client, mem,
+ cache_ops, size);
}
struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index eac7f658eb31..01c1890bf5e1 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -76,7 +76,7 @@ struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type, int map_kernel);
void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem);
int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
- struct msm_smem *mem, enum smem_cache_ops cache_ops);
+ struct msm_smem *mem, enum smem_cache_ops cache_ops, int size);
struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
int fd, u32 offset, enum hal_buffer buffer_type);
enum hal_video_codec get_hal_codec(int fourcc);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 885e61f8bf01..5c13b6fef3ec 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -38,44 +38,31 @@ bool msm_vidc_debug_timeout = false;
#define MAX_DBG_BUF_SIZE 4096
-struct debug_buffer {
- struct mutex lock;
- char ptr[MAX_DBG_BUF_SIZE];
- char *curr;
- u32 filled_size;
-};
-
-static struct debug_buffer dbg_buf;
-
-#define INIT_DBG_BUF(__buf) ({ \
- __buf.curr = __buf.ptr;\
- __buf.filled_size = 0; \
-})
-
#define DYNAMIC_BUF_OWNER(__binfo) ({ \
atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\
})
+struct core_inst_pair {
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+};
+
static int core_info_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
-static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+static u32 write_str(char *buffer,
+ size_t size, const char *fmt, ...)
{
va_list args;
- u32 size;
-
- char *curr = buffer->curr;
- char *end = buffer->ptr + MAX_DBG_BUF_SIZE;
+ u32 len;
va_start(args, fmt);
- size = vscnprintf(curr, end - curr, fmt, args);
+ len = vscnprintf(buffer, size, fmt, args);
va_end(args);
- buffer->curr += size;
- buffer->filled_size += size;
- return size;
+ return len;
}
static ssize_t core_info_read(struct file *file, char __user *buf,
@@ -84,6 +71,7 @@ static ssize_t core_info_read(struct file *file, char __user *buf,
struct msm_vidc_core *core = file->private_data;
struct hfi_device *hdev;
struct hal_fw_info fw_info = { {0} };
+ char *dbuf, *cur, *end;
int i = 0, rc = 0;
ssize_t len = 0;
@@ -91,36 +79,46 @@ static ssize_t core_info_read(struct file *file, char __user *buf,
dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core);
return 0;
}
+
+ dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ return -ENOMEM;
+ }
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
hdev = core->device;
- mutex_lock(&dbg_buf.lock);
- INIT_DBG_BUF(dbg_buf);
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "CORE %d: %pK\n", core->id, core);
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "Core state: %d\n", core->state);
+ cur += write_str(cur, end - cur, "===============================\n");
+ cur += write_str(cur, end - cur, "CORE %d: %pK\n", core->id, core);
+ cur += write_str(cur, end - cur, "===============================\n");
+ cur += write_str(cur, end - cur, "Core state: %d\n", core->state);
rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info);
if (rc) {
dprintk(VIDC_WARN, "Failed to read FW info\n");
goto err_fw_info;
}
- write_str(&dbg_buf, "FW version : %s\n", &fw_info.version);
- write_str(&dbg_buf, "base addr: 0x%x\n", fw_info.base_addr);
- write_str(&dbg_buf, "register_base: 0x%x\n", fw_info.register_base);
- write_str(&dbg_buf, "register_size: %u\n", fw_info.register_size);
- write_str(&dbg_buf, "irq: %u\n", fw_info.irq);
+ cur += write_str(cur, end - cur,
+ "FW version : %s\n", &fw_info.version);
+ cur += write_str(cur, end - cur,
+ "base addr: 0x%x\n", fw_info.base_addr);
+ cur += write_str(cur, end - cur,
+ "register_base: 0x%x\n", fw_info.register_base);
+ cur += write_str(cur, end - cur,
+ "register_size: %u\n", fw_info.register_size);
+ cur += write_str(cur, end - cur, "irq: %u\n", fw_info.irq);
err_fw_info:
for (i = SYS_MSG_START; i < SYS_MSG_END; i++) {
- write_str(&dbg_buf, "completions[%d]: %s\n", i,
+ cur += write_str(cur, end - cur, "completions[%d]: %s\n", i,
completion_done(&core->completions[SYS_MSG_INDEX(i)]) ?
"pending" : "done");
}
len = simple_read_from_buffer(buf, count, ppos,
- dbg_buf.ptr, dbg_buf.filled_size);
+ dbuf, cur - dbuf);
- mutex_unlock(&dbg_buf.lock);
+ kfree(dbuf);
return len;
}
@@ -177,7 +175,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void)
bool ok = false;
struct dentry *dir = NULL;
- mutex_init(&dbg_buf.lock);
dir = debugfs_create_dir("msm_vidc", NULL);
if (IS_ERR_OR_NULL(dir)) {
dir = NULL;
@@ -263,12 +260,15 @@ failed_create_dir:
static int inst_info_open(struct inode *inode, struct file *file)
{
+ dprintk(VIDC_INFO, "Open inode ptr: %pK\n", inode->i_private);
file->private_data = inode->i_private;
return 0;
}
-static int publish_unreleased_reference(struct msm_vidc_inst *inst)
+static int publish_unreleased_reference(struct msm_vidc_inst *inst,
+ char **dbuf, char *end)
{
+ char *cur = *dbuf;
struct buffer_info *temp = NULL;
if (!inst) {
@@ -277,130 +277,228 @@ static int publish_unreleased_reference(struct msm_vidc_inst *inst)
}
if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) {
- write_str(&dbg_buf, "Pending buffer references:\n");
+ cur += write_str(cur, end - cur, "Pending buffer references\n");
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (temp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
!temp->inactive && atomic_read(&temp->ref_count)) {
- write_str(&dbg_buf,
- "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n",
- temp->device_addr[0],
- temp->fd[0],
- atomic_read(&temp->ref_count),
- DYNAMIC_BUF_OWNER(temp));
+ cur += write_str(cur, end - cur,
+ "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n",
+ temp->device_addr[0],
+ temp->fd[0],
+ atomic_read(&temp->ref_count),
+ DYNAMIC_BUF_OWNER(temp));
}
}
mutex_unlock(&inst->registeredbufs.lock);
}
+
+ *dbuf = cur;
return 0;
}
+static void put_inst_helper(struct kref *kref)
+{
+ struct msm_vidc_inst *inst = container_of(kref,
+ struct msm_vidc_inst, kref);
+
+ msm_vidc_destroy(inst);
+}
+
static ssize_t inst_info_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct msm_vidc_inst *inst = file->private_data;
+ struct core_inst_pair *idata = file->private_data;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst, *temp = NULL;
+ char *dbuf, *cur, *end;
int i, j;
ssize_t len = 0;
+ if (!idata || !idata->core || !idata->inst) {
+ dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
+ return 0;
+ }
+
+ core = idata->core;
+ inst = idata->inst;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(temp, &core->instances, list) {
+ if (temp == inst)
+ break;
+ }
+ inst = ((temp == inst) && kref_get_unless_zero(&inst->kref)) ?
+ inst : NULL;
+ mutex_unlock(&core->lock);
+
if (!inst) {
- dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst);
+ dprintk(VIDC_ERR, "%s: Instance has become obsolete", __func__);
return 0;
}
- mutex_lock(&dbg_buf.lock);
- INIT_DBG_BUF(dbg_buf);
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "INSTANCE: %pK (%s)\n", inst,
+ dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ len = -ENOMEM;
+ goto failed_alloc;
+ }
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
+
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst,
inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder");
- write_str(&dbg_buf, "===============================\n");
- write_str(&dbg_buf, "core: %pK\n", inst->core);
- write_str(&dbg_buf, "height: %d\n", inst->prop.height[CAPTURE_PORT]);
- write_str(&dbg_buf, "width: %d\n", inst->prop.width[CAPTURE_PORT]);
- write_str(&dbg_buf, "fps: %d\n", inst->prop.fps);
- write_str(&dbg_buf, "state: %d\n", inst->state);
- write_str(&dbg_buf, "secure: %d\n", !!(inst->flags & VIDC_SECURE));
- write_str(&dbg_buf, "-----------Formats-------------\n");
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "core: %pK\n", inst->core);
+ cur += write_str(cur, end - cur, "height: %d\n",
+ inst->prop.height[CAPTURE_PORT]);
+ cur += write_str(cur, end - cur, "width: %d\n",
+ inst->prop.width[CAPTURE_PORT]);
+ cur += write_str(cur, end - cur, "fps: %d\n", inst->prop.fps);
+ cur += write_str(cur, end - cur, "state: %d\n", inst->state);
+ cur += write_str(cur, end - cur, "secure: %d\n",
+ !!(inst->flags & VIDC_SECURE));
+ cur += write_str(cur, end - cur, "-----------Formats-------------\n");
for (i = 0; i < MAX_PORT_NUM; i++) {
- write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ?
+ cur += write_str(cur, end - cur, "capability: %s\n",
+ i == OUTPUT_PORT ? "Output" : "Capture");
+ cur += write_str(cur, end - cur, "name : %s\n",
+ inst->fmts[i].name);
+ cur += write_str(cur, end - cur, "planes : %d\n",
+ inst->prop.num_planes[i]);
+ cur += write_str(cur, end - cur,
+ "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
"Output" : "Capture");
- write_str(&dbg_buf, "name : %s\n", inst->fmts[i].name);
- write_str(&dbg_buf, "planes : %d\n", inst->prop.num_planes[i]);
- write_str(
- &dbg_buf, "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
- "Output" : "Capture");
switch (inst->buffer_mode_set[i]) {
case HAL_BUFFER_MODE_STATIC:
- write_str(&dbg_buf, "buffer mode : %s\n", "static");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "static");
break;
case HAL_BUFFER_MODE_RING:
- write_str(&dbg_buf, "buffer mode : %s\n", "ring");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "ring");
break;
case HAL_BUFFER_MODE_DYNAMIC:
- write_str(&dbg_buf, "buffer mode : %s\n", "dynamic");
+ cur += write_str(cur, end - cur,
+ "buffer mode : %s\n", "dynamic");
break;
default:
- write_str(&dbg_buf, "buffer mode : unsupported\n");
+ cur += write_str(cur, end - cur,
+ "buffer mode : unsupported\n");
}
- write_str(&dbg_buf, "count: %u\n",
+ cur += write_str(cur, end - cur, "count: %u\n",
inst->bufq[i].vb2_bufq.num_buffers);
for (j = 0; j < inst->prop.num_planes[i]; j++)
- write_str(&dbg_buf, "size for plane %d: %u\n", j,
+ cur += write_str(cur, end - cur,
+ "size for plane %d: %u\n", j,
inst->bufq[i].vb2_bufq.plane_sizes[j]);
if (i < MAX_PORT_NUM - 1)
- write_str(&dbg_buf, "\n");
+ cur += write_str(cur, end - cur, "\n");
}
- write_str(&dbg_buf, "-------------------------------\n");
+ cur += write_str(cur, end - cur, "-------------------------------\n");
for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) {
- write_str(&dbg_buf, "completions[%d]: %s\n", i,
+ cur += write_str(cur, end - cur, "completions[%d]: %s\n", i,
completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ?
"pending" : "done");
}
- write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb);
- write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd);
- write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb);
- write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd);
-
- publish_unreleased_reference(inst);
+ cur += write_str(cur, end - cur, "ETB Count: %d\n", inst->count.etb);
+ cur += write_str(cur, end - cur, "EBD Count: %d\n", inst->count.ebd);
+ cur += write_str(cur, end - cur, "FTB Count: %d\n", inst->count.ftb);
+ cur += write_str(cur, end - cur, "FBD Count: %d\n", inst->count.fbd);
+ publish_unreleased_reference(inst, &cur, end);
len = simple_read_from_buffer(buf, count, ppos,
- dbg_buf.ptr, dbg_buf.filled_size);
- mutex_unlock(&dbg_buf.lock);
+ dbuf, cur - dbuf);
+
+ kfree(dbuf);
+failed_alloc:
+ kref_put(&inst->kref, put_inst_helper);
return len;
}
+static int inst_info_release(struct inode *inode, struct file *file)
+{
+ dprintk(VIDC_INFO, "Release inode ptr: %pK\n", inode->i_private);
+ file->private_data = NULL;
+ return 0;
+}
+
static const struct file_operations inst_info_fops = {
.open = inst_info_open,
.read = inst_info_read,
+ .release = inst_info_release,
};
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent)
{
- struct dentry *dir = NULL;
+ struct dentry *dir = NULL, *info = NULL;
char debugfs_name[MAX_DEBUGFS_NAME];
+ struct core_inst_pair *idata = NULL;
+
if (!inst) {
dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst);
- goto failed_create_dir;
+ goto exit;
}
snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+
+ idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL);
+ if (!idata) {
+ dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__);
+ goto exit;
+ }
+
+ idata->core = inst->core;
+ idata->inst = inst;
+
dir = debugfs_create_dir(debugfs_name, parent);
if (!dir) {
dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
goto failed_create_dir;
}
- if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) {
+
+ info = debugfs_create_file("info", S_IRUGO, dir,
+ idata, &inst_info_fops);
+ if (!info) {
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
- goto failed_create_dir;
+ goto failed_create_file;
}
+
+ dir->d_inode->i_private = info->d_inode->i_private;
inst->debug.pdata[FRAME_PROCESSING].sampling = true;
+ return dir;
+
+failed_create_file:
+ debugfs_remove_recursive(dir);
+ dir = NULL;
failed_create_dir:
+ kfree(idata);
+exit:
return dir;
}
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst)
+{
+ struct dentry *dentry = NULL;
+
+ if (!inst || !inst->debugfs_root)
+ return;
+
+ dentry = inst->debugfs_root;
+ if (dentry->d_inode) {
+ dprintk(VIDC_INFO, "Destroy %pK\n", dentry->d_inode->i_private);
+ kfree(dentry->d_inode->i_private);
+ dentry->d_inode->i_private = NULL;
+ }
+ debugfs_remove_recursive(dentry);
+ inst->debugfs_root = NULL;
+}
+
void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
enum msm_vidc_debugfs_event e)
{
@@ -450,8 +548,3 @@ void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
}
}
-void msm_vidc_debugfs_deinit_drv(void)
-{
- mutex_destroy(&dbg_buf.lock);
-}
-
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index 853ce4b89f2b..95b2a6d60936 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -124,9 +124,9 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
struct dentry *parent);
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst);
void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
enum msm_vidc_debugfs_event e);
-void msm_vidc_debugfs_deinit_drv(void);
static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
char *b)
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 690a61f4824f..4cb900bbca10 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -355,7 +355,7 @@ struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list,
int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo);
int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo);
int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
- struct buffer_info *binfo);
+ struct buffer_info *binfo, struct v4l2_buffer *b);
int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
struct buffer_info *binfo);
int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
@@ -369,7 +369,7 @@ struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
void msm_smem_free(void *clt, struct msm_smem *mem);
void msm_smem_delete_client(void *clt);
int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
- enum smem_cache_ops);
+ enum smem_cache_ops, int size);
struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
enum hal_buffer buffer_type);
struct context_bank_info *msm_smem_get_context_bank(void *clt,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index bc72c4a56c91..52b56f615da9 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -582,7 +582,7 @@ static int __smem_alloc(struct venus_hfi_device *dev,
dprintk(VIDC_DBG, "__smem_alloc: ptr = %pK, size = %d\n",
alloc->kvaddr, size);
rc = msm_smem_cache_operations(dev->hal_client, alloc,
- SMEM_CACHE_CLEAN);
+ SMEM_CACHE_CLEAN, -1);
if (rc) {
dprintk(VIDC_WARN, "Failed to clean cache\n");
dprintk(VIDC_WARN, "This may result in undefined behavior\n");
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 33ec0c15efa6..c6f2dbfe573d 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -12,10 +12,13 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -30,6 +33,8 @@
#include <linux/errno.h>
#include <linux/hdcp_qseecom.h>
#include <linux/kthread.h>
+#include <linux/of.h>
+#include <video/msm_hdmi_hdcp_mgr.h>
#include "qseecom_kernel.h"
@@ -542,6 +547,24 @@ struct hdcp_lib_message_map {
const char *msg_name;
};
+struct msm_hdcp_mgr {
+ struct platform_device *pdev;
+ dev_t dev_num;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ u32 tp_msgid;
+ void *client_ctx;
+ struct hdcp_lib_handle *handle;
+};
+
+#define CLASS_NAME "hdcp"
+#define DRIVER_NAME "msm_hdcp"
+
+static struct msm_hdcp_mgr *hdcp_drv_mgr;
+static struct hdcp_lib_handle *drv_client_handle;
+
static void hdcp_lib_clean(struct hdcp_lib_handle *handle);
static void hdcp_lib_init(struct hdcp_lib_handle *handle);
static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle);
@@ -2288,7 +2311,7 @@ int hdcp1_set_enc(bool enable)
}
if (hdcp1_enc_enabled == enable) {
- pr_debug("already %s\n", enable ? "enabled" : "disabled");
+ pr_info("already %s\n", enable ? "enabled" : "disabled");
goto end;
}
@@ -2318,7 +2341,7 @@ int hdcp1_set_enc(bool enable)
}
hdcp1_enc_enabled = enable;
- pr_debug("%s success\n", enable ? "enable" : "disable");
+ pr_info("%s success\n", enable ? "enable" : "disable");
end:
mutex_unlock(&hdcp1_ta_cmd_lock);
return rc;
@@ -2393,7 +2416,13 @@ int hdcp_library_register(struct hdcp_register_data *data)
}
*data->hdcp_ctx = handle;
+ /* Cache the client ctx to be used later
+ * HDCP driver probe happens earlier than
+ * SDE driver probe hence caching it to
+ * be used later.
+ */
+ drv_client_handle = handle;
handle->thread = kthread_run(kthread_worker_fn,
&handle->worker, "hdcp_tz_lib");
@@ -2433,3 +2462,273 @@ void hdcp_library_deregister(void *phdcpcontext)
kzfree(handle);
}
EXPORT_SYMBOL(hdcp_library_deregister);
+
+void hdcp1_notify_topology(void)
+{
+ char *envp[4];
+ char *a;
+ char *b;
+
+ a = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!a)
+ return;
+
+ b = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!b) {
+ kfree(a);
+ return;
+ }
+
+ envp[0] = "HDCP_MGR_EVENT=MSG_READY";
+ envp[1] = a;
+ envp[2] = b;
+ envp[3] = NULL;
+
+ snprintf(envp[1], 16, "%d", (int)DOWN_CHECK_TOPOLOGY);
+ snprintf(envp[2], 16, "%d", (int)HDCP_V1_TX);
+
+ kobject_uevent_env(&hdcp_drv_mgr->device->kobj, KOBJ_CHANGE, envp);
+ kfree(a);
+ kfree(b);
+}
+
+static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
+struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ if (!hdcp_drv_mgr) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ switch (hdcp_drv_mgr->tp_msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ buf[MSG_ID_IDX] = hdcp_drv_mgr->tp_msgid;
+ buf[RET_CODE_IDX] = HDCP_AUTHED;
+ ret = HEADER_LEN;
+
+ memcpy(buf + HEADER_LEN, &hdcp_drv_mgr->cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+
+ ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
+
+ /* clear the flag once data is read back to user space*/
+ hdcp_drv_mgr->tp_msgid = -1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdcp_1x_sysfs_rda_tp*/
+
+static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int msgid = 0;
+ ssize_t ret = count;
+
+ if (!hdcp_drv_mgr || !buf) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ msgid = buf[0];
+
+ switch (msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ hdcp_drv_mgr->tp_msgid = msgid;
+ break;
+ /* more cases added here */
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdmi_tx_sysfs_wta_hpd */
+
+static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ int min_enc_lvl;
+ struct hdcp_lib_handle *handle;
+ ssize_t ret = count;
+
+ handle = hdcp_drv_mgr->handle;
+
+ rc = kstrtoint(buf, 10, &min_enc_lvl);
+ if (rc) {
+ pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
+ return -EINVAL;
+ }
+
+ if (handle && handle->client_ops->notify_lvl_change) {
+ handle->client_ops->notify_lvl_change(handle->client_ctx,
+ min_enc_lvl);
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(tp, S_IRUGO | S_IWUSR, msm_hdcp_1x_sysfs_rda_tp,
+msm_hdcp_1x_sysfs_wta_tp);
+
+static DEVICE_ATTR(min_level_change, S_IWUSR, NULL,
+hdmi_hdcp2p2_sysfs_wta_min_level_change);
+
+void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp)
+{
+ memcpy((void *)&hdcp_drv_mgr->cached_tp,
+ hdcp1_cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+}
+
+static struct attribute *msm_hdcp_fs_attrs[] = {
+ &dev_attr_tp.attr,
+ &dev_attr_min_level_change.attr,
+ NULL
+};
+
+static struct attribute_group msm_hdcp_fs_attr_group = {
+ .attrs = msm_hdcp_fs_attrs
+};
+
+static int msm_hdcp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int msm_hdcp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations msm_hdcp_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_hdcp_open,
+ .release = msm_hdcp_close,
+};
+
+static const struct of_device_id msm_hdcp_dt_match[] = {
+ { .compatible = "qcom,msm-hdcp",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
+
+static int msm_hdcp_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ hdcp_drv_mgr = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp_mgr),
+ GFP_KERNEL);
+ if (!hdcp_drv_mgr)
+ return -ENOMEM;
+
+ hdcp_drv_mgr->pdev = pdev;
+
+ platform_set_drvdata(pdev, hdcp_drv_mgr);
+
+ ret = alloc_chrdev_region(&hdcp_drv_mgr->dev_num, 0, 1, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed ret = %d\n", ret);
+ goto error_get_dev_num;
+ }
+
+ hdcp_drv_mgr->class = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(hdcp_drv_mgr->class)) {
+ ret = PTR_ERR(hdcp_drv_mgr->class);
+ pr_err("couldn't create class rc = %d\n", ret);
+ goto error_class_create;
+ }
+
+ hdcp_drv_mgr->device = device_create(hdcp_drv_mgr->class, NULL,
+ hdcp_drv_mgr->dev_num, NULL, DRIVER_NAME);
+ if (IS_ERR(hdcp_drv_mgr->device)) {
+ ret = PTR_ERR(hdcp_drv_mgr->device);
+ pr_err("device_create failed %d\n", ret);
+ goto error_class_device_create;
+ }
+
+ cdev_init(&hdcp_drv_mgr->cdev, &msm_hdcp_fops);
+ ret = cdev_add(&hdcp_drv_mgr->cdev,
+ MKDEV(MAJOR(hdcp_drv_mgr->dev_num), 0), 1);
+ if (ret < 0) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto error_cdev_add;
+ }
+
+ ret = sysfs_create_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ if (ret)
+ pr_err("unable to register rotator sysfs nodes\n");
+
+ /* Store the handle in the hdcp drv mgr
+ * to be used for the sysfs notifications
+ */
+ hdcp_drv_mgr->handle = drv_client_handle;
+
+ return 0;
+error_cdev_add:
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+error_class_device_create:
+ class_destroy(hdcp_drv_mgr->class);
+error_class_create:
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+error_get_dev_num:
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return ret;
+}
+
+static int msm_hdcp_remove(struct platform_device *pdev)
+{
+ struct msm_hdcp_mgr *mgr;
+
+ mgr = (struct msm_hdcp_mgr *)platform_get_drvdata(pdev);
+ if (!mgr)
+ return -ENODEV;
+
+ sysfs_remove_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ cdev_del(&hdcp_drv_mgr->cdev);
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+ class_destroy(hdcp_drv_mgr->class);
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return 0;
+}
+
+static struct platform_driver msm_hdcp_driver = {
+ .probe = msm_hdcp_probe,
+ .remove = msm_hdcp_remove,
+ .driver = {
+ .name = "msm_hdcp",
+ .of_match_table = msm_hdcp_dt_match,
+ .pm = NULL,
+ }
+};
+
+static int __init msm_hdcp_init(void)
+{
+ return platform_driver_register(&msm_hdcp_driver);
+}
+
+static void __exit msm_hdcp_exit(void)
+{
+ return platform_driver_unregister(&msm_hdcp_driver);
+}
+
+module_init(msm_hdcp_init);
+module_exit(msm_hdcp_exit);
+
+MODULE_DESCRIPTION("MSM HDCP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 5419bd1655c1..b292ea70fb40 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -26,11 +26,14 @@
#include <linux/debugfs.h>
#include <linux/msm_audio_ion.h>
#include <linux/compat.h>
+#include <linux/mutex.h>
#include "audio_utils_aio.h"
#ifdef CONFIG_USE_DEV_CTRL_VOLUME
#include <linux/qdsp6v2/audio_dev_ctl.h>
#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+static DEFINE_MUTEX(lock);
#ifdef CONFIG_DEBUG_FS
+
int audio_aio_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -43,29 +46,37 @@ ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
const int debug_bufmax = 4096;
static char buffer[4096];
int n = 0;
- struct q6audio_aio *audio = file->private_data;
+ struct q6audio_aio *audio;
- mutex_lock(&audio->lock);
- n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "enabled %d\n", audio->enabled);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "stopped %d\n", audio->stopped);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "feedback %d\n", audio->feedback);
- mutex_unlock(&audio->lock);
- /* Following variables are only useful for debugging when
- * when playback halts unexpectedly. Thus, no mutual exclusion
- * enforced
- */
- n += scnprintf(buffer + n, debug_bufmax - n,
- "wflush %d\n", audio->wflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "rflush %d\n", audio->rflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "inqueue empty %d\n", list_empty(&audio->in_queue));
- n += scnprintf(buffer + n, debug_bufmax - n,
- "outqueue empty %d\n", list_empty(&audio->out_queue));
+ mutex_lock(&lock);
+ if (file->private_data != NULL) {
+ audio = file->private_data;
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n",
+ audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "feedback %d\n", audio->feedback);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "inqueue empty %d\n",
+ list_empty(&audio->in_queue));
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "outqueue empty %d\n",
+ list_empty(&audio->out_queue));
+ }
+ mutex_unlock(&lock);
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
@@ -573,6 +584,7 @@ int audio_aio_release(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = file->private_data;
pr_debug("%s[%pK]\n", __func__, audio);
+ mutex_lock(&lock);
mutex_lock(&audio->lock);
mutex_lock(&audio->read_lock);
mutex_lock(&audio->write_lock);
@@ -616,6 +628,8 @@ int audio_aio_release(struct inode *inode, struct file *file)
#endif
kfree(audio->codec_cfg);
kfree(audio);
+ file->private_data = NULL;
+ mutex_unlock(&lock);
return 0;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 7d09f22d3bc6..500185546599 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2908,7 +2908,11 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data,
}
if (req.cmd_id == QSEOS_RPMB_CHECK_PROV_STATUS_COMMAND) {
pr_warn("RPMB key status is 0x%x\n", resp.result);
- *(uint32_t *)req.resp_buf = resp.result;
+ if (put_user(resp.result,
+ (uint32_t __user *)req.resp_buf)) {
+ ret = -EINVAL;
+ goto exit;
+ }
ret = 0;
}
break;
@@ -6498,11 +6502,16 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
void *cmd_buf = NULL;
size_t cmd_len;
struct sglist_info *table = data->sglistinfo_ptr;
+ void *req_ptr = NULL;
+ void *resp_ptr = NULL;
ret = __qseecom_qteec_validate_msg(data, req);
if (ret)
return ret;
+ req_ptr = req->req_ptr;
+ resp_ptr = req->resp_ptr;
+
/* find app_id & img_name from list */
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
@@ -6520,6 +6529,11 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
return -ENOENT;
}
+ req->req_ptr = (void *)__qseecom_uvirt_to_kvirt(data,
+ (uintptr_t)req->req_ptr);
+ req->resp_ptr = (void *)__qseecom_uvirt_to_kvirt(data,
+ (uintptr_t)req->resp_ptr);
+
if ((cmd_id == QSEOS_TEE_OPEN_SESSION) ||
(cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) {
ret = __qseecom_update_qteec_req_buf(
@@ -6531,10 +6545,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
if (qseecom.qsee_version < QSEE_VERSION_40) {
ireq.app_id = data->client.app_id;
ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->req_ptr);
+ (uintptr_t)req_ptr);
ireq.req_len = req->req_len;
ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->resp_ptr);
+ (uintptr_t)resp_ptr);
ireq.resp_len = req->resp_len;
ireq.sglistinfo_ptr = (uint32_t)virt_to_phys(table);
ireq.sglistinfo_len = SGLISTINFO_TABLE_SIZE;
@@ -6545,10 +6559,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
} else {
ireq_64bit.app_id = data->client.app_id;
ireq_64bit.req_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->req_ptr);
+ (uintptr_t)req_ptr);
ireq_64bit.req_len = req->req_len;
ireq_64bit.resp_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data,
- (uintptr_t)req->resp_ptr);
+ (uintptr_t)resp_ptr);
ireq_64bit.resp_len = req->resp_len;
if ((data->client.app_arch == ELFCLASS32) &&
((ireq_64bit.req_ptr >=
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 9ca73a2b86db..ae54302be8fd 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -327,6 +327,7 @@ void mmc_retune_enable(struct mmc_host *host)
mod_timer(&host->retune_timer,
jiffies + host->retune_period * HZ);
}
+EXPORT_SYMBOL(mmc_retune_enable);
void mmc_retune_disable(struct mmc_host *host)
{
@@ -335,6 +336,7 @@ void mmc_retune_disable(struct mmc_host *host)
host->retune_now = 0;
host->need_retune = 0;
}
+EXPORT_SYMBOL(mmc_retune_disable);
void mmc_retune_timer_stop(struct mmc_host *host)
{
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5b4d5d74fe55..5033107f6e26 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1283,6 +1283,11 @@ static int _mmc_sd_resume(struct mmc_host *host)
#else
err = mmc_sd_init_card(host, host->card->ocr, host->card);
#endif
+ if (err) {
+ pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
mmc_card_clr_suspended(host->card);
if (host->card->sdr104_blocked)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 1eeab7db9722..feb9cbe1f068 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1823,7 +1823,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
}
pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
- if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))
+ if (gpio_is_valid(pdata->status_gpio) && !(flags & OF_GPIO_ACTIVE_LOW))
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
of_property_read_u32(np, "qcom,bus-width", &bus_width);
@@ -1958,7 +1958,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
sdhci_msm_pm_qos_parse(dev, pdata);
if (of_get_property(np, "qcom,core_3_0v_support", NULL))
- pdata->core_3_0v_support = true;
+ msm_host->core_3_0v_support = true;
pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa");
@@ -2639,7 +2639,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
*/
mb();
- if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT))
+ if ((io_level & REQ_IO_HIGH) &&
+ (msm_host->caps_0 & CORE_3_0V_SUPPORT) &&
+ !msm_host->core_3_0v_support)
writel_relaxed((readl_relaxed(host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC) &
~CORE_IO_PAD_PWR_SWITCH), host->ioaddr +
@@ -4133,7 +4135,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
msm_host->use_14lpp_dll = true;
/* Fake 3.0V support for SDIO devices which requires such voltage */
- if (msm_host->pdata->core_3_0v_support) {
+ if (msm_host->core_3_0v_support) {
caps |= CORE_3_0V_SUPPORT;
writel_relaxed((readl_relaxed(host->ioaddr +
SDHCI_CAPABILITIES) | caps), host->ioaddr +
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 92f61708001e..b63b4df3ded3 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -152,7 +152,6 @@ struct sdhci_msm_pltfm_data {
u32 ice_clk_max;
u32 ice_clk_min;
struct sdhci_msm_pm_qos_data pm_qos_data;
- bool core_3_0v_support;
bool sdr104_wa;
};
@@ -226,6 +225,7 @@ struct sdhci_msm_host {
bool tuning_in_progress;
bool mci_removed;
const struct sdhci_msm_offset *offset;
+ bool core_3_0v_support;
};
extern char *saved_command_line;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 40a34c283955..ddb9947ce298 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2418,7 +2418,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->ops->platform_execute_tuning) {
spin_unlock_irqrestore(&host->lock, flags);
+ /*
+ * Make sure re-tuning won't get triggered for the CRC errors
+ * occurred while executing tuning
+ */
+ mmc_retune_disable(mmc);
err = host->ops->platform_execute_tuning(host, opcode);
+ mmc_retune_enable(mmc);
sdhci_runtime_pm_put(host);
return err;
}
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 9acaffa51516..95412139e7f6 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -2375,6 +2375,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
init_completion(&ar->vdev_setup_done);
init_completion(&ar->thermal.wmi_sync);
init_completion(&ar->bss_survey_done);
+ init_completion(&ar->peer_delete_done);
INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 01d5ecc4f6b8..fa2e226ec085 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -957,7 +957,9 @@ struct ath10k {
struct fw_flag *fw_flags;
/* set for bmi chip sets */
+ struct completion peer_delete_done;
bool is_bmi;
+ enum ieee80211_sta_state sta_state;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b9d08b4b4cc5..313eb43cbf2c 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -782,6 +782,7 @@ static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
int ret;
+ unsigned long time_left;
lockdep_assert_held(&ar->conf_mutex);
@@ -793,6 +794,16 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
if (ret)
return ret;
+ if (QCA_REV_WCN3990(ar)) {
+ time_left = wait_for_completion_timeout(&ar->peer_delete_done,
+ 5 * HZ);
+
+ if (time_left == 0) {
+ ath10k_warn(ar, "Timeout in receiving peer delete response\n");
+ return -ETIMEDOUT;
+ }
+ }
+
ar->num_peers--;
return 0;
@@ -5924,6 +5935,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
new_state == IEEE80211_STA_NOTEXIST))
cancel_work_sync(&arsta->update_wk);
+ if (vif->type == NL80211_IFTYPE_STATION && new_state > ar->sta_state)
+ ar->sta_state = new_state;
+
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -7392,8 +7406,9 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
ctx, arvif->vdev_id);
WARN_ON(!arvif->is_started);
-
- if (vif->type == NL80211_IFTYPE_MONITOR) {
+ if (vif->type == NL80211_IFTYPE_MONITOR ||
+ (vif->type == NL80211_IFTYPE_STATION &&
+ ar->sta_state < IEEE80211_STA_ASSOC)) {
WARN_ON(!arvif->is_up);
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
@@ -7409,6 +7424,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
ath10k_warn(ar, "failed to stop vdev %i: %d\n",
arvif->vdev_id, ret);
+ ar->sta_state = IEEE80211_STA_NOTEXIST;
arvif->is_started = false;
mutex_unlock(&ar->conf_mutex);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 75f2528b8b84..36026a15f721 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -412,6 +412,15 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar,
return 0;
}
+static int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_PEER_DELETE_RESP_EVENTID\n");
+ complete(&ar->peer_delete_done);
+
+ return 0;
+}
+
/***********/
/* TLV ops */
/***********/
@@ -552,6 +561,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_TLV_TX_PAUSE_EVENTID:
ath10k_wmi_tlv_event_tx_pause(ar, skb);
break;
+ case WMI_TLV_PEER_DELETE_RESP_EVENTID:
+ ath10k_wmi_tlv_event_peer_delete_resp(ar, skb);
+ break;
default:
ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id);
break;
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index 79f324f132e9..f8139bcf79cc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -313,6 +313,8 @@ enum wmi_tlv_event_id {
WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID,
WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID,
WMI_TLV_PEER_STATE_EVENTID,
+ WMI_TLV_PEER_ASSOC_CONF_EVENTID,
+ WMI_TLV_PEER_DELETE_RESP_EVENTID,
WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT),
WMI_TLV_HOST_SWBA_EVENTID,
WMI_TLV_TBTTOFFSET_UPDATE_EVENTID,
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h
index 0c9c3e7896bf..8cb4b0eeb866 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h
@@ -259,7 +259,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
@@ -320,7 +320,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24),
@@ -336,7 +336,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 40c1971dfe96..10a49b1e75d8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -600,12 +600,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
mem_size = (ipa_ctx->hdr_proc_ctx_tbl_lcl) ?
IPA_MEM_PART(apps_hdr_proc_ctx_size) :
IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
- if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
- IPAERR("hdr proc ctx table overflow\n");
- goto bad_len;
- }
-
if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
+ IPAERR("hdr proc ctx table overflow\n");
+ goto bad_len;
+ }
+
offset = kmem_cache_zalloc(ipa_ctx->hdr_proc_ctx_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -711,30 +711,30 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
mem_size = (ipa_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) :
IPA_MEM_PART(apps_hdr_size_ddr);
- /*
- * if header does not fit to table, place it in DDR
- * This is valid for IPA 2.5 and on,
- * with the exception of IPA2.6L.
- */
- if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
- if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) {
- IPAERR("not enough room for header\n");
- goto bad_hdr_len;
- } else {
- entry->is_hdr_proc_ctx = true;
- entry->phys_base = dma_map_single(ipa_ctx->pdev,
- entry->hdr,
- entry->hdr_len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(ipa_ctx->pdev,
- entry->phys_base)) {
- IPAERR("dma_map_single failure for entry\n");
- goto fail_dma_mapping;
+ if (list_empty(&htbl->head_free_offset_list[bin])) {
+ /*
+ * if header does not fit to table, place it in DDR
+ * This is valid for IPA 2.5 and on,
+ * with the exception of IPA2.6L.
+ */
+ if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
+ if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) {
+ IPAERR("not enough room for header\n");
+ goto bad_hdr_len;
+ } else {
+ entry->is_hdr_proc_ctx = true;
+ entry->phys_base = dma_map_single(ipa_ctx->pdev,
+ entry->hdr,
+ entry->hdr_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failureed\n");
+ goto fail_dma_mapping;
+ }
}
- }
- } else {
- entry->is_hdr_proc_ctx = false;
- if (list_empty(&htbl->head_free_offset_list[bin])) {
+ } else {
+ entry->is_hdr_proc_ctx = false;
offset = kmem_cache_zalloc(ipa_ctx->hdr_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -751,14 +751,15 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
htbl->end += ipa_hdr_bin_sz[bin];
list_add(&offset->link,
&htbl->head_offset_list[bin]);
- } else {
- /* get the first free slot */
- offset =
- list_first_entry(&htbl->head_free_offset_list[bin],
- struct ipa_hdr_offset_entry, link);
- list_move(&offset->link, &htbl->head_offset_list[bin]);
+ entry->offset_entry = offset;
}
-
+ } else {
+ entry->is_hdr_proc_ctx = false;
+ /* get the first free slot */
+ offset =
+ list_first_entry(&htbl->head_free_offset_list[bin],
+ struct ipa_hdr_offset_entry, link);
+ list_move(&offset->link, &htbl->head_offset_list[bin]);
entry->offset_entry = offset;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
index 364cd4b7d38a..69c88bd04b1b 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c
@@ -665,6 +665,12 @@ send_cmd:
retries++;
if (retries == IPA_BAM_STOP_MAX_RETRY) {
IPAERR("Failed after %d tries\n", retries);
+ mutex_unlock(&ipa_ctx->uc_ctx.uc_lock);
+ /*
+ * Max retry reached,
+ * assert to check why cmd send failed.
+ */
+ ipa_assert();
} else {
/* sleep for short period to flush IPA */
usleep_range(IPA_UC_WAIT_MIN_SLEEP,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 9943095abe30..e0200fe50871 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -1008,6 +1008,11 @@ enum ipacm_client_enum ipa2_get_client(int pipe_idx)
*/
bool ipa2_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index e197ee8b06dd..a5186f1aff35 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -377,12 +377,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
mem_size = (ipa3_ctx->hdr_proc_ctx_tbl_lcl) ?
IPA_MEM_PART(apps_hdr_proc_ctx_size) :
IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
- if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
- IPAERR("hdr proc ctx table overflow\n");
- goto bad_len;
- }
-
if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) {
+ IPAERR("hdr proc ctx table overflow\n");
+ goto bad_len;
+ }
+
offset = kmem_cache_zalloc(ipa3_ctx->hdr_proc_ctx_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -487,20 +487,21 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
mem_size = (ipa3_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) :
IPA_MEM_PART(apps_hdr_size_ddr);
- /* if header does not fit to table, place it in DDR */
- if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
- entry->is_hdr_proc_ctx = true;
- entry->phys_base = dma_map_single(ipa3_ctx->pdev,
- entry->hdr,
- entry->hdr_len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(ipa3_ctx->pdev, entry->phys_base)) {
- IPAERR("dma_map_single failure for entry\n");
- goto fail_dma_mapping;
- }
- } else {
- entry->is_hdr_proc_ctx = false;
- if (list_empty(&htbl->head_free_offset_list[bin])) {
+ if (list_empty(&htbl->head_free_offset_list[bin])) {
+ /* if header does not fit to table, place it in DDR */
+ if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) {
+ entry->is_hdr_proc_ctx = true;
+ entry->phys_base = dma_map_single(ipa3_ctx->pdev,
+ entry->hdr,
+ entry->hdr_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa3_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failure for entry\n");
+ goto fail_dma_mapping;
+ }
+ } else {
+ entry->is_hdr_proc_ctx = false;
offset = kmem_cache_zalloc(ipa3_ctx->hdr_offset_cache,
GFP_KERNEL);
if (!offset) {
@@ -517,14 +518,14 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
htbl->end += ipa_hdr_bin_sz[bin];
list_add(&offset->link,
&htbl->head_offset_list[bin]);
- } else {
- /* get the first free slot */
- offset =
- list_first_entry(&htbl->head_free_offset_list[bin],
- struct ipa_hdr_offset_entry, link);
- list_move(&offset->link, &htbl->head_offset_list[bin]);
+ entry->offset_entry = offset;
}
-
+ } else {
+ entry->is_hdr_proc_ctx = false;
+ /* get the first free slot */
+ offset = list_first_entry(&htbl->head_free_offset_list[bin],
+ struct ipa_hdr_offset_entry, link);
+ list_move(&offset->link, &htbl->head_offset_list[bin]);
entry->offset_entry = offset;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index d19de2a7bdb5..6647f919a577 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1064,6 +1064,11 @@ enum ipacm_client_enum ipa3_get_client(int pipe_idx)
*/
bool ipa3_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA3_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa3_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 4354b2600472..68ef2595f3c3 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -537,11 +537,12 @@ void bhi_firmware_download(struct work_struct *work)
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
- wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ ret = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_BHI ||
- mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT);
- if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
- mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
+ if (!ret || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI is not in valid state for firmware download\n");
return;
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index 04ecf13991b3..ee0b9b759e0a 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -27,8 +27,7 @@
#define CMD_EL_PER_RING 128
#define ELEMENT_GAP 1
#define MHI_EPID 4
-#define MHI_MAX_RESUME_TIMEOUT 5000
-#define MHI_MAX_SUSPEND_TIMEOUT 5000
+#define MHI_MAX_STATE_TRANSITION_TIMEOUT 5000
#define MHI_MAX_CMD_TIMEOUT 500
#define MHI_RPM_AUTOSUSPEND_TMR_VAL_MS 1000
#define MAX_BUF_SIZE 32
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index ad9a6fd6b278..49db99100311 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.c
@@ -116,7 +116,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n",
@@ -142,7 +142,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M3 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M3 event, timeout, current state:%s\n",
@@ -180,7 +180,7 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0 event, timeout or LD\n");
@@ -322,9 +322,6 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
else
ret_val = 0;
- /* wait for firmware download to complete */
- flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
-
if (ret_val) {
read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
@@ -333,6 +330,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
unlock_pm_lock:
+ /* wait for firmware download to complete */
+ flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val);
mutex_unlock(&mhi_dev_ctxt->pm_lock);
return ret_val;
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c5346babf310..18736547d422 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -467,6 +467,7 @@ extern void dump_sram(u8 *buf, int addr, int len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
extern bool is_input_present(struct fg_chip *chip);
+extern bool is_qnovo_en(struct fg_chip *chip);
extern void fg_circ_buf_add(struct fg_circ_buf *, int);
extern void fg_circ_buf_clr(struct fg_circ_buf *);
extern int fg_circ_buf_avg(struct fg_circ_buf *, int *);
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index f2395b6ba4ab..9635044e02a5 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -106,14 +106,17 @@ static struct fg_dbgfs dbgfs_data = {
static bool is_usb_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->usb_psy)
chip->usb_psy = power_supply_get_by_name("usb");
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->usb_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -122,14 +125,17 @@ static bool is_usb_present(struct fg_chip *chip)
static bool is_dc_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->dc_psy)
chip->dc_psy = power_supply_get_by_name("dc");
- if (chip->dc_psy)
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->dc_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -140,6 +146,25 @@ bool is_input_present(struct fg_chip *chip)
return is_usb_present(chip) || is_dc_present(chip);
}
+bool is_qnovo_en(struct fg_chip *chip)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &pval);
+ if (rc < 0)
+ return false;
+
+ return pval.intval != 0;
+}
+
#define EXPONENT_SHIFT 11
#define EXPONENT_OFFSET -9
#define MANTISSA_SIGN_BIT 10
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index bd7231705319..81cd4b89ddbf 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -1402,6 +1402,7 @@ out:
static void fg_cap_learning_update(struct fg_chip *chip)
{
int rc, batt_soc, batt_soc_msb;
+ bool input_present = is_input_present(chip);
mutex_lock(&chip->cl.lock);
@@ -1442,11 +1443,29 @@ static void fg_cap_learning_update(struct fg_chip *chip)
chip->cl.init_cc_uah = 0;
}
+ if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (!input_present) {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
+ }
+
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
- fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc_msb);
- chip->cl.active = false;
- chip->cl.init_cc_uah = 0;
+ if (is_qnovo_en(chip) && input_present) {
+ /*
+ * Don't abort the capacity learning when qnovo
+ * is enabled and input is present where the
+ * charging status can go to "not charging"
+ * intermittently.
+ */
+ } else {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
}
}
@@ -1981,7 +2000,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
{
union power_supply_propval prop = {0, };
int rc;
- bool parallel_en = false, qnovo_en = false;
+ bool parallel_en = false, qnovo_en;
if (is_parallel_charger_available(chip)) {
rc = power_supply_get_property(chip->parallel_psy,
@@ -1994,10 +2013,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip)
parallel_en = prop.intval;
}
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop);
- if (!rc)
- qnovo_en = prop.intval;
+ qnovo_en = is_qnovo_en(chip);
fg_dbg(chip, FG_POWER_SUPPLY, "chg_sts: %d par_en: %d qnov_en: %d esr_fcc_ctrl_en: %d\n",
chip->charge_status, parallel_en, qnovo_en,
@@ -4479,7 +4495,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
/* Keep BSOC_DELTA_IRQ irq disabled until we require it */
- rerun_election(chip->delta_bsoc_irq_en_votable);
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
rc = fg_debugfs_create(chip);
if (rc < 0) {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 88956d3ba674..068c7ddfb739 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -344,6 +344,15 @@ config REGULATOR_MAX1586
regulator via I2C bus. The provided regulator is suitable
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_MAX20010
+ tristate "Maxim MAX20010 regulator support"
+ depends on I2C
+ help
+ This driver supports the Maxim MAX20010 switching voltage regulator
+ (buck converter). The regulator is controlled using an I2C interface
+ and supports 2 programmable voltage ranges from 0.5V to 1.27V in 10mV
+ steps and 0.625V to 1.5875V in 12.5mV steps.
+
config REGULATOR_MAX8649
tristate "Maxim 8649 voltage regulator"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e345f10f94af..856ebe2b5c1b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_MAX20010) += max20010-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
diff --git a/drivers/regulator/max20010-regulator.c b/drivers/regulator/max20010-regulator.c
new file mode 100644
index 000000000000..a914ca70ccb7
--- /dev/null
+++ b/drivers/regulator/max20010-regulator.c
@@ -0,0 +1,490 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+struct voltage_range {
+ int vrange_sel;
+ int min_uV;
+ int max_uV;
+ int step_uV;
+};
+
+struct max20010_slew_rate {
+ int slew_sel;
+ int soft_start;
+ int dvs;
+};
+
+struct max20010_device_info {
+ struct device *dev;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ const struct voltage_range *range;
+ const struct max20010_slew_rate *slew_rate;
+ unsigned vout_sel;
+ bool enabled;
+};
+
+#define MAX20010_ID_REG 0x00
+
+#define MAX20010_VMAX_REG 0x02
+#define MAX20010_VMAX_MASK GENMASK(6, 0)
+
+#define MAX20010_CONFIG_REG 0x05
+#define MAX20010_CONFIG_SYNC_IO_MASK GENMASK(1, 0)
+#define MAX20010_CONFIG_MODE_MASK BIT(3)
+#define MAX20010_CONFIG_MODE_SYNC 0
+#define MAX20010_CONFIG_MODE_FPWM 8
+#define MAX20010_CONFIG_VSTEP_MASK BIT(7)
+#define MAX20010_CONFIG_VSTEP_SHIFT 7
+
+#define MAX20010_SLEW_REG 0x06
+#define MAX20010_SLEW_MASK GENMASK(3, 0)
+
+#define MAX20010_VSET_REG 0x07
+#define MAX20010_VSET_MASK GENMASK(6, 0)
+
+static const struct max20010_slew_rate slew_rates[] = {
+ {0, 22000, 22000},
+ {1, 11000, 22000},
+ {2, 5500, 22000},
+ {3, 11000, 11000},
+ {4, 5500, 11000},
+ {5, 44000, 44000},
+ {6, 22000, 44000},
+ {7, 11000, 44000},
+ {8, 5500, 44000},
+ {9, 5500, 5500},
+};
+
+static const struct voltage_range max20010_range0 = {0, 500000, 1270000, 10000};
+static const struct voltage_range max20010_range1 = {1, 625000, 1587500, 12500};
+
+static const struct regmap_config max20010_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX20010_VSET_REG,
+};
+
+static int max20010_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ /* Set the voltage only if the regulator was enabled earlier */
+ if (info->enabled) {
+ rc = regulator_set_voltage_sel_regmap(rdev, sel);
+ if (rc) {
+ dev_err(info->dev,
+ "regulator set voltage failed for selector = 0x%2x, rc=%d\n",
+ sel, rc);
+ return rc;
+ }
+ }
+
+ info->vout_sel = sel;
+ return rc;
+}
+
+static int max20010_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+
+ return (info->enabled == true) ? 1 : 0;
+}
+
+static int max20010_regulator_enable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, info->vout_sel);
+ if (rc) {
+ dev_err(info->dev, "regulator enable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = true;
+
+ return rc;
+}
+
+static int max20010_regulator_disable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, 0x0);
+ if (rc) {
+ dev_err(info->dev, "regulator disable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = false;
+
+ return rc;
+}
+
+static inline unsigned int max20010_map_mode(unsigned int mode)
+{
+ return (mode == MAX20010_CONFIG_MODE_FPWM) ?
+ REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int max20010_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_MODE_MASK,
+ MAX20010_CONFIG_MODE_FPWM);
+ break;
+ case REGULATOR_MODE_IDLE:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ (MAX20010_CONFIG_MODE_MASK
+ | MAX20010_CONFIG_SYNC_IO_MASK),
+ MAX20010_CONFIG_MODE_SYNC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc)
+ dev_err(info->dev, "failed to set %s mode, rc=%d\n",
+ mode == REGULATOR_MODE_NORMAL ? "Force PWM" : "SYNC",
+ rc);
+ return rc;
+}
+
+static unsigned int max20010_get_mode(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int rc = 0;
+
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read mode configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return max20010_map_mode(val & MAX20010_CONFIG_MODE_MASK);
+}
+
+static int max20010_enable_time(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int volt_uV;
+
+ volt_uV = regulator_list_voltage_linear(rdev, info->vout_sel);
+ return DIV_ROUND_UP(volt_uV, info->slew_rate->soft_start);
+}
+
+static struct regulator_ops max20010_regulator_ops = {
+ .set_voltage_sel = max20010_set_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = max20010_regulator_is_enabled,
+ .enable = max20010_regulator_enable,
+ .disable = max20010_regulator_disable,
+ .set_mode = max20010_set_mode,
+ .get_mode = max20010_get_mode,
+ .enable_time = max20010_enable_time,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "max20010-reg",
+ .supply_name = "vin",
+ .owner = THIS_MODULE,
+ .ops = &max20010_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_min_sel = 1,
+ .vsel_reg = MAX20010_VSET_REG,
+ .vsel_mask = MAX20010_VSET_MASK,
+ .of_map_mode = max20010_map_mode,
+};
+
+static int max20010_device_setup(struct max20010_device_info *info)
+{
+ int max_uV, rc = 0;
+ unsigned int val;
+
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_VSTEP_MASK,
+ (info->range->vrange_sel
+ << MAX20010_CONFIG_VSTEP_SHIFT));
+ if (rc) {
+ dev_err(info->dev, "failed to update vstep configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ max_uV = min(info->init_data->constraints.max_uV, info->range->max_uV);
+ val = DIV_ROUND_UP(max_uV - info->range->min_uV,
+ info->range->step_uV) + 1;
+ rc = regmap_update_bits(info->regmap, MAX20010_VMAX_REG,
+ MAX20010_VMAX_MASK, val);
+ if (rc) {
+ dev_err(info->dev, "failed to write VMAX configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = regmap_update_bits(info->regmap, MAX20010_SLEW_REG,
+ MAX20010_SLEW_MASK, info->slew_rate->slew_sel);
+ if (rc) {
+ dev_err(info->dev, "failed to write slew configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Store default voltage register value */
+ rc = regmap_read(info->regmap, MAX20010_VSET_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read voltage register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ info->vout_sel = val & MAX20010_VSET_MASK;
+ info->enabled = (info->vout_sel != 0x0) ? true : false;
+
+ return rc;
+}
+
+static int max20010_parse_init_data(struct max20010_device_info *info)
+{
+ struct device_node *of_node = info->dev->of_node;
+ int i, slew_index, ss_slew_rate, dvs_slew_rate, rc = 0;
+ unsigned int val;
+
+ if (of_find_property(of_node, "maxim,vrange-sel", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,vrange-sel", &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,vrange-sel property read failed, rc=%d\n",
+ rc);
+ return rc;
+ } else if (val > 1) {
+ dev_err(info->dev, "unsupported vrange-sel value = %d, should be either 0 or 1\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ /* Read default voltage range value */
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read config register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = (val & MAX20010_CONFIG_VSTEP_MASK)
+ >> MAX20010_CONFIG_VSTEP_SHIFT;
+ }
+
+ info->range = (val == 0) ? &max20010_range0 : &max20010_range1;
+
+ /*
+ * Verify the min and max constraints specified through regulator device
+ * properties are fit with in that of the selected voltage range of the
+ * device.
+ */
+ if (info->init_data->constraints.min_uV < info->range->min_uV ||
+ info->init_data->constraints.max_uV > info->range->max_uV) {
+ dev_err(info->dev,
+ "Regulator min/max constraints are not fit with in the device min/max constraints\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Read soft-start and dvs slew rates from device node. Use default
+ * values if not specified.
+ *
+ * Read the register default values and modify them with the slew-rates
+ * defined through device node.
+ */
+ rc = regmap_read(info->regmap, MAX20010_SLEW_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read slew register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ slew_index = val & MAX20010_SLEW_MASK;
+
+ if (slew_index >= ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "unsupported default slew configuration\n");
+ return -EINVAL;
+ }
+
+ ss_slew_rate = slew_rates[slew_index].soft_start;
+ dvs_slew_rate = slew_rates[slew_index].dvs;
+
+ if (of_find_property(of_node, "maxim,soft-start-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,soft-start-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,soft-start-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ ss_slew_rate = val;
+ }
+
+ if (of_find_property(of_node, "maxim,dvs-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,dvs-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,dvs-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dvs_slew_rate = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
+ if (ss_slew_rate == slew_rates[i].soft_start
+ && dvs_slew_rate == slew_rates[i].dvs) {
+ info->slew_rate = &slew_rates[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "invalid slew-rate values are specified.\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int max20010_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max20010_device_info *info;
+ struct regulator_config config = { };
+ int val, rc = 0;
+
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &client->dev;
+ info->init_data = of_get_regulator_init_data(info->dev,
+ info->dev->of_node, &rdesc);
+ if (!info->init_data) {
+ dev_err(info->dev, "regulator init_data is missing\n");
+ return -ENODEV;
+ }
+
+ info->init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ info->init_data->constraints.valid_modes_mask
+ = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+
+ info->regmap = devm_regmap_init_i2c(client, &max20010_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(info->dev, "Error in allocating regmap\n");
+ return PTR_ERR(info->regmap);
+ }
+
+ i2c_set_clientdata(client, info);
+
+ /* Get chip Id */
+ rc = regmap_read(info->regmap, MAX20010_ID_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "Failed to get chip ID!\n");
+ return rc;
+ }
+
+ rc = max20010_parse_init_data(info);
+ if (rc) {
+ dev_err(info->dev, "max20010 init data parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = max20010_device_setup(info);
+ if (rc) {
+ dev_err(info->dev, "Failed to setup device, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ config.dev = info->dev;
+ config.init_data = info->init_data;
+ config.regmap = info->regmap;
+ config.driver_data = info;
+ config.of_node = client->dev.of_node;
+
+ rdesc.min_uV = info->range->min_uV;
+ rdesc.uV_step = info->range->step_uV;
+ rdesc.n_voltages = DIV_ROUND_UP((info->range->max_uV
+ - info->range->min_uV),
+ info->range->step_uV);
+ rdesc.ramp_delay = info->slew_rate->dvs;
+
+ info->rdev = devm_regulator_register(info->dev, &rdesc, &config);
+ if (IS_ERR(info->rdev)) {
+ dev_err(info->dev, "Failed to register regulator, rc=%d\n", rc);
+ return PTR_ERR(info->rdev);
+ }
+
+ dev_info(info->dev, "Detected regulator MAX20010 PID = %d : voltage-range(%d) : (%d - %d) uV, step = %d uV\n",
+ val, info->range->vrange_sel, info->range->min_uV,
+ info->range->max_uV, info->range->step_uV);
+
+ return rc;
+}
+
+static const struct of_device_id max20010_match_table[] = {
+ {.compatible = "maxim,max20010", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max20010_match_table);
+
+static const struct i2c_device_id max20010_id[] = {
+ {"max20010", -1},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max20010_id);
+
+static struct i2c_driver max20010_regulator_driver = {
+ .driver = {
+ .name = "max20010-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = max20010_match_table,
+ },
+ .probe = max20010_regulator_probe,
+ .id_table = max20010_id,
+};
+module_i2c_driver(max20010_regulator_driver);
+
+MODULE_DESCRIPTION("MAX20010 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index aadaef7d1bed..aae796678ffe 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -2093,9 +2093,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
struct ufs_qcom_host *host;
struct resource *res;
- if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev)))
- return -ENODEV;
-
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
err = -ENOMEM;
@@ -2787,6 +2784,24 @@ static int ufs_qcom_probe(struct platform_device *pdev)
{
int err;
struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ /*
+ * On qcom platforms, bootdevice is the primary storage
+ * device. This device can either be eMMC or UFS.
+ * The type of device connected is detected at runtime.
+ * So, if an eMMC device is connected, and this function
+ * is invoked, it would turn-off the regulator if it detects
+ * that the storage device is not ufs.
+ * These regulators are turned ON by the bootloaders & turning
+ * them off without sending PON may damage the connected device.
+ * Hence, check for the connected device early-on & don't turn-off
+ * the regulators.
+ */
+ if (of_property_read_bool(np, "non-removable") &&
+ strlen(android_boot_dev) &&
+ strcmp(android_boot_dev, dev_name(dev)))
+ return -ENODEV;
/* Perform generic probe */
err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_variant);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1a360caf3fba..544a71e7c242 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -518,7 +518,7 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
-#define UFSHCD_MAX_CMD_LOGGING 100
+#define UFSHCD_MAX_CMD_LOGGING 200
#ifdef CONFIG_TRACEPOINTS
static inline void ufshcd_add_command_trace(struct ufs_hba *hba,
@@ -594,7 +594,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff);
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
int i;
int pos;
@@ -643,7 +643,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
{
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
}
#endif
@@ -4301,6 +4301,7 @@ out:
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_host_regs(hba);
+ ufshcd_print_cmd_log(hba);
}
ufshcd_save_tstamp_of_last_dme_cmd(hba);
@@ -6129,7 +6130,7 @@ static void ufshcd_err_handler(struct work_struct *work)
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_tmrs(hba, hba->outstanding_tasks);
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
}
}
@@ -6641,7 +6642,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd->request->tag;
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 561b9074f2ee..29e67d9fa389 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -4143,6 +4143,7 @@ static void glink_core_link_down(struct glink_transport_if *if_ptr)
rwref_write_get(&xprt_ptr->xprt_state_lhb0);
xprt_ptr->next_lcid = 1;
xprt_ptr->local_state = GLINK_XPRT_DOWN;
+ xprt_ptr->curr_qos_rate_kBps = 0;
xprt_ptr->local_version_idx = xprt_ptr->versions_entries - 1;
xprt_ptr->remote_version_idx = xprt_ptr->versions_entries - 1;
xprt_ptr->l_features =
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 78c52cd943bf..28f89bfac7c6 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -168,6 +168,76 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_MAX,
};
+enum icnss_msa_perm {
+ ICNSS_MSA_PERM_HLOS_ALL = 0,
+ ICNSS_MSA_PERM_WLAN_HW_RW = 1,
+ ICNSS_MSA_PERM_DUMP_COLLECT = 2,
+ ICNSS_MSA_PERM_MAX,
+};
+
+#define ICNSS_MAX_VMIDS 4
+
+struct icnss_mem_region_info {
+ uint64_t reg_addr;
+ uint32_t size;
+ uint8_t secure_flag;
+ enum icnss_msa_perm perm;
+};
+
+struct icnss_msa_perm_list_t {
+ int vmids[ICNSS_MAX_VMIDS];
+ int perms[ICNSS_MAX_VMIDS];
+ int nelems;
+};
+
+struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 2,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 3,
+ },
+};
+
+struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 3,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 4,
+ },
+};
+
struct icnss_event_pd_service_down_data {
bool crashed;
bool fw_rejuvenate;
@@ -366,8 +436,6 @@ static struct icnss_priv {
bool bypass_s1_smmu;
} *penv;
-static enum cnss_cc_src cnss_cc_source = CNSS_SOURCE_CORE;
-
#ifdef CONFIG_ICNSS_DEBUG
static void icnss_ignore_qmi_timeout(bool ignore)
{
@@ -377,6 +445,84 @@ static void icnss_ignore_qmi_timeout(bool ignore)
static void icnss_ignore_qmi_timeout(bool ignore) { }
#endif
+static int icnss_assign_msa_perm(struct icnss_mem_region_info
+ *mem_region, enum icnss_msa_perm new_perm)
+{
+ int ret = 0;
+ phys_addr_t addr;
+ u32 size;
+ u32 i = 0;
+ u32 source_vmids[ICNSS_MAX_VMIDS];
+ u32 source_nelems;
+ u32 dest_vmids[ICNSS_MAX_VMIDS];
+ u32 dest_perms[ICNSS_MAX_VMIDS];
+ u32 dest_nelems;
+ enum icnss_msa_perm cur_perm = mem_region->perm;
+ struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
+
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
+
+ if (mem_region->secure_flag) {
+ new_perm_list = &msa_perm_secure_list[new_perm];
+ old_perm_list = &msa_perm_secure_list[cur_perm];
+ } else {
+ new_perm_list = &msa_perm_list[new_perm];
+ old_perm_list = &msa_perm_list[cur_perm];
+ }
+
+ source_nelems = old_perm_list->nelems;
+ dest_nelems = new_perm_list->nelems;
+
+ for (i = 0; i < source_nelems; ++i)
+ source_vmids[i] = old_perm_list->vmids[i];
+
+ for (i = 0; i < dest_nelems; ++i) {
+ dest_vmids[i] = new_perm_list->vmids[i];
+ dest_perms[i] = new_perm_list->perms[i];
+ }
+
+ ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
+ dest_vmids, dest_perms, dest_nelems);
+ if (ret) {
+ icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
+ goto out;
+ }
+
+ icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
+ "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
+ source_nelems, source_vmids[0], source_vmids[1],
+ source_vmids[2], source_vmids[3], dest_nelems,
+ dest_vmids[0], dest_vmids[1], dest_vmids[2],
+ dest_vmids[3]);
+out:
+ return ret;
+}
+
+static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
+ enum icnss_msa_perm new_perm)
+{
+ int ret;
+ int i;
+ enum icnss_msa_perm old_perm;
+
+ for (i = 0; i < priv->nr_mem_region; i++) {
+ old_perm = priv->mem_region[i].perm;
+ ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
+ if (ret)
+ goto err_unmap;
+ priv->mem_region[i].perm = new_perm;
+ }
+ return 0;
+
+err_unmap:
+ for (i--; i >= 0; i--) {
+ icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
+ }
+ return ret;
+}
+
static void icnss_pm_stay_awake(struct icnss_priv *priv)
{
if (atomic_inc_return(&priv->pm_count) != 1)
@@ -941,18 +1087,6 @@ static int icnss_hw_power_off(struct icnss_priv *priv)
return ret;
}
-void cnss_set_cc_source(enum cnss_cc_src cc_source)
-{
- cnss_cc_source = cc_source;
-}
-EXPORT_SYMBOL(cnss_set_cc_source);
-
-enum cnss_cc_src cnss_get_cc_source(void)
-{
- return cnss_cc_source;
-}
-EXPORT_SYMBOL(cnss_get_cc_source);
-
int icnss_power_on(struct device *dev)
{
struct icnss_priv *priv = dev_get_drvdata(dev);
@@ -994,119 +1128,6 @@ int icnss_power_off(struct device *dev)
}
EXPORT_SYMBOL(icnss_power_off);
-static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 source_vmlist[1] = {VMID_HLOS};
- int dest_vmids[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[3] = {PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE};
- int source_nelems = sizeof(source_vmlist)/sizeof(u32);
- int dest_nelems = 0;
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- dest_vmids[2] = VMID_WLAN_CE;
- dest_nelems = 3;
- } else {
- dest_vmids[2] = 0;
- dest_nelems = 2;
- }
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
-
- icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
- source_vmlist[0], dest_nelems, dest_vmids[0],
- dest_vmids[1], dest_vmids[2]);
-out:
- return ret;
-
-}
-
-static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 dest_vmids[1] = {VMID_HLOS};
- int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
- int source_nelems = 0;
- int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- source_vmlist[2] = VMID_WLAN_CE;
- source_nelems = 3;
- } else {
- source_vmlist[2] = 0;
- source_nelems = 2;
- }
-
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
- icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
- source_nelems, source_vmlist[0], source_vmlist[1],
- source_vmlist[2], dest_vmids[0]);
-out:
- return ret;
-}
-
-static int icnss_setup_msa_permissions(struct icnss_priv *priv)
-{
- int ret;
- int i;
-
- if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return 0;
-
- for (i = 0; i < priv->nr_mem_region; i++) {
-
- ret = icnss_map_msa_permissions(&priv->mem_region[i]);
- if (ret)
- goto err_unmap;
- }
-
- set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-
- return 0;
-
-err_unmap:
- for (i--; i >= 0; i--)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
- return ret;
-}
-
-static void icnss_remove_msa_permissions(struct icnss_priv *priv)
-{
- int i;
-
- if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return;
-
- for (i = 0; i < priv->nr_mem_region; i++)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
-
- clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-}
-
static int wlfw_msa_mem_info_send_sync_msg(void)
{
int ret;
@@ -1912,9 +1933,12 @@ static int icnss_driver_event_server_arrive(void *data)
if (ret < 0)
goto err_power_on;
- ret = icnss_setup_msa_permissions(penv);
- if (ret < 0)
- goto err_power_on;
+ if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
+ ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
+ if (ret < 0)
+ goto err_power_on;
+ set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
+ }
ret = wlfw_msa_ready_send_sync_msg();
if (ret < 0)
@@ -1932,7 +1956,7 @@ static int icnss_driver_event_server_arrive(void *data)
return ret;
err_setup_msa:
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
err_power_on:
icnss_hw_power_off(penv);
fail:
@@ -2347,14 +2371,22 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
modem_ssr_nb);
struct icnss_uevent_fw_down_data fw_down_data;
+ int ret = 0;
icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
- if (code == SUBSYS_AFTER_SHUTDOWN &&
- notif->crashed == CRASH_STATUS_ERR_FATAL) {
- icnss_remove_msa_permissions(priv);
- icnss_pr_info("Collecting msa0 segment dump\n");
- icnss_msa0_ramdump(priv);
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ ret = icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_DUMP_COLLECT);
+ if (!ret) {
+ icnss_pr_info("Collecting msa0 segment dump\n");
+ icnss_msa0_ramdump(priv);
+ icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_WLAN_HW_RW);
+ } else {
+ icnss_pr_err("Not able to Collect msa0 segment dump"
+ "Apps permissions not assigned %d\n", ret);
+ }
return NOTIFY_OK;
}
@@ -4321,7 +4353,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_hw_power_off(penv);
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
+ clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c
index 6974146c6112..ca22b6cdf4a1 100644
--- a/drivers/soc/qcom/icnss_utils.c
+++ b/drivers/soc/qcom/icnss_utils.c
@@ -19,6 +19,7 @@
static DEFINE_MUTEX(unsafe_channel_list_lock);
static DEFINE_SPINLOCK(dfs_nol_info_lock);
static int driver_load_cnt;
+static enum cnss_cc_src icnss_cc_source = CNSS_SOURCE_CORE;
static struct icnss_unsafe_channel_list {
u16 unsafe_ch_count;
@@ -138,3 +139,16 @@ int icnss_get_driver_load_cnt(void)
return driver_load_cnt;
}
EXPORT_SYMBOL(icnss_get_driver_load_cnt);
+
+
+void icnss_set_cc_source(enum cnss_cc_src cc_source)
+{
+ icnss_cc_source = cc_source;
+}
+EXPORT_SYMBOL(icnss_set_cc_source);
+
+enum cnss_cc_src icnss_get_cc_source(void)
+{
+ return icnss_cc_source;
+}
+EXPORT_SYMBOL(icnss_get_cc_source);
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index 7406dba44320..6dd4b06bf377 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -968,8 +968,8 @@ static int memshare_child_probe(struct platform_device *pdev)
/*
* Memshare allocation for guaranteed clients
*/
- if (memblock[num_clients].guarantee) {
- if (client_id == 1 && size > 0)
+ if (memblock[num_clients].guarantee && size > 0) {
+ if (client_id == 1)
size += MEMSHARE_GUARD_BYTES;
rc = memshare_alloc(memsh_child->dev,
size,
@@ -980,6 +980,7 @@ static int memshare_child_probe(struct platform_device *pdev)
return rc;
}
memblock[num_clients].alloted = 1;
+ shared_hyp_mapping(num_clients);
}
/*
diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c
index 6691418b516e..63545651fe43 100755
--- a/drivers/soundwire/soundwire.c
+++ b/drivers/soundwire/soundwire.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,27 @@ static void swr_dev_release(struct device *dev)
}
/**
+ * swr_remove_device - remove a soundwire device
+ * @swr_dev: soundwire device to remove
+ *
+ * Remove a soundwire device. Go through the soundwire
+ * device list that master has and remove swr_dev from
+ * it.
+ */
+void swr_remove_device(struct swr_device *swr_dev)
+{
+ struct swr_device *swr_dev_loop, *safe;
+
+ list_for_each_entry_safe(swr_dev_loop, safe,
+ &swr_dev->master->devices,
+ dev_list) {
+ if (swr_dev == swr_dev_loop)
+ list_del(&swr_dev_loop->dev_list);
+ }
+}
+EXPORT_SYMBOL(swr_remove_device);
+
+/**
* swr_new_device - instantiate a new soundwire device
* @master: Controller to which device is connected
* @info: Describes the soundwire device
@@ -129,47 +150,6 @@ err_out:
EXPORT_SYMBOL(swr_new_device);
/**
- * swr_startup_devices - perform additional initialization for child devices
- *
- * @swr_dev: pointer to soundwire slave device
- *
- * Performs any additional initialization needed for a soundwire slave device.
- * This is a optional functionality defined by slave devices.
- * Removes the slave node from the list, in case there is any failure.
- */
-int swr_startup_devices(struct swr_device *swr_dev)
-{
- struct swr_driver *swr_drv;
- struct device *dev;
- int ret = 0;
-
- if (!swr_dev)
- return -EINVAL;
-
- dev = &swr_dev->dev;
- if (!dev)
- return -EINVAL;
-
- swr_drv = to_swr_driver(dev->driver);
- if (!swr_drv)
- return -EINVAL;
-
- if (swr_drv->startup) {
- ret = swr_drv->startup(swr_dev);
- if (ret)
- goto out;
-
- dev_dbg(&swr_dev->dev,
- "%s: startup complete for device %lx\n",
- __func__, swr_dev->addr);
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL(swr_startup_devices);
-
-/**
* of_register_swr_devices - register child devices on to the soundwire bus
* @master: pointer to soundwire master device
*
@@ -203,14 +183,15 @@ int of_register_swr_devices(struct swr_master *master)
}
info.addr = addr;
info.of_node = of_node_get(node);
+ master->num_dev++;
swr = swr_new_device(master, &info);
if (!swr) {
dev_err(&master->dev, "of_swr: Register failed %s\n",
node->full_name);
of_node_put(node);
+ master->num_dev--;
continue;
}
- master->num_dev++;
}
return 0;
}
@@ -610,7 +591,7 @@ int swr_device_up(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_up)
return sdrv->device_up(to_swr_device(dev));
@@ -638,7 +619,7 @@ int swr_device_down(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_down)
return sdrv->device_down(to_swr_device(dev));
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 14c13db991a1..cdaf009c5b1f 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -1369,7 +1369,6 @@ static int swrm_probe(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm;
struct swr_ctrl_platform_data *pdata;
- struct swr_device *swr_dev, *safe;
int ret;
/* Allocate soundwire master driver structure */
@@ -1470,9 +1469,6 @@ static int swrm_probe(struct platform_device *pdev)
goto err_mstr_fail;
}
- if (pdev->dev.of_node)
- of_register_swr_devices(&swrm->master);
-
/* Add devices registered with board-info as the
controller will be up now
*/
@@ -1489,15 +1485,11 @@ static int swrm_probe(struct platform_device *pdev)
}
swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION);
- /* Enumerate slave devices */
- list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
- dev_list) {
- ret = swr_startup_devices(swr_dev);
- if (ret)
- list_del(&swr_dev->dev_list);
- }
mutex_unlock(&swrm->mlock);
+ if (pdev->dev.of_node)
+ of_register_swr_devices(&swrm->master);
+
dbgswrm = swrm;
debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0);
if (!IS_ERR(debugfs_swrm_dent)) {
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index c8cbd078bb07..b1f4c3f27111 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -7409,11 +7409,11 @@ static int msm_thermal_dev_probe(struct platform_device *pdev)
pr_err("thermal pre init failed. err:%d\n", ret);
goto probe_exit;
}
+ probe_sensor_info(node, &data, pdev);
ret = probe_deferrable_properties(node, &data, pdev);
if (ret)
goto probe_exit;
- probe_sensor_info(node, &data, pdev);
probe_cc(node, &data, pdev);
probe_freq_mitigation(node, &data, pdev);
probe_cx_phase_ctrl(node, &data, pdev);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 7c35241a487a..b6f4790ffc08 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2015,6 +2015,12 @@ static int ffs_func_eps_enable(struct ffs_function *func)
break;
}
+ /*
+ * userspace setting maxburst > 1 results more fifo
+ * allocation than without maxburst. Change maxburst to 1
+ * only to allocate fifo size of max packet size.
+ */
+ ep->ep->maxburst = 1;
ret = usb_ep_enable(ep->ep);
if (likely(!ret)) {
epfile->ep = ep;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 7d32e2c5cc0d..7ab2fb13061d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -25,6 +25,7 @@ source "drivers/gpu/msm/Kconfig"
source "drivers/gpu/drm/Kconfig"
+source "drivers/video/msm/ba/Kconfig"
menu "Frame buffer Devices"
source "drivers/video/fbdev/Kconfig"
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 1a8c4ced39b2..0a190665a4cf 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm/ba/
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 1c984d02755e..d6e8a213c215 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -272,7 +272,7 @@ struct mdss_smmu_ops {
void (*smmu_unmap_dma_buf)(struct sg_table *table, int domain,
int dir, struct dma_buf *dma_buf);
int (*smmu_dma_alloc_coherent)(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain);
void (*smmu_dma_free_coherent)(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t phys, dma_addr_t iova,
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 2b9c71441d68..2f5aad8ed801 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -3493,6 +3493,7 @@ static int __copy_layer_pp_info_igc_params(
compat_ptr(pp_info32->igc_cfg.c0_c1_data);
pp_info->igc_cfg.c2_data =
compat_ptr(pp_info32->igc_cfg.c2_data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3565,6 +3566,7 @@ static int __copy_layer_pp_info_hist_lut_params(
pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len;
pp_info->hist_lut_cfg.data =
compat_ptr(pp_info32->hist_lut_cfg.data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3654,6 +3656,7 @@ static int __copy_layer_pp_info_pa_v2_params(
break;
default:
pr_debug("version invalid\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3737,6 +3740,7 @@ static int __copy_layer_pp_info_pcc_params(
break;
default:
pr_debug("version invalid, fallback to legacy\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 8cb6c7157230..230b02061b39 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -454,6 +454,9 @@ static ssize_t mdss_debug_base_offset_write(struct file *file,
sscanf(buf, "%5x %x", &off, &cnt);
+ if (off % sizeof(u32))
+ return -EINVAL;
+
if (off > dbg->max_offset)
return -EINVAL;
@@ -526,6 +529,9 @@ static ssize_t mdss_debug_base_reg_write(struct file *file,
if (cnt < 2)
return -EFAULT;
+ if (off % sizeof(u32))
+ return -EFAULT;
+
if (off >= dbg->max_offset)
return -EFAULT;
@@ -571,6 +577,9 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
return -ENOMEM;
}
+ if (dbg->off % sizeof(u32))
+ return -EFAULT;
+
ptr = dbg->base + dbg->off;
tot = 0;
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index a74bf6f60774..7d4578b08244 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -1455,7 +1455,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
mdss_dp_config_misc(dp,
mdss_dp_bpp_to_test_bit_depth(mdss_dp_get_bpp(dp)),
mdss_dp_get_colorimetry_config(dp));
- mdss_dp_sw_config_msa(&dp->ctrl_io, dp->link_rate, &dp->dp_cc_io);
+ mdss_dp_sw_config_msa(dp);
mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info);
}
@@ -1715,7 +1715,7 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp)
{
- return dp->dpcd.downstream_port.dfp_present;
+ return dp->dpcd.downstream_port.dsp_present;
}
static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp)
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index f358aad8a667..afa8e3db590f 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -230,14 +230,38 @@ struct dp_alt_mode {
#define DP_LINK_RATE_MULTIPLIER 27000000
#define DP_KHZ_TO_HZ 1000
#define DP_MAX_PIXEL_CLK_KHZ 675000
+
+enum downstream_port_type {
+ DSP_TYPE_DP = 0x00,
+ DSP_TYPE_VGA,
+ DSP_TYPE_DVI_HDMI_DPPP,
+ DSP_TYPE_OTHER,
+};
+
+static inline char *mdss_dp_dsp_type_to_string(u32 dsp_type)
+{
+ switch (dsp_type) {
+ case DSP_TYPE_DP:
+ return DP_ENUM_STR(DSP_TYPE_DP);
+ case DSP_TYPE_VGA:
+ return DP_ENUM_STR(DSP_TYPE_VGA);
+ case DSP_TYPE_DVI_HDMI_DPPP:
+ return DP_ENUM_STR(DSP_TYPE_DVI_HDMI_DPPP);
+ case DSP_TYPE_OTHER:
+ return DP_ENUM_STR(DSP_TYPE_OTHER);
+ default:
+ return "unknown";
+ }
+}
+
struct downstream_port_config {
/* Byte 02205h */
- bool dfp_present;
- u32 dfp_type;
+ bool dsp_present;
+ enum downstream_port_type dsp_type;
bool format_conversion;
bool detailed_cap_info_available;
/* Byte 02207h */
- u32 dfp_count;
+ u32 dsp_count;
bool msa_timing_par_ignored;
bool oui_support;
};
@@ -1139,6 +1163,12 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc)
crc->en = false;
}
+static inline bool mdss_dp_is_dsp_type_vga(struct mdss_dp_drv_pdata *dp)
+{
+ return (dp->dpcd.downstream_port.dsp_present &&
+ (dp->dpcd.downstream_port.dsp_type == DSP_TYPE_VGA));
+}
+
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 37209c161366..23a63121b78c 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -633,7 +633,8 @@ void dp_extract_edid_video_support(struct edp_edid *edid, char *buf)
pr_debug("Digital Video intf=%d color_depth=%d\n",
edid->video_intf, edid->color_depth);
} else {
- pr_err("Error, Analog video interface\n");
+ pr_debug("Analog video interface, set color depth to 8\n");
+ edid->color_depth = DP_TEST_BIT_DEPTH_8;
}
};
@@ -1140,13 +1141,13 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
pr_debug("rx_ports=%d", cap->num_rx_port);
data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
- cap->downstream_port.dfp_present = data & BIT(0);
- cap->downstream_port.dfp_type = data & 0x6;
+ cap->downstream_port.dsp_present = data & BIT(0);
+ cap->downstream_port.dsp_type = (data & 0x6) >> 1;
cap->downstream_port.format_conversion = data & BIT(3);
cap->downstream_port.detailed_cap_info_available = data & BIT(4);
- pr_debug("dfp_present = %d, dfp_type = %d\n",
- cap->downstream_port.dfp_present,
- cap->downstream_port.dfp_type);
+ pr_debug("dsp_present = %d, dsp_type = %d\n",
+ cap->downstream_port.dsp_present,
+ cap->downstream_port.dsp_type);
pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
cap->downstream_port.format_conversion,
cap->downstream_port.detailed_cap_info_available);
@@ -1154,16 +1155,16 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
bp += 1; /* Skip Byte 6 */
data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
- cap->downstream_port.dfp_count = data & 0x7;
- if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) {
+ cap->downstream_port.dsp_count = data & 0x7;
+ if (cap->downstream_port.dsp_count > DP_MAX_DS_PORT_COUNT) {
pr_debug("DS port count %d greater that max (%d) supported\n",
- cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT);
- cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT;
+ cap->downstream_port.dsp_count, DP_MAX_DS_PORT_COUNT);
+ cap->downstream_port.dsp_count = DP_MAX_DS_PORT_COUNT;
}
cap->downstream_port.msa_timing_par_ignored = data & BIT(6);
cap->downstream_port.oui_support = data & BIT(7);
- pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n",
- cap->downstream_port.dfp_count,
+ pr_debug("dsp_count = %d, msa_timing_par_ignored = %d\n",
+ cap->downstream_port.dsp_count,
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index e8c3fe9f4957..f7b3d4664e86 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -786,19 +786,56 @@ void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER);
}
-void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
- char lrate, struct dss_io_data *dp_cc_io)
+static bool use_fixed_nvid(struct mdss_dp_drv_pdata *dp)
+{
+ /*
+ * For better interop experience, used a fixed NVID=0x8000
+ * whenever connected to a VGA dongle downstream
+ */
+ return mdss_dp_is_dsp_type_vga(dp);
+}
+
+void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid;
-
- pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M);
- pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N);
- pr_debug("pixel_m=0x%x, pixel_n=0x%x\n",
- pixel_m, pixel_n);
-
- mvid = (pixel_m & 0xFFFF) * 5;
- nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+ u64 mvid_calc;
+ u32 const nvid_fixed = 0x8000;
+ struct dss_io_data *ctrl_io = &dp->ctrl_io;
+ struct dss_io_data *dp_cc_io = &dp->dp_cc_io;
+ u32 lrate_kbps;
+ u64 stream_rate_khz;
+
+ if (use_fixed_nvid(dp)) {
+ pr_debug("use fixed NVID=0x%x\n", nvid_fixed);
+ nvid = nvid_fixed;
+
+ lrate_kbps = dp->link_rate * DP_LINK_RATE_MULTIPLIER /
+ DP_KHZ_TO_HZ;
+ stream_rate_khz = div_u64(dp->panel_data.panel_info.clk_rate,
+ DP_KHZ_TO_HZ);
+ pr_debug("link rate=%dkbps, stream_rate_khz=%lluKhz",
+ lrate_kbps, stream_rate_khz);
+
+ /*
+ * For intermediate results, use 64 bit arithmetic to avoid
+ * loss of precision.
+ */
+ mvid_calc = stream_rate_khz * nvid;
+ mvid_calc = div_u64(mvid_calc, lrate_kbps);
+
+ /*
+ * truncate back to 32 bits as this final divided value will
+ * always be within the range of a 32 bit unsigned int.
+ */
+ mvid = (u32) mvid_calc;
+ } else {
+ pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M);
+ pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N);
+ pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
+ mvid = (pixel_m & 0xFFFF) * 5;
+ nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+ }
pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
writel_relaxed(mvid, ctrl_io->base + DP_SOFTWARE_MVID);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 4c93e48e97dc..4970f5bc3a47 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -316,8 +316,7 @@ void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv);
-void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
- char lrate, struct dss_io_data *dp_cc_io);
+void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp);
void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap);
void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 48b151b8080a..82f6d4a123b5 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -773,6 +773,11 @@ static ssize_t mdss_dsi_cmd_state_write(struct file *file,
int *link_state = file->private_data;
char *input;
+ if (!count) {
+ pr_err("%s: Zero bytes to be written\n", __func__);
+ return -EINVAL;
+ }
+
input = kmalloc(count, GFP_KERNEL);
if (!input) {
pr_err("%s: Failed to allocate memory\n", __func__);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 7fabd4944cbd..9847016fed29 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -459,6 +459,7 @@ struct mdss_dsi_ctrl_pdata {
bool avdd_en_gpio_invert;
int lcd_mode_sel_gpio;
int bklt_ctrl; /* backlight ctrl */
+ enum dsi_ctrl_op_mode bklt_dcs_op_mode; /* backlight dcs ctrl mode */
bool pwm_pmi;
int pwm_period;
int pwm_pmic_gpio;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 695dbfa95e29..dbd58f93e907 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -238,6 +238,11 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level)
cmdreq.rlen = 0;
cmdreq.cb = NULL;
+ if (ctrl->bklt_dcs_op_mode == DSI_HS_MODE)
+ cmdreq.flags |= CMD_REQ_HS_MODE;
+ else
+ cmdreq.flags |= CMD_REQ_LP_MODE;
+
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
@@ -2413,6 +2418,13 @@ int mdss_panel_parse_bl_settings(struct device_node *np,
}
} else if (!strcmp(data, "bl_ctrl_dcs")) {
ctrl_pdata->bklt_ctrl = BL_DCS_CMD;
+ data = of_get_property(np,
+ "qcom,mdss-dsi-bl-dcs-command-state", NULL);
+ if (data && !strcmp(data, "dsi_hs_mode"))
+ ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE;
+ else
+ ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE;
+
pr_debug("%s: Configured DCS_CMD bklt ctrl\n",
__func__);
}
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index 518c24810acd..6e52390c2886 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -243,8 +243,8 @@ struct msm_mdp_interface {
do_div(out, 2 * max_bright);\
} while (0)
#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\
- out = ((v) * (max_bright));\
- do_div(out, bl_max);\
+ out = (2 * ((v) * (max_bright)) + (bl_max));\
+ do_div(out, 2 * bl_max);\
} while (0)
struct mdss_fb_file_info {
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index ddc5edbe010d..a953dd1a2ac2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -469,8 +469,10 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc, page_id;
+ u32 i = 0, j, page;
ssize_t ret = strnlen(buf, PAGE_SIZE);
struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
+ struct msm_hdmi_mode_timing_info info = {0};
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -483,7 +485,22 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
return rc;
}
- edid_ctrl->page_id = page_id;
+ if (page_id > MSM_HDMI_INIT_RES_PAGE) {
+ page = MSM_HDMI_INIT_RES_PAGE;
+ while (page < page_id) {
+ j = 1;
+ while (sizeof(info) * j < PAGE_SIZE) {
+ i++;
+ j++;
+ }
+ page++;
+ }
+ }
+
+ if (i < HDMI_VFRMT_MAX)
+ edid_ctrl->page_id = page_id;
+ else
+ DEV_ERR("%s: invalid page id\n", __func__);
DEV_DBG("%s: %d\n", __func__, edid_ctrl->page_id);
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index a9ab970fb4bc..2e267f2695d7 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -581,7 +581,8 @@ static ssize_t hdmi_tx_sysfs_wta_edid(struct device *dev,
}
mutex_lock(&hdmi_ctrl->tx_lock);
- if (edid_size < EDID_BLOCK_SIZE) {
+ if ((edid_size < EDID_BLOCK_SIZE) ||
+ (edid_size > hdmi_ctrl->edid_buf_size)) {
DEV_DBG("%s: disabling custom edid\n", __func__);
ret = -EINVAL;
@@ -633,6 +634,11 @@ static ssize_t hdmi_tx_sysfs_rda_edid(struct device *dev,
mutex_lock(&hdmi_ctrl->tx_lock);
cea_blks = hdmi_ctrl->edid_buf[EDID_BLOCK_SIZE - 2];
+ if (cea_blks >= MAX_EDID_BLOCKS) {
+ DEV_ERR("%s: invalid cea blocks\n", __func__);
+ mutex_unlock(&hdmi_ctrl->tx_lock);
+ return -EINVAL;
+ }
size = (cea_blks + 1) * EDID_BLOCK_SIZE;
size = min_t(u32, size, PAGE_SIZE);
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 5a15b557e5c7..feea8986af91 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -586,6 +586,7 @@ struct mdss_mdp_ctl {
struct mdss_mdp_avr_info avr_info;
bool commit_in_progress;
struct mutex ds_lock;
+ bool need_vsync_on;
};
struct mdss_mdp_mixer {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 0da462394ab8..efd681a5d954 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -2513,6 +2513,7 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
u32 nmixers_wb;
u32 i;
u32 nmixers;
+ u32 nmixers_active;
struct mdss_mdp_mixer *mixer_pool = NULL;
if (!ctl || !ctl->mdata)
@@ -2526,10 +2527,21 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
case MDSS_MDP_MIXER_TYPE_INTF:
mixer_pool = ctl->mdata->mixer_intf;
nmixers = nmixers_intf;
+ nmixers_active = nmixers;
+
+ for (i = 0; i < nmixers; i++) {
+ mixer = mixer_pool + i;
+ if (mixer->ref_cnt)
+ nmixers_active--;
+ }
+ mixer = NULL;
/*
* try to reserve first layer mixer for write back if
- * assertive display needs to be supported through wfd
+ * assertive display needs to be supported through wfd.
+ * For external displays(pluggable) and writeback avoid
+ * allocating mixers LM0 and LM1 which are allocated
+ * to primary display first.
*/
if (ctl->mdata->has_wb_ad && ctl->intf_num &&
((ctl->panel_data->panel_info.type != MIPI_CMD_PANEL) ||
@@ -2541,6 +2553,10 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(
&& (ctl->mdata->ndspp < nmixers)) {
mixer_pool += ctl->mdata->ndspp;
nmixers -= ctl->mdata->ndspp;
+ } else if ((ctl->panel_data->panel_info.is_pluggable) &&
+ nmixers_active) {
+ mixer_pool += ctl->mdata->ndspp;
+ nmixers -= ctl->mdata->ndspp;
}
break;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 2e017fe5ec02..747b4e3e2f81 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -3859,12 +3859,24 @@ static int mdss_mdp_cmd_reconfigure(struct mdss_mdp_ctl *ctl,
}
ctl->switch_with_handoff = false;
}
+ /*
+ * keep track of vsync, so it can be enabled as part
+ * of the post switch sequence
+ */
+ if (ctl->vsync_handler.enabled)
+ ctl->need_vsync_on = true;
mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF);
mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_DSI_DYNAMIC_SWITCH,
(void *) mode, CTL_INTF_EVENT_FLAG_DEFAULT);
} else {
+ if (ctl->need_vsync_on &&
+ ctl->ops.add_vsync_handler) {
+ ctl->ops.add_vsync_handler(ctl,
+ &ctl->vsync_handler);
+ ctl->need_vsync_on = false;
+ }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index a1d79a89b463..b048f356c965 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -741,14 +741,15 @@ static int __cursor_layer_check(struct msm_fb_data_type *mfd,
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
if ((layer->z_order != HW_CURSOR_STAGE(mdata))
+ || layer->flags & MDP_LAYER_FLIP_LR
|| layer->src_rect.w > mdata->max_cursor_size
|| layer->src_rect.h > mdata->max_cursor_size
|| layer->src_rect.w != layer->dst_rect.w
|| layer->src_rect.h != layer->dst_rect.h
|| !mdata->ncursor_pipes) {
- pr_err("Incorrect cursor configs for pipe:%d, cursor_pipes:%d, z_order:%d\n",
+ pr_err("Incorrect cursor configs for pipe:0x%x, ncursor_pipes:%d, z_order:%d, flags:0x%x\n",
layer->pipe_ndx, mdata->ncursor_pipes,
- layer->z_order);
+ layer->z_order, layer->flags);
pr_err("src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n",
layer->src_rect.x, layer->src_rect.y,
layer->src_rect.w, layer->src_rect.h,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index fbd5b01a81e7..91816611d24f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -4413,7 +4413,7 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd,
if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
ret = mdss_smmu_dma_alloc_coherent(&pdev->dev,
cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys,
- &mfd->cursor_buf_iova, mfd->cursor_buf,
+ &mfd->cursor_buf_iova, &mfd->cursor_buf,
GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE);
if (ret) {
pr_err("can't allocate cursor buffer rc:%d\n", ret);
@@ -4601,7 +4601,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
ret = mdss_smmu_dma_alloc_coherent(&pdev->dev,
cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys,
- &mfd->cursor_buf_iova, mfd->cursor_buf,
+ &mfd->cursor_buf_iova, &mfd->cursor_buf,
GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE);
if (ret) {
pr_err("can't allocate cursor buffer rc:%d\n", ret);
@@ -4649,7 +4649,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
- if (cursor->set & FB_CUR_SETIMAGE) {
+ if (mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
u32 cursor_addr;
ret = copy_from_user(mfd->cursor_buf, img->data,
img->width * img->height * 4);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 1b4765837c61..75f502415589 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -477,7 +477,7 @@ static void mdss_smmu_unmap_dma_buf_v2(struct sg_table *table, int domain,
* bank device
*/
static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain)
{
struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain);
@@ -486,8 +486,8 @@ static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size,
return -EINVAL;
}
- cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp);
- if (!cpu_addr) {
+ *cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp);
+ if (!*cpu_addr) {
pr_err("dma alloc coherent failed!\n");
return -ENOMEM;
}
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index b1ee17a01c3f..a5c7af74cdbf 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -253,7 +253,7 @@ static inline void mdss_smmu_unmap_dma_buf(struct sg_table *table, int domain,
}
static inline int mdss_smmu_dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr,
+ dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr,
gfp_t gfp, int domain)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
diff --git a/drivers/video/msm/ba/Kconfig b/drivers/video/msm/ba/Kconfig
new file mode 100644
index 000000000000..1ebc7eceeda6
--- /dev/null
+++ b/drivers/video/msm/ba/Kconfig
@@ -0,0 +1,12 @@
+#
+# MSM BA V4L2
+#
+
+config MSM_BA_V4L2
+ tristate "Qualcomm technologies Inc MSM V4L2 based BA driver"
+ depends on VIDEO_V4L2
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Enables support for the MSM V4L2 bridge abstraction
diff --git a/drivers/video/msm/ba/Makefile b/drivers/video/msm/ba/Makefile
new file mode 100644
index 000000000000..b4e7ddf3c79a
--- /dev/null
+++ b/drivers/video/msm/ba/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm_v4l2_ba.o \
+ msm_ba_common.o \
+ msm_ba.o \
+ msm_ba_debug.o
+
diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c
new file mode 100644
index 000000000000..3e0838115ca6
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+#define MSM_BA_DEV_NAME "msm_ba_8064"
+
+#define MSM_BA_MAX_EVENTS 10
+
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ poll_wait(filp, &inst->event_handler.wait, wait);
+ if (v4l2_event_pending(&inst->event_handler))
+ rc |= POLLPRI;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_poll);
+
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !cap) {
+ dprintk(BA_ERR,
+ "Invalid input, inst = 0x%p, cap = 0x%p", inst, cap);
+ return -EINVAL;
+ }
+
+ strlcpy(cap->driver, MSM_BA_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MSM_BA_DEV_NAME, sizeof(cap->card));
+ cap->bus_info[0] = 0;
+ cap->version = MSM_BA_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ memset(cap->reserved, 0x00, sizeof(cap->reserved));
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_querycap);
+
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !a)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_parm);
+
+int msm_ba_enum_input(void *instance, struct v4l2_input *input)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !input)
+ return -EINVAL;
+
+ if (input->index >= inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_input(input->index);
+ if (ba_input) {
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL;
+ strlcpy(input->name, ba_input->name, sizeof(input->name));
+ if (ba_input->input_type == BA_INPUT_HDMI ||
+ ba_input->input_type == BA_INPUT_MHL)
+ input->capabilities = V4L2_IN_CAP_CUSTOM_TIMINGS;
+ else
+ input->capabilities = V4L2_IN_CAP_STD;
+ dprintk(BA_DBG, "msm_ba_find_input: name %s", input->name);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_input);
+
+int msm_ba_g_input(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ do {
+ /* First find current input */
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ if (ba_input->input_user_type ==
+ BA_INPUT_USERTYPE_KERNEL) {
+ inst->sd_input.index++;
+ continue;
+ }
+ break;
+ }
+ } while (ba_input);
+
+ if (ba_input)
+ *index = inst->sd_input.index;
+ else
+ rc = -ENOENT;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_input);
+
+int msm_ba_s_input(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+ int rc_sig = 0;
+
+ if (!inst)
+ return -EINVAL;
+ if (index > inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ /* Find requested input */
+ ba_input = msm_ba_find_input(index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d", index);
+ return -EINVAL;
+ }
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (ba_input->in_use &&
+ inst->event_handler.prio == V4L2_PRIORITY_RECORD) {
+ dprintk(BA_WARN, "Input %d in use", index);
+ return -EBUSY;
+ }
+ if (ba_input->ba_out_in_use) {
+ if (inst->ext_ops) {
+ if (inst->restore) {
+ dprintk(BA_DBG, "Stream off in set input: %d",
+ ba_input->bridge_chip_ip);
+ rc_sig = v4l2_subdev_call(ba_input->sd,
+ video, s_stream, 0);
+ if (rc_sig)
+ dprintk(BA_ERR,
+ "%s: Error in stream off. rc_sig %d",
+ __func__, rc_sig);
+ }
+ } else {
+ dprintk(BA_WARN, "Sd %d in use", ba_input->ba_out);
+ return -EBUSY;
+ }
+ }
+ rc = v4l2_subdev_call(ba_input->sd, video, s_routing,
+ ba_input->bridge_chip_ip, 0, 0);
+ if (rc) {
+ dprintk(BA_ERR, "Error: %d setting input: %d",
+ rc, ba_input->bridge_chip_ip);
+ return rc;
+ }
+ msm_ba_reset_ip_in_use_from_sd(ba_input->sd);
+ inst->sd_input.index = index;
+ strlcpy(inst->sd_input.name, ba_input->name,
+ sizeof(inst->sd_input.name));
+ inst->sd = ba_input->sd;
+ ba_input->in_use = 1;
+ /* get current signal status */
+ rc_sig = v4l2_subdev_call(
+ ba_input->sd, video, g_input_status, &ba_input->signal_status);
+ dprintk(BA_DBG, "Set input %s : %d - signal status: %d",
+ ba_input->name, index, ba_input->signal_status);
+ if (!rc_sig && !ba_input->signal_status) {
+ struct v4l2_event sd_event = {
+ .id = 0,
+ .type = V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK};
+ int *ptr = (int *)sd_event.u.data;
+ ptr[0] = index;
+ ptr[1] = ba_input->signal_status;
+ msm_ba_queue_v4l2_event(inst, &sd_event);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_input);
+
+int msm_ba_enum_output(void *instance, struct v4l2_output *output)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !output)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(output->index);
+ if (!ba_input)
+ return -EINVAL;
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->std = V4L2_STD_ALL;
+ strlcpy(output->name, ba_input->sd->name, sizeof(output->name));
+ output->capabilities = V4L2_OUT_CAP_STD;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_output);
+
+int msm_ba_g_output(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ *index = inst->sd_output.index;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_output);
+
+int msm_ba_s_output(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(index);
+ if (ba_input) {
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input->ba_out = index;
+ inst->sd_output.index = index;
+ inst->sd = ba_input->sd;
+ inst->sd_input.index = ba_input->ba_ip_idx;
+ } else {
+ dprintk(BA_ERR, "Could not find output index: %d", index);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_output);
+
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_enum_fmt);
+
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_fmt);
+
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct msm_ba_input *ba_input = NULL;
+ v4l2_std_id new_std = V4L2_STD_UNKNOWN;
+ struct v4l2_dv_timings sd_dv_timings;
+ struct v4l2_subdev_format sd_fmt;
+ int rc = 0;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d",
+ inst->sd_input.index);
+ return -EINVAL;
+ }
+ if (ba_input->input_type != BA_INPUT_HDMI) {
+ rc = v4l2_subdev_call(sd, video, querystd, &new_std);
+ if (rc) {
+ dprintk(BA_ERR, "querystd failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ inst->sd_input.std = new_std;
+ } else {
+ rc = v4l2_subdev_call(sd, video, g_dv_timings, &sd_dv_timings);
+ if (rc) {
+ dprintk(BA_ERR, "g_dv_timings failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ }
+
+ rc = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (rc) {
+ dprintk(BA_ERR, "get_fmt failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ f->fmt.pix.height = sd_fmt.format.height;
+ f->fmt.pix.width = sd_fmt.format.width;
+ switch (sd_fmt.format.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_VYUY;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown sd_mbus_fmt.code 0x%x",
+ sd_fmt.format.code);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_fmt);
+
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_s_ctrl(NULL, &inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_s_ctrl);
+
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_g_ctrl(&inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_g_ctrl);
+
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_ext_ctrl);
+
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (rc)
+ dprintk(BA_ERR, "Stream on failed on input: %d",
+ inst->sd_input.index);
+ else
+ msm_ba_set_out_in_use(sd, 1);
+
+ dprintk(BA_DBG, "Stream on: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamon);
+
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (rc)
+ dprintk(BA_ERR, "Stream off failed on input: %d",
+ inst->sd_input.index);
+
+ dprintk(BA_DBG, "Stream off: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+ msm_ba_set_out_in_use(sd, 0);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamoff);
+
+long msm_ba_private_ioctl(void *instance, int cmd, void *arg)
+{
+ long rc = 0;
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int *s_ioctl = arg;
+
+ dprintk(BA_DBG, "Enter %s with command: 0x%x", __func__, cmd);
+
+ if (!inst)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_HDMI_RX_CEC_S_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_PHYSICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_CONNECTED: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_CONNECTED");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_S_ENABLE: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_ENABLE");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ default:
+ dprintk(BA_WARN, "Not a typewriter! Command: 0x%x", cmd);
+ rc = -ENOTTY;
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_private_ioctl);
+
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ if (sr == BA_SR_RESTORE_IP &&
+ inst->restore) {
+ dprintk(BA_DBG, "Restoring input: %d",
+ inst->saved_input);
+ rc = v4l2_subdev_call(inst->sd, video, s_routing,
+ inst->saved_input, 0, 0);
+ if (rc)
+ dprintk(BA_ERR, "Failed to restore input: %d",
+ inst->saved_input);
+ msm_ba_reset_ip_in_use_from_sd(inst->sd);
+ ba_input = msm_ba_find_input_from_sd(inst->sd,
+ inst->saved_input);
+ if (ba_input)
+ ba_input->in_use = 1;
+ else
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ inst->saved_input, inst->sd->name);
+ inst->restore = 0;
+ inst->saved_input = BA_IP_MAX;
+ dprintk(BA_DBG, "Stream on from save restore");
+ rc = msm_ba_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ } else if (sr == BA_SR_SAVE_IP) {
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input == NULL) {
+ dprintk(BA_ERR, "Could not find input %d",
+ inst->sd_input.index);
+ } else if (ba_input->ba_out_in_use) {
+ inst->restore = 1;
+ inst->saved_input =
+ msm_ba_find_ip_in_use_from_sd(inst->sd);
+ if (inst->saved_input == BA_IP_MAX) {
+ dprintk(BA_ERR, "Could not find input to save");
+ inst->restore = 0;
+ }
+ dprintk(BA_DBG, "Saving input: %d",
+ inst->saved_input);
+ rc = -EBUSY;
+ }
+ } else {
+ dprintk(BA_DBG, "Nothing to do in save and restore");
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_save_restore_input);
+
+void msm_ba_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+static int msm_ba_register_v4l2_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+ struct video_device *vdev;
+ int rc = 0;
+
+ dprintk(BA_DBG, "Enter %s: v4l2_dev 0x%p, v4l2_subdev 0x%p",
+ __func__, v4l2_dev, sd);
+ if (NULL == v4l2_dev || NULL == sd || !sd->name[0]) {
+ dprintk(BA_ERR, "Invalid input");
+ return -EINVAL;
+ }
+ rc = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (rc < 0) {
+ dprintk(BA_ERR,
+ "%s(%d), V4L2 subdev register failed for %s rc: %d",
+ __func__, __LINE__, sd->name, rc);
+ return rc;
+ }
+ if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) {
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ dprintk(BA_ERR, "%s Not enough memory", __func__);
+ return -ENOMEM;
+ }
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_ba_release_subdev_node;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s Error registering video device %s",
+ __func__, sd->name);
+ kfree(vdev);
+ } else {
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.dev.major = VIDEO_MAJOR;
+ sd->entity.info.dev.minor = vdev->minor;
+ sd->entity.name = video_device_node_name(vdev);
+#endif
+ sd->devnode = vdev;
+ }
+ }
+ dprintk(BA_DBG, "Exit %s with rc: %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ rc = msm_ba_register_v4l2_subdev(&ba_ctxt->dev_ctxt->v4l2_dev, sd);
+ if (!rc) {
+ ba_ctxt->dev_ctxt->num_ba_subdevs++;
+ msm_ba_add_inputs(sd);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_register_subdev_node);
+
+static void __msm_ba_sd_unregister(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ mutex_lock(&ba_ctxt->ba_cs);
+
+ v4l2_device_unregister_subdev(sub_dev);
+ ba_ctxt->dev_ctxt->num_ba_subdevs--;
+ msm_ba_del_inputs(sub_dev);
+
+ dprintk(BA_DBG, "%s(%d), BA Unreg Sub Device : num ba devices %d : %s",
+ __func__, __LINE__,
+ ba_ctxt->dev_ctxt->num_ba_subdevs, sub_dev->name);
+
+ mutex_unlock(&ba_ctxt->ba_cs);
+}
+
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ if (!ba_ctxt || !ba_ctxt->dev_ctxt)
+ return -ENODEV;
+ if (!sub_dev)
+ return -EINVAL;
+ __msm_ba_sd_unregister(sub_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_unregister_subdev_node);
+
+static int msm_ba_setup_event_queue(void *inst,
+ struct video_device *pvdev)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ v4l2_fh_init(&ba_inst->event_handler, pvdev);
+ v4l2_fh_add(&ba_inst->event_handler);
+
+ return rc;
+}
+
+int msm_ba_subscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_subscribe(&ba_inst->event_handler, sub,
+ MSM_BA_MAX_EVENTS, NULL);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_subscribe_event);
+
+int msm_ba_unsubscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_unsubscribe(&ba_inst->event_handler, sub);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_unsubscribe_event);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input *ba_input;
+ struct msm_ba_sd_event *ba_sd_event;
+ int bridge_chip_ip;
+
+ if (!sd || !arg) {
+ dprintk(BA_ERR, "%s null v4l2 subdev or arg", __func__);
+ return;
+ }
+
+ bridge_chip_ip = ((int *)((struct v4l2_event *)arg)->u.data)[0];
+ ba_input = msm_ba_find_input_from_sd(sd, bridge_chip_ip);
+ if (!ba_input) {
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ bridge_chip_ip, sd->name);
+ return;
+ }
+
+ ba_sd_event = kzalloc(sizeof(*ba_sd_event), GFP_KERNEL);
+ if (!ba_sd_event) {
+ dprintk(BA_ERR, "%s out of memory", __func__);
+ return;
+ }
+
+ dev_ctxt = get_ba_dev();
+
+ ba_sd_event->sd_event = *(struct v4l2_event *)arg;
+ ((int *)ba_sd_event->sd_event.u.data)[0] = ba_input->ba_ip_idx;
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&ba_sd_event->list, &dev_ctxt->sd_events);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ schedule_delayed_work(&dev_ctxt->sd_events_work, 0);
+}
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int rc = 0;
+
+ dev_ctxt = get_ba_dev();
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+
+ if (!inst) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ return NULL;
+ }
+
+ mutex_init(&inst->inst_cs);
+
+ init_waitqueue_head(&inst->kernel_event_queue);
+ inst->state = MSM_BA_DEV_UNINIT_DONE;
+ inst->dev_ctxt = dev_ctxt;
+ rc = msm_ba_ctrl_init(inst);
+ if (rc) {
+ dprintk(BA_WARN, "Failed to initialize controls: %d", rc);
+ msm_ba_ctrl_deinit(inst);
+ }
+
+ if (!list_empty(&(inst->dev_ctxt->v4l2_dev.subdevs)))
+ inst->sd = list_first_entry(&(inst->dev_ctxt->v4l2_dev.subdevs),
+ struct v4l2_subdev, list);
+
+ msm_ba_setup_event_queue(inst, dev_ctxt->vdev);
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&inst->list, &dev_ctxt->instances);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_INIT;
+ dev_ctxt->state = BA_DEV_INIT_DONE;
+ inst->state = MSM_BA_DEV_INIT_DONE;
+ inst->sd_input.index = 0;
+ inst->event_handler.prio = V4L2_PRIORITY_DEFAULT;
+
+ inst->debugfs_root =
+ msm_ba_debugfs_init_inst(inst, dev_ctxt->debugfs_root);
+
+ inst->ext_ops = ext_ops;
+
+ return inst;
+}
+EXPORT_SYMBOL(msm_ba_open);
+
+int msm_ba_close(void *instance)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_inst *temp;
+ struct msm_ba_dev *dev_ctxt;
+ struct list_head *ptr;
+ struct list_head *next;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ dev_ctxt = inst->dev_ctxt;
+ mutex_lock(&dev_ctxt->dev_cs);
+
+ list_for_each_safe(ptr, next, &dev_ctxt->instances) {
+ temp = list_entry(ptr, struct msm_ba_inst, list);
+ if (temp == inst)
+ list_del(&inst->list);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ msm_ba_ctrl_deinit(inst);
+
+ v4l2_fh_del(&inst->event_handler);
+ v4l2_fh_exit(&inst->event_handler);
+
+ debugfs_remove_recursive(inst->debugfs_root);
+
+ dprintk(BA_DBG, "Closed BA instance: %p", inst);
+ kfree(inst);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_close);
diff --git a/drivers/video/msm/ba/msm_ba_common.c b/drivers/video/msm/ba/msm_ba_common.c
new file mode 100644
index 000000000000..cc8eb2da3e3b
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.c
@@ -0,0 +1,645 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+static struct msm_ba_ctrl msm_ba_ctrls[] = {
+ {
+ .id = MSM_BA_PRIV_SD_NODE_ADDR,
+ .name = "Sub-device Node Address",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 16,
+ .default_value = 0,
+ .step = 2,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+ {
+ .id = MSM_BA_PRIV_FPS,
+ .name = "FPS in Q16 format",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 0x7fffffff,
+ .default_value = 60 << 16,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+};
+
+#define BA_NUM_CTRLS ARRAY_SIZE(msm_ba_ctrls)
+
+/* Assuming den is not zero, max 32 bits */
+#define BA_FRAC_TO_Q16(q, num, den) { \
+ uint32_t pwr; \
+ pwr = ilog2(den); \
+ (q) = (num) << (16 - pwr); \
+ }
+
+struct msm_ba_dev *get_ba_dev(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ mutex_lock(&ba_ctxt->ba_cs);
+ dev_ctxt = ba_ctxt->dev_ctxt;
+ mutex_unlock(&ba_ctxt->ba_cs);
+
+ return dev_ctxt;
+}
+
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event)
+{
+ v4l2_event_queue_fh(&inst->event_handler, sd_event);
+ wake_up(&inst->kernel_event_queue);
+}
+
+static void msm_ba_print_event(struct v4l2_event *sd_event)
+{
+ switch (sd_event->type) {
+ case V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED:
+ dprintk(BA_DBG, "Port settings changed for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK:
+ dprintk(BA_DBG, "Signal in lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK:
+ dprintk(BA_DBG, "Signal lost lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SOURCE_CHANGE:
+ dprintk(BA_DBG, "Video source change 0x%x",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_HPD:
+ dprintk(BA_DBG, "HDMI hotplug detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE:
+ dprintk(BA_DBG, "HDMI CEC message!");
+ break;
+ case V4L2_EVENT_MSM_BA_CP:
+ dprintk(BA_DBG, "Content protection detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_CABLE_DETECT:
+ dprintk(BA_DBG, "Cable detected: %d on ip_idx %d",
+ ((int *)sd_event->u.data)[1],
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_ERROR:
+ dprintk(BA_DBG, "Subdev error %d!",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown event: 0x%x", sd_event->type);
+ break;
+ }
+}
+
+static void msm_ba_signal_sessions_event(struct v4l2_event *sd_event)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int *ptr;
+
+ msm_ba_print_event(sd_event);
+ dev_ctxt = get_ba_dev();
+ ptr = (int *)sd_event->u.data;
+
+ list_for_each_entry(inst, &(dev_ctxt->instances), list) {
+ if (inst->ext_ops && inst->ext_ops->msm_ba_cb)
+ inst->ext_ops->msm_ba_cb(
+ inst, sd_event->id, (void *)&ptr[1]);
+ else
+ msm_ba_queue_v4l2_event(inst, sd_event);
+ }
+}
+
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ if (!list_empty(&dev_ctxt->sd_events)) {
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &(dev_ctxt->sd_events), list) {
+ msm_ba_signal_sessions_event(&ba_sd_event->sd_event);
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ break;
+ }
+ } else {
+ dprintk(BA_ERR, "%s - queue empty!!!", __func__);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+}
+
+struct v4l2_subdev *msm_ba_sd_find(const char *name)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev *sd_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&(dev_ctxt->v4l2_dev.subdevs))) {
+ list_for_each_entry(sd, &(dev_ctxt->v4l2_dev.subdevs), list)
+ if (!strcmp(name, sd->name)) {
+ sd_out = sd;
+ break;
+ }
+ }
+ return sd_out;
+}
+
+void msm_ba_add_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input_config *msm_ba_inp_cfg = NULL;
+ int i;
+ int str_length = 0;
+ int rc;
+ int start_index = 0;
+ int end_index = 0;
+ int dev_id = 0;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&dev_ctxt->inputs))
+ start_index = dev_ctxt->num_inputs;
+
+ msm_ba_inp_cfg = dev_ctxt->msm_ba_inp_cfg;
+ dev_id = msm_ba_inp_cfg[start_index].ba_out;
+ end_index = dev_ctxt->num_config_inputs;
+ for (i = start_index; i < end_index; i++) {
+ str_length = strlen(msm_ba_inp_cfg[i].sd_name);
+ rc = memcmp(sd->name, msm_ba_inp_cfg[i].sd_name, str_length);
+ if (!rc && dev_id == msm_ba_inp_cfg[i].ba_out) {
+ input = kzalloc(sizeof(*input), GFP_KERNEL);
+
+ if (!input) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ break;
+ }
+ input->input_type = msm_ba_inp_cfg[i].input_type;
+ strlcpy(input->name, msm_ba_inp_cfg[i].name,
+ sizeof(input->name));
+ input->bridge_chip_ip = msm_ba_inp_cfg[i].ba_ip;
+ input->ba_out = msm_ba_inp_cfg[i].ba_out;
+ input->ba_node_addr = msm_ba_inp_cfg[i].ba_node;
+ input->ba_ip_idx = i;
+ input->input_user_type =
+ msm_ba_inp_cfg[i].input_user_type;
+ input->sd = sd;
+ list_add_tail(&input->list, &dev_ctxt->inputs);
+ dev_ctxt->num_inputs++;
+ dprintk(BA_DBG, "Add input: name %s on %d",
+ input->name, input->ba_out);
+ }
+ }
+}
+
+void msm_ba_del_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct list_head *ptr;
+ struct list_head *next;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ list_for_each_safe(ptr, next, &(dev_ctxt->inputs)) {
+ input = list_entry(ptr, struct msm_ba_input, list);
+ if (input->sd == sd) {
+ list_del(&input->list);
+ kfree(input);
+ }
+ }
+}
+
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd)
+ input->ba_out_in_use = on;
+ }
+}
+
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int ba_ip = BA_IP_MAX;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ ba_ip = input->bridge_chip_ip;
+ break;
+ }
+ }
+ return ba_ip;
+}
+
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ input->in_use = 0;
+ break;
+ }
+ }
+}
+
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->bridge_chip_ip == bridge_chip_ip) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->ba_ip_idx == ba_input_idx) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_output(int ba_output)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list) {
+ if (input->ba_out == ba_output) {
+ input_out = input;
+ break;
+ }
+ }
+ }
+ return input_out;
+}
+
+int msm_ba_g_fps(void *instance, int *fps_q16)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev_frame_interval sd_frame_int;
+ int rc = 0;
+
+ if (!inst || !fps_q16)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, g_frame_interval, &sd_frame_int);
+ if (rc) {
+ dprintk(BA_ERR, "get frame interval failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ /* subdevice returns frame interval not fps! */
+ if (sd_frame_int.interval.numerator) {
+ BA_FRAC_TO_Q16(*fps_q16,
+ sd_frame_int.interval.denominator,
+ sd_frame_int.interval.numerator);
+ } else {
+ *fps_q16 =
+ sd_frame_int.interval.denominator << 16;
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_try_get_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ctrl->val = ba_input->ba_node_addr;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x ctrl->val:%d",
+ __func__, ctrl->id, ctrl->val);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case MSM_BA_PRIV_FPS:
+ rc = msm_ba_g_fps(inst, &ctrl->val);
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_try_set_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ba_input->ba_node_addr = ctrl->val;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x node_addr:%d",
+ __func__, ctrl->id, ba_input->ba_node_addr);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ for (c = 0; c < ctrl->ncontrols; ++c) {
+ if (ctrl->cluster[c]->is_new) {
+ rc = msm_ba_try_set_ctrl(inst, ctrl->cluster[c]);
+ if (rc) {
+ dprintk(BA_ERR, "Failed setting 0x%x",
+ ctrl->cluster[c]->id);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ for (c = 0; c < master->ncontrols; c++) {
+ if (master->cluster[c]->id == ctrl->id) {
+ rc = msm_ba_try_get_ctrl(inst, ctrl);
+ if (rc) {
+ dprintk(BA_ERR, "Failed getting 0x%x",
+ ctrl->id);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+static const struct v4l2_ctrl_ops msm_ba_ctrl_ops = {
+
+ .g_volatile_ctrl = msm_ba_op_g_volatile_ctrl,
+ .s_ctrl = msm_ba_op_s_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_ba_get_ctrl_ops(void)
+{
+ return &msm_ba_ctrl_ops;
+}
+
+static struct v4l2_ctrl **msm_ba_get_super_cluster(struct msm_ba_inst *inst,
+ int *size)
+{
+ int c = 0;
+ int sz = 0;
+ struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
+ BA_NUM_CTRLS, GFP_KERNEL);
+
+ if (!size || !cluster || !inst)
+ return NULL;
+
+ for (c = 0; c < BA_NUM_CTRLS; c++)
+ cluster[sz++] = inst->ctrls[c];
+
+ *size = sz;
+ return cluster;
+}
+
+/*
+ * Controls init function.
+ * Caller is expected to call deinit in case of failure.
+ */
+int msm_ba_ctrl_init(struct msm_ba_inst *inst)
+{
+ int idx = 0;
+ struct v4l2_ctrl_config ctrl_cfg = {0};
+ int rc = 0;
+ int cluster_size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s - invalid instance", __func__);
+ return -EINVAL;
+ }
+
+ inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * BA_NUM_CTRLS,
+ GFP_KERNEL);
+ if (!inst->ctrls) {
+ dprintk(BA_ERR, "%s - failed to allocate ctrl", __func__);
+ return -ENOMEM;
+ }
+
+ rc = v4l2_ctrl_handler_init(&inst->ctrl_handler, BA_NUM_CTRLS);
+
+ if (rc) {
+ dprintk(BA_ERR, "CTRL ERR: Control handler init failed, %d",
+ inst->ctrl_handler.error);
+ return rc;
+ }
+ for (; idx < BA_NUM_CTRLS; idx++) {
+ struct v4l2_ctrl *ctrl = NULL;
+ if (BA_IS_PRIV_CTRL(msm_ba_ctrls[idx].id)) {
+ /* add private control */
+ ctrl_cfg.def = msm_ba_ctrls[idx].default_value;
+ ctrl_cfg.flags = 0;
+ ctrl_cfg.id = msm_ba_ctrls[idx].id;
+ ctrl_cfg.max = msm_ba_ctrls[idx].maximum;
+ ctrl_cfg.min = msm_ba_ctrls[idx].minimum;
+ ctrl_cfg.menu_skip_mask =
+ msm_ba_ctrls[idx].menu_skip_mask;
+ ctrl_cfg.name = msm_ba_ctrls[idx].name;
+ ctrl_cfg.ops = &msm_ba_ctrl_ops;
+ ctrl_cfg.step = msm_ba_ctrls[idx].step;
+ ctrl_cfg.type = msm_ba_ctrls[idx].type;
+ ctrl_cfg.qmenu = msm_ba_ctrls[idx].qmenu;
+
+ ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+ &ctrl_cfg, NULL);
+ } else {
+ if (msm_ba_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(
+ &inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].menu_skip_mask,
+ msm_ba_ctrls[idx].default_value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].minimum,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].step,
+ msm_ba_ctrls[idx].default_value);
+ }
+ }
+
+ if (!ctrl) {
+ dprintk(BA_ERR, "%s - invalid ctrl", __func__);
+ return -EINVAL;
+ }
+
+ rc = inst->ctrl_handler.error;
+ if (rc) {
+ dprintk(BA_ERR,
+ "Error adding ctrl (%s) to ctrl handle, %d",
+ msm_ba_ctrls[idx].name,
+ inst->ctrl_handler.error);
+ return rc;
+ }
+
+ switch (msm_ba_ctrls[idx].id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ case MSM_BA_PRIV_FPS:
+ ctrl->flags |= msm_ba_ctrls[idx].flags;
+ break;
+ }
+
+ inst->ctrls[idx] = ctrl;
+ }
+
+ /* Construct a super cluster of all controls */
+ inst->cluster = msm_ba_get_super_cluster(inst, &cluster_size);
+ if (!inst->cluster || !cluster_size) {
+ dprintk(BA_WARN,
+ "Failed to setup super cluster");
+ return -EINVAL;
+ }
+ v4l2_ctrl_cluster(cluster_size, inst->cluster);
+
+ return rc;
+}
+
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst)
+{
+ kfree(inst->ctrls);
+ kfree(inst->cluster);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+}
diff --git a/drivers/video/msm/ba/msm_ba_common.h b/drivers/video/msm/ba/msm_ba_common.h
new file mode 100644
index 000000000000..64ef2ab7537e
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_COMMON_H_
+#define _MSM_BA_COMMON_H_
+
+#include "msm_ba_internal.h"
+
+#define BA_IS_PRIV_CTRL(idx) (\
+ (V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_USER) && \
+ V4L2_CTRL_DRIVER_PRIV(idx))
+
+struct msm_ba_dev *get_ba_dev(void);
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event);
+struct v4l2_subdev *msm_ba_sd_find(const char *name);
+void msm_ba_add_inputs(struct v4l2_subdev *sd);
+void msm_ba_del_inputs(struct v4l2_subdev *sd);
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on);
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd);
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd);
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip);
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx);
+struct msm_ba_input *msm_ba_find_output(int ba_output);
+int msm_ba_g_fps(void *instance, int *fps_q16);
+int msm_ba_ctrl_init(struct msm_ba_inst *inst);
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_debug.c b/drivers/video/msm/ba/msm_ba_debug.c
new file mode 100644
index 000000000000..aa5109eb8e64
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_ba_debug.h"
+
+#define MAX_DBG_BUF_SIZE 1008
+
+int msm_ba_debug = BA_ERR | BA_WARN;
+int msm_ba_debug_out = BA_OUT_PRINTK;
+
+struct debug_buffer {
+ char ptr[MAX_DBG_BUF_SIZE];
+ char *curr;
+ u32 filled_size;
+};
+
+#define INIT_DBG_BUF(__buf) ({ \
+ __buf->curr = __buf->ptr;\
+ __buf->filled_size = 0; \
+})
+
+static int dev_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+{
+ va_list args;
+ u32 size = 0;
+ size_t buf_size = 0;
+
+ if (MAX_DBG_BUF_SIZE - 1 > buffer->filled_size) {
+ buf_size = MAX_DBG_BUF_SIZE - 1 - buffer->filled_size;
+ va_start(args, fmt);
+ size = vscnprintf(buffer->curr, buf_size, fmt, args);
+ va_end(args);
+ buffer->curr += size;
+ buffer->filled_size += size;
+ }
+ return size;
+}
+
+static ssize_t dev_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_dev *dev_ctxt = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, dev: 0x%p", dev_ctxt);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "DEV: 0x%p", dev_ctxt);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "state: %d", dev_ctxt->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations dev_info_fops = {
+ .open = dev_info_open,
+ .read = dev_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_drv(void)
+{
+ bool ok = false;
+ struct dentry *dir = debugfs_create_dir(BA_DBG_LABEL, NULL);
+ struct ba_ctxt *ba_ctxt;
+
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ goto failed_create_dir;
+ }
+
+#define __debugfs_create(__type, __name, __value) ({ \
+ struct dentry *f = debugfs_create_##__type(__name, S_IRUGO | S_IWUSR, \
+ dir, __value); \
+ if (IS_ERR_OR_NULL(f)) { \
+ dprintk(BA_ERR, "Failed creating debugfs file '%pd/%s'", \
+ dir, __name); \
+ f = NULL; \
+ } \
+ f; \
+})
+
+ ok =
+ __debugfs_create(x32, "debug_level", &msm_ba_debug) &&
+ __debugfs_create(u32, "debug_output", &msm_ba_debug_out);
+
+#undef __debugfs_create
+
+ if (!ok)
+ goto failed_create_dir;
+
+ return dir;
+
+failed_create_dir:
+ if (dir) {
+ ba_ctxt = msm_ba_get_ba_context();
+ debugfs_remove_recursive(ba_ctxt->debugfs_root);
+ }
+ return NULL;
+}
+
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, core: %p", dev_ctxt);
+ goto failed_create_dir;
+ }
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "dev_%p", dev_ctxt);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, dev_ctxt,
+ &dev_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+failed_create_dir:
+ return dir;
+}
+
+static int inst_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t inst_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_inst *inst = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, dev: %p", inst);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "INSTANCE: %p (%s)", inst,
+ "BA device");
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "dev: %p", inst->dev_ctxt);
+ write_str(dbg_buf, "state: %d", inst->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations inst_info_fops = {
+ .open = inst_info_open,
+ .read = inst_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, inst: %p", inst);
+ goto failed_create_dir;
+ }
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+ inst->debug.pdata[SESSION_INIT].sampling = true;
+failed_create_dir:
+ return dir;
+}
diff --git a/drivers/video/msm/ba/msm_ba_debug.h b/drivers/video/msm/ba/msm_ba_debug.h
new file mode 100644
index 000000000000..baabb712cc58
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_BA_DEBUG__
+#define __MSM_BA_DEBUG__
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include "msm_ba_internal.h"
+
+#ifndef BA_DBG_LABEL
+#define BA_DBG_LABEL "msm_ba"
+#endif
+
+#define BA_DBG_TAG BA_DBG_LABEL "(%d): %4s: "
+
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x001F
+ */
+
+enum ba_msg_prio {
+ BA_ERR = 0x0001,
+ BA_WARN = 0x0002,
+ BA_INFO = 0x0004,
+ BA_DBG = 0x0008,
+ BA_PROF = 0x0010
+};
+
+enum ba_msg_out {
+ BA_OUT_PRINTK = 0,
+ BA_OUT_FTRACE
+};
+
+extern int msm_ba_debug;
+extern int msm_ba_debug_out;
+
+#define BA_MSG_PRIO2STRING(__level) ({ \
+ char *__str; \
+ \
+ __str = (__level == BA_ERR ? "err" : \
+ (__level == BA_WARN ? "warn" : \
+ (__level == BA_INFO ? "info" : \
+ (__level == BA_DBG ? "dbg" : \
+ (__level == BA_PROF ? "prof" : "????"))))); \
+ \
+ __str; \
+ })
+
+#define dprintk(__level, __fmt, arg...) \
+ do { \
+ if (msm_ba_debug & __level) { \
+ if (msm_ba_debug_out == BA_OUT_PRINTK) { \
+ pr_info(BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } else if (msm_ba_debug_out == BA_OUT_FTRACE) { \
+ trace_printk(KERN_DEBUG BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } \
+ } \
+ } while (0)
+
+
+struct dentry *msm_ba_debugfs_init_drv(void);
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent);
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_internal.h b/drivers/video/msm/ba/msm_ba_internal.h
new file mode 100644
index 000000000000..bd52e8e400ce
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_internal.h
@@ -0,0 +1,220 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_INTERNAL_H_
+#define _MSM_BA_INTERNAL_H_
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_ba.h>
+
+#define MSM_BA_DRV_NAME "msm_ba_driver"
+
+#define MSM_BA_VERSION KERNEL_VERSION(0, 0, 1)
+
+#define MAX_NAME_LENGTH 64
+
+#define MAX_DEBUGFS_NAME MAX_NAME_LENGTH
+
+#define DEFAULT_WIDTH 720
+#define DEFAULT_HEIGHT 507
+
+enum ba_dev_state {
+ BA_DEV_UNINIT = 0,
+ BA_DEV_LOADED,
+ BA_DEV_INIT,
+ BA_DEV_INIT_DONE,
+ BA_DEV_INVALID
+};
+
+enum instance_state {
+ MSM_BA_DEV_UNINIT_DONE = 0x0001,
+ MSM_BA_DEV_INIT,
+ MSM_BA_DEV_INIT_DONE,
+ MSM_BA_OPEN,
+ MSM_BA_OPEN_DONE,
+ MSM_BA_START,
+ MSM_BA_START_DONE,
+ MSM_BA_STOP,
+ MSM_BA_STOP_DONE,
+ MSM_BA_CLOSE,
+ MSM_BA_CLOSE_DONE,
+ MSM_BA_DEV_UNINIT,
+ MSM_BA_DEV_INVALID
+};
+
+struct ba_ctxt {
+ struct mutex ba_cs;
+ struct msm_ba_dev *dev_ctxt;
+ struct dentry *debugfs_root;
+};
+
+enum profiling_points {
+ SYS_INIT = 0,
+ SESSION_INIT,
+ MAX_PROFILING_POINTS
+};
+
+struct profile_data {
+ int start;
+ int stop;
+ int cumulative;
+ char name[64];
+ int sampling;
+ int average;
+};
+
+struct msm_ba_debug {
+ struct profile_data pdata[MAX_PROFILING_POINTS];
+ int profile;
+ int samples;
+};
+
+struct msm_ba_dev_capability {
+ u32 capability_set;
+};
+
+enum msm_ba_ip_type {
+ BA_INPUT_CVBS = 0,
+ BA_INPUT_COMPONENT,
+ BA_INPUT_YC,
+ BA_INPUT_RGB,
+ BA_INPUT_HDMI,
+ BA_INPUT_MHL,
+ BA_INPUT_DVI,
+ BA_INPUT_TTL,
+ BA_INPUT_MAX = 0xffffffff
+};
+
+enum msm_ba_input_usr_type {
+ BA_INPUT_USERTYPE_KERNEL = 0,
+ BA_INPUT_USERTYPE_USER,
+ BA_INPUT_USERTYPE_MAX = 0xffffffff
+};
+
+struct msm_ba_input_config {
+ enum msm_ba_ip_type input_type;
+ const char *name;
+ int ba_ip;
+ int ba_out;
+ const char *sd_name;
+ int ba_node;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_sd_event {
+ struct list_head list;
+ struct v4l2_event sd_event;
+};
+
+struct msm_ba_input {
+ struct list_head list;
+ enum msm_ba_ip_type input_type;
+ unsigned int name_index;
+ char name[32];
+ int bridge_chip_ip;
+ int ba_node_addr;
+ int ba_out;
+ int ba_ip_idx;
+ struct v4l2_subdev *sd;
+ int signal_status;
+ int in_use;
+ int ba_out_in_use;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_dev {
+ struct mutex dev_cs;
+
+ enum ba_dev_state state;
+
+ struct list_head inputs;
+ uint32_t num_inputs;
+
+ /* V4L2 Framework */
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct media_device mdev;
+
+ struct list_head instances;
+
+ /* BA v4l2 sub devs */
+ uint32_t num_ba_subdevs;
+ struct list_head sd_events;
+ struct delayed_work sd_events_work;
+
+ /* BA input config list */
+ struct msm_ba_input_config *msm_ba_inp_cfg;
+ uint32_t num_config_inputs;
+
+ struct dentry *debugfs_root;
+};
+
+struct msm_ba_inst {
+ struct list_head list;
+ struct mutex inst_cs;
+ struct msm_ba_dev *dev_ctxt;
+
+ struct v4l2_input sd_input;
+ struct v4l2_output sd_output;
+ struct v4l2_subdev *sd;
+ int state;
+ int saved_input;
+ int restore;
+
+ struct v4l2_fh event_handler;
+ wait_queue_head_t kernel_event_queue;
+
+ struct v4l2_ctrl **cluster;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl **ctrls;
+
+ struct msm_ba_debug debug;
+ struct dentry *debugfs_root;
+
+ const struct msm_ba_ext_ops *ext_ops;
+};
+
+struct msm_ba_ctrl {
+ u32 id;
+ char name[MAX_NAME_LENGTH];
+ enum v4l2_ctrl_type type;
+ s32 minimum;
+ s32 maximum;
+ s32 default_value;
+ u32 step;
+ u32 menu_skip_mask;
+ u32 flags;
+ const char * const *qmenu;
+};
+
+struct ba_ctxt *msm_ba_get_ba_context(void);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg);
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_v4l2_ba.c b/drivers/video/msm/ba/msm_v4l2_ba.c
new file mode 100644
index 000000000000..0cd3fc3b238f
--- /dev/null
+++ b/drivers/video/msm/ba/msm_v4l2_ba.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+
+#define BASE_DEVICE_NUMBER 35
+
+static struct ba_ctxt *gp_ba_ctxt;
+
+struct ba_ctxt *msm_ba_get_ba_context(void)
+{
+ return gp_ba_ctxt;
+}
+
+void msm_ba_set_ba_context(struct ba_ctxt *ba_ctxt)
+{
+ gp_ba_ctxt = ba_ctxt;
+}
+
+static inline struct msm_ba_inst *get_ba_inst(struct file *filp, void *fh)
+{
+ return container_of(filp->private_data,
+ struct msm_ba_inst, event_handler);
+}
+
+static int msm_ba_v4l2_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = msm_ba_open(NULL);
+ if (!ba_inst) {
+ dprintk(BA_ERR,
+ "Failed to create video instance");
+ return -ENOMEM;
+ }
+ clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags);
+ filp->private_data = &(ba_inst->event_handler);
+ return 0;
+}
+
+static int msm_ba_v4l2_close(struct file *filp)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = get_ba_inst(filp, NULL);
+
+ rc = msm_ba_close(ba_inst);
+ return rc;
+}
+
+static int msm_ba_v4l2_querycap(struct file *filp, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, fh);
+
+ return msm_ba_querycap((void *)ba_inst, cap);
+}
+
+int msm_ba_v4l2_enum_input(struct file *file, void *fh,
+ struct v4l2_input *input)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_input((void *)ba_inst, input);
+}
+
+int msm_ba_v4l2_g_input(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_input(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_output(struct file *file, void *fh,
+ struct v4l2_output *output)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_output((void *)ba_inst, output);
+}
+
+int msm_ba_v4l2_g_output(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_output(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_s_ext_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ext_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamon((void *)ba_inst, i);
+}
+
+int msm_ba_v4l2_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamoff((void *)ba_inst, i);
+}
+
+static int msm_ba_v4l2_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_subscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_unsubscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_parm((void *)ba_inst, a);
+}
+
+static int msm_ba_v4l2_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = {
+ .vidioc_querycap = msm_ba_v4l2_querycap,
+ .vidioc_enum_fmt_vid_cap = msm_ba_v4l2_enum_fmt,
+ .vidioc_enum_fmt_vid_out = msm_ba_v4l2_enum_fmt,
+ .vidioc_s_fmt_vid_cap = msm_ba_v4l2_s_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = msm_ba_v4l2_s_fmt,
+ .vidioc_g_fmt_vid_cap = msm_ba_v4l2_g_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = msm_ba_v4l2_g_fmt,
+ .vidioc_streamon = msm_ba_v4l2_streamon,
+ .vidioc_streamoff = msm_ba_v4l2_streamoff,
+ .vidioc_s_ctrl = msm_ba_v4l2_s_ctrl,
+ .vidioc_g_ctrl = msm_ba_v4l2_g_ctrl,
+ .vidioc_s_ext_ctrls = msm_ba_v4l2_s_ext_ctrl,
+ .vidioc_subscribe_event = msm_ba_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = msm_ba_v4l2_unsubscribe_event,
+ .vidioc_s_parm = msm_ba_v4l2_s_parm,
+ .vidioc_g_parm = msm_ba_v4l2_g_parm,
+ .vidioc_enum_input = msm_ba_v4l2_enum_input,
+ .vidioc_g_input = msm_ba_v4l2_g_input,
+ .vidioc_s_input = msm_ba_v4l2_s_input,
+ .vidioc_enum_output = msm_ba_v4l2_enum_output,
+ .vidioc_g_output = msm_ba_v4l2_g_output,
+ .vidioc_s_output = msm_ba_v4l2_s_output,
+};
+
+static unsigned int msm_ba_v4l2_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, NULL);
+
+ return msm_ba_poll((void *)ba_inst, filp, pt);
+}
+
+void msm_ba_release_video_device(struct video_device *pvdev)
+{
+}
+
+static const struct v4l2_file_operations msm_ba_v4l2_ba_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_ba_v4l2_open,
+ .release = msm_ba_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = msm_ba_v4l2_poll,
+};
+
+static int parse_ba_dt(struct platform_device *pdev)
+{
+ uint32_t profile_count = 0;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child_np = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct ba_ctxt *ba_ctxt = msm_ba_get_ba_context();
+ char *key = NULL;
+ uint32_t err = 0, i = 0;
+
+ dev_ctxt = ba_ctxt->dev_ctxt;
+
+ profile_count = of_get_child_count(np);
+ if (profile_count == 0) {
+ dprintk(BA_ERR, "%s: Error reading DT. node=%s",
+ __func__, np->full_name);
+ return -ENODEV;
+ }
+
+ dev_ctxt->msm_ba_inp_cfg = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_ba_input_config) * profile_count,
+ GFP_KERNEL);
+ if (!dev_ctxt->msm_ba_inp_cfg)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(np, child_np) {
+ key = "qcom,type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_type);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-input";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_ip);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-output";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_out);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,sd-name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].sd_name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-node";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_node);
+ if (err)
+ goto read_fail;
+
+
+ key = "qcom,user-type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_user_type);
+ if (err)
+ goto read_fail;
+
+ i++;
+ }
+ dev_ctxt->num_config_inputs = i;
+
+read_fail:
+ if (err) {
+ dprintk(BA_INFO, "%s: Error reading DT. node=%s key=%s",
+ __func__, np->full_name, key);
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+
+ dev_ctxt->num_config_inputs = 0;
+ }
+
+ return err;
+}
+
+static int msm_ba_device_init(struct platform_device *pdev,
+ struct msm_ba_dev **ret_dev_ctxt)
+{
+ struct msm_ba_dev *dev_ctxt;
+ int nr = BASE_DEVICE_NUMBER;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ if ((ret_dev_ctxt == NULL) ||
+ (*ret_dev_ctxt != NULL) ||
+ (pdev == NULL)) {
+ dprintk(BA_ERR, "%s(%d) Invalid params %p %p %p",
+ __func__, __LINE__,
+ ret_dev_ctxt, *ret_dev_ctxt, pdev);
+ return -EINVAL;
+ }
+
+ dev_ctxt = devm_kzalloc(&pdev->dev, sizeof(struct msm_ba_dev),
+ GFP_KERNEL);
+ if (dev_ctxt == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dev_ctxt);
+
+ INIT_LIST_HEAD(&dev_ctxt->inputs);
+ INIT_LIST_HEAD(&dev_ctxt->instances);
+ INIT_LIST_HEAD(&dev_ctxt->sd_events);
+ INIT_DELAYED_WORK(&dev_ctxt->sd_events_work,
+ msm_ba_subdev_event_hndlr_delayed);
+ mutex_init(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_UNINIT;
+
+ strlcpy(dev_ctxt->v4l2_dev.name, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->v4l2_dev.name));
+ dev_ctxt->v4l2_dev.dev = &pdev->dev;
+ dev_ctxt->v4l2_dev.notify = msm_ba_subdev_event_hndlr;
+
+ rc = v4l2_device_register(dev_ctxt->v4l2_dev.dev, &dev_ctxt->v4l2_dev);
+ if (!rc) {
+ dev_ctxt->vdev = video_device_alloc();
+ if (dev_ctxt->vdev == NULL) {
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ rc = -ENOMEM;
+ } else {
+ strlcpy(dev_ctxt->vdev->name,
+ pdev->name, sizeof(dev_ctxt->vdev->name));
+ dev_ctxt->vdev->v4l2_dev = &dev_ctxt->v4l2_dev;
+ dev_ctxt->vdev->release = msm_ba_release_video_device;
+ dev_ctxt->vdev->fops = &msm_ba_v4l2_ba_fops;
+ dev_ctxt->vdev->ioctl_ops = &msm_ba_v4l2_ioctl_ops;
+ dev_ctxt->vdev->minor = nr;
+ dev_ctxt->vdev->vfl_type = VFL_TYPE_GRABBER;
+
+ video_set_drvdata(dev_ctxt->vdev, &dev_ctxt);
+
+ strlcpy(dev_ctxt->mdev.model, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->mdev.model));
+ dev_ctxt->mdev.dev = &pdev->dev;
+ rc = media_device_register(&dev_ctxt->mdev);
+ dev_ctxt->v4l2_dev.mdev = &dev_ctxt->mdev;
+ rc = media_entity_init(&dev_ctxt->vdev->entity,
+ 0, NULL, 0);
+ dev_ctxt->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ dev_ctxt->vdev->entity.group_id = 2;
+
+ rc = video_register_device(dev_ctxt->vdev,
+ VFL_TYPE_GRABBER, nr);
+ if (!rc) {
+ dev_ctxt->vdev->entity.name =
+ video_device_node_name(dev_ctxt->vdev);
+ *ret_dev_ctxt = dev_ctxt;
+ } else {
+ dprintk(BA_ERR,
+ "Failed to register BA video device");
+ }
+ }
+ } else {
+ dprintk(BA_ERR, "Failed to register v4l2 device");
+ }
+
+ if (rc) {
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_probe(struct platform_device *pdev)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s: pdev %p device id = %d",
+ __func__, pdev, pdev->id);
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context not yet created");
+ return -EINVAL;
+ }
+ rc = msm_ba_device_init(pdev, &ba_ctxt->dev_ctxt);
+ if (rc)
+ dprintk(BA_ERR, "Failed to init device");
+ else
+ ba_ctxt->dev_ctxt->debugfs_root = msm_ba_debugfs_init_dev(
+ ba_ctxt->dev_ctxt, ba_ctxt->debugfs_root);
+
+ rc = parse_ba_dt(pdev);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s: devicetree error. Exit init", __func__);
+ return rc;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_remove(struct platform_device *pdev)
+{
+ struct msm_ba_dev *dev_ctxt = platform_get_drvdata(pdev);
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+ int rc = 0;
+
+ if (dev_ctxt == NULL) {
+ dprintk(BA_ERR, "%s invalid device", __func__);
+ rc = -EINVAL;
+ } else {
+ video_unregister_device(dev_ctxt->vdev);
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ cancel_delayed_work_sync(&dev_ctxt->sd_events_work);
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &dev_ctxt->sd_events, list) {
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ }
+
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_create(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt != NULL) {
+ dprintk(BA_ERR, "BA context already created");
+ return -EINVAL;
+ }
+ ba_ctxt = kzalloc(sizeof(struct ba_ctxt), GFP_KERNEL);
+
+ if (ba_ctxt == NULL)
+ return -ENOMEM;
+
+ memset(ba_ctxt, 0x00, sizeof(struct ba_ctxt));
+
+ mutex_init(&ba_ctxt->ba_cs);
+ ba_ctxt->debugfs_root = msm_ba_debugfs_init_drv();
+ if (!ba_ctxt->debugfs_root)
+ dprintk(BA_ERR,
+ "Failed to create debugfs for msm_ba");
+
+ msm_ba_set_ba_context(ba_ctxt);
+
+ dprintk(BA_DBG, "%s(%d), BA create complete",
+ __func__, __LINE__);
+
+ return rc;
+}
+
+int msm_ba_destroy(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context non existent");
+ return -EINVAL;
+ }
+
+ if (ba_ctxt->dev_ctxt != NULL) {
+ dprintk(BA_ERR, "Device instances exist on BA context");
+ return -EBUSY;
+ }
+ mutex_destroy(&ba_ctxt->ba_cs);
+
+ kfree(ba_ctxt);
+ ba_ctxt = NULL;
+ msm_ba_set_ba_context(ba_ctxt);
+
+ return rc;
+}
+
+static const struct of_device_id msm_ba_dt_match[] = {
+ {.compatible = "qcom,msm-ba"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ba_dt_match);
+
+static struct platform_driver msm_ba_driver = {
+ .probe = msm_ba_probe,
+ .remove = msm_ba_remove,
+ .driver = {
+ .name = "msm_ba_v4l2",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ba_dt_match,
+ },
+};
+
+static int __init msm_ba_mod_init(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ rc = msm_ba_create();
+ if (!rc) {
+ rc = platform_driver_register(&msm_ba_driver);
+ if (rc) {
+ dprintk(BA_ERR,
+ "Failed to register platform driver");
+ msm_ba_destroy();
+ }
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static void __exit msm_ba_mod_exit(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ platform_driver_unregister(&msm_ba_driver);
+ rc = msm_ba_destroy();
+ dprintk(BA_INFO, "Exit %s", __func__);
+}
+
+module_init(msm_ba_mod_init);
+module_exit(msm_ba_mod_exit);
+
diff --git a/include/dt-bindings/regulator/max20010.h b/include/dt-bindings/regulator/max20010.h
new file mode 100644
index 000000000000..492e7287216f
--- /dev/null
+++ b/include/dt-bindings/regulator/max20010.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_REGULATOR_MAX20010_H
+#define _DT_BINDINGS_REGULATOR_MAX20010_H
+
+/* Regulator operating modes */
+#define MAX20010_OPMODE_SYNC 0
+#define MAX20010_OPMODE_FPWM 8
+
+#endif /* _DT_BINDINGS_REGULATOR_MAX20010_H */
diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h
index 68f2dd993170..dc513fbab580 100644
--- a/include/linux/hdcp_qseecom.h
+++ b/include/linux/hdcp_qseecom.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -125,6 +125,7 @@ struct hdcp_txmtr_ops {
struct hdcp_client_ops {
int (*wakeup)(struct hdmi_hdcp_wakeup_data *data);
+ void (*notify_lvl_change)(void *client_ctx, int min_lvl);
};
enum hdcp_device_type {
@@ -146,5 +147,6 @@ void hdcp_library_deregister(void *phdcpcontext);
bool hdcp1_check_if_supported_load_app(void);
int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb);
int hdcp1_set_enc(bool enable);
-
+void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp);
+void hdcp1_notify_topology(void);
#endif /* __HDCP_QSEECOM_H */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 279411c42ded..aea4c0f2ef5f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -823,6 +823,8 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
}
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
void mmc_retune_timer_stop(struct mmc_host *host);
static inline void mmc_retune_needed(struct mmc_host *host)
diff --git a/include/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h
index 2083e7b5da25..1287d2b73bf8 100755
--- a/include/linux/soundwire/soundwire.h
+++ b/include/linux/soundwire/soundwire.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,7 +196,6 @@ static inline struct swr_device *to_swr_device(struct device *dev)
* @shutdown: standard shutdown callback used during power down/halt
* @suspend: standard suspend callback used during system suspend
* @resume: standard resume callback used during system resume
- * @startup: additional init operation for slave devices
* @driver: soundwire device drivers should initialize name and
* owner field of this structure
* @id_table: list of soundwire devices supported by this driver
@@ -210,7 +209,6 @@ struct swr_driver {
int (*device_up)(struct swr_device *swr);
int (*device_down)(struct swr_device *swr);
int (*reset_device)(struct swr_device *swr);
- int (*startup)(struct swr_device *swr);
struct device_driver driver;
const struct swr_device_id *id_table;
};
@@ -309,4 +307,6 @@ extern int swr_reset_device(struct swr_device *swr_dev);
extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num,
bool enable);
extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num);
+
+extern void swr_remove_device(struct swr_device *swr_dev);
#endif /* _LINUX_SOUNDWIRE_H */
diff --git a/include/media/msm_ba.h b/include/media/msm_ba.h
new file mode 100644
index 000000000000..1b51e3f754d8
--- /dev/null
+++ b/include/media/msm_ba.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_H_
+#define _MSM_BA_H_
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <linux/poll.h>
+
+enum msm_ba_ip {
+ BA_IP_CVBS_0 = 0,
+ BA_IP_CVBS_1,
+ BA_IP_CVBS_2,
+ BA_IP_CVBS_3,
+ BA_IP_CVBS_4,
+ BA_IP_CVBS_5,
+ BA_IP_SVIDEO_0,
+ BA_IP_SVIDEO_1,
+ BA_IP_SVIDEO_2,
+ BA_IP_COMPONENT_0,
+ BA_IP_COMPONENT_1,
+ BA_IP_DVI_0,
+ BA_IP_DVI_1,
+ BA_IP_HDMI_1,
+ BA_IP_MHL_1,
+ BA_IP_TTL,
+ BA_IP_MAX = 0xffffffff
+};
+
+enum msm_ba_save_restore_ip {
+ BA_SR_RESTORE_IP = 0,
+ BA_SR_SAVE_IP,
+ BA_SR_MAX = 0xffffffff
+};
+
+struct msm_ba_ext_ops {
+ void (*msm_ba_cb)(void *instance,
+ unsigned int event_id, void *arg);
+};
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops);
+int msm_ba_close(void *instance);
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap);
+int msm_ba_g_priority(void *instance, enum v4l2_priority *prio);
+int msm_ba_s_priority(void *instance, enum v4l2_priority prio);
+int msm_ba_enum_input(void *instance, struct v4l2_input *input);
+int msm_ba_g_input(void *instance, unsigned int *index);
+int msm_ba_s_input(void *instance, unsigned int index);
+int msm_ba_enum_output(void *instance, struct v4l2_output *output);
+int msm_ba_g_output(void *instance, unsigned int *index);
+int msm_ba_s_output(void *instance, unsigned int index);
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i);
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i);
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr);
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *pt);
+int msm_ba_subscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_unsubscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a);
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd);
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sd);
+#endif
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 0764b9e26962..78ca0f4bbd08 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -83,13 +83,6 @@ struct icnss_wlan_enable_cfg {
struct icnss_shadow_reg_cfg *shadow_reg_cfg;
};
-/* MSA Memory Regions Information */
-struct icnss_mem_region_info {
- uint64_t reg_addr;
- uint32_t size;
- uint8_t secure_flag;
-};
-
/* driver modes */
enum icnss_driver_mode {
ICNSS_MISSION,
@@ -154,4 +147,6 @@ extern void cnss_set_cc_source(enum cnss_cc_src cc_source);
extern enum cnss_cc_src cnss_get_cc_source(void);
extern int icnss_get_driver_load_cnt(void);
extern void icnss_increment_driver_load_cnt(void);
+extern void icnss_set_cc_source(enum cnss_cc_src cc_source);
+extern enum cnss_cc_src icnss_get_cc_source(void);
#endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
index 2db61a40e2cc..5eb18cb1a365 100644
--- a/include/soc/qcom/minidump.h
+++ b/include/soc/qcom/minidump.h
@@ -37,12 +37,13 @@ struct md_region {
*/
#ifdef CONFIG_QCOM_MINIDUMP
extern int msm_minidump_add_region(const struct md_region *entry);
+/* Sets to true, if minidump table is initialized */
extern bool minidump_enabled;
#else
static inline int msm_minidump_add_region(const struct md_region *entry)
{
- return -ENODEV;
+ /* Return quietly, if minidump is not supported */
+ return 0;
}
-static inline bool msm_minidump_enabled(void) { return false; }
#endif
#endif
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index cc6d4fb42d9f..831a0c81cddb 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -225,6 +225,8 @@ struct drm_msm_gem_submit {
__u32 nr_cmds; /* in, number of submit_cmd's */
__u64 __user bos; /* in, ptr to array of submit_bo's */
__u64 __user cmds; /* in, ptr to array of submit_cmd's */
+ __s32 fence_fd; /* gap for the fence_fd which is upstream */
+ __u32 queueid; /* in, submitqueue id */
};
struct drm_msm_gem_submit_profile_buffer {
@@ -353,6 +355,21 @@ struct drm_msm_gem_sync {
__u64 __user ops;
};
+/*
+ * Draw queues allow the user to set specific submission parameter. Command
+ * submissions will specify a specific submit queue id to use. id '0' is
+ * reserved as a "default" drawqueue with medium priority. The user can safely
+ * use and query 0 but cannot destroy it.
+ */
+
+#define MSM_SUBMITQUEUE_FLAGS (0)
+
+struct drm_msm_submitqueue {
+ __u32 flags; /* in, MSM_SUBMITQUEUE_x */
+ __u32 prio; /* in, Priority level */
+ __u32 id; /* out, identifier */
+};
+
#define DRM_MSM_GET_PARAM 0x00
/* placeholder:
#define DRM_MSM_SET_PARAM 0x01
@@ -365,6 +382,8 @@ struct drm_msm_gem_sync {
#define DRM_MSM_WAIT_FENCE 0x07
/* Gap for upstream DRM_MSM_GEM_MADVISE */
#define DRM_MSM_GEM_SVM_NEW 0x09
+#define DRM_MSM_SUBMITQUEUE_NEW 0x0A
+#define DRM_MSM_SUBMITQUEUE_CLOSE 0x0B
#define DRM_SDE_WB_CONFIG 0x40
#define DRM_MSM_REGISTER_EVENT 0x41
@@ -407,6 +426,12 @@ struct drm_msm_gem_sync {
#define DRM_IOCTL_MSM_GEM_SVM_NEW \
DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SVM_NEW, \
struct drm_msm_gem_svm_new)
+#define DRM_IOCTL_MSM_SUBMITQUEUE_NEW \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, \
+ struct drm_msm_submitqueue)
+#define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, \
+ struct drm_msm_submitqueue)
#if defined(__cplusplus)
}
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 336d318c5187..2e4c02f24a47 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1331,6 +1331,12 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_PAN_SPEED (V4L2_CID_CAMERA_CLASS_BASE+32)
#define V4L2_CID_TILT_SPEED (V4L2_CID_CAMERA_CLASS_BASE+33)
+/* User-class control IDs specific to the msm_ba driver */
+
+#define MSM_BA_PRIV_BASE_START (V4L2_CID_USER_BASE | 0x7000)
+#define MSM_BA_PRIV_SD_NODE_ADDR (MSM_BA_PRIV_BASE_START + 1)
+#define MSM_BA_PRIV_FPS (MSM_BA_PRIV_BASE_START + 2)
+
/* FM Modulator class control IDs */
#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 686fc6143010..bb2c4ebf9ff4 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2187,6 +2187,31 @@ struct v4l2_streamparm {
#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9)
#define V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED (V4L2_EVENT_MSM_VIDC_START + 10)
+#define V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE \
+ (V4L2_EVENT_PRIVATE_START + 0x00005000)
+#define V4L2_EVENT_MSM_BA_START V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE
+#define V4L2_EVENT_MSM_BA_DEVICE_AVAILABLE (V4L2_EVENT_MSM_BA_START + 1)
+#define V4L2_EVENT_MSM_BA_DEVICE_UNAVAILABLE \
+ (V4L2_EVENT_MSM_BA_START + 2)
+#define V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED \
+ (V4L2_EVENT_MSM_BA_START + 3)
+#define V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 4)
+#define V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 5)
+#define V4L2_EVENT_MSM_BA_SOURCE_CHANGE \
+ (V4L2_EVENT_MSM_BA_START + 6)
+#define V4L2_EVENT_MSM_BA_HDMI_HPD \
+ (V4L2_EVENT_MSM_BA_START + 7)
+#define V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE \
+ (V4L2_EVENT_MSM_BA_START + 8)
+#define V4L2_EVENT_MSM_BA_CP \
+ (V4L2_EVENT_MSM_BA_START + 9)
+#define V4L2_EVENT_MSM_BA_CABLE_DETECT \
+ (V4L2_EVENT_MSM_BA_START + 10)
+#define V4L2_EVENT_MSM_BA_ERROR \
+ (V4L2_EVENT_MSM_BA_START + 11)
+
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
/* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
@@ -2442,4 +2467,11 @@ struct v4l2_create_buffers {
#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */
+/* HDMI rx provide ioctls */
+#define VIDIOC_HDMI_RX_CEC_S_LOGICAL _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
+#define VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL _IO('V', BASE_VIDIOC_PRIVATE + 1)
+#define VIDIOC_HDMI_RX_CEC_G_PHYSICAL _IOR('V', BASE_VIDIOC_PRIVATE + 2, int)
+#define VIDIOC_HDMI_RX_CEC_G_CONNECTED _IOR('V', BASE_VIDIOC_PRIVATE + 3, int)
+#define VIDIOC_HDMI_RX_CEC_S_ENABLE _IOR('V', BASE_VIDIOC_PRIVATE + 4, int)
+
#endif /* _UAPI__LINUX_VIDEODEV2_H */
diff --git a/net/core/dev.c b/net/core/dev.c
index 2587d7f30191..57922df9c250 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4552,6 +4552,19 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb)
}
EXPORT_SYMBOL(__skb_gro_checksum_complete);
+static void net_rps_send_ipi(struct softnet_data *remsd)
+{
+#ifdef CONFIG_RPS
+ while (remsd) {
+ struct softnet_data *next = remsd->rps_ipi_next;
+
+ if (cpu_online(remsd->cpu))
+ smp_call_function_single_async(remsd->cpu, &remsd->csd);
+ remsd = next;
+ }
+#endif
+}
+
/*
* net_rps_action_and_irq_enable sends any pending IPI's for rps.
* Note: called with local irq disabled, but exits with local irq enabled.
@@ -4567,20 +4580,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
local_irq_enable();
/* Send pending IPI's to kick RPS processing on remote cpus. */
- while (remsd) {
- struct softnet_data *next = remsd->rps_ipi_next;
-
- if (cpu_online(remsd->cpu)) {
- smp_call_function_single_async(remsd->cpu,
- &remsd->csd);
- } else {
- pr_err("%s() cpu offline\n", __func__);
- rps_lock(remsd);
- remsd->backlog.state = 0;
- rps_unlock(remsd);
- }
- remsd = next;
- }
+ net_rps_send_ipi(remsd);
} else
#endif
local_irq_enable();
@@ -7495,7 +7495,7 @@ static int dev_cpu_callback(struct notifier_block *nfb,
struct sk_buff **list_skb;
struct sk_buff *skb;
unsigned int cpu, oldcpu = (unsigned long)ocpu;
- struct softnet_data *sd, *oldsd;
+ struct softnet_data *sd, *oldsd, *remsd;
if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
return NOTIFY_OK;
@@ -7539,6 +7539,13 @@ static int dev_cpu_callback(struct notifier_block *nfb,
raise_softirq_irqoff(NET_TX_SOFTIRQ);
local_irq_enable();
+#ifdef CONFIG_RPS
+ remsd = oldsd->rps_ipi_list;
+ oldsd->rps_ipi_list = NULL;
+#endif
+ /* send out pending IPI's on offline CPU */
+ net_rps_send_ipi(remsd);
+
/* Process offline CPU's input_pkt_queue */
while ((skb = __skb_dequeue(&oldsd->process_queue))) {
netif_rx_ni(skb);
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index c8c4f547b4f1..9ff010cee67e 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -771,9 +771,6 @@ country KR: DFS-ETSI
# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
(57240 - 65880 @ 2160), (43)
-country KP: DFS-ETSI
- (2402 - 2482 @ 40), (20)
-
country KW: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
@@ -1335,9 +1332,6 @@ country SV: DFS-FCC
(5250 - 5330 @ 20), (23), DFS
(5735 - 5835 @ 20), (30)
-country SY:
- (2402 - 2482 @ 40), (20)
-
country TC: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index eaaca97e2b8e..0af656ce48f0 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1123,54 +1123,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
.get_regmap = wsa881x_get_regmap,
};
-static int wsa881x_swr_startup(struct swr_device *swr_dev)
-{
- int ret = 0;
- u8 devnum = 0;
- struct wsa881x_priv *wsa881x;
-
- wsa881x = swr_get_dev_data(swr_dev);
- if (!wsa881x) {
- dev_err(&swr_dev->dev, "%s: wsa881x is NULL\n", __func__);
- return -EINVAL;
- }
-
- /*
- * Add 5msec delay to provide sufficient time for
- * soundwire auto enumeration of slave devices as
- * as per HW requirement.
- */
- usleep_range(5000, 5010);
- ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
- if (ret) {
- dev_dbg(&swr_dev->dev,
- "%s get devnum %d for dev addr %lx failed\n",
- __func__, devnum, swr_dev->addr);
- goto err;
- }
- swr_dev->dev_num = devnum;
-
- wsa881x->regmap = devm_regmap_init_swr(swr_dev,
- &wsa881x_regmap_config);
- if (IS_ERR(wsa881x->regmap)) {
- ret = PTR_ERR(wsa881x->regmap);
- dev_err(&swr_dev->dev, "%s: regmap_init failed %d\n",
- __func__, ret);
- goto err;
- }
-
- ret = snd_soc_register_codec(&swr_dev->dev, &soc_codec_dev_wsa881x,
- NULL, 0);
- if (ret) {
- dev_err(&swr_dev->dev, "%s: Codec registration failed\n",
- __func__);
- goto err;
- }
-
-err:
- return ret;
-}
-
static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable)
{
int ret = 0;
@@ -1232,6 +1184,7 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
{
int ret = 0;
struct wsa881x_priv *wsa881x;
+ u8 devnum = 0;
wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
GFP_KERNEL);
@@ -1291,8 +1244,43 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
&codec_debug_ops);
}
}
+
+ /*
+ * Add 5msec delay to provide sufficient time for
+ * soundwire auto enumeration of slave devices as
+ * as per HW requirement.
+ */
+ usleep_range(5000, 5010);
+ ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s get devnum %d for dev addr %lx failed\n",
+ __func__, devnum, pdev->addr);
+ goto dev_err;
+ }
+ pdev->dev_num = devnum;
+
+ wsa881x->regmap = devm_regmap_init_swr(pdev,
+ &wsa881x_regmap_config);
+ if (IS_ERR(wsa881x->regmap)) {
+ ret = PTR_ERR(wsa881x->regmap);
+ dev_err(&pdev->dev, "%s: regmap_init failed %d\n",
+ __func__, ret);
+ goto dev_err;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed\n",
+ __func__);
+ goto dev_err;
+ }
+
return 0;
+dev_err:
+ swr_remove_device(pdev);
err:
return ret;
}
@@ -1425,7 +1413,6 @@ static struct swr_driver wsa881x_codec_driver = {
.device_up = wsa881x_swr_up,
.device_down = wsa881x_swr_down,
.reset_device = wsa881x_swr_reset,
- .startup = wsa881x_swr_startup,
};
static int __init wsa881x_codec_init(void)
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index a71fb74d35bc..6b1c150f1ee4 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -283,6 +283,7 @@ static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
*dai_data->status_mask);
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ memset(&dai_data->ca, 0, sizeof(dai_data->ca));
}
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index da156bf61610..6f097b7e8dd5 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -3399,11 +3399,12 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac,
open->shared_circ_buf_start_phy_addr_lsw =
lower_32_bits(buf_circ->phys);
open->shared_circ_buf_start_phy_addr_msw =
- upper_32_bits(buf_circ->phys);
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
open->shared_circ_buf_size = bufsz * bufcnt;
open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys);
- open->map_region_circ_buf.shm_addr_msw = upper_32_bits(buf_circ->phys);
+ open->map_region_circ_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc;
mutex_unlock(&ac->cmd_lock);
@@ -3445,10 +3446,12 @@ int q6asm_set_shared_pos_buff(struct audio_client *ac,
open->shared_pos_buf_num_regions = 1;
open->shared_pos_buf_property_flag = 0x00;
open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys);
- open->shared_pos_buf_phy_addr_msw = upper_32_bits(buf_pos->phys);
+ open->shared_pos_buf_phy_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys);
- open->map_region_pos_buf.shm_addr_msw = upper_32_bits(buf_pos->phys);
+ open->map_region_pos_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc;
done: