diff options
387 files changed, 13232 insertions, 3498 deletions
diff --git a/Documentation/arm64/tagged-pointers.txt b/Documentation/arm64/tagged-pointers.txt index d9995f1f51b3..a25a99e82bb1 100644 --- a/Documentation/arm64/tagged-pointers.txt +++ b/Documentation/arm64/tagged-pointers.txt @@ -11,24 +11,56 @@ in AArch64 Linux. The kernel configures the translation tables so that translations made via TTBR0 (i.e. userspace mappings) have the top byte (bits 63:56) of the virtual address ignored by the translation hardware. This frees up -this byte for application use, with the following caveats: +this byte for application use. - (1) The kernel requires that all user addresses passed to EL1 - are tagged with tag 0x00. This means that any syscall - parameters containing user virtual addresses *must* have - their top byte cleared before trapping to the kernel. - (2) Non-zero tags are not preserved when delivering signals. - This means that signal handlers in applications making use - of tags cannot rely on the tag information for user virtual - addresses being maintained for fields inside siginfo_t. - One exception to this rule is for signals raised in response - to watchpoint debug exceptions, where the tag information - will be preserved. +Passing tagged addresses to the kernel +-------------------------------------- - (3) Special care should be taken when using tagged pointers, - since it is likely that C compilers will not hazard two - virtual addresses differing only in the upper byte. +All interpretation of userspace memory addresses by the kernel assumes +an address tag of 0x00. + +This includes, but is not limited to, addresses found in: + + - pointer arguments to system calls, including pointers in structures + passed to system calls, + + - the stack pointer (sp), e.g. when interpreting it to deliver a + signal, + + - the frame pointer (x29) and frame records, e.g. when interpreting + them to generate a backtrace or call graph. + +Using non-zero address tags in any of these locations may result in an +error code being returned, a (fatal) signal being raised, or other modes +of failure. + +For these reasons, passing non-zero address tags to the kernel via +system calls is forbidden, and using a non-zero address tag for sp is +strongly discouraged. + +Programs maintaining a frame pointer and frame records that use non-zero +address tags may suffer impaired or inaccurate debug and profiling +visibility. + + +Preserving tags +--------------- + +Non-zero tags are not preserved when delivering signals. This means that +signal handlers in applications making use of tags cannot rely on the +tag information for user virtual addresses being maintained for fields +inside siginfo_t. One exception to this rule is for signals raised in +response to watchpoint debug exceptions, where the tag information will +be preserved. The architecture prevents the use of a tagged PC, so the upper byte will be set to a sign-extension of bit 55 on exception return. + + +Other considerations +-------------------- + +Special care should be taken when using tagged pointers, since it is +likely that C compilers will not hazard two virtual addresses differing +only in the upper byte. diff --git a/Documentation/devicetree/bindings/arm/msm/adv7481.txt b/Documentation/devicetree/bindings/arm/msm/adv7481.txt new file mode 100644 index 000000000000..974c0877ac30 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/adv7481.txt @@ -0,0 +1,54 @@ +ADV7481 chip driver (VIDEO_ADV7481) + +VIDEO_ADV7481 is a kernel platform driver that is used for video decoder +and dual mode HDMI/MHL receiver. + +The devicetree representation of the VIDEO_ADV7481 block should be: + +Required properties + +- compatible: "qcom,adv7481" +- reg: The i2c slave address of adv7481 device. +- qcom,cci-master: The i2c master id to be used for adv7481 driver. +- gpios: The GPIOs required to be configured for the driver. It should + be in the order I2C data line, i2c clock line, reset line, + interrupt 1, interrupt 2 and interrupt 3. +- cam_vdig-supply: Should contain regulator to be used for the digital + vdd. +- cam_vio-supply: Should contain regulator to be used for the IO vdd. +- cam_vana-supply: Should contain regulator from which analog voltage + is supplied. +- qcom,cam-vreg-name: Should specify array of regulator names required + for the device. +- qcom,cam-vreg-min-voltage: Should specify array of minimum voltage + level in uV for the regulators specified in the property + "qcom,cam-vreg-name". +- qcom,cam-vreg-max-voltage: Should specify array of maximum voltage + level in uV for the regulators specified in the property + "qcom,cam-vreg-name". +- qcom,cam-vreg-op-mode: Should specify array of current level in uA + for the regulators specified in the property "qcom,cam-vreg-name". + +Example: + + qcom,adv7481@70 { + compatible = "qcom,adv7481"; + reg = <0x70 0xff>; + cam_vdig-supply = <&vph_pwr_vreg>; + /* 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>; + qcom,slave-addr = <0x70>; + 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 */ + }; diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt index c9e7d7423d7f..e14acdc6303e 100644 --- a/Documentation/devicetree/bindings/display/msm/sde.txt +++ b/Documentation/devicetree/bindings/display/msm/sde.txt @@ -264,6 +264,22 @@ Bus Scaling Data: * Current values of src & dst are defined at include/linux/msm-bus-board.h +SMMU Subnodes: +- smmu_sde_****: Child nodes representing sde smmu virtual + devices + +Subnode properties: +- compatible: Compatible names used for smmu devices. + names should be: + "qcom,smmu_sde_unsec": smmu context bank device + for unsecure sde real time domain. + "qcom,smmu_sde_sec": smmu context bank device + for secure sde real time domain. + "qcom,smmu_sde_nrt_unsec": smmu context bank device + for unsecure sde non-real time domain. + "qcom,smmu_sde_nrt_sec": smmu context bank device + for secure sde non-real time domain. + Please refer to ../../interrupt-controller/interrupts.txt for a general description of interrupt bindings. @@ -469,4 +485,14 @@ Example: <1 590 0 160000>, <1 590 0 320000>; }; + + smmu_kms_unsec: qcom,smmu_kms_unsec_cb { + compatible = "qcom,smmu_sde_unsec"; + iommus = <&mmss_smmu 0>; + }; + + smmu_kms_sec: qcom,smmu_kms_sec_cb { + compatible = "qcom,smmu_sde_sec"; + iommus = <&mmss_smmu 1>; + }; }; diff --git a/Documentation/devicetree/bindings/drm/msm/mdp.txt b/Documentation/devicetree/bindings/drm/msm/mdp.txt new file mode 100644 index 000000000000..3a6db0553fe3 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/msm/mdp.txt @@ -0,0 +1,42 @@ +Qualcomm Technologies,Inc. Adreno/Snapdragon display controller + +Required properties: + +Optional properties: +- qcom,sde-plane-id-map: plane id mapping for virtual plane. +- qcom,sde-plane-id: each virtual plane mapping node. +- reg: reg property. +- qcom,display-type: display type this plane is mapped to. It could be + "primary", "secondary" and "tertiary". +- qcom,plane-name: plane name array for this virtual plane. It could be + "rgb0", "rgb1", "rgb2", "rgb3", "vig0", "vig1", "vig2", "vig3", "dma0", "dma1", + "dma2", "dma3", "cursor0", "cursor1". +- qcom,plane-type: virtual plane type. It could be "primary", "overlay", + "cursor". + +Example: + +&mdss_mdp { + qcom,sde-plane-id-map { + qcom,sde-plane-id@0 { + reg = <0x0>; + qcom,display-type = "primary"; + qcom,plane-name = "rgb0", "rgb1"; + qcom,plane-type = "primary"; + }; + + qcom,sde-plane-id@1 { + reg = <0x1>; + qcom,display-type = "primary"; + qcom,plane-name = "vig0", "vig1"; + qcom,plane-type = "overlay"; + }; + + qcom,sde-plane-id@2 { + reg = <0x2>; + qcom,display-type = "primary"; + qcom,plane-name = "cursor0", "cursor1"; + qcom,plane-type = "cursor"; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/fb/adv7533.txt b/Documentation/devicetree/bindings/fb/adv7533.txt index 8b85fcd730cb..b198f37f8fc6 100644 --- a/Documentation/devicetree/bindings/fb/adv7533.txt +++ b/Documentation/devicetree/bindings/fb/adv7533.txt @@ -23,6 +23,8 @@ Optional properties: - qcom,max-voltage-level Maximum voltage level to be supplied to bridge chip - qcom,enable-load Load current to bridge chip when enabled - qcom,disable-load Load current to bridge chip when disabled +- qcom,post-on-sleep Sleep time (ms) to indicate the sleep + time after the vreg is enabled Example: &soc { @@ -46,6 +48,7 @@ Example: qcom,max-voltage-level = <0>; qcom,enable-load = <0>; qcom,disable-load = <0>; + qcom,post-on-sleep = <10>; }; }; }; 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/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 @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 68 +SUBLEVEL = 70 EXTRAVERSION = NAME = Blurry Fish Butt diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 6cc08166ff00..63f06a2b1f7f 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1188,8 +1188,10 @@ SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) return -EFAULT; - err = 0; - err |= put_user(status, ustatus); + err = put_user(status, ustatus); + if (ret < 0) + return err ? err : ret; + err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec); err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec); err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec); diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts index f3e2b96c06a3..0bd325c314e1 100644 --- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts @@ -162,9 +162,10 @@ }; adc0: adc@f8018000 { + atmel,adc-vref = <3300>; + atmel,adc-channels-used = <0xfe>; pinctrl-0 = < &pinctrl_adc0_adtrg - &pinctrl_adc0_ad0 &pinctrl_adc0_ad1 &pinctrl_adc0_ad2 &pinctrl_adc0_ad3 @@ -172,8 +173,6 @@ &pinctrl_adc0_ad5 &pinctrl_adc0_ad6 &pinctrl_adc0_ad7 - &pinctrl_adc0_ad8 - &pinctrl_adc0_ad9 >; status = "okay"; }; 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/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index ee069f5d8bdf..7c3f035a841b 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -1737,6 +1737,7 @@ mhi: qcom,mhi { compatible = "qcom,mhi"; + status = "disabled"; }; qcom,ipc-spinlock@740000 { 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-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi index b53ff87d5092..93b6a7664ed8 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi @@ -83,25 +83,39 @@ }; &dsi_dual_nt35597_video { - qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00]; - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-panel-timings = [00 1a 04 06 0a 0a 05 06 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x25>; + qcom,mdss-dsi-tx-eot-append; qcom,cmd-sync-wait-broadcast; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; qcom,mdss-dsi-min-refresh-rate = <55>; qcom,mdss-dsi-max-refresh-rate = <60>; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; }; &dsi_dual_nt35597_cmd { qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-tx-eot-append; qcom,cmd-sync-wait-broadcast; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; }; &dsi_dual_nt35597_truly_video { @@ -120,6 +134,7 @@ qcom,mdss-dsi-panel-timings = [00 11 04 04 07 0c 04 04 03 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x05>; qcom,mdss-dsi-t-clk-pre = <0x1b>; + qcom,mdss-dsi-tx-eot-append; qcom,esd-check-enabled; qcom,mdss-dsi-panel-status-check-mode = "reg_read"; qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; @@ -138,6 +153,7 @@ qcom,mdss-dsi-panel-timings = [00 11 04 04 07 0c 04 04 03 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x05>; qcom,mdss-dsi-t-clk-pre = <0x1b>; + qcom,mdss-dsi-tx-eot-append; qcom,esd-check-enabled; qcom,mdss-dsi-panel-status-check-mode = "reg_read"; qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index 795635d8d13d..354ac830e0fa 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -139,10 +139,15 @@ }; smmu_kms_unsec: qcom,smmu_kms_unsec_cb { - compatible = "qcom,smmu_mdp_unsec"; + compatible = "qcom,smmu_sde_unsec"; iommus = <&mmss_smmu 0>; }; + smmu_kms_sec: qcom,smmu_kms_sec_cb { + compatible = "qcom,smmu_sde_sec"; + iommus = <&mmss_smmu 1>; + }; + /* data and reg bus scale settings */ qcom,sde-data-bus { qcom,msm-bus,name = "mdss_sde"; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 31ad9ecf8c0a..fc546512992d 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -1544,6 +1544,7 @@ qcom,xprt-linkid = <1>; qcom,xprt-version = <1>; qcom,fragmented-data; + qcom,dynamic-wakeup-source; }; qcom,spcom { diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts index 7be428693f83..7fb0c9d03825 100644 --- a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts +++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts @@ -208,3 +208,14 @@ "SpkrLeft IN", "SPK1 OUT"; qcom,msm-mbhc-hphl-swh = <0>; }; + +&usb2s { + status = "okay"; +}; + +&qusb_phy0 { + reg = <0x0c012000 0x180>, + <0x00188018 0x4>; + reg-names = "qusb_phy_base", + "ref_clk_addr"; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index baced7758c9f..99766dbcdfe5 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -267,13 +267,19 @@ qusb_phy0: qusb@c012000 { 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-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi index 19862f02aa84..3ffd43bcda60 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi @@ -133,7 +133,13 @@ 23 1e 07 08 05 03 04 a0 23 18 07 08 04 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; qcom,mdss-dsi-min-refresh-rate = <53>; qcom,mdss-dsi-max-refresh-rate = <60>; qcom,mdss-dsi-pan-enable-dynamic-fps; @@ -190,7 +196,13 @@ qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; }; &dsi_nt35597_truly_dsc_cmd { diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index b701ecd562cd..66bea3050586 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi @@ -453,6 +453,8 @@ pm660l_bob: regulator-bob { regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; status = "okay"; }; @@ -462,6 +464,8 @@ qcom,set = <3>; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage1; }; @@ -471,6 +475,8 @@ qcom,set = <3>; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage2; }; @@ -480,6 +486,8 @@ qcom,set = <3>; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage3; }; }; 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/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts index 8ad7ca7f9743..7cf55acf900b 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts @@ -261,88 +261,107 @@ qcom,msm-cpudai-afe-clk-ver = <2>; }; - qcom,msm-dai-mi2s { - compatible = "qcom,msm-dai-mi2s"; - dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec { - compatible = "qcom,msm-dai-q6-mi2s"; - qcom,msm-dai-q6-mi2s-dev-id = <1>; - qcom,msm-mi2s-rx-lines = <2>; - qcom,msm-mi2s-tx-lines = <1>; - }; - - dai_mi2s: qcom,msm-dai-q6-mi2s-tert { - compatible = "qcom,msm-dai-q6-mi2s"; - qcom,msm-dai-q6-mi2s-dev-id = <2>; - qcom,msm-mi2s-rx-lines = <2>; - qcom,msm-mi2s-tx-lines = <1>; - }; - - dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat { - compatible = "qcom,msm-dai-q6-mi2s"; - qcom,msm-dai-q6-mi2s-dev-id = <3>; - qcom,msm-mi2s-rx-lines = <1>; - qcom,msm-mi2s-tx-lines = <0>; - }; + qcom,msm-dai-mi2s { + compatible = "qcom,msm-dai-mi2s"; + dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <1>; + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; }; + dai_mi2s: qcom,msm-dai-q6-mi2s-tert { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <2>; + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + }; + + dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat { + compatible = "qcom,msm-dai-q6-mi2s"; + qcom,msm-dai-q6-mi2s-dev-id = <3>; + qcom,msm-mi2s-rx-lines = <1>; + qcom,msm-mi2s-tx-lines = <0>; + }; + }; + + qcom,msm-dai-tdm-sec-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37137>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36881 36883 36885 36887>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <0>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; + dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36881>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_sec_tdm_tx_1: qcom,msm-dai-q6-tdm-sec-tx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36883>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_sec_tdm_tx_2: qcom,msm-dai-q6-tdm-sec-tx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36885>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + + dai_sec_tdm_tx_3: qcom,msm-dai-q6-tdm-sec-tx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36887>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + qcom,msm-dai-tdm-tert-rx { compatible = "qcom,msm-dai-tdm"; qcom,msm-cpudai-tdm-group-id = <37152>; qcom,msm-cpudai-tdm-group-num-ports = <5>; - qcom,msm-cpudai-tdm-group-port-id = - <36896 36898 36900 36902 36904>; - qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900 + 36902 36904>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <0>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; dai_tert_tdm_rx_0: qcom,msm-dai-q6-tdm-tert-rx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36896>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_rx_1: qcom,msm-dai-q6-tdm-tert-rx-1 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36898>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_rx_2: qcom,msm-dai-q6-tdm-tert-rx-2 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36900>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_rx_3: qcom,msm-dai-q6-tdm-tert-rx-3 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36902>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; + dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36904>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; @@ -352,48 +371,34 @@ qcom,msm-cpudai-tdm-group-id = <37153>; qcom,msm-cpudai-tdm-group-num-ports = <4>; qcom,msm-cpudai-tdm-group-port-id = <36897 36899 36901 36903>; - qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <0>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; dai_tert_tdm_tx_0: qcom,msm-dai-q6-tdm-tert-tx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36897>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_tx_1: qcom,msm-dai-q6-tdm-tert-tx-1 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36899>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_tx_2: qcom,msm-dai-q6-tdm-tert-tx-2 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36901>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_tert_tdm_tx_3: qcom,msm-dai-q6-tdm-tert-tx-3 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36903>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; @@ -403,48 +408,34 @@ qcom,msm-cpudai-tdm-group-id = <37168>; qcom,msm-cpudai-tdm-group-num-ports = <4>; qcom,msm-cpudai-tdm-group-port-id = <36912 36914 36916 36918>; - qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <0>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36912>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_rx_1: qcom,msm-dai-q6-tdm-quat-rx-1 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36914>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_rx_2: qcom,msm-dai-q6-tdm-quat-rx-2 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36916>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_rx_3: qcom,msm-dai-q6-tdm-quat-rx-3 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36918>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; @@ -454,96 +445,34 @@ qcom,msm-cpudai-tdm-group-id = <37169>; qcom,msm-cpudai-tdm-group-num-ports = <4>; qcom,msm-cpudai-tdm-group-port-id = <36913 36915 36917 36919>; - qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-clk-internal = <0>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <0>; dai_quat_tdm_tx_0: qcom,msm-dai-q6-tdm-quat-tx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36913>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_tx_1: qcom,msm-dai-q6-tdm-quat-tx-1 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36915>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_tx_2: qcom,msm-dai-q6-tdm-quat-tx-2 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36917>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_quat_tdm_tx_3: qcom,msm-dai-q6-tdm-quat-tx-3 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36919>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - }; - - qcom,msm-dai-tdm-sec-tx { - compatible = "qcom,msm-dai-tdm"; - qcom,msm-cpudai-tdm-group-id = <37137>; - qcom,msm-cpudai-tdm-group-num-ports = <4>; - qcom,msm-cpudai-tdm-group-port-id = <36881 36883 36885 36887>; - qcom,msm-cpudai-tdm-clk-rate = <0>; - dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36881>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_sec_tdm_tx_1: qcom,msm-dai-q6-tdm-sec-tx-1 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36883>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_sec_tdm_tx_2: qcom,msm-dai-q6-tdm-sec-tx-2 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36885>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_sec_tdm_tx_3: qcom,msm-dai-q6-tdm-sec-tx-3 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36887>; - qcom,msm-cpudai-tdm-sync-mode = <1>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index ed7e1009326c..d9ee0fd817e9 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -565,6 +565,7 @@ regulator-name = "+3VS,vdd_pnl"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + regulator-boot-on; gpio = <&gpio TEGRA_GPIO(A, 4) GPIO_ACTIVE_HIGH>; enable-active-high; }; diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c index a9b3b905e661..443db0c43d7c 100644 --- a/arch/arm/kvm/psci.c +++ b/arch/arm/kvm/psci.c @@ -208,9 +208,10 @@ int kvm_psci_version(struct kvm_vcpu *vcpu) static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) { - int ret = 1; + struct kvm *kvm = vcpu->kvm; unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0); unsigned long val; + int ret = 1; switch (psci_fn) { case PSCI_0_2_FN_PSCI_VERSION: @@ -230,7 +231,9 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) break; case PSCI_0_2_FN_CPU_ON: case PSCI_0_2_FN64_CPU_ON: + mutex_lock(&kvm->lock); val = kvm_psci_vcpu_on(vcpu); + mutex_unlock(&kvm->lock); break; case PSCI_0_2_FN_AFFINITY_INFO: case PSCI_0_2_FN64_AFFINITY_INFO: @@ -279,6 +282,7 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) { + struct kvm *kvm = vcpu->kvm; unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0); unsigned long val; @@ -288,7 +292,9 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) val = PSCI_RET_SUCCESS; break; case KVM_PSCI_FN_CPU_ON: + mutex_lock(&kvm->lock); val = kvm_psci_vcpu_on(vcpu); + mutex_unlock(&kvm->lock); break; default: val = PSCI_RET_NOT_SUPPORTED; diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig index c9dfe25b938a..d2a0fc53235d 100644 --- a/arch/arm64/configs/msm-auto-perf_defconfig +++ b/arch/arm64/configs/msm-auto-perf_defconfig @@ -225,6 +225,7 @@ CONFIG_MSM_BT_POWER=y CONFIG_BTFM_SLIM=y CONFIG_BTFM_SLIM_WCN3990=y CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig index 7a139efa1455..e67df6338136 100644 --- a/arch/arm64/configs/msm-auto_defconfig +++ b/arch/arm64/configs/msm-auto_defconfig @@ -227,6 +227,7 @@ CONFIG_MSM_BT_POWER=y CONFIG_BTFM_SLIM=y CONFIG_BTFM_SLIM_WCN3990=y CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 6f5be663140f..5adb7697cc3e 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -32,7 +32,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_KALLSYMS_ALL=y # CONFIG_AIO is not set # CONFIG_MEMBARRIER is not set diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index f09a134a2fd5..b362a0561d33 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -32,7 +32,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_KALLSYMS_ALL=y # CONFIG_AIO is not set # CONFIG_MEMBARRIER is not set @@ -486,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/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h index 510c7b404454..270c6b7b0a61 100644 --- a/arch/arm64/include/asm/cmpxchg.h +++ b/arch/arm64/include/asm/cmpxchg.h @@ -48,7 +48,7 @@ static inline unsigned long __xchg_case_##name(unsigned long x, \ " swp" #acq_lse #rel #sz "\t%" #w "3, %" #w "0, %2\n" \ " nop\n" \ " " #nop_lse) \ - : "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr) \ + : "=&r" (ret), "=&r" (tmp), "+Q" (*(unsigned long *)ptr) \ : "r" (x) \ : cl); \ \ diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index efafdf39cb3b..ac177d96e773 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -108,11 +108,12 @@ static inline void set_fs(mm_segment_t fs) */ #define __range_ok(addr, size) \ ({ \ + unsigned long __addr = (unsigned long __force)(addr); \ unsigned long flag, roksum; \ __chk_user_ptr(addr); \ asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls" \ : "=&r" (flag), "=&r" (roksum) \ - : "1" (addr), "Ir" (size), \ + : "1" (__addr), "Ir" (size), \ "r" (current_thread_info()->addr_limit) \ : "cc"); \ flag; \ diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index eec3598b4184..3ff507c177a5 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1055,8 +1055,8 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu, { struct sys_reg_params params; u32 hsr = kvm_vcpu_get_hsr(vcpu); - int Rt = (hsr >> 5) & 0xf; - int Rt2 = (hsr >> 10) & 0xf; + int Rt = (hsr >> 5) & 0x1f; + int Rt2 = (hsr >> 10) & 0x1f; params.is_aarch32 = true; params.is_32bit = false; @@ -1107,7 +1107,7 @@ static int kvm_handle_cp_32(struct kvm_vcpu *vcpu, { struct sys_reg_params params; u32 hsr = kvm_vcpu_get_hsr(vcpu); - int Rt = (hsr >> 5) & 0xf; + int Rt = (hsr >> 5) & 0x1f; params.is_aarch32 = true; params.is_32bit = true; diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h index 07238b39638c..3db381205928 100644 --- a/arch/metag/include/asm/uaccess.h +++ b/arch/metag/include/asm/uaccess.h @@ -28,24 +28,32 @@ #define segment_eq(a, b) ((a).seg == (b).seg) -#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) -/* - * Explicitly allow NULL pointers here. Parts of the kernel such - * as readv/writev use access_ok to validate pointers, but want - * to allow NULL pointers for various reasons. NULL pointers are - * safe to allow through because the first page is not mappable on - * Meta. - * - * We also wish to avoid letting user code access the system area - * and the kernel half of the address space. - */ -#define __user_bad(addr, size) (((addr) > 0 && (addr) < META_MEMORY_BASE) || \ - ((addr) > PAGE_OFFSET && \ - (addr) < LINCORE_BASE)) - static inline int __access_ok(unsigned long addr, unsigned long size) { - return __kernel_ok || !__user_bad(addr, size); + /* + * Allow access to the user mapped memory area, but not the system area + * before it. The check extends to the top of the address space when + * kernel access is allowed (there's no real reason to user copy to the + * system area in any case). + */ + if (likely(addr >= META_MEMORY_BASE && addr < get_fs().seg && + size <= get_fs().seg - addr)) + return true; + /* + * Explicitly allow NULL pointers here. Parts of the kernel such + * as readv/writev use access_ok to validate pointers, but want + * to allow NULL pointers for various reasons. NULL pointers are + * safe to allow through because the first page is not mappable on + * Meta. + */ + if (!addr) + return true; + /* Allow access to core code memory area... */ + if (addr >= LINCORE_CODE_BASE && addr <= LINCORE_CODE_LIMIT && + size <= LINCORE_CODE_LIMIT + 1 - addr) + return true; + /* ... but no other areas. */ + return false; } #define access_ok(type, addr, size) __access_ok((unsigned long)(addr), \ @@ -186,8 +194,13 @@ do { \ extern long __must_check __strncpy_from_user(char *dst, const char __user *src, long count); -#define strncpy_from_user(dst, src, count) __strncpy_from_user(dst, src, count) - +static inline long +strncpy_from_user(char *dst, const char __user *src, long count) +{ + if (!access_ok(VERIFY_READ, src, 1)) + return -EFAULT; + return __strncpy_from_user(dst, src, count); +} /* * Return the size of a string (including the ending 0) * diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 488e6314f993..5cc93f0b52ca 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -735,8 +735,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) andis. r15,r14,(DBSR_IC|DBSR_BT)@h beq+ 1f +#ifdef CONFIG_RELOCATABLE + ld r15,PACATOC(r13) + ld r14,interrupt_base_book3e@got(r15) + ld r15,__end_interrupts@got(r15) +#else LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e) LOAD_REG_IMMEDIATE(r15,__end_interrupts) +#endif cmpld cr0,r10,r14 cmpld cr1,r10,r15 blt+ cr0,1f @@ -799,8 +805,14 @@ kernel_dbg_exc: andis. r15,r14,(DBSR_IC|DBSR_BT)@h beq+ 1f +#ifdef CONFIG_RELOCATABLE + ld r15,PACATOC(r13) + ld r14,interrupt_base_book3e@got(r15) + ld r15,__end_interrupts@got(r15) +#else LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e) LOAD_REG_IMMEDIATE(r15,__end_interrupts) +#endif cmpld cr0,r10,r14 cmpld cr1,r10,r15 blt+ cr0,1f diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index b2eb4686bd8f..da3c4c3f4ec8 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -204,6 +204,8 @@ static void machine_check_process_queued_event(struct irq_work *work) { int index; + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); + /* * For now just print it to console. * TODO: log this error event to FSP or nvram. diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 37de90f8a845..e4dcb0a43e3f 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -297,8 +297,6 @@ long machine_check_early(struct pt_regs *regs) __this_cpu_inc(irq_stat.mce_exceptions); - add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); - if (cur_cpu_spec && cur_cpu_spec->machine_check_early) handled = cur_cpu_spec->machine_check_early(regs); return handled; @@ -704,6 +702,8 @@ void machine_check_exception(struct pt_regs *regs) __this_cpu_inc(irq_stat.mce_exceptions); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); + /* See if any machine dependent calls. In theory, we would want * to call the CPU first, and call the ppc_md. one if the CPU * one returns a positive number. However there is existing code diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index f244dcb4f2cf..96536c969c9c 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -280,7 +280,6 @@ int dlpar_detach_node(struct device_node *dn) if (rc) return rc; - of_node_put(dn); /* Must decrement the refcount */ return 0; } diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index f7c3a61040bd..df4685905015 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -464,6 +464,20 @@ static void *nt_vmcoreinfo(void *ptr) } /* + * Initialize final note (needed for /proc/vmcore code) + */ +static void *nt_final(void *ptr) +{ + Elf64_Nhdr *note; + + note = (Elf64_Nhdr *) ptr; + note->n_namesz = 0; + note->n_descsz = 0; + note->n_type = 0; + return PTR_ADD(ptr, sizeof(Elf64_Nhdr)); +} + +/* * Initialize ELF header (new kernel) */ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt) @@ -553,6 +567,7 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs); } ptr = nt_vmcoreinfo(ptr); + ptr = nt_final(ptr); memset(phdr, 0, sizeof(*phdr)); phdr->p_type = PT_NOTE; phdr->p_offset = notes_offset; diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 424e6809ad07..7460df3eec6b 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -308,6 +308,7 @@ ENTRY(system_call) lg %r14,__LC_VDSO_PER_CPU lmg %r0,%r10,__PT_R0(%r11) mvc __LC_RETURN_PSW(16),__PT_PSW(%r11) +.Lsysc_exit_timer: stpt __LC_EXIT_TIMER mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER lmg %r11,%r15,__PT_R11(%r11) @@ -593,6 +594,7 @@ ENTRY(io_int_handler) lg %r14,__LC_VDSO_PER_CPU lmg %r0,%r10,__PT_R0(%r11) mvc __LC_RETURN_PSW(16),__PT_PSW(%r11) +.Lio_exit_timer: stpt __LC_EXIT_TIMER mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER lmg %r11,%r15,__PT_R11(%r11) @@ -1118,15 +1120,23 @@ cleanup_critical: br %r14 .Lcleanup_sysc_restore: + # check if stpt has been executed clg %r9,BASED(.Lcleanup_sysc_restore_insn) + jh 0f + mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER + cghi %r11,__LC_SAVE_AREA_ASYNC je 0f + mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER +0: clg %r9,BASED(.Lcleanup_sysc_restore_insn+8) + je 1f lg %r9,24(%r11) # get saved pointer to pt_regs mvc __LC_RETURN_PSW(16),__PT_PSW(%r9) mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) -0: lmg %r8,%r9,__LC_RETURN_PSW +1: lmg %r8,%r9,__LC_RETURN_PSW br %r14 .Lcleanup_sysc_restore_insn: + .quad .Lsysc_exit_timer .quad .Lsysc_done - 4 .Lcleanup_io_tif: @@ -1134,15 +1144,20 @@ cleanup_critical: br %r14 .Lcleanup_io_restore: + # check if stpt has been executed clg %r9,BASED(.Lcleanup_io_restore_insn) - je 0f + jh 0f + mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER +0: clg %r9,BASED(.Lcleanup_io_restore_insn+8) + je 1f lg %r9,24(%r11) # get saved r11 pointer to pt_regs mvc __LC_RETURN_PSW(16),__PT_PSW(%r9) mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) -0: lmg %r8,%r9,__LC_RETURN_PSW +1: lmg %r8,%r9,__LC_RETURN_PSW br %r14 .Lcleanup_io_restore_insn: + .quad .Lio_exit_timer .quad .Lio_done - 4 .Lcleanup_idle: diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h index 9011a88353de..ed1e9206f830 100644 --- a/arch/x86/boot/boot.h +++ b/arch/x86/boot/boot.h @@ -16,7 +16,7 @@ #ifndef BOOT_BOOT_H #define BOOT_BOOT_H -#define STACK_SIZE 512 /* Minimum number of bytes for stack */ +#define STACK_SIZE 1024 /* Minimum number of bytes for stack */ #ifndef __ASSEMBLY__ diff --git a/arch/x86/include/asm/pmem.h b/arch/x86/include/asm/pmem.h index bd8ce6bcdfc9..6503526d7b24 100644 --- a/arch/x86/include/asm/pmem.h +++ b/arch/x86/include/asm/pmem.h @@ -122,7 +122,7 @@ static inline size_t arch_copy_from_iter_pmem(void __pmem *addr, size_t bytes, if (bytes < 8) { if (!IS_ALIGNED(dest, 4) || (bytes != 4)) - __arch_wb_cache_pmem(addr, 1); + __arch_wb_cache_pmem(addr, bytes); } else { if (!IS_ALIGNED(dest, 8)) { dest = ALIGN(dest, boot_cpu_data.x86_clflush_size); diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c index be39b5fde4b9..1011c05b1bd5 100644 --- a/arch/x86/kernel/fpu/init.c +++ b/arch/x86/kernel/fpu/init.c @@ -96,6 +96,7 @@ static void fpu__init_system_early_generic(struct cpuinfo_x86 *c) * Boot time FPU feature detection code: */ unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; +EXPORT_SYMBOL_GPL(mxcsr_feature_mask); static void __init fpu__init_system_mxcsr(void) { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e75095fa414e..ae2b9cd358f2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2960,6 +2960,12 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, | KVM_VCPUEVENT_VALID_SMM)) return -EINVAL; + /* INITs are latched while in SMM */ + if (events->flags & KVM_VCPUEVENT_VALID_SMM && + (events->smi.smm || events->smi.pending) && + vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) + return -EINVAL; + process_nmi(vcpu); vcpu->arch.exception.pending = events->exception.injected; vcpu->arch.exception.nr = events->exception.nr; @@ -3134,11 +3140,14 @@ static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, } } +#define XSAVE_MXCSR_OFFSET 24 + static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, struct kvm_xsave *guest_xsave) { u64 xstate_bv = *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)]; + u32 mxcsr = *(u32 *)&guest_xsave->region[XSAVE_MXCSR_OFFSET / sizeof(u32)]; if (cpu_has_xsave) { /* @@ -3146,11 +3155,13 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, * CPUID leaf 0xD, index 0, EDX:EAX. This is for compatibility * with old userspace. */ - if (xstate_bv & ~kvm_supported_xcr0()) + if (xstate_bv & ~kvm_supported_xcr0() || + mxcsr & ~mxcsr_feature_mask) return -EINVAL; load_xsave(vcpu, (u8 *)guest_xsave->region); } else { - if (xstate_bv & ~XFEATURE_MASK_FPSSE) + if (xstate_bv & ~XFEATURE_MASK_FPSSE || + mxcsr & ~mxcsr_feature_mask) return -EINVAL; memcpy(&vcpu->arch.guest_fpu.state.fxsave, guest_xsave->region, sizeof(struct fxregs_state)); @@ -4597,16 +4608,20 @@ emul_write: static int kernel_pio(struct kvm_vcpu *vcpu, void *pd) { - /* TODO: String I/O for in kernel device */ - int r; + int r = 0, i; - if (vcpu->arch.pio.in) - r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port, - vcpu->arch.pio.size, pd); - else - r = kvm_io_bus_write(vcpu, KVM_PIO_BUS, - vcpu->arch.pio.port, vcpu->arch.pio.size, - pd); + for (i = 0; i < vcpu->arch.pio.count; i++) { + if (vcpu->arch.pio.in) + r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port, + vcpu->arch.pio.size, pd); + else + r = kvm_io_bus_write(vcpu, KVM_PIO_BUS, + vcpu->arch.pio.port, vcpu->arch.pio.size, + pd); + if (r) + break; + pd += vcpu->arch.pio.size; + } return r; } @@ -4644,6 +4659,8 @@ static int emulator_pio_in_emulated(struct x86_emulate_ctxt *ctxt, if (vcpu->arch.pio.count) goto data_avail; + memset(vcpu->arch.pio_data, 0, size * count); + ret = emulator_pio_in_out(vcpu, size, port, val, count, true); if (ret) { data_avail: @@ -6993,6 +7010,12 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, mp_state->mp_state != KVM_MP_STATE_RUNNABLE) return -EINVAL; + /* INITs are latched while in SMM */ + if ((is_smm(vcpu) || vcpu->arch.smi_pending) && + (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED || + mp_state->mp_state == KVM_MP_STATE_INIT_RECEIVED)) + return -EINVAL; + if (mp_state->mp_state == KVM_MP_STATE_SIPI_RECEIVED) { vcpu->arch.mp_state = KVM_MP_STATE_INIT_RECEIVED; set_bit(KVM_APIC_SIPI, &vcpu->arch.apic->pending_events); diff --git a/arch/x86/um/ptrace_64.c b/arch/x86/um/ptrace_64.c index a629694ee750..e14c43a2d187 100644 --- a/arch/x86/um/ptrace_64.c +++ b/arch/x86/um/ptrace_64.c @@ -121,7 +121,7 @@ int poke_user(struct task_struct *child, long addr, long data) else if ((addr >= offsetof(struct user, u_debugreg[0])) && (addr <= offsetof(struct user, u_debugreg[7]))) { addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; + addr = addr >> 3; if ((addr == 4) || (addr == 5)) return -EIO; child->thread.arch.debugregs[addr] = data; diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 1e56ff583459..63146c378f1e 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -2038,7 +2038,8 @@ static unsigned long __init xen_read_phys_ulong(phys_addr_t addr) /* * Translate a virtual address to a physical one without relying on mapped - * page tables. + * page tables. Don't rely on big pages being aligned in (guest) physical + * space! */ static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr) { @@ -2059,7 +2060,7 @@ static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr) sizeof(pud))); if (!pud_present(pud)) return 0; - pa = pud_pfn(pud) << PAGE_SHIFT; + pa = pud_val(pud) & PTE_PFN_MASK; if (pud_large(pud)) return pa + (vaddr & ~PUD_MASK); @@ -2067,7 +2068,7 @@ static phys_addr_t __init xen_early_virt_to_phys(unsigned long vaddr) sizeof(pmd))); if (!pmd_present(pmd)) return 0; - pa = pmd_pfn(pmd) << PAGE_SHIFT; + pa = pmd_val(pmd) & PTE_PFN_MASK; if (pmd_large(pmd)) return pa + (vaddr & ~PMD_MASK); diff --git a/block/blk-integrity.c b/block/blk-integrity.c index 319f2e4f4a8b..478f572cb1e7 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -412,7 +412,8 @@ void blk_integrity_register(struct gendisk *disk, struct blk_integrity *template bi->flags = BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE | template->flags; - bi->interval_exp = ilog2(queue_logical_block_size(disk->queue)); + bi->interval_exp = template->interval_exp ? : + ilog2(queue_logical_block_size(disk->queue)); bi->profile = template->profile ? template->profile : &nop_profile; bi->tuple_size = template->tuple_size; bi->tag_size = template->tag_size; diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 6d4d4569447e..faea9d728fd2 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -29,6 +29,11 @@ struct aead_sg_list { struct scatterlist sg[ALG_MAX_PAGES]; }; +struct aead_tfm { + struct crypto_aead *aead; + bool has_key; +}; + struct aead_ctx { struct aead_sg_list tsgl; /* @@ -513,24 +518,146 @@ static struct proto_ops algif_aead_ops = { .poll = aead_poll, }; +static int aead_check_key(struct socket *sock) +{ + int err = 0; + struct sock *psk; + struct alg_sock *pask; + struct aead_tfm *tfm; + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + + lock_sock(sk); + if (ask->refcnt) + goto unlock_child; + + psk = ask->parent; + pask = alg_sk(ask->parent); + tfm = pask->private; + + err = -ENOKEY; + lock_sock_nested(psk, SINGLE_DEPTH_NESTING); + if (!tfm->has_key) + goto unlock; + + if (!pask->refcnt++) + sock_hold(psk); + + ask->refcnt = 1; + sock_put(psk); + + err = 0; + +unlock: + release_sock(psk); +unlock_child: + release_sock(sk); + + return err; +} + +static int aead_sendmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t size) +{ + int err; + + err = aead_check_key(sock); + if (err) + return err; + + return aead_sendmsg(sock, msg, size); +} + +static ssize_t aead_sendpage_nokey(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + int err; + + err = aead_check_key(sock); + if (err) + return err; + + return aead_sendpage(sock, page, offset, size, flags); +} + +static int aead_recvmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + int err; + + err = aead_check_key(sock); + if (err) + return err; + + return aead_recvmsg(sock, msg, ignored, flags); +} + +static struct proto_ops algif_aead_ops_nokey = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .getsockopt = sock_no_getsockopt, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + .setsockopt = sock_no_setsockopt, + + .release = af_alg_release, + .sendmsg = aead_sendmsg_nokey, + .sendpage = aead_sendpage_nokey, + .recvmsg = aead_recvmsg_nokey, + .poll = aead_poll, +}; + static void *aead_bind(const char *name, u32 type, u32 mask) { - return crypto_alloc_aead(name, type, mask); + struct aead_tfm *tfm; + struct crypto_aead *aead; + + tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); + if (!tfm) + return ERR_PTR(-ENOMEM); + + aead = crypto_alloc_aead(name, type, mask); + if (IS_ERR(aead)) { + kfree(tfm); + return ERR_CAST(aead); + } + + tfm->aead = aead; + + return tfm; } static void aead_release(void *private) { - crypto_free_aead(private); + struct aead_tfm *tfm = private; + + crypto_free_aead(tfm->aead); + kfree(tfm); } static int aead_setauthsize(void *private, unsigned int authsize) { - return crypto_aead_setauthsize(private, authsize); + struct aead_tfm *tfm = private; + + return crypto_aead_setauthsize(tfm->aead, authsize); } static int aead_setkey(void *private, const u8 *key, unsigned int keylen) { - return crypto_aead_setkey(private, key, keylen); + struct aead_tfm *tfm = private; + int err; + + err = crypto_aead_setkey(tfm->aead, key, keylen); + tfm->has_key = !err; + + return err; } static void aead_sock_destruct(struct sock *sk) @@ -546,12 +673,14 @@ static void aead_sock_destruct(struct sock *sk) af_alg_release_parent(sk); } -static int aead_accept_parent(void *private, struct sock *sk) +static int aead_accept_parent_nokey(void *private, struct sock *sk) { struct aead_ctx *ctx; struct alg_sock *ask = alg_sk(sk); - unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(private); - unsigned int ivlen = crypto_aead_ivsize(private); + struct aead_tfm *tfm = private; + struct crypto_aead *aead = tfm->aead; + unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(aead); + unsigned int ivlen = crypto_aead_ivsize(aead); ctx = sock_kmalloc(sk, len, GFP_KERNEL); if (!ctx) @@ -577,7 +706,7 @@ static int aead_accept_parent(void *private, struct sock *sk) ask->private = ctx; - aead_request_set_tfm(&ctx->aead_req, private); + aead_request_set_tfm(&ctx->aead_req, aead); aead_request_set_callback(&ctx->aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG, af_alg_complete, &ctx->completion); @@ -586,13 +715,25 @@ static int aead_accept_parent(void *private, struct sock *sk) return 0; } +static int aead_accept_parent(void *private, struct sock *sk) +{ + struct aead_tfm *tfm = private; + + if (!tfm->has_key) + return -ENOKEY; + + return aead_accept_parent_nokey(private, sk); +} + static const struct af_alg_type algif_type_aead = { .bind = aead_bind, .release = aead_release, .setkey = aead_setkey, .setauthsize = aead_setauthsize, .accept = aead_accept_parent, + .accept_nokey = aead_accept_parent_nokey, .ops = &algif_aead_ops, + .ops_nokey = &algif_aead_ops_nokey, .name = "aead", .owner = THIS_MODULE }; diff --git a/drivers/Makefile b/drivers/Makefile index eb67aadf2ee0..2545cf95e8db 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/ +obj-$(CONFIG_OF) += usb/ obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ 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/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index cb852cc750b7..f9b569ef3dd7 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -287,6 +287,9 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; + if (!hu->tty->dev) + goto out; + mutex_lock(&bcm_device_lock); list_for_each(p, &bcm_device_list) { struct bcm_device *dev = list_entry(p, struct bcm_device, list); @@ -307,7 +310,7 @@ static int bcm_open(struct hci_uart *hu) } mutex_unlock(&bcm_device_lock); - +out: return 0; } diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index b9065506a847..0c63fce0c1e0 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -307,6 +307,9 @@ static int intel_set_power(struct hci_uart *hu, bool powered) struct list_head *p; int err = -ENODEV; + if (!hu->tty->dev) + return err; + mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -379,6 +382,9 @@ static void intel_busy_work(struct work_struct *work) struct intel_data *intel = container_of(work, struct intel_data, busy_work); + if (!intel->hu->tty->dev) + return; + /* Link is busy, delay the suspend */ mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -913,6 +919,8 @@ done: list_for_each(p, &intel_device_list) { struct intel_device *dev = list_entry(p, struct intel_device, list); + if (!hu->tty->dev) + break; if (hu->tty->dev->parent == dev->pdev->dev.parent) { if (device_may_wakeup(&dev->pdev->dev)) idev = dev; @@ -1094,6 +1102,9 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); + if (!hu->tty->dev) + goto out_enqueue; + /* Be sure our controller is resumed and potential LPM transaction * completed before enqueuing any packet. */ @@ -1110,7 +1121,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) } } mutex_unlock(&intel_device_list_lock); - +out_enqueue: skb_queue_tail(&intel->txq, skb); return 0; diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 14c833691194..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) @@ -1686,6 +1687,9 @@ static int fastrpc_init_process(struct fastrpc_file *fl, int namelen; int pageslen; } inbuf; + + if (!init->filelen) + goto bail; VERIFY(err, proc_name = kzalloc(init->filelen, GFP_KERNEL)); if (err) goto bail; @@ -1694,7 +1698,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl, if (err) goto bail; inbuf.pgid = current->tgid; - inbuf.namelen = strlen(proc_name)+1; + inbuf.namelen = init->filelen; inbuf.pageslen = 0; if (!me->staticpd_flags) { inbuf.pageslen = 1; 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_masks.c b/drivers/char/diag/diag_masks.c index 382717bad828..21994d53db91 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -28,7 +28,8 @@ #define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7))) #define diag_check_update(x) \ - (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \ + (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x))) \ + || (info && (info->peripheral_mask & MD_PERIPHERAL_PD_MASK(x)))) \ struct diag_mask_info msg_mask; struct diag_mask_info msg_bt_mask; @@ -89,7 +90,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) int err = 0; int send_once = 0; int header_len = sizeof(struct diag_ctrl_log_mask); - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_ctrl_log_mask ctrl_pkt; @@ -106,11 +107,25 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) return; } - if (driver->md_session_mask != 0 && - driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) - mask_info = driver->md_session_map[peripheral]->log_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->log_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->log_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &log_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -195,7 +210,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) static void diag_send_event_mask_update(uint8_t peripheral) { - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; struct diag_ctrl_event_mask header; struct diag_mask_info *mask_info = NULL; @@ -220,11 +235,25 @@ static void diag_send_event_mask_update(uint8_t peripheral) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->event_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->event_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->event_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &event_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -284,7 +313,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) int err = 0; int header_len = sizeof(struct diag_ctrl_msg_mask); int temp_len = 0; - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_mask_info *mask_info = NULL; @@ -301,11 +330,25 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->msg_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->msg_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->msg_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &msg_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index a5d92c51cc0b..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++) { @@ -360,17 +358,11 @@ drop_data: err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int)); - } else { - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag: md_session_map[%d] with pid = %d Exited..\n", - peripheral, driver->md_session_map[peripheral]->pid); } diag_ws_on_copy_complete(DIAG_WS_MUX); 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 73296b573436..92cf24dcab5e 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -235,6 +235,10 @@ #define MD_PERIPHERAL_MASK(x) (1 << x) +#define MD_PERIPHERAL_PD_MASK(x) \ + ((x == PERIPHERAL_MODEM) ? (1 << UPD_WLAN) : \ + ((x == PERIPHERAL_LPASS) ? (1 << UPD_AUDIO | 1 << UPD_SENSORS) : 0))\ + /* * Number of stm processors includes all the peripherals and * apps.Added 1 below to indicate apps @@ -543,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; @@ -609,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; @@ -675,6 +672,7 @@ void diag_cmd_remove_reg_by_proc(int proc); int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry); int diag_mask_param(void); void diag_clear_masks(struct diag_md_session_t *info); +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask); void diag_record_stats(int type, int flag); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 5c1094b48e92..0bc23199b92e 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -403,6 +403,30 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask) ret |= DIAG_CON_UPD_SENSORS; return ret; } + +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask) +{ + uint8_t upd = 0; + uint32_t pd_mask = 0; + + pd_mask = diag_translate_kernel_to_user_mask(peripheral_mask); + switch (pd_mask) { + case DIAG_CON_UPD_WLAN: + upd = UPD_WLAN; + break; + case DIAG_CON_UPD_AUDIO: + upd = UPD_AUDIO; + break; + case DIAG_CON_UPD_SENSORS: + upd = UPD_SENSORS; + break; + default: + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with no pd mask set\n"); + } + return upd; +} + int diag_mask_param(void) { return diag_mask_clear_param; @@ -457,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); @@ -1588,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; @@ -1629,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 = @@ -1638,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); @@ -3599,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_glink.h b/drivers/char/diag/diagfwd_glink.h index a84fa4edfca0..6cad44522ab6 100644 --- a/drivers/char/diag/diagfwd_glink.h +++ b/drivers/char/diag/diagfwd_glink.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and 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/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 90e624662257..0d83cfb9708f 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -888,6 +888,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, * for details on the intricacies of this. */ int left; + unsigned char *data_to_send; ssif_inc_stat(ssif_info, sent_messages_parts); @@ -896,6 +897,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, left = 32; /* Length byte. */ ssif_info->multi_data[ssif_info->multi_pos] = left; + data_to_send = ssif_info->multi_data + ssif_info->multi_pos; ssif_info->multi_pos += left; if (left < 32) /* @@ -909,7 +911,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, - ssif_info->multi_data + ssif_info->multi_pos, + data_to_send, I2C_SMBUS_BLOCK_DATA); if (rv < 0) { /* request failed, just return the error. */ diff --git a/drivers/char/lp.c b/drivers/char/lp.c index c4094c4e22c1..34ef474a3923 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -859,7 +859,11 @@ static int __init lp_setup (char *str) } else if (!strcmp(str, "auto")) { parport_nr[0] = LP_PARPORT_AUTO; } else if (!strcmp(str, "none")) { - parport_nr[parport_ptr++] = LP_PARPORT_NONE; + if (parport_ptr < LP_NO) + parport_nr[parport_ptr++] = LP_PARPORT_NONE; + else + printk(KERN_INFO "lp: too many ports, %s ignored.\n", + str); } else if (!strcmp(str, "reset")) { reset = 1; } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index e901463d4972..0975d23031ea 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -343,6 +343,11 @@ static const struct vm_operations_struct mmap_mem_ops = { static int mmap_mem(struct file *file, struct vm_area_struct *vma) { size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + /* It's illegal to wrap around the end of the physical address space. */ + if (offset + (phys_addr_t)size < offset) + return -EINVAL; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 2b21398c3adc..35308dfff754 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -118,8 +118,7 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) memcpy_fromio(buf, priv->rsp, 6); expected = be32_to_cpup((__be32 *) &buf[2]); - - if (expected > count) + if (expected > count || expected < 6) return -EIO; memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); 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/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 5b261adb4b69..3a25da4a6e60 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -1126,23 +1126,10 @@ static u32 dce_v10_0_latency_watermark(struct dce10_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1250,14 +1237,14 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce10_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1272,7 +1259,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1311,7 +1298,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 267749a94c5a..d6d3cda77762 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -1114,23 +1114,10 @@ static u32 dce_v11_0_latency_watermark(struct dce10_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1238,14 +1225,14 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce10_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1260,7 +1247,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1299,7 +1286,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 9b4dcf76ce6c..d6e51d4b04f0 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -1096,23 +1096,10 @@ static u32 dce_v8_0_latency_watermark(struct dce8_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1220,14 +1207,14 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce8_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1242,7 +1229,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1281,7 +1268,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 02a60a1df50d..39b8e171cad5 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -75,6 +75,8 @@ #define EDID_QUIRK_FORCE_12BPC (1 << 9) /* Force 6bpc */ #define EDID_QUIRK_FORCE_6BPC (1 << 10) +/* Force 10bpc */ +#define EDID_QUIRK_FORCE_10BPC (1 << 11) struct detailed_mode_closure { struct drm_connector *connector; @@ -125,6 +127,9 @@ static struct edid_quirk { { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | EDID_QUIRK_DETAILED_IN_CM }, + /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */ + { "LGD", 764, EDID_QUIRK_FORCE_10BPC }, + /* LG Philips LCD LP154W01-A5 */ { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, @@ -4478,6 +4483,9 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) if (quirks & EDID_QUIRK_FORCE_8BPC) connector->display_info.bpc = 8; + if (quirks & EDID_QUIRK_FORCE_10BPC) + connector->display_info.bpc = 10; + if (quirks & EDID_QUIRK_FORCE_12BPC) connector->display_info.bpc = 12; diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 04de6fd88f8c..6e4dd62d4ed9 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,8 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h> +#include <linux/rbtree.h> /** * DOC: Overview @@ -73,7 +75,8 @@ * allocations and avoiding too much fragmentation. This means free space * searches are O(num_holes). Given that all the fancy features drm_mm supports * something better would be fairly complex and since gfx thrashing is a fairly - * steep cliff not a real concern. Removing a node again is O(1). + * steep cliff not a real concern. Removing a node again is O(1). With the + * rbtree to track free holes, free hole search becomes O(log(num_holes)). * * drm_mm supports a few features: Alignment and range restrictions can be * supplied. Further more every &drm_mm_node has a color value (which is just an @@ -103,6 +106,98 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags); +#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1) + +INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, + u64, __subtree_last, + START, LAST, static inline, drm_mm_interval_tree) + +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_first(&mm->interval_tree, + start, last); +} +EXPORT_SYMBOL(drm_mm_interval_first); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_next(node, start, last); +} +EXPORT_SYMBOL(drm_mm_interval_next); + +static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, + struct drm_mm_node *node) +{ + struct drm_mm *mm = hole_node->mm; + struct rb_node **link, *rb; + struct drm_mm_node *parent; + + node->__subtree_last = LAST(node); + + if (hole_node->allocated) { + rb = &hole_node->rb; + while (rb) { + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last >= node->__subtree_last) + break; + + parent->__subtree_last = node->__subtree_last; + rb = rb_parent(rb); + } + + rb = &hole_node->rb; + link = &hole_node->rb.rb_right; + } else { + rb = NULL; + link = &mm->interval_tree.rb_node; + } + + while (*link) { + rb = *link; + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last < node->__subtree_last) + parent->__subtree_last = node->__subtree_last; + if (node->start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + rb_link_node(&node->rb, rb, link); + rb_insert_augmented(&node->rb, + &mm->interval_tree, + &drm_mm_interval_tree_augment); +} + +static void +rb_insert_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + struct rb_node **new = &(mm->holes_tree.rb_node); + struct rb_node *parent = NULL; + struct drm_mm_node *cur; + + while (*new) { + parent = *new; + cur = rb_entry(parent, struct drm_mm_node, hole_node); + + if (__drm_mm_hole_node_start(hole_node) + < __drm_mm_hole_node_start(cur)) + new = &parent->rb_left; + else + new = &parent->rb_right; + } + rb_link_node(&hole_node->hole_node, parent, new); + rb_insert_color(&hole_node->hole_node, &mm->holes_tree); +} + +static void rb_erase_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + rb_erase(&hole_node->hole_node, &mm->holes_tree); +} + static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -142,6 +237,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -150,14 +246,16 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -178,39 +276,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { - struct drm_mm_node *hole; u64 end = node->start + node->size; - u64 hole_start; - u64 hole_end; + struct drm_mm_node *hole; + u64 hole_start, hole_end; - BUG_ON(node == NULL); + if (WARN_ON(node->size == 0)) + return -EINVAL; /* Find the relevant hole to add our node to */ - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > node->start || hole_end < end) - continue; + hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, + node->start, ~(u64)0); + if (hole) { + if (hole->start < end) + return -ENOSPC; + } else { + hole = list_entry(&mm->head_node.node_list, + typeof(*hole), node_list); + } - node->mm = mm; - node->allocated = 1; + hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); + if (!hole->hole_follows) + return -ENOSPC; - INIT_LIST_HEAD(&node->hole_stack); - list_add(&node->node_list, &hole->node_list); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + if (hole_start > node->start || hole_end < end) + return -ENOSPC; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del_init(&hole->hole_stack); - } + node->mm = mm; + node->allocated = 1; - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + list_add(&node->node_list, &hole->node_list); - return 0; + drm_mm_interval_tree_add_node(hole, node); + + if (node->start == hole_start) { + hole->hole_follows = 0; + list_del(&hole->hole_stack); + rb_erase_hole_node(hole, mm); } - return -ENOSPC; + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); + node->hole_follows = 1; + } + + return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); @@ -237,6 +350,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node) @@ -289,6 +405,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -297,9 +414,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); @@ -308,6 +426,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -338,6 +457,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags); @@ -377,6 +499,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) BUG_ON(__drm_mm_hole_node_start(node) == __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); + rb_erase_hole_node(node, mm); } else BUG_ON(__drm_mm_hole_node_start(node) != __drm_mm_hole_node_end(node)); @@ -385,9 +508,11 @@ void drm_mm_remove_node(struct drm_mm_node *node) if (!prev_node->hole_follows) { prev_node->hole_follows = 1; list_add(&prev_node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(prev_node, mm); } else list_move(&prev_node->hole_stack, &mm->hole_stack); + drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0; } @@ -410,6 +535,46 @@ static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment) return end >= start + size; } +static struct drm_mm_node *get_first_hole(const struct drm_mm *mm, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + struct rb_node *node = rb_first(&mm->holes_tree); + + return rb_entry(node, struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry((mm)->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry((mm)->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static struct drm_mm_node *get_next_hole(struct drm_mm_node *entry, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + return rb_entry(rb_next(&entry->hole_node), + struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry(entry->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry(entry->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static bool drm_mm_hole_traversal_condition(const struct drm_mm *mm, + struct drm_mm_node *entry, enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) + return entry ? 1 : 0; + else + return (&entry->hole_stack != &(mm)->hole_stack) ? 1 : 0; +} + static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, u64 size, unsigned alignment, @@ -427,9 +592,14 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); @@ -471,9 +641,14 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (adj_start < start) adj_start = start; @@ -514,14 +689,21 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color; + new->__subtree_last = old->__subtree_last; old->allocated = 0; new->allocated = 1; + + if (old->hole_follows) + rb_replace_node(&old->hole_node, &new->hole_node, + &old->mm->holes_tree); + } EXPORT_SYMBOL(drm_mm_replace_node); @@ -746,7 +928,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0; @@ -756,7 +937,10 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->interval_tree = RB_ROOT; mm->color_adjust = NULL; + mm->holes_tree = RB_ROOT; + rb_insert_hole_node(&mm->head_node, mm); } EXPORT_SYMBOL(drm_mm_init); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 5838545468f8..dbc198b00792 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -12,6 +12,8 @@ config DRM_MSM select QCOM_SCM select BACKLIGHT_CLASS_DEVICE select MSM_EXT_DISPLAY + select MMU_NOTIFIER + select INTERVAL_TREE default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 245d8911033d..999d5e45e5c5 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -148,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 0d8fe805828c..47bee42c59fa 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -51,6 +51,9 @@ static LIST_HEAD(sde_hdmi_list); #define HDMI_SCDC_ERR_DET_2_H 0x55 #define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 +#define HDMI_DISPLAY_MAX_WIDTH 4096 +#define HDMI_DISPLAY_MAX_HEIGHT 2160 + static const struct of_device_id sde_hdmi_dt_match[] = { {.compatible = "qcom,hdmi-display"}, {} @@ -1524,8 +1527,8 @@ int sde_hdmi_get_info(struct msm_display_info *info, MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE; } info->is_connected = hdmi_display->connected; - info->max_width = 4096; - info->max_height = 2160; + info->max_width = HDMI_DISPLAY_MAX_WIDTH; + info->max_height = HDMI_DISPLAY_MAX_HEIGHT; info->compression = MSM_DISPLAY_COMPRESS_NONE; mutex_unlock(&hdmi_display->display_lock); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index 0956617442af..ea485a2ec2cd 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -466,13 +466,13 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val) #define REG_HDMI_CEC_RD_FILTER 0x000002b0 #define REG_HDMI_ACTIVE_HSYNC 0x000002b4 -#define HDMI_ACTIVE_HSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_HSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_HSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & HDMI_ACTIVE_HSYNC_START__MASK; } -#define HDMI_ACTIVE_HSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_HSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) { @@ -480,13 +480,13 @@ static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) } #define REG_HDMI_ACTIVE_VSYNC 0x000002b8 -#define HDMI_ACTIVE_VSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_VSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_VSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & HDMI_ACTIVE_VSYNC_START__MASK; } -#define HDMI_ACTIVE_VSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_VSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) { @@ -494,13 +494,13 @@ static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) } #define REG_HDMI_VSYNC_ACTIVE_F2 0x000002bc -#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00000fff +#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff #define HDMI_VSYNC_ACTIVE_F2_START__SHIFT 0 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_START(uint32_t val) { return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & HDMI_VSYNC_ACTIVE_F2_START__MASK; } -#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x0fff0000 +#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000 #define HDMI_VSYNC_ACTIVE_F2_END__SHIFT 16 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) { @@ -508,13 +508,13 @@ static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) } #define REG_HDMI_TOTAL 0x000002c0 -#define HDMI_TOTAL_H_TOTAL__MASK 0x00000fff +#define HDMI_TOTAL_H_TOTAL__MASK 0x00001fff #define HDMI_TOTAL_H_TOTAL__SHIFT 0 static inline uint32_t HDMI_TOTAL_H_TOTAL(uint32_t val) { return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & HDMI_TOTAL_H_TOTAL__MASK; } -#define HDMI_TOTAL_V_TOTAL__MASK 0x0fff0000 +#define HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000 #define HDMI_TOTAL_V_TOTAL__SHIFT 16 static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) { @@ -522,7 +522,7 @@ static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) } #define REG_HDMI_VSYNC_TOTAL_F2 0x000002c4 -#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00000fff +#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff #define HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT 0 static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) { diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b9503564cdd6..02a20462121d 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -391,6 +391,8 @@ static int msm_load(struct drm_device *dev, unsigned long flags) INIT_LIST_HEAD(&priv->vblank_ctrl.event_list); init_kthread_work(&priv->vblank_ctrl.work, vblank_ctrl_worker); spin_lock_init(&priv->vblank_ctrl.lock); + hash_init(priv->mn_hash); + mutex_init(&priv->mn_lock); drm_mode_config_init(dev); @@ -559,7 +561,8 @@ static struct msm_file_private *setup_pagetable(struct msm_drm_private *priv) return ERR_PTR(-ENOMEM); ctx->aspace = msm_gem_address_space_create_instance( - priv->gpu->aspace->mmu, "gpu", 0x100000000, 0x1ffffffff); + priv->gpu->aspace->mmu, "gpu", 0x100000000ULL, + TASK_SIZE_64 - 1); if (IS_ERR(ctx->aspace)) { int ret = PTR_ERR(ctx->aspace); @@ -601,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; @@ -629,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); @@ -1141,6 +1152,20 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data, args->flags, &args->handle); } +static int msm_ioctl_gem_svm_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_svm_new *args = data; + + if (args->flags & ~MSM_BO_FLAGS) { + DRM_ERROR("invalid flags: %08x\n", args->flags); + return -EINVAL; + } + + return msm_gem_svm_new_handle(dev, file, args->hostptr, args->size, + args->flags, &args->handle); +} + static inline ktime_t to_ktime(struct drm_msm_timespec timeout) { return ktime_set(timeout.tv_sec, timeout.tv_nsec); @@ -1193,6 +1218,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, { struct drm_msm_gem_info *args = data; struct drm_gem_object *obj; + struct msm_gem_object *msm_obj; struct msm_file_private *ctx = file->driver_priv; int ret = 0; @@ -1203,10 +1229,10 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!obj) return -ENOENT; + msm_obj = to_msm_bo(obj); if (args->flags & MSM_INFO_IOVA) { struct msm_gem_address_space *aspace = NULL; struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj = to_msm_bo(obj); uint64_t iova; if (msm_obj->flags & MSM_BO_SECURE && priv->gpu) @@ -1223,6 +1249,14 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!ret) args->offset = iova; } else { + if (msm_obj->flags & MSM_BO_SVM) { + /* + * Offset for an SVM object is not needed as they are + * already mmap'ed before the SVM ioctl is invoked. + */ + ret = -EACCES; + goto out; + } args->offset = msm_gem_mmap_offset(obj); } @@ -1657,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; @@ -1700,6 +1762,12 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync, 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 = { @@ -1968,6 +2036,7 @@ void __exit adreno_unregister(void) static int __init msm_drm_register(void) { DBG("init"); + msm_smmu_driver_init(); msm_dsi_register(); msm_edp_register(); hdmi_register(); @@ -1983,6 +2052,7 @@ static void __exit msm_drm_unregister(void) adreno_unregister(); msm_edp_unregister(); msm_dsi_unregister(); + msm_smmu_driver_cleanup(); } module_init(msm_drm_register); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 54a3568ca11f..660c608b64f0 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -34,6 +34,7 @@ #include <linux/of_graph.h> #include <linux/of_device.h> #include <linux/sde_io_util.h> +#include <linux/hashtable.h> #include <asm/sizes.h> #include <linux/kthread.h> @@ -77,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 { @@ -111,6 +115,7 @@ enum msm_mdp_plane_property { PLANE_PROP_ROTATION, PLANE_PROP_BLEND_OP, PLANE_PROP_SRC_CONFIG, + PLANE_PROP_FB_TRANSLATION_MODE, /* total # of properties */ PLANE_PROP_COUNT @@ -129,6 +134,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_CORE_CLK, CRTC_PROP_CORE_AB, CRTC_PROP_CORE_IB, + CRTC_PROP_SECURITY_LEVEL, /* total # of properties */ CRTC_PROP_COUNT @@ -325,6 +331,11 @@ struct msm_drm_private { unsigned int num_connectors; struct drm_connector *connectors[MAX_CONNECTORS]; + /* hash to store mm_struct to msm_mmu_notifier mappings */ + DECLARE_HASHTABLE(mn_hash, 7); + /* protects mn_hash and the msm_mmu_notifier for the process */ + struct mutex mn_lock; + /* Properties */ struct drm_property *plane_property[PLANE_PROP_COUNT]; struct drm_property *crtc_property[CRTC_PROP_COUNT]; @@ -404,10 +415,15 @@ void msm_update_fence(struct drm_device *dev, uint32_t fence); void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv); + void *priv, bool invalidated); int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags); +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *domain, + uint64_t hostptr, uint64_t size); +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma); void msm_gem_address_space_put(struct msm_gem_address_space *aspace); @@ -424,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); @@ -471,6 +488,12 @@ struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev, struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt, u32 flags); void msm_gem_sync(struct drm_gem_object *obj, u32 op); +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle); +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags); int msm_framebuffer_prepare(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace); @@ -487,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 4b1a586d474d..b7dd84cb23b9 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -25,6 +25,129 @@ #include "msm_gpu.h" #include "msm_mmu.h" +static void msm_gem_mn_free(struct kref *refcount) +{ + struct msm_mmu_notifier *msm_mn = container_of(refcount, + struct msm_mmu_notifier, refcount); + + mmu_notifier_unregister(&msm_mn->mn, msm_mn->mm); + hash_del(&msm_mn->node); + + kfree(msm_mn); +} + +static int msm_gem_mn_get(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) + return kref_get_unless_zero(&msm_mn->refcount); + return 0; +} + +static void msm_gem_mn_put(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) { + struct msm_drm_private *msm_dev = msm_mn->msm_dev; + + mutex_lock(&msm_dev->mn_lock); + kref_put(&msm_mn->refcount, msm_gem_mn_free); + mutex_unlock(&msm_dev->mn_lock); + } +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end); + +static const struct mmu_notifier_ops msm_mn_ops = { + .invalidate_range_start = msm_mn_invalidate_range_start, +}; + +static struct msm_mmu_notifier * +msm_gem_mn_find(struct msm_drm_private *msm_dev, struct mm_struct *mm, + struct msm_gem_address_space *aspace) +{ + struct msm_mmu_notifier *msm_mn; + int ret = 0; + + mutex_lock(&msm_dev->mn_lock); + hash_for_each_possible(msm_dev->mn_hash, msm_mn, node, + (unsigned long) mm) { + if (msm_mn->mm == mm) { + if (!msm_gem_mn_get(msm_mn)) { + ret = -EINVAL; + goto fail; + } + mutex_unlock(&msm_dev->mn_lock); + return msm_mn; + } + } + + msm_mn = kzalloc(sizeof(*msm_mn), GFP_KERNEL); + if (!msm_mn) { + ret = -ENOMEM; + goto fail; + } + + msm_mn->mm = current->mm; + msm_mn->mn.ops = &msm_mn_ops; + ret = mmu_notifier_register(&msm_mn->mn, msm_mn->mm); + if (ret) { + kfree(msm_mn); + goto fail; + } + + msm_mn->svm_tree = RB_ROOT; + spin_lock_init(&msm_mn->svm_tree_lock); + kref_init(&msm_mn->refcount); + msm_mn->msm_dev = msm_dev; + + /* Insert the msm_mn into the hash */ + hash_add(msm_dev->mn_hash, &msm_mn->node, (unsigned long) msm_mn->mm); + mutex_unlock(&msm_dev->mn_lock); + + return msm_mn; + +fail: + mutex_unlock(&msm_dev->mn_lock); + return ERR_PTR(ret); +} + +static int msm_gem_mn_register(struct msm_gem_svm_object *msm_svm_obj, + struct msm_gem_address_space *aspace) +{ + struct drm_gem_object *obj = &msm_svm_obj->msm_obj_base.base; + struct msm_drm_private *msm_dev = obj->dev->dev_private; + struct msm_mmu_notifier *msm_mn; + + msm_svm_obj->mm = current->mm; + msm_svm_obj->svm_node.start = msm_svm_obj->hostptr; + msm_svm_obj->svm_node.last = msm_svm_obj->hostptr + obj->size - 1; + + msm_mn = msm_gem_mn_find(msm_dev, msm_svm_obj->mm, aspace); + if (IS_ERR(msm_mn)) + return PTR_ERR(msm_mn); + + msm_svm_obj->msm_mn = msm_mn; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_insert(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); + + return 0; +} + +static void msm_gem_mn_unregister(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_mmu_notifier *msm_mn = msm_svm_obj->msm_mn; + + /* invalid: bo already unregistered */ + if (!msm_mn || msm_svm_obj->invalid) + return; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_remove(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); +} + static int protect_pages(struct msm_gem_object *msm_obj) { int perm = PERM_READ | PERM_WRITE; @@ -175,10 +298,19 @@ static void put_pages(struct drm_gem_object *obj) sg_free_table(msm_obj->sgt); kfree(msm_obj->sgt); - if (use_pages(obj)) - drm_gem_put_pages(obj, msm_obj->pages, true, false); - else + if (use_pages(obj)) { + if (msm_obj->flags & MSM_BO_SVM) { + int npages = obj->size >> PAGE_SHIFT; + + release_pages(msm_obj->pages, npages, 0); + kfree(msm_obj->pages); + } else { + drm_gem_put_pages(obj, msm_obj->pages, + true, false); + } + } else { put_pages_vram(obj); + } msm_obj->pages = NULL; } @@ -205,8 +337,8 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj, { struct msm_gem_object *msm_obj = to_msm_bo(obj); - /* We can't mmap secure objects */ - if (msm_obj->flags & MSM_BO_SECURE) { + /* We can't mmap secure objects or SVM objects */ + if (msm_obj->flags & (MSM_BO_SECURE | MSM_BO_SVM)) { drm_gem_vm_close(vma); return -EACCES; } @@ -348,14 +480,21 @@ static void put_iova(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj; struct msm_gem_vma *domain, *tmp; + bool invalid = false; WARN_ON(!mutex_is_locked(&msm_obj->lock)); + if (msm_obj->flags & MSM_BO_SVM) { + msm_svm_obj = to_msm_svm_obj(msm_obj); + invalid = msm_svm_obj->invalid; + } + list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { if (iommu_present(&platform_bus_type)) { msm_gem_unmap_vma(domain->aspace, domain, - msm_obj->sgt, get_dmabuf_ptr(obj)); + msm_obj->sgt, get_dmabuf_ptr(obj), invalid); } obj_remove_domain(domain); @@ -514,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); @@ -658,15 +805,26 @@ void msm_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj = NULL; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); /* object should not be on active list: */ WARN_ON(is_active(msm_obj)); + + if (msm_obj->flags & MSM_BO_SVM) + msm_svm_obj = to_msm_svm_obj(msm_obj); + list_del(&msm_obj->mm_list); - mutex_lock(&msm_obj->lock); + /* Unregister SVM object from mmu notifications */ + if (msm_obj->flags & MSM_BO_SVM) { + msm_gem_mn_unregister(msm_svm_obj); + msm_gem_mn_put(msm_svm_obj->msm_mn); + msm_svm_obj->msm_mn = NULL; + } + mutex_lock(&msm_obj->lock); put_iova(obj); if (obj->import_attach) { @@ -691,7 +849,10 @@ void msm_gem_free_object(struct drm_gem_object *obj) drm_gem_object_release(obj); mutex_unlock(&msm_obj->lock); - kfree(msm_obj); + if (msm_obj->flags & MSM_BO_SVM) + kfree(msm_svm_obj); + else + kfree(msm_obj); } /* convenience method to construct a GEM buffer object, and userspace handle */ @@ -714,26 +875,32 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, return ret; } -static inline void msm_gem_add_to_inactive_list(struct msm_gem_object *msm_obj, - struct drm_device *dev, bool struct_mutex_locked) +/* convenience method to construct an SVM buffer object, and userspace handle */ +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle) { - struct msm_drm_private *priv = dev->dev_private; + struct drm_gem_object *obj; + int ret; - if (struct_mutex_locked) { - list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - } else { - mutex_lock(&dev->struct_mutex); - list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - mutex_unlock(&dev->struct_mutex); - } + obj = msm_gem_svm_new(dev, file, hostptr, size, flags); + + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = drm_gem_handle_create(file, obj, handle); + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); + + return ret; } -static int msm_gem_new_impl(struct drm_device *dev, - uint32_t size, uint32_t flags, struct drm_gem_object **obj, - bool struct_mutex_locked) +static int msm_gem_obj_init(struct drm_device *dev, + uint32_t size, uint32_t flags, + struct msm_gem_object *msm_obj, bool struct_mutex_locked) { struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj; bool use_vram = false; switch (flags & MSM_BO_CACHE_MASK) { @@ -755,10 +922,6 @@ static int msm_gem_new_impl(struct drm_device *dev, if (WARN_ON(use_vram && !priv->vram.size)) return -EINVAL; - msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); - if (!msm_obj) - return -ENOMEM; - mutex_init(&msm_obj->lock); if (use_vram) { @@ -773,20 +936,44 @@ static int msm_gem_new_impl(struct drm_device *dev, msm_obj->resv = &msm_obj->_resv; reservation_object_init(msm_obj->resv); + INIT_LIST_HEAD(&msm_obj->mm_list); INIT_LIST_HEAD(&msm_obj->submit_entry); INIT_LIST_HEAD(&msm_obj->domains); - msm_gem_add_to_inactive_list(msm_obj, dev, struct_mutex_locked); - - *obj = &msm_obj->base; + if (struct_mutex_locked) { + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + } else { + mutex_lock(&dev->struct_mutex); + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + mutex_unlock(&dev->struct_mutex); + } return 0; } +static struct drm_gem_object *msm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags, bool struct_mutex_locked) +{ + struct msm_gem_object *msm_obj; + int ret; + + msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); + if (!msm_obj) + return ERR_PTR(-ENOMEM); + + ret = msm_gem_obj_init(dev, size, flags, msm_obj, struct_mutex_locked); + if (ret) { + kfree(msm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags, bool struct_mutex_locked) { - struct drm_gem_object *obj = NULL; + struct drm_gem_object *obj; int ret; size = PAGE_ALIGN(size); @@ -798,9 +985,9 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, if (!size) return ERR_PTR(-EINVAL); - ret = msm_gem_new_impl(dev, size, flags, &obj, struct_mutex_locked); - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, flags, struct_mutex_locked); + if (IS_ERR(obj)) + return obj; if (use_pages(obj)) { ret = drm_gem_object_init(dev, obj, size); @@ -813,8 +1000,7 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } @@ -831,6 +1017,143 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, return _msm_gem_new(dev, size, flags, false); } +static struct drm_gem_object *msm_svm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_object *msm_obj; + int ret; + + msm_svm_obj = kzalloc(sizeof(*msm_svm_obj), GFP_KERNEL); + if (!msm_svm_obj) + return ERR_PTR(-ENOMEM); + + msm_obj = &msm_svm_obj->msm_obj_base; + + ret = msm_gem_obj_init(dev, size, flags | MSM_BO_SVM, msm_obj, false); + if (ret) { + kfree(msm_svm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + +/* convenience method to construct an SVM GEM bo, and userspace handle */ +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags) +{ + struct drm_gem_object *obj; + struct msm_file_private *ctx = file->driver_priv; + struct msm_gem_address_space *aspace = ctx->aspace; + struct msm_gem_object *msm_obj; + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_vma *domain = NULL; + struct page **p; + int npages; + int num_pinned = 0; + int write; + int ret; + + /* if we don't have IOMMU, don't bother pretending we can import: */ + if (!iommu_present(&platform_bus_type)) { + dev_err_once(dev->dev, "cannot import without IOMMU\n"); + return ERR_PTR(-EINVAL); + } + + /* hostptr and size must be page-aligned */ + if (offset_in_page(hostptr | size)) + return ERR_PTR(-EINVAL); + + /* Only CPU cached SVM objects are allowed */ + if ((flags & MSM_BO_CACHE_MASK) != MSM_BO_CACHED) + return ERR_PTR(-EINVAL); + + /* Allocate and initialize a new msm_gem_object */ + obj = msm_svm_gem_new_impl(dev, size, flags); + if (IS_ERR(obj)) + return obj; + + drm_gem_private_object_init(dev, obj, size); + + msm_obj = to_msm_bo(obj); + domain = obj_add_domain(&msm_obj->base, aspace); + if (IS_ERR(domain)) { + drm_gem_object_unreference_unlocked(obj); + return ERR_CAST(domain); + } + + /* Reserve iova if not already in use, else fail */ + ret = msm_gem_reserve_iova(aspace, domain, hostptr, size); + if (ret) { + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(ret); + } + + msm_svm_obj = to_msm_svm_obj(msm_obj); + msm_svm_obj->hostptr = hostptr; + msm_svm_obj->invalid = false; + + ret = msm_gem_mn_register(msm_svm_obj, aspace); + if (ret) + goto fail; + + /* + * Get physical pages and map into smmu in the ioctl itself. + * The driver handles iova allocation, physical page allocation and + * SMMU map all in one go. If we break this, then we have to maintain + * state to tell if physical pages allocation/map needs to happen. + * For SVM, iova reservation needs to happen in the ioctl itself, + * so do the rest right here as well. + */ + npages = size >> PAGE_SHIFT; + p = kcalloc(npages, sizeof(struct page *), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto fail; + } + + write = (msm_obj->flags & MSM_BO_GPU_READONLY) ? 0 : 1; + /* This may hold mm->mmap_sem */ + num_pinned = get_user_pages_fast(hostptr, npages, write, p); + if (num_pinned != npages) { + ret = -EINVAL; + goto free_pages; + } + + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); + if (IS_ERR(msm_obj->sgt)) { + ret = PTR_ERR(msm_obj->sgt); + goto free_pages; + } + + msm_obj->pages = p; + + ret = aspace->mmu->funcs->map(aspace->mmu, domain->iova, + msm_obj->sgt, msm_obj->flags, get_dmabuf_ptr(obj)); + if (ret) + goto free_pages; + + kref_get(&aspace->kref); + + return obj; + +free_pages: + release_pages(p, num_pinned, 0); + kfree(p); + +fail: + if (domain) + msm_gem_release_iova(aspace, domain); + + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); + + return ERR_PTR(ret); +} + struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt, u32 flags) { @@ -846,10 +1169,9 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, size = PAGE_ALIGN(size); - ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj, false); - - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, MSM_BO_WC, false); + if (IS_ERR(obj)) + return obj; drm_gem_private_object_init(dev, obj, size); @@ -868,7 +1190,8 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, /* OR the passed in flags */ msm_obj->flags |= flags; - ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages); + ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, + NULL, npages); if (ret) { mutex_unlock(&msm_obj->lock); goto fail; @@ -879,8 +1202,86 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } + +/* Timeout in ms, long enough so we are sure the GPU is hung */ +#define SVM_OBJ_WAIT_TIMEOUT 10000 +static void invalidate_svm_object(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_gem_object *msm_obj = &msm_svm_obj->msm_obj_base; + struct drm_device *dev = msm_obj->base.dev; + struct msm_gem_vma *domain, *tmp; + uint32_t fence; + int ret; + + if (is_active(msm_obj)) { + ktime_t timeout = ktime_add_ms(ktime_get(), + SVM_OBJ_WAIT_TIMEOUT); + + /* Get the most recent fence that touches the object */ + fence = msm_gem_fence(msm_obj, MSM_PREP_READ | MSM_PREP_WRITE); + + /* Wait for the fence to retire */ + ret = msm_wait_fence(dev, fence, &timeout, true); + if (ret) + /* The GPU could be hung! Not much we can do */ + dev_err(dev->dev, "drm: Error (%d) waiting for svm object: 0x%llx", + ret, msm_svm_obj->hostptr); + } + + /* GPU is done, unmap object from SMMU */ + mutex_lock(&msm_obj->lock); + list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { + struct msm_gem_address_space *aspace = domain->aspace; + + if (domain->iova) + aspace->mmu->funcs->unmap(aspace->mmu, + domain->iova, msm_obj->sgt, + get_dmabuf_ptr(&msm_obj->base)); + } + /* Let go of the physical pages */ + put_pages(&msm_obj->base); + mutex_unlock(&msm_obj->lock); +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end) +{ + struct msm_mmu_notifier *msm_mn = + container_of(mn, struct msm_mmu_notifier, mn); + struct interval_tree_node *itn = NULL; + struct msm_gem_svm_object *msm_svm_obj; + struct drm_gem_object *obj; + LIST_HEAD(inv_list); + + if (!msm_gem_mn_get(msm_mn)) + return; + + spin_lock(&msm_mn->svm_tree_lock); + itn = interval_tree_iter_first(&msm_mn->svm_tree, start, end - 1); + while (itn) { + msm_svm_obj = container_of(itn, + struct msm_gem_svm_object, svm_node); + obj = &msm_svm_obj->msm_obj_base.base; + + if (kref_get_unless_zero(&obj->refcount)) + list_add(&msm_svm_obj->lnode, &inv_list); + + itn = interval_tree_iter_next(itn, start, end - 1); + } + spin_unlock(&msm_mn->svm_tree_lock); + + list_for_each_entry(msm_svm_obj, &inv_list, lnode) { + obj = &msm_svm_obj->msm_obj_base.base; + /* Unregister SVM object from mmu notifications */ + msm_gem_mn_unregister(msm_svm_obj); + msm_svm_obj->invalid = true; + invalidate_svm_object(msm_svm_obj); + drm_gem_object_unreference_unlocked(obj); + } + + msm_gem_mn_put(msm_mn); +} diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index eb850952f1f5..e8528892939f 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -20,11 +20,14 @@ #include <linux/kref.h> #include <linux/reservation.h> +#include <linux/mmu_notifier.h> +#include <linux/interval_tree.h> #include "msm_drv.h" /* Additional internal-use only BO flags: */ #define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */ #define MSM_BO_LOCKED 0x20000000 /* Pages have been securely locked */ +#define MSM_BO_SVM 0x40000000 /* bo is SVM */ struct msm_gem_address_space { const char *name; @@ -85,6 +88,32 @@ struct msm_gem_object { }; #define to_msm_bo(x) container_of(x, struct msm_gem_object, base) +struct msm_mmu_notifier { + struct mmu_notifier mn; + struct mm_struct *mm; /* mm_struct owning the mmu notifier mn */ + struct hlist_node node; + struct rb_root svm_tree; /* interval tree holding all svm bos */ + spinlock_t svm_tree_lock; /* Protects svm_tree*/ + struct msm_drm_private *msm_dev; + struct kref refcount; +}; + +struct msm_gem_svm_object { + struct msm_gem_object msm_obj_base; + uint64_t hostptr; + struct mm_struct *mm; /* mm_struct the svm bo belongs to */ + struct interval_tree_node svm_node; + struct msm_mmu_notifier *msm_mn; + struct list_head lnode; + /* bo has been unmapped on CPU, cannot be part of GPU submits */ + bool invalid; +}; + +#define to_msm_svm_obj(x) \ + ((struct msm_gem_svm_object *) \ + container_of(x, struct msm_gem_svm_object, msm_obj_base)) + + static inline bool is_active(struct msm_gem_object *msm_obj) { return msm_obj->gpu != NULL; @@ -103,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 @@ -111,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 c9b3151180dc..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) @@ -217,6 +234,18 @@ retry: submit->bos[i].flags |= BO_LOCKED; } + /* + * An invalid SVM object is part of + * this submit's buffer list, fail. + */ + if (msm_obj->flags & MSM_BO_SVM) { + struct msm_gem_svm_object *msm_svm_obj = + to_msm_svm_obj(msm_obj); + if (msm_svm_obj->invalid) { + ret = -EINVAL; + goto fail; + } + } /* if locking succeeded, pin bo: */ ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova); @@ -369,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); @@ -386,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; @@ -400,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; @@ -422,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) { @@ -454,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; } @@ -470,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) @@ -485,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_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 12e3c0f7c101..f399d24019e4 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -88,7 +88,7 @@ static int allocate_iova(struct msm_gem_address_space *aspace, return 0; } ret = drm_mm_insert_node(&aspace->mm, &vma->node, - size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_DEFAULT); + size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_BOTTOM_UP); spin_unlock(&aspace->lock); @@ -98,6 +98,45 @@ static int allocate_iova(struct msm_gem_address_space *aspace, return ret; } +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, + uint64_t hostptr, uint64_t size) +{ + struct drm_mm *mm = &aspace->mm; + uint64_t start = hostptr >> PAGE_SHIFT; + uint64_t last = (hostptr + size - 1) >> PAGE_SHIFT; + int ret; + + spin_lock(&aspace->lock); + + if (drm_mm_interval_first(mm, start, last)) { + /* iova already in use, fail */ + spin_unlock(&aspace->lock); + return -EADDRINUSE; + } + + vma->node.start = hostptr >> PAGE_SHIFT; + vma->node.size = size >> PAGE_SHIFT; + vma->node.color = 0; + + ret = drm_mm_reserve_node(mm, &vma->node); + if (!ret) + vma->iova = hostptr; + + spin_unlock(&aspace->lock); + + return ret; +} + +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma) +{ + spin_lock(&aspace->lock); + if (drm_mm_node_allocated(&vma->node)) + drm_mm_remove_node(&vma->node); + spin_unlock(&aspace->lock); +} + int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags) @@ -116,11 +155,7 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, flags, priv); if (ret) { - spin_lock(&aspace->lock); - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); - spin_unlock(&aspace->lock); - + msm_gem_release_iova(aspace, vma); return ret; } @@ -131,17 +166,16 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, } void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) + struct msm_gem_vma *vma, struct sg_table *sgt, + void *priv, bool invalidated) { if (!aspace || !vma->iova) return; - aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); + if (!invalidated) + aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); - spin_lock(&aspace->lock); - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); - spin_unlock(&aspace->lock); + msm_gem_release_iova(aspace, vma); vma->iova = 0; 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_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 033370ccbe24..8148d3e9e850 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -83,4 +83,8 @@ static inline void msm_mmu_disable(struct msm_mmu *mmu) mmu->funcs->disable(mmu); } +/* SDE smmu driver initialize and cleanup functions */ +int __init msm_smmu_driver_init(void); +void __exit msm_smmu_driver_cleanup(void); + #endif /* __MSM_MMU_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 a6efb22b5ed4..7673f54b7691 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -133,7 +133,7 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova, domain = client->mmu_mapping->domain; return iommu_map_sg(domain, iova, sgt->sgl, - sgt->nents, flags); + sgt->nents, flags); } else { if (priv) ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, @@ -151,27 +151,13 @@ static void msm_smmu_unmap(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 = client->mmu_mapping->domain; - struct scatterlist *sg; - size_t len = 0; - int unmapped, i = 0; - if (iova != 0) { - for_each_sg(sgt->sgl, sg, sgt->nents, i) - len += sg->length; - - unmapped = iommu_unmap(domain, iova, len); - if (unmapped < len) - dev_warn(mmu->dev, - "could not unmap iova@%llx\n", iova); - } else { - if (priv) - msm_dma_unmap_sg(client->dev, sgt->sgl, - sgt->nents, DMA_BIDIRECTIONAL, priv); - else - dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, - DMA_BIDIRECTIONAL); - } + if (priv) + msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL, priv); + else + dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL); } static void msm_smmu_destroy(struct msm_mmu *mmu) @@ -220,13 +206,13 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = { }; static const struct of_device_id msm_smmu_dt_match[] = { - { .compatible = "qcom,smmu_mdp_unsec", + { .compatible = "qcom,smmu_sde_unsec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_UNSECURE] }, - { .compatible = "qcom,smmu_mdp_sec", + { .compatible = "qcom,smmu_sde_sec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_SECURE] }, - { .compatible = "qcom,smmu_rot_unsec", + { .compatible = "qcom,smmu_sde_nrt_unsec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_UNSECURE] }, - { .compatible = "qcom,smmu_rot_sec", + { .compatible = "qcom,smmu_sde_nrt_sec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_SECURE] }, {} }; @@ -422,7 +408,7 @@ static struct platform_driver msm_smmu_driver = { }, }; -static int __init msm_smmu_driver_init(void) +int __init msm_smmu_driver_init(void) { int ret; @@ -432,13 +418,11 @@ static int __init msm_smmu_driver_init(void) return ret; } -module_init(msm_smmu_driver_init); -static void __exit msm_smmu_driver_cleanup(void) +void __exit msm_smmu_driver_cleanup(void) { platform_driver_unregister(&msm_smmu_driver); } -module_exit(msm_smmu_driver_cleanup); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MSM SMMU driver"); 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_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index adfe9e54e6f4..cb5f7d3cf19f 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -57,6 +57,7 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { struct msm_drm_private *priv = crtc->dev->dev_private; + return to_sde_kms(priv->kms); } @@ -183,9 +184,6 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, pstate = to_sde_plane_state(plane->state); - flush_mask = ctl->ops.get_bitmask_sspp(ctl, - sde_plane_pipe(plane)); - /* always stage plane on either left or right lm */ if (plane->state->crtc_x >= crtc_split_width) { lm_idx = RIGHT_MIXER; @@ -195,20 +193,36 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, idx = left_crtc_zpos_cnt[pstate->stage]++; } + /* + * program each mixer with two hw pipes in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[LEFT_MIXER][pstate->stage][1] = + sde_plane_pipe(plane, 1); + + flush_mask = ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, 1)); + } + + flush_mask |= ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, lm_idx ? 1 : 0)); + /* stage plane on right LM if it crosses the boundary */ lm_right = (lm_idx == LEFT_MIXER) && (plane->state->crtc_x + plane->state->crtc_w > crtc_split_width); stage_cfg->stage[lm_idx][pstate->stage][idx] = - sde_plane_pipe(plane); + sde_plane_pipe(plane, lm_idx ? 1 : 0); + mixer[lm_idx].flush_mask |= flush_mask; SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n", crtc->base.id, pstate->stage, plane->base.id, - sde_plane_pipe(plane) - SSPP_VIG0, + sde_plane_pipe(plane, + lm_idx ? 1 : 0) - SSPP_VIG0, plane->state->fb ? plane->state->fb->base.id : -1); @@ -230,8 +244,19 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, if (lm_right) { idx = right_crtc_zpos_cnt[pstate->stage]++; - stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] = - sde_plane_pipe(plane); + + /* + * program each mixer with two hw pipes + in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[RIGHT_MIXER][pstate->stage][1] + = sde_plane_pipe(plane, 0); + } + + stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] + = sde_plane_pipe(plane, 1); + mixer[RIGHT_MIXER].flush_mask |= flush_mask; /* blend config update */ @@ -1256,7 +1281,8 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) return 0; } -void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) +void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, + struct drm_file *file) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); @@ -1275,6 +1301,10 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, struct drm_device *dev; struct sde_kms_info *info; struct sde_kms *sde_kms; + static const struct drm_prop_enum_list e_secure_level[] = { + {SDE_DRM_SEC_NON_SEC, "sec_and_non_sec"}, + {SDE_DRM_SEC_ONLY, "sec_only"}, + }; SDE_DEBUG("\n"); @@ -1320,6 +1350,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, msm_property_install_blob(&sde_crtc->property_info, "capabilities", DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO); + + msm_property_install_enum(&sde_crtc->property_info, "security_level", + 0x0, 0, e_secure_level, + ARRAY_SIZE(e_secure_level), + CRTC_PROP_SECURITY_LEVEL); + sde_kms_info_reset(info); sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion); @@ -1665,7 +1701,8 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc, #endif /* initialize crtc */ -struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) +struct drm_crtc *sde_crtc_init(struct drm_device *dev, + struct drm_plane *plane) { struct drm_crtc *crtc = NULL; struct sde_crtc *sde_crtc = NULL; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 519288f0dda2..17b678cfca46 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -14,6 +14,8 @@ #include <linux/slab.h> #include <linux/of_address.h> +#include <linux/of_platform.h> + #include "sde_hw_mdss.h" #include "sde_hw_catalog.h" #include "sde_hw_catalog_format.h" @@ -715,6 +717,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "vig%d", *vig_count-1); } static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, @@ -753,6 +756,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "rgb%d", *rgb_count-1); } static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, @@ -766,6 +770,7 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count; sblk->format_list = plane_formats; (*cursor_count)++; + snprintf(sspp->name, sizeof(sspp->name), "cursor%d", *cursor_count-1); } static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, @@ -779,6 +784,7 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, sblk->format_list = plane_formats; set_bit(SDE_SSPP_QOS, &sspp->features); (*dma_count)++; + snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1); } static int sde_sspp_parse_dt(struct device_node *np, @@ -1200,7 +1206,8 @@ end: return rc; } -static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_wb_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[WB_PROP_MAX], i, j; struct sde_prop_value *prop_value = NULL; @@ -1686,7 +1693,8 @@ end: return rc; } -static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_pp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[PP_PROP_MAX], i; struct sde_prop_value *prop_value = NULL; @@ -1760,6 +1768,94 @@ end: return rc; } +static inline u32 _sde_parse_sspp_id(struct sde_mdss_cfg *cfg, + const char *name) +{ + int i; + + for (i = 0; i < cfg->sspp_count; i++) { + if (!strcmp(cfg->sspp[i].name, name)) + return cfg->sspp[i].id; + } + + return SSPP_NONE; +} + +static int _sde_vp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) +{ + int rc = 0, i = 0; + struct device_node *node = NULL; + struct device_node *root_node = NULL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; + struct property *prop; + const char *cname; + + root_node = of_get_child_by_name(np, "qcom,sde-plane-id-map"); + if (!root_node) { + root_node = of_parse_phandle(np, "qcom,sde-plane-id-map", 0); + if (!root_node) { + SDE_ERROR("No entry present for qcom,sde-plane-id-map"); + rc = -EINVAL; + goto end; + } + } + + for_each_child_of_node(root_node, node) { + if (i >= MAX_BLOCKS) { + SDE_ERROR("num of nodes(%d) is bigger than max(%d)\n", + i, MAX_BLOCKS); + rc = -EINVAL; + goto end; + } + cfg->vp_count++; + vp = &(cfg->vp[i]); + vp->id = i; + rc = of_property_read_string(node, "qcom,display-type", + &(vp->display_type)); + if (rc) { + SDE_ERROR("failed to read display-type, rc = %d\n", rc); + goto end; + } + + rc = of_property_read_string(node, "qcom,plane-type", + &(vp->plane_type)); + if (rc) { + SDE_ERROR("failed to read plane-type, rc = %d\n", rc); + goto end; + } + + INIT_LIST_HEAD(&vp->sub_blks); + of_property_for_each_string(node, "qcom,plane-name", + prop, cname) { + vp_sub = kzalloc(sizeof(*vp_sub), GFP_KERNEL); + if (!vp_sub) { + rc = -ENOMEM; + goto end; + } + vp_sub->sspp_id = _sde_parse_sspp_id(cfg, cname); + list_add_tail(&vp_sub->pipeid_list, &vp->sub_blks); + } + i++; + } + +end: + if (rc && cfg->vp_count) { + vp = &(cfg->vp[i]); + for (i = 0; i < cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &vp->sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + memset(&(cfg->vp[0]), 0, sizeof(cfg->vp)); + cfg->vp_count = 0; + } + return rc; +} + static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) { int rc, len, prop_count[SDE_PROP_MAX]; @@ -1851,7 +1947,8 @@ end: return rc; } -static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) +static int sde_perf_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) { int rc, len, prop_count[PERF_PROP_MAX]; struct sde_prop_value *prop_value = NULL; @@ -1891,7 +1988,8 @@ end: return rc; } -static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) +static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, + uint32_t hw_rev) { switch (hw_rev) { case SDE_HW_VER_170: @@ -1909,6 +2007,7 @@ static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) { int i; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; if (!sde_cfg) return; @@ -1932,13 +2031,23 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) kfree(sde_cfg->vbif[i].dynamic_ot_rd_tbl.cfg); kfree(sde_cfg->vbif[i].dynamic_ot_wr_tbl.cfg); } + + for (i = 0; i < sde_cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &sde_cfg->vp[i].sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + kfree(sde_cfg); } /************************************************************* * hardware catalog init *************************************************************/ -struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) +struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, + u32 hw_rev) { int rc; struct sde_mdss_cfg *sde_cfg; @@ -1996,6 +2105,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) if (rc) goto end; + rc = _sde_vp_parse_dt(np, sde_cfg); + if (rc) + SDE_DEBUG("virtual plane is not supported.\n"); + sde_hardware_caps(sde_cfg, hw_rev); return sde_cfg; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index a8f9169aaf35..bca221d2a959 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -57,6 +57,8 @@ #define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16) #define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF) +#define SSPP_NAME_SIZE 12 + /** * MDP TOP BLOCK features * @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe @@ -455,12 +457,14 @@ struct sde_ctl_cfg { * @sblk: SSPP sub-blocks information * @xin_id: bus client identifier * @clk_ctrl clock control identifier + *@name source pipe name */ struct sde_sspp_cfg { SDE_HW_BLK_INFO; const struct sde_sspp_sub_blks *sblk; u32 xin_id; enum sde_clk_ctrl_type clk_ctrl; + char name[SSPP_NAME_SIZE]; }; /** @@ -608,6 +612,31 @@ struct sde_perf_cfg { }; /** +* struct sde_vp_sub_blks - Virtual Plane sub-blocks +* @pipeid_list list for hw pipe id +* @sspp_id SSPP ID, refer to enum sde_sspp. +*/ +struct sde_vp_sub_blks { + struct list_head pipeid_list; + u32 sspp_id; +}; + +/** +* struct sde_vp_cfg - information of Virtual Plane SW blocks +* @id enum identifying this block +* @sub_blks list head for virtual plane sub blocks +* @plane_type plane type, such as primary, overlay or cursor +* @display_type which display the plane bound to, such as primary, +* secondary or tertiary +*/ +struct sde_vp_cfg { + u32 id; + struct list_head sub_blks; + const char *plane_type; + const char *display_type; +}; + +/** * struct sde_mdss_cfg - information of MDSS HW * This is the main catalog data structure representing * this HW version. Contains number of instances, @@ -672,6 +701,9 @@ struct sde_mdss_cfg { /* Add additional block data structures here */ struct sde_perf_cfg perf; + + u32 vp_count; + struct sde_vp_cfg vp[MAX_BLOCKS]; }; struct sde_mdss_hw_cfg_handler { diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 709c9970b357..45a87456e5ec 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -797,6 +797,16 @@ static void _sde_kms_drm_obj_destroy(struct sde_kms *sde_kms) _sde_kms_release_displays(sde_kms); } +static inline int sde_get_crtc_id(const char *display_type) +{ + if (!strcmp(display_type, "primary")) + return 0; + else if (!strcmp(display_type, "secondary")) + return 1; + else + return 2; +} + static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) { struct drm_device *dev; @@ -829,28 +839,57 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) (void)_sde_kms_setup_displays(dev, priv, sde_kms); max_crtc_count = min(catalog->mixer_count, priv->num_encoders); - max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); /* Create the planes */ primary_planes_idx = 0; - for (i = 0; i < max_plane_count; i++) { - bool primary = true; - - if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) - || primary_planes_idx >= max_crtc_count) - primary = false; - - plane = sde_plane_init(dev, catalog->sspp[i].id, primary, - (1UL << max_crtc_count) - 1); - if (IS_ERR(plane)) { - SDE_ERROR("sde_plane_init failed\n"); - ret = PTR_ERR(plane); - goto fail; + if (catalog->vp_count) { + max_plane_count = min_t(u32, catalog->vp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + int crtc_id = + sde_get_crtc_id(catalog->vp[i].display_type); + + if (strcmp(catalog->vp[i].plane_type, "primary")) + primary = false; + + plane = sde_plane_init(dev, catalog->vp[i].id, + primary, 1UL << crtc_id, true); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) { + primary_planes[crtc_id] = plane; + primary_planes_idx++; + } + } + } else { + max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + + if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) + || primary_planes_idx >= max_crtc_count) + primary = false; + + plane = sde_plane_init(dev, catalog->sspp[i].id, + primary, (1UL << max_crtc_count) - 1, + false); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) + primary_planes[primary_planes_idx++] = plane; } - priv->planes[priv->num_planes++] = plane; - - if (primary) - primary_planes[primary_planes_idx++] = plane; } max_crtc_count = min(max_crtc_count, primary_planes_idx); diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 114acfd7a173..85ebff08761b 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -78,20 +78,22 @@ enum sde_plane_qos { }; /* - * struct sde_plane - local sde plane structure + * struct sde_phy_plane - physical plane structure + * @sde_plane: Points to virtual plane + * @phy_plane_list: list of hw pipe(physical plane) + * @index: index of physical plane (starts from 0, order from left to right) + * @features: capabilities from catalog * @csc_cfg: Decoded user configuration for csc * @csc_usr_ptr: Points to csc_cfg if valid user config available * @csc_ptr: Points to sde_csc_cfg structure to use for current */ -struct sde_plane { - struct drm_plane base; - - struct msm_gem_address_space *aspace; - - struct mutex lock; - +struct sde_phy_plane { + struct sde_plane *sde_plane; + struct list_head phy_plane_list; enum sde_sspp pipe; - uint32_t features; /* capabilities from catalog */ + uint32_t index; + + uint32_t features; uint32_t nformats; uint32_t formats[64]; @@ -101,7 +103,6 @@ struct sde_plane { struct sde_hw_scaler3_cfg *scaler3_cfg; struct sde_hw_pipe_qos_cfg pipe_qos_cfg; uint32_t color_fill; - bool is_error; bool is_rt_pipe; struct sde_hw_pixel_ext pixel_ext; @@ -112,9 +113,22 @@ struct sde_plane { struct sde_csc_cfg *csc_ptr; const struct sde_sspp_sub_blks *pipe_sblk; +}; +/* + * struct sde_plane - local sde plane structure + */ +struct sde_plane { + struct drm_plane base; + + struct msm_gem_address_space *aspace; + struct mutex lock; + bool is_error; char pipe_name[SDE_NAME_SIZE]; + struct list_head phy_plane_head; + u32 num_of_phy_planes; + struct msm_property_info property_info; struct msm_property_data property_data[PLANE_PROP_COUNT]; struct drm_property_blob *blob_info; @@ -133,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 @@ -140,20 +168,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state) * @src_wdith: width of source buffer * Return: fill level corresponding to the source buffer/format or 0 if error */ -static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, +static inline int _sde_plane_calc_fill_level(struct sde_phy_plane *pp, const struct sde_format *fmt, u32 src_width) { struct sde_plane *psde; u32 fixed_buff_size; u32 total_fl; - if (!plane || !fmt) { + if (!pp || !fmt) { SDE_ERROR("invalid arguments\n"); return 0; } - psde = to_sde_plane(plane); - fixed_buff_size = psde->pipe_sblk->pixel_ram_size; + psde = pp->sde_plane; + fixed_buff_size = pp->pipe_sblk->pixel_ram_size; if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) { if (fmt->chroma_sample == SDE_CHROMA_420) { @@ -171,7 +199,7 @@ static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, } SDE_DEBUG("plane%u: pnum:%d fmt:%x w:%u fl:%u\n", - plane->base.id, psde->pipe - SSPP_VIG0, + psde->base.base.id, pp->pipe - SSPP_VIG0, fmt->base.pixel_format, src_width, total_fl); return total_fl; @@ -236,7 +264,7 @@ static inline u32 _sde_plane_get_qos_lut_macrotile(u32 total_fl) * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_qos_lut(struct drm_plane *plane, +static void _sde_plane_set_qos_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; @@ -244,30 +272,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, u32 qos_lut; u32 total_fl = 0; - if (!plane || !fb) { - SDE_ERROR("invalid arguments plane %d fb %d\n", - plane != 0, fb != 0); + if (!pp || !fb) { + SDE_ERROR("invalid arguments phy_plane %d fb %d\n", + pp != NULL, fb != NULL); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_creq_lut) { + } else if (!pp->pipe_hw->ops.setup_creq_lut) { return; } - if (!psde->is_rt_pipe) { - qos_lut = psde->pipe_sblk->creq_lut_nrt; + if (!pp->is_rt_pipe) { + qos_lut = pp->pipe_sblk->creq_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, fb->modifier, drm_format_num_planes(fb->pixel_format)); - total_fl = _sde_plane_calc_fill_level(plane, fmt, - psde->pipe_cfg.src_rect.w); + total_fl = _sde_plane_calc_fill_level(pp, fmt, + pp->pipe_cfg.src_rect.w); if (SDE_FORMAT_IS_LINEAR(fmt)) qos_lut = _sde_plane_get_qos_lut_linear(total_fl); @@ -275,20 +303,20 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, qos_lut = _sde_plane_get_qos_lut_macrotile(total_fl); } - psde->pipe_qos_cfg.creq_lut = qos_lut; + pp->pipe_qos_cfg.creq_lut = qos_lut; - trace_sde_perf_set_qos_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_qos_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut, + pp->is_rt_pipe, total_fl, qos_lut, (fmt) ? SDE_FORMAT_IS_LINEAR(fmt) : 0); SDE_DEBUG("plane%u: pnum:%d fmt:%x rt:%d fl:%u lut:0x%x\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut); + pp->is_rt_pipe, total_fl, qos_lut); - psde->pipe_hw->ops.setup_creq_lut(psde->pipe_hw, &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_creq_lut(pp->pipe_hw, &pp->pipe_qos_cfg); } /** @@ -296,30 +324,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_danger_lut(struct drm_plane *plane, +static void _sde_plane_set_danger_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; const struct sde_format *fmt = NULL; u32 danger_lut, safe_lut; - if (!plane || !fb) { + if (!pp || !fb) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_danger_safe_lut) { + } else if (!pp->pipe_hw->ops.setup_danger_safe_lut) { return; } - if (!psde->is_rt_pipe) { - danger_lut = psde->pipe_sblk->danger_lut_nrt; - safe_lut = psde->pipe_sblk->safe_lut_nrt; + if (!pp->is_rt_pipe) { + danger_lut = pp->pipe_sblk->danger_lut_nrt; + safe_lut = pp->pipe_sblk->safe_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, @@ -327,33 +355,33 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, drm_format_num_planes(fb->pixel_format)); if (SDE_FORMAT_IS_LINEAR(fmt)) { - danger_lut = psde->pipe_sblk->danger_lut_linear; - safe_lut = psde->pipe_sblk->safe_lut_linear; + danger_lut = pp->pipe_sblk->danger_lut_linear; + safe_lut = pp->pipe_sblk->safe_lut_linear; } else { - danger_lut = psde->pipe_sblk->danger_lut_tile; - safe_lut = psde->pipe_sblk->safe_lut_tile; + danger_lut = pp->pipe_sblk->danger_lut_tile; + safe_lut = pp->pipe_sblk->safe_lut_tile; } } - psde->pipe_qos_cfg.danger_lut = danger_lut; - psde->pipe_qos_cfg.safe_lut = safe_lut; + pp->pipe_qos_cfg.danger_lut = danger_lut; + pp->pipe_qos_cfg.safe_lut = safe_lut; - trace_sde_perf_set_danger_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_danger_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, (fmt) ? fmt->fetch_mode : 0, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); SDE_DEBUG("plane%u: pnum:%d fmt:%x mode:%d luts[0x%x, 0x%x]\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, fmt ? fmt->base.pixel_format : 0, fmt ? fmt->fetch_mode : -1, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); - psde->pipe_hw->ops.setup_danger_safe_lut(psde->pipe_hw, - &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_danger_safe_lut(pp->pipe_hw, + &pp->pipe_qos_cfg); } /** @@ -362,85 +390,90 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, * @enable: true to enable QoS control * @flags: QoS control mode (enum sde_plane_qos) */ -static void _sde_plane_set_qos_ctrl(struct drm_plane *plane, +static void _sde_plane_set_qos_ctrl(struct sde_phy_plane *pp, bool enable, u32 flags) { struct sde_plane *psde; - if (!plane) { + if (!pp) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_qos_ctrl) { + } else if (!pp->pipe_hw->ops.setup_qos_ctrl) { return; } if (flags & SDE_PLANE_QOS_VBLANK_CTRL) { - psde->pipe_qos_cfg.creq_vblank = psde->pipe_sblk->creq_vblank; - psde->pipe_qos_cfg.danger_vblank = - psde->pipe_sblk->danger_vblank; - psde->pipe_qos_cfg.vblank_en = enable; + pp->pipe_qos_cfg.creq_vblank = pp->pipe_sblk->creq_vblank; + pp->pipe_qos_cfg.danger_vblank = + pp->pipe_sblk->danger_vblank; + pp->pipe_qos_cfg.vblank_en = enable; } if (flags & SDE_PLANE_QOS_VBLANK_AMORTIZE) { /* this feature overrules previous VBLANK_CTRL */ - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ } if (flags & SDE_PLANE_QOS_PANIC_CTRL) - psde->pipe_qos_cfg.danger_safe_en = enable; + pp->pipe_qos_cfg.danger_safe_en = enable; - if (!psde->is_rt_pipe) { - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.danger_safe_en = false; + if (!pp->is_rt_pipe) { + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.danger_safe_en = false; } SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n", - plane->base.id, - psde->pipe - SSPP_VIG0, - psde->pipe_qos_cfg.danger_safe_en, - psde->pipe_qos_cfg.vblank_en, - psde->pipe_qos_cfg.creq_vblank, - psde->pipe_qos_cfg.danger_vblank, - psde->is_rt_pipe); - - psde->pipe_hw->ops.setup_qos_ctrl(psde->pipe_hw, - &psde->pipe_qos_cfg); + psde->base.base.id, + pp->pipe - SSPP_VIG0, + pp->pipe_qos_cfg.danger_safe_en, + pp->pipe_qos_cfg.vblank_en, + pp->pipe_qos_cfg.creq_vblank, + pp->pipe_qos_cfg.danger_vblank, + pp->is_rt_pipe); + + pp->pipe_hw->ops.setup_qos_ctrl(pp->pipe_hw, + &pp->pipe_qos_cfg); } -int sde_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) +static int sde_plane_danger_signal_ctrl(struct sde_phy_plane *pp, bool enable) { struct sde_plane *psde; struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev) { + if (!pp) { SDE_ERROR("invalid arguments\n"); return -EINVAL; } + psde = pp->sde_plane; - priv = plane->dev->dev_private; + if (!psde->base.dev) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return -EINVAL; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->is_rt_pipe) + if (!pp->is_rt_pipe) goto end; sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); - _sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_qos_ctrl(pp, enable, SDE_PLANE_QOS_PANIC_CTRL); sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); @@ -453,7 +486,7 @@ end: * @plane: Pointer to drm plane * @crtc: Pointer to drm crtc */ -static void _sde_plane_set_ot_limit(struct drm_plane *plane, +static void _sde_plane_set_ot_limit(struct sde_phy_plane *pp, struct drm_crtc *crtc) { struct sde_plane *psde; @@ -461,34 +494,38 @@ static void _sde_plane_set_ot_limit(struct drm_plane *plane, struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev || !crtc) { - SDE_ERROR("invalid arguments plane %d crtc %d\n", - plane != 0, crtc != 0); + if (!pp || !crtc) { + SDE_ERROR("invalid arguments phy_plane %d crtc %d\n", + pp != NULL, crtc != NULL); + return; + } + psde = pp->sde_plane; + if (!psde->base.dev) { + SDE_ERROR("invalid DRM device\n"); return; } - priv = plane->dev->dev_private; + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + if (!pp->pipe_hw) { SDE_ERROR("invalid pipe reference\n"); return; } memset(&ot_params, 0, sizeof(ot_params)); - ot_params.xin_id = psde->pipe_hw->cap->xin_id; - ot_params.num = psde->pipe_hw->idx - SSPP_NONE; - ot_params.width = psde->pipe_cfg.src_rect.w; - ot_params.height = psde->pipe_cfg.src_rect.h; - ot_params.is_wfd = !psde->is_rt_pipe; + ot_params.xin_id = pp->pipe_hw->cap->xin_id; + ot_params.num = pp->pipe_hw->idx - SSPP_NONE; + ot_params.width = pp->pipe_cfg.src_rect.w; + ot_params.height = pp->pipe_cfg.src_rect.h; + ot_params.is_wfd = !pp->is_rt_pipe; ot_params.frame_rate = crtc->mode.vrefresh; ot_params.vbif_idx = VBIF_RT; - ot_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl; + ot_params.clk_ctrl = pp->pipe_hw->cap->clk_ctrl; ot_params.rd = true; sde_vbif_set_ot_limit(sde_kms, &ot_params); @@ -559,40 +596,97 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms) return ret; } -static inline void _sde_plane_set_scanout(struct drm_plane *plane, +/** + * _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 (!plane || !pstate || !pipe_cfg || !fb) { + if (!pp || !pstate || !pipe_cfg || !fb) { SDE_ERROR( - "invalid arg(s), plane %d state %d cfg %d fb %d\n", - plane != 0, pstate != 0, pipe_cfg != 0, fb != 0); + "invalid arg(s), phy_plane %d state %d cfg %d fb %d\n", + pp != 0, pstate != 0, pipe_cfg != 0, fb != 0); return; } - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + psde = pp->sde_plane; + if (!pp->pipe_hw) { SDE_ERROR_PLANE(psde, "invalid pipe_hw\n"); 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) SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret); - else if (psde->pipe_hw->ops.setup_sourceaddress) - psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg); + else if (pp->pipe_hw && pp->pipe_hw->ops.setup_sourceaddress) + pp->pipe_hw->ops.setup_sourceaddress(pp->pipe_hw, pipe_cfg); } -static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, +static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp, struct sde_plane_state *pstate) { - struct sde_hw_scaler3_cfg *cfg = psde->scaler3_cfg; + struct sde_plane *psde = pp->sde_plane; + struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg; int ret = 0; cfg->dir_lut = msm_property_get_blob( @@ -612,7 +706,7 @@ static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, return ret; } -static void _sde_plane_setup_scaler3(struct sde_plane *psde, +static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp, uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h, struct sde_hw_scaler3_cfg *scale_cfg, const struct sde_format *fmt, @@ -620,10 +714,10 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, { uint32_t decimated, i; - if (!psde || !scale_cfg || !fmt || !chroma_subsmpl_h || + if (!pp || !scale_cfg || !fmt || !chroma_subsmpl_h || !chroma_subsmpl_v) { SDE_ERROR("psde %pK scale_cfg %pK fmt %pK smp_h %d smp_v %d\n" - , psde, scale_cfg, fmt, chroma_subsmpl_h, + , pp, scale_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); return; } @@ -631,11 +725,11 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, memset(scale_cfg, 0, sizeof(*scale_cfg)); decimated = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->phase_step_x[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_w); decimated = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); scale_cfg->phase_step_y[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_h); @@ -657,9 +751,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, for (i = 0; i < SDE_MAX_PLANES; i++) { scale_cfg->src_width[i] = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->src_height[i] = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); if (SDE_FORMAT_IS_YUV(fmt)) scale_cfg->src_width[i] &= ~0x1; if (i == SDE_SSPP_COMP_1_2 || i == SDE_SSPP_COMP_2) { @@ -668,9 +762,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, } scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H; scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V; - psde->pixel_ext.num_ext_pxls_top[i] = + pp->pixel_ext.num_ext_pxls_top[i] = scale_cfg->src_height[i]; - psde->pixel_ext.num_ext_pxls_left[i] = + pp->pixel_ext.num_ext_pxls_left[i] = scale_cfg->src_width[i]; } if (!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h) @@ -835,7 +929,7 @@ static void _sde_plane_setup_pixel_ext(struct sde_plane *psde, } } -static inline void _sde_plane_setup_csc(struct sde_plane *psde) +static inline void _sde_plane_setup_csc(struct sde_phy_plane *pp) { static const struct sde_csc_cfg sde_csc_YUV2RGB_601L = { { @@ -866,26 +960,30 @@ static inline void _sde_plane_setup_csc(struct sde_plane *psde) { 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,}, }; - if (!psde) { + struct sde_plane *psde; + + if (!pp) { SDE_ERROR("invalid plane\n"); return; } + psde = pp->sde_plane; /* revert to kernel default if override not available */ - if (psde->csc_usr_ptr) - psde->csc_ptr = psde->csc_usr_ptr; - else if (BIT(SDE_SSPP_CSC_10BIT) & psde->features) - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; + if (pp->csc_usr_ptr) + pp->csc_ptr = pp->csc_usr_ptr; + else if (BIT(SDE_SSPP_CSC_10BIT) & pp->features) + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; else - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; SDE_DEBUG_PLANE(psde, "using 0x%X 0x%X 0x%X...\n", - psde->csc_ptr->csc_mv[0], - psde->csc_ptr->csc_mv[1], - psde->csc_ptr->csc_mv[2]); + pp->csc_ptr->csc_mv[0], + pp->csc_ptr->csc_mv[1], + pp->csc_ptr->csc_mv[2]); } -static void sde_color_process_plane_setup(struct drm_plane *plane) +static void sde_color_process_plane_setup(struct drm_plane *plane, + struct sde_phy_plane *pp) { struct sde_plane *psde; struct sde_plane_state *pstate; @@ -893,32 +991,32 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) struct drm_msm_memcol *memcol = NULL; size_t memcol_sz = 0; - psde = to_sde_plane(plane); + psde = pp->sde_plane; pstate = to_sde_plane_state(plane->state); hue = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_HUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_hue) - psde->pipe_hw->ops.setup_pa_hue(psde->pipe_hw, &hue); + if (pp->pipe_hw->ops.setup_pa_hue) + pp->pipe_hw->ops.setup_pa_hue(pp->pipe_hw, &hue); saturation = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_SATURATION_ADJUST); - if (psde->pipe_hw->ops.setup_pa_sat) - psde->pipe_hw->ops.setup_pa_sat(psde->pipe_hw, &saturation); + if (pp->pipe_hw->ops.setup_pa_sat) + pp->pipe_hw->ops.setup_pa_sat(pp->pipe_hw, &saturation); value = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_VALUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_val) - psde->pipe_hw->ops.setup_pa_val(psde->pipe_hw, &value); + if (pp->pipe_hw->ops.setup_pa_val) + pp->pipe_hw->ops.setup_pa_val(pp->pipe_hw, &value); contrast = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_CONTRAST_ADJUST); - if (psde->pipe_hw->ops.setup_pa_cont) - psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast); + if (pp->pipe_hw->ops.setup_pa_cont) + pp->pipe_hw->ops.setup_pa_cont(pp->pipe_hw, &contrast); - if (psde->pipe_hw->ops.setup_pa_memcolor) { + if (pp->pipe_hw->ops.setup_pa_memcolor) { /* Skin memory color setup */ memcol = msm_property_get_blob(&psde->property_info, pstate->property_blobs, &memcol_sz, PLANE_PROP_SKIN_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKIN, memcol); /* Sky memory color setup */ @@ -926,7 +1024,7 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_SKY_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKY, memcol); /* Foliage memory color setup */ @@ -934,87 +1032,89 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_FOLIAGE_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_FOLIAGE, memcol); } } -static void _sde_plane_setup_scaler(struct sde_plane *psde, +static void _sde_plane_setup_scaler(struct sde_phy_plane *pp, const struct sde_format *fmt, struct sde_plane_state *pstate) { struct sde_hw_pixel_ext *pe; uint32_t chroma_subsmpl_h, chroma_subsmpl_v; + struct sde_plane *psde; - if (!psde || !fmt) { - SDE_ERROR("invalid arg(s), plane %d fmt %d state %d\n", - psde != 0, fmt != 0, pstate != 0); + if (!pp || !fmt || !pstate || !pp->sde_plane) { + SDE_ERROR("invalid arg(s), phy_plane %d fmt %d\n", + pp != NULL, fmt != NULL); return; } + psde = pp->sde_plane; - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); - psde->pipe_cfg.horz_decimation = + pp->pipe_cfg.horz_decimation = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); - psde->pipe_cfg.vert_decimation = + pp->pipe_cfg.vert_decimation = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); /* don't chroma subsample if decimating */ - chroma_subsmpl_h = psde->pipe_cfg.horz_decimation ? 1 : + chroma_subsmpl_h = pp->pipe_cfg.horz_decimation ? 1 : drm_format_horz_chroma_subsampling(fmt->base.pixel_format); - chroma_subsmpl_v = psde->pipe_cfg.vert_decimation ? 1 : + chroma_subsmpl_v = pp->pipe_cfg.vert_decimation ? 1 : drm_format_vert_chroma_subsampling(fmt->base.pixel_format); /* update scaler */ - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { int error; - error = _sde_plane_setup_scaler3_lut(psde, pstate); - if (error || !psde->pixel_ext_usr) { + error = _sde_plane_setup_scaler3_lut(pp, pstate); + if (error || !pp->pixel_ext_usr) { memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); /* calculate default config for QSEED3 */ - _sde_plane_setup_scaler3(psde, - psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.w, - psde->pipe_cfg.dst_rect.h, - psde->scaler3_cfg, fmt, + _sde_plane_setup_scaler3(pp, + pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.h, + pp->scaler3_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); } - } else if (!psde->pixel_ext_usr) { + } else if (!pp->pixel_ext_usr) { uint32_t deci_dim, i; /* calculate default configuration for QSEED2 */ memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); SDE_DEBUG_PLANE(psde, "default config\n"); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.horz_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.horz_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.w, pe->phase_step_x, pe->horz_filter, fmt, chroma_subsmpl_h); if (SDE_FORMAT_IS_YUV(fmt)) deci_dim &= ~0x1; - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.dst_rect.w, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.dst_rect.w, deci_dim, pe->phase_step_x, pe->roi_w, pe->num_ext_pxls_left, pe->num_ext_pxls_right, pe->horz_filter, fmt, chroma_subsmpl_h, 0); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.vert_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.vert_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.h, + pp->pipe_cfg.dst_rect.h, pe->phase_step_y, pe->vert_filter, fmt, chroma_subsmpl_v); - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.h, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.h, deci_dim, pe->phase_step_y, pe->roi_h, pe->num_ext_pxls_top, @@ -1052,22 +1152,22 @@ static void _sde_plane_setup_scaler(struct sde_plane *psde, * @alpha: 8-bit fill alpha value, 255 selects 100% alpha * Returns: 0 on success */ -static int _sde_plane_color_fill(struct sde_plane *psde, +static int _sde_plane_color_fill(struct sde_phy_plane *pp, uint32_t color, uint32_t alpha) { const struct sde_format *fmt; - if (!psde) { + if (!pp) { SDE_ERROR("invalid plane\n"); return -EINVAL; } - if (!psde->pipe_hw) { - SDE_ERROR_PLANE(psde, "invalid plane h/w pointer\n"); + if (!pp->pipe_hw) { + SDE_ERROR_PLANE(pp->sde_plane, "invalid plane h/w pointer\n"); return -EINVAL; } - SDE_DEBUG_PLANE(psde, "\n"); + SDE_DEBUG_PLANE(pp->sde_plane, "\n"); /* * select fill format to match user property expectation, @@ -1076,35 +1176,35 @@ static int _sde_plane_color_fill(struct sde_plane *psde, fmt = sde_get_sde_format(DRM_FORMAT_ABGR8888); /* update sspp */ - if (fmt && psde->pipe_hw->ops.setup_solidfill) { - psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw, + if (fmt && pp->pipe_hw->ops.setup_solidfill) { + pp->pipe_hw->ops.setup_solidfill(pp->pipe_hw, (color & 0xFFFFFF) | ((alpha & 0xFF) << 24)); /* override scaler/decimation if solid fill */ - psde->pipe_cfg.src_rect.x = 0; - psde->pipe_cfg.src_rect.y = 0; - psde->pipe_cfg.src_rect.w = psde->pipe_cfg.dst_rect.w; - psde->pipe_cfg.src_rect.h = psde->pipe_cfg.dst_rect.h; + pp->pipe_cfg.src_rect.x = 0; + pp->pipe_cfg.src_rect.y = 0; + pp->pipe_cfg.src_rect.w = pp->pipe_cfg.dst_rect.w; + pp->pipe_cfg.src_rect.h = pp->pipe_cfg.dst_rect.h; - _sde_plane_setup_scaler(psde, fmt, 0); + _sde_plane_setup_scaler(pp, fmt, NULL); - if (psde->pipe_hw->ops.setup_format) - psde->pipe_hw->ops.setup_format(psde->pipe_hw, + if (pp->pipe_hw->ops.setup_format) + pp->pipe_hw->ops.setup_format(pp->pipe_hw, fmt, SDE_SSPP_SOLID_FILL); - if (psde->pipe_hw->ops.setup_rects) - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + if (pp->pipe_hw->ops.setup_rects) + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } return 0; } 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; @@ -1113,6 +1213,9 @@ static int _sde_plane_mode_set(struct drm_plane *plane, struct sde_rect src, dst; bool q16_data = true; 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"); @@ -1170,18 +1273,32 @@ static int _sde_plane_mode_set(struct drm_plane *plane, } } - if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) - memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg)); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) + memset(&(pp->pipe_cfg), 0, + sizeof(struct sde_hw_pipe_cfg)); + + _sde_plane_set_scanout(pp, pstate, &pp->pipe_cfg, fb); + + pstate->pending = true; - _sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb); + pp->is_rt_pipe = sde_crtc_is_rt(crtc); + _sde_plane_set_qos_ctrl(pp, false, SDE_PLANE_QOS_PANIC_CTRL); + } /* early out if nothing dirty */ if (!pstate->dirty) return 0; - pstate->pending = true; - psde->is_rt_pipe = sde_crtc_is_rt(crtc); - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); + 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) { @@ -1201,72 +1318,100 @@ static int _sde_plane_mode_set(struct drm_plane *plane, BIT(SDE_DRM_DEINTERLACE)) { SDE_DEBUG_PLANE(psde, "deinterlace\n"); for (idx = 0; idx < SDE_MAX_PLANES; ++idx) - psde->pipe_cfg.layout.plane_pitch[idx] <<= 1; + pp->pipe_cfg.layout.plane_pitch[idx] <<= 1; src.h /= 2; src.y = DIV_ROUND_UP(src.y, 2); src.y &= ~0x1; } + } + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (maxlinewidth > pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + num_of_phy_planes++; + } - psde->pipe_cfg.src_rect = src; - psde->pipe_cfg.dst_rect = dst; + /* + * Only need to use one physical plane if plane width is still within + * the limitation. + */ + if (maxlinewidth >= (src.x + src.w)) + num_of_phy_planes = 1; + + if (num_of_phy_planes > 1) { + /* Adjust width for multi-pipe */ + src.w /= num_of_phy_planes; + dst.w /= num_of_phy_planes; + } + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Adjust offset for multi-pipe */ + src.x += src.w * pp->index; + dst.x += dst.w * pp->index; + + pp->pipe_cfg.src_rect = src; + pp->pipe_cfg.dst_rect = dst; /* check for color fill */ - psde->color_fill = (uint32_t)sde_plane_get_property(pstate, + pp->color_fill = (uint32_t)sde_plane_get_property(pstate, PLANE_PROP_COLOR_FILL); - if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { + if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { /* skip remaining processing on color fill */ pstate->dirty = 0x0; - } else if (psde->pipe_hw->ops.setup_rects) { - _sde_plane_setup_scaler(psde, fmt, pstate); + } else if (pp->pipe_hw->ops.setup_rects) { + _sde_plane_setup_scaler(pp, fmt, pstate); - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } - } - if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) && - psde->pipe_hw->ops.setup_format) { - src_flags = 0x0; + if (((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) || + (src_flags & + SDE_SSPP_SECURE_OVERLAY_SESSION)) && + pp->pipe_hw->ops.setup_format) { 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)) - src_flags |= SDE_SSPP_FLIP_LR; - if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) & - BIT(DRM_REFLECT_Y)) - src_flags |= SDE_SSPP_FLIP_UD; - - /* update format */ - psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags); - - /* update csc */ - if (SDE_FORMAT_IS_YUV(fmt)) - _sde_plane_setup_csc(psde); - else - psde->csc_ptr = 0; - } + if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) + & BIT(DRM_REFLECT_X)) + src_flags |= SDE_SSPP_FLIP_LR; + if (sde_plane_get_property(pstate, + PLANE_PROP_ROTATION) & BIT(DRM_REFLECT_Y)) + src_flags |= SDE_SSPP_FLIP_UD; + + /* update format */ + pp->pipe_hw->ops.setup_format(pp->pipe_hw, + fmt, src_flags); + + /* update csc */ + if (SDE_FORMAT_IS_YUV(fmt)) + _sde_plane_setup_csc(pp); + else + pp->csc_ptr = NULL; + } - sde_color_process_plane_setup(plane); + sde_color_process_plane_setup(plane, pp); - /* update sharpening */ - if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && - psde->pipe_hw->ops.setup_sharpening) { - psde->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; - psde->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; - psde->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; - psde->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; + /* update sharpening */ + if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && + pp->pipe_hw->ops.setup_sharpening) { + pp->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; + pp->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; + pp->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; + pp->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; - psde->pipe_hw->ops.setup_sharpening(psde->pipe_hw, - &psde->sharp_cfg); - } + pp->pipe_hw->ops.setup_sharpening(pp->pipe_hw, + &pp->sharp_cfg); + } - _sde_plane_set_qos_lut(plane, fb); - _sde_plane_set_danger_lut(plane, fb); + _sde_plane_set_qos_lut(pp, fb); + _sde_plane_set_danger_lut(pp, fb); - if (plane->type != DRM_PLANE_TYPE_CURSOR) { - _sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL); - _sde_plane_set_ot_limit(plane, crtc); + if (plane->type != DRM_PLANE_TYPE_CURSOR) { + _sde_plane_set_qos_ctrl(pp, true, + SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_ot_limit(pp, crtc); + } } /* clear dirty */ @@ -1280,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); } @@ -1393,10 +1551,12 @@ static int sde_plane_atomic_check(struct drm_plane *plane, uint32_t deci_w, deci_h, src_deci_w, src_deci_h; uint32_t max_upscale, max_downscale, min_src_size, max_linewidth; bool q16_data = true; + struct sde_phy_plane *pp; + uint32_t num_of_phy_planes = 0; if (!plane || !state) { - SDE_ERROR("invalid arg(s), plane %d state %d\n", - plane != 0, state != 0); + SDE_ERROR("invalid arg(s), plane %d state %d.\n", + plane != NULL, state != NULL); ret = -EINVAL; goto exit; } @@ -1404,11 +1564,8 @@ static int sde_plane_atomic_check(struct drm_plane *plane, psde = to_sde_plane(plane); pstate = to_sde_plane_state(state); - if (!psde->pipe_sblk) { - SDE_ERROR_PLANE(psde, "invalid catalog\n"); - ret = -EINVAL; - goto exit; - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) + num_of_phy_planes++; deci_w = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); deci_h = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); @@ -1422,10 +1579,6 @@ static int sde_plane_atomic_check(struct drm_plane *plane, src_deci_w = DECIMATED_DIMENSION(src.w, deci_w); src_deci_h = DECIMATED_DIMENSION(src.h, deci_h); - max_upscale = psde->pipe_sblk->maxupscale; - max_downscale = psde->pipe_sblk->maxdwnscale; - max_linewidth = psde->pipe_sblk->maxlinewidth; - SDE_DEBUG_PLANE(psde, "check %d -> %d\n", sde_plane_enabled(plane->state), sde_plane_enabled(state)); @@ -1436,73 +1589,87 @@ static int sde_plane_atomic_check(struct drm_plane *plane, min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1; - if (SDE_FORMAT_IS_YUV(fmt) && - (!(psde->features & SDE_SSPP_SCALER) || - !(psde->features & (BIT(SDE_SSPP_CSC) - | BIT(SDE_SSPP_CSC_10BIT))))) { - SDE_ERROR_PLANE(psde, - "plane doesn't have scaler/csc for yuv\n"); - ret = -EINVAL; - - /* check src bounds */ - } else if (state->fb->width > MAX_IMG_WIDTH || - state->fb->height > MAX_IMG_HEIGHT || - src.w < min_src_size || src.h < min_src_size || - CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || - CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { - SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -E2BIG; - - /* valid yuv image */ - } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) || (src.y & 0x1) || - (src.w & 0x1) || (src.h & 0x1))) { - SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -EINVAL; + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_sblk) { + SDE_ERROR("invalid plane catalog\n"); + ret = -EINVAL; + goto exit; + } - /* min dst support */ - } else if (dst.w < 0x1 || dst.h < 0x1) { - SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u, %ux%u\n", - dst.x, dst.y, dst.w, dst.h); - ret = -EINVAL; + max_upscale = pp->pipe_sblk->maxupscale; + max_downscale = pp->pipe_sblk->maxdwnscale; + max_linewidth = pp->pipe_sblk->maxlinewidth; - /* decimation validation */ - } else if (deci_w || deci_h) { - if ((deci_w > psde->pipe_sblk->maxhdeciexp) || - (deci_h > psde->pipe_sblk->maxvdeciexp)) { + if (SDE_FORMAT_IS_YUV(fmt) && + (!(pp->features & SDE_SSPP_SCALER) || + !(pp->features & (BIT(SDE_SSPP_CSC) + | BIT(SDE_SSPP_CSC_10BIT))))) { SDE_ERROR_PLANE(psde, - "too much decimation requested\n"); + "plane doesn't have scaler/csc for yuv\n"); ret = -EINVAL; - } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { - SDE_ERROR_PLANE(psde, - "decimation requires linear fetch\n"); + + /* check src bounds */ + } else if (state->fb->width > MAX_IMG_WIDTH || + state->fb->height > MAX_IMG_HEIGHT || + src.w < min_src_size || src.h < min_src_size || + CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || + CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { + SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", + src.x, src.y, src.w, src.h); + ret = -E2BIG; + + /* valid yuv image */ + } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) + || (src.y & 0x1) || (src.w & 0x1) + || (src.h & 0x1))) { + SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u,\"\ + %ux%u\n", src.x, src.y, src.w, src.h); ret = -EINVAL; - } - } else if (!(psde->features & SDE_SSPP_SCALER) && - ((src.w != dst.w) || (src.h != dst.h))) { - SDE_ERROR_PLANE(psde, - "pipe doesn't support scaling %ux%u->%ux%u\n", - src.w, src.h, dst.w, dst.h); - ret = -EINVAL; + /* min dst support */ + } else if (dst.w < 0x1 || dst.h < 0x1) { + SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u,\"\ + %ux%u\n", dst.x, dst.y, dst.w, dst.h); + ret = -EINVAL; - /* check decimated source width */ - } else if (src_deci_w > max_linewidth) { - SDE_ERROR_PLANE(psde, - "invalid src w:%u, deci w:%u, line w:%u\n", - src.w, src_deci_w, max_linewidth); - ret = -E2BIG; + /* decimation validation */ + } else if (deci_w || deci_h) { + if ((deci_w > pp->pipe_sblk->maxhdeciexp) || + (deci_h > pp->pipe_sblk->maxvdeciexp)) { + SDE_ERROR_PLANE(psde, + "too much decimation requested\n"); + ret = -EINVAL; + } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { + SDE_ERROR_PLANE(psde, + "decimation requires linear fetch\n"); + ret = -EINVAL; + } - /* check max scaler capability */ - } else if (((src_deci_w * max_upscale) < dst.w) || - ((src_deci_h * max_upscale) < dst.h) || - ((dst.w * max_downscale) < src_deci_w) || - ((dst.h * max_downscale) < src_deci_h)) { - SDE_ERROR_PLANE(psde, - "too much scaling requested %ux%u->%ux%u\n", - src_deci_w, src_deci_h, dst.w, dst.h); - ret = -E2BIG; + } else if (!(pp->features & SDE_SSPP_SCALER) && + ((src.w != dst.w) || (src.h != dst.h))) { + SDE_ERROR_PLANE(psde, + "pipe doesn't support scaling %ux%u->%ux%u\n", + src.w, src.h, dst.w, dst.h); + ret = -EINVAL; + + /* check decimated source width */ + } else if (src_deci_w > max_linewidth * num_of_phy_planes) { + SDE_ERROR_PLANE(psde, + "invalid src w:%u, deci w:%u, line w:%u, num_phy_planes:%u\n", + src.w, src_deci_w, max_linewidth, + num_of_phy_planes); + ret = -E2BIG; + + /* check max scaler capability */ + } else if (((src_deci_w * max_upscale) < dst.w) || + ((src_deci_h * max_upscale) < dst.h) || + ((dst.w * max_downscale) < src_deci_w) || + ((dst.h * max_downscale) < src_deci_h)) { + SDE_ERROR_PLANE(psde, + "too much scaling requested %ux%u->%ux%u\n", + src_deci_w, src_deci_h, dst.w, dst.h); + ret = -E2BIG; + } } modeset_update: @@ -1519,6 +1686,7 @@ exit: void sde_plane_flush(struct drm_plane *plane) { struct sde_plane *psde; + struct sde_phy_plane *pp; if (!plane) { SDE_ERROR("invalid plane\n"); @@ -1531,14 +1699,17 @@ void sde_plane_flush(struct drm_plane *plane) * These updates have to be done immediately before the plane flush * timing, and may not be moved to the atomic_update/mode_set functions. */ - if (psde->is_error) + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (psde->is_error) /* force white frame with 0% alpha pipe output on error */ - _sde_plane_color_fill(psde, 0xFFFFFF, 0x0); - else if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) - /* force 100% alpha */ - _sde_plane_color_fill(psde, psde->color_fill, 0xFF); - else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc) - psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr); + _sde_plane_color_fill(pp, 0xFFFFFF, 0x0); + else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) + /* force 100% alpha */ + _sde_plane_color_fill(pp, pp->color_fill, 0xFF); + else if (pp->pipe_hw && pp->csc_ptr && + pp->pipe_hw->ops.setup_csc) + pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr); + } /* flag h/w flush complete */ if (plane->state) @@ -1592,25 +1763,60 @@ static void _sde_plane_install_properties(struct drm_plane *plane, static const struct drm_prop_enum_list e_src_config[] = { {SDE_DRM_DEINTERLACE, "deinterlace"} }; + static const struct drm_prop_enum_list e_fb_translation_mode[] = { + {SDE_DRM_FB_NON_SEC, "non_sec"}, + {SDE_DRM_FB_SEC, "sec"}, + {SDE_DRM_FB_NON_SEC_DIR_TRANS, "non_sec_direct_translation"}, + {SDE_DRM_FB_SEC_DIR_TRANS, "sec_direct_translation"}, + }; const struct sde_format_extended *format_list; struct sde_kms_info *info; struct sde_plane *psde = to_sde_plane(plane); int zpos_max = 255; int zpos_def = 0; char feature_name[256]; + struct sde_phy_plane *pp; + uint32_t features = 0xFFFFFFFF, nformats = 64; + u32 maxlinewidth = -1, maxupscale = -1, maxdwnscale = -1; + u32 maxhdeciexp = -1, maxvdeciexp = -1; if (!plane || !psde) { SDE_ERROR("invalid plane\n"); return; - } else if (!psde->pipe_hw || !psde->pipe_sblk) { - SDE_ERROR("invalid plane, pipe_hw %d pipe_sblk %d\n", - psde->pipe_hw != 0, psde->pipe_sblk != 0); - return; - } else if (!catalog) { + } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_hw || !pp->pipe_sblk) { + SDE_ERROR("invalid phy_plane, pipe_hw %d\"\ + pipe_sblk %d\n", pp->pipe_hw != NULL, + pp->pipe_sblk != NULL); + return; + } + } + if (!catalog) { SDE_ERROR("invalid catalog\n"); return; } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + format_list = pp->pipe_sblk->format_list; + } + if (maxlinewidth < pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + if (maxupscale < pp->pipe_sblk->maxupscale) + maxupscale = pp->pipe_sblk->maxupscale; + if (maxdwnscale < pp->pipe_sblk->maxdwnscale) + maxdwnscale = pp->pipe_sblk->maxdwnscale; + if (maxhdeciexp < pp->pipe_sblk->maxhdeciexp) + maxhdeciexp = pp->pipe_sblk->maxhdeciexp; + if (maxvdeciexp < pp->pipe_sblk->maxvdeciexp) + maxvdeciexp = pp->pipe_sblk->maxvdeciexp; + break; + } + if (sde_is_custom_client()) { if (catalog->mixer_count && catalog->mixer && catalog->mixer[0].sblk->maxblendstages) { @@ -1633,19 +1839,24 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_range(&psde->property_info, "input_fence", 0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE); - if (psde->pipe_sblk->maxhdeciexp) { - msm_property_install_range(&psde->property_info, "h_decimate", - 0x0, 0, psde->pipe_sblk->maxhdeciexp, 0, - PLANE_PROP_H_DECIMATE); - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_sblk->maxhdeciexp) { + msm_property_install_range(&psde->property_info, + "h_decimate", 0x0, 0, + pp->pipe_sblk->maxhdeciexp, 0, + PLANE_PROP_H_DECIMATE); + } - if (psde->pipe_sblk->maxvdeciexp) { - msm_property_install_range(&psde->property_info, "v_decimate", - 0x0, 0, psde->pipe_sblk->maxvdeciexp, 0, + if (pp->pipe_sblk->maxvdeciexp) { + msm_property_install_range(&psde->property_info, + "v_decimate", 0x0, 0, + pp->pipe_sblk->maxvdeciexp, 0, PLANE_PROP_V_DECIMATE); + } + break; } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (features & BIT(SDE_SSPP_SCALER_QSEED3)) { msm_property_install_volatile_range(&psde->property_info, "scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2); msm_property_install_blob(&psde->property_info, "lut_ed", 0, @@ -1654,38 +1865,38 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_SCALER_LUT_CIR); msm_property_install_blob(&psde->property_info, "lut_sep", 0, PLANE_PROP_SCALER_LUT_SEP); - } else if (psde->features & SDE_SSPP_SCALER) { + } else if (features & SDE_SSPP_SCALER) { msm_property_install_volatile_range(&psde->property_info, "scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1); } - if (psde->features & BIT(SDE_SSPP_CSC)) { + if (features & BIT(SDE_SSPP_CSC)) { msm_property_install_volatile_range(&psde->property_info, "csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1); } - if (psde->features & BIT(SDE_SSPP_HSIC)) { + if (features & BIT(SDE_SSPP_HSIC)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_HUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_HUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SATURATION_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_SATURATION_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_VALUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_VALUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_CONTRAST_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_CONTRAST_ADJUST); @@ -1701,9 +1912,13 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_enum(&psde->property_info, "src_config", 0x0, 1, e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG); - if (psde->pipe_hw->ops.setup_solidfill) - msm_property_install_range(&psde->property_info, "color_fill", - 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_COLOR_FILL); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw->ops.setup_solidfill) + msm_property_install_range(&psde->property_info, + "color_fill", 0, 0, 0xFFFFFFFF, 0, + PLANE_PROP_COLOR_FILL); + break; + } info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL); if (!info) { @@ -1715,7 +1930,6 @@ static void _sde_plane_install_properties(struct drm_plane *plane, DRM_MODE_PROP_IMMUTABLE, PLANE_PROP_INFO); sde_kms_info_reset(info); - format_list = psde->pipe_sblk->format_list; if (format_list) { sde_kms_info_start(info, "pixel_formats"); while (format_list->fourcc_format) { @@ -1727,51 +1941,55 @@ static void _sde_plane_install_properties(struct drm_plane *plane, sde_kms_info_stop(info); } - sde_kms_info_add_keyint(info, "max_linewidth", - psde->pipe_sblk->maxlinewidth); - sde_kms_info_add_keyint(info, "max_upscale", - psde->pipe_sblk->maxupscale); - sde_kms_info_add_keyint(info, "max_downscale", - psde->pipe_sblk->maxdwnscale); - sde_kms_info_add_keyint(info, "max_horizontal_deci", - psde->pipe_sblk->maxhdeciexp); - sde_kms_info_add_keyint(info, "max_vertical_deci", - psde->pipe_sblk->maxvdeciexp); + sde_kms_info_add_keyint(info, "max_linewidth", maxlinewidth); + sde_kms_info_add_keyint(info, "max_upscale", maxupscale); + sde_kms_info_add_keyint(info, "max_downscale", maxdwnscale); + sde_kms_info_add_keyint(info, "max_horizontal_deci", maxhdeciexp); + sde_kms_info_add_keyint(info, "max_vertical_deci", maxvdeciexp); msm_property_set_blob(&psde->property_info, &psde->blob_info, info->data, info->len, PLANE_PROP_INFO); kfree(info); - if (psde->features & BIT(SDE_SSPP_MEMCOLOR)) { + if (features & BIT(SDE_SSPP_MEMCOLOR)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKIN_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKIN_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKY_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKY_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_FOLIAGE_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_FOLIAGE_COLOR); } + + msm_property_install_enum(&psde->property_info, "fb_translation_mode", + 0x0, + 0, e_fb_translation_mode, + ARRAY_SIZE(e_fb_translation_mode), + PLANE_PROP_FB_TRANSLATION_MODE); } -static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) +static inline void _sde_plane_set_csc_v1(struct sde_phy_plane *pp, + void *usr_ptr) { struct sde_drm_csc_v1 csc_v1; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->csc_usr_ptr = NULL; + pp->csc_usr_ptr = NULL; if (!usr_ptr) { SDE_DEBUG_PLANE(psde, "csc data removed\n"); return; @@ -1784,30 +2002,33 @@ static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) /* populate from user space */ for (i = 0; i < SDE_CSC_MATRIX_COEFF_SIZE; ++i) - psde->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; + pp->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; for (i = 0; i < SDE_CSC_BIAS_SIZE; ++i) { - psde->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; - psde->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; + pp->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; + pp->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; } for (i = 0; i < SDE_CSC_CLAMP_SIZE; ++i) { - psde->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; - psde->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; + pp->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; + pp->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; } - psde->csc_usr_ptr = &psde->csc_cfg; + pp->csc_usr_ptr = &pp->csc_cfg; } -static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) +static inline void _sde_plane_set_scaler_v1(struct sde_phy_plane *pp, + void *usr) { struct sde_drm_scaler_v1 scale_v1; struct sde_hw_pixel_ext *pe; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->pixel_ext_usr = false; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1819,7 +2040,7 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); for (i = 0; i < SDE_MAX_PLANES; i++) { pe->init_phase_x[i] = scale_v1.init_phase_x[i]; @@ -1844,26 +2065,28 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) pe->roi_h[i] = scale_v1.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } -static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, +static inline void _sde_plane_set_scaler_v2(struct sde_phy_plane *pp, struct sde_plane_state *pstate, void *usr) { struct sde_drm_scaler_v2 scale_v2; struct sde_hw_pixel_ext *pe; int i; struct sde_hw_scaler3_cfg *cfg; + struct sde_plane *psde; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - cfg = psde->scaler3_cfg; - psde->pixel_ext_usr = false; + cfg = pp->scaler3_cfg; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1875,7 +2098,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); cfg->enable = scale_v2.enable; cfg->dir_en = scale_v2.dir_en; @@ -1933,7 +2156,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, pe->btm_rpt[i] = scale_v2.pe.btm_rpt[i]; pe->roi_h[i] = scale_v2.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } @@ -1945,6 +2168,7 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; struct sde_plane_state *pstate; int idx, ret = -EINVAL; + struct sde_phy_plane *pp; SDE_DEBUG_PLANE(psde, "\n"); @@ -1965,14 +2189,24 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, _sde_plane_set_input_fence(psde, pstate, val); break; case PLANE_PROP_CSC_V1: - _sde_plane_set_csc_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_csc_v1(pp, (void *)val); + } break; case PLANE_PROP_SCALER_V1: - _sde_plane_set_scaler_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v1(pp, + (void *)val); + } break; case PLANE_PROP_SCALER_V2: - _sde_plane_set_scaler_v2(psde, pstate, - (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v2(pp, pstate, + (void *)val); + } break; default: /* nothing to do */ @@ -2019,12 +2253,15 @@ static int sde_plane_atomic_get_property(struct drm_plane *plane, static void sde_plane_destroy(struct drm_plane *plane) { struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; + struct sde_phy_plane *pp, *n; SDE_DEBUG_PLANE(psde, "\n"); if (psde) { - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); - + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + _sde_plane_set_qos_ctrl(pp, + false, SDE_PLANE_QOS_PANIC_CTRL); + } debugfs_remove_recursive(psde->debugfs_root); if (psde->blob_info) @@ -2037,8 +2274,13 @@ static void sde_plane_destroy(struct drm_plane *plane) /* this will destroy the states as well */ drm_plane_cleanup(plane); - if (psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + list_del(&pp->phy_plane_list); + kfree(pp); + } kfree(psde); } @@ -2174,9 +2416,22 @@ static const struct drm_plane_helper_funcs sde_plane_helper_funcs = { .atomic_update = sde_plane_atomic_update, }; -enum sde_sspp sde_plane_pipe(struct drm_plane *plane) +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index) { - return plane ? to_sde_plane(plane)->pipe : SSPP_NONE; + struct sde_plane *sde_plane = to_sde_plane(plane); + struct sde_phy_plane *pp; + int i = 0; + enum sde_sspp default_sspp = SSPP_NONE; + + list_for_each_entry(pp, &sde_plane->phy_plane_head, phy_plane_list) { + if (i == 0) + default_sspp = pp->pipe; + if (i == index) + return pp->pipe; + i++; + } + + return default_sspp; } static ssize_t _sde_plane_danger_read(struct file *file, @@ -2208,10 +2463,16 @@ static ssize_t _sde_plane_danger_read(struct file *file, static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) { struct drm_plane *plane; + struct sde_plane *psde; + struct sde_phy_plane *pp; drm_for_each_plane(plane, kms->dev) { if (plane->fb && plane->state) { - sde_plane_danger_signal_ctrl(plane, enable); + psde = to_sde_plane(plane); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + sde_plane_danger_signal_ctrl(pp, enable); + } SDE_DEBUG("plane:%d img:%dx%d ", plane->base.id, plane->fb->width, plane->fb->height); @@ -2229,7 +2490,7 @@ static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) } static ssize_t _sde_plane_danger_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) + const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_kms *kms = file->private_data; struct sde_mdss_cfg *cfg = kms->catalog; @@ -2271,85 +2532,166 @@ static const struct file_operations sde_plane_danger_enable = { .write = _sde_plane_danger_write, }; -static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms) +static void _sde_plane_init_debugfs(struct sde_plane *psde, + struct sde_kms *kms) { const struct sde_sspp_sub_blks *sblk = 0; const struct sde_sspp_cfg *cfg = 0; + struct sde_phy_plane *pp; - if (psde && psde->pipe_hw) - cfg = psde->pipe_hw->cap; - if (cfg) + if (!psde || !kms) { + SDE_ERROR("invalid arg(s), psde %d kms %d\n", + psde != NULL, kms != NULL); + return; + } + + /* create overall sub-directory for the pipe */ + psde->debugfs_root = debugfs_create_dir(psde->pipe_name, + sde_debugfs_get_root(kms)); + if (!psde->debugfs_root) + return; + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + debugfs_create_u32("pipe", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->pipe); + + if (!pp->pipe_hw || !pp->pipe_hw->cap || + !pp->pipe_hw->cap->sblk) + continue; + cfg = pp->pipe_hw->cap; sblk = cfg->sblk; - if (kms && sblk) { - /* create overall sub-directory for the pipe */ - psde->debugfs_root = - debugfs_create_dir(psde->pipe_name, - sde_debugfs_get_root(kms)); - if (psde->debugfs_root) { - /* don't error check these */ - debugfs_create_x32("features", S_IRUGO | S_IWUSR, - psde->debugfs_root, &psde->features); - - /* add register dump support */ - sde_debugfs_setup_regset32(&psde->debugfs_src, - sblk->src_blk.base + cfg->base, - sblk->src_blk.len, - kms); - sde_debugfs_create_regset32("src_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_src); - - sde_debugfs_setup_regset32(&psde->debugfs_scaler, - sblk->scaler_blk.base + cfg->base, - sblk->scaler_blk.len, - kms); - sde_debugfs_create_regset32("scaler_blk", S_IRUGO, - psde->debugfs_root, - &psde->debugfs_scaler); - - sde_debugfs_setup_regset32(&psde->debugfs_csc, - sblk->csc_blk.base + cfg->base, - sblk->csc_blk.len, - kms); - sde_debugfs_create_regset32("csc_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_csc); - - debugfs_create_u32("xin_id", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->xin_id); - debugfs_create_u32("clk_ctrl", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->clk_ctrl); - debugfs_create_x32("creq_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->creq_vblank); - debugfs_create_x32("danger_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->danger_vblank); - - debugfs_create_file("disable_danger", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - kms, &sde_plane_danger_enable); + /* don't error check these */ + debugfs_create_x32("features", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->features); + + /* add register dump support */ + sde_debugfs_setup_regset32(&psde->debugfs_src, + sblk->src_blk.base + cfg->base, + sblk->src_blk.len, + kms); + sde_debugfs_create_regset32("src_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_src); + + sde_debugfs_setup_regset32(&psde->debugfs_scaler, + sblk->scaler_blk.base + cfg->base, + sblk->scaler_blk.len, + kms); + sde_debugfs_create_regset32("scaler_blk", S_IRUGO, + psde->debugfs_root, + &psde->debugfs_scaler); + + sde_debugfs_setup_regset32(&psde->debugfs_csc, + sblk->csc_blk.base + cfg->base, + sblk->csc_blk.len, + kms); + sde_debugfs_create_regset32("csc_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_csc); + + debugfs_create_u32("xin_id", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->xin_id); + debugfs_create_u32("clk_ctrl", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->clk_ctrl); + debugfs_create_x32("creq_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->creq_vblank); + debugfs_create_x32("danger_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->danger_vblank); + + debugfs_create_file("disable_danger", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + kms, &sde_plane_danger_enable); + + break; + } +} + +static int _sde_init_phy_plane(struct sde_kms *sde_kms, + struct sde_plane *psde, uint32_t pipe, uint32_t index, + struct sde_phy_plane *pp) +{ + int rc = 0; + + pp->pipe_hw = sde_rm_get_hw_by_id(&sde_kms->rm, + SDE_HW_BLK_SSPP, pipe); + if (!pp->pipe_hw) { + SDE_ERROR("Not found resource for id=%d\n", pipe); + rc = -EINVAL; + goto end; + } else if (!pp->pipe_hw->cap || !pp->pipe_hw->cap->sblk) { + SDE_ERROR("[%u]SSPP returned invalid cfg\n", pipe); + rc = -EINVAL; + goto end; + } + + /* cache features mask for later */ + pp->features = pp->pipe_hw->cap->features; + pp->pipe_sblk = pp->pipe_hw->cap->sblk; + if (!pp->pipe_sblk) { + SDE_ERROR("invalid sblk on pipe %d\n", pipe); + rc = -EINVAL; + goto end; + } + + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + pp->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), + GFP_KERNEL); + if (!pp->scaler3_cfg) { + SDE_ERROR("[%u]failed to allocate scale struct\n", + pipe); + rc = -ENOMEM; + goto end; } } + + /* add plane to DRM framework */ + pp->nformats = sde_populate_formats( + pp->pipe_sblk->format_list, + pp->formats, + NULL, + ARRAY_SIZE(pp->formats)); + + if (!pp->nformats) { + SDE_ERROR("[%u]no valid formats for plane\n", pipe); + if (pp->scaler3_cfg) + kzfree(pp->scaler3_cfg); + + rc = -EINVAL; + goto end; + } + + pp->sde_plane = psde; + pp->pipe = pipe; + pp->index = index; + +end: + return rc; } /* initialize plane */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs) + unsigned long possible_crtcs, bool vp_enabled) { struct drm_plane *plane = NULL; struct sde_plane *psde; + struct sde_phy_plane *pp, *n; struct msm_drm_private *priv; struct sde_kms *kms; enum drm_plane_type type; int ret = -EINVAL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub; + uint32_t features = 0xFFFFFFFF, nformats = 64, formats[64]; + uint32_t index = 0; if (!dev) { SDE_ERROR("[%u]device is NULL\n", pipe); @@ -2383,60 +2725,76 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, /* cache local stuff for later */ plane = &psde->base; - psde->pipe = pipe; - psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE]; - /* initialize underlying h/w driver */ - psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog); - if (IS_ERR(psde->pipe_hw)) { - SDE_ERROR("[%u]SSPP init failed\n", pipe); - ret = PTR_ERR(psde->pipe_hw); - goto clean_plane; - } else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) { - SDE_ERROR("[%u]SSPP init returned invalid cfg\n", pipe); - goto clean_sspp; - } + INIT_LIST_HEAD(&psde->phy_plane_head); - /* cache features mask for later */ - psde->features = psde->pipe_hw->cap->features; - psde->pipe_sblk = psde->pipe_hw->cap->sblk; - if (!psde->pipe_sblk) { - SDE_ERROR("[%u]invalid sblk\n", pipe); - goto clean_sspp; - } + /* initialize underlying h/w driver */ + if (vp_enabled) { + vp = &(kms->catalog->vp[pipe]); + list_for_each_entry(vp_sub, &vp->sub_blks, pipeid_list) { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); + ret = -ENOMEM; + goto clean_plane; + } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { - psde->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), - GFP_KERNEL); - if (!psde->scaler3_cfg) { - SDE_ERROR("[%u]failed to allocate scale struct\n", - pipe); + ret = _sde_init_phy_plane(kms, psde, vp_sub->sspp_id, + index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error vp=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(formats)); + } + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + index++; + psde->num_of_phy_planes++; + } + } else { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); ret = -ENOMEM; - goto clean_sspp; + goto clean_plane; } - } - - /* add plane to DRM framework */ - psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list, - psde->formats, - 0, - ARRAY_SIZE(psde->formats)); - if (!psde->nformats) { - SDE_ERROR("[%u]no valid formats for plane\n", pipe); - goto clean_sspp; + ret = _sde_init_phy_plane(kms, psde, pipe, index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error id=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + features = pp->features; + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(uint32_t) * 64); + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + psde->num_of_phy_planes++; } - if (psde->features & BIT(SDE_SSPP_CURSOR)) + if (features & BIT(SDE_SSPP_CURSOR)) type = DRM_PLANE_TYPE_CURSOR; else if (primary_plane) type = DRM_PLANE_TYPE_PRIMARY; else type = DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(dev, plane, possible_crtcs, - &sde_plane_funcs, psde->formats, psde->nformats, type); + &sde_plane_funcs, formats, nformats, type); if (ret) - goto clean_sspp; + goto clean_plane; /* success! finalize initialization */ drm_plane_helper_add(plane, &sde_plane_helper_funcs); @@ -2458,14 +2816,20 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, DRM_INFO("%s created for pipe %u\n", psde->pipe_name, pipe); return plane; -clean_sspp: - if (psde && psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); - - if (psde && psde->scaler3_cfg) - kfree(psde->scaler3_cfg); clean_plane: - kfree(psde); + if (psde) { + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + + kfree(pp->scaler3_cfg); + list_del(&pp->phy_plane_list); + kfree(pp); + } + kfree(psde); + } + exit: return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h index 1514f633c61e..7b91822d4cde 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.h +++ b/drivers/gpu/drm/msm/sde/sde_plane.h @@ -59,9 +59,10 @@ struct sde_plane_state { /** * sde_plane_pipe - return sspp identifier for the given plane * @plane: Pointer to DRM plane object + * @index: Plane index * Returns: sspp identifier of the given plane */ -enum sde_sspp sde_plane_pipe(struct drm_plane *plane); +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index); /** * sde_plane_flush - final plane operations before commit flush @@ -75,10 +76,11 @@ void sde_plane_flush(struct drm_plane *plane); * @pipe: sde hardware pipe identifier * @primary_plane: true if this pipe is primary plane for crtc * @possible_crtcs: bitmask of crtc that can be attached to the given pipe + * @vp_enabled: Flag indicating if virtual planes enabled */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs); + unsigned long possible_crtcs, bool vp_enabled); /** * sde_plane_wait_input_fence - wait for input fence object diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index fca0768e2734..fe4b73b4ffea 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -23,6 +23,7 @@ #include "sde_hw_wb.h" #include "sde_encoder.h" #include "sde_connector.h" +#include "sde_hw_sspp.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) @@ -197,6 +198,33 @@ bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *i) return false; } +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id) +{ + struct list_head *blk_list; + struct sde_rm_hw_blk *blk; + void *hw = NULL; + + if (!rm || type >= SDE_HW_BLK_MAX) { + SDE_ERROR("invalid rm\n"); + return hw; + } + + blk_list = &rm->hw_blks[type]; + + list_for_each_entry(blk, blk_list, list) { + if (blk->id == id) { + hw = blk->hw; + SDE_DEBUG("found type %d %s id %d\n", + type, blk->type_name, blk->id); + return hw; + } + } + + SDE_DEBUG("no match, type %d id=%d\n", type, id); + + return hw; +} + static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) { switch (type) { @@ -222,7 +250,8 @@ static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) sde_hw_wb_destroy(hw); break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + sde_hw_sspp_destroy(hw); + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -310,7 +339,9 @@ static int _sde_rm_hw_blk_create( name = "wb"; break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + hw = sde_hw_sspp_init(id, (void __iomem *)mmio, cat); + name = "sspp"; + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -369,6 +400,13 @@ int sde_rm_init(struct sde_rm *rm, goto fail; } + for (i = 0; i < cat->sspp_count; i++) { + rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_SSPP, + cat->sspp[i].id, &cat->sspp[i]); + if (rc) + goto fail; + } + /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h index 855b12ce8150..1cc22c5fbbf4 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.h +++ b/drivers/gpu/drm/msm/sde/sde_rm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -185,6 +185,18 @@ void sde_rm_init_hw_iter( bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *iter); /** + * sde_rm_get_hw_by_id - retrieve hw object given hw type and hw id + * Meant to do a single pass through the hardware list to iteratively + * retrieve hardware blocks of a given type and id. + * Function returns the hw resource pointer. + * @rm: SDE Resource Manager handle + * @type: hw type + * @id: hw id + * @Return: hw resource pointer on match found, NULL on no match found + */ +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id); + +/** * sde_rm_check_property_topctl - validate property bitmask before it is set * @val: user's proposed topology control bitmask * @Return: 0 on success or error diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c index 949dc6101a58..7c0b58613747 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -130,7 +130,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) poll = false; } - if (list_empty(&therm->alarm.head) && poll) + if (poll) nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); spin_unlock_irqrestore(&therm->lock, flags); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c index 91198d79393a..e2feccec25f5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c @@ -83,7 +83,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target) spin_unlock_irqrestore(&fan->lock, flags); /* schedule next fan update, if not at target speed already */ - if (list_empty(&fan->alarm.head) && target != duty) { + if (target != duty) { u16 bump_period = fan->bios.bump_period; u16 slow_down_period = fan->bios.slow_down_period; u64 delay; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c index 59701b7a6597..ff9fbe7950e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c @@ -53,7 +53,7 @@ nvkm_fantog_update(struct nvkm_fantog *fan, int percent) duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff); nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); - if (list_empty(&fan->alarm.head) && percent != (duty * 100)) { + if (percent != (duty * 100)) { u64 next_change = (percent * fan->period_us) / 100; if (!duty) next_change = fan->period_us - next_change; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c index b9703c02d8ca..9a79e91fdfdc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c @@ -185,7 +185,7 @@ alarm_timer_callback(struct nvkm_alarm *alarm) spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags); /* schedule the next poll in one second */ - if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head)) + if (therm->func->temp_get(therm) >= 0) nvkm_timer_alarm(tmr, 1000000000ULL, alarm); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c index d4dae1f12d62..79fcdb43e174 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c @@ -36,23 +36,29 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) unsigned long flags; LIST_HEAD(exec); - /* move any due alarms off the pending list */ + /* Process pending alarms. */ spin_lock_irqsave(&tmr->lock, flags); list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { - if (alarm->timestamp <= nvkm_timer_read(tmr)) - list_move_tail(&alarm->head, &exec); + /* Have we hit the earliest alarm that hasn't gone off? */ + if (alarm->timestamp > nvkm_timer_read(tmr)) { + /* Schedule it. If we didn't race, we're done. */ + tmr->func->alarm_init(tmr, alarm->timestamp); + if (alarm->timestamp > nvkm_timer_read(tmr)) + break; + } + + /* Move to completed list. We'll drop the lock before + * executing the callback so it can reschedule itself. + */ + list_move_tail(&alarm->head, &exec); } - /* reschedule interrupt for next alarm time */ - if (!list_empty(&tmr->alarms)) { - alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head); - tmr->func->alarm_init(tmr, alarm->timestamp); - } else { + /* Shut down interrupt if no more pending alarms. */ + if (list_empty(&tmr->alarms)) tmr->func->alarm_fini(tmr); - } spin_unlock_irqrestore(&tmr->lock, flags); - /* execute any pending alarm handlers */ + /* Execute completed callbacks. */ list_for_each_entry_safe(alarm, atemp, &exec, head) { list_del_init(&alarm->head); alarm->func(alarm); @@ -65,24 +71,37 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) struct nvkm_alarm *list; unsigned long flags; - alarm->timestamp = nvkm_timer_read(tmr) + nsec; - - /* append new alarm to list, in soonest-alarm-first order */ + /* Remove alarm from pending list. + * + * This both protects against the corruption of the list, + * and implements alarm rescheduling/cancellation. + */ spin_lock_irqsave(&tmr->lock, flags); - if (!nsec) { - if (!list_empty(&alarm->head)) - list_del(&alarm->head); - } else { + list_del_init(&alarm->head); + + if (nsec) { + /* Insert into pending list, ordered earliest to latest. */ + alarm->timestamp = nvkm_timer_read(tmr) + nsec; list_for_each_entry(list, &tmr->alarms, head) { if (list->timestamp > alarm->timestamp) break; } + list_add_tail(&alarm->head, &list->head); + + /* Update HW if this is now the earliest alarm. */ + list = list_first_entry(&tmr->alarms, typeof(*list), head); + if (list == alarm) { + tmr->func->alarm_init(tmr, alarm->timestamp); + /* This shouldn't happen if callers aren't stupid. + * + * Worst case scenario is that it'll take roughly + * 4 seconds for the next alarm to trigger. + */ + WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr)); + } } spin_unlock_irqrestore(&tmr->lock, flags); - - /* process pending alarms */ - nvkm_timer_alarm_trigger(tmr); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c index 7b9ce87f0617..7f48249f41de 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c @@ -76,8 +76,8 @@ nv04_timer_intr(struct nvkm_timer *tmr) u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0); if (stat & 0x00000001) { - nvkm_timer_alarm_trigger(tmr); nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001); + nvkm_timer_alarm_trigger(tmr); stat &= ~0x00000001; } 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/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index e690dd11e99f..4b0f942b8914 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -184,9 +184,9 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { .address = (chan), \ .scan_type = { \ .sign = 'u', \ - .realbits = '8', \ - .storagebits = '8', \ - .shift = '0', \ + .realbits = 8, \ + .storagebits = 8, \ + .shift = 0, \ }, \ .ext_info = ad7303_ext_info, \ } diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index a0aedda7dfd7..bf0bd7e03aff 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -50,7 +50,6 @@ #define AS3935_TUNE_CAP 0x08 #define AS3935_CALIBRATE 0x3D -#define AS3935_WRITE_DATA BIT(15) #define AS3935_READ_DATA BIT(14) #define AS3935_ADDRESS(x) ((x) << 8) @@ -105,7 +104,7 @@ static int as3935_write(struct as3935_state *st, { u8 *buf = st->buf; - buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8; + buf[0] = AS3935_ADDRESS(reg) >> 8; buf[1] = val; return spi_write(st->spi, buf, 2); diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 34b1adad07aa..6a8024d9d742 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -277,8 +277,8 @@ static int addr6_resolve(struct sockaddr_in6 *src_in, fl6.saddr = src_in->sin6_addr; fl6.flowi6_oif = addr->bound_dev_if; - dst = ip6_route_output(addr->net, NULL, &fl6); - if ((ret = dst->error)) + ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6); + if (ret < 0) goto put; if (ipv6_addr_any(&fl6.saddr)) { diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index b1f37d4095fa..e76d52a203a7 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -863,7 +863,7 @@ err_put: free_port_list_attributes(device); err_unregister: - device_unregister(class_dev); + device_del(class_dev); err: return ret; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 77ddf2fa8625..8763fb832b01 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2491,6 +2491,7 @@ err_counter: mlx4_ib_delete_counters_table(ibdev, &ibdev->counters_table[i]); err_map: + mlx4_ib_free_eqs(dev, ibdev); iounmap(ibdev->uar_map); err_uar: diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c index 36ec8aa048aa..0b5bb0cee6f9 100644 --- a/drivers/infiniband/hw/mlx4/mcg.c +++ b/drivers/infiniband/hw/mlx4/mcg.c @@ -1105,7 +1105,8 @@ static void _mlx4_ib_mcg_port_cleanup(struct mlx4_ib_demux_ctx *ctx, int destroy while ((p = rb_first(&ctx->mcg_table)) != NULL) { group = rb_entry(p, struct mcast_group, node); if (atomic_read(&group->refcount)) - mcg_warn_group(group, "group refcount %d!!! (pointer %p)\n", atomic_read(&group->refcount), group); + mcg_debug_group(group, "group refcount %d!!! (pointer %p)\n", + atomic_read(&group->refcount), group); force_clean_group(group); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c index 6bd5740e2691..09396bd7b02d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c @@ -281,8 +281,11 @@ void ipoib_delete_debug_files(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); + WARN_ONCE(!priv->mcg_dentry, "null mcg debug file\n"); + WARN_ONCE(!priv->path_dentry, "null path debug file\n"); debugfs_remove(priv->mcg_dentry); debugfs_remove(priv->path_dentry); + priv->mcg_dentry = priv->path_dentry = NULL; } int ipoib_register_debugfs(void) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 8efcff1beb8f..6699ecd855f0 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -106,6 +106,33 @@ static struct ib_client ipoib_client = { .get_net_dev_by_params = ipoib_get_net_dev_by_params, }; +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG +static int ipoib_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netdev_notifier_info *ni = ptr; + struct net_device *dev = ni->dev; + + if (dev->netdev_ops->ndo_open != ipoib_open) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_REGISTER: + ipoib_create_debug_files(dev); + break; + case NETDEV_CHANGENAME: + ipoib_delete_debug_files(dev); + ipoib_create_debug_files(dev); + break; + case NETDEV_UNREGISTER: + ipoib_delete_debug_files(dev); + break; + } + + return NOTIFY_DONE; +} +#endif + int ipoib_open(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -1595,8 +1622,6 @@ void ipoib_dev_cleanup(struct net_device *dev) ASSERT_RTNL(); - ipoib_delete_debug_files(dev); - /* Delete any child interfaces first */ list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) { /* Stop GC on child */ @@ -1908,8 +1933,6 @@ static struct net_device *ipoib_add_port(const char *format, goto register_failed; } - ipoib_create_debug_files(priv->dev); - if (ipoib_cm_add_mode_attr(priv->dev)) goto sysfs_failed; if (ipoib_add_pkey_attr(priv->dev)) @@ -1924,7 +1947,6 @@ static struct net_device *ipoib_add_port(const char *format, return priv->dev; sysfs_failed: - ipoib_delete_debug_files(priv->dev); unregister_netdev(priv->dev); register_failed: @@ -2006,6 +2028,12 @@ static void ipoib_remove_one(struct ib_device *device, void *client_data) kfree(dev_list); } +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG +static struct notifier_block ipoib_netdev_notifier = { + .notifier_call = ipoib_netdev_event, +}; +#endif + static int __init ipoib_init_module(void) { int ret; @@ -2057,6 +2085,9 @@ static int __init ipoib_init_module(void) if (ret) goto err_client; +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG + register_netdevice_notifier(&ipoib_netdev_notifier); +#endif return 0; err_client: @@ -2074,6 +2105,9 @@ err_fs: static void __exit ipoib_cleanup_module(void) { +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG + unregister_netdevice_notifier(&ipoib_netdev_notifier); +#endif ipoib_netlink_fini(); ib_unregister_client(&ipoib_client); ib_sa_unregister_client(&ipoib_sa_client); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index fca1a882de27..57a34f87dedf 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -85,8 +85,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, goto register_failed; } - ipoib_create_debug_files(priv->dev); - /* RTNL childs don't need proprietary sysfs entries */ if (type == IPOIB_LEGACY_CHILD) { if (ipoib_cm_add_mode_attr(priv->dev)) @@ -107,7 +105,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, sysfs_failed: result = -ENOMEM; - ipoib_delete_debug_files(priv->dev); unregister_netdevice(priv->dev); register_failed: diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0628372f3591..b92b8a724efb 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2005,11 +2005,14 @@ static int domain_context_mapping_one(struct dmar_domain *domain, if (context_copied(context)) { u16 did_old = context_domain_id(context); - if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) + if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) { iommu->flush.flush_context(iommu, did_old, (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, DMA_CCMD_DEVICE_INVL); + iommu->flush.flush_iotlb(iommu, did_old, 0, 0, + DMA_TLB_DSI_FLUSH); + } } pgd = domain->pgd; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 4c15dee0857b..27713412c881 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -374,6 +374,7 @@ config DM_LOG_USERSPACE config DM_RAID tristate "RAID 1/4/5/6/10 target" depends on BLK_DEV_DM + select MD_RAID0 select MD_RAID1 select MD_RAID10 select MD_RAID456 diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 2dd33085b331..cdceefd0e57d 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -222,7 +222,7 @@ static DEFINE_SPINLOCK(param_spinlock); * Buffers are freed after this timeout */ static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS; -static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; +static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; static unsigned long dm_bufio_peak_allocated; static unsigned long dm_bufio_allocated_kmem_cache; @@ -914,10 +914,11 @@ static void __get_memory_limit(struct dm_bufio_client *c, { unsigned long buffers; - if (ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch) { - mutex_lock(&dm_bufio_clients_lock); - __cache_size_refresh(); - mutex_unlock(&dm_bufio_clients_lock); + if (unlikely(ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch)) { + if (mutex_trylock(&dm_bufio_clients_lock)) { + __cache_size_refresh(); + mutex_unlock(&dm_bufio_clients_lock); + } } buffers = dm_bufio_cache_size_per_client >> @@ -1513,10 +1514,10 @@ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) return true; } -static unsigned get_retain_buffers(struct dm_bufio_client *c) +static unsigned long get_retain_buffers(struct dm_bufio_client *c) { - unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes); - return retain_bytes / c->block_size; + unsigned long retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes); + return retain_bytes >> (c->sectors_per_block_bits + SECTOR_SHIFT); } static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, @@ -1526,7 +1527,7 @@ static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, struct dm_buffer *b, *tmp; unsigned long freed = 0; unsigned long count = nr_to_scan; - unsigned retain_target = get_retain_buffers(c); + unsigned long retain_target = get_retain_buffers(c); for (l = 0; l < LIST_SIZE; l++) { list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) { @@ -1752,11 +1753,19 @@ static bool older_than(struct dm_buffer *b, unsigned long age_hz) static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz) { struct dm_buffer *b, *tmp; - unsigned retain_target = get_retain_buffers(c); - unsigned count; + unsigned long retain_target = get_retain_buffers(c); + unsigned long count; + LIST_HEAD(write_list); dm_bufio_lock(c); + __check_watermark(c, &write_list); + if (unlikely(!list_empty(&write_list))) { + dm_bufio_unlock(c); + __flush_write_list(&write_list); + dm_bufio_lock(c); + } + count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY]; list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) { if (count <= retain_target) @@ -1781,6 +1790,8 @@ static void cleanup_old_buffers(void) mutex_lock(&dm_bufio_clients_lock); + __cache_size_refresh(); + list_for_each_entry(c, &dm_bufio_all_clients, client_list) __evict_old_buffers(c, max_age_hz); @@ -1904,7 +1915,7 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache"); module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds"); -module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR); +module_param_named(retain_bytes, dm_bufio_retain_bytes, ulong, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory"); module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR); diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 3970cda10080..d3c55d7754af 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -1326,17 +1326,19 @@ void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd, int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown) { - int r; + int r = -EINVAL; flags_mutator mutator = (clean_shutdown ? set_clean_shutdown : clear_clean_shutdown); WRITE_LOCK(cmd); + if (cmd->fail_io) + goto out; + r = __commit_transaction(cmd, mutator); if (r) goto out; r = __begin_transaction(cmd); - out: WRITE_UNLOCK(cmd); return r; @@ -1348,7 +1350,8 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd, int r = -EINVAL; READ_LOCK(cmd); - r = dm_sm_get_nr_free(cmd->metadata_sm, result); + if (!cmd->fail_io) + r = dm_sm_get_nr_free(cmd->metadata_sm, result); READ_UNLOCK(cmd); return r; @@ -1360,7 +1363,8 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd, int r = -EINVAL; READ_LOCK(cmd); - r = dm_sm_get_nr_blocks(cmd->metadata_sm, result); + if (!cmd->fail_io) + r = dm_sm_get_nr_blocks(cmd->metadata_sm, result); READ_UNLOCK(cmd); return r; diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 665bf3285618..32e76c5ee741 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -961,15 +961,15 @@ static int metadata_commit(struct era_metadata *md) } } - r = save_sm_root(md); + r = dm_tm_pre_commit(md->tm); if (r) { - DMERR("%s: save_sm_root failed", __func__); + DMERR("%s: pre commit failed", __func__); return r; } - r = dm_tm_pre_commit(md->tm); + r = save_sm_root(md); if (r) { - DMERR("%s: pre commit failed", __func__); + DMERR("%s: save_sm_root failed", __func__); return r; } diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 911ada643364..3b67afda430b 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -485,11 +485,11 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd) if (r < 0) return r; - r = save_sm_roots(pmd); + r = dm_tm_pre_commit(pmd->tm); if (r < 0) return r; - r = dm_tm_pre_commit(pmd->tm); + r = save_sm_roots(pmd); if (r < 0) return r; diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c index 88487346c4c6..727aacbb1480 100644 --- a/drivers/md/dm-verity-avb.c +++ b/drivers/md/dm-verity-avb.c @@ -12,11 +12,14 @@ #define DM_MSG_PREFIX "verity-avb" -/* Set via module parameter. */ +/* Set via module parameters. */ static char avb_vbmeta_device[64]; +static char avb_invalidate_on_error[4]; static void invalidate_vbmeta_endio(struct bio *bio) { + if (bio->bi_error) + DMERR("invalidate_vbmeta_endio: error %d", bio->bi_error); complete(bio->bi_private); } @@ -30,20 +33,19 @@ static int invalidate_vbmeta_submit(struct bio *bio, bio->bi_private = &wait; bio->bi_end_io = invalidate_vbmeta_endio; bio->bi_bdev = bdev; + bio->bi_rw = rw; bio->bi_iter.bi_sector = 0; if (access_last_sector) { - sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + sector_t last_sector; + + last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; bio->bi_iter.bi_sector = last_sector; } - bio->bi_vcnt = 1; - bio->bi_iter.bi_idx = 0; - bio->bi_iter.bi_size = 512; - bio->bi_iter.bi_bvec_done = 0; - bio->bi_rw = rw; - bio->bi_io_vec[0].bv_page = page; - bio->bi_io_vec[0].bv_len = 512; - bio->bi_io_vec[0].bv_offset = 0; + if (!bio_add_page(bio, page, PAGE_SIZE, 0)) { + DMERR("invalidate_vbmeta_submit: bio_add_page error"); + return -EIO; + } submit_bio(rw, bio); /* Wait up to 2 seconds for completion or fail. */ @@ -65,6 +67,9 @@ static int invalidate_vbmeta(dev_t vbmeta_devt) int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE; int access_last_sector = 0; + DMINFO("invalidate_vbmeta: acting on device %d:%d", + MAJOR(vbmeta_devt), MINOR(vbmeta_devt)); + /* First we open the device for reading. */ dev_mode = FMODE_READ | FMODE_EXCL; bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, @@ -115,7 +120,7 @@ static int invalidate_vbmeta(dev_t vbmeta_devt) goto failed_to_submit_read; } if (memcmp("AVBf", page_address(page) + offset, 4) != 0) { - DMERR("invalidate_vbmeta called on non-vbmeta partition"); + DMERR("invalidate_vbmeta on non-vbmeta partition"); ret = -EINVAL; goto invalid_header; } @@ -175,6 +180,11 @@ void dm_verity_avb_error_handler(void) DMINFO("AVB error handler called for %s", avb_vbmeta_device); + if (strcmp(avb_invalidate_on_error, "yes") != 0) { + DMINFO("Not configured to invalidate"); + return; + } + if (avb_vbmeta_device[0] == '\0') { DMERR("avb_vbmeta_device parameter not set"); goto fail_no_dev; @@ -215,3 +225,5 @@ MODULE_LICENSE("GPL"); #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "androidboot.vbmeta." module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); +module_param_string(invalidate_on_error, avb_invalidate_on_error, + sizeof(avb_invalidate_on_error), 0); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 5d42d8f09421..1a7b11d57256 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2191,8 +2191,11 @@ static void dm_request_fn(struct request_queue *q) tio = tio_from_request(rq); /* Establish tio->ti before queuing work (map_tio_request) */ tio->ti = ti; - queue_kthread_work(&md->kworker, &tio->work); + spin_unlock(q->queue_lock); + if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE) + dm_requeue_original_request(md, rq); BUG_ON(!irqs_disabled()); + spin_lock(q->queue_lock); } goto out; diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index b1ced58eb5e1..a1a68209bd36 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -887,8 +887,12 @@ static int find_key(struct ro_spine *s, dm_block_t block, bool find_highest, else *result_key = le64_to_cpu(ro_node(s)->keys[0]); - if (next_block || flags & INTERNAL_NODE) - block = value64(ro_node(s), i); + if (next_block || flags & INTERNAL_NODE) { + if (find_highest) + block = value64(ro_node(s), i); + else + block = value64(ro_node(s), 0); + } } while (flags & INTERNAL_NODE); diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index ebb280a14325..32adf6b4a9c7 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c @@ -142,10 +142,23 @@ static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b) static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b) { + int r; + uint32_t old_count; enum allocation_event ev; struct sm_disk *smd = container_of(sm, struct sm_disk, sm); - return sm_ll_dec(&smd->ll, b, &ev); + r = sm_ll_dec(&smd->ll, b, &ev); + if (!r && (ev == SM_FREE)) { + /* + * It's only free if it's also free in the last + * transaction. + */ + r = sm_ll_lookup(&smd->old_ll, b, &old_count); + if (!r && !old_count) + smd->nr_allocated_this_transaction--; + } + + return r; } static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 7af976934441..4384b46cee1a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2232,6 +2232,10 @@ static int resize_stripes(struct r5conf *conf, int newsize) err = -ENOMEM; mutex_unlock(&conf->cache_size_mutex); + + conf->slab_cache = sc; + conf->active_name = 1-conf->active_name; + /* Step 4, return new stripes to service */ while(!list_empty(&newstripes)) { nsh = list_entry(newstripes.next, struct stripe_head, lru); @@ -2249,8 +2253,6 @@ static int resize_stripes(struct r5conf *conf, int newsize) } /* critical section pass, GFP_NOIO no longer needed */ - conf->slab_cache = sc; - conf->active_name = 1-conf->active_name; if (!err) conf->pool_size = newsize; return err; diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index fdffb2f0ded8..107853b0fddd 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -2678,7 +2678,9 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { FE_CAN_MUTE_TS | FE_CAN_2G_MODULATION, .frequency_min = 42000000, - .frequency_max = 1002000000 + .frequency_max = 1002000000, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000 }, .init = cxd2841er_init_tc, .sleep = cxd2841er_sleep_tc, diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 359a860fdabb..b382a1d83d92 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-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 @@ -27,29 +27,68 @@ #include <media/v4l2-ctrls.h> #include <linux/mutex.h> #include <linux/delay.h> + +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> + #include <media/adv7481.h> #include <media/msm_ba.h> #include "adv7481_reg.h" +#include "msm_cci.h" +#include "msm_camera_i2c.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" + #define DRIVER_NAME "adv7481" -#define I2C_RW_DELAY 100 -#define I2C_SW_RST_DELAY 10000 -#define GPIO_HW_DELAY_LOW 100000 -#define GPIO_HW_DELAY_HI 10000 +#define I2C_RW_DELAY 1 +#define I2C_SW_RST_DELAY 5000 +#define GPIO_HW_RST_DELAY_HI 10000 +#define GPIO_HW_RST_DELAY_LOW 10000 #define SDP_MIN_SLEEP 5000 #define SDP_MAX_SLEEP 6000 #define SDP_NUM_TRIES 30 #define LOCK_MIN_SLEEP 5000 #define LOCK_MAX_SLEEP 6000 -#define LOCK_NUM_TRIES 20 +#define LOCK_NUM_TRIES 200 + +#define MAX_DEFAULT_WIDTH 1280 +#define MAX_DEFAULT_HEIGHT 720 +#define MAX_DEFAULT_FRAME_RATE 60 +#define MAX_DEFAULT_PIX_CLK_HZ 74240000 #define ONE_MHZ_TO_HZ 1000000 +#define I2C_BLOCK_WRITE_SIZE 1024 + +enum adv7481_gpio_t { + + CCI_I2C_SDA = 0, + CCI_I2C_SCL, + + ADV7481_GPIO_RST, + + ADV7481_GPIO_INT1, + ADV7481_GPIO_INT2, + ADV7481_GPIO_INT3, + + ADV7481_GPIO_MAX, +}; struct adv7481_state { - /* Platform Data */ - struct adv7481_platform_data pdata; + struct device *dev; + + /* VREG */ + struct camera_vreg_t *cci_vreg; + struct regulator *cci_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; + + /* I2C */ + struct msm_camera_i2c_client i2c_client; + u32 cci_master; + u32 i2c_slave_addr; /* V4L2 Data */ struct v4l2_subdev sd; @@ -63,19 +102,25 @@ struct adv7481_state { struct workqueue_struct *work_queues; struct mutex mutex; - struct i2c_client *client; - struct i2c_client *i2c_csi_txa; - struct i2c_client *i2c_csi_txb; - struct i2c_client *i2c_hdmi; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_cp; - struct i2c_client *i2c_sdp; - struct i2c_client *i2c_rep; + uint8_t i2c_io_addr; + uint8_t i2c_csi_txa_addr; + uint8_t i2c_csi_txb_addr; + uint8_t i2c_hdmi_addr; + uint8_t i2c_edid_addr; + uint8_t i2c_cp_addr; + uint8_t i2c_sdp_addr; + uint8_t i2c_rep_addr; + uint8_t i2c_cbus_addr; /* device status and Flags */ int irq; int device_num; int powerup; + int cec_detected; + int clocks_requested; + + /* GPIOs */ + struct gpio gpio_array[ADV7481_GPIO_MAX]; /* routing configuration data */ int csia_src; @@ -196,6 +241,9 @@ const uint8_t adv7481_default_edid_data[] = { #define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data) +static u32 adv7481_inp_to_ba(u32 adv_input); +static bool adv7481_is_timing_locked(struct adv7481_state *state); + static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) { return &(container_of(ctrl->handler, @@ -208,56 +256,151 @@ static inline struct adv7481_state *to_state(struct v4l2_subdev *sd) } /* I2C Rd/Rw Functions */ -static int adv7481_wr_byte(struct i2c_client *i2c_client, unsigned int reg, - unsigned int value) +static int32_t adv7481_cci_i2c_write(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) { - int ret; + return i2c_client->i2c_func_tbl->i2c_write(i2c_client, reg, + *data, data_type); +} - ret = i2c_smbus_write_byte_data(i2c_client, reg & 0xFF, value); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); +static int32_t adv7481_cci_i2c_write_seq( + struct msm_camera_i2c_client *i2c_client, + uint8_t reg, const uint8_t *data, uint32_t size) +{ + return i2c_client->i2c_func_tbl->i2c_write_seq(i2c_client, reg, + (uint8_t *)data, size); +} + +static int32_t adv7481_cci_i2c_read(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) +{ + return i2c_client->i2c_func_tbl->i2c_read(i2c_client, reg, + data, data_type); +} + +static int32_t adv7481_wr_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, uint8_t data) +{ + uint16_t write_data = data; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write(c_i2c_client, reg, &write_data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) + pr_err("Error %d writing cci i2c\n", ret); return ret; } -static int adv7481_rd_byte(struct i2c_client *i2c_client, unsigned int reg) +static int32_t adv7481_wr_block(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, const uint8_t *data, uint32_t size) { - int ret; + int ret = 0; - ret = i2c_smbus_read_byte_data(i2c_client, reg & 0xFF); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write_seq(c_i2c_client, reg, data, size); + if (ret < 0) + pr_err("Error %d writing cci i2c block data\n", ret); return ret; } +static uint8_t adv7481_rd_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return (uint8_t)(data & 0xFF); +} + +static uint16_t adv7481_rd_word(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_WORD_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return data; +} + static int adv7481_set_irq(struct adv7481_state *state) { int ret = 0; - ret = adv7481_wr_byte(state->client, IO_REG_PAD_CTRL_1_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PAD_CTRL_1_ADDR, ADV_REG_SETFIELD(1, IO_PDN_INT2) | ADV_REG_SETFIELD(1, IO_PDN_INT3) | ADV_REG_SETFIELD(1, IO_INV_LLC) | ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT1_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT1_CONF_ADDR, ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR, IO_INTRQ_DUR_SEL) | ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT2_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT2_CONF_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_UNLOCK_EDGE_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_DATAPATH_INT_MASKB_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_CP_UNLOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_VMUTE_REQUEST_HDMI_MB1) | ADV_REG_SETFIELD(1, IO_INT_SD_MB1)); - /* Set hpa */ - ret |= adv7481_wr_byte(state->client, IO_HDMI_LVL_INT_MASKB_3_ADDR, - ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1)); + /* Set cable detect */ + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, + ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1) | + ADV_REG_SETFIELD(1, IO_V_LOCKED_MB1) | + ADV_REG_SETFIELD(1, IO_DE_REGEN_LCK_MB1)); + + /* set CVBS lock/unlock interrupts */ + /* Select SDP MAP 1 */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x20); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_MASK_ADDR, 0x03); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (ret) pr_err("%s: Failed %d to setup interrupt regs\n", __func__, ret); - else - enable_irq(state->irq); + + return ret; +} + +static int adv7481_reset_irq(struct adv7481_state *state) +{ + int ret = 0; + + disable_irq(state->irq); + + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, 0x00); return ret; } @@ -267,27 +410,42 @@ static int adv7481_set_edid(struct adv7481_state *state) int i; int ret = 0; uint8_t edid_state; + uint32_t data_left = 0; + uint32_t start_pos; /* Enable Manual Control of EDID on Port A */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x74, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x74, + 0x01); /* Disable Auto Enable of EDID */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x7A, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x7A, + 0x08); /* Set Primary EDID Size to 256 Bytes */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x70, 0x20); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x70, + 0x20); /* * Readback EDID enable state after a combination of manual * and automatic functions */ - edid_state = adv7481_rd_byte(state->i2c_rep, + edid_state = adv7481_rd_byte(&state->i2c_client, state->i2c_rep_addr, HDMI_REG_RO_EDID_DEBUG_2_ADDR); pr_debug("%s: Readback EDID enable state: 0x%x\n", __func__, edid_state); - for (i = 0; i < ADV7481_EDID_SIZE; i++) { - ret |= adv7481_wr_byte(state->i2c_edid, i, - adv7481_default_edid_data[i]); - } + for (i = 0; i < ADV7481_EDID_SIZE && !ret; i += I2C_BLOCK_WRITE_SIZE) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + i, &adv7481_default_edid_data[i], + I2C_BLOCK_WRITE_SIZE); + + data_left = ADV7481_EDID_SIZE % I2C_BLOCK_WRITE_SIZE; + start_pos = ADV7481_EDID_SIZE - data_left; + if (data_left && !ret) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + start_pos, + &adv7481_default_edid_data[start_pos], + data_left); return ret; } @@ -301,47 +459,232 @@ static irqreturn_t adv7481_irq(int irq, void *dev) return IRQ_HANDLED; } +/* Request CCI clocks for adv7481 register access */ +static int adv7481_request_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == TRUE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + /* enable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) + pr_err("%s:cci enable_vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + +static int adv7481_release_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == FALSE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_RELEASE); + if (ret < 0) + pr_err("%s - cci_release failed\n", __func__); + else + state->clocks_requested = FALSE; + + /* disable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 0); + if (ret < 0) + pr_err("%s:cci disable vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + static void adv7481_irq_delay_work(struct work_struct *work) { struct adv7481_state *state; - uint8_t status; + uint8_t int_raw_status; + uint8_t int_status; + uint8_t raw_status; state = container_of(work, struct adv7481_state, irq_delayed_work.work); mutex_lock(&state->mutex); - /* workaround for irq trigger */ - status = adv7481_rd_byte(state->client, + /* Read raw irq status register */ + int_raw_status = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_INT_RAW_STATUS_ADDR); pr_debug("%s: dev: %d got int raw status: 0x%x\n", __func__, - state->device_num, status); + state->device_num, int_raw_status); + state->cec_detected = ADV_REG_GETFIELD(int_raw_status, IO_INT_CEC_ST); + + while (int_raw_status) { + if (ADV_REG_GETFIELD(int_raw_status, IO_INTRQ1_RAW)) { + int lock_status = -1; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; + + pr_debug("%s: dev: %d got intrq1_raw\n", __func__, + state->device_num); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_INT_STATUS_ADDR); + + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_RAW_STATUS_ADDR); + + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_CLEAR_ADDR, int_status); + + pr_debug("%s: dev: %d got datapath int status: 0x%x\n", + __func__, state->device_num, int_status); + + pr_debug("%s: dev: %d got datapath raw status: 0x%x\n", + __func__, state->device_num, raw_status); + + if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) && + ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) { + uint8_t sdp_sts = 0; + + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x01); + sdp_sts = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RO_MAIN_STATUS1_ADDR); + pr_debug("%s: dev: %d got sdp status: 0x%x\n", + __func__, state->device_num, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + if (ADV_REG_GETFIELD(sdp_sts, + SDP_RO_MAIN_IN_LOCK)) { + lock_status = 0; + pr_debug( + "%s: set lock_status SDP_IN_LOCK:0x%x\n", + __func__, lock_status); + } else { + lock_status = 1; + pr_debug( + "%s: set lock_status SDP_UNLOCK:0x%x\n", + __func__, lock_status); + } + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x20); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_CLR_ADDR, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + } else { + if (ADV_REG_GETFIELD(int_status, + IO_CP_LOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_LOCK_CP_RAW)) { + lock_status = 0; + pr_debug( + "%s: set lock_status IO_CP_LOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + if (ADV_REG_GETFIELD(int_status, + IO_CP_UNLOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_UNLOCK_CP_RAW)) { + lock_status = 1; + pr_debug( + "%s: set lock_status IO_CP_UNLOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + } + + if (lock_status >= 0) { + ptr[0] = adv7481_inp_to_ba(state->mode); + ptr[1] = lock_status; + event.type = lock_status ? + V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK : + V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_INT_STATUS_ADDR); + if (ADV_REG_GETFIELD(int_raw_status, IO_INT_HDMI_ST)) { + int cable_detected = 0; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; - pr_debug("%s: dev: %d got datapath int status: 0x%x\n", __func__, - state->device_num, status); + ptr[0] = adv7481_inp_to_ba(state->mode); - adv7481_wr_byte(state->client, - IO_REG_DATAPATH_INT_CLEAR_ADDR, status); + pr_debug("%s: dev: %d got int_hdmi_st\n", __func__, + state->device_num); - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_RAW_STATUS_ADDR); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_INT_STATUS_3_ADDR); - pr_debug("%s: dev: %d got datapath rawstatus: 0x%x\n", __func__, - state->device_num, status); + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); - status = adv7481_rd_byte(state->client, - IO_HDMI_LVL_INT_STATUS_3_ADDR); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_CLEAR_3_ADDR, int_status); - pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", __func__, - state->device_num, status); + pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", + __func__, state->device_num, int_status); + pr_debug("%s: dev: %d got hdmi lvl raw status 3: 0x%x\n", + __func__, state->device_num, raw_status); - adv7481_wr_byte(state->client, - IO_HDMI_LVL_INT_CLEAR_3_ADDR, status); + if (ADV_REG_GETFIELD(int_status, IO_CABLE_DET_A_ST)) { + cable_detected = ADV_REG_GETFIELD(raw_status, + IO_CABLE_DET_A_RAW); + pr_debug("%s: set cable_detected: 0x%x\n", + __func__, cable_detected); + ptr[1] = cable_detected; + event.type = V4L2_EVENT_MSM_BA_CABLE_DETECT; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + /* Assumption is that vertical sync int + * is the last one to come + */ + if (ADV_REG_GETFIELD(int_status, IO_V_LOCKED_ST)) { + if (ADV_REG_GETFIELD(raw_status, + IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_DE_REGEN_LCK_RAW)) { + pr_debug("%s: port settings changed\n", + __func__); + event.type = + V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } + } + int_raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_INT_RAW_STATUS_ADDR); + } mutex_unlock(&state->mutex); } @@ -350,110 +693,100 @@ static int adv7481_cec_wakeup(struct adv7481_state *state, bool enable) uint8_t val; int ret = 0; - val = adv7481_rd_byte(state->client, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR); val = ADV_REG_GETFIELD(val, IO_PROG_XTAL_FREQ_HIGH); if (enable) { /* CEC wake up enabled in power-down mode */ val |= ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } else { /* CEC wake up disabled in power-down mode */ val |= ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } return ret; } /* Initialize adv7481 I2C Settings */ -static int adv7481_dev_init(struct adv7481_state *state, - struct i2c_client *client) +static int adv7481_dev_init(struct adv7481_state *state) { + uint16_t chip_rev_id; int ret; mutex_lock(&state->mutex); /* Soft reset */ - ret = adv7481_wr_byte(state->client, - IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); /* Delay required following I2C reset and I2C transactions */ - usleep_range(I2C_SW_RST_DELAY, I2C_SW_RST_DELAY+1000); + udelay(I2C_SW_RST_DELAY); + + chip_rev_id = adv7481_rd_word(&state->i2c_client, state->i2c_io_addr, + IO_REG_CHIP_REV_ID_1_ADDR); + pr_debug("%s: ADV7481 chip rev id: 0x%x", __func__, chip_rev_id); /* Disable CEC wake up in power-down mode */ ret |= adv7481_cec_wakeup(state, 0); /* Setting Vid_Std to 720x480p60 */ - ret |= adv7481_wr_byte(state->client, - IO_REG_CP_VID_STD_ADDR, 0x4A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_VID_STD_ADDR, 0x4A); /* Configure I2C Maps and I2C Communication Settings */ /* io_reg_f2 I2C Auto Increment */ - ret |= adv7481_wr_byte(state->client, IO_REG_I2C_CFG_ADDR, - IO_REG_I2C_AUTOINC_EN_REG_VALUE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_I2C_CFG_ADDR, IO_REG_I2C_AUTOINC_EN_REG_VALUE); /* DPLL Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_DPLL_ADDR, - IO_REG_DPLL_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DPLL_ADDR, IO_REG_DPLL_SADDR); /* CP Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CP_ADDR, - IO_REG_CP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_ADDR, IO_REG_CP_SADDR); /* HDMI RX Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_ADDR, - IO_REG_HDMI_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_ADDR, IO_REG_HDMI_SADDR); /* EDID Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_EDID_ADDR, - IO_REG_EDID_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_EDID_ADDR, IO_REG_EDID_SADDR); /* HDMI RX Repeater Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_REP_ADDR, - IO_REG_HDMI_REP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_REP_ADDR, IO_REG_HDMI_REP_SADDR); /* HDMI RX Info-frame Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_INF_ADDR, - IO_REG_HDMI_INF_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_INF_ADDR, IO_REG_HDMI_INF_SADDR); /* CBUS Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CBUS_ADDR, - IO_REG_CBUS_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CBUS_ADDR, IO_REG_CBUS_SADDR); /* CEC Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CEC_ADDR, - IO_REG_CEC_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CEC_ADDR, IO_REG_CEC_SADDR); /* SDP Main Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_SDP_ADDR, - IO_REG_SDP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_SDP_ADDR, IO_REG_SDP_SADDR); /* CSI-TXB Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXB_ADDR, - IO_REG_CSI_TXB_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXB_ADDR, IO_REG_CSI_TXB_SADDR); /* CSI-TXA Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXA_ADDR, - IO_REG_CSI_TXA_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXA_ADDR, IO_REG_CSI_TXA_SADDR); if (ret) { pr_err("%s: Failed dev init %d\n", __func__, ret); goto err_exit; } /* Configure i2c clients */ - state->i2c_csi_txa = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXA_SADDR >> 1); - state->i2c_csi_txb = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXB_SADDR >> 1); - state->i2c_cp = i2c_new_dummy(client->adapter, - IO_REG_CP_SADDR >> 1); - state->i2c_hdmi = i2c_new_dummy(client->adapter, - IO_REG_HDMI_SADDR >> 1); - state->i2c_edid = i2c_new_dummy(client->adapter, - IO_REG_EDID_SADDR >> 1); - state->i2c_sdp = i2c_new_dummy(client->adapter, - IO_REG_SDP_SADDR >> 1); - state->i2c_rep = i2c_new_dummy(client->adapter, - IO_REG_HDMI_REP_SADDR >> 1); - - if (!state->i2c_csi_txa || !state->i2c_csi_txb || !state->i2c_cp || - !state->i2c_sdp || !state->i2c_hdmi || !state->i2c_edid || - !state->i2c_rep) { - pr_err("%s: Additional I2C Client Fail\n", __func__); - ret = -EFAULT; - goto err_exit; - } + state->i2c_csi_txa_addr = IO_REG_CSI_TXA_SADDR >> 1; + state->i2c_csi_txb_addr = IO_REG_CSI_TXB_SADDR >> 1; + state->i2c_cp_addr = IO_REG_CP_SADDR >> 1; + state->i2c_hdmi_addr = IO_REG_HDMI_SADDR >> 1; + state->i2c_edid_addr = IO_REG_EDID_SADDR >> 1; + state->i2c_sdp_addr = IO_REG_SDP_SADDR >> 1; + state->i2c_rep_addr = IO_REG_HDMI_REP_SADDR >> 1; + state->i2c_cbus_addr = IO_REG_CBUS_SADDR >> 1; ret = adv7481_set_edid(state); ret |= adv7481_set_irq(state); @@ -465,28 +798,25 @@ err_exit: } /* Initialize adv7481 hardware */ -static int adv7481_hw_init(struct adv7481_platform_data *pdata, - struct adv7481_state *state) +static int adv7481_hw_init(struct adv7481_state *state) { int ret = 0; - if (!pdata) { - pr_err("%s: PDATA is NULL\n", __func__); - return -EFAULT; - } - mutex_lock(&state->mutex); - if (gpio_is_valid(pdata->rstb_gpio)) { - ret = gpio_request(pdata->rstb_gpio, "rstb_gpio"); - if (ret) { - pr_err("%s: Request GPIO Fail %d\n", __func__, ret); - goto err_exit; - } - ret = gpio_direction_output(pdata->rstb_gpio, 0); - usleep_range(GPIO_HW_DELAY_LOW, GPIO_HW_DELAY_LOW+1000); - ret = gpio_direction_output(pdata->rstb_gpio, 1); - usleep_range(GPIO_HW_DELAY_HI, GPIO_HW_DELAY_HI+1000); + /* Bring ADV7481 out of reset */ + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_RST], 1); + if (ret < 0) { + pr_err("%s: Failed to request reset GPIO %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_RST].gpio)) { + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 0); + udelay(GPIO_HW_RST_DELAY_LOW); + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 1); + udelay(GPIO_HW_RST_DELAY_HI); if (ret) { pr_err("%s: Set GPIO Fail %d\n", __func__, ret); goto err_exit; @@ -494,22 +824,21 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, } /* Only setup IRQ1 for now... */ - if (gpio_is_valid(pdata->irq1_gpio)) { - ret = gpio_request(pdata->irq1_gpio, "irq_gpio"); - if (ret) { - pr_err("%s: Failed to request irq_gpio %d\n", - __func__, ret); - goto err_exit; - } - - ret = gpio_direction_input(pdata->irq1_gpio); + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_INT1], 1); + if (ret < 0) { + pr_err("%s: Failed to request irq_gpio %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_INT1].gpio)) { + ret |= gpio_direction_input( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (ret) { pr_err("%s: Failed gpio_direction irq %d\n", __func__, ret); goto err_exit; } - - state->irq = gpio_to_irq(pdata->irq1_gpio); + state->irq = gpio_to_irq( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (state->irq) { ret = request_irq(state->irq, adv7481_irq, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, @@ -519,18 +848,16 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, __func__, ret); goto err_exit; } + /* disable irq until chip interrupts are programmed */ + disable_irq(state->irq); } else { pr_err("%s: Failed gpio_to_irq %d\n", __func__, ret); ret = -EINVAL; goto err_exit; } - - /* disable irq until chip interrupts are programmed */ - disable_irq(state->irq); - - INIT_DELAYED_WORK(&state->irq_delayed_work, - adv7481_irq_delay_work); } + INIT_DELAYED_WORK(&state->irq_delayed_work, + adv7481_irq_delay_work); err_exit: mutex_unlock(&state->mutex); @@ -548,31 +875,40 @@ static int adv7481_s_ctrl(struct v4l2_ctrl *ctrl) pr_debug("Enter %s: id = 0x%x\n", __func__, ctrl->id); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_BRIGHTNESS, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_BRIGHTNESS, ctrl->val); break; case V4L2_CID_CONTRAST: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_CONTRAST, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_CONTRAST, ctrl->val); break; case V4L2_CID_SATURATION: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_SATURATION, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_SATURATION, ctrl->val); break; case V4L2_CID_HUE: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, CP_REG_HUE, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_HUE, ctrl->val); break; default: break; @@ -611,6 +947,108 @@ static int adv7481_s_power(struct v4l2_subdev *sd, int on) return ret; } +static int adv7481_set_cec_logical_addr(struct adv7481_state *state, int *la) +{ + int rc = 0; + uint8_t val; + + if (!la) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOG_ADDR_MASK_ADDR); + if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK0)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS0); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS0); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK1)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS1); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS1); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK2)) { + val = ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS2); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS2_ADDR, val); + } else { + pr_err("No cec logical address mask set\n"); + } + + return rc; +} + +static int adv7481_cec_powerup(struct adv7481_state *state, int *powerup) +{ + int rc = 0; + uint8_t val = 0; + + if (!powerup) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + pr_debug("%s: set power %d\n", __func__, *powerup); + + val = ADV_REG_SETFIELD(*powerup, CEC_REG_CEC_POWER_UP); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_CEC_POWER_UP_ADDR, val); + + return rc; +} + +static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct adv7481_state *state = to_state(sd); + int *ret_val = arg; + long ret = 0; + int param = 0; + + pr_debug("Enter %s with command: 0x%x", __func__, cmd); + + if (!sd) + return -EINVAL; + + switch (cmd) { + case VIDIOC_HDMI_RX_CEC_S_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, arg); + break; + case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, ¶m); + break; + case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: + if (ret_val) { + *ret_val = 0; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_G_CONNECTED: + if (ret_val) { + *ret_val = state->cec_detected; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_S_ENABLE: + ret = adv7481_cec_powerup(state, arg); + break; + default: + pr_err("Not a typewriter! Command: 0x%x", cmd); + ret = -ENOTTY; + break; + } + return ret; +} + static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) { int ret = 0; @@ -620,18 +1058,22 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) if (sd_standard == NULL) return -EINVAL; + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); do { - sdp_stat = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); usleep_range(SDP_MIN_SLEEP, SDP_MAX_SLEEP); timeout++; - sdp_stat2 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); } while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES)); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (sdp_stat != sdp_stat2) { - pr_err("%s(%d), adv7481 SDP status unstable: 1\n", - __func__, __LINE__); + pr_err("%s, adv7481 SDP status unstable: 1\n", __func__); return -ETIMEDOUT; } @@ -681,22 +1123,50 @@ static int adv7481_set_cvbs_mode(struct adv7481_state *state) pr_debug("Enter %s\n", __func__); state->mode = ADV7481_IP_CVBS_1; /* cvbs video settings ntsc etc */ - ret = adv7481_wr_byte(state->client, 0x00, 0x30); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0f, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x00, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x03, 0x42); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x04, 0x07); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x13, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x17, 0x41); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x31, 0x12); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x52, 0xcd); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0xff); - val = adv7481_rd_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x00, 0x30); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0e, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x0f, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x52, 0xcd); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x00, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x80, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x81, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x82, 0x68); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x03, 0x42); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x04, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x13, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x17, 0x41); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x31, 0x12); + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR); /* Output of SD core routed to MIPI CSI 4-lane Tx */ - val |= ADV_REG_SETFIELD(0x10, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, val); - /* Enable autodetect */ - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0x81); + val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); return ret; } @@ -713,69 +1183,101 @@ static int adv7481_set_hdmi_mode(struct adv7481_state *state) * YUV 422 out via TxA CSI: 4-Lane */ /* Disable chip powerdown & Enable HDMI Rx block */ - temp = adv7481_rd_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR); val = ADV_REG_SETFIELD(1, IO_CTRL_RX_EN) | ADV_REG_SETFIELD(0, IO_CTRL_RX_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_XTAL_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_CORE_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_MASTER_PWDN); - ret = adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, val); /* SDR mode */ - ret |= adv7481_wr_byte(state->client, 0x11, 0x48); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x11, 0x48); /* Set CP core to YUV out */ - ret |= adv7481_wr_byte(state->client, 0x04, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x04, 0x00); /* Set CP core to SDR 422 */ - ret |= adv7481_wr_byte(state->client, 0x12, 0xF2); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x12, 0xF2); /* Saturate both Luma and Chroma values to 254 */ - ret |= adv7481_wr_byte(state->client, 0x17, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x17, 0x80); /* Set CP core to enable AV codes */ - ret |= adv7481_wr_byte(state->client, 0x03, 0x86); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x03, 0x86); /* ADI RS CP Core: */ - ret |= adv7481_wr_byte(state->i2c_cp, 0x7C, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_cp_addr, + 0x7C, 0x00); /* Set CP core Phase Adjustment */ - ret |= adv7481_wr_byte(state->client, 0x0C, 0xE0); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0C, 0xE0); /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ - ret |= adv7481_wr_byte(state->client, IO_PAD_CTRLS_ADDR, 0xDD); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_PAD_CTRLS_ADDR, 0xDD); /* Enable Tx A CSI 4-Lane & data from CP core */ val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, - val); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); /* start to configure HDMI Rx once io-map is configured */ /* Enable HDCP 1.1 */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x40, 0x83); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, + 0x40, 0x83); /* Foreground Channel = A */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x00, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x00, 0x08); /* ADI Required Write */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x98, 0xFF); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x99, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9A, 0x00); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9B, 0x0A); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9D, 0x40); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xCB, 0x09); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x98, 0xFF); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x99, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9A, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9B, 0x0A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9D, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xCB, 0x09); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3D, 0x10); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3E, 0x7B); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3F, 0x5E); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4E, 0xFE); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4F, 0x18); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x57, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x58, 0x04); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x85, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3D, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3E, 0x7B); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3F, 0x5E); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4E, 0xFE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4F, 0x18); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x57, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x58, 0x04); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x85, 0x10); /* Enable All Terminations */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x83, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x83, 0x00); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xA3, 0x01); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xBE, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xA3, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xBE, 0x00); /* HPA Manual Enable */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x6C, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x6C, 0x01); /* HPA Asserted */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xF8, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xF8, 0x01); /* Audio Mute Speed Set to Fastest (Smallest Step Size) */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x0F, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x0F, 0x00); return ret; } @@ -860,7 +1362,7 @@ static int adv7481_set_ip_mode(struct adv7481_state *state, int input) } static int adv7481_set_op_src(struct adv7481_state *state, - int output, int input) + int output, int input) { int ret = 0; int temp = 0; @@ -897,10 +1399,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, default: ret = -EINVAL; } - temp = adv7481_rd_byte(state->client, + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DOWN_CTRL_ADDR); temp |= val; - adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, temp); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, temp); state->csia_src = input; break; case ADV7481_OP_CSIB: @@ -915,11 +1418,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, return ret; } -static u32 ba_inp_to_adv7481(u32 input) +static u32 ba_inp_to_adv7481(u32 ba_input) { u32 adv_input = ADV7481_IP_HDMI; - switch (input) { + switch (ba_input) { case BA_IP_CVBS_0: adv_input = ADV7481_IP_CVBS_1; break; @@ -954,6 +1457,42 @@ static u32 ba_inp_to_adv7481(u32 input) return adv_input; } +static u32 adv7481_inp_to_ba(u32 adv_input) +{ + u32 ba_input = BA_IP_HDMI_1; + + switch (adv_input) { + case ADV7481_IP_CVBS_1: + ba_input = BA_IP_CVBS_0; + break; + case ADV7481_IP_CVBS_2: + ba_input = BA_IP_CVBS_1; + break; + case ADV7481_IP_CVBS_3: + ba_input = BA_IP_CVBS_2; + break; + case ADV7481_IP_CVBS_4: + ba_input = BA_IP_CVBS_3; + break; + case ADV7481_IP_CVBS_5: + ba_input = BA_IP_CVBS_4; + break; + case ADV7481_IP_CVBS_6: + ba_input = BA_IP_CVBS_5; + break; + case ADV7481_IP_HDMI: + ba_input = BA_IP_HDMI_1; + break; + case ADV7481_IP_TTL: + ba_input = BA_IP_TTL; + break; + default: + ba_input = BA_IP_HDMI_1; + break; + } + return ba_input; +} + static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -971,14 +1510,11 @@ static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, goto unlock_exit; } - if (state->mode != adv_input) { - ret = adv7481_set_ip_mode(state, adv_input); - if (ret) - pr_err("%s: Set input mode failed: %d\n", - __func__, ret); - else - state->mode = adv_input; - } + ret = adv7481_set_ip_mode(state, adv_input); + if (ret) + pr_err("%s: Set input mode failed: %d\n", __func__, ret); + else + state->mode = adv_input; unlock_exit: mutex_unlock(&state->mutex); @@ -986,6 +1522,27 @@ unlock_exit: return ret; } +static bool adv7481_is_timing_locked(struct adv7481_state *state) +{ + bool ret = false; + int val1 = 0; + int val2 = 0; + + /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + val1 = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); + val2 = adv7481_rd_byte(&state->i2c_client, state->i2c_cp_addr, + CP_REG_STDI_CH_ADDR); + + if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && + ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + ret = true; + + return ret; +} + static int adv7481_get_hdmi_timings(struct adv7481_state *state, struct adv7481_vid_params *vid_params, struct adv7481_hdmi_params *hdmi_params) @@ -998,13 +1555,15 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, pr_debug("Enter %s\n", __func__); /* Check TMDS PLL Lock and Frequency */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM4_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM4_ADDR); hdmi_params->pll_lock = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_PLL_LOCKED); if (hdmi_params->pll_lock) { - temp1 = adv7481_rd_byte(state->i2c_hdmi, - HDMI_REG_TMDS_FREQ_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_FRAC_ADDR); hdmi_params->tmds_freq = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_FREQ); @@ -1015,34 +1574,29 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, hdmi_params->tmds_freq += ADV_REG_GETFIELD(temp2, HDMI_REG_TMDS_FREQ_FRAC)*ONE_MHZ_TO_HZ/128; } else { - pr_err("%s: PLL not locked return EBUSY\n", __func__); - return -EBUSY; + pr_err("%s(%d): PLL not locked return EBUSY\n", + __func__, __LINE__); + ret = -EBUSY; + goto set_default; } - /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + /* Check Timing Lock */ do { - temp1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - temp2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(temp1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(temp1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(temp1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(temp2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), adv7481 HDMI DE regeneration block NOT Locked: 0x%x", - __func__, __LINE__, temp1); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); } /* Check Timing Lock HDMI Map V:0x07[7], H:0x7[5] */ do { - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_LINE_WIDTH_1_ADDR); if (ADV_REG_GETFIELD(temp1, HDMI_VERT_FILTER_LOCKED) && @@ -1059,7 +1613,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, } /* Check HDMI Parameters */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_FIELD1_HEIGHT1_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_FIELD1_HEIGHT1_ADDR); hdmi_params->color_depth = ADV_REG_GETFIELD(temp1, HDMI_REG_DEEP_COLOR_MODE); @@ -1068,22 +1623,25 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_HDMI_INTERLACED); fieldfactor = (vid_params->intrlcd == 1) ? 2 : 1; - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM5_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM5_ADDR); hdmi_params->pix_rep = ADV_REG_GETFIELD(temp1, HDMI_REG_PIXEL_REPETITION); /* Get Active Timing Data HDMI Map H:0x07[4:0] + 0x08[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_2_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_1_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_2_ADDR); vid_params->act_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_LINE_WIDTH_1) << 8) & 0x1F00) | ADV_REG_GETFIELD(temp2, HDMI_REG_LINE_WIDTH_2)); /* Get Total Timing Data HDMI Map H:0x1E[5:0] + 0x1F[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR); vid_params->tot_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_TOTAL_LINE_WIDTH_1) << 8) & 0x3F00) | @@ -1091,9 +1649,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_TOTAL_LINE_WIDTH_2)); /* Get Active Timing Data HDMI Map V:0x09[4:0] + 0x0A[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_2_ADDR); vid_params->act_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_HEIGHT_1) << 8) & 0x1F00) | @@ -1101,9 +1659,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_FIELD0_HEIGHT_2)); /* Get Total Timing Data HDMI Map V:0x26[5:0] + 0x27[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR); vid_params->tot_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_TOT_HEIGHT_1) << 8) & 0x3F00) | @@ -1139,6 +1697,18 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, (hdmi_params->pix_rep + 1)); } +set_default: + if (ret) { + pr_debug("%s(%d), error %d resort to default fmt\n", + __func__, __LINE__, ret); + vid_params->act_pix = MAX_DEFAULT_WIDTH; + vid_params->act_lines = MAX_DEFAULT_HEIGHT; + vid_params->fr_rate = MAX_DEFAULT_FRAME_RATE; + vid_params->pix_clk = MAX_DEFAULT_PIX_CLK_HZ; + vid_params->intrlcd = 0; + ret = 0; + } + pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d @ %d fps\n", __func__, __LINE__, vid_params->act_pix, vid_params->act_lines, @@ -1170,15 +1740,22 @@ static int adv7481_query_dv_timings(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - timings->type = V4L2_DV_BT_656_1120; - bt_timings->width = vid_params.act_pix; - bt_timings->height = vid_params.act_lines; - bt_timings->pixelclock = vid_params.pix_clk; - bt_timings->interlaced = vid_params.intrlcd ? + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + timings->type = V4L2_DV_BT_656_1120; + bt_timings->width = vid_params.act_pix; + bt_timings->height = vid_params.act_lines; + bt_timings->pixelclock = vid_params.pix_clk; + bt_timings->interlaced = vid_params.intrlcd ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - if (bt_timings->interlaced == V4L2_DV_INTERLACED) - bt_timings->height /= 2; + if (bt_timings->interlaced == V4L2_DV_INTERLACED) + bt_timings->height /= 2; + } else { + pr_err( + "%s: Error in adv7481_get_hdmi_timings. ret %d\n", + __func__, ret); + } break; default: return -EINVAL; @@ -1193,10 +1770,24 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) int temp = 0; struct adv7481_state *state = to_state(sd); uint8_t tStatus = 0x0; + uint32_t count = 0; pr_debug("Enter %s\n", __func__); - tStatus = adv7481_rd_byte(state->i2c_sdp, SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + tStatus = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n", __func__, __LINE__, tStatus); @@ -1245,13 +1836,15 @@ static int adv7481_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int adv7481_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { int ret; struct adv7481_vid_params vid_params; struct adv7481_hdmi_params hdmi_params; struct adv7481_state *state = to_state(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; if (!fmt) return -EINVAL; @@ -1267,17 +1860,29 @@ static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - fmt->width = vid_params.act_pix; - fmt->height = vid_params.act_lines; - if (vid_params.intrlcd) - fmt->height /= 2; + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + fmt->width = vid_params.act_pix; + fmt->height = vid_params.act_lines; + if (vid_params.intrlcd) + fmt->height /= 2; + } else { + pr_err("%s: Error %d in adv7481_get_hdmi_timings\n", + __func__, ret); + } + break; + case ADV7481_IP_CVBS_1: + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->width = 720; + fmt->height = 576; break; default: return -EINVAL; } mutex_unlock(&state->mutex); - fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; return ret; } @@ -1290,17 +1895,17 @@ static int adv7481_set_audio_spdif(struct adv7481_state *state, if (on) { /* Configure I2S_SDATA output pin as an SPDIF output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val |= ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } else { /* Configure I2S_SDATA output pin as an I2S output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val &= ~ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } return ret; @@ -1310,40 +1915,42 @@ static int adv7481_csi_powerdown(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* CSI Tx: power down DPHY */ - ret = adv7481_wr_byte(csi_map, CSI_REG_TX_DPHY_PWDN_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_DPHY_PWDN_ADDR, ADV_REG_SETFIELD(1, CSI_CTRL_DPHY_PWDN)); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x00); /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* * ADI Recommended power down sequence * DPHY and CSI Tx A Power down Sequence * CSI TxA: MIPI PLL DIS */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x3b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x3b); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1354,17 +1961,23 @@ static int adv7481_csi_powerup(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; uint8_t csi_sel = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | - ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | - ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txa; + if (state->csia_src == ADV7481_IP_HDMI) { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + } else { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + } + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { /* Enable 1-Lane MIPI Tx, enable pixel output and * route SD through Pixel port @@ -1373,54 +1986,57 @@ static int adv7481_csi_powerup(struct adv7481_state *state, ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(1, IO_CTRL_SD_THRU_PIX_OUT) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* Enable Tx A/B CSI #-lane */ - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_CSI_PIX_EN_SEL_ADDR, csi_sel); /* TXA MIPI lane settings for CSI */ /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* CSI TxA: Auto D-PHY Timing */ val |= ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* DPHY and CSI Tx A */ - ret |= adv7481_wr_byte(csi_map, 0xdb, 0x10); - ret |= adv7481_wr_byte(csi_map, 0xd6, 0x07); - ret |= adv7481_wr_byte(csi_map, 0xc4, 0x0a); - ret |= adv7481_wr_byte(csi_map, 0x71, 0x33); - ret |= adv7481_wr_byte(csi_map, 0x72, 0x11); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xdb, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xd6, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc4, 0x0a); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x71, 0x33); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x72, 0x11); /* CSI TxA: power up DPHY */ - ret |= adv7481_wr_byte(csi_map, 0xf0, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xf0, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x40); /* adi Recommended power up sequence */ /* DPHY and CSI Tx A Power up Sequence */ /* CSI TxA: MIPI PLL EN */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x01); msleep(200); /* CSI TxA: # MIPI Lane : Power ON */ val = ADV_REG_SETFIELD(0, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x2b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x2b); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x80); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1476,42 +2092,43 @@ static int adv7481_g_input_status(struct v4l2_subdev *sd, u32 *status) { int ret = 0; struct adv7481_state *state = to_state(sd); - uint8_t val1 = 0; - uint8_t val2 = 0; + uint8_t val = 0; uint32_t count = 0; + *status = 0; pr_debug("Enter %s\n", __func__); if (ADV7481_IP_HDMI == state->mode) { - /* - * Check Timing Lock IO Map Status3:0x71[0] && - * 0x71[1] && 0x71[7] - */ + /* Check Timing Lock */ do { - val1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - val2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), HDMI DE regeneration block NOT Locked: 0x%x, 0x%x", - __func__, __LINE__, val1, val2); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); *status |= V4L2_IN_ST_NO_SIGNAL; } } else { - val1 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(val1, SDP_RO_MAIN_IN_LOCK)) { + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + val = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(val, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) { pr_err("%s(%d), SD Input NOT Locked: 0x%x\n", - __func__, __LINE__, val1); + __func__, __LINE__, val); *status |= V4L2_IN_ST_NO_SIGNAL; } } @@ -1530,7 +2147,6 @@ static int adv7481_s_stream(struct v4l2_subdev *sd, int on) static const struct v4l2_subdev_video_ops adv7481_video_ops = { .s_routing = adv7481_s_routing, .g_frame_interval = adv7481_g_frame_interval, - .g_mbus_fmt = adv7481_g_mbus_fmt, .querystd = adv7481_query_sd_std, .g_dv_timings = adv7481_query_dv_timings, .g_input_status = adv7481_g_input_status, @@ -1539,6 +2155,11 @@ static const struct v4l2_subdev_video_ops adv7481_video_ops = { static const struct v4l2_subdev_core_ops adv7481_core_ops = { .s_power = adv7481_s_power, + .ioctl = adv7481_ioctl, +}; + +static const struct v4l2_subdev_pad_ops adv7481_pad_ops = { + .get_fmt = adv7481_get_fmt, }; static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { @@ -1548,10 +2169,13 @@ static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { static const struct v4l2_subdev_ops adv7481_ops = { .core = &adv7481_core_ops, .video = &adv7481_video_ops, + .pad = &adv7481_pad_ops, }; static int adv7481_init_v4l2_controls(struct adv7481_state *state) { + int ret = 0; + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, @@ -1565,59 +2189,195 @@ static int adv7481_init_v4l2_controls(struct adv7481_state *state) state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { - int err = state->ctrl_hdl.error; + ret = state->ctrl_hdl.error; v4l2_ctrl_handler_free(&state->ctrl_hdl); - return err; + } else { + v4l2_ctrl_handler_setup(&state->ctrl_hdl); } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); - return 0; + pr_err("%s: Exit with ret: %d\n", __func__, ret); + return ret; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static int adv7481_cci_init(struct adv7481_state *state) +{ + struct msm_camera_cci_client *cci_client = NULL; + int ret = 0; + + pr_err("%s: Enter\n", __func__); + + state->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + state->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR; + state->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + cci_client = state->i2c_client.cci_client; + if (!cci_client) { + ret = -ENOMEM; + goto err_cci_init; + } + cci_client->cci_subdev = msm_cci_get_subdev(); + pr_debug("%s cci_subdev: %p\n", __func__, cci_client->cci_subdev); + if (!cci_client->cci_subdev) { + ret = -EPROBE_DEFER; + goto err_cci_init; + } + cci_client->cci_i2c_master = state->cci_master; + cci_client->sid = state->i2c_slave_addr; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = I2C_CUSTOM_MODE; + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + pr_debug("%s i2c_client.client: %p\n", __func__, + state->i2c_client.client); + +err_cci_init: + return ret; } -static int adv7481_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7481_parse_dt(struct platform_device *pdev, + struct adv7481_state *state) +{ + struct device_node *np = state->dev->of_node; + uint32_t i = 0; + int gpio_count = 0; + struct resource *adv_addr_res = NULL; + int ret = 0; + + /* config CCI */ + ret = of_property_read_u32(np, "qcom,cci-master", + &state->cci_master); + if (ret < 0 || state->cci_master >= MASTER_MAX) { + pr_err("%s: failed to read cci master . ret %d\n", + __func__, ret); + goto exit; + } + pr_debug("%s: cci_master: 0x%x\n", __func__, state->cci_master); + adv_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!adv_addr_res) { + pr_err("%s: failed to read adv7481 resource.\n", __func__); + goto exit; + } + state->i2c_slave_addr = adv_addr_res->start; + pr_debug("%s: i2c_slave_addr: 0x%x\n", __func__, state->i2c_slave_addr); + state->i2c_io_addr = (uint8_t)state->i2c_slave_addr; + + gpio_count = of_gpio_count(np); + if (gpio_count != ADV7481_GPIO_MAX) { + ret = -EFAULT; + pr_err("%s: dt gpio count %d doesn't match required. ret %d\n", + __func__, gpio_count, ret); + goto exit; + } + for (i = 0; i < ADV7481_GPIO_MAX; i++) { + state->gpio_array[i].gpio = of_get_gpio_flags(np, i, + (enum of_gpio_flags *)&state->gpio_array[i].flags); + if (!gpio_is_valid(state->gpio_array[i].gpio)) { + pr_err("invalid gpio setting for index %d\n", i); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: gpio_array[%d] = %d flag = %ld\n", __func__, i, + state->gpio_array[i].gpio, state->gpio_array[i].flags); + } + +exit: + return ret; +} + +static const struct of_device_id adv7481_id[] = { + { .compatible = "qcom,adv7481", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, adv7481_id); + +static int adv7481_probe(struct platform_device *pdev) { struct adv7481_state *state; - struct adv7481_platform_data *pdata = NULL; + const struct of_device_id *device_id; struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; int ret; - pr_debug("Attempting to probe...\n"); - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_err("%s %s Check i2c Functionality Fail\n", - __func__, client->name); - ret = -EIO; + device_id = of_match_device(adv7481_id, &pdev->dev); + if (!device_id) { + pr_err("%s: device_id is NULL\n", __func__); + ret = -ENODEV; goto err; } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); /* Create 7481 State */ - state = devm_kzalloc(&client->dev, - sizeof(struct adv7481_state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, + sizeof(struct adv7481_state), GFP_KERNEL); if (state == NULL) { ret = -ENOMEM; - pr_err("Check Kzalloc Fail\n"); - goto err_mem; + goto err; } - state->client = client; + platform_set_drvdata(pdev, state); + state->dev = &pdev->dev; + mutex_init(&state->mutex); + ret = adv7481_parse_dt(pdev, state); + if (ret < 0) { + pr_err("Error parsing dt tree\n"); + goto err_mem_free; + } - /* Get and Check Platform Data */ - pdata = (struct adv7481_platform_data *) client->dev.platform_data; - if (!pdata) { - ret = -ENOMEM; - pr_err("Getting Platform data failed\n"); - goto err_mem; + ret = adv7481_cci_init(state); + if (ret < 0) { + pr_err("%s: failed adv7481_cci_init ret %d\n", __func__, ret); + goto err_mem_free; + } + + /* config VREG */ + ret = msm_camera_get_dt_vreg_data(pdev->dev.of_node, + &(state->cci_vreg), &(state->regulator_count)); + if (ret < 0) { + pr_err("%s:cci get_dt_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_config_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci config_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_enable_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci enable_vreg failed\n", __func__); + goto err_mem_free; } + pr_debug("%s - VREG Initialized...\n", __func__); - /* Configure and Register V4L2 I2C Sub-device */ + /* Configure and Register V4L2 Sub-device */ sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7481_ops); + v4l2_subdev_init(sd, &adv7481_ops); + sd->owner = pdev->dev.driver->owner; + v4l2_set_subdevdata(sd, state); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; @@ -1627,103 +2387,136 @@ static int adv7481_probe(struct i2c_client *client, ret = media_entity_init(&state->sd.entity, 1, &state->pad, 0); if (ret) { ret = -EIO; - pr_err("Media entity init failed\n"); + pr_err("%s(%d): Media entity init failed\n", + __func__, __LINE__); goto err_media_entity; } /* Initialize HW Config */ - ret = adv7481_hw_init(pdata, state); + ret = adv7481_hw_init(state); if (ret) { ret = -EIO; - pr_err("HW Initialisation Failed\n"); + pr_err("%s: HW Initialisation Failed\n", __func__); goto err_media_entity; } /* Register V4l2 Control Functions */ - hdl = &state->ctrl_hdl; - v4l2_ctrl_handler_init(hdl, 4); - adv7481_init_v4l2_controls(state); + ret = adv7481_init_v4l2_controls(state); + if (ret) { + pr_err("%s: V4L2 Controls Initialisation Failed %d\n", + __func__, ret); + } - /* Initials ADV7481 State Settings */ + /* Initial ADV7481 State Settings */ state->tx_auto_params = ADV7481_AUTO_PARAMS; - state->tx_lanes = ADV7481_MIPI_2LANE; /* Initialize SW Init Settings and I2C sub maps 7481 */ - ret = adv7481_dev_init(state, client); + ret = adv7481_dev_init(state); if (ret) { ret = -EIO; - pr_err("SW Initialisation Failed\n"); + pr_err("%s(%d): SW Initialisation Failed\n", + __func__, __LINE__); goto err_media_entity; } - /* Set hdmi settings */ - ret = adv7481_set_hdmi_mode(state); - /* BA registration */ - ret |= msm_ba_register_subdev_node(sd); + ret = msm_ba_register_subdev_node(sd); if (ret) { ret = -EIO; - pr_err("BA INIT FAILED\n"); + pr_err("%s: BA init failed\n", __func__); goto err_media_entity; } + enable_irq(state->irq); pr_debug("Probe successful!\n"); return ret; err_media_entity: media_entity_cleanup(&sd->entity); -err_mem: - kfree(state); + +err_mem_free: + adv7481_release_cci_clks(state); + devm_kfree(&pdev->dev, state); + err: - if (!ret) - ret = 1; return ret; } -static int adv7481_remove(struct i2c_client *client) +static int adv7481_remove(struct platform_device *pdev) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7481_state *state = to_state(sd); + struct adv7481_state *state = platform_get_drvdata(pdev); - msm_ba_unregister_subdev_node(sd); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); + msm_ba_unregister_subdev_node(&state->sd); + v4l2_device_unregister_subdev(&state->sd); + media_entity_cleanup(&state->sd.entity); v4l2_ctrl_handler_free(&state->ctrl_hdl); + adv7481_reset_irq(state); if (state->irq > 0) free_irq(state->irq, state); - i2c_unregister_device(state->i2c_csi_txa); - i2c_unregister_device(state->i2c_csi_txb); - i2c_unregister_device(state->i2c_hdmi); - i2c_unregister_device(state->i2c_edid); - i2c_unregister_device(state->i2c_cp); - i2c_unregister_device(state->i2c_sdp); - i2c_unregister_device(state->i2c_rep); + cancel_delayed_work(&state->irq_delayed_work); mutex_destroy(&state->mutex); - kfree(state); + devm_kfree(&pdev->dev, state); return 0; } -static const struct i2c_device_id adv7481_id[] = { - { DRIVER_NAME, 0 }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, adv7481_id); +#ifdef CONFIG_PM_SLEEP +static int adv7481_suspend(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* release CCI clocks */ + ret = adv7481_release_cci_clks(state); + if (ret) + pr_err("%s: adv7481 release cci clocks failed\n", __func__); + else + pr_debug("released cci clocks in suspend"); + + return 0; +} + +static int adv7481_resume(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* Request CCI clocks */ + ret = adv7481_request_cci_clks(state); + if (ret) + pr_err("%s: adv7481 request cci clocks failed\n", __func__); + else + pr_debug("requested cci clocks in resume"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(adv7481_pm_ops, adv7481_suspend, adv7481_resume); +#define ADV7481_PM_OPS (&adv7481_pm_ops) +#else +#define ADV7481_PM_OPS NULL +#endif -static struct i2c_driver adv7481_driver = { +static struct platform_driver adv7481_driver = { .driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, + .of_match_table = adv7481_id, + .pm = ADV7481_PM_OPS, }, .probe = adv7481_probe, .remove = adv7481_remove, - .id_table = adv7481_id, }; -module_i2c_driver(adv7481_driver); +module_driver(adv7481_driver, platform_driver_register, + platform_driver_unregister); MODULE_DESCRIPTION("ADI ADV7481 HDMI/MHL/SD video receiver"); diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h index a4e14fa18e0a..60b1301abbe6 100644 --- a/drivers/media/i2c/adv7481_reg.h +++ b/drivers/media/i2c/adv7481_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -19,329 +19,451 @@ #define ADV_REG_GETFIELD(val, field) \ (((val) & (field##_BMSK)) >> (field##_SHFT)) +#define ADV_REG_RSTFIELD(val, field) \ + ((val) & ~((field##_BMSK) << (field##_SHFT))) + /* IO Map Registers */ -#define IO_REG_MAIN_RST_ADDR 0xFF -#define IO_REG_MAIN_RST_VALUE 0xFF - -#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 -#define IO_CTRL_RX_EN_BMSK 0x0040 -#define IO_CTRL_RX_EN_SHFT 6 -#define IO_CTRL_RX_PWDN_BMSK 0x0020 -#define IO_CTRL_RX_PWDN_SHFT 5 -#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 -#define IO_CTRL_XTAL_PWDN_SHFT 2 -#define IO_CTRL_CORE_PWDN_BMSK 0x0002 -#define IO_CTRL_CORE_PWDN_SHFT 1 -#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 -#define IO_CTRL_MASTER_PWDN_SHFT 0 - -#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 -#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F -#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 - -#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 -#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF -#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 - -#define IO_REG_CP_VID_STD_ADDR 0x05 - -#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 -#define IO_CTRL_CSI4_EN_BMSK 0x0080 -#define IO_CTRL_CSI4_EN_SHFT 7 -#define IO_CTRL_CSI1_EN_BMSK 0x0040 -#define IO_CTRL_CSI1_EN_SHFT 6 -#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 -#define IO_CTRL_PIX_OUT_EN_SHFT 5 -#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 -#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 -#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C -#define IO_CTRL_CSI4_IN_SEL_SHFT 2 - -#define IO_PAD_CTRLS_ADDR 0x0E - -#define IO_REG_I2C_CFG_ADDR 0xF2 -#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 - -#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 - -#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 -#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 -#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 -#define IO_CABLE_DET_A_RAW_BMSK 0x0040 -#define IO_CABLE_DET_A_RAW_SHFT 6 -#define IO_V_LOCKED_RAW_BMSK 0x0002 -#define IO_V_LOCKED_RAW_SHFT 1 -#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 -#define IO_DE_REGEN_LCK_RAW_SHFT 0 +#define IO_REG_MAIN_RST_ADDR 0xFF +#define IO_REG_MAIN_RST_VALUE 0xFF + +#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 +#define IO_CTRL_RX_EN_BMSK 0x0040 +#define IO_CTRL_RX_EN_SHFT 6 +#define IO_CTRL_RX_PWDN_BMSK 0x0020 +#define IO_CTRL_RX_PWDN_SHFT 5 +#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 +#define IO_CTRL_XTAL_PWDN_SHFT 2 +#define IO_CTRL_CORE_PWDN_BMSK 0x0002 +#define IO_CTRL_CORE_PWDN_SHFT 1 +#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 +#define IO_CTRL_MASTER_PWDN_SHFT 0 + +#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 +#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F +#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 + +#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 +#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF +#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 + +#define IO_REG_CP_VID_STD_ADDR 0x05 + +#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 +#define IO_CTRL_CSI4_EN_BMSK 0x0080 +#define IO_CTRL_CSI4_EN_SHFT 7 +#define IO_CTRL_CSI1_EN_BMSK 0x0040 +#define IO_CTRL_CSI1_EN_SHFT 6 +#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 +#define IO_CTRL_PIX_OUT_EN_SHFT 5 +#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 +#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 +#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C +#define IO_CTRL_CSI4_IN_SEL_SHFT 2 + +#define IO_PAD_CTRLS_ADDR 0x0E +#define IO_PAD_FILTER_CTRLS_ADDR 0x0F + +#define IO_REG_I2C_CFG_ADDR 0xF2 +#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 + +#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 /* Interrupts */ -#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 -#define IO_CABLE_DET_A_ST_BMSK 0x0040 -#define IO_CABLE_DET_A_ST_SHFT 6 - -#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 -#define IO_CABLE_DET_A_CLR_BMSK 0x0040 -#define IO_CABLE_DET_A_CLR_SHFT 6 - -#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 -#define IO_CABLE_DET_A_MB2_BMSK 0x0040 -#define IO_CABLE_DET_A_MB2_SHFT 6 - -#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 -#define IO_CABLE_DET_A_MB1_BMSK 0x0040 -#define IO_CABLE_DET_A_MB1_SHFT 6 - -#define IO_REG_PAD_CTRL_1_ADDR 0x1D -#define IO_PDN_INT1_BMSK 0x0080 -#define IO_PDN_INT1_SHFT 7 -#define IO_PDN_INT2_BMSK 0x0040 -#define IO_PDN_INT2_SHFT 6 -#define IO_PDN_INT3_BMSK 0x0020 -#define IO_PDN_INT3_SHFT 5 -#define IO_INV_LLC_BMSK 0x0010 -#define IO_INV_LLC_SHFT 4 -#define IO_DRV_LLC_PAD_BMSK 0x000C -#define IO_DRV_LLC_PAD_SHFT 2 - -#define IO_REG_INT_RAW_STATUS_ADDR 0x3F - -#define IO_REG_INT1_CONF_ADDR 0x40 -#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ_DUR_SEL_SHFT 6 -#define IO_INTRQ_OP_SEL_BMSK 0x0003 -#define IO_INTRQ_OP_SEL_SHFT 0 - -#define IO_REG_INT2_CONF_ADDR 0x41 -#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ2_DUR_SEL_SHFT 6 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 -#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 -#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 -#define IO_INT2_EN_BMSK 0x0004 -#define IO_INT2_EN_SHFT 2 -#define IO_INTRQ2_OP_SEL_BMSK 0x0003 -#define IO_INTRQ2_OP_SEL_SHFT 0 - -#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 -#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 -#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 - -#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 -#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 -#define IO_CP_LOCK_CP_MB1_SHFT 7 -#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 -#define IO_CP_UNLOCK_CP_MB1_SHFT 6 -#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 -#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 -#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 -#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 -#define IO_INT_SD_MB1_BMSK 0x0001 -#define IO_INT_SD_MB1_SHFT 0 +#define IO_HDMI_LVL_INT_CLEAR_1_ADDR 0x69 + +#define IO_HDMI_LVL_INT_MASKB_1_ADDR 0x6B +#define IO_AVI_INFO_MB1_BMSK 0x0001 +#define IO_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_2_ADDR 0x6E + +#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 +#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 +#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 +#define IO_CABLE_DET_A_RAW_BMSK 0x0040 +#define IO_CABLE_DET_A_RAW_SHFT 6 +#define IO_V_LOCKED_RAW_BMSK 0x0002 +#define IO_V_LOCKED_RAW_SHFT 1 +#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 +#define IO_DE_REGEN_LCK_RAW_SHFT 0 + +#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 +#define IO_CABLE_DET_A_ST_BMSK 0x0040 +#define IO_CABLE_DET_A_ST_SHFT 6 +#define IO_V_LOCKED_ST_BMSK 0x0002 +#define IO_V_LOCKED_ST_SHFT 1 +#define IO_DE_REGEN_LCK_ST_BMSK 0x0001 +#define IO_DE_REGEN_LCK_ST_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 +#define IO_CABLE_DET_A_CLR_BMSK 0x0040 +#define IO_CABLE_DET_A_CLR_SHFT 6 + +#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 +#define IO_CABLE_DET_A_MB2_BMSK 0x0040 +#define IO_CABLE_DET_A_MB2_SHFT 6 + +#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 +#define IO_CABLE_DET_A_MB1_BMSK 0x0040 +#define IO_CABLE_DET_A_MB1_SHFT 6 +#define IO_V_LOCKED_MB1_BMSK 0x0002 +#define IO_V_LOCKED_MB1_SHFT 1 +#define IO_DE_REGEN_LCK_MB1_BMSK 0x0001 +#define IO_DE_REGEN_LCK_MB1_SHFT 0 + +#define IO_HDMI_EDG_RAW_STATUS_1_ADDR 0x80 +#define IO_NEW_AVI_INFO_RAW_BMSK 0x0001 +#define IO_NEW_AVI_INFO_RAW_SHFT 0 + +#define IO_HDMI_EDG_INT_STATUS_1_ADDR 0x81 +#define IO_NEW_AVI_INFO_ST_BMSK 0x0001 +#define IO_NEW_AVI_INFO_ST_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_1_ADDR 0x82 +#define IO_NEW_AVI_INFO_CLR_BMSK 0x0001 +#define IO_NEW_AVI_INFO_CLR_SHFT 0 + +#define IO_HDMI_EDG_INT2_MASKB_1_ADDR 0x83 +#define IO_NEW_AVI_INFO_MB2_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB2_SHFT 0 + +#define IO_HDMI_EDG_INT_MASKB_1_ADDR 0x84 +#define IO_NEW_AVI_INFO_MB1_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_2_ADDR 0x87 +#define IO_HDMI_EDG_INT_CLEAR_3_ADDR 0x8C + +#define IO_REG_PAD_CTRL_1_ADDR 0x1D +#define IO_PDN_INT1_BMSK 0x0080 +#define IO_PDN_INT1_SHFT 7 +#define IO_PDN_INT2_BMSK 0x0040 +#define IO_PDN_INT2_SHFT 6 +#define IO_PDN_INT3_BMSK 0x0020 +#define IO_PDN_INT3_SHFT 5 +#define IO_INV_LLC_BMSK 0x0010 +#define IO_INV_LLC_SHFT 4 +#define IO_DRV_LLC_PAD_BMSK 0x000C +#define IO_DRV_LLC_PAD_SHFT 2 + +#define IO_REG_INT_RAW_STATUS_ADDR 0x3F +#define IO_INT_CEC_ST_BMSK 0x0010 +#define IO_INT_CEC_ST_SHFT 4 +#define IO_INT_HDMI_ST_BMSK 0x0008 +#define IO_INT_HDMI_ST_SHFT 3 +#define IO_INTRQ3_RAW_BMSK 0x0004 +#define IO_INTRQ3_RAW_SHFT 2 +#define IO_INTRQ2_RAW_BMSK 0x0002 +#define IO_INTRQ2_RAW_SHFT 1 +#define IO_INTRQ1_RAW_BMSK 0x0001 +#define IO_INTRQ1_RAW_SHFT 0 + +#define IO_REG_INT1_CONF_ADDR 0x40 +#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ_DUR_SEL_SHFT 6 +#define IO_INTRQ_OP_SEL_BMSK 0x0003 +#define IO_INTRQ_OP_SEL_SHFT 0 + +#define IO_REG_INT2_CONF_ADDR 0x41 +#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ2_DUR_SEL_SHFT 6 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 +#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 +#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 +#define IO_INT2_EN_BMSK 0x0004 +#define IO_INT2_EN_SHFT 2 +#define IO_INTRQ2_OP_SEL_BMSK 0x0003 +#define IO_INTRQ2_OP_SEL_SHFT 0 + +#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 +#define IO_CP_LOCK_CP_RAW_BMSK 0x0080 +#define IO_CP_LOCK_CP_RAW_SHFT 7 +#define IO_CP_UNLOCK_CP_RAW_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_RAW_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_RAW_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_RAW_SHFT 5 +#define IO_MPU_STIM_INTRQ_RAW_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_RAW_SHFT 1 +#define IO_INT_SD_RAW_BMSK 0x0001 +#define IO_INT_SD_RAW_SHFT 0 + +#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 +#define IO_CP_LOCK_CP_ST_BMSK 0x0080 +#define IO_CP_LOCK_CP_ST_SHFT 7 +#define IO_CP_UNLOCK_CP_ST_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_ST_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_ST_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_ST_SHFT 5 +#define IO_MPU_STIM_INTRQ_ST_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_ST_SHFT 1 +#define IO_INT_SD_ST_BMSK 0x0001 +#define IO_INT_SD_ST_SHFT 0 + +#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 + +#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 +#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 +#define IO_CP_LOCK_CP_MB1_SHFT 7 +#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_MB1_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 +#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 +#define IO_INT_SD_MB1_BMSK 0x0001 +#define IO_INT_SD_MB1_SHFT 0 + +#define IO_REG_CHIP_REV_ID_1_ADDR 0xDF +#define IO_REG_CHIP_REV_ID_2_ADDR 0xE0 /* Offsets */ -#define IO_REG_DPLL_ADDR 0xF3 -#define IO_REG_CP_ADDR 0xF4 -#define IO_REG_HDMI_ADDR 0xF5 -#define IO_REG_EDID_ADDR 0xF6 -#define IO_REG_HDMI_REP_ADDR 0xF7 -#define IO_REG_HDMI_INF_ADDR 0xF8 -#define IO_REG_CBUS_ADDR 0xF9 -#define IO_REG_CEC_ADDR 0xFA -#define IO_REG_SDP_ADDR 0xFB -#define IO_REG_CSI_TXB_ADDR 0xFC -#define IO_REG_CSI_TXA_ADDR 0xFD +#define IO_REG_DPLL_ADDR 0xF3 +#define IO_REG_CP_ADDR 0xF4 +#define IO_REG_HDMI_ADDR 0xF5 +#define IO_REG_EDID_ADDR 0xF6 +#define IO_REG_HDMI_REP_ADDR 0xF7 +#define IO_REG_HDMI_INF_ADDR 0xF8 +#define IO_REG_CBUS_ADDR 0xF9 +#define IO_REG_CEC_ADDR 0xFA +#define IO_REG_SDP_ADDR 0xFB +#define IO_REG_CSI_TXB_ADDR 0xFC +#define IO_REG_CSI_TXA_ADDR 0xFD /* Sub Address Map Locations */ -#define IO_REG_DPLL_SADDR 0x4C -#define IO_REG_CP_SADDR 0x44 -#define IO_REG_HDMI_SADDR 0x74 -#define IO_REG_EDID_SADDR 0x78 -#define IO_REG_HDMI_REP_SADDR 0x64 -#define IO_REG_HDMI_INF_SADDR 0x62 -#define IO_REG_CBUS_SADDR 0xF0 -#define IO_REG_CEC_SADDR 0x82 -#define IO_REG_SDP_SADDR 0xF2 -#define IO_REG_CSI_TXB_SADDR 0x90 -#define IO_REG_CSI_TXA_SADDR 0x94 +#define IO_REG_DPLL_SADDR 0x4C +#define IO_REG_CP_SADDR 0x44 +#define IO_REG_HDMI_SADDR 0x74 +#define IO_REG_EDID_SADDR 0x78 +#define IO_REG_HDMI_REP_SADDR 0x64 +#define IO_REG_HDMI_INF_SADDR 0x62 +#define IO_REG_CBUS_SADDR 0xF0 +#define IO_REG_CEC_SADDR 0x82 +#define IO_REG_SDP_SADDR 0xF2 +#define IO_REG_CSI_TXB_SADDR 0x90 +#define IO_REG_CSI_TXA_SADDR 0x94 /* HDMI Map Registers */ -#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 -#define HDMI_REG_AV_MUTE_BMSK 0x0040 -#define HDMI_REG_AV_MUTE_SHFT 6 -#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 -#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 -#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 -#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 - -#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 -#define HDMI_REG_HDMI_MODE_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 -#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 -#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 -#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 -#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 -#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 -#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F -#define HDMI_REG_PIXEL_REPETITION_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 -#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 -#define HDMI_VERT_FILTER_LOCKED_SHFT 7 -#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 -#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 -#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 -#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 -#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F -#define HDMI_REG_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 -#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 -#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F -#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 -#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A -#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 - -#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B -#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 -#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 -#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 -#define HDMI_REG_HDMI_INTERLACED_SHFT 5 - -#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E -#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F -#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F -#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 - -#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 -#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 -#define HDMI_DIS_CABLE_DET_RST_SHFT 6 - -#define HDMI_REG_TMDS_FREQ_ADDR 0x51 -#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF -#define HDMI_REG_TMDS_FREQ_SHFT 0 - -#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 -#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F -#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 - -#define HDMI_REG_RST_CTRLS_ADDR 0x5A -#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 -#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 - -#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E -#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 -#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 +#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 +#define HDMI_REG_AV_MUTE_BMSK 0x0040 +#define HDMI_REG_AV_MUTE_SHFT 6 +#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 +#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 +#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 +#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 + +#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 +#define HDMI_REG_HDMI_MODE_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 +#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 +#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 +#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 +#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 +#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 +#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F +#define HDMI_REG_PIXEL_REPETITION_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 +#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 +#define HDMI_VERT_FILTER_LOCKED_SHFT 7 +#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 +#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 +#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 +#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 +#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F +#define HDMI_REG_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 +#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 +#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F +#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 +#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A +#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 + +#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B +#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 +#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 +#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 +#define HDMI_REG_HDMI_INTERLACED_SHFT 5 + +#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E +#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F +#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F +#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 + +#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 +#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 +#define HDMI_DIS_CABLE_DET_RST_SHFT 6 + +#define HDMI_REG_TMDS_FREQ_ADDR 0x51 +#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF +#define HDMI_REG_TMDS_FREQ_SHFT 0 + +#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 +#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F +#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 + +#define HDMI_REG_RST_CTRLS_ADDR 0x5A +#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 +#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 + +#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E +#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 +#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 /* HDMI Repeater Map Registers */ -#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 -#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 +#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 +#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 + +#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 +#define HDMI_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_EDID_A_ENABLE_SHFT 0 + +/* CEC Map Registers */ +#define CEC_REG_LOG_ADDR_MASK_ADDR 0x27 +#define CEC_REG_LOG_ADDR_MASK2_BMSK 0x0040 +#define CEC_REG_LOG_ADDR_MASK2_SHFT 6 +#define CEC_REG_LOG_ADDR_MASK1_BMSK 0x0020 +#define CEC_REG_LOG_ADDR_MASK1_SHFT 5 +#define CEC_REG_LOG_ADDR_MASK0_BMSK 0x0010 +#define CEC_REG_LOG_ADDR_MASK0_SHFT 4 +#define CEC_REG_ERROR_REPORT_MODE_BMSK 0x0008 +#define CEC_REG_ERROR_REPORT_MODE_SHFT 3 +#define CEC_REG_ERROR_REPORT_DET_BMSK 0x0004 +#define CEC_REG_ERROR_REPORT_DET_SHFT 2 +#define CEC_REG_FORCE_NACK_BMSK 0x0002 +#define CEC_REG_FORCE_NACK_SHFT 1 +#define CEC_REG_FORCE_IGNORE_BMSK 0x0001 +#define CEC_REG_FORCE_IGNORE_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS0_1_ADDR 0x28 +#define CEC_REG_LOGICAL_ADDRESS1_BMSK 0x00F0 +#define CEC_REG_LOGICAL_ADDRESS1_SHFT 4 +#define CEC_REG_LOGICAL_ADDRESS0_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS0_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS2_ADDR 0x29 +#define CEC_REG_LOGICAL_ADDRESS2_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS2_SHFT 0 + +#define CEC_REG_CEC_POWER_UP_ADDR 0x2A +#define CEC_REG_CEC_POWER_UP_BMSK 0x0001 +#define CEC_REG_CEC_POWER_UP_SHFT 0 + +#define CEC_REG_CLR_RX_RDY_SFT_RST_ADDR 0x2C +#define CEC_REG_CEC_SOFT_RESET_BMSK 0x0001 +#define CEC_REG_CEC_SOFT_RESET_SHFT 0 -#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 -#define HDMI_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_EDID_A_ENABLE_SHFT 0 /* CP Map Registers */ -#define CP_REG_CONTRAST 0x3A -#define CP_REG_SATURATION 0x3B -#define CP_REG_BRIGHTNESS 0x3C -#define CP_REG_HUE 0x3D -#define CP_REG_VID_ADJ 0x3E -#define CP_CTR_VID_ADJ_EN 0x80 -#define CP_REG_STDI_CH_ADDR 0xB1 -#define CP_STDI_DVALID_CH1_BMSK 0x0080 -#define CP_STDI_DVALID_CH1_SHFT 7 +#define CP_REG_CONTRAST 0x3A +#define CP_REG_SATURATION 0x3B +#define CP_REG_BRIGHTNESS 0x3C +#define CP_REG_HUE 0x3D +#define CP_REG_VID_ADJ 0x3E +#define CP_CTR_VID_ADJ_EN 0x80 +#define CP_REG_STDI_CH_ADDR 0xB1 +#define CP_STDI_DVALID_CH1_BMSK 0x0080 +#define CP_STDI_DVALID_CH1_SHFT 7 + +/* SDP Main Map */ +#define SDP_RW_MAP_REG 0x0e + +/* SDP MAP 1 Registers */ +#define SDP_RW_LOCK_UNLOCK_CLR_ADDR 0x43 +#define SDP_RW_LOCK_UNLOCK_MASK_ADDR 0x44 /* SDP R/O Main Map Registers */ -#define SDP_RO_MAIN_STATUS1_ADDR 0x10 -#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 -#define SDP_RO_MAIN_COL_KILL_SHFT 7 -#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 -#define SDP_RO_MAIN_AD_RESULT_SHFT 4 -#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 -#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 -#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 -#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 -#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 -#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 -#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 -#define SDP_RO_MAIN_IN_LOCK_SHFT 0 +#define SDP_RO_MAIN_STATUS1_ADDR 0x10 +#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 +#define SDP_RO_MAIN_COL_KILL_SHFT 7 +#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 +#define SDP_RO_MAIN_AD_RESULT_SHFT 4 +#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 +#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 +#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 +#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 +#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 +#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 +#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 +#define SDP_RO_MAIN_IN_LOCK_SHFT 0 + /* * CSI Map Registers */ -#define CSI_REG_TX_CFG1_ADDR 0x00 -#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 -#define CSI_CTRL_TX_PWRDN_SHFT 7 -#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 -#define CSI_CTRL_AUTO_PARAMS_SHFT 5 -#define CSI_CTRL_NUM_LANES_BMSK 0x0007 -#define CSI_CTRL_NUM_LANES_SHFT 0 - -#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 -#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 -#define CSI_CTRL_DPHY_PWDN_SHFT 0 +#define CSI_REG_TX_CFG1_ADDR 0x00 +#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 +#define CSI_CTRL_TX_PWRDN_SHFT 7 +#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 +#define CSI_CTRL_AUTO_PARAMS_SHFT 5 +#define CSI_CTRL_NUM_LANES_BMSK 0x0007 +#define CSI_CTRL_NUM_LANES_SHFT 0 + +#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 +#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 +#define CSI_CTRL_DPHY_PWDN_SHFT 0 enum adv7481_adresult { - AD_NTSM_M_J = 0x0, - AD_NTSC_4_43 = 0x1, - AD_PAL_M = 0x2, - AD_PAL_60 = 0x3, - AD_PAL_B_G = 0x4, - AD_SECAM = 0x5, - AD_PAL_COMB_N = 0x6, - AD_SECAM_525 = 0x7, + AD_NTSM_M_J = 0x0, + AD_NTSC_4_43 = 0x1, + AD_PAL_M = 0x2, + AD_PAL_60 = 0x3, + AD_PAL_B_G = 0x4, + AD_SECAM = 0x5, + AD_PAL_COMB_N = 0x6, + AD_SECAM_525 = 0x7, }; enum adv7481_color_depth { - CD_8BIT = 0x0, - CD_10BIT = 0x1, - CD_12BIT = 0x2, - CD_16BIT = 0x3, + CD_8BIT = 0x0, + CD_10BIT = 0x1, + CD_12BIT = 0x2, + CD_16BIT = 0x3, }; enum adv7481_intrq_dur_sel { - AD_4_XTAL_PER = 0x0, - AD_16_XTAL_PER = 0x1, - AD_64_XTAL_PER = 0x2, - AD_ACTIVE_UNTIL_CLR = 0x3, + AD_4_XTAL_PER = 0x0, + AD_16_XTAL_PER = 0x1, + AD_64_XTAL_PER = 0x2, + AD_ACTIVE_UNTIL_CLR = 0x3, }; enum adv7481_intrq_op_sel { - AD_OP_OPEN_DRAIN = 0x0, - AD_OP_DRIVE_LOW = 0x1, - AD_OP_DRIVE_HIGH = 0x2, - AD_OP_DISABLED = 0x3, + AD_OP_OPEN_DRAIN = 0x0, + AD_OP_DRIVE_LOW = 0x1, + AD_OP_DRIVE_HIGH = 0x2, + AD_OP_DISABLED = 0x3, }; enum adv7481_drv_llc_pad { - AD_LLC_PAD_NOT_USED = 0x0, - AD_MIN_DRIVE_STRNGTH = 0x1, - AD_MID_DRIVE_STRNGTH = 0x2, - AD_MAX_DRIVE_STRNGTH = 0x3, + AD_LLC_PAD_NOT_USED = 0x0, + AD_MIN_DRIVE_STRNGTH = 0x1, + AD_MID_DRIVE_STRNGTH = 0x2, + AD_MAX_DRIVE_STRNGTH = 0x3, }; #endif 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/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index c8946f98ced4..7727789dbda1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -173,6 +173,7 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work) } s5p_mfc_clock_on(); ret = s5p_mfc_init_hw(dev); + s5p_mfc_clock_off(); if (ret) mfc_err("Failed to reinit FW\n"); } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2cdb740cde48..f838d9c7ed12 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1321,8 +1321,8 @@ static int mceusb_dev_probe(struct usb_interface *intf, } } } - if (ep_in == NULL) { - dev_dbg(&intf->dev, "inbound and/or endpoint not found"); + if (!ep_in || !ep_out) { + dev_dbg(&intf->dev, "required endpoints not found\n"); return -ENODEV; } diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 317ef63ee789..8d96a22647b3 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -281,6 +281,14 @@ static void free_firmware(struct xc2028_data *priv) int i; tuner_dbg("%s called\n", __func__); + /* free allocated f/w string */ + if (priv->fname != firmware_name) + kfree(priv->fname); + priv->fname = NULL; + + priv->state = XC2028_NO_FIRMWARE; + memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); + if (!priv->firm) return; @@ -291,9 +299,6 @@ static void free_firmware(struct xc2028_data *priv) priv->firm = NULL; priv->firm_size = 0; - priv->state = XC2028_NO_FIRMWARE; - - memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); } static int load_all_firmwares(struct dvb_frontend *fe, @@ -884,9 +889,8 @@ read_not_reliable: return 0; fail: - priv->state = XC2028_NO_FIRMWARE; + free_firmware(priv); - memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); if (retry_count < 8) { msleep(50); retry_count++; @@ -1332,11 +1336,8 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) mutex_lock(&xc2028_list_mutex); /* only perform final cleanup if this is the last instance */ - if (hybrid_tuner_report_instance_count(priv) == 1) { + if (hybrid_tuner_report_instance_count(priv) == 1) free_firmware(priv); - kfree(priv->ctrl.fname); - priv->ctrl.fname = NULL; - } if (priv) hybrid_tuner_release_state(priv); @@ -1399,19 +1400,8 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) /* * Copy the config data. - * For the firmware name, keep a local copy of the string, - * in order to avoid troubles during device release. */ - kfree(priv->ctrl.fname); - priv->ctrl.fname = NULL; memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - if (p->fname) { - priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); - if (priv->ctrl.fname == NULL) { - rc = -ENOMEM; - goto unlock; - } - } /* * If firmware name changed, frees firmware. As free_firmware will @@ -1426,10 +1416,15 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) if (priv->state == XC2028_NO_FIRMWARE) { if (!firmware_name[0]) - priv->fname = priv->ctrl.fname; + priv->fname = kstrdup(p->fname, GFP_KERNEL); else priv->fname = firmware_name; + if (!priv->fname) { + rc = -ENOMEM; + goto unlock; + } + rc = request_firmware_nowait(THIS_MODULE, 1, priv->fname, priv->i2c_props.adap->dev.parent, diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index de4ae5eb4830..10d8a08e36e6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -671,10 +671,8 @@ static int cx231xx_audio_init(struct cx231xx *dev) spin_lock_init(&adev->slock); err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto err_free_card; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx231xx_pcm_capture); @@ -688,10 +686,9 @@ static int cx231xx_audio_init(struct cx231xx *dev) INIT_WORK(&dev->wq_trigger, audio_trigger); err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto err_free_card; + adev->sndcard = card; adev->udev = dev->udev; @@ -701,6 +698,11 @@ static int cx231xx_audio_init(struct cx231xx *dev) hs_config_info[0].interface_info. audio_index + 1]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { + err = -ENODEV; + goto err_free_card; + } + adev->end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -710,13 +712,20 @@ static int cx231xx_audio_init(struct cx231xx *dev) "audio EndPoint Addr 0x%x, Alternate settings: %i\n", adev->end_point_addr, adev->num_alt); adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); - - if (adev->alt_max_pkt_size == NULL) - return -ENOMEM; + if (!adev->alt_max_pkt_size) { + err = -ENOMEM; + goto err_free_card; + } for (i = 0; i < adev->num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { + err = -ENODEV; + goto err_free_pkt_size; + } + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. wMaxPacketSize); adev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -726,6 +735,13 @@ static int cx231xx_audio_init(struct cx231xx *dev) } return 0; + +err_free_pkt_size: + kfree(adev->alt_max_pkt_size); +err_free_card: + snd_card_free(card); + + return err; } static int cx231xx_audio_fini(struct cx231xx *dev) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 8389c162bc89..2c5f76d588ac 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1447,6 +1447,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress; dev->video_mode.num_alt = uif->num_altsetting; @@ -1460,7 +1463,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->video_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); dev_dbg(dev->dev, "Alternate setting %i, max size= %i\n", i, @@ -1477,6 +1485,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->vbi_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -1493,8 +1504,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->vbi_mode.num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. desc.wMaxPacketSize); dev->vbi_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -1514,6 +1529,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->sliced_cc_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -1528,7 +1546,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. desc.wMaxPacketSize); dev->sliced_cc_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -1693,6 +1716,11 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { + retval = -ENODEV; + goto err_video_alt; + } + dev->ts1_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe]. desc.bEndpointAddress; @@ -1710,7 +1738,14 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } for (i = 0; i < dev->ts1_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { + retval = -ENODEV; + goto err_video_alt; + } + + tmp = le16_to_cpu(uif->altsetting[i]. endpoint[isoc_pipe].desc. wMaxPacketSize); dev->ts1_mode.alt_max_pkt_size[i] = diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index ab58f0b9da5c..d1b4b729e814 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -783,6 +783,9 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ + if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1) + return -ENODEV; + purb = usb_alloc_urb(0, GFP_KERNEL); if (purb == NULL) { err("rc usb alloc urb failed"); diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index f10717311e05..dd93c2c8fea9 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -78,6 +78,9 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, u8 *s, *r = NULL; int ret = 0; + if (4 + rlen > 64) + return -EIO; + s = kzalloc(wlen+4, GFP_KERNEL); if (!s) return -ENOMEM; @@ -381,6 +384,22 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD); read = msg[i].flags & I2C_M_RD; + if (3 + msg[i].len > sizeof(obuf)) { + err("i2c wr len=%d too high", msg[i].len); + break; + } + if (write_read) { + if (3 + msg[i+1].len > sizeof(ibuf)) { + err("i2c rd len=%d too high", msg[i+1].len); + break; + } + } else if (read) { + if (3 + msg[i].len > sizeof(ibuf)) { + err("i2c rd len=%d too high", msg[i].len); + break; + } + } + obuf[0] = (msg[i].addr << 1) | (write_read | read); if (read) obuf[1] = 0; diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c index 0712b1bc90b4..0f6d57fbf91b 100644 --- a/drivers/media/usb/gspca/konica.c +++ b/drivers/media/usb/gspca/konica.c @@ -188,6 +188,9 @@ static int sd_start(struct gspca_dev *gspca_dev) return -EIO; } + if (alt->desc.bNumEndpoints < 2) + return -ENODEV; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index d1dc1a198e3e..91d709efef7a 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1523,7 +1523,14 @@ static int usbvision_probe(struct usb_interface *intf, } for (i = 0; i < usbvision->num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < 2) { + ret = -ENODEV; + goto err_pkt; + } + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. wMaxPacketSize); usbvision->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 7433ba5c4bad..fd6a3b36208e 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -604,6 +604,14 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, ptr = pdest = frm->lpvbits; if (frm->ulState == ZR364XX_READ_IDLE) { + if (purb->actual_length < 128) { + /* header incomplete */ + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n", + __func__, purb->actual_length); + return -EINVAL; + } + frm->ulState = ZR364XX_READ_FRAME; frm->cur_size = 0; 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/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ad21276c8d9e..091370f4ea40 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -49,7 +49,8 @@ struct io_stats { #define UID_STATE_TOTAL_CURR 2 #define UID_STATE_TOTAL_LAST 3 -#define UID_STATE_SIZE 4 +#define UID_STATE_DEAD_TASKS 4 +#define UID_STATE_SIZE 5 struct uid_entry { uid_t uid; @@ -214,35 +215,44 @@ static u64 compute_write_bytes(struct task_struct *task) return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; } -static void add_uid_io_curr_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void add_uid_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) { - struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + struct io_stats *io_slot = &uid_entry->io[slot]; - io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += compute_write_bytes(task); - io_curr->rchar += task->ioac.rchar; - io_curr->wchar += task->ioac.wchar; - io_curr->fsync += task->ioac.syscfs; + io_slot->read_bytes += task->ioac.read_bytes; + io_slot->write_bytes += compute_write_bytes(task); + io_slot->rchar += task->ioac.rchar; + io_slot->wchar += task->ioac.wchar; + io_slot->fsync += task->ioac.syscfs; } -static void clean_uid_io_last_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) { - struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= compute_write_bytes(task); - io_last->rchar -= task->ioac.rchar; - io_last->wchar -= task->ioac.wchar; - io_last->fsync -= task->ioac.syscfs; + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); } static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; uid_t uid; @@ -257,70 +267,38 @@ static void update_io_stats_all_locked(void) uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } } -static void update_io_stats_uid_locked(uid_t target_uid) +static void update_io_stats_uid_locked(struct uid_entry *uid_entry) { - struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); - uid_entry = find_or_register_uid(target_uid); - if (!uid_entry) - return; - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); rcu_read_lock(); do_each_thread(temp, task) { - if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } static int uid_io_show(struct seq_file *m, void *v) @@ -405,7 +383,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_uid_locked(uid); + update_io_stats_uid_locked(uid_entry); uid_entry->state = state; @@ -443,8 +421,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_uid_locked(uid); - clean_uid_io_last_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS); exit: rt_mutex_unlock(&uid_lock); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 63f7bf87843f..2cb0ea03a338 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -616,17 +616,39 @@ static int mmc_devfreq_create_freq_table(struct mmc_host *host) host->card->clk_scaling_lowest, host->card->clk_scaling_highest); + /* + * Create the frequency table and initialize it with default values. + * Initialize it with platform specific frequencies if the frequency + * table supplied by platform driver is present, otherwise initialize + * it with min and max frequencies supported by the card. + */ if (!clk_scaling->freq_table) { - pr_debug("%s: no frequency table defined - setting default\n", - mmc_hostname(host)); + if (clk_scaling->pltfm_freq_table_sz) + clk_scaling->freq_table_sz = + clk_scaling->pltfm_freq_table_sz; + else + clk_scaling->freq_table_sz = 2; + clk_scaling->freq_table = kzalloc( - 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL); + (clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->freq_table))), GFP_KERNEL); if (!clk_scaling->freq_table) return -ENOMEM; - clk_scaling->freq_table[0] = host->card->clk_scaling_lowest; - clk_scaling->freq_table[1] = host->card->clk_scaling_highest; - clk_scaling->freq_table_sz = 2; - goto out; + + if (clk_scaling->pltfm_freq_table) { + memcpy(clk_scaling->freq_table, + clk_scaling->pltfm_freq_table, + (clk_scaling->pltfm_freq_table_sz * + sizeof(*(clk_scaling->pltfm_freq_table)))); + } else { + pr_debug("%s: no frequency table defined - setting default\n", + mmc_hostname(host)); + clk_scaling->freq_table[0] = + host->card->clk_scaling_lowest; + clk_scaling->freq_table[1] = + host->card->clk_scaling_highest; + goto out; + } } if (host->card->clk_scaling_lowest > @@ -835,7 +857,7 @@ int mmc_resume_clk_scaling(struct mmc_host *host) devfreq_min_clk = host->clk_scaling.freq_table[0]; host->clk_scaling.curr_freq = devfreq_max_clk; - if (host->ios.clock < host->card->clk_scaling_highest) + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) host->clk_scaling.curr_freq = devfreq_min_clk; host->clk_scaling.clk_scaling_in_progress = false; @@ -895,6 +917,10 @@ int mmc_exit_clk_scaling(struct mmc_host *host) host->clk_scaling.devfreq = NULL; atomic_set(&host->clk_scaling.devfreq_abort, 1); + + kfree(host->clk_scaling.freq_table); + host->clk_scaling.freq_table = NULL; + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); return 0; 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 ca72ebfd55a3..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); @@ -1837,13 +1837,13 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, } if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table", - &msm_host->mmc->clk_scaling.freq_table, - &msm_host->mmc->clk_scaling.freq_table_sz, 0)) + &msm_host->mmc->clk_scaling.pltfm_freq_table, + &msm_host->mmc->clk_scaling.pltfm_freq_table_sz, 0)) pr_debug("%s: no clock scaling frequencies were supplied\n", dev_name(dev)); - else if (!msm_host->mmc->clk_scaling.freq_table || - !msm_host->mmc->clk_scaling.freq_table_sz) - dev_err(dev, "bad dts clock scaling frequencies\n"); + else if (!msm_host->mmc->clk_scaling.pltfm_freq_table || + !msm_host->mmc->clk_scaling.pltfm_freq_table_sz) + dev_err(dev, "bad dts clock scaling frequencies\n"); /* * Few hosts can support DDR52 mode at the same lower @@ -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/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 25f21968fa5c..de2ea9f2f966 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -1077,7 +1077,7 @@ static int stir421x_patch_device(struct irda_usb_cb *self) * are "42101001.sb" or "42101002.sb" */ sprintf(stir421x_fw_name, "4210%4X.sb", - self->usbdev->descriptor.bcdDevice); + le16_to_cpu(self->usbdev->descriptor.bcdDevice)); ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); if (ret < 0) return ret; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 09052f9e324f..c6f5d9a6bec6 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -754,6 +754,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ + {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ 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/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 165dd202c365..c92564b3ec85 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -37,6 +37,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */ + { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ @@ -1216,6 +1217,9 @@ static int send_eject_command(struct usb_interface *interface) u8 bulk_out_ep; int r; + if (iface_desc->desc.bNumEndpoints < 2) + return -ENODEV; + /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 95a7fdb3cc1c..c602a1e674ca 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -2135,9 +2135,7 @@ void cw1200_mcast_timeout(unsigned long arg) int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 6656215a13a9..04b0349a6ad9 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct il_priv *il = hw->priv; int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index b3ad34e8bf5a..1eb1a823a111 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -729,12 +729,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index ce12717e656a..1a8ea775de08 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -826,13 +826,16 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; bool tx_agg_ref = false; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", sta->addr, tid, action); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 21192b6f9c64..268e50ba88a5 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -947,6 +947,7 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) if (card && card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); } return 0; } @@ -1513,6 +1514,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -1; card->cmd_buf = skb; + /* + * Need to keep a reference, since core driver might free up this + * buffer before we've unmapped it. + */ + skb_get(skb); /* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to @@ -1610,6 +1616,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); card->cmd_buf = NULL; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 9b4d8a637915..4b354918e183 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -359,6 +359,107 @@ bool rtl8821ae_phy_rf_config(struct ieee80211_hw *hw) return rtl8821ae_phy_rf6052_config(hw); } +static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 4: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } + case 0: + case 2: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } +} + +static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 0: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } else { + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } + break; + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 2: + case 4: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + } +} + u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band, u8 rf_path) { @@ -553,14 +654,9 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2b'00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 0); } - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x000); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x000); - } + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_24g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1); @@ -615,14 +711,8 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2'b00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 1); - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x010); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x010); - } + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_5g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h index 1d6110f9c1fb..ed69dbe178ff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h @@ -2424,6 +2424,7 @@ #define BMASKH4BITS 0xf0000000 #define BMASKOFDM_D 0xffc00000 #define BMASKCCK 0x3f3f3f3f +#define BMASKRFEINV 0x3ff00000 #define BRFREGOFFSETMASK 0xfffff diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index 09c7e098f460..085ef5c87262 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -206,5 +206,33 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) mbox->sc_pwd_len, mbox->sc_pwd); + if (vector & RX_BA_WIN_SIZE_CHANGE_EVENT_ID) { + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + u8 link_id = mbox->rx_ba_link_id; + u8 win_size = mbox->rx_ba_win_size; + const u8 *addr; + + wlvif = wl->links[link_id].wlvif; + vif = wl12xx_wlvif_to_vif(wlvif); + + /* Update RX aggregation window size and call + * MAC routine to stop active RX aggregations for this link + */ + if (wlvif->bss_type != BSS_TYPE_AP_BSS) + addr = vif->bss_conf.bssid; + else + addr = wl->links[link_id].addr; + + sta = ieee80211_find_sta(vif, addr); + if (sta) { + sta->max_rx_aggregation_subframes = win_size; + ieee80211_stop_rx_ba_session(vif, + wl->links[link_id].ba_bitmap, + addr); + } + } + return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index f3d4f13379cb..9495fadc8093 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -38,6 +38,7 @@ enum { REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), + RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21), SMART_CONFIG_SYNC_EVENT_ID = BIT(22), SMART_CONFIG_DECODE_EVENT_ID = BIT(23), TIME_SYNC_EVENT_ID = BIT(24), diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 50cce42089a5..47f355e92193 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1029,7 +1029,8 @@ static int wl18xx_boot(struct wl1271 *wl) DFS_CHANNELS_CONFIG_COMPLETE_EVENT | SMART_CONFIG_SYNC_EVENT_ID | SMART_CONFIG_DECODE_EVENT_ID | - TIME_SYNC_EVENT_ID; + TIME_SYNC_EVENT_ID | + RX_BA_WIN_SIZE_CHANGE_EVENT_ID; wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index f28fa3b5029d..0646c9b6f8d7 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1419,7 +1419,8 @@ out: /* setup BA session receiver setting in the FW. */ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid) + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size) { struct wl1271_acx_ba_receiver_setup *acx; int ret; @@ -1435,7 +1436,7 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, acx->hlid = peer_hlid; acx->tid = tid_index; acx->enable = enable; - acx->win_size = wl->conf.ht.rx_ba_win_size; + acx->win_size = win_size; acx->ssn = ssn; ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx, diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 954d57ec98f4..524aea495dff 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1112,7 +1112,8 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl, int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid); + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size); int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, u64 *mactime); int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 30165ea0fa25..7b27c7e23af2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5328,7 +5328,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true, - hlid); + hlid, + params->buf_size); + if (!ret) { *ba_bitmap |= BIT(tid); wl->ba_rx_session_count++; @@ -5349,7 +5351,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false, - hlid); + hlid, 0); if (!ret) { *ba_bitmap &= ~BIT(tid); wl->ba_rx_session_count--; diff --git a/drivers/of/address.c b/drivers/of/address.c index ec5eb17ae283..5393be762da4 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -260,7 +260,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + parser->np > parser->end) return NULL; - range->pci_space = parser->range[0]; + range->pci_space = be32_to_cpup(parser->range); range->flags = of_bus_pci_get_flags(parser->range); range->pci_addr = of_read_number(parser->range + 1, ns); range->cpu_addr = of_translate_address(parser->node, diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 1aebd49220b0..217c7ce3f57b 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -4675,6 +4675,8 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) goto link_fail; } + msleep(500); + msm_pcie_config_controller(dev); if (!dev->msi_gicm_addr) @@ -5608,16 +5610,18 @@ static void msm_pcie_unmap_qgic_addr(struct msm_pcie_dev_t *dev, } } -void msm_pcie_destroy_irq(unsigned int irq) +void msm_pcie_destroy_irq(unsigned int irq, struct pci_dev *pdev) { int pos; - struct pci_dev *pdev = irq_get_chip_data(irq); struct msi_desc *entry = irq_get_msi_desc(irq); struct msi_desc *firstentry; struct msm_pcie_dev_t *dev; u32 nvec; int firstirq; + if (!pdev) + pdev = irq_get_chip_data(irq); + if (!pdev) { pr_err("PCIe: pci device is null. IRQ:%d\n", irq); return; @@ -5677,7 +5681,7 @@ void msm_pcie_destroy_irq(unsigned int irq) void arch_teardown_msi_irq(unsigned int irq) { PCIE_GEN_DBG("irq %d deallocated\n", irq); - msm_pcie_destroy_irq(irq); + msm_pcie_destroy_irq(irq, NULL); } void arch_teardown_msi_irqs(struct pci_dev *dev) @@ -5696,7 +5700,7 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) continue; nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) - arch_teardown_msi_irq(entry->irq + i); + msm_pcie_destroy_irq(entry->irq + i, dev); } } @@ -5892,7 +5896,6 @@ static int arch_setup_msi_irq_qgic(struct pci_dev *pdev, firstirq = irq; irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); - irq_set_chip_data(irq, pdev); } /* write msi vector and data */ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d7508704c992..f8b2b5987ea9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -973,15 +973,19 @@ void pci_remove_legacy_files(struct pci_bus *b) int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, enum pci_mmap_api mmap_api) { - unsigned long nr, start, size, pci_start; + unsigned long nr, start, size; + resource_size_t pci_start = 0, pci_end; if (pci_resource_len(pdev, resno) == 0) return 0; nr = vma_pages(vma); start = vma->vm_pgoff; size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; - pci_start = (mmap_api == PCI_MMAP_PROCFS) ? - pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; + if (mmap_api == PCI_MMAP_PROCFS) { + pci_resource_to_user(pdev, resno, &pdev->resource[resno], + &pci_start, &pci_end); + pci_start >>= PAGE_SHIFT; + } if (start >= pci_start && start < pci_start + size && start + nr <= pci_start + size) return 1; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0e53488f8ec1..1a14ca8965e6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1732,8 +1732,8 @@ static void pci_pme_list_scan(struct work_struct *work) } } if (!list_empty(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); + queue_delayed_work(system_freezable_wq, &pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } @@ -1798,8 +1798,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable) mutex_lock(&pci_pme_list_mutex); list_add(&pme_dev->list, &pci_pme_list); if (list_is_singular(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); + queue_delayed_work(system_freezable_wq, + &pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } else { mutex_lock(&pci_pme_list_mutex); 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/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index be59947ba6f2..d469463a0b00 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -95,6 +95,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41 #define QPNP_SMPS_REG_MODE 0x45 #define QPNP_SMPS_REG_STEP_CTRL 0x61 +#define QPNP_SMPS_REG_UL_LL_CTRL 0x68 /* FTS426 voltage control registers */ #define QPNP_FTS426_REG_VOLTAGE_LB 0x40 @@ -102,6 +103,22 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_FTS426_REG_VOLTAGE_VALID_LB 0x42 #define QPNP_FTS426_REG_VOLTAGE_VALID_UB 0x43 +/* HF voltage limit registers */ +#define QPNP_HF_REG_VOLTAGE_ULS 0x69 +#define QPNP_HF_REG_VOLTAGE_LLS 0x6B + +/* FTS voltage limit registers */ +#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A +#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C + +/* FTS426 voltage limit registers */ +#define QPNP_FTS426_REG_VOLTAGE_ULS_LB 0x68 +#define QPNP_FTS426_REG_VOLTAGE_ULS_UB 0x69 + +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 + #define QPNP_SMPS_MODE_PWM 0x80 #define QPNP_SMPS_MODE_AUTO 0x40 #define QPNP_FTS426_MODE_PWM 0x07 @@ -924,6 +941,88 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) return rc; } +static int qpnp_smps_check_constraints(struct spm_vreg *vreg, + struct regulator_init_data *init_data) +{ + int rc = 0, limit_min_uV, limit_max_uV; + u16 ul_reg, ll_reg; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID; + ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID; + + switch (vreg->regulator_type) { + case QPNP_TYPE_HF: + ul_reg = QPNP_HF_REG_VOLTAGE_ULS; + ll_reg = QPNP_HF_REG_VOLTAGE_LLS; + case QPNP_TYPE_FTS2: + case QPNP_TYPE_FTS2p5: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ul_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ll_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + break; + case QPNP_TYPE_FTS426: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_FTS426_REG_VOLTAGE_ULS_LB, + reg, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, + ((unsigned)reg[1] << 8) | reg[0]); + break; + case QPNP_TYPE_ULT_HF: + /* no HW voltage limit configuration */ + break; + } + + if (init_data->constraints.min_uV < limit_min_uV + || init_data->constraints.max_uV > limit_max_uV) { + dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + init_data->constraints.min_uV, + init_data->constraints.max_uV, limit_min_uV, + limit_max_uV); + return -EINVAL; + } + + return rc; +} + static bool spm_regulator_using_range0(struct spm_vreg *vreg) { return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0 @@ -1105,6 +1204,13 @@ static int spm_regulator_probe(struct platform_device *pdev) return -EINVAL; } + rc = qpnp_smps_check_constraints(vreg, init_data); + if (rc) { + dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n", + __func__, rc); + return rc; + } + vreg->rdesc.name = init_data->constraints.name; vreg->rdesc.type = REGULATOR_VOLTAGE; vreg->rdesc.owner = THIS_MODULE; diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index d2c3d7cc35f5..5ca6d2130593 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -311,8 +311,7 @@ static int tps_65023_probe(struct i2c_client *client, /* Enable setting output voltage by I2C */ regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, - TPS65023_REG_CTRL2_CORE_ADJ, - TPS65023_REG_CTRL2_CORE_ADJ); + TPS65023_REG_CTRL2_CORE_ADJ, 0); return 0; } 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 a90c51a113d2..544a71e7c242 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -365,6 +365,8 @@ static int ufshcd_disable_clocks(struct ufs_hba *hba, bool is_gating_context); static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba, bool is_gating_context); +static void ufshcd_hold_all(struct ufs_hba *hba); +static void ufshcd_release_all(struct ufs_hba *hba); static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused); static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba); static inline void ufshcd_save_tstamp_of_last_dme_cmd(struct ufs_hba *hba); @@ -516,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, @@ -592,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; @@ -641,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 @@ -2038,6 +2040,22 @@ out: return; } +static void __ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, + unsigned long delay_ms) +{ + pm_runtime_get_sync(hba->dev); + ufshcd_hold_all(hba); + ufshcd_scsi_block_requests(hba); + down_write(&hba->lock); + /* wait for all the outstanding requests to finish */ + ufshcd_wait_for_doorbell_clr(hba, U64_MAX); + ufshcd_set_auto_hibern8_timer(hba, delay_ms); + up_write(&hba->lock); + ufshcd_scsi_unblock_requests(hba); + ufshcd_release_all(hba); + pm_runtime_put_sync(hba->dev); +} + static void ufshcd_hibern8_exit_work(struct work_struct *work) { int ret; @@ -2089,19 +2107,32 @@ static ssize_t ufshcd_hibern8_on_idle_delay_store(struct device *dev, { struct ufs_hba *hba = dev_get_drvdata(dev); unsigned long flags, value; + bool change = true; if (kstrtoul(buf, 0, &value)) return -EINVAL; spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->hibern8_on_idle.delay_ms == value) + change = false; + + if (value >= hba->clk_gating.delay_ms_pwr_save || + value >= hba->clk_gating.delay_ms_perf) { + dev_err(hba->dev, "hibern8_on_idle_delay (%lu) can not be >= to clkgate_delay_ms_pwr_save (%lu) and clkgate_delay_ms_perf (%lu)\n", + value, hba->clk_gating.delay_ms_pwr_save, + hba->clk_gating.delay_ms_perf); + spin_unlock_irqrestore(hba->host->host_lock, flags); + return -EINVAL; + } + hba->hibern8_on_idle.delay_ms = value; spin_unlock_irqrestore(hba->host->host_lock, flags); /* Update auto hibern8 timer value if supported */ - if (ufshcd_is_auto_hibern8_supported(hba) && + if (change && ufshcd_is_auto_hibern8_supported(hba) && hba->hibern8_on_idle.is_enabled) - ufshcd_set_auto_hibern8_timer(hba, - hba->hibern8_on_idle.delay_ms); + __ufshcd_set_auto_hibern8_timer(hba, + hba->hibern8_on_idle.delay_ms); return count; } @@ -2131,7 +2162,7 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev, /* Update auto hibern8 timer value if supported */ if (ufshcd_is_auto_hibern8_supported(hba)) { - ufshcd_set_auto_hibern8_timer(hba, + __ufshcd_set_auto_hibern8_timer(hba, value ? hba->hibern8_on_idle.delay_ms : value); goto update; } @@ -3285,8 +3316,10 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, /* * May get invoked from shutdown and IOCTL contexts. * In shutdown context, it comes in with lock acquired. + * In error recovery context, it may come with lock acquired. */ - if (!ufshcd_is_shutdown_ongoing(hba)) + + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) down_read(&hba->lock); /* @@ -3320,7 +3353,7 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, out_put_tag: ufshcd_put_dev_cmd_tag(hba, tag); wake_up(&hba->dev_cmd.tag_wq); - if (!ufshcd_is_shutdown_ongoing(hba)) + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) up_read(&hba->lock); return err; } @@ -4268,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); @@ -6096,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); } } @@ -6608,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/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 168db46084df..8385987e8888 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -849,7 +849,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) rcu_id = srcu_read_lock(&einfo->use_ref); - if (unlikely(!einfo->rx_fifo)) { + if (unlikely(!einfo->rx_fifo) && atomic_ctx) { if (!get_rx_fifo(einfo)) { srcu_read_unlock(&einfo->use_ref, rcu_id); return; diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 43d954a0f7c7..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; @@ -191,8 +261,8 @@ enum icnss_driver_state { ICNSS_FW_TEST_MODE, ICNSS_PM_SUSPEND, ICNSS_PM_SUSPEND_NOIRQ, - ICNSS_SSR_ENABLED, - ICNSS_PDR_ENABLED, + ICNSS_SSR_REGISTERED, + ICNSS_PDR_REGISTERED, ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, ICNSS_WLFW_EXISTS, @@ -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,21 +2371,29 @@ 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; } if (code != SUBSYS_BEFORE_SHUTDOWN) return NOTIFY_OK; - if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) + if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) return NOTIFY_OK; icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", @@ -2402,14 +2434,14 @@ static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv) icnss_pr_err("Modem register notifier failed: %d\n", ret); } - set_bit(ICNSS_SSR_ENABLED, &priv->state); + set_bit(ICNSS_SSR_REGISTERED, &priv->state); return ret; } static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv) { - if (!test_and_clear_bit(ICNSS_SSR_ENABLED, &priv->state)) + if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state)) return 0; subsys_notif_unregister_notifier(priv->modem_notify_handler, @@ -2423,7 +2455,7 @@ static int icnss_pdr_unregister_notifier(struct icnss_priv *priv) { int i; - if (!test_and_clear_bit(ICNSS_PDR_ENABLED, &priv->state)) + if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state)) return 0; for (i = 0; i < priv->total_domains; i++) @@ -2547,9 +2579,10 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, priv->service_notifier = notifier; priv->total_domains = pd->total_domains; - set_bit(ICNSS_PDR_ENABLED, &priv->state); + set_bit(ICNSS_PDR_REGISTERED, &priv->state); - icnss_pr_dbg("PD restart enabled, state: 0x%lx\n", priv->state); + icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n", + priv->state); return NOTIFY_OK; @@ -3204,7 +3237,7 @@ int icnss_trigger_recovery(struct device *dev) goto out; } - if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) { + if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) { icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n", priv->state); ret = -EOPNOTSUPP; @@ -3658,11 +3691,11 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_PM_SUSPEND_NOIRQ: seq_puts(s, "PM SUSPEND NOIRQ"); continue; - case ICNSS_SSR_ENABLED: - seq_puts(s, "SSR ENABLED"); + case ICNSS_SSR_REGISTERED: + seq_puts(s, "SSR REGISTERED"); continue; - case ICNSS_PDR_ENABLED: - seq_puts(s, "PDR ENABLED"); + case ICNSS_PDR_REGISTERED: + seq_puts(s, "PDR REGISTERED"); continue; case ICNSS_PD_RESTART: seq_puts(s, "PD RESTART"); @@ -4276,6 +4309,11 @@ static int icnss_probe(struct platform_device *pdev) icnss_debugfs_create(priv); + ret = device_init_wakeup(&priv->pdev->dev, true); + if (ret) + icnss_pr_err("Failed to init platform device wakeup source, err = %d\n", + ret); + penv = priv; icnss_pr_info("Platform driver probed successfully\n"); @@ -4296,6 +4334,8 @@ static int icnss_remove(struct platform_device *pdev) { icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state); + device_init_wakeup(&penv->pdev->dev, false); + icnss_debugfs_destroy(penv); icnss_modem_ssr_unregister_notifier(penv); @@ -4313,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 a7a0ffa2c18e..ca22b6cdf4a1 100644 --- a/drivers/soc/qcom/icnss_utils.c +++ b/drivers/soc/qcom/icnss_utils.c @@ -12,11 +12,14 @@ #include <linux/module.h> #include <linux/slab.h> +#include <soc/qcom/icnss.h> #define ICNSS_MAX_CH_NUM 45 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; @@ -124,3 +127,28 @@ int icnss_wlan_get_dfs_nol(void *info, u16 info_len) return len; } EXPORT_SYMBOL(icnss_wlan_get_dfs_nol); + +void icnss_increment_driver_load_cnt(void) +{ + ++driver_load_cnt; +} +EXPORT_SYMBOL(icnss_increment_driver_load_cnt); + +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/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 1de3dddfd0f4..8deee007218b 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -429,6 +429,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) global_page_state(NR_FILE_PAGES) + zcache_pages()) other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - total_swapcache_pages(); else other_file = 0; diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c index b87192e0f9aa..109becdabc24 100644 --- a/drivers/staging/comedi/drivers/jr3_pci.c +++ b/drivers/staging/comedi/drivers/jr3_pci.c @@ -610,7 +610,7 @@ static void jr3_pci_poll_dev(unsigned long data) s = &dev->subdevices[i]; spriv = s->private; - if (now > spriv->next_time_min) { + if (time_after_eq(now, spriv->next_time_min)) { struct jr3_pci_poll_delay sub_delay; sub_delay = jr3_pci_poll_subdevice(s); @@ -726,11 +726,12 @@ static int jr3_pci_auto_attach(struct comedi_device *dev, s->insn_read = jr3_pci_ai_insn_read; spriv = jr3_pci_alloc_spriv(dev, s); - if (spriv) { - /* Channel specific range and maxdata */ - s->range_table_list = spriv->range_table_list; - s->maxdata_list = spriv->maxdata_list; - } + if (!spriv) + return -ENOMEM; + + /* Channel specific range and maxdata */ + s->range_table_list = spriv->range_table_list; + s->maxdata_list = spriv->maxdata_list; } /* Reset DSP card */ diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c index 445f83615575..fb4f3fea6c66 100644 --- a/drivers/staging/gdm724x/gdm_mux.c +++ b/drivers/staging/gdm724x/gdm_mux.c @@ -670,14 +670,14 @@ static int __init gdm_usb_mux_init(void) static void __exit gdm_usb_mux_exit(void) { - unregister_lte_tty_driver(); - if (mux_rx_wq) { flush_workqueue(mux_rx_wq); destroy_workqueue(mux_rx_wq); } usb_deregister(&gdm_mux_driver); + unregister_lte_tty_driver(); + } module_init(gdm_usb_mux_init); diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index e9c4f973bba9..79bf13f5c0d1 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -97,8 +97,9 @@ void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val) switch (variable) { case HW_VAR_BSSID: - rtl92e_writel(dev, BSSIDR, ((u32 *)(val))[0]); - rtl92e_writew(dev, BSSIDR+2, ((u16 *)(val+2))[0]); + /* BSSIDR 2 byte alignment */ + rtl92e_writew(dev, BSSIDR, *(u16 *)val); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(val + 2)); break; case HW_VAR_MEDIA_STATUS: @@ -626,7 +627,7 @@ void rtl92e_get_eeprom_size(struct net_device *dev) struct r8192_priv *priv = rtllib_priv(dev); RT_TRACE(COMP_INIT, "===========>%s()\n", __func__); - curCR = rtl92e_readl(dev, EPROM_CMD); + curCR = rtl92e_readw(dev, EPROM_CMD); RT_TRACE(COMP_INIT, "read from Reg Cmd9346CR(%x):%x\n", EPROM_CMD, curCR); priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 : @@ -963,8 +964,8 @@ static void _rtl92e_net_update(struct net_device *dev) rtl92e_config_rate(dev, &rate_config); priv->dot11CurrentPreambleMode = PREAMBLE_AUTO; priv->basic_rate = rate_config &= 0x15f; - rtl92e_writel(dev, BSSIDR, ((u32 *)net->bssid)[0]); - rtl92e_writew(dev, BSSIDR+4, ((u16 *)net->bssid)[2]); + rtl92e_writew(dev, BSSIDR, *(u16 *)net->bssid); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(net->bssid + 2)); if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { rtl92e_writew(dev, ATIMWND, 2); diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c index c975c3b87093..cfc3017fd64a 100644 --- a/drivers/staging/vt6656/usbpipe.c +++ b/drivers/staging/vt6656/usbpipe.c @@ -50,15 +50,25 @@ int vnt_control_out(struct vnt_private *priv, u8 request, u16 value, u16 index, u16 length, u8 *buffer) { int status = 0; + u8 *usb_buffer; if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) return STATUS_FAILURE; mutex_lock(&priv->usb_lock); + usb_buffer = kmemdup(buffer, length, GFP_KERNEL); + if (!usb_buffer) { + mutex_unlock(&priv->usb_lock); + return -ENOMEM; + } + status = usb_control_msg(priv->usb, - usb_sndctrlpipe(priv->usb, 0), request, 0x40, value, - index, buffer, length, USB_CTL_WAIT); + usb_sndctrlpipe(priv->usb, 0), + request, 0x40, value, + index, usb_buffer, length, USB_CTL_WAIT); + + kfree(usb_buffer); mutex_unlock(&priv->usb_lock); @@ -78,15 +88,28 @@ int vnt_control_in(struct vnt_private *priv, u8 request, u16 value, u16 index, u16 length, u8 *buffer) { int status; + u8 *usb_buffer; if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) return STATUS_FAILURE; mutex_lock(&priv->usb_lock); + usb_buffer = kmalloc(length, GFP_KERNEL); + if (!usb_buffer) { + mutex_unlock(&priv->usb_lock); + return -ENOMEM; + } + status = usb_control_msg(priv->usb, - usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value, - index, buffer, length, USB_CTL_WAIT); + usb_rcvctrlpipe(priv->usb, 0), + request, 0xc0, value, + index, usb_buffer, length, USB_CTL_WAIT); + + if (status == length) + memcpy(buffer, usb_buffer, length); + + kfree(usb_buffer); mutex_unlock(&priv->usb_lock); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 6ed80b05d674..200d3de8bc1e 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4821,6 +4821,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) continue; } atomic_set(&sess->session_reinstatement, 1); + atomic_set(&sess->session_fall_back_to_erl0, 1); spin_unlock(&sess->conn_lock); list_move_tail(&se_sess->sess_list, &free_list); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index b4bfd706ac94..dc1bd1f1bdfe 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -725,11 +725,8 @@ static ssize_t lio_target_nacl_cmdsn_depth_store(struct config_item *item, if (iscsit_get_tpg(tpg) < 0) return -EINVAL; - /* - * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1 - */ - ret = iscsit_tpg_set_initiator_node_queue_depth(tpg, - config_item_name(acl_ci), cmdsn_depth, 1); + + ret = core_tpg_set_initiator_node_queue_depth(se_nacl, cmdsn_depth); pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for" "InitiatorName: %s\n", config_item_name(wwn_ci), @@ -1593,42 +1590,31 @@ static int lio_tpg_check_prot_fabric_only( } /* - * Called with spin_lock_irq(struct se_portal_group->session_lock) held - * or not held. - * - * Also, this function calls iscsit_inc_session_usage_count() on the + * This function calls iscsit_inc_session_usage_count() on the * struct iscsi_session in question. */ static int lio_tpg_shutdown_session(struct se_session *se_sess) { struct iscsi_session *sess = se_sess->fabric_sess_ptr; - struct se_portal_group *se_tpg = se_sess->se_tpg; - bool local_lock = false; - - if (!spin_is_locked(&se_tpg->session_lock)) { - spin_lock_irq(&se_tpg->session_lock); - local_lock = true; - } + struct se_portal_group *se_tpg = &sess->tpg->tpg_se_tpg; + spin_lock_bh(&se_tpg->session_lock); spin_lock(&sess->conn_lock); if (atomic_read(&sess->session_fall_back_to_erl0) || atomic_read(&sess->session_logout) || (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { spin_unlock(&sess->conn_lock); - if (local_lock) - spin_unlock_irq(&sess->conn_lock); + spin_unlock_bh(&se_tpg->session_lock); return 0; } atomic_set(&sess->session_reinstatement, 1); + atomic_set(&sess->session_fall_back_to_erl0, 1); spin_unlock(&sess->conn_lock); iscsit_stop_time2retain_timer(sess); - spin_unlock_irq(&se_tpg->session_lock); + spin_unlock_bh(&se_tpg->session_lock); iscsit_stop_session(sess, 1, 1); - if (!local_lock) - spin_lock_irq(&se_tpg->session_lock); - return 1; } diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 316f66172335..4a137b0ae3dc 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -195,6 +195,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) initiatorname_param->value) && (sess_p->sess_ops->SessionType == sessiontype))) { atomic_set(&sess_p->session_reinstatement, 1); + atomic_set(&sess_p->session_fall_back_to_erl0, 1); spin_unlock(&sess_p->conn_lock); iscsit_inc_session_usage_count(sess_p); iscsit_stop_time2retain_timer(sess_p); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 68261b7dcefe..205a509b0dfb 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -589,16 +589,6 @@ int iscsit_tpg_del_network_portal( return iscsit_tpg_release_np(tpg_np, tpg, np); } -int iscsit_tpg_set_initiator_node_queue_depth( - struct iscsi_portal_group *tpg, - unsigned char *initiatorname, - u32 queue_depth, - int force) -{ - return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg, - initiatorname, queue_depth, force); -} - int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication) { unsigned char buf1[256], buf2[256], *none = NULL; diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 9db32bd24cd4..2da211920c18 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -26,8 +26,6 @@ extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_gr int); extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, struct iscsi_tpg_np *); -extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *, - unsigned char *, u32, int); extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32); extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32); extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32); diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 79291869bce6..041a56987845 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -594,8 +594,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, if (ret < 0) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - if (ret) - target_complete_cmd(cmd, SAM_STAT_GOOD); + target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 90c5dffc9fa4..608117819366 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -498,8 +498,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes * been failed with a non-zero SCSI status. */ if (cmd->scsi_status) { - pr_err("compare_and_write_callback: non zero scsi_status:" + pr_debug("compare_and_write_callback: non zero scsi_status:" " 0x%02x\n", cmd->scsi_status); + *post_ret = 1; + if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION) + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out; } diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 2794c6ec5c3c..899c33b3c734 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -169,28 +169,25 @@ void core_tpg_add_node_to_devs( mutex_unlock(&tpg->tpg_lun_mutex); } -/* core_set_queue_depth_for_node(): - * - * - */ -static int core_set_queue_depth_for_node( - struct se_portal_group *tpg, - struct se_node_acl *acl) +static void +target_set_nacl_queue_depth(struct se_portal_group *tpg, + struct se_node_acl *acl, u32 queue_depth) { + acl->queue_depth = queue_depth; + if (!acl->queue_depth) { - pr_err("Queue depth for %s Initiator Node: %s is 0," + pr_warn("Queue depth for %s Initiator Node: %s is 0," "defaulting to 1.\n", tpg->se_tpg_tfo->get_fabric_name(), acl->initiatorname); acl->queue_depth = 1; } - - return 0; } static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg, const unsigned char *initiatorname) { struct se_node_acl *acl; + u32 queue_depth; acl = kzalloc(max(sizeof(*acl), tpg->se_tpg_tfo->node_acl_size), GFP_KERNEL); @@ -205,24 +202,20 @@ static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg, spin_lock_init(&acl->nacl_sess_lock); mutex_init(&acl->lun_entry_mutex); atomic_set(&acl->acl_pr_ref_count, 0); + if (tpg->se_tpg_tfo->tpg_get_default_depth) - acl->queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg); + queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg); else - acl->queue_depth = 1; + queue_depth = 1; + target_set_nacl_queue_depth(tpg, acl, queue_depth); + snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname); acl->se_tpg = tpg; acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX); tpg->se_tpg_tfo->set_default_node_attributes(acl); - if (core_set_queue_depth_for_node(tpg, acl) < 0) - goto out_free_acl; - return acl; - -out_free_acl: - kfree(acl); - return NULL; } static void target_add_node_acl(struct se_node_acl *acl) @@ -369,7 +362,8 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl) if (sess->sess_tearing_down != 0) continue; - target_get_session(sess); + if (!target_get_session(sess)) + continue; list_move(&sess->sess_acl_list, &sess_list); } spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); @@ -406,108 +400,52 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl) * */ int core_tpg_set_initiator_node_queue_depth( - struct se_portal_group *tpg, - unsigned char *initiatorname, - u32 queue_depth, - int force) + struct se_node_acl *acl, + u32 queue_depth) { - struct se_session *sess, *init_sess = NULL; - struct se_node_acl *acl; + LIST_HEAD(sess_list); + struct se_portal_group *tpg = acl->se_tpg; + struct se_session *sess, *sess_tmp; unsigned long flags; - int dynamic_acl = 0; - - mutex_lock(&tpg->acl_node_mutex); - acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); - if (!acl) { - pr_err("Access Control List entry for %s Initiator" - " Node %s does not exists for TPG %hu, ignoring" - " request.\n", tpg->se_tpg_tfo->get_fabric_name(), - initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg)); - mutex_unlock(&tpg->acl_node_mutex); - return -ENODEV; - } - if (acl->dynamic_node_acl) { - acl->dynamic_node_acl = 0; - dynamic_acl = 1; - } - mutex_unlock(&tpg->acl_node_mutex); - - spin_lock_irqsave(&tpg->session_lock, flags); - list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) { - if (sess->se_node_acl != acl) - continue; - - if (!force) { - pr_err("Unable to change queue depth for %s" - " Initiator Node: %s while session is" - " operational. To forcefully change the queue" - " depth and force session reinstatement" - " use the \"force=1\" parameter.\n", - tpg->se_tpg_tfo->get_fabric_name(), initiatorname); - spin_unlock_irqrestore(&tpg->session_lock, flags); - - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return -EEXIST; - } - /* - * Determine if the session needs to be closed by our context. - */ - if (!tpg->se_tpg_tfo->shutdown_session(sess)) - continue; - - init_sess = sess; - break; - } + int rc; /* * User has requested to change the queue depth for a Initiator Node. * Change the value in the Node's struct se_node_acl, and call - * core_set_queue_depth_for_node() to add the requested queue depth. - * - * Finally call tpg->se_tpg_tfo->close_session() to force session - * reinstatement to occur if there is an active session for the - * $FABRIC_MOD Initiator Node in question. + * target_set_nacl_queue_depth() to set the new queue depth. */ - acl->queue_depth = queue_depth; + target_set_nacl_queue_depth(tpg, acl, queue_depth); + + spin_lock_irqsave(&acl->nacl_sess_lock, flags); + list_for_each_entry_safe(sess, sess_tmp, &acl->acl_sess_list, + sess_acl_list) { + if (sess->sess_tearing_down != 0) + continue; + if (!target_get_session(sess)) + continue; + spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); - if (core_set_queue_depth_for_node(tpg, acl) < 0) { - spin_unlock_irqrestore(&tpg->session_lock, flags); /* - * Force session reinstatement if - * core_set_queue_depth_for_node() failed, because we assume - * the $FABRIC_MOD has already the set session reinstatement - * bit from tpg->se_tpg_tfo->shutdown_session() called above. + * Finally call tpg->se_tpg_tfo->close_session() to force session + * reinstatement to occur if there is an active session for the + * $FABRIC_MOD Initiator Node in question. */ - if (init_sess) - tpg->se_tpg_tfo->close_session(init_sess); - - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return -EINVAL; + rc = tpg->se_tpg_tfo->shutdown_session(sess); + target_put_session(sess); + if (!rc) { + spin_lock_irqsave(&acl->nacl_sess_lock, flags); + continue; + } + target_put_session(sess); + spin_lock_irqsave(&acl->nacl_sess_lock, flags); } - spin_unlock_irqrestore(&tpg->session_lock, flags); - /* - * If the $FABRIC_MOD session for the Initiator Node ACL exists, - * forcefully shutdown the $FABRIC_MOD session/nexus. - */ - if (init_sess) - tpg->se_tpg_tfo->close_session(init_sess); + spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); pr_debug("Successfully changed queue depth to: %d for Initiator" - " Node: %s on %s Target Portal Group: %u\n", queue_depth, - initiatorname, tpg->se_tpg_tfo->get_fabric_name(), + " Node: %s on %s Target Portal Group: %u\n", acl->queue_depth, + acl->initiatorname, tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg)); - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return 0; } EXPORT_SYMBOL(core_tpg_set_initiator_node_queue_depth); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index df2059984e14..af301414a9f3 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -383,9 +383,9 @@ static void target_release_session(struct kref *kref) se_tpg->se_tpg_tfo->close_session(se_sess); } -void target_get_session(struct se_session *se_sess) +int target_get_session(struct se_session *se_sess) { - kref_get(&se_sess->sess_kref); + return kref_get_unless_zero(&se_sess->sess_kref); } EXPORT_SYMBOL(target_get_session); 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/tty/pty.c b/drivers/tty/pty.c index 807d80145686..96aa0ad32497 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -216,16 +216,11 @@ static int pty_signal(struct tty_struct *tty, int sig) static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - struct tty_ldisc *ld; if (!to) return; - ld = tty_ldisc_ref(to); - tty_buffer_flush(to, ld); - if (ld) - tty_ldisc_deref(ld); - + tty_buffer_flush(to, NULL); if (to->packet) { spin_lock_irq(&tty->ctrl_lock); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 24280d9a05e9..de1c143b475f 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1712,7 +1712,8 @@ static int serial_omap_probe(struct platform_device *pdev) return 0; err_add_port: - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(up->dev, false); @@ -1725,9 +1726,13 @@ static int serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); + pm_runtime_get_sync(up->dev); + + uart_remove_one_port(&serial_omap_reg, &up->port); + + pm_runtime_dont_use_autosuspend(up->dev); pm_runtime_put_sync(up->dev); pm_runtime_disable(up->dev); - uart_remove_one_port(&serial_omap_reg, &up->port); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(&dev->dev, false); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index cad76b1cf672..df642496f989 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -900,14 +900,13 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) return -ENOMEM; } - dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf, + dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf, dma->rx_size, DMA_FROM_DEVICE); spin_lock_irqsave(&p->port.lock, flags); /* TX buffer */ - dma->tx_addr = dma_map_single(dma->tx_chan->device->dev, - p->port.state->xmit.buf, + dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); spin_unlock_irqrestore(&p->port.lock, flags); @@ -921,7 +920,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->rx_chan) { dmaengine_terminate_all(dma->rx_chan); - dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr, + dma_unmap_single(p->port.dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); kfree(dma->rx_buf); dma_release_channel(dma->rx_chan); @@ -930,7 +929,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->tx_chan) { dmaengine_terminate_all(dma->tx_chan); - dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr, + dma_unmap_single(p->port.dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(dma->tx_chan); dma->tx_chan = NULL; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 96849e2e7435..0b7194086c5a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -311,6 +311,12 @@ static void acm_ctrl_irq(struct urb *urb) break; case USB_CDC_NOTIFY_SERIAL_STATE: + if (le16_to_cpu(dr->wLength) != 2) { + dev_dbg(&acm->control->dev, + "%s - malformed serial state\n", __func__); + break; + } + newctrl = get_unaligned_le16(data); if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { @@ -347,11 +353,10 @@ static void acm_ctrl_irq(struct urb *urb) default: dev_dbg(&acm->control->dev, - "%s - unknown notification %d received: index %d " - "len %d data0 %d data1 %d\n", + "%s - unknown notification %d received: index %d len %d\n", __func__, - dr->bNotificationType, dr->wIndex, - dr->wLength, data[0], data[1]); + dr->bNotificationType, dr->wIndex, dr->wLength); + break; } exit: diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 26a305f43ff6..ee33c0d796b5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1328,6 +1328,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) */ if (udev->parent && !PMSG_IS_AUTO(msg)) status = 0; + + /* + * If the device is inaccessible, don't try to resume + * suspended interfaces and just return the error. + */ + if (status && status != -EBUSY) { + int err; + u16 devstat; + + err = usb_get_status(udev, USB_RECIP_DEVICE, 0, + &devstat); + if (err) { + dev_err(&udev->dev, + "Failed to suspend device, error %d\n", + status); + goto done; + } + } } /* If the suspend failed, resume interfaces that did get suspended */ @@ -1772,6 +1790,9 @@ static int autosuspend_check(struct usb_device *udev) int w, i; struct usb_interface *intf; + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + /* Fail if autosuspend is disabled, or any interfaces are in use, or * any interface drivers require remote wakeup but it isn't available. */ diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index ea337a718cc1..b3de806085f0 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -26,6 +26,7 @@ #define MAX_USB_MINORS 256 static const struct file_operations *usb_minors[MAX_USB_MINORS]; static DECLARE_RWSEM(minor_rwsem); +static DEFINE_MUTEX(init_usb_class_mutex); static int usb_open(struct inode *inode, struct file *file) { @@ -108,8 +109,9 @@ static void release_usb_class(struct kref *kref) static void destroy_usb_class(void) { - if (usb_class) - kref_put(&usb_class->kref, release_usb_class); + mutex_lock(&init_usb_class_mutex); + kref_put(&usb_class->kref, release_usb_class); + mutex_unlock(&init_usb_class_mutex); } int usb_major_init(void) @@ -171,7 +173,10 @@ int usb_register_dev(struct usb_interface *intf, if (intf->minor >= 0) return -EADDRINUSE; + mutex_lock(&init_usb_class_mutex); retval = init_usb_class(); + mutex_unlock(&init_usb_class_mutex); + if (retval) return retval; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 87912ead87b7..a4efaecf85ef 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -363,7 +363,8 @@ static void usb_set_lpm_parameters(struct usb_device *udev) } /* USB 2.0 spec Section 11.24.4.5 */ -static int get_hub_descriptor(struct usb_device *hdev, void *data) +static int get_hub_descriptor(struct usb_device *hdev, + struct usb_hub_descriptor *desc) { int i, ret, size; unsigned dtype; @@ -379,10 +380,18 @@ static int get_hub_descriptor(struct usb_device *hdev, void *data) for (i = 0; i < 3; i++) { ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - dtype << 8, 0, data, size, + dtype << 8, 0, desc, size, USB_CTRL_GET_TIMEOUT); - if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) + if (hub_is_superspeed(hdev)) { + if (ret == size) + return ret; + } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) { + /* Make sure we have the DeviceRemovable field. */ + size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1; + if (ret < size) + return -EMSGSIZE; return ret; + } } return -EINVAL; } @@ -1059,6 +1068,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) portstatus = portchange = 0; status = hub_port_status(hub, port1, &portstatus, &portchange); + if (status) + goto abort; + if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) dev_dbg(&port_dev->dev, "status %04x change %04x\n", portstatus, portchange); @@ -1191,7 +1203,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Scan all ports that need attention */ kick_hub_wq(hub); - + abort: if (type == HUB_INIT2 || type == HUB_INIT3) { /* Allow autosuspend if it was suppressed */ disconnected: @@ -1303,7 +1315,7 @@ static int hub_configure(struct usb_hub *hub, } mutex_init(&hub->status_mutex); - hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); + hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { ret = -ENOMEM; goto fail; @@ -1311,7 +1323,7 @@ static int hub_configure(struct usb_hub *hub, /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, - * but the hub can/will return fewer bytes here. + * but a (non-SS) hub can/will return fewer bytes here. */ ret = get_hub_descriptor(hdev, hub->descriptor); if (ret < 0) { @@ -2079,6 +2091,12 @@ void usb_disconnect(struct usb_device **pdev) dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); + /* + * Ensure that the pm runtime code knows that the USB device + * is in the process of being disconnected. + */ + pm_runtime_barrier(&udev->dev); + usb_lock_device(udev); hub_disconnect_children(udev); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index e5d3a0bdf32a..a2c14bb5efa4 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -23,6 +23,7 @@ void acc_disconnect(void); static struct class *android_class; static struct device *android_device; static int index; +static int gadget_index; struct device *create_function_device(char *name) { @@ -1439,21 +1440,21 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; @@ -1613,23 +1614,28 @@ static int android_device_create(struct gadget_info *gi) { struct device_attribute **attrs; struct device_attribute *attr; + char str[10]; INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) - return PTR_ERR(android_device); + snprintf(str, sizeof(str), "android%d", gadget_index - 1); + pr_debug("Creating android device %s\n", str); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, str); + if (IS_ERR(gi->dev)) + return PTR_ERR(gi->dev); - dev_set_drvdata(android_device, gi); + dev_set_drvdata(gi->dev, gi); + if (gadget_index == 1) + android_device = gi->dev; attrs = android_usb_attributes; while ((attr = *attrs++)) { int err; - err = device_create_file(android_device, attr); + err = device_create_file(gi->dev, attr); if (err) { - device_destroy(android_device->class, - android_device->devt); + device_destroy(gi->dev->class, + gi->dev->devt); return err; } } @@ -1637,15 +1643,15 @@ static int android_device_create(struct gadget_info *gi) return 0; } -static void android_device_destroy(void) +static void android_device_destroy(struct device *dev) { struct device_attribute **attrs; struct device_attribute *attr; attrs = android_usb_attributes; while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); + device_remove_file(dev, attr); + device_destroy(dev->class, dev->devt); } #else static inline int android_device_create(struct gadget_info *gi) @@ -1653,7 +1659,7 @@ static inline int android_device_create(struct gadget_info *gi) return 0; } -static inline void android_device_destroy(void) +static inline void android_device_destroy(struct device *dev) { } #endif @@ -1705,6 +1711,8 @@ static struct config_group *gadgets_make( if (!gi->composite.gadget_driver.function) goto err; + gadget_index++; + pr_debug("Creating gadget index %d\n", gadget_index); if (android_device_create(gi) < 0) goto err; @@ -1719,8 +1727,14 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { + struct gadget_info *gi; + + gi = container_of(to_config_group(item), struct gadget_info, group); config_item_put(item); - android_device_destroy(); + if (gi->dev) { + android_device_destroy(gi->dev); + gi->dev = NULL; + } } static struct configfs_group_operations gadgets_ops = { 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/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 999433ae2d72..d8cc5fd39e85 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1012,6 +1012,10 @@ static void receive_file_work(struct work_struct *data) usb_ep_dequeue(dev->ep_out, read_req); break; } + if (read_req->status) { + r = read_req->status; + break; + } mutex_lock(&dev->read_mutex); if (dev->state == STATE_OFFLINE) { diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 82483599a882..35e0c046fdcc 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1711,7 +1711,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; - void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, + void *buf = dma_zalloc_coherent(dev, xhci->page_size, &dma, flags); if (!buf) goto fail_sp5; @@ -2768,7 +2768,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) | xhci->cmd_ring->cycle_state; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Setting command ring address to 0x%x", val); + "// Setting command ring address to 0x%016llx", val_64); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); xhci_dbg_cmd_ptrs(xhci); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index dd262f418140..30c4ae80c8f9 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -52,6 +52,7 @@ #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 #define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 +#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 static const char hcd_name[] = "xhci_hcd"; @@ -167,12 +168,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) { + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) { xhci->quirks |= XHCI_PME_STUCK_QUIRK; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) xhci->quirks |= XHCI_MISSING_CAS; if (pdev->vendor == PCI_VENDOR_ID_ETRON && diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1ddf882fb607..56a9cd62f2c4 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -154,7 +154,7 @@ static int xhci_plat_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENODEV; + return irq; /* Try to set 64-bit DMA first */ if (WARN_ON(!pdev->dev.dma_mask)) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 775690bed4c0..5e43fd881a9c 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -557,7 +557,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice); /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */ - info.speed = le16_to_cpu(dev->udev->speed); + info.speed = dev->udev->speed; info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber; info.report_size = dev->report_size; diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 4dd531ac5a7f..0ec9ee573ffa 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -317,9 +317,16 @@ static int tower_open (struct inode *inode, struct file *file) int subminor; int retval = 0; struct usb_interface *interface; - struct tower_reset_reply reset_reply; + struct tower_reset_reply *reset_reply; int result; + reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL); + + if (!reset_reply) { + retval = -ENOMEM; + goto exit; + } + nonseekable_open(inode, file); subminor = iminor(inode); @@ -364,8 +371,8 @@ static int tower_open (struct inode *inode, struct file *file) USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, - &reset_reply, - sizeof(reset_reply), + reset_reply, + sizeof(*reset_reply), 1000); if (result < 0) { dev_err(&dev->udev->dev, @@ -406,6 +413,7 @@ unlock_exit: mutex_unlock(&dev->lock); exit: + kfree(reset_reply); return retval; } @@ -808,7 +816,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; - struct tower_get_version_reply get_version_reply; + struct tower_get_version_reply *get_version_reply = NULL; int i; int retval = -ENOMEM; int result; @@ -898,6 +906,13 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; + get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL); + + if (!get_version_reply) { + retval = -ENOMEM; + goto error; + } + /* get the firmware version and log it */ result = usb_control_msg (udev, usb_rcvctrlpipe(udev, 0), @@ -905,18 +920,19 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, - &get_version_reply, - sizeof(get_version_reply), + get_version_reply, + sizeof(*get_version_reply), 1000); if (result < 0) { dev_err(idev, "LEGO USB Tower get version control request failed\n"); retval = result; goto error; } - dev_info(&interface->dev, "LEGO USB Tower firmware version is %d.%d " - "build %d\n", get_version_reply.major, - get_version_reply.minor, - le16_to_cpu(get_version_reply.build_no)); + dev_info(&interface->dev, + "LEGO USB Tower firmware version is %d.%d build %d\n", + get_version_reply->major, + get_version_reply->minor, + le16_to_cpu(get_version_reply->build_no)); /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); @@ -937,9 +953,11 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device USB_MAJOR, dev->minor); exit: + kfree(get_version_reply); return retval; error: + kfree(get_version_reply); tower_delete(dev); return retval; } diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 1624b09d9748..2e947dc94e32 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -135,6 +135,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) case USB_ENDPOINT_XFER_INT: if (dev->info->intr) goto try_intr; + continue; case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) goto try_iso; diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 4c82077da475..6020024cb87c 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -220,6 +220,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, u32 dma_remaining; int src_burst, dst_burst; u16 csr; + u32 psize; int ch; s8 dmareq; s8 sync_dev; @@ -391,15 +392,19 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, if (chdat->tx) { /* Send transfer_packet_sz packets at a time */ - musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, - chdat->transfer_packet_sz); + psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); + psize &= ~0x7ff; + psize |= chdat->transfer_packet_sz; + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); musb_writel(ep_conf, TUSB_EP_TX_OFFSET, TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); } else { /* Receive transfer_packet_sz packets at a time */ - musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, - chdat->transfer_packet_sz << 16); + psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); + psize &= ~(0x7ff << 16); + psize |= (chdat->transfer_packet_sz << 16); + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); musb_writel(ep_conf, TUSB_EP_RX_OFFSET, TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 7a250c31f44d..03aeec2e878c 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -34,6 +34,10 @@ static bool usb_compliance_mode; module_param(usb_compliance_mode, bool, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(usb_compliance_mode, "Start USB stack for USB3.1 compliance testing"); +static bool disable_usb_pd; +module_param(disable_usb_pd, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(disable_usb_pd, "Disable USB PD for USB3.1 compliance testing"); + enum usbpd_state { PE_UNKNOWN, PE_ERROR_RECOVERY, @@ -920,7 +924,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; } - if (!val.intval) + if (!val.intval || disable_usb_pd) break; pd_reset_protocol(pd); @@ -2552,6 +2556,7 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, { struct usbpd *pd = dual_role_get_drvdata(dual_role); bool do_swap = false; + int wait_count = 5; if (!pd) return -ENODEV; @@ -2578,9 +2583,15 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, set_power_role(pd, PR_NONE); /* wait until it takes effect */ - while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) + while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE && + --wait_count) msleep(20); + if (!wait_count) { + usbpd_err(&pd->dev, "setting mode timed out\n"); + return -ETIMEDOUT; + } + break; case DUAL_ROLE_PROP_DR: diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b3a21fcbbaf9..e0385d6c0abb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -809,10 +809,10 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) }, { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), @@ -873,6 +873,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0x00) }, + { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -1507,9 +1508,9 @@ static int set_serial_info(struct tty_struct *tty, (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; +check_and_exit: write_latency_timer(port); -check_and_exit: if ((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) { if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 48ee04c94a75..4fcf1cecb6d7 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -873,9 +873,17 @@ #define FIC_VID 0x1457 #define FIC_NEO1973_DEBUG_PID 0x5118 +/* + * Actel / Microsemi + */ +#define ACTEL_VID 0x1514 +#define MICROSEMI_ARROW_SF2PLUS_BOARD_PID 0x2008 + /* Olimex */ #define OLIMEX_VID 0x15BA #define OLIMEX_ARM_USB_OCD_PID 0x0003 +#define OLIMEX_ARM_USB_TINY_PID 0x0004 +#define OLIMEX_ARM_USB_TINY_H_PID 0x002a #define OLIMEX_ARM_USB_OCD_H_PID 0x002b /* diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index f1a8fdcd8674..e98532feb0cc 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2349,8 +2349,11 @@ static void change_port_settings(struct tty_struct *tty, if (!baud) { /* pick a default, any default... */ baud = 9600; - } else + } else { + /* Avoid a zero divisor. */ + baud = min(baud, 461550); tty_encode_baud_rate(tty, baud, baud); + } edge_port->baud_rate = baud; config->wBaudRate = (__u16)((461550L + baud/2) / baud); diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 9bf82c262c5b..a6c07c6be25f 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -189,7 +189,7 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty, return -ENOMEM; divisor = mct_u232_calculate_baud_rate(serial, value, &speed); - put_unaligned_le32(cpu_to_le32(divisor), buf); + put_unaligned_le32(divisor, buf); rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCT_U232_SET_BAUD_RATE_REQUEST, MCT_U232_SET_REQUEST_TYPE, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index af67a0de6b5d..3bf61acfc26b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -281,6 +281,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_LE922_USBCFG0 0x1042 #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 +#define TELIT_PRODUCT_ME910 0x1100 #define TELIT_PRODUCT_LE920 0x1200 #define TELIT_PRODUCT_LE910 0x1201 #define TELIT_PRODUCT_LE910_USBCFG4 0x1206 @@ -640,6 +641,11 @@ static const struct option_blacklist_info simcom_sim7100e_blacklist = { .reserved = BIT(5) | BIT(6), }; +static const struct option_blacklist_info telit_me910_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(1) | BIT(3), +}; + static const struct option_blacklist_info telit_le910_blacklist = { .sendsetup = BIT(0), .reserved = BIT(1) | BIT(2), @@ -1235,6 +1241,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), + .driver_info = (kernel_ulong_t)&telit_me910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910), .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4), diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 38b3f0d8cd58..fd509ed6cf70 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -162,6 +162,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */ {DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */ {DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */ + {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */ + {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */ {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index f3cf4cecd2b7..091e8ec7a6c0 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -446,6 +446,10 @@ struct ms_lib_ctrl { #define SD_BLOCK_LEN 9 struct ene_ub6250_info { + + /* I/O bounce buffer */ + u8 *bbuf; + /* for 6250 code */ struct SD_STATUS SD_Status; struct MS_STATUS MS_Status; @@ -493,8 +497,11 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag); static void ene_ub6250_info_destructor(void *extra) { + struct ene_ub6250_info *info = (struct ene_ub6250_info *) extra; + if (!extra) return; + kfree(info->bbuf); } static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) @@ -858,8 +865,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, u8 PageNum, u32 *PageBuf, struct ms_lib_type_extdat *ExtraDat) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; int result; - u8 ExtBuf[4]; u32 bn = PhyBlockAddr * 0x20 + PageNum; /* printk(KERN_INFO "MS --- MS_ReaderReadPage, @@ -902,7 +910,7 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16); bcb->CDB[6] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -911,9 +919,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, ExtraDat->status0 = 0x10; /* Not yet,fireware support */ ExtraDat->status1 = 0x00; /* Not yet,fireware support */ - ExtraDat->ovrflg = ExtBuf[0]; - ExtraDat->mngflg = ExtBuf[1]; - ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]); + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); return USB_STOR_TRANSPORT_GOOD; } @@ -1339,8 +1347,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, u8 PageNum, struct ms_lib_type_extdat *ExtraDat) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; int result; - u8 ExtBuf[4]; /* printk("MS_LibReadExtra --- PhyBlock = %x, PageNum = %x\n", PhyBlock, PageNum); */ memset(bcb, 0, sizeof(struct bulk_cb_wrap)); @@ -1355,7 +1364,7 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, bcb->CDB[2] = (unsigned char)(PhyBlock>>16); bcb->CDB[6] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -1363,9 +1372,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, ExtraDat->intr = 0x80; /* Not yet, waiting for fireware support */ ExtraDat->status0 = 0x10; /* Not yet, waiting for fireware support */ ExtraDat->status1 = 0x00; /* Not yet, waiting for fireware support */ - ExtraDat->ovrflg = ExtBuf[0]; - ExtraDat->mngflg = ExtBuf[1]; - ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]); + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); return USB_STOR_TRANSPORT_GOOD; } @@ -1569,9 +1578,9 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st) u16 PhyBlock, newblk, i; u16 LogStart, LogEnde; struct ms_lib_type_extdat extdat; - u8 buf[0x200]; u32 count = 0, index = 0; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; for (PhyBlock = 0; PhyBlock < info->MS_Lib.NumberOfPhyBlock;) { ms_lib_phy_to_log_range(PhyBlock, &LogStart, &LogEnde); @@ -1585,14 +1594,16 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st) } if (count == PhyBlock) { - ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, &buf); + ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, + bbuf); count += 0x80; } index = (PhyBlock % 0x80) * 4; - extdat.ovrflg = buf[index]; - extdat.mngflg = buf[index+1]; - extdat.logadr = memstick_logaddr(buf[index+2], buf[index+3]); + extdat.ovrflg = bbuf[index]; + extdat.mngflg = bbuf[index+1]; + extdat.logadr = memstick_logaddr(bbuf[index+2], + bbuf[index+3]); if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) { ms_lib_setacquired_errorblock(us, PhyBlock); @@ -2075,9 +2086,9 @@ static int ene_ms_init(struct us_data *us) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; int result; - u8 buf[0x200]; u16 MSP_BlockSize, MSP_UserAreaBlocks; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; printk(KERN_INFO "transport --- ENE_MSInit\n"); @@ -2096,13 +2107,13 @@ static int ene_ms_init(struct us_data *us) bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) { printk(KERN_ERR "Execution MS Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } /* the same part to test ENE */ - info->MS_Status = *(struct MS_STATUS *)&buf[0]; + info->MS_Status = *(struct MS_STATUS *) bbuf; if (info->MS_Status.Insert && info->MS_Status.Ready) { printk(KERN_INFO "Insert = %x\n", info->MS_Status.Insert); @@ -2111,15 +2122,15 @@ static int ene_ms_init(struct us_data *us) printk(KERN_INFO "IsMSPHG = %x\n", info->MS_Status.IsMSPHG); printk(KERN_INFO "WtP= %x\n", info->MS_Status.WtP); if (info->MS_Status.IsMSPro) { - MSP_BlockSize = (buf[6] << 8) | buf[7]; - MSP_UserAreaBlocks = (buf[10] << 8) | buf[11]; + MSP_BlockSize = (bbuf[6] << 8) | bbuf[7]; + MSP_UserAreaBlocks = (bbuf[10] << 8) | bbuf[11]; info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks; } else { ms_card_init(us); /* Card is MS (to ms.c)*/ } usb_stor_dbg(us, "MS Init Code OK !!\n"); } else { - usb_stor_dbg(us, "MS Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "MS Card Not Ready --- %x\n", bbuf[0]); return USB_STOR_TRANSPORT_ERROR; } @@ -2129,9 +2140,9 @@ static int ene_ms_init(struct us_data *us) static int ene_sd_init(struct us_data *us) { int result; - u8 buf[0x200]; struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; usb_stor_dbg(us, "transport --- ENE_SDInit\n"); /* SD Init Part-1 */ @@ -2165,17 +2176,17 @@ static int ene_sd_init(struct us_data *us) bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; - result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) { usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } - info->SD_Status = *(struct SD_STATUS *)&buf[0]; + info->SD_Status = *(struct SD_STATUS *) bbuf; if (info->SD_Status.Insert && info->SD_Status.Ready) { struct SD_STATUS *s = &info->SD_Status; - ene_get_card_status(us, (unsigned char *)&buf); + ene_get_card_status(us, bbuf); usb_stor_dbg(us, "Insert = %x\n", s->Insert); usb_stor_dbg(us, "Ready = %x\n", s->Ready); usb_stor_dbg(us, "IsMMC = %x\n", s->IsMMC); @@ -2183,7 +2194,7 @@ static int ene_sd_init(struct us_data *us) usb_stor_dbg(us, "HiSpeed = %x\n", s->HiSpeed); usb_stor_dbg(us, "WtP = %x\n", s->WtP); } else { - usb_stor_dbg(us, "SD Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "SD Card Not Ready --- %x\n", bbuf[0]); return USB_STOR_TRANSPORT_ERROR; } return USB_STOR_TRANSPORT_GOOD; @@ -2193,13 +2204,15 @@ static int ene_sd_init(struct us_data *us) static int ene_init(struct us_data *us) { int result; - u8 misc_reg03 = 0; + u8 misc_reg03; struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + u8 *bbuf = info->bbuf; - result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03); + result = ene_get_card_type(us, REG_CARD_STATUS, bbuf); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; + misc_reg03 = bbuf[0]; if (misc_reg03 & 0x01) { if (!info->SD_Status.Ready) { result = ene_sd_init(us); @@ -2316,8 +2329,9 @@ static int ene_ub6250_probe(struct usb_interface *intf, const struct usb_device_id *id) { int result; - u8 misc_reg03 = 0; + u8 misc_reg03; struct us_data *us; + struct ene_ub6250_info *info; result = usb_stor_probe1(&us, intf, id, (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list, @@ -2326,11 +2340,16 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* FIXME: where should the code alloc extra buf ? */ - if (!us->extra) { - us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL); - if (!us->extra) - return -ENOMEM; - us->extra_destructor = ene_ub6250_info_destructor; + us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = ene_ub6250_info_destructor; + + info = (struct ene_ub6250_info *)(us->extra); + info->bbuf = kmalloc(512, GFP_KERNEL); + if (!info->bbuf) { + kfree(us->extra); + return -ENOMEM; } us->transport_name = "ene_ub6250"; @@ -2342,12 +2361,13 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* probe card type */ - result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03); + result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf); if (result != USB_STOR_XFER_GOOD) { usb_stor_disconnect(intf); return USB_STOR_TRANSPORT_ERROR; } + misc_reg03 = info->bbuf[0]; if (!(misc_reg03 & 0x01)) { pr_info("ums_eneub6250: This driver only supports SD/MS cards. " "It does not support SM cards.\n"); diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 6345e85822a4..a50cf45e530f 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -341,6 +341,7 @@ error_submit_ep1: static int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(iface); struct i1480_usb *i1480_usb; struct i1480 *i1480; struct device *dev = &iface->dev; @@ -352,8 +353,8 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) iface->cur_altsetting->desc.bInterfaceNumber); goto error; } - if (iface->num_altsetting > 1 - && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) { + if (iface->num_altsetting > 1 && + le16_to_cpu(udev->descriptor.idProduct) == 0xbabe) { /* Need altsetting #1 [HW QUIRK] or EP1 won't work */ result = usb_set_interface(interface_to_usbdev(iface), 0, 1); if (result < 0) diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index ecb826eefe02..2fa280671c1e 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -130,57 +130,34 @@ static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old) rb_erase(&old->node, &iommu->dma_list); } -struct vwork { - struct mm_struct *mm; - long npage; - struct work_struct work; -}; - -/* delayed decrement/increment for locked_vm */ -static void vfio_lock_acct_bg(struct work_struct *work) +static int vfio_lock_acct(long npage, bool *lock_cap) { - struct vwork *vwork = container_of(work, struct vwork, work); - struct mm_struct *mm; - - mm = vwork->mm; - down_write(&mm->mmap_sem); - mm->locked_vm += vwork->npage; - up_write(&mm->mmap_sem); - mmput(mm); - kfree(vwork); -} + int ret = 0; -static void vfio_lock_acct(long npage) -{ - struct vwork *vwork; - struct mm_struct *mm; + if (!npage) + return 0; - if (!current->mm || !npage) - return; /* process exited or nothing to do */ + if (!current->mm) + return -ESRCH; /* process exited */ - if (down_write_trylock(¤t->mm->mmap_sem)) { - current->mm->locked_vm += npage; - up_write(¤t->mm->mmap_sem); - return; - } + down_write(¤t->mm->mmap_sem); + if (npage > 0) { + if (lock_cap ? !*lock_cap : !capable(CAP_IPC_LOCK)) { + unsigned long limit; - /* - * Couldn't get mmap_sem lock, so must setup to update - * mm->locked_vm later. If locked_vm were atomic, we - * wouldn't need this silliness - */ - vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); - if (!vwork) - return; - mm = get_task_mm(current); - if (!mm) { - kfree(vwork); - return; + limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + if (current->mm->locked_vm + npage > limit) + ret = -ENOMEM; + } } - INIT_WORK(&vwork->work, vfio_lock_acct_bg); - vwork->mm = mm; - vwork->npage = npage; - schedule_work(&vwork->work); + + if (!ret) + current->mm->locked_vm += npage; + + up_write(¤t->mm->mmap_sem); + + return ret; } /* @@ -262,9 +239,9 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) static long vfio_pin_pages(unsigned long vaddr, long npage, int prot, unsigned long *pfn_base) { - unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + unsigned long pfn = 0, limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; bool lock_cap = capable(CAP_IPC_LOCK); - long ret, i; + long ret, i = 1; bool rsvd; if (!current->mm) @@ -283,16 +260,11 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, return -ENOMEM; } - if (unlikely(disable_hugepages)) { - if (!rsvd) - vfio_lock_acct(1); - return 1; - } + if (unlikely(disable_hugepages)) + goto out; /* Lock all the consecutive pages from pfn_base */ - for (i = 1, vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) { - unsigned long pfn = 0; - + for (vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) { ret = vaddr_get_pfn(vaddr, prot, &pfn); if (ret) break; @@ -308,12 +280,24 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, put_pfn(pfn, prot); pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, limit << PAGE_SHIFT); - break; + ret = -ENOMEM; + goto unpin_out; } } +out: if (!rsvd) - vfio_lock_acct(i); + ret = vfio_lock_acct(i, &lock_cap); + +unpin_out: + if (ret) { + if (!rsvd) { + for (pfn = *pfn_base ; i ; pfn++, i--) + put_pfn(pfn, prot); + } + + return ret; + } return i; } @@ -328,7 +312,7 @@ static long vfio_unpin_pages(unsigned long pfn, long npage, unlocked += put_pfn(pfn++, prot); if (do_accounting) - vfio_lock_acct(-unlocked); + vfio_lock_acct(-unlocked, NULL); return unlocked; } @@ -390,7 +374,7 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma) cond_resched(); } - vfio_lock_acct(-unlocked); + vfio_lock_acct(-unlocked, NULL); } static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) 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 4f1333426113..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__); @@ -3429,9 +3434,10 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) hw_vsync_handler, IRQF_TRIGGER_FALLING, "VSYNC_GPIO", ctrl_pdata); if (rc) { - pr_err("TE request_irq failed.\n"); + pr_err("%s: TE request_irq failed for ESD\n", __func__); goto error_shadow_clk_deinit; } + te_irq_registered = 1; disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); } 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_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index b448153df1df..c766ff983045 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -2275,6 +2275,7 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, bool ack_error = false; char reg[16] = {0x0}; int repeated_bytes = 0; + struct mdss_dsi_ctrl_pdata *mctrl = mdss_dsi_get_other_ctrl(ctrl); lp = (u32 *)rp->data; temp = (u32 *)reg; @@ -2335,7 +2336,11 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, off += ((cnt - 1) * 4); for (i = 0; i < cnt; i++) { - data = (u32)MIPI_INP((ctrl->ctrl_base) + off); + if (mdss_dsi_sync_wait_trigger(ctrl)) + data = (u32)MIPI_INP((mctrl->ctrl_base) + off); + else + data = (u32)MIPI_INP((ctrl->ctrl_base) + off); + /* to network byte order */ if (!repeated_bytes) *lp++ = ntohl(data); 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 3c679877705d..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); @@ -4987,9 +4987,10 @@ static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd, ret = mdss_fb_get_hw_caps(mfd, &metadata->data.caps); break; case metadata_op_get_ion_fd: - if (mfd->fb_ion_handle) { + if (mfd->fb_ion_handle && mfd->fb_ion_client) { metadata->data.fbmem_ionfd = - dma_buf_fd(mfd->fbmem_buf, 0); + ion_share_dma_buf_fd(mfd->fb_ion_client, + mfd->fb_ion_handle); if (metadata->data.fbmem_ionfd < 0) pr_err("fd allocation failed. fd = %d\n", metadata->data.fbmem_ionfd); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h index 809c389e99e8..136e2d79787c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.h +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-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 @@ -181,7 +181,7 @@ struct mdss_pp_res_type { struct mdp_hist_lut_data enhist_disp_cfg[MDSS_BLOCK_DISP_NUM]; struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM]; struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM]; - uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE]; + uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE * 3]; u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE]; struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM]; struct mdp_dither_cfg_data pa_dither_cfg[MDSS_BLOCK_DISP_NUM]; 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/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c index 63e178d76403..09632b49d33b 100644 --- a/drivers/video/fbdev/msm/msm_dba/adv7533.c +++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c @@ -517,13 +517,25 @@ static void adv7533_parse_vreg_dt(struct device *dev, } mp->vreg_config[i].disable_load = val_array[i]; - pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d\n", + /* post-on-sleep */ + memset(val_array, 0, sizeof(u32) * dt_vreg_total); + rc = of_property_read_u32_array(of_node, + "qcom,post-on-sleep", val_array, + dt_vreg_total); + if (rc) + pr_warn("%s: error read post on sleep. rc=%d\n", + __func__, rc); + else + mp->vreg_config[i].post_on_sleep = val_array[i]; + + pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d post-on-sleep=%d\n", __func__, mp->vreg_config[i].vreg_name, mp->vreg_config[i].min_voltage, mp->vreg_config[i].max_voltage, mp->vreg_config[i].enable_load, - mp->vreg_config[i].disable_load); + mp->vreg_config[i].disable_load, + mp->vreg_config[i].post_on_sleep); } devm_kfree(dev, val_array); 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/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 1a11aedc4fe8..9eb5b314ba06 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface, return -ENODEV; } + if (iface_desc->desc.bNumEndpoints < 1) + return -ENODEV; + /* check out the endpoint: it has to be Interrupt & IN */ endpoint = &iface_desc->endpoint[0].desc; diff --git a/fs/block_dev.c b/fs/block_dev.c index e5733bb537c9..26bbaaefdff4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -88,12 +88,11 @@ void invalidate_bdev(struct block_device *bdev) { struct address_space *mapping = bdev->bd_inode->i_mapping; - if (mapping->nrpages == 0) - return; - - invalidate_bh_lrus(); - lru_add_drain_all(); /* make sure all lru add caches are flushed */ - invalidate_mapping_pages(mapping, 0, -1); + if (mapping->nrpages) { + invalidate_bh_lrus(); + lru_add_drain_all(); /* make sure all lru add caches are flushed */ + invalidate_mapping_pages(mapping, 0, -1); + } /* 99% of the time, we don't need to flush the cleancache on the bdev. * But, for the strange corners, lets be cautious */ diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index 4d8caeb94a11..bdb9c94335f1 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -128,7 +128,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (new_mode != old_mode) { newattrs.ia_mode = new_mode; newattrs.ia_valid = ATTR_MODE; - ret = ceph_setattr(dentry, &newattrs); + ret = __ceph_setattr(dentry, &newattrs); if (ret) goto out_dput; } @@ -138,7 +138,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (new_mode != old_mode) { newattrs.ia_mode = old_mode; newattrs.ia_valid = ATTR_MODE; - ceph_setattr(dentry, &newattrs); + __ceph_setattr(dentry, &newattrs); } goto out_dput; } diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index d98536c8abfc..9f0d99094cc1 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1773,7 +1773,7 @@ static const struct inode_operations ceph_symlink_iops = { /* * setattr */ -int ceph_setattr(struct dentry *dentry, struct iattr *attr) +int __ceph_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); struct ceph_inode_info *ci = ceph_inode(inode); @@ -1975,11 +1975,6 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (inode_dirty_flags) __mark_inode_dirty(inode, inode_dirty_flags); - if (ia_valid & ATTR_MODE) { - err = posix_acl_chmod(inode, attr->ia_mode); - if (err) - goto out_put; - } if (mask) { req->r_inode = inode; @@ -1993,13 +1988,23 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) ceph_cap_string(dirtied), mask); ceph_mdsc_put_request(req); - if (mask & CEPH_SETATTR_SIZE) - __ceph_do_pending_vmtruncate(inode); ceph_free_cap_flush(prealloc_cf); + + if (err >= 0 && (mask & CEPH_SETATTR_SIZE)) + __ceph_do_pending_vmtruncate(inode); + return err; -out_put: - ceph_mdsc_put_request(req); - ceph_free_cap_flush(prealloc_cf); +} + +int ceph_setattr(struct dentry *dentry, struct iattr *attr) +{ + int err; + + err = __ceph_setattr(dentry, attr); + + if (err >= 0 && (attr->ia_valid & ATTR_MODE)) + err = posix_acl_chmod(d_inode(dentry), attr->ia_mode); + return err; } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 75b7d125ce66..8c8cb8fe3d32 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -788,6 +788,7 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force) return __ceph_do_getattr(inode, NULL, mask, force); } extern int ceph_permission(struct inode *inode, int mask); +extern int __ceph_setattr(struct dentry *dentry, struct iattr *attr); extern int ceph_setattr(struct dentry *dentry, struct iattr *attr); extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 819163d8313b..b24275ef97f7 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -369,6 +369,7 @@ static int __set_xattr(struct ceph_inode_info *ci, if (update_xattr) { int err = 0; + if (xattr && (flags & XATTR_CREATE)) err = -EEXIST; else if (!xattr && (flags & XATTR_REPLACE)) @@ -376,12 +377,14 @@ static int __set_xattr(struct ceph_inode_info *ci, if (err) { kfree(name); kfree(val); + kfree(*newxattr); return err; } if (update_xattr < 0) { if (xattr) __remove_xattr(ci, xattr); kfree(name); + kfree(*newxattr); return 0; } } diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 02b071bf3732..a0b3e7d1be48 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target) case SFM_COLON: *target = ':'; break; + case SFM_DOUBLEQUOTE: + *target = '"'; + break; case SFM_ASTERISK: *target = '*'; break; @@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string) case ':': dest_char = cpu_to_le16(SFM_COLON); break; + case '"': + dest_char = cpu_to_le16(SFM_DOUBLEQUOTE); + break; case '*': dest_char = cpu_to_le16(SFM_ASTERISK); break; diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 479bc0a941f3..07ade707fa60 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -57,6 +57,7 @@ * not conflict (although almost does) with the mapping above. */ +#define SFM_DOUBLEQUOTE ((__u16) 0xF020) #define SFM_ASTERISK ((__u16) 0xF021) #define SFM_QUESTION ((__u16) 0xF025) #define SFM_COLON ((__u16) 0xF022) @@ -64,8 +65,8 @@ #define SFM_LESSTHAN ((__u16) 0xF023) #define SFM_PIPE ((__u16) 0xF027) #define SFM_SLASH ((__u16) 0xF026) -#define SFM_PERIOD ((__u16) 0xF028) -#define SFM_SPACE ((__u16) 0xF029) +#define SFM_SPACE ((__u16) 0xF028) +#define SFM_PERIOD ((__u16) 0xF029) /* * Mapping mechanism to use when one of the seven reserved characters is diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5e2f8b8ca08a..b60150e5b5ce 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -717,6 +717,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) if (rc) return rc; + if (server->capabilities & CAP_UNICODE) + smb->hdr.Flags2 |= SMBFLG2_UNICODE; + /* set up echo request */ smb->hdr.Tid = 0xffff; smb->hdr.WordCount = 1; diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 35cf990f87d3..a8f5b31636dc 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -272,6 +272,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) rc = -EOPNOTSUPP; break; case CIFS_IOC_GET_MNT_INFO: + if (pSMBFile == NULL) + break; tcon = tlink_tcon(pSMBFile->tlink); rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); break; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 6cb2603f8a5c..f4afa3b1cc56 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -564,8 +564,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { - cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); - return -EIO; + cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n", + rsplen); + + /* relax check since Mac returns max bufsize allowed on ioctl */ + if (rsplen > CIFSMaxBufSize) + return -EIO; } /* check validate negotiate info response matches what we got earlier */ @@ -1518,8 +1522,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, * than one credit. Windows typically sets this smaller, but for some * ioctls it may be useful to allow server to send more. No point * limiting what the server can send as long as fits in one credit + * Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE + * (by default, note that it can be overridden to make max larger) + * in responses (except for read responses which can be bigger. + * We may want to bump this limit up */ - req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ + req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize); if (is_fsctl) req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index e14b1b8fceb0..b9f838af5a72 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -271,7 +271,7 @@ static int ext4_page_crypto(struct inode *inode, struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; - req = ablkcipher_request_alloc(tfm, GFP_NOFS); + req = ablkcipher_request_alloc(tfm, gfp_flags); if (!req) { printk_ratelimited(KERN_ERR "%s: crypto_request_alloc() failed\n", diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c index e2645ca9b95e..026716bdbbfc 100644 --- a/fs/ext4/crypto_fname.c +++ b/fs/ext4/crypto_fname.c @@ -344,7 +344,7 @@ int _ext4_fname_disk_to_usr(struct inode *inode, memcpy(buf+4, &hinfo->minor_hash, 4); } else memset(buf, 0, 8); - memcpy(buf + 8, iname->name + iname->len - 16, 16); + memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16); oname->name[0] = '_'; ret = digest_encode(buf, 24, oname->name+1); oname->len = ret + 1; diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c index dd561f916f0b..e4f4fc4e56ab 100644 --- a/fs/ext4/crypto_policy.c +++ b/fs/ext4/crypto_policy.c @@ -148,26 +148,38 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy) int ext4_is_child_context_consistent_with_parent(struct inode *parent, struct inode *child) { - struct ext4_crypt_info *parent_ci, *child_ci; + const struct ext4_crypt_info *parent_ci, *child_ci; + struct ext4_encryption_context parent_ctx, child_ctx; int res; - if ((parent == NULL) || (child == NULL)) { - pr_err("parent %p child %p\n", parent, child); - WARN_ON(1); /* Should never happen */ - return 0; - } - /* No restrictions on file types which are never encrypted */ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && !S_ISLNK(child->i_mode)) return 1; - /* no restrictions if the parent directory is not encrypted */ + /* No restrictions if the parent directory is unencrypted */ if (!ext4_encrypted_inode(parent)) return 1; - /* if the child directory is not encrypted, this is always a problem */ + + /* Encrypted directories must not contain unencrypted files */ if (!ext4_encrypted_inode(child)) return 0; + + /* + * Both parent and child are encrypted, so verify they use the same + * encryption policy. Compare the fscrypt_info structs if the keys are + * available, otherwise retrieve and compare the fscrypt_contexts. + * + * Note that the fscrypt_context retrieval will be required frequently + * when accessing an encrypted directory tree without the key. + * Performance-wise this is not a big deal because we already don't + * really optimize for file access without the key (to the extent that + * such access is even possible), given that any attempted access + * already causes a fscrypt_context retrieval and keyring search. + * + * In any case, if an unexpected error occurs, fall back to "forbidden". + */ + res = ext4_get_encryption_info(parent); if (res) return 0; @@ -176,17 +188,35 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent, return 0; parent_ci = EXT4_I(parent)->i_crypt_info; child_ci = EXT4_I(child)->i_crypt_info; - if (!parent_ci && !child_ci) - return 1; - if (!parent_ci || !child_ci) + if (parent_ci && child_ci) { + return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key, + EXT4_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == + child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags); + } + + res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION, + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, + &parent_ctx, sizeof(parent_ctx)); + if (res != sizeof(parent_ctx)) + return 0; + + res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION, + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, + &child_ctx, sizeof(child_ctx)); + if (res != sizeof(child_ctx)) return 0; - return (memcmp(parent_ci->ci_master_key, - child_ci->ci_master_key, - EXT4_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ci->ci_data_mode == child_ci->ci_data_mode) && - (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && - (parent_ci->ci_flags == child_ci->ci_flags)); + return memcmp(parent_ctx.master_key_descriptor, + child_ctx.master_key_descriptor, + EXT4_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ctx.contents_encryption_mode == + child_ctx.contents_encryption_mode) && + (parent_ctx.filenames_encryption_mode == + child_ctx.filenames_encryption_mode) && + (parent_ctx.flags == child_ctx.flags); } /** diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 4caa0c1f77d8..2892a799f6f8 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5461,6 +5461,11 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) file_update_time(vma->vm_file); down_read(&EXT4_I(inode)->i_mmap_sem); + + ret = ext4_convert_inline_data(inode); + if (ret) + goto out_ret; + /* Delalloc case is easy... */ if (test_opt(inode->i_sb, DELALLOC) && !ext4_should_journal_data(inode) && diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index b936da9d3d0c..8a196e9b0bf3 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1243,9 +1243,9 @@ static inline int ext4_match(struct ext4_filename *fname, if (unlikely(!name)) { if (fname->usr_fname->name[0] == '_') { int ret; - if (de->name_len < 16) + if (de->name_len <= 32) return 0; - ret = memcmp(de->name + de->name_len - 16, + ret = memcmp(de->name + ((de->name_len - 17) & ~15), fname->crypto_buf.name + 8, 16); return (ret == 0) ? 1 : 0; } diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 1a6a8ca4de3a..978141e8b800 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -497,7 +497,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, if (IS_ERR(data_page)) { ret = PTR_ERR(data_page); - if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { + if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { if (io->io_bio) { ext4_io_submit(io); congestion_wait(BLK_RW_ASYNC, HZ/50); diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c index ab377d496a39..38349ed5ea51 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/f2fs/crypto_fname.c @@ -333,7 +333,7 @@ int f2fs_fname_disk_to_usr(struct inode *inode, memset(buf + 4, 0, 4); } else memset(buf, 0, 8); - memcpy(buf + 8, iname->name + iname->len - 16, 16); + memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16); oname->name[0] = '_'; ret = digest_encode(buf, 24, oname->name + 1); oname->len = ret + 1; diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c index 5bbd1989d5e6..884f3f0fe29d 100644 --- a/fs/f2fs/crypto_policy.c +++ b/fs/f2fs/crypto_policy.c @@ -141,25 +141,38 @@ int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy) int f2fs_is_child_context_consistent_with_parent(struct inode *parent, struct inode *child) { - struct f2fs_crypt_info *parent_ci, *child_ci; + const struct f2fs_crypt_info *parent_ci, *child_ci; + struct f2fs_encryption_context parent_ctx, child_ctx; int res; - if ((parent == NULL) || (child == NULL)) { - pr_err("parent %p child %p\n", parent, child); - BUG_ON(1); - } - /* No restrictions on file types which are never encrypted */ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && !S_ISLNK(child->i_mode)) return 1; - /* no restrictions if the parent directory is not encrypted */ + /* No restrictions if the parent directory is unencrypted */ if (!f2fs_encrypted_inode(parent)) return 1; - /* if the child directory is not encrypted, this is always a problem */ + + /* Encrypted directories must not contain unencrypted files */ if (!f2fs_encrypted_inode(child)) return 0; + + /* + * Both parent and child are encrypted, so verify they use the same + * encryption policy. Compare the fscrypt_info structs if the keys are + * available, otherwise retrieve and compare the fscrypt_contexts. + * + * Note that the fscrypt_context retrieval will be required frequently + * when accessing an encrypted directory tree without the key. + * Performance-wise this is not a big deal because we already don't + * really optimize for file access without the key (to the extent that + * such access is even possible), given that any attempted access + * already causes a fscrypt_context retrieval and keyring search. + * + * In any case, if an unexpected error occurs, fall back to "forbidden". + */ + res = f2fs_get_encryption_info(parent); if (res) return 0; @@ -168,17 +181,35 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent, return 0; parent_ci = F2FS_I(parent)->i_crypt_info; child_ci = F2FS_I(child)->i_crypt_info; - if (!parent_ci && !child_ci) - return 1; - if (!parent_ci || !child_ci) + if (parent_ci && child_ci) { + return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == + child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags); + } + + res = f2fs_getxattr(parent, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &parent_ctx, sizeof(parent_ctx), NULL); + if (res != sizeof(parent_ctx)) + return 0; + + res = f2fs_getxattr(child, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &child_ctx, sizeof(child_ctx), NULL); + if (res != sizeof(child_ctx)) return 0; - return (memcmp(parent_ci->ci_master_key, - child_ci->ci_master_key, - F2FS_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ci->ci_data_mode == child_ci->ci_data_mode) && - (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && - (parent_ci->ci_flags == child_ci->ci_flags)); + return memcmp(parent_ctx.master_key_descriptor, + child_ctx.master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ctx.contents_encryption_mode == + child_ctx.contents_encryption_mode) && + (parent_ctx.filenames_encryption_mode == + child_ctx.filenames_encryption_mode) && + (parent_ctx.flags == child_ctx.flags); } /** diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 7c1678ba8f92..60972a559685 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -124,19 +124,29 @@ struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname, de = &d->dentry[bit_pos]; - /* encrypted case */ + if (de->hash_code != namehash) + goto not_match; + de_name.name = d->filename[bit_pos]; de_name.len = le16_to_cpu(de->name_len); - /* show encrypted name */ - if (fname->hash) { - if (de->hash_code == fname->hash) - goto found; - } else if (de_name.len == name->len && - de->hash_code == namehash && - !memcmp(de_name.name, name->name, name->len)) +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (unlikely(!name->name)) { + if (fname->usr_fname->name[0] == '_') { + if (de_name.len > 32 && + !memcmp(de_name.name + ((de_name.len - 17) & ~15), + fname->crypto_buf.name + 8, 16)) + goto found; + goto not_match; + } + name->name = fname->crypto_buf.name; + name->len = fname->crypto_buf.len; + } +#endif + if (de_name.len == name->len && + !memcmp(de_name.name, name->name, name->len)) goto found; - +not_match: if (max_slots && max_len > *max_slots) *max_slots = max_len; max_len = 0; @@ -170,7 +180,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, int max_slots; f2fs_hash_t namehash; - namehash = f2fs_dentry_hash(&name); + namehash = f2fs_dentry_hash(&name, fname); f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); @@ -547,7 +557,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, level = 0; slots = GET_DENTRY_SLOTS(new_name.len); - dentry_hash = f2fs_dentry_hash(&new_name); + dentry_hash = f2fs_dentry_hash(&new_name, NULL); current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3c7594b9d109..9dfbfe6dc775 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1723,7 +1723,8 @@ void f2fs_msg(struct super_block *, const char *, const char *, ...); /* * hash.c */ -f2fs_hash_t f2fs_dentry_hash(const struct qstr *); +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info, + struct f2fs_filename *fname); /* * node.c diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index 71b7206c431e..b238d2fec3e5 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len, *buf++ = pad; } -f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info, + struct f2fs_filename *fname) { __u32 hash; f2fs_hash_t f2fs_hash; @@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) const unsigned char *name = name_info->name; size_t len = name_info->len; + /* encrypted bigname case */ + if (fname && !fname->disk_name.name) + return cpu_to_le32(fname->hash); + if (is_dot_dotdot(name_info)) return 0; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index dbb2cc4df603..f35f3eb3541f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -321,7 +321,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, if (IS_ERR(ipage)) return NULL; - namehash = f2fs_dentry_hash(&name); + namehash = f2fs_dentry_hash(&name, fname); inline_dentry = inline_data_addr(ipage); @@ -486,7 +486,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, f2fs_wait_on_page_writeback(ipage, NODE); - name_hash = f2fs_dentry_hash(name); + name_hash = f2fs_dentry_hash(name, NULL); make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 12935209deca..c3e1cb481fe0 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4041,8 +4041,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getdeviceinfo *gdev) { struct xdr_stream *xdr = &resp->xdr; - const struct nfsd4_layout_ops *ops = - nfsd4_layout_ops[gdev->gd_layout_type]; + const struct nfsd4_layout_ops *ops; u32 starting_len = xdr->buf->len, needed_len; __be32 *p; @@ -4059,6 +4058,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr, /* If maxcount is 0 then just update notifications */ if (gdev->gd_maxcount != 0) { + ops = nfsd4_layout_ops[gdev->gd_layout_type]; nfserr = ops->encode_getdeviceinfo(xdr, gdev); if (nfserr) { /* @@ -4111,8 +4111,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_layoutget *lgp) { struct xdr_stream *xdr = &resp->xdr; - const struct nfsd4_layout_ops *ops = - nfsd4_layout_ops[lgp->lg_layout_type]; + const struct nfsd4_layout_ops *ops; __be32 *p; dprintk("%s: err %d\n", __func__, nfserr); @@ -4135,6 +4134,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr, *p++ = cpu_to_be32(lgp->lg_seg.iomode); *p++ = cpu_to_be32(lgp->lg_layout_type); + ops = nfsd4_layout_ops[lgp->lg_layout_type]; nfserr = ops->encode_layoutget(xdr, lgp); out: kfree(lgp->lg_content); diff --git a/fs/proc/generic.c b/fs/proc/generic.c index ff3ffc76a937..3773335791da 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -469,6 +469,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name) ent->data = NULL; ent->proc_fops = NULL; ent->proc_iops = NULL; + parent->nlink++; if (proc_register(parent, ent) < 0) { kfree(ent); parent->nlink--; diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 7a19e77fce99..13da7e5245bd 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -34,6 +34,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) struct dentry *parent_lower_dentry = NULL; struct dentry *lower_cur_parent_dentry = NULL; struct dentry *lower_dentry = NULL; + struct inode *inode; + struct sdcardfs_inode_data *data; if (flags & LOOKUP_RCU) return -ECHILD; @@ -103,6 +105,21 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); spin_unlock(&lower_dentry->d_lock); } + if (!err) + goto out; + + /* If our top's inode is gone, we may be out of date */ + inode = igrab(d_inode(dentry)); + if (inode) { + data = top_data_get(SDCARDFS_I(inode)); + if (!data || data->abandoned) { + d_drop(dentry); + err = 0; + } + if (data) + data_put(data); + iput(inode); + } out: dput(parent_dentry); diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index b4595aab5713..85a60fb5ff39 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -26,28 +26,28 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); struct sdcardfs_inode_info *ci = SDCARDFS_I(child); - ci->perm = PERM_INHERIT; - ci->userid = pi->userid; - ci->d_uid = pi->d_uid; - ci->under_android = pi->under_android; - ci->under_cache = pi->under_cache; - ci->under_obb = pi->under_obb; - set_top(ci, pi->top); + ci->data->perm = PERM_INHERIT; + ci->data->userid = pi->data->userid; + ci->data->d_uid = pi->data->d_uid; + ci->data->under_android = pi->data->under_android; + ci->data->under_cache = pi->data->under_cache; + ci->data->under_obb = pi->data->under_obb; + set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct inode *top) + uid_t uid, bool under_android, + struct sdcardfs_inode_data *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - info->perm = perm; - info->userid = userid; - info->d_uid = uid; - info->under_android = under_android; - info->under_cache = false; - info->under_obb = false; + info->data->perm = perm; + info->data->userid = userid; + info->data->d_uid = uid; + info->data->under_android = under_android; + info->data->under_cache = false; + info->data->under_obb = false; set_top(info, top); } @@ -58,7 +58,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_data *parent_data = + SDCARDFS_I(d_inode(parent))->data; appid_t appid; unsigned long user_num; int err; @@ -82,60 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (!S_ISDIR(d_inode(dentry)->i_mode)) return; /* Derive custom permissions based on parent and current node */ - switch (parent_info->perm) { + switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: /* Already inherited above */ break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; + info->data->perm = PERM_ROOT; err = kstrtoul(name->name, 10, &user_num); if (err) - info->userid = 0; + info->data->userid = 0; else - info->userid = user_num; - set_top(info, &info->vfs_inode); + info->data->userid = user_num; + set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID; + info->data->under_android = true; + set_top(info, info->data); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_DATA; + set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - info->under_obb = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_OBB; + info->data->under_obb = true; + set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_MEDIA; + set_top(info, info->data); } break; case PERM_ANDROID_OBB: case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: - info->perm = PERM_ANDROID_PACKAGE; + info->data->perm = PERM_ANDROID_PACKAGE; appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - set_top(info, &info->vfs_inode); + if (appid != 0 && !is_excluded(name->name, parent_data->userid)) + info->data->d_uid = + multiuser_get_uid(parent_data->userid, appid); + set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { - info->perm = PERM_ANDROID_PACKAGE_CACHE; - info->under_cache = true; + info->data->perm = PERM_ANDROID_PACKAGE_CACHE; + info->data->under_cache = true; } break; } @@ -166,7 +168,8 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct inode *delegated_inode = NULL; int error; struct sdcardfs_inode_info *info; - struct sdcardfs_inode_info *info_top; + struct sdcardfs_inode_data *info_d; + struct sdcardfs_inode_data *info_top; perm_t perm; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); uid_t uid = sbi->options.fs_low_uid; @@ -174,15 +177,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct iattr newattrs; info = SDCARDFS_I(d_inode(dentry)); - perm = info->perm; - if (info->under_obb) { + info_d = info->data; + perm = info_d->perm; + if (info_d->under_obb) { perm = PERM_ANDROID_OBB; - } else if (info->under_cache) { + } else if (info_d->under_cache) { perm = PERM_ANDROID_PACKAGE_CACHE; } else if (perm == PERM_INHERIT) { - info_top = SDCARDFS_I(grab_top(info)); + info_top = top_data_get(info); perm = info_top->perm; - release_top(info); + data_put(info_top); } switch (perm) { @@ -192,7 +196,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_MEDIA: case PERM_ANDROID_PACKAGE: case PERM_ANDROID_PACKAGE_CACHE: - uid = multiuser_get_uid(info->userid, uid); + uid = multiuser_get_uid(info_d->userid, uid); break; case PERM_ANDROID_OBB: uid = AID_MEDIA_OBB; @@ -207,24 +211,24 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: if (S_ISDIR(d_inode(dentry)->i_mode)) - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); else - gid = multiuser_get_uid(info->userid, get_type(name)); + gid = multiuser_get_uid(info_d->userid, get_type(name)); break; case PERM_ANDROID_OBB: gid = AID_MEDIA_OBB; break; case PERM_ANDROID_PACKAGE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_ANDROID_PACKAGE_CACHE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_cache_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_cache_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_PRE_ROOT: default: @@ -257,11 +261,13 @@ retry_deleg: sdcardfs_put_lower_path(dentry, &path); } -static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +static int descendant_may_need_fixup(struct sdcardfs_inode_data *data, + struct limit_search *limit) { - if (info->perm == PERM_ROOT) - return (limit->flags & BY_USERID)?info->userid == limit->userid:1; - if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) + if (data->perm == PERM_ROOT) + return (limit->flags & BY_USERID) ? + data->userid == limit->userid : 1; + if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID) return 1; return 0; } @@ -292,7 +298,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } info = SDCARDFS_I(d_inode(dentry)); - if (needs_fixup(info->perm)) { + if (needs_fixup(info->data->perm)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { spin_lock_nested(&child->d_lock, depth + 1); if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { @@ -305,7 +311,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } spin_unlock(&child->d_lock); } - } else if (descendant_may_need_fixup(info, limit)) { + } else if (descendant_may_need_fixup(info->data, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { __fixup_perms_recursive(child, limit, depth + 1); } @@ -349,12 +355,12 @@ int need_graft_path(struct dentry *dentry) struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if (parent_info->perm == PERM_ANDROID && + if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if (!(sbi->options.multiuser == false - && parent_info->userid == 0)) { + && parent_info->data->userid == 0)) { ret = 1; } } @@ -415,11 +421,11 @@ int is_base_obbpath(struct dentry *dentry) spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if (parent_info->perm == PERM_PRE_ROOT && + if (parent_info->data->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } - } else if (parent_info->perm == PERM_ANDROID && + } else if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4f09eebd7d95..60fea424835f 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -23,7 +23,8 @@ #include <linux/ratelimit.h> /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data) { struct cred *cred; const struct cred *old_cred; @@ -33,10 +34,10 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_ if (!cred) return NULL; - if (info->under_obb) + if (data->under_obb) uid = AID_MEDIA_OBB; else - uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); @@ -96,7 +97,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, + SDCARDFS_I(dir)->data->userid); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); @@ -267,7 +269,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); + struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data; int touch_err = 0; struct fs_struct *saved_fs; struct fs_struct *copied_fs; @@ -336,7 +338,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode make_nomedia_in_obb = 1; } - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid); if (err) { unlock_dir(lower_parent_dentry); goto out; @@ -349,12 +351,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) - && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) + && (pd->perm == PERM_ANDROID) && (pd->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { + ((pd->perm == PERM_ANDROID) + && (qstr_case_eq(&dentry->d_name, &q_data)))) { REVERT_CRED(saved_cred); OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); @@ -616,7 +619,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma { int err; struct inode tmp; - struct inode *top = grab_top(SDCARDFS_I(inode)); + struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode)); if (!top) return -EINVAL; @@ -633,10 +636,11 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * locks must be dealt with to avoid undefined behavior. */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); - release_top(SDCARDFS_I(inode)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); + data_put(top); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); @@ -687,11 +691,11 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct struct iattr lower_ia; struct dentry *parent; struct inode tmp; - struct inode *top; + struct sdcardfs_inode_data *top; const struct cred *saved_cred = NULL; inode = d_inode(dentry); - top = grab_top(SDCARDFS_I(inode)); + top = top_data_get(SDCARDFS_I(inode)); if (!top) return -EINVAL; @@ -709,11 +713,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); - release_top(SDCARDFS_I(inode)); + data_put(top); tmp.i_sb = inode->i_sb; /* @@ -815,17 +820,17 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - struct inode *top = grab_top(info); + struct sdcardfs_inode_data *top = top_data_get(info); if (!top) return -EINVAL; stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; - stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; - stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + stat->uid = make_kuid(&init_user_ns, top->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; @@ -833,7 +838,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->ctime = inode->i_ctime; stat->blksize = (1 << inode->i_blkbits); stat->blocks = inode->i_blocks; - release_top(info); + data_put(top); return 0; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 509d5fbcb472..00ae21151f52 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -71,7 +71,7 @@ struct inode_data { static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/) { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); - userid_t current_userid = SDCARDFS_I(inode)->userid; + userid_t current_userid = SDCARDFS_I(inode)->data->userid; if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) @@ -438,7 +438,8 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, + SDCARDFS_I(dir)->data->userid); if (IS_ERR(ret)) goto out; if (ret) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 953d2156d2e9..3c5b51d49d21 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -327,13 +327,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(d_inode(sb->s_root), PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 89196e31073e..8495474258d5 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -156,7 +156,7 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na struct qstr q_android_secure = QSTR_LITERAL("android_secure"); /* Always block security-sensitive files at root */ - if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { + if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) { if (qstr_case_eq(name, &q_autorun) || qstr_case_eq(name, &q__android_secure) || qstr_case_eq(name, &q_android_secure)) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 2b67b9a8ef9f..31d37d7da4f9 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -30,6 +30,7 @@ #include <linux/file.h> #include <linux/fs.h> #include <linux/aio.h> +#include <linux/kref.h> #include <linux/mm.h> #include <linux/mount.h> #include <linux/namei.h> @@ -81,7 +82,8 @@ */ #define fixup_tmp_permissions(x) \ do { \ - (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ + (x)->i_uid = make_kuid(&init_user_ns, \ + SDCARDFS_I(x)->data->d_uid); \ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) @@ -97,16 +99,16 @@ */ #define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) \ - return -ENOMEM; \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ + if (!saved_cred) \ + return -ENOMEM; \ } while (0) #define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) \ - return ERR_PTR(-ENOMEM); \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ } while (0) #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) @@ -142,9 +144,11 @@ typedef enum { struct sdcardfs_sb_info; struct sdcardfs_mount_options; struct sdcardfs_inode_info; +struct sdcardfs_inode_data; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred); @@ -178,18 +182,26 @@ struct sdcardfs_file_info { const struct vm_operations_struct *lower_vm_ops; }; -/* sdcardfs inode data in memory */ -struct sdcardfs_inode_info { - struct inode *lower_inode; - /* state derived based on current position in hierachy */ +struct sdcardfs_inode_data { + struct kref refcount; + bool abandoned; + perm_t perm; userid_t userid; uid_t d_uid; bool under_android; bool under_cache; bool under_obb; +}; + +/* sdcardfs inode data in memory */ +struct sdcardfs_inode_info { + struct inode *lower_inode; + /* state derived based on current position in hierarchy */ + struct sdcardfs_inode_data *data; + /* top folder for ownership */ - struct inode *top; + struct sdcardfs_inode_data *top_data; struct inode vfs_inode; }; @@ -351,39 +363,56 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb + && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } -/* grab a refererence if we aren't linking to ourself */ -static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +static inline struct sdcardfs_inode_data *data_get( + struct sdcardfs_inode_data *data) { - struct inode *old_top = NULL; - - BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) - old_top = info->top; - if (top != &info->vfs_inode) - igrab(top); - info->top = top; - iput(old_top); + if (data) + kref_get(&data->refcount); + return data; } -static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +static inline struct sdcardfs_inode_data *top_data_get( + struct sdcardfs_inode_info *info) { - struct inode *top = info->top; + return data_get(info->top_data); +} - if (top) - return igrab(top); - else - return NULL; +extern void data_release(struct kref *ref); + +static inline void data_put(struct sdcardfs_inode_data *data) +{ + kref_put(&data->refcount, data_release); +} + +static inline void release_own_data(struct sdcardfs_inode_info *info) +{ + /* + * This happens exactly once per inode. At this point, the inode that + * originally held this data is about to be freed, and all references + * to it are held as a top value, and will likely be released soon. + */ + info->data->abandoned = true; + data_put(info->data); } -static inline void release_top(struct sdcardfs_inode_info *info) +static inline void set_top(struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *top) { - iput(info->top); + struct sdcardfs_inode_data *old_top = info->top_data; + + if (top) + data_get(top); + info->top_data = top; + if (old_top) + data_put(old_top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_gid(struct vfsmount *mnt, + struct sdcardfs_inode_data *data) { struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -396,10 +425,12 @@ static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info */ return AID_SDCARD_RW; else - return multiuser_get_uid(info->userid, opts->gid); + return multiuser_get_uid(data->userid, opts->gid); } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_mode(struct vfsmount *mnt, + struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *data) { int owner_mode; int filtered_mode; @@ -407,12 +438,12 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf int visible_mode = 0775 & ~opts->mask; - if (info->perm == PERM_PRE_ROOT) { + if (data->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure * secondary users can traverse inside. */ visible_mode = 0711; - } else if (info->under_android) { + } else if (data->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. @@ -481,8 +512,9 @@ struct limit_search { userid_t userid; }; -extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top); +extern void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, bool under_android, + struct sdcardfs_inode_data *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); @@ -601,7 +633,7 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct { dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; /* 0775 */ - dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); + dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid); dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 8a9c9c7adca2..7f4539b4b249 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -26,6 +26,23 @@ */ static struct kmem_cache *sdcardfs_inode_cachep; +/* + * To support the top references, we must track some data separately. + * An sdcardfs_inode_info always has a reference to its data, and once set up, + * also has a reference to its top. The top may be itself, in which case it + * holds two references to its data. When top is changed, it takes a ref to the + * new data and then drops the ref to the old data. + */ +static struct kmem_cache *sdcardfs_inode_data_cachep; + +void data_release(struct kref *ref) +{ + struct sdcardfs_inode_data *data = + container_of(ref, struct sdcardfs_inode_data, refcount); + + kmem_cache_free(sdcardfs_inode_data_cachep, data); +} + /* final actions when unmounting a file system */ static void sdcardfs_put_super(struct super_block *sb) { @@ -166,6 +183,7 @@ static void sdcardfs_evict_inode(struct inode *inode) struct inode *lower_inode; truncate_inode_pages(&inode->i_data, 0); + set_top(SDCARDFS_I(inode), NULL); clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented @@ -173,13 +191,13 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); - set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } static struct inode *sdcardfs_alloc_inode(struct super_block *sb) { struct sdcardfs_inode_info *i; + struct sdcardfs_inode_data *d; i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL); if (!i) @@ -188,6 +206,16 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) /* memset everything up to the inode to 0 */ memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode)); + d = kmem_cache_alloc(sdcardfs_inode_data_cachep, + GFP_KERNEL | __GFP_ZERO); + if (!d) { + kmem_cache_free(sdcardfs_inode_cachep, i); + return NULL; + } + + i->data = d; + kref_init(&d->refcount); + i->vfs_inode.i_version = 1; return &i->vfs_inode; } @@ -196,6 +224,7 @@ static void i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); + release_own_data(SDCARDFS_I(inode)); kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); } @@ -214,20 +243,30 @@ static void init_once(void *obj) int sdcardfs_init_inode_cache(void) { - int err = 0; - sdcardfs_inode_cachep = kmem_cache_create("sdcardfs_inode_cache", sizeof(struct sdcardfs_inode_info), 0, SLAB_RECLAIM_ACCOUNT, init_once); + if (!sdcardfs_inode_cachep) - err = -ENOMEM; - return err; + return -ENOMEM; + + sdcardfs_inode_data_cachep = + kmem_cache_create("sdcardfs_inode_data_cache", + sizeof(struct sdcardfs_inode_data), 0, + SLAB_RECLAIM_ACCOUNT, NULL); + if (!sdcardfs_inode_data_cachep) { + kmem_cache_destroy(sdcardfs_inode_cachep); + return -ENOMEM; + } + + return 0; } /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { + kmem_cache_destroy(sdcardfs_inode_data_cachep); kmem_cache_destroy(sdcardfs_inode_cachep); } diff --git a/fs/xattr.c b/fs/xattr.c index 9b932b95d74e..f0da9d24e9ca 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -442,7 +442,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, size = XATTR_SIZE_MAX; kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!kvalue) { - vvalue = vmalloc(size); + vvalue = vzalloc(size); if (!vvalue) return -ENOMEM; kvalue = vvalue; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 0de6290df4da..6649b6d8c437 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -37,6 +37,7 @@ * Generic range manager structs */ #include <linux/bug.h> +#include <linux/rbtree.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -48,6 +49,7 @@ enum drm_mm_search_flags { DRM_MM_SEARCH_DEFAULT = 0, DRM_MM_SEARCH_BEST = 1 << 0, DRM_MM_SEARCH_BELOW = 1 << 1, + DRM_MM_SEARCH_BOTTOM_UP = 1 << 2, }; enum drm_mm_allocator_flags { @@ -61,6 +63,8 @@ enum drm_mm_allocator_flags { struct drm_mm_node { struct list_head node_list; struct list_head hole_stack; + struct rb_node rb; + struct rb_node hole_node; unsigned hole_follows : 1; unsigned scanned_block : 1; unsigned scanned_prev_free : 1; @@ -70,6 +74,7 @@ struct drm_mm_node { unsigned long color; u64 start; u64 size; + u64 __subtree_last; struct drm_mm *mm; }; @@ -79,6 +84,10 @@ struct drm_mm { /* head_node.node_list is the list of all memory nodes, ordered * according to the (increasing) start address of the memory node. */ struct drm_mm_node head_node; + /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */ + struct rb_root interval_tree; + struct rb_root holes_tree; + unsigned int scan_check_range : 1; unsigned scan_alignment; unsigned long scan_color; @@ -301,6 +310,12 @@ void drm_mm_init(struct drm_mm *mm, void drm_mm_takedown(struct drm_mm *mm); bool drm_mm_clean(struct drm_mm *mm); +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last); + void drm_mm_init_scan(struct drm_mm *mm, u64 size, unsigned alignment, 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/kprobes.h b/include/linux/kprobes.h index 8f6849084248..e23392517db9 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -330,7 +330,9 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos); #endif - +extern void wait_for_kprobe_optimizer(void); +#else +static inline void wait_for_kprobe_optimizer(void) { } #endif /* CONFIG_OPTPROBES */ #ifdef CONFIG_KPROBES_ON_FTRACE extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index d9e12c1b1748..aea4c0f2ef5f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -330,7 +330,9 @@ struct mmc_devfeq_clk_scaling { atomic_t devfreq_abort; bool skip_clk_scale_freq_update; int freq_table_sz; + int pltfm_freq_table_sz; u32 *freq_table; + u32 *pltfm_freq_table; unsigned long total_busy_time_us; unsigned long target_freq; unsigned long curr_freq; @@ -821,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/net/mac80211.h b/include/net/mac80211.h index e4a54cd23211..b63712b00047 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1673,6 +1673,9 @@ struct ieee80211_sta_rates { * @supp_rates: Bitmap of supported rates (per band) * @ht_cap: HT capabilities of this STA; restricted to our own capabilities * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities + * @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU + * that this station is allowed to transmit to us. + * Can be modified by driver. * @wme: indicates whether the STA supports QoS/WME (if local devices does, * otherwise always false) * @drv_priv: data area for driver use, will always be aligned to @@ -1699,6 +1702,7 @@ struct ieee80211_sta { u16 aid; struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_vht_cap vht_cap; + u8 max_rx_aggregation_subframes; bool wme; u8 uapsd_queues; u8 max_sp; diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index b434da092b8e..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, @@ -152,4 +145,8 @@ extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num); extern int icnss_trigger_recovery(struct device *dev); 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/sound/q6adm-v2.h b/include/sound/q6adm-v2.h index 900d2455993a..e689e9357012 100644 --- a/include/sound/q6adm-v2.h +++ b/include/sound/q6adm-v2.h @@ -65,6 +65,20 @@ struct route_payload { unsigned int session_id; }; +struct default_chmixer_param_id_coeff { + uint32_t index; + uint16_t num_output_channels; + uint16_t num_input_channels; +}; + +struct msm_pcm_channel_mixer { + int output_channel; + int input_channels[ADM_MAX_CHANNELS]; + bool enable; + int rule; + int channel_weight[ADM_MAX_CHANNELS][ADM_MAX_CHANNELS]; +}; + int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, void *srs_params); @@ -166,4 +180,8 @@ int adm_get_source_tracking(int port_id, int copp_idx, struct source_tracking_param *sourceTrackingData); int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate, bool spk_swap); +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index); #endif /* __Q6_ADM_V2_H__ */ diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 97069ecabe49..5f9b62c129fc 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -117,7 +117,7 @@ void __transport_register_session(struct se_portal_group *, struct se_node_acl *, struct se_session *, void *); void transport_register_session(struct se_portal_group *, struct se_node_acl *, struct se_session *, void *); -void target_get_session(struct se_session *); +int target_get_session(struct se_session *); void target_put_session(struct se_session *); ssize_t target_show_dynamic_sessions(struct se_portal_group *, char *); void transport_free_session(struct se_session *); @@ -172,8 +172,7 @@ bool target_tpg_has_node_acl(struct se_portal_group *tpg, const char *); struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *, unsigned char *); -int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *, - unsigned char *, u32, int); +int core_tpg_set_initiator_node_queue_depth(struct se_node_acl *, u32); int core_tpg_set_initiator_node_tag(struct se_portal_group *, struct se_node_acl *, const char *); int core_tpg_register(struct se_wwn *, struct se_portal_group *, int); diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 7aa6496c7608..831a0c81cddb 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -102,6 +102,13 @@ struct drm_msm_gem_new { __u32 handle; /* out */ }; +struct drm_msm_gem_svm_new { + __u64 hostptr; /* in, must be page-aligned */ + __u64 size; /* in, must be page-aligned */ + __u32 flags; /* in, mask of MSM_BO_x */ + __u32 handle; /* out */ +}; + #define MSM_INFO_IOVA 0x01 #define MSM_INFO_FLAGS (MSM_INFO_IOVA) @@ -218,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 { @@ -346,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 @@ -356,6 +380,10 @@ struct drm_msm_gem_sync { #define DRM_MSM_GEM_CPU_FINI 0x05 #define DRM_MSM_GEM_SUBMIT 0x06 #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 @@ -395,6 +423,15 @@ struct drm_msm_gem_sync { struct drm_msm_counter_read) #define DRM_IOCTL_MSM_GEM_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_GEM_SYNC,\ 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/drm/sde_drm.h b/include/uapi/drm/sde_drm.h index c7bed3b1ccf3..bef841446247 100644 --- a/include/uapi/drm/sde_drm.h +++ b/include/uapi/drm/sde_drm.h @@ -65,6 +65,48 @@ #define SDE_DRM_BITMASK_COUNT 64 /** + * Framebuffer modes for "fb_translation_mode" PLANE property + * + * @SDE_DRM_FB_NON_SEC: IOMMU configuration for this framebuffer mode + * is non-secure domain and requires + * both stage I and stage II translations when + * this buffer is accessed by the display HW. + * This is the default mode of all frambuffers. + * @SDE_DRM_FB_SEC: IOMMU configuration for this framebuffer mode + * is secure domain and requires + * both stage I and stage II translations when + * this buffer is accessed by the display HW. + * @SDE_DRM_FB_NON_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode + * is non-secure domain and requires + * only stage II translation when + * this buffer is accessed by the display HW. + * @SDE_DRM_FB_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode + * is secure domain and requires + * only stage II translation when + * this buffer is accessed by the display HW. +*/ + +#define SDE_DRM_FB_NON_SEC 0 +#define SDE_DRM_FB_SEC 1 +#define SDE_DRM_FB_NON_SEC_DIR_TRANS 2 +#define SDE_DRM_FB_SEC_DIR_TRANS 3 + +/** + * Secure levels for "security_level" CRTC property. + * CRTC property which specifies what plane types + * can be attached to this CRTC. Plane component + * derives the plane type based on the FB_MODE. + * @ SDE_DRM_SEC_NON_SEC: Both Secure and non-secure plane types can be + * attached to this CRTC. This is the default state of + * the CRTC. + * @ SDE_DRM_SEC_ONLY: Only secure planes can be added to this CRTC. If a + * CRTC is instructed to be in this mode it follows the + * platform dependent restrictions. + */ +#define SDE_DRM_SEC_NON_SEC 0 +#define SDE_DRM_SEC_ONLY 1 + +/** * struct sde_drm_pix_ext_v1 - version 1 of pixel ext structure * @num_ext_pxls_lr: Number of total horizontal pixels * @num_ext_pxls_tb: Number of total vertical lines 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/kernel/fork.c b/kernel/fork.c index 622571d2a833..2845c5bdc8e3 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1596,11 +1596,13 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ recalc_sigpending(); if (signal_pending(current)) { - spin_unlock(¤t->sighand->siglock); - write_unlock_irq(&tasklist_lock); retval = -ERESTARTNOINTR; goto bad_fork_cancel_cgroup; } + if (unlikely(!(ns_of_pid(pid)->nr_hashed & PIDNS_HASH_ADDING))) { + retval = -ENOMEM; + goto bad_fork_cancel_cgroup; + } if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); @@ -1651,6 +1653,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, return p; bad_fork_cancel_cgroup: + spin_unlock(¤t->sighand->siglock); + write_unlock_irq(&tasklist_lock); cgroup_cancel_fork(p, cgrp_ss_priv); bad_fork_free_pid: threadgroup_change_end(current); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 9812d9c0d483..e0449956298e 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -810,8 +810,8 @@ irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle, if (!desc) return; - __irq_do_set_handler(desc, handle, 1, NULL); desc->irq_common_data.handler_data = data; + __irq_do_set_handler(desc, handle, 1, NULL); irq_put_desc_busunlock(desc, flags); } diff --git a/kernel/kprobes.c b/kernel/kprobes.c index d10ab6b9b5e0..695763516908 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -563,7 +563,7 @@ static void kprobe_optimizer(struct work_struct *work) } /* Wait for completing optimization and unoptimization */ -static void wait_for_kprobe_optimizer(void) +void wait_for_kprobe_optimizer(void) { mutex_lock(&kprobe_mutex); diff --git a/kernel/padata.c b/kernel/padata.c index 401227e3967c..ecc7b3f452c7 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -357,7 +357,7 @@ static int padata_setup_cpumasks(struct parallel_data *pd, cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_online_mask); if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL)) { - free_cpumask_var(pd->cpumask.cbcpu); + free_cpumask_var(pd->cpumask.pcpu); return -ENOMEM; } diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index a65ba137fd15..567ecc826bc8 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -255,7 +255,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns) * if reparented. */ for (;;) { - set_current_state(TASK_UNINTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); if (pid_ns->nr_hashed == init_pids) break; schedule(); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9abc3e65dbd9..099a1b93bebf 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3240,7 +3240,8 @@ retry: sbc_flag |= SBC_FLAG_IDLE_LEAST_LOADED; } } else if (stats.best_cpu >= 0) { - if (stats.best_cpu != task_cpu(p) && + if (stats.best_sibling_cpu >= 0 && + stats.best_cpu != task_cpu(p) && stats.min_cost == stats.best_sibling_cpu_cost) { stats.best_cpu = stats.best_sibling_cpu; sbc_flag |= SBC_FLAG_BEST_SIBLING; @@ -5112,6 +5113,26 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq) if (!cfs_bandwidth_used()) return; + /* Synchronize hierarchical throttle counter: */ + if (unlikely(!cfs_rq->throttle_uptodate)) { + struct rq *rq = rq_of(cfs_rq); + struct cfs_rq *pcfs_rq; + struct task_group *tg; + + cfs_rq->throttle_uptodate = 1; + + /* Get closest up-to-date node, because leaves go first: */ + for (tg = cfs_rq->tg->parent; tg; tg = tg->parent) { + pcfs_rq = tg->cfs_rq[cpu_of(rq)]; + if (pcfs_rq->throttle_uptodate) + break; + } + if (tg) { + cfs_rq->throttle_count = pcfs_rq->throttle_count; + cfs_rq->throttled_clock_task = rq_clock_task(rq); + } + } + /* an active group must be handled by the update_curr()->put() path */ if (!cfs_rq->runtime_enabled || cfs_rq->curr) return; @@ -5502,15 +5523,14 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) /* Don't dequeue parent if it has other entities besides us */ if (cfs_rq->load.weight) { + /* Avoid re-evaluating load for this entity: */ + se = parent_entity(se); /* * Bias pick_next to pick a task from this cfs_rq, as * p is sleeping when it is within its sched_slice. */ - if (task_sleep && parent_entity(se)) - set_next_buddy(parent_entity(se)); - - /* avoid re-evaluating load for this entity */ - se = parent_entity(se); + if (task_sleep && se && !throttled_hierarchy(cfs_rq)) + set_next_buddy(se); break; } flags |= DEQUEUE_SLEEP; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index a6733b57bcbc..b88f647ea935 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -511,7 +511,7 @@ struct cfs_rq { u64 throttled_clock, throttled_clock_task; u64 throttled_clock_task_time; - int throttled, throttle_count; + int throttled, throttle_count, throttle_uptodate; struct list_head throttled_list; #endif /* CONFIG_CFS_BANDWIDTH */ #endif /* CONFIG_FAIR_GROUP_SCHED */ diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index c9956440d0e6..12ea4ea619ee 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1471,6 +1471,11 @@ static __init int kprobe_trace_self_tests_init(void) end: release_all_trace_kprobes(); + /* + * Wait for the optimizer work to finish. Otherwise it might fiddle + * with probes in already freed __init text. + */ + wait_for_kprobe_optimizer(); if (warn) pr_cont("NG: Some tests are failed. Please check them.\n"); else diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 530e6427f823..47b469663822 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1269,6 +1269,16 @@ out_unlock: return ret; } +/* + * FOLL_FORCE can write to even unwritable pmd's, but only + * after we've gone through a COW cycle and they are dirty. + */ +static inline bool can_follow_write_pmd(pmd_t pmd, unsigned int flags) +{ + return pmd_write(pmd) || + ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pmd_dirty(pmd)); +} + struct page *follow_trans_huge_pmd(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmd, @@ -1279,7 +1289,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma, assert_spin_locked(pmd_lockptr(mm, pmd)); - if (flags & FOLL_WRITE && !pmd_write(*pmd)) + if (flags & FOLL_WRITE && !can_follow_write_pmd(*pmd, flags)) goto out; /* Avoid dumping huge zero page */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 87fd1a07326b..8402c34592ec 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1164,7 +1164,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE| + MSG_CMSG_COMPAT)) return -EINVAL; if (len < 4 || len > HCI_MAX_FRAME_SIZE) 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/ipx/af_ipx.c b/net/ipx/af_ipx.c index 48d0dc89b58d..e735f781e4f3 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1168,11 +1168,10 @@ static int ipxitf_ioctl(unsigned int cmd, void __user *arg) sipx->sipx_network = ipxif->if_netnum; memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node)); - rc = -EFAULT; + rc = 0; if (copy_to_user(arg, &ifr, sizeof(ifr))) - break; + rc = -EFAULT; ipxitf_put(ipxif); - rc = 0; break; } case SIOCAIPXITFCRT: diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f598ff80b30e..a830356b94ac 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -290,10 +290,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, buf_size = IEEE80211_MAX_AMPDU_BUF; /* make sure the size doesn't exceed the maximum supported by the hw */ - if (buf_size > local->hw.max_rx_aggregation_subframes) - buf_size = local->hw.max_rx_aggregation_subframes; + if (buf_size > sta->sta.max_rx_aggregation_subframes) + buf_size = sta->sta.max_rx_aggregation_subframes; params.buf_size = buf_size; + ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n", + buf_size, sta->sta.addr); + /* examine state machine */ mutex_lock(&sta->ampdu_mlme.mtx); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index fe88071d4abb..d2075804cbff 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -330,6 +330,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, memcpy(sta->addr, addr, ETH_ALEN); memcpy(sta->sta.addr, addr, ETH_ALEN); + sta->sta.max_rx_aggregation_subframes = + local->hw.max_rx_aggregation_subframes; + sta->local = local; sta->sdata = sdata; sta->rx_stats.last_rx = jiffies; diff --git a/net/netfilter/xt_HARDIDLETIMER.c b/net/netfilter/xt_HARDIDLETIMER.c index 06322e4de632..ecb4a7fb6bf8 100644 --- a/net/netfilter/xt_HARDIDLETIMER.c +++ b/net/netfilter/xt_HARDIDLETIMER.c @@ -4,7 +4,7 @@ * Netfilter module to trigger a timer when packet matches. * After timer expires a kevent will be sent. * - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2017 The Linux Foundation. All rights reserved. * * Copyright (C) 2004, 2010 Nokia Corporation * @@ -187,6 +187,8 @@ static int hardidletimer_tg_create(struct hardidletimer_tg_info *info) pr_debug("couldn't add file to sysfs"); goto out_free_attr; } + /* notify userspace */ + kobject_uevent(hardidletimer_tg_kobj, KOBJ_ADD); list_add(&info->timer->entry, &hardidletimer_tg_list); diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 80b32de1d99c..f9eb8641dc3a 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -307,6 +307,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) pr_debug("couldn't add file to sysfs"); goto out_free_attr; } + /* notify userspace */ + kobject_uevent(idletimer_tg_kobj, KOBJ_ADD); list_add(&info->timer->entry, &idletimer_tg_list); 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/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index ed5a9c110b3a..9ce9d5003dcc 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -203,10 +203,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, cause = "missing-hash"; status = INTEGRITY_NOLABEL; - if (opened & FILE_CREATED) { + if (opened & FILE_CREATED) iint->flags |= IMA_NEW_FILE; + if ((iint->flags & IMA_NEW_FILE) && + !(iint->flags & IMA_DIGSIG_REQUIRED)) status = INTEGRITY_PASS; - } goto out; } diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index 00f2aa766363..ee057e99bc41 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -1400,8 +1400,26 @@ static int msm_anlg_cdc_codec_enable_on_demand_supply( } switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (atomic_inc_return(&supply->ref) == 1) + if (atomic_inc_return(&supply->ref) == 1) { + ret = regulator_set_voltage(supply->supply, + supply->min_uv, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(en) for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, + supply->optimum_ua); + if (ret < 0) { + dev_err(codec->dev, + "Setting regulator optimum mode(en) failed for micbias with err = %d\n", + ret); + goto out; + } ret = regulator_enable(supply->supply); + } if (ret) dev_err(codec->dev, "%s: Failed to enable %s\n", __func__, @@ -1413,12 +1431,27 @@ static int msm_anlg_cdc_codec_enable_on_demand_supply( __func__, on_demand_supply_name[w->shift]); goto out; } - if (atomic_dec_return(&supply->ref) == 0) + if (atomic_dec_return(&supply->ref) == 0) { ret = regulator_disable(supply->supply); if (ret) dev_err(codec->dev, "%s: Failed to disable %s\n", __func__, on_demand_supply_name[w->shift]); + ret = regulator_set_voltage(supply->supply, + 0, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(dis) failed for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, 0); + if (ret < 0) + dev_err(codec->dev, + "Setting regulator optimum mode(dis) failed for micbias with err = %d\n", + ret); + } break; default: break; @@ -3685,6 +3718,30 @@ static struct regulator *msm_anlg_cdc_find_regulator( return NULL; } +static void msm_anlg_cdc_update_micbias_regulator( + const struct sdm660_cdc_priv *sdm660_cdc, + const char *name, + struct on_demand_supply *micbias_supply) +{ + int i; + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (sdm660_cdc->supplies[i].supply && + !strcmp(sdm660_cdc->supplies[i].supply, name)) { + micbias_supply->supply = + sdm660_cdc->supplies[i].consumer; + micbias_supply->min_uv = pdata->regulator[i].min_uv; + micbias_supply->max_uv = pdata->regulator[i].max_uv; + micbias_supply->optimum_ua = + pdata->regulator[i].optimum_ua; + return; + } + } + + dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n", name); +} + static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec) { struct msm_asoc_mach_data *pdata = NULL; @@ -4114,10 +4171,10 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) wcd9xxx_spmi_set_codec(codec); - sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].supply = - msm_anlg_cdc_find_regulator( + msm_anlg_cdc_update_micbias_regulator( sdm660_cdc, - on_demand_supply_name[ON_DEMAND_MICBIAS]); + on_demand_supply_name[ON_DEMAND_MICBIAS], + &sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS]); atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); @@ -4183,7 +4240,7 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum( if (pdata->regulator[i].ondemand) continue; if (regulator_count_voltages( - sdm660_cdc->supplies[i].consumer) <= 0) + sdm660_cdc->supplies[i].consumer) <= 0) continue; ret = regulator_set_voltage( @@ -4216,7 +4273,7 @@ static int msm_anlg_cdc_disable_static_supplies_to_optimum( if (pdata->regulator[i].ondemand) continue; if (regulator_count_voltages( - sdm660_cdc->supplies[i].consumer) <= 0) + sdm660_cdc->supplies[i].consumer) <= 0) continue; regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, pdata->regulator[i].max_uv); @@ -4317,6 +4374,28 @@ static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc, if (regulator_count_voltages( sdm660_cdc->supplies[i].consumer) <= 0) continue; + if (pdata->regulator[i].ondemand) { + ret = regulator_set_voltage( + sdm660_cdc->supplies[i].consumer, + 0, pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_load( + sdm660_cdc->supplies[i].consumer, 0); + if (ret < 0) { + dev_err(sdm660_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + continue; + } + } ret = regulator_set_voltage(sdm660_cdc->supplies[i].consumer, pdata->regulator[i].min_uv, pdata->regulator[i].max_uv); diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h index 0c9e9a6aeb6a..9563565f36d2 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -144,6 +144,9 @@ struct sdm660_cdc_regulator { struct on_demand_supply { struct regulator *supply; atomic_t ref; + int min_uv; + int max_uv; + int optimum_ua; }; struct wcd_imped_i_ref { 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/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index f41c6107aac8..974d4a582540 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -81,6 +81,12 @@ static bool is_custom_stereo_on; static bool is_ds2_on; static bool swap_ch; +#define WEIGHT_0_DB 0x4000 +/* all the FEs which can support channel mixer */ +static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE]; +/* input BE for each FE */ +static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS]; + enum { MADNONE, MADAUDIO, @@ -1257,6 +1263,62 @@ static u32 msm_pcm_routing_get_voc_sessionid(u16 val) return session_id; } +static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode, + int dspst_id, int stream_type) +{ + int copp_idx = 0; + int sess_type = 0; + int i = 0, j = 0, be_id; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return 0; + } + + if (!(channel_mixer[fe_id].enable)) { + pr_debug("%s: channel mixer not enabled for FE %d\n", + __func__, fe_id); + return 0; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + sess_type = SESSION_TYPE_RX; + else + sess_type = SESSION_TYPE_TX; + + for (i = 0; i < ADM_MAX_CHANNELS && channel_input[fe_id][i] > 0; + ++i) { + be_id = channel_input[fe_id][i] - 1; + channel_mixer[fe_id].input_channels[i] = + msm_bedais[be_id].channel; + + if ((msm_bedais[be_id].active) && + test_bit(fe_id, + &msm_bedais[be_id].fe_sessions[0])) { + unsigned long copp = + session_copp_map[fe_id][sess_type][be_id]; + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + if (test_bit(j, &copp)) { + copp_idx = j; + break; + } + } + + pr_debug("%s: fe %d, be %d, channel %d, copp %d\n", + __func__, + fe_id, be_id, msm_bedais[be_id].channel, + copp_idx); + ret = adm_programable_channel_mixer( + msm_bedais[be_id].port_id, + copp_idx, dspst_id, sess_type, + channel_mixer + fe_id, i); + } + } + + return ret; +} + int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id, int stream_type) { @@ -1265,6 +1327,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, u32 channels, sample_rate; uint16_t bits_per_sample = 16; uint32_t passthr_mode = LEGACY_PCM; + int ret = 0; if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { /* bad ID assigned in machine driver */ @@ -1388,8 +1451,11 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, adm_matrix_map(path_type, payload, perf_mode, passthr_mode); msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); } + + ret = msm_pcm_routing_channel_mixer(fedai_id, perf_mode, + dspst_id, stream_type); mutex_unlock(&routing_lock); - return 0; + return ret; } int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode, @@ -2614,6 +2680,649 @@ static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol, return 1; } +static int msm_pcm_get_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = channel_mixer[fe_id].rule; + + return 0; +} + +static int msm_pcm_put_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].rule = ucontrol->value.integer.value[0]; + + return 1; +} + +static int msm_pcm_get_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + channel_mixer[fe_id].output_channel; + return 0; +} + +static int msm_pcm_put_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: fe_id is %d, output channels = %d\n", __func__, + fe_id, + (unsigned int)(ucontrol->value.integer.value[0])); + channel_mixer[fe_id].output_channel = + (unsigned int)(ucontrol->value.integer.value[0]); + + return 1; +} + +static const char *const ch_mixer[] = {"Disable", "Enable"}; + +/* If new backend is added, need update this array */ +static const char *const be_name[] = { +"ZERO", "PRI_I2S_RX", "PRI_I2S_TX", "SLIM_0_RX", +"SLIM_0_TX", "HDMI_RX", "INT_BT_SCO_RX", "INT_BT_SCO_TX", +"INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX", +"AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX", +"INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX", +"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX", +"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX", +"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX", +"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX", +"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX", +"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX", +"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX", +"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SPDIF_RX", +"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX", +"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1", +"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3", +"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5", +"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7", +"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1", +"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3", +"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5", +"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7", +"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1", +"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3", +"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5", +"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7", +"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1", +"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3", +"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5", +"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7", +"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", +"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX", +"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", +"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", +"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", +"INT6_MI2S_RX", "INT6_MI2S_TX" +}; + +static SOC_ENUM_SINGLE_DECL(mm1_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm2_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA2, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm3_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA3, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm4_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA4, ch_mixer); + +static SOC_ENUM_DOUBLE_DECL(mm1_ch1_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 0, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch2_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 1, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch3_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 2, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch4_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 3, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch5_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 4, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch6_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 5, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch7_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 6, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch8_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 7, be_name); + +static int msm_pcm_get_ctl_enum_info(struct snd_ctl_elem_info *uinfo, + unsigned int channels, + unsigned int items, const char *const names[]) +{ + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + WARN(strlen(names[uinfo->value.enumerated.item]) >= + sizeof(uinfo->value.enumerated.name), + "ALSA: too long item name '%s'\n", + names[uinfo->value.enumerated.item]); + strlcpy(uinfo->value.enumerated.name, + names[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + return 0; +} + +static int msm_pcm_channel_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(ch_mixer); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} +static int msm_pcm_channel_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: FE %d %s\n", __func__, + fe_id, + channel_mixer[fe_id].enable ? "Enabled" : "Disabled"); + ucontrol->value.enumerated.item[0] = channel_mixer[fe_id].enable; + return 0; +} + +static int msm_pcm_channel_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].enable = ucontrol->value.enumerated.item[0]; + pr_debug("%s: %s FE %d\n", __func__, + channel_mixer[fe_id].enable ? "Enable" : "Disable", + fe_id); + return 0; +} + +static int msm_pcm_channel_input_be_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(be_name); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} + +static int msm_pcm_channel_input_be_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + channel_input[fe_id][in_ch] = ucontrol->value.enumerated.item[0]; + return 1; +} + +static int msm_pcm_channel_input_be_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + ucontrol->value.enumerated.item[0] = channel_input[fe_id][in_ch]; + return 1; +} + + +static int msm_pcm_channel_weight_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ADM_MAX_CHANNELS; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WEIGHT_0_DB; + + return 0; +} + +static int msm_pcm_channel_weight_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i, weight; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + pr_debug("%s: FE_ID: %d, channel weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld\n", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) { + weight = ucontrol->value.integer.value[i]; + channel_mixer[fe_id].channel_weight[out_ch][i] = weight; + pr_debug("%s: FE_ID %d, output %d input %d weight %d\n", + __func__, fe_id, out_ch, i, + channel_mixer[fe_id].channel_weight[out_ch][i]); + } + + return 0; +} + +static int msm_pcm_channel_weight_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) + ucontrol->value.integer.value[i] = + channel_mixer[fe_id].channel_weight[out_ch][i]; + + pr_debug("%s: FE_ID: %d, weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + return 0; +} + +static const struct snd_kcontrol_new channel_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia2 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia3 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia4 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia5 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia6 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + + SOC_SINGLE_EXT("MultiMedia1 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia2 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia3 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia4 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia5 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia6 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm1_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm2_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm3_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia4 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm4_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 1, } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel4", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 3,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel5", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 4,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel6", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 5,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel7", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 6,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel8", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 7,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel1", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch1_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel2", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch2_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel3", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch3_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel4", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch4_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel5", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch5_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel6", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch6_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel7", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch7_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel8", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch8_enum) + }, +}; static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -14809,6 +15518,9 @@ static int msm_routing_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, ec_ref_param_controls, ARRAY_SIZE(ec_ref_param_controls)); + snd_soc_add_platform_controls(platform, channel_mixer_controls, + ARRAY_SIZE(channel_mixer_controls)); + msm_qti_pp_add_controls(platform); msm_dts_srs_tm_add_controls(platform); diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index 7cf19a9ed335..61dd478b97e4 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -520,6 +520,267 @@ fail_cmd: return ret; } +static int adm_populate_channel_weight(u16 *ptr, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + u16 i, j, start_index = 0; + + if (channel_index > ch_mixer->output_channel) { + pr_err("%s: channel index %d is larger than output_channel %d\n", + __func__, channel_index, ch_mixer->output_channel); + return -EINVAL; + } + + for (i = 0; i < ch_mixer->output_channel; i++) { + pr_debug("%s: weight for output %d:", __func__, i); + for (j = 0; j < ADM_MAX_CHANNELS; j++) + pr_debug(" %d", + ch_mixer->channel_weight[i][j]); + pr_debug("\n"); + } + + for (i = 0; i < channel_index; ++i) + start_index += ch_mixer->input_channels[i]; + + for (i = 0; i < ch_mixer->output_channel; ++i) { + for (j = start_index; + j < start_index + + ch_mixer->input_channels[channel_index]; j++) { + *ptr = ch_mixer->channel_weight[i][j]; + pr_debug("%s: ptr[%d][%d] = %d\n", + __func__, i, j, *ptr); + ptr++; + } + } + + return 0; +} + +/* + * adm_programable_channel_mixer + * + * Receives port_id, copp_idx, session_id, session_type, ch_mixer + * and channel_index to send ADM command to mix COPP data. + * + * port_id - Passed value, port_id for which backend is wanted + * copp_idx - Passed value, copp_idx for which COPP is wanted + * session_id - Passed value, session_id for which session is needed + * session_type - Passed value, session_type for RX or TX + * ch_mixer - Passed value, ch_mixer for which channel mixer config is needed + * channel_index - Passed value, channel_index for which channel is needed + */ +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL; + struct adm_param_data_v5 data_v5; + int ret = 0, port_idx, sz = 0, param_size = 0; + u16 *adm_pspd_params; + u16 *ptr; + int index = 0; + + pr_debug("%s: port_id = %d\n", __func__, port_id); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } + /* + * First 8 bytes are 4 bytes as rule number, 2 bytes as output + * channel and 2 bytes as input channel. + * 2 * ch_mixer->output_channel means output channel mapping. + * 2 * ch_mixer->input_channels[channel_index]) means input + * channel mapping. + * 2 * ch_mixer->input_channels[channel_index] * + * ch_mixer->output_channel) means the channel mixer weighting + * coefficients. + * param_size needs to be a multiple of 4 bytes. + */ + + param_size = 2 * (4 + ch_mixer->output_channel + + ch_mixer->input_channels[channel_index] + + ch_mixer->input_channels[channel_index] * + ch_mixer->output_channel); + roundup(param_size, 4); + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct adm_param_data_v5) + param_size; + pr_debug("%s: sz = %d\n", __func__, sz); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) + return -ENOMEM; + + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->direction = session_type; + adm_params->sessionid = session_id; + pr_debug("%s: copp_id = %d, session id %d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx][copp_idx]), + session_id); + adm_params->deviceid = atomic_read( + &this_adm.copp.id[port_idx][copp_idx]); + adm_params->reserved = 0; + + data_v5.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER; + data_v5.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF; + data_v5.reserved = 0; + data_v5.param_size = param_size; + adm_params->payload_size = + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct adm_param_data_v5) + data_v5.param_size; + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)); + memcpy(adm_pspd_params, &data_v5, sizeof(data_v5)); + + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(data_v5)); + + adm_pspd_params[0] = ch_mixer->rule; + adm_pspd_params[2] = ch_mixer->output_channel; + adm_pspd_params[3] = ch_mixer->input_channels[channel_index]; + index = 4; + + if (ch_mixer->output_channel == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->output_channel == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->output_channel; + if (ch_mixer->input_channels[channel_index] == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->input_channels[channel_index] == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->input_channels[channel_index]; + ret = adm_populate_channel_weight(&adm_pspd_params[index], + ch_mixer, channel_index); + if (!ret) { + pr_err("%s: fail to get channel weight with error %d\n", + __func__, ret); + goto fail_cmd; + } + + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5; + adm_params->hdr.pkt_size = sz; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->reserved = 0; + + ptr = (u16 *)adm_params; + for (index = 0; index < (sz / 2); index++) + pr_debug("%s: adm_params[%d] = 0x%x\n", + __func__, index, (unsigned int)ptr[index]); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (ret < 0) { + pr_err("%s: Set params failed port %d rc %d\n", __func__, + port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read( + &this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: set params timed out port = %d\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + ret = 0; +fail_cmd: + kfree(adm_params); + + return ret; +} + int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx, unsigned int session_id, char *params, uint32_t params_length) 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: diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c index 31a3035cd4eb..923e59eb82c7 100644 --- a/tools/testing/selftests/x86/ldt_gdt.c +++ b/tools/testing/selftests/x86/ldt_gdt.c @@ -394,6 +394,51 @@ static void *threadproc(void *ctx) } } +#ifdef __i386__ + +#ifndef SA_RESTORE +#define SA_RESTORER 0x04000000 +#endif + +/* + * The UAPI header calls this 'struct sigaction', which conflicts with + * glibc. Sigh. + */ +struct fake_ksigaction { + void *handler; /* the real type is nasty */ + unsigned long sa_flags; + void (*sa_restorer)(void); + unsigned char sigset[8]; +}; + +static void fix_sa_restorer(int sig) +{ + struct fake_ksigaction ksa; + + if (syscall(SYS_rt_sigaction, sig, NULL, &ksa, 8) == 0) { + /* + * glibc has a nasty bug: it sometimes writes garbage to + * sa_restorer. This interacts quite badly with anything + * that fiddles with SS because it can trigger legacy + * stack switching. Patch it up. See: + * + * https://sourceware.org/bugzilla/show_bug.cgi?id=21269 + */ + if (!(ksa.sa_flags & SA_RESTORER) && ksa.sa_restorer) { + ksa.sa_restorer = NULL; + if (syscall(SYS_rt_sigaction, sig, &ksa, NULL, + sizeof(ksa.sigset)) != 0) + err(1, "rt_sigaction"); + } + } +} +#else +static void fix_sa_restorer(int sig) +{ + /* 64-bit glibc works fine. */ +} +#endif + static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags) { @@ -405,6 +450,7 @@ static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), if (sigaction(sig, &sa, 0)) err(1, "sigaction"); + fix_sa_restorer(sig); } static jmp_buf jmpbuf; |
