diff options
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(¶ms); @@ -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(®ister_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(®_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: |
